diff options
author | neel <neel@FreeBSD.org> | 2014-01-18 02:20:10 +0000 |
---|---|---|
committer | neel <neel@FreeBSD.org> | 2014-01-18 02:20:10 +0000 |
commit | 4dec904890da28dee619bd8b7a33ef04c0ece1d7 (patch) | |
tree | ef9c85702d9b647084c25baaedcfc4fc26d73212 /sys/amd64 | |
parent | 4b064bf9acc3d91a2019c8df378d816ff4f147d6 (diff) | |
download | FreeBSD-src-4dec904890da28dee619bd8b7a33ef04c0ece1d7.zip FreeBSD-src-4dec904890da28dee619bd8b7a33ef04c0ece1d7.tar.gz |
If the guest exits due to a fault while it is executing IRET then restore
the state of "Virtual NMI blocking" in the guest's interruptibility-state
field before resuming the guest.
Diffstat (limited to 'sys/amd64')
-rw-r--r-- | sys/amd64/vmm/intel/vmcs.h | 6 | ||||
-rw-r--r-- | sys/amd64/vmm/intel/vmx.c | 66 |
2 files changed, 68 insertions, 4 deletions
diff --git a/sys/amd64/vmm/intel/vmcs.h b/sys/amd64/vmm/intel/vmcs.h index cd2f84b..8c8231c 100644 --- a/sys/amd64/vmm/intel/vmcs.h +++ b/sys/amd64/vmm/intel/vmcs.h @@ -331,6 +331,12 @@ vmcs_write(uint32_t encoding, uint64_t val) #define EXIT_REASON_APIC_WRITE 56 /* + * NMI unblocking due to IRET. + * + * Applies to VM-exits due to hardware exception or EPT fault. + */ +#define EXIT_QUAL_NMIUDTI (1 << 12) +/* * VMCS interrupt information fields */ #define VMCS_INTR_VALID (1U << 31) diff --git a/sys/amd64/vmm/intel/vmx.c b/sys/amd64/vmm/intel/vmx.c index 0c37175..3b4c7a2 100644 --- a/sys/amd64/vmm/intel/vmx.c +++ b/sys/amd64/vmm/intel/vmx.c @@ -1155,6 +1155,37 @@ cantinject: VCPU_CTR0(vmx->vm, vcpu, "Enabling interrupt window exiting"); } +/* + * If the Virtual NMIs execution control is '1' then the logical processor + * tracks virtual-NMI blocking in the Guest Interruptibility-state field of + * the VMCS. An IRET instruction in VMX non-root operation will remove any + * virtual-NMI blocking. + * + * This unblocking occurs even if the IRET causes a fault. In this case the + * hypervisor needs to restore virtual-NMI blocking before resuming the guest. + */ +static void +vmx_restore_nmi_blocking(struct vmx *vmx, int vcpuid) +{ + uint32_t gi; + + VCPU_CTR0(vmx->vm, vcpuid, "Restore Virtual-NMI blocking"); + gi = vmcs_read(VMCS_GUEST_INTERRUPTIBILITY); + gi |= VMCS_INTERRUPTIBILITY_NMI_BLOCKING; + vmcs_write(VMCS_GUEST_INTERRUPTIBILITY, gi); +} + +static void +vmx_clear_nmi_blocking(struct vmx *vmx, int vcpuid) +{ + uint32_t gi; + + VCPU_CTR0(vmx->vm, vcpuid, "Clear Virtual-NMI blocking"); + gi = vmcs_read(VMCS_GUEST_INTERRUPTIBILITY); + gi &= ~VMCS_INTERRUPTIBILITY_NMI_BLOCKING; + vmcs_write(VMCS_GUEST_INTERRUPTIBILITY, gi); +} + static int vmx_emulate_cr_access(struct vmx *vmx, int vcpu, uint64_t exitqual) { @@ -1444,7 +1475,7 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit) int error, handled; struct vmxctx *vmxctx; struct vlapic *vlapic; - uint32_t eax, ecx, edx, gi, idtvec_info, idtvec_err, intr_info, reason; + uint32_t eax, ecx, edx, idtvec_info, idtvec_err, intr_info, reason; uint64_t qual, gpa; bool retu; @@ -1490,13 +1521,12 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit) */ if ((idtvec_info & VMCS_INTR_T_MASK) == VMCS_INTR_T_NMI) { - gi = vmcs_read(VMCS_GUEST_INTERRUPTIBILITY); - gi &= ~VMCS_INTERRUPTIBILITY_NMI_BLOCKING; - vmcs_write(VMCS_GUEST_INTERRUPTIBILITY, gi); + vmx_clear_nmi_blocking(vmx, vcpu); } vmcs_write(VMCS_ENTRY_INST_LENGTH, vmexit->inst_length); } default: + idtvec_info = 0; break; } @@ -1601,6 +1631,23 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit) vmm_stat_incr(vmx->vm, vcpu, VMEXIT_CPUID, 1); handled = vmx_handle_cpuid(vmx->vm, vcpu, vmxctx); break; + case EXIT_REASON_EXCEPTION: + intr_info = vmcs_read(VMCS_EXIT_INTR_INFO); + KASSERT((intr_info & VMCS_INTR_VALID) != 0, + ("VM exit interruption info invalid: %#x", intr_info)); + /* + * If Virtual NMIs control is 1 and the VM-exit is due to a + * fault encountered during the execution of IRET then we must + * restore the state of "virtual-NMI blocking" before resuming + * the guest. + * + * See "Resuming Guest Software after Handling an Exception". + */ + if ((idtvec_info & VMCS_IDT_VEC_VALID) == 0 && + (intr_info & 0xff) != IDT_DF && + (intr_info & EXIT_QUAL_NMIUDTI) != 0) + vmx_restore_nmi_blocking(vmx, vcpu); + break; case EXIT_REASON_EPT_FAULT: vmm_stat_incr(vmx->vm, vcpu, VMEXIT_EPT_FAULT, 1); /* @@ -1619,6 +1666,17 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit) vmexit->u.inst_emul.gla = vmcs_gla(); vmexit->u.inst_emul.cr3 = vmcs_guest_cr3(); } + /* + * If Virtual NMIs control is 1 and the VM-exit is due to an + * EPT fault during the execution of IRET then we must restore + * the state of "virtual-NMI blocking" before resuming. + * + * See description of "NMI unblocking due to IRET" in + * "Exit Qualification for EPT Violations". + */ + if ((idtvec_info & VMCS_IDT_VEC_VALID) == 0 && + (qual & EXIT_QUAL_NMIUDTI) != 0) + vmx_restore_nmi_blocking(vmx, vcpu); break; case EXIT_REASON_APIC_ACCESS: handled = vmx_handle_apic_access(vmx, vcpu, vmexit); |