Index: /trunk/include/VBox/err.h
===================================================================
--- /trunk/include/VBox/err.h	(revision 81166)
+++ /trunk/include/VBox/err.h	(revision 81167)
@@ -1145,8 +1145,4 @@
  * 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. */
@@ -1170,11 +1166,20 @@
 
 /** Too many I/O port registrations. */
-#define VERR_IOM_TOO_MANY_IOPORT_REGISTRATIONS (-2650)
+#define VERR_IOM_TOO_MANY_IOPORT_REGISTRATIONS  (-2650)
 /** Invalid I/O port handle. */
-#define VERR_IOM_INVALID_IOPORT_HANDLE      (-2651)
+#define VERR_IOM_INVALID_IOPORT_HANDLE          (-2651)
 /** I/O ports are already mapped. */
-#define VERR_IOM_IOPORTS_ALREADY_MAPPED     (-2652)
+#define VERR_IOM_IOPORTS_ALREADY_MAPPED         (-2652)
 /** I/O ports are not mapped. */
-#define VERR_IOM_IOPORTS_NOT_MAPPED         (-2653)
+#define VERR_IOM_IOPORTS_NOT_MAPPED             (-2653)
+
+/** Too many MMIO registrations. */
+#define VERR_IOM_TOO_MANY_MMIO_REGISTRATIONS    (-2660)
+/** Invalid MMIO handle. */
+#define VERR_IOM_INVALID_MMIO_HANDLE            (-2661)
+/** MMIO region is already mapped. */
+#define VERR_IOM_MMIO_REGION_ALREADY_MAPPED     (-2662)
+/** MMIO region is not mapped. */
+#define VERR_IOM_MMIO_REGION_NOT_MAPPED         (-2663)
 /** @} */
 
Index: /trunk/include/VBox/vmm/iom.h
===================================================================
--- /trunk/include/VBox/vmm/iom.h	(revision 81166)
+++ /trunk/include/VBox/vmm/iom.h	(revision 81167)
@@ -573,4 +573,6 @@
 VMMR0_INT_DECL(int)  IOMR0IoPortGrowRegistrationTables(PGVM pGVM, uint64_t cMinEntries);
 VMMR0_INT_DECL(int)  IOMR0IoPortGrowStatisticsTable(PGVM pGVM, uint64_t cMinEntries);
+VMMR0_INT_DECL(int)  IOMR0MmioGrowRegistrationTables(PGVM pGVM, uint64_t cMinEntries);
+VMMR0_INT_DECL(int)  IOMR0MmioGrowStatisticsTable(PGVM pGVM, uint64_t cMinEntries);
 
 
Index: /trunk/include/VBox/vmm/vmm.h
===================================================================
--- /trunk/include/VBox/vmm/vmm.h	(revision 81166)
+++ /trunk/include/VBox/vmm/vmm.h	(revision 81167)
@@ -415,4 +415,8 @@
     /** Grow the I/O port statistics tables. */
     VMMR0_DO_IOM_GROW_IO_PORT_STATS,
+    /** Grow the MMIO registration tables. */
+    VMMR0_DO_IOM_GROW_MMIO_REGS,
+    /** Grow the MMIO statistics tables. */
+    VMMR0_DO_IOM_GROW_MMIO_STATS,
 
     /** Official call we use for testing Ring-0 APIs. */
Index: /trunk/src/VBox/VMM/Makefile.kmk
===================================================================
--- /trunk/src/VBox/VMM/Makefile.kmk	(revision 81166)
+++ /trunk/src/VBox/VMM/Makefile.kmk	(revision 81167)
@@ -121,4 +121,5 @@
 	VMMR3/IOM.cpp \
 	VMMR3/IOMR3IoPort.cpp \
+	VMMR3/IOMR3Mmio.cpp \
 	VMMR3/GMM.cpp \
 	VMMR3/MM.cpp \
@@ -482,4 +483,5 @@
 	VMMR0/HMSVMR0.cpp \
 	VMMR0/IOMR0.cpp \
+	VMMR0/IOMR0IoPort.cpp \
 	VMMR0/PDMR0Device.cpp \
 	VMMR0/PDMR0Driver.cpp \
Index: /trunk/src/VBox/VMM/VMMR0/IOMR0.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/IOMR0.cpp	(revision 81166)
+++ /trunk/src/VBox/VMM/VMMR0/IOMR0.cpp	(revision 81167)
@@ -23,13 +23,8 @@
 #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>
+#include <iprt/assertcompile.h>
 
 
@@ -48,10 +43,5 @@
     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
+    iomR0IoPortInitPerVMData(pGVM);
 }
 
@@ -62,274 +52,5 @@
 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
+    iomR0IoPortCleanupVM(pGVM);
 }
 
-
-/**
- * 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,
-                                             PFNIOMIOPORTNEWOUT pfnOut,  PFNIOMIOPORTNEWIN pfnIn,
-                                             PFNIOMIOPORTNEWOUTSTRING pfnOutStr, PFNIOMIOPORTNEWINSTRING 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);
-
-    uint16_t const fFlags = pGVM->iomr0.s.paIoPortRing3Regs[hIoPorts].fFlags;
-    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;
-    pGVM->iomr0.s.paIoPortRegs[hIoPorts].fFlags             = fFlags;
-#ifdef VBOX_WITH_STATISTICS
-    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;
-#else
-    pGVM->iomr0.s.paIoPortRegs[hIoPorts].idxStats           = UINT16_MAX;
-#endif
-
-    pGVM->iomr0.s.paIoPortRing3Regs[hIoPorts].fRing0 = true;
-
-    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;
-#ifdef VBOX_WITH_STATISTICS
-    uint32_t const cOldEntries = pGVM->iomr0.s.cIoPortStatsAllocation;
-    ASMCompilerBarrier();
-#else
-    uint32_t const cOldEntries = 0;
-#endif
-    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/IOMR0IoPort.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/IOMR0IoPort.cpp	(revision 81167)
+++ /trunk/src/VBox/VMM/VMMR0/IOMR0IoPort.cpp	(revision 81167)
@@ -0,0 +1,329 @@
+/* $Id$ */
+/** @file
+ * IOM - Host Context Ring 0, I/O ports.
+ */
+
+/*
+ * 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 I/O port related members.
+ *
+ * @param   pGVM    Pointer to the global VM structure.
+ */
+void iomR0IoPortInitPerVMData(PGVM pGVM)
+{
+    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 I/O port related resources.
+ */
+void iomR0IoPortCleanupVM(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,
+                                             PFNIOMIOPORTNEWOUT pfnOut,  PFNIOMIOPORTNEWIN pfnIn,
+                                             PFNIOMIOPORTNEWOUTSTRING pfnOutStr, PFNIOMIOPORTNEWINSTRING 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);
+
+    uint16_t const fFlags = pGVM->iomr0.s.paIoPortRing3Regs[hIoPorts].fFlags;
+    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;
+    pGVM->iomr0.s.paIoPortRegs[hIoPorts].fFlags             = fFlags;
+#ifdef VBOX_WITH_STATISTICS
+    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;
+#else
+    pGVM->iomr0.s.paIoPortRegs[hIoPorts].idxStats           = UINT16_MAX;
+#endif
+
+    pGVM->iomr0.s.paIoPortRing3Regs[hIoPorts].fRing0 = true;
+
+    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;
+#ifdef VBOX_WITH_STATISTICS
+    uint32_t const cOldEntries = pGVM->iomr0.s.cIoPortStatsAllocation;
+    ASMCompilerBarrier();
+#else
+    uint32_t const cOldEntries = 0;
+#endif
+    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/VMMR0.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp	(revision 81166)
+++ /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp	(revision 81167)
@@ -2199,4 +2199,22 @@
         }
 
+        case VMMR0_DO_IOM_GROW_MMIO_REGS:
+        {
+            if (pReqHdr || idCpu != 0)
+                return VERR_INVALID_PARAMETER;
+            rc = VERR_NOT_SUPPORTED; //rc = IOMR0MmioGrowRegistrationTables(pGVM, u64Arg);
+            VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
+            break;
+        }
+
+        case VMMR0_DO_IOM_GROW_MMIO_STATS:
+        {
+            if (pReqHdr || idCpu != 0)
+                return VERR_INVALID_PARAMETER;
+            rc = VERR_NOT_SUPPORTED; //rc = IOMR0MmioGrowStatisticsTable(pGVM, u64Arg);
+            VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
+            break;
+        }
+
         /*
          * For profiling.
Index: /trunk/src/VBox/VMM/VMMR3/IOM.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/IOM.cpp	(revision 81166)
+++ /trunk/src/VBox/VMM/VMMR3/IOM.cpp	(revision 81167)
@@ -2327,29 +2327,2 @@
 #endif /* VBOX_WITH_STATISTICS */
 
-
-VMMR3_INT_DECL(int)  IOMR3MmioCreate(PVM pVM, PPDMDEVINS pDevIns, RTGCPHYS cbRegion, uint32_t fFlags, PPDMPCIDEV pPciDev,
-                                     uint32_t iPciRegion, PFNIOMMMIONEWWRITE pfnWrite, PFNIOMMMIONEWREAD pfnRead,
-                                     PFNIOMMMIONEWFILL pfnFill, void *pvUser, const char *pszDesc, PIOMMMIOHANDLE phRegion)
-{
-    RT_NOREF(pVM, pDevIns, cbRegion, fFlags, pPciDev, iPciRegion, pfnWrite, pfnRead, pfnFill, pvUser, pszDesc, phRegion);
-    return VERR_NOT_IMPLEMENTED;
-}
-
-VMMR3_INT_DECL(int)  IOMR3MmioMap(PVM pVM, PPDMDEVINS pDevIns, IOMMMIOHANDLE hRegion, RTGCPHYS GCPhys)
-{
-    RT_NOREF(pVM, pDevIns, hRegion, GCPhys);
-    return VERR_NOT_IMPLEMENTED;
-}
-
-VMMR3_INT_DECL(int)  IOMR3MmioUnmap(PVM pVM, PPDMDEVINS pDevIns, IOMMMIOHANDLE hRegion)
-{
-    RT_NOREF(pVM, pDevIns, hRegion);
-    return VERR_NOT_IMPLEMENTED;
-}
-
-VMMR3_INT_DECL(int)  IOMR3MmioReduce(PVM pVM, PPDMDEVINS pDevIns, IOMMMIOHANDLE hRegion, RTGCPHYS cbRegion)
-{
-    RT_NOREF(pVM, pDevIns, hRegion, cbRegion);
-    return VERR_NOT_IMPLEMENTED;
-}
-
Index: /trunk/src/VBox/VMM/VMMR3/IOMR3Mmio.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/IOMR3Mmio.cpp	(revision 81166)
+++ /trunk/src/VBox/VMM/VMMR3/IOMR3Mmio.cpp	(revision 81167)
@@ -291,5 +291,5 @@
     {
         AssertFailed();
-        rc = VERR_IOM_MMIO_ALREADY_MAPPED;
+        rc = VERR_IOM_MMIO_REGION_ALREADY_MAPPED;
     }
 
@@ -401,9 +401,16 @@
     {
         AssertFailed();
-        rc = VERR_IOM_MMIO_NOT_MAPPED;
+        rc = VERR_IOM_MMIO_REGION_NOT_MAPPED;
     }
 
     IOM_UNLOCK_EXCL(pVM);
     return rc;
+}
+
+
+VMMR3_INT_DECL(int)  IOMR3MmioReduce(PVM pVM, PPDMDEVINS pDevIns, IOMMMIOHANDLE hRegion, RTGCPHYS cbRegion)
+{
+    RT_NOREF(pVM, pDevIns, hRegion, cbRegion);
+    return VERR_NOT_IMPLEMENTED;
 }
 
Index: /trunk/src/VBox/VMM/include/IOMInternal.h
===================================================================
--- /trunk/src/VBox/VMM/include/IOMInternal.h	(revision 81166)
+++ /trunk/src/VBox/VMM/include/IOMInternal.h	(revision 81167)
@@ -842,4 +842,8 @@
 void                iomR3IoPortRegStats(PVM pVM, PIOMIOPORTENTRYR3 pRegEntry);
 #endif /* IN_RING3 */
+#ifdef IN_RING0
+void                iomR0IoPortCleanupVM(PGVM pGVM);
+void                iomR0IoPortInitPerVMData(PGVM pGVM);
+#endif
 
 #ifndef IN_RING3
