Author: neel
Date: Fri Jan 17 04:21:39 2014
New Revision: 260802
URL: http://svnweb.freebsd.org/changeset/base/260802

Log:
  If a VM-exit happens during an NMI injection then clear the "NMI Blocking" bit
  in the Guest Interruptibility-state VMCS field.
  
  If we fail to do this then a subsequent VM-entry will fail because it is an
  error to inject an NMI into the guest while "NMI Blocking" is turned on. This
  is described in "Checks on Guest Non-Register State" in the Intel SDM.
  
  Submitted by: David Reed ([email protected])

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

Modified: head/sys/amd64/vmm/intel/vmcs.h
==============================================================================
--- head/sys/amd64/vmm/intel/vmcs.h     Fri Jan 17 04:16:39 2014        
(r260801)
+++ head/sys/amd64/vmm/intel/vmcs.h     Fri Jan 17 04:21:39 2014        
(r260802)
@@ -333,10 +333,10 @@ vmcs_write(uint32_t encoding, uint64_t v
 /*
  * VMCS interrupt information fields
  */
-#define        VMCS_INTR_INFO_VALID            (1U << 31)
-#define        VMCS_INTR_INFO_TYPE(info)       (((info) >> 8) & 0x7)
-#define        VMCS_INTR_INFO_HW_INTR          (0 << 8)
-#define        VMCS_INTR_INFO_NMI              (2 << 8)
+#define        VMCS_INTR_VALID         (1U << 31)
+#define        VMCS_INTR_T_MASK        0x700           /* Interruption-info 
type */
+#define        VMCS_INTR_T_HWINTR      (0 << 8)
+#define        VMCS_INTR_T_NMI         (2 << 8)
 
 /*
  * VMCS IDT-Vectoring information fields

Modified: head/sys/amd64/vmm/intel/vmx.c
==============================================================================
--- head/sys/amd64/vmm/intel/vmx.c      Fri Jan 17 04:16:39 2014        
(r260801)
+++ head/sys/amd64/vmm/intel/vmx.c      Fri Jan 17 04:21:39 2014        
(r260802)
@@ -1065,7 +1065,7 @@ vmx_inject_nmi(struct vmx *vmx, int vcpu
         * Inject the virtual NMI. The vector must be the NMI IDT entry
         * or the VMCS entry check will fail.
         */
-       info = VMCS_INTR_INFO_NMI | VMCS_INTR_INFO_VALID;
+       info = VMCS_INTR_T_NMI | VMCS_INTR_VALID;
        info |= IDT_NMI;
        vmcs_write(VMCS_ENTRY_INTR_INFO, info);
 
@@ -1103,7 +1103,7 @@ vmx_inject_interrupts(struct vmx *vmx, i
         * because of a pending AST.
         */
        info = vmcs_read(VMCS_ENTRY_INTR_INFO);
-       if (info & VMCS_INTR_INFO_VALID)
+       if (info & VMCS_INTR_VALID)
                return;
 
        /*
@@ -1134,7 +1134,7 @@ vmx_inject_interrupts(struct vmx *vmx, i
                goto cantinject;
 
        /* Inject the interrupt */
-       info = VMCS_INTR_INFO_HW_INTR | VMCS_INTR_INFO_VALID;
+       info = VMCS_INTR_T_HWINTR | VMCS_INTR_VALID;
        info |= vector;
        vmcs_write(VMCS_ENTRY_INTR_INFO, info);
 
@@ -1444,10 +1444,12 @@ vmx_exit_process(struct vmx *vmx, int vc
        int error, handled;
        struct vmxctx *vmxctx;
        struct vlapic *vlapic;
-       uint32_t eax, ecx, edx, idtvec_info, idtvec_err, intr_info, reason;
+       uint32_t eax, ecx, edx, gi, idtvec_info, idtvec_err, intr_info, reason;
        uint64_t qual, gpa;
        bool retu;
 
+       CTASSERT((PINBASED_CTLS_ONE_SETTING & PINBASED_VIRTUAL_NMI) != 0);
+
        handled = 0;
        vmxctx = &vmx->ctx[vcpu];
 
@@ -1480,6 +1482,18 @@ vmx_exit_process(struct vmx *vmx, int vc
                                vmcs_write(VMCS_ENTRY_EXCEPTION_ERROR,
                                    idtvec_err);
                        }
+                       /*
+                        * If 'virtual NMIs' are being used and the VM-exit
+                        * happened while injecting an NMI during the previous
+                        * VM-entry, then clear "blocking by NMI" in the Guest
+                        * Interruptibility-state.
+                        */
+                       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);
+                       }
                        vmcs_write(VMCS_ENTRY_INST_LENGTH, vmexit->inst_length);
                }
        default:
@@ -1556,8 +1570,8 @@ vmx_exit_process(struct vmx *vmx, int vc
                 * this virtual interrupt during the subsequent VM enter.
                 */
                intr_info = vmcs_read(VMCS_EXIT_INTR_INFO);
-               KASSERT((intr_info & VMCS_INTR_INFO_VALID) != 0 &&
-                   VMCS_INTR_INFO_TYPE(intr_info) == 0,
+               KASSERT((intr_info & VMCS_INTR_VALID) != 0 &&
+                   (intr_info & VMCS_INTR_T_MASK) == VMCS_INTR_T_HWINTR,
                    ("VM exit interruption info invalid: %#x", intr_info));
                vmx_trigger_hostintr(intr_info & 0xff);
 
@@ -2039,11 +2053,11 @@ vmx_inject(void *arg, int vcpu, int type
        if (error)
                return (error);
 
-       if (info & VMCS_INTR_INFO_VALID)
+       if (info & VMCS_INTR_VALID)
                return (EAGAIN);
 
        info = vector | (type_map[type] << 8) | (code_valid ? 1 << 11 : 0);
-       info |= VMCS_INTR_INFO_VALID;
+       info |= VMCS_INTR_VALID;
        error = vmcs_setreg(vmcs, 0, VMCS_IDENT(VMCS_ENTRY_INTR_INFO), info);
        if (error != 0)
                return (error);
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to