Index: /trunk/include/VBox/vmm/hm_vmx.h
===================================================================
--- /trunk/include/VBox/vmm/hm_vmx.h	(revision 52040)
+++ /trunk/include/VBox/vmm/hm_vmx.h	(revision 52041)
@@ -1351,5 +1351,5 @@
 /** Use TPR shadow. */
 #define VMX_VMCS_CTRL_PROC_EXEC_USE_TPR_SHADOW                  RT_BIT(21)
-/** VM-exit when virtual nmi blocking is disabled. */
+/** VM-exit when virtual NMI blocking is disabled. */
 #define VMX_VMCS_CTRL_PROC_EXEC_NMI_WINDOW_EXIT                 RT_BIT(22)
 /** VM-exit when executing a MOV DRx instruction. */
@@ -1489,5 +1489,5 @@
 #define VMX_EXIT_INTERRUPTION_INFO_ERROR_CODE_VALID               RT_BIT(11)
 #define VMX_EXIT_INTERRUPTION_INFO_ERROR_CODE_IS_VALID(a)         RT_BOOL((a) & VMX_EXIT_INTERRUPTION_INFO_ERROR_CODE_VALID)
-#define VMX_EXIT_INTERRUPTION_INFO_NMI_UNBLOCK(a)                 ((a) & RT_BIT(12))
+#define VMX_EXIT_INTERRUPTION_INFO_NMI_UNBLOCK_IRET(a)            ((a) & RT_BIT(12))
 #define VMX_EXIT_INTERRUPTION_INFO_VALID                          RT_BIT(31)
 #define VMX_EXIT_INTERRUPTION_INFO_IS_VALID(a)                    RT_BOOL((a) & RT_BIT(31))
Index: /trunk/include/VBox/vmm/vm.h
===================================================================
--- /trunk/include/VBox/vmm/vm.h	(revision 52040)
+++ /trunk/include/VBox/vmm/vm.h	(revision 52041)
@@ -405,4 +405,6 @@
 /** Inhibit interrupts pending. See EMGetInhibitInterruptsPC(). */
 #define VMCPU_FF_INHIBIT_INTERRUPTS         RT_BIT_32(24)
+/** Inhibit non-maskable interrupts. */
+#define VMCPU_FF_INHIBIT_NMIS               RT_BIT_32(25)
 #ifdef VBOX_WITH_RAW_MODE
 /** CSAM needs to scan the page that's being executed */
Index: /trunk/src/VBox/VMM/VMMAll/IEMAllCImpl.cpp.h
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/IEMAllCImpl.cpp.h	(revision 52040)
+++ /trunk/src/VBox/VMM/VMMAll/IEMAllCImpl.cpp.h	(revision 52041)
@@ -2989,4 +2989,10 @@
 IEM_CIMPL_DEF_1(iemCImpl_iret, IEMMODE, enmEffOpSize)
 {
+    /*
+     * First, clear NMI inhibition before causing any exceptions.
+     */
+    PVMCPU pVCpu = IEMCPU_TO_VMCPU(pIemCpu);
+    VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_NMIS);
+
     /*
      * Call a mode specific worker.
Index: /trunk/src/VBox/VMM/VMMR0/HMSVMR0.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/HMSVMR0.cpp	(revision 52040)
+++ /trunk/src/VBox/VMM/VMMR0/HMSVMR0.cpp	(revision 52041)
@@ -229,7 +229,7 @@
 typedef enum SVMMSREXITREAD
 {
-    /** Reading this MSR causes a VM-exit. */
+    /** Reading this MSR causes a #VMEXIT. */
     SVMMSREXIT_INTERCEPT_READ = 0xb,
-    /** Reading this MSR does not cause a VM-exit. */
+    /** Reading this MSR does not cause a #VMEXIT. */
     SVMMSREXIT_PASSTHRU_READ
 } SVMMSREXITREAD;
@@ -240,12 +240,12 @@
 typedef enum SVMMSREXITWRITE
 {
-    /** Writing to this MSR causes a VM-exit. */
+    /** Writing to this MSR causes a #VMEXIT. */
     SVMMSREXIT_INTERCEPT_WRITE = 0xd,
-    /** Writing to this MSR does not cause a VM-exit. */
+    /** Writing to this MSR does not cause a #VMEXIT. */
     SVMMSREXIT_PASSTHRU_WRITE
 } SVMMSREXITWRITE;
 
 /**
- * SVM VM-exit handler.
+ * SVM #VMEXIT handler.
  *
  * @returns VBox status code.
@@ -263,5 +263,5 @@
 static void hmR0SvmLeave(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx);
 
-/** @name VM-exit handlers.
+/** @name #VMEXIT handlers.
  * @{
  */
@@ -289,4 +289,5 @@
 static FNSVMEXITHANDLER hmR0SvmExitTaskSwitch;
 static FNSVMEXITHANDLER hmR0SvmExitVmmCall;
+static FNSVMEXITHANDLER hmR0SvmExitIret;
 static FNSVMEXITHANDLER hmR0SvmExitXcptPF;
 static FNSVMEXITHANDLER hmR0SvmExitXcptNM;
@@ -692,27 +693,27 @@
 
         /* Set up unconditional intercepts and conditions. */
-        pVmcb->ctrl.u32InterceptCtrl1 =   SVM_CTRL1_INTERCEPT_INTR          /* External interrupt causes a VM-exit. */
-                                        | SVM_CTRL1_INTERCEPT_NMI           /* Non-Maskable Interrupts causes a VM-exit. */
-                                        | SVM_CTRL1_INTERCEPT_INIT          /* INIT signal causes a VM-exit. */
-                                        | SVM_CTRL1_INTERCEPT_RDPMC         /* RDPMC causes a VM-exit. */
-                                        | SVM_CTRL1_INTERCEPT_CPUID         /* CPUID causes a VM-exit. */
-                                        | SVM_CTRL1_INTERCEPT_RSM           /* RSM causes a VM-exit. */
-                                        | SVM_CTRL1_INTERCEPT_HLT           /* HLT causes a VM-exit. */
-                                        | SVM_CTRL1_INTERCEPT_INOUT_BITMAP  /* Use the IOPM to cause IOIO VM-exits. */
-                                        | SVM_CTRL1_INTERCEPT_MSR_SHADOW    /* MSR access not covered by MSRPM causes a VM-exit.*/
-                                        | SVM_CTRL1_INTERCEPT_INVLPGA       /* INVLPGA causes a VM-exit. */
-                                        | SVM_CTRL1_INTERCEPT_SHUTDOWN      /* Shutdown events causes a VM-exit. */
+        pVmcb->ctrl.u32InterceptCtrl1 =   SVM_CTRL1_INTERCEPT_INTR          /* External interrupt causes a #VMEXIT. */
+                                        | SVM_CTRL1_INTERCEPT_NMI           /* Non-maskable interrupts causes a #VMEXIT. */
+                                        | SVM_CTRL1_INTERCEPT_INIT          /* INIT signal causes a #VMEXIT. */
+                                        | SVM_CTRL1_INTERCEPT_RDPMC         /* RDPMC causes a #VMEXIT. */
+                                        | SVM_CTRL1_INTERCEPT_CPUID         /* CPUID causes a #VMEXIT. */
+                                        | SVM_CTRL1_INTERCEPT_RSM           /* RSM causes a #VMEXIT. */
+                                        | SVM_CTRL1_INTERCEPT_HLT           /* HLT causes a #VMEXIT. */
+                                        | SVM_CTRL1_INTERCEPT_INOUT_BITMAP  /* Use the IOPM to cause IOIO #VMEXITs. */
+                                        | SVM_CTRL1_INTERCEPT_MSR_SHADOW    /* MSR access not covered by MSRPM causes a #VMEXIT.*/
+                                        | SVM_CTRL1_INTERCEPT_INVLPGA       /* INVLPGA causes a #VMEXIT. */
+                                        | SVM_CTRL1_INTERCEPT_SHUTDOWN      /* Shutdown events causes a #VMEXIT. */
                                         | SVM_CTRL1_INTERCEPT_FERR_FREEZE;  /* Intercept "freezing" during legacy FPU handling. */
 
-        pVmcb->ctrl.u32InterceptCtrl2 =   SVM_CTRL2_INTERCEPT_VMRUN         /* VMRUN causes a VM-exit. */
-                                        | SVM_CTRL2_INTERCEPT_VMMCALL       /* VMMCALL causes a VM-exit. */
-                                        | SVM_CTRL2_INTERCEPT_VMLOAD        /* VMLOAD causes a VM-exit. */
-                                        | SVM_CTRL2_INTERCEPT_VMSAVE        /* VMSAVE causes a VM-exit. */
-                                        | SVM_CTRL2_INTERCEPT_STGI          /* STGI causes a VM-exit. */
-                                        | SVM_CTRL2_INTERCEPT_CLGI          /* CLGI causes a VM-exit. */
-                                        | SVM_CTRL2_INTERCEPT_SKINIT        /* SKINIT causes a VM-exit. */
-                                        | SVM_CTRL2_INTERCEPT_WBINVD        /* WBINVD causes a VM-exit. */
-                                        | SVM_CTRL2_INTERCEPT_MONITOR       /* MONITOR causes a VM-exit. */
-                                        | SVM_CTRL2_INTERCEPT_MWAIT;        /* MWAIT causes a VM-exit. */
+        pVmcb->ctrl.u32InterceptCtrl2 =   SVM_CTRL2_INTERCEPT_VMRUN         /* VMRUN causes a #VMEXIT. */
+                                        | SVM_CTRL2_INTERCEPT_VMMCALL       /* VMMCALL causes a #VMEXIT. */
+                                        | SVM_CTRL2_INTERCEPT_VMLOAD        /* VMLOAD causes a #VMEXIT. */
+                                        | SVM_CTRL2_INTERCEPT_VMSAVE        /* VMSAVE causes a #VMEXIT. */
+                                        | SVM_CTRL2_INTERCEPT_STGI          /* STGI causes a #VMEXIT. */
+                                        | SVM_CTRL2_INTERCEPT_CLGI          /* CLGI causes a #VMEXIT. */
+                                        | SVM_CTRL2_INTERCEPT_SKINIT        /* SKINIT causes a #VMEXIT. */
+                                        | SVM_CTRL2_INTERCEPT_WBINVD        /* WBINVD causes a #VMEXIT. */
+                                        | SVM_CTRL2_INTERCEPT_MONITOR       /* MONITOR causes a #VMEXIT. */
+                                        | SVM_CTRL2_INTERCEPT_MWAIT;        /* MWAIT causes a #VMEXIT. */
 
         /* CR0, CR4 reads must be intercepted, our shadow values are not necessarily the same as the guest's. */
@@ -1133,5 +1134,5 @@
         {
             u64GuestCR0 |= X86_CR0_PG;     /* When Nested Paging is not available, use shadow page tables. */
-            u64GuestCR0 |= X86_CR0_WP;     /* Guest CPL 0 writes to its read-only pages should cause a #PF VM-exit. */
+            u64GuestCR0 |= X86_CR0_WP;     /* Guest CPL 0 writes to its read-only pages should cause a #PF #VMEXIT. */
         }
 
@@ -1153,5 +1154,5 @@
         else
         {
-            fInterceptNM = true;           /* Guest FPU inactive, VM-exit on #NM for lazy FPU loading. */
+            fInterceptNM = true;           /* Guest FPU inactive, #VMEXIT on #NM for lazy FPU loading. */
             u64GuestCR0 |=  X86_CR0_TS     /* Guest can task switch quickly and do lazy FPU syncing. */
                           | X86_CR0_MP;    /* FWAIT/WAIT should not ignore CR0.TS and should generate #NM. */
@@ -1886,5 +1887,5 @@
     if (pVmcb->ctrl.u64IntShadow & SVM_INTERRUPT_SHADOW_ACTIVE)
         EMSetInhibitInterruptsPC(pVCpu, pMixedCtx->rip);
-    else
+    else if (VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS))
         VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS);
 
@@ -2525,4 +2526,40 @@
 
 /**
+ * Sets the IRET intercept control in the VMCB which instructs AMD-V to cause a
+ * #VMEXIT as soon as a guest starts executing an IRET. This is used to unblock
+ * virtual NMIs.
+ *
+ * @param pVmcb         Pointer to the VM control block.
+ */
+DECLINLINE(void) hmR0SvmSetIretIntercept(PSVMVMCB pVmcb)
+{
+    if (!(pVmcb->ctrl.u32InterceptCtrl1 & SVM_CTRL1_INTERCEPT_IRET))
+    {
+        pVmcb->ctrl.u32InterceptCtrl1 |= SVM_CTRL1_INTERCEPT_IRET;
+        pVmcb->ctrl.u64VmcbCleanBits &= ~(HMSVM_VMCB_CLEAN_INTERCEPTS);
+
+        Log4(("Setting IRET intercept\n"));
+    }
+}
+
+
+/**
+ * Clears the IRET intercept control in the VMCB.
+ *
+ * @param pVmcb         Pointer to the VM control block.
+ */
+DECLINLINE(void) hmR0SvmClearIretIntercept(PSVMVMCB pVmcb)
+{
+    if (pVmcb->ctrl.u32InterceptCtrl1 & SVM_CTRL1_INTERCEPT_IRET)
+    {
+        pVmcb->ctrl.u32InterceptCtrl1 &= ~SVM_CTRL1_INTERCEPT_IRET;
+        pVmcb->ctrl.u64VmcbCleanBits &= ~(HMSVM_VMCB_CLEAN_INTERCEPTS);
+
+        Log4(("Clearing IRET intercept\n"));
+    }
+}
+
+
+/**
  * Evaluates the event to be delivered to the guest and sets it as the pending
  * event.
@@ -2538,4 +2575,5 @@
     const bool fIntShadow = RT_BOOL(hmR0SvmGetGuestIntrShadow(pVCpu, pCtx));
     const bool fBlockInt  = !(pCtx->eflags.u32 & X86_EFL_IF);
+    const bool fBlockNmi  = RT_BOOL(VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_INHIBIT_NMIS));
     PSVMVMCB pVmcb        = (PSVMVMCB)pVCpu->hm.s.svm.pvVmcb;
 
@@ -2545,5 +2583,9 @@
     if (VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_INTERRUPT_NMI))   /* NMI. NMIs take priority over regular interrupts . */
     {
-        if (!fIntShadow)
+        if (fBlockNmi)
+            hmR0SvmSetIretIntercept(pVmcb);
+        else if (fIntShadow)
+            hmR0SvmSetVirtIntrIntercept(pVmcb);
+        else
         {
             Log4(("Pending NMI\n"));
@@ -2556,6 +2598,4 @@
             VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_NMI);
         }
-        else
-            hmR0SvmSetVirtIntrIntercept(pVmcb);
     }
     else if (VMCPU_FF_IS_PENDING(pVCpu, (VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC)))
@@ -2962,4 +3002,24 @@
     }
 
+    /*
+     * If we are injecting an NMI, we must set VMCPU_FF_INHIBIT_NMIS only when we are going to execute
+     * guest code for certain (no exits to ring-3). Otherwise, we could re-read the flag on re-entry into
+     * AMD-V and conclude that NMI inhibition is active when we have not even delivered the NMI.
+     *
+     * With VT-x, this is handled by the Guest interruptibility information VMCS field which will set the
+     * VMCS field after actually delivering the NMI which we read on VM-exit to determine the state.
+     */
+    if (pVCpu->hm.s.Event.fPending)
+    {
+        SVMEVENT Event;
+        Event.u = pVCpu->hm.s.Event.u64IntInfo;
+        if (   Event.n.u1Valid
+            && Event.n.u3Type == SVM_EVENT_NMI
+            && Event.n.u8Vector == X86_XCPT_NMI)
+        {
+            VMCPU_FF_SET(pVCpu, VMCPU_FF_INHIBIT_NMIS);
+        }
+    }
+
     return VINF_SUCCESS;
 }
@@ -3390,5 +3450,5 @@
 
     /*
-     * The ordering of the case labels is based on most-frequently-occurring VM-exits for most guests under
+     * The ordering of the case labels is based on most-frequently-occurring #VMEXITs for most guests under
      * normal workloads (for some definition of "normal").
      */
@@ -3487,4 +3547,7 @@
                 case SVM_EXIT_VMMCALL:
                     return hmR0SvmExitVmmCall(pVCpu, pCtx, pSvmTransient);
+
+                case SVM_EXIT_IRET:
+                    return hmR0SvmExitIret(pVCpu, pCtx, pSvmTransient);
 
                 case SVM_EXIT_SHUTDOWN:
@@ -3930,5 +3993,5 @@
  *
  * @returns VBox status code (informational error codes included).
- * @retval VINF_SUCCESS if we should continue handling the VM-exit.
+ * @retval VINF_SUCCESS if we should continue handling the #VMEXIT.
  * @retval VINF_HM_DOUBLE_FAULT if a #DF condition was detected and we ought to
  *         continue execution of the guest which will delivery the #DF.
@@ -4002,5 +4065,5 @@
                 /*
                  * If event delivery caused an #VMEXIT that is not an exception (e.g. #NPF) then reflect the original
-                 * exception to the guest after handling the VM-exit.
+                 * exception to the guest after handling the #VMEXIT.
                  */
                 enmReflect = SVMREFLECTXCPT_XCPT;
@@ -4077,5 +4140,5 @@
 /* -=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
 
-/** @name VM-exit handlers.
+/** @name #VMEXIT handlers.
  * @{
  */
@@ -4515,5 +4578,5 @@
     STAM_COUNTER_INC(&pVCpu->hm.s.StatExitDRxRead);
 
-    /* We should -not- get this VM-exit if the guest's debug registers were active. */
+    /* We should -not- get this #VMEXIT if the guest's debug registers were active. */
     if (pSvmTransient->fWasGuestDebugStateActive)
     {
@@ -4855,9 +4918,16 @@
     pVmcb->ctrl.IntCtrl.n.u8VIrqVector = 0;
 
-    /* Indicate that we no longer need to VM-exit when the guest is ready to receive interrupts, it is now ready. */
+    /* Clear NMI inhibition, if it's active. */
+    if (VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_INHIBIT_NMIS))
+    {
+        hmR0SvmClearIretIntercept(pVmcb);
+        VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_NMIS);
+    }
+
+    /* Indicate that we no longer need to #VMEXIT when the guest is ready to receive interrupts/NMIs, it is now ready. */
     pVmcb->ctrl.u32InterceptCtrl1 &= ~SVM_CTRL1_INTERCEPT_VINTR;
     pVmcb->ctrl.u64VmcbCleanBits &= ~(HMSVM_VMCB_CLEAN_INTERCEPTS | HMSVM_VMCB_CLEAN_TPR);
 
-    /* Deliver the pending interrupt via hmR0SvmPreRunGuest()->hmR0SvmInjectEventVmcb() and resume guest execution. */
+    /* Deliver the pending interrupt/NMI via hmR0SvmEvaluatePendingEvent() and resume guest execution. */
     STAM_COUNTER_INC(&pVCpu->hm.s.StatExitIntWindow);
     return VINF_SUCCESS;
@@ -4918,4 +4988,23 @@
     if (rc != VINF_SUCCESS)
         hmR0SvmSetPendingXcptUD(pVCpu);
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * #VMEXIT handler for IRET (SVM_EXIT_IRET). Conditional #VMEXIT.
+ */
+HMSVM_EXIT_DECL hmR0SvmExitIret(PVMCPU pVCpu, PCPUMCTX pCtx, PSVMTRANSIENT pSvmTransient)
+{
+    HMSVM_VALIDATE_EXIT_HANDLER_PARAMS();
+
+    /* Clear NMI inhibition. */
+    VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_NMIS);
+
+    /* Indicate that we no longer need to #VMEXIT when the guest is ready to receive NMIs, it is now ready. */
+    PSVMVMCB pVmcb = (PSVMVMCB)pVCpu->hm.s.svm.pvVmcb;
+    hmR0SvmClearIretIntercept(pVmcb);
+
+    /* Deliver the pending NMI via hmR0SvmEvaluatePendingEvent() and resume guest execution. */
     return VINF_SUCCESS;
 }
Index: /trunk/src/VBox/VMM/VMMR0/HMVMXR0.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/HMVMXR0.cpp	(revision 52040)
+++ /trunk/src/VBox/VMM/VMMR0/HMVMXR0.cpp	(revision 52041)
@@ -2323,6 +2323,8 @@
 
     val |=   VMX_VMCS_CTRL_PIN_EXEC_EXT_INT_EXIT           /* External interrupts causes a VM-exits. */
-           | VMX_VMCS_CTRL_PIN_EXEC_NMI_EXIT;              /* Non-maskable interrupts causes a VM-exit. */
-    Assert(!(val & VMX_VMCS_CTRL_PIN_EXEC_VIRTUAL_NMI));
+           | VMX_VMCS_CTRL_PIN_EXEC_NMI_EXIT;              /* Non-maskable interrupts (NMIs) causes a VM-exit. */
+
+    if (pVM->hm.s.vmx.Msrs.VmxPinCtls.n.allowed1 & VMX_VMCS_CTRL_PIN_EXEC_VIRTUAL_NMI)
+        val |= VMX_VMCS_CTRL_PIN_EXEC_VIRTUAL_NMI;         /* Use virtual NMIs and virtual-NMI blocking features. */
 
     /* Enable the VMX preemption timer. */
@@ -2451,4 +2453,11 @@
         }
 #endif
+    }
+
+    /* If we're using virtual NMIs, we need the NMI-window exiting feature. */
+    if (   (pVCpu->hm.s.vmx.u32PinCtls & VMX_VMCS_CTRL_PIN_EXEC_VIRTUAL_NMI)
+        && (pVM->hm.s.vmx.Msrs.VmxProcCtls.n.allowed1 & VMX_VMCS_CTRL_PROC_EXEC_NMI_WINDOW_EXIT))
+    {
+        val |= VMX_VMCS_CTRL_PROC_EXEC_NMI_WINDOW_EXIT;
     }
 
@@ -3513,4 +3522,19 @@
             uIntrState = VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_MOVSS;
     }
+
+    /*
+     * NMIs to the guest are inhibited until the guest executes an IRET. We only
+     * bother with virtual-NMI blocking when we have support for virtual NMIs in the
+     * CPU, otherwise setting this would block host-NMIs and IRET will not clear the
+     * blocking.
+     *
+     * See Intel spec. 26.6.1 "Interruptibility state". See @bugref{7445}.
+     */
+    if (   VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_INHIBIT_NMIS)
+        && (pVCpu->hm.s.vmx.u32PinCtls & VMX_VMCS_CTRL_PIN_EXEC_VIRTUAL_NMI))
+    {
+        uIntrState |= VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_NMI;
+    }
+
     return uIntrState;
 }
@@ -6069,16 +6093,28 @@
 
     if (!uIntrState)
-        VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS);
+    {
+        if (VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS))
+            VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS);
+        if (VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_INHIBIT_NMIS))
+            VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_NMIS);
+    }
     else
     {
-        Assert(   uIntrState == VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_STI
-               || uIntrState == VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_MOVSS);
-        rc  = hmR0VmxSaveGuestRip(pVCpu, pMixedCtx);
-        AssertRC(rc);
-        rc = hmR0VmxSaveGuestRflags(pVCpu, pMixedCtx);    /* for hmR0VmxGetGuestIntrState(). */
-        AssertRC(rc);
-
-        EMSetInhibitInterruptsPC(pVCpu, pMixedCtx->rip);
-        Assert(VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS));
+        if (   (uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_MOVSS)
+            || (uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_STI))
+        {
+            rc  = hmR0VmxSaveGuestRip(pVCpu, pMixedCtx);
+            AssertRC(rc);
+            rc = hmR0VmxSaveGuestRflags(pVCpu, pMixedCtx);    /* for hmR0VmxGetGuestIntrState(). */
+            AssertRC(rc);
+
+            EMSetInhibitInterruptsPC(pVCpu, pMixedCtx->rip);
+            Assert(VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS));
+        }
+
+        if (uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_NMI)
+            VMCPU_FF_SET(pVCpu, VMCPU_FF_INHIBIT_NMIS);
+        else if (VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_INHIBIT_NMIS))
+            VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_NMIS);
     }
 }
@@ -7277,4 +7313,40 @@
 
 /**
+ * Sets the NMI-window exiting control in the VMCS which instructs VT-x to
+ * cause a VM-exit as soon as the guest is in a state to receive NMIs.
+ *
+ * @param pVCpu         Pointer to the VMCPU.
+ */
+DECLINLINE(void) hmR0VmxSetNmiWindowExitVmcs(PVMCPU pVCpu)
+{
+    if (RT_LIKELY(pVCpu->CTX_SUFF(pVM)->hm.s.vmx.Msrs.VmxProcCtls.n.allowed1 & VMX_VMCS_CTRL_PROC_EXEC_NMI_WINDOW_EXIT))
+    {
+        if (!(pVCpu->hm.s.vmx.u32ProcCtls & VMX_VMCS_CTRL_PROC_EXEC_NMI_WINDOW_EXIT))
+        {
+            pVCpu->hm.s.vmx.u32ProcCtls |= VMX_VMCS_CTRL_PROC_EXEC_NMI_WINDOW_EXIT;
+            int rc = VMXWriteVmcs32(VMX_VMCS32_CTRL_PROC_EXEC, pVCpu->hm.s.vmx.u32ProcCtls);
+            AssertRC(rc);
+            Log4(("Setup NMI-window exiting\n"));
+        }
+    } /* else we will deliver NMIs whenever we VM-exit next, even possibly nesting NMIs. Can't be helped on ancient CPUs. */
+}
+
+
+/**
+ * Clears the NMI-window exiting control in the VMCS.
+ *
+ * @param pVCpu             Pointer to the VMCPU.
+ */
+DECLINLINE(void) hmR0VmxClearNmiWindowExitVmcs(PVMCPU pVCpu)
+{
+    Assert(pVCpu->hm.s.vmx.u32ProcCtls & VMX_VMCS_CTRL_PROC_EXEC_NMI_WINDOW_EXIT);
+    pVCpu->hm.s.vmx.u32ProcCtls &= ~VMX_VMCS_CTRL_PROC_EXEC_NMI_WINDOW_EXIT;
+    int rc = VMXWriteVmcs32(VMX_VMCS32_CTRL_PROC_EXEC, pVCpu->hm.s.vmx.u32ProcCtls);
+    AssertRC(rc);
+    Log4(("Cleared NMI-window exiting\n"));
+}
+
+
+/**
  * Evaluates the event to be delivered to the guest and sets it as the pending
  * event.
@@ -7293,8 +7365,8 @@
     bool fBlockMovSS    = RT_BOOL(uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_MOVSS);
     bool fBlockSti      = RT_BOOL(uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_STI);
+    bool fBlockNmi      = RT_BOOL(uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_NMI);
 
     Assert(!fBlockSti || HMVMXCPU_GST_IS_UPDATED(pVCpu, HMVMX_UPDATED_GUEST_RFLAGS));
-    Assert(   !(uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_NMI)      /* We don't support block-by-NMI and SMI yet.*/
-           && !(uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_SMI));
+    Assert(!(uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_SMI));    /* We don't support block-by-SMI yet.*/
     Assert(!fBlockSti || pMixedCtx->eflags.Bits.u1IF);     /* Cannot set block-by-STI when interrupts are disabled. */
     Assert(!TRPMHasTrap(pVCpu));
@@ -7303,9 +7375,13 @@
     if (VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_INTERRUPT_NMI))    /* NMI. NMIs take priority over regular interrupts . */
     {
-        /* On some CPUs block-by-STI also blocks NMIs. See Intel spec. 26.3.1.5 "Checks On Guest Non-Register State". */
-        if (   !fBlockMovSS
-            && !fBlockSti)
+        if (   fBlockNmi
+            || fBlockSti
+            || fBlockMovSS)
         {
             /* On some CPUs block-by-STI also blocks NMIs. See Intel spec. 26.3.1.5 "Checks On Guest Non-Register State". */
+            hmR0VmxSetNmiWindowExitVmcs(pVCpu);
+        }
+        else
+        {
             Log4(("Pending NMI vcpu[%RU32]\n", pVCpu->idCpu));
             uint32_t u32IntInfo = X86_XCPT_NMI | VMX_EXIT_INTERRUPTION_INFO_VALID;
@@ -7315,6 +7391,4 @@
             VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INTERRUPT_NMI);
         }
-        else
-            hmR0VmxSetIntWindowExitVmcs(pVCpu);
     }
     /*
@@ -7396,6 +7470,5 @@
 
     Assert(!fBlockSti || HMVMXCPU_GST_IS_UPDATED(pVCpu, HMVMX_UPDATED_GUEST_RFLAGS));
-    Assert(   !(uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_NMI)      /* We don't support block-by-NMI and SMI yet.*/
-           && !(uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_SMI));
+    Assert(!(uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_SMI));     /* We don't support block-by-SMI yet.*/
     Assert(!fBlockSti || pMixedCtx->eflags.Bits.u1IF);       /* Cannot set block-by-STI when interrupts are disabled. */
     Assert(!TRPMHasTrap(pVCpu));
@@ -7411,11 +7484,11 @@
         uint32_t uIntType = VMX_EXIT_INTERRUPTION_INFO_TYPE(pVCpu->hm.s.Event.u64IntInfo);
         if (   (pVCpu->hm.s.vmx.u32ProcCtls & VMX_VMCS_CTRL_PROC_EXEC_INT_WINDOW_EXIT)
-            && (   uIntType == VMX_EXIT_INTERRUPTION_INFO_TYPE_EXT_INT
-                || uIntType == VMX_EXIT_INTERRUPTION_INFO_TYPE_NMI))
+            && uIntType == VMX_EXIT_INTERRUPTION_INFO_TYPE_EXT_INT)
         {
             Assert(pVCpu->CTX_SUFF(pVM)->hm.s.vmx.Msrs.VmxProcCtls.n.allowed1 & VMX_VMCS_CTRL_PROC_EXEC_INT_WINDOW_EXIT);
             hmR0VmxClearIntWindowExitVmcs(pVCpu);
         }
-#if 1 /* defined(VBOX_STRICT) */  /* Temporarily for debugging. */
+
+#ifdef VBOX_STRICT
         if (uIntType == VMX_EXIT_INTERRUPTION_INFO_TYPE_EXT_INT)
         {
@@ -7425,11 +7498,11 @@
             Assert(!fBlockSti);
             Assert(!fBlockMovSS);
-            Assert(!(pVCpu->hm.s.vmx.u32ProcCtls & VMX_VMCS_CTRL_PROC_EXEC_INT_WINDOW_EXIT));
         }
         else if (uIntType == VMX_EXIT_INTERRUPTION_INFO_TYPE_NMI)
         {
+            bool fBlockNmi = RT_BOOL(uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_NMI);
             Assert(!fBlockSti);
             Assert(!fBlockMovSS);
-            Assert(!(pVCpu->hm.s.vmx.u32ProcCtls & VMX_VMCS_CTRL_PROC_EXEC_INT_WINDOW_EXIT));
+            Assert(!fBlockNmi);
         }
 #endif
@@ -7838,7 +7911,7 @@
 
     /* Validate. */
-    Assert(VMX_EXIT_INTERRUPTION_INFO_IS_VALID(u32IntInfo));        /* Bit 31 (Valid bit) must be set by caller. */
-    Assert(!VMX_EXIT_INTERRUPTION_INFO_NMI_UNBLOCK(u32IntInfo));    /* Bit 12 MBZ. */
-    Assert(!(u32IntInfo & 0x7ffff000));                             /* Bits 30:12 MBZ. */
+    Assert(VMX_EXIT_INTERRUPTION_INFO_IS_VALID(u32IntInfo));             /* Bit 31 (Valid bit) must be set by caller. */
+    Assert(!VMX_EXIT_INTERRUPTION_INFO_NMI_UNBLOCK_IRET(u32IntInfo));    /* Bit 12 MBZ. */
+    Assert(!(u32IntInfo & 0x7ffff000));                                  /* Bits 30:12 MBZ. */
 
     /* Inject. */
@@ -9812,5 +9885,5 @@
     hmR0VmxClearIntWindowExitVmcs(pVCpu);
 
-    /* Deliver the pending interrupt via hmR0VmxPreRunGuest()->hmR0VmxInjectEvent() and resume guest execution. */
+    /* Deliver the pending interrupts via hmR0VmxEvaluatePendingEvent() and resume guest execution. */
     STAM_COUNTER_INC(&pVCpu->hm.s.StatExitIntWindow);
     return VINF_SUCCESS;
@@ -9824,6 +9897,25 @@
 {
     HMVMX_VALIDATE_EXIT_HANDLER_PARAMS();
-    AssertMsgFailed(("Unexpected NMI-window exit.\n"));
-    HMVMX_RETURN_UNEXPECTED_EXIT();
+    if (RT_UNLIKELY(!(pVCpu->hm.s.vmx.u32ProcCtls & VMX_VMCS_CTRL_PROC_EXEC_NMI_WINDOW_EXIT)))
+    {
+        AssertMsgFailed(("Unexpected NMI-window exit.\n"));
+        HMVMX_RETURN_UNEXPECTED_EXIT();
+    }
+
+    Assert(!VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_INHIBIT_NMIS));
+
+    /*
+     * Clear block-by-STI if it's active. The force-flag couldn't have been set by block-by-Mov SS in
+     * hmR0VmxSaveGuestIntrState() when this VM-exit happens as Intel CPUs are consistent with
+     * block-by-Mov SS and NMIs. See @bugref{7445}.
+     */
+    if (VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS))
+        VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS);
+
+    /* Indicate that we no longer need to VM-exit when the guest is ready to receive NMIs, it is now ready */
+    hmR0VmxClearNmiWindowExitVmcs(pVCpu);
+
+    /* Deliver the pending NMI via hmR0VmxEvaluatePendingEvent() and resume guest execution. */
+    return VINF_SUCCESS;
 }
 
Index: /trunk/src/VBox/VMM/VMMR3/HM.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/HM.cpp	(revision 52040)
+++ /trunk/src/VBox/VMM/VMMR3/HM.cpp	(revision 52041)
@@ -70,5 +70,5 @@
     EXIT_REASON(VMX_EXIT_SMI_IRQ            ,  6, "Other SMI."),
     EXIT_REASON(VMX_EXIT_INT_WINDOW         ,  7, "Interrupt window."),
-    EXIT_REASON_NIL(),
+    EXIT_REASON(VMX_EXIT_NMI_WINDOW         ,  8, "NMI window."),
     EXIT_REASON(VMX_EXIT_TASK_SWITCH        ,  9, "Task switch."),
     EXIT_REASON(VMX_EXIT_CPUID              , 10, "Guest attempted to execute CPUID."),
Index: /trunk/src/recompiler/VBoxRecompiler.c
===================================================================
--- /trunk/src/recompiler/VBoxRecompiler.c	(revision 52040)
+++ /trunk/src/recompiler/VBoxRecompiler.c	(revision 52041)
@@ -2237,4 +2237,9 @@
     }
 
+    /* Update the inhibit NMI mask. */
+    pVM->rem.s.Env.hflags2 &= ~HF2_NMI_MASK;
+    if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_NMIS))
+        pVM->rem.s.Env.hflags2 |= HF2_NMI_MASK;
+
     /*
      * Sync the A20 gate.
@@ -2723,4 +2728,16 @@
         Log(("Clearing VMCPU_FF_INHIBIT_INTERRUPTS at %RGv - successor %RGv (REM#2)\n", (RTGCPTR)pCtx->rip, EMGetInhibitInterruptsPC(pVCpu)));
         VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_INTERRUPTS);
+    }
+
+    /* Inhibit NMI flag. */
+    if (pVM->rem.s.Env.hflags2 & HF2_NMI_MASK)
+    {
+        Log(("Settings VMCPU_FF_INHIBIT_NMIS at %RGv (REM)\n", (RTGCPTR)pCtx->rip));
+        VMCPU_FF_SET(pVCpu, VMCPU_FF_INHIBIT_NMIS);
+    }
+    else if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INHIBIT_NMIS))
+    {
+        Log(("Clearing VMCPU_FF_INHIBIT_NMIS at %RGv (REM)\n", (RTGCPTR)pCtx->rip));
+        VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_INHIBIT_NMIS);
     }
 
