Index: /trunk/include/VBox/vmm/cpum.h
===================================================================
--- /trunk/include/VBox/vmm/cpum.h	(revision 54762)
+++ /trunk/include/VBox/vmm/cpum.h	(revision 54763)
@@ -1280,11 +1280,7 @@
 
 # if defined(VBOX_WITH_RAW_MODE) || defined(DOXYGEN_RUNNING)
-/** @name APIs for the CPUID raw-mode patch.
+/** @name APIs for the CPUID raw-mode patch (legacy).
  * @{ */
 VMMR3_INT_DECL(RCPTRTYPE(PCCPUMCPUID))     CPUMR3GetGuestCpuIdPatmDefRCPtr(PVM pVM);
-VMMR3_INT_DECL(RCPTRTYPE(PCCPUMCPUIDLEAF)) CPUMR3GetGuestCpuIdPatmArrayRCPtr(PVM pVM);
-VMMR3_INT_DECL(RCPTRTYPE(PCCPUMCPUIDLEAF)) CPUMR3GetGuestCpuIdPatmArrayEndRCPtr(PVM pVM);
-VMMR3_INT_DECL(CPUMUNKNOWNCPUID)            CPUMR3GetGuestCpuIdPatmUnknownLeafMethod(PVM pVM);
-/* Legacy: */
 VMMR3_INT_DECL(uint32_t)                   CPUMR3GetGuestCpuIdPatmStdMax(PVM pVM);
 VMMR3_INT_DECL(uint32_t)                   CPUMR3GetGuestCpuIdPatmExtMax(PVM pVM);
Index: /trunk/include/VBox/vmm/patm.h
===================================================================
--- /trunk/include/VBox/vmm/patm.h	(revision 54762)
+++ /trunk/include/VBox/vmm/patm.h	(revision 54763)
@@ -141,5 +141,6 @@
 #define PATMIsEnabled(a_pVM)    ((a_pVM)->fPATMEnabled)
 
-VMMDECL(bool)           PATMIsPatchGCAddr(PVM pVM, RTRCUINTPTR pAddr);
+VMMDECL(bool)           PATMIsPatchGCAddr(PVM pVM, RTRCUINTPTR uGCAddr);
+VMMDECL(bool)           PATMIsPatchGCAddrExclHelpers(PVM pVM, RTRCUINTPTR uGCAddr);
 VMM_INT_DECL(int)       PATMReadPatchCode(PVM pVM, RTGCPTR GCPtrPatchCode, void *pvDst, size_t cbToRead, size_t *pcbRead);
 
@@ -182,13 +183,10 @@
 VMMR3_INT_DECL(int)             PATMR3Init(PVM pVM);
 VMMR3_INT_DECL(int)             PATMR3InitFinalize(PVM pVM);
-VMMR3_INT_DECL(void)            PATMR3Relocate(PVM pVM);
+VMMR3_INT_DECL(void)            PATMR3Relocate(PVM pVM, RTRCINTPTR offDelta);
 VMMR3_INT_DECL(int)             PATMR3Term(PVM pVM);
 VMMR3_INT_DECL(int)             PATMR3Reset(PVM pVM);
 
-VMMR3_INT_DECL(void *)          PATMR3QueryPatchMemHC(PVM pVM, uint32_t *pcb);
-VMMR3_INT_DECL(RTRCPTR)         PATMR3QueryPatchMemGC(PVM pVM, uint32_t *pcb);
 VMMR3_INT_DECL(bool)            PATMR3IsInsidePatchJump(PVM pVM, RTRCPTR pAddr, PRTGCPTR32 pPatchAddr);
 VMMR3_INT_DECL(RTRCPTR)         PATMR3QueryPatchGCPtr(PVM pVM, RTRCPTR pAddrGC);
-VMMR3_INT_DECL(bool)            PATMR3IsPatchHCAddr(PVM pVM, void *pAddrHC);
 VMMR3_INT_DECL(void *)          PATMR3GCPtrToHCPtr(PVM pVM, RTRCPTR pAddrGC);
 VMMR3_INT_DECL(PPATMGCSTATE)    PATMR3QueryGCStateHC(PVM pVM);
Index: /trunk/include/iprt/asmdefs.mac
===================================================================
--- /trunk/include/iprt/asmdefs.mac	(revision 54762)
+++ /trunk/include/iprt/asmdefs.mac	(revision 54763)
@@ -199,4 +199,16 @@
 
 ;;
+; Gets the pointer to an imported object.
+%ifdef ASM_FORMAT_PE
+ %ifdef RT_ARCH_AMD64
+  %define IMP_SEG(SegOverride, name)  qword [SegOverride:IMPNAME(name) wrt rip]
+ %else
+  %define IMP_SEG(SegOverride, name)  dword [SegOverride:IMPNAME(name)]
+ %endif
+%else
+ %define IMP_SEG(SegOverride, name)   IMPNAME(name)
+%endif
+
+;;
 ; Declares an imported object for use with IMP2.
 ; @note May change the current section!
Index: /trunk/src/VBox/VMM/Makefile.kmk
===================================================================
--- /trunk/src/VBox/VMM/Makefile.kmk	(revision 54762)
+++ /trunk/src/VBox/VMM/Makefile.kmk	(revision 54763)
@@ -439,4 +439,5 @@
  	VMMRC/CPUMRC.cpp \
  	VMMRC/CPUMRCA.asm \
+ 	VMMRC/CPUMRCPatchHlp.asm \
  	VMMRC/EMRCA.asm \
  	VMMRC/IOMRC.cpp \
Index: /trunk/src/VBox/VMM/VMMAll/PATMAll.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMAll/PATMAll.cpp	(revision 54762)
+++ /trunk/src/VBox/VMM/VMMAll/PATMAll.cpp	(revision 54763)
@@ -256,6 +256,7 @@
 VMM_INT_DECL(bool) PATMShouldUseRawMode(PVM pVM, RTRCPTR pAddrGC)
 {
-    return (    PATMIsEnabled(pVM)
-            && ((pAddrGC >= (RTRCPTR)pVM->patm.s.pPatchMemGC && pAddrGC < (RTRCPTR)((RTRCUINTPTR)pVM->patm.s.pPatchMemGC + pVM->patm.s.cbPatchMem)))) ? true : false;
+    return   PATMIsEnabled(pVM)
+          && (   (RTRCUINTPTR)pAddrGC - (RTRCUINTPTR)pVM->patm.s.pPatchMemGC      < pVM->patm.s.cbPatchMem
+              || (RTRCUINTPTR)pAddrGC - (RTRCUINTPTR)pVM->patm.s.pbPatchHelpersRC < pVM->patm.s.cbPatchHelpers);
 }
 
@@ -273,14 +274,30 @@
 
 /**
- * Checks whether the GC address is part of our patch region
+ * Checks whether the GC address is part of our patch or helper regions.
  *
  * @returns VBox status code.
  * @param   pVM         Pointer to the VM.
- * @param   pAddrGC     Guest context address
+ * @param   uGCAddr     Guest context address.
  * @internal
  */
-VMMDECL(bool) PATMIsPatchGCAddr(PVM pVM, RTRCUINTPTR pAddrGC)
-{
-    return (PATMIsEnabled(pVM) && pAddrGC - (RTRCUINTPTR)pVM->patm.s.pPatchMemGC < pVM->patm.s.cbPatchMem) ? true : false;
+VMMDECL(bool) PATMIsPatchGCAddr(PVM pVM, RTRCUINTPTR uGCAddr)
+{
+    return PATMIsEnabled(pVM)
+        && (   uGCAddr - (RTRCUINTPTR)pVM->patm.s.pPatchMemGC      < pVM->patm.s.cbPatchMem
+            || uGCAddr - (RTRCUINTPTR)pVM->patm.s.pbPatchHelpersRC < pVM->patm.s.cbPatchHelpers);
+}
+
+/**
+ * Checks whether the GC address is part of our patch region.
+ *
+ * @returns VBox status code.
+ * @param   pVM         Pointer to the VM.
+ * @param   uGCAddr     Guest context address.
+ * @internal
+ */
+VMMDECL(bool) PATMIsPatchGCAddrExclHelpers(PVM pVM, RTRCUINTPTR uGCAddr)
+{
+    return PATMIsEnabled(pVM)
+        && uGCAddr - (RTRCUINTPTR)pVM->patm.s.pPatchMemGC < pVM->patm.s.cbPatchMem;
 }
 
@@ -288,5 +305,4 @@
  * Reads patch code.
  *
- * @returns
  * @retval  VINF_SUCCESS on success.
  * @retval  VERR_PATCH_NOT_FOUND if the request is entirely outside the patch
@@ -307,17 +323,42 @@
     Assert(!HMIsEnabled(pVM));
 
-    RTGCPTR offPatchedInstr = GCPtrPatchCode - (RTGCPTR32)pVM->patm.s.pPatchMemGC;
-    if (offPatchedInstr >= pVM->patm.s.cbPatchMem)
-        return VERR_PATCH_NOT_FOUND;
-
-    uint32_t cbMaxRead = pVM->patm.s.cbPatchMem - (uint32_t)offPatchedInstr;
-    if (cbToRead > cbMaxRead)
-        cbToRead = cbMaxRead;
-
+    /*
+     * Check patch code and patch helper code.  We assume the requested bytes
+     * are not in either.
+     */
+    RTGCPTR offPatchCode = GCPtrPatchCode - (RTGCPTR32)pVM->patm.s.pPatchMemGC;
+    if (offPatchCode >= pVM->patm.s.cbPatchMem)
+    {
+        offPatchCode = GCPtrPatchCode - (RTGCPTR32)pVM->patm.s.pbPatchHelpersRC;
+        if (offPatchCode >= pVM->patm.s.cbPatchHelpers)
+            return VERR_PATCH_NOT_FOUND;
+
+        /*
+         * Patch helper memory.
+         */
+        uint32_t cbMaxRead = pVM->patm.s.cbPatchHelpers - (uint32_t)offPatchCode;
+        if (cbToRead > cbMaxRead)
+            cbToRead = cbMaxRead;
 #ifdef IN_RC
-    memcpy(pvDst, pVM->patm.s.pPatchMemGC + (uint32_t)offPatchedInstr, cbToRead);
+        memcpy(pvDst, pVM->patm.s.pbPatchHelpersRC + (uint32_t)offPatchCode, cbToRead);
 #else
-    memcpy(pvDst, pVM->patm.s.pPatchMemHC + (uint32_t)offPatchedInstr, cbToRead);
+        memcpy(pvDst, pVM->patm.s.pbPatchHelpersR3 + (uint32_t)offPatchCode, cbToRead);
 #endif
+    }
+    else
+    {
+        /*
+         * Patch memory.
+         */
+        uint32_t cbMaxRead = pVM->patm.s.cbPatchMem - (uint32_t)offPatchCode;
+        if (cbToRead > cbMaxRead)
+            cbToRead = cbMaxRead;
+#ifdef IN_RC
+        memcpy(pvDst, pVM->patm.s.pPatchMemGC + (uint32_t)offPatchCode, cbToRead);
+#else
+        memcpy(pvDst, pVM->patm.s.pPatchMemHC + (uint32_t)offPatchCode, cbToRead);
+#endif
+    }
+
     if (pcbRead)
         *pcbRead = cbToRead;
Index: /trunk/src/VBox/VMM/VMMR3/CPUMR3CpuId.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/CPUMR3CpuId.cpp	(revision 54762)
+++ /trunk/src/VBox/VMM/VMMR3/CPUMR3CpuId.cpp	(revision 54763)
@@ -5057,47 +5057,4 @@
 
 /**
- * Gets a pointer to the CPUID leaf array.
- *
- * @returns Raw-mode pointer to the CPUID leaf array.
- * @param   pVM         Pointer to the VM.
- * @remark  Intended for PATM only.
- */
-VMMR3_INT_DECL(RCPTRTYPE(PCCPUMCPUIDLEAF)) CPUMR3GetGuestCpuIdPatmArrayRCPtr(PVM pVM)
-{
-    Assert(MMHyperRCToR3(pVM, pVM->cpum.s.GuestInfo.paCpuIdLeavesRC) == pVM->cpum.s.GuestInfo.paCpuIdLeavesR3);
-    return pVM->cpum.s.GuestInfo.paCpuIdLeavesRC;
-}
-
-
-/**
- * Gets a pointer to the CPUID leaf array.
- *
- * @returns Raw-mode pointer to the end of CPUID leaf array (exclusive).
- * @param   pVM         Pointer to the VM.
- * @remark  Intended for PATM only.
- */
-VMMR3_INT_DECL(RCPTRTYPE(PCCPUMCPUIDLEAF)) CPUMR3GetGuestCpuIdPatmArrayEndRCPtr(PVM pVM)
-{
-    Assert(MMHyperRCToR3(pVM, pVM->cpum.s.GuestInfo.paCpuIdLeavesRC) == pVM->cpum.s.GuestInfo.paCpuIdLeavesR3);
-    return pVM->cpum.s.GuestInfo.paCpuIdLeavesRC
-         + pVM->cpum.s.GuestInfo.cCpuIdLeaves * sizeof(CPUMCPUIDLEAF);
-}
-
-
-/**
- * Gets the unknown CPUID leaf method.
- *
- * @returns Unknown CPUID leaf method.
- * @param   pVM         Pointer to the VM.
- * @remark  Intended for PATM only.
- */
-VMMR3_INT_DECL(CPUMUNKNOWNCPUID) CPUMR3GetGuestCpuIdPatmUnknownLeafMethod(PVM pVM)
-{
-    return pVM->cpum.s.GuestInfo.enmUnknownCpuIdMethod;
-}
-
-
-
-/**
  * Gets a number of standard CPUID leaves (PATM only).
  *
Index: /trunk/src/VBox/VMM/VMMR3/PATM.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/PATM.cpp	(revision 54762)
+++ /trunk/src/VBox/VMM/VMMR3/PATM.cpp	(revision 54763)
@@ -24,4 +24,5 @@
 #include <VBox/vmm/patm.h>
 #include <VBox/vmm/stam.h>
+#include <VBox/vmm/pdmapi.h>
 #include <VBox/vmm/pgm.h>
 #include <VBox/vmm/cpum.h>
@@ -109,5 +110,5 @@
 
 static int               patmReinit(PVM pVM);
-static DECLCALLBACK(int) RelocatePatches(PAVLOU32NODECORE pNode, void *pParam);
+static DECLCALLBACK(int) patmR3RelocatePatches(PAVLOU32NODECORE pNode, void *pParam);
 static RTRCPTR      patmR3GuestGCPtrToPatchGCPtrSimple(PVM pVM, RCPTRTYPE(uint8_t*) pInstrGC);
 static int          patmR3MarkDirtyPatch(PVM pVM, PPATCHINFO pPatch);
@@ -331,17 +332,39 @@
         return VINF_SUCCESS;
 
-    /* The GC state, stack and statistics must be read/write for the guest (supervisor only of course). */
+    /*
+     * The GC state, stack and statistics must be read/write for the guest
+     * (supervisor only of course).
+     *
+     * Remember, we run guest code at ring-1 and ring-2 levels, which are
+     * considered supervisor levels by the paging  structures.  We run the VMM
+     * in ring-0 with CR0.WP=0 and mapping all VMM structures as read-only
+     * pages.  The following structures are exceptions and must be mapped with
+     * write access so the ring-1 and ring-2 code can modify them.
+     */
     int rc = PGMMapSetPage(pVM, pVM->patm.s.pGCStateGC, PAGE_SIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
-    if (RT_FAILURE(rc))
-        Log(("PATMR3InitFinalize: PGMMapSetPage failed with %Rrc!!\n", rc));
+    AssertLogRelMsgReturn(RT_SUCCESS(rc), ("Failed to make the GCState accessible to ring-1 and ring-2 code: %Rrc\n", rc), rc);
 
     rc = PGMMapSetPage(pVM, pVM->patm.s.pGCStackGC, PATM_STACK_TOTAL_SIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
-    if (RT_FAILURE(rc))
-        Log(("PATMR3InitFinalize: PGMMapSetPage failed with %Rrc!!\n", rc));
+    AssertLogRelMsgReturn(RT_SUCCESS(rc), ("Failed to make the GCStack accessible to ring-1 and ring-2 code: %Rrc\n", rc), rc);
 
     rc = PGMMapSetPage(pVM, pVM->patm.s.pStatsGC, PATM_STAT_MEMSIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
-    if (RT_FAILURE(rc))
-        Log(("PATMR3InitFinalize: PGMMapSetPage failed with %Rrc!!\n", rc));
-
+    AssertLogRelMsgReturn(RT_SUCCESS(rc), ("Failed to make the stats struct accessible to ring-1 and ring-2 code: %Rrc\n", rc), rc);
+
+    /*
+     * Find the patch helper segment so we can identify code running there as patch code.
+     */
+    rc = PDMR3LdrGetSymbolRC(pVM, NULL, "g_PatchHlpBegin", &pVM->patm.s.pbPatchHelpersRC);
+    AssertLogRelMsgReturn(RT_SUCCESS(rc), ("Failed to resolve g_PatchHlpBegin: %Rrc\n", rc), rc);
+    pVM->patm.s.pbPatchHelpersR3 = (uint8_t *)MMHyperRCToR3(pVM, pVM->patm.s.pbPatchHelpersRC);
+    AssertLogRelReturn(pVM->patm.s.pbPatchHelpersR3 != NULL, VERR_INTERNAL_ERROR_3);
+
+    RTRCPTR RCPtrEnd;
+    rc = PDMR3LdrGetSymbolRC(pVM, NULL, "g_PatchHlpEnd", &RCPtrEnd);
+    AssertLogRelMsgReturn(RT_SUCCESS(rc), ("Failed to resolve g_PatchHlpEnd: %Rrc\n", rc), rc);
+
+    pVM->patm.s.cbPatchHelpers = RCPtrEnd - pVM->patm.s.pbPatchHelpersRC;
+    AssertLogRelMsgReturn(pVM->patm.s.cbPatchHelpers < _128K,
+                          ("%RRv-%RRv => %#x\n", pVM->patm.s.pbPatchHelpersRC, RCPtrEnd, pVM->patm.s.cbPatchHelpers),
+                          VERR_INTERNAL_ERROR_4);
     return rc;
 }
@@ -460,7 +483,8 @@
  * The PATM will update the addresses used by the switcher.
  *
- * @param   pVM     The VM.
- */
-VMMR3_INT_DECL(void) PATMR3Relocate(PVM pVM)
+ * @param   pVM         The VM.
+ * @param   offDelta    The relocation delta.
+ */
+VMMR3_INT_DECL(void) PATMR3Relocate(PVM pVM, RTRCINTPTR offDelta)
 {
     if (HMIsEnabled(pVM))
@@ -468,43 +492,40 @@
 
     RTRCPTR     GCPtrNew = MMHyperR3ToRC(pVM, pVM->patm.s.pGCStateHC);
-    RTRCINTPTR  delta = GCPtrNew - pVM->patm.s.pGCStateGC;
-
-    Log(("PATMR3Relocate from %RRv to %RRv - delta %08X\n", pVM->patm.s.pGCStateGC, GCPtrNew, delta));
-    if (delta)
+    Assert((RTRCINTPTR)(GCPtrNew - pVM->patm.s.pGCStateGC) == offDelta);
+
+    Log(("PATMR3Relocate from %RRv to %RRv - delta %08X\n", pVM->patm.s.pGCStateGC, GCPtrNew, offDelta));
+    if (offDelta)
     {
         PCPUMCTX pCtx;
 
         /* Update CPUMCTX guest context pointer. */
-        pVM->patm.s.pCPUMCtxGC   += delta;
-
-        pVM->patm.s.deltaReloc = delta;
-
-        RTAvloU32DoWithAll(&pVM->patm.s.PatchLookupTreeHC->PatchTree, true, RelocatePatches, (void *)pVM);
-
+        pVM->patm.s.pCPUMCtxGC += offDelta;
+
+        pVM->patm.s.deltaReloc = offDelta;
+        RTAvloU32DoWithAll(&pVM->patm.s.PatchLookupTreeHC->PatchTree, true, patmR3RelocatePatches, (void *)pVM);
+
+        pVM->patm.s.pGCStateGC        = GCPtrNew;
+        pVM->patm.s.pPatchMemGC       = MMHyperR3ToRC(pVM, pVM->patm.s.pPatchMemHC);
+        pVM->patm.s.pGCStackGC        = MMHyperR3ToRC(pVM, pVM->patm.s.pGCStackHC);
+        pVM->patm.s.pStatsGC          = MMHyperR3ToRC(pVM, pVM->patm.s.pStatsHC);
+        pVM->patm.s.PatchLookupTreeGC = MMHyperR3ToRC(pVM, pVM->patm.s.PatchLookupTreeHC);
+
+        if (pVM->patm.s.pfnSysEnterPatchGC)
+            pVM->patm.s.pfnSysEnterPatchGC += offDelta;
+
+        /* If we are running patch code right now, then also adjust EIP. */
         pCtx = CPUMQueryGuestCtxPtr(VMMGetCpu(pVM));
-
-        /* If we are running patch code right now, then also adjust EIP. */
         if (PATMIsPatchGCAddr(pVM, pCtx->eip))
-            pCtx->eip += delta;
-
-        pVM->patm.s.pGCStateGC  = GCPtrNew;
-        pVM->patm.s.pPatchMemGC = MMHyperR3ToRC(pVM, pVM->patm.s.pPatchMemHC);
-
-        pVM->patm.s.pGCStackGC  = MMHyperR3ToRC(pVM, pVM->patm.s.pGCStackHC);
-
-        pVM->patm.s.pStatsGC    = MMHyperR3ToRC(pVM, pVM->patm.s.pStatsHC);
-
-        pVM->patm.s.PatchLookupTreeGC = MMHyperR3ToRC(pVM, pVM->patm.s.PatchLookupTreeHC);
-
-        if (pVM->patm.s.pfnSysEnterPatchGC)
-            pVM->patm.s.pfnSysEnterPatchGC += delta;
+            pCtx->eip += offDelta;
 
         /* Deal with the global patch functions. */
-        pVM->patm.s.pfnHelperCallGC += delta;
-        pVM->patm.s.pfnHelperRetGC  += delta;
-        pVM->patm.s.pfnHelperIretGC += delta;
-        pVM->patm.s.pfnHelperJumpGC += delta;
-
-        RelocatePatches(&pVM->patm.s.pGlobalPatchRec->Core, (void *)pVM);
+        pVM->patm.s.pfnHelperCallGC += offDelta;
+        pVM->patm.s.pfnHelperRetGC  += offDelta;
+        pVM->patm.s.pfnHelperIretGC += offDelta;
+        pVM->patm.s.pfnHelperJumpGC += offDelta;
+
+        pVM->patm.s.pbPatchHelpersRC += offDelta;
+
+        patmR3RelocatePatches(&pVM->patm.s.pGlobalPatchRec->Core, (void *)pVM);
     }
 }
@@ -730,5 +751,5 @@
  * @param   pParam      Pointer to the VM.
  */
-static DECLCALLBACK(int) RelocatePatches(PAVLOU32NODECORE pNode, void *pParam)
+static DECLCALLBACK(int) patmR3RelocatePatches(PAVLOU32NODECORE pNode, void *pParam)
 {
     PPATMPATCHREC   pPatch = (PPATMPATCHREC)pNode;
@@ -917,4 +938,5 @@
         }
 
+        case FIXUP_REL_HELPER_IN_PATCH_ASM_TMPL:
         case FIXUP_CONSTANT_IN_PATCH_ASM_TMPL:
             /* Only applicable when loading state. */
@@ -1000,39 +1022,4 @@
 
 #endif /* VBOX_WITH_DEBUGGER */
-#ifdef UNUSED_FUNCTIONS
-
-/**
- * Returns the host context pointer and size of the patch memory block
- *
- * @returns Host context pointer.
- * @param   pVM         Pointer to the VM.
- * @param   pcb         Size of the patch memory block
- * @internal
- */
-VMMR3_INT_DECL(void *) PATMR3QueryPatchMemHC(PVM pVM, uint32_t *pcb)
-{
-    AssertReturn(!HMIsEnabled(pVM), NULL);
-    if (pcb)
-        *pcb = pVM->patm.s.cbPatchMem;
-    return pVM->patm.s.pPatchMemHC;
-}
-
-
-/**
- * Returns the guest context pointer and size of the patch memory block
- *
- * @returns Guest context pointer.
- * @param   pVM         Pointer to the VM.
- * @param   pcb         Size of the patch memory block
- */
-VMMR3_INT_DECL(RTRCPTR) PATMR3QueryPatchMemGC(PVM pVM, uint32_t *pcb)
-{
-    AssertReturn(!HMIsEnabled(pVM), NIL_RTRCPTR);
-    if (pcb)
-        *pcb = pVM->patm.s.cbPatchMem;
-    return pVM->patm.s.pPatchMemGC;
-}
-
-#endif /* UNUSED_FUNCTIONS */
 
 /**
@@ -1047,20 +1034,4 @@
     return pVM->patm.s.pGCStateHC;
 }
-
-
-#ifdef UNUSED_FUNCTION
-/**
- * Checks whether the HC address is part of our patch region
- *
- * @returns true/false.
- * @param   pVM         Pointer to the VM.
- * @param   pAddrHC     Host context ring-3 address to check.
- */
-VMMR3_INT_DECL(bool) PATMR3IsPatchHCAddr(PVM pVM, void *pAddrHC)
-{
-    return (uintptr_t)pAddrHC >= (uintptr_t)pVM->patm.s.pPatchMemHC
-        && (uintptr_t)pAddrHC <  (uintptr_t)pVM->patm.s.pPatchMemHC + pVM->patm.s.cbPatchMem;
-}
-#endif
 
 
@@ -1111,7 +1082,13 @@
 {
     AssertReturn(!HMIsEnabled(pVM), NULL);
-    if (pVM->patm.s.pPatchMemGC <= pAddrGC && pVM->patm.s.pPatchMemGC + pVM->patm.s.cbPatchMem > pAddrGC)
-        return pVM->patm.s.pPatchMemHC + (pAddrGC - pVM->patm.s.pPatchMemGC);
-    return NULL;
+    RTRCUINTPTR offPatch = (RTRCUINTPTR)pAddrGC - (RTRCUINTPTR)pVM->patm.s.pPatchMemGC;
+    if (offPatch >= pVM->patm.s.cbPatchMem)
+    {
+        offPatch = (RTRCUINTPTR)pAddrGC - (RTRCUINTPTR)pVM->patm.s.pbPatchHelpersRC;
+        if (offPatch >= pVM->patm.s.cbPatchHelpers)
+            return NULL;
+        return pVM->patm.s.pbPatchHelpersR3 + offPatch;
+    }
+    return pVM->patm.s.pPatchMemHC + offPatch;
 }
 
@@ -1134,10 +1111,14 @@
     uint32_t offset;
 
-    if (PATMIsPatchGCAddr(pVM, pGCPtr))
-    {
+    offset = (RTRCUINTPTR)pGCPtr - (RTRCUINTPTR)pVM->patm.s.pPatchMemGC;
+    if (offset < pVM->patm.s.cbPatchMem)
+    {
+#ifdef VBOX_STRICT
         PPATCHINFO pPatch = (PPATCHINFO)pCacheRec->pPatch;
-        Assert(pPatch);
-        return PATCHCODE_PTR_HC(pPatch) + (pGCPtr - PATCHCODE_PTR_GC(pPatch));
-    }
+        Assert(pPatch); Assert(offset - pPatch->pPatchBlockOffset < pPatch->cbPatchBlockSize);
+#endif
+        return pVM->patm.s.pPatchMemHC + offset;
+    }
+    /* Note! We're _not_ including the patch helpers here. */
 
     offset = pGCPtr & PAGE_OFFSET_MASK;
Index: /trunk/src/VBox/VMM/VMMR3/PATMA.asm
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/PATMA.asm	(revision 54762)
+++ /trunk/src/VBox/VMM/VMMR3/PATMA.asm	(revision 54763)
@@ -1723,115 +1723,21 @@
 ; PATMCpuidReplacement
 ;
+; Calls a helper function that does the job.
+;
+; This way we can change the CPUID structures and how we organize them without
+; breaking patches.  It also saves a bit of memory for patch code and fixups.
+;
 BEGIN_PATCH g_patmCpuidRecord, PATMCpuidReplacement
-    not     dword [esp-16]              ; probe stack before starting, just in case.
-    not     dword [esp-16]
+    not     dword [esp-32]              ; probe stack before starting
+    not     dword [esp-32]
+
     mov     dword [ss:PATM_INTERRUPTFLAG], 0
 PATCH_FIXUP PATM_INTERRUPTFLAG
     pushf
 
-;; @todo We could put all this stuff in a CPUM assembly function can simply call it.
-
-    ; Save the registers we use for passthru and sub-leaf matching (eax is not used).
-    push    edx
-    push    ecx
-    push    ebx
-
-    ;
-    ; Perform a linear search of the strictly sorted CPUID leaf array. 
-    ;
-    ; (Was going to do a binary search, but that ended up being complicated if 
-    ; we want a flexible leaf size. Linear search is probably good enough.)
-    ;
-    mov     ebx, PATM_CPUID_ARRAY_PTR
-PATCH_FIXUP PATM_CPUID_ARRAY_PTR
-    mov     edx, PATM_CPUID_ARRAY_END_PTR
-PATCH_FIXUP PATM_CPUID_ARRAY_END_PTR
-    cmp     ebx, edx
-    jae     cpuid_unknown
-
-cpuid_lookup_leaf:
-    cmp     eax, [ss:ebx + CPUMCPUIDLEAF.uLeaf]
-    jbe     cpuid_maybe_match_eax
-    add     ebx, PATM_CPUID_ARRAY_ENTRY_SIZE
-PATCH_FIXUP PATM_CPUID_ARRAY_ENTRY_SIZE
-    cmp     ebx, edx
-    jb      cpuid_lookup_leaf
-    jmp     cpuid_unknown
-
-cpuid_maybe_match_eax:    
-    jne     cpuid_unknown
-
-    ; Sub-leaf match too?
-    mov     ecx, [esp + 4]
-    and     ecx, [ss:ebx + CPUMCPUIDLEAF.fSubLeafMask]
-    cmp     ecx, [ss:ebx + CPUMCPUIDLEAF.uSubLeaf]
-    je      cpuid_fetch
-
-    ; Search forward until we've got a matching sub-leaf (or not). 
-cpuid_subleaf_lookup:
-    add     ebx, PATM_CPUID_ARRAY_ENTRY_SIZE
-PATCH_FIXUP PATM_CPUID_ARRAY_ENTRY_SIZE
-    cmp     ebx, edx
-    jae     cpuid_subleaf_not_found_sub_ebx
-    cmp     eax, [ss:ebx + CPUMCPUIDLEAF.uLeaf]    
-    jne     cpuid_subleaf_not_found_sub_ebx
-    cmp     ecx, [ss:ebx + CPUMCPUIDLEAF.uSubLeaf]    
-    ja      cpuid_subleaf_lookup
-    je      cpuid_fetch
-cpuid_subleaf_not_found_sub_ebx:
-    sub     ebx, PATM_CPUID_ARRAY_ENTRY_SIZE
-PATCH_FIXUP PATM_CPUID_ARRAY_ENTRY_SIZE
-    
-    ;
-    ; Out of range sub-leaves aren't quite as easy and pretty as we emulate them
-    ; here, but we do an adequate job.
-    ;    
-cpuid_subleaf_not_found:
-    xor     ecx, ecx
-    test    dword [ss:ebx + CPUMCPUIDLEAF.fFlags], CPUMCPUIDLEAF_F_INTEL_TOPOLOGY_SUBLEAVES
-    jz      cpuid_load_zeros_except_ecx
-    mov     ecx, [esp + 4]
-    and     ecx, 0ffh
-cpuid_load_zeros_except_ecx:
-    xor     edx, edx
-    xor     eax, eax
-    xor     ebx, ebx
-    jmp     cpuid_done
-
-    ;
-    ; Different CPUs have different ways of dealing with unknown CPUID leaves.
-    ;
-cpuid_unknown:
-    mov     edx, PATM_CPUID_UNKNOWN_METHOD
-PATCH_FIXUP PATM_CPUID_UNKNOWN_METHOD
-    cmp     edx, CPUMUNKNOWNCPUID_PASSTHRU
-    je      cpuid_unknown_passthru
-    ; Load the default cpuid leaf.
-cpuid_unknown_def_leaf:
-    mov     ebx, PATM_CPUID_DEF_PTR
-PATCH_FIXUP PATM_CPUID_DEF_PTR
-    mov     edx, [ss:ebx + CPUMCPUID.uEdx]
-    mov     ecx, [ss:ebx + CPUMCPUID.uEcx]
-    mov     eax, [ss:ebx + CPUMCPUID.uEax]
-    mov     ebx, [ss:ebx + CPUMCPUID.uEbx]
-    jmp     cpuid_done
-    ; Pass thru the input values unmodified (eax is still virgin).
-cpuid_unknown_passthru:
-    mov     edx, [esp + 8]
-    mov     ecx, [esp + 4]
-    mov     ebx, [esp]
-    jmp     cpuid_done
-
-    ;
-    ; Normal return.
-    ;
-cpuid_fetch:
-    mov     edx, [ss:ebx + CPUMCPUIDLEAF.uEdx]
-    mov     ecx, [ss:ebx + CPUMCPUIDLEAF.uEcx]
-    mov     eax, [ss:ebx + CPUMCPUIDLEAF.uEax]
-    mov     ebx, [ss:ebx + CPUMCPUIDLEAF.uEbx]
-             
-cpuid_done:
-    add     esp, 12
+    db      0e8h                        ; call
+    dd      PATM_ASMFIX_PATCH_HLP_CPUM_CPUID
+PATCH_FIXUP PATM_ASMFIX_PATCH_HLP_CPUM_CPUID
+
     popf
     mov     dword [ss:PATM_INTERRUPTFLAG], 1
Index: /trunk/src/VBox/VMM/VMMR3/PATMA.mac
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/PATMA.mac	(revision 54762)
+++ /trunk/src/VBox/VMM/VMMR3/PATMA.mac	(revision 54763)
@@ -53,9 +53,9 @@
 %define PATM_CALL_RETURN_ADDR                   0xF1ABCD19
 %define PATM_CPUID_CENTAUR_PTR                  0xF1ABCD1a
-%define PATM_CPUID_ARRAY_PTR                    0xF1ABCD1b
-%define PATM_CPUID_ARRAY_END_PTR                0xF1ABCD1c
-%define PATM_CPUID_ARRAY_ENTRY_SIZE             0xF1ABCD1d
-%define PATM_CPUID_UNKNOWN_METHOD               0xF1ABCD1e
-
+%define PATM_ASMFIX_REUSE_LATER_0               0xF1ABCD1b
+%define PATM_ASMFIX_REUSE_LATER_1               0xF1ABCD1c
+%define PATM_ASMFIX_REUSE_LATER_2               0xF1ABCD1d
+%define PATM_ASMFIX_REUSE_LATER_3               0xF1ABCD1e
+%define PATM_ASMFIX_PATCH_HLP_CPUM_CPUID        0xF1ABCD1f
 
 ;/* Anything larger doesn't require a fixup */
Index: /trunk/src/VBox/VMM/VMMR3/PATMPatch.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/PATMPatch.cpp	(revision 54762)
+++ /trunk/src/VBox/VMM/VMMR3/PATMPatch.cpp	(revision 54763)
@@ -23,4 +23,5 @@
 #define LOG_GROUP LOG_GROUP_PATM
 #include <VBox/vmm/patm.h>
+#include <VBox/vmm/pdmapi.h>
 #include <VBox/vmm/pgm.h>
 #include <VBox/vmm/cpum.h>
@@ -104,5 +105,7 @@
 
     Assert(   uType == FIXUP_ABSOLUTE
-           || (   (uType == FIXUP_ABSOLUTE_IN_PATCH_ASM_TMPL || uType == FIXUP_CONSTANT_IN_PATCH_ASM_TMPL)
+           || (   (   uType == FIXUP_ABSOLUTE_IN_PATCH_ASM_TMPL
+                   || uType == FIXUP_CONSTANT_IN_PATCH_ASM_TMPL
+                   || uType == FIXUP_REL_HELPER_IN_PATCH_ASM_TMPL)
                && pSource == pDest
                && PATM_IS_FIXUP_TYPE(pSource))
@@ -361,14 +364,8 @@
                         dest = pVM->pVMRC + RT_OFFSETOF(VM, aCpus[0].fLocalForcedActions);
                         break;
-                    case PATM_CPUID_DEF_PTR:
+
+                    case PATM_CPUID_DEF_PTR: /* saved state only */
                         dest = CPUMR3GetGuestCpuIdPatmDefRCPtr(pVM);
                         break;
-                    case PATM_CPUID_ARRAY_PTR:
-                        dest = CPUMR3GetGuestCpuIdPatmArrayRCPtr(pVM);
-                        break;
-                    case PATM_CPUID_ARRAY_END_PTR:
-                        dest = CPUMR3GetGuestCpuIdPatmArrayEndRCPtr(pVM);
-                        break;
-
                     case PATM_CPUID_STD_PTR: /* saved state only */
                         dest = CPUMR3GetGuestCpuIdPatmStdRCPtr(pVM);
@@ -382,19 +379,22 @@
 
                     /*
-                     * The following fixups are constants that needs to be corrected when
-                     * loading saved state as these may change between VBox versions.
+                     * The following fixups are constants and helper code calls that only
+                     * needs to be corrected when loading saved state.
                      */
-                    case PATM_CPUID_ARRAY_ENTRY_SIZE:
-                        dest = sizeof(CPUMCPUIDLEAF);
-                        uRelocType = FIXUP_CONSTANT_IN_PATCH_ASM_TMPL;
-                        break;
-                    case PATM_CPUID_UNKNOWN_METHOD:
-                        dest = CPUMR3GetGuestCpuIdPatmUnknownLeafMethod(pVM);
-                        uRelocType = FIXUP_CONSTANT_IN_PATCH_ASM_TMPL;
-                        break;
+                    case PATM_ASMFIX_HELPER_CPUM_CPUID:
+                    {
+                        int rc = PDMR3LdrGetSymbolRC(pVM, NULL, "CPUMPatchHlpCpuId", &dest);
+                        AssertReleaseRCBreakStmt(rc, dest = PATM_ILLEGAL_DESTINATION);
+                        uRelocType = FIXUP_REL_HELPER_IN_PATCH_ASM_TMPL;
+                        break;
+                    }
 
                     /*
                      * Unknown fixup.
                      */
+                    case PATM_ASMFIX_REUSE_LATER_0:
+                    case PATM_ASMFIX_REUSE_LATER_1:
+                    case PATM_ASMFIX_REUSE_LATER_2:
+                    case PATM_ASMFIX_REUSE_LATER_3:
                     default:
                         AssertReleaseMsgFailed(("Unknown fixup: %#x\n", pAsmRecord->aRelocs[i].uType));
@@ -403,5 +403,13 @@
                 }
 
-                *(RTRCPTR *)&pPB[j] = dest;
+                if (uRelocType == FIXUP_REL_HELPER_IN_PATCH_ASM_TMPL)
+                {
+                    RTRCUINTPTR RCPtrAfter = pVM->patm.s.pPatchMemGC
+                                           + (RTRCUINTPTR)(&pPB[j + sizeof(RTRCPTR)] - pVM->patm.s.pPatchMemHC);
+                    dest -= RCPtrAfter;
+                }
+
+                *(PRTRCPTR)&pPB[j] = dest;
+
                 if (pAsmRecord->aRelocs[i].uType < PATM_NO_FIXUP)
                 {
Index: /trunk/src/VBox/VMM/VMMR3/PATMSSM.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/PATMSSM.cpp	(revision 54762)
+++ /trunk/src/VBox/VMM/VMMR3/PATMSSM.cpp	(revision 54763)
@@ -7,5 +7,5 @@
 
 /*
- * Copyright (C) 2006-2014 Oracle Corporation
+ * Copyright (C) 2006-2015 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -23,4 +23,5 @@
 #define LOG_GROUP LOG_GROUP_PATM
 #include <VBox/vmm/patm.h>
+#include <VBox/vmm/pdmapi.h>
 #include <VBox/vmm/cpum.h>
 #include <VBox/vmm/cpumctx-v1_6.h>
@@ -1361,10 +1362,4 @@
                     *pFixup = CPUMR3GetGuestCpuIdPatmDefRCPtr(pVM);
                     break;
-                case PATM_CPUID_ARRAY_PTR:
-                    *pFixup = CPUMR3GetGuestCpuIdPatmArrayRCPtr(pVM);
-                    break;
-                case PATM_CPUID_ARRAY_END_PTR:
-                    *pFixup = CPUMR3GetGuestCpuIdPatmArrayEndRCPtr(pVM);
-                    break;
                 case PATM_CPUID_STD_PTR: /* Saved again patches only. */
                     *pFixup = CPUMR3GetGuestCpuIdPatmStdRCPtr(pVM);
@@ -1376,4 +1371,9 @@
                     *pFixup = CPUMR3GetGuestCpuIdPatmCentaurRCPtr(pVM);
                     break;
+                case PATM_ASMFIX_REUSE_LATER_0: /* Was only used for a few days. Don't want to keep this legacy around.  */
+                case PATM_ASMFIX_REUSE_LATER_1:
+                    AssertLogRelMsgFailedReturn(("Unsupported PATM fixup. You have to discard this saved state or snapshot."),
+                                                VERR_INTERNAL_ERROR);
+                    break;
             }
         }
@@ -1387,9 +1387,8 @@
             switch (pRec->pSource)
             {
-                case PATM_CPUID_ARRAY_ENTRY_SIZE:
-                    *pFixup = sizeof(CPUMCPUIDLEAF);
-                    break;
-                case PATM_CPUID_UNKNOWN_METHOD:
-                    *pFixup = CPUMR3GetGuestCpuIdPatmUnknownLeafMethod(pVM);
+                case PATM_ASMFIX_REUSE_LATER_2: /* Was only used for a few days. Don't want to keep this legacy around.  */
+                case PATM_ASMFIX_REUSE_LATER_3:
+                    AssertLogRelMsgFailedReturn(("Unsupported PATM fixup. You have to discard this saved state or snapshot."),
+                                                VERR_INTERNAL_ERROR);
                     break;
                 default:
@@ -1397,4 +1396,29 @@
                     return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
             }
+        }
+        /*
+         * Relative fixups for calling or jumping to helper functions inside VMMRC.
+         * (The distance between the helper function and the patch is subject to
+         * new code being added to VMMRC as well as VM configurations influencing
+         * heap allocations and so on and so forth.)
+         */
+        else if (pRec->uType == FIXUP_REL_HELPER_IN_PATCH_ASM_TMPL)
+        {
+            AssertLogRelReturn(uVersion > PATM_SAVED_STATE_VERSION_NO_RAW_MEM, VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
+            Assert(pRec->pSource == pRec->pDest); Assert(PATM_IS_FIXUP_TYPE(pRec->pSource));
+            int     rc;
+            RTRCPTR uRCPtrDest;
+            switch (pRec->pSource)
+            {
+                case PATM_ASMFIX_HELPER_CPUM_CPUID:
+                    rc = PDMR3LdrGetSymbolRC(pVM, NULL, "CPUMPatchHlpCpuId", &uRCPtrDest);
+                    AssertLogRelRCReturn(rc, rc);
+                    break;
+                default:
+                    AssertLogRelMsgFailed(("Unknown FIXUP_REL_HLP_CALL_IN_PATCH_ASM_TMPL fixup: %#x\n", pRec->pSource));
+                    return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+            }
+            RTRCPTR uRCPtrAfter = pVM->patm.s.pPatchMemGC + ((uintptr_t)&pFixup[1] - (uintptr_t)pVM->patm.s.pPatchMemHC);
+            *pFixup = uRCPtrDest - uRCPtrAfter;
         }
 
Index: /trunk/src/VBox/VMM/VMMR3/VM.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/VM.cpp	(revision 54762)
+++ /trunk/src/VBox/VMM/VMMR3/VM.cpp	(revision 54763)
@@ -1250,5 +1250,5 @@
     TRPMR3Relocate(pVM, offDelta);
 #ifdef VBOX_WITH_RAW_MODE
-    PATMR3Relocate(pVM);
+    PATMR3Relocate(pVM, (RTRCINTPTR)offDelta);
     CSAMR3Relocate(pVM, offDelta);
 #endif
Index: /trunk/src/VBox/VMM/VMMRC/CPUMRCPatchHlp.asm
===================================================================
--- /trunk/src/VBox/VMM/VMMRC/CPUMRCPatchHlp.asm	(revision 54763)
+++ /trunk/src/VBox/VMM/VMMRC/CPUMRCPatchHlp.asm	(revision 54763)
@@ -0,0 +1,194 @@
+; $Id$
+;; @file
+; CPUM - Patch Helpers.
+;
+
+; Copyright (C) 2015 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                                                                *
+;*******************************************************************************
+%include "VBox/asmdefs.mac"
+%include "VBox/vmm/cpum.mac"
+%include "CPUMInternal.mac"
+%include "VBox/vmm/vm.mac"
+%include "VMMRC.mac"
+
+
+;*******************************************************************************
+;*  External Symbols                                                           *
+;*******************************************************************************
+extern IMPNAME(g_VM)
+
+
+BEGIN_PATCH_HLP_SEG
+
+;;
+; Helper for PATMCpuidReplacement. 
+; 
+; We have at most 32 bytes of stack to play with, .
+;
+; @input    eax, ecx(, edx, ebx)
+; @output   eax, ebx, ecx, ebx
+;
+; @uses     eflags (caller saves them)
+;
+BEGINPROC_EXPORTED CPUMPatchHlpCpuId
+    ; Save the registers we use for passthru and sub-leaf matching (eax is not used).
+    push    edx
+    push    ecx
+    push    ebx
+
+    ; Use edi as VM pointer.
+    push    edi
+    mov     edi, IMP_SEG(ss, g_VM)
+
+%define CPUMCPUIDLEAF_SIZE_LOG2 5       ; ASSUMES CPUMCPUIDLEAF_size == 32
+
+    ;
+    ; Perform a binary search looking for leaf with the EAX value.
+    ;             
+    mov     edx, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.cCpuIdLeaves]   
+    mov     ecx, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.paCpuIdLeavesRC]
+    test    edx, edx
+    jz      cpuid_unknown
+    shl     edx, CPUMCPUIDLEAF_SIZE_LOG2
+    add     edx, ecx                    ; edx = end (exclusive); ecx = start.
+
+cpuid_lookup_leaf:
+    ; Find the middle element
+    mov     ebx, edx
+cpuid_lookup_leaf_ebx_loeaded:
+    sub     ebx, ecx
+    shr     ebx, 1 + CPUMCPUIDLEAF_SIZE_LOG2
+    shl     ebx, CPUMCPUIDLEAF_SIZE_LOG2
+    add     ebx, ecx                    ; ebx = middle element.
+
+    ; Compare.
+    cmp     eax, [ss:ebx + CPUMCPUIDLEAF.uLeaf]
+    jae     cpuid_lookup_split_up
+
+    ; The leaf is before ebx.
+cpuid_lookup_split_down:
+    cmp     ecx, ebx                    ; start == middle? if so, we failed.
+    mov     edx, ebx                    ; end = middle;
+    jne     cpuid_lookup_leaf_ebx_loeaded
+    jmp     cpuid_unknown
+
+    ; The leaf is at or after ebx.
+cpuid_lookup_split_up:
+    je      cpuid_match_eax
+    lea     ecx, [ebx + CPUMCPUIDLEAF_size] ; start = middle + 1
+    cmp     ecx, edx                    ; middle + 1 == start? if so, we failed.
+    jne     cpuid_lookup_leaf
+    jmp     cpuid_unknown
+
+    ;
+    ; We've to a matching leaf, does the sub-leaf match too?
+    ;
+cpuid_match_eax:    
+    mov     ecx, [esp + 4]
+    and     ecx, [ss:ebx + CPUMCPUIDLEAF.fSubLeafMask]
+    cmp     ecx, [ss:ebx + CPUMCPUIDLEAF.uSubLeaf]
+    je      cpuid_fetch
+    ja      cpuid_lookup_subleaf_forwards
+
+    ;
+    ; Search backwards.
+    ;
+cpuid_lookup_subleaf_backwards:
+    mov     edx, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.paCpuIdLeavesRC] ; edx = first leaf
+
+cpuid_lookup_subleaf_backwards_loop:
+    cmp     ebx, edx                    ; Is there a leaf before the current? 
+    jbe     cpuid_subleaf_not_found     ; If not we're out of luck.
+    cmp     eax, [ss:ebx - CPUMCPUIDLEAF_size + CPUMCPUIDLEAF.uLeaf]
+    jne     cpuid_subleaf_not_found     ; If the leaf before us does not have the same leaf number, we failed.
+    sub     ebx, CPUMCPUIDLEAF_size
+    cmp     ecx, [ss:ebx + CPUMCPUIDLEAF.uSubLeaf]
+    je      cpuid_fetch                 ; If the subleaf matches, we're good!.
+    jb      cpuid_lookup_subleaf_backwards_loop ; Still hope if the subleaf we're seeking is smaller.
+    jmp     cpuid_subleaf_not_found     ; Too bad.
+
+    ;
+    ; Search forward until we've got a matching sub-leaf (or not).  
+    ;
+cpuid_lookup_subleaf_forwards:
+    ; Calculate the last leaf address.
+    mov     edx, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.cCpuIdLeaves]   
+    dec     edx
+    shl     edx, CPUMCPUIDLEAF_SIZE_LOG2
+    add     edx, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.paCpuIdLeavesRC] ; edx = last leaf (inclusive)
+
+cpuid_subleaf_lookup:
+    cmp     ebx, edx
+    jae     cpuid_subleaf_not_found
+    cmp     eax, [ss:ebx + CPUMCPUIDLEAF_size + CPUMCPUIDLEAF.uLeaf]
+    jne     cpuid_subleaf_not_found
+    add     ebx, CPUMCPUIDLEAF_size
+    cmp     ecx, [ss:ebx + CPUMCPUIDLEAF.uSubLeaf]    
+    ja      cpuid_subleaf_lookup
+    je      cpuid_fetch
+    
+    ;
+    ; Out of range sub-leaves aren't quite as easy and pretty as we emulate them
+    ; here, but we do an adequate job.
+    ;    
+cpuid_subleaf_not_found:
+    xor     ecx, ecx
+    test    dword [ss:ebx + CPUMCPUIDLEAF.fFlags], CPUMCPUIDLEAF_F_INTEL_TOPOLOGY_SUBLEAVES
+    jz      cpuid_load_zeros_except_ecx
+    mov     ecx, [esp + 4]
+    and     ecx, 0ffh
+cpuid_load_zeros_except_ecx:
+    xor     edx, edx
+    xor     eax, eax
+    xor     ebx, ebx
+    jmp     cpuid_done
+
+    ;
+    ; Different CPUs have different ways of dealing with unknown CPUID leaves.
+    ;
+cpuid_unknown:
+    mov     ebx, IMP_SEG(ss, g_VM)
+    mov     dword [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.enmUnknownCpuIdMethod], CPUMUNKNOWNCPUID_PASSTHRU
+    je      cpuid_unknown_passthru
+    ; Load the default cpuid leaf.
+cpuid_unknown_def_leaf:
+    mov     edx, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.DefCpuId + CPUMCPUID.uEdx]
+    mov     ecx, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.DefCpuId + CPUMCPUID.uEcx]
+    mov     eax, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.DefCpuId + CPUMCPUID.uEax]
+    mov     ebx, [ss:edi + VM.cpum + CPUM.GuestInfo + CPUMINFO.DefCpuId + CPUMCPUID.uEbx]
+    jmp     cpuid_done
+    ; Pass thru the input values unmodified (eax is still virgin).
+cpuid_unknown_passthru:
+    mov     edx, [esp + 8]
+    mov     ecx, [esp + 4]
+    mov     ebx, [esp]
+    jmp     cpuid_done
+
+    ;
+    ; Normal return.
+    ;
+cpuid_fetch:
+    mov     edx, [ss:ebx + CPUMCPUIDLEAF.uEdx]
+    mov     ecx, [ss:ebx + CPUMCPUIDLEAF.uEcx]
+    mov     eax, [ss:ebx + CPUMCPUIDLEAF.uEax]
+    mov     ebx, [ss:ebx + CPUMCPUIDLEAF.uEbx]
+             
+cpuid_done:
+    pop     edi
+    add     esp, 12
+    ret
+ENDPROC CPUMPatchHlpCpuId
+
Index: /trunk/src/VBox/VMM/VMMRC/VMMRC.mac
===================================================================
--- /trunk/src/VBox/VMM/VMMRC/VMMRC.mac	(revision 54762)
+++ /trunk/src/VBox/VMM/VMMRC/VMMRC.mac	(revision 54763)
@@ -26,9 +26,16 @@
 ; @param    %1      The segment name.
 ; @remark   Use BEGINCODE to switch back to the code segment.
+
+;; @def VMMR0_SEG_CODE
+; Set the output segment to one of the special VMMR0 code segments.
+; @param    %1      The segment name.
 %ifdef ASM_FORMAT_OMF
  %macro VMMR0_SEG 1
- segment VMMR0.%1 public CLASS=CONST align=1 use32
+ segment VMMR0.%1 public CLASS=CONST align=1 use32 flat
  %endmacro
- %define VMMR0_SEG_DEFINED
+
+ %macro VMMR0_CODE_SEG 1
+ segment VMMR0.%1 public CLASS=CODE align=16 use32 flat
+ %endmacro
 %endif
 
@@ -42,5 +49,13 @@
   %endif
  %endmacro
- %define VMMR0_SEG_DEFINED
+
+ %macro VMMR0_CODE_SEG 1
+  %ifndef DEFINED_VMMR0_CODE_SEG.%1
+   %define DEFINED_VMMR0_CODE_SEG.%1 1
+   [section .VMMR0.%1 progbits alloc exec nowrite align=16 ]
+  %else
+   [section .VMMR0.%1 ]
+  %endif
+ %endmacro
 %endif
 
@@ -60,5 +75,19 @@
   %endmacro
  %endif
- %define VMMR0_SEG_DEFINED
+
+ %ifdef __YASM__
+  %macro VMMR0_CODE_SEG 1
+   %ifndef DEFINED_VMMR0_CODE_SEG.%1
+    %define DEFINED_VMMR0_CODE_SEG.%1 1
+    [section VMMR0 %1 exec align=16 ]
+   %else
+    [section VMMR0 %1 ]
+   %endif
+  %endmacro
+ %else
+  %macro VMMR0_CODE_SEG 1
+  [section VMMR0.%1 exec align=16 ]
+  %endmacro
+ %endif
 %endif
 
@@ -67,9 +96,15 @@
  [section .rdata$VMMR0.%1 align=1 ]
  %endmacro
- %define VMMR0_SEG_DEFINED
+
+ %macro VMMR0_CODE_SEG 1
+ [section .text$VMMR0.%1 align=16 ]
+ %endmacro
 %endif
 
-%ifndef VMMR0_SEG_DEFINED
- %error "VMMR0_SEG / ASM_FORMAT_xxx"
+%ifnmacro VMMR0_SEG
+ %error "VMMR0_CODE_SEG / ASM_FORMAT_xxx"
+%endif
+%ifnmacro VMMR0_CODE_SEG
+ %error "VMMR0_CODE_SEG / ASM_FORMAT_xxx"
 %endif
 
@@ -138,3 +173,12 @@
 
 
+
+;; @def PATCH_HLP_SEG
+; Set the output segment a special code segment for patch helpers (runs in ring-1 or ring-2).
+; @param    %1      The segment name.
+; @remark   Use BEGINCODE to switch back to the code segment.
+%macro BEGIN_PATCH_HLP_SEG 0
+VMMR0_CODE_SEG PatchHlp
+%endmacro
+
 %endif
Index: /trunk/src/VBox/VMM/VMMRC/VMMRC0.asm
===================================================================
--- /trunk/src/VBox/VMM/VMMRC/VMMRC0.asm	(revision 54762)
+++ /trunk/src/VBox/VMM/VMMRC/VMMRC0.asm	(revision 54763)
@@ -1,9 +1,9 @@
 ; $Id$
 ;; @file
-; VMMGC0 - The first object module in the link.
+; VMMRC0 - The first object module in the link.
 ;
 
 ;
-; Copyright (C) 2006-2010 Oracle Corporation
+; Copyright (C) 2006-2015 Oracle Corporation
 ;
 ; This file is part of VirtualBox Open Source Edition (OSE), as
@@ -34,3 +34,7 @@
 GLOBALNAME g_aTrap0eHandlers
 
+;;
+; Start the patch helper segment
+BEGIN_PATCH_HLP_SEG
+EXPORTEDNAME g_PatchHlpBegin
 
Index: /trunk/src/VBox/VMM/VMMRC/VMMRC99.asm
===================================================================
--- /trunk/src/VBox/VMM/VMMRC/VMMRC99.asm	(revision 54762)
+++ /trunk/src/VBox/VMM/VMMRC/VMMRC99.asm	(revision 54763)
@@ -1,8 +1,8 @@
 ; $Id$
 ;; @file
-; VMMGC99 - The last object module in the link.
+; VMMRC99 - The last object module in the link.
 ;
 
-; Copyright (C) 2006-2010 Oracle Corporation
+; Copyright (C) 2006-2015 Oracle Corporation
 ;
 ; This file is part of VirtualBox Open Source Edition (OSE), as
@@ -22,5 +22,5 @@
 VMMR0_SEG Trap0b
 GLOBALNAME g_aTrap0bHandlersEnd
-    dd 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    dd  0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 0
 
 
@@ -29,5 +29,5 @@
 VMMR0_SEG Trap0d
 GLOBALNAME g_aTrap0dHandlersEnd
-    dd 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    dd  0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 0
 
 
@@ -36,4 +36,11 @@
 VMMR0_SEG Trap0e
 GLOBALNAME g_aTrap0eHandlersEnd
-    dd 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    dd  0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 0
 
+
+;;
+; End the patch helper segment
+BEGIN_PATCH_HLP_SEG
+EXPORTEDNAME g_PatchHlpEnd
+    dd  0, 0, 0, 0,  0, 0, 0, 0,   0, 0, 0, 0,  0, 0, 0, 0
+
Index: /trunk/src/VBox/VMM/include/CPUMInternal.mac
===================================================================
--- /trunk/src/VBox/VMM/include/CPUMInternal.mac	(revision 54762)
+++ /trunk/src/VBox/VMM/include/CPUMInternal.mac	(revision 54763)
@@ -17,4 +17,23 @@
 
 %include "VBox/asmdefs.mac"
+
+;;
+; CPU info
+struc CPUMINFO
+    .cMsrRanges             resd 1                  ; uint32_t
+    .fMsrMask               resd 1                  ; uint32_t
+    .cCpuIdLeaves           resd 1                  ; uint32_t
+    .iFirstExtCpuIdLeaf     resd 1                  ; uint32_t
+    .uPadding               resd 1                  ; uint32_t
+    .enmUnknownCpuIdMethod  resd 1                  ; CPUMUNKNOWNCPUID
+    .DefCpuId               resb CPUMCPUID_size     ; CPUMCPUID
+    .uScalableBusFreq       resq 1                  ; uint64_t
+    .paMsrRangesR0          RTR0PTR_RES 1           ; R0PTRTYPE(PCPUMMSRRANGE)
+    .paCpuIdLeavesR0        RTR0PTR_RES 1           ; R0PTRTYPE(PCPUMCPUIDLEAF)
+    .paMsrRangesR3          RTR3PTR_RES 1           ; R3PTRTYPE(PCPUMMSRRANGE)
+    .paCpuIdLeavesR3        RTR3PTR_RES 1           ; R3PTRTYPE(PCPUMCPUIDLEAF)
+    .paMsrRangesRC          RTRCPTR_RES 1           ; RCPTRTYPE(PCPUMMSRRANGE)
+    .paCpuIdLeavesRC        RTRCPTR_RES 1           ; RCPTRTYPE(PCPUMCPUIDLEAF)
+endstruc
 
 
Index: /trunk/src/VBox/VMM/include/PATMA.h
===================================================================
--- /trunk/src/VBox/VMM/include/PATMA.h	(revision 54762)
+++ /trunk/src/VBox/VMM/include/PATMA.h	(revision 54763)
@@ -37,5 +37,5 @@
 #define PATM_CPUID_STD_PTR                      0xF1ABCD09  /**< Legacy, saved state only. */
 #define PATM_CPUID_EXT_PTR                      0xF1ABCD0a  /**< Legacy, saved state only. */
-#define PATM_CPUID_DEF_PTR                      0xF1ABCD0b
+#define PATM_CPUID_DEF_PTR                      0xF1ABCD0b  /**< Legacy, saved state only. */
 #define PATM_STACKBASE                          0xF1ABCD0c  /**< Stack to store our private patch return addresses */
 #define PATM_STACKBASE_GUEST                    0xF1ABCD0d  /**< Stack to store guest return addresses */
@@ -53,8 +53,9 @@
 #define PATM_CALL_RETURN_ADDR                   0xF1ABCD19
 #define PATM_CPUID_CENTAUR_PTR                  0xF1ABCD1a  /**< Legacy, saved state only. */
-#define PATM_CPUID_ARRAY_PTR                    0xF1ABCD1b
-#define PATM_CPUID_ARRAY_END_PTR                0xF1ABCD1c
-#define PATM_CPUID_ARRAY_ENTRY_SIZE             0xF1ABCD1d
-#define PATM_CPUID_UNKNOWN_METHOD               0xF1ABCD1e
+#define PATM_ASMFIX_REUSE_LATER_0               0xF1ABCD1b
+#define PATM_ASMFIX_REUSE_LATER_1               0xF1ABCD1c
+#define PATM_ASMFIX_REUSE_LATER_2               0xF1ABCD1d
+#define PATM_ASMFIX_REUSE_LATER_3               0xF1ABCD1e
+#define PATM_ASMFIX_HELPER_CPUM_CPUID           0xF1ABCD1f
 
 /* Anything larger doesn't require a fixup */
Index: /trunk/src/VBox/VMM/include/PATMInternal.h
===================================================================
--- /trunk/src/VBox/VMM/include/PATMInternal.h	(revision 54762)
+++ /trunk/src/VBox/VMM/include/PATMInternal.h	(revision 54763)
@@ -32,6 +32,8 @@
 /** @name Saved state version numbers.
  * @{ */
+/** New concept of helper code (for CPUID). */
+#define PATM_SAVED_STATE_VERSION                    58
 /** New fixup type FIXUP_ABSOLUTE_IN_PATCH_ASM_TMPL. */
-#define PATM_SAVED_STATE_VERSION                    57
+#define PATM_SAVED_STATE_VERSION_FORGET_THIS_ONE    57
 /** Uses normal structure serialization with markers and everything. */
 #define PATM_SAVED_STATE_VERSION_NO_RAW_MEM         56
@@ -118,4 +120,7 @@
  * like for FIXUP_ABSOLUTE_IN_PATCH_ASM_TMPL.  */
 #define FIXUP_CONSTANT_IN_PATCH_ASM_TMPL   4
+/** Relative call to a patch helper routine in VMMRC.  The source and destination
+ * address are set like for FIXUP_ABSOLUTE_IN_PATCH_ASM_TMPL.  */
+#define FIXUP_REL_HELPER_IN_PATCH_ASM_TMPL 5
 /** @} */
 
@@ -446,8 +451,17 @@
      * Used only during PATMR3Relocate(). */
     int32_t                     deltaReloc;
+
+    /** The ring-3 address of the PatchHlp segment (for PATMReadPatchCode). */
+    R3PTRTYPE(uint8_t *)        pbPatchHelpersR3;
+    /** The raw-mode address of the PatchHlp segment. */
+    RCPTRTYPE(uint8_t *)        pbPatchHelpersRC;
+    /** Size of the PatchHlp segment containing the callable helper code.   */
+    uint32_t                    cbPatchHelpers;
+
     /** GC PATM state pointer - HC pointer. */
     R3PTRTYPE(PPATMGCSTATE)     pGCStateHC;
     /** GC PATM state pointer - RC pointer. */
     RCPTRTYPE(PPATMGCSTATE)     pGCStateGC;
+
     /** PATM stack page for call instruction execution.
      * 2 parts: one for our private stack and one to store the original return
@@ -458,11 +472,13 @@
     /** GC pointer to CPUMCTX structure. */
     RCPTRTYPE(PCPUMCTX)         pCPUMCtxGC;
+
     /** GC statistics pointer. */
     RCPTRTYPE(PSTAMRATIOU32)    pStatsGC;
     /** HC statistics pointer. */
     R3PTRTYPE(PSTAMRATIOU32)    pStatsHC;
-    /* Current free index value (uPatchRun/uPatchTrap arrays). */
+
+    /** Current free index value (uPatchRun/uPatchTrap arrays). */
     uint32_t                    uCurrentPatchIdx;
-    /* Temporary counter for patch installation call depth. (in order not to go on forever) */
+    /** Temporary counter for patch installation call depth. (in order not to go on forever) */
     uint32_t                    ulCallDepth;
     /** Number of page lookup records. */
