Index: trunk/src/VBox/VMM/VMMR0/DBGFR0Bp.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR0/DBGFR0Bp.cpp	(revision 89911)
+++ trunk/src/VBox/VMM/VMMR0/DBGFR0Bp.cpp	(revision 89912)
@@ -75,7 +75,11 @@
     }
 
-    pGVM->dbgfr0.s.hMemObjBpLocL1 = NIL_RTR0MEMOBJ;
-    //pGVM->dbgfr0.s.paBpLocL1R0    = NULL;
-    //pGVM->dbgfr0.s.fInit          = false;
+    pGVM->dbgfr0.s.hMemObjBpLocL1     = NIL_RTR0MEMOBJ;
+    pGVM->dbgfr0.s.hMapObjBpLocL1     = NIL_RTR0MEMOBJ;
+    pGVM->dbgfr0.s.hMemObjBpLocPortIo = NIL_RTR0MEMOBJ;
+    pGVM->dbgfr0.s.hMapObjBpLocPortIo = NIL_RTR0MEMOBJ;
+    //pGVM->dbgfr0.s.paBpLocL1R0      = NULL;
+    //pGVM->dbgfr0.s.paBpLocPortIoR0  = NULL;
+    //pGVM->dbgfr0.s.fInit            = false;
 }
 
@@ -118,4 +122,19 @@
         RTR0MemObjFree(hMemObj, true);
 
+        if (pGVM->dbgfr0.s.paBpLocPortIoR0)
+        {
+            Assert(pGVM->dbgfr0.s.hMemObjBpLocPortIo != NIL_RTR0MEMOBJ);
+            Assert(pGVM->dbgfr0.s.hMapObjBpLocPortIo != NIL_RTR0MEMOBJ);
+
+            hMemObj = pGVM->dbgfr0.s.hMapObjBpLocPortIo;
+            pGVM->dbgfr0.s.hMapObjBpLocPortIo = NIL_RTR0MEMOBJ;
+            RTR0MemObjFree(hMemObj, true);
+
+            hMemObj = pGVM->dbgfr0.s.hMemObjBpLocPortIo;
+            pGVM->dbgfr0.s.hMemObjBpLocPortIo = NIL_RTR0MEMOBJ;
+            pGVM->dbgfr0.s.paBpLocPortIoR0    = NULL;
+            RTR0MemObjFree(hMemObj, true);
+        }
+
         for (uint32_t i = 0; i < RT_ELEMENTS(pGVM->dbgfr0.s.aBpChunks); i++)
         {
@@ -167,4 +186,7 @@
         Assert(!pGVM->dbgfr0.s.paBpLocL1R0);
 
+        Assert(pGVM->dbgfr0.s.hMemObjBpLocPortIo == NIL_RTR0MEMOBJ);
+        Assert(!pGVM->dbgfr0.s.paBpLocPortIoR0);
+
         for (uint32_t i = 0; i < RT_ELEMENTS(pGVM->dbgfr0.s.aBpChunks); i++)
         {
@@ -226,4 +248,47 @@
         *ppaBpLocL1R3 = RTR0MemObjAddressR3(hMapObj);
         pGVM->dbgfr0.s.fInit = true;
+        return rc;
+    }
+
+    RTR0MemObjFree(hMemObj, true);
+    return rc;
+}
+
+
+/**
+ * Worker for DBGFR0BpPortIoInitReqHandler() that does the actual initialization.
+ *
+ * @returns VBox status code.
+ * @param   pGVM                The global (ring-0) VM structure.
+ * @param   ppaBpLocPortIoR3    Where to return the ring-3 L1 lookup table address on success.
+ * @thread  EMT(0)
+ */
+static int dbgfR0BpPortIoInitWorker(PGVM pGVM, R3PTRTYPE(volatile uint32_t *) *ppaBpLocPortIoR3)
+{
+    /*
+     * Figure out how much memory we need for the I/O port breakpoint lookup table and allocate it.
+     */
+    uint32_t const cbPortIoLoc = RT_ALIGN_32(UINT16_MAX * sizeof(uint32_t), PAGE_SIZE);
+
+    RTR0MEMOBJ hMemObj;
+    int rc = RTR0MemObjAllocPage(&hMemObj, cbPortIoLoc, false /*fExecutable*/);
+    if (RT_FAILURE(rc))
+        return rc;
+    RT_BZERO(RTR0MemObjAddress(hMemObj), cbPortIoLoc);
+
+    /* Map it. */
+    RTR0MEMOBJ hMapObj;
+    rc = RTR0MemObjMapUserEx(&hMapObj, hMemObj, (RTR3PTR)-1, 0, RTMEM_PROT_READ | RTMEM_PROT_WRITE, RTR0ProcHandleSelf(),
+                             0 /*offSub*/, cbPortIoLoc);
+    if (RT_SUCCESS(rc))
+    {
+        pGVM->dbgfr0.s.hMemObjBpLocPortIo = hMemObj;
+        pGVM->dbgfr0.s.hMapObjBpLocPortIo = hMapObj;
+        pGVM->dbgfr0.s.paBpLocPortIoR0    = (volatile uint32_t *)RTR0MemObjAddress(hMemObj);
+
+        /*
+         * We're done.
+         */
+        *ppaBpLocPortIoR3 = RTR0MemObjAddressR3(hMapObj);
         return rc;
     }
@@ -412,5 +477,5 @@
 
 /**
- * Used by ring-3 DBGF to initialize the breakpoint owner table for operation.
+ * Used by ring-3 DBGF to initialize the breakpoint manager for port I/O breakpoint operation.
  *
  * @returns VBox status code.
@@ -419,7 +484,7 @@
  * @thread  EMT(0)
  */
-VMMR0_INT_DECL(int) DBGFR0BpOwnerInitReqHandler(PGVM pGVM, PDBGFBPOWNERINITREQ pReq)
-{
-    LogFlow(("DBGFR0BpOwnerInitReqHandler:\n"));
+VMMR0_INT_DECL(int) DBGFR0BpPortIoInitReqHandler(PGVM pGVM, PDBGFBPINITREQ pReq)
+{
+    LogFlow(("DBGFR0BpPortIoInitReqHandler:\n"));
 
     /*
@@ -431,12 +496,13 @@
     AssertRCReturn(rc, rc);
 
-    AssertReturn(!pGVM->dbgfr0.s.paBpOwnersR0, VERR_WRONG_ORDER);
-
-    return dbgfR0BpOwnerInitWorker(pGVM, &pReq->paBpOwnerR3);
-}
-
-
-/**
- * Used by ring-3 DBGF to allocate a given chunk in the global breakpoint table.
+    AssertReturn(!pGVM->dbgfr0.s.fInit, VERR_WRONG_ORDER);
+    AssertReturn(!pGVM->dbgfr0.s.paBpLocPortIoR0, VERR_WRONG_ORDER);
+
+    return dbgfR0BpPortIoInitWorker(pGVM, &pReq->paBpLocL1R3);
+}
+
+
+/**
+ * Used by ring-3 DBGF to initialize the breakpoint owner table for operation.
  *
  * @returns VBox status code.
@@ -445,4 +511,30 @@
  * @thread  EMT(0)
  */
+VMMR0_INT_DECL(int) DBGFR0BpOwnerInitReqHandler(PGVM pGVM, PDBGFBPOWNERINITREQ pReq)
+{
+    LogFlow(("DBGFR0BpOwnerInitReqHandler:\n"));
+
+    /*
+     * Validate the request.
+     */
+    AssertReturn(pReq->Hdr.cbReq == sizeof(*pReq), VERR_INVALID_PARAMETER);
+
+    int rc = GVMMR0ValidateGVMandEMT(pGVM, 0);
+    AssertRCReturn(rc, rc);
+
+    AssertReturn(!pGVM->dbgfr0.s.paBpOwnersR0, VERR_WRONG_ORDER);
+
+    return dbgfR0BpOwnerInitWorker(pGVM, &pReq->paBpOwnerR3);
+}
+
+
+/**
+ * Used by ring-3 DBGF to allocate a given chunk in the global breakpoint table.
+ *
+ * @returns VBox status code.
+ * @param   pGVM    The global (ring-0) VM structure.
+ * @param   pReq    Pointer to the request buffer.
+ * @thread  EMT(0)
+ */
 VMMR0_INT_DECL(int) DBGFR0BpChunkAllocReqHandler(PGVM pGVM, PDBGFBPCHUNKALLOCREQ pReq)
 {
Index: trunk/src/VBox/VMM/VMMR0/VMMR0.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR0/VMMR0.cpp	(revision 89911)
+++ trunk/src/VBox/VMM/VMMR0/VMMR0.cpp	(revision 89912)
@@ -2379,4 +2379,14 @@
         }
 
+        case VMMR0_DO_DBGF_BP_PORTIO_INIT:
+        {
+            if (!pReqHdr || u64Arg || idCpu != 0)
+                return VERR_INVALID_PARAMETER;
+            rc = DBGFR0BpPortIoInitReqHandler(pGVM, (PDBGFBPINITREQ)pReqHdr);
+            VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
+            break;
+        }
+
+
         /*
          * TM requests.
Index: trunk/src/VBox/VMM/VMMR3/DBGFR3Bp.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR3/DBGFR3Bp.cpp	(revision 89911)
+++ trunk/src/VBox/VMM/VMMR3/DBGFR3Bp.cpp	(revision 89912)
@@ -113,4 +113,13 @@
  *        Each entry in the L2 table is 16 bytes big and densly packed to avoid excessive memory usage.
  *
+ * @section sec_dbgf_bp_ioport  Handling I/O port breakpoints
+ *
+ * Because of the limited amount of I/O ports being available (65536) a single table with 65536 entries,
+ * each 4 byte big will be allocated. This amounts to 256KiB of memory being used additionally as soon as
+ * an I/O breakpoint is enabled. The entries contain the breakpoint handle directly allowing only one breakpoint
+ * per port right now, which is something we accept as a limitation right now to keep things relatively simple.
+ * When there is at least one I/O breakpoint active IOM will be notified and it will afterwards call the DBGF API
+ * whenever the guest does an I/O port access to decide whether a breakpoint was hit. This keeps the overhead small
+ * when there is no I/O port breakpoint enabled.
  *
  * @section sec_dbgf_bp_note    Random thoughts and notes for the implementation
@@ -217,5 +226,6 @@
     }
 
-    //pUVM->dbgf.s.paBpLocL1R3 = NULL;
+    //pUVM->dbgf.s.paBpLocL1R3     = NULL;
+    //pUVM->dbgf.s.paBpLocPortIoR3 = NULL;
     pUVM->dbgf.s.hMtxBpL2Wr = NIL_RTSEMFASTMUTEX;
     return RTSemFastMutexCreate(&pUVM->dbgf.s.hMtxBpL2Wr);
@@ -324,4 +334,62 @@
     /* Gather all EMTs and call into ring-0 to initialize the breakpoint manager. */
     return VMMR3EmtRendezvous(pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpInitEmtWorker, NULL /*pvUser*/);
+}
+
+
+/**
+ * @callback_method_impl{FNVMMEMTRENDEZVOUS}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpPortIoInitEmtWorker(PVM pVM, PVMCPU pVCpu, void *pvUser)
+{
+    RT_NOREF(pvUser);
+
+    VMCPU_ASSERT_EMT(pVCpu);
+    VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
+
+    /*
+     * The initialization will be done on EMT(0). It is possible that multiple
+     * initialization attempts are done because dbgfR3BpPortIoEnsureInit() can be called
+     * from racing non EMT threads when trying to set a breakpoint for the first time.
+     * Just fake success if the L1 is already present which means that a previous rendezvous
+     * successfully initialized the breakpoint manager.
+     */
+    PUVM pUVM = pVM->pUVM;
+    if (   pVCpu->idCpu == 0
+        && !pUVM->dbgf.s.paBpLocPortIoR3)
+    {
+        DBGFBPINITREQ Req;
+        Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+        Req.Hdr.cbReq    = sizeof(Req);
+        Req.paBpLocL1R3  = NULL;
+        int rc = VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_DBGF_BP_PORTIO_INIT, 0 /*u64Arg*/, &Req.Hdr);
+        AssertLogRelMsgRCReturn(rc, ("VMMR0_DO_DBGF_BP_PORTIO_INIT failed: %Rrc\n", rc), rc);
+        pUVM->dbgf.s.paBpLocPortIoR3 = Req.paBpLocL1R3;
+    }
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Ensures that the breakpoint manager is initialized to handle I/O port breakpoint.
+ *
+ * @returns VBox status code.
+ * @param   pUVM                The user mode VM handle.
+ *
+ * @thread Any thread.
+ */
+static int dbgfR3BpPortIoEnsureInit(PUVM pUVM)
+{
+    /* If the L1 lookup table is allocated initialization succeeded before. */
+    if (RT_LIKELY(pUVM->dbgf.s.paBpLocPortIoR3))
+        return VINF_SUCCESS;
+
+    /* Ensure that the breakpoint manager is initialized. */
+    int rc = dbgfR3BpEnsureInit(pUVM);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    /* Gather all EMTs and call into ring-0 to initialize the breakpoint manager. */
+    return VMMR3EmtRendezvous(pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpPortIoInitEmtWorker, NULL /*pvUser*/);
 }
 
@@ -1358,4 +1426,38 @@
 
 /**
+ * Adds the given port I/O breakpoint to the appropriate lookup tables.
+ *
+ * @returns VBox status code.
+ * @param   pUVM                The user mode VM handle.
+ * @param   hBp                 The breakpoint handle to add.
+ * @param   pBp                 The internal breakpoint state.
+ */
+static int dbgfR3BpPortIoAdd(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
+{
+    AssertReturn(DBGF_BP_PUB_GET_TYPE(&pBp->Pub) == DBGFBPTYPE_PORT_IO, VERR_DBGF_BP_IPE_3);
+
+    uint16_t uPortExcl = pBp->Pub.u.PortIo.uPort + pBp->Pub.u.PortIo.cPorts;
+    uint32_t u32Entry  = DBGF_BP_INT3_L1_ENTRY_CREATE_BP_HND(hBp);
+    for (uint16_t idxPort = pBp->Pub.u.PortIo.uPort; idxPort < uPortExcl; idxPort++)
+    {
+        bool fXchg = ASMAtomicCmpXchgU32(&pUVM->dbgf.s.paBpLocPortIoR3[idxPort], u32Entry, DBGF_BP_INT3_L1_ENTRY_TYPE_NULL);
+        if (!fXchg)
+        {
+            /* Something raced us, so roll back the other registrations. */
+            while (idxPort > pBp->Pub.u.PortIo.uPort)
+            {
+                fXchg = ASMAtomicCmpXchgU32(&pUVM->dbgf.s.paBpLocPortIoR3[idxPort], DBGF_BP_INT3_L1_ENTRY_TYPE_NULL, u32Entry);
+                Assert(fXchg); RT_NOREF(fXchg);
+            }
+
+            return VERR_DBGF_BP_INT3_ADD_TRIES_REACHED; /** @todo New status code */
+        }
+    }
+
+    return VINF_SUCCESS;
+}
+
+
+/**
  * Get a breakpoint give by address.
  *
@@ -1438,4 +1540,34 @@
             AssertMsgFailed(("enmType=%d\n", enmType));
             break;
+    }
+
+    if (   hBp != NIL_DBGFBP
+        && ppBp)
+        *ppBp =  dbgfR3BpGetByHnd(pUVM, hBp);
+    return hBp;
+}
+
+
+/**
+ * Get a port I/O breakpoint given by the range.
+ *
+ * @returns The breakpoint handle on success or NIL_DBGF if not found.
+ * @param   pUVM                The user mode VM handle.
+ * @param   uPort               First port in the range.
+ * @param   cPorts              Number of ports in the range.
+ * @param   ppBp                Where to store the pointer to the internal breakpoint state on success, optional.
+ */
+static DBGFBP dbgfR3BpPortIoGetByRange(PUVM pUVM, RTIOPORT uPort, RTIOPORT cPorts, PDBGFBPINT *ppBp)
+{
+    DBGFBP hBp = NIL_DBGFBP;
+
+    for (RTIOPORT idxPort = uPort; idxPort < uPort + cPorts; idxPort)
+    {
+        const uint32_t u32Entry = ASMAtomicReadU32(&pUVM->dbgf.s.CTX_SUFF(paBpLocPortIo)[idxPort]);
+        if (u32Entry != DBGF_BP_INT3_L1_ENTRY_TYPE_NULL)
+        {
+            hBp = DBGF_BP_INT3_L1_ENTRY_GET_BP_HND(u32Entry);
+            break;
+        }
     }
 
@@ -1517,4 +1649,63 @@
      */
     return VMMR3EmtRendezvous(pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpInt3RemoveEmtWorker, (void *)(uintptr_t)hBp);
+}
+
+
+/**
+ * @callback_method_impl{FNVMMEMTRENDEZVOUS}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpPortIoRemoveEmtWorker(PVM pVM, PVMCPU pVCpu, void *pvUser)
+{
+    DBGFBP hBp = (DBGFBP)(uintptr_t)pvUser;
+
+    VMCPU_ASSERT_EMT(pVCpu);
+    VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
+
+    PUVM pUVM = pVM->pUVM;
+    PDBGFBPINT pBp = dbgfR3BpGetByHnd(pUVM, hBp);
+    AssertPtrReturn(pBp, VERR_DBGF_BP_IPE_8);
+
+    int rc = VINF_SUCCESS;
+    if (pVCpu->idCpu == 0)
+    {
+        /*
+         * Remove the whole range, there shouldn't be any other breakpoint configured for this range as this is not
+         * allowed right now.
+         */
+        uint16_t uPortExcl = pBp->Pub.u.PortIo.uPort + pBp->Pub.u.PortIo.cPorts;
+        for (uint16_t idxPort = pBp->Pub.u.PortIo.uPort; idxPort < uPortExcl; idxPort++)
+        {
+            uint32_t u32Entry = ASMAtomicReadU32(&pUVM->dbgf.s.paBpLocPortIoR3[idxPort]);
+            AssertReturn(u32Entry != DBGF_BP_INT3_L1_ENTRY_TYPE_NULL, VERR_DBGF_BP_IPE_6);
+
+            uint8_t u8Type = DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32Entry);
+            AssertReturn(u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_BP_HND, VERR_DBGF_BP_IPE_7);
+
+            bool fXchg = ASMAtomicCmpXchgU32(&pUVM->dbgf.s.paBpLocL1R3[idxPort], DBGF_BP_INT3_L1_ENTRY_TYPE_NULL, u32Entry);
+            Assert(fXchg); RT_NOREF(fXchg);
+        }
+    }
+
+    return rc;
+}
+
+
+/**
+ * Removes the given port I/O breakpoint from all lookup tables.
+ *
+ * @returns VBox status code.
+ * @param   pUVM                The user mode VM handle.
+ * @param   hBp                 The breakpoint handle to remove.
+ * @param   pBp                 The internal breakpoint state.
+ */
+static int dbgfR3BpPortIoRemove(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
+{
+    AssertReturn(DBGF_BP_PUB_GET_TYPE(&pBp->Pub) == DBGFBPTYPE_PORT_IO, VERR_DBGF_BP_IPE_3);
+
+    /*
+     * This has to be done by an EMT rendezvous in order to not have an EMT accessing
+     * the breakpoint while it is removed.
+     */
+    return VMMR3EmtRendezvous(pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpPortIoRemoveEmtWorker, (void *)(uintptr_t)hBp);
 }
 
@@ -2063,5 +2254,5 @@
 {
     return DBGFR3BpSetPortIoEx(pUVM, NIL_DBGFBPOWNER, NULL /*pvUser*/, uPort, cPorts,
-                               fAccess, iHitTrigger, iHitDisable, phBp);
+                               DBGF_BP_F_DEFAULT, fAccess, iHitTrigger, iHitDisable, phBp);
 }
 
@@ -2077,4 +2268,5 @@
  * @param   cPorts          The number of I/O ports, see DBGFBPIOACCESS_XXX.
  * @param   fAccess         The access we want to break on.
+ * @param   fFlags          Combination of DBGF_BP_F_XXX.
  * @param   iHitTrigger     The hit count at which the breakpoint start
  *                          triggering. Use 0 (or 1) if it's gonna trigger at
@@ -2088,5 +2280,5 @@
 VMMR3DECL(int) DBGFR3BpSetPortIoEx(PUVM pUVM, DBGFBPOWNER hOwner, void *pvUser,
                                    RTIOPORT uPort, RTIOPORT cPorts, uint32_t fAccess,
-                                   uint64_t iHitTrigger, uint64_t iHitDisable, PDBGFBP phBp)
+                                   uint32_t fFlags, uint64_t iHitTrigger, uint64_t iHitDisable, PDBGFBP phBp)
 {
     UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
@@ -2099,8 +2291,53 @@
     AssertReturn((RTIOPORT)(uPort + cPorts) < uPort, VERR_OUT_OF_RANGE);
 
-    int rc = dbgfR3BpEnsureInit(pUVM);
+    int rc = dbgfR3BpPortIoEnsureInit(pUVM);
     AssertRCReturn(rc, rc);
 
-    return VERR_NOT_IMPLEMENTED;
+    PDBGFBPINT pBp = NULL;
+    DBGFBP hBp = dbgfR3BpPortIoGetByRange(pUVM, uPort, cPorts, &pBp);
+    if (   hBp != NIL_DBGFBP
+        && pBp->Pub.u.PortIo.uPort == uPort
+        && pBp->Pub.u.PortIo.cPorts == cPorts
+        && pBp->Pub.u.PortIo.fAccess == fAccess)
+    {
+        rc = VINF_SUCCESS;
+        if (!DBGF_BP_PUB_IS_ENABLED(&pBp->Pub))
+            rc = dbgfR3BpArm(pUVM, hBp, pBp);
+        if (RT_SUCCESS(rc))
+        {
+            rc = VINF_DBGF_BP_ALREADY_EXIST;
+            if (phBp)
+                *phBp = hBp;
+        }
+        return rc;
+    }
+
+    rc = dbgfR3BpAlloc(pUVM, hOwner, pvUser, DBGFBPTYPE_PORT_IO, fFlags, iHitTrigger, iHitDisable, &hBp, &pBp);
+    if (RT_SUCCESS(rc))
+    {
+        pBp->Pub.u.PortIo.uPort   = uPort;
+        pBp->Pub.u.PortIo.cPorts  = cPorts;
+        pBp->Pub.u.PortIo.fAccess = fAccess;
+
+        /* Add the breakpoint to the lookup tables. */
+        rc = dbgfR3BpPortIoAdd(pUVM, hBp, pBp);
+        if (RT_SUCCESS(rc))
+        {
+            /* Enable the breakpoint if requested. */
+            if (fFlags & DBGF_BP_F_ENABLED)
+                rc = dbgfR3BpArm(pUVM, hBp, pBp);
+            if (RT_SUCCESS(rc))
+            {
+                *phBp = hBp;
+                return VINF_SUCCESS;
+            }
+
+            int rc2 = dbgfR3BpPortIoRemove(pUVM, hBp, pBp); AssertRC(rc2);
+        }
+
+        dbgfR3BpFree(pUVM, hBp, pBp);
+    }
+
+    return rc;
 }
 
@@ -2205,4 +2442,10 @@
         {
             int rc = dbgfR3BpInt3Remove(pUVM, hBp, pBp);
+            AssertRC(rc);
+            break;
+        }
+        case DBGFBPTYPE_PORT_IO:
+        {
+            int rc = dbgfR3BpPortIoRemove(pUVM, hBp, pBp);
             AssertRC(rc);
             break;
Index: trunk/src/VBox/VMM/include/DBGFInternal.h
===================================================================
--- trunk/src/VBox/VMM/include/DBGFInternal.h	(revision 89911)
+++ trunk/src/VBox/VMM/include/DBGFInternal.h	(revision 89912)
@@ -1288,6 +1288,12 @@
     /** The L1 lookup tables mapping object. */
     RTR0MEMOBJ                          hMapObjBpLocL1;
+    /** The I/O port breakpoint lookup tables memory object. */
+    RTR0MEMOBJ                          hMemObjBpLocPortIo;
+    /** The I/O port breakpoint lookup tables mapping object. */
+    RTR0MEMOBJ                          hMapObjBpLocPortIo;
     /** Base pointer to the L1 locator table. */
     R0PTRTYPE(volatile uint32_t *)      paBpLocL1R0;
+    /** Base pointer to the L1 locator table. */
+    R0PTRTYPE(volatile uint32_t *)      paBpLocPortIoR0;
     /** Flag whether the breakpoint manager was initialized (on demand). */
     bool                                fInit;
@@ -1380,4 +1386,6 @@
     /** Base pointer to the L1 locator table. */
     R3PTRTYPE(volatile uint32_t *)  paBpLocL1R3;
+    /** Base pointer to the Port I/O breakpoint locator table. */
+    R3PTRTYPE(volatile uint32_t *)  paBpLocPortIoR3;
     /** Fast mutex protecting the L2 table from concurrent write accesses (EMTs
      * can still do read accesses without holding it while traversing the trees). */
