From f2919978119e5a7cdf30501661df5b693f276bb2 Mon Sep 17 00:00:00 2001 From: Jie Deng Date: Fri, 27 Nov 2020 13:09:54 +0800 Subject: [PATCH] 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 --- hypervisor/arch/x86/guest/virq.c | 44 +++++++++--------------------- hypervisor/arch/x86/guest/vmcs.c | 4 +-- hypervisor/arch/x86/guest/vmexit.c | 39 +++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 34 deletions(-) diff --git a/hypervisor/arch/x86/guest/virq.c b/hypervisor/arch/x86/guest/virq.c index 3481431d7..bc7a1ed63 100644 --- a/hypervisor/arch/x86/guest/virq.c +++ b/hypervisor/arch/x86/guest/virq.c @@ -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; diff --git a/hypervisor/arch/x86/guest/vmcs.c b/hypervisor/arch/x86/guest/vmcs.c index f09e8b3a9..65f64a538 100644 --- a/hypervisor/arch/x86/guest/vmcs.c +++ b/hypervisor/arch/x86/guest/vmcs.c @@ -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); diff --git a/hypervisor/arch/x86/guest/vmexit.c b/hypervisor/arch/x86/guest/vmexit.c index 8beffaae4..e5aa012de 100644 --- a/hypervisor/arch/x86/guest/vmexit.c +++ b/hypervisor/arch/x86/guest/vmexit.c @@ -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",