PATM.cpp revision ec8b6be29ad79018de08646f1dd13e7721e74e50
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * PATM - Dynamic Guest OS Patching Manager
145f3def013dbea3281a655f726aba48cee34ea9vboxsync * NOTE: Never ever reuse patch memory!!
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * Copyright (C) 2006-2007 Oracle Corporation
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * available from http://www.virtualbox.org. This file is free software;
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * you can redistribute it and/or modify it under the terms of the GNU
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * General Public License (GPL) as published by the Free Software
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync/*******************************************************************************
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync* Header Files *
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync*******************************************************************************/
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync//#define PATM_REMOVE_PATCH_ON_TOO_MANY_TRAPS
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync//#define PATM_DISABLE_ALL
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * Refresh trampoline patch state.
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /** Pointer to the VM structure. */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /** The trampoline patch record. */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /** The new patch we want to jump to. */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync/*******************************************************************************
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync* Internal Functions *
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync*******************************************************************************/
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsyncstatic int patmDisableUnusablePatch(PVM pVM, RTRCPTR pInstrGC, RTRCPTR pConflictAddr, PPATCHINFO pPatch);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsyncstatic int patmActivateInt3Patch(PVM pVM, PPATCHINFO pPatch);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsyncstatic int patmDeactivateInt3Patch(PVM pVM, PPATCHINFO pPatch);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsyncstatic bool patmIsCommonIDTHandlerPatch(PVM pVM, RTRCPTR pInstrGC);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsyncstatic const char *PATMPatchType(PVM pVM, PPATCHINFO pPatch);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsyncstatic void patmPrintStat(PVM pVM, void *pvSample, char *pszBuf, size_t cchBuf);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync#define patmPatchHCPtr2PatchGCPtr(pVM, pHC) (pVM->patm.s.pPatchMemGC + (pHC - pVM->patm.s.pPatchMemHC))
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync#define patmPatchGCPtr2PatchHCPtr(pVM, pGC) (pVM->patm.s.pPatchMemHC + (pGC - pVM->patm.s.pPatchMemGC))
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsyncstatic DECLCALLBACK(int) RelocatePatches(PAVLOU32NODECORE pNode, void *pParam);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsyncstatic DECLCALLBACK(int) DisableAllPatches(PAVLOU32NODECORE pNode, void *pVM);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsyncstatic DECLCALLBACK(int) patmr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsyncstatic DECLCALLBACK(int) patmr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync/** Command descriptors. */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, fFlags, pfnHandler pszSyntax, ....pszDescription */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync { "patmon", 0, 0, NULL, 0, 0, patmr3CmdOn, "", "Enable patching." },
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync { "patmoff", 0, 0, NULL, 0, 0, patmr3CmdOff, "", "Disable patching." },
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync/* Don't want to break saved states, so put it here as a global variable. */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsyncstatic unsigned int cIDTHandlersDisabled = 0;
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * Initializes the PATM.
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * @returns VBox status code.
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * @param pVM The VM to operate on.
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync Log(("PATMR3Init: Patch record size %d\n", sizeof(PATCHINFO)));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /* These values can't change as they are hardcoded in patch code (old saved states!) */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync AssertCompile(VMCPU_FF_INTERRUPT_APIC == RT_BIT_32(0));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync AssertCompile(VMCPU_FF_INTERRUPT_PIC == RT_BIT_32(1));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync AssertReleaseMsg(PATMInterruptFlag == (VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST),
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync ("Interrupt flags out of sync!! PATMInterruptFlag=%#x expected %#x. broken assembler?\n", PATMInterruptFlag, VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /* Allocate patch memory and GC patch state memory. */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /* Add another page in case the generated code is much larger than expected. */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /** @todo bad safety precaution */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync rc = MMR3HyperAllocOnceNoRel(pVM, PATCH_MEMORY_SIZE + PAGE_SIZE + PATM_STACK_TOTAL_SIZE + PAGE_SIZE + PATM_STAT_MEMSIZE, PAGE_SIZE, MM_TAG_PATM, (void **)&pVM->patm.s.pPatchMemHC);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.pPatchMemGC = MMHyperR3ToRC(pVM, pVM->patm.s.pPatchMemHC);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /* PATM stack page for call instruction execution. (2 parts: one for our private stack and one to store the original return address */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.pGCStackHC = (RTRCPTR *)(pVM->patm.s.pPatchMemHC + PATCH_MEMORY_SIZE + PAGE_SIZE);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.pGCStackGC = MMHyperR3ToRC(pVM, pVM->patm.s.pGCStackHC);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * Hypervisor memory for GC status data (read/write)
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * Note1: This is non-critical data; if trashed by the guest, then it will only cause problems for itself
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * Note2: This doesn't really belong here, but we need access to it for relocation purposes
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync Assert(sizeof(PATMGCSTATE) < PAGE_SIZE); /* Note: hardcoded dependencies on this exist. */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.pGCStateHC = (PPATMGCSTATE)((uint8_t *)pVM->patm.s.pGCStackHC + PATM_STACK_TOTAL_SIZE);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.pGCStateGC = MMHyperR3ToRC(pVM, pVM->patm.s.pGCStateHC);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /* Hypervisor memory for patch statistics */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.pStatsHC = (PSTAMRATIOU32)((uint8_t *)pVM->patm.s.pGCStateHC + PAGE_SIZE);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.pStatsGC = MMHyperR3ToRC(pVM, pVM->patm.s.pStatsHC);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /* Memory for patch lookup trees. */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync rc = MMHyperAlloc(pVM, sizeof(*pVM->patm.s.PatchLookupTreeHC), 0, MM_TAG_PATM, (void **)&pVM->patm.s.PatchLookupTreeHC);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.PatchLookupTreeGC = MMHyperR3ToRC(pVM, pVM->patm.s.PatchLookupTreeHC);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /* Check CFGM option. */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync rc = CFGMR3QueryBool(CFGMR3GetRoot(pVM), "PATMEnabled", &pVM->fPATMEnabled);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * Register save and load state notifiers.
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync rc = SSMR3RegisterInternal(pVM, "PATM", 0, PATM_SSM_VERSION, sizeof(pVM->patm.s) + PATCH_MEMORY_SIZE + PAGE_SIZE + PATM_STACK_TOTAL_SIZE + PAGE_SIZE,
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * Debugger commands.
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync static bool s_fRegisteredCmds = false;
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync int rc2 = DBGCRegisterCommands(&g_aCmds[0], RT_ELEMENTS(g_aCmds));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatNrOpcodeRead, STAMTYPE_COUNTER, "/PATM/OpcodeBytesRead", STAMUNIT_OCCURENCES, "The number of opcode bytes read by the recompiler.");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatPATMMemoryUsed,STAMTYPE_COUNTER, "/PATM/MemoryUsed", STAMUNIT_OCCURENCES, "The amount of hypervisor heap used for patches.");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatDisabled, STAMTYPE_COUNTER, "/PATM/Patch/Disabled", STAMUNIT_OCCURENCES, "Number of times patches were disabled.");
0d9f0399d4356c471af5e68c30b51c73f7909487vboxsync STAM_REG(pVM, &pVM->patm.s.StatEnabled, STAMTYPE_COUNTER, "/PATM/Patch/Enabled", STAMUNIT_OCCURENCES, "Number of times patches were enabled.");
0d9f0399d4356c471af5e68c30b51c73f7909487vboxsync STAM_REG(pVM, &pVM->patm.s.StatDirty, STAMTYPE_COUNTER, "/PATM/Patch/Dirty", STAMUNIT_OCCURENCES, "Number of times patches were marked dirty.");
0d9f0399d4356c471af5e68c30b51c73f7909487vboxsync STAM_REG(pVM, &pVM->patm.s.StatUnusable, STAMTYPE_COUNTER, "/PATM/Patch/Unusable", STAMUNIT_OCCURENCES, "Number of unusable patches (conflicts).");
0d9f0399d4356c471af5e68c30b51c73f7909487vboxsync STAM_REG(pVM, &pVM->patm.s.StatInstalled, STAMTYPE_COUNTER, "/PATM/Patch/Installed", STAMUNIT_OCCURENCES, "Number of installed patches.");
0d9f0399d4356c471af5e68c30b51c73f7909487vboxsync STAM_REG(pVM, &pVM->patm.s.StatInt3Callable, STAMTYPE_COUNTER, "/PATM/Patch/Int3Callable", STAMUNIT_OCCURENCES, "Number of cli patches turned into int3 patches.");
f862648ac24687a4e6c0327b9f728fa93ad69a62vboxsync STAM_REG(pVM, &pVM->patm.s.StatInt3BlockRun, STAMTYPE_COUNTER, "/PATM/Patch/Run/Int3", STAMUNIT_OCCURENCES, "Number of times an int3 block patch was executed.");
f862648ac24687a4e6c0327b9f728fa93ad69a62vboxsync STAMR3RegisterF(pVM, &pVM->patm.s.pGCStateHC->uPatchCalls, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Patch/Run/Normal");
f862648ac24687a4e6c0327b9f728fa93ad69a62vboxsync STAM_REG(pVM, &pVM->patm.s.StatInstalledFunctionPatches, STAMTYPE_COUNTER, "/PATM/Patch/Installed/Function", STAMUNIT_OCCURENCES, "Number of installed function duplication patches.");
f862648ac24687a4e6c0327b9f728fa93ad69a62vboxsync STAM_REG(pVM, &pVM->patm.s.StatInstalledTrampoline, STAMTYPE_COUNTER, "/PATM/Patch/Installed/Trampoline", STAMUNIT_OCCURENCES, "Number of installed trampoline patches.");
f862648ac24687a4e6c0327b9f728fa93ad69a62vboxsync STAM_REG(pVM, &pVM->patm.s.StatInstalledJump, STAMTYPE_COUNTER, "/PATM/Patch/Installed/Jump", STAMUNIT_OCCURENCES, "Number of installed jump patches.");
f862648ac24687a4e6c0327b9f728fa93ad69a62vboxsync STAM_REG(pVM, &pVM->patm.s.StatOverwritten, STAMTYPE_COUNTER, "/PATM/Patch/Overwritten", STAMUNIT_OCCURENCES, "Number of overwritten patches.");
f862648ac24687a4e6c0327b9f728fa93ad69a62vboxsync STAM_REG(pVM, &pVM->patm.s.StatFixedConflicts,STAMTYPE_COUNTER, "/PATM/Patch/ConflictFixed", STAMUNIT_OCCURENCES, "Number of fixed conflicts.");
f862648ac24687a4e6c0327b9f728fa93ad69a62vboxsync STAM_REG(pVM, &pVM->patm.s.StatFlushed, STAMTYPE_COUNTER, "/PATM/Patch/Flushed", STAMUNIT_OCCURENCES, "Number of flushes of pages with patch jumps.");
f862648ac24687a4e6c0327b9f728fa93ad69a62vboxsync STAM_REG(pVM, &pVM->patm.s.StatMonitored, STAMTYPE_COUNTER, "/PATM/Patch/Monitored", STAMUNIT_OCCURENCES, "Number of patches in monitored patch pages.");
f862648ac24687a4e6c0327b9f728fa93ad69a62vboxsync STAM_REG(pVM, &pVM->patm.s.StatPageBoundaryCrossed, STAMTYPE_COUNTER, "/PATM/Patch/BoundaryCross", STAMUNIT_OCCURENCES, "Number of refused patches due to patch jump crossing page boundary.");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatHandleTrap, STAMTYPE_PROFILE, "/PATM/HandleTrap", STAMUNIT_TICKS_PER_CALL, "Profiling of PATMR3HandleTrap");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatPushTrap, STAMTYPE_COUNTER, "/PATM/HandleTrap/PushWP", STAMUNIT_OCCURENCES, "Number of traps due to monitored stack pages.");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatSwitchBack, STAMTYPE_COUNTER, "/PATM/SwitchBack", STAMUNIT_OCCURENCES, "Switch back to original guest code when IF=1 & executing PATM instructions");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatSwitchBackFail,STAMTYPE_COUNTER, "/PATM/SwitchBackFail", STAMUNIT_OCCURENCES, "Failed switch back to original guest code when IF=1 & executing PATM instructions");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatDuplicateREQFailed, STAMTYPE_COUNTER, "/PATM/Function/DupREQ/Failed", STAMUNIT_OCCURENCES, "Nr of failed PATMR3DuplicateFunctionRequest calls");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatDuplicateREQSuccess, STAMTYPE_COUNTER, "/PATM/Function/DupREQ/Success", STAMUNIT_OCCURENCES, "Nr of successful PATMR3DuplicateFunctionRequest calls");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatDuplicateUseExisting,STAMTYPE_COUNTER, "/PATM/Function/DupREQ/UseExist", STAMUNIT_OCCURENCES, "Nr of successful PATMR3DuplicateFunctionRequest calls when using an existing patch");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatFunctionLookupInsert, STAMTYPE_COUNTER, "/PATM/Function/Lookup/Insert", STAMUNIT_OCCURENCES, "Nr of successful function address insertions");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatFunctionLookupReplace, STAMTYPE_COUNTER, "/PATM/Function/Lookup/Replace", STAMUNIT_OCCURENCES, "Nr of successful function address replacements");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatU32FunctionMaxSlotsUsed, STAMTYPE_U32_RESET,"/PATM/Function/Lookup/MaxSlots", STAMUNIT_OCCURENCES, "Maximum nr of lookup slots used in all call patches");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatFunctionFound, STAMTYPE_COUNTER, "/PATM/Function/Found", STAMUNIT_OCCURENCES, "Nr of successful function patch lookups in GC");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatFunctionNotFound, STAMTYPE_COUNTER, "/PATM/Function/NotFound", STAMUNIT_OCCURENCES, "Nr of failed function patch lookups in GC");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchWrite, STAMTYPE_PROFILE, "/PATM/Write/Handle", STAMUNIT_TICKS_PER_CALL, "Profiling of PATMR3PatchWrite");
d35226805940eec624c095ff8232486a46b9232fvboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchWriteDetect, STAMTYPE_PROFILE, "/PATM/Write/Detect", STAMUNIT_TICKS_PER_CALL, "Profiling of PATMIsWriteToPatchPage");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchWriteInterpreted, STAMTYPE_COUNTER, "/PATM/Write/Interpreted/Success", STAMUNIT_OCCURENCES, "Nr of interpreted patch writes.");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchWriteInterpretedFailed, STAMTYPE_COUNTER, "/PATM/Write/Interpreted/Failed", STAMUNIT_OCCURENCES, "Nr of failed interpreted patch writes.");
b0ef8e9a92e464839d4cb902775788a5fb738451vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchRefreshSuccess, STAMTYPE_COUNTER, "/PATM/Refresh/Success", STAMUNIT_OCCURENCES, "Successful patch refreshes");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchRefreshFailed, STAMTYPE_COUNTER, "/PATM/Refresh/Failure", STAMUNIT_OCCURENCES, "Failed patch refreshes");
db2645ffc7122bff72559ef7a5cd83950a82f0f5vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchPageInserted, STAMTYPE_COUNTER, "/PATM/Page/Inserted", STAMUNIT_OCCURENCES, "Nr of inserted guest pages that were patched");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchPageRemoved, STAMTYPE_COUNTER, "/PATM/Page/Removed", STAMUNIT_OCCURENCES, "Nr of removed guest pages that were patched");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatInstrDirty, STAMTYPE_COUNTER, "/PATM/Instr/Dirty/Detected", STAMUNIT_OCCURENCES, "Number of times instructions were marked dirty.");
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync STAM_REG(pVM, &pVM->patm.s.StatInstrDirtyGood, STAMTYPE_COUNTER, "/PATM/Instr/Dirty/Corrected", STAMUNIT_OCCURENCES, "Number of times instructions were marked dirty and corrected later on.");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatInstrDirtyBad, STAMTYPE_COUNTER, "/PATM/Instr/Dirty/Failed", STAMUNIT_OCCURENCES, "Number of times instructions were marked dirty and we were not able to correct them.");
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync STAM_REG(pVM, &pVM->patm.s.StatSysEnter, STAMTYPE_COUNTER, "/PATM/Emul/SysEnter", STAMUNIT_OCCURENCES, "Number of times sysenter was emulated.");
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync STAM_REG(pVM, &pVM->patm.s.StatSysExit, STAMTYPE_COUNTER, "/PATM/Emul/SysExit" , STAMUNIT_OCCURENCES, "Number of times sysexit was emulated.");
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync STAM_REG(pVM, &pVM->patm.s.StatEmulIret, STAMTYPE_COUNTER, "/PATM/Emul/Iret/Success", STAMUNIT_OCCURENCES, "Number of times iret was emulated.");
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync STAM_REG(pVM, &pVM->patm.s.StatEmulIretFailed, STAMTYPE_COUNTER, "/PATM/Emul/Iret/Failed", STAMUNIT_OCCURENCES, "Number of times iret was emulated.");
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync STAM_REG(pVM, &pVM->patm.s.StatGenRet, STAMTYPE_COUNTER, "/PATM/Gen/Ret" , STAMUNIT_OCCURENCES, "Number of generated ret instructions.");
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync STAM_REG(pVM, &pVM->patm.s.StatGenRetReused, STAMTYPE_COUNTER, "/PATM/Gen/RetReused" , STAMUNIT_OCCURENCES, "Number of reused ret instructions.");
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync STAM_REG(pVM, &pVM->patm.s.StatGenCall, STAMTYPE_COUNTER, "/PATM/Gen/Call", STAMUNIT_OCCURENCES, "Number of generated call instructions.");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatGenJump, STAMTYPE_COUNTER, "/PATM/Gen/Jmp" , STAMUNIT_OCCURENCES, "Number of generated indirect jump instructions.");
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync STAM_REG(pVM, &pVM->patm.s.StatGenPopf, STAMTYPE_COUNTER, "/PATM/Gen/Popf" , STAMUNIT_OCCURENCES, "Number of generated popf instructions.");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync STAM_REG(pVM, &pVM->patm.s.StatCheckPendingIRQ, STAMTYPE_COUNTER, "/PATM/GC/CheckIRQ" , STAMUNIT_OCCURENCES, "Number of traps that ask to check for pending irqs.");
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync#endif /* VBOX_WITH_STATISTICS */
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync Log(("PATMCallRecord.size %d\n", PATMCallRecord.size));
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync Log(("PATMCallIndirectRecord.size %d\n", PATMCallIndirectRecord.size));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync Log(("PATMRetRecord.size %d\n", PATMRetRecord.size));
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync Log(("PATMJumpIndirectRecord.size %d\n", PATMJumpIndirectRecord.size));
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync Log(("PATMPopf32Record.size %d\n", PATMPopf32Record.size));
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync Log(("PATMIretRecord.size %d\n", PATMIretRecord.size));
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync Log(("PATMStiRecord.size %d\n", PATMStiRecord.size));
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync Log(("PATMCheckIFRecord.size %d\n", PATMCheckIFRecord.size));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * Finalizes HMA page attributes.
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * @returns VBox status code.
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * @param pVM The VM handle.
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync /* The GC state, stack and statistics must be read/write for the guest (supervisor only of course). */
690f529f58cd70ffd9f4271edd7c2540651dccccvboxsync int rc = PGMMapSetPage(pVM, pVM->patm.s.pGCStateGC, PAGE_SIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
690f529f58cd70ffd9f4271edd7c2540651dccccvboxsync Log(("PATMR3InitFinalize: PGMMapSetPage failed with %Rrc!!\n", rc));
690f529f58cd70ffd9f4271edd7c2540651dccccvboxsync rc = PGMMapSetPage(pVM, pVM->patm.s.pGCStackGC, PATM_STACK_TOTAL_SIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
690f529f58cd70ffd9f4271edd7c2540651dccccvboxsync Log(("PATMR3InitFinalize: PGMMapSetPage failed with %Rrc!!\n", rc));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync rc = PGMMapSetPage(pVM, pVM->patm.s.pStatsGC, PATM_STAT_MEMSIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync Log(("PATMR3InitFinalize: PGMMapSetPage failed with %Rrc!!\n", rc));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * (Re)initializes PATM
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * @param pVM The VM.
5f0e6ed665965d99bd03b7c2906771b446cff7dbvboxsync * Assert alignment and sizes.
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync AssertRelease(sizeof(pVM->patm.s) <= sizeof(pVM->patm.padding));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * Setup any fixed pointers and offsets.
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync#ifndef RT_ARCH_AMD64 /* would be nice if this was changed everywhere. was driving me crazy on AMD64. */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync AssertReleaseMsg(pVM->patm.s.pGCStateGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pGCStateGC));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync Log(("Patch memory allocated at %p - %RRv\n", pVM->patm.s.pPatchMemHC, pVM->patm.s.pPatchMemGC));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync AssertReleaseMsg(pVM->patm.s.pGCStackGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pGCStackGC));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.pGCStateHC->fPIF = 1; /* PATM Interrupt Flag */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync memset(pVM->patm.s.pStatsHC, 0, PATM_STAT_MEMSIZE);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync AssertReleaseMsg(pVM->patm.s.pStatsGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pStatsGC));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync Assert(pVM->patm.s.pPatchMemGC == MMHyperR3ToRC(pVM, pVM->patm.s.pPatchMemHC));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync memset(pVM->patm.s.pPatchMemHC, 0, PATCH_MEMORY_SIZE);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync AssertReleaseMsg(pVM->patm.s.pPatchMemGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pPatchMemHC));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /* Needed for future patching of sldt/sgdt/sidt/str etc. */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.pCPUMCtxGC = VM_RC_ADDR(pVM, CPUMQueryGuestCtxPtr(VMMGetCpu(pVM)));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync Assert(pVM->patm.s.PatchLookupTreeGC == MMHyperR3ToRC(pVM, pVM->patm.s.PatchLookupTreeHC));
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * (Re)Initialize PATM structure
145f3def013dbea3281a655f726aba48cee34ea9vboxsync Assert(!pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync Assert(!pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage);
145f3def013dbea3281a655f726aba48cee34ea9vboxsync pVM->patm.s.offPatchMem = 16; /* don't start with zero here */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.uCurrentPatchIdx = 1; /* Index zero is a dummy */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /* Lowest and highest patched instruction */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr = 0;
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage = 0;
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /* Generate all global functions to be used by future patches. */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /* We generate a fake patch in order to use the existing code for relocation. */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync rc = MMHyperAlloc(pVM, sizeof(PATMPATCHREC), 0, MM_TAG_PATM_PATCH, (void **)&pVM->patm.s.pGlobalPatchRec);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.pGlobalPatchRec->patch.flags = PATMFL_GLOBAL_FUNCTIONS;
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.pGlobalPatchRec->patch.uState = PATCH_ENABLED;
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.pGlobalPatchRec->patch.pPatchBlockOffset = pVM->patm.s.offPatchMem;
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync rc = patmPatchGenGlobalFunctions(pVM, &pVM->patm.s.pGlobalPatchRec->patch);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /* Update free pointer in patch memory. */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.offPatchMem += pVM->patm.s.pGlobalPatchRec->patch.uCurPatchOffset;
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync /* Round to next 8 byte boundary. */
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync pVM->patm.s.offPatchMem = RT_ALIGN_32(pVM->patm.s.offPatchMem, 8);
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * Applies relocations to data and code managed by this
10b932b145ceeaa0e8edf111bb0e46dbd0750e14vboxsync * component. This function will be called at init and
if (delta)
return VINF_SUCCESS;
PPATMPATCHREC pPatchRec = (PPATMPATCHREC)RTAvloU32RemoveBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, 0, true);
if (pPatchRec)
return rc;
if (size == 0)
return VERR_INVALID_PARAMETER;
* Trap/interrupt handler typically call common code on entry. Which might already have patches inserted.
* As we currently don't support calling patch code from patch code, we'll let it read the original opcode bytes instead.
for (int i=0;i<orgsize;i++)
pSrc++;
pDest++;
size--;
if (size == 0)
return VINF_SUCCESS;
#ifdef VBOX_STRICT
return VINF_SUCCESS;
#ifdef LOG_ENABLED
bool disret;
int rc;
#ifdef LOG_ENABLED
disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pPatch->patch.pPrivInstrGC, NULL, &opsize, szOutput, PATMREAD_RAWCODE);
if (pRec == 0)
case FIXUP_ABSOLUTE:
Log(("Absolute fixup at %RRv %RHv -> %RHv at %RRv\n", pRec->pSource, *(RTRCUINTPTR *)pRec->pRelocPos, *(RTRCINTPTR*)pRec->pRelocPos + delta, pRec->pRelocPos));
rc = PGMPhysSimpleReadGCPtr(VMMGetCpu0(pVM), curInstr, pPatch->patch.pPrivInstrGC, pPatch->patch.cbPrivInstr);
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_ALL, pPage, pPage + (PAGE_SIZE - 1) /* inclusive! */, 0, patmVirtPageHandler, "PATMGCMonitorPage", 0, "PATMMonitorPatchJump");
rc = PGMPhysSimpleDirtyWriteGCPtr(VMMGetCpu0(pVM), pRec->pSource, curInstr, pPatch->patch.cbPrivInstr);
case FIXUP_REL_JMPTOPATCH:
#if 0 /** @todo '*(int32_t*)pRec->pRelocPos' crashes on restore of an XP VM here. pRelocPos=0x8000dbe2180a (bird) */
Log(("Relative fixup (g2p) %08X -> %08X at %08X (source=%08x, target=%08x)\n", *(int32_t*)pRec->pRelocPos, displ, pRec->pRelocPos, pRec->pSource, pRec->pDest));
Log(("Relative fixup (g2p) ???????? -> %08X at %08X (source=%08x, target=%08x)\n", displ, pRec->pRelocPos, pRec->pSource, pRec->pDest));
rc = PGMPhysSimpleReadGCPtr(VMMGetCpu0(pVM), temp, pPatch->patch.pPrivInstrGC, pPatch->patch.cbPatchJump);
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_ALL, pPage, pPage + (PAGE_SIZE - 1) /* inclusive! */, 0, patmVirtPageHandler, "PATMGCMonitorPage", 0, "PATMMonitorPatchJump");
Log(("Skip the guest jump to patch code for this disabled patch %RGv - %08X\n", pPatch->patch.pPrivInstrGC, pRec->pRelocPos));
case FIXUP_REL_JMPTOGUEST:
Log(("Relative fixup (p2g) %08X -> %08X at %08X (source=%08x, target=%08x)\n", *(int32_t*)pRec->pRelocPos, displ, pRec->pRelocPos, pRec->pSource, pRec->pDest));
return VERR_INVALID_PARAMETER;
#ifdef LOG_ENABLED
disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pPatch->patch.pPrivInstrGC, NULL, &opsize, szOutput, PATMREAD_RAWCODE);
DECLCALLBACK(int) patmVirtPageHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
return VINF_PGM_HANDLER_DO_DEFAULT;
#ifdef VBOX_WITH_DEBUGGER
#ifdef VBOX_WITH_DEBUGGER
if (pcb)
if (pcb)
return (pAddrHC >= pVM->patm.s.pPatchMemHC && pAddrHC < pVM->patm.s.pPatchMemHC + pVM->patm.s.cbPatchMem) ? true : false;
return VINF_SUCCESS;
if (pVM->patm.s.pPatchMemGC <= pAddrGC && pVM->patm.s.pPatchMemGC + pVM->patm.s.cbPatchMem > pAddrGC)
return NULL;
R3PTRTYPE(uint8_t *) PATMGCVirtToHCVirt(PVM pVM, PPATMP2GLOOKUPREC pCacheRec, RCPTRTYPE(uint8_t *) pGCPtr)
int rc;
AssertMsg(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("MMR3PhysGCVirt2HCVirtEx failed for %08X\n", pGCPtr));
return NULL;
return pHCPtr;
unsigned nrJumpRecs = 0;
if (pRec == 0)
nrJumpRecs++;
if (!pFunctionRec)
int rc;
if (pBranchTargetGC == 0)
AssertMsgFailed(("patmr3SetBranchTargets: patmGuestGCPtrToPatchGCPtr failed for %08X\n", pRec->pTargetGC));
return VERR_PATCHING_REFUSED;
Log(("Set branch target %d to %08X : %08x - (%08x + %d + %d)\n", nrJumpRecs, displ, pBranchTargetGC, pInstrGC, pRec->offDispl, sizeof(RTRCPTR)));
return VINF_SUCCESS;
if (pRec)
/** @note Be extremely careful with this function. Make absolutely sure the guest address is correct! (to avoid executing instructions twice!) */
void patmr3AddP2GLookupRecord(PVM pVM, PPATCHINFO pPatch, uint8_t *pPatchInstrHC, RTRCPTR pInstrGC, PATM_LOOKUP_TYPE enmType, bool fDirty)
bool ret;
uint32_t PatchOffset = pPatchInstrHC - pVM->patm.s.pPatchMemHC; /* Offset in memory reserved for PATM. */
#ifdef VBOX_STRICT
pPatchToGuestRec = (PRECPATCHTOGUEST)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(RECPATCHTOGUEST) + sizeof(RECGUESTTOPATCH));
if (!pGuestToPatchRec)
uint32_t PatchOffset = pPatchInstrGC - pVM->patm.s.pPatchMemGC; /* Offset in memory reserved for PATM. */
if (pPatchToGuestRec)
static int patmAnalyseBlockCallback(PVM pVM, DISCPUSTATE *pCpu, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, PPATMP2GLOOKUPREC pCacheRec)
bool fIllegalInstr = false;
//- no jumps in the instructions following cli (4+ bytes; enough for the replacement jump (5 bytes))
fIllegalInstr = true;
|| (OP_PARM_VTYPE(pCpu->pCurInstr->param1) != OP_PARM_J && !(pPatch->flags & PATMFL_SUPPORT_INDIRECT_CALLS))
fIllegalInstr = true;
/* An unconditional (short) jump right after a cli is a potential problem; we will overwrite whichever function comes afterwards */
if (pCurInstrGC > pPatch->pPrivInstrGC && pCurInstrGC + pCpu->opsize < pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32) /* hardcoded patch jump size; cbPatchJump is still zero */
Log(("Dangerous unconditional jump ends in our generated patch jump!! (%x vs %x)\n", pCurInstrGC, pPatch->pPrivInstrGC));
fIllegalInstr = true;
fIllegalInstr = true;
if (pCpu->pCurInstr->opcode == OP_INT3 || pCpu->pCurInstr->opcode == OP_INT || pCpu->pCurInstr->opcode == OP_INTO)
fIllegalInstr = true;
return VINF_SUCCESS;
case OP_SYSEXIT:
case OP_SYSENTER:
case OP_ILLUD2:
return VINF_SUCCESS;
case OP_STI:
case OP_POPF:
if (pCurInstrGC > pPatch->pPrivInstrGC && pCurInstrGC < pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32) /* hardcoded patch jump size; cbPatchJump is still zero */
return VERR_PATCHING_REFUSED;
return VINF_SUCCESS;
Log(("WARNING: End of block reached, but we need to duplicate some extra instruction to avoid a conflict with the patch jump\n"));
return VINF_SUCCESS;
case OP_IRET:
case OP_CPUID:
case OP_CALL:
case OP_JMP:
// If single instruction patch, we've copied enough instructions *and* the current instruction is not a relative jump
if ((pPatch->flags & PATMFL_CHECK_SIZE) && pPatch->cbPatchBlockSize > SIZEOF_NEARJUMP32 && !(pCpu->pCurInstr->optype & OPTYPE_RELATIVE_CONTROLFLOW))
return VINF_SUCCESS;
return VWRN_CONTINUE_ANALYSIS;
static int patmAnalyseFunctionCallback(PVM pVM, DISCPUSTATE *pCpu, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, PPATMP2GLOOKUPREC pCacheRec)
bool fIllegalInstr = false;
Log(("Code block too big (%x) for function patch at %RRv!!\n", pPatch->cbPatchBlockSize, pCurInstrGC));
fIllegalInstr = true;
|| (OP_PARM_VTYPE(pCpu->pCurInstr->param1) != OP_PARM_J && !(pPatch->flags & PATMFL_SUPPORT_INDIRECT_CALLS))
fIllegalInstr = true;
fIllegalInstr = true;
if (pCpu->pCurInstr->opcode == OP_INT3 || pCpu->pCurInstr->opcode == OP_INT || pCpu->pCurInstr->opcode == OP_INTO)
fIllegalInstr = true;
return VERR_PATCHING_REFUSED;
return VINF_SUCCESS;
case OP_ILLUD2:
return VINF_SUCCESS;
case OP_IRET:
case OP_RETN:
return VINF_SUCCESS;
case OP_POPF:
case OP_STI:
return VWRN_CONTINUE_ANALYSIS;
return VWRN_CONTINUE_ANALYSIS;
return VWRN_CONTINUE_ANALYSIS;
static int patmRecompileCallback(PVM pVM, DISCPUSTATE *pCpu, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, PPATMP2GLOOKUPREC pCacheRec)
&& !(pPatch->flags & PATMFL_RECOMPILE_NEXT)) /* do not do this when the next instruction *must* be executed! */
return rc;
patmr3AddP2GLookupRecord(pVM, pPatch, PATCHCODE_PTR_HC(pPatch) + pPatch->uCurPatchOffset, pCurInstrGC, PATM_LOOKUP_BOTHDIR);
goto end;
/* For our first attempt, we'll handle only simple relative jumps (immediate offset coded in instruction).
if (pTargetGC == 0)
return VERR_PATCHING_REFUSED;
goto end;
rc = patmPatchGenRelJump(pVM, pPatch, pTargetGC, pCpu->pCurInstr->opcode, !!(pCpu->prefix & PREFIX_OPSIZE));
goto end;
case OP_CLI:
/* If a cli is found while duplicating instructions for another patch, then it's of vital importance to continue
Log(("cli instruction found in other instruction patch block; force it to continue & find an exit point\n"));
case OP_MOV:
&& (pCpu->param1.flags & (USE_REG_GEN32|USE_REG_GEN16))) /** @todo memory operand must in theory be handled too */
goto duplicate_instr;
case OP_POP:
goto duplicate_instr;
case OP_STI:
fInhibitIRQInstr = true;
unsigned opsize;
int disret;
AssertFailed();
return VERR_PATCHING_REFUSED;
if (disret == false)
return VERR_PATCHING_REFUSED;
case OP_POPF:
/* Note: keep IOPL in mind when changing any of this!! (see comments in PATMA.asm, PATMPopf32Replacement) */
fGenerateJmpBack = false;
rc = patmPatchGenPopf(pVM, pPatch, pCurInstrGC + pCpu->opsize, !!(pCpu->prefix & PREFIX_OPSIZE), fGenerateJmpBack);
if (fGenerateJmpBack == false)
case OP_PUSHF:
case OP_PUSH:
goto duplicate_instr;
case OP_IRET:
case OP_ILLUD2:
case OP_CPUID:
case OP_STR:
case OP_SLDT:
case OP_SGDT:
case OP_SIDT:
case OP_RETN:
case OP_SYSEXIT:
case OP_CALL:
/* In interrupt gate handlers it's possible to encounter jumps or calls when IF has been enabled again.
* In that case we'll jump to the original instruction and continue from there. Otherwise an int 3 is executed.
goto gen_illegal_instr;
case OP_JMP:
/* In interrupt gate handlers it's possible to encounter jumps or calls when IF has been enabled again.
* In that case we'll jump to the original instruction and continue from there. Otherwise an int 3 is executed.
goto gen_illegal_instr;
case OP_INT3:
case OP_INT:
case OP_INTO:
goto gen_illegal_instr;
case OP_MOV_DR:
/* Note: currently we let DRx writes cause a trap d; our trap handler will decide to interpret it or not. */
goto duplicate_instr;
case OP_MOV_CR:
/* Note: currently we let CRx writes cause a trap d; our trap handler will decide to interpret it or not. */
goto duplicate_instr;
end:
if ( !fInhibitIRQInstr
int rc2;
Log(("patmRecompileCallback: generate jump back to guest (%RRv) after fused instruction\n", pNextInstrGC));
// If single instruction patch, we've copied enough instructions *and* the current instruction is not a relative jump
&& !(pPatch->flags & PATMFL_RECOMPILE_NEXT) /* do not do this when the next instruction *must* be executed! */
Log(("patmRecompileCallback: end found for single instruction patch at %RRv opsize %d\n", pNextInstrGC, pCpu->opsize));
return rc;
#ifdef LOG_ENABLED
if (pRec)
int patmr3DisasmCallback(PVM pVM, DISCPUSTATE *pCpu, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, PPATMP2GLOOKUPREC pCacheRec)
return VINF_SUCCESS;
return VWRN_CONTINUE_ANALYSIS;
/* the indirect call patch contains an 0xF/0xB illegal instr to call for assistance; check for this and continue */
return VWRN_CONTINUE_ANALYSIS;
return VINF_SUCCESS;
return VINF_SUCCESS;
return VWRN_CONTINUE_ANALYSIS;
* Disassembles the code stream until the callback function detects a failure or decides everything is acceptable
int patmr3DisasmCode(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, PFN_PATMR3ANALYSE pfnPATMR3Disasm, PPATMP2GLOOKUPREC pCacheRec)
bool disret;
goto end;
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pCurInstrGC, pCurInstrHC, &opsize, szOutput, PATMREAD_RAWCODE);
goto end;
if (disret == false)
goto end;
/* For our first attempt, we'll handle only simple relative jumps and calls (immediate offset coded in instruction) */
if (pTargetGC == 0)
goto end;
goto end;
goto end;
end:
return rc;
* Disassembles the code stream until the callback function detects a failure or decides everything is acceptable
int patmr3DisasmCodeStream(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, PFN_PATMR3ANALYSE pfnPATMR3Disasm, PPATMP2GLOOKUPREC pCacheRec)
return rc;
* @note also checks for patch hints to make sure they can never be enabled if a conflict is present.
PPATCHINFO pTargetPatch = PATMFindActivePatchByEntrypoint(pVM, pConflictGC, true /* include patch hints */);
if (pTargetPatch)
return VERR_PATCH_NO_CONFLICT;
* Recompile the code stream until the callback function detects a failure or decides everything is acceptable
static int patmRecompileCodeStream(PVM pVM, RCPTRTYPE(uint8_t *) pInstrGC, RCPTRTYPE(uint8_t *) pCurInstrGC, PFN_PATMR3ANALYSE pfnPATMR3Recompile, PPATMP2GLOOKUPREC pCacheRec)
bool disret;
#ifdef LOG_ENABLED
goto end;
#ifdef LOG_ENABLED
if (disret == false)
patmr3AddP2GLookupRecord(pVM, pPatch, PATCHCODE_PTR_HC(pPatch) + pPatch->uCurPatchOffset, pCurInstrGC, PATM_LOOKUP_BOTHDIR);
goto end;
/* If irqs are inhibited because of the current instruction, then we must make sure the next one is executed! */
/* Certain instructions (e.g. sti) force the next instruction to be executed before any interrupts can occur.
goto end;
if (disret == false)
goto end;
case OP_HLT:
goto end;
/** @todo continue with the instructions following the jump and then recompile the jump target code */
/* For our first attempt, we'll handle only simple relative jumps and calls (immediate offset coded in instruction). */
if (addr == 0)
/* We don't check if the branch target lies in a valid page as we've already done that in the analysis phase. */
* If we are jumping to an existing patch (or within 5 bytes of the entrypoint), then we must temporarily disable
if(pTargetPatch)
Log(("Found active patch at target %RRv (%RRv) -> temporarily disabling it!!\n", addr, pTargetPatch->pPrivInstrGC));
if(pTargetPatch)
goto end;
goto end;
end:
return rc;
static int patmGenJumpToPatch(PVM pVM, PPATCHINFO pPatch, PPATMP2GLOOKUPREC pCacheRec, bool fAddFixup = true)
int rc;
if (fAddFixup)
if (patmPatchAddReloc32(pVM, pPatch, &pPB[1], FIXUP_REL_JMPTOPATCH, pPatch->pPrivInstrGC + pPatch->cbPatchJump, pPatch->pPatchJumpDestGC) != VINF_SUCCESS)
return VERR_PATCHING_REFUSED;
*(uint32_t *)&temp[1] = (uint32_t)pPatch->pPatchJumpDestGC - ((uint32_t)pPatch->pPrivInstrGC + pPatch->cbPatchJump); //return address
if (fAddFixup)
if (patmPatchAddReloc32(pVM, pPatch, &pPB[2], FIXUP_REL_JMPTOPATCH, pPatch->pPrivInstrGC + pPatch->cbPatchJump, pPatch->pPatchJumpDestGC) != VINF_SUCCESS)
return VERR_PATCHING_REFUSED;
*(uint32_t *)&temp[2] = (uint32_t)pPatch->pPatchJumpDestGC - ((uint32_t)pPatch->pPrivInstrGC + pPatch->cbPatchJump); //return address
Assert(0);
return VERR_PATCHING_REFUSED;
if (fAddFixup)
if (patmPatchAddReloc32(pVM, pPatch, &pPB[1], FIXUP_REL_JMPTOPATCH, pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32, PATCHCODE_PTR_GC(pPatch)) != VINF_SUCCESS)
return VERR_PATCHING_REFUSED;
*(uint32_t *)&temp[1] = (RTRCUINTPTR)PATCHCODE_PTR_GC(pPatch) - ((RTRCUINTPTR)pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32); //return address
rc = PGMPhysSimpleDirtyWriteGCPtr(VMMGetCpu0(pVM), pPatch->pPrivInstrGC, temp, pPatch->cbPatchJump);
return rc;
#ifdef DEBUG
bool disret;
if (disret == false)
i += opsize;
/* Restore original code (privileged instruction + following instructions that were overwritten because of the 5/6 byte jmp). */
int rc = PGMPhysSimpleDirtyWriteGCPtr(VMMGetCpu0(pVM), pPatch->pPrivInstrGC, pPatch->aPrivInstr, pPatch->cbPatchJump);
#ifdef DEBUG
if (disret == false)
i += opsize;
return rc;
static int patmGenCallToPatch(PVM pVM, PPATCHINFO pPatch, RTRCPTR pTargetGC, PPATMP2GLOOKUPREC pCacheRec, bool fAddFixup = true)
int rc;
if (fAddFixup)
if (patmPatchAddReloc32(pVM, pPatch, &pPB[1], FIXUP_REL_JMPTOPATCH, pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32, pTargetGC) != VINF_SUCCESS)
return VERR_PATCHING_REFUSED;
*(uint32_t *)&temp[1] = (uint32_t)pTargetGC - ((uint32_t)pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32); //return address
rc = PGMPhysSimpleDirtyWriteGCPtr(VMMGetCpu0(pVM), pPatch->pPrivInstrGC, temp, pPatch->cbPatchJump);
return rc;
#ifdef LOG_ENABLED
bool disret;
switch (uOpcode)
case OP_MOV:
case OP_CLI:
case OP_PUSHF:
/* We can 'call' a cli or pushf patch. It will either return to the original guest code when IF is set again, or fault. */
return VERR_INVALID_PARAMETER;
if (!(pPatch->flags & (PATMFL_IDTHANDLER|PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT|PATMFL_SYSENTER|PATMFL_INT3_REPLACEMENT_BLOCK)))
/* If we're going to insert a patch jump, then the jump itself is not allowed to cross a page boundary. */
goto failure;
#ifdef PATM_ENABLE_CALL
if ((pPatch->flags & (PATMFL_IDTHANDLER|PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT|PATMFL_SYSENTER)) == PATMFL_IDTHANDLER)
goto failure;
/***************************************************************************************************************************/
/* Note: We can't insert *any* code before a sysenter handler; some linux guests have an invalid stack at this point!!!!! */
/***************************************************************************************************************************/
#ifdef VBOX_WITH_STATISTICS
goto failure;
goto failure;
goto failure;
LogFlow(("Insert %RRv patch offset %RRv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
if (!rc)
goto failure;
goto failure;
#ifdef LOG_ENABLED
patmr3DisasmCodeStream(pVM, PATCHCODE_PTR_GC(pPatch), PATCHCODE_PTR_GC(pPatch), patmr3DisasmCallback, &cacheRec);
rc = PGMPhysSimpleReadGCPtr(VMMGetCpu0(pVM), pPatch->aPrivInstr, pPatch->pPrivInstrGC, pPatch->cbPatchJump);
goto failure;
Assert(!(pPatch->flags & (PATMFL_IDTHANDLER|PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT|PATMFL_SYSENTER|PATMFL_INT3_REPLACEMENT_BLOCK)));
goto failure;
#ifdef LOG_ENABLED
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, NULL, &opsize, szOutput, PATMREAD_RAWCODE);
Log(("Successfully installed %s patch at %RRv\n", patmGetInstructionString(pPatch->opcode, pPatch->flags), pInstrGC));
return VINF_SUCCESS;
return rc;
static int patmIdtHandler(PVM pVM, RTRCPTR pInstrGC, uint32_t uOpSize, PPATMPATCHREC pPatchRec, PPATMP2GLOOKUPREC pCacheRec)
bool disret;
* and then jump to a common entrypoint. In order not to waste a lot of memory, we will check for this
int rc;
if ( disret
PPATMPATCHREC pJmpPatch = (PPATMPATCHREC)RTAvloU32Get(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pJmpInstrGC);
if (pJmpPatch == 0)
goto failure;
goto failure;
#ifdef VBOX_WITH_STATISTICS
goto failure;
goto failure;
patmr3AddP2GLookupRecord(pVM, pPatch, PATCHCODE_PTR_HC(pPatch) + pPatch->uCurPatchOffset, pInstrGC, PATM_LOOKUP_BOTHDIR);
goto failure;
goto failure;
#ifdef LOG_ENABLED
patmr3DisasmCodeStream(pVM, PATCHCODE_PTR_GC(pPatch), PATCHCODE_PTR_GC(pPatch), patmr3DisasmCallback, pCacheRec);
LogFlow(("Insert %RRv patch offset %RRv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
return VINF_SUCCESS;
static int patmInstallTrapTrampoline(PVM pVM, RTRCPTR pInstrGC, PPATMPATCHREC pPatchRec, PPATMP2GLOOKUPREC pCacheRec)
#ifdef LOG_ENABLED
bool disret;
#ifdef VBOX_WITH_STATISTICS
goto failure;
goto failure;
#ifdef LOG_ENABLED
patmr3DisasmCodeStream(pVM, PATCHCODE_PTR_GC(pPatch), PATCHCODE_PTR_GC(pPatch), patmr3DisasmCallback, pCacheRec);
#ifdef LOG_ENABLED
LogFlow(("Insert %RRv patch offset %RRv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
return VINF_SUCCESS;
return rc;
#ifdef LOG_ENABLED
static int patmDuplicateFunction(PVM pVM, RTRCPTR pInstrGC, PPATMPATCHREC pPatchRec, PPATMP2GLOOKUPREC pCacheRec)
return VERR_PATCHING_REFUSED;
#ifdef PATM_ENABLE_CALL
goto failure;
#ifdef VBOX_WITH_STATISTICS
goto failure;
goto failure;
LogFlow(("Insert %RRv patch offset %RRv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
if (!rc)
goto failure;
goto failure;
#ifdef LOG_ENABLED
patmr3DisasmCodeStream(pVM, PATCHCODE_PTR_GC(pPatch), PATCHCODE_PTR_GC(pPatch), patmr3DisasmCallback, pCacheRec);
return VINF_SUCCESS;
return rc;
bool fInserted = false;
/* First we check if the duplicate function target lies in some existing function patch already. Will save some space. */
PPATMPATCHPAGE pPatchPage = (PPATMPATCHPAGE)RTAvloU32Get(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, (RTRCPTR)pPage);
if (pPatchPage)
uint32_t i;
if (pPatchTargetGC)
PRECPATCHTOGUEST pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatchToJmp->Patch2GuestAddrTree, offsetPatch, false);
Log(("patmCreateTrampoline: generating jump to code inside patch at %RRv (patch target %RRv)\n", pPatchToJmp->pPrivInstrGC, pPatchTargetGC));
if (!pTrampRec)
goto failure;
#ifdef VBOX_WITH_STATISTICS
goto failure;
goto failure;
LogFlow(("Insert %RRv patch offset %RRv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
fInserted = RTAvloU32Insert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
if (!fInserted)
goto failure;
if (pTrampRec)
return VINF_SUCCESS;
if (pTrampRec)
return rc;
int rc;
/* First we check if the duplicate function target lies in some existing function patch already. Will save some space. */
PPATMPATCHPAGE pPatchPage = (PPATMPATCHPAGE)RTAvloU32Get(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, (RTRCPTR)pPage);
if (pPatchPage)
uint32_t i;
if (pPatchTargetGC)
if (pPatchTargetGC)
if (pPatchTargetGC)
/* We add a dummy entry into the lookup cache so we won't get bombarded with the same requests over and over again. */
return VINF_SUCCESS;
static int patmReplaceFunctionCall(PVM pVM, DISCPUSTATE *pCpu, RTRCPTR pInstrGC, PPATMP2GLOOKUPREC pCacheRec)
bool disret;
#ifdef LOG_ENABLED
Assert((pCpu->pCurInstr->opcode == OP_CALL || pCpu->pCurInstr->opcode == OP_JMP) && pCpu->opsize == SIZEOF_NEARJUMP32);
if ((pCpu->pCurInstr->opcode != OP_CALL && pCpu->pCurInstr->opcode != OP_JMP) || pCpu->opsize != SIZEOF_NEARJUMP32)
goto failure;
if (pTargetGC == 0)
goto failure;
if (pTmpInstrHC == 0)
if (pTargetGC == 0)
if (pPatchFunction == 0)
goto failure;
rc = PGMPhysSimpleReadGCPtr(VMMGetCpu0(pVM), pPatch->aPrivInstr, pPatch->pPrivInstrGC, pPatch->cbPatchJump);
goto failure;
#ifdef LOG_ENABLED
return VINF_SUCCESS;
return rc;
static int patmPatchMMIOInstr(PVM pVM, RTRCPTR pInstrGC, DISCPUSTATE *pCpu, PPATMP2GLOOKUPREC pCacheRec)
#ifdef LOG_ENABLED
bool disret;
goto failure;
goto failure;
if (pPB == 0)
goto failure;
if (patmPatchAddReloc32(pVM, pPatch, &pPB[pCpu->opsize - sizeof(RTRCPTR)], FIXUP_ABSOLUTE, pPatch->pPrivInstrGC, pVM->patm.s.mmio.pCachedData) != VINF_SUCCESS)
return VERR_PATCHING_REFUSED;
#ifdef LOG_ENABLED
rc = PGMPhysSimpleReadGCPtr(VMMGetCpu0(pVM), pPatch->aPrivInstr, pPatch->pPrivInstrGC, pPatch->cbPrivInstr);
pPatch->cbPatchJump = pPatch->cbPrivInstr; /* bit of a misnomer in this case; size of replacement instruction. */
rc = PGMPhysSimpleDirtyWriteGCPtr(VMMGetCpu0(pVM), pInstrGC + pCpu->opsize - sizeof(RTRCPTR), &pVM->patm.s.mmio.pCachedData, sizeof(RTRCPTR));
goto failure;
#ifdef LOG_ENABLED
return VINF_SUCCESS;
return rc;
* Replace the address in an MMIO instruction with the cached version. (instruction is part of an existing patch)
bool disret;
#ifdef LOG_ENABLED
if (disret == false)
return VERR_PATCHING_REFUSED;
return VERR_PATCHING_REFUSED;
return VERR_PATCHING_REFUSED;
if (patmPatchAddReloc32(pVM, pPatch, &pInstrHC[cpu.opsize - sizeof(RTRCPTR)], FIXUP_ABSOLUTE) != VINF_SUCCESS)
return VERR_PATCHING_REFUSED;
#ifdef LOG_ENABLED
return VINF_SUCCESS;
int rc;
rc = PGMPhysSimpleDirtyWriteGCPtr(VMMGetCpu0(pVM), pPatch->pPrivInstrGC, &ASMInt3, sizeof(ASMInt3));
return rc;
int rc;
rc = PGMPhysSimpleDirtyWriteGCPtr(VMMGetCpu0(pVM), pPatch->pPrivInstrGC, pPatch->aPrivInstr, sizeof(ASMInt3));
return rc;
* Replace an instruction with a breakpoint (0xCC), that is handled dynamically in the guest context.
VMMR3DECL(int) PATMR3PatchInstrInt3(PVM pVM, RTRCPTR pInstrGC, R3PTRTYPE(uint8_t *) pInstrHC, DISCPUSTATE *pCpu, PPATCHINFO pPatch)
int rc;
#ifdef LOG_ENABLED
rc = PGMPhysSimpleReadGCPtr(VMMGetCpu0(pVM), pPatch->aPrivInstr, pPatch->pPrivInstrGC, pPatch->cbPrivInstr);
pPatch->cbPatchJump = sizeof(ASMInt3); /* bit of a misnomer in this case; size of replacement instruction. */
goto failure;
return VINF_SUCCESS;
return VERR_PATCHING_REFUSED;
int patmPatchJump(PVM pVM, RTRCPTR pInstrGC, R3PTRTYPE(uint8_t *) pInstrHC, DISCPUSTATE *pCpu, PPATMPATCHREC pPatchRec)
#ifdef LOG_ENABLED
bool disret;
* Instruction replacements such as these should never be interrupted. I've added code to EM.cpp to
case OP_JO:
case OP_JNO:
case OP_JC:
case OP_JNC:
case OP_JE:
case OP_JNE:
case OP_JBE:
case OP_JNBE:
case OP_JS:
case OP_JNS:
case OP_JP:
case OP_JNP:
case OP_JL:
case OP_JNL:
case OP_JLE:
case OP_JNLE:
case OP_JMP:
goto failure;
goto failure;
goto failure;
goto failure;
rc = PGMPhysSimpleReadGCPtr(VMMGetCpu0(pVM), pPatch->aPrivInstr, pPatch->pPrivInstrGC, pPatch->cbPatchJump);
* A conflict jump patch needs to be treated differently; we'll just replace the relative jump address with one that
RTRCPTR pJmpDest = PATMR3GuestGCPtrToPatchGCPtr(pVM, pInstrGC + pCpu->opsize + (int32_t)pCpu->param1.parval);
AssertMsg(pJmpDest, ("PATMR3GuestGCPtrToPatchGCPtr failed for %RRv\n", pInstrGC + pCpu->opsize + (int32_t)pCpu->param1.parval));
goto failure;
#ifdef LOG_ENABLED
Log(("Successfully installed %s patch at %RRv\n", patmGetInstructionString(pPatch->opcode, pPatch->flags), pInstrGC));
return VINF_SUCCESS;
return rc;
bool disret;
int rc;
if ( !pVM
|| pInstrGC == 0
|| (flags & ~(PATMFL_CODE32|PATMFL_IDTHANDLER|PATMFL_INTHANDLER|PATMFL_SYSENTER|PATMFL_TRAPHANDLER|PATMFL_DUPLICATE_FUNCTION|PATMFL_REPLACE_FUNCTION_CALL|PATMFL_GUEST_SPECIFIC|PATMFL_INT3_REPLACEMENT|PATMFL_TRAPHANDLER_WITH_ERRORCODE|PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT|PATMFL_MMIO_ACCESS|PATMFL_TRAMPOLINE|PATMFL_INSTR_HINT|PATMFL_JUMP_CONFLICT)))
AssertFailed();
return VERR_INVALID_PARAMETER;
return VERR_PATCHING_REFUSED;
AssertReleaseMsg(pConflictPatch == 0, ("Unable to patch overwritten instruction at %RRv (%RRv)\n", pInstrGC, pConflictPatch->pPrivInstrGC));
if (pConflictPatch != 0)
return VERR_PATCHING_REFUSED;
return VERR_NOT_IMPLEMENTED;
return VERR_PATCHING_REFUSED;
Log(("PATMR3InstallPatch: code selector not wide open: %04x:%RRv != %RRv eflags=%08x\n", pCtx->cs, pInstrGCFlat, pInstrGC, pCtx->eflags.u32));
return VERR_PATCHING_REFUSED;
/* Note: the OpenBSD specific check will break if we allow additional patches to be installed (int 3)) */
void *pvPatchCoreOffset;
pvPatchCoreOffset = RTAvloU32GetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, offset, false);
if (pPatchRec)
return VERR_PATCHING_REFUSED;
Log(("PATMR3InstallPatch: disable operation is pending for patch at %RRv\n", pPatchRec->patch.pPrivInstrGC));
/* A patch, for which we previously received a hint, will be enabled and turned into a normal patch. */
/** @todo we shouldn't disable and enable patches too often (it's relatively cheap, but pointless if it always happens) */
return VWRN_PATCH_ENABLED;
return rc;
if (pPatchRec->patch.flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_IDTHANDLER|PATMFL_MMIO_ACCESS|PATMFL_INT3_REPLACEMENT|PATMFL_INT3_REPLACEMENT_BLOCK))
pPatchRec->patch.flags |= (flags & (PATMFL_IDTHANDLER|PATMFL_TRAPHANDLER|PATMFL_INTHANDLER)); /* update the type */
return VERR_PATCHING_REFUSED;
AssertMsg(pPatchRec->patch.uState == PATCH_REFUSED || pPatchRec->patch.uState == PATCH_UNUSABLE, ("Patch an existing patched instruction?!? (%RRv, state=%d)\n", pInstrGC, pPatchRec->patch.uState));
return VERR_PATCHING_REFUSED;
return rc;
/* Disallow patching instructions inside ROM code; complete function duplication is allowed though. */
return VERR_PATCHING_REFUSED;
return VERR_NO_MEMORY;
if (!(pPatchRec->patch.flags & (PATMFL_DUPLICATE_FUNCTION | PATMFL_IDTHANDLER | PATMFL_SYSENTER | PATMFL_TRAMPOLINE)))
* Close proximity to an unusable patch is a possible hint that this patch would turn out to be dangerous too!
PPATMPATCHREC pPatchNear = (PPATMPATCHREC)RTAvloU32GetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, (pInstrGC + SIZEOF_NEARJUMP32 - 1), false);
if (pPatchNear)
if (pPatchNear->patch.uState == PATCH_UNUSABLE && pInstrGC < pPatchNear->patch.pPrivInstrGC && pInstrGC + SIZEOF_NEARJUMP32 > pPatchNear->patch.pPrivInstrGC)
Log(("Dangerous patch; would overwrite the unusable patch at %RRv\n", pPatchNear->patch.pPrivInstrGC));
* Leave the new patch active as it's marked unusable; to prevent us from checking it over and over again
return VERR_PATCHING_REFUSED;
pPatchRec->patch.pTempInfo = (PPATCHINFOTEMP)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(PATCHINFOTEMP));
return VERR_NO_MEMORY;
if (disret == false)
return VERR_PATCHING_REFUSED;
return VERR_PATCHING_REFUSED;
#ifdef VBOX_WITH_STATISTICS
case OP_SYSENTER:
case OP_PUSH:
Log(("PATMR3InstallPatch GUEST: %s %RRv code32=%d\n", patmGetInstructionString(pPatchRec->patch.opcode, pPatchRec->patch.flags), pInstrGC, (flags & PATMFL_CODE32) ? 1 : 0));
return rc;
case OP_SYSENTER:
Log(("PATMR3InstallPatch GUEST: %s %RRv code32=%d\n", patmGetInstructionString(pPatchRec->patch.opcode, pPatchRec->patch.flags), pInstrGC, (flags & PATMFL_CODE32) ? 1 : 0));
return VINF_SUCCESS;
case OP_JO:
case OP_JNO:
case OP_JC:
case OP_JNC:
case OP_JE:
case OP_JNE:
case OP_JBE:
case OP_JNBE:
case OP_JS:
case OP_JNS:
case OP_JP:
case OP_JNP:
case OP_JL:
case OP_JNL:
case OP_JLE:
case OP_JNLE:
case OP_JECXZ:
case OP_LOOP:
case OP_LOOPNE:
case OP_LOOPE:
case OP_JMP:
return VERR_NOT_IMPLEMENTED;
case OP_PUSHF:
case OP_CLI:
Log(("PATMR3InstallPatch %s %RRv code32=%d\n", patmGetInstructionString(pPatchRec->patch.opcode, pPatchRec->patch.flags), pInstrGC, (flags & PATMFL_CODE32) ? 1 : 0));
case OP_STR:
case OP_SGDT:
case OP_SLDT:
case OP_SIDT:
case OP_CPUID:
case OP_LSL:
case OP_LAR:
case OP_SMSW:
case OP_VERW:
case OP_VERR:
case OP_IRET:
return VERR_NOT_IMPLEMENTED;
Log(("Patch lowest %RRv highest %RRv\n", pPatchRec->patch.pInstrGCLowest, pPatchRec->patch.pInstrGCHighest));
Log(("Global lowest %RRv highest %RRv\n", pVM->patm.s.pPatchedInstrGCLowest, pVM->patm.s.pPatchedInstrGCHighest));
#ifdef VBOX_WITH_STATISTICS
STAMR3RegisterCallback(pVM, &pPatchRec->patch, STAMVISIBILITY_NOT_GUI, STAMUNIT_GOOD_BAD, patmResetStat, patmPrintStat, "Patch statistics",
#ifndef DEBUG_sandervl
STAMR3RegisterF(pVM, &pVM->patm.s.pStatsHC[pPatchRec->patch.uPatchIdx], STAMTYPE_RATIO_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_GOOD_BAD, PATMPatchType(pVM, &pPatchRec->patch),
STAMR3RegisterF(pVM, &pPatchRec->patch.cbPatchBlockSize,STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, NULL, "/PATM/Stats/PatchBD/0x%RRv/cbPatchBlockSize", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.cbPatchJump, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, NULL, "/PATM/Stats/PatchBD/0x%RRv/cbPatchJump", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.cbPrivInstr, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, NULL, "/PATM/Stats/PatchBD/0x%RRv/cbPrivInstr", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.cCodeWrites, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%RRv/cCodeWrites", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.cInvalidWrites, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%RRv/cInvalidWrites", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.cTraps, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%RRv/cTraps", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.flags, STAMTYPE_X32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, NULL, "/PATM/Stats/PatchBD/0x%RRv/flags", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.nrJumpRecs, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%RRv/nrJumpRecs", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.nrFixups, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%RRv/nrFixups", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.opcode, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%RRv/opcode", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.uOldState, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, NULL, "/PATM/Stats/PatchBD/0x%RRv/uOldState", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.uOpMode, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, NULL, "/PATM/Stats/PatchBD/0x%RRv/uOpMode", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.uState, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, NULL, "/PATM/Stats/PatchBD/0x%RRv/uState", pPatchRec->patch.pPrivInstrGC);
return rc;
bool disret;
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pInstrGC, pInstrHC, &opsize, NULL, PATMREAD_ORGCODE | PATMREAD_NOCHECK);
if (disret)
return opsize;
int rc;
if (pPatchPage)
rc = MMHyperAlloc(pVM, sizeof(PPATCHINFO)*pPatchPage->cMaxPatches, 0, MM_TAG_PATM_PATCH, (void **)&pPatchPage->aPatch);
return VERR_NO_MEMORY;
return VERR_NO_MEMORY;
rc = MMHyperAlloc(pVM, sizeof(PPATCHINFO)*PATMPATCHPAGE_PREALLOC_INCREMENT, 0, MM_TAG_PATM_PATCH, (void **)&pPatchPage->aPatch);
return VERR_NO_MEMORY;
PRECGUESTTOPATCH pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlU32GetBestFit(&pPatch->Guest2PatchAddrTree, pPage, true);
if (pGuestToPatchRec)
LogFlow(("patmAddPatchToPage: lowest patch page address %RRv current lowest %RRv\n", pGuestToPatchRec->Core.Key, pPatchPage->pLowestAddrGC));
/* If we're too close to the page boundary, then make sure an instruction from the previous page doesn't cross the boundary itself. */
pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlU32GetBestFit(&pPatch->Guest2PatchAddrTree, pPage-1, false);
if (pGuestToPatchRec)
pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlU32GetBestFit(&pPatch->Guest2PatchAddrTree, pPage+PAGE_SIZE-1, false);
if (pGuestToPatchRec)
LogFlow(("patmAddPatchToPage: highest patch page address %RRv current lowest %RRv\n", pGuestToPatchRec->Core.Key, pPatchPage->pHighestAddrGC));
return VINF_SUCCESS;
int rc;
if (!pPatchPage)
return VERR_INVALID_PARAMETER;
uint32_t i;
memcpy(&pPatchPage->aPatch[i], &pPatchPage->aPatch[i+1], sizeof(PPATCHINFO)*(pPatchPage->cCount - (i+1)));
pPatchNode = (PPATMPATCHPAGE)RTAvloU32Remove(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, pPage);
return VINF_SUCCESS;
* Insert page records for all guest pages that contain instructions that were recompiled for this patch
int rc;
/* Insert the pages that contain patched instructions into a lookup tree for detecting self-modifying code. */
PRECGUESTTOPATCH pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlU32GetBestFit(&pPatch->Guest2PatchAddrTree, pPage, true);
if ( pGuestToPatchRec
return VINF_SUCCESS;
* Remove page records for all guest pages that contain instructions that were recompiled for this patch
int rc;
/* Insert the pages that contain patched instructions into a lookup tree for detecting self-modifying code. */
PRECGUESTTOPATCH pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlU32GetBestFit(&pPatch->Guest2PatchAddrTree, pPage, true);
if ( pGuestToPatchRec
&& PAGE_ADDRESS(pGuestToPatchRec->Core.Key) == PAGE_ADDRESS(pPage) /** @todo bird: PAGE_ADDRESS is for the current context really. check out these. */
return VINF_SUCCESS;
return VINF_SUCCESS;
PPATMPATCHPAGE pPatchPage = (PPATMPATCHPAGE)RTAvloU32Get(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, (RTRCPTR)pPage);
if (pPatchPage)
uint32_t i;
bool fValidPatchWrite = false;
goto loop_start;
/* Find the closest instruction from below; the above quick check ensured that we are indeed in patched code */
if (!pPatchInstrGC)
if (pPatchInstrGC)
pPatchInstrGC = 0;
if (pPatchInstrGC)
uint32_t PatchOffset = pPatchInstrGC - pVM->patm.s.pPatchMemGC; /* Offset in memory reserved for PATM. */
fValidPatchWrite = true;
PRECPATCHTOGUEST pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32Get(&pPatch->Patch2GuestAddrTree, PatchOffset);
LogRel(("PATM: Disable block at %RRv - write %RRv-%RRv\n", pPatch->pPrivInstrGC, pGuestPtrGC, pGuestPtrGC+cbWrite));
goto loop_start;
/* Replace the patch instruction with a breakpoint; when it's hit, then we'll attempt to recompile the instruction again. */
if (fValidPatchWrite == false)
pPatchPage = (PPATMPATCHPAGE)RTAvloU32Get(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, (RTRCPTR)pPage);
if (pPatchPage)
LogRel(("PATM: Stop monitoring IDT handler pages at %RRv - invalid write %RRv-%RRv (this is not a fatal error)\n", pPatch->pPrivInstrGC, GCPtr, GCPtr+cbWrite));
LogRel(("PATM: Disable block at %RRv - invalid write %RRv-%RRv \n", pPatch->pPrivInstrGC, GCPtr, GCPtr+cbWrite));
goto invalid_write_loop_start;
return VINF_SUCCESS;
/** @note Currently only called by CSAMR3FlushPage; optimization to avoid having to double check if the physical address has changed
PPATMPATCHPAGE pPatchPage = (PPATMPATCHPAGE)RTAvloU32Get(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, addr);
if (pPatchPage)
return VINF_SUCCESS;
return VERR_PATCH_NOT_FOUND;
pPatchRec = (PPATMPATCHREC)RTAvloU32GetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC, false);
// if the patch is enabled and the pointer lies within 5 bytes of this priv instr ptr, then we've got a hit!
if ( pPatchRec
return VINF_SUCCESS;
return VERR_PATCH_NOT_FOUND;
if (pPatchRec)
return VINF_SUCCESS;
/* Mark the entry with a breakpoint in case somebody else calls it later on (cli patch used as a function, function, trampoline or idt patches) */
Assert(!(pPatch->flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_IDTHANDLER|PATMFL_TRAMPOLINE|PATMFL_INT3_REPLACEMENT|PATMFL_INT3_REPLACEMENT_BLOCK)));
RTRCINTPTR displ = (RTRCUINTPTR)PATCHCODE_PTR_GC(pPatch) - ((RTRCUINTPTR)pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32);
return VWRN_PATCH_REMOVED;
return VERR_PATCH_ALREADY_DISABLED;
return VWRN_PATCH_REMOVED;
/* Just to be safe, let's make sure this one can never be reused; the patch might be marked dirty already (int3 at start) */
return VINF_SUCCESS;
return VERR_PATCH_NOT_FOUND;
static int patmDisableUnusablePatch(PVM pVM, RTRCPTR pInstrGC, RTRCPTR pConflictAddr, PPATCHINFO pConflictPatch)
bool disret;
int rc;
* If it's a 5 byte relative jump, then we can work around the problem by replacing the 32 bits relative offset
if ( disret == true
return rc;
return VINF_SUCCESS;
Log(("PATM -> CONFLICT: Found active patch at instruction %RRv with target %RRv -> turn into int 3 patch!!\n", pInstrGC, pConflictPatch->pPrivInstrGC));
return VINF_SUCCESS;
return VINF_SUCCESS;
Log(("PATM -> CONFLICT: Found active patch at instruction %RRv with target %RRv -> DISABLING it!!\n", pInstrGC, pConflictPatch->pPrivInstrGC));
return VINF_SUCCESS;
return VERR_PATCH_DISABLED;
if (pPatchRec)
return VERR_PATCH_NOT_FOUND;
return rc2;
#ifdef DEBUG
bool disret;
i += opsize;
return VERR_PATCH_NOT_FOUND;
return rc2;
return rc;
return VERR_PATCH_NOT_FOUND;
return VERR_ACCESS_DENIED;
pNode = RTAvloU32Remove(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, pPatchRec->patch.pPatchBlockOffset);
#ifdef VBOX_WITH_STATISTICS
#ifndef DEBUG_sandervl
/* Note: no need to free Guest2PatchAddrTree as those records share memory with Patch2GuestAddrTree records. */
return VINF_SUCCESS;
int32_t displ = pPatchTargetGC - (pVM->patm.s.pPatchMemGC + pPatch2GuestRec->Core.Key + SIZEOF_NEARJUMP32);
return VINF_SUCCESS;
int rc;
AssertReturn(pPatch->flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_IDTHANDLER|PATMFL_TRAPHANDLER), VERR_PATCHING_REFUSED);
Log(("patmR3RefreshPatch: refused because external jumps to this patch exist but the jumps are not recorded\n"));
return VERR_PATCHING_REFUSED;
/** Note: quite ugly to enable/disable/remove/insert old and new patches, but there's no easy way around it. */
#ifdef VBOX_WITH_STATISTICS
#ifndef DEBUG_sandervl
/** Note: We don't attempt to reuse patch memory here as it's quite common that the new code block requires more memory. */
rc = PATMR3InstallPatch(pVM, pInstrGC, pPatch->flags & (PATMFL_CODE32|PATMFL_IDTHANDLER|PATMFL_INTHANDLER|PATMFL_TRAPHANDLER|PATMFL_DUPLICATE_FUNCTION|PATMFL_TRAPHANDLER_WITH_ERRORCODE|PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT));
if (!pPatchTargetGC)
goto failure;
goto failure;
/* Put the new patch back into the tree, because removing the old one kicked this one out. (hack alert) */
while (pTrampRec)
LogRel(("PATM: patmR3RefreshPatch: failed to refresh patch at %RRv. Reactiving old one. \n", pInstrGC));
return rc;
* @param pInstr Guest context point to instruction that might lie within 5 bytes of an existing patch jump
PPATMPATCHREC pPatchRec = (PPATMPATCHREC)RTAvloU32GetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC, false);
/* if the patch is enabled, the pointer is not identical to the privileged patch ptr and it lies within 5 bytes of this priv instr ptr, then we've got a hit! */
if (pPatchRec)
if ( fIncludeHints
return NULL;
*pPatchAddr = 0;
if (pPatch)
return *pPatchAddr == 0 ? false : true;
if (pPatchRec)
return VINF_SUCCESS;
AssertFailed();
return VERR_PATCH_NOT_FOUND;
return VINF_SUCCESS;
/* Paranoia; make sure this patch is not somewhere in the callchain, so prevent ret instructions from succeeding. */
return VINF_SUCCESS;
PRECPATCHTOGUEST pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatch->Patch2GuestAddrTree, pPatchGC - pVM->patm.s.pPatchMemGC, false);
if (pPatchToGuestRec)
PRECGUESTTOPATCH pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlU32Get(&pPatch->Guest2PatchAddrTree, pInstrGC);
if (pGuestToPatchRec)
PRECGUESTTOPATCH pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlU32GetBestFit(&pPatch->Guest2PatchAddrTree, pInstrGC, false);
if (pGuestToPatchRec)
PPATMPATCHREC pPatchRec = (PPATMPATCHREC)RTAvloU32GetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC, false);
if (pPatchRec && pPatchRec->patch.uState == PATCH_ENABLED && pInstrGC >= pPatchRec->patch.pPrivInstrGC)
void *pvPatchCoreOffset;
pvPatchCoreOffset = RTAvloU32GetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, pPatchGC - pVM->patm.s.pPatchMemGC, false);
if (pvPatchCoreOffset == 0)
Log(("PATMR3PatchToGCPtr failed for %RRv offset %x\n", pPatchGC, pPatchGC - pVM->patm.s.pPatchMemGC));
if (pEnmState)
if ( !pPrivInstrGC
pPrivInstrGC = 0;
return pPrivInstrGC;
if (pPatchRec && (pPatchRec->patch.uState == PATCH_ENABLED || pPatchRec->patch.uState == PATCH_DIRTY))
static int patmR3HandleDirtyInstr(PVM pVM, PCPUMCTX pCtx, PPATMPATCHREC pPatch, PRECPATCHTOGUEST pPatchToGuestRec, RTRCPTR pEip)
int rc;
cbDirty = 0;
LogRel(("PATM: patmR3HandleDirtyInstr: dirty instruction at %RRv (%RRv) ignored, because instruction in function was reused as target of jump\n", pEip, pOrgInstrGC));
return VERR_PATCHING_REFUSED;
if (!cbDirty)
#ifdef DEBUG
pRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatch->patch.Patch2GuestAddrTree, pCurPatchInstrGC - pVM->patm.s.pPatchMemGC, true);
bool fValidInstr;
if ( !fValidInstr
fValidInstr = true;
&& fValidInstr
#ifdef DEBUG
#ifdef DEBUG
/** @todo in theory we need to restore the lookup records for the remaining dirty instructions too! */
if (!cbLeft)
/* If the next patch instruction doesn't correspond to the next guest instruction, then we have some extra room to fill. */
if (RTAvlU32Get(&pPatch->patch.Patch2GuestAddrTree, pCurPatchInstrGC - pVM->patm.s.pPatchMemGC) == NULL)
pRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatch->patch.Patch2GuestAddrTree, pCurPatchInstrGC - pVM->patm.s.pPatchMemGC, true);
if (pRec)
#ifdef DEBUG
for (unsigned i = 0; i < cbFiller; i++)
#ifdef DEBUG
if (cbDirty)
LogRel(("PATM: Failed to refresh dirty patch at %RRv. Disabling it.\n", pPatch->patch.pPrivInstrGC));
/* Even if we succeed, we must go back to the original instruction as the patched one could be invalid. */
return rc;
void *pvPatchCoreOffset;
int rc ;
pNewEip = 0;
*ppNewEip = 0;
pvPatchCoreOffset = RTAvloU32GetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, offset, false);
if (pvPatchCoreOffset)
Assert(offset >= pPatch->patch.pPatchBlockOffset && offset < pPatch->patch.pPatchBlockOffset + pPatch->patch.cbPatchBlockSize);
Log(("PATMR3HandleTrap: disable operation is pending for patch at %RRv\n", pPatch->patch.pPrivInstrGC));
AssertReleaseMsg(rc != VWRN_PATCH_REMOVED, ("PATMR3DisablePatch removed patch at %RRv\n", pPrivInstrGC));
AssertMsg(pPatch->patch.uState == PATCH_DISABLED || pPatch->patch.uState == PATCH_UNUSABLE, ("Unexpected failure to disable patch state=%d rc=%Rrc\n", pPatch->patch.uState, rc));
pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatch->patch.Patch2GuestAddrTree, offset, false);
AssertReleaseMsg(pPatchToGuestRec, ("PATMR3HandleTrap: Unable to find corresponding guest address for %RRv (offset %x)\n", pEip, offset));
AssertReleaseMsg(pVM->patm.s.pGCStateHC->fPIF == 0, ("PATMR3HandleTrap: Unable to find translation record for %RRv (PIF=0)\n", pEip));
return VINF_SUCCESS;
return VINF_PATCH_CONTINUE;
return VINF_SUCCESS;
DBGFR3DisasInstrEx(pVM, pVCpu->idCpu, pCtx->cs, pEip, DBGF_DISAS_FLAGS_DEFAULT_MODE, szBuf, sizeof(szBuf), NULL);
if (pPatch)
("Crash in patch code %RRv (%RRv) esp=%RX32\nPatch state=%x flags=%RX64 fDirty=%d\n%s\n", pEip, pNewEip, CPUMGetGuestESP(pVCpu), pPatch->patch.uState, pPatch->patch.flags, pPatchToGuestRec->fDirty, szBuf));
if (pvPatchCoreOffset == 0)
return VERR_PATCH_NOT_FOUND;
return rc;
#ifdef VBOX_STRICT
bool disret;
disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pNewEip, PATMGCVirtToHCVirt(pVM, &cacheRec, pNewEip), &opsize, NULL, PATMREAD_RAWCODE);
Log(("Expected return address %RRv found address %RRv Psp=%x\n", pVM->patm.s.pGCStackHC[(pVM->patm.s.pGCStateHC->Psp+PATM_STACK_SIZE)/sizeof(RTRCPTR)], retaddr, pVM->patm.s.pGCStateHC->Psp));
#ifdef VBOX_STRICT
bool disret;
disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pNewEip, PATMGCVirtToHCVirt(pVM, &cacheRec, pNewEip), &opsize, NULL, PATMREAD_ORGCODE);
if (disret && (cpu.pCurInstr->opcode == OP_SYSEXIT || cpu.pCurInstr->opcode == OP_HLT || cpu.pCurInstr->opcode == OP_INT3))
disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pNewEip, PATMGCVirtToHCVirt(pVM, &cacheRec, pNewEip), &opsize, NULL, PATMREAD_RAWCODE);
Assert(cpu.pCurInstr->opcode == OP_SYSEXIT || cpu.pCurInstr->opcode == OP_HLT || cpu.pCurInstr->opcode == OP_IRET);
Log2(("pPatchBlockGC %RRv - pEip %RRv corresponding GC address %RRv\n", PATCHCODE_PTR_GC(&pPatch->patch), pEip, pNewEip));
#ifdef LOG_ENABLED
if (pNewEip >= pPatch->patch.pPrivInstrGC && pNewEip < pPatch->patch.pPrivInstrGC + pPatch->patch.cbPatchJump)
Log(("Disabling patch at location %RRv due to trap too close to the privileged instruction \n", pPatch->patch.pPrivInstrGC));
return VERR_PATCH_DISABLED;
/** @todo compare to nr of successful runs. add some aging algorithm and determine the best time to disable the patch */
Log(("Disabling patch at location %RRv due to too many traps inside patch code\n", pPatch->patch.pPrivInstrGC));
return VERR_PATCH_DISABLED;
return VINF_SUCCESS;
PPATMPATCHREC pPatchRec = (PPATMPATCHREC)RTAvloU32GetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, addr, false);
if (pPatchRec && pPatchRec->patch.uState == PATCH_ENABLED && PAGE_ADDRESS(pPatchRec->patch.pPrivInstrGC) == PAGE_ADDRESS(addr))
return VINF_SUCCESS;
addr++;
pPatchRec = (PPATMPATCHREC)RTAvloU32GetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, addr, true);
return VINF_SUCCESS;
#ifdef VBOX_WITH_STATISTICS
RTStrPrintf(szTrap, sizeof(szTrap), (pPatch->flags & PATMFL_INTHANDLER) ? "INT-%2X" : "TRAP-%2X", iGate);
return szTrap;
case PATCH_ENABLED:
case PATCH_DISABLED:
case PATCH_DIRTY:
case PATCH_UNUSABLE:
case PATCH_REFUSED:
case PATCH_DISABLE_PENDING:
AssertFailed();
return pVM->patm.s.pStatsGC + sizeof(STAMRATIOU32) * pPatch->uPatchIdx + RT_OFFSETOF(STAMRATIOU32, u32A);
#ifdef VBOX_WITH_DEBUGGER
static DECLCALLBACK(int) patmr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs)
if (!pVM)
static DECLCALLBACK(int) patmr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs)
if (!pVM)