Index: /trunk/include/VBox/err.h
===================================================================
--- /trunk/include/VBox/err.h	(revision 80640)
+++ /trunk/include/VBox/err.h	(revision 80641)
@@ -1145,4 +1145,8 @@
  * some other VINF_EM_XXX statuses. */
 #define VINF_IOM_R3_MMIO_COMMIT_WRITE       2626
+/** Too many MMIO range registrations. */
+#define VERR_IOM_TOO_MANY_MMIO_REGISTRATIONS (-2627)
+/** Invalid MMIO handle. */
+#define VERR_IOM_INVALID_MMIO_HANDLE        (-2628)
 
 /** IOMGCIOPortHandler was given an unexpected opcode. */
@@ -1164,4 +1168,13 @@
 /** Internal processing error while merging status codes. */
 #define VERR_IOM_FF_STATUS_IPE              (-2638)
+
+/** Too many I/O port registrations. */
+#define VERR_IOM_TOO_MANY_IOPORT_REGISTRATIONS (-2650)
+/** Invalid I/O port handle. */
+#define VERR_IOM_INVALID_IOPORT_HANDLE      (-2651)
+/** I/O ports are already mapped. */
+#define VERR_IOM_IOPORTS_ALREADY_MAPPED     (-2652)
+/** I/O ports are not mapped. */
+#define VERR_IOM_IOPORTS_NOT_MAPPED         (-2653)
 /** @} */
 
Index: /trunk/include/VBox/vmm/gvm.h
===================================================================
--- /trunk/include/VBox/vmm/gvm.h	(revision 80640)
+++ /trunk/include/VBox/vmm/gvm.h	(revision 80641)
@@ -205,9 +205,17 @@
     } pdmr0;
 
+    union
+    {
+#if defined(VMM_INCLUDED_SRC_include_IOMInternal_h) && defined(IN_RING0)
+        struct IOMR0PERVM   s;
+#endif
+        uint8_t             padding[256];
+    } iomr0;
+
     /** Padding so aCpus starts on a page boundrary.  */
 #ifdef VBOX_WITH_NEM_R0
-    uint8_t         abPadding2[4096 - 64 - 256 - 512 - 256 - 64 - 1536 - sizeof(PGVMCPU) * VMM_MAX_CPU_COUNT];
-#else
-    uint8_t         abPadding2[4096 - 64 - 256 - 512       - 64 - 1536 - sizeof(PGVMCPU) * VMM_MAX_CPU_COUNT];
+    uint8_t         abPadding2[4096 - 64 - 256 - 512 - 256 - 64 - 1536 - 256 - sizeof(PGVMCPU) * VMM_MAX_CPU_COUNT];
+#else
+    uint8_t         abPadding2[4096 - 64 - 256 - 512       - 64 - 1536 - 256 - sizeof(PGVMCPU) * VMM_MAX_CPU_COUNT];
 #endif
 
Index: /trunk/include/VBox/vmm/iom.h
===================================================================
--- /trunk/include/VBox/vmm/iom.h	(revision 80640)
+++ /trunk/include/VBox/vmm/iom.h	(revision 80641)
@@ -293,12 +293,10 @@
 typedef FNIOMMMIOFILL *PFNIOMMMIOFILL;
 
-VMMDECL(VBOXSTRICTRC)   IOMIOPortRead(PVM pVM, PVMCPU pVCpu, RTIOPORT Port, uint32_t *pu32Value, size_t cbValue);
-VMMDECL(VBOXSTRICTRC)   IOMIOPortWrite(PVM pVM, PVMCPU pVCpu, RTIOPORT Port, uint32_t u32Value, size_t cbValue);
-VMM_INT_DECL(VBOXSTRICTRC) IOMIOPortReadString(PVM pVM, PVMCPU pVCpu, RTIOPORT Port, void *pvDst,
+VMMDECL(VBOXSTRICTRC)   IOMIOPortRead(PVMCC pVM, PVMCPU pVCpu, RTIOPORT Port, uint32_t *pu32Value, size_t cbValue);
+VMMDECL(VBOXSTRICTRC)   IOMIOPortWrite(PVMCC pVM, PVMCPU pVCpu, RTIOPORT Port, uint32_t u32Value, size_t cbValue);
+VMM_INT_DECL(VBOXSTRICTRC) IOMIOPortReadString(PVMCC pVM, PVMCPU pVCpu, RTIOPORT Port, void *pvDst,
                                                uint32_t *pcTransfers, unsigned cb);
-VMM_INT_DECL(VBOXSTRICTRC) IOMIOPortWriteString(PVM pVM, PVMCPU pVCpu, RTIOPORT uPort, void const *pvSrc,
+VMM_INT_DECL(VBOXSTRICTRC) IOMIOPortWriteString(PVMCC pVM, PVMCPU pVCpu, RTIOPORT uPort, void const *pvSrc,
                                                 uint32_t *pcTransfers, unsigned cb);
-VMMDECL(VBOXSTRICTRC)   IOMInterpretINSEx(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, uint32_t uPort, uint32_t uPrefix, DISCPUMODE enmAddrMode, uint32_t cbTransfer);
-VMMDECL(VBOXSTRICTRC)   IOMInterpretOUTSEx(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, uint32_t uPort, uint32_t uPrefix, DISCPUMODE enmAddrMode, uint32_t cbTransfer);
 VMMDECL(VBOXSTRICTRC)   IOMMMIORead(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, uint32_t *pu32Value, size_t cbValue);
 VMMDECL(VBOXSTRICTRC)   IOMMMIOWrite(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, uint32_t u32Value, size_t cbValue);
@@ -318,4 +316,12 @@
 VMMR3_INT_DECL(void) IOMR3Relocate(PVM pVM, RTGCINTPTR offDelta);
 VMMR3_INT_DECL(int)  IOMR3Term(PVM pVM);
+
+VMMR3_INT_DECL(int)  IOMR3IoPortCreate(PVM pVM, PPDMDEVINS pDevIns, RTIOPORT cPorts, uint32_t fFlags, PPDMPCIDEV pPciDev,
+                                       uint32_t iPciRegion, PFNIOMIOPORTOUT pfnOut, PFNIOMIOPORTIN pfnIn,
+                                       PFNIOMIOPORTOUTSTRING pfnOutStr, PFNIOMIOPORTINSTRING pfnInStr, RTR3PTR pvUser,
+                                       const char *pszDesc, PIOMIOPORTHANDLE phIoPorts);
+VMMR3_INT_DECL(int)  IOMR3IoPortMap(PVM pVM, PPDMDEVINS pDevIns, IOMIOPORTHANDLE hIoPorts, RTIOPORT Port);
+VMMR3_INT_DECL(int)  IOMR3IoPortUnmap(PVM pVM, PPDMDEVINS pDevIns, IOMIOPORTHANDLE hIoPorts);
+
 VMMR3_INT_DECL(int)  IOMR3IOPortRegisterR3(PVM pVM, PPDMDEVINS pDevIns, RTIOPORT PortStart, RTUINT cPorts, RTHCPTR pvUser,
                                            R3PTRTYPE(PFNIOMIOPORTOUT) pfnOutCallback, R3PTRTYPE(PFNIOMIOPORTIN) pfnInCallback,
@@ -377,5 +383,20 @@
 
 
+#if defined(IN_RING0) || defined(DOXYGEN_RUNNING)
+/** @defgroup grpm_iom_r0   The IOM Host Context Ring-0 API
+ * @{ */
+VMMR0_INT_DECL(void) IOMR0InitPerVMData(PGVM pGVM);
+VMMR0_INT_DECL(void) IOMR0CleanupVM(PGVM pGVM);
+VMMR0_INT_DECL(int)  IOMR0IoPortSetUpContext(PGVM pGVM, PPDMDEVINS pDevIns, IOMIOPORTHANDLE hIoPorts,
+                                             PFNIOMIOPORTOUT pfnOut,  PFNIOMIOPORTIN pfnIn,
+                                             PFNIOMIOPORTOUTSTRING pfnOutStr, PFNIOMIOPORTINSTRING pfnInStr, void *pvUser);
+VMMR0_INT_DECL(int)  IOMR0IoPortGrowRegistrationTables(PGVM pGVM, uint64_t cMinEntries);
+VMMR0_INT_DECL(int)  IOMR0IoPortGrowStatisticsTable(PGVM pGVM, uint64_t cMinEntries);
+
+
 /** @} */
+#endif /* IN_RING0 || DOXYGEN_RUNNING */
+
+/** @} */
 
 RT_C_DECLS_END
Index: /trunk/include/VBox/vmm/pdmdev.h
===================================================================
--- /trunk/include/VBox/vmm/pdmdev.h	(revision 80640)
+++ /trunk/include/VBox/vmm/pdmdev.h	(revision 80641)
@@ -5370,8 +5370,8 @@
  */
 DECLINLINE(int) PDMDevHlpIoPortCreateAndMap(PPDMDEVINS pDevIns, RTIOPORT Port, RTIOPORT cPorts, PFNIOMIOPORTOUT pfnOut,
-                                            PFNIOMIOPORTIN pfnIn, void *pvUser, const char *pszDesc, PIOMIOPORTHANDLE phIoPorts)
+                                            PFNIOMIOPORTIN pfnIn, const char *pszDesc, PIOMIOPORTHANDLE phIoPorts)
 {
     int rc = pDevIns->pHlpR3->pfnIoPortCreateEx(pDevIns, cPorts, 0, NULL, UINT32_MAX,
-                                                pfnOut, pfnIn, NULL, NULL, pvUser, pszDesc, phIoPorts);
+                                                pfnOut, pfnIn, NULL, NULL, NULL, pszDesc, phIoPorts);
     if (RT_SUCCESS(rc))
         rc = pDevIns->pHlpR3->pfnIoPortMap(pDevIns, *phIoPorts, Port);
Index: /trunk/include/VBox/vmm/vm.h
===================================================================
--- /trunk/include/VBox/vmm/vm.h	(revision 80640)
+++ /trunk/include/VBox/vmm/vm.h	(revision 80641)
@@ -1007,5 +1007,9 @@
  * it isn't.
  */
-#define VM_ASSERT_EMT0_RETURN(pVM, rc)      VMCPU_ASSERT_EMT_RETURN(&(pVM)->aCpus[0], (rc))
+#ifdef IN_RING3
+# define VM_ASSERT_EMT0_RETURN(pVM, rc)     VMCPU_ASSERT_EMT_RETURN((pVM)->apCpusR3[0], (rc))
+#else
+# define VM_ASSERT_EMT0_RETURN(pVM, rc)     VMCPU_ASSERT_EMT_RETURN(&(pVM)->aCpus[0], (rc))
+#endif
 
 
@@ -1323,5 +1327,5 @@
         struct IOM s;
 #endif
-        uint8_t     padding[896];       /* multiple of 64 */
+        uint8_t     padding[960];       /* multiple of 64 */
     } iom;
 
@@ -1447,7 +1451,7 @@
     /** Padding for aligning the structure size on a page boundrary. */
 #ifdef VBOX_WITH_REM
-    uint8_t         abAlignment2[3032       - sizeof(PVMCPUR3) * VMM_MAX_CPU_COUNT];
-#else
-    uint8_t         abAlignment2[3032 + 256 - sizeof(PVMCPUR3) * VMM_MAX_CPU_COUNT];
+    uint8_t         abAlignment2[2968       - sizeof(PVMCPUR3) * VMM_MAX_CPU_COUNT];
+#else
+    uint8_t         abAlignment2[2968 + 256 - sizeof(PVMCPUR3) * VMM_MAX_CPU_COUNT];
 #endif
 
Index: /trunk/include/VBox/vmm/vm.mac
===================================================================
--- /trunk/include/VBox/vmm/vm.mac	(revision 80640)
+++ /trunk/include/VBox/vmm/vm.mac	(revision 80641)
@@ -127,5 +127,5 @@
     .mm                     resb 192
     .pdm                    resb 1920
-    .iom                    resb 896
+    .iom                    resb 960
     .em                     resb 256
     .nem                    resb 128
@@ -142,7 +142,7 @@
 
 %ifdef VBOX_WITH_REM
-    .abAlignment2           resb 3032       - RTR0PTR_CB * VMM_MAX_CPU_COUNT
+    .abAlignment2           resb 2968       - RTR0PTR_CB * VMM_MAX_CPU_COUNT
 %else
-    .abAlignment2           resb 3032 + 256 - RTR0PTR_CB * VMM_MAX_CPU_COUNT
+    .abAlignment2           resb 2968 + 256 - RTR0PTR_CB * VMM_MAX_CPU_COUNT
 %endif
 
Index: /trunk/include/VBox/vmm/vmm.h
===================================================================
--- /trunk/include/VBox/vmm/vmm.h	(revision 80640)
+++ /trunk/include/VBox/vmm/vmm.h	(revision 80641)
@@ -415,6 +415,11 @@
     VMMR0_DO_NEM_EXPERIMENT,
 
+    /** Grow the I/O port registration tables. */
+    VMMR0_DO_IOM_GROW_IO_PORTS = 640,
+    /** Grow the I/O port statistics tables. */
+    VMMR0_DO_IOM_GROW_IO_PORT_STATS,
+
     /** Official call we use for testing Ring-0 APIs. */
-    VMMR0_DO_TESTS = 640,
+    VMMR0_DO_TESTS = 704,
 
     /** The usual 32-bit type blow up. */
Index: /trunk/src/VBox/VMM/Makefile.kmk
===================================================================
--- /trunk/src/VBox/VMM/Makefile.kmk	(revision 80640)
+++ /trunk/src/VBox/VMM/Makefile.kmk	(revision 80641)
@@ -484,4 +484,5 @@
 	VMMR0/HMVMXR0.cpp \
 	VMMR0/HMSVMR0.cpp \
+	VMMR0/IOMR0.cpp \
 	VMMR0/PDMR0Device.cpp \
 	VMMR0/PDMR0Driver.cpp \
Index: /trunk/src/VBox/VMM/VMMAll/IEMAllCImplStrInstr.cpp.h
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/IEMAllCImplStrInstr.cpp.h	(revision 80640)
+++ /trunk/src/VBox/VMM/VMMAll/IEMAllCImplStrInstr.cpp.h	(revision 80641)
@@ -1158,5 +1158,5 @@
 IEM_CIMPL_DEF_1(RT_CONCAT4(iemCImpl_ins_op,OP_SIZE,_addr,ADDR_SIZE), bool, fIoChecked)
 {
-    PVM             pVM  = pVCpu->CTX_SUFF(pVM);
+    PVMCC           pVM  = pVCpu->CTX_SUFF(pVM);
     VBOXSTRICTRC    rcStrict;
 
@@ -1250,5 +1250,5 @@
 IEM_CIMPL_DEF_1(RT_CONCAT4(iemCImpl_rep_ins_op,OP_SIZE,_addr,ADDR_SIZE), bool, fIoChecked)
 {
-    PVM pVM = pVCpu->CTX_SUFF(pVM);
+    PVMCC pVM = pVCpu->CTX_SUFF(pVM);
 
     IEM_CTX_IMPORT_RET(pVCpu, CPUMCTX_EXTRN_ES | CPUMCTX_EXTRN_TR);
@@ -1467,5 +1467,5 @@
 IEM_CIMPL_DEF_2(RT_CONCAT4(iemCImpl_outs_op,OP_SIZE,_addr,ADDR_SIZE), uint8_t, iEffSeg, bool, fIoChecked)
 {
-    PVM             pVM  = pVCpu->CTX_SUFF(pVM);
+    PVMCC           pVM  = pVCpu->CTX_SUFF(pVM);
     VBOXSTRICTRC    rcStrict;
 
@@ -1540,5 +1540,5 @@
 IEM_CIMPL_DEF_2(RT_CONCAT4(iemCImpl_rep_outs_op,OP_SIZE,_addr,ADDR_SIZE), uint8_t, iEffSeg, bool, fIoChecked)
 {
-    PVM pVM = pVCpu->CTX_SUFF(pVM);
+    PVMCC pVM = pVCpu->CTX_SUFF(pVM);
 
     /*
Index: /trunk/src/VBox/VMM/VMMAll/IOMAll.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/IOMAll.cpp	(revision 80640)
+++ /trunk/src/VBox/VMM/VMMAll/IOMAll.cpp	(revision 80641)
@@ -75,5 +75,5 @@
  * @param   cbValue     The size of the register to read in bytes. 1, 2 or 4 bytes.
  */
-VMMDECL(VBOXSTRICTRC) IOMIOPortRead(PVM pVM, PVMCPU pVCpu, RTIOPORT Port, uint32_t *pu32Value, size_t cbValue)
+VMMDECL(VBOXSTRICTRC) IOMIOPortRead(PVMCC pVM, PVMCPU pVCpu, RTIOPORT Port, uint32_t *pu32Value, size_t cbValue)
 {
     Assert(pVCpu->iom.s.PendingIOPortWrite.cbValue == 0);
@@ -81,5 +81,6 @@
 /** @todo should initialize *pu32Value here because it can happen that some
  *        handle is buggy and doesn't handle all cases. */
-    /* Take the IOM lock before performing any device I/O. */
+
+    /* For lookups we need to share lock IOM. */
     int rc2 = IOM_LOCK_SHARED(pVM);
 #ifndef IN_RING3
@@ -88,4 +89,79 @@
 #endif
     AssertRC(rc2);
+
+    /*
+     * Get the entry for the current context.
+     */
+    CTX_SUFF(PIOMIOPORTENTRY) pRegEntry = iomIoPortGetEntry(pVM, Port, &pVCpu->iom.s.idxIoPortLastRead);
+    if (pRegEntry)
+    {
+#ifdef VBOX_WITH_STATISTICS
+        PIOMIOPORTSTATSENTRY  pStats    = iomIoPortGetStats(pVM, pRegEntry);
+#endif
+
+        /*
+         * Found an entry, get the data so we can leave the IOM lock.
+         */
+        PFNIOMIOPORTIN pfnInCallback = pRegEntry->pfnInCallback;
+        PPDMDEVINS     pDevIns       = pRegEntry->pDevIns;
+#ifndef IN_RING3
+        if (   pfnInCallback
+            && pDevIns
+            && pRegEntry->cPorts > 0)
+        { /* likely */ }
+        else
+        {
+            STAM_COUNTER_INC(&pStats->InRZToR3);
+            IOM_UNLOCK_SHARED(pVM);
+            return VINF_IOM_R3_IOPORT_READ;
+        }
+#endif
+        void           *pvUser       = pRegEntry->pvUser;
+        IOM_UNLOCK_SHARED(pVM);
+        AssertPtr(pDevIns);
+        AssertPtr(pfnInCallback);
+
+        /*
+         * Call the device.
+         */
+        VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_READ);
+        if (rcStrict == VINF_SUCCESS)
+        {
+            STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfIn), a);
+            rcStrict = pfnInCallback(pDevIns, pvUser, Port, pu32Value, (unsigned)cbValue);
+            STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
+            PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
+
+            if (rcStrict == VINF_SUCCESS)
+                STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
+#ifndef IN_RING3
+            else if (rcStrict == VINF_IOM_R3_IOPORT_READ)
+                STAM_COUNTER_INC(&pStats->InRZToR3);
+#endif
+            else if (rcStrict == VERR_IOM_IOPORT_UNUSED)
+            {
+                /* make return value */
+                rcStrict = VINF_SUCCESS;
+                switch (cbValue)
+                {
+                    case 1: *(uint8_t  *)pu32Value = 0xff; break;
+                    case 2: *(uint16_t *)pu32Value = 0xffff; break;
+                    case 4: *(uint32_t *)pu32Value = UINT32_C(0xffffffff); break;
+                    default:
+                        AssertMsgFailedReturn(("Invalid I/O port size %d. Port=%d\n", cbValue, Port), VERR_IOM_INVALID_IOPORT_SIZE);
+                }
+            }
+            Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=%Rrc\n", Port, *pu32Value, cbValue, VBOXSTRICTRC_VAL(rcStrict)));
+        }
+        else
+            STAM_COUNTER_INC(&pStats->InRZToR3);
+        return rcStrict;
+    }
+
+    /*
+     * Old code
+     * Old code
+     * Old code
+     */
 
 #ifdef VBOX_WITH_STATISTICS
@@ -242,10 +318,10 @@
  * @param   cb          Size of the transfer unit (1, 2 or 4 bytes).
  */
-VMM_INT_DECL(VBOXSTRICTRC) IOMIOPortReadString(PVM pVM, PVMCPU pVCpu, RTIOPORT uPort,
+VMM_INT_DECL(VBOXSTRICTRC) IOMIOPortReadString(PVMCC pVM, PVMCPU pVCpu, RTIOPORT uPort,
                                                void *pvDst, uint32_t *pcTransfers, unsigned cb)
 {
     Assert(pVCpu->iom.s.PendingIOPortWrite.cbValue == 0);
 
-    /* Take the IOM lock before performing any device I/O. */
+    /* For lookups we need to share lock IOM. */
     int rc2 = IOM_LOCK_SHARED(pVM);
 #ifndef IN_RING3
@@ -257,4 +333,111 @@
     const uint32_t cRequestedTransfers = *pcTransfers;
     Assert(cRequestedTransfers > 0);
+
+    /*
+     * Get the entry for the current context.
+     */
+    CTX_SUFF(PIOMIOPORTENTRY) pRegEntry = iomIoPortGetEntry(pVM, uPort, &pVCpu->iom.s.idxIoPortLastReadStr);
+    if (pRegEntry)
+    {
+#ifdef VBOX_WITH_STATISTICS
+        PIOMIOPORTSTATSENTRY  pStats    = iomIoPortGetStats(pVM, pRegEntry);
+#endif
+
+        /*
+         * Found an entry, get the data so we can leave the IOM lock.
+         */
+        PFNIOMIOPORTINSTRING pfnInStrCallback = pRegEntry->pfnInStrCallback;
+        PFNIOMIOPORTIN       pfnInCallback    = pRegEntry->pfnInCallback;
+        PPDMDEVINS           pDevIns          = pRegEntry->pDevIns;
+#ifndef IN_RING3
+        if (   pfnInCallback
+            && pDevIns
+            && pRegEntry->cPorts > 0)
+        { /* likely */ }
+        else
+        {
+            STAM_COUNTER_INC(&pStats->InRZToR3);
+            IOM_UNLOCK_SHARED(pVM);
+            return VINF_IOM_R3_IOPORT_READ;
+        }
+#endif
+        void           *pvUser       = pRegEntry->pvUser;
+        IOM_UNLOCK_SHARED(pVM);
+        AssertPtr(pDevIns);
+        AssertPtr(pfnInCallback);
+
+        /*
+         * Call the device.
+         */
+        VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_READ);
+        if (rcStrict == VINF_SUCCESS)
+        {
+            /*
+             * First using the string I/O callback.
+             */
+            if (pfnInStrCallback)
+            {
+                STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfIn), a);
+                rcStrict = pfnInStrCallback(pDevIns, pvUser, uPort, (uint8_t *)pvDst, pcTransfers, cb);
+                STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
+            }
+
+            /*
+             * Then doing the single I/O fallback.
+             */
+            if (   *pcTransfers > 0
+                && rcStrict == VINF_SUCCESS)
+            {
+                pvDst = (uint8_t *)pvDst + (cRequestedTransfers - *pcTransfers) * cb;
+                do
+                {
+                    uint32_t u32Value = 0;
+                    STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfIn), a);
+                    rcStrict = pfnInCallback(pDevIns, pvUser, uPort, &u32Value, cb);
+                    STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfIn), a);
+                    if (rcStrict == VERR_IOM_IOPORT_UNUSED)
+                    {
+                        u32Value = UINT32_MAX;
+                        rcStrict = VINF_SUCCESS;
+                    }
+                    if (IOM_SUCCESS(rcStrict))
+                    {
+                        switch (cb)
+                        {
+                            case 4: *(uint32_t *)pvDst =           u32Value; pvDst = (uint8_t *)pvDst + 4; break;
+                            case 2: *(uint16_t *)pvDst = (uint16_t)u32Value; pvDst = (uint8_t *)pvDst + 2; break;
+                            case 1: *(uint8_t  *)pvDst = (uint8_t )u32Value; pvDst = (uint8_t *)pvDst + 1; break;
+                            default: AssertFailed();
+                        }
+                        *pcTransfers -= 1;
+                    }
+                } while (   *pcTransfers > 0
+                         && rcStrict == VINF_SUCCESS);
+            }
+            PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
+
+#ifdef VBOX_WITH_STATISTICS
+            if (rcStrict == VINF_SUCCESS && pStats)
+                STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(In));
+# ifndef IN_RING3
+            else if (rcStrict == VINF_IOM_R3_IOPORT_READ && pStats)
+                STAM_COUNTER_INC(&pStats->InRZToR3);
+# endif
+#endif
+            Log3(("IOMIOPortReadStr: uPort=%RTiop pvDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Rrc\n",
+                  uPort, pvDst, pcTransfers, cRequestedTransfers, *pcTransfers, cb, VBOXSTRICTRC_VAL(rcStrict)));
+        }
+#ifndef IN_RING3
+        else
+            STAM_COUNTER_INC(&pStats->InRZToR3);
+#endif
+        return rcStrict;
+    }
+
+    /*
+     * Old code
+     * Old code
+     * Old code
+     */
 
 #ifdef VBOX_WITH_STATISTICS
@@ -458,5 +641,5 @@
  * @param   cbValue     The size of the register to read in bytes. 1, 2 or 4 bytes.
  */
-VMMDECL(VBOXSTRICTRC) IOMIOPortWrite(PVM pVM, PVMCPU pVCpu, RTIOPORT Port, uint32_t u32Value, size_t cbValue)
+VMMDECL(VBOXSTRICTRC) IOMIOPortWrite(PVMCC pVM, PVMCPU pVCpu, RTIOPORT Port, uint32_t u32Value, size_t cbValue)
 {
 #ifndef IN_RING3
@@ -464,5 +647,5 @@
 #endif
 
-    /* Take the IOM lock before performing any device I/O. */
+    /* For lookups we need to share lock IOM. */
     int rc2 = IOM_LOCK_SHARED(pVM);
 #ifndef IN_RING3
@@ -472,6 +655,70 @@
     AssertRC(rc2);
 
-/** @todo bird: When I get time, I'll remove the RC/R0 trees and link the RC/R0
- *        entries to the ring-3 node. */
+    /*
+     * Get the entry for the current context.
+     */
+    CTX_SUFF(PIOMIOPORTENTRY) pRegEntry = iomIoPortGetEntry(pVM, Port, &pVCpu->iom.s.idxIoPortLastWrite);
+    if (pRegEntry)
+    {
+#ifdef VBOX_WITH_STATISTICS
+        PIOMIOPORTSTATSENTRY  pStats    = iomIoPortGetStats(pVM, pRegEntry);
+#endif
+
+        /*
+         * Found an entry, get the data so we can leave the IOM lock.
+         */
+        PFNIOMIOPORTOUT pfnOutCallback   = pRegEntry->pfnOutCallback;
+        PPDMDEVINS      pDevIns          = pRegEntry->pDevIns;
+#ifndef IN_RING3
+        if (   pfnOutCallback
+            && pDevIns
+            && pRegEntry->cPorts > 0)
+        { /* likely */ }
+        else
+        {
+            IOM_UNLOCK_SHARED(pVM);
+            STAM_COUNTER_INC(&pStats->OutRZToR3);
+            return iomIOPortRing3WritePending(pVCpu, Port, u32Value, cbValue);
+        }
+#endif
+        void           *pvUser       = pRegEntry->pvUser;
+        IOM_UNLOCK_SHARED(pVM);
+        AssertPtr(pDevIns);
+        AssertPtr(pfnOutCallback);
+
+        /*
+         * Call the device.
+         */
+        VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_WRITE);
+        if (rcStrict == VINF_SUCCESS)
+        {
+            STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
+            rcStrict = pfnOutCallback(pDevIns, pvUser, Port, u32Value, (unsigned)cbValue);
+            STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
+
+            PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
+
+#ifdef VBOX_WITH_STATISTICS
+            if (rcStrict == VINF_SUCCESS)
+                STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
+#endif
+            Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d rc=%Rrc\n", Port, u32Value, cbValue, VBOXSTRICTRC_VAL(rcStrict)));
+        }
+#ifndef IN_RING3
+        if (rcStrict == VINF_IOM_R3_IOPORT_WRITE)
+        {
+            STAM_COUNTER_INC(&pStats->OutRZToR3);
+            return iomIOPortRing3WritePending(pVCpu, Port, u32Value, cbValue);
+        }
+#endif
+        return rcStrict;
+    }
+
+    /*
+     * Old code
+     * Old code
+     * Old code
+     */
+
 #ifdef VBOX_WITH_STATISTICS
     /*
@@ -611,5 +858,5 @@
  * @param   cb          Size of the transfer unit (1, 2 or 4 bytes).
  */
-VMM_INT_DECL(VBOXSTRICTRC) IOMIOPortWriteString(PVM pVM, PVMCPU pVCpu, RTIOPORT uPort, void const *pvSrc,
+VMM_INT_DECL(VBOXSTRICTRC) IOMIOPortWriteString(PVMCC pVM, PVMCPU pVCpu, RTIOPORT uPort, void const *pvSrc,
                                                 uint32_t *pcTransfers, unsigned cb)
 {
@@ -627,4 +874,105 @@
     const uint32_t cRequestedTransfers = *pcTransfers;
     Assert(cRequestedTransfers > 0);
+
+    /*
+     * Get the entry for the current context.
+     */
+    CTX_SUFF(PIOMIOPORTENTRY) pRegEntry = iomIoPortGetEntry(pVM, uPort, &pVCpu->iom.s.idxIoPortLastWriteStr);
+    if (pRegEntry)
+    {
+#ifdef VBOX_WITH_STATISTICS
+        PIOMIOPORTSTATSENTRY  pStats    = iomIoPortGetStats(pVM, pRegEntry);
+#endif
+
+        /*
+         * Found an entry, get the data so we can leave the IOM lock.
+         */
+        PFNIOMIOPORTOUTSTRING   pfnOutStrCallback = pRegEntry->pfnOutStrCallback;
+        PFNIOMIOPORTOUT         pfnOutCallback    = pRegEntry->pfnOutCallback;
+        PPDMDEVINS              pDevIns           = pRegEntry->pDevIns;
+#ifndef IN_RING3
+        if (   pfnOutCallback
+            && pDevIns
+            && pRegEntry->cPorts > 0)
+        { /* likely */ }
+        else
+        {
+            IOM_UNLOCK_SHARED(pVM);
+            STAM_COUNTER_INC(&pStats->OutRZToR3);
+            return VINF_IOM_R3_IOPORT_WRITE;
+        }
+#endif
+        void           *pvUser       = pRegEntry->pvUser;
+        IOM_UNLOCK_SHARED(pVM);
+        AssertPtr(pDevIns);
+        AssertPtr(pfnOutCallback);
+
+        /*
+         * Call the device.
+         */
+        VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_IOPORT_WRITE);
+        if (rcStrict == VINF_SUCCESS)
+        {
+            /*
+             * First using string I/O if possible.
+             */
+            if (pfnOutStrCallback)
+            {
+                STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
+                rcStrict = pfnOutStrCallback(pDevIns, pvUser, uPort, (uint8_t const *)pvSrc, pcTransfers, cb);
+                STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
+            }
+
+            /*
+             * Then doing the single I/O fallback.
+             */
+            if (   *pcTransfers > 0
+                && rcStrict == VINF_SUCCESS)
+            {
+                pvSrc = (uint8_t *)pvSrc + (cRequestedTransfers - *pcTransfers) * cb;
+                do
+                {
+                    uint32_t u32Value;
+                    switch (cb)
+                    {
+                        case 4: u32Value = *(uint32_t *)pvSrc; pvSrc = (uint8_t const *)pvSrc + 4; break;
+                        case 2: u32Value = *(uint16_t *)pvSrc; pvSrc = (uint8_t const *)pvSrc + 2; break;
+                        case 1: u32Value = *(uint8_t  *)pvSrc; pvSrc = (uint8_t const *)pvSrc + 1; break;
+                        default: AssertFailed(); u32Value = UINT32_MAX;
+                    }
+                    STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfOut), a);
+                    rcStrict = pfnOutCallback(pDevIns, pvUser, uPort, u32Value, cb);
+                    STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfOut), a);
+                    if (IOM_SUCCESS(rcStrict))
+                        *pcTransfers -= 1;
+                } while (   *pcTransfers > 0
+                         && rcStrict == VINF_SUCCESS);
+            }
+
+            PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo));
+
+#ifdef VBOX_WITH_STATISTICS
+            if (rcStrict == VINF_SUCCESS)
+                STAM_COUNTER_INC(&pStats->CTX_SUFF_Z(Out));
+# ifndef IN_RING3
+            else if (rcStrict == VINF_IOM_R3_IOPORT_WRITE)
+                STAM_COUNTER_INC(&pStats->OutRZToR3);
+# endif
+#endif
+            Log3(("IOMIOPortWriteStr: uPort=%RTiop pvSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rcStrict=%Rrc\n",
+                  uPort, pvSrc, pcTransfers, cRequestedTransfers, *pcTransfers, cb, VBOXSTRICTRC_VAL(rcStrict)));
+        }
+#ifndef IN_RING3
+        else
+            STAM_COUNTER_INC(&pStats->OutRZToR3);
+#endif
+        return rcStrict;
+    }
+
+    /*
+     * Old code.
+     * Old code.
+     * Old code.
+     */
 
 #ifdef VBOX_WITH_STATISTICS
Index: /trunk/src/VBox/VMM/VMMR0/GVMMR0.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/GVMMR0.cpp	(revision 80640)
+++ /trunk/src/VBox/VMM/VMMR0/GVMMR0.cpp	(revision 80641)
@@ -54,11 +54,12 @@
 #include <VBox/vmm/gmm.h>
 #include "GVMMR0Internal.h"
+#include <VBox/vmm/iom.h>
 #include <VBox/vmm/pdm.h>
-#include <VBox/vmm/vmcc.h>
-#include <VBox/vmm/vmcpuset.h>
 #include <VBox/vmm/vmm.h>
 #ifdef VBOX_WITH_NEM_R0
 # include <VBox/vmm/nem.h>
 #endif
+#include <VBox/vmm/vmcpuset.h>
+#include <VBox/vmm/vmcc.h>
 #include <VBox/param.h>
 #include <VBox/err.h>
@@ -903,4 +904,5 @@
                         GMMR0InitPerVMData(pGVM);
                         PDMR0InitPerVMData(pGVM);
+                        IOMR0InitPerVMData(pGVM);
                         pGVM->gvmm.s.VMMemObj  = hVMMemObj;
 
@@ -1294,4 +1296,5 @@
 #endif
     PDMR0CleanupVM(pGVM);
+    IOMR0CleanupVM(pGVM);
 
     AssertCompile(NIL_RTTHREADCTXHOOK == (RTTHREADCTXHOOK)0); /* Depends on zero initialized memory working for NIL at the moment. */
Index: /trunk/src/VBox/VMM/VMMR0/IOMR0.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/IOMR0.cpp	(revision 80641)
+++ /trunk/src/VBox/VMM/VMMR0/IOMR0.cpp	(revision 80641)
@@ -0,0 +1,322 @@
+/* $Id$ */
+/** @file
+ * IOM - Host Context Ring 0.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_IOM
+#include <VBox/vmm/iom.h>
+#include "IOMInternal.h"
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/vmcc.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/memobj.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+
+
+
+/**
+ * Initializes the per-VM data for the IOM.
+ *
+ * This is called from under the GVMM lock, so it should only initialize the
+ * data so IOMR0CleanupVM and others will work smoothly.
+ *
+ * @param   pGVM    Pointer to the global VM structure.
+ */
+VMMR0_INT_DECL(void) IOMR0InitPerVMData(PGVM pGVM)
+{
+    AssertCompile(sizeof(pGVM->iom.s) <= sizeof(pGVM->iom.padding));
+    AssertCompile(sizeof(pGVM->iomr0.s) <= sizeof(pGVM->iomr0.padding));
+
+    pGVM->iomr0.s.hIoPortMapObj      = NIL_RTR0MEMOBJ;
+    pGVM->iomr0.s.hIoPortMemObj      = NIL_RTR0MEMOBJ;
+#ifdef VBOX_WITH_STATISTICS
+    pGVM->iomr0.s.hIoPortStatsMapObj = NIL_RTR0MEMOBJ;
+    pGVM->iomr0.s.hIoPortStatsMemObj = NIL_RTR0MEMOBJ;
+#endif
+}
+
+
+/**
+ * Cleans up any loose ends before the GVM structure is destroyed.
+ */
+VMMR0_INT_DECL(void) IOMR0CleanupVM(PGVM pGVM)
+{
+    RTR0MemObjFree(pGVM->iomr0.s.hIoPortMapObj, true /*fFreeMappings*/);
+    pGVM->iomr0.s.hIoPortMapObj      = NIL_RTR0MEMOBJ;
+    RTR0MemObjFree(pGVM->iomr0.s.hIoPortMemObj, true /*fFreeMappings*/);
+    pGVM->iomr0.s.hIoPortMemObj      = NIL_RTR0MEMOBJ;
+#ifdef VBOX_WITH_STATISTICS
+    RTR0MemObjFree(pGVM->iomr0.s.hIoPortStatsMapObj, true /*fFreeMappings*/);
+    pGVM->iomr0.s.hIoPortStatsMapObj = NIL_RTR0MEMOBJ;
+    RTR0MemObjFree(pGVM->iomr0.s.hIoPortStatsMemObj, true /*fFreeMappings*/);
+    pGVM->iomr0.s.hIoPortStatsMemObj = NIL_RTR0MEMOBJ;
+#endif
+}
+
+
+/**
+ * Implements PDMDEVHLPR0::pfnIoPortSetUpContext.
+ *
+ * @param   pGVM            The global (ring-0) VM structure.
+ * @param   pDevIns         The device instance.
+ * @param   hIoPorts        The I/O port handle (already registered in ring-3).
+ * @param   pfnOut          The OUT handler callback, optional.
+ * @param   pfnIn           The IN handler callback, optional.
+ * @param   pfnOutStr       The REP OUTS handler callback, optional.
+ * @param   pfnInStr        The REP INS handler callback, optional.
+ * @param   pvUser          User argument for the callbacks.
+ * @thread  EMT(0)
+ * @note    Only callable at VM creation time.
+ */
+VMMR0_INT_DECL(int)  IOMR0IoPortSetUpContext(PGVM pGVM, PPDMDEVINS pDevIns, IOMIOPORTHANDLE hIoPorts,
+                                             PFNIOMIOPORTOUT pfnOut,  PFNIOMIOPORTIN pfnIn,
+                                             PFNIOMIOPORTOUTSTRING pfnOutStr, PFNIOMIOPORTINSTRING pfnInStr, void *pvUser)
+{
+    /*
+     * Validate input and state.
+     */
+    VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT);
+    VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
+    AssertReturn(hIoPorts < pGVM->iomr0.s.cIoPortAlloc, VERR_IOM_INVALID_IOPORT_HANDLE);
+    AssertReturn(hIoPorts < pGVM->iom.s.cIoPortRegs, VERR_IOM_INVALID_IOPORT_HANDLE);
+    AssertPtrReturn(pDevIns, VERR_INVALID_HANDLE);
+    AssertReturn(pDevIns->pDevInsForR3 != NIL_RTR3PTR && !(pDevIns->pDevInsForR3 & PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER);
+    AssertReturn(pGVM->iomr0.s.paIoPortRing3Regs[hIoPorts].pDevIns == pDevIns->pDevInsForR3, VERR_IOM_INVALID_IOPORT_HANDLE);
+    AssertReturn(pGVM->iomr0.s.paIoPortRegs[hIoPorts].pDevIns == NULL, VERR_WRONG_ORDER);
+
+    AssertReturn(pfnOut || pfnIn || pfnOutStr || pfnInStr, VERR_INVALID_PARAMETER);
+    AssertPtrNullReturn(pfnOut, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pfnIn, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pfnOutStr, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pfnInStr, VERR_INVALID_POINTER);
+
+    RTIOPORT const cPorts = pGVM->iomr0.s.paIoPortRing3Regs[hIoPorts].cPorts;
+    AssertMsgReturn(cPorts > 0 && cPorts <= _8K, ("cPorts=%s\n", cPorts), VERR_IOM_INVALID_IOPORT_HANDLE);
+
+    /*
+     * Do the job.
+     */
+    pGVM->iomr0.s.paIoPortRegs[hIoPorts].pvUser             = pvUser;
+    pGVM->iomr0.s.paIoPortRegs[hIoPorts].pDevIns            = pDevIns;
+    pGVM->iomr0.s.paIoPortRegs[hIoPorts].pfnOutCallback     = pfnOut;
+    pGVM->iomr0.s.paIoPortRegs[hIoPorts].pfnInCallback      = pfnIn;
+    pGVM->iomr0.s.paIoPortRegs[hIoPorts].pfnOutStrCallback  = pfnOutStr;
+    pGVM->iomr0.s.paIoPortRegs[hIoPorts].pfnInStrCallback   = pfnInStr;
+    pGVM->iomr0.s.paIoPortRegs[hIoPorts].cPorts             = cPorts;
+    uint16_t const idxStats = pGVM->iomr0.s.paIoPortRing3Regs[hIoPorts].idxStats;
+    pGVM->iomr0.s.paIoPortRegs[hIoPorts].idxStats           = (uint32_t)idxStats + cPorts <= pGVM->iomr0.s.cIoPortStatsAllocation
+                                                            ? idxStats : UINT16_MAX;
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Grows the I/O port registration (all contexts) and lookup tables.
+ *
+ * @returns VBox status code.
+ * @param   pGVM            The global (ring-0) VM structure.
+ * @param   cReqMinEntries  The minimum growth (absolute).
+ * @thread  EMT(0)
+ * @note    Only callable at VM creation time.
+ */
+VMMR0_INT_DECL(int) IOMR0IoPortGrowRegistrationTables(PGVM pGVM, uint64_t cReqMinEntries)
+{
+    /*
+     * Validate input and state.
+     */
+    VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT);
+    VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
+    AssertReturn(cReqMinEntries <= _4K, VERR_IOM_TOO_MANY_IOPORT_REGISTRATIONS);
+    uint32_t cNewEntries = (uint32_t)cReqMinEntries;
+    AssertReturn(cNewEntries >= pGVM->iom.s.cIoPortAlloc, VERR_IOM_IOPORT_IPE_1);
+    uint32_t const cOldEntries = pGVM->iomr0.s.cIoPortAlloc;
+    ASMCompilerBarrier();
+    AssertReturn(cNewEntries >= cOldEntries, VERR_IOM_IOPORT_IPE_2);
+    AssertReturn(pGVM->iom.s.cIoPortRegs >= pGVM->iomr0.s.cIoPortMax, VERR_IOM_IOPORT_IPE_3);
+
+    /*
+     * Allocate the new tables.  We use a single allocation for the three tables (ring-0,
+     * ring-3, lookup) and does a partial mapping of the result to ring-3.
+     */
+    uint32_t const cbRing0  = RT_ALIGN_32(cNewEntries * sizeof(IOMIOPORTENTRYR0),     PAGE_SIZE);
+    uint32_t const cbRing3  = RT_ALIGN_32(cNewEntries * sizeof(IOMIOPORTENTRYR3),     PAGE_SIZE);
+    uint32_t const cbShared = RT_ALIGN_32(cNewEntries * sizeof(IOMIOPORTLOOKUPENTRY), PAGE_SIZE);
+    uint32_t const cbNew    = cbRing0 + cbRing3 + cbShared;
+
+    /* Use the rounded up space as best we can. */
+    cNewEntries = RT_MIN(RT_MIN(cbRing0 / sizeof(IOMIOPORTENTRYR0), cbRing3 / sizeof(IOMIOPORTENTRYR3)),
+                         cbShared / sizeof(IOMIOPORTLOOKUPENTRY));
+
+    RTR0MEMOBJ hMemObj;
+    int rc = RTR0MemObjAllocPage(&hMemObj, cbNew, false /*fExecutable*/);
+    if (RT_SUCCESS(rc))
+    {
+        /*
+         * Zero and map it.
+         */
+        RT_BZERO(RTR0MemObjAddress(hMemObj), cbNew);
+
+        RTR0MEMOBJ hMapObj;
+        rc = RTR0MemObjMapUserEx(&hMapObj, hMemObj, (RTR3PTR)-1, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE,
+                                 RTR0ProcHandleSelf(), cbRing0, cbNew - cbRing0);
+        if (RT_SUCCESS(rc))
+        {
+            PIOMIOPORTENTRYR0     const paRing0    = (PIOMIOPORTENTRYR0)RTR0MemObjAddress(hMemObj);
+            PIOMIOPORTENTRYR3     const paRing3    = (PIOMIOPORTENTRYR3)((uintptr_t)paRing0 + cbRing0);
+            PIOMIOPORTLOOKUPENTRY const paLookup   = (PIOMIOPORTLOOKUPENTRY)((uintptr_t)paRing3 + cbRing3);
+            RTR3UINTPTR           const uAddrRing3 = RTR0MemObjAddressR3(hMapObj);
+
+            /*
+             * Copy over the old info and initialize the idxSelf and idxStats members.
+             */
+            if (pGVM->iomr0.s.paIoPortRegs != NULL)
+            {
+                memcpy(paRing0,  pGVM->iomr0.s.paIoPortRegs,      sizeof(paRing0[0])  * cOldEntries);
+                memcpy(paRing3,  pGVM->iomr0.s.paIoPortRing3Regs, sizeof(paRing3[0])  * cOldEntries);
+                memcpy(paLookup, pGVM->iomr0.s.paIoPortLookup,    sizeof(paLookup[0]) * cOldEntries);
+            }
+
+            size_t i = cbRing0 / sizeof(*paRing0);
+            while (i-- > cOldEntries)
+            {
+                paRing0[i].idxSelf  = (uint16_t)i;
+                paRing0[i].idxStats = UINT16_MAX;
+                paRing3[i].idxSelf  = (uint16_t)i;
+                paRing3[i].idxStats = UINT16_MAX;
+            }
+
+            /*
+             * Switch the memory handles.
+             */
+            RTR0MEMOBJ hTmp = pGVM->iomr0.s.hIoPortMapObj;
+            pGVM->iomr0.s.hIoPortMapObj = hMapObj;
+            hMapObj = hTmp;
+
+            hTmp = pGVM->iomr0.s.hIoPortMemObj;
+            pGVM->iomr0.s.hIoPortMemObj = hMemObj;
+            hMemObj = hTmp;
+
+            /*
+             * Update the variables.
+             */
+            pGVM->iomr0.s.paIoPortRegs      = paRing0;
+            pGVM->iomr0.s.paIoPortRing3Regs = paRing3;
+            pGVM->iomr0.s.paIoPortLookup    = paLookup;
+            pGVM->iom.s.paIoPortRegs        = uAddrRing3;
+            pGVM->iom.s.paIoPortLookup      = uAddrRing3 + cbRing3;
+            pGVM->iom.s.cIoPortAlloc        = cNewEntries;
+            pGVM->iomr0.s.cIoPortAlloc      = cNewEntries;
+
+            /*
+             * Free the old allocation.
+             */
+            RTR0MemObjFree(hMapObj, true /*fFreeMappings*/);
+        }
+        RTR0MemObjFree(hMemObj, true /*fFreeMappings*/);
+    }
+
+    return rc;
+}
+
+
+/**
+ * Grows the I/O port statistics table.
+ *
+ * @returns VBox status code.
+ * @param   pGVM            The global (ring-0) VM structure.
+ * @param   cReqMinEntries  The minimum growth (absolute).
+ * @thread  EMT(0)
+ * @note    Only callable at VM creation time.
+ */
+VMMR0_INT_DECL(int) IOMR0IoPortGrowStatisticsTable(PGVM pGVM, uint64_t cReqMinEntries)
+{
+    /*
+     * Validate input and state.
+     */
+    VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT);
+    VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
+    AssertReturn(cReqMinEntries <= _64K, VERR_IOM_TOO_MANY_IOPORT_REGISTRATIONS);
+    uint32_t cNewEntries = (uint32_t)cReqMinEntries;
+    uint32_t const cOldEntries = pGVM->iomr0.s.cIoPortStatsAllocation;
+    ASMCompilerBarrier();
+    AssertReturn(cNewEntries > cOldEntries, VERR_IOM_IOPORT_IPE_1);
+    AssertReturn(pGVM->iom.s.cIoPortStatsAllocation == cOldEntries, VERR_IOM_IOPORT_IPE_1);
+    AssertReturn(pGVM->iom.s.cIoPortStats <= cOldEntries, VERR_IOM_IOPORT_IPE_2);
+
+    /*
+     * Allocate a new table, zero it and map it.
+     */
+#ifndef VBOX_WITH_STATISTICS
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+#else
+    uint32_t const cbNew = RT_ALIGN_32(cNewEntries * sizeof(IOMIOPORTSTATSENTRY), PAGE_SIZE);
+    cNewEntries = cbNew / sizeof(IOMIOPORTSTATSENTRY);
+
+    RTR0MEMOBJ hMemObj;
+    int rc = RTR0MemObjAllocPage(&hMemObj, cbNew, false /*fExecutable*/);
+    if (RT_SUCCESS(rc))
+    {
+        RT_BZERO(RTR0MemObjAddress(hMemObj), cbNew);
+
+        RTR0MEMOBJ hMapObj;
+        rc = RTR0MemObjMapUser(&hMapObj, hMemObj, (RTR3PTR)-1, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE, RTR0ProcHandleSelf());
+        if (RT_SUCCESS(rc))
+        {
+            PIOMIOPORTSTATSENTRY pIoPortStats = (PIOMIOPORTSTATSENTRY)RTR0MemObjAddress(hMemObj);
+
+            /*
+             * Anything to copy over and free up?
+             */
+            if (pGVM->iomr0.s.paIoPortStats)
+                memcpy(pIoPortStats, pGVM->iomr0.s.paIoPortStats, cOldEntries * sizeof(IOMIOPORTSTATSENTRY));
+
+            /*
+             * Switch the memory handles.
+             */
+            RTR0MEMOBJ hTmp = pGVM->iomr0.s.hIoPortStatsMapObj;
+            pGVM->iomr0.s.hIoPortStatsMapObj = hMapObj;
+            hMapObj = hTmp;
+
+            hTmp = pGVM->iomr0.s.hIoPortStatsMemObj;
+            pGVM->iomr0.s.hIoPortStatsMemObj = hMemObj;
+            hMemObj = hTmp;
+
+            /*
+             * Update the variables.
+             */
+            pGVM->iomr0.s.paIoPortStats          = pIoPortStats;
+            pGVM->iom.s.paIoPortStats            = RTR0MemObjAddressR3(pGVM->iomr0.s.hIoPortStatsMapObj);
+            pGVM->iom.s.cIoPortStatsAllocation   = cNewEntries;
+            pGVM->iomr0.s.cIoPortStatsAllocation = cNewEntries;
+
+            /*
+             * Free the old allocation.
+             */
+            RTR0MemObjFree(hMapObj, true /*fFreeMappings*/);
+        }
+        RTR0MemObjFree(hMemObj, true /*fFreeMappings*/);
+    }
+    return rc;
+#endif /* VBOX_WITH_STATISTICS */
+}
+
Index: /trunk/src/VBox/VMM/VMMR0/PDMR0Device.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/PDMR0Device.cpp	(revision 80640)
+++ /trunk/src/VBox/VMM/VMMR0/PDMR0Device.cpp	(revision 80641)
@@ -195,6 +195,15 @@
                                                           void *pvUser)
 {
-    RT_NOREF(pDevIns, hIoPorts, pfnOut, pfnIn, pfnOutStr, pfnInStr, pvUser);
-    return VERR_NOT_IMPLEMENTED;
+    PDMDEV_ASSERT_DEVINS(pDevIns);
+    LogFlow(("pdmR0DevHlp_IoPortSetUpContextEx: caller='%s'/%d: hIoPorts=%#x pfnOut=%p pfnIn=%p pfnOutStr=%p pfnInStr=%p pvUser=%p\n",
+             pDevIns->pReg->szName, pDevIns->iInstance, hIoPorts, pfnOut, pfnIn, pfnOutStr, pfnInStr, pvUser));
+    PGVM pGVM = pDevIns->Internal.s.pGVM;
+    VM_ASSERT_EMT0_RETURN(pGVM, VERR_VM_THREAD_NOT_EMT);
+    VM_ASSERT_STATE_RETURN(pGVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
+
+    int rc = IOMR0IoPortSetUpContext(pGVM, pDevIns, hIoPorts, pfnOut, pfnIn, pfnOutStr, pfnInStr, pvUser);
+
+    LogFlow(("pdmR0DevHlp_IoPortSetUpContextEx: caller='%s'/%d: returns %Rrc\n", pDevIns->pReg->szName, pDevIns->iInstance, rc));
+    return rc;
 }
 
Index: /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp	(revision 80640)
+++ /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp	(revision 80641)
@@ -23,4 +23,5 @@
 #include <VBox/vmm/vmm.h>
 #include <VBox/sup.h>
+#include <VBox/vmm/iom.h>
 #include <VBox/vmm/trpm.h>
 #include <VBox/vmm/cpum.h>
@@ -2198,4 +2199,25 @@
 
         /*
+         * IOM requests.
+         */
+        case VMMR0_DO_IOM_GROW_IO_PORTS:
+        {
+            if (pReqHdr || idCpu != 0)
+                return VERR_INVALID_PARAMETER;
+            rc = IOMR0IoPortGrowRegistrationTables(pGVM, u64Arg);
+            VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
+            break;
+        }
+
+        case VMMR0_DO_IOM_GROW_IO_PORT_STATS:
+        {
+            if (pReqHdr || idCpu != 0)
+                return VERR_INVALID_PARAMETER;
+            rc = IOMR0IoPortGrowStatisticsTable(pGVM, u64Arg);
+            VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
+            break;
+        }
+
+        /*
          * For profiling.
          */
@@ -2296,4 +2318,6 @@
 
             case VMMR0_DO_PDM_DEVICE_CREATE:
+            case VMMR0_DO_IOM_GROW_IO_PORTS:
+            case VMMR0_DO_IOM_GROW_IO_PORT_STATS:
             {
                 PGVMCPU        pGVCpu        = &pGVM->aCpus[idCpu];
Index: /trunk/src/VBox/VMM/VMMR3/IOM.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/IOM.cpp	(revision 80640)
+++ /trunk/src/VBox/VMM/VMMR3/IOM.cpp	(revision 80641)
@@ -230,8 +230,10 @@
             STAM_REG(pVM, &pVM->iom.s.StatRZInstOther,        STAMTYPE_COUNTER, "/IOM/RZ-MMIOHandler/Inst/Other",           STAMUNIT_OCCURENCES,     "Other instructions counter.");
             STAM_REG(pVM, &pVM->iom.s.StatR3MMIOHandler,      STAMTYPE_COUNTER, "/IOM/R3-MMIOHandler",                      STAMUNIT_OCCURENCES,     "Number of calls to iomR3MmioHandler.");
+#if 0 /* unused */
             STAM_REG(pVM, &pVM->iom.s.StatInstIn,             STAMTYPE_COUNTER, "/IOM/IOWork/In",                           STAMUNIT_OCCURENCES,     "Counter of any IN instructions.");
             STAM_REG(pVM, &pVM->iom.s.StatInstOut,            STAMTYPE_COUNTER, "/IOM/IOWork/Out",                          STAMUNIT_OCCURENCES,     "Counter of any OUT instructions.");
             STAM_REG(pVM, &pVM->iom.s.StatInstIns,            STAMTYPE_COUNTER, "/IOM/IOWork/Ins",                          STAMUNIT_OCCURENCES,     "Counter of any INS instructions.");
             STAM_REG(pVM, &pVM->iom.s.StatInstOuts,           STAMTYPE_COUNTER, "/IOM/IOWork/Outs",                         STAMUNIT_OCCURENCES,     "Counter of any OUTS instructions.");
+#endif
         }
     }
@@ -413,4 +415,313 @@
     return VINF_SUCCESS;
 }
+
+
+/**
+ * Worker for PDMDEVHLPR3::pfnIoPortCreateEx.
+ */
+VMMR3_INT_DECL(int)  IOMR3IoPortCreate(PVM pVM, PPDMDEVINS pDevIns, RTIOPORT cPorts, uint32_t fFlags, PPDMPCIDEV pPciDev,
+                                       uint32_t iPciRegion, PFNIOMIOPORTOUT pfnOut, PFNIOMIOPORTIN pfnIn,
+                                       PFNIOMIOPORTOUTSTRING pfnOutStr, PFNIOMIOPORTINSTRING pfnInStr, RTR3PTR pvUser,
+                                       const char *pszDesc, PIOMIOPORTHANDLE phIoPorts)
+{
+    /*
+     * Validate input.
+     */
+    AssertPtrReturn(phIoPorts, VERR_INVALID_POINTER);
+    *phIoPorts = UINT32_MAX;
+    VM_ASSERT_EMT0_RETURN(pVM, VERR_VM_THREAD_NOT_EMT);
+    VM_ASSERT_STATE_RETURN(pVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
+
+    AssertPtrReturn(pDevIns, VERR_INVALID_POINTER);
+
+    AssertMsgReturn(cPorts > 0 && cPorts <= _8K, ("cPorts=%s\n", cPorts), VERR_OUT_OF_RANGE);
+    AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+    AssertReturn(pfnOut || pfnIn || pfnOutStr || pfnInStr, VERR_INVALID_PARAMETER);
+    AssertPtrNullReturn(pfnOut, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pfnIn, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pfnOutStr, VERR_INVALID_POINTER);
+    AssertPtrNullReturn(pfnInStr, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszDesc, VERR_INVALID_POINTER);
+    AssertReturn(*pszDesc != '\0', VERR_INVALID_POINTER);
+    AssertReturn(strlen(pszDesc) < 128, VERR_INVALID_POINTER);
+
+    /*
+     * Ensure that we've got table space for it.
+     */
+#ifndef VBOX_WITH_STATISTICS
+    uint16_t const idxStats        = UINT16_MAX;
+#else
+    uint32_t const idxStats        = pVM->iom.s.cIoPortStats;
+    uint32_t const cNewIoPortStats = idxStats + cPorts;
+    AssertReturn(cNewIoPortStats <= _64K, VERR_IOM_TOO_MANY_IOPORT_REGISTRATIONS);
+    if (cNewIoPortStats > pVM->iom.s.cIoPortStatsAllocation)
+    {
+        int rc = VMMR3CallR0Emt(pVM, pVM->apCpusR3[0], VMMR0_DO_IOM_GROW_IO_PORT_STATS, cNewIoPortStats, NULL);
+        AssertLogRelRCReturn(rc, rc);
+        AssertReturn(idxStats == pVM->iom.s.cIoPortStats, VERR_IOM_IOPORT_IPE_1);
+        AssertReturn(cNewIoPortStats <= pVM->iom.s.cIoPortStatsAllocation, VERR_IOM_IOPORT_IPE_2);
+    }
+#endif
+
+    uint32_t idx = pVM->iom.s.cIoPortRegs;
+    if (idx >= pVM->iom.s.cIoPortAlloc)
+    {
+        int rc = VMMR3CallR0Emt(pVM, pVM->apCpusR3[0], VMMR0_DO_IOM_GROW_IO_PORTS, pVM->iom.s.cIoPortAlloc + 1, NULL);
+        AssertLogRelRCReturn(rc, rc);
+        AssertReturn(idx == pVM->iom.s.cIoPortRegs, VERR_IOM_IOPORT_IPE_1);
+        AssertReturn(idx < pVM->iom.s.cIoPortAlloc, VERR_IOM_IOPORT_IPE_2);
+    }
+
+    /*
+     * Enter it.
+     */
+    pVM->iom.s.paIoPortRegs[idx].pvUser             = pvUser;
+    pVM->iom.s.paIoPortRegs[idx].pDevIns            = pDevIns;
+    pVM->iom.s.paIoPortRegs[idx].pfnOutCallback     = pfnOut    ? pfnOut    : iomR3IOPortDummyOut;
+    pVM->iom.s.paIoPortRegs[idx].pfnInCallback      = pfnIn     ? pfnIn     : iomR3IOPortDummyIn;
+    pVM->iom.s.paIoPortRegs[idx].pfnOutStrCallback  = pfnOutStr ? pfnOutStr : iomR3IOPortDummyOutStr;
+    pVM->iom.s.paIoPortRegs[idx].pfnInStrCallback   = pfnInStr  ? pfnInStr  : iomR3IOPortDummyInStr;
+    pVM->iom.s.paIoPortRegs[idx].pszDesc            = pszDesc;
+    pVM->iom.s.paIoPortRegs[idx].pPciDev            = pPciDev;
+    pVM->iom.s.paIoPortRegs[idx].iPciRegion         = iPciRegion;
+    pVM->iom.s.paIoPortRegs[idx].cPorts             = cPorts;
+    pVM->iom.s.paIoPortRegs[idx].uPort              = UINT16_MAX;
+    pVM->iom.s.paIoPortRegs[idx].idxStats           = (uint16_t)idxStats;
+    pVM->iom.s.paIoPortRegs[idx].fMapped            = false;
+    pVM->iom.s.paIoPortRegs[idx].idxSelf            = idx;
+
+    pVM->iom.s.cIoPortRegs = idx + 1;
+    *phIoPorts = idx;
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for PDMDEVHLPR3::pfnIoPortMap.
+ */
+VMMR3_INT_DECL(int)  IOMR3IoPortMap(PVM pVM, PPDMDEVINS pDevIns, IOMIOPORTHANDLE hIoPorts, RTIOPORT uPort)
+{
+    /*
+     * Validate input and state.
+     */
+    AssertPtrReturn(pDevIns, VERR_INVALID_HANDLE);
+    AssertReturn(hIoPorts < pVM->iom.s.cIoPortRegs, VERR_IOM_INVALID_IOPORT_HANDLE);
+    PIOMIOPORTENTRYR3 const pRegEntry = &pVM->iom.s.paIoPortRegs[hIoPorts];
+    AssertReturn(pRegEntry->pDevIns == pDevIns, VERR_IOM_INVALID_IOPORT_HANDLE);
+
+    RTIOPORT const cPorts = pRegEntry->cPorts;
+    AssertMsgReturn(cPorts > 0 && cPorts <= _8K, ("cPorts=%s\n", cPorts), VERR_IOM_IOPORT_IPE_1);
+    AssertReturn((uint32_t)uPort + cPorts <= _64K, VERR_OUT_OF_RANGE);
+    RTIOPORT const uLastPort = uPort + cPorts - 1;
+
+    /*
+     * Do the mapping.
+     */
+    int rc = VINF_SUCCESS;
+    IOM_LOCK_EXCL(pVM);
+
+    if (!pRegEntry->fMapped)
+    {
+        uint32_t const cEntries = RT_MIN(pVM->iom.s.cIoPortLookupEntries, pVM->iom.s.cIoPortRegs);
+        Assert(pVM->iom.s.cIoPortLookupEntries == cEntries);
+
+        PIOMIOPORTLOOKUPENTRY paEntries = pVM->iom.s.paIoPortLookup;
+        PIOMIOPORTLOOKUPENTRY pEntry;
+        if (cEntries > 0)
+        {
+            uint32_t iFirst = 0;
+            uint32_t iEnd   = cEntries;
+            uint32_t i      = cEntries / 2;
+            for (;;)
+            {
+                pEntry = &paEntries[i];
+                if (pEntry->uLastPort < uPort)
+                {
+                    i += 1;
+                    if (i < iEnd)
+                        iFirst = i;
+                    else
+                    {
+                        /* Insert after the entry we just considered: */
+                        pEntry += 1;
+                        if (iEnd < cEntries)
+                            memmove(pEntry + 1, pEntry, sizeof(*pEntry) * (cEntries - iEnd));
+                        break;
+                    }
+                }
+                else if (pEntry->uFirstPort > uLastPort)
+                {
+                    if (i > iFirst)
+                        iEnd = i;
+                    else
+                    {
+                        /* Insert at the entry we just considered: */
+                        if (iEnd < cEntries)
+                            memmove(pEntry + 1, pEntry, sizeof(*pEntry) * (cEntries - iEnd));
+                        break;
+                    }
+                }
+                else
+                {
+                    /* Oops! We've got a conflict. */
+                    AssertLogRelMsgFailed(("%u..%u (%s) conflicts with existing mapping %u..%u (%s)\n",
+                                           uPort, uLastPort, pRegEntry->pszDesc,
+                                           pEntry->uFirstPort, pEntry->uLastPort, pVM->iom.s.paIoPortRegs[pEntry->idx].pszDesc));
+                    IOM_UNLOCK_EXCL(pVM);
+                    return VERR_IOM_IOPORT_RANGE_CONFLICT;
+                }
+
+                i = iFirst + (iEnd - iFirst) / 2;
+            }
+        }
+        else
+            pEntry = paEntries;
+
+        /*
+         * Fill in the entry and bump the table size.
+         */
+        pEntry->idx        = hIoPorts;
+        pEntry->uFirstPort = uPort;
+        pEntry->uLastPort  = uLastPort;
+        pVM->iom.s.cIoPortLookupEntries = cEntries + 1;
+
+        pRegEntry->uPort   = uPort;
+        pRegEntry->fMapped = true;
+
+#ifdef VBOX_STRICT
+        /*
+         * Assert table sanity.
+         */
+        AssertMsg(paEntries[0].uLastPort >= paEntries[0].uFirstPort, ("%#x %#x\n", paEntries[0].uLastPort, paEntries[0].uFirstPort));
+        AssertMsg(paEntries[0].idx < pVM->iom.s.cIoPortRegs, ("%#x %#x\n", paEntries[0].idx, pVM->iom.s.cIoPortRegs));
+
+        RTIOPORT uPortPrev = paEntries[0].uLastPort;
+        for (size_t i = 1; i <= cEntries; i++)
+        {
+            AssertMsg(paEntries[i].uLastPort >= paEntries[i].uFirstPort, ("%u: %#x %#x\n", i, paEntries[i].uLastPort, paEntries[i].uFirstPort));
+            AssertMsg(paEntries[i].idx < pVM->iom.s.cIoPortRegs, ("%u: %#x %#x\n", i, paEntries[i].idx, pVM->iom.s.cIoPortRegs));
+            AssertMsg(uPortPrev < paEntries[i].uFirstPort, ("%u: %#x %#x\n", i, uPortPrev, paEntries[i].uFirstPort));
+            uPortPrev = paEntries[i].uLastPort;
+        }
+#endif
+    }
+    else
+    {
+        AssertFailed();
+        rc = VERR_IOM_IOPORTS_ALREADY_MAPPED;
+    }
+
+    IOM_UNLOCK_EXCL(pVM);
+    return rc;
+}
+
+
+/**
+ * Worker for PDMDEVHLPR3::pfnIoPortUnmap.
+ */
+VMMR3_INT_DECL(int)  IOMR3IoPortUnmap(PVM pVM, PPDMDEVINS pDevIns, IOMIOPORTHANDLE hIoPorts)
+{
+    /*
+     * Validate input and state.
+     */
+    AssertPtrReturn(pDevIns, VERR_INVALID_HANDLE);
+    AssertReturn(hIoPorts < pVM->iom.s.cIoPortRegs, VERR_IOM_INVALID_IOPORT_HANDLE);
+    PIOMIOPORTENTRYR3 const pRegEntry = &pVM->iom.s.paIoPortRegs[hIoPorts];
+    AssertReturn(pRegEntry->pDevIns == pDevIns, VERR_IOM_INVALID_IOPORT_HANDLE);
+
+    /*
+     * Do the mapping.
+     */
+    int rc;
+    IOM_LOCK_EXCL(pVM);
+
+    if (pRegEntry->fMapped)
+    {
+        RTIOPORT const uPort     = pRegEntry->uPort;
+        RTIOPORT const uLastPort = uPort + pRegEntry->cPorts - 1;
+        uint32_t const cEntries  = RT_MIN(pVM->iom.s.cIoPortLookupEntries, pVM->iom.s.cIoPortRegs);
+        Assert(pVM->iom.s.cIoPortLookupEntries == cEntries);
+        Assert(cEntries > 0);
+
+        PIOMIOPORTLOOKUPENTRY paEntries = pVM->iom.s.paIoPortLookup;
+        uint32_t iFirst = 0;
+        uint32_t iEnd   = cEntries;
+        uint32_t i      = cEntries / 2;
+        for (;;)
+        {
+            PIOMIOPORTLOOKUPENTRY pEntry = &paEntries[i];
+            if (pEntry->uLastPort < uPort)
+            {
+                i += 1;
+                if (i < iEnd)
+                    iFirst = i;
+                else
+                {
+                    rc = VERR_IOM_IOPORT_IPE_1;
+                    AssertLogRelMsgFailedBreak(("%u..%u (%s) not found!\n", uPort, uLastPort, pRegEntry->pszDesc));
+                }
+            }
+            else if (pEntry->uFirstPort > uLastPort)
+            {
+                if (i > iFirst)
+                    iEnd = i;
+                else
+                {
+                    rc = VERR_IOM_IOPORT_IPE_1;
+                    AssertLogRelMsgFailedBreak(("%u..%u (%s) not found!\n", uPort, uLastPort, pRegEntry->pszDesc));
+                }
+            }
+            else if (pEntry->idx == hIoPorts)
+            {
+                Assert(pEntry->uFirstPort == uPort);
+                Assert(pEntry->uLastPort == uLastPort);
+                if (i + 1 < cEntries)
+                    memmove(pEntry, pEntry + 1, sizeof(*pEntry) * (cEntries - i - 1));
+                pVM->iom.s.cIoPortLookupEntries = cEntries - 1;
+                pRegEntry->uPort   = UINT16_MAX;
+                pRegEntry->fMapped = false;
+                rc = VINF_SUCCESS;
+                break;
+            }
+            else
+            {
+                AssertLogRelMsgFailed(("Lookig for %u..%u (%s), found %u..%u (%s) instead!\n",
+                                       uPort, uLastPort, pRegEntry->pszDesc,
+                                       pEntry->uFirstPort, pEntry->uLastPort, pVM->iom.s.paIoPortRegs[pEntry->idx].pszDesc));
+                rc = VERR_IOM_IOPORT_IPE_1;
+                break;
+            }
+
+            i = iFirst + (iEnd - iFirst) / 2;
+        }
+
+#ifdef VBOX_STRICT
+        /*
+         * Assert table sanity.
+         */
+        AssertMsg(paEntries[0].uLastPort >= paEntries[0].uFirstPort, ("%#x %#x\n", paEntries[0].uLastPort, paEntries[0].uFirstPort));
+        AssertMsg(paEntries[0].idx < pVM->iom.s.cIoPortRegs, ("%#x %#x\n", paEntries[0].idx, pVM->iom.s.cIoPortRegs));
+
+        RTIOPORT uPortPrev = paEntries[0].uLastPort;
+        for (i = 1; i <= cEntries; i++)
+        {
+            AssertMsg(paEntries[i].uLastPort >= paEntries[i].uFirstPort, ("%u: %#x %#x\n", i, paEntries[i].uLastPort, paEntries[i].uFirstPort));
+            AssertMsg(paEntries[i].idx < pVM->iom.s.cIoPortRegs, ("%u: %#x %#x\n", i, paEntries[i].idx, pVM->iom.s.cIoPortRegs));
+            AssertMsg(uPortPrev < paEntries[i].uFirstPort, ("%u: %#x %#x\n", i, uPortPrev, paEntries[i].uFirstPort));
+            uPortPrev = paEntries[i].uLastPort;
+        }
+#endif
+    }
+    else
+    {
+        AssertFailed();
+        rc = VERR_IOM_IOPORTS_NOT_MAPPED;
+    }
+
+    IOM_UNLOCK_EXCL(pVM);
+    return rc;
+}
+
 
 #ifdef VBOX_WITH_STATISTICS
Index: /trunk/src/VBox/VMM/VMMR3/PDMDevHlp.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/PDMDevHlp.cpp	(revision 80640)
+++ /trunk/src/VBox/VMM/VMMR3/PDMDevHlp.cpp	(revision 80641)
@@ -104,6 +104,18 @@
                                                     RTR3PTR pvUser, const char *pszDesc, PIOMIOPORTHANDLE phIoPorts)
 {
-    RT_NOREF(pDevIns, cPorts, fFlags, pPciDev, iPciRegion, pfnOut, pfnIn, pfnOutStr, pfnInStr, pvUser, pszDesc, phIoPorts);
-    return VERR_NOT_IMPLEMENTED;
+    PDMDEV_ASSERT_DEVINS(pDevIns);
+    LogFlow(("pdmR3DevHlp_IoPortCreateEx: caller='%s'/%d: cPorts=%#x fFlags=%#x pPciDev=%p iPciRegion=%#x pfnOut=%p pfnIn=%p pfnOutStr=%p pfnInStr=%p pvUser=%p pszDesc=%p:{%s} phIoPorts=%p\n",
+             pDevIns->pReg->szName, pDevIns->iInstance, cPorts, fFlags, pPciDev, iPciRegion, pfnOut, pfnIn, pfnOutStr, pfnInStr,
+             pvUser, pszDesc, pszDesc, phIoPorts));
+    PVM pVM = pDevIns->Internal.s.pVMR3;
+    VM_ASSERT_EMT0_RETURN(pVM, VERR_VM_THREAD_NOT_EMT);
+    VM_ASSERT_STATE_RETURN(pVM, VMSTATE_CREATING, VERR_VM_INVALID_VM_STATE);
+
+    int rc = IOMR3IoPortCreate(pVM, pDevIns, cPorts, fFlags, pPciDev, iPciRegion,
+                               pfnOut, pfnIn, pfnOutStr, pfnInStr, pvUser, pszDesc, phIoPorts);
+
+    LogFlow(("pdmR3DevHlp_IoPortCreateEx: caller='%s'/%d: returns %Rrc (*phIoPorts=%#x)\n",
+             pDevIns->pReg->szName, pDevIns->iInstance, rc, *phIoPorts));
+    return rc;
 }
 
@@ -112,6 +124,13 @@
 static DECLCALLBACK(int) pdmR3DevHlp_IoPortMap(PPDMDEVINS pDevIns, IOMIOPORTHANDLE hIoPorts, RTIOPORT Port)
 {
-    RT_NOREF(pDevIns, hIoPorts, Port);
-    return VERR_NOT_IMPLEMENTED;
+    PDMDEV_ASSERT_DEVINS(pDevIns);
+    LogFlow(("pdmR3DevHlp_IoPortMap: caller='%s'/%d: hIoPorts=%#x Port=%#x\n", pDevIns->pReg->szName, pDevIns->iInstance, hIoPorts, Port));
+    PVM pVM = pDevIns->Internal.s.pVMR3;
+    VM_ASSERT_EMT_RETURN(pVM, VERR_VM_THREAD_NOT_EMT);
+
+    int rc = IOMR3IoPortMap(pVM, pDevIns, hIoPorts, Port);
+
+    LogFlow(("pdmR3DevHlp_IoPortMap: caller='%s'/%d: returns %Rrc\n", pDevIns->pReg->szName, pDevIns->iInstance, rc));
+    return rc;
 }
 
@@ -120,6 +139,13 @@
 static DECLCALLBACK(int) pdmR3DevHlp_IoPortUnmap(PPDMDEVINS pDevIns, IOMIOPORTHANDLE hIoPorts)
 {
-    RT_NOREF(pDevIns, hIoPorts);
-    return VERR_NOT_IMPLEMENTED;
+    PDMDEV_ASSERT_DEVINS(pDevIns);
+    LogFlow(("pdmR3DevHlp_IoPortMap: caller='%s'/%d: hIoPorts=%#x\n", pDevIns->pReg->szName, pDevIns->iInstance, hIoPorts));
+    PVM pVM = pDevIns->Internal.s.pVMR3;
+    VM_ASSERT_EMT_RETURN(pVM, VERR_VM_THREAD_NOT_EMT);
+
+    int rc = IOMR3IoPortUnmap(pVM, pDevIns, hIoPorts);
+
+    LogFlow(("pdmR3DevHlp_IoPortMap: caller='%s'/%d: returns %Rrc\n", pDevIns->pReg->szName, pDevIns->iInstance, rc));
+    return rc;
 }
 
@@ -129,5 +155,5 @@
 {
     PDMDEV_ASSERT_DEVINS(pDevIns);
-    LogFlow(("pdmR3DevHlp_IOPortRegister: caller='%s'/%d: Port=%#x cPorts=%#x pvUser=%p pfnOut=%p pfnIn=%p pfnOutStr=%p pfnInStr=%p p32_tszDesc=%p:{%s}\n", pDevIns->pReg->szName, pDevIns->iInstance,
+    LogFlow(("pdmR3DevHlp_IOPortRegister: caller='%s'/%d: Port=%#x cPorts=%#x pvUser=%p pfnOut=%p pfnIn=%p pfnOutStr=%p pfnInStr=%p pszDesc=%p:{%s}\n", pDevIns->pReg->szName, pDevIns->iInstance,
              Port, cPorts, pvUser, pfnOut, pfnIn, pfnOutStr, pfnInStr, pszDesc, pszDesc));
     VM_ASSERT_EMT(pDevIns->Internal.s.pVMR3);
Index: /trunk/src/VBox/VMM/include/IOMInline.h
===================================================================
--- /trunk/src/VBox/VMM/include/IOMInline.h	(revision 80640)
+++ /trunk/src/VBox/VMM/include/IOMInline.h	(revision 80641)
@@ -28,4 +28,111 @@
  * @{
  */
+
+
+/**
+ * Gets the I/O port entry for the specified I/O port in the current context.
+ *
+ * @returns Pointer to I/O port entry.
+ * @returns NULL if no port registered.
+ *
+ * @param   pVM             The cross context VM structure.
+ * @param   Port            The I/O port lookup.
+ * @param   pidxLastHint    Pointer to IOMCPU::idxIoPortLastRead or
+ *                          IOMCPU::idxIoPortLastWrite.
+ *
+ * @note    In ring-0 it is possible to get an uninitialized entry (pDevIns is
+ *          NULL, cPorts is 0), in which case there should be ring-3 handlers
+ *          for the entry.  Use IOMIOPORTENTRYR0::idxSelf to get the ring-3
+ *          entry.
+ */
+DECLINLINE(CTX_SUFF(PIOMIOPORTENTRY)) iomIoPortGetEntry(PVMCC pVM, RTIOPORT uPort, uint16_t *pidxLastHint)
+{
+    Assert(IOM_IS_SHARED_LOCK_OWNER(pVM));
+
+#ifdef IN_RING0
+    uint32_t              iEnd      = RT_MIN(pVM->iom.s.cIoPortLookupEntries, pVM->iomr0.s.cIoPortAlloc);
+    PIOMIOPORTLOOKUPENTRY paLookup  = pVM->iomr0.s.paIoPortLookup;
+#else
+    uint32_t              iEnd      = pVM->iom.s.cIoPortLookupEntries;
+    PIOMIOPORTLOOKUPENTRY paLookup  = pVM->iom.s.paIoPortLookup;
+#endif
+    if (iEnd > 0)
+    {
+        uint32_t iFirst = 0;
+        uint32_t i      = *pidxLastHint;
+        if (i < iEnd)
+        { /* likely */ }
+        else
+            i = iEnd / 2;
+        for (;;)
+        {
+            PIOMIOPORTLOOKUPENTRY pCur = &paLookup[i];
+            if (pCur->uFirstPort > uPort)
+            {
+                if (i > iFirst)
+                    iEnd = i;
+                else
+                    return NULL;
+            }
+            else if (pCur->uLastPort < uPort)
+            {
+                i += 1;
+                if (i < iEnd)
+                    iFirst = i;
+                else
+                    return NULL;
+            }
+            else
+            {
+                *pidxLastHint = (uint16_t)i;
+
+                /*
+                 * Translate the 'idx' member into a pointer.
+                 */
+                size_t const idx = pCur->idx;
+#ifdef IN_RING0
+                AssertMsg(idx < pVM->iom.s.cIoPortRegs && idx < pVM->iomr0.s.cIoPortAlloc,
+                          ("%#zx vs %#x/%x (port %#x)\n", idx, pVM->iom.s.cIoPortRegs, pVM->iomr0.s.cIoPortMax, uPort));
+                if (idx < pVM->iomr0.s.cIoPortAlloc)
+                    return &pVM->iomr0.s.paIoPortRegs[idx];
+#else
+                if (idx < pVM->iom.s.cIoPortRegs)
+                    return &pVM->iom.s.paIoPortRegs[idx];
+                AssertMsgFailed(("%#zx vs %#x (port %#x)\n", idx, pVM->iom.s.cIoPortRegs, uPort));
+#endif
+                break;
+            }
+
+            i = iFirst + (iEnd - iFirst) / 2;
+        }
+    }
+    return NULL;
+}
+
+
+#ifdef VBOX_WITH_STATISTICS
+/**
+ * Gets the I/O port statistics entry .
+ *
+ * @returns Pointer to stats.  Instead of NULL, a pointer to IoPortDummyStats is
+ *          returned, so the caller does not need to check for NULL.
+ *
+ * @param   pVM         The cross context VM structure.
+ * @param   pRegEntry   The I/O port entry to get stats for.
+ */
+DECLINLINE(PIOMIOPORTSTATSENTRY) iomIoPortGetStats(PVMCC pVM, CTX_SUFF(PIOMIOPORTENTRY) pRegEntry)
+{
+    size_t idxStats = pRegEntry->idxStats;
+# ifdef IN_RING0
+    if (idxStats < pVM->iomr0.s.cIoPortStatsAllocation)
+        return &pVM->iomr0.s.paIoPortStats[idxStats];
+# else
+    if (idxStats < pVM->iom.s.cIoPortStats)
+        return &pVM->iom.s.paIoPortStats[idxStats];
+# endif
+    return &pVM->iom.s.IoPortDummyStats;
+}
+#endif
+
 
 /**
Index: /trunk/src/VBox/VMM/include/IOMInternal.h
===================================================================
--- /trunk/src/VBox/VMM/include/IOMInternal.h	(revision 80640)
+++ /trunk/src/VBox/VMM/include/IOMInternal.h	(revision 80641)
@@ -139,4 +139,122 @@
 /** Pointer to I/O port statistics. */
 typedef IOMMMIOSTATS *PIOMMMIOSTATS;
+
+/**
+ * I/O port lookup table entry.
+ */
+typedef struct IOMIOPORTLOOKUPENTRY
+{
+    /** The first port in the range. */
+    RTIOPORT                    uFirstPort;
+    /** The last port in the range (inclusive). */
+    RTIOPORT                    uLastPort;
+    /** The registration handle/index. */
+    uint16_t                    idx;
+} IOMIOPORTLOOKUPENTRY;
+/** Pointer to an I/O port lookup table entry. */
+typedef IOMIOPORTLOOKUPENTRY *PIOMIOPORTLOOKUPENTRY;
+/** Pointer to a const I/O port lookup table entry. */
+typedef IOMIOPORTLOOKUPENTRY const *PCIOMIOPORTLOOKUPENTRY;
+
+/**
+ * Ring-0 I/O port handle table entry.
+ */
+typedef struct IOMIOPORTENTRYR0
+{
+    /** Pointer to user argument. */
+    RTR0PTR                             pvUser;
+    /** Pointer to the associated device instance, NULL if entry not used. */
+    R0PTRTYPE(PPDMDEVINS)               pDevIns;
+    /** Pointer to OUT callback function. */
+    R0PTRTYPE(PFNIOMIOPORTOUT)          pfnOutCallback;
+    /** Pointer to IN callback function. */
+    R0PTRTYPE(PFNIOMIOPORTIN)           pfnInCallback;
+    /** Pointer to string OUT callback function. */
+    R0PTRTYPE(PFNIOMIOPORTOUTSTRING)    pfnOutStrCallback;
+    /** Pointer to string IN callback function. */
+    R0PTRTYPE(PFNIOMIOPORTINSTRING)     pfnInStrCallback;
+    /** The entry of the first statistics entry, UINT16_MAX if no stats. */
+    uint16_t                            idxStats;
+    /** The number of ports covered by this entry, 0 if entry not used. */
+    RTIOPORT                            cPorts;
+    /** Same as the handle index. */
+    uint16_t                            idxSelf;
+} IOMIOPORTENTRYR0;
+/** Pointer to a ring-0 I/O port handle table entry. */
+typedef IOMIOPORTENTRYR0 *PIOMIOPORTENTRYR0;
+/** Pointer to a const ring-0 I/O port handle table entry. */
+typedef IOMIOPORTENTRYR0 const *PCIOMIOPORTENTRYR0;
+
+/**
+ * Ring-3 I/O port handle table entry.
+ */
+typedef struct IOMIOPORTENTRYR3
+{
+    /** Pointer to user argument. */
+    RTR3PTR                             pvUser;
+    /** Pointer to the associated device instance. */
+    R3PTRTYPE(PPDMDEVINS)               pDevIns;
+    /** Pointer to OUT callback function. */
+    R3PTRTYPE(PFNIOMIOPORTOUT)          pfnOutCallback;
+    /** Pointer to IN callback function. */
+    R3PTRTYPE(PFNIOMIOPORTIN)           pfnInCallback;
+    /** Pointer to string OUT callback function. */
+    R3PTRTYPE(PFNIOMIOPORTOUTSTRING)    pfnOutStrCallback;
+    /** Pointer to string IN callback function. */
+    R3PTRTYPE(PFNIOMIOPORTINSTRING)     pfnInStrCallback;
+    /** Description / Name. For easing debugging. */
+    R3PTRTYPE(const char *)             pszDesc;
+    /** PCI device the registration is associated with. */
+    R3PTRTYPE(PPDMPCIDEV)               pPciDev;
+    /** The PCI device region (high 16-bit word) and subregion (low word),
+     *  UINT32_MAX if not applicable. */
+    uint32_t                            iPciRegion;
+    /** The number of ports covered by this entry. */
+    RTIOPORT                            cPorts;
+    /** The current port mapping (duplicates lookup table). */
+    RTIOPORT                            uPort;
+    /** The entry of the first statistics entry, UINT16_MAX if no stats. */
+    uint16_t                            idxStats;
+    /** Set if mapped, clear if not.
+     * Only updated when critsect is held exclusively.   */
+    bool                                fMapped;
+    /** Same as the handle index. */
+    uint16_t                            idxSelf;
+} IOMIOPORTENTRYR3;
+/** Pointer to a ring-3 I/O port handle table entry. */
+typedef IOMIOPORTENTRYR3 *PIOMIOPORTENTRYR3;
+/** Pointer to a const ring-3 I/O port handle table entry. */
+typedef IOMIOPORTENTRYR3 const *PCIOMIOPORTENTRYR3;
+
+/**
+ * I/O port statistics entry (one I/O port).
+ */
+typedef struct IOMIOPORTSTATSENTRY
+{
+    /** Number of INs to this port from R3. */
+    STAMCOUNTER                 InR3;
+    /** Profiling IN handler overhead in R3. */
+    STAMPROFILE                 ProfInR3;
+    /** Number of OUTs to this port from R3. */
+    STAMCOUNTER                 OutR3;
+    /** Profiling OUT handler overhead in R3. */
+    STAMPROFILE                 ProfOutR3;
+
+    /** Number of INs to this port from R0/RC. */
+    STAMCOUNTER                 InRZ;
+    /** Profiling IN handler overhead in R0/RC. */
+    STAMPROFILE                 ProfInRZ;
+    /** Number of INs to this port from R0/RC which was serviced in R3. */
+    STAMCOUNTER                 InRZToR3;
+
+    /** Number of OUTs to this port from R0/RC. */
+    STAMCOUNTER                 OutRZ;
+    /** Profiling OUT handler overhead in R0/RC. */
+    STAMPROFILE                 ProfOutRZ;
+    /** Number of OUTs to this port from R0/RC which was serviced in R3. */
+    STAMCOUNTER                 OutRZToR3;
+} IOMIOPORTSTATSENTRY;
+/** Pointer to I/O port statistics entry. */
+typedef IOMIOPORTSTATSENTRY *PIOMIOPORTSTATSENTRY;
 
 
@@ -281,4 +399,5 @@
 /**
  * The IOM trees.
+ *
  * These are offset based the nodes and root must be in the same
  * memory block in HC. The locations of IOM structure and the hypervisor heap
@@ -306,78 +425,4 @@
 /** Pointer to the IOM trees. */
 typedef IOMTREES *PIOMTREES;
-
-
-/**
- * Converts an IOM pointer into a VM pointer.
- * @returns Pointer to the VM structure the PGM is part of.
- * @param   pIOM   Pointer to IOM instance data.
- */
-#define IOM2VM(pIOM)  ( (PVM)((char*)pIOM - pIOM->offVM) )
-
-/**
- * IOM Data (part of VM)
- */
-typedef struct IOM
-{
-    /** Pointer to the trees - R3 ptr. */
-    R3PTRTYPE(PIOMTREES)            pTreesR3;
-    /** Pointer to the trees - R0 ptr. */
-    R0PTRTYPE(PIOMTREES)            pTreesR0;
-
-    /** MMIO physical access handler type.   */
-    PGMPHYSHANDLERTYPE              hMmioHandlerType;
-    uint32_t                        u32Padding;
-
-    /** Lock serializing EMT access to IOM. */
-#ifdef IOM_WITH_CRIT_SECT_RW
-    PDMCRITSECTRW                   CritSect;
-#else
-    PDMCRITSECT                     CritSect;
-#endif
-
-    /** @name I/O Port statistics.
-     * @{ */
-    STAMCOUNTER                     StatInstIn;
-    STAMCOUNTER                     StatInstOut;
-    STAMCOUNTER                     StatInstIns;
-    STAMCOUNTER                     StatInstOuts;
-    /** @} */
-
-    /** @name MMIO statistics.
-     * @{ */
-    STAMPROFILE                     StatRZMMIOHandler;
-    STAMCOUNTER                     StatRZMMIOFailures;
-
-    STAMPROFILE                     StatRZInstMov;
-    STAMPROFILE                     StatRZInstCmp;
-    STAMPROFILE                     StatRZInstAnd;
-    STAMPROFILE                     StatRZInstOr;
-    STAMPROFILE                     StatRZInstXor;
-    STAMPROFILE                     StatRZInstBt;
-    STAMPROFILE                     StatRZInstTest;
-    STAMPROFILE                     StatRZInstXchg;
-    STAMPROFILE                     StatRZInstStos;
-    STAMPROFILE                     StatRZInstLods;
-#ifdef IOM_WITH_MOVS_SUPPORT
-    STAMPROFILEADV                  StatRZInstMovs;
-    STAMPROFILE                     StatRZInstMovsToMMIO;
-    STAMPROFILE                     StatRZInstMovsFromMMIO;
-    STAMPROFILE                     StatRZInstMovsMMIO;
-#endif
-    STAMCOUNTER                     StatRZInstOther;
-
-    STAMCOUNTER                     StatRZMMIO1Byte;
-    STAMCOUNTER                     StatRZMMIO2Bytes;
-    STAMCOUNTER                     StatRZMMIO4Bytes;
-    STAMCOUNTER                     StatRZMMIO8Bytes;
-
-    STAMCOUNTER                     StatR3MMIOHandler;
-
-    RTUINT                          cMovsMaxBytes;
-    RTUINT                          cStosMaxBytes;
-    /** @} */
-} IOM;
-/** Pointer to IOM instance data. */
-typedef IOM *PIOM;
 
 
@@ -433,4 +478,14 @@
      * (Saves quite some time in rep outs/ins instruction emulation.)
      * @{ */
+    /** I/O port registration index for the last read operation. */
+    uint16_t                            idxIoPortLastRead;
+    /** I/O port registration index for the last write operation. */
+    uint16_t                            idxIoPortLastWrite;
+    /** I/O port registration index for the last read string operation. */
+    uint16_t                            idxIoPortLastReadStr;
+    /** I/O port registration index for the last write string operation. */
+    uint16_t                            idxIoPortLastWriteStr;
+    uint32_t                            u32Padding;
+
     R3PTRTYPE(PIOMIOPORTRANGER3)    pRangeLastReadR3;
     R3PTRTYPE(PIOMIOPORTRANGER3)    pRangeLastWriteR3;
@@ -450,4 +505,138 @@
 /** Pointer to IOM per virtual CPU instance data. */
 typedef IOMCPU *PIOMCPU;
+
+
+/**
+ * IOM Data (part of VM)
+ */
+typedef struct IOM
+{
+    /** Pointer to the trees - R3 ptr. */
+    R3PTRTYPE(PIOMTREES)            pTreesR3;
+    /** Pointer to the trees - R0 ptr. */
+    R0PTRTYPE(PIOMTREES)            pTreesR0;
+
+    /** MMIO physical access handler type.   */
+    PGMPHYSHANDLERTYPE              hMmioHandlerType;
+    uint32_t                        u32Padding;
+
+    /** @name I/O ports
+     * @note The updating of these variables is done exclusively from EMT(0).
+     * @{ */
+    /** Number of I/O port registrations. */
+    uint32_t                        cIoPortRegs;
+    /** The size of the paIoPortsRegs allocation (in entries). */
+    uint32_t                        cIoPortAlloc;
+    /** I/O port registration table for ring-3.
+     * There is a parallel table in ring-0, IOMR0PERVM::paIoPortRegs. */
+    R3PTRTYPE(PIOMIOPORTENTRYR3)    paIoPortRegs;
+    /** Number of entries in the lookup table. */
+    uint32_t                        cIoPortLookupEntries;
+    uint32_t                        u32Padding1;
+    /** I/O port lookup table. */
+    R3PTRTYPE(PIOMIOPORTLOOKUPENTRY) paIoPortLookup;
+
+    /** The number of valid entries in paioPortStats. */
+    uint32_t                        cIoPortStats;
+    /** The size of the paIoPortStats allocation (in entries). */
+    uint32_t                        cIoPortStatsAllocation;
+    /** I/O port lookup table.   */
+    R3PTRTYPE(PIOMIOPORTSTATSENTRY) paIoPortStats;
+    /** Dummy stats entry so we don't need to check for NULL pointers so much. */
+    IOMIOPORTSTATSENTRY             IoPortDummyStats;
+    /** @} */
+
+
+    /** Lock serializing EMT access to IOM. */
+#ifdef IOM_WITH_CRIT_SECT_RW
+    PDMCRITSECTRW                   CritSect;
+#else
+    PDMCRITSECT                     CritSect;
+#endif
+
+#if 0 /* unused */
+    /** @name I/O Port statistics.
+     * @{ */
+    STAMCOUNTER                     StatInstIn;
+    STAMCOUNTER                     StatInstOut;
+    STAMCOUNTER                     StatInstIns;
+    STAMCOUNTER                     StatInstOuts;
+    /** @} */
+#endif
+
+    /** @name MMIO statistics.
+     * @{ */
+    STAMPROFILE                     StatRZMMIOHandler;
+    STAMCOUNTER                     StatRZMMIOFailures;
+
+    STAMPROFILE                     StatRZInstMov;
+    STAMPROFILE                     StatRZInstCmp;
+    STAMPROFILE                     StatRZInstAnd;
+    STAMPROFILE                     StatRZInstOr;
+    STAMPROFILE                     StatRZInstXor;
+    STAMPROFILE                     StatRZInstBt;
+    STAMPROFILE                     StatRZInstTest;
+    STAMPROFILE                     StatRZInstXchg;
+    STAMPROFILE                     StatRZInstStos;
+    STAMPROFILE                     StatRZInstLods;
+#ifdef IOM_WITH_MOVS_SUPPORT
+    STAMPROFILEADV                  StatRZInstMovs;
+    STAMPROFILE                     StatRZInstMovsToMMIO;
+    STAMPROFILE                     StatRZInstMovsFromMMIO;
+    STAMPROFILE                     StatRZInstMovsMMIO;
+#endif
+    STAMCOUNTER                     StatRZInstOther;
+
+    STAMCOUNTER                     StatRZMMIO1Byte;
+    STAMCOUNTER                     StatRZMMIO2Bytes;
+    STAMCOUNTER                     StatRZMMIO4Bytes;
+    STAMCOUNTER                     StatRZMMIO8Bytes;
+
+    STAMCOUNTER                     StatR3MMIOHandler;
+
+    RTUINT                          cMovsMaxBytes;
+    RTUINT                          cStosMaxBytes;
+    /** @} */
+} IOM;
+/** Pointer to IOM instance data. */
+typedef IOM *PIOM;
+
+
+/**
+ * IOM data kept in the ring-0 GVM.
+ */
+typedef struct IOMR0PERVM
+{
+    /** @name I/O ports
+     * @{ */
+    /** The higest ring-0 I/O port registration plus one. */
+    uint32_t                        cIoPortMax;
+    /** The size of the paIoPortsRegs allocation (in entries). */
+    uint32_t                        cIoPortAlloc;
+    /** I/O port registration table for ring-0.
+     * There is a parallel table for ring-3, paIoPortRing3Regs. */
+    R0PTRTYPE(PIOMIOPORTENTRYR0)    paIoPortRegs;
+    /** I/O port lookup table. */
+    R0PTRTYPE(PIOMIOPORTLOOKUPENTRY) paIoPortLookup;
+    /** I/O port registration table for ring-3.
+     * Also mapped to ring-3 as IOM::paIoPortRegs. */
+    R0PTRTYPE(PIOMIOPORTENTRYR3)    paIoPortRing3Regs;
+    /** Handle to the allocation backing both the ring-0 and ring-3 registration
+     * tables as well as the lookup table. */
+    RTR0MEMOBJ                      hIoPortMemObj;
+    /** Handle to the ring-3 mapping of the lookup and ring-3 registration table. */
+    RTR0MEMOBJ                      hIoPortMapObj;
+#ifdef VBOX_WITH_STATISTICS
+    /** The size of the paIoPortStats allocation (in entries). */
+    uint32_t                        cIoPortStatsAllocation;
+    /** I/O port lookup table.   */
+    R0PTRTYPE(PIOMIOPORTSTATSENTRY) paIoPortStats;
+    /** Handle to the allocation backing the I/O port statistics. */
+    RTR0MEMOBJ                      hIoPortStatsMemObj;
+    /** Handle to the ring-3 mapping of the I/O port statistics. */
+    RTR0MEMOBJ                      hIoPortStatsMapObj;
+#endif
+    /** @} */
+} IOMR0PERVM;
 
 
Index: /trunk/src/VBox/VMM/include/NEMInternal.h
===================================================================
--- /trunk/src/VBox/VMM/include/NEMInternal.h	(revision 80640)
+++ /trunk/src/VBox/VMM/include/NEMInternal.h	(revision 80641)
@@ -51,5 +51,5 @@
 //# define NEM_WIN_USE_OUR_OWN_RUN_API          /**< Applies to ring-3 code only. Useful for testing VID API. */
 # define NEM_WIN_WITH_RING0_RUNLOOP             /**< Enables the ring-0 runloop. */
-# define NEM_WIN_USE_RING0_RUNLOOP_BY_DEFAULT   /**< For quickly testing ring-3 API without messing with CFGM. */
+//# define NEM_WIN_USE_RING0_RUNLOOP_BY_DEFAULT   /**< For quickly testing ring-3 API without messing with CFGM. */
 # if defined(NEM_WIN_USE_OUR_OWN_RUN_API) && !defined(NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS)
 #  error "NEM_WIN_USE_OUR_OWN_RUN_API requires NEM_WIN_USE_HYPERCALLS_FOR_REGISTERS"
