Index: /trunk/src/VBox/VMM/VMMAll/IEMAllCImplVmxInstr.cpp.h
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/IEMAllCImplVmxInstr.cpp.h	(revision 74783)
+++ /trunk/src/VBox/VMM/VMMAll/IEMAllCImplVmxInstr.cpp.h	(revision 74784)
@@ -1336,5 +1336,5 @@
  *
  * @returns The VMX event type.
- * @param   uVector     The interrupt / exception vector number.
+ * @param   uVector     The interrupt / exception vector.
  * @param   fFlags      The IEM event flag (see IEM_XCPT_FLAGS_XXX).
  */
@@ -3668,9 +3668,76 @@
 
 /**
- * VMX VM-exit handler for VM-exits due to delivery of an event (indirectly).
+ * VMX VM-exit handler for VM-exits due to external interrupts.
+ *
+ * @returns VBox strict status code.
+ * @param   pVCpu           The cross context virtual CPU structure.
+ * @param   uVector         The external interrupt vector.
+ * @param   fIntPending     Whether the external interrupt is pending or
+ *                          acknowdledged in the interrupt controller.
+ */
+IEM_STATIC VBOXSTRICTRC iemVmxVmexitExtInt(PVMCPU pVCpu, uint8_t uVector, bool fIntPending)
+{
+    PCVMXVVMCS pVmcs = pVCpu->cpum.GstCtx.hwvirt.vmx.CTX_SUFF(pVmcs);
+    Assert(pVmcs);
+
+    /* The VM-exit is subject to "External interrupt exiting" is being set. */
+    if (pVmcs->u32PinCtls & VMX_PIN_CTLS_EXT_INT_EXIT)
+    {
+        if (fIntPending)
+        {
+            /*
+             * If the interrupt is pending and we don't need to acknowledge the
+             * interrupt on VM-exit, cause the VM-exit immediately.
+             *
+             * See Intel spec 25.2 "Other Causes Of VM Exits".
+             */
+            if (!(pVmcs->u32ExitCtls & VMX_EXIT_CTLS_ACK_EXT_INT))
+            {
+                iemVmxVmcsSetExitIntInfo(pVCpu, 0);
+                iemVmxVmcsSetExitIntErrCode(pVCpu, 0);
+                iemVmxVmcsSetExitQual(pVCpu, 0);
+                return iemVmxVmexit(pVCpu, VMX_EXIT_EXT_INT);
+            }
+
+            /*
+             * If the interrupt is pending and we -do- need to acknowledge the interrupt
+             * on VM-exit, postpone VM-exit til after the interrupt controller has been
+             * acknowledged that the interrupt has been consumed.
+             */
+            return VINF_VMX_INTERCEPT_NOT_ACTIVE;
+        }
+
+        /*
+         * If the interrupt is no longer pending (i.e. it has been acknowledged) and the
+         * "External interrupt exiting" and "Acknowledge interrupt on VM-exit" controls are
+         * all set, we cause the VM-exit now. We need to record the external interrupt that
+         * just occurred in the VM-exit interruption information field.
+         *
+         * See Intel spec. 27.2.2 "Information for VM Exits Due to Vectored Events".
+         */
+        if (pVmcs->u32ExitCtls & VMX_EXIT_CTLS_ACK_EXT_INT)
+        {
+            uint8_t  const fNmiUnblocking = 0;  /** @todo NSTVMX: Implement NMI-unblocking due to IRET. */
+            uint32_t const uExitIntInfo   = RT_BF_MAKE(VMX_BF_EXIT_INT_INFO_VECTOR,           uVector)
+                                          | RT_BF_MAKE(VMX_BF_EXIT_INT_INFO_TYPE,             VMX_EXIT_INT_INFO_TYPE_EXT_INT)
+                                          | RT_BF_MAKE(VMX_BF_EXIT_INT_INFO_NMI_UNBLOCK_IRET, fNmiUnblocking)
+                                          | RT_BF_MAKE(VMX_BF_EXIT_INT_INFO_VALID,            1);
+            iemVmxVmcsSetExitIntInfo(pVCpu, uExitIntInfo);
+            iemVmxVmcsSetExitIntErrCode(pVCpu, 0);
+            iemVmxVmcsSetExitQual(pVCpu, 0);
+            return iemVmxVmexit(pVCpu, VMX_EXIT_EXT_INT);
+        }
+    }
+
+    return VINF_VMX_INTERCEPT_NOT_ACTIVE;
+}
+
+
+/**
+ * VMX VM-exit handler for VM-exits due to delivery of an event.
  *
  * @returns VBox strict status code.
  * @param   pVCpu       The cross context virtual CPU structure.
- * @param   uVector     The interrupt / exception vector number.
+ * @param   uVector     The interrupt / exception vector.
  * @param   fFlags      The flags (see IEM_XCPT_FLAGS_XXX).
  * @param   uErrCode    The error code associated with the event.
@@ -3708,51 +3775,49 @@
 
     /*
+     * We are injecting an external interrupt, check if we need to cause a VM-exit now.
+     * If not, the caller will continue delivery of the external interrupt as it would
+     * normally.
+     */
+    if (fFlags & IEM_XCPT_FLAGS_T_EXT_INT)
+    {
+        Assert(!VMX_IDT_VECTORING_INFO_IS_VALID(pVmcs->u32RoIdtVectoringInfo));
+        return iemVmxVmexitExtInt(pVCpu, uVector, false /* fIntPending */);
+    }
+
+    /*
      * Evaluate intercepts for hardware exceptions including #BP, #DB, #OF
      * generated by INT3, INT1 (ICEBP) and INTO respectively.
      */
-    bool     fIntercept = false;
-    bool     fIsHwXcpt  = false;
-    if (fFlags & (IEM_XCPT_FLAGS_T_CPU_XCPT | IEM_XCPT_FLAGS_T_SOFT_INT))
-    {
-        if (   !(fFlags & IEM_XCPT_FLAGS_T_SOFT_INT)
-            ||  (fFlags & (IEM_XCPT_FLAGS_BP_INSTR | IEM_XCPT_FLAGS_OF_INSTR | IEM_XCPT_FLAGS_ICEBP_INSTR)))
-        {
-            fIsHwXcpt = true;
-            do
+    Assert(fFlags & (IEM_XCPT_FLAGS_T_CPU_XCPT | IEM_XCPT_FLAGS_T_SOFT_INT));
+    bool fIntercept = false;
+    bool fIsHwXcpt  = false;
+    if (   !(fFlags & IEM_XCPT_FLAGS_T_SOFT_INT)
+        ||  (fFlags & (IEM_XCPT_FLAGS_BP_INSTR | IEM_XCPT_FLAGS_OF_INSTR | IEM_XCPT_FLAGS_ICEBP_INSTR)))
+    {
+        fIsHwXcpt = true;
+        /* NMIs have a dedicated VM-execution control for causing VM-exits. */
+        if (uVector == X86_XCPT_NMI)
+            fIntercept = RT_BOOL(pVmcs->u32PinCtls & VMX_PIN_CTLS_NMI_EXIT);
+        else
+        {
+            /* Page-faults are subject to masking using its error code. */
+            uint32_t fXcptBitmap = pVmcs->u32XcptBitmap;
+            if (uVector == X86_XCPT_PF)
             {
-                /* NMIs have a dedicated VM-execution control for causing VM-exits. */
-                if (uVector == X86_XCPT_NMI)
-                {
-                    fIntercept = RT_BOOL(pVmcs->u32PinCtls & VMX_PIN_CTLS_NMI_EXIT);
-                    break;
-                }
-
-                /* Page-faults are subject to masking using its error code. */
-                uint32_t fXcptBitmap = pVmcs->u32XcptBitmap;
-                if (uVector == X86_XCPT_PF)
-                {
-                    uint32_t const fXcptPFMask  = pVmcs->u32XcptPFMask;
-                    uint32_t const fXcptPFMatch = pVmcs->u32XcptPFMatch;
-                    if ((uErrCode & fXcptPFMask) != fXcptPFMatch)
-                        fXcptBitmap ^= RT_BIT(X86_XCPT_PF);
-                }
-
-                /* Consult the exception bitmap. */
-                if (fXcptBitmap & RT_BIT(uVector))
-                    fIntercept = true;
-            } while (0);
-        }
-        /* else: Software interrupts cannot be intercepted and therefore do not cause a VM-exit. */
-    }
-    else if (fFlags & IEM_XCPT_FLAGS_T_EXT_INT)
-    {
-        /* External interrupts have a dedicated VM-execution control for causing VM-exits. */
-        fIntercept  = RT_BOOL(pVmcs->u32PinCtls & VMX_PIN_CTLS_EXT_INT_EXIT);
-
-        /** @todo NSTVMX: What about "acknowledge interrupt on exit" control? */
-    }
-
-    /*
-     * Now that we've determined whether the external interrupt or hardware exception
+                uint32_t const fXcptPFMask  = pVmcs->u32XcptPFMask;
+                uint32_t const fXcptPFMatch = pVmcs->u32XcptPFMatch;
+                if ((uErrCode & fXcptPFMask) != fXcptPFMatch)
+                    fXcptBitmap ^= RT_BIT(X86_XCPT_PF);
+            }
+
+            /* Consult the exception bitmap for all hardware exceptions (except NMI). */
+            if (fXcptBitmap & RT_BIT(uVector))
+                fIntercept = true;
+        }
+    }
+    /* else: Software interrupts cannot be intercepted and therefore do not cause a VM-exit. */
+
+    /*
+     * Now that we've determined whether the software interrupt or hardware exception
      * causes a VM-exit, we need to construct the relevant VM-exit information and
      * cause the VM-exit.
@@ -3760,4 +3825,7 @@
     if (fIntercept)
     {
+        Assert(!(fFlags & IEM_XCPT_FLAGS_T_EXT_INT));
+
+        /* Construct the rest of the event related information fields and cause the VM-exit. */
         uint64_t uExitQual = 0;
         if (fIsHwXcpt)
@@ -3772,6 +3840,4 @@
         }
 
-        /* Construct the rest of the event related information fields and cause the VM-exit. */
-        uint32_t const uExitReason    = (fFlags & IEM_XCPT_FLAGS_T_EXT_INT) ? VMX_EXIT_EXT_INT : VMX_EXIT_XCPT_OR_NMI;
         uint8_t  const fNmiUnblocking = 0;  /** @todo NSTVMX: Implement NMI-unblocking due to IRET. */
         uint8_t  const fErrCodeValid  = (fFlags & IEM_XCPT_FLAGS_ERR);
@@ -3797,5 +3863,5 @@
             iemVmxVmcsSetExitInstrLen(pVCpu, 0);
 
-        return iemVmxVmexit(pVCpu, uExitReason);
+        return iemVmxVmexit(pVCpu, VMX_EXIT_XCPT_OR_NMI);
     }
 
