Index: /trunk/include/VBox/vmm/nem.h
===================================================================
--- /trunk/include/VBox/vmm/nem.h	(revision 91675)
+++ /trunk/include/VBox/vmm/nem.h	(revision 91676)
@@ -89,4 +89,6 @@
 /** @defgroup grp_nem_r0    The NEM ring-0 Context API
  * @{  */
+VMMR0_INT_DECL(int)  NEMR0Init(void);
+VMMR0_INT_DECL(void) NEMR0Term(void);
 VMMR0_INT_DECL(int)  NEMR0InitVM(PGVM pGVM);
 VMMR0_INT_DECL(int)  NEMR0InitVMPart2(PGVM pGVM);
@@ -101,4 +103,7 @@
 VMMR0_INT_DECL(int)  NEMR0UpdateStatistics(PGVM pGVM, VMCPUID idCpu);
 VMMR0_INT_DECL(int)  NEMR0DoExperiment(PGVM pGVM, VMCPUID idCpu, uint64_t u64Arg);
+#ifdef RT_OS_WINDOWS
+VMMR0_INT_DECL(int)  NEMR0WinGetPartitionId(PGVM pGVM, uintptr_t uHandle);
+#endif
 /** @} */
 
Index: /trunk/src/VBox/VMM/Makefile.kmk
===================================================================
--- /trunk/src/VBox/VMM/Makefile.kmk	(revision 91675)
+++ /trunk/src/VBox/VMM/Makefile.kmk	(revision 91676)
@@ -580,5 +580,7 @@
  endif
  ifdef VBOX_WITH_NATIVE_NEM
-VMMR0_SOURCES.win.amd64 += VMMR0/NEMR0Native-win.cpp
+VMMR0_SOURCES.win.amd64 += \
+	VMMR0/NEMR0Native-win.cpp \
+	VMMR0/NEMR0NativeA-win.asm
 VMMR0_DEFS.win.amd64    += VBOX_WITH_NATIVE_NEM VBOX_WITH_NEM_R0
 VMMR0/NEMR0Native-win.cpp_SDKS.win = ReorderCompilerIncs $(VBOX_WINDDK) $(VBOX_WINPSDK)INCS
Index: /trunk/src/VBox/VMM/VMMR0/NEMR0Native-win.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/NEMR0Native-win.cpp	(revision 91675)
+++ /trunk/src/VBox/VMM/VMMR0/NEMR0Native-win.cpp	(revision 91676)
@@ -39,8 +39,13 @@
 #include <VBox/param.h>
 
+#include <iprt/ctype.h>
+#include <iprt/critsect.h>
 #include <iprt/dbg.h>
+#include <iprt/mem.h>
 #include <iprt/memobj.h>
 #include <iprt/string.h>
 #include <iprt/time.h>
+#define PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS32_PECOFF
+#include <iprt/formats/pecoff.h>
 
 
@@ -75,4 +80,38 @@
  */
 static NTSTATUS (*g_pfnWinHvDepositMemory)(uintptr_t idPartition, size_t cPages, uintptr_t IdealNode, size_t *pcActuallyAdded);
+
+RT_C_DECLS_BEGIN
+/**
+ * The WinHvGetPartitionProperty function we intercept in VID.SYS to get the
+ * Hyper-V partition ID.
+ *
+ * This is used from assembly.
+ */
+NTSTATUS WinHvGetPartitionProperty(uintptr_t idPartition, HV_PARTITION_PROPERTY_CODE enmProperty, PHV_PARTITION_PROPERTY puValue);
+decltype(WinHvGetPartitionProperty) *g_pfnWinHvGetPartitionProperty;
+RT_C_DECLS_END
+
+/** @name VID.SYS image details.
+ * @{ */
+static uint8_t                                 *g_pbVidSys                            = NULL;
+static uintptr_t                                g_cbVidSys                            = 0;
+static PIMAGE_NT_HEADERS                        g_pVidSysHdrs                         = NULL;
+/** Pointer to the import thunk entry in VID.SYS for WinHvGetPartitionProperty if we found it. */
+static decltype(WinHvGetPartitionProperty)    **g_ppfnVidSysWinHvGetPartitionProperty = NULL;
+
+/** Critical section protecting the WinHvGetPartitionProperty hacking. */
+static RTCRITSECT                               g_VidSysCritSect;
+RT_C_DECLS_BEGIN
+/** The partition ID passed to WinHvGetPartitionProperty by VID.SYS.   */
+HV_PARTITION_ID                                 g_idVidSysFoundPartition = HV_PARTITION_ID_INVALID;
+/** The thread which is currently looking for a partition ID. */
+RTNATIVETHREAD                                  g_hVidSysMatchThread     = NIL_RTNATIVETHREAD;
+/** The property code we expect in WinHvGetPartitionProperty. */
+VID_PARTITION_PROPERTY_CODE                     g_enmVidSysMatchProperty = INT64_MAX;
+/* NEMR0NativeA-win.asm: */
+extern uint8_t                                  g_abNemR0WinHvrWinHvGetPartitionProperty_OriginalProlog[64];
+RT_C_DECLS_END
+/** @} */
+
 
 
@@ -92,4 +131,10 @@
                                              void *pvOutput, uint32_t cbOutput);
 
+/* NEMR0NativeA-win.asm: */
+DECLASM(NTSTATUS)    nemR0VidSysWinHvGetPartitionProperty(uintptr_t idPartition, HV_PARTITION_PROPERTY_CODE enmProperty,
+                                                          PHV_PARTITION_PROPERTY puValue);
+DECLASM(NTSTATUS)    nemR0WinHvrWinHvGetPartitionProperty(uintptr_t idPartition, HV_PARTITION_PROPERTY_CODE enmProperty,
+                                                          PHV_PARTITION_PROPERTY puValue);
+
 
 /*
@@ -103,4 +148,21 @@
 #include "../VMMAll/NEMAllNativeTemplate-win.cpp.h"
 
+
+/**
+ * Module initialization for NEM.
+ */
+VMMR0_INT_DECL(int)  NEMR0Init(void)
+{
+    return RTCritSectInit(&g_VidSysCritSect);
+}
+
+
+/**
+ * Module termination for NEM.
+ */
+VMMR0_INT_DECL(void) NEMR0Term(void)
+{
+    RTCritSectDelete(&g_VidSysCritSect);
+}
 
 
@@ -132,4 +194,5 @@
 }
 
+
 /**
  * Worker for NEMR0CleanupVM and NEMR0InitVM that cleans up a hypercall page.
@@ -148,4 +211,167 @@
     pHypercallData->hMemObj    = NIL_RTR0MEMOBJ;
     pHypercallData->HCPhysPage = NIL_RTHCPHYS;
+}
+
+
+static int nemR0StrICmp(const char *psz1, const char *psz2)
+{
+    for (;;)
+    {
+        char ch1 = *psz1++;
+        char ch2 = *psz2++;
+        if (   ch1 != ch2
+            && RT_C_TO_LOWER(ch1) != RT_C_TO_LOWER(ch2))
+            return ch1 - ch2;
+        if (!ch1)
+            return 0;
+    }
+}
+
+
+/**
+ * Worker for nemR0PrepareForVidSysIntercept().
+ */
+static void nemR0PrepareForVidSysInterceptInner(void)
+{
+    uint32_t const             cbImage       = g_cbVidSys;
+    uint8_t * const            pbImage       = g_pbVidSys;
+    PIMAGE_NT_HEADERS const    pNtHdrs       = g_pVidSysHdrs;
+    uintptr_t const            offEndNtHdrs  = (uintptr_t)(pNtHdrs + 1) - (uintptr_t)pbImage;
+
+#define CHECK_LOG_RET(a_Expr, a_LogRel) do { \
+            if (RT_LIKELY(a_Expr)) { /* likely */ } \
+            else \
+            { \
+                LogRel(a_LogRel); \
+                return; \
+            } \
+        } while (0)
+
+    //__try
+    {
+        /*
+         * Get and validate the import directory entry.
+         */
+        CHECK_LOG_RET(   pNtHdrs->OptionalHeader.NumberOfRvaAndSizes >  IMAGE_DIRECTORY_ENTRY_IMPORT
+                      || pNtHdrs->OptionalHeader.NumberOfRvaAndSizes <= IMAGE_NUMBEROF_DIRECTORY_ENTRIES * 4,
+                      ("NEMR0: vid.sys: NumberOfRvaAndSizes is out of range: %#x\n", pNtHdrs->OptionalHeader.NumberOfRvaAndSizes));
+
+        IMAGE_DATA_DIRECTORY const ImportDir = pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
+        CHECK_LOG_RET(   ImportDir.Size >= sizeof(IMAGE_IMPORT_DESCRIPTOR)
+                      && ImportDir.VirtualAddress >= offEndNtHdrs /* ASSUMES NT headers before imports */
+                      && (uint64_t)ImportDir.VirtualAddress + ImportDir.Size <= cbImage,
+                      ("NEMR0: vid.sys: Bad import directory entry: %#x LB %#x (cbImage=%#x, offEndNtHdrs=%#zx)\n",
+                       ImportDir.VirtualAddress, ImportDir.Size, cbImage, offEndNtHdrs));
+
+        /*
+         * Walk the import descriptor table looking for NTDLL.DLL.
+         */
+        for (PIMAGE_IMPORT_DESCRIPTOR pImps = (PIMAGE_IMPORT_DESCRIPTOR)&pbImage[ImportDir.VirtualAddress];
+             pImps->Name != 0 && pImps->FirstThunk != 0;
+             pImps++)
+        {
+            CHECK_LOG_RET(pImps->Name < cbImage, ("NEMR0: vid.sys: Bad import directory entry name: %#x", pImps->Name));
+            const char *pszModName = (const char *)&pbImage[pImps->Name];
+            if (nemR0StrICmp(pszModName, "winhvr.sys"))
+                continue;
+            CHECK_LOG_RET(pImps->FirstThunk < cbImage && pImps->FirstThunk >= offEndNtHdrs,
+                          ("NEMR0: vid.sys: Bad FirstThunk: %#x", pImps->FirstThunk));
+            CHECK_LOG_RET(   pImps->u.OriginalFirstThunk == 0
+                          || (pImps->u.OriginalFirstThunk >= offEndNtHdrs && pImps->u.OriginalFirstThunk < cbImage),
+                          ("NEMR0: vid.sys: Bad OriginalFirstThunk: %#x", pImps->u.OriginalFirstThunk));
+
+            /*
+             * Walk the thunks table(s) looking for WinHvGetPartitionProperty.
+             */
+            uintptr_t *puFirstThunk = (uintptr_t *)&pbImage[pImps->FirstThunk]; /* update this. */
+            if (   pImps->u.OriginalFirstThunk != 0
+                && pImps->u.OriginalFirstThunk != pImps->FirstThunk)
+            {
+                uintptr_t const *puOrgThunk = (uintptr_t const *)&pbImage[pImps->u.OriginalFirstThunk]; /* read from this. */
+                uintptr_t        cLeft      = (cbImage - (RT_MAX(pImps->FirstThunk, pImps->u.OriginalFirstThunk)))
+                                            / sizeof(*puFirstThunk);
+                while (cLeft-- > 0 && *puOrgThunk != 0)
+                {
+                    if (!(*puOrgThunk & IMAGE_ORDINAL_FLAG64))
+                    {
+                        CHECK_LOG_RET(*puOrgThunk >= offEndNtHdrs && *puOrgThunk < cbImage,
+                                      ("NEMR0: vid.sys: Bad thunk entry: %#x", *puOrgThunk));
+
+                        const char *pszSymbol = (const char *)&pbImage[*puOrgThunk + 2];
+                        if (strcmp(pszSymbol, "WinHvGetPartitionProperty") == 0)
+                            g_ppfnVidSysWinHvGetPartitionProperty = (decltype(WinHvGetPartitionProperty) **)puFirstThunk;
+                    }
+
+                    puOrgThunk++;
+                    puFirstThunk++;
+                }
+            }
+            else
+            {
+                /* No original thunk table, so scan the resolved symbols for a match
+                   with the WinHvGetPartitionProperty address. */
+                uintptr_t const uNeedle = (uintptr_t)g_pfnWinHvGetPartitionProperty;
+                uintptr_t       cLeft   = (cbImage - pImps->FirstThunk) / sizeof(*puFirstThunk);
+                while (cLeft-- > 0 && *puFirstThunk != 0)
+                {
+                    if (*puFirstThunk == uNeedle)
+                        g_ppfnVidSysWinHvGetPartitionProperty = (decltype(WinHvGetPartitionProperty) **)puFirstThunk;
+                    puFirstThunk++;
+                }
+            }
+        }
+
+        /* Report the findings: */
+        if (g_ppfnVidSysWinHvGetPartitionProperty)
+            LogRel(("NEMR0: vid.sys: Found WinHvGetPartitionProperty import thunk at %p (value %p vs %p)\n",
+                    g_ppfnVidSysWinHvGetPartitionProperty,*g_ppfnVidSysWinHvGetPartitionProperty, g_pfnWinHvGetPartitionProperty));
+        else
+            LogRel(("NEMR0: vid.sys: Did not find WinHvGetPartitionProperty!\n"));
+    }
+    //__except(EXCEPTION_EXECUTE_HANDLER)
+    //{
+    //    return;
+    //}
+#undef CHECK_LOG_RET
+}
+
+
+/**
+ * Worker for NEMR0InitVM that prepares for intercepting stuff in VID.SYS.
+ */
+static void nemR0PrepareForVidSysIntercept(RTDBGKRNLINFO hKrnlInfo)
+{
+    /*
+     * Resolve the symbols we need first.
+     */
+    int rc = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, "vid.sys", "__ImageBase", (void **)&g_pbVidSys);
+    if (RT_SUCCESS(rc))
+    {
+        rc = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, "vid.sys", "__ImageSize", (void **)&g_cbVidSys);
+        if (RT_SUCCESS(rc))
+        {
+            rc = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, "vid.sys", "__ImageNtHdrs", (void **)&g_pVidSysHdrs);
+            if (RT_SUCCESS(rc))
+            {
+                rc = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, "winhvr.sys", "WinHvGetPartitionProperty",
+                                                (void **)&g_pfnWinHvGetPartitionProperty);
+                if (RT_SUCCESS(rc))
+                {
+                    /*
+                     * Now locate the import thunk entry for WinHvGetPartitionProperty in vid.sys.
+                     */
+                    nemR0PrepareForVidSysInterceptInner();
+                }
+                else
+                    LogRel(("NEMR0: Failed to find winhvr.sys!WinHvGetPartitionProperty (%Rrc)\n", rc));
+            }
+            else
+                LogRel(("NEMR0: Failed to find vid.sys!__ImageNtHdrs (%Rrc)\n", rc));
+        }
+        else
+            LogRel(("NEMR0: Failed to find vid.sys!__ImageSize (%Rrc)\n", rc));
+    }
+    else
+        LogRel(("NEMR0: Failed to find vid.sys!__ImageBase (%Rrc)\n", rc));
 }
 
@@ -187,4 +413,12 @@
                 rc = rc == VERR_MODULE_NOT_FOUND ? VERR_NEM_MISSING_KERNEL_API_2 : VERR_NEM_MISSING_KERNEL_API_3;
         }
+
+        /*
+         * Since late 2021 we may also need to do some nasty trickery with vid.sys to get
+         * the partition ID. So, ge the necessary info while we have a hKrnlInfo instance.
+         */
+        if (RT_SUCCESS(rc))
+            nemR0PrepareForVidSysIntercept(hKrnlInfo);
+
         RTR0DbgKrnlInfoRelease(hKrnlInfo);
         if (RT_SUCCESS(rc))
@@ -283,4 +517,284 @@
 
 /**
+ * Here is something that we really do not wish to do, but find us force do to
+ * right now as we cannot rewrite the memory management of VBox 6.1 in time for
+ * windows 11.
+ *
+ * @returns VBox status code.
+ * @param   pGVM        The ring-0 VM structure.
+ * @param   pahMemObjs  Array of 6 memory objects that the caller will release.
+ *                      ASSUMES that they are initialized to NIL.
+ */
+static int nemR0InitVMPart2DontWannaDoTheseUglyPartitionIdFallbacks(PGVM pGVM, PRTR0MEMOBJ pahMemObjs)
+{
+    /*
+     * Check preconditions:
+     */
+    if (   !g_ppfnVidSysWinHvGetPartitionProperty
+        || (uintptr_t)g_ppfnVidSysWinHvGetPartitionProperty & (sizeof(uintptr_t) - 1))
+    {
+        LogRel(("NEMR0: g_ppfnVidSysWinHvGetPartitionProperty is NULL or misaligned (%p), partition ID fallback not possible.\n",
+                g_ppfnVidSysWinHvGetPartitionProperty));
+        return VERR_NEM_INIT_FAILED;
+    }
+    if (!g_pfnWinHvGetPartitionProperty)
+    {
+        LogRel(("NEMR0: g_pfnWinHvGetPartitionProperty is NULL, partition ID fallback not possible.\n"));
+        return VERR_NEM_INIT_FAILED;
+    }
+    if (!pGVM->nem.s.IoCtlGetPartitionProperty.uFunction)
+    {
+        LogRel(("NEMR0: IoCtlGetPartitionProperty.uFunction is 0, partition ID fallback not possible.\n"));
+        return VERR_NEM_INIT_FAILED;
+    }
+
+    /*
+     * Create an alias for the thunk table entry because its very likely to be read-only.
+     */
+    int rc = RTR0MemObjLockKernel(&pahMemObjs[0], g_ppfnVidSysWinHvGetPartitionProperty, sizeof(uintptr_t), RTMEM_PROT_READ);
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("NEMR0: RTR0MemObjLockKernel failed on VID.SYS thunk table entry: %Rrc\n", rc));
+        return rc;
+    }
+
+    rc = RTR0MemObjEnterPhys(&pahMemObjs[1], RTR0MemObjGetPagePhysAddr(pahMemObjs[0], 0), PAGE_SIZE, RTMEM_CACHE_POLICY_DONT_CARE);
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("NEMR0: RTR0MemObjEnterPhys failed on VID.SYS thunk table entry: %Rrc\n", rc));
+        return rc;
+    }
+
+    rc = RTR0MemObjMapKernel(&pahMemObjs[2], pahMemObjs[1], (void *)-1, 0, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("NEMR0: RTR0MemObjMapKernel failed on VID.SYS thunk table entry: %Rrc\n", rc));
+        return rc;
+    }
+
+    decltype(WinHvGetPartitionProperty) **ppfnThunkAlias
+        = (decltype(WinHvGetPartitionProperty) **)(  (uintptr_t)RTR0MemObjAddress(pahMemObjs[2])
+                                                   | ((uintptr_t)g_ppfnVidSysWinHvGetPartitionProperty & PAGE_OFFSET_MASK));
+    LogRel(("NEMR0: ppfnThunkAlias=%p *ppfnThunkAlias=%p; original: %p & %p, phys %RHp\n", ppfnThunkAlias, *ppfnThunkAlias,
+            g_ppfnVidSysWinHvGetPartitionProperty, *g_ppfnVidSysWinHvGetPartitionProperty,
+            RTR0MemObjGetPagePhysAddr(pahMemObjs[0], 0) ));
+
+    /*
+     * Create an alias for the target code in WinHvr.sys as there is a very decent
+     * chance we have to patch it.
+     */
+    rc = RTR0MemObjLockKernel(&pahMemObjs[3], g_pfnWinHvGetPartitionProperty, sizeof(uintptr_t), RTMEM_PROT_READ);
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("NEMR0: RTR0MemObjLockKernel failed on WinHvGetPartitionProperty (%p): %Rrc\n", g_pfnWinHvGetPartitionProperty, rc));
+        return rc;
+    }
+
+    rc = RTR0MemObjEnterPhys(&pahMemObjs[4], RTR0MemObjGetPagePhysAddr(pahMemObjs[3], 0), PAGE_SIZE, RTMEM_CACHE_POLICY_DONT_CARE);
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("NEMR0: RTR0MemObjEnterPhys failed on WinHvGetPartitionProperty: %Rrc\n", rc));
+        return rc;
+    }
+
+    rc = RTR0MemObjMapKernel(&pahMemObjs[5], pahMemObjs[4], (void *)-1, 0, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("NEMR0: RTR0MemObjMapKernel failed on WinHvGetPartitionProperty: %Rrc\n", rc));
+        return rc;
+    }
+
+    uint8_t *pbTargetAlias = (uint8_t *)(  (uintptr_t)RTR0MemObjAddress(pahMemObjs[5])
+                                         | ((uintptr_t)g_pfnWinHvGetPartitionProperty & PAGE_OFFSET_MASK));
+    LogRel(("NEMR0: pbTargetAlias=%p %.16Rhxs; original: %p %.16Rhxs, phys %RHp\n", pbTargetAlias, pbTargetAlias,
+            g_pfnWinHvGetPartitionProperty, g_pfnWinHvGetPartitionProperty, RTR0MemObjGetPagePhysAddr(pahMemObjs[3], 0) ));
+
+    /*
+     * Analyse the target functions prologue to figure out how much we should copy
+     * when patching it.  We repeat this every time because we don't want to get
+     * tripped up by someone else doing the same stuff as we're doing here.
+     * We need at least 12 bytes for the patch sequence (MOV RAX, QWORD; JMP RAX)
+     */
+    union
+    {
+        uint8_t  ab[48];    /**< Must be equal or smallar than g_abNemR0WinHvrWinHvGetPartitionProperty_OriginalProlog */
+        int64_t  ai64[6];
+    } Org;
+    memcpy(Org.ab, g_pfnWinHvGetPartitionProperty, sizeof(Org)); /** @todo ASSUMES 48 valid bytes start at function... */
+
+    uint32_t       offJmpBack = 0;
+    uint32_t const cbMinJmpPatch = 12;
+    DISSTATE       Dis;
+    while (offJmpBack < cbMinJmpPatch && offJmpBack < sizeof(Org) - 16)
+    {
+        uint32_t cbInstr = 1;
+        rc = DISInstr(&Org.ab[offJmpBack], DISCPUMODE_64BIT, &Dis, &cbInstr);
+        if (RT_FAILURE(rc))
+        {
+            LogRel(("NEMR0: DISInstr failed %#x bytes into WinHvGetPartitionProperty: %Rrc (%.48Rhxs)\n",
+                    offJmpBack, rc, Org.ab));
+            break;
+        }
+        if (Dis.pCurInstr->fOpType & DISOPTYPE_CONTROLFLOW)
+        {
+            LogRel(("NEMR0: Control flow instruction %#x bytes into WinHvGetPartitionProperty prologue: %.48Rhxs\n",
+                    offJmpBack, Org.ab));
+            break;
+        }
+        if (Dis.ModRM.Bits.Mod == 0 && Dis.ModRM.Bits.Rm == 5 /* wrt RIP */)
+        {
+            LogRel(("NEMR0: RIP relative addressing %#x bytes into WinHvGetPartitionProperty prologue: %.48Rhxs\n",
+                    offJmpBack, Org.ab));
+            break;
+        }
+        offJmpBack += cbInstr;
+    }
+
+    uintptr_t const cbLeftInPage = PAGE_SIZE - ((uintptr_t)g_pfnWinHvGetPartitionProperty & PAGE_OFFSET_MASK);
+    if (cbLeftInPage < 16 && offJmpBack >= cbMinJmpPatch)
+    {
+        LogRel(("NEMR0: WinHvGetPartitionProperty patching not possible do the page crossing: %p (%#zx)\n",
+                g_pfnWinHvGetPartitionProperty, cbLeftInPage));
+        offJmpBack = 0;
+    }
+    if (offJmpBack >= cbMinJmpPatch)
+        LogRel(("NEMR0: offJmpBack=%#x for WinHvGetPartitionProperty (%p: %.48Rhxs)\n",
+                offJmpBack, g_pfnWinHvGetPartitionProperty, Org.ab));
+    else
+        offJmpBack = 0;
+    rc = VINF_SUCCESS;
+
+    /*
+     * Now enter serialization lock and get on with it...
+     */
+    PVMCPUCC const pVCpu0 = &pGVM->aCpus[0];
+    NTSTATUS       rcNt;
+    RTCritSectEnter(&g_VidSysCritSect);
+
+    /*
+     * First attempt, patching the import table entry.
+     */
+    g_idVidSysFoundPartition = HV_PARTITION_ID_INVALID;
+    g_hVidSysMatchThread     = RTThreadNativeSelf();
+    g_enmVidSysMatchProperty = pVCpu0->nem.s.uIoCtlBuf.GetProp.enmProperty = HvPartitionPropertyProcessorVendor;
+    pVCpu0->nem.s.uIoCtlBuf.GetProp.uValue = 0;
+
+    void *pvOld = NULL;
+    if (ASMAtomicCmpXchgExPtr(ppfnThunkAlias, (void *)(uintptr_t)nemR0VidSysWinHvGetPartitionProperty,
+                              (void *)(uintptr_t)g_pfnWinHvGetPartitionProperty, &pvOld))
+    {
+        LogRel(("NEMR0: after switch to %p: ppfnThunkAlias=%p *ppfnThunkAlias=%p; original: %p & %p\n",
+                nemR0VidSysWinHvGetPartitionProperty, ppfnThunkAlias, *ppfnThunkAlias,
+                g_ppfnVidSysWinHvGetPartitionProperty, *g_ppfnVidSysWinHvGetPartitionProperty));
+
+        rcNt = nemR0NtPerformIoControl(pGVM, pVCpu0, pGVM->nemr0.s.IoCtlGetPartitionProperty.uFunction,
+                                       &pVCpu0->nem.s.uIoCtlBuf.GetProp.enmProperty,
+                                       sizeof(pVCpu0->nem.s.uIoCtlBuf.GetProp.enmProperty),
+                                       &pVCpu0->nem.s.uIoCtlBuf.GetProp.uValue,
+                                       sizeof(pVCpu0->nem.s.uIoCtlBuf.GetProp.uValue));
+        ASMAtomicWritePtr(ppfnThunkAlias, (void *)(uintptr_t)g_pfnWinHvGetPartitionProperty);
+        HV_PARTITION_ID idHvPartition = g_idVidSysFoundPartition;
+
+        LogRel(("NEMR0: WinHvGetPartitionProperty trick #1 yielded: rcNt=%#x idHvPartition=%#RX64 uValue=%#RX64\n",
+                rcNt, idHvPartition, pVCpu0->nem.s.uIoCtlBuf.GetProp.uValue));
+        pGVM->nemr0.s.idHvPartition = idHvPartition;
+    }
+    else
+    {
+        LogRel(("NEMR0: Unexpected WinHvGetPartitionProperty pointer in VID.SYS: %p, expected %p\n",
+                pvOld, g_pfnWinHvGetPartitionProperty));
+        rc = VERR_NEM_INIT_FAILED;
+    }
+
+    /*
+     * If that didn't succeed, try patching the winhvr.sys code.
+     */
+    if (   pGVM->nemr0.s.idHvPartition == HV_PARTITION_ID_INVALID
+        && offJmpBack >= cbMinJmpPatch)
+    {
+        g_idVidSysFoundPartition = HV_PARTITION_ID_INVALID;
+        g_hVidSysMatchThread     = RTThreadNativeSelf();
+        g_enmVidSysMatchProperty = pVCpu0->nem.s.uIoCtlBuf.GetProp.enmProperty = HvPartitionPropertyProcessorVendor;
+        pVCpu0->nem.s.uIoCtlBuf.GetProp.uValue = 0;
+
+        /*
+         * Prepare the hook area.
+         */
+        uint8_t *pbDst = g_abNemR0WinHvrWinHvGetPartitionProperty_OriginalProlog;
+        memcpy(pbDst, (uint8_t const *)(uintptr_t)g_pfnWinHvGetPartitionProperty, offJmpBack);
+        pbDst += offJmpBack;
+
+        *pbDst++ = 0x48; /* mov rax, imm64 */
+        *pbDst++ = 0xb8;
+        *(uint64_t *)pbDst = (uintptr_t)g_pfnWinHvGetPartitionProperty + offJmpBack;
+        pbDst += sizeof(uint64_t);
+        *pbDst++ = 0xff; /* jmp rax */
+        *pbDst++ = 0xe0;
+        *pbDst++ = 0xcc; /* int3 */
+
+        /*
+         * Patch the original. We use cmpxchg16b here to avoid concurrency problems
+         * (this also makes sure we don't trample over someone else doing similar
+         * patching at the same time).
+         */
+        union
+        {
+            uint8_t  ab[16];
+            uint64_t au64[2];
+        } Patch;
+        memcpy(Patch.ab, Org.ab, sizeof(Patch));
+        pbDst = Patch.ab;
+        *pbDst++ = 0x48; /* mov rax, imm64 */
+        *pbDst++ = 0xb8;
+        *(uint64_t *)pbDst = (uintptr_t)nemR0WinHvrWinHvGetPartitionProperty;
+        pbDst += sizeof(uint64_t);
+        *pbDst++ = 0xff; /* jmp rax */
+        *pbDst++ = 0xe0;
+
+        int64_t ai64CmpCopy[2] = { Org.ai64[0], Org.ai64[1] }; /* paranoia  */
+        if (_InterlockedCompareExchange128((__int64 volatile *)pbTargetAlias, Patch.au64[1], Patch.au64[0], ai64CmpCopy) != 0)
+        {
+            rcNt = nemR0NtPerformIoControl(pGVM, pVCpu0, pGVM->nemr0.s.IoCtlGetPartitionProperty.uFunction,
+                                           &pVCpu0->nem.s.uIoCtlBuf.GetProp.enmProperty,
+                                           sizeof(pVCpu0->nem.s.uIoCtlBuf.GetProp.enmProperty),
+                                           &pVCpu0->nem.s.uIoCtlBuf.GetProp.uValue,
+                                           sizeof(pVCpu0->nem.s.uIoCtlBuf.GetProp.uValue));
+
+            for (uint32_t cFailures = 0; cFailures < 10; cFailures++)
+            {
+                ai64CmpCopy[0] = Patch.au64[0]; /* paranoia */
+                ai64CmpCopy[1] = Patch.au64[1];
+                if (_InterlockedCompareExchange128((__int64 volatile *)pbTargetAlias, Org.ai64[1], Org.ai64[0], ai64CmpCopy) != 0)
+                {
+                    if (cFailures > 0)
+                        LogRel(("NEMR0: Succeeded on try #%u.\n", cFailures));
+                    break;
+                }
+                LogRel(("NEMR0: Patch restore failure #%u: %.16Rhxs, expected %.16Rhxs\n",
+                        cFailures + 1, &ai64CmpCopy[0], &Patch.au64[0]));
+                RTThreadSleep(1000);
+            }
+
+            HV_PARTITION_ID idHvPartition = g_idVidSysFoundPartition;
+            LogRel(("NEMR0: WinHvGetPartitionProperty trick #2 yielded: rcNt=%#x idHvPartition=%#RX64 uValue=%#RX64\n",
+                    rcNt, idHvPartition, pVCpu0->nem.s.uIoCtlBuf.GetProp.uValue));
+            pGVM->nemr0.s.idHvPartition = idHvPartition;
+
+        }
+        else
+        {
+            LogRel(("NEMR0: Failed to install WinHvGetPartitionProperty patch: %.16Rhxs, expected %.16Rhxs\n",
+                    &ai64CmpCopy[0], &Org.ai64[0]));
+            rc = VERR_NEM_INIT_FAILED;
+        }
+    }
+
+    RTCritSectLeave(&g_VidSysCritSect);
+
+    return rc;
+}
+
+
+/**
  * 2nd part of the initialization, after we've got a partition handle.
  *
@@ -305,4 +819,10 @@
     pGVM->nemr0.s.IoCtlGetHvPartitionId = Copy;
 
+    Copy = pGVM->nem.s.IoCtlGetPartitionProperty;
+    AssertLogRelReturn(Copy.uFunction != 0, VERR_NEM_INIT_FAILED);
+    AssertLogRelReturn(Copy.cbInput == sizeof(VID_PARTITION_PROPERTY_CODE), VERR_NEM_INIT_FAILED);
+    AssertLogRelReturn(Copy.cbOutput == sizeof(HV_PARTITION_PROPERTY), VERR_NEM_INIT_FAILED);
+    pGVM->nemr0.s.IoCtlGetPartitionProperty = Copy;
+
     pGVM->nemr0.s.fMayUseRing0Runloop = pGVM->nem.s.fUseRing0Runloop;
 
@@ -356,9 +876,33 @@
         NTSTATUS rcNt = nemR0NtPerformIoControl(pGVM, pVCpu0, pGVM->nemr0.s.IoCtlGetHvPartitionId.uFunction, NULL, 0,
                                                 &pVCpu0->nem.s.uIoCtlBuf.idPartition, sizeof(pVCpu0->nem.s.uIoCtlBuf.idPartition));
+#if 0
         AssertLogRelMsgReturn(NT_SUCCESS(rcNt), ("IoCtlGetHvPartitionId failed: %#x\n", rcNt), VERR_NEM_INIT_FAILED);
         pGVM->nemr0.s.idHvPartition = pVCpu0->nem.s.uIoCtlBuf.idPartition;
+#else
+        /*
+         * Since 2021 (Win11) the above I/O control doesn't work on exo-partitions
+         * so we have to go to extremes to get at it.  Sigh.
+         */
+        if (   !NT_SUCCESS(rcNt)
+            || pVCpu0->nem.s.uIoCtlBuf.idPartition == HV_PARTITION_ID_INVALID)
+        {
+            LogRel(("IoCtlGetHvPartitionId failed: r0=%#RX64, r3=%#RX64, rcNt=%#x\n",
+                    pGVM->nemr0.s.idHvPartition, pGVM->nem.s.idHvPartition, rcNt));
+
+            RTR0MEMOBJ ahMemObjs[6]
+                = { NIL_RTR0MEMOBJ, NIL_RTR0MEMOBJ, NIL_RTR0MEMOBJ, NIL_RTR0MEMOBJ, NIL_RTR0MEMOBJ, NIL_RTR0MEMOBJ };
+            rc = nemR0InitVMPart2DontWannaDoTheseUglyPartitionIdFallbacks(pGVM, ahMemObjs);
+            size_t i = RT_ELEMENTS(ahMemObjs);
+            while (i-- > 0)
+                RTR0MemObjFree(ahMemObjs[i], false /*fFreeMappings*/);
+        }
+        if (pGVM->nem.s.idHvPartition == HV_PARTITION_ID_INVALID)
+            pGVM->nem.s.idHvPartition = pGVM->nemr0.s.idHvPartition;
+#endif
         AssertLogRelMsgReturn(pGVM->nemr0.s.idHvPartition == pGVM->nem.s.idHvPartition,
                               ("idHvPartition mismatch: r0=%#RX64, r3=%#RX64\n", pGVM->nemr0.s.idHvPartition, pGVM->nem.s.idHvPartition),
                               VERR_NEM_INIT_FAILED);
+        if (RT_SUCCESS(rc) && pGVM->nemr0.s.idHvPartition == HV_PARTITION_ID_INVALID)
+            rc = VERR_NEM_INIT_FAILED;
     }
 
Index: /trunk/src/VBox/VMM/VMMR0/NEMR0NativeA-win.asm
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/NEMR0NativeA-win.asm	(revision 91676)
+++ /trunk/src/VBox/VMM/VMMR0/NEMR0NativeA-win.asm	(revision 91676)
@@ -0,0 +1,159 @@
+ ; $Id$
+;; @file
+; NEM/win - Ring-0 Assembly Routines.
+;
+
+;
+; Copyright (C) 2021 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 RT_ASM_WITH_SEH64
+%include "iprt/asmdefs.mac"
+
+
+;*********************************************************************************************************************************
+;*  External Symbols                                                                                                             *
+;*********************************************************************************************************************************
+BEGINDATA
+extern  NAME(g_pfnWinHvGetPartitionProperty)
+extern  NAME(g_idVidSysFoundPartition)
+extern  NAME(g_hVidSysMatchThread)
+extern  NAME(g_enmVidSysMatchProperty)
+;extern  NAME(g_pfnHvrWinHvGetPartitionPropertyLeadIn)
+
+BEGINCODE
+extern  NAME(RTThreadNativeSelf)
+
+
+;;
+; This is a replacement for WinHvGetPartitionProperty that we slot into VID.SYS's
+; import table so we can fish out the partition ID (first parameter).
+;
+BEGINPROC nemR0VidSysWinHvGetPartitionProperty
+;
+; Code is shared with nemR0WinHvrWinHvGetPartitionProperty.
+;
+%macro WinHvGetPartitionPropertyHookBody 0
+        ;
+        ; Create a frame and save all volatile registers.
+        ;
+        push    xBP
+        SEH64_PUSH_xBP
+        mov     xBP, xSP
+        SEH64_SET_FRAME_xBP 0
+        sub     xSP, 0x100+0x20
+        SEH64_ALLOCATE_STACK 0x100+0x20
+        movdqa  [rbp - 10h], xmm0
+        movdqa  [rbp - 20h], xmm1
+        movdqa  [rbp - 30h], xmm2
+        movdqa  [rbp - 40h], xmm3
+        movdqa  [rbp - 50h], xmm4
+        movdqa  [rbp - 60h], xmm5
+        mov     [rbp - 70h], rcx
+        mov     [rbp - 78h], rdx
+        mov     [rbp - 80h], r8
+        mov     [rbp - 88h], r9
+        mov     [rbp - 90h], r10
+        mov     [rbp - 98h], r11
+SEH64_END_PROLOGUE
+
+        ;
+        ; Check if the property matches the one we're querying
+        ;
+        cmp     edx, [NAME(g_enmVidSysMatchProperty) wrt rip] ; It is really 32-bit.
+        jne     .return
+
+        ;
+        ; Get the current thread and compare that to g_hVidSysTargetThread,
+        ; making sure it's not NULL or NIL.
+        ;
+        call    NAME(RTThreadNativeSelf)
+        or      rax, rax                ; check that it isn't zero.
+        jz      .return
+
+        cmp     rax, [NAME(g_hVidSysMatchThread) wrt rip]
+        jne     .return
+
+        inc     rax                     ; check for ~0.
+        jz      .return
+
+        ;
+        ; Got a match, save the partition ID.
+        ;
+        mov     rcx, [rbp - 70h]
+        mov     [NAME(g_idVidSysFoundPartition) wrt rip], rcx
+
+        ;
+        ; Restore the volatile registers.
+        ;
+.return:
+        movdqa  xmm0, [rbp - 10h]
+        movdqa  xmm1, [rbp - 20h]
+        movdqa  xmm2, [rbp - 30h]
+        movdqa  xmm3, [rbp - 40h]
+        movdqa  xmm4, [rbp - 50h]
+        movdqa  xmm5, [rbp - 60h]
+        mov     rcx,  [rbp - 70h]
+        mov     rdx,  [rbp - 78h]
+        mov     r8,   [rbp - 80h]
+        mov     r9,   [rbp - 88h]
+        mov     r10,  [rbp - 90h]
+        mov     r11,  [rbp - 98h]
+        leave
+%endmacro
+
+        ;
+        ; Identical to nemR0WinHvrWinHvGetPartitionProperty except for the
+        ; resuming of the real code.
+        ;
+        WinHvGetPartitionPropertyHookBody
+
+        ;
+        ; Instead of returning, jump to the real WinHvGetPartitionProperty code.
+        ;
+        mov     rax, [NAME(g_pfnWinHvGetPartitionProperty) wrt rip]
+        jmp     rax
+ENDPROC   nemR0VidSysWinHvGetPartitionProperty
+
+
+section .textrwx code page execute read write
+
+;;
+; This is where we jump to from a WinHvr.sys jmp patch.
+;
+; This is used if the import table patching doesn't work because the indirect
+; call was converted into a direct call by the the retpoline patching code.
+;
+ALIGNCODE(16)
+BEGINPROC nemR0WinHvrWinHvGetPartitionProperty
+
+        ;
+        ; Identical to nemR0VidSysWinHvGetPartitionProperty except for the
+        ; resuming of the real code.
+        ;
+        WinHvGetPartitionPropertyHookBody
+
+        ;
+        ; Now back to the orignal code.
+        ;
+        ; We reserve 64 bytes of space for the lead-in code that we replaced
+        ; from WinHvGetPartitionProperty and additional 12 for the jump back.
+        ;
+GLOBALNAME g_abNemR0WinHvrWinHvGetPartitionProperty_OriginalProlog
+        times 64 int3                   ; IMPORTANT! Must be at least as big as the 'Org' variable in the C code.
+        mov     rax, 01234567890abcdefh
+        jmp     rax
+ENDPROC   nemR0WinHvrWinHvGetPartitionProperty
+
Index: /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp	(revision 91675)
+++ /trunk/src/VBox/VMM/VMMR0/VMMR0.cpp	(revision 91676)
@@ -184,5 +184,8 @@
 #endif
                                     {
+#ifdef VBOX_WITH_NEM_R0
+                                        rc = NEMR0Init();
                                         if (RT_SUCCESS(rc))
+#endif
                                         {
                                             LogFlow(("ModuleInit: returns success\n"));
@@ -267,4 +270,7 @@
 #ifdef VBOX_WITH_TRIPLE_FAULT_HACK
     vmmR0TripleFaultHackTerm();
+#endif
+#ifdef VBOX_WITH_NEM_R0
+    NEMR0Term();
 #endif
 
Index: /trunk/src/VBox/VMM/VMMR3/NEMR3Native-win.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/NEMR3Native-win.cpp	(revision 91675)
+++ /trunk/src/VBox/VMM/VMMR3/NEMR3Native-win.cpp	(revision 91676)
@@ -69,11 +69,15 @@
 
 /** VID I/O control detection: Fake partition handle input. */
-#define NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE          ((HANDLE)(uintptr_t)38479125)
+#define NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE                      ((HANDLE)(uintptr_t)38479125)
 /** VID I/O control detection: Fake partition ID return. */
-#define NEM_WIN_IOCTL_DETECTOR_FAKE_PARTITION_ID    UINT64_C(0xfa1e000042424242)
+#define NEM_WIN_IOCTL_DETECTOR_FAKE_PARTITION_ID                UINT64_C(0xfa1e000042424242)
+/** VID I/O control detection: The property we get via VidGetPartitionProperty. */
+#define NEM_WIN_IOCTL_DETECTOR_FAKE_PARTITION_PROPERTY_CODE     HvPartitionPropertyProcessorVendor
+/** VID I/O control detection: Fake property value return. */
+#define NEM_WIN_IOCTL_DETECTOR_FAKE_PARTITION_PROPERTY_VALUE    UINT64_C(0xf00dface01020304)
 /** VID I/O control detection: Fake CPU index input. */
-#define NEM_WIN_IOCTL_DETECTOR_FAKE_VP_INDEX        UINT32_C(42)
+#define NEM_WIN_IOCTL_DETECTOR_FAKE_VP_INDEX                    UINT32_C(42)
 /** VID I/O control detection: Fake timeout input. */
-#define NEM_WIN_IOCTL_DETECTOR_FAKE_TIMEOUT         UINT32_C(0x00080286)
+#define NEM_WIN_IOCTL_DETECTOR_FAKE_TIMEOUT                     UINT32_C(0x00080286)
 
 
@@ -105,4 +109,5 @@
  * @{ */
 static decltype(VidGetHvPartitionId)               *g_pfnVidGetHvPartitionId;
+static decltype(VidGetPartitionProperty)           *g_pfnVidGetPartitionProperty;
 static decltype(VidStartVirtualProcessor)          *g_pfnVidStartVirtualProcessor;
 static decltype(VidStopVirtualProcessor)           *g_pfnVidStopVirtualProcessor;
@@ -151,4 +156,5 @@
 #endif
     NEM_WIN_IMPORT(1, false, VidGetHvPartitionId),
+    NEM_WIN_IMPORT(1, false, VidGetPartitionProperty),
     NEM_WIN_IMPORT(1, false, VidMessageSlotMap),
     NEM_WIN_IMPORT(1, false, VidMessageSlotHandleAndGetNext),
@@ -170,4 +176,6 @@
 /** Info about the VidGetHvPartitionId I/O control interface. */
 static NEMWINIOCTL g_IoCtlGetHvPartitionId;
+/** Info about the VidGetPartitionProperty I/O control interface. */
+static NEMWINIOCTL g_IoCtlGetPartitionProperty;
 /** Info about the VidStartVirtualProcessor I/O control interface. */
 static NEMWINIOCTL g_IoCtlStartVirtualProcessor;
@@ -374,32 +382,48 @@
          * Walk the thunks table(s) looking for NtDeviceIoControlFile.
          */
-        PIMAGE_THUNK_DATA pFirstThunk = (PIMAGE_THUNK_DATA)&pbImage[pImps->FirstThunk]; /* update this. */
-        PIMAGE_THUNK_DATA pThunk      = pImps->OriginalFirstThunk == 0                  /* read from this. */
-                                      ? (PIMAGE_THUNK_DATA)&pbImage[pImps->FirstThunk]
-                                      : (PIMAGE_THUNK_DATA)&pbImage[pImps->OriginalFirstThunk];
-        while (pThunk->u1.Ordinal != 0)
+        uintptr_t *puFirstThunk = (uintptr_t *)&pbImage[pImps->FirstThunk]; /* update this. */
+        if (   pImps->OriginalFirstThunk != 0
+            && pImps->OriginalFirstThunk != pImps->FirstThunk)
         {
-            if (!(pThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32))
+            uintptr_t const *puOrgThunk = (uintptr_t const *)&pbImage[pImps->OriginalFirstThunk]; /* read from this. */
+            uintptr_t        cLeft      = (cbImage - (RT_MAX(pImps->FirstThunk, pImps->OriginalFirstThunk)))
+                                        / sizeof(*puFirstThunk);
+            while (cLeft-- > 0 && *puOrgThunk != 0)
             {
-                AssertReturn(pThunk->u1.Ordinal > 0 && pThunk->u1.Ordinal < cbImage,
-                             RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "VID.DLL bad FirstThunk: %#x", pImps->FirstThunk));
-
-                const char *pszSymbol = (const char *)&pbImage[(uintptr_t)pThunk->u1.AddressOfData + 2];
-                if (strcmp(pszSymbol, "NtDeviceIoControlFile") == 0)
+                if (!(*puOrgThunk & IMAGE_ORDINAL_FLAG64)) /* ASSUMES 64-bit */
                 {
-                    DWORD fOldProt = PAGE_READONLY;
-                    VirtualProtect(&pFirstThunk->u1.Function, sizeof(uintptr_t), PAGE_EXECUTE_READWRITE, &fOldProt);
-                    g_ppfnVidNtDeviceIoControlFile = (decltype(NtDeviceIoControlFile) **)&pFirstThunk->u1.Function;
-                    /* Don't restore the protection here, so we modify the NtDeviceIoControlFile pointer later. */
+                    AssertReturn(*puOrgThunk > 0 && *puOrgThunk < cbImage,
+                                 RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED, "VID.DLL bad thunk entry: %#x", *puOrgThunk));
+
+                    const char *pszSymbol = (const char *)&pbImage[*puOrgThunk + 2];
+                    if (strcmp(pszSymbol, "NtDeviceIoControlFile") == 0)
+                        g_ppfnVidNtDeviceIoControlFile = (decltype(NtDeviceIoControlFile) **)puFirstThunk;
                 }
+
+                puOrgThunk++;
+                puFirstThunk++;
             }
-
-            pThunk++;
-            pFirstThunk++;
+        }
+        else
+        {
+            /* No original thunk table, so scan the resolved symbols for a match
+               with the NtDeviceIoControlFile address. */
+            uintptr_t const uNeedle = (uintptr_t)g_pfnNtDeviceIoControlFile;
+            uintptr_t       cLeft   = (cbImage - pImps->FirstThunk) / sizeof(*puFirstThunk);
+            while (cLeft-- > 0 && *puFirstThunk != 0)
+            {
+                if (*puFirstThunk == uNeedle)
+                    g_ppfnVidNtDeviceIoControlFile = (decltype(NtDeviceIoControlFile) **)puFirstThunk;
+                puFirstThunk++;
+            }
         }
     }
 
-    if (*g_ppfnVidNtDeviceIoControlFile)
-    {
+    if (g_ppfnVidNtDeviceIoControlFile != NULL)
+    {
+        /* Make the thunk writable we can freely modify it. */
+        DWORD fOldProt = PAGE_READONLY;
+        VirtualProtect((void *)(uintptr_t)g_ppfnVidNtDeviceIoControlFile, sizeof(uintptr_t), PAGE_EXECUTE_READWRITE, &fOldProt);
+
 #ifdef NEM_WIN_INTERCEPT_NT_IO_CTLS
         *g_ppfnVidNtDeviceIoControlFile = nemR3WinLogWrapper_NtDeviceIoControlFile;
@@ -793,4 +817,32 @@
 
 /**
+ * Used to fill in g_IoCtlGetHvPartitionId.
+ */
+static NTSTATUS WINAPI
+nemR3WinIoctlDetector_GetPartitionProperty(HANDLE hFile, HANDLE hEvt, PIO_APC_ROUTINE pfnApcCallback, PVOID pvApcCtx,
+                                           PIO_STATUS_BLOCK pIos, ULONG uFunction, PVOID pvInput, ULONG cbInput,
+                                           PVOID pvOutput, ULONG cbOutput)
+{
+    AssertLogRelMsgReturn(hFile == NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE, ("hFile=%p\n", hFile), STATUS_INVALID_PARAMETER_1);
+    RT_NOREF(hEvt); RT_NOREF(pfnApcCallback); RT_NOREF(pvApcCtx);
+    AssertLogRelMsgReturn(RT_VALID_PTR(pIos), ("pIos=%p\n", pIos), STATUS_INVALID_PARAMETER_5);
+    AssertLogRelMsgReturn(cbInput == sizeof(VID_PARTITION_PROPERTY_CODE), ("cbInput=%#x\n", cbInput), STATUS_INVALID_PARAMETER_8);
+    AssertLogRelMsgReturn(RT_VALID_PTR(pvInput), ("pvInput=%p\n", pvInput), STATUS_INVALID_PARAMETER_9);
+    AssertLogRelMsgReturn(*(VID_PARTITION_PROPERTY_CODE *)pvInput == NEM_WIN_IOCTL_DETECTOR_FAKE_PARTITION_PROPERTY_CODE,
+                          ("*pvInput=%#x, expected %#x\n", *(HV_PARTITION_PROPERTY_CODE *)pvInput,
+                           NEM_WIN_IOCTL_DETECTOR_FAKE_PARTITION_PROPERTY_CODE), STATUS_INVALID_PARAMETER_9);
+    AssertLogRelMsgReturn(RT_VALID_PTR(pvOutput), ("pvOutput=%p\n", pvOutput), STATUS_INVALID_PARAMETER_9);
+    AssertLogRelMsgReturn(cbOutput == sizeof(HV_PARTITION_PROPERTY), ("cbInput=%#x\n", cbInput), STATUS_INVALID_PARAMETER_10);
+    *(HV_PARTITION_PROPERTY *)pvOutput = NEM_WIN_IOCTL_DETECTOR_FAKE_PARTITION_PROPERTY_VALUE;
+
+    g_IoCtlGetPartitionProperty.cbInput   = cbInput;
+    g_IoCtlGetPartitionProperty.cbOutput  = cbOutput;
+    g_IoCtlGetPartitionProperty.uFunction = uFunction;
+
+    return STATUS_SUCCESS;
+}
+
+
+/**
  * Used to fill in g_IoCtlStartVirtualProcessor.
  */
@@ -936,7 +988,7 @@
     decltype(NtDeviceIoControlFile) * const pfnOrg = *g_ppfnVidNtDeviceIoControlFile;
 
-    /* VidGetHvPartitionId - must work due to memory. */
+    /* VidGetHvPartitionId - must work due to our memory management. */
+    HV_PARTITION_ID idHvPartition = HV_PARTITION_ID_INVALID;
     *g_ppfnVidNtDeviceIoControlFile = nemR3WinIoctlDetector_GetHvPartitionId;
-    HV_PARTITION_ID idHvPartition = HV_PARTITION_ID_INVALID;
     BOOL fRet = g_pfnVidGetHvPartitionId(NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE, &idHvPartition);
     *g_ppfnVidNtDeviceIoControlFile = pfnOrg;
@@ -947,4 +999,19 @@
     LogRel(("NEM: VidGetHvPartitionId            -> fun:%#x in:%#x out:%#x\n",
             g_IoCtlGetHvPartitionId.uFunction, g_IoCtlGetHvPartitionId.cbInput, g_IoCtlGetHvPartitionId.cbOutput));
+
+    /* VidGetPartitionProperty - must work as it's fallback for VidGetHvPartitionId. */
+    HV_PARTITION_PROPERTY uPropValue = ~NEM_WIN_IOCTL_DETECTOR_FAKE_PARTITION_PROPERTY_VALUE;
+    *g_ppfnVidNtDeviceIoControlFile = nemR3WinIoctlDetector_GetPartitionProperty;
+    fRet = g_pfnVidGetPartitionProperty(NEM_WIN_IOCTL_DETECTOR_FAKE_HANDLE, NEM_WIN_IOCTL_DETECTOR_FAKE_PARTITION_PROPERTY_CODE,
+                                        &uPropValue);
+    *g_ppfnVidNtDeviceIoControlFile = pfnOrg;
+    AssertReturn(   fRet
+                 && uPropValue == NEM_WIN_IOCTL_DETECTOR_FAKE_PARTITION_PROPERTY_VALUE
+                 && g_IoCtlGetHvPartitionId.uFunction != 0,
+                 RTErrInfoSetF(pErrInfo, VERR_NEM_INIT_FAILED,
+                               "Problem figuring out VidGetPartitionProperty: fRet=%u uPropValue=%#x dwErr=%u",
+                               fRet, uPropValue, GetLastError()) );
+    LogRel(("NEM: VidGetPartitionProperty        -> fun:%#x in:%#x out:%#x\n",
+            g_IoCtlGetPartitionProperty.uFunction, g_IoCtlGetPartitionProperty.cbInput, g_IoCtlGetPartitionProperty.cbOutput));
 
     int rcRet = VINF_SUCCESS;
@@ -1030,4 +1097,5 @@
     /* Done. */
     pVM->nem.s.IoCtlGetHvPartitionId            = g_IoCtlGetHvPartitionId;
+    pVM->nem.s.IoCtlGetPartitionProperty        = g_IoCtlGetPartitionProperty;
     pVM->nem.s.IoCtlStartVirtualProcessor       = g_IoCtlStartVirtualProcessor;
     pVM->nem.s.IoCtlStopVirtualProcessor        = g_IoCtlStopVirtualProcessor;
@@ -1387,5 +1455,5 @@
 
     /*
-     * Set up the partition and create EMTs.
+     * Set up the partition.
      *
      * Seems like this is where the partition is actually instantiated and we get
@@ -1398,5 +1466,5 @@
                           hrc, RTNtLastStatusValue(), RTNtLastErrorValue());
 
-    /* Get the handle. */
+    /* Get the handle (could also fish this out via VID.DLL NtDeviceIoControlFile intercepting). */
     HANDLE hPartitionDevice;
     __try
@@ -1414,9 +1482,31 @@
                           "Failed to get device handle for partition %p: %Rhrc", hPartition, hrc);
 
-    HV_PARTITION_ID idHvPartition = HV_PARTITION_ID_INVALID;
-    if (!g_pfnVidGetHvPartitionId(hPartitionDevice, &idHvPartition))
+    /* Test the handle. */
+    HV_PARTITION_PROPERTY uValue;
+    if (!g_pfnVidGetPartitionProperty(hPartitionDevice, HvPartitionPropertyProcessorVendor, &uValue))
         return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
                           "Failed to get device handle and/or partition ID for %p (hPartitionDevice=%p, Last=%#x/%u)",
                           hPartition, hPartitionDevice, RTNtLastStatusValue(), RTNtLastErrorValue());
+    LogRel(("NEM: HvPartitionPropertyProcessorVendor=%#llx (%lld)\n", uValue, uValue));
+
+    /*
+     * Get the partition ID so we can keep managing our memory the way we've
+     * been doing for the last 12+ years.
+     *
+     * The WHvMapGpaRange/WHvUnmapGpaRange interface is very ill-fitting and
+     * very inflexible compared to what we need.  Fortunately, the hypervisor
+     * have a much better interface which we are able to use from ring-0.
+     * Not pretty, but necessary for the time being.
+     */
+    HV_PARTITION_ID idHvPartition = HV_PARTITION_ID_INVALID;
+    if (!g_pfnVidGetHvPartitionId(hPartitionDevice, &idHvPartition))
+    {
+        if (RTNtLastErrorValue() != ERROR_INVALID_FUNCTION) /* Will try get it later in VMMR0_DO_NEM_INIT_VM_PART_2. */
+            return VMSetError(pVM, VERR_NEM_VM_CREATE_FAILED, RT_SRC_POS,
+                              "Failed to get device handle and/or partition ID for %p (hPartitionDevice=%p, Last=%#x/%u)",
+                              hPartition, hPartitionDevice, RTNtLastStatusValue(), RTNtLastErrorValue());
+        LogRel(("NEM: VidGetHvPartitionId failed with ERROR_NOT_SUPPORTED, will try again later from ring-0...\n"));
+        idHvPartition = HV_PARTITION_ID_INVALID;
+    }
     pVM->nem.s.hPartitionDevice = hPartitionDevice;
     pVM->nem.s.idHvPartition    = idHvPartition;
@@ -1484,5 +1574,6 @@
     if (RT_SUCCESS(rc))
     {
-        LogRel(("NEM: Successfully set up partition (device handle %p, partition ID %#llx)\n", hPartitionDevice, idHvPartition));
+        LogRel(("NEM: Successfully set up partition (device handle %p, partition ID %#llx)\n",
+                hPartitionDevice, pVM->nem.s.idHvPartition));
 
 #if 1
Index: /trunk/src/VBox/VMM/include/NEMInternal.h
===================================================================
--- /trunk/src/VBox/VMM/include/NEMInternal.h	(revision 91675)
+++ /trunk/src/VBox/VMM/include/NEMInternal.h	(revision 91676)
@@ -195,4 +195,6 @@
     /** Info about the VidGetHvPartitionId I/O control interface. */
     NEMWINIOCTL                 IoCtlGetHvPartitionId;
+    /** Info about the VidGetPartitionProperty I/O control interface. */
+    NEMWINIOCTL                 IoCtlGetPartitionProperty;
     /** Info about the VidStartVirtualProcessor I/O control interface. */
     NEMWINIOCTL                 IoCtlStartVirtualProcessor;
@@ -290,4 +292,9 @@
         HV_PARTITION_ID         idPartition;
         HV_VP_INDEX             idCpu;
+        struct
+        {
+            uint64_t            enmProperty;
+            uint64_t            uValue;
+        } GetProp;
 # ifdef VID_MSHAGN_F_GET_NEXT_MESSAGE
         VID_IOCTL_INPUT_MESSAGE_SLOT_HANDLE_AND_GET_NEXT MsgSlotHandleAndGetNext;
@@ -384,4 +391,6 @@
     /** Info about the VidGetHvPartitionId I/O control interface. */
     NEMWINIOCTL                 IoCtlGetHvPartitionId;
+    /** Info about the VidGetPartitionProperty I/O control interface. */
+    NEMWINIOCTL                 IoCtlGetPartitionProperty;
     /** Info about the VidStartVirtualProcessor I/O control interface. */
     NEMWINIOCTL                 IoCtlStartVirtualProcessor;
