PATM.cpp revision 5aefacb29c648cdea4211b783e04d52223ddb257
5b281ba489ca18f0380d7efc7a5108b606cce449vboxsync * PATM - Dynamic Guest OS Patching Manager
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * NOTE: Never ever reuse patch memory!!
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Copyright (C) 2006-2007 Oracle Corporation
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * available from http://www.virtualbox.org. This file is free software;
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * you can redistribute it and/or modify it under the terms of the GNU
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * General Public License (GPL) as published by the Free Software
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync/*******************************************************************************
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync* Header Files *
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync*******************************************************************************/
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync//#define PATM_REMOVE_PATCH_ON_TOO_MANY_TRAPS
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync//#define PATM_DISABLE_ALL
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync/*******************************************************************************
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync* Internal Functions *
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync*******************************************************************************/
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncstatic int patmDisableUnusablePatch(PVM pVM, RTRCPTR pInstrGC, RTRCPTR pConflictAddr, PPATCHINFO pPatch);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncstatic int patmActivateInt3Patch(PVM pVM, PPATCHINFO pPatch);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncstatic int patmDeactivateInt3Patch(PVM pVM, PPATCHINFO pPatch);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncstatic bool patmIsCommonIDTHandlerPatch(PVM pVM, RTRCPTR pInstrGC);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncstatic const char *PATMPatchType(PVM pVM, PPATCHINFO pPatch);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncstatic void patmPrintStat(PVM pVM, void *pvSample, char *pszBuf, size_t cchBuf);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync#define patmPatchHCPtr2PatchGCPtr(pVM, pHC) (pVM->patm.s.pPatchMemGC + (pHC - pVM->patm.s.pPatchMemHC))
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync#define patmPatchGCPtr2PatchHCPtr(pVM, pGC) (pVM->patm.s.pPatchMemHC + (pGC - pVM->patm.s.pPatchMemGC))
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncstatic DECLCALLBACK(int) RelocatePatches(PAVLOU32NODECORE pNode, void *pParam);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncstatic DECLCALLBACK(int) DisableAllPatches(PAVLOU32NODECORE pNode, void *pVM);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncstatic DECLCALLBACK(int) patmr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncstatic DECLCALLBACK(int) patmr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync/** Command descriptors. */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, pResultDesc, fFlags, pfnHandler pszSyntax, ....pszDescription */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync { "patmon", 0, 0, NULL, 0, NULL, 0, patmr3CmdOn, "", "Enable patching." },
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync { "patmoff", 0, 0, NULL, 0, NULL, 0, patmr3CmdOff, "", "Disable patching." },
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync/* Don't want to break saved states, so put it here as a global variable. */
5b0a093ca572a855886faa6747ad46df859dd041vboxsyncstatic unsigned int cIDTHandlersDisabled = 0;
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Initializes the PATM.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @returns VBox status code.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @param pVM The VM to operate on.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("PATMR3Init: Patch record size %d\n", sizeof(PATCHINFO)));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* These values can't change as they are hardcoded in patch code (old saved states!) */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync AssertCompile(VMCPU_FF_INTERRUPT_APIC == RT_BIT_32(0));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync AssertCompile(VMCPU_FF_INTERRUPT_PIC == RT_BIT_32(1));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync AssertReleaseMsg(PATMInterruptFlag == (VMCPU_FF_INTERRUPT_APIC | VMCPU_FF_INTERRUPT_PIC | VMCPU_FF_TIMER | VMCPU_FF_REQUEST),
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync ("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));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Allocate patch memory and GC patch state memory. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Add another page in case the generated code is much larger than expected. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /** @todo bad safety precaution */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync 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);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.pPatchMemGC = MMHyperR3ToRC(pVM, pVM->patm.s.pPatchMemHC);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* PATM stack page for call instruction execution. (2 parts: one for our private stack and one to store the original return address */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.pGCStackHC = (RTRCPTR *)(pVM->patm.s.pPatchMemHC + PATCH_MEMORY_SIZE + PAGE_SIZE);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.pGCStackGC = MMHyperR3ToRC(pVM, pVM->patm.s.pGCStackHC);
3ae788d4138a852743619b65c7404deb5cbae3e7vboxsync * Hypervisor memory for GC status data (read/write)
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Note1: This is non-critical data; if trashed by the guest, then it will only cause problems for itself
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Note2: This doesn't really belong here, but we need access to it for relocation purposes
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Assert(sizeof(PATMGCSTATE) < PAGE_SIZE); /* Note: hardcoded dependencies on this exist. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.pGCStateHC = (PPATMGCSTATE)((uint8_t *)pVM->patm.s.pGCStackHC + PATM_STACK_TOTAL_SIZE);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.pGCStateGC = MMHyperR3ToRC(pVM, pVM->patm.s.pGCStateHC);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Hypervisor memory for patch statistics */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.pStatsHC = (PSTAMRATIOU32)((uint8_t *)pVM->patm.s.pGCStateHC + PAGE_SIZE);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.pStatsGC = MMHyperR3ToRC(pVM, pVM->patm.s.pStatsHC);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Memory for patch lookup trees. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync rc = MMHyperAlloc(pVM, sizeof(*pVM->patm.s.PatchLookupTreeHC), 0, MM_TAG_PATM, (void **)&pVM->patm.s.PatchLookupTreeHC);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.PatchLookupTreeGC = MMHyperR3ToRC(pVM, pVM->patm.s.PatchLookupTreeHC);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Check CFGM option. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync rc = CFGMR3QueryBool(CFGMR3GetRoot(pVM), "PATMEnabled", &pVM->fPATMEnabled);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Register save and load state notifiers.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync rc = SSMR3RegisterInternal(pVM, "PATM", 0, PATM_SSM_VERSION, sizeof(pVM->patm.s) + PATCH_MEMORY_SIZE + PAGE_SIZE + PATM_STACK_TOTAL_SIZE + PAGE_SIZE,
3ae788d4138a852743619b65c7404deb5cbae3e7vboxsync * Debugger commands.
3ae788d4138a852743619b65c7404deb5cbae3e7vboxsync static bool s_fRegisteredCmds = false;
3ae788d4138a852743619b65c7404deb5cbae3e7vboxsync int rc2 = DBGCRegisterCommands(&g_aCmds[0], RT_ELEMENTS(g_aCmds));
3ae788d4138a852743619b65c7404deb5cbae3e7vboxsync STAM_REG(pVM, &pVM->patm.s.StatNrOpcodeRead, STAMTYPE_COUNTER, "/PATM/OpcodeBytesRead", STAMUNIT_OCCURENCES, "The number of opcode bytes read by the recompiler.");
3ae788d4138a852743619b65c7404deb5cbae3e7vboxsync STAM_REG(pVM, &pVM->patm.s.StatPATMMemoryUsed,STAMTYPE_COUNTER, "/PATM/MemoryUsed", STAMUNIT_OCCURENCES, "The amount of hypervisor heap used for patches.");
3ae788d4138a852743619b65c7404deb5cbae3e7vboxsync STAM_REG(pVM, &pVM->patm.s.StatDisabled, STAMTYPE_COUNTER, "/PATM/Patch/Disabled", STAMUNIT_OCCURENCES, "Number of times patches were disabled.");
3ae788d4138a852743619b65c7404deb5cbae3e7vboxsync STAM_REG(pVM, &pVM->patm.s.StatEnabled, STAMTYPE_COUNTER, "/PATM/Patch/Enabled", STAMUNIT_OCCURENCES, "Number of times patches were enabled.");
192a1d418422c3b5905dd2577527c07a8ed8b61evboxsync STAM_REG(pVM, &pVM->patm.s.StatDirty, STAMTYPE_COUNTER, "/PATM/Patch/Dirty", STAMUNIT_OCCURENCES, "Number of times patches were marked dirty.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatUnusable, STAMTYPE_COUNTER, "/PATM/Patch/Unusable", STAMUNIT_OCCURENCES, "Number of unusable patches (conflicts).");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatInstalled, STAMTYPE_COUNTER, "/PATM/Patch/Installed", STAMUNIT_OCCURENCES, "Number of installed patches.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatInt3Callable, STAMTYPE_COUNTER, "/PATM/Patch/Int3Callable", STAMUNIT_OCCURENCES, "Number of cli patches turned into int3 patches.");
192a1d418422c3b5905dd2577527c07a8ed8b61evboxsync STAM_REG(pVM, &pVM->patm.s.StatInt3BlockRun, STAMTYPE_COUNTER, "/PATM/Patch/Run/Int3", STAMUNIT_OCCURENCES, "Number of times an int3 block patch was executed.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAMR3RegisterF(pVM, &pVM->patm.s.pGCStateHC->uPatchCalls, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Patch/Run/Normal");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatInstalledFunctionPatches, STAMTYPE_COUNTER, "/PATM/Patch/Installed/Function", STAMUNIT_OCCURENCES, "Number of installed function duplication patches.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatInstalledTrampoline, STAMTYPE_COUNTER, "/PATM/Patch/Installed/Trampoline", STAMUNIT_OCCURENCES, "Number of installed trampoline patches.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatInstalledJump, STAMTYPE_COUNTER, "/PATM/Patch/Installed/Jump", STAMUNIT_OCCURENCES, "Number of installed jump patches.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatOverwritten, STAMTYPE_COUNTER, "/PATM/Patch/Overwritten", STAMUNIT_OCCURENCES, "Number of overwritten patches.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatFixedConflicts,STAMTYPE_COUNTER, "/PATM/Patch/ConflictFixed", STAMUNIT_OCCURENCES, "Number of fixed conflicts.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatFlushed, STAMTYPE_COUNTER, "/PATM/Patch/Flushed", STAMUNIT_OCCURENCES, "Number of flushes of pages with patch jumps.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatMonitored, STAMTYPE_COUNTER, "/PATM/Patch/Monitored", STAMUNIT_OCCURENCES, "Number of patches in monitored patch pages.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync 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.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatHandleTrap, STAMTYPE_PROFILE, "/PATM/HandleTrap", STAMUNIT_TICKS_PER_CALL, "Profiling of PATMR3HandleTrap");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatPushTrap, STAMTYPE_COUNTER, "/PATM/HandleTrap/PushWP", STAMUNIT_OCCURENCES, "Number of traps due to monitored stack pages.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync 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");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync 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");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatDuplicateREQFailed, STAMTYPE_COUNTER, "/PATM/Function/DupREQ/Failed", STAMUNIT_OCCURENCES, "Nr of failed PATMR3DuplicateFunctionRequest calls");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatDuplicateREQSuccess, STAMTYPE_COUNTER, "/PATM/Function/DupREQ/Success", STAMUNIT_OCCURENCES, "Nr of successful PATMR3DuplicateFunctionRequest calls");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync 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");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatFunctionLookupInsert, STAMTYPE_COUNTER, "/PATM/Function/Lookup/Insert", STAMUNIT_OCCURENCES, "Nr of successful function address insertions");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatFunctionLookupReplace, STAMTYPE_COUNTER, "/PATM/Function/Lookup/Replace", STAMUNIT_OCCURENCES, "Nr of successful function address replacements");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync 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");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatFunctionFound, STAMTYPE_COUNTER, "/PATM/Function/Found", STAMUNIT_OCCURENCES, "Nr of successful function patch lookups in GC");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatFunctionNotFound, STAMTYPE_COUNTER, "/PATM/Function/NotFound", STAMUNIT_OCCURENCES, "Nr of failed function patch lookups in GC");
5b0a093ca572a855886faa6747ad46df859dd041vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchWrite, STAMTYPE_PROFILE, "/PATM/Write/Handle", STAMUNIT_TICKS_PER_CALL, "Profiling of PATMR3PatchWrite");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchWriteDetect, STAMTYPE_PROFILE, "/PATM/Write/Detect", STAMUNIT_TICKS_PER_CALL, "Profiling of PATMIsWriteToPatchPage");
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchWriteInterpreted, STAMTYPE_COUNTER, "/PATM/Write/Interpreted/Success", STAMUNIT_OCCURENCES, "Nr of interpreted patch writes.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchWriteInterpretedFailed, STAMTYPE_COUNTER, "/PATM/Write/Interpreted/Failed", STAMUNIT_OCCURENCES, "Nr of failed interpreted patch writes.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchRefreshSuccess, STAMTYPE_COUNTER, "/PATM/Refresh/Success", STAMUNIT_OCCURENCES, "Successful patch refreshes");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchRefreshFailed, STAMTYPE_COUNTER, "/PATM/Refresh/Failure", STAMUNIT_OCCURENCES, "Failed patch refreshes");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchPageInserted, STAMTYPE_COUNTER, "/PATM/Page/Inserted", STAMUNIT_OCCURENCES, "Nr of inserted guest pages that were patched");
5b0a093ca572a855886faa6747ad46df859dd041vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchPageRemoved, STAMTYPE_COUNTER, "/PATM/Page/Removed", STAMUNIT_OCCURENCES, "Nr of removed guest pages that were patched");
830a019ad79a45e6bf7a5419efd5a729a36e599evboxsync STAM_REG(pVM, &pVM->patm.s.StatInstrDirty, STAMTYPE_COUNTER, "/PATM/Instr/Dirty/Detected", STAMUNIT_OCCURENCES, "Number of times instructions were marked dirty.");
830a019ad79a45e6bf7a5419efd5a729a36e599evboxsync 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.");
830a019ad79a45e6bf7a5419efd5a729a36e599evboxsync 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.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatSysEnter, STAMTYPE_COUNTER, "/PATM/Emul/SysEnter", STAMUNIT_OCCURENCES, "Number of times sysenter was emulated.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatSysExit, STAMTYPE_COUNTER, "/PATM/Emul/SysExit" , STAMUNIT_OCCURENCES, "Number of times sysexit was emulated.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatEmulIret, STAMTYPE_COUNTER, "/PATM/Emul/Iret/Success", STAMUNIT_OCCURENCES, "Number of times iret was emulated.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatEmulIretFailed, STAMTYPE_COUNTER, "/PATM/Emul/Iret/Failed", STAMUNIT_OCCURENCES, "Number of times iret was emulated.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatGenRet, STAMTYPE_COUNTER, "/PATM/Gen/Ret" , STAMUNIT_OCCURENCES, "Number of generated ret instructions.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatGenRetReused, STAMTYPE_COUNTER, "/PATM/Gen/RetReused" , STAMUNIT_OCCURENCES, "Number of reused ret instructions.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatGenCall, STAMTYPE_COUNTER, "/PATM/Gen/Call", STAMUNIT_OCCURENCES, "Number of generated call instructions.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatGenJump, STAMTYPE_COUNTER, "/PATM/Gen/Jmp" , STAMUNIT_OCCURENCES, "Number of generated indirect jump instructions.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatGenPopf, STAMTYPE_COUNTER, "/PATM/Gen/Popf" , STAMUNIT_OCCURENCES, "Number of generated popf instructions.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync STAM_REG(pVM, &pVM->patm.s.StatCheckPendingIRQ, STAMTYPE_COUNTER, "/PATM/GC/CheckIRQ" , STAMUNIT_OCCURENCES, "Number of traps that ask to check for pending irqs.");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync#endif /* VBOX_WITH_STATISTICS */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("PATMCallRecord.size %d\n", PATMCallRecord.size));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("PATMCallIndirectRecord.size %d\n", PATMCallIndirectRecord.size));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("PATMRetRecord.size %d\n", PATMRetRecord.size));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("PATMJumpIndirectRecord.size %d\n", PATMJumpIndirectRecord.size));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("PATMPopf32Record.size %d\n", PATMPopf32Record.size));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("PATMIretRecord.size %d\n", PATMIretRecord.size));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("PATMStiRecord.size %d\n", PATMStiRecord.size));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("PATMCheckIFRecord.size %d\n", PATMCheckIFRecord.size));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Finalizes HMA page attributes.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @returns VBox status code.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @param pVM The VM handle.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* The GC state, stack and statistics must be read/write for the guest (supervisor only of course). */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync int rc = PGMMapSetPage(pVM, pVM->patm.s.pGCStateGC, PAGE_SIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("PATMR3InitFinalize: PGMMapSetPage failed with %Rrc!!\n", rc));
5b0a093ca572a855886faa6747ad46df859dd041vboxsync rc = PGMMapSetPage(pVM, pVM->patm.s.pGCStackGC, PATM_STACK_TOTAL_SIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("PATMR3InitFinalize: PGMMapSetPage failed with %Rrc!!\n", rc));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync rc = PGMMapSetPage(pVM, pVM->patm.s.pStatsGC, PATM_STAT_MEMSIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("PATMR3InitFinalize: PGMMapSetPage failed with %Rrc!!\n", rc));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * (Re)initializes PATM
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @param pVM The VM.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Assert alignment and sizes.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync AssertRelease(sizeof(pVM->patm.s) <= sizeof(pVM->patm.padding));
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync * Setup any fixed pointers and offsets.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync#ifndef RT_ARCH_AMD64 /* would be nice if this was changed everywhere. was driving me crazy on AMD64. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync AssertReleaseMsg(pVM->patm.s.pGCStateGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pGCStateGC));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("Patch memory allocated at %p - %RRv\n", pVM->patm.s.pPatchMemHC, pVM->patm.s.pPatchMemGC));
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync AssertReleaseMsg(pVM->patm.s.pGCStackGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pGCStackGC));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.pGCStateHC->fPIF = 1; /* PATM Interrupt Flag */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync memset(pVM->patm.s.pStatsHC, 0, PATM_STAT_MEMSIZE);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync AssertReleaseMsg(pVM->patm.s.pStatsGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pStatsGC));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Assert(pVM->patm.s.pPatchMemGC == MMHyperR3ToRC(pVM, pVM->patm.s.pPatchMemHC));
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync memset(pVM->patm.s.pPatchMemHC, 0, PATCH_MEMORY_SIZE);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync AssertReleaseMsg(pVM->patm.s.pPatchMemGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pPatchMemHC));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Needed for future patching of sldt/sgdt/sidt/str etc. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.pCPUMCtxGC = VM_RC_ADDR(pVM, CPUMQueryGuestCtxPtr(VMMGetCpu(pVM)));
5b0a093ca572a855886faa6747ad46df859dd041vboxsync Assert(pVM->patm.s.PatchLookupTreeGC == MMHyperR3ToRC(pVM, pVM->patm.s.PatchLookupTreeHC));
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync * (Re)Initialize PATM structure
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Assert(!pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Assert(!pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.offPatchMem = 16; /* don't start with zero here */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pVM->patm.s.uCurrentPatchIdx = 1; /* Index zero is a dummy */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Lowest and highest patched instruction */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr = 0;
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage = 0;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /* Generate all global functions to be used by future patches. */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /* We generate a fake patch in order to use the existing code for relocation. */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync rc = MMHyperAlloc(pVM, sizeof(PATMPATCHREC), 0, MM_TAG_PATM_PATCH, (void **)&pVM->patm.s.pGlobalPatchRec);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pVM->patm.s.pGlobalPatchRec->patch.flags = PATMFL_GLOBAL_FUNCTIONS;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pVM->patm.s.pGlobalPatchRec->patch.uState = PATCH_ENABLED;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pVM->patm.s.pGlobalPatchRec->patch.pPatchBlockOffset = pVM->patm.s.offPatchMem;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync rc = patmPatchGenGlobalFunctions(pVM, &pVM->patm.s.pGlobalPatchRec->patch);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Update free pointer in patch memory. */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pVM->patm.s.offPatchMem += pVM->patm.s.pGlobalPatchRec->patch.uCurPatchOffset;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /* Round to next 8 byte boundary. */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pVM->patm.s.offPatchMem = RT_ALIGN_32(pVM->patm.s.offPatchMem, 8);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Applies relocations to data and code managed by this
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * component. This function will be called at init and
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * whenever the VMM need to relocate it self inside the GC.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * The PATM will update the addresses used by the switcher.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @param pVM The VM.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync RTRCPTR GCPtrNew = MMHyperR3ToRC(pVM, pVM->patm.s.pGCStateHC);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync RTRCINTPTR delta = GCPtrNew - pVM->patm.s.pGCStateGC;
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Log(("PATMR3Relocate from %RRv to %RRv - delta %08X\n", pVM->patm.s.pGCStateGC, GCPtrNew, delta));
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Update CPUMCTX guest context pointer. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync RTAvloU32DoWithAll(&pVM->patm.s.PatchLookupTreeHC->PatchTree, true, RelocatePatches, (void *)pVM);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* If we are running patch code right now, then also adjust EIP. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.pPatchMemGC = MMHyperR3ToRC(pVM, pVM->patm.s.pPatchMemHC);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.pGCStackGC = MMHyperR3ToRC(pVM, pVM->patm.s.pGCStackHC);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.pStatsGC = MMHyperR3ToRC(pVM, pVM->patm.s.pStatsHC);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.PatchLookupTreeGC = MMHyperR3ToRC(pVM, pVM->patm.s.PatchLookupTreeHC);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Deal with the global patch functions. */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync RelocatePatches(&pVM->patm.s.pGlobalPatchRec->Core, (void *)pVM);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * Terminates the PATM.
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * Termination means cleaning up and freeing all resources,
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * the VM it self is at this point powered off or suspended.
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * @returns VBox status code.
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * @param pVM The VM to operate on.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Memory was all allocated from the two MM heaps and requires no freeing. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * PATM reset callback.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @returns VBox status code.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @param pVM The VM which is reset.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* Free all patches. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync while (true)
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync PPATMPATCHREC pPatchRec = (PPATMPATCHREC)RTAvloU32RemoveBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, 0, true);
192a1d418422c3b5905dd2577527c07a8ed8b61evboxsync Assert(!pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr = 0;
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage = 0;
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Read callback for disassembly function; supports reading bytes that cross a page boundary
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @returns VBox status code.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @param pSrc GC source pointer
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @param pDest HC destination pointer
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @param size Number of bytes to read
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @param pvUserdata Callback specific user data (pCpu)
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncint patmReadBytes(RTUINTPTR pSrc, uint8_t *pDest, unsigned size, void *pvUserdata)
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync PATMDISASM *pDisInfo = (PATMDISASM *)pCpu->apvUserData[0];
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * Trap/interrupt handler typically call common code on entry. Which might already have patches inserted.
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * As we currently don't support calling patch code from patch code, we'll let it read the original opcode bytes instead.
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /** @todo could change in the future! */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync for (int i=0;i<orgsize;i++)
5b0a093ca572a855886faa6747ad46df859dd041vboxsync int rc = PATMR3QueryOpcode(pDisInfo->pVM, (RTRCPTR)pSrc, pDest);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync else break;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync if ( !(pDisInfo->pPatchInfo->flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_IDTHANDLER))
5b0a093ca572a855886faa6747ad46df859dd041vboxsync Assert(PATMR3IsInsidePatchJump(pDisInfo->pVM, pSrc, NULL) == false);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync Assert(PATMR3IsInsidePatchJump(pDisInfo->pVM, pSrc+size-1, NULL) == false);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync || ( PAGE_ADDRESS(pDisInfo->pInstrGC) != PAGE_ADDRESS(pSrc + size - 1)
5b0a093ca572a855886faa6747ad46df859dd041vboxsync return PGMPhysSimpleReadGCPtr(&pDisInfo->pVM->aCpus[0], pDest, pSrc, size);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync /* pInstrHC is the base address; adjust according to the GC pointer. */
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Callback function for RTAvloU32DoWithAll
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Updates all fixups in the patches
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @returns VBox status code.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @param pNode Current node
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @param pParam The VM to operate on.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncstatic DECLCALLBACK(int) RelocatePatches(PAVLOU32NODECORE pNode, void *pParam)
4e47bb772df0d04d1ded3e06354de547d52e2d06vboxsync /* Nothing to do if the patch is not active. */
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync if (pPatch->patch.flags & PATMFL_PATCHED_GUEST_CODE)
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync cpu.mode = (pPatch->patch.flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pPatch->patch.pPrivInstrGC, NULL, &opsize, szOutput, PATMREAD_RAWCODE);
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync Log(("Nr of fixups %d\n", pPatch->patch.nrFixups));
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync * Apply fixups
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync while (true)
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync /* Get the record that's closest from above */
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync pRec = (PRELOCREC)RTAvlPVGetBestFit(&pPatch->patch.FixupTree, key, true);
daa94352f51be2329ac8660f70396e03a7cb983bvboxsync key = (AVLPVKEY)(pRec->pRelocPos + 1); /* search for the next record during the next round. */
5b0a093ca572a855886faa6747ad46df859dd041vboxsync Log(("Absolute fixup at %RRv %RHv -> %RHv at %RRv\n", pRec->pSource, *(RTRCUINTPTR *)pRec->pRelocPos, *(RTRCINTPTR*)pRec->pRelocPos + delta, pRec->pRelocPos));
5b0a093ca572a855886faa6747ad46df859dd041vboxsync Assert(pRec->pSource && pPatch->patch.cbPrivInstr <= 15);
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync Assert(!(pPatch->patch.flags & PATMFL_GLOBAL_FUNCTIONS));
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync memcpy(oldInstr, pPatch->patch.aPrivInstr, pPatch->patch.cbPrivInstr);
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync *(RTRCPTR *)&oldInstr[pPatch->patch.cbPrivInstr - sizeof(RTRCPTR)] = pRec->pDest;
5b0a093ca572a855886faa6747ad46df859dd041vboxsync rc = PGMPhysSimpleReadGCPtr(VMMGetCpu0(pVM), curInstr, pPatch->patch.pPrivInstrGC, pPatch->patch.cbPrivInstr);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync Assert(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync pRec->pDest = (RTRCPTR)((RTRCUINTPTR)pRec->pDest + delta);
5b0a093ca572a855886faa6747ad46df859dd041vboxsync RTRCPTR pPage = pPatch->patch.pPrivInstrGC & PAGE_BASE_GC_MASK;
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync Log(("PATM: Patch page not present -> check later!\n"));
c58c758d3642ac45d3f12356c406c631fcd8f538vboxsync rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_ALL, pPage, pPage + (PAGE_SIZE - 1) /* inclusive! */, 0, patmVirtPageHandler, "PATMGCMonitorPage", 0, "PATMMonitorPatchJump");
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync Assert(RT_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT);
d3dea25ec07f6546715fe3af943ea863294b392evboxsync if (memcmp(curInstr, oldInstr, pPatch->patch.cbPrivInstr))
d3dea25ec07f6546715fe3af943ea863294b392evboxsync Log(("PATM: Patch was overwritten -> disabling patch!!\n"));
d3dea25ec07f6546715fe3af943ea863294b392evboxsync * Disable patch; this is not a good solution
d3dea25ec07f6546715fe3af943ea863294b392evboxsync /* @todo hopefully it was completely overwritten (if the read was successful)!!!! */
d3dea25ec07f6546715fe3af943ea863294b392evboxsync *(RTRCPTR *)&curInstr[pPatch->patch.cbPrivInstr - sizeof(RTRCPTR)] = pRec->pDest;
d3dea25ec07f6546715fe3af943ea863294b392evboxsync rc = PGMPhysSimpleDirtyWriteGCPtr(VMMGetCpu0(pVM), pRec->pSource, curInstr, pPatch->patch.cbPrivInstr);
d3dea25ec07f6546715fe3af943ea863294b392evboxsync RTRCPTR pTarget = (RTRCPTR)((RTRCINTPTR)pRec->pDest + delta);
d3dea25ec07f6546715fe3af943ea863294b392evboxsync && (pPatch->patch.flags & PATMFL_PATCHED_GUEST_CODE))
d3dea25ec07f6546715fe3af943ea863294b392evboxsync RTRCINTPTR displ = (RTRCINTPTR)pTarget - (RTRCINTPTR)pRec->pSource;
d3dea25ec07f6546715fe3af943ea863294b392evboxsync RTRCINTPTR displOld= (RTRCINTPTR)pRec->pDest - (RTRCINTPTR)pRec->pSource;
d3dea25ec07f6546715fe3af943ea863294b392evboxsync#if 0 /** @todo '*(int32_t*)pRec->pRelocPos' crashes on restore of an XP VM here. pRelocPos=0x8000dbe2180a (bird) */
d3dea25ec07f6546715fe3af943ea863294b392evboxsync Log(("Relative fixup (g2p) %08X -> %08X at %08X (source=%08x, target=%08x)\n", *(int32_t*)pRec->pRelocPos, displ, pRec->pRelocPos, pRec->pSource, pRec->pDest));
d3dea25ec07f6546715fe3af943ea863294b392evboxsync Log(("Relative fixup (g2p) ???????? -> %08X at %08X (source=%08x, target=%08x)\n", displ, pRec->pRelocPos, pRec->pSource, pRec->pDest));
d3dea25ec07f6546715fe3af943ea863294b392evboxsync Assert(pRec->pSource - pPatch->patch.cbPatchJump == pPatch->patch.pPrivInstrGC);
d3dea25ec07f6546715fe3af943ea863294b392evboxsync if (pPatch->patch.cbPatchJump == SIZEOF_NEAR_COND_JUMP32)
d3dea25ec07f6546715fe3af943ea863294b392evboxsync Assert(pPatch->patch.flags & PATMFL_JUMP_CONFLICT);
d3dea25ec07f6546715fe3af943ea863294b392evboxsync pJumpOffGC = pPatch->patch.pPrivInstrGC + 2; //two byte opcode
d3dea25ec07f6546715fe3af943ea863294b392evboxsync if (pPatch->patch.cbPatchJump == SIZEOF_NEARJUMP32)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync pJumpOffGC = pPatch->patch.pPrivInstrGC + 1; //one byte opcode
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync AssertMsgFailed(("Invalid patch jump size %d\n", pPatch->patch.cbPatchJump));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync continue; //this should never happen!!
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * Read old patch jump and compare it to the one we previously installed
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync rc = PGMPhysSimpleReadGCPtr(VMMGetCpu0(pVM), temp, pPatch->patch.pPrivInstrGC, pPatch->patch.cbPatchJump);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync Assert(RT_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync RTRCPTR pPage = pPatch->patch.pPrivInstrGC & PAGE_BASE_GC_MASK;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_ALL, pPage, pPage + (PAGE_SIZE - 1) /* inclusive! */, 0, patmVirtPageHandler, "PATMGCMonitorPage", 0, "PATMMonitorPatchJump");
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync Assert(RT_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync if (memcmp(temp, oldJump, pPatch->patch.cbPatchJump))
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync Log(("PATM: Patch jump was overwritten -> disabling patch!!\n"));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * Disable patch; this is not a good solution
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync /* @todo hopefully it was completely overwritten (if the read was successful)!!!! */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync rc = PGMPhysSimpleDirtyWriteGCPtr(VMMGetCpu0(pVM), pJumpOffGC, &displ, sizeof(displ));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync AssertMsgFailed(("Unexpected error %d from MMR3PhysReadGCVirt\n", rc));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync Log(("Skip the guest jump to patch code for this disabled patch %RGv - %08X\n", pPatch->patch.pPrivInstrGC, pRec->pRelocPos));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync RTRCPTR pSource = (RTRCPTR)((RTRCINTPTR)pRec->pSource + delta);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync RTRCINTPTR displ = (RTRCINTPTR)pRec->pDest - (RTRCINTPTR)pSource;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync Assert(!(pPatch->patch.flags & PATMFL_GLOBAL_FUNCTIONS));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync Log(("Relative fixup (p2g) %08X -> %08X at %08X (source=%08x, target=%08x)\n", *(int32_t*)pRec->pRelocPos, displ, pRec->pRelocPos, pRec->pSource, pRec->pDest));
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync if (pPatch->patch.flags & PATMFL_PATCHED_GUEST_CODE)
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync cpu.mode = (pPatch->patch.flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pPatch->patch.pPrivInstrGC, NULL, &opsize, szOutput, PATMREAD_RAWCODE);
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * \#PF Handler callback for virtual access handler ranges.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * Important to realize that a physical page in a range can have aliases, and
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * for ALL and WRITE handlers these will also trigger.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * @returns VINF_SUCCESS if the handler have carried out the operation.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * @param pVM VM Handle.
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * @param pvPtr The HC mapping of that address.
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * @param pvBuf What the guest is reading/writing.
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * @param cbBuf How much it's reading/writing.
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * @param enmAccessType The access type.
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * @param pvUser User argument.
5b0a093ca572a855886faa6747ad46df859dd041vboxsyncDECLCALLBACK(int) patmVirtPageHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
5b0a093ca572a855886faa6747ad46df859dd041vboxsync /** @todo could be the wrong virtual address (alias) */
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * Callback function for RTAvloU32DoWithAll
4bf996d915405be92dc4394b2db1395e00e14d58vboxsync * Enables the patch that's being enumerated
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * @returns 0 (continue enumeration).
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * @param pNode Current node
5b0a093ca572a855886faa6747ad46df859dd041vboxsync * @param pVM The VM to operate on.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncstatic DECLCALLBACK(int) EnableAllPatches(PAVLOU32NODECORE pNode, void *pVM)
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync PATMR3EnablePatch((PVM)pVM, (RTRCPTR)pPatch->Core.Key);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync#endif /* VBOX_WITH_DEBUGGER */
6c2750d8e30830bf114880ca33922b108ab3e942vboxsync * Callback function for RTAvloU32DoWithAll
6c2750d8e30830bf114880ca33922b108ab3e942vboxsync * Disables the patch that's being enumerated
6c2750d8e30830bf114880ca33922b108ab3e942vboxsync * @returns 0 (continue enumeration).
6c2750d8e30830bf114880ca33922b108ab3e942vboxsync * @param pNode Current node
6c2750d8e30830bf114880ca33922b108ab3e942vboxsync * @param pVM The VM to operate on.
6c2750d8e30830bf114880ca33922b108ab3e942vboxsyncstatic DECLCALLBACK(int) DisableAllPatches(PAVLOU32NODECORE pNode, void *pVM)
6c2750d8e30830bf114880ca33922b108ab3e942vboxsync PATMR3DisablePatch((PVM)pVM, (RTRCPTR)pPatch->Core.Key);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Returns the host context pointer and size of the patch memory block
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @returns VBox status code.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @param pVM The VM to operate on.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @param pcb Size of the patch memory block
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsyncVMMR3DECL(void *) PATMR3QueryPatchMemHC(PVM pVM, uint32_t *pcb)
830a019ad79a45e6bf7a5419efd5a729a36e599evboxsync * Returns the guest context pointer and size of the patch memory block
830a019ad79a45e6bf7a5419efd5a729a36e599evboxsync * @returns VBox status code.
6ba706e9f431401d425d16817fdcd6316f83b584vboxsync * @param pVM The VM to operate on.
6ba706e9f431401d425d16817fdcd6316f83b584vboxsync * @param pcb Size of the patch memory block
6ba706e9f431401d425d16817fdcd6316f83b584vboxsyncVMMR3DECL(RTRCPTR) PATMR3QueryPatchMemGC(PVM pVM, uint32_t *pcb)
6ba706e9f431401d425d16817fdcd6316f83b584vboxsync * Returns the host context pointer of the GC context structure
6ba706e9f431401d425d16817fdcd6316f83b584vboxsync * @returns VBox status code.
6ba706e9f431401d425d16817fdcd6316f83b584vboxsync * @param pVM The VM to operate on.
6ba706e9f431401d425d16817fdcd6316f83b584vboxsyncVMMR3DECL(PPATMGCSTATE) PATMR3QueryGCStateHC(PVM pVM)
6ba706e9f431401d425d16817fdcd6316f83b584vboxsync * Checks whether the HC address is part of our patch region
6ba706e9f431401d425d16817fdcd6316f83b584vboxsync * @returns VBox status code.
6ba706e9f431401d425d16817fdcd6316f83b584vboxsync * @param pVM The VM to operate on.
6ba706e9f431401d425d16817fdcd6316f83b584vboxsync * @param pAddrGC Guest context address
6ba706e9f431401d425d16817fdcd6316f83b584vboxsyncVMMR3DECL(bool) PATMR3IsPatchHCAddr(PVM pVM, R3PTRTYPE(uint8_t *) pAddrHC)
6ba706e9f431401d425d16817fdcd6316f83b584vboxsync return (pAddrHC >= pVM->patm.s.pPatchMemHC && pAddrHC < pVM->patm.s.pPatchMemHC + pVM->patm.s.cbPatchMem) ? true : false;
6ba706e9f431401d425d16817fdcd6316f83b584vboxsync * Allows or disallow patching of privileged instructions executed by the guest OS
830a019ad79a45e6bf7a5419efd5a729a36e599evboxsync * @returns VBox status code.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @param pVM The VM to operate on.
192a1d418422c3b5905dd2577527c07a8ed8b61evboxsync * @param fAllowPatching Allow/disallow patching
192a1d418422c3b5905dd2577527c07a8ed8b61evboxsyncVMMR3DECL(int) PATMR3AllowPatching(PVM pVM, uint32_t fAllowPatching)
192a1d418422c3b5905dd2577527c07a8ed8b61evboxsync pVM->fPATMEnabled = (fAllowPatching) ? true : false;
192a1d418422c3b5905dd2577527c07a8ed8b61evboxsync * Convert a GC patch block pointer to a HC patch pointer
192a1d418422c3b5905dd2577527c07a8ed8b61evboxsync * @returns HC pointer or NULL if it's not a GC patch pointer
192a1d418422c3b5905dd2577527c07a8ed8b61evboxsync * @param pVM The VM to operate on.
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * @param pAddrGC GC pointer
830a019ad79a45e6bf7a5419efd5a729a36e599evboxsyncVMMR3DECL(R3PTRTYPE(void *)) PATMR3GCPtrToHCPtr(PVM pVM, RTRCPTR pAddrGC)
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync if (pVM->patm.s.pPatchMemGC <= pAddrGC && pVM->patm.s.pPatchMemGC + pVM->patm.s.cbPatchMem > pAddrGC)
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync return pVM->patm.s.pPatchMemHC + (pAddrGC - pVM->patm.s.pPatchMemGC);
809e0c4b84167932d92a1df4edcbab2edf0ddf25vboxsync * Query PATM state (enabled/disabled)
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;
/* 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(&pPatch2->Patch2GuestAddrTree, offsetPatch, false);
Log(("patmCreateTrampoline: generating jump to code inside patch at %RRv\n", pPatch2->pPrivInstrGC));
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;
return VINF_SUCCESS;
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;
int rc;
AssertReturn(pPatch->flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_IDTHANDLER|PATMFL_TRAPHANDLER), VERR_PATCHING_REFUSED);
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) */
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, PDBGCVAR pResult)
if (!pVM)
static DECLCALLBACK(int) patmr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
if (!pVM)