Index: trunk/src/VBox/VMM/VMMAll/DBGFAll.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMAll/DBGFAll.cpp	(revision 47680)
+++ trunk/src/VBox/VMM/VMMAll/DBGFAll.cpp	(revision 47681)
@@ -24,4 +24,5 @@
 #include "DBGFInternal.h"
 #include <VBox/vmm/vm.h>
+#include <VBox/err.h>
 #include <iprt/assert.h>
 
@@ -131,4 +132,128 @@
 
 /**
+ * Checks if any of the hardware I/O breakpoints are armed.
+ *
+ * @returns true if armed, false if not.
+ * @param   pVM         The cross context VM structure.
+ */
+VMM_INT_DECL(bool) DBGFBpIsHwIoArmed(PVM pVM)
+{
+    Assert(RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints) == 4);
+    /** @todo cache this! */
+    return (   pVM->dbgf.s.aHwBreakpoints[0].u.Reg.fType == X86_DR7_RW_IO
+            && pVM->dbgf.s.aHwBreakpoints[0].fEnabled
+            && pVM->dbgf.s.aHwBreakpoints[0].enmType     == DBGFBPTYPE_REG
+           )
+        || (   pVM->dbgf.s.aHwBreakpoints[1].u.Reg.fType == X86_DR7_RW_IO
+            && pVM->dbgf.s.aHwBreakpoints[1].fEnabled
+            && pVM->dbgf.s.aHwBreakpoints[1].enmType     == DBGFBPTYPE_REG
+           )
+        || (   pVM->dbgf.s.aHwBreakpoints[2].u.Reg.fType == X86_DR7_RW_IO
+            && pVM->dbgf.s.aHwBreakpoints[2].fEnabled
+            && pVM->dbgf.s.aHwBreakpoints[2].enmType     == DBGFBPTYPE_REG
+           )
+        || (   pVM->dbgf.s.aHwBreakpoints[3].u.Reg.fType == X86_DR7_RW_IO
+            && pVM->dbgf.s.aHwBreakpoints[3].fEnabled
+            && pVM->dbgf.s.aHwBreakpoints[3].enmType     == DBGFBPTYPE_REG
+           );
+}
+
+
+/**
+ * Checks I/O access for guest or hypervisor breakpoints.
+ *
+ * @returns Strict VBox status code
+ * @retval  VINF_SUCCESS no breakpoint.
+ * @retval  VINF_EM_DBG_BREAKPOINT hypervisor breakpoint triggered.
+ * @retval  VINF_EM_RAW_GUEST_TRAP guest breakpoint triggered, DR6 and DR7 have
+ *          been updated appropriately.
+ *
+ * @param   pVM         The cross context VM structure.
+ * @param   pVCpu       The cross context CPU structure for the calling EMT.
+ * @param   pCtx        The CPU context for the calling EMT.
+ * @param   uIoPort     The I/O port being accessed.
+ * @param   cbValue     The size/width of the access, in bytes.
+ */
+VMM_INT_DECL(VBOXSTRICTRC)  DBGFBpCheckIo(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, RTIOPORT uIoPort, uint8_t cbValue)
+{
+    static uint8_t const s_abInvAlign[4] = { 0, 1, 7, 3 };
+    uint32_t const uIoPortFirst = uIoPort;
+    uint32_t const uIoPortLast  = uIoPortFirst + cbValue - 1;
+
+
+    /*
+     * Check hyper breakpoints first as the VMM debugger has priority over
+     * the guest.
+     */
+    for (unsigned iBp = 0; iBp < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); iBp++)
+    {
+        if (   pVM->dbgf.s.aHwBreakpoints[iBp].u.Reg.fType == X86_DR7_RW_IO
+            && pVM->dbgf.s.aHwBreakpoints[iBp].fEnabled
+            && pVM->dbgf.s.aHwBreakpoints[iBp].enmType     == DBGFBPTYPE_REG )
+        {
+            uint8_t  cbReg      = pVM->dbgf.s.aHwBreakpoints[iBp].u.Reg.cb; Assert(RT_IS_POWER_OF_TWO(cbReg));
+            uint64_t uDrXFirst  = pVM->dbgf.s.aHwBreakpoints[iBp].GCPtr & ~(uint64_t)(cbReg - 1);
+            uint64_t uDrXLast   = uDrXFirst + cbReg - 1;
+            if (uDrXFirst <= uIoPortLast && uDrXLast >= uIoPortFirst)
+            {
+                /* (See also DBGFRZTrap01Handler.) */
+                pVCpu->dbgf.s.iActiveBp = pVM->dbgf.s.aHwBreakpoints[iBp].iBp;
+                pVCpu->dbgf.s.fSingleSteppingRaw = false;
+
+                LogFlow(("DBGFBpCheckIo: hit hw breakpoint %d at %04x:%RGv (iop %#x)\n",
+                         pVM->dbgf.s.aHwBreakpoints[iBp].iBp, pCtx->cs.Sel, pCtx->rip, uIoPort));
+                return VINF_EM_DBG_BREAKPOINT;
+            }
+        }
+    }
+
+    /*
+     * Check the guest.
+     */
+    uint32_t const uDr7 = pCtx->dr[7];
+    if (   (uDr7 & X86_DR7_ENABLED_MASK)
+        && X86_DR7_ANY_RW_IO(uDr7)
+        && (pCtx->cr4 & X86_CR4_DE) )
+    {
+        for (unsigned iBp = 0; iBp < 4; iBp++)
+        {
+            if (   (uDr7 & X86_DR7_L_G(iBp))
+                && X86_DR7_GET_RW(uDr7, iBp) == X86_DR7_RW_IO)
+            {
+                /* ASSUME the breakpoint and the I/O width qualifier uses the same encoding (1 2 x 4). */
+                static uint8_t const s_abInvAlign[4] = { 0, 1, 7, 3 };
+                uint8_t  cbInvAlign = s_abInvAlign[X86_DR7_GET_LEN(uDr7, iBp)];
+                uint64_t uDrXFirst  = pCtx->dr[iBp] & ~(uint64_t)cbInvAlign;
+                uint64_t uDrXLast   = uDrXFirst + cbInvAlign;
+
+                if (uDrXFirst <= uIoPortLast && uDrXLast >= uIoPortFirst)
+                {
+                    /*
+                     * Update DR6 and DR7.
+                     *
+                     * See "AMD64 Architecture Programmer's Manual Volume 2",
+                     * chapter 13.1.1.3 for details on DR6 bits.  The basics is
+                     * that the B0..B3 bits are always cleared while the others
+                     * must be cleared by software.
+                     *
+                     * The following section says the GD bit is always cleared
+                     * when generating a #DB so the handler can safely access
+                     * the debug registers.
+                     */
+                    pCtx->dr[6] &= ~X86_DR6_B_MASK;
+                    pCtx->dr[6] |= X86_DR6_B(iBp);
+                    pCtx->dr[7] &= ~X86_DR7_GD;
+                    LogFlow(("DBGFBpCheckIo: hit hw breakpoint %d at %04x:%RGv (iop %#x)\n",
+                             pVM->dbgf.s.aHwBreakpoints[iBp].iBp, pCtx->cs.Sel, pCtx->rip, uIoPort));
+                    return VINF_EM_RAW_GUEST_TRAP;
+                }
+            }
+        }
+    }
+    return VINF_SUCCESS;
+}
+
+
+/**
  * Returns the single stepping state for a virtual CPU.
  *
Index: trunk/src/VBox/VMM/VMMR0/CPUMR0.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR0/CPUMR0.cpp	(revision 47680)
+++ trunk/src/VBox/VMM/VMMR0/CPUMR0.cpp	(revision 47681)
@@ -87,5 +87,5 @@
  * XXX Host-CPU hot-plugging?
  */
-VMMR0DECL(int) CPUMR0ModuleInit(void)
+VMMR0_INT_DECL(int) CPUMR0ModuleInit(void)
 {
     int rc = VINF_SUCCESS;
@@ -100,5 +100,5 @@
  * Terminate the module.
  */
-VMMR0DECL(int) CPUMR0ModuleTerm(void)
+VMMR0_INT_DECL(int) CPUMR0ModuleTerm(void)
 {
 #ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
@@ -166,5 +166,5 @@
  * @param   pVM         Pointer to the VM.
  */
-VMMR0DECL(int) CPUMR0Init(PVM pVM)
+VMMR0_INT_DECL(int) CPUMR0Init(PVM pVM)
 {
     LogFlow(("CPUMR0Init: %p\n", pVM));
@@ -283,5 +283,5 @@
  * @param   pCtx        Pointer to the guest CPU context.
  */
-VMMR0DECL(int) CPUMR0LoadGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
+VMMR0_INT_DECL(int) CPUMR0LoadGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
 {
     Assert(pVM->cpum.s.CPUFeatures.edx.u1FXSR);
@@ -446,5 +446,5 @@
  * @param   pCtx        Pointer to the guest CPU context.
  */
-VMMR0DECL(int) CPUMR0SaveGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
+VMMR0_INT_DECL(int) CPUMR0SaveGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
 {
     Assert(pVM->cpum.s.CPUFeatures.edx.u1FXSR);
@@ -573,8 +573,8 @@
  * @returns true if either guest or hypervisor debug registers were loaded.
  * @param   pVCpu       The cross context CPU structure for the calling EMT.
- * @param   fDR6        Whether to include DR6 or not.
+ * @param   fDr6        Whether to include DR6 or not.
  * @thread  EMT(pVCpu)
  */
-VMMR0DECL(bool) CPUMR0DebugStateMaybeSaveGuestAndRestoreHost(PVMCPU pVCpu, bool fDR6)
+VMMR0_INT_DECL(bool) CPUMR0DebugStateMaybeSaveGuestAndRestoreHost(PVMCPU pVCpu, bool fDr6)
 {
     bool const fDrXLoaded = RT_BOOL(pVCpu->cpum.s.fUseFlags & (CPUM_USED_DEBUG_REGS_GUEST | CPUM_USED_DEBUG_REGS_HYPER));
@@ -582,5 +582,5 @@
     /*
      * Do we need to save the guest DRx registered loaded into host registers?
-     * (DR7 and DR6 (if fDR6 is true) are left to the caller.)
+     * (DR7 and DR6 (if fDr6 is true) are left to the caller.)
      */
     if (pVCpu->cpum.s.fUseFlags & CPUM_USED_DEBUG_REGS_GUEST)
@@ -591,5 +591,5 @@
             uint64_t uDr6 = pVCpu->cpum.s.Guest.dr[6];
             HMR0SaveDebugState(pVCpu->CTX_SUFF(pVM), pVCpu, &pVCpu->cpum.s.Guest);
-            if (!fDR6)
+            if (!fDr6)
                 pVCpu->cpum.s.Guest.dr[6] = uDr6;
         }
@@ -605,5 +605,5 @@
             pVCpu->cpum.s.Guest.dr[3] = ASMGetDR3();
 #endif
-            if (fDR6)
+            if (fDr6)
                 pVCpu->cpum.s.Guest.dr[6] = ASMGetDR6();
         }
@@ -644,11 +644,59 @@
 
 /**
+ * Saves the guest DRx state if it resides host registers.
+ *
+ * This does NOT clear any use flags, so the host registers remains loaded with
+ * the guest DRx state upon return.  The purpose is only to make sure the values
+ * in the CPU context structure is up to date.
+ *
+ * @returns true if the host registers contains guest values, false if not.
+ * @param   pVCpu       The cross context CPU structure for the calling EMT.
+ * @param   fDr6        Whether to include DR6 or not.
+ * @thread  EMT(pVCpu)
+ */
+VMMR0_INT_DECL(bool) CPUMR0DebugStateMaybeSaveGuest(PVMCPU pVCpu, bool fDr6)
+{
+    /*
+     * Do we need to save the guest DRx registered loaded into host registers?
+     * (DR7 and DR6 (if fDr6 is true) are left to the caller.)
+     */
+    if (pVCpu->cpum.s.fUseFlags & CPUM_USED_DEBUG_REGS_GUEST)
+    {
+#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
+        if (CPUMIsGuestInLongModeEx(&pVCpu->cpum.s.Guest))
+        {
+            uint64_t uDr6 = pVCpu->cpum.s.Guest.dr[6];
+            HMR0SaveDebugState(pVCpu->CTX_SUFF(pVM), pVCpu, &pVCpu->cpum.s.Guest);
+            if (!fDr6)
+                pVCpu->cpum.s.Guest.dr[6] = uDr6;
+        }
+        else
+#endif
+        {
+#ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
+            cpumR0SaveDRx(&pVCpu->cpum.s.Guest.dr[0]);
+#else
+            pVCpu->cpum.s.Guest.dr[0] = ASMGetDR0();
+            pVCpu->cpum.s.Guest.dr[1] = ASMGetDR1();
+            pVCpu->cpum.s.Guest.dr[2] = ASMGetDR2();
+            pVCpu->cpum.s.Guest.dr[3] = ASMGetDR3();
+#endif
+            if (fDr6)
+                pVCpu->cpum.s.Guest.dr[6] = ASMGetDR6();
+        }
+        return true;
+    }
+    return false;
+}
+
+
+/**
  * Lazily sync in the debug state.
  *
  * @param   pVCpu       The cross context CPU structure for the calling EMT.
- * @param   fDR6        Whether to include DR6 or not.
+ * @param   fDr6        Whether to include DR6 or not.
  * @thread  EMT(pVCpu)
  */
-VMMR0DECL(void) CPUMR0LoadGuestDebugState(PVMCPU pVCpu, bool fDR6)
+VMMR0_INT_DECL(void) CPUMR0LoadGuestDebugState(PVMCPU pVCpu, bool fDr6)
 {
     /*
@@ -660,5 +708,5 @@
     /*
      * Activate the guest state DR0-3.
-     * DR7 and DR6 (if fDR6 is true) are left to the caller.
+     * DR7 and DR6 (if fDr6 is true) are left to the caller.
      */
 #if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
@@ -676,5 +724,5 @@
         ASMSetDR3(pVCpu->cpum.s.Guest.dr[3]);
 #endif
-        if (fDR6)
+        if (fDr6)
             ASMSetDR6(pVCpu->cpum.s.Guest.dr[6]);
 
@@ -689,8 +737,8 @@
  * @returns VBox status code.
  * @param   pVCpu       The cross context CPU structure for the calling EMT.
- * @param   fDR6        Whether to include DR6 or not.
+ * @param   fDr6        Whether to include DR6 or not.
  * @thread  EMT(pVCpu)
  */
-VMMR0DECL(void) CPUMR0LoadHyperDebugState(PVMCPU pVCpu, bool fDR6)
+VMMR0_INT_DECL(void) CPUMR0LoadHyperDebugState(PVMCPU pVCpu, bool fDr6)
 {
     /*
@@ -707,5 +755,5 @@
     /*
      * Activate the guest state DR0-3.
-     * DR7 and DR6 (if fDR6 is true) are left to the caller.
+     * DR7 and DR6 (if fDr6 is true) are left to the caller.
      */
 #if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
@@ -723,5 +771,5 @@
         ASMSetDR3(pVCpu->cpum.s.Hyper.dr[3]);
 #endif
-        if (fDR6)
+        if (fDr6)
             ASMSetDR6(X86_DR6_INIT_VAL);
 
@@ -884,5 +932,5 @@
  * @param   idHostCpu   The ID of the current host CPU.
  */
-VMMR0DECL(void) CPUMR0SetLApic(PVM pVM, RTCPUID idHostCpu)
+VMMR0_INT_DECL(void) CPUMR0SetLApic(PVM pVM, RTCPUID idHostCpu)
 {
     pVM->cpum.s.pvApicBase = g_aLApics[RTMpCpuIdToSetIndex(idHostCpu)].pv;
Index: trunk/src/VBox/VMM/VMMR0/HMVMXR0.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR0/HMVMXR0.cpp	(revision 47680)
+++ trunk/src/VBox/VMM/VMMR0/HMVMXR0.cpp	(revision 47681)
@@ -9011,12 +9011,12 @@
     STAM_PROFILE_ADV_START(&pVCpu->hm.s.StatExitIO, y1);
 
-    int rc = hmR0VmxReadExitQualificationVmcs(pVCpu, pVmxTransient);
-    rc    |= hmR0VmxReadExitInstrLenVmcs(pVCpu, pVmxTransient);
-    rc    |= hmR0VmxSaveGuestRip(pVCpu, pMixedCtx);
-    rc    |= hmR0VmxSaveGuestRflags(pVCpu, pMixedCtx);         /* Eflag checks in EMInterpretDisasCurrent(). */
-    rc    |= hmR0VmxSaveGuestControlRegs(pVCpu, pMixedCtx);    /* CR0 checks & PGM* in EMInterpretDisasCurrent(). */
-    rc    |= hmR0VmxSaveGuestSegmentRegs(pVCpu, pMixedCtx);    /* SELM checks in EMInterpretDisasCurrent(). */
+    int rc2 = hmR0VmxReadExitQualificationVmcs(pVCpu, pVmxTransient);
+    rc2    |= hmR0VmxReadExitInstrLenVmcs(pVCpu, pVmxTransient);
+    rc2    |= hmR0VmxSaveGuestRip(pVCpu, pMixedCtx);
+    rc2    |= hmR0VmxSaveGuestRflags(pVCpu, pMixedCtx);         /* Eflag checks in EMInterpretDisasCurrent(). */
+    rc2    |= hmR0VmxSaveGuestControlRegs(pVCpu, pMixedCtx);    /* CR0 checks & PGM* in EMInterpretDisasCurrent(). */
+    rc2    |= hmR0VmxSaveGuestSegmentRegs(pVCpu, pMixedCtx);    /* SELM checks in EMInterpretDisasCurrent(). */
     /* EFER also required for longmode checks in EMInterpretDisasCurrent(), but it's always up-to-date. */
-    AssertRCReturn(rc, rc);
+    AssertRCReturn(rc2, rc2);
 
     /* Refer Intel spec. 27-5. "Exit Qualifications for I/O Instructions" for the format. */
@@ -9032,4 +9032,5 @@
     static const uint32_t s_aIOOpAnd[4] = { 0xff, 0xffff, 0, 0xffffffff };  /* AND masks for saving the result (in AL/AX/EAX). */
 
+    VBOXSTRICTRC   rcStrict;
     const uint32_t cbValue  = s_aIOSizes[uIOWidth];
     const uint32_t cbInstr  = pVmxTransient->cbInstr;
@@ -9049,8 +9050,8 @@
         if (MSR_IA32_VMX_BASIC_INFO_VMCS_INS_OUTS(pVM->hm.s.vmx.msr.vmx_basic_info))
         {
-            rc  = hmR0VmxReadExitIntrInfoVmcs(pVCpu, pVmxTransient);
+            rc2  = hmR0VmxReadExitIntrInfoVmcs(pVCpu, pVmxTransient);
             /** @todo optimize this, IEM should request the additional state if it needs it (GP, PF, ++). */
-            rc |= hmR0VmxSaveGuestState(pVCpu, pMixedCtx);
-            AssertRCReturn(rc, rc);
+            rc2 |= hmR0VmxSaveGuestState(pVCpu, pMixedCtx);
+            AssertRCReturn(rc2, rc2);
             AssertReturn(pVmxTransient->ExitInstrInfo.StrIo.u3AddrSize <= 2, VERR_HMVMX_IPE_3);
             AssertCompile(IEMMODE_16BIT == 0 && IEMMODE_32BIT == 1 && IEMMODE_64BIT == 2);
@@ -9059,7 +9060,7 @@
             if (fIOWrite)
             {
-                rc = IEMExecStringIoWrite(pVCpu, cbValue, enmAddrMode, fRep, cbInstr,
-                                          pVmxTransient->ExitInstrInfo.StrIo.iSegReg);
-                //if (rc == VINF_IOM_R3_IOPORT_WRITE)
+                rcStrict = IEMExecStringIoWrite(pVCpu, cbValue, enmAddrMode, fRep, cbInstr,
+                                                pVmxTransient->ExitInstrInfo.StrIo.iSegReg);
+                //if (rcStrict == VINF_IOM_R3_IOPORT_WRITE)
                 //    hmR0SavePendingIOPortWriteStr(pVCpu, pMixedCtx->rip, cbValue, enmAddrMode, fRep, cbInstr,
                 //                                  pVmxTransient->ExitInstrInfo.StrIo.iSegReg);
@@ -9070,6 +9071,6 @@
                                 ("%#x (%#llx)\n", pVmxTransient->ExitInstrInfo.StrIo.iSegReg, pVmxTransient->ExitInstrInfo.u),
                                 VERR_HMVMX_IPE_4);
-                rc = IEMExecStringIoRead(pVCpu, cbValue, enmAddrMode, fRep, cbInstr);
-                //if (rc == VINF_IOM_R3_IOPORT_READ)
+                rcStrict = IEMExecStringIoRead(pVCpu, cbValue, enmAddrMode, fRep, cbInstr);
+                //if (rcStrict == VINF_IOM_R3_IOPORT_READ)
                 //    hmR0SavePendingIOPortReadStr(pVCpu, pMixedCtx->rip, cbValue, enmAddrMode, fRep, cbInstr);
             }
@@ -9078,7 +9079,7 @@
         {
             /** @todo optimize this, IEM should request the additional state if it needs it (GP, PF, ++). */
-            rc = hmR0VmxSaveGuestState(pVCpu, pMixedCtx);
-            AssertRCReturn(rc, rc);
-            rc = IEMExecOne(pVCpu);
+            rc2 = hmR0VmxSaveGuestState(pVCpu, pMixedCtx);
+            AssertRCReturn(rc2, rc2);
+            rcStrict = IEMExecOne(pVCpu);
         }
         /** @todo IEM needs to be setting these flags somehow. */
@@ -9088,19 +9089,17 @@
 #else
         PDISCPUSTATE pDis = &pVCpu->hm.s.DisState;
-        rc = EMInterpretDisasCurrent(pVM, pVCpu, pDis, NULL);
-        if (RT_SUCCESS(rc))
+        rcStrict = EMInterpretDisasCurrent(pVM, pVCpu, pDis, NULL);
+        if (RT_SUCCESS(rcStrict))
         {
             if (fIOWrite)
             {
-                VBOXSTRICTRC rc2 = IOMInterpretOUTSEx(pVM, pVCpu, CPUMCTX2CORE(pMixedCtx), uIOPort, pDis->fPrefix,
-                                                      (DISCPUMODE)pDis->uAddrMode, cbValue);
-                rc = VBOXSTRICTRC_VAL(rc2);
+                rcStrict = IOMInterpretOUTSEx(pVM, pVCpu, CPUMCTX2CORE(pMixedCtx), uIOPort, pDis->fPrefix,
+                                              (DISCPUMODE)pDis->uAddrMode, cbValue);
                 STAM_COUNTER_INC(&pVCpu->hm.s.StatExitIOStringWrite);
             }
             else
             {
-                VBOXSTRICTRC rc2 = IOMInterpretINSEx(pVM, pVCpu, CPUMCTX2CORE(pMixedCtx), uIOPort, pDis->fPrefix,
-                                                     (DISCPUMODE)pDis->uAddrMode, cbValue);
-                rc = VBOXSTRICTRC_VAL(rc2);
+                rcStrict = IOMInterpretINSEx(pVM, pVCpu, CPUMCTX2CORE(pMixedCtx), uIOPort, pDis->fPrefix,
+                                             (DISCPUMODE)pDis->uAddrMode, cbValue);
                 STAM_COUNTER_INC(&pVCpu->hm.s.StatExitIOStringRead);
             }
@@ -9108,6 +9107,6 @@
         else
         {
-            AssertMsg(rc == VERR_EM_INTERPRETER, ("rc=%Rrc RIP %#RX64\n", rc, pMixedCtx->rip));
-            rc = VINF_EM_RAW_EMULATE_INSTR;
+            AssertMsg(rcStrict == VERR_EM_INTERPRETER, ("rcStrict=%Rrc RIP %#RX64\n", VBOXSTRICTRC_VAL(rcStrict), pMixedCtx->rip));
+            rcStrict = VINF_EM_RAW_EMULATE_INSTR;
         }
 #endif
@@ -9123,7 +9122,6 @@
         if (fIOWrite)
         {
-            VBOXSTRICTRC rc2 = IOMIOPortWrite(pVM, pVCpu, uIOPort, pMixedCtx->eax & uAndVal, cbValue);
-            rc = VBOXSTRICTRC_VAL(rc2);
-            if (rc == VINF_IOM_R3_IOPORT_WRITE)
+            rcStrict = IOMIOPortWrite(pVM, pVCpu, uIOPort, pMixedCtx->eax & uAndVal, cbValue);
+            if (rcStrict == VINF_IOM_R3_IOPORT_WRITE)
                 HMR0SavePendingIOPortWrite(pVCpu, pMixedCtx->rip, pMixedCtx->rip + cbInstr, uIOPort, uAndVal, cbValue);
             STAM_COUNTER_INC(&pVCpu->hm.s.StatExitIOWrite);
@@ -9132,12 +9130,11 @@
         {
             uint32_t u32Result = 0;
-            VBOXSTRICTRC rc2 = IOMIOPortRead(pVM, pVCpu, uIOPort, &u32Result, cbValue);
-            rc = VBOXSTRICTRC_VAL(rc2);
-            if (IOM_SUCCESS(rc))
+            rcStrict = IOMIOPortRead(pVM, pVCpu, uIOPort, &u32Result, cbValue);
+            if (IOM_SUCCESS(rcStrict))
             {
                 /* Save result of I/O IN instr. in AL/AX/EAX. */
                 pMixedCtx->eax = (pMixedCtx->eax & ~uAndVal) | (u32Result & uAndVal);
             }
-            else if (rc == VINF_IOM_R3_IOPORT_READ)
+            else if (rcStrict == VINF_IOM_R3_IOPORT_READ)
                 HMR0SavePendingIOPortRead(pVCpu, pMixedCtx->rip, pMixedCtx->rip + cbInstr, uIOPort, uAndVal, cbValue);
             STAM_COUNTER_INC(&pVCpu->hm.s.StatExitIORead);
@@ -9145,5 +9142,5 @@
     }
 
-    if (IOM_SUCCESS(rc))
+    if (IOM_SUCCESS(rcStrict))
     {
         if (!fUpdateRipAlready)
@@ -9152,100 +9149,65 @@
             pVCpu->hm.s.fContextUseFlags |= HM_CHANGED_GUEST_RIP;
         }
-        if (RT_LIKELY(rc == VINF_SUCCESS))
-        {
-            /*
-             * If any I/O breakpoints are armed, then we should check if a debug trap needs to be generated.
-             * Note that the I/O breakpoint type is undefined if CR4.DE is 0.
-             */
-            /** @todo We're not honoring I/O BPs if informational status code is returned.
-             *        We're also ignoring our own debugger's attempt at using I/O
-             *        breakpoints.  The whole host & guest debugger stuff needs to be
-             *        looked over at some point.  For now, it's just best effort. */
-            rc = hmR0VmxSaveGuestDR7(pVCpu, pMixedCtx);
-            AssertRCReturn(rc, rc);
-            uint32_t const uDr7 = pMixedCtx->dr[7];
-            if (   (uDr7 & X86_DR7_ENABLED_MASK)
+
+        /*
+         * If any I/O breakpoints are armed, we need to check if one triggered
+         * and take appropriate action.
+         * Note that the I/O breakpoint type is undefined if CR4.DE is 0.
+         */
+        /** @todo We're not honoring I/O BPs if informational status code is returned.
+         *        We're also ignoring our own debugger's attempt at using I/O
+         *        breakpoints.  The whole host & guest debugger stuff needs to be
+         *        looked over at some point.  For now, it's just best effort. */
+        int rc2 = hmR0VmxSaveGuestDR7(pVCpu, pMixedCtx);
+        AssertRCReturn(rc2, rc2);
+
+        uint32_t const uDr7 = pMixedCtx->dr[7];
+        if (   (   (uDr7 & X86_DR7_ENABLED_MASK)
                 && X86_DR7_ANY_RW_IO(uDr7)
-                && (pMixedCtx->cr4 & X86_CR4_DE) )
+                && (pMixedCtx->cr4 & X86_CR4_DE))
+            || DBGFBpIsHwIoArmed(pVM))
+        {
+            STAM_COUNTER_INC(&pVCpu->hm.s.StatDRxIoCheck);
+            bool fIsGuestDbgActive = CPUMR0DebugStateMaybeSaveGuest(pVCpu, true /*fDr6*/);
+
+            VBOXSTRICTRC rcStrict2 = DBGFBpCheckIo(pVM, pVCpu, pMixedCtx, uIOPort, cbValue);
+            if (rcStrict2 == VINF_EM_RAW_GUEST_TRAP)
             {
-                /** @todo We're a little late here if we're doing string I/O, as we're supposed
-                 *        to break after the each repetition.  Not sooo important, just for a
-                 *        rainy day. (Should probably refactor some of this code; after the uDr7
-                 *        detection let someone else handle it.) */
-                /** @todo The AMD is mumbling something that sounds like cbValue == cbBp.  The
-                 *        Intel manual describes it differently, data and I/O breakpoints are to
-                 *        be matched in the same way, probably.  Bochs does it that way. We've
-                 *        implemented it that way too, but it would be worth having a
-                 *        bootsector testcase for asserting the correct behavior (as well as
-                 *        correctness of this code). */
-                /** @todo r=bird: DR0-3 are normally in host registers when the guest is using
-                 *        them, so we're testing against potentially stale values here! */
-                STAM_COUNTER_INC(&pVCpu->hm.s.StatDRxIoCheck);
-                uint32_t uIOPortLast = uIOPort + cbValue - 1;
-                for (unsigned iBp = 0; iBp < 4; iBp++)
-                {
-                    if (   (uDr7 & X86_DR7_L_G(iBp))
-                        && X86_DR7_GET_RW(uDr7, iBp) == X86_DR7_RW_IO)
-                    {
-                        /* ASSUME the breakpoint and the I/O width qualifier uses the same encoding (1 2 x 4). */
-                        static uint8_t const s_abInvAlign[4] = { 0, 1, 7, 3 };
-                        uint8_t  cbInvAlign = s_abInvAlign[X86_DR7_GET_LEN(uDr7, iBp)];
-                        uint64_t uDrXFirst  = pMixedCtx->dr[iBp] & ~(uint64_t)cbInvAlign;
-                        uint64_t uDrXLast   = uDrXFirst + cbInvAlign;
-
-                        if (uDrXFirst <= uIOPortLast && uDrXLast >= uIOPort)
-                        {
-                            Assert(CPUMIsGuestDebugStateActive(pVCpu));
-                            uint64_t uDR6 = ASMGetDR6();
-
-                            /* Clear all breakpoint status flags and set the one we just hit. */
-                            uDR6 &= ~X86_DR6_B_MASK;
-                            uDR6 |= X86_DR6_B(iBp);
-
-                            /*
-                             * Note: AMD64 Architecture Programmer's Manual 13.1:
-                             * Bits 15:13 of the DR6 register is never cleared by the processor and must
-                             * be cleared by software after the contents have been read.
-                             */
-                            ASMSetDR6(uDR6);
-
-                            /* X86_DR7_GD will be cleared if DRx accesses should be trapped inside the guest. */
-                            pMixedCtx->dr[7] &= ~X86_DR7_GD;
-
-                            /* Paranoia. */
-                            pMixedCtx->dr[7] &= ~(X86_DR7_RAZ_MASK | X86_DR7_MBZ_MASK);
-                            pMixedCtx->dr[7] |= X86_DR7_RA1_MASK;
-
-                            /* Resync DR7 */
-                            /** @todo probably cheaper to just reload DR7, nothing else needs changing. */
-                            pVCpu->hm.s.fContextUseFlags |= HM_CHANGED_GUEST_DEBUG;
-
-                            /* Set #DB to be injected into the VM and continue guest execution. */
-                            hmR0VmxSetPendingXcptDB(pVCpu, pMixedCtx);
-                            break;
-                        }
-                    }
-                }
+                /* Raise #DB. */
+                if (fIsGuestDbgActive)
+                    ASMSetDR6(pMixedCtx->dr[6]);
+                if (pMixedCtx->dr[7] != uDr7)
+                    pVCpu->hm.s.fContextUseFlags |= HM_CHANGED_GUEST_DEBUG;
+
+                hmR0VmxSetPendingXcptDB(pVCpu, pMixedCtx);
             }
+            /* rcStrict is VINF_SUCCESS or in [VINF_EM_FIRST..VINF_EM_LAST]. */
+            else if (   rcStrict2 != VINF_SUCCESS
+                     && (rcStrict == VINF_SUCCESS || rcStrict2 < rcStrict))
+                rcStrict = rcStrict2;
         }
     }
 
 #ifdef DEBUG
-    if (rc == VINF_IOM_R3_IOPORT_READ)
+    if (rcStrict == VINF_IOM_R3_IOPORT_READ)
         Assert(!fIOWrite);
-    else if (rc == VINF_IOM_R3_IOPORT_WRITE)
+    else if (rcStrict == VINF_IOM_R3_IOPORT_WRITE)
         Assert(fIOWrite);
     else
     {
-        AssertMsg(   RT_FAILURE(rc)
-                  || rc == VINF_SUCCESS
-                  || rc == VINF_EM_RAW_EMULATE_INSTR
-                  || rc == VINF_EM_RAW_GUEST_TRAP
-                  || rc == VINF_TRPM_XCPT_DISPATCHED, ("%Rrc\n", rc));
+        /** @todo r=bird: This is missing a bunch of VINF_EM_FIRST..VINF_EM_LAST
+         *        statuses, that the VMM device and some others may return. See
+         *        IOM_SUCCESS() for guidance. */
+        AssertMsg(   RT_FAILURE(rcStrict)
+                  || rcStrict == VINF_SUCCESS
+                  || rcStrict == VINF_EM_RAW_EMULATE_INSTR
+                  || rcStrict == VINF_EM_DBG_BREAKPOINT
+                  || rcStrict == VINF_EM_RAW_GUEST_TRAP
+                  || rcStrict == VINF_TRPM_XCPT_DISPATCHED, ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
     }
 #endif
 
     STAM_PROFILE_ADV_STOP(&pVCpu->hm.s.StatExitIO, y1);
-    return rc;
+    return VBOXSTRICTRC_TODO(rcStrict);
 }
 
Index: trunk/src/VBox/VMM/VMMR3/HM.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR3/HM.cpp	(revision 47680)
+++ trunk/src/VBox/VMM/VMMR3/HM.cpp	(revision 47681)
@@ -2806,4 +2806,24 @@
     }
 
+    if (IOM_SUCCESS(rcStrict))
+    {
+        /*
+         * Check for I/O breakpoints.
+         */
+        uint32_t const uDr7 = pCtx->dr[7];
+        if (   (   (uDr7 & X86_DR7_ENABLED_MASK)
+                && X86_DR7_ANY_RW_IO(uDr7)
+                && (pCtx->cr4 & X86_CR4_DE))
+            || DBGFBpIsHwIoArmed(pVM))
+        {
+            VBOXSTRICTRC rcStrict2 = DBGFBpCheckIo(pVM, pVCpu, pCtx, pVCpu->hm.s.PendingIO.s.Port.uPort,
+                                                   pVCpu->hm.s.PendingIO.s.Port.cbSize);
+            if (rcStrict2 == VINF_EM_RAW_GUEST_TRAP)
+                rcStrict2 = TRPMAssertTrap(pVCpu, X86_XCPT_DB, TRPM_TRAP);
+            /* rcStrict is VINF_SUCCESS or in [VINF_EM_FIRST..VINF_EM_LAST]. */
+            else if (rcStrict2 != VINF_SUCCESS && (rcStrict == VINF_SUCCESS || rcStrict2 < rcStrict))
+                rcStrict = rcStrict2;
+        }
+    }
     return rcStrict;
 }
