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