PATM.cpp revision 389b9f89a42b9efcb0d843a6a45c54418dd02e11
77b1a2d8b5dbe2c0b5200794914239fee3c8ee5dvboxsync * PATM - Dynamic Guest OS Patching Manager
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync * NOTE: Never ever reuse patch memory!!
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync * Copyright (C) 2006-2007 innotek GmbH
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync * 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 as published by the Free Software Foundation,
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync * distribution. VirtualBox OSE is distributed in the hope that it will
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync * be useful, but WITHOUT ANY WARRANTY of any kind.
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync/*******************************************************************************
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync* Header Files *
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync*******************************************************************************/
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync//#define PATM_REMOVE_PATCH_ON_TOO_MANY_TRAPS
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync//#define PATM_DISABLE_ALL
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync/*******************************************************************************
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync* Internal Functions *
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync*******************************************************************************/
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsyncstatic int patmDisableUnusablePatch(PVM pVM, RTGCPTR pInstrGC, RTGCPTR pConflictAddr, PPATCHINFO pPatch);
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsyncstatic int patmActivateInt3Patch(PVM pVM, PPATCHINFO pPatch);
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsyncstatic int patmDeactivateInt3Patch(PVM pVM, PPATCHINFO pPatch);
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsyncstatic bool patmIsCommonIDTHandlerPatch(PVM pVM, RTGCPTR pInstrGC);
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsyncstatic const char *PATMPatchType(PVM pVM, PPATCHINFO pPatch);
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsyncstatic void patmPrintStat(PVM pVM, void *pvSample, char *pszBuf, size_t cchBuf);
9496f2d398b49813176939d7a339ae513d5175efvboxsync#define patmPatchHCPtr2PatchGCPtr(pVM, pHC) (pVM->patm.s.pPatchMemGC + (pHC - pVM->patm.s.pPatchMemHC))
9496f2d398b49813176939d7a339ae513d5175efvboxsync#define patmPatchGCPtr2PatchHCPtr(pVM, pGC) (pVM->patm.s.pPatchMemHC + (pGC - pVM->patm.s.pPatchMemGC))
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsyncstatic DECLCALLBACK(int) RelocatePatches(PAVLOGCPTRNODECORE pNode, void *pParam);
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsyncstatic DECLCALLBACK(int) patmVirtPageHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsyncstatic DECLCALLBACK(int) DisableAllPatches(PAVLOGCPTRNODECORE pNode, void *pVM);
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsyncstatic DECLCALLBACK(int) patmr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsyncstatic DECLCALLBACK(int) patmr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync/** Command descriptors. */
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync /* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, pResultDesc, fFlags, pfnHandler pszSyntax, ....pszDescription */
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync { "patmon", 0, 0, NULL, 0, NULL, 0, patmr3CmdOn, "", "Enable patching." },
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync { "patmoff", 0, 0, NULL, 0, NULL, 0, patmr3CmdOff, "", "Disable patching." },
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync * Initializes the PATM.
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync * @returns VBox status code.
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync * @param pVM The VM to operate on.
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync Log(("PATMR3Init: Patch record size %d\n", sizeof(PATCHINFO)));
7766bf675357fd940d8c49e69a5d72dc6eaa6be4vboxsync AssertReleaseMsg(PATMInterruptFlag == (VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC | VM_FF_TIMER | VM_FF_REQUEST), ("Interrupt flags out of sync!!\n"));
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync /* Allocate patch memory and GC patch state memory. */
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync /* Add another page in case the generated code is much larger than expected. */
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync /** @todo bad safety precaution */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync 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);
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync pVM->patm.s.pPatchMemGC = MMHyperHC2GC(pVM, pVM->patm.s.pPatchMemHC);
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync /* PATM stack page for call instruction execution. (2 parts: one for our private stack and one to store the original return address */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync pVM->patm.s.pGCStackHC = (RTGCPTR *)(pVM->patm.s.pPatchMemHC + PATCH_MEMORY_SIZE + PAGE_SIZE);
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync pVM->patm.s.pGCStackGC = MMHyperHC2GC(pVM, pVM->patm.s.pGCStackHC);
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync * Hypervisor memory for GC status data (read/write)
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync * Note1: This is non-critical data; if trashed by the guest, then it will only cause problems for itself
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync * Note2: This doesn't really belong here, but we need access to it for relocation purposes
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync Assert(sizeof(PATMGCSTATE) < PAGE_SIZE); /** @note hardcoded dependencies on this exist. */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync pVM->patm.s.pGCStateHC = (PPATMGCSTATE)((uint8_t *)pVM->patm.s.pGCStackHC + PATM_STACK_TOTAL_SIZE);
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync pVM->patm.s.pGCStateGC = MMHyperHC2GC(pVM, pVM->patm.s.pGCStateHC);
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync /* Hypervisor memory for patch statistics */
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync pVM->patm.s.pStatsHC = (PSTAMRATIOU32)((uint8_t *)pVM->patm.s.pGCStateHC + PAGE_SIZE);
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync pVM->patm.s.pStatsGC = MMHyperHC2GC(pVM, pVM->patm.s.pStatsHC);
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync /* Memory for patch lookup trees. */
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync rc = MMHyperAlloc(pVM, sizeof(*pVM->patm.s.PatchLookupTreeHC), 0, MM_TAG_PATM, (void **)&pVM->patm.s.PatchLookupTreeHC);
c0a370e600bb60153a269fb32b5f709347c35768vboxsync pVM->patm.s.PatchLookupTreeGC = MMHyperHC2GC(pVM, pVM->patm.s.PatchLookupTreeHC);
9496f2d398b49813176939d7a339ae513d5175efvboxsync /* Check CFGM option. */
9496f2d398b49813176939d7a339ae513d5175efvboxsync rc = CFGMR3QueryBool(CFGMR3GetRoot(pVM), "PATMEnabled", &pVM->fPATMEnabled);
16a9adc14900ca18e6909679a579f6833425e030vboxsync * Register save and load state notificators.
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync rc = SSMR3RegisterInternal(pVM, "PATM", 0, PATM_SSM_VERSION, sizeof(pVM->patm.s) + PATCH_MEMORY_SIZE + PAGE_SIZE + PATM_STACK_TOTAL_SIZE + PAGE_SIZE,
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync * Debugger commands.
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync static bool fRegisteredCmds = false;
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync int rc = DBGCRegisterCommands(&g_aCmds[0], ELEMENTS(g_aCmds));
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync STAM_REG(pVM, &pVM->patm.s.StatNrOpcodeRead, STAMTYPE_COUNTER, "/PATM/OpcodeBytesRead", STAMUNIT_OCCURENCES, "The number of opcode bytes read by the recompiler.");
009d45aa55691312278d41edb20154dc208d9cd8vboxsync STAM_REG(pVM, &pVM->patm.s.StatPATMMemoryUsed,STAMTYPE_COUNTER, "/PATM/MemoryUsed", STAMUNIT_OCCURENCES, "The amount of hypervisor heap used for patches.");
009d45aa55691312278d41edb20154dc208d9cd8vboxsync STAM_REG(pVM, &pVM->patm.s.StatDisabled, STAMTYPE_COUNTER, "/PATM/Patch/Disabled", STAMUNIT_OCCURENCES, "Number of times patches were disabled.");
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync STAM_REG(pVM, &pVM->patm.s.StatEnabled, STAMTYPE_COUNTER, "/PATM/Patch/Enabled", STAMUNIT_OCCURENCES, "Number of times patches were enabled.");
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync STAM_REG(pVM, &pVM->patm.s.StatDirty, STAMTYPE_COUNTER, "/PATM/Patch/Dirty", STAMUNIT_OCCURENCES, "Number of times patches were marked dirty.");
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync STAM_REG(pVM, &pVM->patm.s.StatUnusable, STAMTYPE_COUNTER, "/PATM/Patch/Unusable", STAMUNIT_OCCURENCES, "Number of unusable patches (conflicts).");
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync STAM_REG(pVM, &pVM->patm.s.StatInstalled, STAMTYPE_COUNTER, "/PATM/Patch/Installed", STAMUNIT_OCCURENCES, "Number of installed patches.");
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync STAM_REG(pVM, &pVM->patm.s.StatInt3Callable, STAMTYPE_COUNTER, "/PATM/Patch/Int3Callable", STAMUNIT_OCCURENCES, "Number of cli patches turned into int3 patches.");
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync STAM_REG(pVM, &pVM->patm.s.StatInt3BlockRun, STAMTYPE_COUNTER, "/PATM/Patch/Run/Int3", STAMUNIT_OCCURENCES, "Number of times an int3 block patch was executed.");
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync STAMR3RegisterF(pVM, &pVM->patm.s.pGCStateHC->uPatchCalls, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Patch/Run/Normal");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatInstalledFunctionPatches, STAMTYPE_COUNTER, "/PATM/Patch/Installed/Function", STAMUNIT_OCCURENCES, "Number of installed function duplication patches.");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatInstalledTrampoline, STAMTYPE_COUNTER, "/PATM/Patch/Installed/Trampoline", STAMUNIT_OCCURENCES, "Number of installed trampoline patches.");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatInstalledJump, STAMTYPE_COUNTER, "/PATM/Patch/Installed/Jump", STAMUNIT_OCCURENCES, "Number of installed jump patches.");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatOverwritten, STAMTYPE_COUNTER, "/PATM/Patch/Overwritten", STAMUNIT_OCCURENCES, "Number of overwritten patches.");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatFixedConflicts,STAMTYPE_COUNTER, "/PATM/Patch/ConflictFixed", STAMUNIT_OCCURENCES, "Number of fixed conflicts.");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatFlushed, STAMTYPE_COUNTER, "/PATM/Patch/Flushed", STAMUNIT_OCCURENCES, "Number of flushes of pages with patch jumps.");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatMonitored, STAMTYPE_COUNTER, "/PATM/Patch/Monitored", STAMUNIT_OCCURENCES, "Number of patches in monitored patch pages.");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync 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.");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatHandleTrap, STAMTYPE_PROFILE, "/PATM/HandleTrap", STAMUNIT_TICKS_PER_CALL, "Profiling of PATMR3HandleTrap");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatPushTrap, STAMTYPE_COUNTER, "/PATM/HandleTrap/PushWP", STAMUNIT_OCCURENCES, "Number of traps due to monitored stack pages.");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync 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");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync 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");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatDuplicateREQFailed, STAMTYPE_COUNTER, "/PATM/Function/DupREQ/Failed", STAMUNIT_OCCURENCES, "Nr of failed PATMR3DuplicateFunctionRequest calls");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatDuplicateREQSuccess, STAMTYPE_COUNTER, "/PATM/Function/DupREQ/Success", STAMUNIT_OCCURENCES, "Nr of successful PATMR3DuplicateFunctionRequest calls");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync 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");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatFunctionLookupInsert, STAMTYPE_COUNTER, "/PATM/Function/Lookup/Insert", STAMUNIT_OCCURENCES, "Nr of successful function address insertions");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatFunctionLookupReplace, STAMTYPE_COUNTER, "/PATM/Function/Lookup/Replace", STAMUNIT_OCCURENCES, "Nr of successful function address replacements");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync 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");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatFunctionFound, STAMTYPE_COUNTER, "/PATM/Function/Found", STAMUNIT_OCCURENCES, "Nr of successful function patch lookups in GC");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatFunctionNotFound, STAMTYPE_COUNTER, "/PATM/Function/NotFound", STAMUNIT_OCCURENCES, "Nr of failed function patch lookups in GC");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchWrite, STAMTYPE_PROFILE, "/PATM/Write/Handle", STAMUNIT_TICKS_PER_CALL, "Profiling of PATMR3PatchWrite");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchWriteDetect, STAMTYPE_PROFILE, "/PATM/Write/Detect", STAMUNIT_TICKS_PER_CALL, "Profiling of PATMIsWriteToPatchPage");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchWriteInterpreted, STAMTYPE_COUNTER, "/PATM/Write/Interpreted/Success", STAMUNIT_OCCURENCES, "Nr of interpreted patch writes.");
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchWriteInterpretedFailed, STAMTYPE_COUNTER, "/PATM/Write/Interpreted/Failed", STAMUNIT_OCCURENCES, "Nr of failed interpreted patch writes.");
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchRefreshSuccess, STAMTYPE_COUNTER, "/PATM/Refresh/Success", STAMUNIT_OCCURENCES, "Successful patch refreshes");
16a9adc14900ca18e6909679a579f6833425e030vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchRefreshFailed, STAMTYPE_COUNTER, "/PATM/Refresh/Failure", STAMUNIT_OCCURENCES, "Failed patch refreshes");
16a9adc14900ca18e6909679a579f6833425e030vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchPageInserted, STAMTYPE_COUNTER, "/PATM/Page/Inserted", STAMUNIT_OCCURENCES, "Nr of inserted guest pages that were patched");
16a9adc14900ca18e6909679a579f6833425e030vboxsync STAM_REG(pVM, &pVM->patm.s.StatPatchPageRemoved, STAMTYPE_COUNTER, "/PATM/Page/Removed", STAMUNIT_OCCURENCES, "Nr of removed guest pages that were patched");
16a9adc14900ca18e6909679a579f6833425e030vboxsync STAM_REG(pVM, &pVM->patm.s.StatInstrDirty, STAMTYPE_COUNTER, "/PATM/Instr/Dirty/Detected", STAMUNIT_OCCURENCES, "Number of times instructions were marked dirty.");
16a9adc14900ca18e6909679a579f6833425e030vboxsync 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.");
16a9adc14900ca18e6909679a579f6833425e030vboxsync 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.");
16a9adc14900ca18e6909679a579f6833425e030vboxsync STAM_REG(pVM, &pVM->patm.s.StatSysEnter, STAMTYPE_COUNTER, "/PATM/Emul/SysEnter", STAMUNIT_OCCURENCES, "Number of times sysenter was emulated.");
16a9adc14900ca18e6909679a579f6833425e030vboxsync STAM_REG(pVM, &pVM->patm.s.StatSysExit, STAMTYPE_COUNTER, "/PATM/Emul/SysExit" , STAMUNIT_OCCURENCES, "Number of times sysexit was emulated.");
16a9adc14900ca18e6909679a579f6833425e030vboxsync STAM_REG(pVM, &pVM->patm.s.StatEmulIret, STAMTYPE_COUNTER, "/PATM/Emul/Iret/Success", STAMUNIT_OCCURENCES, "Number of times iret was emulated.");
16a9adc14900ca18e6909679a579f6833425e030vboxsync STAM_REG(pVM, &pVM->patm.s.StatEmulIretFailed, STAMTYPE_COUNTER, "/PATM/Emul/Iret/Failed", STAMUNIT_OCCURENCES, "Number of times iret was emulated.");
16a9adc14900ca18e6909679a579f6833425e030vboxsync STAM_REG(pVM, &pVM->patm.s.StatGenRet, STAMTYPE_COUNTER, "/PATM/Gen/Ret" , STAMUNIT_OCCURENCES, "Number of generated ret instructions.");
16a9adc14900ca18e6909679a579f6833425e030vboxsync STAM_REG(pVM, &pVM->patm.s.StatGenRetReused, STAMTYPE_COUNTER, "/PATM/Gen/RetReused" , STAMUNIT_OCCURENCES, "Number of reused ret instructions.");
16a9adc14900ca18e6909679a579f6833425e030vboxsync STAM_REG(pVM, &pVM->patm.s.StatGenCall, STAMTYPE_COUNTER, "/PATM/Gen/Call", STAMUNIT_OCCURENCES, "Number of generated call instructions.");
16a9adc14900ca18e6909679a579f6833425e030vboxsync STAM_REG(pVM, &pVM->patm.s.StatGenJump, STAMTYPE_COUNTER, "/PATM/Gen/Jmp" , STAMUNIT_OCCURENCES, "Number of generated indirect jump instructions.");
16a9adc14900ca18e6909679a579f6833425e030vboxsync STAM_REG(pVM, &pVM->patm.s.StatGenPopf, STAMTYPE_COUNTER, "/PATM/Gen/Popf" , STAMUNIT_OCCURENCES, "Number of generated popf instructions.");
16a9adc14900ca18e6909679a579f6833425e030vboxsync STAM_REG(pVM, &pVM->patm.s.StatCheckPendingIRQ, STAMTYPE_COUNTER, "/PATM/GC/CheckIRQ" , STAMUNIT_OCCURENCES, "Number of traps that ask to check for pending irqs.");
16a9adc14900ca18e6909679a579f6833425e030vboxsync#endif /* VBOX_WITH_STATISTICS */
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync Log(("PATMCallRecord.size %d\n", PATMCallRecord.size));
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync Log(("PATMCallIndirectRecord.size %d\n", PATMCallIndirectRecord.size));
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync Log(("PATMRetRecord.size %d\n", PATMRetRecord.size));
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync Log(("PATMJumpIndirectRecord.size %d\n", PATMJumpIndirectRecord.size));
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync Log(("PATMPopf32Record.size %d\n", PATMPopf32Record.size));
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync Log(("PATMIretRecord.size %d\n", PATMIretRecord.size));
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync Log(("PATMStiRecord.size %d\n", PATMStiRecord.size));
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync Log(("PATMCheckIFRecord.size %d\n", PATMCheckIFRecord.size));
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync * Finalizes HMA page attributes.
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync * @returns VBox status code.
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync * @param pVM The VM handle.
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync /* The GC state, stack and statistics must be read/write for the guest (supervisor only of course). */
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync int rc = PGMMapSetPage(pVM, pVM->patm.s.pGCStateGC, PAGE_SIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync Log(("PATMR3InitFinalize: PGMMapSetPage failed with %Vrc!!\n", rc));
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync rc = PGMMapSetPage(pVM, pVM->patm.s.pGCStackGC, PATM_STACK_TOTAL_SIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync Log(("PATMR3InitFinalize: PGMMapSetPage failed with %Vrc!!\n", rc));
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync rc = PGMMapSetPage(pVM, pVM->patm.s.pStatsGC, PATM_STAT_MEMSIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync Log(("PATMR3InitFinalize: PGMMapSetPage failed with %Vrc!!\n", rc));
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync * (Re)initializes PATM
a9d49c8f2b28a72e6a4db86eee91e4569290157bvboxsync * @param pVM The VM.
9496f2d398b49813176939d7a339ae513d5175efvboxsync * Assert alignment and sizes.
59d7f5939d42ad9d344fbad8401e00a900db92c5vboxsync AssertRelease(sizeof(pVM->patm.s) <= sizeof(pVM->patm.padding));
9496f2d398b49813176939d7a339ae513d5175efvboxsync * Setup any fixed pointers and offsets.
aa834e89e076db44fa8fe82d177748f0a45d14c2vboxsync#ifndef RT_ARCH_AMD64 /* would be nice if this was changed everywhere. was driving me crazy on AMD64. */
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync AssertReleaseMsg(pVM->patm.s.pGCStateGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pGCStateGC));
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync Log(("Patch memory allocated at %p - %VGv\n", pVM->patm.s.pPatchMemHC, pVM->patm.s.pPatchMemGC));
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync AssertReleaseMsg(pVM->patm.s.pGCStackGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pGCStackGC));
d5ea45cc92d7f1d3ade8189944531f665bfe8ed5vboxsync pVM->patm.s.pGCStateHC->fPIF = 1; /* PATM Interrupt Flag */
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync memset(pVM->patm.s.pStatsHC, 0, PATM_STAT_MEMSIZE);
9496f2d398b49813176939d7a339ae513d5175efvboxsync AssertReleaseMsg(pVM->patm.s.pStatsGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pStatsGC));
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync Assert(pVM->patm.s.pPatchMemGC = MMHyperHC2GC(pVM, pVM->patm.s.pPatchMemHC));
468c2bcb36eb9a032f5dd0fcb34db10bd58e9996vboxsync memset(pVM->patm.s.pPatchMemHC, 0, PATCH_MEMORY_SIZE);
bbede9c189def47a9880f0ffb03c0c230c774185vboxsync AssertReleaseMsg(pVM->patm.s.pPatchMemGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pPatchMemHC));
16a9adc14900ca18e6909679a579f6833425e030vboxsync /* Needed for future patching of sldt/sgdt/sidt/str etc. */
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync rc = CPUMR3QueryGuestCtxGCPtr(pVM, &pVM->patm.s.pCPUMCtxGC);
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync Assert(pVM->patm.s.PatchLookupTreeGC == MMHyperHC2GC(pVM, pVM->patm.s.PatchLookupTreeHC));
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync * (Re)Initialize PATM structure
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync Assert(!pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr);
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync Assert(!pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage);
fbf08fabb4c4b383d6aa2830c2bd5b943a26f10cvboxsync pVM->patm.s.offPatchMem = 16; /* don't start with zero here */
fbf08fabb4c4b383d6aa2830c2bd5b943a26f10cvboxsync pVM->patm.s.uCurrentPatchIdx = 1; /* Index zero is a dummy */
adefd5e9babba486cba0cfae52f5a0f6c8c4ef24vboxsync /* Lowest and highest patched instruction */
ba74637cb4d2e749337d51ccbfb1038bdd3e2092vboxsync pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr = 0;
61d064a54f03596920c3918f58ecc7764074a5d8vboxsync pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage = 0;
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync /* Generate all global functions to be used by future patches. */
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync /* We generate a fake patch in order to use the existing code for relocation. */
b5ad839a3757b305d4e98d7264da2b53c9cd27f0vboxsync rc = MMHyperAlloc(pVM, sizeof(PATMPATCHREC), 0, MM_TAG_PATM_PATCH, (void **)&pVM->patm.s.pGlobalPatchRec);
b5ad839a3757b305d4e98d7264da2b53c9cd27f0vboxsync pVM->patm.s.pGlobalPatchRec->patch.flags = PATMFL_GLOBAL_FUNCTIONS;
b5ad839a3757b305d4e98d7264da2b53c9cd27f0vboxsync pVM->patm.s.pGlobalPatchRec->patch.uState = PATCH_ENABLED;
b5ad839a3757b305d4e98d7264da2b53c9cd27f0vboxsync pVM->patm.s.pGlobalPatchRec->patch.pPatchBlockOffset = pVM->patm.s.offPatchMem;
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync rc = patmPatchGenGlobalFunctions(pVM, &pVM->patm.s.pGlobalPatchRec->patch);
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync /* Update free pointer in patch memory. */
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync pVM->patm.s.offPatchMem += pVM->patm.s.pGlobalPatchRec->patch.uCurPatchOffset;
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync /* Round to next 8 byte boundary. */
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync pVM->patm.s.offPatchMem = RT_ALIGN_32(pVM->patm.s.offPatchMem, 8);
9496f2d398b49813176939d7a339ae513d5175efvboxsync * Applies relocations to data and code managed by this
9496f2d398b49813176939d7a339ae513d5175efvboxsync * component. This function will be called at init and
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync * whenever the VMM need to relocate it self inside the GC.
16a9adc14900ca18e6909679a579f6833425e030vboxsync * The PATM will update the addresses used by the switcher.
9496f2d398b49813176939d7a339ae513d5175efvboxsync * @param pVM The VM.
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync RTGCPTR GCPtrNew = MMHyperHC2GC(pVM, pVM->patm.s.pGCStateHC);
9496f2d398b49813176939d7a339ae513d5175efvboxsync RTGCINTPTR delta = GCPtrNew - pVM->patm.s.pGCStateGC;
9496f2d398b49813176939d7a339ae513d5175efvboxsync Log(("PATMR3Relocate from %VGv to %VGv - delta %08X\n", pVM->patm.s.pGCStateGC, GCPtrNew, delta));
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync /* Update CPUMCTX guest context pointer. */
ce03ea57fdcf3d48523b1de5b894feb75e1b34davboxsync RTAvloGCPtrDoWithAll(&pVM->patm.s.PatchLookupTreeHC->PatchTree, true, RelocatePatches, (void *)pVM);
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync /* If we are running patch code right now, then also adjust EIP. */
ce03ea57fdcf3d48523b1de5b894feb75e1b34davboxsync pVM->patm.s.pPatchMemGC = MMHyperHC2GC(pVM, pVM->patm.s.pPatchMemHC);
ce03ea57fdcf3d48523b1de5b894feb75e1b34davboxsync pVM->patm.s.pGCStackGC = MMHyperHC2GC(pVM, pVM->patm.s.pGCStackHC);
ce03ea57fdcf3d48523b1de5b894feb75e1b34davboxsync pVM->patm.s.pStatsGC = MMHyperHC2GC(pVM, pVM->patm.s.pStatsHC);
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync pVM->patm.s.PatchLookupTreeGC = MMHyperHC2GC(pVM, pVM->patm.s.PatchLookupTreeHC);
9496f2d398b49813176939d7a339ae513d5175efvboxsync /* Deal with the global patch functions. */
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync RelocatePatches(&pVM->patm.s.pGlobalPatchRec->Core, (void *)pVM);
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync * Terminates the PATM.
9496f2d398b49813176939d7a339ae513d5175efvboxsync * Termination means cleaning up and freeing all resources,
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync * the VM it self is at this point powered off or suspended.
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync * @returns VBox status code.
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync * @param pVM The VM to operate on.
ce03ea57fdcf3d48523b1de5b894feb75e1b34davboxsync /* Memory was all allocated from the two MM heaps and requires no freeing. */
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync * PATM reset callback.
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync * @returns VBox status code.
7766bf675357fd940d8c49e69a5d72dc6eaa6be4vboxsync * @param pVM The VM which is reset.
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync /* Free all patches. */
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync while (true)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync PPATMPATCHREC pPatchRec = (PPATMPATCHREC)RTAvloGCPtrRemoveBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, 0, true);
9496f2d398b49813176939d7a339ae513d5175efvboxsync Assert(!pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage);
ce03ea57fdcf3d48523b1de5b894feb75e1b34davboxsync pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr = 0;
ce03ea57fdcf3d48523b1de5b894feb75e1b34davboxsync pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage = 0;
9496f2d398b49813176939d7a339ae513d5175efvboxsync * Read callback for disassembly function; supports reading bytes that cross a page boundary
a9f41cb889f53e8407561a6155052c441eb0fc5fvboxsync * @returns VBox status code.
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync * @param pSrc GC source pointer
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync * @param pDest HC destination pointer
9496f2d398b49813176939d7a339ae513d5175efvboxsync * @param size Number of bytes to read
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @param pvUserdata Callback specific user data (pCpu)
c7551981eb6d97331da479f68f14a9c56247e4f7vboxsyncint patmReadBytes(RTHCUINTPTR pSrc, uint8_t *pDest, unsigned size, void *pvUserdata)
c7551981eb6d97331da479f68f14a9c56247e4f7vboxsync PATMDISASM *pDisInfo = (PATMDISASM *)pCpu->apvUserData[0];
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync * Trap/interrupt handler typically call common code on entry. Which might already have patches inserted.
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync * As we currently don't support calling patch code from patch code, we'll let it read the original opcode bytes instead.
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync /** @todo could change in the future! */
c7551981eb6d97331da479f68f14a9c56247e4f7vboxsync for (int i=0;i<orgsize;i++)
c7551981eb6d97331da479f68f14a9c56247e4f7vboxsync int rc = PATMR3QueryOpcode(pDisInfo->pVM, (RTGCPTR)pSrc, pDest);
5341459ca931b65de60b5af2a4cba6836b6b45cavboxsync else break;
c7551981eb6d97331da479f68f14a9c56247e4f7vboxsync if ( !(pDisInfo->pPatchInfo->flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_IDTHANDLER))
c7551981eb6d97331da479f68f14a9c56247e4f7vboxsync Assert(PATMR3IsInsidePatchJump(pDisInfo->pVM, pSrc, NULL) == false);
c7551981eb6d97331da479f68f14a9c56247e4f7vboxsync Assert(PATMR3IsInsidePatchJump(pDisInfo->pVM, pSrc+size-1, NULL) == false);
369a8817da53dbd5ea6ac360ca0376dba003cde4vboxsync if (PAGE_ADDRESS(pDisInfo->pInstrGC) != PAGE_ADDRESS(pSrc + size - 1) && !PATMIsPatchGCAddr(pDisInfo->pVM, pSrc))
369a8817da53dbd5ea6ac360ca0376dba003cde4vboxsync return PGMPhysReadGCPtr(pDisInfo->pVM, pDest, pSrc, size);
369a8817da53dbd5ea6ac360ca0376dba003cde4vboxsync /* pInstrHC is the base address; adjust according to the GC pointer. */
16a9adc14900ca18e6909679a579f6833425e030vboxsync * Callback function for RTAvloGCPtrDoWithAll
16a9adc14900ca18e6909679a579f6833425e030vboxsync * Updates all fixups in the patches
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @returns VBox status code.
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @param pNode Current node
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @param pParam The VM to operate on.
16a9adc14900ca18e6909679a579f6833425e030vboxsyncstatic DECLCALLBACK(int) RelocatePatches(PAVLOGCPTRNODECORE pNode, void *pParam)
16a9adc14900ca18e6909679a579f6833425e030vboxsync /* Nothing to do if the patch is not active. */
16a9adc14900ca18e6909679a579f6833425e030vboxsync if (pPatch->patch.flags & PATMFL_PATCHED_GUEST_CODE)
16a9adc14900ca18e6909679a579f6833425e030vboxsync /** @note pPrivInstrHC is probably not valid anymore */
16a9adc14900ca18e6909679a579f6833425e030vboxsync rc = PGMPhysGCPtr2HCPtr(pVM, pPatch->patch.pPrivInstrGC, (PRTHCPTR)&pPatch->patch.pPrivInstrHC);
16a9adc14900ca18e6909679a579f6833425e030vboxsync cpu.mode = (pPatch->patch.flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
9c149a2789022f5011e88fb62f02a1cc8068e88fvboxsync disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pPatch->patch.pPrivInstrGC, pPatch->patch.pPrivInstrHC, &opsize, szOutput, PATMREAD_RAWCODE);
975ad9d9bc9c4dc96b41d9f67a65228b1b338e2avboxsync Log(("Nr of fixups %d\n", pPatch->patch.nrFixups));
9c149a2789022f5011e88fb62f02a1cc8068e88fvboxsync * Apply fixups
9c149a2789022f5011e88fb62f02a1cc8068e88fvboxsync while (true)
9c149a2789022f5011e88fb62f02a1cc8068e88fvboxsync /* Get the record that's closest from above */
9c149a2789022f5011e88fb62f02a1cc8068e88fvboxsync pRec = (PRELOCREC)RTAvlPVGetBestFit(&pPatch->patch.FixupTree, key, true);
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync key = (AVLPVKEY)(pRec->pRelocPos + 1); /* search for the next record during the next round. */
8dbf70ba2345e69b0b6d45c38cf1add0ef10591cvboxsync Log(("Absolute fixup at %VGv %VHv -> %VHv at %VGv\n", pRec->pSource, *(RTGCUINTPTR*)pRec->pRelocPos, *(RTGCINTPTR*)pRec->pRelocPos + delta, pRec->pRelocPos));
8dbf70ba2345e69b0b6d45c38cf1add0ef10591cvboxsync if (!pRec->pSource || PATMIsPatchGCAddr(pVM, pRec->pSource))
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync Assert(pRec->pSource && pPatch->patch.cbPrivInstr <= 15);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync Assert(!(pPatch->patch.flags & PATMFL_GLOBAL_FUNCTIONS));
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync memcpy(oldInstr, pPatch->patch.aPrivInstr, pPatch->patch.cbPrivInstr);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync *(RTGCPTR *)&oldInstr[pPatch->patch.cbPrivInstr - sizeof(RTGCPTR)] = pRec->pDest;
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync rc = PGMPhysReadGCPtr(pVM, curInstr, pPatch->patch.pPrivInstrGC, pPatch->patch.cbPrivInstr);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync Assert(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pRec->pDest = (RTGCPTR)((RTGCUINTPTR)pRec->pDest + delta);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync RTGCPTR pPage = pPatch->patch.pPrivInstrGC & PAGE_BASE_GC_MASK;
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync Log(("PATM: Patch page not present -> check later!\n"));
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_ALL, pPage, pPage + (PAGE_SIZE - 1) /* inclusive! */, 0, patmVirtPageHandler, "PATMGCMonitorPage", 0, "PATMMonitorPatchJump");
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync Assert(VBOX_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync if (memcmp(curInstr, oldInstr, pPatch->patch.cbPrivInstr))
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync Log(("PATM: Patch was overwritten -> disabling patch!!\n"));
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * Disable patch; this is not a good solution
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync /* @todo hopefully it was completely overwritten (if the read was successful)!!!! */
8dbf70ba2345e69b0b6d45c38cf1add0ef10591cvboxsync *(RTGCPTR *)&curInstr[pPatch->patch.cbPrivInstr - sizeof(RTGCPTR)] = pRec->pDest;
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync rc = PGMPhysWriteGCPtrDirty(pVM, pRec->pSource, curInstr, pPatch->patch.cbPrivInstr);
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync RTGCPTR pTarget = (RTGCPTR)((RTGCINTPTR)pRec->pDest + delta);
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync && (pPatch->patch.flags & PATMFL_PATCHED_GUEST_CODE))
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync RTGCINTPTR displ = (RTGCINTPTR)pTarget - (RTGCINTPTR)pRec->pSource;
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync RTGCINTPTR displOld= (RTGCINTPTR)pRec->pDest - (RTGCINTPTR)pRec->pSource;
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync Log(("Relative fixup (g2p) %08X -> %08X at %08X (source=%08x, target=%08x)\n", *(int32_t*)pRec->pRelocPos, displ, pRec->pRelocPos, pRec->pSource, pRec->pDest));
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync Assert(pRec->pSource - pPatch->patch.cbPatchJump == pPatch->patch.pPrivInstrGC);
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync if (pPatch->patch.cbPatchJump == SIZEOF_NEAR_COND_JUMP32)
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync Assert(pPatch->patch.flags & PATMFL_JUMP_CONFLICT);
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync pJumpOffGC = pPatch->patch.pPrivInstrGC + 2; //two byte opcode
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync if (pPatch->patch.cbPatchJump == SIZEOF_NEARJUMP32)
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync pJumpOffGC = pPatch->patch.pPrivInstrGC + 1; //one byte opcode
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync AssertMsgFailed(("Invalid patch jump size %d\n", pPatch->patch.cbPatchJump));
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync continue; //this should never happen!!
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync * Read old patch jump and compare it to the one we previously installed
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync rc = PGMPhysReadGCPtr(pVM, temp, pPatch->patch.pPrivInstrGC, pPatch->patch.cbPatchJump);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync Assert(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync RTGCPTR pPage = pPatch->patch.pPrivInstrGC & PAGE_BASE_GC_MASK;
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_ALL, pPage, pPage + (PAGE_SIZE - 1) /* inclusive! */, 0, patmVirtPageHandler, "PATMGCMonitorPage", 0, "PATMMonitorPatchJump");
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync Assert(VBOX_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT);
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync if (memcmp(temp, oldJump, pPatch->patch.cbPatchJump))
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync Log(("PATM: Patch jump was overwritten -> disabling patch!!\n"));
251b3801f86afc901bee955a7e5a8d14b5836e74vboxsync * Disable patch; this is not a good solution
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync /* @todo hopefully it was completely overwritten (if the read was successful)!!!! */
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync rc = PGMPhysWriteGCPtrDirty(pVM, pJumpOffGC, &displ, sizeof(displ));
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync AssertMsgFailed(("Unexpected error %d from MMR3PhysReadGCVirt\n", rc));
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync Log(("Skip the guest jump to patch code for this disabled patch %08X - %08X\n", pPatch->patch.pPrivInstrHC, pRec->pRelocPos));
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync RTGCPTR pSource = (RTGCPTR)((RTGCINTPTR)pRec->pSource + delta);
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync RTGCINTPTR displ = (RTGCINTPTR)pRec->pDest - (RTGCINTPTR)pSource;
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync Assert(!(pPatch->patch.flags & PATMFL_GLOBAL_FUNCTIONS));
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync Log(("Relative fixup (p2g) %08X -> %08X at %08X (source=%08x, target=%08x)\n", *(int32_t*)pRec->pRelocPos, displ, pRec->pRelocPos, pRec->pSource, pRec->pDest));
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync if (pPatch->patch.flags & PATMFL_PATCHED_GUEST_CODE)
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync /** @note pPrivInstrHC is probably not valid anymore */
59a2c1c0a4a0762b46bc5ff056f5705ec9c0a660vboxsync rc = PGMPhysGCPtr2HCPtr(pVM, pPatch->patch.pPrivInstrGC, (PRTHCPTR)&pPatch->patch.pPrivInstrHC);
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync cpu.mode = (pPatch->patch.flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pPatch->patch.pPrivInstrGC, pPatch->patch.pPrivInstrHC, &opsize, szOutput, PATMREAD_RAWCODE);
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync * #PF Handler callback for virtual access handler ranges.
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync * Important to realize that a physical page in a range can have aliases, and
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync * for ALL and WRITE handlers these will also trigger.
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync * @returns VINF_SUCCESS if the handler have carried out the operation.
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync * @param pVM VM Handle.
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync * @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pvPtr The HC mapping of that address.
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync * @param pvBuf What the guest is reading/writing.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param cbBuf How much it's reading/writing.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param enmAccessType The access type.
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync * @param pvUser User argument.
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsyncstatic DECLCALLBACK(int) patmVirtPageHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync /** @todo could be the wrong virtual address (alias) */
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync * Callback function for RTAvloGCPtrDoWithAll
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync * Enables the patch that's being enumerated
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync * @returns 0 (continue enumeration).
8dbf70ba2345e69b0b6d45c38cf1add0ef10591cvboxsync * @param pNode Current node
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pVM The VM to operate on.
8dbf70ba2345e69b0b6d45c38cf1add0ef10591cvboxsyncstatic DECLCALLBACK(int) EnableAllPatches(PAVLOGCPTRNODECORE pNode, void *pVM)
4bd3e7685494afe7c303fc131c66e685023b6b4avboxsync PATMR3EnablePatch((PVM)pVM, (RTGCPTR)pPatch->Core.Key);
4bd3e7685494afe7c303fc131c66e685023b6b4avboxsync#endif /* VBOX_WITH_DEBUGGER */
8dbf70ba2345e69b0b6d45c38cf1add0ef10591cvboxsync * Callback function for RTAvloGCPtrDoWithAll
8dbf70ba2345e69b0b6d45c38cf1add0ef10591cvboxsync * Disables the patch that's being enumerated
8dbf70ba2345e69b0b6d45c38cf1add0ef10591cvboxsync * @returns 0 (continue enumeration).
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync * @param pNode Current node
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pVM The VM to operate on.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsyncstatic DECLCALLBACK(int) DisableAllPatches(PAVLOGCPTRNODECORE pNode, void *pVM)
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync PATMR3DisablePatch((PVM)pVM, (RTGCPTR)pPatch->Core.Key);
8dbf70ba2345e69b0b6d45c38cf1add0ef10591cvboxsync * Returns the host context pointer and size of the patch memory block
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @returns VBox status code.
8dbf70ba2345e69b0b6d45c38cf1add0ef10591cvboxsync * @param pVM The VM to operate on.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pcb Size of the patch memory block
8dbf70ba2345e69b0b6d45c38cf1add0ef10591cvboxsyncPATMR3DECL(void *) PATMR3QueryPatchMemHC(PVM pVM, uint32_t *pcb)
8dbf70ba2345e69b0b6d45c38cf1add0ef10591cvboxsync * Returns the guest context pointer and size of the patch memory block
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @returns VBox status code.
d571b6e3237f0ce89ea27f6fa4635d41c5ee3d88vboxsync * @param pVM The VM to operate on.
d571b6e3237f0ce89ea27f6fa4635d41c5ee3d88vboxsync * @param pcb Size of the patch memory block
d571b6e3237f0ce89ea27f6fa4635d41c5ee3d88vboxsyncPATMR3DECL(RTGCPTR) PATMR3QueryPatchMemGC(PVM pVM, uint32_t *pcb)
d571b6e3237f0ce89ea27f6fa4635d41c5ee3d88vboxsync * Returns the host context pointer of the GC context structure
d571b6e3237f0ce89ea27f6fa4635d41c5ee3d88vboxsync * @returns VBox status code.
d571b6e3237f0ce89ea27f6fa4635d41c5ee3d88vboxsync * @param pVM The VM to operate on.
d571b6e3237f0ce89ea27f6fa4635d41c5ee3d88vboxsyncPATMR3DECL(PPATMGCSTATE) PATMR3QueryGCStateHC(PVM pVM)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * Checks whether the HC address is part of our patch region
d571b6e3237f0ce89ea27f6fa4635d41c5ee3d88vboxsync * @returns VBox status code.
d571b6e3237f0ce89ea27f6fa4635d41c5ee3d88vboxsync * @param pVM The VM to operate on.
d571b6e3237f0ce89ea27f6fa4635d41c5ee3d88vboxsync * @param pAddrGC Guest context address
d571b6e3237f0ce89ea27f6fa4635d41c5ee3d88vboxsyncPATMR3DECL(bool) PATMR3IsPatchHCAddr(PVM pVM, R3PTRTYPE(uint8_t *) pAddrHC)
d571b6e3237f0ce89ea27f6fa4635d41c5ee3d88vboxsync return (pAddrHC >= pVM->patm.s.pPatchMemHC && pAddrHC < pVM->patm.s.pPatchMemHC + pVM->patm.s.cbPatchMem) ? true : false;
d571b6e3237f0ce89ea27f6fa4635d41c5ee3d88vboxsync * Allows or disallow patching of privileged instructions executed by the guest OS
d571b6e3237f0ce89ea27f6fa4635d41c5ee3d88vboxsync * @returns VBox status code.
d571b6e3237f0ce89ea27f6fa4635d41c5ee3d88vboxsync * @param pVM The VM to operate on.
d571b6e3237f0ce89ea27f6fa4635d41c5ee3d88vboxsync * @param fAllowPatching Allow/disallow patching
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsyncPATMR3DECL(int) PATMR3AllowPatching(PVM pVM, uint32_t fAllowPatching)
51a01524909c95ee04b636218b6a89b29fb81825vboxsync pVM->fPATMEnabled = (fAllowPatching) ? true : false;
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * Convert a GC patch block pointer to a HC patch pointer
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @returns HC pointer or NULL if it's not a GC patch pointer
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pVM The VM to operate on.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pAddrGC GC pointer
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsyncPATMR3DECL(R3PTRTYPE(void *)) PATMR3GCPtrToHCPtr(PVM pVM, RTGCPTR pAddrGC)
efff36b306e370346025647a158689021df2e1d1vboxsync if (pVM->patm.s.pPatchMemGC <= pAddrGC && pVM->patm.s.pPatchMemGC + pVM->patm.s.cbPatchMem > pAddrGC)
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync return pVM->patm.s.pPatchMemHC + (pAddrGC - pVM->patm.s.pPatchMemGC);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * Query PATM state (enabled/disabled)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @returns 0 - disabled, 1 - enabled
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pVM The VM to operate on.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * Convert guest context address to host context pointer
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @returns VBox status code.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pVM The VM to operate on.
51a01524909c95ee04b636218b6a89b29fb81825vboxsync * @param pPatch Patch block structure pointer
51a01524909c95ee04b636218b6a89b29fb81825vboxsync * @param pGCPtr Guest context pointer
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @returns Host context pointer or NULL in case of an error
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsyncR3PTRTYPE(uint8_t *) PATMGCVirtToHCVirt(PVM pVM, PPATCHINFO pPatch, GCPTRTYPE(uint8_t *) pGCPtr)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync return PATCHCODE_PTR_HC(pPatch) + (pGCPtr - PATCHCODE_PTR_GC(pPatch));
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync if (pPatch->cacheRec.pGuestLoc == (pGCPtr & PAGE_BASE_GC_MASK))
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync rc = PGMPhysGCPtr2HCPtr(pVM, pGCPtr, (void **)&pHCPtr);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync AssertMsg(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("MMR3PhysGCVirt2HCVirtEx failed for %08X\n", pGCPtr));
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync////invalid? Assert(sizeof(R3PTRTYPE(uint8_t*)) == sizeof(uint32_t));
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pPatch->cacheRec.pPatchLocStartHC = (R3PTRTYPE(uint8_t*))((RTHCUINTPTR)pHCPtr & PAGE_BASE_HC_MASK);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pPatch->cacheRec.pGuestLoc = pGCPtr & PAGE_BASE_GC_MASK;
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync/* Calculates and fills in all branch targets
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @returns VBox status code.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pVM The VM to operate on.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pPatch Current patch block pointer
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsyncstatic int patmr3SetBranchTargets(PVM pVM, PPATCHINFO pPatch)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * Set all branch targets inside the patch block.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * We remove all jump records as they are no longer needed afterwards.
b0b15690f00527424b2d5fb88456d747252322f7vboxsync while (true)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pRec = (PJUMPREC)RTAvlPVRemoveBestFit(&pPatch->JumpTree, 0, true);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync /* HC in patch block to GC in patch block. */
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pInstrGC = patmPatchHCPtr2PatchGCPtr(pVM, pRec->pJumpHC);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync /* Special case: call function replacement patch from this patch block.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync if (PATMQueryFunctionPatch(pVM, pRec->pTargetGC) == 0)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync if (PATMR3HasBeenPatched(pVM, pRec->pTargetGC) == false)
b0b15690f00527424b2d5fb88456d747252322f7vboxsync rc = PATMR3InstallPatch(pVM, pRec->pTargetGC, PATMFL_CODE32 | PATMFL_DUPLICATE_FUNCTION);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync rc = VERR_PATCHING_REFUSED; /* exists as a normal patch; can't use it */
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pOrgInstrGC = PATMR3PatchToGCPtr(pVM, pInstrGC, 0);
a2f96875f61628e5a5fd33785f8c0bbb310f981fvboxsync /* Failure for some reason -> mark exit point with int 3. */
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync Log(("Failed to install function replacement patch (at %x) for reason %Vrc\n", pOrgInstrGC, rc));
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pPatchGC = patmGuestGCPtrToPatchGCPtr(pVM, pPatch, pOrgInstrGC);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pPatchHC = pVM->patm.s.pPatchMemHC + (pPatchGC - pVM->patm.s.pPatchMemGC);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync /* Set a breakpoint at the very beginning of the recompiled instruction */
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pBranchTargetGC = PATMR3QueryPatchGCPtr(pVM, pRec->pTargetGC);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pBranchTargetGC = patmGuestGCPtrToPatchGCPtr(pVM, pPatch, pRec->pTargetGC);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync AssertMsgFailed(("patmr3SetBranchTargets: patmGuestGCPtrToPatchGCPtr failed for %08X\n", pRec->pTargetGC));
b0b15690f00527424b2d5fb88456d747252322f7vboxsync /* Our jumps *always* have a dword displacement (to make things easier). */
a2f96875f61628e5a5fd33785f8c0bbb310f981fvboxsync displ = pBranchTargetGC - (pInstrGC + pRec->offDispl + sizeof(RTGCPTR));
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync *(RTGCPTR *)(pRec->pJumpHC + pRec->offDispl) = displ;
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync Log(("Set branch target %d to %08X : %08x - (%08x + %d + %d)\n", nrJumpRecs, displ, pBranchTargetGC, pInstrGC, pRec->offDispl, sizeof(RTGCPTR)));
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync/* Add an illegal instruction record
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pVM The VM to operate on.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pPatch Patch structure ptr
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pInstrGC Guest context pointer to privileged instruction
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsyncstatic void patmAddIllegalInstrRecord(PVM pVM, PPATCHINFO pPatch, RTGCPTR pInstrGC)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pRec = (PAVLPVNODECORE)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(*pRec));
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync bool ret = RTAvlPVInsert(&pPatch->pTempInfo->IllegalInstrTree, pRec);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsyncstatic bool patmIsIllegalInstr(PPATCHINFO pPatch, RTGCPTR pInstrGC)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pRec = RTAvlPVGet(&pPatch->pTempInfo->IllegalInstrTree, (AVLPVKEY)pInstrGC);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync return true;
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync return false;
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * Add a patch to guest lookup record
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pVM The VM to operate on.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pPatch Patch structure ptr
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pPatchInstrHC Guest context pointer to patch block
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pInstrGC Guest context pointer to privileged instruction
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param enmType Lookup type
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param fDirty Dirty flag
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync /** @note Be extremely careful with this function. Make absolutely sure the guest address is correct! (to avoid executing instructions twice!) */
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsyncvoid patmr3AddP2GLookupRecord(PVM pVM, PPATCHINFO pPatch, uint8_t *pPatchInstrHC, RTGCPTR pInstrGC, PATM_LOOKUP_TYPE enmType, bool fDirty)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync uint32_t PatchOffset = pPatchInstrHC - pVM->patm.s.pPatchMemHC; /* Offset in memory reserved for PATM. */
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32Get(&pPatch->Patch2GuestAddrTree, PatchOffset);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync if (pPatchToGuestRec && pPatchToGuestRec->Core.Key == PatchOffset)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync return; /* already there */
51a01524909c95ee04b636218b6a89b29fb81825vboxsync pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32Get(&pPatch->Patch2GuestAddrTree, PatchOffset);
51a01524909c95ee04b636218b6a89b29fb81825vboxsync pPatchToGuestRec = (PRECPATCHTOGUEST)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(RECPATCHTOGUEST) + sizeof(RECGUESTTOPATCH));
51a01524909c95ee04b636218b6a89b29fb81825vboxsync ret = RTAvlU32Insert(&pPatch->Patch2GuestAddrTree, &pPatchToGuestRec->Core);
51a01524909c95ee04b636218b6a89b29fb81825vboxsync /* GC to patch address */
51a01524909c95ee04b636218b6a89b29fb81825vboxsync pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlGCPtrGet(&pPatch->Guest2PatchAddrTree, pInstrGC);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pGuestToPatchRec = (PRECGUESTTOPATCH)(pPatchToGuestRec+1);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync ret = RTAvlGCPtrInsert(&pPatch->Guest2PatchAddrTree, &pGuestToPatchRec->Core);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * Removes a patch to guest lookup record
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pVM The VM to operate on.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pPatch Patch structure ptr
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pPatchInstrGC Guest context pointer to patch block
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsyncvoid patmr3RemoveP2GLookupRecord(PVM pVM, PPATCHINFO pPatch, RTGCPTR pPatchInstrGC)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync uint32_t PatchOffset = pPatchInstrGC - pVM->patm.s.pPatchMemGC; /* Offset in memory reserved for PATM. */
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32Get(&pPatch->Patch2GuestAddrTree, PatchOffset);
b0b15690f00527424b2d5fb88456d747252322f7vboxsync if (pPatchToGuestRec->enmType == PATM_LOOKUP_BOTHDIR)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync PRECGUESTTOPATCH pGuestToPatchRec = (PRECGUESTTOPATCH)(pPatchToGuestRec+1);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pNode2 = RTAvlGCPtrRemove(&pPatch->Guest2PatchAddrTree, pGuestToPatchRec->Core.Key);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync pNode = RTAvlU32Remove(&pPatch->Patch2GuestAddrTree, pPatchToGuestRec->Core.Key);
51a01524909c95ee04b636218b6a89b29fb81825vboxsync * RTAvlPVDestroy callback.
51a01524909c95ee04b636218b6a89b29fb81825vboxsyncstatic DECLCALLBACK(int) patmEmptyTreePVCallback(PAVLPVNODECORE pNode, void *)
51a01524909c95ee04b636218b6a89b29fb81825vboxsync * Empty the specified tree (PV tree, MMR3 heap)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pVM The VM to operate on.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param ppTree Tree to empty
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync RTAvlPVDestroy(ppTree, patmEmptyTreePVCallback, NULL);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * RTAvlU32Destroy callback.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsyncstatic DECLCALLBACK(int) patmEmptyTreeU32Callback(PAVLU32NODECORE pNode, void *)
51a01524909c95ee04b636218b6a89b29fb81825vboxsync * Empty the specified tree (U32 tree, MMR3 heap)
51a01524909c95ee04b636218b6a89b29fb81825vboxsync * @param pVM The VM to operate on.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param ppTree Tree to empty
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsyncvoid patmEmptyTreeU32(PVM pVM, PPAVLU32NODECORE ppTree)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync RTAvlU32Destroy(ppTree, patmEmptyTreeU32Callback, NULL);
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * Analyses the instructions following the cli for compliance with our heuristics for cli & pushf
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @returns VBox status code.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pVM The VM to operate on.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pCpu CPU disassembly state
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pInstrGC Guest context pointer to privileged instruction
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pCurInstrGC Guest context pointer to the current instruction
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pUserData User pointer (callback specific)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsyncstatic int patmAnalyseBlockCallback(PVM pVM, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, void *pUserData)
b0b15690f00527424b2d5fb88456d747252322f7vboxsync //Preliminary heuristics:
b0b15690f00527424b2d5fb88456d747252322f7vboxsync //- no call instructions without a fixed displacement between cli and sti/popf
b0b15690f00527424b2d5fb88456d747252322f7vboxsync //- no jumps in the instructions following cli (4+ bytes; enough for the replacement jump (5 bytes))
b0b15690f00527424b2d5fb88456d747252322f7vboxsync //- no nested pushf/cli
b0b15690f00527424b2d5fb88456d747252322f7vboxsync //- sti/popf should be the (eventual) target of all branches
b0b15690f00527424b2d5fb88456d747252322f7vboxsync //- no near or far returns; no int xx, no into
b0b15690f00527424b2d5fb88456d747252322f7vboxsync // Note: Later on we can impose less stricter guidelines if the need arises
b0b15690f00527424b2d5fb88456d747252322f7vboxsync /* Bail out if the patch gets too big. */
b0b15690f00527424b2d5fb88456d747252322f7vboxsync Log(("Code block too big (%x) for patch at %VGv!!\n", pPatch->cbPatchBlockSize, pCurInstrGC));
b0b15690f00527424b2d5fb88456d747252322f7vboxsync patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
b0b15690f00527424b2d5fb88456d747252322f7vboxsync /* No unconditinal jumps or calls without fixed displacements. */
b0b15690f00527424b2d5fb88456d747252322f7vboxsync if ( (pCpu->pCurInstr->optype & OPTYPE_CONTROLFLOW)
b0b15690f00527424b2d5fb88456d747252322f7vboxsync && (pCpu->pCurInstr->opcode == OP_JMP || pCpu->pCurInstr->opcode == OP_CALL)
b0b15690f00527424b2d5fb88456d747252322f7vboxsync Assert(pCpu->param1.size <= 4 || pCpu->param1.size == 8);
b0b15690f00527424b2d5fb88456d747252322f7vboxsync || (pCpu->pCurInstr->opcode == OP_CALL && !(pPatch->flags & PATMFL_SUPPORT_CALLS))
b0b15690f00527424b2d5fb88456d747252322f7vboxsync || (OP_PARM_VTYPE(pCpu->pCurInstr->param1) != OP_PARM_J && !(pPatch->flags & PATMFL_SUPPORT_INDIRECT_CALLS))
b0b15690f00527424b2d5fb88456d747252322f7vboxsync patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
b0b15690f00527424b2d5fb88456d747252322f7vboxsync /* An unconditional (short) jump right after a cli is a potential problem; we will overwrite whichever function comes afterwards */
b0b15690f00527424b2d5fb88456d747252322f7vboxsync if (pPatch->opcode == OP_CLI && pCpu->pCurInstr->opcode == OP_JMP)
b0b15690f00527424b2d5fb88456d747252322f7vboxsync if (pCurInstrGC > pPatch->pPrivInstrGC && pCurInstrGC + pCpu->opsize < pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32) /* hardcoded patch jump size; cbPatchJump is still zero */
b0b15690f00527424b2d5fb88456d747252322f7vboxsync Log(("Dangerous unconditional jump ends in our generated patch jump!! (%x vs %x)\n", pCurInstrGC, pPatch->pPrivInstrGC));
b0b15690f00527424b2d5fb88456d747252322f7vboxsync /* We turn this one into a int 3 callable patch. */
b0b15690f00527424b2d5fb88456d747252322f7vboxsync /* no nested pushfs just yet; nested cli is allowed for cli patches though. */
b0b15690f00527424b2d5fb88456d747252322f7vboxsync if (pCurInstrGC != pInstrGC && pCpu->pCurInstr->opcode == OP_PUSHF)
b0b15690f00527424b2d5fb88456d747252322f7vboxsync patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
b0b15690f00527424b2d5fb88456d747252322f7vboxsync // no far returns
b0b15690f00527424b2d5fb88456d747252322f7vboxsync patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
b0b15690f00527424b2d5fb88456d747252322f7vboxsync // no int xx or into either
b0b15690f00527424b2d5fb88456d747252322f7vboxsync if (pCpu->pCurInstr->opcode == OP_INT3 || pCpu->pCurInstr->opcode == OP_INT || pCpu->pCurInstr->opcode == OP_INTO)
b0b15690f00527424b2d5fb88456d747252322f7vboxsync patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
b0b15690f00527424b2d5fb88456d747252322f7vboxsync /* Illegal instruction -> end of analysis phase for this code block */
b0b15690f00527424b2d5fb88456d747252322f7vboxsync if (fIllegalInstr || patmIsIllegalInstr(pPatch, pCurInstrGC))
b0b15690f00527424b2d5fb88456d747252322f7vboxsync /* Check for exit points. */
b0b15690f00527424b2d5fb88456d747252322f7vboxsync return VINF_SUCCESS; /* duplicate it; will fault or emulated in GC. */
b0b15690f00527424b2d5fb88456d747252322f7vboxsync //This appears to be some kind of kernel panic in Linux 2.4; no point to analyse more
b0b15690f00527424b2d5fb88456d747252322f7vboxsync Log(("Illegal opcode (0xf 0xb) -> return here\n"));
b0b15690f00527424b2d5fb88456d747252322f7vboxsync Assert(!(pPatch->flags & (PATMFL_DUPLICATE_FUNCTION)));
b0b15690f00527424b2d5fb88456d747252322f7vboxsync /* If out exit point lies within the generated patch jump, then we have to refuse!! */
b0b15690f00527424b2d5fb88456d747252322f7vboxsync if (pCurInstrGC > pPatch->pPrivInstrGC && pCurInstrGC < pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32) /* hardcoded patch jump size; cbPatchJump is still zero */
b0b15690f00527424b2d5fb88456d747252322f7vboxsync Log(("Exit point within patch jump itself!! (%x vs %x)\n", pCurInstrGC, pPatch->pPrivInstrGC));
b0b15690f00527424b2d5fb88456d747252322f7vboxsync /* Or else we need to duplicate more instructions, because we can't jump back yet! */
b0b15690f00527424b2d5fb88456d747252322f7vboxsync Log(("WARNING: End of block reached, but we need to duplicate some extra instruction to avoid a conflict with the patch jump\n"));
b0b15690f00527424b2d5fb88456d747252322f7vboxsync break; //sti doesn't mark the end of a pushf block; only popf does
b0b15690f00527424b2d5fb88456d747252322f7vboxsync //else no break
b0b15690f00527424b2d5fb88456d747252322f7vboxsync case OP_RETN: /* exit point for function replacement */
b0b15690f00527424b2d5fb88456d747252322f7vboxsync if (pCpu->pCurInstr->optype & (OPTYPE_PRIVILEGED_NOTRAP))
b0b15690f00527424b2d5fb88456d747252322f7vboxsync patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
b0b15690f00527424b2d5fb88456d747252322f7vboxsync // If single instruction patch, we've copied enough instructions *and* the current instruction is not a relative jump
b0b15690f00527424b2d5fb88456d747252322f7vboxsync if ((pPatch->flags & PATMFL_CHECK_SIZE) && pPatch->cbPatchBlockSize > SIZEOF_NEARJUMP32 && !(pCpu->pCurInstr->optype & OPTYPE_RELATIVE_CONTROLFLOW))
b0b15690f00527424b2d5fb88456d747252322f7vboxsync // The end marker for this kind of patch is any instruction at a location outside our patch jump
b0b15690f00527424b2d5fb88456d747252322f7vboxsync Log(("End of block at %VGv size %d\n", pCurInstrGC, pCpu->opsize));
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * Analyses the instructions inside a function for compliance
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @returns VBox status code.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pVM The VM to operate on.
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pCpu CPU disassembly state
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pInstrGC Guest context pointer to privileged instruction
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pCurInstrGC Guest context pointer to the current instruction
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsync * @param pUserData User pointer (callback specific)
cc15c3fa4bb2d3fb91e4d0cd15a73133963f86b0vboxsyncstatic int patmAnalyseFunctionCallback(PVM pVM, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, void *pUserData)
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync //Preliminary heuristics:
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync //- no call instructions
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync //- ret ends a block
8083a259c13e6e26e56ca2582edbad4a8cfac25avboxsync Assert(pPatch->flags & (PATMFL_DUPLICATE_FUNCTION));
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync // bail out if the patch gets too big
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync Log(("Code block too big (%x) for function patch at %VGv!!\n", pPatch->cbPatchBlockSize, pCurInstrGC));
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync // no unconditinal jumps or calls without fixed displacements
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync if ( (pCpu->pCurInstr->optype & OPTYPE_CONTROLFLOW)
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync && (pCpu->pCurInstr->opcode == OP_JMP || pCpu->pCurInstr->opcode == OP_CALL)
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync Assert(pCpu->param1.size <= 4 || pCpu->param1.size == 8);
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync || (pCpu->pCurInstr->opcode == OP_CALL && !(pPatch->flags & PATMFL_SUPPORT_CALLS))
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync || (OP_PARM_VTYPE(pCpu->pCurInstr->param1) != OP_PARM_J && !(pPatch->flags & PATMFL_SUPPORT_INDIRECT_CALLS))
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync else /* no far returns */
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync else /* no int xx or into either */
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync if (pCpu->pCurInstr->opcode == OP_INT3 || pCpu->pCurInstr->opcode == OP_INT || pCpu->pCurInstr->opcode == OP_INTO)
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync ///@todo we can handle certain in/out and privileged instructions in the guest context
b7a5b3f9f9ecce32ddacf8404c625ce0451bbdc1vboxsync if (pCpu->pCurInstr->optype & OPTYPE_PRIVILEGED && pCpu->pCurInstr->opcode != OP_STI)
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync Log(("Illegal instructions for function patch!!\n"));
8083a259c13e6e26e56ca2582edbad4a8cfac25avboxsync /* Illegal instruction -> end of analysis phase for this code block */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync if (fIllegalInstr || patmIsIllegalInstr(pPatch, pCurInstrGC))
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync // Check for exit points
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync //This appears to be some kind of kernel panic in Linux 2.4; no point to analyse more
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync Log(("Illegal opcode (0xf 0xb) -> return here\n"));
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync case OP_SYSEXIT: /* will fault or emulated in GC */
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync if (pCpu->pCurInstr->optype & (OPTYPE_PRIVILEGED_NOTRAP))
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
16a9adc14900ca18e6909679a579f6833425e030vboxsync * Recompiles the instructions in a code block
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @returns VBox status code.
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @param pVM The VM to operate on.
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @param pCpu CPU disassembly state
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @param pInstrGC Guest context pointer to privileged instruction
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @param pCurInstrGC Guest context pointer to the current instruction
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @param pUserData User pointer (callback specific)
16a9adc14900ca18e6909679a579f6833425e030vboxsyncstatic int patmRecompileCallback(PVM pVM, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, void *pUserData)
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync bool fInhibitIRQInstr = false; /* did the instruction cause PATMFL_INHIBITIRQS to be set? */
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync LogFlow(("patmRecompileCallback %VGv %VGv\n", pInstrGC, pCurInstrGC));
16a9adc14900ca18e6909679a579f6833425e030vboxsync if ( patmGuestGCPtrToPatchGCPtr(pVM, pPatch, pCurInstrGC) != 0
16a9adc14900ca18e6909679a579f6833425e030vboxsync && !(pPatch->flags & PATMFL_RECOMPILE_NEXT)) /* do not do this when the next instruction *must* be executed! */
16a9adc14900ca18e6909679a579f6833425e030vboxsync * Been there, done that; so insert a jump (we don't want to duplicate code)
16a9adc14900ca18e6909679a579f6833425e030vboxsync * no need to record this instruction as it's glue code that never crashes (it had better not!)
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync Log(("patmRecompileCallback: jump to code we've recompiled before %VGv!\n", pCurInstrGC));
16a9adc14900ca18e6909679a579f6833425e030vboxsync return patmPatchGenRelJump(pVM, pPatch, pCurInstrGC, OP_JMP, !!(pCpu->prefix & PREFIX_OPSIZE));
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync rc = patmAnalyseFunctionCallback(pVM, pCpu, pInstrGC, pCurInstrGC, pUserData);
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync rc = patmAnalyseBlockCallback(pVM, pCpu, pInstrGC, pCurInstrGC, pUserData);
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /** @note Never do a direct return unless a failure is encountered! */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /* Clear recompilation of next instruction flag; we are doing that right here. */
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync /* Add lookup record for patch to guest address translation */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync patmr3AddP2GLookupRecord(pVM, pPatch, PATCHCODE_PTR_HC(pPatch) + pPatch->uCurPatchOffset, pCurInstrGC, PATM_LOOKUP_BOTHDIR);
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /* Update lowest and highest instruction address for this patch */
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync pPatch->pInstrGCHighest = pCurInstrGC + pCpu->opsize;
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync /* Illegal instruction -> end of recompile phase for this code block. */
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync Log(("Illegal instruction at %VGv -> mark with int 3\n", pCurInstrGC));
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync /* For our first attempt, we'll handle only simple relative jumps (immediate offset coded in instruction).
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync * Indirect calls are handled below.
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync if ( (pCpu->pCurInstr->optype & OPTYPE_CONTROLFLOW)
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync && (pCpu->pCurInstr->opcode != OP_CALL || (pPatch->flags & PATMFL_SUPPORT_CALLS))
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync && (OP_PARM_VTYPE(pCpu->pCurInstr->param1) == OP_PARM_J))
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync GCPTRTYPE(uint8_t *) pTargetGC = PATMResolveBranch(pCpu, pCurInstrGC);
8083a259c13e6e26e56ca2582edbad4a8cfac25avboxsync Log(("We don't support far jumps here!! (%08X)\n", pCpu->param1.flags));
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync rc = patmPatchGenCall(pVM, pPatch, pCpu, pCurInstrGC, pTargetGC, false);
594521f7faf13f7a88f31e6cd76629bd67340229vboxsync rc = patmPatchGenRelJump(pVM, pPatch, pTargetGC, pCpu->pCurInstr->opcode, !!(pCpu->prefix & PREFIX_OPSIZE));
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync /* If a cli is found while duplicating instructions for another patch, then it's of vital importance to continue
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync * until we've found the proper exit point(s).
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync Log(("cli instruction found in other instruction patch block; force it to continue & find an exit point\n"));
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync pPatch->flags &= ~(PATMFL_CHECK_SIZE | PATMFL_SINGLE_INSTRUCTION);
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync /* Set by irq inhibition; no longer valid now. */
8083a259c13e6e26e56ca2582edbad4a8cfac25avboxsync if (pCpu->pCurInstr->optype & OPTYPE_POTENTIALLY_DANGEROUS)
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync /* mov ss, src? */
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync Log(("Force recompilation of next instruction for OP_MOV at %VGv\n", pCurInstrGC));
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync /** @todo this could cause a fault (ring 0 selector being loaded in ring 1) */
594521f7faf13f7a88f31e6cd76629bd67340229vboxsync#if 0 /* necessary for Haiku */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync && (pCpu->param1.flags & (USE_REG_GEN32|USE_REG_GEN16))) /** @todo memory operand must in theory be handled too */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /* mov GPR, ss */
8083a259c13e6e26e56ca2582edbad4a8cfac25avboxsync rc = patmPatchGenMovFromSS(pVM, pPatch, pCpu, pCurInstrGC);
9496f2d398b49813176939d7a339ae513d5175efvboxsync Assert(pCpu->pCurInstr->optype & OPTYPE_INHIBIT_IRQS);
c7551981eb6d97331da479f68f14a9c56247e4f7vboxsync Log(("Force recompilation of next instruction for OP_MOV at %VGv\n", pCurInstrGC));
009d45aa55691312278d41edb20154dc208d9cd8vboxsync RTGCPTR pNextInstrGC = 0; /* by default no inhibit irq */
009d45aa55691312278d41edb20154dc208d9cd8vboxsync /** In a sequence of instructions that inhibit irqs, only the first one actually inhibits irqs. */
c7551981eb6d97331da479f68f14a9c56247e4f7vboxsync pPatch->flags |= PATMFL_INHIBIT_IRQS | PATMFL_GENERATE_JUMPTOGUEST;
9496f2d398b49813176939d7a339ae513d5175efvboxsync Log(("Inhibit irqs for instruction OP_STI at %VGv\n", pCurInstrGC));
009d45aa55691312278d41edb20154dc208d9cd8vboxsync rc = patmPatchGenSti(pVM, pPatch, pCurInstrGC, pNextInstrGC);
009d45aa55691312278d41edb20154dc208d9cd8vboxsync pNextInstrHC = PATMGCVirtToHCVirt(pVM, pPatch, pNextInstrGC);
009d45aa55691312278d41edb20154dc208d9cd8vboxsync // Disassemble the next instruction
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync disret = PATMR3DISInstr(pVM, pPatch, &cpu, pNextInstrGC, pNextInstrHC, &opsize, NULL);
9496f2d398b49813176939d7a339ae513d5175efvboxsync if (disret == false)
9496f2d398b49813176939d7a339ae513d5175efvboxsync AssertMsgFailed(("STI: Disassembly failed (probably page not present) -> return to caller\n"));
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync /* Not an exit point for function duplication patches */
009d45aa55691312278d41edb20154dc208d9cd8vboxsync pPatch->flags &= ~PATMFL_GENERATE_JUMPTOGUEST; /* Don't generate a jump back */
009d45aa55691312278d41edb20154dc208d9cd8vboxsync Log(("PATM: sti occurred too soon; refusing patch!\n"));
9496f2d398b49813176939d7a339ae513d5175efvboxsync bool fGenerateJmpBack = (pCurInstrGC + pCpu->opsize - pInstrGC >= SIZEOF_NEARJUMP32);
fc78e01f665145ab3641c5f8095e9ae984ddcb84vboxsync /* Not an exit point for IDT handler or function replacement patches */
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync /* Note: keep IOPL in mind when changing any of this!! (see comments in PATMA.asm, PATMPopf32Replacement) */
009d45aa55691312278d41edb20154dc208d9cd8vboxsync if (pPatch->flags & (PATMFL_IDTHANDLER|PATMFL_DUPLICATE_FUNCTION))
009d45aa55691312278d41edb20154dc208d9cd8vboxsync rc = patmPatchGenPopf(pVM, pPatch, pCurInstrGC + pCpu->opsize, !!(pCpu->prefix & PREFIX_OPSIZE), fGenerateJmpBack);
bdaae7f756db4f5cf2d62f495a2a80acaf581a0cvboxsync /* Not an exit point for IDT handler or function replacement patches */
009d45aa55691312278d41edb20154dc208d9cd8vboxsync rc = patmPatchGenPushf(pVM, pPatch, !!(pCpu->prefix & PREFIX_OPSIZE));
009d45aa55691312278d41edb20154dc208d9cd8vboxsync rc = patmPatchGenIret(pVM, pPatch, pCurInstrGC, !!(pCpu->prefix & PREFIX_OPSIZE));
009d45aa55691312278d41edb20154dc208d9cd8vboxsync /* This appears to be some kind of kernel panic in Linux 2.4; no point to continue */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync rc = patmPatchGenSldtStr(pVM, pPatch, pCpu, pCurInstrGC);
ee4d840f54fd2dcea8a73b1b86d5ec0db370b05dvboxsync rc = patmPatchGenSxDT(pVM, pPatch, pCpu, pCurInstrGC);
da3503c04ce76e653401396fe2795a9bc2427a1dvboxsync /* retn is an exit point for function patches */
da3503c04ce76e653401396fe2795a9bc2427a1dvboxsync rc = patmPatchGenRet(pVM, pPatch, pCpu, pCurInstrGC);
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync /* Duplicate it, so it can be emulated in GC (or fault). */
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync rc = patmPatchGenDuplicate(pVM, pPatch, pCpu, pCurInstrGC);
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync Assert(pPatch->flags & PATMFL_SUPPORT_INDIRECT_CALLS);
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync /* In interrupt gate handlers it's possible to encounter jumps or calls when IF has been enabled again.
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync * In that case we'll jump to the original instruction and continue from there. Otherwise an int 3 is executed.
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync Assert(pCpu->param1.size == 4 || pCpu->param1.size == 8);
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync if (pPatch->flags & PATMFL_SUPPORT_INDIRECT_CALLS && pCpu->param1.size == 4 /* no far calls! */)
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync rc = patmPatchGenCall(pVM, pPatch, pCpu, pCurInstrGC, (RTGCPTR)0xDEADBEEF, true);
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync Assert(pPatch->flags & PATMFL_SUPPORT_INDIRECT_CALLS);
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync /* In interrupt gate handlers it's possible to encounter jumps or calls when IF has been enabled again.
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync * In that case we'll jump to the original instruction and continue from there. Otherwise an int 3 is executed.
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync Assert(pCpu->param1.size == 4 || pCpu->param1.size == 8);
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync if (pPatch->flags & PATMFL_SUPPORT_INDIRECT_CALLS && pCpu->param1.size == 4 /* no far jumps! */)
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync rc = patmPatchGenJump(pVM, pPatch, pCpu, pCurInstrGC);
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync /** @note: currently we let DRx writes cause a trap d; our trap handler will decide to interpret it or not. */
a5e6836247bf89fb87d0fdf08c1ac8c79dd979advboxsync /** @note: currently we let CRx writes cause a trap d; our trap handler will decide to interpret it or not. */
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync if (pCpu->pCurInstr->optype & (OPTYPE_CONTROLFLOW | OPTYPE_PRIVILEGED_NOTRAP))
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync rc = patmPatchGenDuplicate(pVM, pPatch, pCpu, pCurInstrGC);
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync Log(("Clear inhibit IRQ flag at %VGv\n", pCurInstrGC));
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync Log(("patmRecompileCallback: generate jump back to guest (%VGv) after fused instruction\n", pNextInstrGC));
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync rc2 = patmPatchGenJumpToGuest(pVM, pPatch, pNextInstrGC, true /* clear inhibit irq flag */);
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync rc2 = patmPatchGenClearInhibitIRQ(pVM, pPatch, pNextInstrGC);
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync // If single instruction patch, we've copied enough instructions *and* the current instruction is not a relative jump
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync && pCurInstrGC + pCpu->opsize - pInstrGC >= SIZEOF_NEARJUMP32
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync && !(pCpu->pCurInstr->optype & OPTYPE_RELATIVE_CONTROLFLOW)
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync && !(pPatch->flags & PATMFL_RECOMPILE_NEXT) /* do not do this when the next instruction *must* be executed! */
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync // The end marker for this kind of patch is any instruction at a location outside our patch jump
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync Log(("patmRecompileCallback: end found for single instruction patch at %VGv opsize %d\n", pNextInstrGC, pCpu->opsize));
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync rc = patmPatchGenJumpToGuest(pVM, pPatch, pNextInstrGC);
c7ff622115966b69b482bd2896662e40d823b22fvboxsync/* Add a disasm jump record (temporary for prevent duplicate analysis)
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @param pVM The VM to operate on.
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @param pPatch Patch structure ptr
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @param pInstrGC Guest context pointer to privileged instruction
16a9adc14900ca18e6909679a579f6833425e030vboxsyncstatic void patmPatchAddDisasmJump(PVM pVM, PPATCHINFO pPatch, RTGCPTR pInstrGC)
16a9adc14900ca18e6909679a579f6833425e030vboxsync pRec = (PAVLPVNODECORE)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(*pRec));
009d45aa55691312278d41edb20154dc208d9cd8vboxsync int ret = RTAvlPVInsert(&pPatch->pTempInfo->DisasmJumpTree, pRec);
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync * Checks if jump target has been analysed before.
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync * @returns VBox status code.
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync * @param pPatch Patch struct
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync * @param pInstrGC Jump target
76364cddabfeb143dad91862d41a5638d8860b25vboxsyncstatic bool patmIsKnownDisasmJump(PPATCHINFO pPatch, RTGCPTR pInstrGC)
76364cddabfeb143dad91862d41a5638d8860b25vboxsync pRec = RTAvlPVGet(&pPatch->pTempInfo->DisasmJumpTree, (AVLPVKEY)pInstrGC);
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync return true;
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync return false;
b5ad839a3757b305d4e98d7264da2b53c9cd27f0vboxsync * For proper disassembly of the final patch block
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync * @returns VBox status code.
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync * @param pVM The VM to operate on.
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync * @param pCpu CPU disassembly state
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync * @param pInstrGC Guest context pointer to privileged instruction
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync * @param pCurInstrGC Guest context pointer to the current instruction
cba6719bd64ec749967bbe931230452664109857vboxsync * @param pUserData User pointer (callback specific)
c28fa006ba669ad8f26ae31d00a338379c04ea1bvboxsyncint patmr3DisasmCallback(PVM pVM, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, void *pUserData)
681fd85cc7cd49e9cf66a917d6ae9ff36eb7d9e9vboxsync /* Could be an int3 inserted in a call patch. Check to be sure */
681fd85cc7cd49e9cf66a917d6ae9ff36eb7d9e9vboxsync cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
e74eef731a813e4e06680c587a6759b9974b29c9vboxsync pOrgJumpGC = patmPatchGCPtr2GuestGCPtr(pVM, pPatch, pCurInstrGC);
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync pOrgJumpHC = PATMGCVirtToHCVirt(pVM, pPatch, pOrgJumpGC);
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync bool disret = PATMR3DISInstr(pVM, pPatch, &cpu, pOrgJumpGC, pOrgJumpHC, &dummy, NULL);
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync if (!disret || cpu.pCurInstr->opcode != OP_CALL || cpu.param1.size != 4 /* only near calls */)
16a9adc14900ca18e6909679a579f6833425e030vboxsync /* the indirect call patch contains an 0xF/0xB illegal instr to call for assistance; check for this and continue */
548ca31b6b47c36bacce49bed3339cb8075b9681vboxsync if ( (pCpu->pCurInstr->opcode == OP_CALL && !(pPatch->flags & PATMFL_SUPPORT_CALLS))
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync * Disassembles the code stream until the callback function detects a failure or decides everything is acceptable
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync * @returns VBox status code.
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync * @param pVM The VM to operate on.
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync * @param pInstrGC Guest context pointer to the initial privileged instruction
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync * @param pCurInstrGC Guest context pointer to the current instruction
9496f2d398b49813176939d7a339ae513d5175efvboxsync * @param pfnPATMR3Disasm Callback for testing the disassembled instruction
9496f2d398b49813176939d7a339ae513d5175efvboxsync * @param pUserData User pointer (callback specific)
9c149a2789022f5011e88fb62f02a1cc8068e88fvboxsyncint patmr3DisasmCode(PVM pVM, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, PFN_PATMR3ANALYSE pfnPATMR3Disasm, void *pUserData)
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync Assert(pCurInstrHC != PATCHCODE_PTR_HC(pPatch) || pPatch->pTempInfo->DisasmJumpTree == 0);
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /* We need this to determine branch targets (and for disassembling). */
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync delta = pVM->patm.s.pPatchMemGC - (uintptr_t)pVM->patm.s.pPatchMemHC;
c28fa006ba669ad8f26ae31d00a338379c04ea1bvboxsync cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
090d729e786b999dc285f8ea267f9effd1319544vboxsync pCurInstrHC = PATMGCVirtToHCVirt(pVM, pPatch, pCurInstrGC);
d9d5fbda1b8f7a6f7fae555db60d0e636fd03af8vboxsync disret = PATMR3DISInstr(pVM, pPatch, &cpu, pCurInstrGC, pCurInstrHC, &opsize, szOutput, PATMREAD_RAWCODE);
d9d5fbda1b8f7a6f7fae555db60d0e636fd03af8vboxsync RTGCPTR pOrgInstrGC = patmPatchGCPtr2GuestGCPtr(pVM, pPatch, pCurInstrGC);
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync if (pOrgInstrGC != pPatch->pTempInfo->pLastDisasmInstrGC)
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync pPatch->pTempInfo->pLastDisasmInstrGC = pOrgInstrGC;
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync if (disret == false)
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync Log(("Disassembly failed (probably page not present) -> return to caller\n"));
0ccdfa1953b2f57311fb9ec01a2baf5e1e366f5avboxsync rc = pfnPATMR3Disasm(pVM, &cpu, pInstrGC, pCurInstrGC, pUserData);
0ccdfa1953b2f57311fb9ec01a2baf5e1e366f5avboxsync break; //done!
0ccdfa1953b2f57311fb9ec01a2baf5e1e366f5avboxsync /* For our first attempt, we'll handle only simple relative jumps and calls (immediate offset coded in instruction) */
f35c44bfc9e1036d0cb376fb144cdae416c7ef3avboxsync && (OP_PARM_VTYPE(cpu.pCurInstr->param1) == OP_PARM_J)
0ccdfa1953b2f57311fb9ec01a2baf5e1e366f5avboxsync && cpu.pCurInstr->opcode != OP_CALL /* complete functions are replaced; don't bother here. */
f35c44bfc9e1036d0cb376fb144cdae416c7ef3avboxsync RTGCPTR pTargetGC = PATMResolveBranch(&cpu, pCurInstrGC);
0ccdfa1953b2f57311fb9ec01a2baf5e1e366f5avboxsync Log(("We don't support far jumps here!! (%08X)\n", cpu.param1.flags));
24a0cc1776a88752cc25446a98e2a3881e623216vboxsync //jump back to guest code
24a0cc1776a88752cc25446a98e2a3881e623216vboxsync pOrgTargetGC = PATMR3PatchToGCPtr(pVM, pTargetGC, 0);
24a0cc1776a88752cc25446a98e2a3881e623216vboxsync if (patmIsCommonIDTHandlerPatch(pVM, pOrgTargetGC))
f35c44bfc9e1036d0cb376fb144cdae416c7ef3avboxsync if (patmIsKnownDisasmJump(pPatch, pTargetGC) == false)
f35c44bfc9e1036d0cb376fb144cdae416c7ef3avboxsync /* New jump, let's check it. */
f35c44bfc9e1036d0cb376fb144cdae416c7ef3avboxsync if (cpu.pCurInstr->opcode == OP_CALL) pPatch->pTempInfo->nrCalls++;
0ccdfa1953b2f57311fb9ec01a2baf5e1e366f5avboxsync rc = patmr3DisasmCode(pVM, pInstrGC, pTargetGC, pfnPATMR3Disasm, pUserData);
0ccdfa1953b2f57311fb9ec01a2baf5e1e366f5avboxsync if (cpu.pCurInstr->opcode == OP_CALL) pPatch->pTempInfo->nrCalls--;
61d064a54f03596920c3918f58ecc7764074a5d8vboxsync break; //done!
61d064a54f03596920c3918f58ecc7764074a5d8vboxsync /* Unconditional jump; return to caller. */
16a9adc14900ca18e6909679a579f6833425e030vboxsync * Disassembles the code stream until the callback function detects a failure or decides everything is acceptable
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @returns VBox status code.
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @param pVM The VM to operate on.
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @param pInstrGC Guest context pointer to the initial privileged instruction
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @param pCurInstrGC Guest context pointer to the current instruction
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @param pfnPATMR3Disasm Callback for testing the disassembled instruction
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @param pUserData User pointer (callback specific)
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsyncint patmr3DisasmCodeStream(PVM pVM, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, PFN_PATMR3ANALYSE pfnPATMR3Disasm, void *pUserData)
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync int rc = patmr3DisasmCode(pVM, pInstrGC, pCurInstrGC, pfnPATMR3Disasm, pUserData);
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync /* Free all disasm jump records. */
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync patmEmptyTree(pVM, &pPatch->pTempInfo->DisasmJumpTree);
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync#endif /* LOG_ENABLED */
009d45aa55691312278d41edb20154dc208d9cd8vboxsync * Detects it the specified address falls within a 5 byte jump generated for an active patch.
009d45aa55691312278d41edb20154dc208d9cd8vboxsync * If so, this patch is permanently disabled.
009d45aa55691312278d41edb20154dc208d9cd8vboxsync * @param pVM The VM to operate on.
009d45aa55691312278d41edb20154dc208d9cd8vboxsync * @param pInstrGC Guest context pointer to instruction
009d45aa55691312278d41edb20154dc208d9cd8vboxsync * @param pConflictGC Guest context pointer to check
b5ad839a3757b305d4e98d7264da2b53c9cd27f0vboxsync * @note also checks for patch hints to make sure they can never be enabled if a conflict is present.
b5ad839a3757b305d4e98d7264da2b53c9cd27f0vboxsyncPATMR3DECL(int) PATMR3DetectConflict(PVM pVM, RTGCPTR pInstrGC, RTGCPTR pConflictGC)
b5ad839a3757b305d4e98d7264da2b53c9cd27f0vboxsync PPATCHINFO pTargetPatch = PATMFindActivePatchByEntrypoint(pVM, pConflictGC, true /* include patch hints */);
ee00a0b29854e7f513198772bccb6650f6dd2184vboxsync return patmDisableUnusablePatch(pVM, pInstrGC, pConflictGC, pTargetPatch);
ee00a0b29854e7f513198772bccb6650f6dd2184vboxsync * Recompile the code stream until the callback function detects a failure or decides everything is acceptable
ee00a0b29854e7f513198772bccb6650f6dd2184vboxsync * @returns VBox status code.
ee00a0b29854e7f513198772bccb6650f6dd2184vboxsync * @param pVM The VM to operate on.
ee00a0b29854e7f513198772bccb6650f6dd2184vboxsync * @param pInstrGC Guest context pointer to privileged instruction
ee00a0b29854e7f513198772bccb6650f6dd2184vboxsync * @param pCurInstrGC Guest context pointer to the current instruction
ee00a0b29854e7f513198772bccb6650f6dd2184vboxsync * @param pfnPATMR3Recompile Callback for testing the disassembled instruction
ee00a0b29854e7f513198772bccb6650f6dd2184vboxsync * @param pUserData User pointer (callback specific)
ee00a0b29854e7f513198772bccb6650f6dd2184vboxsyncstatic int patmRecompileCodeStream(PVM pVM, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, PFN_PATMR3ANALYSE pfnPATMR3Recompile, void *pUserData)
681fd85cc7cd49e9cf66a917d6ae9ff36eb7d9e9vboxsync cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
681fd85cc7cd49e9cf66a917d6ae9ff36eb7d9e9vboxsync ////Log(("patmRecompileCodeStream %VGv %VGv\n", pInstrGC, pCurInstrGC));
681fd85cc7cd49e9cf66a917d6ae9ff36eb7d9e9vboxsync pCurInstrHC = PATMGCVirtToHCVirt(pVM, pPatch, pCurInstrGC);
681fd85cc7cd49e9cf66a917d6ae9ff36eb7d9e9vboxsync rc = VERR_PATCHING_REFUSED; /* fatal in this case */
681fd85cc7cd49e9cf66a917d6ae9ff36eb7d9e9vboxsync disret = PATMR3DISInstr(pVM, pPatch, &cpu, pCurInstrGC, pCurInstrHC, &opsize, szOutput);
0ccdfa1953b2f57311fb9ec01a2baf5e1e366f5avboxsync disret = PATMR3DISInstr(pVM, pPatch, &cpu, pCurInstrGC, pCurInstrHC, &opsize, NULL);
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync if (disret == false)
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync Log(("Disassembly failed (probably page not present) -> return to caller\n"));
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /* Add lookup record for patch to guest address translation */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync patmr3AddP2GLookupRecord(pVM, pPatch, PATCHCODE_PTR_HC(pPatch) + pPatch->uCurPatchOffset, pCurInstrGC, PATM_LOOKUP_BOTHDIR);
16a9adc14900ca18e6909679a579f6833425e030vboxsync rc = VINF_SUCCESS; /* Note: don't fail here; we might refuse an important patch!! */
61b5982fad4660d0fe3dd6ceba9eda85eb32f7e8vboxsync rc = pfnPATMR3Recompile(pVM, &cpu, pInstrGC, pCurInstrGC, pUserData);
61b5982fad4660d0fe3dd6ceba9eda85eb32f7e8vboxsync /* If irqs are inhibited because of the current instruction, then we must make sure the next one is executed! */
c8cedf818a53e003ce5a59c552d2f15c1b51f64cvboxsync Log(("patmRecompileCodeStream: irqs inhibited by instruction %VGv\n", pNextInstrGC));
c8cedf818a53e003ce5a59c552d2f15c1b51f64cvboxsync /* Certain instructions (e.g. sti) force the next instruction to be executed before any interrupts can occur.
c8cedf818a53e003ce5a59c552d2f15c1b51f64cvboxsync * Recompile the next instruction as well
16a9adc14900ca18e6909679a579f6833425e030vboxsync pNextInstrHC = PATMGCVirtToHCVirt(pVM, pPatch, pNextInstrGC);
16a9adc14900ca18e6909679a579f6833425e030vboxsync rc = VERR_PATCHING_REFUSED; /* fatal in this case */
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync cpunext.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync disret = PATMR3DISInstr(pVM, pPatch, &cpunext, pNextInstrGC, pNextInstrHC, &opsizenext, NULL);
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync if (disret == false)
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync rc = VERR_PATCHING_REFUSED; /* fatal in this case */
16a9adc14900ca18e6909679a579f6833425e030vboxsync case OP_IRET: /* inhibit cleared in generated code */
16a9adc14900ca18e6909679a579f6833425e030vboxsync case OP_SYSEXIT: /* faults; inhibit should be cleared in HC handling */
16a9adc14900ca18e6909679a579f6833425e030vboxsync break; /* recompile these */
a3369a746b56a8966dd78619f4d191c9662f400dvboxsync if (cpunext.pCurInstr->optype & OPTYPE_CONTROLFLOW)
ee00a0b29854e7f513198772bccb6650f6dd2184vboxsync Log(("Unexpected control flow instruction after inhibit irq instruction\n"));
51fe8789a74f6c118894aaa12eb69ec155386dbdvboxsync rc = patmPatchGenJumpToGuest(pVM, pPatch, pNextInstrGC, true /* clear inhibit irq flag */);
d5ea45cc92d7f1d3ade8189944531f665bfe8ed5vboxsync goto end; /** @todo should be ok to ignore instruction fusing in this case */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /** @note after a cli we must continue to a proper exit point */
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync rc = pfnPATMR3Recompile(pVM, &cpunext, pInstrGC, pNextInstrGC, pUserData);
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync break; /* done! */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /** @todo continue with the instructions following the jump and then recompile the jump target code */
b7a5b3f9f9ecce32ddacf8404c625ce0451bbdc1vboxsync /* For our first attempt, we'll handle only simple relative jumps and calls (immediate offset coded in instruction). */
d9d5fbda1b8f7a6f7fae555db60d0e636fd03af8vboxsync && (OP_PARM_VTYPE(cpu.pCurInstr->param1) == OP_PARM_J)
d9d5fbda1b8f7a6f7fae555db60d0e636fd03af8vboxsync && cpu.pCurInstr->opcode != OP_CALL /* complete functions are replaced; don't bother here. */
d9d5fbda1b8f7a6f7fae555db60d0e636fd03af8vboxsync GCPTRTYPE(uint8_t *) addr = PATMResolveBranch(&cpu, pCurInstrGC);
16a9adc14900ca18e6909679a579f6833425e030vboxsync Log(("We don't support far jumps here!! (%08X)\n", cpu.param1.flags));
16a9adc14900ca18e6909679a579f6833425e030vboxsync /* We don't check if the branch target lies in a valid page as we've already done that in the analysis phase. */
16a9adc14900ca18e6909679a579f6833425e030vboxsync if (!(cpu.pCurInstr->optype & OPTYPE_UNCOND_CONTROLFLOW))
16a9adc14900ca18e6909679a579f6833425e030vboxsync Log(("patmRecompileCodeStream continue passed conditional jump\n"));
16a9adc14900ca18e6909679a579f6833425e030vboxsync /* First we need to finish this linear code stream until the next exit point. */
16a9adc14900ca18e6909679a579f6833425e030vboxsync rc = patmRecompileCodeStream(pVM, pInstrGC, pCurInstrGC+opsize, pfnPATMR3Recompile, pUserData);
16a9adc14900ca18e6909679a579f6833425e030vboxsync Log(("patmRecompileCodeStream fatal error %d\n", rc));
16a9adc14900ca18e6909679a579f6833425e030vboxsync break; //fatal error
16a9adc14900ca18e6909679a579f6833425e030vboxsync if (patmGuestGCPtrToPatchGCPtr(pVM, pPatch, addr) == 0)
16a9adc14900ca18e6909679a579f6833425e030vboxsync /* New code; let's recompile it. */
16a9adc14900ca18e6909679a579f6833425e030vboxsync Log(("patmRecompileCodeStream continue with jump\n"));
16a9adc14900ca18e6909679a579f6833425e030vboxsync * If we are jumping to an existing patch (or within 5 bytes of the entrypoint), then we must temporarily disable
16a9adc14900ca18e6909679a579f6833425e030vboxsync * this patch so we can continue our analysis
16a9adc14900ca18e6909679a579f6833425e030vboxsync * We rely on CSAM to detect and resolve conflicts
16a9adc14900ca18e6909679a579f6833425e030vboxsync PPATCHINFO pTargetPatch = PATMFindActivePatchByEntrypoint(pVM, addr);
16a9adc14900ca18e6909679a579f6833425e030vboxsync Log(("Found active patch at target %VGv (%VGv) -> temporarily disabling it!!\n", addr, pTargetPatch->pPrivInstrGC));
16a9adc14900ca18e6909679a579f6833425e030vboxsync PATMR3DisablePatch(pVM, pTargetPatch->pPrivInstrGC);
670b83d458bceb92123155b5b47a39b9d24e3266vboxsync if (cpu.pCurInstr->opcode == OP_CALL) pPatch->pTempInfo->nrCalls++;
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync rc = patmRecompileCodeStream(pVM, pInstrGC, addr, pfnPATMR3Recompile, pUserData);
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync if (cpu.pCurInstr->opcode == OP_CALL) pPatch->pTempInfo->nrCalls--;
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync PATMR3EnablePatch(pVM, pTargetPatch->pPrivInstrGC);
c0a370e600bb60153a269fb32b5f709347c35768vboxsync Log(("patmRecompileCodeStream fatal error %d\n", rc));
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync break; //done!
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync /* Always return to caller here; we're done! */
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync if (cpu.pCurInstr->optype & OPTYPE_UNCOND_CONTROLFLOW)
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync * Generate the jump from guest to patch code
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync * @returns VBox status code.
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync * @param pVM The VM to operate on.
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync * @param pPatch Patch record
e4f367251aede667a6de69baa54ef9eb5f150871vboxsyncstatic int patmGenJumpToPatch(PVM pVM, PPATCHINFO pPatch, bool fAddFixup = true)
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync Assert(!(pPatch->flags & PATMFL_PATCHED_GUEST_CODE));
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync // jmp [PatchCode]
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync if (patmPatchAddReloc32(pVM, pPatch, &pPB[1], FIXUP_REL_JMPTOPATCH, pPatch->pPrivInstrGC + pPatch->cbPatchJump, pPatch->pPatchJumpDestGC) != VINF_SUCCESS)
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync Log(("Relocation failed for the jump in the guest code!!\n"));
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync temp[0] = pPatch->aPrivInstr[0]; //jump opcode copied from original instruction
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync *(uint32_t *)&temp[1] = (uint32_t)pPatch->pPatchJumpDestGC - ((uint32_t)pPatch->pPrivInstrGC + pPatch->cbPatchJump); //return address
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync if (pPatch->cbPatchJump == SIZEOF_NEAR_COND_JUMP32)
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync // jmp [PatchCode]
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync if (patmPatchAddReloc32(pVM, pPatch, &pPB[2], FIXUP_REL_JMPTOPATCH, pPatch->pPrivInstrGC + pPatch->cbPatchJump, pPatch->pPatchJumpDestGC) != VINF_SUCCESS)
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync Log(("Relocation failed for the jump in the guest code!!\n"));
c0a370e600bb60153a269fb32b5f709347c35768vboxsync temp[0] = pPatch->aPrivInstr[0]; //jump opcode copied from original instruction
c0a370e600bb60153a269fb32b5f709347c35768vboxsync temp[1] = pPatch->aPrivInstr[1]; //jump opcode copied from original instruction
c0a370e600bb60153a269fb32b5f709347c35768vboxsync *(uint32_t *)&temp[2] = (uint32_t)pPatch->pPatchJumpDestGC - ((uint32_t)pPatch->pPrivInstrGC + pPatch->cbPatchJump); //return address
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync // jmp [PatchCode]
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync if (patmPatchAddReloc32(pVM, pPatch, &pPB[1], FIXUP_REL_JMPTOPATCH, pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32, PATCHCODE_PTR_GC(pPatch)) != VINF_SUCCESS)
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync Log(("Relocation failed for the jump in the guest code!!\n"));
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync *(uint32_t *)&temp[1] = (RTGCUINTPTR)PATCHCODE_PTR_GC(pPatch) - ((RTGCUINTPTR)pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32); //return address
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync rc = PGMPhysWriteGCPtrDirty(pVM, pPatch->pPrivInstrGC, temp, pPatch->cbPatchJump);
61d064a54f03596920c3918f58ecc7764074a5d8vboxsync * Remove the jump from guest to patch code
9496f2d398b49813176939d7a339ae513d5175efvboxsync * @returns VBox status code.
f35c44bfc9e1036d0cb376fb144cdae416c7ef3avboxsync * @param pVM The VM to operate on.
d80c85a1bc7317da7d0cd1254fae6a20db039c8cvboxsync * @param pPatch Patch record
9496f2d398b49813176939d7a339ae513d5175efvboxsyncstatic int patmRemoveJumpToPatch(PVM pVM, PPATCHINFO pPatch)
1189c7dde5d7f6c26f338ced3d40fc830b822e68vboxsync cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
24a0cc1776a88752cc25446a98e2a3881e623216vboxsync disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC + i, &pPatch->pPrivInstrHC[i], &opsize, szOutput);
d1bffa158f98ff3c18f7d085e7372c9ea00e9a43vboxsync if (disret == false)
6b223679d40f5e57e55e867e806a9f194e2cde12vboxsync /* Restore original code (privileged instruction + following instructions that were overwritten because of the 5/6 byte jmp). */
6b223679d40f5e57e55e867e806a9f194e2cde12vboxsync int rc = PGMPhysWriteGCPtrDirty(pVM, pPatch->pPrivInstrGC, pPatch->aPrivInstr, pPatch->cbPatchJump);
d9d5fbda1b8f7a6f7fae555db60d0e636fd03af8vboxsync cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
d9d5fbda1b8f7a6f7fae555db60d0e636fd03af8vboxsync disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC + i, &pPatch->pPrivInstrHC[i], &opsize, szOutput);
d9d5fbda1b8f7a6f7fae555db60d0e636fd03af8vboxsync if (disret == false)
681fd85cc7cd49e9cf66a917d6ae9ff36eb7d9e9vboxsync * Generate the call from guest to patch code
681fd85cc7cd49e9cf66a917d6ae9ff36eb7d9e9vboxsync * @returns VBox status code.
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync * @param pVM The VM to operate on.
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync * @param pPatch Patch record
e4f367251aede667a6de69baa54ef9eb5f150871vboxsyncstatic int patmGenCallToPatch(PVM pVM, PPATCHINFO pPatch, RTGCPTR pTargetGC, bool fAddFixup = true)
e4f367251aede667a6de69baa54ef9eb5f150871vboxsync // jmp [PatchCode]
681fd85cc7cd49e9cf66a917d6ae9ff36eb7d9e9vboxsync if (patmPatchAddReloc32(pVM, pPatch, &pPB[1], FIXUP_REL_JMPTOPATCH, pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32, pTargetGC) != VINF_SUCCESS)
681fd85cc7cd49e9cf66a917d6ae9ff36eb7d9e9vboxsync Log(("Relocation failed for the jump in the guest code!!\n"));
681fd85cc7cd49e9cf66a917d6ae9ff36eb7d9e9vboxsync Assert(pPatch->aPrivInstr[0] == 0xE8 || pPatch->aPrivInstr[0] == 0xE9); /* call or jmp */
681fd85cc7cd49e9cf66a917d6ae9ff36eb7d9e9vboxsync *(uint32_t *)&temp[1] = (uint32_t)pTargetGC - ((uint32_t)pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32); //return address
681fd85cc7cd49e9cf66a917d6ae9ff36eb7d9e9vboxsync rc = PGMPhysWriteGCPtrDirty(pVM, pPatch->pPrivInstrGC, temp, pPatch->cbPatchJump);
16a9adc14900ca18e6909679a579f6833425e030vboxsync * Patch cli/sti pushf/popf instruction block at specified location
16a9adc14900ca18e6909679a579f6833425e030vboxsync * @returns VBox status code.
009d45aa55691312278d41edb20154dc208d9cd8vboxsync * @param pVM The VM to operate on.
009d45aa55691312278d41edb20154dc208d9cd8vboxsync * @param pInstrGC Guest context point to privileged instruction
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync * @param pInstrHC Host context point to privileged instruction
009d45aa55691312278d41edb20154dc208d9cd8vboxsync * @param uOpcode Instruction opcode
009d45aa55691312278d41edb20154dc208d9cd8vboxsync * @param uOpSize Size of starting instruction
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync * @param pPatchRec Patch record
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync * @note returns failure if patching is not allowed or possible
cab115cfa31c584def7069312a1e23c3fc88533bvboxsyncPATMR3DECL(int) PATMR3PatchBlock(PVM pVM, RTGCPTR pInstrGC, R3PTRTYPE(uint8_t *) pInstrHC,
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync uint32_t uOpcode, uint32_t uOpSize, PPATMPATCHREC pPatchRec)
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync /* Save original offset (in case of failures later on) */
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync /** @todo use the hypervisor heap (that has quite a few consequences for save/restore though) */
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync Assert(!(pPatch->flags & (PATMFL_GUEST_SPECIFIC|PATMFL_USER_MODE|PATMFL_TRAPHANDLER)));
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync /* We can 'call' a cli or pushf patch. It will either return to the original guest code when IF is set again, or fault. */
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync /** @note special precautions are taken when disabling and enabling such patches. */
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync AssertMsg(0, ("PATMR3PatchBlock: Invalid opcode %x\n", uOpcode));
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync if (!(pPatch->flags & (PATMFL_IDTHANDLER|PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT|PATMFL_SYSENTER|PATMFL_INT3_REPLACEMENT_BLOCK)))
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync /* If we're going to insert a patch jump, then the jump itself is not allowed to cross a page boundary. */
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync if ( (pPatch->flags & PATMFL_MUST_INSTALL_PATCHJMP)
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync && PAGE_ADDRESS(pInstrGC) != PAGE_ADDRESS(pInstrGC + SIZEOF_NEARJUMP32)
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync STAM_COUNTER_INC(&pVM->patm.s.StatPageBoundaryCrossed);
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync//// AssertMsgFailed(("Patch jump would cross page boundary -> refuse!!\n"));
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync pPatch->flags |= PATMFL_SUPPORT_CALLS | PATMFL_SUPPORT_INDIRECT_CALLS;
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync pPatch->pPatchBlockOffset = pVM->patm.s.offPatchMem;
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
d9d5fbda1b8f7a6f7fae555db60d0e636fd03af8vboxsync if ((pPatch->flags & (PATMFL_IDTHANDLER|PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT|PATMFL_SYSENTER)) == PATMFL_IDTHANDLER)
d9d5fbda1b8f7a6f7fae555db60d0e636fd03af8vboxsync /* Install fake cli patch (to clear the virtual IF and check int xx parameters) */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /***************************************************************************************************************************/
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /** @note We can't insert *any* code before a sysenter handler; some linux guests have an invalid stack at this point!!!!! */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /***************************************************************************************************************************/
ad77e3ec3cde24263bc7537575f5cae442bee3b1vboxsync rc = patmRecompileCodeStream(pVM, pInstrGC, pInstrGC, patmRecompileCallback, pPatch);
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync Log(("PATMR3PatchCli: patmRecompileCodeStream failed with %d\n", rc));
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /* Calculated during analysis. */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /* Most likely cause: we encountered an illegal instruction very early on. */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /** @todo could turn it into an int3 callable patch. */
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync Log(("PATMR3PatchBlock: patch block too small -> refuse\n"));
ad77e3ec3cde24263bc7537575f5cae442bee3b1vboxsync /* size of patch block */
ad77e3ec3cde24263bc7537575f5cae442bee3b1vboxsync pPatch->cbPatchBlockSize = pPatch->uCurPatchOffset;
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /* Update free pointer in patch memory. */
1bf495e3eec00dd79cecb6b36ef2a97f422c3737vboxsync pVM->patm.s.offPatchMem += pPatch->cbPatchBlockSize;
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /* Round to next 8 byte boundary. */
63c12491acc2b8b95c8ac454f1c48b98eec8f7d8vboxsync pVM->patm.s.offPatchMem = RT_ALIGN_32(pVM->patm.s.offPatchMem, 8);
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync * Insert into patch to guest lookup tree
cba6719bd64ec749967bbe931230452664109857vboxsync LogFlow(("Insert %VGv patch offset %VGv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
cba6719bd64ec749967bbe931230452664109857vboxsync pPatchRec->CoreOffset.Key = pPatch->pPatchBlockOffset;
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
9496f2d398b49813176939d7a339ae513d5175efvboxsync AssertMsg(rc, ("RTAvlULInsert failed for %x\n", pPatchRec->CoreOffset.Key));
a0240ff4f7663045c848fdbc192ea3d4d9f70a11vboxsync /* Note that patmr3SetBranchTargets can install additional patches!! */
goto failure;
#ifdef LOG_ENABLED
patmr3DisasmCodeStream(pVM, PATCHCODE_PTR_GC(pPatch), PATCHCODE_PTR_GC(pPatch), patmr3DisasmCallback, pPatch);
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, pPatch->pPrivInstrHC, &opsize, szOutput, PATMREAD_RAWCODE);
Log(("Successfully installed %s patch at %VGv\n", patmGetInstructionString(pPatch->opcode, pPatch->flags), pInstrGC));
return VINF_SUCCESS;
return rc;
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)RTAvloGCPtrGet(&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, pPatch);
LogFlow(("Insert %VGv patch offset %VGv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
return VINF_SUCCESS;
#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, pPatch);
#ifdef LOG_ENABLED
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput);
LogFlow(("Insert %VGv patch offset %VGv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
return VINF_SUCCESS;
return rc;
#ifdef DEBUG
return VERR_PATCHING_REFUSED;
#ifdef PATM_ENABLE_CALL
goto failure;
#ifdef VBOX_WITH_STATISTICS
goto failure;
goto failure;
LogFlow(("Insert %VGv patch offset %VGv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
if (!rc)
goto failure;
goto failure;
#ifdef LOG_ENABLED
patmr3DisasmCodeStream(pVM, PATCHCODE_PTR_GC(pPatch), PATCHCODE_PTR_GC(pPatch), patmr3DisasmCallback, pPatch);
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)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, (RTGCPTR)pPage);
if (pPatchPage)
uint32_t i;
if (pPatchTargetGC)
PRECPATCHTOGUEST pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatch->Patch2GuestAddrTree, offsetPatch, false);
Log(("patmCreateTrampoline: generating jump to code inside patch at %VGv\n", pPatch->pPrivInstrGC));
goto failure;
#ifdef VBOX_WITH_STATISTICS
goto failure;
goto failure;
LogFlow(("Insert %VGv patch offset %VGv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
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)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, (RTGCPTR)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;
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;
pPatchFunction = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pTargetGC);
if (pTmpInstrHC == 0)
if (pTargetGC == 0)
pPatchFunction = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pTargetGC);
if (pPatchFunction == 0)
goto failure;
goto failure;
#ifdef LOG_ENABLED
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput);
return VINF_SUCCESS;
return rc;
#ifdef LOG_ENABLED
bool disret;
goto failure;
goto failure;
if (patmPatchAddReloc32(pVM, pPatch, &pPB[pCpu->opsize - sizeof(RTGCPTR)], FIXUP_ABSOLUTE, pPatch->pPrivInstrGC, pVM->patm.s.mmio.pCachedData) != VINF_SUCCESS)
return VERR_PATCHING_REFUSED;
#ifdef LOG_ENABLED
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput);
pPatch->cbPatchJump = pPatch->cbPrivInstr; /* bit of a misnomer in this case; size of replacement instruction. */
rc = PGMPhysWriteGCPtrDirty(pVM, pInstrGC + pCpu->opsize - sizeof(RTGCPTR), &pVM->patm.s.mmio.pCachedData, sizeof(RTGCPTR));
goto failure;
#ifdef LOG_ENABLED
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput);
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(RTGCPTR)], FIXUP_ABSOLUTE) != VINF_SUCCESS)
return VERR_PATCHING_REFUSED;
#ifdef LOG_ENABLED
return VINF_SUCCESS;
int rc;
return rc;
int rc;
return rc;
* Replace an instruction with a breakpoint (0xCC), that is handled dynamically in the guest context.
PATMR3DECL(int) PATMR3PatchInstrInt3(PVM pVM, RTGCPTR pInstrGC, R3PTRTYPE(uint8_t *) pInstrHC, DISCPUSTATE *pCpu, PPATCHINFO pPatch)
int rc;
#ifdef LOG_ENABLED
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, RTGCPTR 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;
* A conflict jump patch needs to be treated differently; we'll just replace the relative jump address with one that
RTGCPTR pJmpDest = PATMR3GuestGCPtrToPatchGCPtr(pVM, pInstrGC + pCpu->opsize + (int32_t)pCpu->param1.parval);
AssertMsg(pJmpDest, ("PATMR3GuestGCPtrToPatchGCPtr failed for %VGv\n", pInstrGC + pCpu->opsize + (int32_t)pCpu->param1.parval));
goto failure;
#ifdef LOG_ENABLED
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput);
Log(("Successfully installed %s patch at %VGv\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 %VGv (%VGv)\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:%VGv != %VGv 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 = RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, offset, false);
if (pPatchRec)
return VERR_PATCHING_REFUSED;
Log(("PATMR3InstallPatch: disable operation is pending for patch at %VGv\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 */
AssertMsg(pPatchRec->patch.uState == PATCH_REFUSED || pPatchRec->patch.uState == PATCH_UNUSABLE, ("Patch an existing patched instruction?!? (%VGv, state=%d)\n", pInstrGC, pPatchRec->patch.uState));
return VERR_PATCHING_REFUSED;
return VERR_NO_MEMORY;
return rc;
/* Disallow patching instructions inside ROM code; complete function duplication is allowed though. */
return VERR_PATCHING_REFUSED;
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)RTAvloGCPtrGetBestFit(&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 ususable patch at %VGv\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 %VGv 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 %VGv 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 %VGv 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 %VGv highest %VGv\n", pPatchRec->patch.pInstrGCLowest, pPatchRec->patch.pInstrGCHighest));
Log(("Global lowest %VGv highest %VGv\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%VGv/cbPatchBlockSize", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.cbPatchJump, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cbPatchJump", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.cbPrivInstr, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cbPrivInstr", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.cCodeWrites, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cCodeWrites", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.cInvalidWrites, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cInvalidWrites", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.cTraps, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cTraps", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.flags, STAMTYPE_X32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, NULL, "/PATM/Stats/PatchBD/0x%VGv/flags", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.nrJumpRecs, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/nrJumpRecs", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.nrFixups, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/nrFixups", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.opcode, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/opcode", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.uOldState, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, NULL, "/PATM/Stats/PatchBD/0x%VGv/uOldState", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.uOpMode, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, NULL, "/PATM/Stats/PatchBD/0x%VGv/uOpMode", pPatchRec->patch.pPrivInstrGC);
STAMR3RegisterF(pVM, &pPatchRec->patch.uState, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, NULL, "/PATM/Stats/PatchBD/0x%VGv/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;
pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, pPage);
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)RTAvlGCPtrGetBestFit(&pPatch->Guest2PatchAddrTree, pPage, true);
if (pGuestToPatchRec)
/* 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)RTAvlGCPtrGetBestFit(&pPatch->Guest2PatchAddrTree, pPage-1, false);
if (pGuestToPatchRec)
pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlGCPtrGetBestFit(&pPatch->Guest2PatchAddrTree, pPage+PAGE_SIZE-1, false);
if (pGuestToPatchRec)
return VINF_SUCCESS;
int rc;
pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, pPage);
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)RTAvloGCPtrRemove(&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)RTAvlGCPtrGetBestFit(&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)RTAvlGCPtrGetBestFit(&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)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, (RTGCPTR)pPage);
if (pPatchPage)
uint32_t i;
bool fValidPatchWrite = false;
goto loop_start;
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 %VGv - write %VGv-%VGv\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)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, (RTGCPTR)pPage);
if (pPatchPage)
LogRel(("PATM: Stop monitoring IDT handler pages at %VGv - invalid write %VGv-%VGv (this is not a fatal error)\n", pPatch->pPrivInstrGC, GCPtr, GCPtr+cbWrite));
LogRel(("PATM: Disable block at %VGv - invalid write %VGv-%VGv \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)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, addr);
if (pPatchPage)
return VINF_SUCCESS;
return VERR_PATCH_NOT_FOUND;
pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGetBestFit(&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)));
rc = PGMPhysGCPtr2HCPtr(pVM, pPatchRec->patch.pPrivInstrGC, (PRTHCPTR)&pPatchRec->patch.pPrivInstrHC);
RTGCINTPTR displ = (RTGCUINTPTR)PATCHCODE_PTR_GC(pPatch) - ((RTGCUINTPTR)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, RTGCPTR pInstrGC, RTGCPTR 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 %VGv with target %VGv -> turn into int 3 patch!!\n", pInstrGC, pConflictPatch->pPrivInstrGC));
return VINF_SUCCESS;
return VINF_SUCCESS;
Log(("PATM -> CONFLICT: Found active patch at instruction %VGv with target %VGv -> DISABLING it!!\n", pInstrGC, pConflictPatch->pPrivInstrGC));
return VINF_SUCCESS;
return VERR_PATCH_DISABLED;
if (pPatchRec)
rc = PGMPhysGCPtr2HCPtr(pVM, pPatchRec->patch.pPrivInstrGC, (PRTHCPTR)&pPatchRec->patch.pPrivInstrHC);
#ifdef DEBUG
return VERR_PATCH_NOT_FOUND;
return rc;
#ifdef DEBUG
bool disret;
disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC + i, &pPatch->pPrivInstrHC[i], &opsize, szOutput);
i += opsize;
return VERR_PATCH_NOT_FOUND;
return rc;
return rc;
return VERR_PATCH_NOT_FOUND;
pNode = RTAvloGCPtrRemove(&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);
/** 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 %VGv. 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)RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC, false);
/* if the patch is enabled, the pointer is not indentical 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)RTAvlGCPtrGet(&pPatch->Guest2PatchAddrTree, pInstrGC);
if (pGuestToPatchRec)
PPATMPATCHREC pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC, false);
if (pPatchRec && pPatchRec->patch.uState == PATCH_ENABLED && pInstrGC >= pPatchRec->patch.pPrivInstrGC)
void *pvPatchCoreOffset;
pvPatchCoreOffset = RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, pPatchGC - pVM->patm.s.pPatchMemGC, false);
if (pvPatchCoreOffset == 0)
Log(("PATMR3PatchToGCPtr failed for %VGv 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, RTGCPTR pEip)
int rc;
Log(("patmR3HandleDirtyInstr: dirty instruction at %VGv (%VGv)\n", pEip, pPatchToGuestRec->pOrgInstrGC));
cbDirty = 0;
LogRel(("PATM: patmR3HandleDirtyInstr: dirty instruction at %VGv (%VGv) ignored, because instruction in function was reused as target of jump\n", pEip, pPatchToGuestRec->pOrgInstrGC));
return VERR_PATCHING_REFUSED;
#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! */
LogRel(("PATM: Failed to refresh dirty patch at %VGv. 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 = RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, offset, false);
if (pvPatchCoreOffset)
Log(("PATMR3HandleTrap: disable operation is pending for patch at %VGv\n", pPatch->patch.pPrivInstrGC));
AssertReleaseMsg(rc != VWRN_PATCH_REMOVED, ("PATMR3DisablePatch removed patch at %VGv\n", pPrivInstrGC));
AssertMsg(pPatch->patch.uState == PATCH_DISABLED || pPatch->patch.uState == PATCH_UNUSABLE, ("Unexpected failure to disable patch state=%d rc=%Vrc\n", pPatch->patch.uState, rc));
pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatch->patch.Patch2GuestAddrTree, offset, false);
AssertReleaseMsg(pPatchToGuestRec, ("PATMR3HandleTrap: Unable to find corresponding guest address for %VGv (offset %x)\n", pEip, offset));
AssertReleaseMsg(pVM->patm.s.pGCStateHC->fPIF == 0, ("PATMR3HandleTrap: Unable to find translation record for %VGv (PIF=0)\n", pEip));
return VINF_SUCCESS;
return VINF_PATCH_CONTINUE;
if (pPatch)
("Crash in patch code %VGv (%VGv) esp=%RX32\nPatch state=%x flags=%x fDirty=%d\n%s\n", pEip, pNewEip, CPUMGetGuestESP(pVM), pPatch->patch.uState, pPatch->patch.flags, pPatchToGuestRec->fDirty, szBuf));
if (pvPatchCoreOffset == 0)
return rc;
#ifdef VBOX_STRICT
bool disret;
disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pNewEip, PATMGCVirtToHCVirt(pVM, &pPatch->patch, pNewEip), &opsize, NULL, PATMREAD_RAWCODE);
int rc;
Log(("Expected return address %VGv found address %VGv Psp=%x\n", pVM->patm.s.pGCStackHC[(pVM->patm.s.pGCStateHC->Psp+PATM_STACK_SIZE)/sizeof(RTGCPTR)], retaddr, pVM->patm.s.pGCStateHC->Psp));
#ifdef VBOX_STRICT
bool disret;
disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pNewEip, PATMGCVirtToHCVirt(pVM, &pPatch->patch, 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, &pPatch->patch, pNewEip), &opsize, NULL, PATMREAD_RAWCODE);
Assert(cpu.pCurInstr->opcode == OP_SYSEXIT || cpu.pCurInstr->opcode == OP_HLT || cpu.pCurInstr->opcode == OP_IRET);
Log2(("pPatchBlockGC %VGv - pEip %VGv corresponding GC address %VGv\n", PATCHCODE_PTR_GC(&pPatch->patch), pEip, pNewEip));
if (pNewEip >= pPatch->patch.pPrivInstrGC && pNewEip < pPatch->patch.pPrivInstrGC + pPatch->patch.cbPatchJump)
Log(("Disabling patch at location %VGv 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 %VGv due to too many traps inside patch code\n", pPatch->patch.pPrivInstrGC));
return VERR_PATCH_DISABLED;
return VINF_SUCCESS;
PPATMPATCHREC pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGetBestFit(&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)RTAvloGCPtrGetBestFit(&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)