svn commit: r261617 - head/sys/amd64/vmm/intel

Neel Natu neel at FreeBSD.org
Sat Feb 8 05:04:35 UTC 2014


Author: neel
Date: Sat Feb  8 05:04:34 2014
New Revision: 261617
URL: http://svnweb.freebsd.org/changeset/base/261617

Log:
  Fix a bug in the handling of VM-exits caused by non-maskable interrupts (NMI).
  
  If a VM-exit is caused by an NMI then "blocking by NMI" is in effect on the
  CPU when the VM-exit is completed. No more NMIs will be recognized until
  the execution of an "iret".
  
  Prior to this change the NMI handler was dispatched via a software interrupt
  with interrupts enabled. This meant that an interrupt could be recognized
  by the processor before the NMI handler completed its execution. The "iret"
  issued by the interrupt handler would then cause the "blocking by NMI" to
  be cleared prematurely.
  
  This is now fixed by handling the NMI with interrupts disabled in addition
  to "blocking by NMI" already established by the VM-exit.

Modified:
  head/sys/amd64/vmm/intel/vmx.c

Modified: head/sys/amd64/vmm/intel/vmx.c
==============================================================================
--- head/sys/amd64/vmm/intel/vmx.c	Sat Feb  8 04:29:36 2014	(r261616)
+++ head/sys/amd64/vmm/intel/vmx.c	Sat Feb  8 05:04:34 2014	(r261617)
@@ -1719,19 +1719,10 @@ vmx_exit_process(struct vmx *vmx, int vc
 			vmx_restore_nmi_blocking(vmx, vcpu);
 
 		/*
-		 * If the NMI-exiting VM execution control is set to '1'
-		 * then an NMI in non-root operation causes a VM-exit.
-		 * NMI blocking is in effect for this logical processor so
-		 * it is sufficient to simply vector to the NMI handler via
-		 * a software interrupt.
+		 * The NMI has already been handled in vmx_exit_handle_nmi().
 		 */
-		if ((intr_info & VMCS_INTR_T_MASK) == VMCS_INTR_T_NMI) {
-			KASSERT((intr_info & 0xff) == IDT_NMI, ("VM exit due "
-			    "to NMI has invalid vector: %#x", intr_info));
-			VCPU_CTR0(vmx->vm, vcpu, "Vectoring to NMI handler");
-			__asm __volatile("int $2");
+		if ((intr_info & VMCS_INTR_T_MASK) == VMCS_INTR_T_NMI)
 			return (1);
-		}
 		break;
 	case EXIT_REASON_EPT_FAULT:
 		vmm_stat_incr(vmx->vm, vcpu, VMEXIT_EPT_FAULT, 1);
@@ -1874,6 +1865,36 @@ vmx_exit_inst_error(struct vmxctx *vmxct
 	return (UNHANDLED);
 }
 
+/*
+ * If the NMI-exiting VM execution control is set to '1' then an NMI in
+ * non-root operation causes a VM-exit. NMI blocking is in effect so it is
+ * sufficient to simply vector to the NMI handler via a software interrupt.
+ * However, this must be done before maskable interrupts are enabled
+ * otherwise the "iret" issued by an interrupt handler will incorrectly
+ * clear NMI blocking.
+ */
+static __inline void
+vmx_exit_handle_nmi(struct vmx *vmx, int vcpuid, struct vm_exit *vmexit)
+{
+	uint32_t intr_info;
+
+	KASSERT((read_rflags() & PSL_I) == 0, ("interrupts enabled"));
+
+	if (vmexit->u.vmx.exit_reason != EXIT_REASON_EXCEPTION)
+		return;
+
+	intr_info = vmcs_read(VMCS_EXIT_INTR_INFO);
+	KASSERT((intr_info & VMCS_INTR_VALID) != 0,
+	    ("VM exit interruption info invalid: %#x", intr_info));
+
+	if ((intr_info & VMCS_INTR_T_MASK) == VMCS_INTR_T_NMI) {
+		KASSERT((intr_info & 0xff) == IDT_NMI, ("VM exit due "
+		    "to NMI has invalid vector: %#x", intr_info));
+		VCPU_CTR0(vmx->vm, vcpuid, "Vectoring to NMI handler");
+		__asm __volatile("int $2");
+	}
+}
+
 static int
 vmx_run(void *arg, int vcpu, register_t startrip, pmap_t pmap,
     void *rendezvous_cookie)
@@ -1949,8 +1970,6 @@ vmx_run(void *arg, int vcpu, register_t 
 		vmx_run_trace(vmx, vcpu);
 		rc = vmx_enter_guest(vmxctx, vmx, launched);
 
-		enable_intr();
-
 		/* Collect some information for VM exit processing */
 		vmexit->rip = rip = vmcs_guest_rip();
 		vmexit->inst_length = vmexit_instruction_length();
@@ -1958,12 +1977,14 @@ vmx_run(void *arg, int vcpu, register_t 
 		vmexit->u.vmx.exit_qualification = vmcs_exit_qualification();
 
 		if (rc == VMX_GUEST_VMEXIT) {
-			launched = 1;
+			vmx_exit_handle_nmi(vmx, vcpu, vmexit);
+			enable_intr();
 			handled = vmx_exit_process(vmx, vcpu, vmexit);
 		} else {
+			enable_intr();
 			handled = vmx_exit_inst_error(vmxctx, rc, vmexit);
 		}
-
+		launched = 1;
 		vmx_exit_trace(vmx, vcpu, rip, exit_reason, handled);
 	} while (handled);
 


More information about the svn-src-all mailing list