hv: split-lock: using MTF instead of TF(#DB)

The TF is visible to guest which may be modified by
the guest, so it is not a safe method to emulate the
split-lock. While MTF is specifically designed for
single-stepping in x86/Intel hardware virtualization
VT-x technology which is invisible to the guest. Use MTF
to single step the VCPU during the emulation of split lock.

Tracked-On: #5605
Signed-off-by: Jie Deng <jie.deng@intel.com>
This commit is contained in:
Jie Deng 2020-11-27 13:09:54 +08:00 committed by acrnsi-robot
parent 6852438e3a
commit f291997811
3 changed files with 53 additions and 34 deletions

View File

@ -432,7 +432,13 @@ int32_t acrn_handle_pending_request(struct acrn_vcpu *vcpu)
}
}
acrn_inject_pending_intr(vcpu, pending_req_bits, injected);
/*
* Defer injection of interrupt to be after MTF VM exit,
* when emulating the split-lock.
*/
if (!vcpu->arch.emulating_lock) {
acrn_inject_pending_intr(vcpu, pending_req_bits, injected);
}
/*
* If "virtual-interrupt delivered" is enabled, CPU will evaluate
@ -548,6 +554,7 @@ static int32_t emulate_splitlock(struct acrn_vcpu *vcpu, uint32_t exception_vect
uint8_t inst[1];
uint32_t err_code = 0U;
uint64_t fault_addr;
uint32_t value32;
/* Queue the exception by default if the exception cannot be handled. */
*queue_exception = true;
@ -587,11 +594,11 @@ static int32_t emulate_splitlock(struct acrn_vcpu *vcpu, uint32_t exception_vect
*/
vcpu->arch.inst_len = 1U;
if (vcpu->vm->hw.created_vcpus > 1U) {
/*
* Set the TF to have a #DB after running the split-lock
* instruction and tag the emulating_lock to be true.
*/
vcpu_set_rflags(vcpu, vcpu_get_rflags(vcpu) | HV_ARCH_VCPU_RFLAGS_TF);
/* Enable MTF to start single-stepping execution */
value32 = exec_vmread32(VMX_PROC_VM_EXEC_CONTROLS);
value32 |= VMX_PROCBASED_CTLS_MON_TRAP;
exec_vmwrite32(VMX_PROC_VM_EXEC_CONTROLS, value32);
vcpu->arch.emulating_lock = true;
}
@ -644,31 +651,6 @@ static int32_t emulate_splitlock(struct acrn_vcpu *vcpu, uint32_t exception_vect
}
}
break;
case IDT_DB:
/*
* We only handle #DB caused by split-lock emulation,
* otherwise, inject it back.
*/
if (vcpu->arch.emulating_lock) {
/*
* The split-lock emulation has been completed, tag the emulating_lock
* to be false, and clear the TF flag.
*/
vcpu->arch.emulating_lock = false;
vcpu_set_rflags(vcpu, vcpu_get_rflags(vcpu) & (~HV_ARCH_VCPU_RFLAGS_TF));
/*
* Notify other vcpus of the guest to restart execution.
*/
vcpu_complete_splitlock_emulation(vcpu);
/* This #DB is for split-lock emulation, do not inject it */
*queue_exception = false;
/* This case we should not skip the instruction */
vcpu->arch.inst_len = 0U;
}
break;
default:
break;

View File

@ -368,11 +368,11 @@ static void init_exec_ctrl(struct acrn_vcpu *vcpu)
/* Set up guest exception mask bitmap setting a bit * causes a VM exit
* on corresponding guest * exception - pg 2902 24.6.3
* enable VM exit on MC always
* enable AC and DB for split lock emulation when split lock detection is enabled on physical platform.
* enable AC for split-lock emulation when split-lock detection is enabled on physical platform.
*/
value32 = (1U << IDT_MC);
if (is_ac_enabled()) {
value32 = (value32 | (1U << IDT_AC) | (1U << IDT_DB));
value32 = (value32 | (1U << IDT_AC));
}
exec_vmwrite32(VMX_EXCEPTION_BITMAP, value32);

View File

@ -33,6 +33,7 @@ static int32_t wbinvd_vmexit_handler(struct acrn_vcpu *vcpu);
static int32_t undefined_vmexit_handler(struct acrn_vcpu *vcpu);
static int32_t pause_vmexit_handler(__unused struct acrn_vcpu *vcpu);
static int32_t hlt_vmexit_handler(struct acrn_vcpu *vcpu);
static int32_t mtf_vmexit_handler(struct acrn_vcpu *vcpu);
/* VM Dispatch table for Exit condition handling */
static const struct vm_exit_dispatch dispatch_table[NR_VMX_EXIT_REASONS] = {
@ -112,7 +113,7 @@ static const struct vm_exit_dispatch dispatch_table[NR_VMX_EXIT_REASONS] = {
[VMX_EXIT_REASON_MWAIT] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_MONITOR_TRAP] = {
.handler = unhandled_vmexit_handler},
.handler = mtf_vmexit_handler},
[VMX_EXIT_REASON_MONITOR] = {
.handler = unhandled_vmexit_handler},
[VMX_EXIT_REASON_PAUSE] = {
@ -271,6 +272,42 @@ static int32_t unhandled_vmexit_handler(struct acrn_vcpu *vcpu)
return 0;
}
static void vcpu_complete_split_lock_emulation(struct acrn_vcpu *cur_vcpu)
{
struct acrn_vcpu *other;
uint16_t i;
if (cur_vcpu->vm->hw.created_vcpus > 1U) {
foreach_vcpu(i, cur_vcpu->vm, other) {
if (other != cur_vcpu) {
bitmap_clear_lock(ACRN_REQUEST_SPLIT_LOCK, &other->arch.pending_req);
signal_event(&other->events[VCPU_EVENT_SPLIT_LOCK]);
}
}
put_vm_lock(cur_vcpu->vm);
}
}
/* MTF is currently only used for split-lock emulation */
static int32_t mtf_vmexit_handler(struct acrn_vcpu *vcpu)
{
uint32_t value32;
value32 = exec_vmread32(VMX_PROC_VM_EXEC_CONTROLS);
value32 &= ~(VMX_PROCBASED_CTLS_MON_TRAP);
exec_vmwrite32(VMX_PROC_VM_EXEC_CONTROLS, value32);
vcpu_retain_rip(vcpu);
if (vcpu->arch.emulating_lock) {
vcpu->arch.emulating_lock = false;
vcpu_complete_split_lock_emulation(vcpu);
}
return 0;
}
static int32_t triple_fault_vmexit_handler(struct acrn_vcpu *vcpu)
{
pr_fatal("VM%d: triple fault @ guest RIP 0x%016lx, exit qualification: 0x%016lx",