aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorNeel Natu <neel@FreeBSD.org>2014-01-18 02:20:10 +0000
committerNeel Natu <neel@FreeBSD.org>2014-01-18 02:20:10 +0000
commite5a1d950899c0579986abc298d0e3919eb3db37b (patch)
treeef9c85702d9b647084c25baaedcfc4fc26d73212 /sys
parentbea6313e6b0cdf0cc4a4cd10f2cca8b3e6733a80 (diff)
downloadsrc-e5a1d950899c0579986abc298d0e3919eb3db37b.tar.gz
src-e5a1d950899c0579986abc298d0e3919eb3db37b.zip
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.
Notes
Notes: svn path=/head/; revision=260836
Diffstat (limited to 'sys')
-rw-r--r--sys/amd64/vmm/intel/vmcs.h6
-rw-r--r--sys/amd64/vmm/intel/vmx.c66
2 files changed, 68 insertions, 4 deletions
diff --git a/sys/amd64/vmm/intel/vmcs.h b/sys/amd64/vmm/intel/vmcs.h
index cd2f84b8978b..8c8231cd24d3 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 0c3717596489..3b4c7a23daeb 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);