Index: trunk/src/VBox/VMM/VMMR0/HMSVMR0.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR0/HMSVMR0.cpp	(revision 69932)
+++ trunk/src/VBox/VMM/VMMR0/HMSVMR0.cpp	(revision 69934)
@@ -1230,8 +1230,14 @@
  * updates the corresponding VMCB Clean bit.
  *
+ * @param   pVCpu       The cross context virtual CPU structure.
+ * @param   pCtx        Pointer to the guest-CPU context.
  * @param   pVmcb       Pointer to the VM control block.
  * @param   u32Xcpt     The value of the exception (X86_XCPT_*).
- */
-DECLINLINE(void) hmR0SvmRemoveXcptIntercept(PSVMVMCB pVmcb, uint32_t u32Xcpt)
+ *
+ * @remarks This takes into account if we're executing a nested-guest and only
+ *          removes the exception intercept if both the guest -and- nested-guest
+ *          are not intercepting it.
+ */
+DECLINLINE(void) hmR0SvmRemoveXcptIntercept(PVMCPU pVCpu, PCPUMCTX pCtx, PSVMVMCB pVmcb, uint32_t u32Xcpt)
 {
     Assert(u32Xcpt != X86_XCPT_DB);
@@ -1240,13 +1246,32 @@
     if (pVmcb->ctrl.u32InterceptXcpt & RT_BIT(u32Xcpt))
     {
-        pVmcb->ctrl.u32InterceptXcpt &= ~RT_BIT(u32Xcpt);
-        pVmcb->ctrl.u64VmcbCleanBits &= ~HMSVM_VMCB_CLEAN_INTERCEPTS;
-    }
+        bool fRemoveXcpt = true;
+#ifdef VBOX_WITH_NESTED_HWVIRT
+        /* Only remove the intercept if the nested-guest is also not intercepting it! */
+        if (CPUMIsGuestInSvmNestedHwVirtMode(pCtx))
+        {
+            Assert(pCtx->hwvirt.svm.fHMCachedVmcb); NOREF(pCtx);
+            PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
+            fRemoveXcpt = !(pVmcbNstGstCache->u32InterceptXcpt & RT_BIT(u32Xcpt));
+        }
+#else
+        RT_NOREF2(pVCpu, pCtx);
 #endif
-}
-
-
-/**
- * Loads the guest CR0 control register into the guest-state area in the VMCB.
+        if (fRemoveXcpt)
+        {
+            pVmcb->ctrl.u32InterceptXcpt &= ~RT_BIT(u32Xcpt);
+            pVmcb->ctrl.u64VmcbCleanBits &= ~HMSVM_VMCB_CLEAN_INTERCEPTS;
+        }
+    }
+#else
+    RT_NOREF3(pVCpu, pCtx, pVmcb);
+#endif
+}
+
+
+/**
+ * Loads the guest (or nested-guest) CR0 control register into the guest-state
+ * area in the VMCB.
+ *
  * Although the guest CR0 is a separate field in the VMCB we have to consider
  * the FPU state itself which is shared between the host and the guest.
@@ -1303,10 +1328,10 @@
         hmR0SvmAddXcptIntercept(pVmcb, X86_XCPT_NM);
     else
-        hmR0SvmRemoveXcptIntercept(pVmcb, X86_XCPT_NM);
+        hmR0SvmRemoveXcptIntercept(pVCpu, pCtx, pVmcb, X86_XCPT_NM);
 
     if (fInterceptMF)
         hmR0SvmAddXcptIntercept(pVmcb, X86_XCPT_MF);
     else
-        hmR0SvmRemoveXcptIntercept(pVmcb, X86_XCPT_MF);
+        hmR0SvmRemoveXcptIntercept(pVCpu, pCtx, pVmcb, X86_XCPT_MF);
 
     pVmcb->guest.u64CR0 = u64GuestCR0;
@@ -1816,6 +1841,7 @@
  * @param   pVCpu       The cross context virtual CPU structure.
  * @param   pVmcb       Pointer to the VM control block.
- */
-static void hmR0SvmLoadGuestXcptIntercepts(PVMCPU pVCpu, PSVMVMCB pVmcb)
+ * @param   pCtx        Pointer to the guest-CPU context.
+ */
+static void hmR0SvmLoadGuestXcptIntercepts(PVMCPU pVCpu, PSVMVMCB pVmcb, PCPUMCTX pCtx)
 {
     if (HMCPU_CF_IS_PENDING(pVCpu, HM_CHANGED_GUEST_XCPT_INTERCEPTS))
@@ -1825,5 +1851,5 @@
             hmR0SvmAddXcptIntercept(pVmcb, X86_XCPT_UD);
         else
-            hmR0SvmRemoveXcptIntercept(pVmcb, X86_XCPT_UD);
+            hmR0SvmRemoveXcptIntercept(pVCpu, pCtx, pVmcb, X86_XCPT_UD);
 
         /* Trap #BP for INT3 debug breakpoints set by the VM debugger. */
@@ -1831,5 +1857,5 @@
             hmR0SvmAddXcptIntercept(pVmcb, X86_XCPT_BP);
         else
-            hmR0SvmRemoveXcptIntercept(pVmcb, X86_XCPT_BP);
+            hmR0SvmRemoveXcptIntercept(pVCpu, pCtx, pVmcb, X86_XCPT_BP);
 
         /* The remaining intercepts are handled elsewhere, e.g. in hmR0SvmLoadSharedCR0(). */
@@ -1849,6 +1875,7 @@
  * @param   pVCpu           The cross context virtual CPU structure.
  * @param   pVmcbNstGst     Pointer to the nested-guest VM control block.
- */
-static void hmR0SvmLoadGuestXcptInterceptsNested(PVMCPU pVCpu, PSVMVMCB pVmcbNstGst)
+ * @param   pCtx            Pointer to the guest-CPU context.
+ */
+static void hmR0SvmLoadGuestXcptInterceptsNested(PVMCPU pVCpu, PSVMVMCB pVmcbNstGst, PCPUMCTX pCtx)
 {
     if (HMCPU_CF_IS_PENDING(pVCpu, HM_CHANGED_GUEST_XCPT_INTERCEPTS))
@@ -1856,5 +1883,5 @@
         /* First, load the guest intercepts into the guest VMCB. */
         PSVMVMCB pVmcb = pVCpu->hm.s.svm.pVmcb;
-        hmR0SvmLoadGuestXcptIntercepts(pVCpu, pVmcb);
+        hmR0SvmLoadGuestXcptIntercepts(pVCpu, pVmcb, pCtx);
 
         /* Next, merge the intercepts into the nested-guest VMCB. */
@@ -1894,7 +1921,4 @@
         pVmcbNstGst->ctrl.u64InterceptCtrl  &= ~SVM_CTRL_INTERCEPT_VMMCALL;
 
-        /* Remove exception intercepts that we don't need while executing the nested-guest. */
-        pVmcbNstGst->ctrl.u32InterceptXcpt  &= ~RT_BIT(X86_XCPT_UD);
-
         Assert(!HMCPU_CF_IS_PENDING(pVCpu, HM_CHANGED_GUEST_XCPT_INTERCEPTS));
     }
@@ -2085,5 +2109,5 @@
     AssertLogRelMsgRCReturn(rc, ("hmR0SvmLoadGuestApicState! rc=%Rrc (pVM=%p pVCpu=%p)\n", rc, pVM, pVCpu), rc);
 
-    hmR0SvmLoadGuestXcptIntercepts(pVCpu, pVmcb);
+    hmR0SvmLoadGuestXcptIntercepts(pVCpu, pVmcb, pCtx);
 
     rc = hmR0SvmSetupVMRunHandler(pVCpu);
@@ -2241,5 +2265,5 @@
 
     hmR0SvmLoadGuestApicStateNested(pVCpu, pVmcbNstGst);
-    hmR0SvmLoadGuestXcptInterceptsNested(pVCpu, pVmcbNstGst);
+    hmR0SvmLoadGuestXcptInterceptsNested(pVCpu, pVmcbNstGst, pCtx);
 
     rc = hmR0SvmSetupVMRunHandler(pVCpu);
@@ -2289,13 +2313,5 @@
     if (HMCPU_CF_IS_PENDING(pVCpu, HM_CHANGED_GUEST_CR0))
     {
-#ifdef VBOX_WITH_NESTED_HWVIRT
-        /* We use nested-guest CR0 unmodified, hence nothing to do here. */
-        if (!CPUMIsGuestInSvmNestedHwVirtMode(pCtx))
-            hmR0SvmLoadSharedCR0(pVCpu, pVmcb, pCtx);
-        else
-            Assert(pVmcb->guest.u64CR0 == pCtx->cr0);
-#else
         hmR0SvmLoadSharedCR0(pVCpu, pVmcb, pCtx);
-#endif
         HMCPU_CF_CLEAR(pVCpu, HM_CHANGED_GUEST_CR0);
     }
@@ -2303,5 +2319,5 @@
     if (HMCPU_CF_IS_PENDING(pVCpu, HM_CHANGED_GUEST_DEBUG))
     {
-        /* We use nested-guest CR0 unmodified, hence nothing to do here. */
+        /** @todo Figure out stepping with nested-guest. */
         if (!CPUMIsGuestInSvmNestedHwVirtMode(pCtx))
             hmR0SvmLoadSharedDebugState(pVCpu, pVmcb, pCtx);
@@ -6481,43 +6497,43 @@
     STAM_COUNTER_INC(&pVCpu->hm.s.StatExitDRxRead);
 
-    /* We should -not- get this #VMEXIT if the guest's debug registers were active. */
-    if (pSvmTransient->fWasGuestDebugStateActive)
-    {
-        AssertMsgFailed(("hmR0SvmExitReadDRx: Unexpected exit %#RX32\n", (uint32_t)pSvmTransient->u64ExitCode));
-        pVCpu->hm.s.u32HMError = (uint32_t)pSvmTransient->u64ExitCode;
-        return VERR_SVM_UNEXPECTED_EXIT;
-    }
-
-    /*
-     * Lazy DR0-3 loading.
-     */
-    if (   !pSvmTransient->fWasHyperDebugStateActive
-#ifdef VBOX_WITH_NESTED_HWVIRT
-           && !CPUMIsGuestInSvmNestedHwVirtMode(pCtx) /** @todo implement single-stepping when executing a nested-guest. */
-#endif
-       )
-    {
-        Assert(!DBGFIsStepping(pVCpu)); Assert(!pVCpu->hm.s.fSingleInstruction);
-        Log5(("hmR0SvmExitReadDRx: Lazy loading guest debug registers\n"));
-
-        /* Don't intercept DRx read and writes. */
-        PSVMVMCB pVmcb = pVCpu->hm.s.svm.pVmcb;
-        pVmcb->ctrl.u16InterceptRdDRx = 0;
-        pVmcb->ctrl.u16InterceptWrDRx = 0;
-        pVmcb->ctrl.u64VmcbCleanBits &= ~HMSVM_VMCB_CLEAN_INTERCEPTS;
-
-        /* We're playing with the host CPU state here, make sure we don't preempt or longjmp. */
-        VMMRZCallRing3Disable(pVCpu);
-        HM_DISABLE_PREEMPT();
-
-        /* Save the host & load the guest debug state, restart execution of the MOV DRx instruction. */
-        CPUMR0LoadGuestDebugState(pVCpu, false /* include DR6 */);
-        Assert(CPUMIsGuestDebugStateActive(pVCpu) || HC_ARCH_BITS == 32);
-
-        HM_RESTORE_PREEMPT();
-        VMMRZCallRing3Enable(pVCpu);
-
-        STAM_COUNTER_INC(&pVCpu->hm.s.StatDRxContextSwitch);
-        return VINF_SUCCESS;
+    /** @todo Stepping with nested-guest. */
+    if (!CPUMIsGuestInSvmNestedHwVirtMode(pCtx))
+    {
+        /* We should -not- get this #VMEXIT if the guest's debug registers were active. */
+        if (pSvmTransient->fWasGuestDebugStateActive)
+        {
+            AssertMsgFailed(("hmR0SvmExitReadDRx: Unexpected exit %#RX32\n", (uint32_t)pSvmTransient->u64ExitCode));
+            pVCpu->hm.s.u32HMError = (uint32_t)pSvmTransient->u64ExitCode;
+            return VERR_SVM_UNEXPECTED_EXIT;
+        }
+
+        /*
+         * Lazy DR0-3 loading.
+         */
+        if (!pSvmTransient->fWasHyperDebugStateActive)
+        {
+            Assert(!DBGFIsStepping(pVCpu)); Assert(!pVCpu->hm.s.fSingleInstruction);
+            Log5(("hmR0SvmExitReadDRx: Lazy loading guest debug registers\n"));
+
+            /* Don't intercept DRx read and writes. */
+            PSVMVMCB pVmcb = pVCpu->hm.s.svm.pVmcb;
+            pVmcb->ctrl.u16InterceptRdDRx = 0;
+            pVmcb->ctrl.u16InterceptWrDRx = 0;
+            pVmcb->ctrl.u64VmcbCleanBits &= ~HMSVM_VMCB_CLEAN_INTERCEPTS;
+
+            /* We're playing with the host CPU state here, make sure we don't preempt or longjmp. */
+            VMMRZCallRing3Disable(pVCpu);
+            HM_DISABLE_PREEMPT();
+
+            /* Save the host & load the guest debug state, restart execution of the MOV DRx instruction. */
+            CPUMR0LoadGuestDebugState(pVCpu, false /* include DR6 */);
+            Assert(CPUMIsGuestDebugStateActive(pVCpu) || HC_ARCH_BITS == 32);
+
+            HM_RESTORE_PREEMPT();
+            VMMRZCallRing3Enable(pVCpu);
+
+            STAM_COUNTER_INC(&pVCpu->hm.s.StatDRxContextSwitch);
+            return VINF_SUCCESS;
+        }
     }
 
