Index: trunk/src/VBox/HostDrivers/Support/SUPDrvGip.cpp
===================================================================
--- trunk/src/VBox/HostDrivers/Support/SUPDrvGip.cpp	(revision 64236)
+++ trunk/src/VBox/HostDrivers/Support/SUPDrvGip.cpp	(revision 64255)
@@ -274,4 +274,6 @@
             && RT_IS_POWER_OF_TWO(RTCPUSET_MAX_CPUS))
         {
+            PSUPGIPCPU pGipCpu = SUPGetGipCpuBySetIndex(pGip, iCpuSet);
+
             /*
              * Check whether the IDTR.LIMIT contains a CPU number.
@@ -305,5 +307,6 @@
                     && (ASMCpuId_EDX(UINT32_C(0x80000001)) & X86_CPUID_EXT_FEATURE_EDX_RDTSCP) )
                 {
-                    uint32_t uAux;
+                    uint32_t const  uGroupedAux = (uint8_t)pGipCpu->iCpuGroupMember | ((uint32_t)pGipCpu->iCpuGroup << 8);
+                    uint32_t        uAux;
                     ASMReadTscWithAux(&uAux);
                     if ((uAux & (RTCPUSET_MAX_CPUS - 1)) == idCpu)
@@ -313,4 +316,13 @@
                         if ((uAux & (RTCPUSET_MAX_CPUS - 1)) == idCpu)
                             fSupported |= SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS;
+                    }
+
+                    if (   (uAux & UINT16_MAX) == uGroupedAux
+                        && pGipCpu->iCpuGroupMember <= UINT8_MAX)
+                    {
+                        ASMNopPause();
+                        ASMReadTscWithAux(&uAux);
+                        if ((uAux & UINT16_MAX) == uGroupedAux)
+                            fSupported |= SUPGIPGETCPU_RDTSCP_GROUP_IN_CH_NUMBER_IN_CL;
                     }
                 }
@@ -1259,9 +1271,9 @@
 static void supdrvGipMpEventOnlineOrInitOnCpu(PSUPDRVDEVEXT pDevExt, RTCPUID idCpu)
 {
-    int         iCpuSet = 0;
-    uint16_t    idApic = UINT16_MAX;
-    uint32_t    i = 0;
-    uint64_t    u64NanoTS = 0;
-    PSUPGLOBALINFOPAGE pGip = pDevExt->pGip;
+    PSUPGLOBALINFOPAGE  pGip      = pDevExt->pGip;
+    int                 iCpuSet   = 0;
+    uint16_t            idApic    = UINT16_MAX;
+    uint32_t            i         = 0;
+    uint64_t            u64NanoTS = 0;
 
     AssertPtrReturnVoid(pGip);
@@ -1301,4 +1313,10 @@
     ASMAtomicWriteS16(&pGip->aCPUs[i].iCpuSet, (int16_t)iCpuSet);
     ASMAtomicWriteSize(&pGip->aCPUs[i].idCpu,  idCpu);
+
+    pGip->aCPUs[i].iCpuGroup = 0;
+    pGip->aCPUs[i].iCpuGroupMember = iCpuSet;
+#ifdef RT_OS_WINDOWS
+    pGip->aCPUs[i].iCpuGroup = supdrvOSGipGetGroupFromCpu(pDevExt, idCpu, &pGip->aCPUs[i].iCpuGroupMember);
+#endif
 
     /*
@@ -1683,8 +1701,11 @@
     pCpu->i64TSCDelta        = pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED ? INT64_MAX : 0;
 
-    ASMAtomicWriteSize(&pCpu->enmState, SUPGIPCPUSTATE_INVALID);
-    ASMAtomicWriteU32(&pCpu->idCpu,     NIL_RTCPUID);
-    ASMAtomicWriteS16(&pCpu->iCpuSet,   -1);
-    ASMAtomicWriteU16(&pCpu->idApic,    UINT16_MAX);
+    ASMAtomicWriteSize(&pCpu->enmState,             SUPGIPCPUSTATE_INVALID);
+    ASMAtomicWriteU32(&pCpu->idCpu,                 NIL_RTCPUID);
+    ASMAtomicWriteS16(&pCpu->iCpuSet,               -1);
+    ASMAtomicWriteU16(&pCpu->iCpuGroup,             0);
+    ASMAtomicWriteU16(&pCpu->iCpuGroupMember,       UINT16_MAX);
+    ASMAtomicWriteU16(&pCpu->idApic,                UINT16_MAX);
+    ASMAtomicWriteU32(&pCpu->iReservedForNumaNode,  0);
 
     /*
@@ -1764,4 +1785,5 @@
     pGip->cPresentCpus            = RTMpGetPresentCount();
     pGip->cPossibleCpus           = RTMpGetCount();
+    pGip->cPossibleCpuGroups      = 1;
     pGip->idCpuMax                = RTMpGetMaxCpuId();
     for (i = 0; i < RT_ELEMENTS(pGip->aiCpuFromApicId); i++)
@@ -1769,4 +1791,10 @@
     for (i = 0; i < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx); i++)
         pGip->aiCpuFromCpuSetIdx[i] = UINT16_MAX;
+    pGip->aiFirstCpuSetIdxFromCpuGroup[0] = 0;
+    for (i = 1; i < RT_ELEMENTS(pGip->aiFirstCpuSetIdxFromCpuGroup); i++)
+        pGip->aiFirstCpuSetIdxFromCpuGroup[i] = UINT16_MAX;
+#ifdef RT_OS_WINDOWS
+    supdrvOSInitGipGroupTable(pDevExt, pGip);
+#endif
     for (i = 0; i < cCpus; i++)
         supdrvGipInitCpu(pGip, &pGip->aCPUs[i], u64NanoTS, 0 /*uCpuHz*/);
@@ -2394,5 +2422,7 @@
      * run.
      */
-    if (RT_UNLIKELY(iTick == 1))
+    if (RT_LIKELY(iTick != 1))
+    { /* likely*/ }
+    else
     {
         iCpu = supdrvGipFindOrAllocCpuIndexForCpuId(pGip, idCpu);
Index: trunk/src/VBox/HostDrivers/Support/SUPDrvIOC.h
===================================================================
--- trunk/src/VBox/HostDrivers/Support/SUPDrvIOC.h	(revision 64236)
+++ trunk/src/VBox/HostDrivers/Support/SUPDrvIOC.h	(revision 64255)
@@ -215,5 +215,5 @@
  *          - nothing.
  */
-#define SUPDRV_IOC_VERSION                              0x00260000
+#define SUPDRV_IOC_VERSION                              0x00270000
 
 /** SUP_IOCTL_COOKIE. */
Index: trunk/src/VBox/HostDrivers/Support/SUPDrvInternal.h
===================================================================
--- trunk/src/VBox/HostDrivers/Support/SUPDrvInternal.h	(revision 64236)
+++ trunk/src/VBox/HostDrivers/Support/SUPDrvInternal.h	(revision 64255)
@@ -800,4 +800,27 @@
 void VBOXCALL   supdrvOSSessionHashTabRemoved(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, void *pvUser);
 
+/**
+ * Called during GIP initialization to set up the group table and group count.
+ *
+ * This is currently only implemented on windows [lazy bird].
+ *
+ * @param   pDevExt             The device globals.
+ * @param   pGip                The GIP which group table needs initialization.
+ *                              It's only partially initialized at this point.
+ */
+void VBOXCALL   supdrvOSInitGipGroupTable(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip);
+
+/**
+ * Gets the CPU group and member indexes for the given CPU ID.
+ *
+ * This is currently only implemented on windows [lazy bird].
+ *
+ * @returns CPU group number.
+ * @param   pDevExt             The device globals.
+ * @param   idCpu               The ID of the CPU.
+ * @param   piCpuGroupMember    Where to return the group member number.
+ */
+uint16_t VBOXCALL supdrvOSGipGetGroupFromCpu(PSUPDRVDEVEXT pDevExt, RTCPUID idCpu, uint16_t *piCpuGroupMember);
+
 void VBOXCALL   supdrvOSObjInitCreator(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession);
 bool VBOXCALL   supdrvOSObjCanAccess(PSUPDRVOBJ pObj, PSUPDRVSESSION pSession, const char *pszObjName, int *prc);
Index: trunk/src/VBox/HostDrivers/Support/testcase/tstGIP-2.cpp
===================================================================
--- trunk/src/VBox/HostDrivers/Support/testcase/tstGIP-2.cpp	(revision 64236)
+++ trunk/src/VBox/HostDrivers/Support/testcase/tstGIP-2.cpp	(revision 64255)
@@ -131,15 +131,23 @@
                 SUPR3GipSetFlags(SUPGIP_FLAGS_TESTING_ENABLE, UINT32_MAX);
 
-            RTPrintf("tstGIP-2: cCpus=%d  u32UpdateHz=%RU32  u32UpdateIntervalNS=%RU32  u64NanoTSLastUpdateHz=%RX64  u64CpuHz=%RU64  uCpuHzRef=%RU64  u32Mode=%d (%s)  fTestMode=%RTbool  u32Version=%#x\n",
+            RTPrintf("tstGIP-2: u32Mode=%d (%s)  fTestMode=%RTbool  u32Version=%#x  fGetGipCpu=%#RX32\n",
+                     g_pSUPGlobalInfoPage->u32Mode,
+                     SUPGetGIPModeName(g_pSUPGlobalInfoPage),
+                     fTestMode,
+                     g_pSUPGlobalInfoPage->u32Version,
+                     g_pSUPGlobalInfoPage->fGetGipCpu);
+            RTPrintf("tstGIP-2: cCpus=%d  cPossibleCpus=%d cPossibleCpuGroups=%d cPresentCpus=%d cOnlineCpus=%d\n",
                      g_pSUPGlobalInfoPage->cCpus,
+                     g_pSUPGlobalInfoPage->cPossibleCpus,
+                     g_pSUPGlobalInfoPage->cPossibleCpuGroups,
+                     g_pSUPGlobalInfoPage->cPresentCpus,
+                     g_pSUPGlobalInfoPage->cOnlineCpus);
+            RTPrintf("tstGIP-2: u32UpdateHz=%RU32  u32UpdateIntervalNS=%RU32  u64NanoTSLastUpdateHz=%RX64  u64CpuHz=%RU64  uCpuHzRef=%RU64\n",
                      g_pSUPGlobalInfoPage->u32UpdateHz,
                      g_pSUPGlobalInfoPage->u32UpdateIntervalNS,
                      g_pSUPGlobalInfoPage->u64NanoTSLastUpdateHz,
                      g_pSUPGlobalInfoPage->u64CpuHz,
-                     uCpuHzRef,
-                     g_pSUPGlobalInfoPage->u32Mode,
-                     SUPGetGIPModeName(g_pSUPGlobalInfoPage),
-                     fTestMode,
-                     g_pSUPGlobalInfoPage->u32Version);
+                     uCpuHzRef);
+
             RTPrintf(fHex
                      ? "tstGIP-2:     it: u64NanoTS        delta     u64TSC           UpIntTSC H  TransId      CpuHz      %sTSC Interval History...\n"
Index: trunk/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp
===================================================================
--- trunk/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp	(revision 64236)
+++ trunk/src/VBox/HostDrivers/Support/win/SUPDrv-win.cpp	(revision 64255)
@@ -359,4 +359,38 @@
 #endif /* VBOXDRV_WITH_FAST_IO */
 
+/** Default ZERO value. */
+static ULONG                        g_fOptDefaultZero = 0;
+/** Registry values.
+ * We wrap these in a struct to ensure they are followed by a little zero
+ * padding in order to limit the chance of trouble on unpatched systems.  */
+struct
+{
+    /** The ForceAsync registry value. */
+    ULONG                           fOptForceAsyncTsc;
+    /** Padding. */
+    uint64_t                        au64Padding[2];
+}                                   g_Options = { FALSE, 0, 0 };
+/** Registry query table for RtlQueryRegistryValues. */
+static RTL_QUERY_REGISTRY_TABLE     g_aRegValues[] =
+{
+    {
+        /* .QueryRoutine = */   NULL,
+        /* .Flags = */          RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK,
+        /* .Name = */           L"ForceAsyncTsc",
+        /* .EntryContext = */   &g_Options.fOptForceAsyncTsc,
+        /* .DefaultType = */    (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_DWORD,
+        /* .DefaultData = */    &g_fOptDefaultZero,
+        /* .DefaultLength = */  sizeof(g_fOptDefaultZero),
+    },
+    {   NULL, 0, NULL, NULL, 0, NULL, 0 } /* terminator entry. */
+};
+
+/** Pointer to KeQueryMaximumGroupCount. */
+static PFNKEQUERYMAXIMUMGROUPCOUNT      g_pfnKeQueryMaximumGroupCount = NULL;
+/** Pointer to KeGetProcessorIndexFromNumber. */
+static PFNKEGETPROCESSORINDEXFROMNUMBER g_pfnKeGetProcessorIndexFromNumber = NULL;
+/** Pointer to KeGetProcessorNumberFromIndex. */
+static PFNKEGETPROCESSORNUMBERFROMINDEX g_pfnKeGetProcessorNumberFromIndex = NULL;
+
 #ifdef VBOX_WITH_HARDENING
 /** Pointer to the stub device instance. */
@@ -554,7 +588,41 @@
 
     /*
+     * Query options first so any overflows on unpatched machines will do less
+     * harm (see MS11-011 / 2393802 / 2011-03-18).
+     *
+     * Unfortunately, pRegPath isn't documented as zero terminated, even if it
+     * quite likely always is, so we have to make a copy here.
+     */
+    NTSTATUS rcNt;
+    PWSTR pwszCopy = (PWSTR)ExAllocatePoolWithTag(NonPagedPool, pRegPath->Length + sizeof(WCHAR), 'VBox');
+    if (pwszCopy)
+    {
+        memcpy(pwszCopy, pRegPath->Buffer, pRegPath->Length);
+        pwszCopy[pRegPath->Length / sizeof(WCHAR)] = '\0';
+        rcNt = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, pwszCopy,
+                                      g_aRegValues, NULL /*pvContext*/, NULL /*pvEnv*/);
+        ExFreePoolWithTag(pwszCopy, 'VBox');
+        /* Probably safe to ignore rcNt here. */
+    }
+
+    /*
+     * Resolve methods we want but isn't available everywhere.
+     */
+    UNICODE_STRING RoutineName;
+    RtlInitUnicodeString(&RoutineName, L"KeQueryMaximumGroupCount");
+    g_pfnKeQueryMaximumGroupCount = (PFNKEQUERYMAXIMUMGROUPCOUNT)MmGetSystemRoutineAddress(&RoutineName);
+
+    RtlInitUnicodeString(&RoutineName, L"KeGetProcessorIndexFromNumber");
+    g_pfnKeGetProcessorIndexFromNumber = (PFNKEGETPROCESSORINDEXFROMNUMBER)MmGetSystemRoutineAddress(&RoutineName);
+
+    RtlInitUnicodeString(&RoutineName, L"KeGetProcessorNumberFromIndex");
+    g_pfnKeGetProcessorNumberFromIndex = (PFNKEGETPROCESSORNUMBERFROMINDEX)MmGetSystemRoutineAddress(&RoutineName);
+
+    Assert(   (g_pfnKeGetProcessorNumberFromIndex != NULL) == (g_pfnKeGetProcessorIndexFromNumber != NULL)
+           && (g_pfnKeGetProcessorNumberFromIndex != NULL) == (g_pfnKeQueryMaximumGroupCount != NULL)); /* all or nothing. */
+
+    /*
      * Initialize the runtime (IPRT).
      */
-    NTSTATUS rcNt;
     int vrc = RTR0Init(0);
     if (RT_SUCCESS(vrc))
@@ -1643,4 +1711,71 @@
 
 
+void VBOXCALL supdrvOSInitGipGroupTable(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip)
+{
+    NOREF(pDevExt);
+
+    /*
+     * The indexes are assigned in group order (see initterm-r0drv-nt.cpp).
+     */
+    if (   g_pfnKeQueryMaximumGroupCount
+        && g_pfnKeGetProcessorIndexFromNumber)
+    {
+        unsigned cGroups = g_pfnKeQueryMaximumGroupCount();
+        AssertStmt(cGroups > 0, cGroups = 1);
+        AssertStmt(cGroups < RT_ELEMENTS(pGip->aiFirstCpuSetIdxFromCpuGroup),
+                   cGroups = RT_ELEMENTS(pGip->aiFirstCpuSetIdxFromCpuGroup));
+        pGip->cPossibleCpuGroups = cGroups;
+
+        KEPROCESSORINDEX idxCpuMin = 0;
+        for (unsigned iGroup = 0; iGroup < cGroups; iGroup++)
+        {
+            PROCESSOR_NUMBER ProcNum;
+            ProcNum.Group    = (USHORT)iGroup;
+            ProcNum.Number   = 0;
+            ProcNum.Reserved = 0;
+            KEPROCESSORINDEX idxCpu = g_pfnKeGetProcessorIndexFromNumber(&ProcNum);
+            Assert(idxCpu != INVALID_PROCESSOR_INDEX);
+            Assert(idxCpu >= idxCpuMin);
+            idxCpuMin = idxCpu;
+            pGip->aiFirstCpuSetIdxFromCpuGroup[iGroup] = (uint16_t)idxCpu;
+        }
+    }
+    else
+    {
+        Assert(!g_pfnKeQueryMaximumGroupCount);
+        Assert(!g_pfnKeGetProcessorIndexFromNumber);
+
+        pGip->cPossibleCpuGroups              = 1;
+        pGip->aiFirstCpuSetIdxFromCpuGroup[0] = 0;
+    }
+}
+
+
+uint16_t VBOXCALL supdrvOSGipGetGroupFromCpu(PSUPDRVDEVEXT pDevExt, RTCPUID idCpu, uint16_t *piCpuGroupMember)
+{
+    NOREF(pDevExt);
+
+    /*
+     * This is just a wrapper around KeGetProcessorNumberFromIndex.
+     */
+    if (g_pfnKeGetProcessorNumberFromIndex)
+    {
+        PROCESSOR_NUMBER ProcNum = { UINT16_MAX, UINT8_MAX, 0 };
+        NTSTATUS rcNt = g_pfnKeGetProcessorNumberFromIndex(idCpu, &ProcNum);
+        if (NT_SUCCESS(rcNt))
+        {
+            Assert(ProcNum.Group < g_pfnKeQueryMaximumGroupCount());
+            *piCpuGroupMember = ProcNum.Number;
+            return ProcNum.Group;
+        }
+
+        AssertMsgFailed(("rcNt=%#x for idCpu=%u\n", rcNt, idCpu));
+    }
+
+    *piCpuGroupMember = 0;
+    return idCpu;
+}
+
+
 /**
  * Initializes any OS specific object creator fields.
@@ -1680,5 +1815,5 @@
 {
     RT_NOREF1(pDevExt);
-    return false;
+    return g_Options.fOptForceAsyncTsc != 0;
 }
 
