Index: trunk/src/VBox/VMM/VMMR0/NEMR0Native-win.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR0/NEMR0Native-win.cpp	(revision 72299)
+++ trunk/src/VBox/VMM/VMMR0/NEMR0Native-win.cpp	(revision 72300)
@@ -90,4 +90,49 @@
  */
 #include "../VMMAll/NEMAllNativeTemplate-win.cpp.h"
+
+/**
+ * Worker for NEMR0InitVM that allocates a hypercall page.
+ *
+ * @returns VBox status code.
+ * @param   pHypercallData  The hypercall data page to initialize.
+ */
+static int nemR0InitHypercallData(PNEMR0HYPERCALLDATA pHypercallData)
+{
+    int rc = RTR0MemObjAllocPage(&pHypercallData->hMemObj, PAGE_SIZE, false /*fExecutable*/);
+    if (RT_SUCCESS(rc))
+    {
+        pHypercallData->HCPhysPage = RTR0MemObjGetPagePhysAddr(pHypercallData->hMemObj, 0 /*iPage*/);
+        AssertStmt(pHypercallData->HCPhysPage != NIL_RTHCPHYS, rc = VERR_INTERNAL_ERROR_3);
+        pHypercallData->pbPage     = (uint8_t *)RTR0MemObjAddress(pHypercallData->hMemObj);
+        AssertStmt(pHypercallData->pbPage, rc = VERR_INTERNAL_ERROR_3);
+        if (RT_SUCCESS(rc))
+            return VINF_SUCCESS;
+
+        /* bail out */
+        RTR0MemObjFree(pHypercallData->hMemObj, true /*fFreeMappings*/);
+    }
+    pHypercallData->hMemObj     = NIL_RTR0MEMOBJ;
+    pHypercallData->HCPhysPage  = NIL_RTHCPHYS;
+    pHypercallData->pbPage      = NULL;
+    return rc;
+}
+
+/**
+ * Worker for NEMR0CleanupVM and NEMR0InitVM that cleans up a hypercall page.
+ *
+ * @param   pHypercallData  The hypercall data page to uninitialize.
+ */
+static void nemR0DeleteHypercallData(PNEMR0HYPERCALLDATA pHypercallData)
+{
+    /* Check pbPage here since it's NULL, whereas the hMemObj can be either
+       NIL_RTR0MEMOBJ or 0 (they aren't necessarily the same). */
+    if (pHypercallData->pbPage != NULL)
+    {
+        RTR0MemObjFree(pHypercallData->hMemObj, true /*fFreeMappings*/);
+        pHypercallData->pbPage = NULL;
+    }
+    pHypercallData->hMemObj    = NIL_RTR0MEMOBJ;
+    pHypercallData->HCPhysPage = NIL_RTHCPHYS;
+}
 
 
@@ -127,42 +172,47 @@
         {
             /*
-             * Allocate a page for each VCPU to place hypercall data on.
+             * Allocate a page for non-EMT threads to use for hypercalls (update
+             * statistics and such) and a critical section protecting it.
              */
-            for (VMCPUID i = 0; i < pGVM->cCpus; i++)
+            rc = RTCritSectInit(&pGVM->nem.s.HypercallDataCritSect);
+            if (RT_SUCCESS(rc))
             {
-                PGVMCPU pGVCpu = &pGVM->aCpus[i];
-                rc = RTR0MemObjAllocPage(&pGVCpu->nem.s.hHypercallDataMemObj, PAGE_SIZE, false /*fExecutable*/);
+                rc = nemR0InitHypercallData(&pGVM->nem.s.HypercallData);
                 if (RT_SUCCESS(rc))
                 {
-                    pGVCpu->nem.s.HCPhysHypercallData = RTR0MemObjGetPagePhysAddr(pGVCpu->nem.s.hHypercallDataMemObj, 0 /*iPage*/);
-                    pGVCpu->nem.s.pbHypercallData     = (uint8_t *)RTR0MemObjAddress(pGVCpu->nem.s.hHypercallDataMemObj);
-                    AssertStmt(pGVCpu->nem.s.HCPhysHypercallData != NIL_RTHCPHYS, rc = VERR_INTERNAL_ERROR_3);
-                    AssertStmt(pGVCpu->nem.s.pbHypercallData, rc = VERR_INTERNAL_ERROR_3);
+                    /*
+                     * Allocate a page for each VCPU to place hypercall data on.
+                     */
+                    for (VMCPUID i = 0; i < pGVM->cCpus; i++)
+                    {
+                        rc = nemR0InitHypercallData(&pGVM->aCpus[i].nem.s.HypercallData);
+                        if (RT_FAILURE(rc))
+                        {
+                            while (i-- > 0)
+                                nemR0DeleteHypercallData(&pGVM->aCpus[i].nem.s.HypercallData);
+                            break;
+                        }
+                    }
+                    if (RT_SUCCESS(rc))
+                    {
+                        /*
+                         * So far, so good.
+                         */
+                        return rc;
+                    }
+
+                    /*
+                     * Bail out.
+                     */
+                    nemR0DeleteHypercallData(&pGVM->nem.s.HypercallData);
                 }
-                else
-                    pGVCpu->nem.s.hHypercallDataMemObj = NIL_RTR0MEMOBJ;
-                if (RT_FAILURE(rc))
-                {
-                    /* bail. */
-                    do
-                    {
-                        RTR0MemObjFree(pGVCpu->nem.s.hHypercallDataMemObj, true /*fFreeMappings*/);
-                        pGVCpu->nem.s.hHypercallDataMemObj = NIL_RTR0MEMOBJ;
-                        pGVCpu->nem.s.HCPhysHypercallData  = NIL_RTHCPHYS;
-                        pGVCpu->nem.s.pbHypercallData      = NULL;
-                    } while (i-- > 0);
-                    return rc;
-                }
+                RTCritSectDelete(&pGVM->nem.s.HypercallDataCritSect);
             }
-            /*
-             * So far, so good.
-             */
-            return rc;
-        }
-
-        rc = VERR_NEM_MISSING_KERNEL_API;
-    }
-
-    RT_NOREF(pGVM, pVM);
+        }
+        else
+            rc = VERR_NEM_MISSING_KERNEL_API;
+    }
+
+    RT_NOREF(pVM);
     return rc;
 }
@@ -309,15 +359,10 @@
     VMCPUID i = pGVM->cCpus;
     while (i-- > 0)
-    {
-        PGVMCPU pGVCpu = &pGVM->aCpus[i];
-        if (pGVCpu->nem.s.pbHypercallData)
-        {
-            pGVCpu->nem.s.pbHypercallData = NULL;
-            int rc = RTR0MemObjFree(pGVCpu->nem.s.hHypercallDataMemObj, true /*fFreeMappings*/);
-            AssertRC(rc);
-        }
-        pGVCpu->nem.s.hHypercallDataMemObj = NIL_RTR0MEMOBJ;
-        pGVCpu->nem.s.HCPhysHypercallData  = NIL_RTHCPHYS;
-    }
+        nemR0DeleteHypercallData(&pGVM->aCpus[i].nem.s.HypercallData);
+
+    /* The non-EMT one too. */
+    if (RTCritSectIsInitialized(&pGVM->nem.s.HypercallDataCritSect))
+        RTCritSectDelete(&pGVM->nem.s.HypercallDataCritSect);
+    nemR0DeleteHypercallData(&pGVM->nem.s.HypercallData);
 }
 
@@ -372,5 +417,5 @@
      * Ring-3 is not allowed to fill in the host physical addresses of the call.
      */
-    HV_INPUT_MAP_GPA_PAGES *pMapPages = (HV_INPUT_MAP_GPA_PAGES *)pGVCpu->nem.s.pbHypercallData;
+    HV_INPUT_MAP_GPA_PAGES *pMapPages = (HV_INPUT_MAP_GPA_PAGES *)pGVCpu->nem.s.HypercallData.pbPage;
     AssertPtrReturn(pMapPages, VERR_INTERNAL_ERROR_3);
     pMapPages->TargetPartitionId    = pGVM->nem.s.idHvPartition;
@@ -387,5 +432,5 @@
 
     uint64_t uResult = g_pfnHvlInvokeHypercall(HvCallMapGpaPages | ((uint64_t)cPages << 32),
-                                               pGVCpu->nem.s.HCPhysHypercallData, 0);
+                                               pGVCpu->nem.s.HypercallData.HCPhysPage, 0);
     Log6(("NEMR0MapPages: %RGp/%RGp L %u prot %#x -> %#RX64\n",
           GCPhysDst, GCPhysSrc - cPages * X86_PAGE_SIZE, cPages, fFlags, uResult));
@@ -454,5 +499,5 @@
      * Compose and make the hypercall.
      */
-    HV_INPUT_UNMAP_GPA_PAGES *pUnmapPages = (HV_INPUT_UNMAP_GPA_PAGES *)pGVCpu->nem.s.pbHypercallData;
+    HV_INPUT_UNMAP_GPA_PAGES *pUnmapPages = (HV_INPUT_UNMAP_GPA_PAGES *)pGVCpu->nem.s.HypercallData.pbPage;
     AssertPtrReturn(pUnmapPages, VERR_INTERNAL_ERROR_3);
     pUnmapPages->TargetPartitionId    = pGVM->nem.s.idHvPartition;
@@ -461,5 +506,5 @@
 
     uint64_t uResult = g_pfnHvlInvokeHypercall(HvCallUnmapGpaPages | ((uint64_t)cPages << 32),
-                                               pGVCpu->nem.s.HCPhysHypercallData, 0);
+                                               pGVCpu->nem.s.HypercallData.HCPhysPage, 0);
     Log6(("NEMR0UnmapPages: %RGp L %u -> %#RX64\n", GCPhys, cPages, uResult));
     if (uResult == ((uint64_t)cPages << 32))
@@ -467,5 +512,5 @@
 #if 1       /* Do we need to do this? Hopefully not... */
         uint64_t volatile uR = g_pfnHvlInvokeHypercall(HvCallUncommitGpaPages | ((uint64_t)cPages << 32),
-                                                       pGVCpu->nem.s.HCPhysHypercallData, 0);
+                                                       pGVCpu->nem.s.HypercallData.HCPhysPage, 0);
         AssertMsg(uR == ((uint64_t)cPages << 32), ("uR=%#RX64\n", uR)); NOREF(uR);
 #endif
@@ -527,5 +572,5 @@
 {
     PVMCPU                     pVCpu  = &pGVM->pVM->aCpus[pGVCpu->idCpu];
-    HV_INPUT_SET_VP_REGISTERS *pInput = (HV_INPUT_SET_VP_REGISTERS *)pGVCpu->nem.s.pbHypercallData;
+    HV_INPUT_SET_VP_REGISTERS *pInput = (HV_INPUT_SET_VP_REGISTERS *)pGVCpu->nem.s.HypercallData.pbPage;
     AssertPtrReturn(pInput, VERR_INTERNAL_ERROR_3);
 
@@ -1096,5 +1141,5 @@
      * Set the registers.
      */
-    Assert((uintptr_t)&pInput->Elements[iReg] - (uintptr_t)pGVCpu->nem.s.pbHypercallData < PAGE_SIZE); /* max is 127 */
+    Assert((uintptr_t)&pInput->Elements[iReg] - (uintptr_t)pGVCpu->nem.s.HypercallData.pbPage < PAGE_SIZE); /* max is 127 */
 
     /*
@@ -1102,5 +1147,5 @@
      */
     uint64_t uResult = g_pfnHvlInvokeHypercall(HV_MAKE_CALL_INFO(HvCallSetVpRegisters, iReg),
-                                               pGVCpu->nem.s.HCPhysHypercallData, 0 /*GCPhysOutput*/);
+                                               pGVCpu->nem.s.HypercallData.HCPhysPage, 0 /*GCPhysOutput*/);
     AssertLogRelMsgReturn(uResult == HV_MAKE_CALL_REP_RET(iReg),
                           ("uResult=%RX64 iRegs=%#x\n", uResult, iReg),
@@ -1156,5 +1201,5 @@
 NEM_TMPL_STATIC int nemR0WinImportState(PGVM pGVM, PGVMCPU pGVCpu, PCPUMCTX pCtx, uint64_t fWhat)
 {
-    HV_INPUT_GET_VP_REGISTERS *pInput = (HV_INPUT_GET_VP_REGISTERS *)pGVCpu->nem.s.pbHypercallData;
+    HV_INPUT_GET_VP_REGISTERS *pInput = (HV_INPUT_GET_VP_REGISTERS *)pGVCpu->nem.s.HypercallData.pbPage;
     AssertPtrReturn(pInput, VERR_INTERNAL_ERROR_3);
 
@@ -1339,5 +1384,5 @@
 
     HV_REGISTER_VALUE *paValues = (HV_REGISTER_VALUE *)((uint8_t *)pInput + cbInput);
-    Assert((uintptr_t)&paValues[cRegs] - (uintptr_t)pGVCpu->nem.s.pbHypercallData < PAGE_SIZE); /* (max is around 168 registers) */
+    Assert((uintptr_t)&paValues[cRegs] - (uintptr_t)pGVCpu->nem.s.HypercallData.pbPage < PAGE_SIZE); /* (max is around 168 registers) */
     RT_BZERO(paValues, cRegs * sizeof(paValues[0]));
 
@@ -1346,6 +1391,6 @@
      */
     uint64_t uResult = g_pfnHvlInvokeHypercall(HV_MAKE_CALL_INFO(HvCallGetVpRegisters, cRegs),
-                                               pGVCpu->nem.s.HCPhysHypercallData,
-                                               pGVCpu->nem.s.HCPhysHypercallData + cbInput);
+                                               pGVCpu->nem.s.HypercallData.HCPhysPage,
+                                               pGVCpu->nem.s.HypercallData.HCPhysPage + cbInput);
     AssertLogRelMsgReturn(uResult == HV_MAKE_CALL_REP_RET(cRegs),
                           ("uResult=%RX64 cRegs=%#x\n", uResult, cRegs),
@@ -1893,2 +1938,75 @@
 }
 
+
+/**
+ * Updates statistics in the VM structure.
+ *
+ * @returns VBox status code.
+ * @param   pGVM        The ring-0 VM handle.
+ * @param   pVM         The cross context VM handle.
+ * @param   idCpu       The calling EMT, or NIL.  Necessary for getting the hypercall
+ *                      page and arguments.
+ */
+VMMR0_INT_DECL(int)  NEMR0UpdateStatistics(PGVM pGVM, PVM pVM, VMCPUID idCpu)
+{
+    /*
+     * Validate the call.
+     */
+    int rc;
+    if (idCpu == NIL_VMCPUID)
+        rc = GVMMR0ValidateGVMandVM(pGVM, pVM);
+    else
+        rc = GVMMR0ValidateGVMandVMandEMT(pGVM, pVM, idCpu);
+    if (RT_SUCCESS(rc))
+    {
+        AssertReturn(g_pfnHvlInvokeHypercall, VERR_NEM_MISSING_KERNEL_API);
+
+        PNEMR0HYPERCALLDATA pHypercallData = idCpu != NIL_VMCPUID
+                                           ? &pGVM->aCpus[idCpu].nem.s.HypercallData
+                                           : &pGVM->nem.s.HypercallData;
+        if (   RT_VALID_PTR(pHypercallData->pbPage)
+            && pHypercallData->HCPhysPage != NIL_RTHCPHYS)
+        {
+            if (idCpu == NIL_VMCPUID)
+                rc = RTCritSectEnter(&pGVM->nem.s.HypercallDataCritSect);
+            if (RT_SUCCESS(rc))
+            {
+                /*
+                 * Query the memory statistics for the partition.
+                 */
+                HV_INPUT_GET_MEMORY_BALANCE  *pInput = (HV_INPUT_GET_MEMORY_BALANCE *)pHypercallData->pbPage;
+                pInput->TargetPartitionId                               = pGVM->nem.s.idHvPartition;
+                pInput->ProximityDomainInfo.Flags.ProximityPreferred    = 0;
+                pInput->ProximityDomainInfo.Flags.ProxyimityInfoValid   = 0;
+                pInput->ProximityDomainInfo.Flags.Reserved              = 0;
+                pInput->ProximityDomainInfo.Id                          = 0;
+
+                HV_OUTPUT_GET_MEMORY_BALANCE *pOutput = (HV_OUTPUT_GET_MEMORY_BALANCE *)(pInput + 1);
+                RT_ZERO(*pOutput);
+
+                uint64_t uResult = g_pfnHvlInvokeHypercall(HvCallGetMemoryBalance,
+                                                           pHypercallData->HCPhysPage,
+                                                           pHypercallData->HCPhysPage + sizeof(*pInput));
+                if (uResult == HV_STATUS_SUCCESS)
+                {
+                    pVM->nem.s.R0Stats.cPagesAvailable = pOutput->PagesAvailable;
+                    pVM->nem.s.R0Stats.cPagesInUse     = pOutput->PagesInUse;
+                    rc = VINF_SUCCESS;
+                }
+                else
+                {
+                    LogRel(("HvCallGetMemoryBalance -> %#RX64 (%#RX64 %#RX64)!!\n",
+                            uResult, pOutput->PagesAvailable, pOutput->PagesInUse));
+                    rc = VINF_NEM_IPE_0;
+                }
+
+                if (idCpu == NIL_VMCPUID)
+                    RTCritSectLeave(&pGVM->nem.s.HypercallDataCritSect);
+            }
+        }
+        else
+            rc = VERR_WRONG_ORDER;
+    }
+    return rc;
+}
+
Index: trunk/src/VBox/VMM/VMMR0/VMMR0.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR0/VMMR0.cpp	(revision 72299)
+++ trunk/src/VBox/VMM/VMMR0/VMMR0.cpp	(revision 72300)
@@ -2062,4 +2062,11 @@
             VMM_CHECK_SMAP_CHECK2(pVM, RT_NOTHING);
             break;
+
+        case VMMR0_DO_NEM_UPDATE_STATISTICS:
+            if (u64Arg || pReqHdr)
+                return VERR_INVALID_PARAMETER;
+            rc = NEMR0UpdateStatistics(pGVM, pVM, idCpu);
+            VMM_CHECK_SMAP_CHECK2(pVM, RT_NOTHING);
+            break;
 # endif
 #endif
Index: trunk/src/VBox/VMM/VMMR3/EM.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR3/EM.cpp	(revision 72299)
+++ trunk/src/VBox/VMM/VMMR3/EM.cpp	(revision 72300)
@@ -2108,4 +2108,5 @@
                         /** @todo this really isn't nice, should properly handle this */
                         rc2 = TRPMR3InjectEvent(pVM, pVCpu, TRPM_HARDWARE_INT);
+Log(("EM: TRPMR3InjectEvent -> %d\n", rc2));
                         if (pVM->em.s.fIemExecutesAll && (   rc2 == VINF_EM_RESCHEDULE_REM
                                                           || rc2 == VINF_EM_RESCHEDULE_HM
Index: trunk/src/VBox/VMM/VMMR3/NEMR3Native-win.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR3/NEMR3Native-win.cpp	(revision 72299)
+++ trunk/src/VBox/VMM/VMMR3/NEMR3Native-win.cpp	(revision 72300)
@@ -725,5 +725,5 @@
             uint32_t iMin, iMax; } s_aUnknowns[] =
         {
-            { 0x0003, 0x000f },
+            { 0x0004, 0x000f },
             { 0x1003, 0x100f },
             { 0x2000, 0x200f },
@@ -1008,14 +1008,4 @@
 
 /**
- * Wrapper for different WHvSetPartitionProperty signatures.
- */
-DECLINLINE(HRESULT) WHvSetPartitionPropertyWrapper(WHV_PARTITION_HANDLE hPartition, WHV_PARTITION_PROPERTY_CODE enmProp,
-                                                   WHV_PARTITION_PROPERTY *pInput, uint32_t cbInput)
-{
-    return g_pfnWHvSetPartitionProperty(hPartition, enmProp, pInput, cbInput - RT_UOFFSETOF(WHV_PARTITION_PROPERTY, ExtendedVmExits));
-}
-
-
-/**
  * Creates and sets up a Hyper-V (exo) partition.
  *
@@ -1054,5 +1044,5 @@
     RT_ZERO(Property);
     Property.ProcessorCount = pVM->cCpus;
-    hrc = WHvSetPartitionPropertyWrapper(hPartition, WHvPartitionPropertyCodeProcessorCount, &Property, sizeof(Property));
+    hrc = WHvSetPartitionProperty(hPartition, WHvPartitionPropertyCodeProcessorCount, &Property, sizeof(Property));
     if (SUCCEEDED(hrc))
     {
@@ -1063,5 +1053,5 @@
         Property.ExtendedVmExits.ExceptionExit = pVM->nem.s.fExtendedXcptExit;
 #endif
-        hrc = WHvSetPartitionPropertyWrapper(hPartition, WHvPartitionPropertyCodeExtendedVmExits, &Property, sizeof(Property));
+        hrc = WHvSetPartitionProperty(hPartition, WHvPartitionPropertyCodeExtendedVmExits, &Property, sizeof(Property));
         if (SUCCEEDED(hrc))
         {
@@ -1163,4 +1153,12 @@
                             STAMR3RegisterF(pVM, &pNemCpu->StatBreakOnStatus,       STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of status code breaks",           "/NEM/CPU%u/BreakOnStatus", iCpu);
                         }
+
+                        PUVM pUVM = pVM->pUVM;
+                        STAMR3RegisterRefresh(pUVM, &pVM->nem.s.R0Stats.cPagesAvailable, STAMTYPE_U64, STAMVISIBILITY_ALWAYS,
+                                              STAMUNIT_PAGES, STAM_REFRESH_GRP_NEM, "Free pages available to the hypervisor",
+                                              "/NEM/R0Stats/cPagesAvailable");
+                        STAMR3RegisterRefresh(pUVM, &pVM->nem.s.R0Stats.cPagesInUse,     STAMTYPE_U64, STAMVISIBILITY_ALWAYS,
+                                              STAMUNIT_PAGES, STAM_REFRESH_GRP_NEM, "Pages in use by hypervisor",
+                                              "/NEM/R0Stats/cPagesInUse");
                     }
                 }
@@ -1212,5 +1210,5 @@
     Property.ProcessorVendor = pVM->nem.s.enmCpuVendor == CPUMCPUVENDOR_AMD ? WHvProcessorVendorAmd
                              : WHvProcessorVendorIntel;
-    hrc = WHvSetPartitionPropertyWrapper(hPartition, WHvPartitionPropertyCodeProcessorVendor, &Property, sizeof(Property));
+    hrc = WHvSetPartitionProperty(hPartition, WHvPartitionPropertyCodeProcessorVendor, &Property, sizeof(Property));
     if (FAILED(hrc))
         return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
@@ -1222,5 +1220,5 @@
     RT_ZERO(Property);
     Property.ProcessorClFlushSize = pVM->nem.s.cCacheLineFlushShift;
-    hrc = WHvSetPartitionPropertyWrapper(hPartition, WHvPartitionPropertyCodeProcessorClFlushSize, &Property, sizeof(Property));
+    hrc = WHvSetPartitionProperty(hPartition, WHvPartitionPropertyCodeProcessorClFlushSize, &Property, sizeof(Property));
     if (FAILED(hrc))
         return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
@@ -1236,5 +1234,5 @@
     RT_ZERO(Property);
     Property.ProcessorFeatures.AsUINT64 = pVM->nem.s.uCpuFeatures.u64;
-    hrc = WHvSetPartitionPropertyWrapper(hPartition, WHvPartitionPropertyCodeProcessorFeatures, &Property, sizeof(Property));
+    hrc = WHvSetPartitionProperty(hPartition, WHvPartitionPropertyCodeProcessorFeatures, &Property, sizeof(Property));
     if (FAILED(hrc))
         return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
@@ -1331,4 +1329,15 @@
     {
         LogRel(("NEM: Successfully set up partition (device handle %p, partition ID %#llx)\n", hPartitionDevice, idHvPartition));
+
+#if 1
+        VMMR3CallR0Emt(pVM, &pVM->aCpus[0], VMMR0_DO_NEM_UPDATE_STATISTICS, 0, NULL);
+        LogRel(("NEM: Memory balance: %#RX64 out of %#RX64 pages in use\n",
+                pVM->nem.s.R0Stats.cPagesInUse, pVM->nem.s.R0Stats.cPagesAvailable));
+#endif
+
+        /*
+         * Register statistics on shared pages.
+         */
+        /** @todo HvCallMapStatsPage */
         return VINF_SUCCESS;
     }
Index: trunk/src/VBox/VMM/VMMR3/STAM.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR3/STAM.cpp	(revision 72299)
+++ trunk/src/VBox/VMM/VMMR3/STAM.cpp	(revision 72300)
@@ -144,6 +144,7 @@
 static void                 stamR3LookupDestroyTree(PSTAMLOOKUP pRoot);
 #endif
-static int                  stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
-                                            STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc);
+static int                  stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfnReset,
+                                            PFNSTAMR3CALLBACKPRINT pfnPrint, STAMTYPE enmType, STAMVISIBILITY enmVisibility,
+                                            const char *pszName, STAMUNIT enmUnit, const char *pszDesc, uint8_t iRefreshGrp);
 static int                  stamR3ResetOne(PSTAMDESC pDesc, void *pvArg);
 static DECLCALLBACK(void)   stamR3EnumLogPrintf(PSTAMR3PRINTONEARGS pvArg, const char *pszFormat, ...);
@@ -158,6 +159,4 @@
 static int                  stamR3EnumU(PUVM pUVM, const char *pszPat, bool fUpdateRing0, int (pfnCallback)(PSTAMDESC pDesc, void *pvArg), void *pvArg);
 static void                 stamR3Ring0StatsRegisterU(PUVM pUVM);
-static void                 stamR3Ring0StatsUpdateU(PUVM pUVM, const char *pszPat);
-static void                 stamR3Ring0StatsUpdateMultiU(PUVM pUVM, const char * const *papszExpressions, unsigned cExpressions);
 
 #ifdef VBOX_WITH_DEBUGGER
@@ -389,9 +388,10 @@
  * @param   pszDesc     Sample description.
  */
-VMMR3DECL(int)  STAMR3RegisterU(PUVM pUVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc)
+VMMR3DECL(int)  STAMR3RegisterU(PUVM pUVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName,
+                                STAMUNIT enmUnit, const char *pszDesc)
 {
     AssertReturn(enmType != STAMTYPE_CALLBACK, VERR_INVALID_PARAMETER);
     UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
-    return stamR3RegisterU(pUVM, pvSample, NULL, NULL, enmType, enmVisibility, pszName, enmUnit, pszDesc);
+    return stamR3RegisterU(pUVM, pvSample, NULL, NULL, enmType, enmVisibility, pszName, enmUnit, pszDesc, STAM_REFRESH_GRP_NONE);
 }
 
@@ -419,8 +419,10 @@
  * @param   pszDesc     Sample description.
  */
-VMMR3DECL(int)  STAMR3Register(PVM pVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName, STAMUNIT enmUnit, const char *pszDesc)
+VMMR3DECL(int)  STAMR3Register(PVM pVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, const char *pszName,
+                               STAMUNIT enmUnit, const char *pszDesc)
 {
     AssertReturn(enmType != STAMTYPE_CALLBACK, VERR_INVALID_PARAMETER);
-    return stamR3RegisterU(pVM->pUVM, pvSample, NULL, NULL, enmType, enmVisibility, pszName, enmUnit, pszDesc);
+    return stamR3RegisterU(pVM->pUVM, pvSample, NULL, NULL, enmType, enmVisibility, pszName, enmUnit, pszDesc,
+                           STAM_REFRESH_GRP_NONE);
 }
 
@@ -576,7 +578,61 @@
         return VERR_NO_MEMORY;
 
-    int rc = stamR3RegisterU(pVM->pUVM, pvSample, pfnReset, pfnPrint, STAMTYPE_CALLBACK, enmVisibility, pszFormattedName, enmUnit, pszDesc);
+    int rc = stamR3RegisterU(pVM->pUVM, pvSample, pfnReset, pfnPrint, STAMTYPE_CALLBACK, enmVisibility, pszFormattedName,
+                             enmUnit, pszDesc, STAM_REFRESH_GRP_NONE);
     RTStrFree(pszFormattedName);
     return rc;
+}
+
+
+/**
+ * Same as STAMR3RegisterFU, except there is an extra refresh group parameter.
+ *
+ * @returns VBox status code.
+ * @param   pUVM        Pointer to the user mode VM structure.
+ * @param   pvSample    Pointer to the sample.
+ * @param   enmType     Sample type. This indicates what pvSample is pointing at.
+ * @param   enmVisibility  Visibility type specifying whether unused statistics should be visible or not.
+ * @param   enmUnit     Sample unit.
+ * @param   iRefreshGrp The refresh group, STAM_REFRESH_GRP_XXX.
+ * @param   pszDesc     Sample description.
+ * @param   pszName     The sample name format string.
+ * @param   ...         Arguments to the format string.
+ */
+VMMR3DECL(int) STAMR3RegisterRefresh(PUVM pUVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
+                                     uint8_t iRefreshGrp, const char *pszDesc, const char *pszName, ...)
+{
+    va_list args;
+    va_start(args, pszName);
+    int rc = STAMR3RegisterRefreshV(pUVM, pvSample, enmType, enmVisibility, enmUnit, iRefreshGrp, pszDesc, pszName, args);
+    va_end(args);
+    return rc;
+}
+
+
+/**
+ * Same as STAMR3RegisterVU, except there is an extra refresh group parameter.
+ *
+ * @returns VBox status code.
+ * @param   pUVM        The user mode VM structure.
+ * @param   pvSample    Pointer to the sample.
+ * @param   enmType     Sample type. This indicates what pvSample is pointing at.
+ * @param   enmVisibility  Visibility type specifying whether unused statistics should be visible or not.
+ * @param   enmUnit     Sample unit.
+ * @param   iRefreshGrp The refresh group, STAM_REFRESH_GRP_XXX.
+ * @param   pszDesc     Sample description.
+ * @param   pszName     The sample name format string.
+ * @param   va          Arguments to the format string.
+ */
+VMMR3DECL(int) STAMR3RegisterRefreshV(PUVM pUVM, void *pvSample, STAMTYPE enmType, STAMVISIBILITY enmVisibility, STAMUNIT enmUnit,
+                                      uint8_t iRefreshGrp, const char *pszDesc, const char *pszName, va_list va)
+{
+    AssertReturn(enmType != STAMTYPE_CALLBACK, VERR_INVALID_PARAMETER);
+
+    char   szFormattedName[STAM_MAX_NAME_LEN + 8];
+    size_t cch = RTStrPrintfV(szFormattedName, sizeof(szFormattedName), pszName, va);
+    AssertReturn(cch <= STAM_MAX_NAME_LEN, VERR_OUT_OF_RANGE);
+
+    UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+    return stamR3RegisterU(pUVM, pvSample, NULL, NULL, enmType, enmVisibility, pszName, enmUnit, pszDesc, iRefreshGrp);
 }
 
@@ -1251,9 +1307,10 @@
  * @param   enmUnit     Sample unit.
  * @param   pszDesc     Sample description.
+ * @param   iRefreshGrp The refresh group, STAM_REFRESH_GRP_XXX.
  * @remark  There is currently no device or driver variant of this API. Add one if it should become necessary!
  */
 static int stamR3RegisterU(PUVM pUVM, void *pvSample, PFNSTAMR3CALLBACKRESET pfnReset, PFNSTAMR3CALLBACKPRINT pfnPrint,
                            STAMTYPE enmType, STAMVISIBILITY enmVisibility,
-                           const char *pszName, STAMUNIT enmUnit, const char *pszDesc)
+                           const char *pszName, STAMUNIT enmUnit, const char *pszDesc, uint8_t iRefreshGrp)
 {
     AssertReturn(pszName[0] == '/', VERR_INVALID_NAME);
@@ -1263,4 +1320,5 @@
     AssertReturn(pszName[cchName - 1] != '/', VERR_INVALID_NAME);
     AssertReturn(memchr(pszName, '\\', cchName) == NULL, VERR_INVALID_NAME);
+    AssertReturn(iRefreshGrp == STAM_REFRESH_GRP_NONE || iRefreshGrp < 64, VERR_INVALID_PARAMETER);
 
     STAM_LOCK_WR(pUVM);
@@ -1416,4 +1474,5 @@
         }
         pNew->enmUnit       = enmUnit;
+        pNew->iRefreshGroup = iRefreshGrp;
         pNew->pszDesc       = NULL;
         if (pszDesc)
@@ -2379,4 +2438,124 @@
 }
 
+static void stamR3RefreshGroup(PUVM pUVM, uint8_t iRefreshGroup, uint64_t *pbmRefreshedGroups)
+{
+    *pbmRefreshedGroups |= RT_BIT_64(iRefreshGroup);
+
+    PVM pVM = pUVM->pVM;
+    if (pVM && pVM->pSession)
+    {
+        switch (iRefreshGroup)
+        {
+            /*
+             * GVMM
+             */
+            case STAM_REFRESH_GRP_GVMM:
+            {
+                GVMMQUERYSTATISTICSSREQ Req;
+                Req.Hdr.cbReq    = sizeof(Req);
+                Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+                Req.pSession     = pVM->pSession;
+                int rc = SUPR3CallVMMR0Ex(pVM->pVMR0, NIL_VMCPUID, VMMR0_DO_GVMM_QUERY_STATISTICS, 0, &Req.Hdr);
+                if (RT_SUCCESS(rc))
+                {
+                    pUVM->stam.s.GVMMStats = Req.Stats;
+
+                    /*
+                     * Check if the number of host CPUs has changed (it will the first
+                     * time around and normally never again).
+                     */
+                    if (RT_UNLIKELY(pUVM->stam.s.GVMMStats.cHostCpus > pUVM->stam.s.cRegisteredHostCpus))
+                    {
+                        if (RT_UNLIKELY(pUVM->stam.s.GVMMStats.cHostCpus > pUVM->stam.s.cRegisteredHostCpus))
+                        {
+                            STAM_UNLOCK_RD(pUVM);
+                            STAM_LOCK_WR(pUVM);
+                            uint32_t cCpus = pUVM->stam.s.GVMMStats.cHostCpus;
+                            for (uint32_t iCpu = pUVM->stam.s.cRegisteredHostCpus; iCpu < cCpus; iCpu++)
+                            {
+                                char   szName[120];
+                                size_t cchBase = RTStrPrintf(szName, sizeof(szName), "/GVMM/HostCpus/%u", iCpu);
+                                stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].idCpu, NULL, NULL,
+                                                STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_NONE,
+                                                "Host CPU ID", STAM_REFRESH_GRP_GVMM);
+                                strcpy(&szName[cchBase], "/idxCpuSet");
+                                stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].idxCpuSet, NULL, NULL,
+                                                STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_NONE,
+                                                "CPU Set index", STAM_REFRESH_GRP_GVMM);
+                                strcpy(&szName[cchBase], "/DesiredHz");
+                                stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].uDesiredHz, NULL, NULL,
+                                                STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_HZ,
+                                                "The desired frequency", STAM_REFRESH_GRP_GVMM);
+                                strcpy(&szName[cchBase], "/CurTimerHz");
+                                stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].uTimerHz, NULL, NULL,
+                                                STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_HZ,
+                                                "The current timer frequency", STAM_REFRESH_GRP_GVMM);
+                                strcpy(&szName[cchBase], "/PPTChanges");
+                                stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].cChanges, NULL, NULL,
+                                                STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES,
+                                                "RTTimerChangeInterval calls", STAM_REFRESH_GRP_GVMM);
+                                strcpy(&szName[cchBase], "/PPTStarts");
+                                stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].cStarts, NULL, NULL,
+                                                STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES,
+                                                "RTTimerStart calls", STAM_REFRESH_GRP_GVMM);
+                            }
+                            pUVM->stam.s.cRegisteredHostCpus = cCpus;
+                            STAM_UNLOCK_WR(pUVM);
+                            STAM_LOCK_RD(pUVM);
+                        }
+                    }
+                }
+                break;
+            }
+
+            /*
+             * GMM
+             */
+            case STAM_REFRESH_GRP_GMM:
+            {
+                GMMQUERYSTATISTICSSREQ Req;
+                Req.Hdr.cbReq    = sizeof(Req);
+                Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+                Req.pSession     = pVM->pSession;
+                int rc = SUPR3CallVMMR0Ex(pVM->pVMR0, NIL_VMCPUID, VMMR0_DO_GMM_QUERY_STATISTICS, 0, &Req.Hdr);
+                if (RT_SUCCESS(rc))
+                    pUVM->stam.s.GMMStats = Req.Stats;
+                break;
+            }
+
+            /*
+             * NEM.
+             */
+            case STAM_REFRESH_GRP_NEM:
+                SUPR3CallVMMR0(pVM->pVMR0, NIL_VMCPUID, VMMR0_DO_NEM_UPDATE_STATISTICS, NULL);
+                break;
+
+            default:
+                AssertMsgFailed(("iRefreshGroup=%d\n", iRefreshGroup));
+        }
+    }
+}
+
+
+/**
+ * Refreshes the statistics behind the given entry, if necessary.
+ *
+ * This helps implement fetching global ring-0 stats into ring-3 accessible
+ * storage.  GVMM, GMM and NEM makes use of this.
+ *
+ * @param   pUVM                The user mode VM handle.
+ * @param   pCur                The statistics descriptor which group to check
+ *                              and maybe update.
+ * @param   pbmRefreshedGroups  Bitmap tracking what has already been updated.
+ */
+DECLINLINE(void) stamR3Refresh(PUVM pUVM, PSTAMDESC pCur, uint64_t *pbmRefreshedGroups)
+{
+    uint8_t const iRefreshGroup = pCur->iRefreshGroup;
+    if (RT_LIKELY(iRefreshGroup == STAM_REFRESH_GRP_NONE))
+    { /* likely */ }
+    else if (!(*pbmRefreshedGroups & RT_BIT_64(iRefreshGroup)))
+        stamR3RefreshGroup(pUVM, iRefreshGroup, pbmRefreshedGroups);
+}
+
 
 /**
@@ -2413,5 +2592,6 @@
             if (piExpression && i > *piExpression)
             {
-                check if we can skip some expressions
+                Check if we can skip some expressions.
+                Requires the expressions to be sorted.
             }*/
             return true;
@@ -2490,6 +2670,7 @@
                        int (*pfnCallback)(PSTAMDESC pDesc, void *pvArg), void *pvArg)
 {
-    int rc = VINF_SUCCESS;
-    PSTAMDESC pCur;
+    int         rc                = VINF_SUCCESS;
+    uint64_t    bmRefreshedGroups = 0;
+    PSTAMDESC   pCur;
 
     /*
@@ -2498,10 +2679,9 @@
     if (!pszPat || !*pszPat || !strcmp(pszPat, "*"))
     {
-        if (fUpdateRing0)
-            stamR3Ring0StatsUpdateU(pUVM, "*");
-
         STAM_LOCK_RD(pUVM);
         RTListForEach(&pUVM->stam.s.List, pCur, STAMDESC, ListEntry)
         {
+            if (fUpdateRing0)
+                stamR3Refresh(pUVM, pCur, &bmRefreshedGroups);
             rc = pfnCallback(pCur, pvArg);
             if (rc)
@@ -2516,7 +2696,4 @@
     else if (!strchr(pszPat, '|'))
     {
-        if (fUpdateRing0)
-            stamR3Ring0StatsUpdateU(pUVM, pszPat);
-
         STAM_LOCK_RD(pUVM);
 #ifdef STAM_WITH_LOOKUP_TREE
@@ -2525,5 +2702,9 @@
             pCur = stamR3LookupFindDesc(pUVM->stam.s.pRoot, pszPat);
             if (pCur)
+            {
+                if (fUpdateRing0)
+                    stamR3Refresh(pUVM, pCur, &bmRefreshedGroups);
                 rc = pfnCallback(pCur, pvArg);
+            }
         }
         else
@@ -2537,4 +2718,6 @@
                     if (RTStrSimplePatternMatch(pszPat, pCur->pszName))
                     {
+                        if (fUpdateRing0)
+                            stamR3Refresh(pUVM, pCur, &bmRefreshedGroups);
                         rc = pfnCallback(pCur, pvArg);
                         if (rc)
@@ -2556,4 +2739,6 @@
             if (RTStrSimplePatternMatch(pszPat, pCur->pszName))
             {
+                if (fUpdateRing0)
+                    stamR3Refresh(pUVM, pCur, &bmRefreshedGroups);
                 rc = pfnCallback(pCur, pvArg);
                 if (rc)
@@ -2582,7 +2767,4 @@
          * Perform the enumeration.
          */
-        if (fUpdateRing0)
-            stamR3Ring0StatsUpdateMultiU(pUVM, papszExpressions, cExpressions);
-
         STAM_LOCK_RD(pUVM);
         unsigned iExpression = 0;
@@ -2591,4 +2773,6 @@
             if (stamR3MultiMatch(papszExpressions, cExpressions, &iExpression, pCur->pszName))
             {
+                if (fUpdateRing0)
+                    stamR3Refresh(pUVM, pCur, &bmRefreshedGroups);
                 rc = pfnCallback(pCur, pvArg);
                 if (rc)
@@ -2617,5 +2801,5 @@
         stamR3RegisterU(pUVM, (uint8_t *)&pUVM->stam.s.GVMMStats + g_aGVMMStats[i].offVar, NULL, NULL,
                         g_aGVMMStats[i].enmType, STAMVISIBILITY_ALWAYS, g_aGVMMStats[i].pszName,
-                        g_aGVMMStats[i].enmUnit, g_aGVMMStats[i].pszDesc);
+                        g_aGVMMStats[i].enmUnit, g_aGVMMStats[i].pszDesc, STAM_REFRESH_GRP_GVMM);
     pUVM->stam.s.cRegisteredHostCpus = 0;
 
@@ -2624,120 +2808,5 @@
         stamR3RegisterU(pUVM, (uint8_t *)&pUVM->stam.s.GMMStats + g_aGMMStats[i].offVar, NULL, NULL,
                         g_aGMMStats[i].enmType, STAMVISIBILITY_ALWAYS, g_aGMMStats[i].pszName,
-                        g_aGMMStats[i].enmUnit, g_aGMMStats[i].pszDesc);
-}
-
-
-/**
- * Updates the ring-0 statistics (the copy).
- *
- * @param   pUVM        Pointer to the user mode VM structure.
- * @param   pszPat      The pattern.
- */
-static void stamR3Ring0StatsUpdateU(PUVM pUVM, const char *pszPat)
-{
-    stamR3Ring0StatsUpdateMultiU(pUVM, &pszPat, 1);
-}
-
-
-/**
- * Updates the ring-0 statistics.
- *
- * The ring-0 statistics aren't directly addressable from ring-3 and must be
- * copied when needed.
- *
- * @param   pUVM                Pointer to the user mode VM structure.
- * @param   papszExpressions    The patterns (for knowing when to skip).
- * @param   cExpressions        Number of patterns.
- */
-static void stamR3Ring0StatsUpdateMultiU(PUVM pUVM, const char * const *papszExpressions, unsigned cExpressions)
-{
-    PVM pVM = pUVM->pVM;
-    if (!pVM || !pVM->pSession)
-        return;
-
-    /*
-     * GVMM
-     */
-    bool fUpdate = false;
-    for (unsigned i = 0; i < RT_ELEMENTS(g_aGVMMStats); i++)
-        if (stamR3MultiMatch(papszExpressions, cExpressions, NULL, g_aGVMMStats[i].pszName))
-        {
-            fUpdate = true;
-            break;
-        }
-    if (!fUpdate)
-    {
-        /** @todo check the cpu leaves - rainy day. */
-    }
-    if (fUpdate)
-    {
-        GVMMQUERYSTATISTICSSREQ Req;
-        Req.Hdr.cbReq = sizeof(Req);
-        Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
-        Req.pSession = pVM->pSession;
-        int rc = SUPR3CallVMMR0Ex(pVM->pVMR0, NIL_VMCPUID, VMMR0_DO_GVMM_QUERY_STATISTICS, 0, &Req.Hdr);
-        if (RT_SUCCESS(rc))
-        {
-            pUVM->stam.s.GVMMStats = Req.Stats;
-
-            /*
-             * Check if the number of host CPUs has changed (it will the first
-             * time around and normally never again).
-             */
-            if (RT_UNLIKELY(pUVM->stam.s.GVMMStats.cHostCpus > pUVM->stam.s.cRegisteredHostCpus))
-            {
-                STAM_LOCK_WR(pUVM);
-                if (RT_UNLIKELY(pUVM->stam.s.GVMMStats.cHostCpus > pUVM->stam.s.cRegisteredHostCpus))
-                {
-                    uint32_t cCpus = pUVM->stam.s.GVMMStats.cHostCpus;
-                    for (uint32_t iCpu  = pUVM->stam.s.cRegisteredHostCpus; iCpu < cCpus; iCpu++)
-                    {
-                        char   szName[120];
-                        size_t cchBase = RTStrPrintf(szName, sizeof(szName), "/GVMM/HostCpus/%u", iCpu);
-                        stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].idCpu, NULL, NULL,
-                                        STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_NONE, "Host CPU ID");
-                        strcpy(&szName[cchBase], "/idxCpuSet");
-                        stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].idxCpuSet, NULL, NULL,
-                                        STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_NONE, "CPU Set index");
-                        strcpy(&szName[cchBase], "/DesiredHz");
-                        stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].uDesiredHz, NULL, NULL,
-                                        STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_HZ, "The desired frequency");
-                        strcpy(&szName[cchBase], "/CurTimerHz");
-                        stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].uTimerHz, NULL, NULL,
-                                        STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_HZ, "The current timer frequency");
-                        strcpy(&szName[cchBase], "/PPTChanges");
-                        stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].cChanges, NULL, NULL,
-                                        STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES, "RTTimerChangeInterval calls");
-                        strcpy(&szName[cchBase], "/PPTStarts");
-                        stamR3RegisterU(pUVM, &pUVM->stam.s.GVMMStats.aHostCpus[iCpu].cStarts, NULL, NULL,
-                                        STAMTYPE_U32, STAMVISIBILITY_ALWAYS, szName, STAMUNIT_OCCURENCES, "RTTimerStart calls");
-                    }
-                    pUVM->stam.s.cRegisteredHostCpus = cCpus;
-                }
-                STAM_UNLOCK_WR(pUVM);
-            }
-        }
-    }
-
-    /*
-     * GMM
-     */
-    fUpdate = false;
-    for (unsigned i = 0; i < RT_ELEMENTS(g_aGMMStats); i++)
-        if (stamR3MultiMatch(papszExpressions, cExpressions, NULL, g_aGMMStats[i].pszName))
-        {
-            fUpdate = true;
-            break;
-        }
-    if (fUpdate)
-    {
-        GMMQUERYSTATISTICSSREQ Req;
-        Req.Hdr.cbReq    = sizeof(Req);
-        Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
-        Req.pSession     = pVM->pSession;
-        int rc = SUPR3CallVMMR0Ex(pVM->pVMR0, NIL_VMCPUID, VMMR0_DO_GMM_QUERY_STATISTICS, 0, &Req.Hdr);
-        if (RT_SUCCESS(rc))
-            pUVM->stam.s.GMMStats = Req.Stats;
-    }
+                        g_aGMMStats[i].enmUnit, g_aGMMStats[i].pszDesc, STAM_REFRESH_GRP_GMM);
 }
 
Index: trunk/src/VBox/VMM/include/NEMInternal.h
===================================================================
--- trunk/src/VBox/VMM/include/NEMInternal.h	(revision 72299)
+++ trunk/src/VBox/VMM/include/NEMInternal.h	(revision 72300)
@@ -27,4 +27,5 @@
 #ifdef RT_OS_WINDOWS
 #include <iprt/nt/hyperv.h>
+#include <iprt/critsect.h>
 #endif
 
@@ -160,4 +161,10 @@
     NEMWINIOCTL                 IoCtlMessageSlotHandleAndGetNext;
 
+    /** Statistics updated by NEMR0UpdateStatistics. */
+    struct
+    {
+        uint64_t                cPagesAvailable;
+        uint64_t                cPagesInUse;
+    } R0Stats;
 #endif /* RT_OS_WINDOWS */
 } NEM;
@@ -256,4 +263,20 @@
 
 #ifdef IN_RING0
+# ifdef RT_OS_WINDOWS
+/**
+ * Windows: Hypercall input/ouput page info.
+ */
+typedef struct NEMR0HYPERCALLDATA
+{
+    /** Host physical address of the hypercall input/output page. */
+    RTHCPHYS                    HCPhysPage;
+    /** Pointer to the hypercall input/output page. */
+    uint8_t                    *pbPage;
+    /** Handle to the memory object of the hypercall input/output page. */
+    RTR0MEMOBJ                  hMemObj;
+} NEMR0HYPERCALLDATA;
+/** Pointer to a Windows hypercall input/output page info. */
+typedef NEMR0HYPERCALLDATA *PNEMR0HYPERCALLDATA;
+# endif /* RT_OS_WINDOWS */
 
 /**
@@ -263,13 +286,6 @@
 {
 # ifdef RT_OS_WINDOWS
-    /** @name Hypercall input/ouput page.
-     * @{ */
-    /** Host physical address of the hypercall input/output page. */
-    RTHCPHYS                    HCPhysHypercallData;
-    /** Pointer to the hypercall input/output page. */
-    uint8_t                    *pbHypercallData;
-    /** Handle to the memory object of the hypercall input/output page. */
-    RTR0MEMOBJ                  hHypercallDataMemObj;
-    /** @} */
+    /** Hypercall input/ouput page. */
+    NEMR0HYPERCALLDATA          HypercallData;
 # else
     uint32_t                    uDummy;
@@ -298,4 +314,9 @@
     NEMWINIOCTL                 IoCtlMessageSlotHandleAndGetNext;
 
+    /** Hypercall input/ouput page for non-EMT. */
+    NEMR0HYPERCALLDATA          HypercallData;
+    /** Critical section protecting use of HypercallData. */
+    RTCRITSECT                  HypercallDataCritSect;
+
 # else
     uint32_t                    uDummy;
Index: trunk/src/VBox/VMM/include/STAMInternal.h
===================================================================
--- trunk/src/VBox/VMM/include/STAMInternal.h	(revision 72299)
+++ trunk/src/VBox/VMM/include/STAMInternal.h	(revision 72300)
@@ -124,4 +124,6 @@
     /** Unit. */
     STAMUNIT            enmUnit;
+    /** The refresh group number (STAM_REFRESH_GRP_XXX). */
+    uint8_t             iRefreshGroup;
     /** Description. */
     const char         *pszDesc;
