VirtualBox

source: vbox/trunk/src/VBox/VMM/PATM/PATM.cpp@ 5679

Last change on this file since 5679 was 5612, checked in by vboxsync, 17 years ago

Bit more checking to ensure we don't invalidate instructions that are not affected by the write

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 240.4 KB
Line 
1/* $Id: PATM.cpp 5612 2007-11-02 11:59:34Z vboxsync $ */
2/** @file
3 * PATM - Dynamic Guest OS Patching Manager
4 *
5 * NOTE: Never ever reuse patch memory!!
6 */
7
8/*
9 * Copyright (C) 2006-2007 innotek GmbH
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License as published by the Free Software Foundation,
15 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
16 * distribution. VirtualBox OSE is distributed in the hope that it will
17 * be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#define LOG_GROUP LOG_GROUP_PATM
24#include <VBox/patm.h>
25#include <VBox/stam.h>
26#include <VBox/pgm.h>
27#include <VBox/cpum.h>
28#include <VBox/cpumdis.h>
29#include <VBox/iom.h>
30#include <VBox/sup.h>
31#include <VBox/mm.h>
32#include <VBox/ssm.h>
33#include <VBox/pdm.h>
34#include <VBox/trpm.h>
35#include <VBox/cfgm.h>
36#include <VBox/param.h>
37#include <VBox/selm.h>
38#include <iprt/avl.h>
39#include "PATMInternal.h"
40#include "PATMPatch.h"
41#include <VBox/vm.h>
42#include <VBox/csam.h>
43
44#include <VBox/dbg.h>
45#include <VBox/err.h>
46#include <VBox/log.h>
47#include <iprt/assert.h>
48#include <iprt/asm.h>
49#include <VBox/dis.h>
50#include <VBox/disopcode.h>
51
52#include <iprt/string.h>
53#include "PATMA.h"
54
55//#define PATM_REMOVE_PATCH_ON_TOO_MANY_TRAPS
56//#define PATM_DISABLE_ALL
57
58/*******************************************************************************
59* Internal Functions *
60*******************************************************************************/
61
62static int patmDisableUnusablePatch(PVM pVM, RTGCPTR pInstrGC, RTGCPTR pConflictAddr, PPATCHINFO pPatch);
63static int patmActivateInt3Patch(PVM pVM, PPATCHINFO pPatch);
64static int patmDeactivateInt3Patch(PVM pVM, PPATCHINFO pPatch);
65
66#ifdef LOG_ENABLED // keep gcc quiet
67static bool patmIsCommonIDTHandlerPatch(PVM pVM, RTGCPTR pInstrGC);
68#endif
69#ifdef VBOX_WITH_STATISTICS
70static const char *PATMPatchType(PVM pVM, PPATCHINFO pPatch);
71static void patmResetStat(PVM pVM, void *pvSample);
72static void patmPrintStat(PVM pVM, void *pvSample, char *pszBuf, size_t cchBuf);
73#endif
74
75#define patmPatchHCPtr2PatchGCPtr(pVM, pHC) (pVM->patm.s.pPatchMemGC + (pHC - pVM->patm.s.pPatchMemHC))
76#define patmPatchGCPtr2PatchHCPtr(pVM, pGC) (pVM->patm.s.pPatchMemHC + (pGC - pVM->patm.s.pPatchMemGC))
77
78static int patmReinit(PVM pVM);
79static DECLCALLBACK(int) RelocatePatches(PAVLOGCPTRNODECORE pNode, void *pParam);
80static DECLCALLBACK(int) patmVirtPageHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
81
82#ifdef VBOX_WITH_DEBUGGER
83static DECLCALLBACK(int) DisableAllPatches(PAVLOGCPTRNODECORE pNode, void *pVM);
84static DECLCALLBACK(int) patmr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
85static DECLCALLBACK(int) patmr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult);
86
87/** Command descriptors. */
88static const DBGCCMD g_aCmds[] =
89{
90 /* pszCmd, cArgsMin, cArgsMax, paArgDesc, cArgDescs, pResultDesc, fFlags, pfnHandler pszSyntax, ....pszDescription */
91 { "patmon", 0, 0, NULL, 0, NULL, 0, patmr3CmdOn, "", "Enable patching." },
92 { "patmoff", 0, 0, NULL, 0, NULL, 0, patmr3CmdOff, "", "Disable patching." },
93};
94#endif
95
96/**
97 * Initializes the PATM.
98 *
99 * @returns VBox status code.
100 * @param pVM The VM to operate on.
101 */
102PATMR3DECL(int) PATMR3Init(PVM pVM)
103{
104 int rc;
105
106 Log(("PATMR3Init: Patch record size %d\n", sizeof(PATCHINFO)));
107
108 AssertReleaseMsg(PATMInterruptFlag == (VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC | VM_FF_TIMER | VM_FF_REQUEST),
109 ("Interrupt flags out of sync!! PATMInterruptFlag=%#x expected %#x. broken assembler?\n", PATMInterruptFlag, VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC | VM_FF_TIMER | VM_FF_REQUEST));
110
111 /* Allocate patch memory and GC patch state memory. */
112 pVM->patm.s.cbPatchMem = PATCH_MEMORY_SIZE;
113 /* Add another page in case the generated code is much larger than expected. */
114 /** @todo bad safety precaution */
115 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);
116 if (VBOX_FAILURE(rc))
117 {
118 Log(("MMR3HyperAlloc failed with %Vrc\n", rc));
119 return rc;
120 }
121 pVM->patm.s.pPatchMemGC = MMHyperHC2GC(pVM, pVM->patm.s.pPatchMemHC);
122
123 /* PATM stack page for call instruction execution. (2 parts: one for our private stack and one to store the original return address */
124 pVM->patm.s.pGCStackHC = (RTGCPTR *)(pVM->patm.s.pPatchMemHC + PATCH_MEMORY_SIZE + PAGE_SIZE);
125 pVM->patm.s.pGCStackGC = MMHyperHC2GC(pVM, pVM->patm.s.pGCStackHC);
126
127 /*
128 * Hypervisor memory for GC status data (read/write)
129 *
130 * Note1: This is non-critical data; if trashed by the guest, then it will only cause problems for itself
131 * Note2: This doesn't really belong here, but we need access to it for relocation purposes
132 *
133 */
134 Assert(sizeof(PATMGCSTATE) < PAGE_SIZE); /** @note hardcoded dependencies on this exist. */
135 pVM->patm.s.pGCStateHC = (PPATMGCSTATE)((uint8_t *)pVM->patm.s.pGCStackHC + PATM_STACK_TOTAL_SIZE);
136 pVM->patm.s.pGCStateGC = MMHyperHC2GC(pVM, pVM->patm.s.pGCStateHC);
137
138 /* Hypervisor memory for patch statistics */
139 pVM->patm.s.pStatsHC = (PSTAMRATIOU32)((uint8_t *)pVM->patm.s.pGCStateHC + PAGE_SIZE);
140 pVM->patm.s.pStatsGC = MMHyperHC2GC(pVM, pVM->patm.s.pStatsHC);
141
142 /* Memory for patch lookup trees. */
143 rc = MMHyperAlloc(pVM, sizeof(*pVM->patm.s.PatchLookupTreeHC), 0, MM_TAG_PATM, (void **)&pVM->patm.s.PatchLookupTreeHC);
144 AssertRCReturn(rc, rc);
145 pVM->patm.s.PatchLookupTreeGC = MMHyperHC2GC(pVM, pVM->patm.s.PatchLookupTreeHC);
146
147#ifdef RT_ARCH_AMD64 /* see patmReinit(). */
148 /* Check CFGM option. */
149 rc = CFGMR3QueryBool(CFGMR3GetRoot(pVM), "PATMEnabled", &pVM->fPATMEnabled);
150 if (VBOX_FAILURE(rc))
151# ifdef PATM_DISABLE_ALL
152 pVM->fPATMEnabled = false;
153# else
154 pVM->fPATMEnabled = true;
155# endif
156#endif
157
158 rc = patmReinit(pVM);
159 AssertRC(rc);
160 if (VBOX_FAILURE(rc))
161 return rc;
162
163 /*
164 * Register save and load state notificators.
165 */
166 rc = SSMR3RegisterInternal(pVM, "PATM", 0, PATM_SSM_VERSION, sizeof(pVM->patm.s) + PATCH_MEMORY_SIZE + PAGE_SIZE + PATM_STACK_TOTAL_SIZE + PAGE_SIZE,
167 NULL, patmr3Save, NULL,
168 NULL, patmr3Load, NULL);
169 if (VBOX_FAILURE(rc))
170 {
171 AssertRC(rc);
172 return rc;
173 }
174
175#ifdef VBOX_WITH_DEBUGGER
176 /*
177 * Debugger commands.
178 */
179 static bool fRegisteredCmds = false;
180 if (!fRegisteredCmds)
181 {
182 int rc = DBGCRegisterCommands(&g_aCmds[0], ELEMENTS(g_aCmds));
183 if (VBOX_SUCCESS(rc))
184 fRegisteredCmds = true;
185 }
186#endif
187
188#ifdef VBOX_WITH_STATISTICS
189 STAM_REG(pVM, &pVM->patm.s.StatNrOpcodeRead, STAMTYPE_COUNTER, "/PATM/OpcodeBytesRead", STAMUNIT_OCCURENCES, "The number of opcode bytes read by the recompiler.");
190 STAM_REG(pVM, &pVM->patm.s.StatPATMMemoryUsed,STAMTYPE_COUNTER, "/PATM/MemoryUsed", STAMUNIT_OCCURENCES, "The amount of hypervisor heap used for patches.");
191 STAM_REG(pVM, &pVM->patm.s.StatDisabled, STAMTYPE_COUNTER, "/PATM/Patch/Disabled", STAMUNIT_OCCURENCES, "Number of times patches were disabled.");
192 STAM_REG(pVM, &pVM->patm.s.StatEnabled, STAMTYPE_COUNTER, "/PATM/Patch/Enabled", STAMUNIT_OCCURENCES, "Number of times patches were enabled.");
193 STAM_REG(pVM, &pVM->patm.s.StatDirty, STAMTYPE_COUNTER, "/PATM/Patch/Dirty", STAMUNIT_OCCURENCES, "Number of times patches were marked dirty.");
194 STAM_REG(pVM, &pVM->patm.s.StatUnusable, STAMTYPE_COUNTER, "/PATM/Patch/Unusable", STAMUNIT_OCCURENCES, "Number of unusable patches (conflicts).");
195 STAM_REG(pVM, &pVM->patm.s.StatInstalled, STAMTYPE_COUNTER, "/PATM/Patch/Installed", STAMUNIT_OCCURENCES, "Number of installed patches.");
196 STAM_REG(pVM, &pVM->patm.s.StatInt3Callable, STAMTYPE_COUNTER, "/PATM/Patch/Int3Callable", STAMUNIT_OCCURENCES, "Number of cli patches turned into int3 patches.");
197
198 STAM_REG(pVM, &pVM->patm.s.StatInt3BlockRun, STAMTYPE_COUNTER, "/PATM/Patch/Run/Int3", STAMUNIT_OCCURENCES, "Number of times an int3 block patch was executed.");
199 STAMR3RegisterF(pVM, &pVM->patm.s.pGCStateHC->uPatchCalls, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Patch/Run/Normal");
200
201 STAM_REG(pVM, &pVM->patm.s.StatInstalledFunctionPatches, STAMTYPE_COUNTER, "/PATM/Patch/Installed/Function", STAMUNIT_OCCURENCES, "Number of installed function duplication patches.");
202 STAM_REG(pVM, &pVM->patm.s.StatInstalledTrampoline, STAMTYPE_COUNTER, "/PATM/Patch/Installed/Trampoline", STAMUNIT_OCCURENCES, "Number of installed trampoline patches.");
203 STAM_REG(pVM, &pVM->patm.s.StatInstalledJump, STAMTYPE_COUNTER, "/PATM/Patch/Installed/Jump", STAMUNIT_OCCURENCES, "Number of installed jump patches.");
204
205 STAM_REG(pVM, &pVM->patm.s.StatOverwritten, STAMTYPE_COUNTER, "/PATM/Patch/Overwritten", STAMUNIT_OCCURENCES, "Number of overwritten patches.");
206 STAM_REG(pVM, &pVM->patm.s.StatFixedConflicts,STAMTYPE_COUNTER, "/PATM/Patch/ConflictFixed", STAMUNIT_OCCURENCES, "Number of fixed conflicts.");
207 STAM_REG(pVM, &pVM->patm.s.StatFlushed, STAMTYPE_COUNTER, "/PATM/Patch/Flushed", STAMUNIT_OCCURENCES, "Number of flushes of pages with patch jumps.");
208 STAM_REG(pVM, &pVM->patm.s.StatMonitored, STAMTYPE_COUNTER, "/PATM/Patch/Monitored", STAMUNIT_OCCURENCES, "Number of patches in monitored patch pages.");
209 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.");
210
211 STAM_REG(pVM, &pVM->patm.s.StatHandleTrap, STAMTYPE_PROFILE, "/PATM/HandleTrap", STAMUNIT_TICKS_PER_CALL, "Profiling of PATMR3HandleTrap");
212 STAM_REG(pVM, &pVM->patm.s.StatPushTrap, STAMTYPE_COUNTER, "/PATM/HandleTrap/PushWP", STAMUNIT_OCCURENCES, "Number of traps due to monitored stack pages.");
213
214 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");
215 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");
216
217 STAM_REG(pVM, &pVM->patm.s.StatDuplicateREQFailed, STAMTYPE_COUNTER, "/PATM/Function/DupREQ/Failed", STAMUNIT_OCCURENCES, "Nr of failed PATMR3DuplicateFunctionRequest calls");
218 STAM_REG(pVM, &pVM->patm.s.StatDuplicateREQSuccess, STAMTYPE_COUNTER, "/PATM/Function/DupREQ/Success", STAMUNIT_OCCURENCES, "Nr of successful PATMR3DuplicateFunctionRequest calls");
219 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");
220
221 STAM_REG(pVM, &pVM->patm.s.StatFunctionLookupInsert, STAMTYPE_COUNTER, "/PATM/Function/Lookup/Insert", STAMUNIT_OCCURENCES, "Nr of successful function address insertions");
222 STAM_REG(pVM, &pVM->patm.s.StatFunctionLookupReplace, STAMTYPE_COUNTER, "/PATM/Function/Lookup/Replace", STAMUNIT_OCCURENCES, "Nr of successful function address replacements");
223 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");
224
225 STAM_REG(pVM, &pVM->patm.s.StatFunctionFound, STAMTYPE_COUNTER, "/PATM/Function/Found", STAMUNIT_OCCURENCES, "Nr of successful function patch lookups in GC");
226 STAM_REG(pVM, &pVM->patm.s.StatFunctionNotFound, STAMTYPE_COUNTER, "/PATM/Function/NotFound", STAMUNIT_OCCURENCES, "Nr of failed function patch lookups in GC");
227
228 STAM_REG(pVM, &pVM->patm.s.StatPatchWrite, STAMTYPE_PROFILE, "/PATM/Write/Handle", STAMUNIT_TICKS_PER_CALL, "Profiling of PATMR3PatchWrite");
229 STAM_REG(pVM, &pVM->patm.s.StatPatchWriteDetect, STAMTYPE_PROFILE, "/PATM/Write/Detect", STAMUNIT_TICKS_PER_CALL, "Profiling of PATMIsWriteToPatchPage");
230 STAM_REG(pVM, &pVM->patm.s.StatPatchWriteInterpreted, STAMTYPE_COUNTER, "/PATM/Write/Interpreted/Success", STAMUNIT_OCCURENCES, "Nr of interpreted patch writes.");
231 STAM_REG(pVM, &pVM->patm.s.StatPatchWriteInterpretedFailed, STAMTYPE_COUNTER, "/PATM/Write/Interpreted/Failed", STAMUNIT_OCCURENCES, "Nr of failed interpreted patch writes.");
232
233 STAM_REG(pVM, &pVM->patm.s.StatPatchRefreshSuccess, STAMTYPE_COUNTER, "/PATM/Refresh/Success", STAMUNIT_OCCURENCES, "Successful patch refreshes");
234 STAM_REG(pVM, &pVM->patm.s.StatPatchRefreshFailed, STAMTYPE_COUNTER, "/PATM/Refresh/Failure", STAMUNIT_OCCURENCES, "Failed patch refreshes");
235
236 STAM_REG(pVM, &pVM->patm.s.StatPatchPageInserted, STAMTYPE_COUNTER, "/PATM/Page/Inserted", STAMUNIT_OCCURENCES, "Nr of inserted guest pages that were patched");
237 STAM_REG(pVM, &pVM->patm.s.StatPatchPageRemoved, STAMTYPE_COUNTER, "/PATM/Page/Removed", STAMUNIT_OCCURENCES, "Nr of removed guest pages that were patched");
238
239 STAM_REG(pVM, &pVM->patm.s.StatInstrDirty, STAMTYPE_COUNTER, "/PATM/Instr/Dirty/Detected", STAMUNIT_OCCURENCES, "Number of times instructions were marked dirty.");
240 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.");
241 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.");
242
243 STAM_REG(pVM, &pVM->patm.s.StatSysEnter, STAMTYPE_COUNTER, "/PATM/Emul/SysEnter", STAMUNIT_OCCURENCES, "Number of times sysenter was emulated.");
244 STAM_REG(pVM, &pVM->patm.s.StatSysExit, STAMTYPE_COUNTER, "/PATM/Emul/SysExit" , STAMUNIT_OCCURENCES, "Number of times sysexit was emulated.");
245 STAM_REG(pVM, &pVM->patm.s.StatEmulIret, STAMTYPE_COUNTER, "/PATM/Emul/Iret/Success", STAMUNIT_OCCURENCES, "Number of times iret was emulated.");
246 STAM_REG(pVM, &pVM->patm.s.StatEmulIretFailed, STAMTYPE_COUNTER, "/PATM/Emul/Iret/Failed", STAMUNIT_OCCURENCES, "Number of times iret was emulated.");
247
248 STAM_REG(pVM, &pVM->patm.s.StatGenRet, STAMTYPE_COUNTER, "/PATM/Gen/Ret" , STAMUNIT_OCCURENCES, "Number of generated ret instructions.");
249 STAM_REG(pVM, &pVM->patm.s.StatGenRetReused, STAMTYPE_COUNTER, "/PATM/Gen/RetReused" , STAMUNIT_OCCURENCES, "Number of reused ret instructions.");
250 STAM_REG(pVM, &pVM->patm.s.StatGenCall, STAMTYPE_COUNTER, "/PATM/Gen/Call", STAMUNIT_OCCURENCES, "Number of generated call instructions.");
251 STAM_REG(pVM, &pVM->patm.s.StatGenJump, STAMTYPE_COUNTER, "/PATM/Gen/Jmp" , STAMUNIT_OCCURENCES, "Number of generated indirect jump instructions.");
252 STAM_REG(pVM, &pVM->patm.s.StatGenPopf, STAMTYPE_COUNTER, "/PATM/Gen/Popf" , STAMUNIT_OCCURENCES, "Number of generated popf instructions.");
253
254 STAM_REG(pVM, &pVM->patm.s.StatCheckPendingIRQ, STAMTYPE_COUNTER, "/PATM/GC/CheckIRQ" , STAMUNIT_OCCURENCES, "Number of traps that ask to check for pending irqs.");
255#endif /* VBOX_WITH_STATISTICS */
256
257 Log(("PATMCallRecord.size %d\n", PATMCallRecord.size));
258 Log(("PATMCallIndirectRecord.size %d\n", PATMCallIndirectRecord.size));
259 Log(("PATMRetRecord.size %d\n", PATMRetRecord.size));
260 Log(("PATMJumpIndirectRecord.size %d\n", PATMJumpIndirectRecord.size));
261 Log(("PATMPopf32Record.size %d\n", PATMPopf32Record.size));
262 Log(("PATMIretRecord.size %d\n", PATMIretRecord.size));
263 Log(("PATMStiRecord.size %d\n", PATMStiRecord.size));
264 Log(("PATMCheckIFRecord.size %d\n", PATMCheckIFRecord.size));
265
266 return rc;
267}
268
269/**
270 * Finalizes HMA page attributes.
271 *
272 * @returns VBox status code.
273 * @param pVM The VM handle.
274 */
275PATMR3DECL(int) PATMR3InitFinalize(PVM pVM)
276{
277 /* The GC state, stack and statistics must be read/write for the guest (supervisor only of course). */
278 int rc = PGMMapSetPage(pVM, pVM->patm.s.pGCStateGC, PAGE_SIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
279 if (VBOX_FAILURE(rc))
280 Log(("PATMR3InitFinalize: PGMMapSetPage failed with %Vrc!!\n", rc));
281
282 rc = PGMMapSetPage(pVM, pVM->patm.s.pGCStackGC, PATM_STACK_TOTAL_SIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
283 if (VBOX_FAILURE(rc))
284 Log(("PATMR3InitFinalize: PGMMapSetPage failed with %Vrc!!\n", rc));
285
286 rc = PGMMapSetPage(pVM, pVM->patm.s.pStatsGC, PATM_STAT_MEMSIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D | X86_PTE_RW);
287 if (VBOX_FAILURE(rc))
288 Log(("PATMR3InitFinalize: PGMMapSetPage failed with %Vrc!!\n", rc));
289
290 return rc;
291}
292
293/**
294 * (Re)initializes PATM
295 *
296 * @param pVM The VM.
297 */
298static int patmReinit(PVM pVM)
299{
300 int rc;
301
302 /*
303 * Assert alignment and sizes.
304 */
305 AssertRelease(!(RT_OFFSETOF(VM, patm.s) & 31));
306 AssertRelease(sizeof(pVM->patm.s) <= sizeof(pVM->patm.padding));
307
308 /*
309 * Setup any fixed pointers and offsets.
310 */
311 pVM->patm.s.offVM = RT_OFFSETOF(VM, patm);
312
313#ifndef RT_ARCH_AMD64 /* would be nice if this was changed everywhere. was driving me crazy on AMD64. */
314#ifndef PATM_DISABLE_ALL
315 pVM->fPATMEnabled = true;
316#endif
317#endif
318
319 Assert(pVM->patm.s.pGCStateHC);
320 memset(pVM->patm.s.pGCStateHC, 0, PAGE_SIZE);
321 AssertReleaseMsg(pVM->patm.s.pGCStateGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pGCStateGC));
322 Log(("Patch memory allocated at %p - %VGv\n", pVM->patm.s.pPatchMemHC, pVM->patm.s.pPatchMemGC));
323 pVM->patm.s.pGCStateHC->uVMFlags = X86_EFL_IF;
324
325 Assert(pVM->patm.s.pGCStackHC);
326 memset(pVM->patm.s.pGCStackHC, 0, PAGE_SIZE);
327 AssertReleaseMsg(pVM->patm.s.pGCStackGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pGCStackGC));
328 pVM->patm.s.pGCStateHC->Psp = PATM_STACK_SIZE;
329 pVM->patm.s.pGCStateHC->fPIF = 1; /* PATM Interrupt Flag */
330
331 Assert(pVM->patm.s.pStatsHC);
332 memset(pVM->patm.s.pStatsHC, 0, PATM_STAT_MEMSIZE);
333 AssertReleaseMsg(pVM->patm.s.pStatsGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pStatsGC));
334
335 Assert(pVM->patm.s.pPatchMemHC);
336 Assert(pVM->patm.s.pPatchMemGC = MMHyperHC2GC(pVM, pVM->patm.s.pPatchMemHC));
337 memset(pVM->patm.s.pPatchMemHC, 0, PATCH_MEMORY_SIZE);
338 AssertReleaseMsg(pVM->patm.s.pPatchMemGC, ("Impossible! MMHyperHC2GC(%p) failed!\n", pVM->patm.s.pPatchMemHC));
339
340 /* Needed for future patching of sldt/sgdt/sidt/str etc. */
341 rc = CPUMR3QueryGuestCtxGCPtr(pVM, &pVM->patm.s.pCPUMCtxGC);
342 AssertRCReturn(rc, rc);
343
344 Assert(pVM->patm.s.PatchLookupTreeHC);
345 Assert(pVM->patm.s.PatchLookupTreeGC == MMHyperHC2GC(pVM, pVM->patm.s.PatchLookupTreeHC));
346
347 /*
348 * (Re)Initialize PATM structure
349 */
350 Assert(!pVM->patm.s.PatchLookupTreeHC->PatchTree);
351 Assert(!pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr);
352 Assert(!pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage);
353 pVM->patm.s.offPatchMem = 16; /* don't start with zero here */
354 pVM->patm.s.uCurrentPatchIdx = 1; /* Index zero is a dummy */
355 pVM->patm.s.pvFaultMonitor = 0;
356 pVM->patm.s.deltaReloc = 0;
357
358 /* Lowest and highest patched instruction */
359 pVM->patm.s.pPatchedInstrGCLowest = ~0;
360 pVM->patm.s.pPatchedInstrGCHighest = 0;
361
362 pVM->patm.s.PatchLookupTreeHC->PatchTree = 0;
363 pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr = 0;
364 pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage = 0;
365
366 pVM->patm.s.pfnSysEnterPatchGC = 0;
367 pVM->patm.s.pfnSysEnterGC = 0;
368
369 pVM->patm.s.fOutOfMemory = false;
370
371 pVM->patm.s.pfnHelperCallGC = 0;
372
373 /* Generate all global functions to be used by future patches. */
374 /* We generate a fake patch in order to use the existing code for relocation. */
375 rc = MMHyperAlloc(pVM, sizeof(PATMPATCHREC), 0, MM_TAG_PATM_PATCH, (void **)&pVM->patm.s.pGlobalPatchRec);
376 if (VBOX_FAILURE(rc))
377 {
378 Log(("Out of memory!!!!\n"));
379 return VERR_NO_MEMORY;
380 }
381 pVM->patm.s.pGlobalPatchRec->patch.flags = PATMFL_GLOBAL_FUNCTIONS;
382 pVM->patm.s.pGlobalPatchRec->patch.uState = PATCH_ENABLED;
383 pVM->patm.s.pGlobalPatchRec->patch.pPatchBlockOffset = pVM->patm.s.offPatchMem;
384
385 rc = patmPatchGenGlobalFunctions(pVM, &pVM->patm.s.pGlobalPatchRec->patch);
386 AssertRC(rc);
387
388 /* Update free pointer in patch memory. */
389 pVM->patm.s.offPatchMem += pVM->patm.s.pGlobalPatchRec->patch.uCurPatchOffset;
390 /* Round to next 8 byte boundary. */
391 pVM->patm.s.offPatchMem = RT_ALIGN_32(pVM->patm.s.offPatchMem, 8);
392 return rc;
393}
394
395
396/**
397 * Applies relocations to data and code managed by this
398 * component. This function will be called at init and
399 * whenever the VMM need to relocate it self inside the GC.
400 *
401 * The PATM will update the addresses used by the switcher.
402 *
403 * @param pVM The VM.
404 */
405PATMR3DECL(void) PATMR3Relocate(PVM pVM)
406{
407 RTGCPTR GCPtrNew = MMHyperHC2GC(pVM, pVM->patm.s.pGCStateHC);
408 RTGCINTPTR delta = GCPtrNew - pVM->patm.s.pGCStateGC;
409
410 Log(("PATMR3Relocate from %VGv to %VGv - delta %08X\n", pVM->patm.s.pGCStateGC, GCPtrNew, delta));
411 if (delta)
412 {
413 PCPUMCTX pCtx;
414 int rc;
415
416 /* Update CPUMCTX guest context pointer. */
417 pVM->patm.s.pCPUMCtxGC += delta;
418
419 pVM->patm.s.deltaReloc = delta;
420
421 RTAvloGCPtrDoWithAll(&pVM->patm.s.PatchLookupTreeHC->PatchTree, true, RelocatePatches, (void *)pVM);
422
423 rc = CPUMQueryGuestCtxPtr(pVM, &pCtx);
424 AssertRC(rc);
425
426 /* If we are running patch code right now, then also adjust EIP. */
427 if (PATMIsPatchGCAddr(pVM, pCtx->eip))
428 pCtx->eip += delta;
429
430 pVM->patm.s.pGCStateGC = GCPtrNew;
431 pVM->patm.s.pPatchMemGC = MMHyperHC2GC(pVM, pVM->patm.s.pPatchMemHC);
432
433 pVM->patm.s.pGCStackGC = MMHyperHC2GC(pVM, pVM->patm.s.pGCStackHC);
434
435 pVM->patm.s.pStatsGC = MMHyperHC2GC(pVM, pVM->patm.s.pStatsHC);
436
437 pVM->patm.s.PatchLookupTreeGC = MMHyperHC2GC(pVM, pVM->patm.s.PatchLookupTreeHC);
438
439 if (pVM->patm.s.pfnSysEnterPatchGC)
440 pVM->patm.s.pfnSysEnterPatchGC += delta;
441
442 /* Deal with the global patch functions. */
443 pVM->patm.s.pfnHelperCallGC += delta;
444 pVM->patm.s.pfnHelperRetGC += delta;
445 pVM->patm.s.pfnHelperIretGC += delta;
446 pVM->patm.s.pfnHelperJumpGC += delta;
447
448 RelocatePatches(&pVM->patm.s.pGlobalPatchRec->Core, (void *)pVM);
449 }
450}
451
452
453/**
454 * Terminates the PATM.
455 *
456 * Termination means cleaning up and freeing all resources,
457 * the VM it self is at this point powered off or suspended.
458 *
459 * @returns VBox status code.
460 * @param pVM The VM to operate on.
461 */
462PATMR3DECL(int) PATMR3Term(PVM pVM)
463{
464 /* Memory was all allocated from the two MM heaps and requires no freeing. */
465 return VINF_SUCCESS;
466}
467
468
469/**
470 * PATM reset callback.
471 *
472 * @returns VBox status code.
473 * @param pVM The VM which is reset.
474 */
475PATMR3DECL(int) PATMR3Reset(PVM pVM)
476{
477 Log(("PATMR3Reset\n"));
478
479 /* Free all patches. */
480 while (true)
481 {
482 PPATMPATCHREC pPatchRec = (PPATMPATCHREC)RTAvloGCPtrRemoveBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, 0, true);
483 if (pPatchRec)
484 {
485 PATMRemovePatch(pVM, pPatchRec, true);
486 }
487 else
488 break;
489 }
490 Assert(!pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage);
491 Assert(!pVM->patm.s.PatchLookupTreeHC->PatchTree);
492 pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr = 0;
493 pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage = 0;
494
495 int rc = patmReinit(pVM);
496 if (VBOX_SUCCESS(rc))
497 rc = PATMR3InitFinalize(pVM); /* paranoia */
498
499 return rc;
500}
501
502/**
503 * Read callback for disassembly function; supports reading bytes that cross a page boundary
504 *
505 * @returns VBox status code.
506 * @param pSrc GC source pointer
507 * @param pDest HC destination pointer
508 * @param size Number of bytes to read
509 * @param pvUserdata Callback specific user data (pCpu)
510 *
511 */
512int patmReadBytes(RTHCUINTPTR pSrc, uint8_t *pDest, unsigned size, void *pvUserdata)
513{
514 DISCPUSTATE *pCpu = (DISCPUSTATE *)pvUserdata;
515 PATMDISASM *pDisInfo = (PATMDISASM *)pCpu->apvUserData[0];
516 int orgsize = size;
517
518 Assert(size);
519 if (size == 0)
520 return VERR_INVALID_PARAMETER;
521
522 /*
523 * Trap/interrupt handler typically call common code on entry. Which might already have patches inserted.
524 * As we currently don't support calling patch code from patch code, we'll let it read the original opcode bytes instead.
525 */
526 /** @todo could change in the future! */
527 if (pDisInfo->fReadFlags & PATMREAD_ORGCODE)
528 {
529 for (int i=0;i<orgsize;i++)
530 {
531 int rc = PATMR3QueryOpcode(pDisInfo->pVM, (RTGCPTR)pSrc, pDest);
532 if (VBOX_SUCCESS(rc))
533 {
534 pSrc++;
535 pDest++;
536 size--;
537 }
538 else break;
539 }
540 if (size == 0)
541 return VINF_SUCCESS;
542#ifdef VBOX_STRICT
543 if ( !(pDisInfo->pPatchInfo->flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_IDTHANDLER))
544 && !(pDisInfo->fReadFlags & PATMREAD_NOCHECK))
545 {
546 Assert(PATMR3IsInsidePatchJump(pDisInfo->pVM, pSrc, NULL) == false);
547 Assert(PATMR3IsInsidePatchJump(pDisInfo->pVM, pSrc+size-1, NULL) == false);
548 }
549#endif
550 }
551
552
553 if (PAGE_ADDRESS(pDisInfo->pInstrGC) != PAGE_ADDRESS(pSrc + size - 1) && !PATMIsPatchGCAddr(pDisInfo->pVM, pSrc))
554 {
555 return PGMPhysReadGCPtr(pDisInfo->pVM, pDest, pSrc, size);
556 }
557 else
558 {
559 uint8_t *pInstrHC = pDisInfo->pInstrHC;
560
561 Assert(pInstrHC);
562
563 /* pInstrHC is the base address; adjust according to the GC pointer. */
564 pInstrHC = pInstrHC + (pSrc - pDisInfo->pInstrGC);
565
566 memcpy(pDest, (void *)pInstrHC, size);
567 }
568
569 return VINF_SUCCESS;
570}
571
572/**
573 * Callback function for RTAvloGCPtrDoWithAll
574 *
575 * Updates all fixups in the patches
576 *
577 * @returns VBox status code.
578 * @param pNode Current node
579 * @param pParam The VM to operate on.
580 */
581static DECLCALLBACK(int) RelocatePatches(PAVLOGCPTRNODECORE pNode, void *pParam)
582{
583 PPATMPATCHREC pPatch = (PPATMPATCHREC)pNode;
584 PVM pVM = (PVM)pParam;
585 RTGCINTPTR delta;
586#ifdef LOG_ENABLED
587 DISCPUSTATE cpu;
588 char szOutput[256];
589 uint32_t opsize;
590 bool disret;
591#endif
592 int rc;
593
594 /* Nothing to do if the patch is not active. */
595 if (pPatch->patch.uState == PATCH_REFUSED)
596 return 0;
597
598#ifdef LOG_ENABLED
599 if (pPatch->patch.flags & PATMFL_PATCHED_GUEST_CODE)
600 {
601 /** @note pPrivInstrHC is probably not valid anymore */
602 rc = PGMPhysGCPtr2HCPtr(pVM, pPatch->patch.pPrivInstrGC, (PRTHCPTR)&pPatch->patch.pPrivInstrHC);
603 if (rc == VINF_SUCCESS)
604 {
605 cpu.mode = (pPatch->patch.flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
606 disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pPatch->patch.pPrivInstrGC, pPatch->patch.pPrivInstrHC, &opsize, szOutput, PATMREAD_RAWCODE);
607 Log(("Org patch jump: %s", szOutput));
608 }
609 }
610#endif
611
612 Log(("Nr of fixups %d\n", pPatch->patch.nrFixups));
613 delta = (RTGCINTPTR)pVM->patm.s.deltaReloc;
614
615 /*
616 * Apply fixups
617 */
618 PRELOCREC pRec = 0;
619 AVLPVKEY key = 0;
620
621 while (true)
622 {
623 /* Get the record that's closest from above */
624 pRec = (PRELOCREC)RTAvlPVGetBestFit(&pPatch->patch.FixupTree, key, true);
625 if (pRec == 0)
626 break;
627
628 key = (AVLPVKEY)(pRec->pRelocPos + 1); /* search for the next record during the next round. */
629
630 switch (pRec->uType)
631 {
632 case FIXUP_ABSOLUTE:
633 Log(("Absolute fixup at %VGv %VHv -> %VHv at %VGv\n", pRec->pSource, *(RTGCUINTPTR*)pRec->pRelocPos, *(RTGCINTPTR*)pRec->pRelocPos + delta, pRec->pRelocPos));
634 if (!pRec->pSource || PATMIsPatchGCAddr(pVM, pRec->pSource))
635 {
636 *(RTGCUINTPTR *)pRec->pRelocPos += delta;
637 }
638 else
639 {
640 uint8_t curInstr[15];
641 uint8_t oldInstr[15];
642 Assert(pRec->pSource && pPatch->patch.cbPrivInstr <= 15);
643
644 Assert(!(pPatch->patch.flags & PATMFL_GLOBAL_FUNCTIONS));
645
646 memcpy(oldInstr, pPatch->patch.aPrivInstr, pPatch->patch.cbPrivInstr);
647 *(RTGCPTR *)&oldInstr[pPatch->patch.cbPrivInstr - sizeof(RTGCPTR)] = pRec->pDest;
648
649 rc = PGMPhysReadGCPtr(pVM, curInstr, pPatch->patch.pPrivInstrGC, pPatch->patch.cbPrivInstr);
650 Assert(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
651
652 pRec->pDest = (RTGCPTR)((RTGCUINTPTR)pRec->pDest + delta);
653
654 if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
655 {
656 RTGCPTR pPage = pPatch->patch.pPrivInstrGC & PAGE_BASE_GC_MASK;
657
658 Log(("PATM: Patch page not present -> check later!\n"));
659 rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_ALL, pPage, pPage + (PAGE_SIZE - 1) /* inclusive! */, 0, patmVirtPageHandler, "PATMGCMonitorPage", 0, "PATMMonitorPatchJump");
660 Assert(VBOX_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT);
661 }
662 else
663 if (memcmp(curInstr, oldInstr, pPatch->patch.cbPrivInstr))
664 {
665 Log(("PATM: Patch was overwritten -> disabling patch!!\n"));
666 /*
667 * Disable patch; this is not a good solution
668 */
669 /* @todo hopefully it was completely overwritten (if the read was successful)!!!! */
670 pPatch->patch.uState = PATCH_DISABLED;
671 }
672 else
673 if (VBOX_SUCCESS(rc))
674 {
675 *(RTGCPTR *)&curInstr[pPatch->patch.cbPrivInstr - sizeof(RTGCPTR)] = pRec->pDest;
676 rc = PGMPhysWriteGCPtrDirty(pVM, pRec->pSource, curInstr, pPatch->patch.cbPrivInstr);
677 AssertRC(rc);
678 }
679 }
680 break;
681
682 case FIXUP_REL_JMPTOPATCH:
683 {
684 RTGCPTR pTarget = (RTGCPTR)((RTGCINTPTR)pRec->pDest + delta);
685
686 if ( pPatch->patch.uState == PATCH_ENABLED
687 && (pPatch->patch.flags & PATMFL_PATCHED_GUEST_CODE))
688 {
689 uint8_t oldJump[SIZEOF_NEAR_COND_JUMP32];
690 uint8_t temp[SIZEOF_NEAR_COND_JUMP32];
691 RTGCPTR pJumpOffGC;
692 RTGCINTPTR displ = (RTGCINTPTR)pTarget - (RTGCINTPTR)pRec->pSource;
693 RTGCINTPTR displOld= (RTGCINTPTR)pRec->pDest - (RTGCINTPTR)pRec->pSource;
694
695 Log(("Relative fixup (g2p) %08X -> %08X at %08X (source=%08x, target=%08x)\n", *(int32_t*)pRec->pRelocPos, displ, pRec->pRelocPos, pRec->pSource, pRec->pDest));
696
697 Assert(pRec->pSource - pPatch->patch.cbPatchJump == pPatch->patch.pPrivInstrGC);
698#ifdef PATM_RESOLVE_CONFLICTS_WITH_JUMP_PATCHES
699 if (pPatch->patch.cbPatchJump == SIZEOF_NEAR_COND_JUMP32)
700 {
701 Assert(pPatch->patch.flags & PATMFL_JUMP_CONFLICT);
702
703 pJumpOffGC = pPatch->patch.pPrivInstrGC + 2; //two byte opcode
704 oldJump[0] = pPatch->patch.aPrivInstr[0];
705 oldJump[1] = pPatch->patch.aPrivInstr[1];
706 *(RTGCUINTPTR *)&oldJump[2] = displOld;
707 }
708 else
709#endif
710 if (pPatch->patch.cbPatchJump == SIZEOF_NEARJUMP32)
711 {
712 pJumpOffGC = pPatch->patch.pPrivInstrGC + 1; //one byte opcode
713 oldJump[0] = 0xE9;
714 *(RTGCUINTPTR *)&oldJump[1] = displOld;
715 }
716 else
717 {
718 AssertMsgFailed(("Invalid patch jump size %d\n", pPatch->patch.cbPatchJump));
719 continue; //this should never happen!!
720 }
721 Assert(pPatch->patch.cbPatchJump <= sizeof(temp));
722
723 /*
724 * Read old patch jump and compare it to the one we previously installed
725 */
726 rc = PGMPhysReadGCPtr(pVM, temp, pPatch->patch.pPrivInstrGC, pPatch->patch.cbPatchJump);
727 Assert(VBOX_SUCCESS(rc) || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT);
728
729 if (rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT)
730 {
731 RTGCPTR pPage = pPatch->patch.pPrivInstrGC & PAGE_BASE_GC_MASK;
732
733 rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_ALL, pPage, pPage + (PAGE_SIZE - 1) /* inclusive! */, 0, patmVirtPageHandler, "PATMGCMonitorPage", 0, "PATMMonitorPatchJump");
734 Assert(VBOX_SUCCESS(rc) || rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT);
735 }
736 else
737 if (memcmp(temp, oldJump, pPatch->patch.cbPatchJump))
738 {
739 Log(("PATM: Patch jump was overwritten -> disabling patch!!\n"));
740 /*
741 * Disable patch; this is not a good solution
742 */
743 /* @todo hopefully it was completely overwritten (if the read was successful)!!!! */
744 pPatch->patch.uState = PATCH_DISABLED;
745 }
746 else
747 if (VBOX_SUCCESS(rc))
748 {
749 rc = PGMPhysWriteGCPtrDirty(pVM, pJumpOffGC, &displ, sizeof(displ));
750 AssertRC(rc);
751 }
752 else
753 {
754 AssertMsgFailed(("Unexpected error %d from MMR3PhysReadGCVirt\n", rc));
755 }
756 }
757 else
758 {
759 Log(("Skip the guest jump to patch code for this disabled patch %08X - %08X\n", pPatch->patch.pPrivInstrHC, pRec->pRelocPos));
760 }
761
762 pRec->pDest = pTarget;
763 break;
764 }
765
766 case FIXUP_REL_JMPTOGUEST:
767 {
768 RTGCPTR pSource = (RTGCPTR)((RTGCINTPTR)pRec->pSource + delta);
769 RTGCINTPTR displ = (RTGCINTPTR)pRec->pDest - (RTGCINTPTR)pSource;
770
771 Assert(!(pPatch->patch.flags & PATMFL_GLOBAL_FUNCTIONS));
772 Log(("Relative fixup (p2g) %08X -> %08X at %08X (source=%08x, target=%08x)\n", *(int32_t*)pRec->pRelocPos, displ, pRec->pRelocPos, pRec->pSource, pRec->pDest));
773 *(RTGCUINTPTR *)pRec->pRelocPos = displ;
774 pRec->pSource = pSource;
775 break;
776 }
777
778 default:
779 AssertMsg(0, ("Invalid fixup type!!\n"));
780 return VERR_INVALID_PARAMETER;
781 }
782 }
783
784#ifdef LOG_ENABLED
785 if (pPatch->patch.flags & PATMFL_PATCHED_GUEST_CODE)
786 {
787 /** @note pPrivInstrHC is probably not valid anymore */
788 rc = PGMPhysGCPtr2HCPtr(pVM, pPatch->patch.pPrivInstrGC, (PRTHCPTR)&pPatch->patch.pPrivInstrHC);
789 if (rc == VINF_SUCCESS)
790 {
791 cpu.mode = (pPatch->patch.flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
792 disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pPatch->patch.pPrivInstrGC, pPatch->patch.pPrivInstrHC, &opsize, szOutput, PATMREAD_RAWCODE);
793 Log(("Rel patch jump: %s", szOutput));
794 }
795 }
796#endif
797 return 0;
798}
799
800/**
801 * #PF Handler callback for virtual access handler ranges.
802 *
803 * Important to realize that a physical page in a range can have aliases, and
804 * for ALL and WRITE handlers these will also trigger.
805 *
806 * @returns VINF_SUCCESS if the handler have carried out the operation.
807 * @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
808 * @param pVM VM Handle.
809 * @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
810 * @param pvPtr The HC mapping of that address.
811 * @param pvBuf What the guest is reading/writing.
812 * @param cbBuf How much it's reading/writing.
813 * @param enmAccessType The access type.
814 * @param pvUser User argument.
815 */
816static DECLCALLBACK(int) patmVirtPageHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
817{
818 Assert(enmAccessType == PGMACCESSTYPE_WRITE);
819 /** @todo could be the wrong virtual address (alias) */
820 pVM->patm.s.pvFaultMonitor = GCPtr;
821 PATMR3HandleMonitoredPage(pVM);
822 return VINF_PGM_HANDLER_DO_DEFAULT;
823}
824
825
826#ifdef VBOX_WITH_DEBUGGER
827/**
828 * Callback function for RTAvloGCPtrDoWithAll
829 *
830 * Enables the patch that's being enumerated
831 *
832 * @returns 0 (continue enumeration).
833 * @param pNode Current node
834 * @param pVM The VM to operate on.
835 */
836static DECLCALLBACK(int) EnableAllPatches(PAVLOGCPTRNODECORE pNode, void *pVM)
837{
838 PPATMPATCHREC pPatch = (PPATMPATCHREC)pNode;
839
840 PATMR3EnablePatch((PVM)pVM, (RTGCPTR)pPatch->Core.Key);
841 return 0;
842}
843#endif /* VBOX_WITH_DEBUGGER */
844
845
846#ifdef VBOX_WITH_DEBUGGER
847/**
848 * Callback function for RTAvloGCPtrDoWithAll
849 *
850 * Disables the patch that's being enumerated
851 *
852 * @returns 0 (continue enumeration).
853 * @param pNode Current node
854 * @param pVM The VM to operate on.
855 */
856static DECLCALLBACK(int) DisableAllPatches(PAVLOGCPTRNODECORE pNode, void *pVM)
857{
858 PPATMPATCHREC pPatch = (PPATMPATCHREC)pNode;
859
860 PATMR3DisablePatch((PVM)pVM, (RTGCPTR)pPatch->Core.Key);
861 return 0;
862}
863#endif
864
865/**
866 * Returns the host context pointer and size of the patch memory block
867 *
868 * @returns VBox status code.
869 * @param pVM The VM to operate on.
870 * @param pcb Size of the patch memory block
871 */
872PATMR3DECL(void *) PATMR3QueryPatchMemHC(PVM pVM, uint32_t *pcb)
873{
874 if (pcb)
875 {
876 *pcb = pVM->patm.s.cbPatchMem;
877 }
878 return pVM->patm.s.pPatchMemHC;
879}
880
881
882/**
883 * Returns the guest context pointer and size of the patch memory block
884 *
885 * @returns VBox status code.
886 * @param pVM The VM to operate on.
887 * @param pcb Size of the patch memory block
888 */
889PATMR3DECL(RTGCPTR) PATMR3QueryPatchMemGC(PVM pVM, uint32_t *pcb)
890{
891 if (pcb)
892 {
893 *pcb = pVM->patm.s.cbPatchMem;
894 }
895 return pVM->patm.s.pPatchMemGC;
896}
897
898
899/**
900 * Returns the host context pointer of the GC context structure
901 *
902 * @returns VBox status code.
903 * @param pVM The VM to operate on.
904 */
905PATMR3DECL(PPATMGCSTATE) PATMR3QueryGCStateHC(PVM pVM)
906{
907 return pVM->patm.s.pGCStateHC;
908}
909
910
911/**
912 * Checks whether the HC address is part of our patch region
913 *
914 * @returns VBox status code.
915 * @param pVM The VM to operate on.
916 * @param pAddrGC Guest context address
917 */
918PATMR3DECL(bool) PATMR3IsPatchHCAddr(PVM pVM, R3PTRTYPE(uint8_t *) pAddrHC)
919{
920 return (pAddrHC >= pVM->patm.s.pPatchMemHC && pAddrHC < pVM->patm.s.pPatchMemHC + pVM->patm.s.cbPatchMem) ? true : false;
921}
922
923
924/**
925 * Allows or disallow patching of privileged instructions executed by the guest OS
926 *
927 * @returns VBox status code.
928 * @param pVM The VM to operate on.
929 * @param fAllowPatching Allow/disallow patching
930 */
931PATMR3DECL(int) PATMR3AllowPatching(PVM pVM, uint32_t fAllowPatching)
932{
933 pVM->fPATMEnabled = (fAllowPatching) ? true : false;
934 return VINF_SUCCESS;
935}
936
937/**
938 * Convert a GC patch block pointer to a HC patch pointer
939 *
940 * @returns HC pointer or NULL if it's not a GC patch pointer
941 * @param pVM The VM to operate on.
942 * @param pAddrGC GC pointer
943 */
944PATMR3DECL(R3PTRTYPE(void *)) PATMR3GCPtrToHCPtr(PVM pVM, RTGCPTR pAddrGC)
945{
946 if (pVM->patm.s.pPatchMemGC <= pAddrGC && pVM->patm.s.pPatchMemGC + pVM->patm.s.cbPatchMem > pAddrGC)
947 {
948 return pVM->patm.s.pPatchMemHC + (pAddrGC - pVM->patm.s.pPatchMemGC);
949 }
950 return NULL;
951}
952
953/**
954 * Query PATM state (enabled/disabled)
955 *
956 * @returns 0 - disabled, 1 - enabled
957 * @param pVM The VM to operate on.
958 */
959PATMR3DECL(int) PATMR3IsEnabled(PVM pVM)
960{
961 return pVM->fPATMEnabled;
962}
963
964
965/**
966 * Convert guest context address to host context pointer
967 *
968 * @returns VBox status code.
969 * @param pVM The VM to operate on.
970 * @param pPatch Patch block structure pointer
971 * @param pGCPtr Guest context pointer
972 *
973 * @returns Host context pointer or NULL in case of an error
974 *
975 */
976R3PTRTYPE(uint8_t *) PATMGCVirtToHCVirt(PVM pVM, PPATCHINFO pPatch, GCPTRTYPE(uint8_t *) pGCPtr)
977{
978 int rc;
979 R3PTRTYPE(uint8_t *) pHCPtr;
980 uint32_t offset;
981
982 if (PATMIsPatchGCAddr(pVM, pGCPtr))
983 {
984 return PATCHCODE_PTR_HC(pPatch) + (pGCPtr - PATCHCODE_PTR_GC(pPatch));
985 }
986
987 offset = pGCPtr & PAGE_OFFSET_MASK;
988 if (pPatch->cacheRec.pGuestLoc == (pGCPtr & PAGE_BASE_GC_MASK))
989 {
990 return pPatch->cacheRec.pPatchLocStartHC + offset;
991 }
992
993 rc = PGMPhysGCPtr2HCPtr(pVM, pGCPtr, (void **)&pHCPtr);
994 if (rc != VINF_SUCCESS)
995 {
996 AssertMsg(rc == VINF_SUCCESS || rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("MMR3PhysGCVirt2HCVirtEx failed for %08X\n", pGCPtr));
997 return NULL;
998 }
999////invalid? Assert(sizeof(R3PTRTYPE(uint8_t*)) == sizeof(uint32_t));
1000
1001 pPatch->cacheRec.pPatchLocStartHC = (R3PTRTYPE(uint8_t*))((RTHCUINTPTR)pHCPtr & PAGE_BASE_HC_MASK);
1002 pPatch->cacheRec.pGuestLoc = pGCPtr & PAGE_BASE_GC_MASK;
1003 return pHCPtr;
1004}
1005
1006
1007/* Calculates and fills in all branch targets
1008 *
1009 * @returns VBox status code.
1010 * @param pVM The VM to operate on.
1011 * @param pPatch Current patch block pointer
1012 *
1013 */
1014static int patmr3SetBranchTargets(PVM pVM, PPATCHINFO pPatch)
1015{
1016 int32_t displ;
1017
1018 PJUMPREC pRec = 0;
1019 int nrJumpRecs = 0;
1020
1021 /*
1022 * Set all branch targets inside the patch block.
1023 * We remove all jump records as they are no longer needed afterwards.
1024 */
1025 while (true)
1026 {
1027 GCPTRTYPE(uint8_t *) pInstrGC;
1028 GCPTRTYPE(uint8_t *) pBranchTargetGC = 0;
1029
1030 pRec = (PJUMPREC)RTAvlPVRemoveBestFit(&pPatch->JumpTree, 0, true);
1031 if (pRec == 0)
1032 break;
1033
1034 nrJumpRecs++;
1035
1036 /* HC in patch block to GC in patch block. */
1037 pInstrGC = patmPatchHCPtr2PatchGCPtr(pVM, pRec->pJumpHC);
1038
1039 if (pRec->opcode == OP_CALL)
1040 {
1041 /* Special case: call function replacement patch from this patch block.
1042 */
1043 if (PATMQueryFunctionPatch(pVM, pRec->pTargetGC) == 0)
1044 {
1045 int rc;
1046
1047 if (PATMR3HasBeenPatched(pVM, pRec->pTargetGC) == false)
1048 rc = PATMR3InstallPatch(pVM, pRec->pTargetGC, PATMFL_CODE32 | PATMFL_DUPLICATE_FUNCTION);
1049 else
1050 rc = VERR_PATCHING_REFUSED; /* exists as a normal patch; can't use it */
1051
1052 if (VBOX_FAILURE(rc))
1053 {
1054 uint8_t *pPatchHC;
1055 RTGCPTR pPatchGC;
1056 RTGCPTR pOrgInstrGC;
1057
1058 pOrgInstrGC = PATMR3PatchToGCPtr(pVM, pInstrGC, 0);
1059 Assert(pOrgInstrGC);
1060
1061 /* Failure for some reason -> mark exit point with int 3. */
1062 Log(("Failed to install function replacement patch (at %x) for reason %Vrc\n", pOrgInstrGC, rc));
1063
1064 pPatchGC = patmGuestGCPtrToPatchGCPtr(pVM, pPatch, pOrgInstrGC);
1065 Assert(pPatchGC);
1066
1067 pPatchHC = pVM->patm.s.pPatchMemHC + (pPatchGC - pVM->patm.s.pPatchMemGC);
1068
1069 /* Set a breakpoint at the very beginning of the recompiled instruction */
1070 *pPatchHC = 0xCC;
1071
1072 continue;
1073 }
1074 }
1075 pBranchTargetGC = PATMR3QueryPatchGCPtr(pVM, pRec->pTargetGC);
1076 }
1077 else
1078 {
1079 pBranchTargetGC = patmGuestGCPtrToPatchGCPtr(pVM, pPatch, pRec->pTargetGC);
1080 }
1081
1082 if (pBranchTargetGC == 0)
1083 {
1084 AssertMsgFailed(("patmr3SetBranchTargets: patmGuestGCPtrToPatchGCPtr failed for %08X\n", pRec->pTargetGC));
1085 return VERR_PATCHING_REFUSED;
1086 }
1087 /* Our jumps *always* have a dword displacement (to make things easier). */
1088 Assert(sizeof(uint32_t) == sizeof(RTGCPTR));
1089 displ = pBranchTargetGC - (pInstrGC + pRec->offDispl + sizeof(RTGCPTR));
1090 *(RTGCPTR *)(pRec->pJumpHC + pRec->offDispl) = displ;
1091 Log(("Set branch target %d to %08X : %08x - (%08x + %d + %d)\n", nrJumpRecs, displ, pBranchTargetGC, pInstrGC, pRec->offDispl, sizeof(RTGCPTR)));
1092 }
1093 Assert(nrJumpRecs == pPatch->nrJumpRecs);
1094 Assert(pPatch->JumpTree == 0);
1095 return VINF_SUCCESS;
1096}
1097
1098/* Add an illegal instruction record
1099 *
1100 * @param pVM The VM to operate on.
1101 * @param pPatch Patch structure ptr
1102 * @param pInstrGC Guest context pointer to privileged instruction
1103 *
1104 */
1105static void patmAddIllegalInstrRecord(PVM pVM, PPATCHINFO pPatch, RTGCPTR pInstrGC)
1106{
1107 PAVLPVNODECORE pRec;
1108
1109 pRec = (PAVLPVNODECORE)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(*pRec));
1110 Assert(pRec);
1111 pRec->Key = (AVLPVKEY)pInstrGC;
1112
1113 bool ret = RTAvlPVInsert(&pPatch->pTempInfo->IllegalInstrTree, pRec);
1114 Assert(ret); NOREF(ret);
1115 pPatch->pTempInfo->nrIllegalInstr++;
1116}
1117
1118static bool patmIsIllegalInstr(PPATCHINFO pPatch, RTGCPTR pInstrGC)
1119{
1120 PAVLPVNODECORE pRec;
1121
1122 pRec = RTAvlPVGet(&pPatch->pTempInfo->IllegalInstrTree, (AVLPVKEY)pInstrGC);
1123 if (pRec)
1124 return true;
1125 return false;
1126}
1127
1128/**
1129 * Add a patch to guest lookup record
1130 *
1131 * @param pVM The VM to operate on.
1132 * @param pPatch Patch structure ptr
1133 * @param pPatchInstrHC Guest context pointer to patch block
1134 * @param pInstrGC Guest context pointer to privileged instruction
1135 * @param enmType Lookup type
1136 * @param fDirty Dirty flag
1137 *
1138 */
1139 /** @note Be extremely careful with this function. Make absolutely sure the guest address is correct! (to avoid executing instructions twice!) */
1140void patmr3AddP2GLookupRecord(PVM pVM, PPATCHINFO pPatch, uint8_t *pPatchInstrHC, RTGCPTR pInstrGC, PATM_LOOKUP_TYPE enmType, bool fDirty)
1141{
1142 bool ret;
1143 PRECPATCHTOGUEST pPatchToGuestRec;
1144 PRECGUESTTOPATCH pGuestToPatchRec;
1145 uint32_t PatchOffset = pPatchInstrHC - pVM->patm.s.pPatchMemHC; /* Offset in memory reserved for PATM. */
1146
1147 if (enmType == PATM_LOOKUP_PATCH2GUEST)
1148 {
1149 pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32Get(&pPatch->Patch2GuestAddrTree, PatchOffset);
1150 if (pPatchToGuestRec && pPatchToGuestRec->Core.Key == PatchOffset)
1151 return; /* already there */
1152
1153 Assert(!pPatchToGuestRec);
1154 }
1155#ifdef VBOX_STRICT
1156 else
1157 {
1158 pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32Get(&pPatch->Patch2GuestAddrTree, PatchOffset);
1159 Assert(!pPatchToGuestRec);
1160 }
1161#endif
1162
1163 pPatchToGuestRec = (PRECPATCHTOGUEST)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(RECPATCHTOGUEST) + sizeof(RECGUESTTOPATCH));
1164 Assert(pPatchToGuestRec);
1165 pPatchToGuestRec->Core.Key = PatchOffset;
1166 pPatchToGuestRec->pOrgInstrGC = pInstrGC;
1167 pPatchToGuestRec->enmType = enmType;
1168 pPatchToGuestRec->fDirty = fDirty;
1169
1170 ret = RTAvlU32Insert(&pPatch->Patch2GuestAddrTree, &pPatchToGuestRec->Core);
1171 Assert(ret);
1172
1173 /* GC to patch address */
1174 if (enmType == PATM_LOOKUP_BOTHDIR)
1175 {
1176 pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlGCPtrGet(&pPatch->Guest2PatchAddrTree, pInstrGC);
1177 if (!pGuestToPatchRec)
1178 {
1179 pGuestToPatchRec = (PRECGUESTTOPATCH)(pPatchToGuestRec+1);
1180 pGuestToPatchRec->Core.Key = pInstrGC;
1181 pGuestToPatchRec->PatchOffset = PatchOffset;
1182
1183 ret = RTAvlGCPtrInsert(&pPatch->Guest2PatchAddrTree, &pGuestToPatchRec->Core);
1184 Assert(ret);
1185 }
1186 }
1187
1188 pPatch->nrPatch2GuestRecs++;
1189}
1190
1191
1192/**
1193 * Removes a patch to guest lookup record
1194 *
1195 * @param pVM The VM to operate on.
1196 * @param pPatch Patch structure ptr
1197 * @param pPatchInstrGC Guest context pointer to patch block
1198 */
1199void patmr3RemoveP2GLookupRecord(PVM pVM, PPATCHINFO pPatch, RTGCPTR pPatchInstrGC)
1200{
1201 PAVLU32NODECORE pNode;
1202 PAVLGCPTRNODECORE pNode2;
1203 PRECPATCHTOGUEST pPatchToGuestRec;
1204 uint32_t PatchOffset = pPatchInstrGC - pVM->patm.s.pPatchMemGC; /* Offset in memory reserved for PATM. */
1205
1206 pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32Get(&pPatch->Patch2GuestAddrTree, PatchOffset);
1207 Assert(pPatchToGuestRec);
1208 if (pPatchToGuestRec)
1209 {
1210 if (pPatchToGuestRec->enmType == PATM_LOOKUP_BOTHDIR)
1211 {
1212 PRECGUESTTOPATCH pGuestToPatchRec = (PRECGUESTTOPATCH)(pPatchToGuestRec+1);
1213
1214 Assert(pGuestToPatchRec->Core.Key);
1215 pNode2 = RTAvlGCPtrRemove(&pPatch->Guest2PatchAddrTree, pGuestToPatchRec->Core.Key);
1216 Assert(pNode2);
1217 }
1218 pNode = RTAvlU32Remove(&pPatch->Patch2GuestAddrTree, pPatchToGuestRec->Core.Key);
1219 Assert(pNode);
1220
1221 MMR3HeapFree(pPatchToGuestRec);
1222 pPatch->nrPatch2GuestRecs--;
1223 }
1224}
1225
1226
1227/**
1228 * RTAvlPVDestroy callback.
1229 */
1230static DECLCALLBACK(int) patmEmptyTreePVCallback(PAVLPVNODECORE pNode, void *)
1231{
1232 MMR3HeapFree(pNode);
1233 return 0;
1234}
1235
1236/**
1237 * Empty the specified tree (PV tree, MMR3 heap)
1238 *
1239 * @param pVM The VM to operate on.
1240 * @param ppTree Tree to empty
1241 */
1242void patmEmptyTree(PVM pVM, PAVLPVNODECORE *ppTree)
1243{
1244 RTAvlPVDestroy(ppTree, patmEmptyTreePVCallback, NULL);
1245}
1246
1247
1248/**
1249 * RTAvlU32Destroy callback.
1250 */
1251static DECLCALLBACK(int) patmEmptyTreeU32Callback(PAVLU32NODECORE pNode, void *)
1252{
1253 MMR3HeapFree(pNode);
1254 return 0;
1255}
1256
1257/**
1258 * Empty the specified tree (U32 tree, MMR3 heap)
1259 *
1260 * @param pVM The VM to operate on.
1261 * @param ppTree Tree to empty
1262 */
1263void patmEmptyTreeU32(PVM pVM, PPAVLU32NODECORE ppTree)
1264{
1265 RTAvlU32Destroy(ppTree, patmEmptyTreeU32Callback, NULL);
1266}
1267
1268
1269/**
1270 * Analyses the instructions following the cli for compliance with our heuristics for cli & pushf
1271 *
1272 * @returns VBox status code.
1273 * @param pVM The VM to operate on.
1274 * @param pCpu CPU disassembly state
1275 * @param pInstrGC Guest context pointer to privileged instruction
1276 * @param pCurInstrGC Guest context pointer to the current instruction
1277 * @param pUserData User pointer (callback specific)
1278 *
1279 */
1280static int patmAnalyseBlockCallback(PVM pVM, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, void *pUserData)
1281{
1282 PPATCHINFO pPatch = (PPATCHINFO)pUserData;
1283 bool fIllegalInstr = false;
1284
1285 //Preliminary heuristics:
1286 //- no call instructions without a fixed displacement between cli and sti/popf
1287 //- no jumps in the instructions following cli (4+ bytes; enough for the replacement jump (5 bytes))
1288 //- no nested pushf/cli
1289 //- sti/popf should be the (eventual) target of all branches
1290 //- no near or far returns; no int xx, no into
1291 //
1292 // Note: Later on we can impose less stricter guidelines if the need arises
1293
1294 /* Bail out if the patch gets too big. */
1295 if (pPatch->cbPatchBlockSize >= MAX_PATCH_SIZE)
1296 {
1297 Log(("Code block too big (%x) for patch at %VGv!!\n", pPatch->cbPatchBlockSize, pCurInstrGC));
1298 fIllegalInstr = true;
1299 patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
1300 }
1301 else
1302 {
1303 /* No unconditinal jumps or calls without fixed displacements. */
1304 if ( (pCpu->pCurInstr->optype & OPTYPE_CONTROLFLOW)
1305 && (pCpu->pCurInstr->opcode == OP_JMP || pCpu->pCurInstr->opcode == OP_CALL)
1306 )
1307 {
1308 Assert(pCpu->param1.size <= 4 || pCpu->param1.size == 8);
1309 if ( pCpu->param1.size == 8 /* far call/jmp */
1310 || (pCpu->pCurInstr->opcode == OP_CALL && !(pPatch->flags & PATMFL_SUPPORT_CALLS))
1311 || (OP_PARM_VTYPE(pCpu->pCurInstr->param1) != OP_PARM_J && !(pPatch->flags & PATMFL_SUPPORT_INDIRECT_CALLS))
1312 )
1313 {
1314 fIllegalInstr = true;
1315 patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
1316 }
1317 }
1318
1319 /* An unconditional (short) jump right after a cli is a potential problem; we will overwrite whichever function comes afterwards */
1320 if (pPatch->opcode == OP_CLI && pCpu->pCurInstr->opcode == OP_JMP)
1321 {
1322 if (pCurInstrGC > pPatch->pPrivInstrGC && pCurInstrGC + pCpu->opsize < pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32) /* hardcoded patch jump size; cbPatchJump is still zero */
1323 {
1324 Log(("Dangerous unconditional jump ends in our generated patch jump!! (%x vs %x)\n", pCurInstrGC, pPatch->pPrivInstrGC));
1325 /* We turn this one into a int 3 callable patch. */
1326 pPatch->flags |= PATMFL_INT3_REPLACEMENT_BLOCK;
1327 }
1328 }
1329 else
1330 /* no nested pushfs just yet; nested cli is allowed for cli patches though. */
1331 if (pPatch->opcode == OP_PUSHF)
1332 {
1333 if (pCurInstrGC != pInstrGC && pCpu->pCurInstr->opcode == OP_PUSHF)
1334 {
1335 fIllegalInstr = true;
1336 patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
1337 }
1338 }
1339
1340 // no far returns
1341 if (pCpu->pCurInstr->opcode == OP_RETF)
1342 {
1343 pPatch->pTempInfo->nrRetInstr++;
1344 fIllegalInstr = true;
1345 patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
1346 }
1347 else
1348 // no int xx or into either
1349 if (pCpu->pCurInstr->opcode == OP_INT3 || pCpu->pCurInstr->opcode == OP_INT || pCpu->pCurInstr->opcode == OP_INTO)
1350 {
1351 fIllegalInstr = true;
1352 patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
1353 }
1354 }
1355
1356 pPatch->cbPatchBlockSize += pCpu->opsize;
1357
1358 /* Illegal instruction -> end of analysis phase for this code block */
1359 if (fIllegalInstr || patmIsIllegalInstr(pPatch, pCurInstrGC))
1360 return VINF_SUCCESS;
1361
1362 /* Check for exit points. */
1363 switch (pCpu->pCurInstr->opcode)
1364 {
1365 case OP_SYSEXIT:
1366 return VINF_SUCCESS; /* duplicate it; will fault or emulated in GC. */
1367
1368 case OP_SYSENTER:
1369 case OP_ILLUD2:
1370 //This appears to be some kind of kernel panic in Linux 2.4; no point to analyse more
1371 Log(("Illegal opcode (0xf 0xb) -> return here\n"));
1372 return VINF_SUCCESS;
1373
1374 case OP_STI:
1375 case OP_POPF:
1376 Assert(!(pPatch->flags & (PATMFL_DUPLICATE_FUNCTION)));
1377 /* If out exit point lies within the generated patch jump, then we have to refuse!! */
1378 if (pCurInstrGC > pPatch->pPrivInstrGC && pCurInstrGC < pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32) /* hardcoded patch jump size; cbPatchJump is still zero */
1379 {
1380 Log(("Exit point within patch jump itself!! (%x vs %x)\n", pCurInstrGC, pPatch->pPrivInstrGC));
1381 return VERR_PATCHING_REFUSED;
1382 }
1383 if (pPatch->opcode == OP_PUSHF)
1384 {
1385 if (pCpu->pCurInstr->opcode == OP_POPF)
1386 {
1387 if (pPatch->cbPatchBlockSize >= SIZEOF_NEARJUMP32)
1388 return VINF_SUCCESS;
1389
1390 /* Or else we need to duplicate more instructions, because we can't jump back yet! */
1391 Log(("WARNING: End of block reached, but we need to duplicate some extra instruction to avoid a conflict with the patch jump\n"));
1392 pPatch->flags |= PATMFL_CHECK_SIZE;
1393 }
1394 break; //sti doesn't mark the end of a pushf block; only popf does
1395 }
1396 //else no break
1397 case OP_RETN: /* exit point for function replacement */
1398 return VINF_SUCCESS;
1399
1400 case OP_IRET:
1401 return VINF_SUCCESS; /* exitpoint */
1402
1403 case OP_CPUID:
1404 case OP_CALL:
1405 case OP_JMP:
1406 break;
1407
1408 default:
1409 if (pCpu->pCurInstr->optype & (OPTYPE_PRIVILEGED_NOTRAP))
1410 {
1411 patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
1412 return VINF_SUCCESS; /* exit point */
1413 }
1414 break;
1415 }
1416
1417 // If single instruction patch, we've copied enough instructions *and* the current instruction is not a relative jump
1418 if ((pPatch->flags & PATMFL_CHECK_SIZE) && pPatch->cbPatchBlockSize > SIZEOF_NEARJUMP32 && !(pCpu->pCurInstr->optype & OPTYPE_RELATIVE_CONTROLFLOW))
1419 {
1420 // The end marker for this kind of patch is any instruction at a location outside our patch jump
1421 Log(("End of block at %VGv size %d\n", pCurInstrGC, pCpu->opsize));
1422 return VINF_SUCCESS;
1423 }
1424
1425 return VWRN_CONTINUE_ANALYSIS;
1426}
1427
1428/**
1429 * Analyses the instructions inside a function for compliance
1430 *
1431 * @returns VBox status code.
1432 * @param pVM The VM to operate on.
1433 * @param pCpu CPU disassembly state
1434 * @param pInstrGC Guest context pointer to privileged instruction
1435 * @param pCurInstrGC Guest context pointer to the current instruction
1436 * @param pUserData User pointer (callback specific)
1437 *
1438 */
1439static int patmAnalyseFunctionCallback(PVM pVM, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, void *pUserData)
1440{
1441 PPATCHINFO pPatch = (PPATCHINFO)pUserData;
1442 bool fIllegalInstr = false;
1443
1444 //Preliminary heuristics:
1445 //- no call instructions
1446 //- ret ends a block
1447
1448 Assert(pPatch->flags & (PATMFL_DUPLICATE_FUNCTION));
1449
1450 // bail out if the patch gets too big
1451 if (pPatch->cbPatchBlockSize >= MAX_PATCH_SIZE)
1452 {
1453 Log(("Code block too big (%x) for function patch at %VGv!!\n", pPatch->cbPatchBlockSize, pCurInstrGC));
1454 fIllegalInstr = true;
1455 patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
1456 }
1457 else
1458 {
1459 // no unconditinal jumps or calls without fixed displacements
1460 if ( (pCpu->pCurInstr->optype & OPTYPE_CONTROLFLOW)
1461 && (pCpu->pCurInstr->opcode == OP_JMP || pCpu->pCurInstr->opcode == OP_CALL)
1462 )
1463 {
1464 Assert(pCpu->param1.size <= 4 || pCpu->param1.size == 8);
1465 if ( pCpu->param1.size == 8 /* far call/jmp */
1466 || (pCpu->pCurInstr->opcode == OP_CALL && !(pPatch->flags & PATMFL_SUPPORT_CALLS))
1467 || (OP_PARM_VTYPE(pCpu->pCurInstr->param1) != OP_PARM_J && !(pPatch->flags & PATMFL_SUPPORT_INDIRECT_CALLS))
1468 )
1469 {
1470 fIllegalInstr = true;
1471 patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
1472 }
1473 }
1474 else /* no far returns */
1475 if (pCpu->pCurInstr->opcode == OP_RETF)
1476 {
1477 patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
1478 fIllegalInstr = true;
1479 }
1480 else /* no int xx or into either */
1481 if (pCpu->pCurInstr->opcode == OP_INT3 || pCpu->pCurInstr->opcode == OP_INT || pCpu->pCurInstr->opcode == OP_INTO)
1482 {
1483 patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
1484 fIllegalInstr = true;
1485 }
1486
1487 #if 0
1488 ///@todo we can handle certain in/out and privileged instructions in the guest context
1489 if (pCpu->pCurInstr->optype & OPTYPE_PRIVILEGED && pCpu->pCurInstr->opcode != OP_STI)
1490 {
1491 Log(("Illegal instructions for function patch!!\n"));
1492 return VERR_PATCHING_REFUSED;
1493 }
1494 #endif
1495 }
1496
1497 pPatch->cbPatchBlockSize += pCpu->opsize;
1498
1499 /* Illegal instruction -> end of analysis phase for this code block */
1500 if (fIllegalInstr || patmIsIllegalInstr(pPatch, pCurInstrGC))
1501 {
1502 return VINF_SUCCESS;
1503 }
1504
1505 // Check for exit points
1506 switch (pCpu->pCurInstr->opcode)
1507 {
1508 case OP_ILLUD2:
1509 //This appears to be some kind of kernel panic in Linux 2.4; no point to analyse more
1510 Log(("Illegal opcode (0xf 0xb) -> return here\n"));
1511 return VINF_SUCCESS;
1512
1513 case OP_IRET:
1514 case OP_SYSEXIT: /* will fault or emulated in GC */
1515 case OP_RETN:
1516 return VINF_SUCCESS;
1517
1518 case OP_POPF:
1519 case OP_STI:
1520 return VWRN_CONTINUE_ANALYSIS;
1521 default:
1522 if (pCpu->pCurInstr->optype & (OPTYPE_PRIVILEGED_NOTRAP))
1523 {
1524 patmAddIllegalInstrRecord(pVM, pPatch, pCurInstrGC);
1525 return VINF_SUCCESS; /* exit point */
1526 }
1527 return VWRN_CONTINUE_ANALYSIS;
1528 }
1529
1530 return VWRN_CONTINUE_ANALYSIS;
1531}
1532
1533/**
1534 * Recompiles the instructions in a code block
1535 *
1536 * @returns VBox status code.
1537 * @param pVM The VM to operate on.
1538 * @param pCpu CPU disassembly state
1539 * @param pInstrGC Guest context pointer to privileged instruction
1540 * @param pCurInstrGC Guest context pointer to the current instruction
1541 * @param pUserData User pointer (callback specific)
1542 *
1543 */
1544static int patmRecompileCallback(PVM pVM, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, void *pUserData)
1545{
1546 PPATCHINFO pPatch = (PPATCHINFO)pUserData;
1547 int rc = VINF_SUCCESS;
1548 bool fInhibitIRQInstr = false; /* did the instruction cause PATMFL_INHIBITIRQS to be set? */
1549
1550 LogFlow(("patmRecompileCallback %VGv %VGv\n", pInstrGC, pCurInstrGC));
1551
1552 if ( patmGuestGCPtrToPatchGCPtr(pVM, pPatch, pCurInstrGC) != 0
1553 && !(pPatch->flags & PATMFL_RECOMPILE_NEXT)) /* do not do this when the next instruction *must* be executed! */
1554 {
1555 /*
1556 * Been there, done that; so insert a jump (we don't want to duplicate code)
1557 * no need to record this instruction as it's glue code that never crashes (it had better not!)
1558 */
1559 Log(("patmRecompileCallback: jump to code we've recompiled before %VGv!\n", pCurInstrGC));
1560 return patmPatchGenRelJump(pVM, pPatch, pCurInstrGC, OP_JMP, !!(pCpu->prefix & PREFIX_OPSIZE));
1561 }
1562
1563 if (pPatch->flags & (PATMFL_DUPLICATE_FUNCTION))
1564 {
1565 rc = patmAnalyseFunctionCallback(pVM, pCpu, pInstrGC, pCurInstrGC, pUserData);
1566 }
1567 else
1568 rc = patmAnalyseBlockCallback(pVM, pCpu, pInstrGC, pCurInstrGC, pUserData);
1569
1570 if (VBOX_FAILURE(rc))
1571 return rc;
1572
1573 /** @note Never do a direct return unless a failure is encountered! */
1574
1575 /* Clear recompilation of next instruction flag; we are doing that right here. */
1576 if (pPatch->flags & PATMFL_RECOMPILE_NEXT)
1577 pPatch->flags &= ~PATMFL_RECOMPILE_NEXT;
1578
1579 /* Add lookup record for patch to guest address translation */
1580 patmr3AddP2GLookupRecord(pVM, pPatch, PATCHCODE_PTR_HC(pPatch) + pPatch->uCurPatchOffset, pCurInstrGC, PATM_LOOKUP_BOTHDIR);
1581
1582 /* Update lowest and highest instruction address for this patch */
1583 if (pCurInstrGC < pPatch->pInstrGCLowest)
1584 pPatch->pInstrGCLowest = pCurInstrGC;
1585 else
1586 if (pCurInstrGC > pPatch->pInstrGCHighest)
1587 pPatch->pInstrGCHighest = pCurInstrGC + pCpu->opsize;
1588
1589 /* Illegal instruction -> end of recompile phase for this code block. */
1590 if (patmIsIllegalInstr(pPatch, pCurInstrGC))
1591 {
1592 Log(("Illegal instruction at %VGv -> mark with int 3\n", pCurInstrGC));
1593 rc = patmPatchGenIllegalInstr(pVM, pPatch);
1594 goto end;
1595 }
1596
1597 /* For our first attempt, we'll handle only simple relative jumps (immediate offset coded in instruction).
1598 * Indirect calls are handled below.
1599 */
1600 if ( (pCpu->pCurInstr->optype & OPTYPE_CONTROLFLOW)
1601 && (pCpu->pCurInstr->opcode != OP_CALL || (pPatch->flags & PATMFL_SUPPORT_CALLS))
1602 && (OP_PARM_VTYPE(pCpu->pCurInstr->param1) == OP_PARM_J))
1603 {
1604 GCPTRTYPE(uint8_t *) pTargetGC = PATMResolveBranch(pCpu, pCurInstrGC);
1605 if (pTargetGC == 0)
1606 {
1607 Log(("We don't support far jumps here!! (%08X)\n", pCpu->param1.flags));
1608 return VERR_PATCHING_REFUSED;
1609 }
1610
1611 if (pCpu->pCurInstr->opcode == OP_CALL)
1612 {
1613 Assert(!PATMIsPatchGCAddr(pVM, pTargetGC));
1614 rc = patmPatchGenCall(pVM, pPatch, pCpu, pCurInstrGC, pTargetGC, false);
1615 if (VBOX_FAILURE(rc))
1616 goto end;
1617 }
1618 else
1619 rc = patmPatchGenRelJump(pVM, pPatch, pTargetGC, pCpu->pCurInstr->opcode, !!(pCpu->prefix & PREFIX_OPSIZE));
1620
1621 if (VBOX_SUCCESS(rc))
1622 rc = VWRN_CONTINUE_RECOMPILE;
1623
1624 goto end;
1625 }
1626
1627 switch (pCpu->pCurInstr->opcode)
1628 {
1629 case OP_CLI:
1630 {
1631 /* If a cli is found while duplicating instructions for another patch, then it's of vital importance to continue
1632 * until we've found the proper exit point(s).
1633 */
1634 if ( pCurInstrGC != pInstrGC
1635 && !(pPatch->flags & (PATMFL_DUPLICATE_FUNCTION))
1636 )
1637 {
1638 Log(("cli instruction found in other instruction patch block; force it to continue & find an exit point\n"));
1639 pPatch->flags &= ~(PATMFL_CHECK_SIZE | PATMFL_SINGLE_INSTRUCTION);
1640 }
1641 /* Set by irq inhibition; no longer valid now. */
1642 pPatch->flags &= ~PATMFL_GENERATE_JUMPTOGUEST;
1643
1644 rc = patmPatchGenCli(pVM, pPatch);
1645 if (VBOX_SUCCESS(rc))
1646 rc = VWRN_CONTINUE_RECOMPILE;
1647 break;
1648 }
1649
1650 case OP_MOV:
1651 if (pCpu->pCurInstr->optype & OPTYPE_POTENTIALLY_DANGEROUS)
1652 {
1653 /* mov ss, src? */
1654 if ( (pCpu->param1.flags & USE_REG_SEG)
1655 && (pCpu->param1.base.reg_seg == USE_REG_SS))
1656 {
1657 Log(("Force recompilation of next instruction for OP_MOV at %VGv\n", pCurInstrGC));
1658 pPatch->flags |= PATMFL_RECOMPILE_NEXT;
1659 /** @todo this could cause a fault (ring 0 selector being loaded in ring 1) */
1660 }
1661#if 0 /* necessary for Haiku */
1662 else
1663 if ( (pCpu->param2.flags & USE_REG_SEG)
1664 && (pCpu->param2.base.reg_seg == USE_REG_SS)
1665 && (pCpu->param1.flags & (USE_REG_GEN32|USE_REG_GEN16))) /** @todo memory operand must in theory be handled too */
1666 {
1667 /* mov GPR, ss */
1668 rc = patmPatchGenMovFromSS(pVM, pPatch, pCpu, pCurInstrGC);
1669 if (VBOX_SUCCESS(rc))
1670 rc = VWRN_CONTINUE_RECOMPILE;
1671 break;
1672 }
1673#endif
1674 }
1675 goto duplicate_instr;
1676
1677 case OP_POP:
1678 if (pCpu->pCurInstr->param1 == OP_PARM_REG_SS)
1679 {
1680 Assert(pCpu->pCurInstr->optype & OPTYPE_INHIBIT_IRQS);
1681
1682 Log(("Force recompilation of next instruction for OP_MOV at %VGv\n", pCurInstrGC));
1683 pPatch->flags |= PATMFL_RECOMPILE_NEXT;
1684 }
1685 goto duplicate_instr;
1686
1687 case OP_STI:
1688 {
1689 RTGCPTR pNextInstrGC = 0; /* by default no inhibit irq */
1690
1691 /** In a sequence of instructions that inhibit irqs, only the first one actually inhibits irqs. */
1692 if (!(pPatch->flags & PATMFL_INHIBIT_IRQS))
1693 {
1694 pPatch->flags |= PATMFL_INHIBIT_IRQS | PATMFL_GENERATE_JUMPTOGUEST;
1695 fInhibitIRQInstr = true;
1696 pNextInstrGC = pCurInstrGC + pCpu->opsize;
1697 Log(("Inhibit irqs for instruction OP_STI at %VGv\n", pCurInstrGC));
1698 }
1699 rc = patmPatchGenSti(pVM, pPatch, pCurInstrGC, pNextInstrGC);
1700
1701 if (VBOX_SUCCESS(rc))
1702 {
1703 DISCPUSTATE cpu = *pCpu;
1704 unsigned opsize;
1705 int disret;
1706 GCPTRTYPE(uint8_t *) pNextInstrGC, pReturnInstrGC;
1707 R3PTRTYPE(uint8_t *) pNextInstrHC;
1708
1709 pPatch->flags |= PATMFL_FOUND_PATCHEND;
1710
1711 pNextInstrGC = pCurInstrGC + pCpu->opsize;
1712 pNextInstrHC = PATMGCVirtToHCVirt(pVM, pPatch, pNextInstrGC);
1713 if (pNextInstrHC == NULL)
1714 {
1715 AssertFailed();
1716 return VERR_PATCHING_REFUSED;
1717 }
1718
1719 // Disassemble the next instruction
1720 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pNextInstrGC, pNextInstrHC, &opsize, NULL);
1721 if (disret == false)
1722 {
1723 AssertMsgFailed(("STI: Disassembly failed (probably page not present) -> return to caller\n"));
1724 return VERR_PATCHING_REFUSED;
1725 }
1726 pReturnInstrGC = pNextInstrGC + opsize;
1727
1728 if ( (pPatch->flags & (PATMFL_DUPLICATE_FUNCTION))
1729 || pReturnInstrGC <= pInstrGC
1730 || pReturnInstrGC - pInstrGC >= SIZEOF_NEARJUMP32
1731 )
1732 {
1733 /* Not an exit point for function duplication patches */
1734 if ( (pPatch->flags & PATMFL_DUPLICATE_FUNCTION)
1735 && VBOX_SUCCESS(rc))
1736 {
1737 pPatch->flags &= ~PATMFL_GENERATE_JUMPTOGUEST; /* Don't generate a jump back */
1738 rc = VWRN_CONTINUE_RECOMPILE;
1739 }
1740 else
1741 rc = VINF_SUCCESS; //exit point
1742 }
1743 else {
1744 Log(("PATM: sti occurred too soon; refusing patch!\n"));
1745 rc = VERR_PATCHING_REFUSED; //not allowed!!
1746 }
1747 }
1748 break;
1749 }
1750
1751 case OP_POPF:
1752 {
1753 bool fGenerateJmpBack = (pCurInstrGC + pCpu->opsize - pInstrGC >= SIZEOF_NEARJUMP32);
1754
1755 /* Not an exit point for IDT handler or function replacement patches */
1756 /* Note: keep IOPL in mind when changing any of this!! (see comments in PATMA.asm, PATMPopf32Replacement) */
1757 if (pPatch->flags & (PATMFL_IDTHANDLER|PATMFL_DUPLICATE_FUNCTION))
1758 fGenerateJmpBack = false;
1759
1760 rc = patmPatchGenPopf(pVM, pPatch, pCurInstrGC + pCpu->opsize, !!(pCpu->prefix & PREFIX_OPSIZE), fGenerateJmpBack);
1761 if (VBOX_SUCCESS(rc))
1762 {
1763 if (fGenerateJmpBack == false)
1764 {
1765 /* Not an exit point for IDT handler or function replacement patches */
1766 rc = VWRN_CONTINUE_RECOMPILE;
1767 }
1768 else
1769 {
1770 pPatch->flags |= PATMFL_FOUND_PATCHEND;
1771 rc = VINF_SUCCESS; /* exit point! */
1772 }
1773 }
1774 break;
1775 }
1776
1777 case OP_PUSHF:
1778 rc = patmPatchGenPushf(pVM, pPatch, !!(pCpu->prefix & PREFIX_OPSIZE));
1779 if (VBOX_SUCCESS(rc))
1780 rc = VWRN_CONTINUE_RECOMPILE;
1781 break;
1782
1783 case OP_PUSH:
1784 if (pCpu->pCurInstr->param1 == OP_PARM_REG_CS)
1785 {
1786 rc = patmPatchGenPushCS(pVM, pPatch);
1787 if (VBOX_SUCCESS(rc))
1788 rc = VWRN_CONTINUE_RECOMPILE;
1789 break;
1790 }
1791 goto duplicate_instr;
1792
1793 case OP_IRET:
1794 Log(("IRET at %VGv\n", pCurInstrGC));
1795 rc = patmPatchGenIret(pVM, pPatch, pCurInstrGC, !!(pCpu->prefix & PREFIX_OPSIZE));
1796 if (VBOX_SUCCESS(rc))
1797 {
1798 pPatch->flags |= PATMFL_FOUND_PATCHEND;
1799 rc = VINF_SUCCESS; /* exit point by definition */
1800 }
1801 break;
1802
1803 case OP_ILLUD2:
1804 /* This appears to be some kind of kernel panic in Linux 2.4; no point to continue */
1805 rc = patmPatchGenIllegalInstr(pVM, pPatch);
1806 if (VBOX_SUCCESS(rc))
1807 rc = VINF_SUCCESS; /* exit point by definition */
1808 Log(("Illegal opcode (0xf 0xb)\n"));
1809 break;
1810
1811 case OP_CPUID:
1812 rc = patmPatchGenCpuid(pVM, pPatch, pCurInstrGC);
1813 if (VBOX_SUCCESS(rc))
1814 rc = VWRN_CONTINUE_RECOMPILE;
1815 break;
1816
1817 case OP_STR:
1818 case OP_SLDT:
1819 rc = patmPatchGenSldtStr(pVM, pPatch, pCpu, pCurInstrGC);
1820 if (VBOX_SUCCESS(rc))
1821 rc = VWRN_CONTINUE_RECOMPILE;
1822 break;
1823
1824 case OP_SGDT:
1825 case OP_SIDT:
1826 rc = patmPatchGenSxDT(pVM, pPatch, pCpu, pCurInstrGC);
1827 if (VBOX_SUCCESS(rc))
1828 rc = VWRN_CONTINUE_RECOMPILE;
1829 break;
1830
1831 case OP_RETN:
1832 /* retn is an exit point for function patches */
1833 rc = patmPatchGenRet(pVM, pPatch, pCpu, pCurInstrGC);
1834 if (VBOX_SUCCESS(rc))
1835 rc = VINF_SUCCESS; /* exit point by definition */
1836 break;
1837
1838 case OP_SYSEXIT:
1839 /* Duplicate it, so it can be emulated in GC (or fault). */
1840 rc = patmPatchGenDuplicate(pVM, pPatch, pCpu, pCurInstrGC);
1841 if (VBOX_SUCCESS(rc))
1842 rc = VINF_SUCCESS; /* exit point by definition */
1843 break;
1844
1845 case OP_CALL:
1846 Assert(pPatch->flags & PATMFL_SUPPORT_INDIRECT_CALLS);
1847 /* In interrupt gate handlers it's possible to encounter jumps or calls when IF has been enabled again.
1848 * In that case we'll jump to the original instruction and continue from there. Otherwise an int 3 is executed.
1849 */
1850 Assert(pCpu->param1.size == 4 || pCpu->param1.size == 8);
1851 if (pPatch->flags & PATMFL_SUPPORT_INDIRECT_CALLS && pCpu->param1.size == 4 /* no far calls! */)
1852 {
1853 rc = patmPatchGenCall(pVM, pPatch, pCpu, pCurInstrGC, (RTGCPTR)0xDEADBEEF, true);
1854 if (VBOX_SUCCESS(rc))
1855 {
1856 rc = VWRN_CONTINUE_RECOMPILE;
1857 }
1858 break;
1859 }
1860 goto gen_illegal_instr;
1861
1862 case OP_JMP:
1863 Assert(pPatch->flags & PATMFL_SUPPORT_INDIRECT_CALLS);
1864 /* In interrupt gate handlers it's possible to encounter jumps or calls when IF has been enabled again.
1865 * In that case we'll jump to the original instruction and continue from there. Otherwise an int 3 is executed.
1866 */
1867 Assert(pCpu->param1.size == 4 || pCpu->param1.size == 8);
1868 if (pPatch->flags & PATMFL_SUPPORT_INDIRECT_CALLS && pCpu->param1.size == 4 /* no far jumps! */)
1869 {
1870 rc = patmPatchGenJump(pVM, pPatch, pCpu, pCurInstrGC);
1871 if (VBOX_SUCCESS(rc))
1872 rc = VINF_SUCCESS; /* end of branch */
1873 break;
1874 }
1875 goto gen_illegal_instr;
1876
1877 case OP_INT3:
1878 case OP_INT:
1879 case OP_INTO:
1880 goto gen_illegal_instr;
1881
1882 case OP_MOV_DR:
1883 /** @note: currently we let DRx writes cause a trap d; our trap handler will decide to interpret it or not. */
1884 if (pCpu->pCurInstr->param2 == OP_PARM_Dd)
1885 {
1886 rc = patmPatchGenMovDebug(pVM, pPatch, pCpu);
1887 if (VBOX_SUCCESS(rc))
1888 rc = VWRN_CONTINUE_RECOMPILE;
1889 break;
1890 }
1891 goto duplicate_instr;
1892
1893 case OP_MOV_CR:
1894 /** @note: currently we let CRx writes cause a trap d; our trap handler will decide to interpret it or not. */
1895 if (pCpu->pCurInstr->param2 == OP_PARM_Cd)
1896 {
1897 rc = patmPatchGenMovControl(pVM, pPatch, pCpu);
1898 if (VBOX_SUCCESS(rc))
1899 rc = VWRN_CONTINUE_RECOMPILE;
1900 break;
1901 }
1902 goto duplicate_instr;
1903
1904 default:
1905 if (pCpu->pCurInstr->optype & (OPTYPE_CONTROLFLOW | OPTYPE_PRIVILEGED_NOTRAP))
1906 {
1907gen_illegal_instr:
1908 rc = patmPatchGenIllegalInstr(pVM, pPatch);
1909 if (VBOX_SUCCESS(rc))
1910 rc = VINF_SUCCESS; /* exit point by definition */
1911 }
1912 else
1913 {
1914duplicate_instr:
1915 Log(("patmPatchGenDuplicate\n"));
1916 rc = patmPatchGenDuplicate(pVM, pPatch, pCpu, pCurInstrGC);
1917 if (VBOX_SUCCESS(rc))
1918 rc = VWRN_CONTINUE_RECOMPILE;
1919 }
1920 break;
1921 }
1922
1923end:
1924
1925 if ( !fInhibitIRQInstr
1926 && (pPatch->flags & PATMFL_INHIBIT_IRQS))
1927 {
1928 int rc2;
1929 RTGCPTR pNextInstrGC = pCurInstrGC + pCpu->opsize;
1930
1931 pPatch->flags &= ~PATMFL_INHIBIT_IRQS;
1932 Log(("Clear inhibit IRQ flag at %VGv\n", pCurInstrGC));
1933 if (pPatch->flags & PATMFL_GENERATE_JUMPTOGUEST)
1934 {
1935 Log(("patmRecompileCallback: generate jump back to guest (%VGv) after fused instruction\n", pNextInstrGC));
1936
1937 rc2 = patmPatchGenJumpToGuest(pVM, pPatch, pNextInstrGC, true /* clear inhibit irq flag */);
1938 pPatch->flags &= ~PATMFL_GENERATE_JUMPTOGUEST;
1939 rc = VINF_SUCCESS; /* end of the line */
1940 }
1941 else
1942 {
1943 rc2 = patmPatchGenClearInhibitIRQ(pVM, pPatch, pNextInstrGC);
1944 }
1945 if (VBOX_FAILURE(rc2))
1946 rc = rc2;
1947 }
1948
1949 if (VBOX_SUCCESS(rc))
1950 {
1951 // If single instruction patch, we've copied enough instructions *and* the current instruction is not a relative jump
1952 if ( (pPatch->flags & PATMFL_CHECK_SIZE)
1953 && pCurInstrGC + pCpu->opsize - pInstrGC >= SIZEOF_NEARJUMP32
1954 && !(pCpu->pCurInstr->optype & OPTYPE_RELATIVE_CONTROLFLOW)
1955 && !(pPatch->flags & PATMFL_RECOMPILE_NEXT) /* do not do this when the next instruction *must* be executed! */
1956 )
1957 {
1958 RTGCPTR pNextInstrGC = pCurInstrGC + pCpu->opsize;
1959
1960 // The end marker for this kind of patch is any instruction at a location outside our patch jump
1961 Log(("patmRecompileCallback: end found for single instruction patch at %VGv opsize %d\n", pNextInstrGC, pCpu->opsize));
1962
1963 rc = patmPatchGenJumpToGuest(pVM, pPatch, pNextInstrGC);
1964 AssertRC(rc);
1965 }
1966 }
1967 return rc;
1968}
1969
1970
1971#ifdef LOG_ENABLED
1972
1973/* Add a disasm jump record (temporary for prevent duplicate analysis)
1974 *
1975 * @param pVM The VM to operate on.
1976 * @param pPatch Patch structure ptr
1977 * @param pInstrGC Guest context pointer to privileged instruction
1978 *
1979 */
1980static void patmPatchAddDisasmJump(PVM pVM, PPATCHINFO pPatch, RTGCPTR pInstrGC)
1981{
1982 PAVLPVNODECORE pRec;
1983
1984 pRec = (PAVLPVNODECORE)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(*pRec));
1985 Assert(pRec);
1986 pRec->Key = (AVLPVKEY)pInstrGC;
1987
1988 int ret = RTAvlPVInsert(&pPatch->pTempInfo->DisasmJumpTree, pRec);
1989 Assert(ret);
1990}
1991
1992/**
1993 * Checks if jump target has been analysed before.
1994 *
1995 * @returns VBox status code.
1996 * @param pPatch Patch struct
1997 * @param pInstrGC Jump target
1998 *
1999 */
2000static bool patmIsKnownDisasmJump(PPATCHINFO pPatch, RTGCPTR pInstrGC)
2001{
2002 PAVLPVNODECORE pRec;
2003
2004 pRec = RTAvlPVGet(&pPatch->pTempInfo->DisasmJumpTree, (AVLPVKEY)pInstrGC);
2005 if (pRec)
2006 return true;
2007 return false;
2008}
2009
2010/**
2011 * For proper disassembly of the final patch block
2012 *
2013 * @returns VBox status code.
2014 * @param pVM The VM to operate on.
2015 * @param pCpu CPU disassembly state
2016 * @param pInstrGC Guest context pointer to privileged instruction
2017 * @param pCurInstrGC Guest context pointer to the current instruction
2018 * @param pUserData User pointer (callback specific)
2019 *
2020 */
2021int patmr3DisasmCallback(PVM pVM, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, void *pUserData)
2022{
2023 PPATCHINFO pPatch = (PPATCHINFO)pUserData;
2024
2025 if (pCpu->pCurInstr->opcode == OP_INT3)
2026 {
2027 /* Could be an int3 inserted in a call patch. Check to be sure */
2028 DISCPUSTATE cpu;
2029 uint8_t *pOrgJumpHC;
2030 RTGCPTR pOrgJumpGC;
2031 uint32_t dummy;
2032
2033 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
2034 pOrgJumpGC = patmPatchGCPtr2GuestGCPtr(pVM, pPatch, pCurInstrGC);
2035 pOrgJumpHC = PATMGCVirtToHCVirt(pVM, pPatch, pOrgJumpGC);
2036
2037 bool disret = PATMR3DISInstr(pVM, pPatch, &cpu, pOrgJumpGC, pOrgJumpHC, &dummy, NULL);
2038 if (!disret || cpu.pCurInstr->opcode != OP_CALL || cpu.param1.size != 4 /* only near calls */)
2039 return VINF_SUCCESS;
2040
2041 return VWRN_CONTINUE_ANALYSIS;
2042 }
2043
2044 if ( pCpu->pCurInstr->opcode == OP_ILLUD2
2045 && PATMIsPatchGCAddr(pVM, pCurInstrGC))
2046 {
2047 /* the indirect call patch contains an 0xF/0xB illegal instr to call for assistance; check for this and continue */
2048 return VWRN_CONTINUE_ANALYSIS;
2049 }
2050
2051 if ( (pCpu->pCurInstr->opcode == OP_CALL && !(pPatch->flags & PATMFL_SUPPORT_CALLS))
2052 || pCpu->pCurInstr->opcode == OP_INT
2053 || pCpu->pCurInstr->opcode == OP_IRET
2054 || pCpu->pCurInstr->opcode == OP_RETN
2055 || pCpu->pCurInstr->opcode == OP_RETF
2056 )
2057 {
2058 return VINF_SUCCESS;
2059 }
2060
2061 if (pCpu->pCurInstr->opcode == OP_ILLUD2)
2062 return VINF_SUCCESS;
2063
2064 return VWRN_CONTINUE_ANALYSIS;
2065}
2066
2067
2068/**
2069 * Disassembles the code stream until the callback function detects a failure or decides everything is acceptable
2070 *
2071 * @returns VBox status code.
2072 * @param pVM The VM to operate on.
2073 * @param pInstrGC Guest context pointer to the initial privileged instruction
2074 * @param pCurInstrGC Guest context pointer to the current instruction
2075 * @param pfnPATMR3Disasm Callback for testing the disassembled instruction
2076 * @param pUserData User pointer (callback specific)
2077 *
2078 */
2079int patmr3DisasmCode(PVM pVM, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, PFN_PATMR3ANALYSE pfnPATMR3Disasm, void *pUserData)
2080{
2081 DISCPUSTATE cpu;
2082 PPATCHINFO pPatch = (PPATCHINFO)pUserData;
2083 int rc = VWRN_CONTINUE_ANALYSIS;
2084 uint32_t opsize, delta;
2085 R3PTRTYPE(uint8_t *) pCurInstrHC = 0;
2086 bool disret;
2087 char szOutput[256];
2088
2089 Assert(pCurInstrHC != PATCHCODE_PTR_HC(pPatch) || pPatch->pTempInfo->DisasmJumpTree == 0);
2090
2091 /* We need this to determine branch targets (and for disassembling). */
2092 delta = pVM->patm.s.pPatchMemGC - (uintptr_t)pVM->patm.s.pPatchMemHC;
2093
2094 while(rc == VWRN_CONTINUE_ANALYSIS)
2095 {
2096 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
2097
2098 pCurInstrHC = PATMGCVirtToHCVirt(pVM, pPatch, pCurInstrGC);
2099 if (pCurInstrHC == NULL)
2100 {
2101 rc = VERR_PATCHING_REFUSED;
2102 goto end;
2103 }
2104
2105 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pCurInstrGC, pCurInstrHC, &opsize, szOutput, PATMREAD_RAWCODE);
2106 if (PATMIsPatchGCAddr(pVM, pCurInstrGC))
2107 {
2108 RTGCPTR pOrgInstrGC = patmPatchGCPtr2GuestGCPtr(pVM, pPatch, pCurInstrGC);
2109
2110 if (pOrgInstrGC != pPatch->pTempInfo->pLastDisasmInstrGC)
2111 Log(("DIS %VGv<-%s", pOrgInstrGC, szOutput));
2112 else
2113 Log(("DIS %s", szOutput));
2114
2115 pPatch->pTempInfo->pLastDisasmInstrGC = pOrgInstrGC;
2116 if (patmIsIllegalInstr(pPatch, pOrgInstrGC))
2117 {
2118 rc = VINF_SUCCESS;
2119 goto end;
2120 }
2121 }
2122 else
2123 Log(("DIS: %s", szOutput));
2124
2125 if (disret == false)
2126 {
2127 Log(("Disassembly failed (probably page not present) -> return to caller\n"));
2128 rc = VINF_SUCCESS;
2129 goto end;
2130 }
2131
2132 rc = pfnPATMR3Disasm(pVM, &cpu, pInstrGC, pCurInstrGC, pUserData);
2133 if (rc != VWRN_CONTINUE_ANALYSIS) {
2134 break; //done!
2135 }
2136
2137 /* For our first attempt, we'll handle only simple relative jumps and calls (immediate offset coded in instruction) */
2138 if ( (cpu.pCurInstr->optype & OPTYPE_CONTROLFLOW)
2139 && (OP_PARM_VTYPE(cpu.pCurInstr->param1) == OP_PARM_J)
2140 && cpu.pCurInstr->opcode != OP_CALL /* complete functions are replaced; don't bother here. */
2141 )
2142 {
2143 RTGCPTR pTargetGC = PATMResolveBranch(&cpu, pCurInstrGC);
2144 RTGCPTR pOrgTargetGC;
2145
2146 if (pTargetGC == 0)
2147 {
2148 Log(("We don't support far jumps here!! (%08X)\n", cpu.param1.flags));
2149 rc = VERR_PATCHING_REFUSED;
2150 break;
2151 }
2152
2153 if (!PATMIsPatchGCAddr(pVM, pTargetGC))
2154 {
2155 //jump back to guest code
2156 rc = VINF_SUCCESS;
2157 goto end;
2158 }
2159 pOrgTargetGC = PATMR3PatchToGCPtr(pVM, pTargetGC, 0);
2160
2161 if (patmIsCommonIDTHandlerPatch(pVM, pOrgTargetGC))
2162 {
2163 rc = VINF_SUCCESS;
2164 goto end;
2165 }
2166
2167 if (patmIsKnownDisasmJump(pPatch, pTargetGC) == false)
2168 {
2169 /* New jump, let's check it. */
2170 patmPatchAddDisasmJump(pVM, pPatch, pTargetGC);
2171
2172 if (cpu.pCurInstr->opcode == OP_CALL) pPatch->pTempInfo->nrCalls++;
2173 rc = patmr3DisasmCode(pVM, pInstrGC, pTargetGC, pfnPATMR3Disasm, pUserData);
2174 if (cpu.pCurInstr->opcode == OP_CALL) pPatch->pTempInfo->nrCalls--;
2175
2176 if (rc != VINF_SUCCESS) {
2177 break; //done!
2178 }
2179 }
2180 if (cpu.pCurInstr->opcode == OP_JMP)
2181 {
2182 /* Unconditional jump; return to caller. */
2183 rc = VINF_SUCCESS;
2184 goto end;
2185 }
2186
2187 rc = VWRN_CONTINUE_ANALYSIS;
2188 }
2189 pCurInstrGC += opsize;
2190 }
2191end:
2192 return rc;
2193}
2194
2195/**
2196 * Disassembles the code stream until the callback function detects a failure or decides everything is acceptable
2197 *
2198 * @returns VBox status code.
2199 * @param pVM The VM to operate on.
2200 * @param pInstrGC Guest context pointer to the initial privileged instruction
2201 * @param pCurInstrGC Guest context pointer to the current instruction
2202 * @param pfnPATMR3Disasm Callback for testing the disassembled instruction
2203 * @param pUserData User pointer (callback specific)
2204 *
2205 */
2206int patmr3DisasmCodeStream(PVM pVM, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, PFN_PATMR3ANALYSE pfnPATMR3Disasm, void *pUserData)
2207{
2208 PPATCHINFO pPatch = (PPATCHINFO)pUserData;
2209
2210 int rc = patmr3DisasmCode(pVM, pInstrGC, pCurInstrGC, pfnPATMR3Disasm, pUserData);
2211 /* Free all disasm jump records. */
2212 patmEmptyTree(pVM, &pPatch->pTempInfo->DisasmJumpTree);
2213 return rc;
2214}
2215
2216#endif /* LOG_ENABLED */
2217
2218/**
2219 * Detects it the specified address falls within a 5 byte jump generated for an active patch.
2220 * If so, this patch is permanently disabled.
2221 *
2222 * @param pVM The VM to operate on.
2223 * @param pInstrGC Guest context pointer to instruction
2224 * @param pConflictGC Guest context pointer to check
2225 *
2226 * @note also checks for patch hints to make sure they can never be enabled if a conflict is present.
2227 *
2228 */
2229PATMR3DECL(int) PATMR3DetectConflict(PVM pVM, RTGCPTR pInstrGC, RTGCPTR pConflictGC)
2230{
2231 PPATCHINFO pTargetPatch = PATMFindActivePatchByEntrypoint(pVM, pConflictGC, true /* include patch hints */);
2232 if (pTargetPatch)
2233 {
2234 return patmDisableUnusablePatch(pVM, pInstrGC, pConflictGC, pTargetPatch);
2235 }
2236 return VERR_PATCH_NO_CONFLICT;
2237}
2238
2239/**
2240 * Recompile the code stream until the callback function detects a failure or decides everything is acceptable
2241 *
2242 * @returns VBox status code.
2243 * @param pVM The VM to operate on.
2244 * @param pInstrGC Guest context pointer to privileged instruction
2245 * @param pCurInstrGC Guest context pointer to the current instruction
2246 * @param pfnPATMR3Recompile Callback for testing the disassembled instruction
2247 * @param pUserData User pointer (callback specific)
2248 *
2249 */
2250static int patmRecompileCodeStream(PVM pVM, GCPTRTYPE(uint8_t *) pInstrGC, GCPTRTYPE(uint8_t *) pCurInstrGC, PFN_PATMR3ANALYSE pfnPATMR3Recompile, void *pUserData)
2251{
2252 DISCPUSTATE cpu;
2253 PPATCHINFO pPatch = (PPATCHINFO)pUserData;
2254 int rc = VWRN_CONTINUE_ANALYSIS;
2255 uint32_t opsize;
2256 R3PTRTYPE(uint8_t *) pCurInstrHC = 0;
2257 bool disret;
2258#ifdef LOG_ENABLED
2259 char szOutput[256];
2260#endif
2261
2262 while (rc == VWRN_CONTINUE_RECOMPILE)
2263 {
2264 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
2265
2266 ////Log(("patmRecompileCodeStream %VGv %VGv\n", pInstrGC, pCurInstrGC));
2267
2268 pCurInstrHC = PATMGCVirtToHCVirt(pVM, pPatch, pCurInstrGC);
2269 if (pCurInstrHC == NULL)
2270 {
2271 rc = VERR_PATCHING_REFUSED; /* fatal in this case */
2272 goto end;
2273 }
2274#ifdef LOG_ENABLED
2275 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pCurInstrGC, pCurInstrHC, &opsize, szOutput);
2276 Log(("Recompile: %s", szOutput));
2277#else
2278 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pCurInstrGC, pCurInstrHC, &opsize, NULL);
2279#endif
2280 if (disret == false)
2281 {
2282 Log(("Disassembly failed (probably page not present) -> return to caller\n"));
2283
2284 /* Add lookup record for patch to guest address translation */
2285 patmr3AddP2GLookupRecord(pVM, pPatch, PATCHCODE_PTR_HC(pPatch) + pPatch->uCurPatchOffset, pCurInstrGC, PATM_LOOKUP_BOTHDIR);
2286 patmPatchGenIllegalInstr(pVM, pPatch);
2287 rc = VINF_SUCCESS; /* Note: don't fail here; we might refuse an important patch!! */
2288 goto end;
2289 }
2290
2291 rc = pfnPATMR3Recompile(pVM, &cpu, pInstrGC, pCurInstrGC, pUserData);
2292 if (rc != VWRN_CONTINUE_RECOMPILE)
2293 {
2294 /* If irqs are inhibited because of the current instruction, then we must make sure the next one is executed! */
2295 if ( rc == VINF_SUCCESS
2296 && (pPatch->flags & PATMFL_INHIBIT_IRQS))
2297 {
2298 DISCPUSTATE cpunext;
2299 uint32_t opsizenext;
2300 uint8_t *pNextInstrHC;
2301 RTGCPTR pNextInstrGC = pCurInstrGC + opsize;
2302
2303 Log(("patmRecompileCodeStream: irqs inhibited by instruction %VGv\n", pNextInstrGC));
2304
2305 /* Certain instructions (e.g. sti) force the next instruction to be executed before any interrupts can occur.
2306 * Recompile the next instruction as well
2307 */
2308 pNextInstrHC = PATMGCVirtToHCVirt(pVM, pPatch, pNextInstrGC);
2309 if (pNextInstrHC == NULL)
2310 {
2311 rc = VERR_PATCHING_REFUSED; /* fatal in this case */
2312 goto end;
2313 }
2314 cpunext.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
2315 disret = PATMR3DISInstr(pVM, pPatch, &cpunext, pNextInstrGC, pNextInstrHC, &opsizenext, NULL);
2316 if (disret == false)
2317 {
2318 rc = VERR_PATCHING_REFUSED; /* fatal in this case */
2319 goto end;
2320 }
2321 switch(cpunext.pCurInstr->opcode)
2322 {
2323 case OP_IRET: /* inhibit cleared in generated code */
2324 case OP_SYSEXIT: /* faults; inhibit should be cleared in HC handling */
2325 case OP_HLT:
2326 break; /* recompile these */
2327
2328 default:
2329 if (cpunext.pCurInstr->optype & OPTYPE_CONTROLFLOW)
2330 {
2331 Log(("Unexpected control flow instruction after inhibit irq instruction\n"));
2332
2333 rc = patmPatchGenJumpToGuest(pVM, pPatch, pNextInstrGC, true /* clear inhibit irq flag */);
2334 AssertRC(rc);
2335 pPatch->flags &= ~PATMFL_INHIBIT_IRQS;
2336 goto end; /** @todo should be ok to ignore instruction fusing in this case */
2337 }
2338 break;
2339 }
2340
2341 /** @note after a cli we must continue to a proper exit point */
2342 if (cpunext.pCurInstr->opcode != OP_CLI)
2343 {
2344 rc = pfnPATMR3Recompile(pVM, &cpunext, pInstrGC, pNextInstrGC, pUserData);
2345 if (VBOX_SUCCESS(rc))
2346 {
2347 rc = VINF_SUCCESS;
2348 goto end;
2349 }
2350 break;
2351 }
2352 else
2353 rc = VWRN_CONTINUE_RECOMPILE;
2354 }
2355 else
2356 break; /* done! */
2357 }
2358
2359 /** @todo continue with the instructions following the jump and then recompile the jump target code */
2360
2361
2362 /* For our first attempt, we'll handle only simple relative jumps and calls (immediate offset coded in instruction). */
2363 if ( (cpu.pCurInstr->optype & OPTYPE_CONTROLFLOW)
2364 && (OP_PARM_VTYPE(cpu.pCurInstr->param1) == OP_PARM_J)
2365 && cpu.pCurInstr->opcode != OP_CALL /* complete functions are replaced; don't bother here. */
2366 )
2367 {
2368 GCPTRTYPE(uint8_t *) addr = PATMResolveBranch(&cpu, pCurInstrGC);
2369 if (addr == 0)
2370 {
2371 Log(("We don't support far jumps here!! (%08X)\n", cpu.param1.flags));
2372 rc = VERR_PATCHING_REFUSED;
2373 break;
2374 }
2375
2376 Log(("Jump encountered target %VGv\n", addr));
2377
2378 /* We don't check if the branch target lies in a valid page as we've already done that in the analysis phase. */
2379 if (!(cpu.pCurInstr->optype & OPTYPE_UNCOND_CONTROLFLOW))
2380 {
2381 Log(("patmRecompileCodeStream continue passed conditional jump\n"));
2382 /* First we need to finish this linear code stream until the next exit point. */
2383 rc = patmRecompileCodeStream(pVM, pInstrGC, pCurInstrGC+opsize, pfnPATMR3Recompile, pUserData);
2384 if (VBOX_FAILURE(rc))
2385 {
2386 Log(("patmRecompileCodeStream fatal error %d\n", rc));
2387 break; //fatal error
2388 }
2389 }
2390
2391 if (patmGuestGCPtrToPatchGCPtr(pVM, pPatch, addr) == 0)
2392 {
2393 /* New code; let's recompile it. */
2394 Log(("patmRecompileCodeStream continue with jump\n"));
2395
2396 /*
2397 * If we are jumping to an existing patch (or within 5 bytes of the entrypoint), then we must temporarily disable
2398 * this patch so we can continue our analysis
2399 *
2400 * We rely on CSAM to detect and resolve conflicts
2401 */
2402 PPATCHINFO pTargetPatch = PATMFindActivePatchByEntrypoint(pVM, addr);
2403 if(pTargetPatch)
2404 {
2405 Log(("Found active patch at target %VGv (%VGv) -> temporarily disabling it!!\n", addr, pTargetPatch->pPrivInstrGC));
2406 PATMR3DisablePatch(pVM, pTargetPatch->pPrivInstrGC);
2407 }
2408
2409 if (cpu.pCurInstr->opcode == OP_CALL) pPatch->pTempInfo->nrCalls++;
2410 rc = patmRecompileCodeStream(pVM, pInstrGC, addr, pfnPATMR3Recompile, pUserData);
2411 if (cpu.pCurInstr->opcode == OP_CALL) pPatch->pTempInfo->nrCalls--;
2412
2413 if(pTargetPatch)
2414 {
2415 PATMR3EnablePatch(pVM, pTargetPatch->pPrivInstrGC);
2416 }
2417
2418 if (VBOX_FAILURE(rc))
2419 {
2420 Log(("patmRecompileCodeStream fatal error %d\n", rc));
2421 break; //done!
2422 }
2423 }
2424 /* Always return to caller here; we're done! */
2425 rc = VINF_SUCCESS;
2426 goto end;
2427 }
2428 else
2429 if (cpu.pCurInstr->optype & OPTYPE_UNCOND_CONTROLFLOW)
2430 {
2431 rc = VINF_SUCCESS;
2432 goto end;
2433 }
2434 pCurInstrGC += opsize;
2435 }
2436end:
2437 Assert(!(pPatch->flags & PATMFL_RECOMPILE_NEXT));
2438 return rc;
2439}
2440
2441
2442/**
2443 * Generate the jump from guest to patch code
2444 *
2445 * @returns VBox status code.
2446 * @param pVM The VM to operate on.
2447 * @param pPatch Patch record
2448 */
2449static int patmGenJumpToPatch(PVM pVM, PPATCHINFO pPatch, bool fAddFixup = true)
2450{
2451 uint8_t temp[8];
2452 uint8_t *pPB;
2453 int rc;
2454
2455 Assert(pPatch->cbPatchJump <= sizeof(temp));
2456 Assert(!(pPatch->flags & PATMFL_PATCHED_GUEST_CODE));
2457
2458 pPB = pPatch->pPrivInstrHC;
2459
2460#ifdef PATM_RESOLVE_CONFLICTS_WITH_JUMP_PATCHES
2461 if (pPatch->flags & PATMFL_JUMP_CONFLICT)
2462 {
2463 Assert(pPatch->pPatchJumpDestGC);
2464
2465 if (pPatch->cbPatchJump == SIZEOF_NEARJUMP32)
2466 {
2467 // jmp [PatchCode]
2468 if (fAddFixup)
2469 {
2470 if (patmPatchAddReloc32(pVM, pPatch, &pPB[1], FIXUP_REL_JMPTOPATCH, pPatch->pPrivInstrGC + pPatch->cbPatchJump, pPatch->pPatchJumpDestGC) != VINF_SUCCESS)
2471 {
2472 Log(("Relocation failed for the jump in the guest code!!\n"));
2473 return VERR_PATCHING_REFUSED;
2474 }
2475 }
2476
2477 temp[0] = pPatch->aPrivInstr[0]; //jump opcode copied from original instruction
2478 *(uint32_t *)&temp[1] = (uint32_t)pPatch->pPatchJumpDestGC - ((uint32_t)pPatch->pPrivInstrGC + pPatch->cbPatchJump); //return address
2479 }
2480 else
2481 if (pPatch->cbPatchJump == SIZEOF_NEAR_COND_JUMP32)
2482 {
2483 // jmp [PatchCode]
2484 if (fAddFixup)
2485 {
2486 if (patmPatchAddReloc32(pVM, pPatch, &pPB[2], FIXUP_REL_JMPTOPATCH, pPatch->pPrivInstrGC + pPatch->cbPatchJump, pPatch->pPatchJumpDestGC) != VINF_SUCCESS)
2487 {
2488 Log(("Relocation failed for the jump in the guest code!!\n"));
2489 return VERR_PATCHING_REFUSED;
2490 }
2491 }
2492
2493 temp[0] = pPatch->aPrivInstr[0]; //jump opcode copied from original instruction
2494 temp[1] = pPatch->aPrivInstr[1]; //jump opcode copied from original instruction
2495 *(uint32_t *)&temp[2] = (uint32_t)pPatch->pPatchJumpDestGC - ((uint32_t)pPatch->pPrivInstrGC + pPatch->cbPatchJump); //return address
2496 }
2497 else
2498 {
2499 Assert(0);
2500 return VERR_PATCHING_REFUSED;
2501 }
2502 }
2503 else
2504#endif
2505 {
2506 Assert(pPatch->cbPatchJump == SIZEOF_NEARJUMP32);
2507
2508 // jmp [PatchCode]
2509 if (fAddFixup)
2510 {
2511 if (patmPatchAddReloc32(pVM, pPatch, &pPB[1], FIXUP_REL_JMPTOPATCH, pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32, PATCHCODE_PTR_GC(pPatch)) != VINF_SUCCESS)
2512 {
2513 Log(("Relocation failed for the jump in the guest code!!\n"));
2514 return VERR_PATCHING_REFUSED;
2515 }
2516 }
2517 temp[0] = 0xE9; //jmp
2518 *(uint32_t *)&temp[1] = (RTGCUINTPTR)PATCHCODE_PTR_GC(pPatch) - ((RTGCUINTPTR)pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32); //return address
2519 }
2520 rc = PGMPhysWriteGCPtrDirty(pVM, pPatch->pPrivInstrGC, temp, pPatch->cbPatchJump);
2521 AssertRC(rc);
2522
2523 if (rc == VINF_SUCCESS)
2524 pPatch->flags |= PATMFL_PATCHED_GUEST_CODE;
2525
2526 return rc;
2527}
2528
2529/**
2530 * Remove the jump from guest to patch code
2531 *
2532 * @returns VBox status code.
2533 * @param pVM The VM to operate on.
2534 * @param pPatch Patch record
2535 */
2536static int patmRemoveJumpToPatch(PVM pVM, PPATCHINFO pPatch)
2537{
2538#ifdef DEBUG
2539 DISCPUSTATE cpu;
2540 char szOutput[256];
2541 uint32_t opsize, i = 0;
2542 bool disret;
2543
2544 while(i < pPatch->cbPrivInstr)
2545 {
2546 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
2547 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC + i, &pPatch->pPrivInstrHC[i], &opsize, szOutput);
2548 if (disret == false)
2549 break;
2550
2551 Log(("Org patch jump: %s", szOutput));
2552 Assert(opsize);
2553 i += opsize;
2554 }
2555#endif
2556
2557 /* Restore original code (privileged instruction + following instructions that were overwritten because of the 5/6 byte jmp). */
2558 int rc = PGMPhysWriteGCPtrDirty(pVM, pPatch->pPrivInstrGC, pPatch->aPrivInstr, pPatch->cbPatchJump);
2559#ifdef DEBUG
2560 if (rc == VINF_SUCCESS)
2561 {
2562 DISCPUSTATE cpu;
2563 char szOutput[256];
2564 uint32_t opsize, i = 0;
2565 bool disret;
2566
2567 while(i < pPatch->cbPrivInstr)
2568 {
2569 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
2570 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC + i, &pPatch->pPrivInstrHC[i], &opsize, szOutput);
2571 if (disret == false)
2572 break;
2573
2574 Log(("Org instr: %s", szOutput));
2575 Assert(opsize);
2576 i += opsize;
2577 }
2578 }
2579#endif
2580 pPatch->flags &= ~PATMFL_PATCHED_GUEST_CODE;
2581 return rc;
2582}
2583
2584/**
2585 * Generate the call from guest to patch code
2586 *
2587 * @returns VBox status code.
2588 * @param pVM The VM to operate on.
2589 * @param pPatch Patch record
2590 */
2591static int patmGenCallToPatch(PVM pVM, PPATCHINFO pPatch, RTGCPTR pTargetGC, bool fAddFixup = true)
2592{
2593 uint8_t temp[8];
2594 uint8_t *pPB;
2595 int rc;
2596
2597 Assert(pPatch->cbPatchJump <= sizeof(temp));
2598
2599 pPB = pPatch->pPrivInstrHC;
2600
2601 Assert(pPatch->cbPatchJump == SIZEOF_NEARJUMP32);
2602
2603 // jmp [PatchCode]
2604 if (fAddFixup)
2605 {
2606 if (patmPatchAddReloc32(pVM, pPatch, &pPB[1], FIXUP_REL_JMPTOPATCH, pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32, pTargetGC) != VINF_SUCCESS)
2607 {
2608 Log(("Relocation failed for the jump in the guest code!!\n"));
2609 return VERR_PATCHING_REFUSED;
2610 }
2611 }
2612
2613 Assert(pPatch->aPrivInstr[0] == 0xE8 || pPatch->aPrivInstr[0] == 0xE9); /* call or jmp */
2614 temp[0] = pPatch->aPrivInstr[0];
2615 *(uint32_t *)&temp[1] = (uint32_t)pTargetGC - ((uint32_t)pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32); //return address
2616
2617 rc = PGMPhysWriteGCPtrDirty(pVM, pPatch->pPrivInstrGC, temp, pPatch->cbPatchJump);
2618 AssertRC(rc);
2619
2620 return rc;
2621}
2622
2623
2624/**
2625 * Patch cli/sti pushf/popf instruction block at specified location
2626 *
2627 * @returns VBox status code.
2628 * @param pVM The VM to operate on.
2629 * @param pInstrGC Guest context point to privileged instruction
2630 * @param pInstrHC Host context point to privileged instruction
2631 * @param uOpcode Instruction opcode
2632 * @param uOpSize Size of starting instruction
2633 * @param pPatchRec Patch record
2634 *
2635 * @note returns failure if patching is not allowed or possible
2636 *
2637 */
2638PATMR3DECL(int) PATMR3PatchBlock(PVM pVM, RTGCPTR pInstrGC, R3PTRTYPE(uint8_t *) pInstrHC,
2639 uint32_t uOpcode, uint32_t uOpSize, PPATMPATCHREC pPatchRec)
2640{
2641 PPATCHINFO pPatch = &pPatchRec->patch;
2642 int rc = VERR_PATCHING_REFUSED;
2643 DISCPUSTATE cpu;
2644 uint32_t orgOffsetPatchMem = ~0;
2645 RTGCPTR pInstrStart;
2646#ifdef LOG_ENABLED
2647 uint32_t opsize;
2648 char szOutput[256];
2649 bool disret;
2650#endif
2651
2652 /* Save original offset (in case of failures later on) */
2653 /** @todo use the hypervisor heap (that has quite a few consequences for save/restore though) */
2654 orgOffsetPatchMem = pVM->patm.s.offPatchMem;
2655
2656 Assert(!(pPatch->flags & (PATMFL_GUEST_SPECIFIC|PATMFL_USER_MODE|PATMFL_TRAPHANDLER)));
2657 switch (uOpcode)
2658 {
2659 case OP_MOV:
2660 break;
2661
2662 case OP_CLI:
2663 case OP_PUSHF:
2664 /* We can 'call' a cli or pushf patch. It will either return to the original guest code when IF is set again, or fault. */
2665 /** @note special precautions are taken when disabling and enabling such patches. */
2666 pPatch->flags |= PATMFL_CALLABLE_AS_FUNCTION;
2667 break;
2668
2669 default:
2670 if (!(pPatch->flags & PATMFL_IDTHANDLER))
2671 {
2672 AssertMsg(0, ("PATMR3PatchBlock: Invalid opcode %x\n", uOpcode));
2673 return VERR_INVALID_PARAMETER;
2674 }
2675 }
2676
2677 if (!(pPatch->flags & (PATMFL_IDTHANDLER|PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT|PATMFL_SYSENTER|PATMFL_INT3_REPLACEMENT_BLOCK)))
2678 pPatch->flags |= PATMFL_MUST_INSTALL_PATCHJMP;
2679
2680 /* If we're going to insert a patch jump, then the jump itself is not allowed to cross a page boundary. */
2681 if ( (pPatch->flags & PATMFL_MUST_INSTALL_PATCHJMP)
2682 && PAGE_ADDRESS(pInstrGC) != PAGE_ADDRESS(pInstrGC + SIZEOF_NEARJUMP32)
2683 )
2684 {
2685 STAM_COUNTER_INC(&pVM->patm.s.StatPageBoundaryCrossed);
2686#ifdef DEBUG_sandervl
2687//// AssertMsgFailed(("Patch jump would cross page boundary -> refuse!!\n"));
2688#endif
2689 rc = VERR_PATCHING_REFUSED;
2690 goto failure;
2691 }
2692
2693 pPatch->nrPatch2GuestRecs = 0;
2694 pInstrStart = pInstrGC;
2695
2696#ifdef PATM_ENABLE_CALL
2697 pPatch->flags |= PATMFL_SUPPORT_CALLS | PATMFL_SUPPORT_INDIRECT_CALLS;
2698#endif
2699
2700 pPatch->pPatchBlockOffset = pVM->patm.s.offPatchMem;
2701 pPatch->uCurPatchOffset = 0;
2702
2703 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
2704
2705 if ((pPatch->flags & (PATMFL_IDTHANDLER|PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT|PATMFL_SYSENTER)) == PATMFL_IDTHANDLER)
2706 {
2707 Assert(pPatch->flags & PATMFL_INTHANDLER);
2708
2709 /* Install fake cli patch (to clear the virtual IF and check int xx parameters) */
2710 rc = patmPatchGenIntEntry(pVM, pPatch, pInstrGC);
2711 if (VBOX_FAILURE(rc))
2712 goto failure;
2713 }
2714
2715 /***************************************************************************************************************************/
2716 /** @note We can't insert *any* code before a sysenter handler; some linux guests have an invalid stack at this point!!!!! */
2717 /***************************************************************************************************************************/
2718#ifdef VBOX_WITH_STATISTICS
2719 if (!(pPatch->flags & PATMFL_SYSENTER))
2720 {
2721 rc = patmPatchGenStats(pVM, pPatch, pInstrGC);
2722 if (VBOX_FAILURE(rc))
2723 goto failure;
2724 }
2725#endif
2726
2727 rc = patmRecompileCodeStream(pVM, pInstrGC, pInstrGC, patmRecompileCallback, pPatch);
2728 if (rc != VINF_SUCCESS)
2729 {
2730 Log(("PATMR3PatchCli: patmRecompileCodeStream failed with %d\n", rc));
2731 goto failure;
2732 }
2733
2734 /* Calculated during analysis. */
2735 if (pPatch->cbPatchBlockSize < SIZEOF_NEARJUMP32)
2736 {
2737 /* Most likely cause: we encountered an illegal instruction very early on. */
2738 /** @todo could turn it into an int3 callable patch. */
2739 Log(("PATMR3PatchBlock: patch block too small -> refuse\n"));
2740 rc = VERR_PATCHING_REFUSED;
2741 goto failure;
2742 }
2743
2744 /* size of patch block */
2745 pPatch->cbPatchBlockSize = pPatch->uCurPatchOffset;
2746
2747
2748 /* Update free pointer in patch memory. */
2749 pVM->patm.s.offPatchMem += pPatch->cbPatchBlockSize;
2750 /* Round to next 8 byte boundary. */
2751 pVM->patm.s.offPatchMem = RT_ALIGN_32(pVM->patm.s.offPatchMem, 8);
2752
2753 /*
2754 * Insert into patch to guest lookup tree
2755 */
2756 LogFlow(("Insert %VGv patch offset %VGv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
2757 pPatchRec->CoreOffset.Key = pPatch->pPatchBlockOffset;
2758 rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
2759 AssertMsg(rc, ("RTAvlULInsert failed for %x\n", pPatchRec->CoreOffset.Key));
2760 if (!rc)
2761 {
2762 rc = VERR_PATCHING_REFUSED;
2763 goto failure;
2764 }
2765
2766 /* Note that patmr3SetBranchTargets can install additional patches!! */
2767 rc = patmr3SetBranchTargets(pVM, pPatch);
2768 if (rc != VINF_SUCCESS)
2769 {
2770 Log(("PATMR3PatchCli: patmr3SetBranchTargets failed with %d\n", rc));
2771 goto failure;
2772 }
2773
2774#ifdef LOG_ENABLED
2775 Log(("Patch code ----------------------------------------------------------\n"));
2776 patmr3DisasmCodeStream(pVM, PATCHCODE_PTR_GC(pPatch), PATCHCODE_PTR_GC(pPatch), patmr3DisasmCallback, pPatch);
2777 Log(("Patch code ends -----------------------------------------------------\n"));
2778#endif
2779
2780 /* make a copy of the guest code bytes that will be overwritten */
2781 pPatch->cbPatchJump = SIZEOF_NEARJUMP32;
2782
2783 rc = PGMPhysReadGCPtr(pVM, pPatch->aPrivInstr, pPatch->pPrivInstrGC, pPatch->cbPatchJump);
2784 AssertRC(rc);
2785
2786 if (pPatch->flags & PATMFL_INT3_REPLACEMENT_BLOCK)
2787 {
2788 /*uint8_t ASMInt3 = 0xCC; - unused */
2789
2790 Log(("PATMR3PatchBlock %VGv -> int 3 callable patch.\n", pPatch->pPrivInstrGC));
2791 /* Replace first opcode byte with 'int 3'. */
2792 rc = patmActivateInt3Patch(pVM, pPatch);
2793 if (VBOX_FAILURE(rc))
2794 goto failure;
2795
2796 /* normal patch can be turned into an int3 patch -> clear patch jump installation flag. */
2797 pPatch->flags &= ~PATMFL_MUST_INSTALL_PATCHJMP;
2798
2799 pPatch->flags &= ~PATMFL_INSTR_HINT;
2800 STAM_COUNTER_INC(&pVM->patm.s.StatInt3Callable);
2801 }
2802 else
2803 if (pPatch->flags & PATMFL_MUST_INSTALL_PATCHJMP)
2804 {
2805 Assert(!(pPatch->flags & (PATMFL_IDTHANDLER|PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT|PATMFL_SYSENTER|PATMFL_INT3_REPLACEMENT_BLOCK)));
2806 /* now insert a jump in the guest code */
2807 rc = patmGenJumpToPatch(pVM, pPatch, true);
2808 AssertRC(rc);
2809 if (VBOX_FAILURE(rc))
2810 goto failure;
2811
2812 }
2813
2814#ifdef LOG_ENABLED
2815 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
2816 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput, PATMREAD_RAWCODE);
2817 Log(("%s patch: %s", patmGetInstructionString(pPatch->opcode, pPatch->flags), szOutput));
2818#endif
2819
2820 patmEmptyTree(pVM, &pPatch->pTempInfo->IllegalInstrTree);
2821 pPatch->pTempInfo->nrIllegalInstr = 0;
2822
2823 Log(("Successfully installed %s patch at %VGv\n", patmGetInstructionString(pPatch->opcode, pPatch->flags), pInstrGC));
2824
2825 pPatch->uState = PATCH_ENABLED;
2826 return VINF_SUCCESS;
2827
2828failure:
2829 if (pPatchRec->CoreOffset.Key)
2830 RTAvloGCPtrRemove(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, pPatchRec->CoreOffset.Key);
2831
2832 patmEmptyTree(pVM, &pPatch->FixupTree);
2833 pPatch->nrFixups = 0;
2834
2835 patmEmptyTree(pVM, &pPatch->JumpTree);
2836 pPatch->nrJumpRecs = 0;
2837
2838 patmEmptyTree(pVM, &pPatch->pTempInfo->IllegalInstrTree);
2839 pPatch->pTempInfo->nrIllegalInstr = 0;
2840
2841 /* Turn this cli patch into a dummy. */
2842 pPatch->uState = PATCH_REFUSED;
2843 pPatch->pPatchBlockOffset = 0;
2844
2845 // Give back the patch memory we no longer need
2846 Assert(orgOffsetPatchMem != (uint32_t)~0);
2847 pVM->patm.s.offPatchMem = orgOffsetPatchMem;
2848
2849 return rc;
2850}
2851
2852/**
2853 * Patch IDT handler
2854 *
2855 * @returns VBox status code.
2856 * @param pVM The VM to operate on.
2857 * @param pInstrGC Guest context point to privileged instruction
2858 * @param pInstrHC Host context point to privileged instruction
2859 * @param uOpSize Size of starting instruction
2860 * @param pPatchRec Patch record
2861 *
2862 * @note returns failure if patching is not allowed or possible
2863 *
2864 */
2865static int patmIdtHandler(PVM pVM, RTGCPTR pInstrGC, R3PTRTYPE(uint8_t *) pInstrHC,
2866 uint32_t uOpSize, PPATMPATCHREC pPatchRec)
2867{
2868 PPATCHINFO pPatch = &pPatchRec->patch;
2869 bool disret;
2870 DISCPUSTATE cpuPush, cpuJmp;
2871 uint32_t opsize;
2872 RTGCPTR pCurInstrGC = pInstrGC;
2873 uint8_t *pCurInstrHC = pInstrHC;
2874 uint32_t orgOffsetPatchMem = ~0;
2875
2876 /*
2877 * In Linux it's often the case that many interrupt handlers push a predefined value onto the stack
2878 * and then jump to a common entrypoint. In order not to waste a lot of memory, we will check for this
2879 * condition here and only patch the common entypoint once.
2880 */
2881 cpuPush.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
2882 disret = PATMR3DISInstr(pVM, pPatch, &cpuPush, pCurInstrGC, pCurInstrHC, &opsize, NULL);
2883 Assert(disret);
2884 if (disret && cpuPush.pCurInstr->opcode == OP_PUSH)
2885 {
2886 RTGCPTR pJmpInstrGC;
2887 int rc;
2888
2889 pCurInstrGC += opsize;
2890 pCurInstrHC = PATMGCVirtToHCVirt(pVM, pPatch, pCurInstrGC);
2891
2892 cpuJmp.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
2893 disret = PATMR3DISInstr(pVM, pPatch, &cpuJmp, pCurInstrGC, pCurInstrHC, &opsize, NULL);
2894 if ( disret
2895 && cpuJmp.pCurInstr->opcode == OP_JMP
2896 && (pJmpInstrGC = PATMResolveBranch(&cpuJmp, pCurInstrGC))
2897 )
2898 {
2899 PPATMPATCHREC pJmpPatch = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pJmpInstrGC);
2900 if (pJmpPatch == 0)
2901 {
2902 /* Patch it first! */
2903 rc = PATMR3InstallPatch(pVM, pJmpInstrGC, pPatch->flags | PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT);
2904 if (rc != VINF_SUCCESS)
2905 goto failure;
2906 pJmpPatch = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pJmpInstrGC);
2907 Assert(pJmpPatch);
2908 }
2909 if (pJmpPatch->patch.uState != PATCH_ENABLED)
2910 goto failure;
2911
2912 /* save original offset (in case of failures later on) */
2913 orgOffsetPatchMem = pVM->patm.s.offPatchMem;
2914
2915 pPatch->pPatchBlockOffset = pVM->patm.s.offPatchMem;
2916 pPatch->uCurPatchOffset = 0;
2917 pPatch->nrPatch2GuestRecs = 0;
2918
2919#ifdef VBOX_WITH_STATISTICS
2920 rc = patmPatchGenStats(pVM, pPatch, pInstrGC);
2921 if (VBOX_FAILURE(rc))
2922 goto failure;
2923#endif
2924
2925 /* Install fake cli patch (to clear the virtual IF) */
2926 rc = patmPatchGenIntEntry(pVM, pPatch, pInstrGC);
2927 if (VBOX_FAILURE(rc))
2928 goto failure;
2929
2930 /* Add lookup record for patch to guest address translation (for the push) */
2931 patmr3AddP2GLookupRecord(pVM, pPatch, PATCHCODE_PTR_HC(pPatch) + pPatch->uCurPatchOffset, pInstrGC, PATM_LOOKUP_BOTHDIR);
2932
2933 /* Duplicate push. */
2934 rc = patmPatchGenDuplicate(pVM, pPatch, &cpuPush, pInstrGC);
2935 if (VBOX_FAILURE(rc))
2936 goto failure;
2937
2938 /* Generate jump to common entrypoint. */
2939 rc = patmPatchGenPatchJump(pVM, pPatch, pCurInstrGC, PATCHCODE_PTR_GC(&pJmpPatch->patch));
2940 if (VBOX_FAILURE(rc))
2941 goto failure;
2942
2943 /* size of patch block */
2944 pPatch->cbPatchBlockSize = pPatch->uCurPatchOffset;
2945
2946 /* Update free pointer in patch memory. */
2947 pVM->patm.s.offPatchMem += pPatch->cbPatchBlockSize;
2948 /* Round to next 8 byte boundary */
2949 pVM->patm.s.offPatchMem = RT_ALIGN_32(pVM->patm.s.offPatchMem, 8);
2950
2951 /* There's no jump from guest to patch code. */
2952 pPatch->cbPatchJump = 0;
2953
2954
2955#ifdef LOG_ENABLED
2956 Log(("Patch code ----------------------------------------------------------\n"));
2957 patmr3DisasmCodeStream(pVM, PATCHCODE_PTR_GC(pPatch), PATCHCODE_PTR_GC(pPatch), patmr3DisasmCallback, pPatch);
2958 Log(("Patch code ends -----------------------------------------------------\n"));
2959#endif
2960 Log(("Successfully installed IDT handler patch at %VGv\n", pInstrGC));
2961
2962 /*
2963 * Insert into patch to guest lookup tree
2964 */
2965 LogFlow(("Insert %VGv patch offset %VGv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
2966 pPatchRec->CoreOffset.Key = pPatch->pPatchBlockOffset;
2967 rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
2968 AssertMsg(rc, ("RTAvlULInsert failed for %x\n", pPatchRec->CoreOffset.Key));
2969
2970 pPatch->uState = PATCH_ENABLED;
2971
2972 return VINF_SUCCESS;
2973 }
2974 }
2975failure:
2976 /* Give back the patch memory we no longer need */
2977 if (orgOffsetPatchMem != (uint32_t)~0)
2978 pVM->patm.s.offPatchMem = orgOffsetPatchMem;
2979
2980 return PATMR3PatchBlock(pVM, pInstrGC, pInstrHC, OP_CLI, uOpSize, pPatchRec);
2981}
2982
2983/**
2984 * Install a trampoline to call a guest trap handler directly
2985 *
2986 * @returns VBox status code.
2987 * @param pVM The VM to operate on.
2988 * @param pInstrGC Guest context point to privileged instruction
2989 * @param pPatchRec Patch record
2990 *
2991 */
2992static int patmInstallTrapTrampoline(PVM pVM, RTGCPTR pInstrGC, PPATMPATCHREC pPatchRec)
2993{
2994 PPATCHINFO pPatch = &pPatchRec->patch;
2995 int rc = VERR_PATCHING_REFUSED;
2996 uint32_t orgOffsetPatchMem = ~0;
2997#ifdef LOG_ENABLED
2998 bool disret;
2999 DISCPUSTATE cpu;
3000 uint32_t opsize;
3001 char szOutput[256];
3002#endif
3003
3004 // save original offset (in case of failures later on)
3005 orgOffsetPatchMem = pVM->patm.s.offPatchMem;
3006
3007 pPatch->pPatchBlockOffset = pVM->patm.s.offPatchMem;
3008 pPatch->uCurPatchOffset = 0;
3009 pPatch->nrPatch2GuestRecs = 0;
3010
3011#ifdef VBOX_WITH_STATISTICS
3012 rc = patmPatchGenStats(pVM, pPatch, pInstrGC);
3013 if (VBOX_FAILURE(rc))
3014 goto failure;
3015#endif
3016
3017 rc = patmPatchGenTrapEntry(pVM, pPatch, pInstrGC);
3018 if (VBOX_FAILURE(rc))
3019 goto failure;
3020
3021 /* size of patch block */
3022 pPatch->cbPatchBlockSize = pPatch->uCurPatchOffset;
3023
3024 /* Update free pointer in patch memory. */
3025 pVM->patm.s.offPatchMem += pPatch->cbPatchBlockSize;
3026 /* Round to next 8 byte boundary */
3027 pVM->patm.s.offPatchMem = RT_ALIGN_32(pVM->patm.s.offPatchMem, 8);
3028
3029 /* There's no jump from guest to patch code. */
3030 pPatch->cbPatchJump = 0;
3031
3032#ifdef LOG_ENABLED
3033 Log(("Patch code ----------------------------------------------------------\n"));
3034 patmr3DisasmCodeStream(pVM, PATCHCODE_PTR_GC(pPatch), PATCHCODE_PTR_GC(pPatch), patmr3DisasmCallback, pPatch);
3035 Log(("Patch code ends -----------------------------------------------------\n"));
3036#endif
3037
3038#ifdef LOG_ENABLED
3039 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
3040 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput);
3041 Log(("TRAP handler patch: %s", szOutput));
3042#endif
3043 Log(("Successfully installed Trap Trampoline patch at %VGv\n", pInstrGC));
3044
3045 /*
3046 * Insert into patch to guest lookup tree
3047 */
3048 LogFlow(("Insert %VGv patch offset %VGv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
3049 pPatchRec->CoreOffset.Key = pPatch->pPatchBlockOffset;
3050 rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
3051 AssertMsg(rc, ("RTAvlULInsert failed for %x\n", pPatchRec->CoreOffset.Key));
3052
3053 pPatch->uState = PATCH_ENABLED;
3054 return VINF_SUCCESS;
3055
3056failure:
3057 AssertMsgFailed(("Failed to install trap handler trampoline!!\n"));
3058
3059 /* Turn this cli patch into a dummy. */
3060 pPatch->uState = PATCH_REFUSED;
3061 pPatch->pPatchBlockOffset = 0;
3062
3063 /* Give back the patch memory we no longer need */
3064 Assert(orgOffsetPatchMem != (uint32_t)~0);
3065 pVM->patm.s.offPatchMem = orgOffsetPatchMem;
3066
3067 return rc;
3068}
3069
3070
3071#ifdef DEBUG
3072/**
3073 * Check if the instruction is patched as a common idt handler
3074 *
3075 * @returns true or false
3076 * @param pVM The VM to operate on.
3077 * @param pInstrGC Guest context point to the instruction
3078 *
3079 */
3080static bool patmIsCommonIDTHandlerPatch(PVM pVM, RTGCPTR pInstrGC)
3081{
3082 PPATMPATCHREC pRec;
3083
3084 pRec = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC);
3085 if (pRec && pRec->patch.flags & PATMFL_IDTHANDLER_WITHOUT_ENTRYPOINT)
3086 return true;
3087 return false;
3088}
3089#endif //DEBUG
3090
3091
3092/**
3093 * Duplicates a complete function
3094 *
3095 * @returns VBox status code.
3096 * @param pVM The VM to operate on.
3097 * @param pInstrGC Guest context point to privileged instruction
3098 * @param pPatchRec Patch record
3099 *
3100 */
3101static int patmDuplicateFunction(PVM pVM, RTGCPTR pInstrGC, PPATMPATCHREC pPatchRec)
3102{
3103 PPATCHINFO pPatch = &pPatchRec->patch;
3104 int rc = VERR_PATCHING_REFUSED;
3105 DISCPUSTATE cpu;
3106 uint32_t orgOffsetPatchMem = ~0;
3107
3108 Log(("patmDuplicateFunction %VGv\n", pInstrGC));
3109 /* Save original offset (in case of failures later on). */
3110 orgOffsetPatchMem = pVM->patm.s.offPatchMem;
3111
3112 /* We will not go on indefinitely with call instruction handling. */
3113 if (pVM->patm.s.ulCallDepth > PATM_MAX_CALL_DEPTH)
3114 {
3115 Log(("patmDuplicateFunction: maximum callback depth reached!!\n"));
3116 return VERR_PATCHING_REFUSED;
3117 }
3118
3119 pVM->patm.s.ulCallDepth++;
3120
3121#ifdef PATM_ENABLE_CALL
3122 pPatch->flags |= PATMFL_SUPPORT_CALLS | PATMFL_SUPPORT_INDIRECT_CALLS;
3123#endif
3124
3125 Assert(pPatch->flags & (PATMFL_DUPLICATE_FUNCTION));
3126
3127 pPatch->nrPatch2GuestRecs = 0;
3128 pPatch->pPatchBlockOffset = pVM->patm.s.offPatchMem;
3129 pPatch->uCurPatchOffset = 0;
3130
3131 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
3132
3133 /** @note Set the PATM interrupt flag here; it was cleared before the patched call. (!!!) */
3134 rc = patmPatchGenSetPIF(pVM, pPatch, pInstrGC);
3135 if (VBOX_FAILURE(rc))
3136 goto failure;
3137
3138#ifdef VBOX_WITH_STATISTICS
3139 rc = patmPatchGenStats(pVM, pPatch, pInstrGC);
3140 if (VBOX_FAILURE(rc))
3141 goto failure;
3142#endif
3143 rc = patmRecompileCodeStream(pVM, pInstrGC, pInstrGC, patmRecompileCallback, pPatch);
3144 if (rc != VINF_SUCCESS)
3145 {
3146 Log(("PATMR3PatchCli: patmRecompileCodeStream failed with %d\n", rc));
3147 goto failure;
3148 }
3149
3150 //size of patch block
3151 pPatch->cbPatchBlockSize = pPatch->uCurPatchOffset;
3152
3153 //update free pointer in patch memory
3154 pVM->patm.s.offPatchMem += pPatch->cbPatchBlockSize;
3155 /* Round to next 8 byte boundary. */
3156 pVM->patm.s.offPatchMem = RT_ALIGN_32(pVM->patm.s.offPatchMem, 8);
3157
3158 pPatch->uState = PATCH_ENABLED;
3159
3160 /*
3161 * Insert into patch to guest lookup tree
3162 */
3163 LogFlow(("Insert %VGv patch offset %VGv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
3164 pPatchRec->CoreOffset.Key = pPatch->pPatchBlockOffset;
3165 rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
3166 AssertMsg(rc, ("RTAvloGCPtrInsert failed for %x\n", pPatchRec->CoreOffset.Key));
3167 if (!rc)
3168 {
3169 rc = VERR_PATCHING_REFUSED;
3170 goto failure;
3171 }
3172
3173 /* Note that patmr3SetBranchTargets can install additional patches!! */
3174 rc = patmr3SetBranchTargets(pVM, pPatch);
3175 if (rc != VINF_SUCCESS)
3176 {
3177 Log(("PATMR3PatchCli: patmr3SetBranchTargets failed with %d\n", rc));
3178 goto failure;
3179 }
3180
3181#ifdef LOG_ENABLED
3182 Log(("Patch code ----------------------------------------------------------\n"));
3183 patmr3DisasmCodeStream(pVM, PATCHCODE_PTR_GC(pPatch), PATCHCODE_PTR_GC(pPatch), patmr3DisasmCallback, pPatch);
3184 Log(("Patch code ends -----------------------------------------------------\n"));
3185#endif
3186
3187 Log(("Successfully installed function duplication patch at %VGv\n", pInstrGC));
3188
3189 patmEmptyTree(pVM, &pPatch->pTempInfo->IllegalInstrTree);
3190 pPatch->pTempInfo->nrIllegalInstr = 0;
3191
3192 pVM->patm.s.ulCallDepth--;
3193 STAM_COUNTER_INC(&pVM->patm.s.StatInstalledFunctionPatches);
3194 return VINF_SUCCESS;
3195
3196failure:
3197 if (pPatchRec->CoreOffset.Key)
3198 RTAvloGCPtrRemove(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, pPatchRec->CoreOffset.Key);
3199
3200 patmEmptyTree(pVM, &pPatch->FixupTree);
3201 pPatch->nrFixups = 0;
3202
3203 patmEmptyTree(pVM, &pPatch->JumpTree);
3204 pPatch->nrJumpRecs = 0;
3205
3206 patmEmptyTree(pVM, &pPatch->pTempInfo->IllegalInstrTree);
3207 pPatch->pTempInfo->nrIllegalInstr = 0;
3208
3209 /* Turn this cli patch into a dummy. */
3210 pPatch->uState = PATCH_REFUSED;
3211 pPatch->pPatchBlockOffset = 0;
3212
3213 // Give back the patch memory we no longer need
3214 Assert(orgOffsetPatchMem != (uint32_t)~0);
3215 pVM->patm.s.offPatchMem = orgOffsetPatchMem;
3216
3217 pVM->patm.s.ulCallDepth--;
3218 Log(("patmDupicateFunction %VGv failed!!\n", pInstrGC));
3219 return rc;
3220}
3221
3222/**
3223 * Creates trampoline code to jump inside an existing patch
3224 *
3225 * @returns VBox status code.
3226 * @param pVM The VM to operate on.
3227 * @param pInstrGC Guest context point to privileged instruction
3228 * @param pPatchRec Patch record
3229 *
3230 */
3231static int patmCreateTrampoline(PVM pVM, RTGCPTR pInstrGC, PPATMPATCHREC pPatchRec)
3232{
3233 PPATCHINFO pPatch = &pPatchRec->patch;
3234 RTGCPTR pPage, pPatchTargetGC = 0;
3235 uint32_t orgOffsetPatchMem = ~0;
3236 int rc = VERR_PATCHING_REFUSED;
3237
3238 Log(("patmCreateTrampoline %VGv\n", pInstrGC));
3239 /* Save original offset (in case of failures later on). */
3240 orgOffsetPatchMem = pVM->patm.s.offPatchMem;
3241
3242 /* First we check if the duplicate function target lies in some existing function patch already. Will save some space. */
3243 /** @todo we already checked this before */
3244 pPage = pInstrGC & PAGE_BASE_GC_MASK;
3245
3246 PPATMPATCHPAGE pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, (RTGCPTR)pPage);
3247 if (pPatchPage)
3248 {
3249 uint32_t i;
3250
3251 for (i=0;i<pPatchPage->cCount;i++)
3252 {
3253 if (pPatchPage->aPatch[i])
3254 {
3255 PPATCHINFO pPatch = pPatchPage->aPatch[i];
3256
3257 if ( (pPatch->flags & PATMFL_DUPLICATE_FUNCTION)
3258 && pPatch->uState == PATCH_ENABLED)
3259 {
3260 pPatchTargetGC = patmGuestGCPtrToPatchGCPtr(pVM, pPatch, pInstrGC);
3261 if (pPatchTargetGC)
3262 {
3263 uint32_t offsetPatch = pPatchTargetGC - pVM->patm.s.pPatchMemGC;
3264 PRECPATCHTOGUEST pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatch->Patch2GuestAddrTree, offsetPatch, false);
3265 Assert(pPatchToGuestRec);
3266
3267 pPatchToGuestRec->fJumpTarget = true;
3268 Assert(pPatchTargetGC != pPatch->pPrivInstrGC);
3269 Log(("patmCreateTrampoline: generating jump to code inside patch at %VGv\n", pPatch->pPrivInstrGC));
3270 pPatch->flags |= PATMFL_EXTERNAL_JUMP_INSIDE;
3271 break;
3272 }
3273 }
3274 }
3275 }
3276 }
3277 AssertReturn(pPatchPage && pPatchTargetGC, VERR_PATCHING_REFUSED);
3278
3279 pPatch->nrPatch2GuestRecs = 0;
3280 pPatch->pPatchBlockOffset = pVM->patm.s.offPatchMem;
3281 pPatch->uCurPatchOffset = 0;
3282
3283 /** @note Set the PATM interrupt flag here; it was cleared before the patched call. (!!!) */
3284 rc = patmPatchGenSetPIF(pVM, pPatch, pInstrGC);
3285 if (VBOX_FAILURE(rc))
3286 goto failure;
3287
3288#ifdef VBOX_WITH_STATISTICS
3289 rc = patmPatchGenStats(pVM, pPatch, pInstrGC);
3290 if (VBOX_FAILURE(rc))
3291 goto failure;
3292#endif
3293
3294 rc = patmPatchGenPatchJump(pVM, pPatch, pInstrGC, pPatchTargetGC);
3295 if (VBOX_FAILURE(rc))
3296 goto failure;
3297
3298 /*
3299 * Insert into patch to guest lookup tree
3300 */
3301 LogFlow(("Insert %VGv patch offset %VGv\n", pPatchRec->patch.pPrivInstrGC, pPatch->pPatchBlockOffset));
3302 pPatchRec->CoreOffset.Key = pPatch->pPatchBlockOffset;
3303 rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, &pPatchRec->CoreOffset);
3304 AssertMsg(rc, ("RTAvloGCPtrInsert failed for %x\n", pPatchRec->CoreOffset.Key));
3305 if (!rc)
3306 {
3307 rc = VERR_PATCHING_REFUSED;
3308 goto failure;
3309 }
3310
3311 /* size of patch block */
3312 pPatch->cbPatchBlockSize = pPatch->uCurPatchOffset;
3313
3314 /* Update free pointer in patch memory. */
3315 pVM->patm.s.offPatchMem += pPatch->cbPatchBlockSize;
3316 /* Round to next 8 byte boundary */
3317 pVM->patm.s.offPatchMem = RT_ALIGN_32(pVM->patm.s.offPatchMem, 8);
3318
3319 /* There's no jump from guest to patch code. */
3320 pPatch->cbPatchJump = 0;
3321
3322 /* Enable the patch. */
3323 pPatch->uState = PATCH_ENABLED;
3324 /* We allow this patch to be called as a function. */
3325 pPatch->flags |= PATMFL_CALLABLE_AS_FUNCTION;
3326 STAM_COUNTER_INC(&pVM->patm.s.StatInstalledTrampoline);
3327 return VINF_SUCCESS;
3328
3329failure:
3330 if (pPatchRec->CoreOffset.Key)
3331 RTAvloGCPtrRemove(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, pPatchRec->CoreOffset.Key);
3332
3333 patmEmptyTree(pVM, &pPatch->FixupTree);
3334 pPatch->nrFixups = 0;
3335
3336 patmEmptyTree(pVM, &pPatch->JumpTree);
3337 pPatch->nrJumpRecs = 0;
3338
3339 patmEmptyTree(pVM, &pPatch->pTempInfo->IllegalInstrTree);
3340 pPatch->pTempInfo->nrIllegalInstr = 0;
3341
3342 /* Turn this cli patch into a dummy. */
3343 pPatch->uState = PATCH_REFUSED;
3344 pPatch->pPatchBlockOffset = 0;
3345
3346 // Give back the patch memory we no longer need
3347 Assert(orgOffsetPatchMem != (uint32_t)~0);
3348 pVM->patm.s.offPatchMem = orgOffsetPatchMem;
3349
3350 return rc;
3351}
3352
3353
3354/**
3355 * Patch branch target function for call/jump at specified location.
3356 * (in responds to a VINF_PATM_DUPLICATE_FUNCTION GC exit reason)
3357 *
3358 * @returns VBox status code.
3359 * @param pVM The VM to operate on.
3360 * @param pCtx Guest context
3361 *
3362 */
3363PATMR3DECL(int) PATMR3DuplicateFunctionRequest(PVM pVM, PCPUMCTX pCtx)
3364{
3365 RTGCPTR pBranchTarget, pPage;
3366 int rc;
3367 RTGCPTR pPatchTargetGC = 0;
3368
3369 pBranchTarget = pCtx->edx;
3370 pBranchTarget = SELMToFlat(pVM, pCtx->eflags, pCtx->cs, &pCtx->csHid, pBranchTarget);
3371
3372 /* First we check if the duplicate function target lies in some existing function patch already. Will save some space. */
3373 pPage = pBranchTarget & PAGE_BASE_GC_MASK;
3374
3375 PPATMPATCHPAGE pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, (RTGCPTR)pPage);
3376 if (pPatchPage)
3377 {
3378 uint32_t i;
3379
3380 for (i=0;i<pPatchPage->cCount;i++)
3381 {
3382 if (pPatchPage->aPatch[i])
3383 {
3384 PPATCHINFO pPatch = pPatchPage->aPatch[i];
3385
3386 if ( (pPatch->flags & PATMFL_DUPLICATE_FUNCTION)
3387 && pPatch->uState == PATCH_ENABLED)
3388 {
3389 pPatchTargetGC = patmGuestGCPtrToPatchGCPtr(pVM, pPatch, pBranchTarget);
3390 if (pPatchTargetGC)
3391 {
3392 STAM_COUNTER_INC(&pVM->patm.s.StatDuplicateUseExisting);
3393 break;
3394 }
3395 }
3396 }
3397 }
3398 }
3399
3400 if (pPatchTargetGC)
3401 {
3402 /* Create a trampoline that also sets PATM_INTERRUPTFLAG. */
3403 rc = PATMR3InstallPatch(pVM, pBranchTarget, PATMFL_CODE32 | PATMFL_TRAMPOLINE);
3404 }
3405 else
3406 {
3407 rc = PATMR3InstallPatch(pVM, pBranchTarget, PATMFL_CODE32 | PATMFL_DUPLICATE_FUNCTION);
3408 }
3409
3410 if (rc == VINF_SUCCESS)
3411 {
3412 pPatchTargetGC = PATMR3QueryPatchGCPtr(pVM, pBranchTarget);
3413 Assert(pPatchTargetGC);
3414 }
3415
3416 if (pPatchTargetGC)
3417 {
3418 pCtx->eax = pPatchTargetGC;
3419 pCtx->eax = pCtx->eax - (RTGCUINTPTR)pVM->patm.s.pPatchMemGC; /* make it relative */
3420 }
3421 else
3422 {
3423 /* We add a dummy entry into the lookup cache so we won't get bombarded with the same requests over and over again. */
3424 pCtx->eax = 0;
3425 STAM_COUNTER_INC(&pVM->patm.s.StatDuplicateREQFailed);
3426 }
3427 Assert(PATMIsPatchGCAddr(pVM, pCtx->edi));
3428 rc = PATMAddBranchToLookupCache(pVM, pCtx->edi, pBranchTarget, pCtx->eax);
3429 AssertRC(rc);
3430
3431 pCtx->eip += PATM_ILLEGAL_INSTR_SIZE;
3432 STAM_COUNTER_INC(&pVM->patm.s.StatDuplicateREQSuccess);
3433 return VINF_SUCCESS;
3434}
3435
3436/**
3437 * Replaces a function call by a call to an existing function duplicate (or jmp -> jmp)
3438 *
3439 * @returns VBox status code.
3440 * @param pVM The VM to operate on.
3441 * @param pCpu Disassembly CPU structure ptr
3442 * @param pInstrGC Guest context point to privileged instruction
3443 * @param pPatch Patch record
3444 *
3445 */
3446static int patmReplaceFunctionCall(PVM pVM, DISCPUSTATE *pCpu, RTGCPTR pInstrGC, PPATCHINFO pPatch)
3447{
3448 int rc = VERR_PATCHING_REFUSED;
3449 DISCPUSTATE cpu;
3450 RTGCPTR pTargetGC;
3451 PPATMPATCHREC pPatchFunction;
3452 uint32_t opsize;
3453 bool disret;
3454#ifdef LOG_ENABLED
3455 char szOutput[256];
3456#endif
3457
3458 Assert(pPatch->flags & PATMFL_REPLACE_FUNCTION_CALL);
3459 Assert((pCpu->pCurInstr->opcode == OP_CALL || pCpu->pCurInstr->opcode == OP_JMP) && pCpu->opsize == SIZEOF_NEARJUMP32);
3460
3461 if ((pCpu->pCurInstr->opcode != OP_CALL && pCpu->pCurInstr->opcode != OP_JMP) || pCpu->opsize != SIZEOF_NEARJUMP32)
3462 {
3463 rc = VERR_PATCHING_REFUSED;
3464 goto failure;
3465 }
3466
3467 pTargetGC = PATMResolveBranch(pCpu, pInstrGC);
3468 if (pTargetGC == 0)
3469 {
3470 Log(("We don't support far jumps here!! (%08X)\n", pCpu->param1.flags));
3471 rc = VERR_PATCHING_REFUSED;
3472 goto failure;
3473 }
3474
3475 pPatchFunction = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pTargetGC);
3476 if (pPatchFunction == NULL)
3477 {
3478 for(;;)
3479 {
3480 /* It could be an indirect call (call -> jmp dest).
3481 * Note that it's dangerous to assume the jump will never change...
3482 */
3483 uint8_t *pTmpInstrHC;
3484
3485 pTmpInstrHC = PATMGCVirtToHCVirt(pVM, pPatch, pTargetGC);
3486 Assert(pTmpInstrHC);
3487 if (pTmpInstrHC == 0)
3488 break;
3489
3490 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
3491 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pTargetGC, pTmpInstrHC, &opsize, NULL);
3492 if (disret == false || cpu.pCurInstr->opcode != OP_JMP)
3493 break;
3494
3495 pTargetGC = PATMResolveBranch(&cpu, pTargetGC);
3496 if (pTargetGC == 0)
3497 {
3498 break;
3499 }
3500
3501 pPatchFunction = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pTargetGC);
3502 break;
3503 }
3504 if (pPatchFunction == 0)
3505 {
3506 AssertMsgFailed(("Unable to find duplicate function %VGv\n", pTargetGC));
3507 rc = VERR_PATCHING_REFUSED;
3508 goto failure;
3509 }
3510 }
3511
3512 // make a copy of the guest code bytes that will be overwritten
3513 pPatch->cbPatchJump = SIZEOF_NEARJUMP32;
3514
3515 rc = PGMPhysReadGCPtr(pVM, pPatch->aPrivInstr, pPatch->pPrivInstrGC, pPatch->cbPatchJump);
3516 AssertRC(rc);
3517
3518 /* Now replace the original call in the guest code */
3519 rc = patmGenCallToPatch(pVM, pPatch, PATCHCODE_PTR_GC(&pPatchFunction->patch), true);
3520 AssertRC(rc);
3521 if (VBOX_FAILURE(rc))
3522 goto failure;
3523
3524 /* Lowest and highest address for write monitoring. */
3525 pPatch->pInstrGCLowest = pInstrGC;
3526 pPatch->pInstrGCHighest = pInstrGC + pCpu->opsize;
3527
3528#ifdef LOG_ENABLED
3529 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
3530 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput);
3531 Log(("Call patch: %s", szOutput));
3532#endif
3533
3534 Log(("Successfully installed function replacement patch at %VGv\n", pInstrGC));
3535
3536 pPatch->uState = PATCH_ENABLED;
3537 return VINF_SUCCESS;
3538
3539failure:
3540 /* Turn this patch into a dummy. */
3541 pPatch->uState = PATCH_REFUSED;
3542
3543 return rc;
3544}
3545
3546/**
3547 * Replace the address in an MMIO instruction with the cached version.
3548 *
3549 * @returns VBox status code.
3550 * @param pVM The VM to operate on.
3551 * @param pInstrGC Guest context point to privileged instruction
3552 * @param pCpu Disassembly CPU structure ptr
3553 * @param pPatch Patch record
3554 *
3555 * @note returns failure if patching is not allowed or possible
3556 *
3557 */
3558static int patmPatchMMIOInstr(PVM pVM, RTGCPTR pInstrGC, DISCPUSTATE *pCpu, PPATCHINFO pPatch)
3559{
3560 uint8_t *pPB;
3561 int rc = VERR_PATCHING_REFUSED;
3562#ifdef LOG_ENABLED
3563 DISCPUSTATE cpu;
3564 uint32_t opsize;
3565 bool disret;
3566 char szOutput[256];
3567#endif
3568
3569 Assert(pVM->patm.s.mmio.pCachedData);
3570 if (!pVM->patm.s.mmio.pCachedData)
3571 goto failure;
3572
3573 if (pCpu->param2.flags != USE_DISPLACEMENT32)
3574 goto failure;
3575
3576 pPB = pPatch->pPrivInstrHC;
3577
3578 /* Add relocation record for cached data access. */
3579 if (patmPatchAddReloc32(pVM, pPatch, &pPB[pCpu->opsize - sizeof(RTGCPTR)], FIXUP_ABSOLUTE, pPatch->pPrivInstrGC, pVM->patm.s.mmio.pCachedData) != VINF_SUCCESS)
3580 {
3581 Log(("Relocation failed for cached mmio address!!\n"));
3582 return VERR_PATCHING_REFUSED;
3583 }
3584#ifdef LOG_ENABLED
3585 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
3586 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput);
3587 Log(("MMIO patch old instruction: %s", szOutput));
3588#endif
3589
3590 /* Save original instruction. */
3591 rc = PGMPhysReadGCPtr(pVM, pPatch->aPrivInstr, pPatch->pPrivInstrGC, pPatch->cbPrivInstr);
3592 AssertRC(rc);
3593
3594 pPatch->cbPatchJump = pPatch->cbPrivInstr; /* bit of a misnomer in this case; size of replacement instruction. */
3595
3596 /* Replace address with that of the cached item. */
3597 rc = PGMPhysWriteGCPtrDirty(pVM, pInstrGC + pCpu->opsize - sizeof(RTGCPTR), &pVM->patm.s.mmio.pCachedData, sizeof(RTGCPTR));
3598 AssertRC(rc);
3599 if (VBOX_FAILURE(rc))
3600 {
3601 goto failure;
3602 }
3603
3604#ifdef LOG_ENABLED
3605 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
3606 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput);
3607 Log(("MMIO patch: %s", szOutput));
3608#endif
3609 pVM->patm.s.mmio.pCachedData = 0;
3610 pVM->patm.s.mmio.GCPhys = 0;
3611 pPatch->uState = PATCH_ENABLED;
3612 return VINF_SUCCESS;
3613
3614failure:
3615 /* Turn this patch into a dummy. */
3616 pPatch->uState = PATCH_REFUSED;
3617
3618 return rc;
3619}
3620
3621
3622/**
3623 * Replace the address in an MMIO instruction with the cached version. (instruction is part of an existing patch)
3624 *
3625 * @returns VBox status code.
3626 * @param pVM The VM to operate on.
3627 * @param pInstrGC Guest context point to privileged instruction
3628 * @param pPatch Patch record
3629 *
3630 * @note returns failure if patching is not allowed or possible
3631 *
3632 */
3633static int patmPatchPATMMMIOInstr(PVM pVM, RTGCPTR pInstrGC, PPATCHINFO pPatch)
3634{
3635 DISCPUSTATE cpu;
3636 uint32_t opsize;
3637 bool disret;
3638 uint8_t *pInstrHC;
3639#ifdef LOG_ENABLED
3640 char szOutput[256];
3641#endif
3642
3643 AssertReturn(pVM->patm.s.mmio.pCachedData, VERR_INVALID_PARAMETER);
3644
3645 /* Convert GC to HC address. */
3646 pInstrHC = patmPatchGCPtr2PatchHCPtr(pVM, pInstrGC);
3647 AssertReturn(pInstrHC, VERR_PATCHING_REFUSED);
3648
3649 /* Disassemble mmio instruction. */
3650 cpu.mode = pPatch->uOpMode;
3651 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pInstrGC, pInstrHC, &opsize, NULL);
3652 if (disret == false)
3653 {
3654 Log(("Disassembly failed (probably page not present) -> return to caller\n"));
3655 return VERR_PATCHING_REFUSED;
3656 }
3657
3658 AssertMsg(opsize <= MAX_INSTR_SIZE, ("privileged instruction too big %d!!\n", opsize));
3659 if (opsize > MAX_INSTR_SIZE)
3660 return VERR_PATCHING_REFUSED;
3661 if (cpu.param2.flags != USE_DISPLACEMENT32)
3662 return VERR_PATCHING_REFUSED;
3663
3664 /* Add relocation record for cached data access. */
3665 if (patmPatchAddReloc32(pVM, pPatch, &pInstrHC[cpu.opsize - sizeof(RTGCPTR)], FIXUP_ABSOLUTE) != VINF_SUCCESS)
3666 {
3667 Log(("Relocation failed for cached mmio address!!\n"));
3668 return VERR_PATCHING_REFUSED;
3669 }
3670 /* Replace address with that of the cached item. */
3671 *(RTGCPTR *)&pInstrHC[cpu.opsize - sizeof(RTGCPTR)] = pVM->patm.s.mmio.pCachedData;
3672
3673 /* Lowest and highest address for write monitoring. */
3674 pPatch->pInstrGCLowest = pInstrGC;
3675 pPatch->pInstrGCHighest = pInstrGC + cpu.opsize;
3676
3677#ifdef LOG_ENABLED
3678 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
3679 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pInstrGC, pInstrHC, &opsize, szOutput);
3680 Log(("MMIO patch: %s", szOutput));
3681#endif
3682
3683 pVM->patm.s.mmio.pCachedData = 0;
3684 pVM->patm.s.mmio.GCPhys = 0;
3685 return VINF_SUCCESS;
3686}
3687
3688/**
3689 * Activates an int3 patch
3690 *
3691 * @returns VBox status code.
3692 * @param pVM The VM to operate on.
3693 * @param pPatch Patch record
3694 */
3695static int patmActivateInt3Patch(PVM pVM, PPATCHINFO pPatch)
3696{
3697 uint8_t ASMInt3 = 0xCC;
3698 int rc;
3699
3700 Assert(pPatch->flags & (PATMFL_INT3_REPLACEMENT|PATMFL_INT3_REPLACEMENT_BLOCK));
3701 Assert(pPatch->uState != PATCH_ENABLED);
3702
3703 /* Replace first opcode byte with 'int 3'. */
3704 rc = PGMPhysWriteGCPtrDirty(pVM, pPatch->pPrivInstrGC, &ASMInt3, sizeof(ASMInt3));
3705 AssertRC(rc);
3706
3707 pPatch->cbPatchJump = sizeof(ASMInt3);
3708
3709 return rc;
3710}
3711
3712/**
3713 * Deactivates an int3 patch
3714 *
3715 * @returns VBox status code.
3716 * @param pVM The VM to operate on.
3717 * @param pPatch Patch record
3718 */
3719static int patmDeactivateInt3Patch(PVM pVM, PPATCHINFO pPatch)
3720{
3721 uint8_t ASMInt3 = 0xCC;
3722 int rc;
3723
3724 Assert(pPatch->flags & (PATMFL_INT3_REPLACEMENT|PATMFL_INT3_REPLACEMENT_BLOCK));
3725 Assert(pPatch->uState == PATCH_ENABLED || pPatch->uState == PATCH_DIRTY);
3726
3727 /* Restore first opcode byte. */
3728 rc = PGMPhysWriteGCPtrDirty(pVM, pPatch->pPrivInstrGC, pPatch->aPrivInstr, sizeof(ASMInt3));
3729 AssertRC(rc);
3730 return rc;
3731}
3732
3733/**
3734 * Replace an instruction with a breakpoint (0xCC), that is handled dynamically in the guest context.
3735 *
3736 * @returns VBox status code.
3737 * @param pVM The VM to operate on.
3738 * @param pInstrGC Guest context point to privileged instruction
3739 * @param pInstrHC Host context point to privileged instruction
3740 * @param pCpu Disassembly CPU structure ptr
3741 * @param pPatch Patch record
3742 *
3743 * @note returns failure if patching is not allowed or possible
3744 *
3745 */
3746PATMR3DECL(int) PATMR3PatchInstrInt3(PVM pVM, RTGCPTR pInstrGC, R3PTRTYPE(uint8_t *) pInstrHC, DISCPUSTATE *pCpu, PPATCHINFO pPatch)
3747{
3748 uint8_t ASMInt3 = 0xCC;
3749 int rc;
3750
3751 /** @note Do not use patch memory here! It might called during patch installation too. */
3752
3753#ifdef LOG_ENABLED
3754 DISCPUSTATE cpu;
3755 char szOutput[256];
3756 uint32_t opsize;
3757
3758 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
3759 PATMR3DISInstr(pVM, pPatch, &cpu, pInstrGC, pInstrHC, &opsize, szOutput);
3760 Log(("PATMR3PatchInstrInt3: %s", szOutput));
3761#endif
3762
3763 /* Save the original instruction. */
3764 rc = PGMPhysReadGCPtr(pVM, pPatch->aPrivInstr, pPatch->pPrivInstrGC, pPatch->cbPrivInstr);
3765 AssertRC(rc);
3766 pPatch->cbPatchJump = sizeof(ASMInt3); /* bit of a misnomer in this case; size of replacement instruction. */
3767
3768 pPatch->flags |= PATMFL_INT3_REPLACEMENT;
3769
3770 /* Replace first opcode byte with 'int 3'. */
3771 rc = patmActivateInt3Patch(pVM, pPatch);
3772 if (VBOX_FAILURE(rc))
3773 goto failure;
3774
3775 /* Lowest and highest address for write monitoring. */
3776 pPatch->pInstrGCLowest = pInstrGC;
3777 pPatch->pInstrGCHighest = pInstrGC + pCpu->opsize;
3778
3779 pPatch->uState = PATCH_ENABLED;
3780 return VINF_SUCCESS;
3781
3782failure:
3783 /* Turn this patch into a dummy. */
3784 return VERR_PATCHING_REFUSED;
3785}
3786
3787#ifdef PATM_RESOLVE_CONFLICTS_WITH_JUMP_PATCHES
3788/**
3789 * Patch a jump instruction at specified location
3790 *
3791 * @returns VBox status code.
3792 * @param pVM The VM to operate on.
3793 * @param pInstrGC Guest context point to privileged instruction
3794 * @param pInstrHC Host context point to privileged instruction
3795 * @param pCpu Disassembly CPU structure ptr
3796 * @param pPatchRec Patch record
3797 *
3798 * @note returns failure if patching is not allowed or possible
3799 *
3800 */
3801int patmPatchJump(PVM pVM, RTGCPTR pInstrGC, R3PTRTYPE(uint8_t *) pInstrHC, DISCPUSTATE *pCpu, PPATMPATCHREC pPatchRec)
3802{
3803 PPATCHINFO pPatch = &pPatchRec->patch;
3804 int rc = VERR_PATCHING_REFUSED;
3805#ifdef LOG_ENABLED
3806 bool disret;
3807 DISCPUSTATE cpu;
3808 uint32_t opsize;
3809 char szOutput[256];
3810#endif
3811
3812 pPatch->pPatchBlockOffset = 0; /* doesn't use patch memory */
3813 pPatch->uCurPatchOffset = 0;
3814 pPatch->cbPatchBlockSize = 0;
3815 pPatch->flags |= PATMFL_SINGLE_INSTRUCTION;
3816
3817 /*
3818 * Instruction replacements such as these should never be interrupted. I've added code to EM.cpp to
3819 * make sure this never happens. (unless a trap is triggered (intentionally or not))
3820 */
3821 switch (pCpu->pCurInstr->opcode)
3822 {
3823 case OP_JO:
3824 case OP_JNO:
3825 case OP_JC:
3826 case OP_JNC:
3827 case OP_JE:
3828 case OP_JNE:
3829 case OP_JBE:
3830 case OP_JNBE:
3831 case OP_JS:
3832 case OP_JNS:
3833 case OP_JP:
3834 case OP_JNP:
3835 case OP_JL:
3836 case OP_JNL:
3837 case OP_JLE:
3838 case OP_JNLE:
3839 case OP_JMP:
3840 Assert(pPatch->flags & PATMFL_JUMP_CONFLICT);
3841 Assert(pCpu->param1.flags & USE_IMMEDIATE32_REL);
3842 if (!(pCpu->param1.flags & USE_IMMEDIATE32_REL))
3843 goto failure;
3844
3845 Assert(pCpu->opsize == SIZEOF_NEARJUMP32 || pCpu->opsize == SIZEOF_NEAR_COND_JUMP32);
3846 if (pCpu->opsize != SIZEOF_NEARJUMP32 && pCpu->opsize != SIZEOF_NEAR_COND_JUMP32)
3847 goto failure;
3848
3849 if (PAGE_ADDRESS(pInstrGC) != PAGE_ADDRESS(pInstrGC + pCpu->opsize))
3850 {
3851 STAM_COUNTER_INC(&pVM->patm.s.StatPageBoundaryCrossed);
3852 AssertMsgFailed(("Patch jump would cross page boundary -> refuse!!\n"));
3853 rc = VERR_PATCHING_REFUSED;
3854 goto failure;
3855 }
3856
3857 break;
3858
3859 default:
3860 goto failure;
3861 }
3862
3863 // make a copy of the guest code bytes that will be overwritten
3864 Assert(pCpu->opsize <= sizeof(pPatch->aPrivInstr));
3865 Assert(pCpu->opsize >= SIZEOF_NEARJUMP32);
3866 pPatch->cbPatchJump = pCpu->opsize;
3867
3868 rc = PGMPhysReadGCPtr(pVM, pPatch->aPrivInstr, pPatch->pPrivInstrGC, pPatch->cbPatchJump);
3869 AssertRC(rc);
3870
3871 /* Now insert a jump in the guest code. */
3872 /*
3873 * A conflict jump patch needs to be treated differently; we'll just replace the relative jump address with one that
3874 * references the target instruction in the conflict patch.
3875 */
3876 RTGCPTR pJmpDest = PATMR3GuestGCPtrToPatchGCPtr(pVM, pInstrGC + pCpu->opsize + (int32_t)pCpu->param1.parval);
3877
3878 AssertMsg(pJmpDest, ("PATMR3GuestGCPtrToPatchGCPtr failed for %VGv\n", pInstrGC + pCpu->opsize + (int32_t)pCpu->param1.parval));
3879 pPatch->pPatchJumpDestGC = pJmpDest;
3880
3881 rc = patmGenJumpToPatch(pVM, pPatch, true);
3882 AssertRC(rc);
3883 if (VBOX_FAILURE(rc))
3884 goto failure;
3885
3886 pPatch->flags |= PATMFL_MUST_INSTALL_PATCHJMP;
3887
3888#ifdef LOG_ENABLED
3889 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
3890 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC, pPatch->pPrivInstrHC, &opsize, szOutput);
3891 Log(("%s patch: %s", patmGetInstructionString(pPatch->opcode, pPatch->flags), szOutput));
3892#endif
3893
3894 Log(("Successfully installed %s patch at %VGv\n", patmGetInstructionString(pPatch->opcode, pPatch->flags), pInstrGC));
3895
3896 STAM_COUNTER_INC(&pVM->patm.s.StatInstalledJump);
3897
3898 /* Lowest and highest address for write monitoring. */
3899 pPatch->pInstrGCLowest = pInstrGC;
3900 pPatch->pInstrGCHighest = pInstrGC + pPatch->cbPatchJump;
3901
3902 pPatch->uState = PATCH_ENABLED;
3903 return VINF_SUCCESS;
3904
3905failure:
3906 /* Turn this cli patch into a dummy. */
3907 pPatch->uState = PATCH_REFUSED;
3908
3909 return rc;
3910}
3911#endif /* PATM_RESOLVE_CONFLICTS_WITH_JUMP_PATCHES */
3912
3913
3914/**
3915 * Gives hint to PATM about supervisor guest instructions
3916 *
3917 * @returns VBox status code.
3918 * @param pVM The VM to operate on.
3919 * @param pInstr Guest context point to privileged instruction
3920 * @param flags Patch flags
3921 */
3922PATMR3DECL(int) PATMR3AddHint(PVM pVM, RTGCPTR pInstrGC, uint32_t flags)
3923{
3924 Assert(pInstrGC);
3925 Assert(flags == PATMFL_CODE32);
3926
3927 Log(("PATMR3AddHint %VGv\n", pInstrGC));
3928 return PATMR3InstallPatch(pVM, pInstrGC, PATMFL_CODE32 | PATMFL_INSTR_HINT);
3929}
3930
3931/**
3932 * Patch privileged instruction at specified location
3933 *
3934 * @returns VBox status code.
3935 * @param pVM The VM to operate on.
3936 * @param pInstr Guest context point to privileged instruction (0:32 flat address)
3937 * @param flags Patch flags
3938 *
3939 * @note returns failure if patching is not allowed or possible
3940 */
3941PATMR3DECL(int) PATMR3InstallPatch(PVM pVM, RTGCPTR pInstrGC, uint64_t flags)
3942{
3943 DISCPUSTATE cpu;
3944 R3PTRTYPE(uint8_t *) pInstrHC;
3945 uint32_t opsize;
3946 PPATMPATCHREC pPatchRec;
3947 PCPUMCTX pCtx = 0;
3948 bool disret;
3949 int rc;
3950
3951 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)))
3952 {
3953 AssertFailed();
3954 return VERR_INVALID_PARAMETER;
3955 }
3956
3957 if (PATMIsEnabled(pVM) == false)
3958 return VERR_PATCHING_REFUSED;
3959
3960 /* Test for patch conflict only with patches that actually change guest code. */
3961 if (!(flags & (PATMFL_GUEST_SPECIFIC|PATMFL_IDTHANDLER|PATMFL_INTHANDLER|PATMFL_TRAMPOLINE)))
3962 {
3963 PPATCHINFO pConflictPatch = PATMFindActivePatchByEntrypoint(pVM, pInstrGC);
3964 AssertReleaseMsg(pConflictPatch == 0, ("Unable to patch overwritten instruction at %VGv (%VGv)\n", pInstrGC, pConflictPatch->pPrivInstrGC));
3965 if (pConflictPatch != 0)
3966 return VERR_PATCHING_REFUSED;
3967 }
3968
3969 if (!(flags & PATMFL_CODE32))
3970 {
3971 /** @todo Only 32 bits code right now */
3972 AssertMsgFailed(("PATMR3InstallPatch: We don't support 16 bits code at this moment!!\n"));
3973 return VERR_NOT_IMPLEMENTED;
3974 }
3975
3976 /* We ran out of patch memory; don't bother anymore. */
3977 if (pVM->patm.s.fOutOfMemory == true)
3978 return VERR_PATCHING_REFUSED;
3979
3980 /* Make sure the code selector is wide open; otherwise refuse. */
3981 CPUMQueryGuestCtxPtr(pVM, &pCtx);
3982 if (CPUMGetGuestCPL(pVM, CPUMCTX2CORE(pCtx)) == 0)
3983 {
3984 RTGCPTR pInstrGCFlat = SELMToFlat(pVM, pCtx->eflags, pCtx->cs, &pCtx->csHid, pInstrGC);
3985 if (pInstrGCFlat != pInstrGC)
3986 {
3987 Log(("PATMR3InstallPatch: code selector not wide open: %04x:%VGv != %VGv eflags=%08x\n", pCtx->cs, pInstrGCFlat, pInstrGC, pCtx->eflags.u32));
3988 return VERR_PATCHING_REFUSED;
3989 }
3990 }
3991
3992 /** @note the OpenBSD specific check will break if we allow additional patches to be installed (int 3)) */
3993 if (!(flags & PATMFL_GUEST_SPECIFIC))
3994 {
3995 /* New code. Make sure CSAM has a go at it first. */
3996 CSAMR3CheckCode(pVM, pInstrGC);
3997 }
3998
3999 /** @note obsolete */
4000 if ( PATMIsPatchGCAddr(pVM, pInstrGC)
4001 && (flags & PATMFL_MMIO_ACCESS))
4002 {
4003 RTGCUINTPTR offset;
4004 void *pvPatchCoreOffset;
4005
4006 /* Find the patch record. */
4007 offset = pInstrGC - pVM->patm.s.pPatchMemGC;
4008 pvPatchCoreOffset = RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, offset, false);
4009 if (pvPatchCoreOffset == NULL)
4010 {
4011 AssertMsgFailed(("PATMR3InstallPatch: patch not found at address %VGv!!\n", pInstrGC));
4012 return VERR_PATCH_NOT_FOUND; //fatal error
4013 }
4014 pPatchRec = PATM_PATCHREC_FROM_COREOFFSET(pvPatchCoreOffset);
4015
4016 return patmPatchPATMMMIOInstr(pVM, pInstrGC, &pPatchRec->patch);
4017 }
4018
4019 AssertReturn(!PATMIsPatchGCAddr(pVM, pInstrGC), VERR_PATCHING_REFUSED);
4020
4021 pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC);
4022 if (pPatchRec)
4023 {
4024 Assert(!(flags & PATMFL_TRAMPOLINE));
4025
4026 /* Hints about existing patches are ignored. */
4027 if (flags & PATMFL_INSTR_HINT)
4028 return VERR_PATCHING_REFUSED;
4029
4030 if (pPatchRec->patch.uState == PATCH_DISABLE_PENDING)
4031 {
4032 Log(("PATMR3InstallPatch: disable operation is pending for patch at %VGv\n", pPatchRec->patch.pPrivInstrGC));
4033 PATMR3DisablePatch(pVM, pPatchRec->patch.pPrivInstrGC);
4034 Assert(pPatchRec->patch.uState == PATCH_DISABLED);
4035 }
4036
4037 if (pPatchRec->patch.uState == PATCH_DISABLED)
4038 {
4039 /* A patch, for which we previously received a hint, will be enabled and turned into a normal patch. */
4040 if (pPatchRec->patch.flags & PATMFL_INSTR_HINT)
4041 {
4042 Log(("Enabling HINTED patch %VGv\n", pInstrGC));
4043 pPatchRec->patch.flags &= ~PATMFL_INSTR_HINT;
4044 }
4045 else
4046 Log(("Enabling patch %VGv again\n", pInstrGC));
4047
4048 /** @todo we shouldn't disable and enable patches too often (it's relatively cheap, but pointless if it always happens) */
4049 rc = PATMR3EnablePatch(pVM, pInstrGC);
4050 if (VBOX_SUCCESS(rc))
4051 return VWRN_PATCH_ENABLED;
4052
4053 return rc;
4054 }
4055 if ( pPatchRec->patch.uState == PATCH_ENABLED
4056 || pPatchRec->patch.uState == PATCH_DIRTY)
4057 {
4058 /*
4059 * The patch might have been overwritten.
4060 */
4061 STAM_COUNTER_INC(&pVM->patm.s.StatOverwritten);
4062 if (pPatchRec->patch.uState != PATCH_REFUSED && pPatchRec->patch.uState != PATCH_UNUSABLE)
4063 {
4064 /* Patch must have been overwritten; remove it and pretend nothing happened. */
4065 Log(("Patch an existing patched instruction?!? (%VGv)\n", pInstrGC));
4066 if (pPatchRec->patch.flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_IDTHANDLER|PATMFL_MMIO_ACCESS|PATMFL_INT3_REPLACEMENT|PATMFL_INT3_REPLACEMENT_BLOCK))
4067 {
4068 if (flags & PATMFL_IDTHANDLER)
4069 pPatchRec->patch.flags |= (flags & (PATMFL_IDTHANDLER|PATMFL_TRAPHANDLER|PATMFL_INTHANDLER)); /* update the type */
4070
4071 return VERR_PATM_ALREADY_PATCHED; /* already done once */
4072 }
4073 }
4074 PATMR3RemovePatch(pVM, pInstrGC);
4075 }
4076 else
4077 {
4078 AssertMsg(pPatchRec->patch.uState == PATCH_REFUSED || pPatchRec->patch.uState == PATCH_UNUSABLE, ("Patch an existing patched instruction?!? (%VGv, state=%d)\n", pInstrGC, pPatchRec->patch.uState));
4079 /* already tried it once! */
4080 return VERR_PATCHING_REFUSED;
4081 }
4082 }
4083
4084 rc = MMHyperAlloc(pVM, sizeof(PATMPATCHREC), 0, MM_TAG_PATM_PATCH, (void **)&pPatchRec);
4085 if (VBOX_FAILURE(rc))
4086 {
4087 Log(("Out of memory!!!!\n"));
4088 return VERR_NO_MEMORY;
4089 }
4090 pPatchRec->Core.Key = pInstrGC;
4091 pPatchRec->patch.uState = PATCH_REFUSED; //default
4092 rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTree, &pPatchRec->Core);
4093 Assert(rc);
4094
4095 RTGCPHYS GCPhys;
4096 rc = PGMGstGetPage(pVM, pInstrGC, NULL, &GCPhys);
4097 if (rc != VINF_SUCCESS)
4098 {
4099 Log(("PGMGstGetPage failed with %Vrc\n", rc));
4100 return rc;
4101 }
4102 /* Disallow patching instructions inside ROM code; complete function duplication is allowed though. */
4103 if ( !(flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_TRAMPOLINE))
4104 && !PGMPhysIsGCPhysNormal(pVM, GCPhys))
4105 {
4106 Log(("Code at %RGv (phys %RGp) is in a ROM, MMIO or invalid page - refused\n", pInstrGC, GCPhys));
4107 return VERR_PATCHING_REFUSED;
4108 }
4109 GCPhys = GCPhys + (pInstrGC & PAGE_OFFSET_MASK);
4110 rc = PGMPhysGCPhys2HCPtr(pVM, GCPhys, MAX_INSTR_SIZE, (void **)&pInstrHC);
4111 AssertRCReturn(rc, rc);
4112
4113 pPatchRec->patch.pPrivInstrHC = pInstrHC;
4114 pPatchRec->patch.pPrivInstrGC = pInstrGC;
4115 pPatchRec->patch.flags = flags;
4116 pPatchRec->patch.uOpMode = (flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
4117
4118 pPatchRec->patch.pInstrGCLowest = pInstrGC;
4119 pPatchRec->patch.pInstrGCHighest = pInstrGC;
4120
4121 if (!(pPatchRec->patch.flags & (PATMFL_DUPLICATE_FUNCTION | PATMFL_IDTHANDLER | PATMFL_SYSENTER | PATMFL_TRAMPOLINE)))
4122 {
4123 /*
4124 * Close proximity to an unusable patch is a possible hint that this patch would turn out to be dangerous too!
4125 */
4126 PPATMPATCHREC pPatchNear = (PPATMPATCHREC)RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, (pInstrGC + SIZEOF_NEARJUMP32 - 1), false);
4127 if (pPatchNear)
4128 {
4129 if (pPatchNear->patch.uState == PATCH_UNUSABLE && pInstrGC < pPatchNear->patch.pPrivInstrGC && pInstrGC + SIZEOF_NEARJUMP32 > pPatchNear->patch.pPrivInstrGC)
4130 {
4131 Log(("Dangerous patch; would overwrite the ususable patch at %VGv\n", pPatchNear->patch.pPrivInstrGC));
4132
4133 pPatchRec->patch.uState = PATCH_UNUSABLE;
4134 /*
4135 * Leave the new patch active as it's marked unusable; to prevent us from checking it over and over again
4136 */
4137 return VERR_PATCHING_REFUSED;
4138 }
4139 }
4140 }
4141
4142 pPatchRec->patch.pTempInfo = (PPATCHINFOTEMP)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(PATCHINFOTEMP));
4143 if (pPatchRec->patch.pTempInfo == 0)
4144 {
4145 Log(("Out of memory!!!!\n"));
4146 return VERR_NO_MEMORY;
4147 }
4148
4149 cpu.mode = pPatchRec->patch.uOpMode;
4150 disret = PATMR3DISInstr(pVM, &pPatchRec->patch, &cpu, pInstrGC, pInstrHC, &opsize, NULL);
4151 if (disret == false)
4152 {
4153 Log(("Disassembly failed (probably page not present) -> return to caller\n"));
4154 return VERR_PATCHING_REFUSED;
4155 }
4156
4157 AssertMsg(opsize <= MAX_INSTR_SIZE, ("privileged instruction too big %d!!\n", opsize));
4158 if (opsize > MAX_INSTR_SIZE)
4159 {
4160 return VERR_PATCHING_REFUSED;
4161 }
4162
4163 pPatchRec->patch.cbPrivInstr = opsize;
4164 pPatchRec->patch.opcode = cpu.pCurInstr->opcode;
4165
4166 /* Restricted hinting for now. */
4167 Assert(!(flags & PATMFL_INSTR_HINT) || cpu.pCurInstr->opcode == OP_CLI);
4168
4169 /* Allocate statistics slot */
4170 if (pVM->patm.s.uCurrentPatchIdx < PATM_STAT_MAX_COUNTERS)
4171 {
4172 pPatchRec->patch.uPatchIdx = pVM->patm.s.uCurrentPatchIdx++;
4173 }
4174 else
4175 {
4176 Log(("WARNING: Patch index wrap around!!\n"));
4177 pPatchRec->patch.uPatchIdx = PATM_STAT_INDEX_DUMMY;
4178 }
4179
4180 if (pPatchRec->patch.flags & PATMFL_TRAPHANDLER)
4181 {
4182 rc = patmInstallTrapTrampoline(pVM, pInstrGC, pPatchRec);
4183 }
4184 else
4185 if (pPatchRec->patch.flags & (PATMFL_DUPLICATE_FUNCTION ))
4186 {
4187 rc = patmDuplicateFunction(pVM, pInstrGC, pPatchRec);
4188 }
4189 else
4190 if (pPatchRec->patch.flags & PATMFL_TRAMPOLINE)
4191 {
4192 rc = patmCreateTrampoline(pVM, pInstrGC, pPatchRec);
4193 }
4194 else
4195 if (pPatchRec->patch.flags & PATMFL_REPLACE_FUNCTION_CALL)
4196 {
4197 rc = patmReplaceFunctionCall(pVM, &cpu, pInstrGC, &pPatchRec->patch);
4198 }
4199 else
4200 if (pPatchRec->patch.flags & PATMFL_INT3_REPLACEMENT)
4201 {
4202 rc = PATMR3PatchInstrInt3(pVM, pInstrGC, pInstrHC, &cpu, &pPatchRec->patch);
4203 }
4204 else
4205 if (pPatchRec->patch.flags & PATMFL_MMIO_ACCESS)
4206 {
4207 rc = patmPatchMMIOInstr(pVM, pInstrGC, &cpu, &pPatchRec->patch);
4208 }
4209 else
4210 if (pPatchRec->patch.flags & (PATMFL_IDTHANDLER|PATMFL_SYSENTER))
4211 {
4212 if (pPatchRec->patch.flags & PATMFL_SYSENTER)
4213 pPatchRec->patch.flags |= PATMFL_IDTHANDLER; /* we treat a sysenter handler as an IDT handler */
4214
4215 rc = patmIdtHandler(pVM, pInstrGC, pInstrHC, opsize, pPatchRec);
4216#ifdef VBOX_WITH_STATISTICS
4217 if ( rc == VINF_SUCCESS
4218 && (pPatchRec->patch.flags & PATMFL_SYSENTER))
4219 {
4220 pVM->patm.s.uSysEnterPatchIdx = pPatchRec->patch.uPatchIdx;
4221 }
4222#endif
4223 }
4224 else
4225 if (pPatchRec->patch.flags & PATMFL_GUEST_SPECIFIC)
4226 {
4227 switch (cpu.pCurInstr->opcode)
4228 {
4229 case OP_SYSENTER:
4230 case OP_PUSH:
4231 rc = PATMInstallGuestSpecificPatch(pVM, &cpu, pInstrGC, pInstrHC, pPatchRec);
4232 if (rc == VINF_SUCCESS)
4233 {
4234 if (rc == VINF_SUCCESS)
4235 Log(("PATMR3InstallPatch GUEST: %s %VGv code32=%d\n", patmGetInstructionString(pPatchRec->patch.opcode, pPatchRec->patch.flags), pInstrGC, (flags & PATMFL_CODE32) ? 1 : 0));
4236 return rc;
4237 }
4238 break;
4239
4240 default:
4241 rc = VERR_NOT_IMPLEMENTED;
4242 break;
4243 }
4244 }
4245 else
4246 {
4247 switch (cpu.pCurInstr->opcode)
4248 {
4249 case OP_SYSENTER:
4250 rc = PATMInstallGuestSpecificPatch(pVM, &cpu, pInstrGC, pInstrHC, pPatchRec);
4251 if (rc == VINF_SUCCESS)
4252 {
4253 Log(("PATMR3InstallPatch GUEST: %s %VGv code32=%d\n", patmGetInstructionString(pPatchRec->patch.opcode, pPatchRec->patch.flags), pInstrGC, (flags & PATMFL_CODE32) ? 1 : 0));
4254 return VINF_SUCCESS;
4255 }
4256 break;
4257
4258#ifdef PATM_RESOLVE_CONFLICTS_WITH_JUMP_PATCHES
4259 case OP_JO:
4260 case OP_JNO:
4261 case OP_JC:
4262 case OP_JNC:
4263 case OP_JE:
4264 case OP_JNE:
4265 case OP_JBE:
4266 case OP_JNBE:
4267 case OP_JS:
4268 case OP_JNS:
4269 case OP_JP:
4270 case OP_JNP:
4271 case OP_JL:
4272 case OP_JNL:
4273 case OP_JLE:
4274 case OP_JNLE:
4275 case OP_JECXZ:
4276 case OP_LOOP:
4277 case OP_LOOPNE:
4278 case OP_LOOPE:
4279 case OP_JMP:
4280 if (pPatchRec->patch.flags & PATMFL_JUMP_CONFLICT)
4281 {
4282 rc = patmPatchJump(pVM, pInstrGC, pInstrHC, &cpu, pPatchRec);
4283 break;
4284 }
4285 return VERR_NOT_IMPLEMENTED;
4286#endif
4287
4288 case OP_PUSHF:
4289 case OP_CLI:
4290 Log(("PATMR3InstallPatch %s %VGv code32=%d\n", patmGetInstructionString(pPatchRec->patch.opcode, pPatchRec->patch.flags), pInstrGC, (flags & PATMFL_CODE32) ? 1 : 0));
4291 rc = PATMR3PatchBlock(pVM, pInstrGC, pInstrHC, cpu.pCurInstr->opcode, opsize, pPatchRec);
4292 break;
4293
4294 case OP_STR:
4295 case OP_SGDT:
4296 case OP_SLDT:
4297 case OP_SIDT:
4298 case OP_CPUID:
4299 case OP_LSL:
4300 case OP_LAR:
4301 case OP_SMSW:
4302 case OP_VERW:
4303 case OP_VERR:
4304 case OP_IRET:
4305 rc = PATMR3PatchInstrInt3(pVM, pInstrGC, pInstrHC, &cpu, &pPatchRec->patch);
4306 break;
4307
4308 default:
4309 return VERR_NOT_IMPLEMENTED;
4310 }
4311 }
4312
4313 if (rc != VINF_SUCCESS)
4314 {
4315 if (pPatchRec && pPatchRec->patch.nrPatch2GuestRecs)
4316 {
4317 patmEmptyTreeU32(pVM, &pPatchRec->patch.Patch2GuestAddrTree);
4318 pPatchRec->patch.nrPatch2GuestRecs = 0;
4319 }
4320 pVM->patm.s.uCurrentPatchIdx--;
4321 }
4322 else
4323 {
4324 rc = patmInsertPatchPages(pVM, &pPatchRec->patch);
4325 AssertRCReturn(rc, rc);
4326
4327 /* Keep track upper and lower boundaries of patched instructions */
4328 if (pPatchRec->patch.pInstrGCLowest < pVM->patm.s.pPatchedInstrGCLowest)
4329 pVM->patm.s.pPatchedInstrGCLowest = pPatchRec->patch.pInstrGCLowest;
4330 if (pPatchRec->patch.pInstrGCHighest > pVM->patm.s.pPatchedInstrGCHighest)
4331 pVM->patm.s.pPatchedInstrGCHighest = pPatchRec->patch.pInstrGCHighest;
4332
4333 Log(("Patch lowest %VGv highest %VGv\n", pPatchRec->patch.pInstrGCLowest, pPatchRec->patch.pInstrGCHighest));
4334 Log(("Global lowest %VGv highest %VGv\n", pVM->patm.s.pPatchedInstrGCLowest, pVM->patm.s.pPatchedInstrGCHighest));
4335
4336 STAM_COUNTER_ADD(&pVM->patm.s.StatInstalled, 1);
4337 STAM_COUNTER_ADD(&pVM->patm.s.StatPATMMemoryUsed, pPatchRec->patch.cbPatchBlockSize);
4338
4339 rc = VINF_SUCCESS;
4340
4341 /* Patch hints are not enabled by default. Only when the are actually encountered. */
4342 if (pPatchRec->patch.flags & PATMFL_INSTR_HINT)
4343 {
4344 rc = PATMR3DisablePatch(pVM, pInstrGC);
4345 AssertRCReturn(rc, rc);
4346 }
4347
4348#ifdef VBOX_WITH_STATISTICS
4349 /* Register statistics counter */
4350 if (PATM_STAT_INDEX_IS_VALID(pPatchRec->patch.uPatchIdx))
4351 {
4352 STAMR3RegisterCallback(pVM, &pPatchRec->patch, STAMVISIBILITY_NOT_GUI, STAMUNIT_GOOD_BAD, patmResetStat, patmPrintStat, "Patch statistics",
4353 "/PATM/Stats/Patch/0x%VGv", pPatchRec->patch.pPrivInstrGC);
4354#ifndef DEBUG_sandervl
4355 /* Full breakdown for the GUI. */
4356 STAMR3RegisterF(pVM, &pVM->patm.s.pStatsHC[pPatchRec->patch.uPatchIdx], STAMTYPE_RATIO_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_GOOD_BAD, PATMPatchType(pVM, &pPatchRec->patch),
4357 "/PATM/Stats/PatchBD/0x%VGv", pPatchRec->patch.pPrivInstrGC);
4358 STAMR3RegisterF(pVM, &pPatchRec->patch.cbPatchBlockSize,STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cbPatchBlockSize", pPatchRec->patch.pPrivInstrGC);
4359 STAMR3RegisterF(pVM, &pPatchRec->patch.cbPatchJump, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cbPatchJump", pPatchRec->patch.pPrivInstrGC);
4360 STAMR3RegisterF(pVM, &pPatchRec->patch.cbPrivInstr, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cbPrivInstr", pPatchRec->patch.pPrivInstrGC);
4361 STAMR3RegisterF(pVM, &pPatchRec->patch.cCodeWrites, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cCodeWrites", pPatchRec->patch.pPrivInstrGC);
4362 STAMR3RegisterF(pVM, &pPatchRec->patch.cInvalidWrites, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cInvalidWrites", pPatchRec->patch.pPrivInstrGC);
4363 STAMR3RegisterF(pVM, &pPatchRec->patch.cTraps, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/cTraps", pPatchRec->patch.pPrivInstrGC);
4364 STAMR3RegisterF(pVM, &pPatchRec->patch.flags, STAMTYPE_X32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, NULL, "/PATM/Stats/PatchBD/0x%VGv/flags", pPatchRec->patch.pPrivInstrGC);
4365 STAMR3RegisterF(pVM, &pPatchRec->patch.nrJumpRecs, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/nrJumpRecs", pPatchRec->patch.pPrivInstrGC);
4366 STAMR3RegisterF(pVM, &pPatchRec->patch.nrFixups, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/nrFixups", pPatchRec->patch.pPrivInstrGC);
4367 STAMR3RegisterF(pVM, &pPatchRec->patch.opcode, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, NULL, "/PATM/Stats/PatchBD/0x%VGv/opcode", pPatchRec->patch.pPrivInstrGC);
4368 STAMR3RegisterF(pVM, &pPatchRec->patch.uOldState, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, NULL, "/PATM/Stats/PatchBD/0x%VGv/uOldState", pPatchRec->patch.pPrivInstrGC);
4369 STAMR3RegisterF(pVM, &pPatchRec->patch.uOpMode, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, NULL, "/PATM/Stats/PatchBD/0x%VGv/uOpMode", pPatchRec->patch.pPrivInstrGC);
4370 /// @todo change the state to be a callback so we can get a state mnemonic instead.
4371 STAMR3RegisterF(pVM, &pPatchRec->patch.uState, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE, NULL, "/PATM/Stats/PatchBD/0x%VGv/uState", pPatchRec->patch.pPrivInstrGC);
4372#endif
4373 }
4374#endif
4375 }
4376 return rc;
4377}
4378
4379/**
4380 * Query instruction size
4381 *
4382 * @returns VBox status code.
4383 * @param pVM The VM to operate on.
4384 * @param pPatch Patch record
4385 * @param pInstrGC Instruction address
4386 */
4387static uint32_t patmGetInstrSize(PVM pVM, PPATCHINFO pPatch, RTGCPTR pInstrGC)
4388{
4389 uint8_t *pInstrHC;
4390
4391 int rc = PGMPhysGCPtr2HCPtr(pVM, pInstrGC, (RTHCPTR *)&pInstrHC);
4392 if (rc == VINF_SUCCESS)
4393 {
4394 DISCPUSTATE cpu;
4395 bool disret;
4396 uint32_t opsize;
4397
4398 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
4399 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pInstrGC, pInstrHC, &opsize, NULL, PATMREAD_ORGCODE | PATMREAD_NOCHECK);
4400 if (disret)
4401 return opsize;
4402 }
4403 return 0;
4404}
4405
4406/**
4407 * Add patch to page record
4408 *
4409 * @returns VBox status code.
4410 * @param pVM The VM to operate on.
4411 * @param pPage Page address
4412 * @param pPatch Patch record
4413 */
4414int patmAddPatchToPage(PVM pVM, RTGCUINTPTR pPage, PPATCHINFO pPatch)
4415{
4416 PPATMPATCHPAGE pPatchPage;
4417 int rc;
4418
4419 Log(("patmAddPatchToPage: insert patch %VHv to page %VGv\n", pPatch, pPage));
4420
4421 pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, pPage);
4422 if (pPatchPage)
4423 {
4424 Assert(pPatchPage->cCount <= pPatchPage->cMaxPatches);
4425 if (pPatchPage->cCount == pPatchPage->cMaxPatches)
4426 {
4427 uint32_t cMaxPatchesOld = pPatchPage->cMaxPatches;
4428 PPATCHINFO *paPatchOld = pPatchPage->aPatch;
4429
4430 pPatchPage->cMaxPatches += PATMPATCHPAGE_PREALLOC_INCREMENT;
4431 rc = MMHyperAlloc(pVM, sizeof(PPATCHINFO)*pPatchPage->cMaxPatches, 0, MM_TAG_PATM_PATCH, (void **)&pPatchPage->aPatch);
4432 if (VBOX_FAILURE(rc))
4433 {
4434 Log(("Out of memory!!!!\n"));
4435 return VERR_NO_MEMORY;
4436 }
4437 memcpy(pPatchPage->aPatch, paPatchOld, cMaxPatchesOld*sizeof(PPATCHINFO));
4438 MMHyperFree(pVM, paPatchOld);
4439 }
4440 pPatchPage->aPatch[pPatchPage->cCount] = pPatch;
4441 pPatchPage->cCount++;
4442 }
4443 else
4444 {
4445 rc = MMHyperAlloc(pVM, sizeof(PATMPATCHPAGE), 0, MM_TAG_PATM_PATCH, (void **)&pPatchPage);
4446 if (VBOX_FAILURE(rc))
4447 {
4448 Log(("Out of memory!!!!\n"));
4449 return VERR_NO_MEMORY;
4450 }
4451 pPatchPage->Core.Key = pPage;
4452 pPatchPage->cCount = 1;
4453 pPatchPage->cMaxPatches = PATMPATCHPAGE_PREALLOC_INCREMENT;
4454
4455 rc = MMHyperAlloc(pVM, sizeof(PPATCHINFO)*PATMPATCHPAGE_PREALLOC_INCREMENT, 0, MM_TAG_PATM_PATCH, (void **)&pPatchPage->aPatch);
4456 if (VBOX_FAILURE(rc))
4457 {
4458 Log(("Out of memory!!!!\n"));
4459 MMHyperFree(pVM, pPatchPage);
4460 return VERR_NO_MEMORY;
4461 }
4462 pPatchPage->aPatch[0] = pPatch;
4463
4464 rc = RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, &pPatchPage->Core);
4465 Assert(rc);
4466 pVM->patm.s.cPageRecords++;
4467
4468 STAM_COUNTER_INC(&pVM->patm.s.StatPatchPageInserted);
4469 }
4470 CSAMR3MonitorPage(pVM, pPage, CSAM_TAG_PATM);
4471
4472 /* Get the closest guest instruction (from below) */
4473 PRECGUESTTOPATCH pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlGCPtrGetBestFit(&pPatch->Guest2PatchAddrTree, pPage, true);
4474 Assert(pGuestToPatchRec);
4475 if (pGuestToPatchRec)
4476 {
4477 LogFlow(("patmAddPatchToPage: lowest patch page address %VGv current lowest %VGv\n", pGuestToPatchRec->Core.Key, pPatchPage->pLowestAddrGC));
4478 if ( pPatchPage->pLowestAddrGC == 0
4479 || pPatchPage->pLowestAddrGC > (RTGCPTR)pGuestToPatchRec->Core.Key)
4480 {
4481 RTGCUINTPTR offset;
4482
4483 pPatchPage->pLowestAddrGC = (RTGCPTR)pGuestToPatchRec->Core.Key;
4484
4485 offset = pPatchPage->pLowestAddrGC & PAGE_OFFSET_MASK;
4486 /* If we're too close to the page boundary, then make sure an instruction from the previous page doesn't cross the boundary itself. */
4487 if (offset && offset < MAX_INSTR_SIZE)
4488 {
4489 /* Get the closest guest instruction (from above) */
4490 pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlGCPtrGetBestFit(&pPatch->Guest2PatchAddrTree, pPage-1, false);
4491
4492 if (pGuestToPatchRec)
4493 {
4494 uint32_t size = patmGetInstrSize(pVM, pPatch, (RTGCPTR)pGuestToPatchRec->Core.Key);
4495 if ((RTGCUINTPTR)pGuestToPatchRec->Core.Key + size > pPage)
4496 {
4497 pPatchPage->pLowestAddrGC = pPage;
4498 LogFlow(("patmAddPatchToPage: new lowest %VGv\n", pPatchPage->pLowestAddrGC));
4499 }
4500 }
4501 }
4502 }
4503 }
4504
4505 /* Get the closest guest instruction (from above) */
4506 pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlGCPtrGetBestFit(&pPatch->Guest2PatchAddrTree, pPage+PAGE_SIZE-1, false);
4507 Assert(pGuestToPatchRec);
4508 if (pGuestToPatchRec)
4509 {
4510 LogFlow(("patmAddPatchToPage: highest patch page address %VGv current lowest %VGv\n", pGuestToPatchRec->Core.Key, pPatchPage->pHighestAddrGC));
4511 if ( pPatchPage->pHighestAddrGC == 0
4512 || pPatchPage->pHighestAddrGC <= (RTGCPTR)pGuestToPatchRec->Core.Key)
4513 {
4514 pPatchPage->pHighestAddrGC = (RTGCPTR)pGuestToPatchRec->Core.Key;
4515 /* Increase by instruction size. */
4516 uint32_t size = patmGetInstrSize(pVM, pPatch, pPatchPage->pHighestAddrGC);
4517//// Assert(size);
4518 pPatchPage->pHighestAddrGC += size;
4519 LogFlow(("patmAddPatchToPage: new highest %VGv\n", pPatchPage->pHighestAddrGC));
4520 }
4521 }
4522
4523 return VINF_SUCCESS;
4524}
4525
4526/**
4527 * Remove patch from page record
4528 *
4529 * @returns VBox status code.
4530 * @param pVM The VM to operate on.
4531 * @param pPage Page address
4532 * @param pPatch Patch record
4533 */
4534int patmRemovePatchFromPage(PVM pVM, RTGCUINTPTR pPage, PPATCHINFO pPatch)
4535{
4536 PPATMPATCHPAGE pPatchPage;
4537 int rc;
4538
4539 pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, pPage);
4540 Assert(pPatchPage);
4541
4542 if (!pPatchPage)
4543 return VERR_INVALID_PARAMETER;
4544
4545 Assert(pPatchPage->cCount <= pPatchPage->cMaxPatches);
4546
4547 Log(("patmRemovePatchPage: remove patch %VHv from page %VGv\n", pPatch, pPage));
4548 if (pPatchPage->cCount > 1)
4549 {
4550 uint32_t i;
4551
4552 /* Used by multiple patches */
4553 for (i=0;i<pPatchPage->cCount;i++)
4554 {
4555 if (pPatchPage->aPatch[i] == pPatch)
4556 {
4557 pPatchPage->aPatch[i] = 0;
4558 break;
4559 }
4560 }
4561 /* close the gap between the remaining pointers. */
4562 if (i < pPatchPage->cCount - 1)
4563 {
4564 memcpy(&pPatchPage->aPatch[i], &pPatchPage->aPatch[i+1], sizeof(PPATCHINFO)*(pPatchPage->cCount - (i+1)));
4565 }
4566 AssertMsg(i < pPatchPage->cCount, ("Unable to find patch %VHv in page %VGv\n", pPatch, pPage));
4567
4568 pPatchPage->cCount--;
4569 }
4570 else
4571 {
4572 PPATMPATCHPAGE pPatchNode;
4573
4574 Log(("patmRemovePatchFromPage %VGv\n", pPage));
4575
4576 STAM_COUNTER_INC(&pVM->patm.s.StatPatchPageRemoved);
4577 pPatchNode = (PPATMPATCHPAGE)RTAvloGCPtrRemove(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, pPage);
4578 Assert(pPatchNode && pPatchNode == pPatchPage);
4579
4580 Assert(pPatchPage->aPatch);
4581 rc = MMHyperFree(pVM, pPatchPage->aPatch);
4582 AssertRC(rc);
4583 rc = MMHyperFree(pVM, pPatchPage);
4584 AssertRC(rc);
4585 pVM->patm.s.cPageRecords--;
4586 }
4587 return VINF_SUCCESS;
4588}
4589
4590/**
4591 * Insert page records for all guest pages that contain instructions that were recompiled for this patch
4592 *
4593 * @returns VBox status code.
4594 * @param pVM The VM to operate on.
4595 * @param pPatch Patch record
4596 */
4597int patmInsertPatchPages(PVM pVM, PPATCHINFO pPatch)
4598{
4599 int rc;
4600 RTGCUINTPTR pPatchPageStart, pPatchPageEnd, pPage;
4601
4602 /* Insert the pages that contain patched instructions into a lookup tree for detecting self-modifying code. */
4603 pPatchPageStart = (RTGCUINTPTR)pPatch->pInstrGCLowest & PAGE_BASE_GC_MASK;
4604 pPatchPageEnd = (RTGCUINTPTR)pPatch->pInstrGCHighest & PAGE_BASE_GC_MASK;
4605
4606 /** @todo optimize better (large gaps between current and next used page) */
4607 for(pPage = pPatchPageStart; pPage <= pPatchPageEnd; pPage += PAGE_SIZE)
4608 {
4609 /* Get the closest guest instruction (from above) */
4610 PRECGUESTTOPATCH pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlGCPtrGetBestFit(&pPatch->Guest2PatchAddrTree, pPage, true);
4611 if ( pGuestToPatchRec
4612 && PAGE_ADDRESS(pGuestToPatchRec->Core.Key) == PAGE_ADDRESS(pPage)
4613 )
4614 {
4615 /* Code in page really patched -> add record */
4616 rc = patmAddPatchToPage(pVM, pPage, pPatch);
4617 AssertRC(rc);
4618 }
4619 }
4620 pPatch->flags |= PATMFL_CODE_MONITORED;
4621 return VINF_SUCCESS;
4622}
4623
4624/**
4625 * Remove page records for all guest pages that contain instructions that were recompiled for this patch
4626 *
4627 * @returns VBox status code.
4628 * @param pVM The VM to operate on.
4629 * @param pPatch Patch record
4630 */
4631int patmRemovePatchPages(PVM pVM, PPATCHINFO pPatch)
4632{
4633 int rc;
4634 RTGCUINTPTR pPatchPageStart, pPatchPageEnd, pPage;
4635
4636 /* Insert the pages that contain patched instructions into a lookup tree for detecting self-modifying code. */
4637 pPatchPageStart = (RTGCUINTPTR)pPatch->pInstrGCLowest & PAGE_BASE_GC_MASK;
4638 pPatchPageEnd = (RTGCUINTPTR)pPatch->pInstrGCHighest & PAGE_BASE_GC_MASK;
4639
4640 for(pPage = pPatchPageStart; pPage <= pPatchPageEnd; pPage += PAGE_SIZE)
4641 {
4642 /* Get the closest guest instruction (from above) */
4643 PRECGUESTTOPATCH pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlGCPtrGetBestFit(&pPatch->Guest2PatchAddrTree, pPage, true);
4644 if ( pGuestToPatchRec
4645 && PAGE_ADDRESS(pGuestToPatchRec->Core.Key) == PAGE_ADDRESS(pPage) /** @todo bird: PAGE_ADDRESS is for the current context really. check out these. */
4646 )
4647 {
4648 /* Code in page really patched -> remove record */
4649 rc = patmRemovePatchFromPage(pVM, pPage, pPatch);
4650 AssertRC(rc);
4651 }
4652 }
4653 pPatch->flags &= ~PATMFL_CODE_MONITORED;
4654 return VINF_SUCCESS;
4655}
4656
4657/**
4658 * Notifies PATM about a (potential) write to code that has been patched.
4659 *
4660 * @returns VBox status code.
4661 * @param pVM The VM to operate on.
4662 * @param GCPtr GC pointer to write address
4663 * @param cbWrite Nr of bytes to write
4664 *
4665 */
4666PATMR3DECL(int) PATMR3PatchWrite(PVM pVM, RTGCPTR GCPtr, uint32_t cbWrite)
4667{
4668 RTGCUINTPTR pWritePageStart, pWritePageEnd, pPage;
4669
4670 Log(("PATMR3PatchWrite %VGv %x\n", GCPtr, cbWrite));
4671
4672 Assert(VM_IS_EMT(pVM));
4673
4674 /* Quick boundary check */
4675 if ( GCPtr < pVM->patm.s.pPatchedInstrGCLowest
4676 || GCPtr > pVM->patm.s.pPatchedInstrGCHighest
4677 )
4678 return VINF_SUCCESS;
4679
4680 STAM_PROFILE_ADV_START(&pVM->patm.s.StatPatchWrite, a);
4681
4682 pWritePageStart = (RTGCUINTPTR)GCPtr & PAGE_BASE_GC_MASK;
4683 pWritePageEnd = ((RTGCUINTPTR)GCPtr + cbWrite - 1) & PAGE_BASE_GC_MASK;
4684
4685 for (pPage = pWritePageStart; pPage <= pWritePageEnd; pPage += PAGE_SIZE)
4686 {
4687loop_start:
4688 PPATMPATCHPAGE pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, (RTGCPTR)pPage);
4689 if (pPatchPage)
4690 {
4691 uint32_t i;
4692 bool fValidPatchWrite = false;
4693
4694 /* Quick check to see if the write is in the patched part of the page */
4695 if ( pPatchPage->pLowestAddrGC > (RTGCPTR)((RTGCUINTPTR)GCPtr + cbWrite - 1)
4696 || pPatchPage->pHighestAddrGC < GCPtr)
4697 {
4698 break;
4699 }
4700
4701 for (i=0;i<pPatchPage->cCount;i++)
4702 {
4703 if (pPatchPage->aPatch[i])
4704 {
4705 PPATCHINFO pPatch = pPatchPage->aPatch[i];
4706 RTGCPTR pPatchInstrGC;
4707 //unused: bool fForceBreak = false;
4708
4709 Assert(pPatchPage->aPatch[i]->flags & PATMFL_CODE_MONITORED);
4710 /** @todo inefficient and includes redundant checks for multiple pages. */
4711 for (uint32_t j=0; j<cbWrite; j++)
4712 {
4713 RTGCPTR pGuestPtrGC = (RTGCPTR)((RTGCUINTPTR)GCPtr + j);
4714
4715 if ( pPatch->cbPatchJump
4716 && pGuestPtrGC >= pPatch->pPrivInstrGC
4717 && pGuestPtrGC < pPatch->pPrivInstrGC + pPatch->cbPatchJump)
4718 {
4719 /* The guest is about to overwrite the 5 byte jump to patch code. Remove the patch. */
4720 Log(("PATMR3PatchWrite: overwriting jump to patch code -> remove patch.\n"));
4721 int rc = PATMR3RemovePatch(pVM, pPatch->pPrivInstrGC);
4722 AssertRC(rc);
4723
4724 /** @note jump back to the start as the pPatchPage has been deleted or changed */
4725 goto loop_start;
4726 }
4727
4728 /* Find the closest instruction from below; the above quick check ensured that we are indeed in patched code */
4729 pPatchInstrGC = patmGuestGCPtrToPatchGCPtr(pVM, pPatch, pGuestPtrGC);
4730 if (!pPatchInstrGC)
4731 {
4732 RTGCPTR pClosestInstrGC;
4733 uint32_t size;
4734
4735 pPatchInstrGC = patmGuestGCPtrToClosestPatchGCPtr(pVM, pPatch, pGuestPtrGC);
4736 if (pPatchInstrGC)
4737 {
4738 pClosestInstrGC = patmPatchGCPtr2GuestGCPtr(pVM, pPatch, pPatchInstrGC);
4739 Assert(pClosestInstrGC <= pGuestPtrGC);
4740 size = patmGetInstrSize(pVM, pPatch, pClosestInstrGC);
4741 /* Check if this is not a write into a gap between two patches */
4742 if (pClosestInstrGC + size - 1 < pGuestPtrGC)
4743 pPatchInstrGC = 0;
4744 }
4745 }
4746 if (pPatchInstrGC)
4747 {
4748 uint32_t PatchOffset = pPatchInstrGC - pVM->patm.s.pPatchMemGC; /* Offset in memory reserved for PATM. */
4749
4750 fValidPatchWrite = true;
4751
4752 PRECPATCHTOGUEST pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32Get(&pPatch->Patch2GuestAddrTree, PatchOffset);
4753 Assert(pPatchToGuestRec);
4754 if (pPatchToGuestRec && !pPatchToGuestRec->fDirty)
4755 {
4756 Log(("PATMR3PatchWrite: Found patched instruction %VGv -> %VGv\n", pGuestPtrGC, pPatchInstrGC));
4757
4758 if (++pPatch->cCodeWrites > PATM_MAX_CODE_WRITES)
4759 {
4760 LogRel(("PATM: Disable block at %VGv - write %VGv-%VGv\n", pPatch->pPrivInstrGC, pGuestPtrGC, pGuestPtrGC+cbWrite));
4761
4762 PATMR3MarkDirtyPatch(pVM, pPatch);
4763
4764 /** @note jump back to the start as the pPatchPage has been deleted or changed */
4765 goto loop_start;
4766 }
4767 else
4768 {
4769 /* Replace the patch instruction with a breakpoint; when it's hit, then we'll attempt to recompile the instruction again. */
4770 uint8_t *pInstrHC = patmPatchGCPtr2PatchHCPtr(pVM, pPatchInstrGC);
4771
4772 pPatchToGuestRec->u8DirtyOpcode = *pInstrHC;
4773 pPatchToGuestRec->fDirty = true;
4774
4775 *pInstrHC = 0xCC;
4776
4777 STAM_COUNTER_INC(&pVM->patm.s.StatInstrDirty);
4778 }
4779 }
4780 /* else already marked dirty */
4781 }
4782 }
4783 }
4784 } /* for each patch */
4785
4786 if (fValidPatchWrite == false)
4787 {
4788 /* Write to a part of the page that either:
4789 * - doesn't contain any code (shared code/data); rather unlikely
4790 * - old code page that's no longer in active use.
4791 */
4792invalid_write_loop_start:
4793 pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, (RTGCPTR)pPage);
4794
4795 if (pPatchPage)
4796 {
4797 for (i=0;i<pPatchPage->cCount;i++)
4798 {
4799 PPATCHINFO pPatch = pPatchPage->aPatch[i];
4800
4801 if (pPatch->cInvalidWrites > PATM_MAX_INVALID_WRITES)
4802 {
4803 /** @note possibly dangerous assumption that all future writes will be harmless. */
4804 if (pPatch->flags & PATMFL_IDTHANDLER)
4805 {
4806 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));
4807
4808 Assert(pPatch->flags & PATMFL_CODE_MONITORED);
4809 int rc = patmRemovePatchPages(pVM, pPatch);
4810 AssertRC(rc);
4811 }
4812 else
4813 {
4814 LogRel(("PATM: Disable block at %VGv - invalid write %VGv-%VGv \n", pPatch->pPrivInstrGC, GCPtr, GCPtr+cbWrite));
4815 PATMR3MarkDirtyPatch(pVM, pPatch);
4816 }
4817 /** @note jump back to the start as the pPatchPage has been deleted or changed */
4818 goto invalid_write_loop_start;
4819 }
4820 } /* for */
4821 }
4822 }
4823 }
4824 }
4825 STAM_PROFILE_ADV_STOP(&pVM->patm.s.StatPatchWrite, a);
4826 return VINF_SUCCESS;
4827
4828}
4829
4830/**
4831 * Disable all patches in a flushed page
4832 *
4833 * @returns VBox status code
4834 * @param pVM The VM to operate on.
4835 * @param addr GC address of the page to flush
4836 */
4837/** @note Currently only called by CSAMR3FlushPage; optimization to avoid having to double check if the physical address has changed
4838 */
4839PATMR3DECL(int) PATMR3FlushPage(PVM pVM, RTGCPTR addr)
4840{
4841 addr &= PAGE_BASE_GC_MASK;
4842
4843 PPATMPATCHPAGE pPatchPage = (PPATMPATCHPAGE)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPage, addr);
4844 if (pPatchPage)
4845 {
4846 int i;
4847
4848 /* From top to bottom as the array is modified by PATMR3MarkDirtyPatch. */
4849 for (i=(int)pPatchPage->cCount-1;i>=0;i--)
4850 {
4851 if (pPatchPage->aPatch[i])
4852 {
4853 PPATCHINFO pPatch = pPatchPage->aPatch[i];
4854
4855 Log(("PATMR3FlushPage %VGv remove patch at %VGv\n", addr, pPatch->pPrivInstrGC));
4856 PATMR3MarkDirtyPatch(pVM, pPatch);
4857 }
4858 }
4859 STAM_COUNTER_INC(&pVM->patm.s.StatFlushed);
4860 }
4861 return VINF_SUCCESS;
4862}
4863
4864/**
4865 * Checks if the instructions at the specified address has been patched already.
4866 *
4867 * @returns boolean, patched or not
4868 * @param pVM The VM to operate on.
4869 * @param pInstrGC Guest context pointer to instruction
4870 */
4871PATMR3DECL(bool) PATMR3HasBeenPatched(PVM pVM, RTGCPTR pInstrGC)
4872{
4873 PPATMPATCHREC pPatchRec;
4874 pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC);
4875 if (pPatchRec && pPatchRec->patch.uState == PATCH_ENABLED)
4876 return true;
4877 return false;
4878}
4879
4880/**
4881 * Query the opcode of the original code that was overwritten by the 5 bytes patch jump
4882 *
4883 * @returns VBox status code.
4884 * @param pVM The VM to operate on.
4885 * @param pInstrGC GC address of instr
4886 * @param pByte opcode byte pointer (OUT)
4887 *
4888 */
4889PATMR3DECL(int) PATMR3QueryOpcode(PVM pVM, RTGCPTR pInstrGC, uint8_t *pByte)
4890{
4891 PPATMPATCHREC pPatchRec;
4892
4893 /** @todo this will not work for aliased pages! (never has, but so far not a problem for us) */
4894
4895 /* Shortcut. */
4896 if ( !PATMIsEnabled(pVM)
4897 || pInstrGC < pVM->patm.s.pPatchedInstrGCLowest
4898 || pInstrGC > pVM->patm.s.pPatchedInstrGCHighest)
4899 {
4900 return VERR_PATCH_NOT_FOUND;
4901 }
4902
4903 pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC, false);
4904 // if the patch is enabled and the pointer lies within 5 bytes of this priv instr ptr, then we've got a hit!
4905 if ( pPatchRec
4906 && pPatchRec->patch.uState == PATCH_ENABLED
4907 && pInstrGC >= pPatchRec->patch.pPrivInstrGC
4908 && pInstrGC < pPatchRec->patch.pPrivInstrGC + pPatchRec->patch.cbPatchJump)
4909 {
4910 RTGCPTR offset = pInstrGC - pPatchRec->patch.pPrivInstrGC;
4911 *pByte = pPatchRec->patch.aPrivInstr[offset];
4912
4913 if (pPatchRec->patch.cbPatchJump == 1)
4914 {
4915 Log(("PATMR3QueryOpcode: returning opcode %2X for instruction at %VGv\n", *pByte, pInstrGC));
4916 }
4917 STAM_COUNTER_ADD(&pVM->patm.s.StatNrOpcodeRead, 1);
4918 return VINF_SUCCESS;
4919 }
4920 return VERR_PATCH_NOT_FOUND;
4921}
4922
4923/**
4924 * Disable patch for privileged instruction at specified location
4925 *
4926 * @returns VBox status code.
4927 * @param pVM The VM to operate on.
4928 * @param pInstr Guest context point to privileged instruction
4929 *
4930 * @note returns failure if patching is not allowed or possible
4931 *
4932 */
4933PATMR3DECL(int) PATMR3DisablePatch(PVM pVM, RTGCPTR pInstrGC)
4934{
4935 PPATMPATCHREC pPatchRec;
4936 PPATCHINFO pPatch;
4937
4938 Log(("PATMR3DisablePatch: %VGv\n", pInstrGC));
4939 pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC);
4940 if (pPatchRec)
4941 {
4942 int rc = VINF_SUCCESS;
4943
4944 pPatch = &pPatchRec->patch;
4945
4946 /* Already disabled? */
4947 if (pPatch->uState == PATCH_DISABLED)
4948 return VINF_SUCCESS;
4949
4950 /* Clear the IDT entries for the patch we're disabling. */
4951 /** @note very important as we clear IF in the patch itself */
4952 /** @todo this needs to be changed */
4953 if (pPatch->flags & PATMFL_IDTHANDLER)
4954 {
4955 uint32_t iGate;
4956
4957 iGate = TRPMR3QueryGateByHandler(pVM, PATCHCODE_PTR_GC(pPatch));
4958 if (iGate != (uint32_t)~0)
4959 {
4960 TRPMR3SetGuestTrapHandler(pVM, iGate, TRPM_INVALID_HANDLER);
4961 LogRel(("PATM: Disabling IDT %x patch handler %VGv\n", iGate, pInstrGC));
4962 }
4963 }
4964
4965 /* 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) */
4966 if ( pPatch->pPatchBlockOffset
4967 && pPatch->uState == PATCH_ENABLED)
4968 {
4969 Log(("Invalidate patch at %VGv (HC=%VGv)\n", PATCHCODE_PTR_GC(pPatch), PATCHCODE_PTR_HC(pPatch)));
4970 pPatch->bDirtyOpcode = *PATCHCODE_PTR_HC(pPatch);
4971 *PATCHCODE_PTR_HC(pPatch) = 0xCC;
4972 }
4973
4974 /* IDT or function patches haven't changed any guest code. */
4975 if (pPatch->flags & PATMFL_PATCHED_GUEST_CODE)
4976 {
4977 Assert(pPatch->flags & PATMFL_MUST_INSTALL_PATCHJMP);
4978 Assert(!(pPatch->flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_IDTHANDLER|PATMFL_TRAMPOLINE|PATMFL_INT3_REPLACEMENT|PATMFL_INT3_REPLACEMENT_BLOCK)));
4979
4980 if (pPatch->uState != PATCH_REFUSED)
4981 {
4982 AssertMsg(pPatch->pPrivInstrHC, ("Invalid HC pointer?!? (%VGv)\n", pInstrGC));
4983 Assert(pPatch->cbPatchJump);
4984
4985 /** pPrivInstrHC is probably not valid anymore */
4986 rc = PGMPhysGCPtr2HCPtr(pVM, pPatchRec->patch.pPrivInstrGC, (PRTHCPTR)&pPatchRec->patch.pPrivInstrHC);
4987 if (rc == VINF_SUCCESS)
4988 {
4989 uint8_t temp[16];
4990
4991 Assert(pPatch->cbPatchJump < sizeof(temp));
4992
4993 /* Let's first check if the guest code is still the same. */
4994 rc = PGMPhysReadGCPtr(pVM, temp, pPatch->pPrivInstrGC, pPatch->cbPatchJump);
4995 Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_TABLE_NOT_PRESENT || rc == VERR_PAGE_NOT_PRESENT);
4996 if (rc == VINF_SUCCESS)
4997 {
4998 RTGCINTPTR displ = (RTGCUINTPTR)PATCHCODE_PTR_GC(pPatch) - ((RTGCUINTPTR)pPatch->pPrivInstrGC + SIZEOF_NEARJUMP32);
4999
5000 if ( temp[0] != 0xE9 /* jmp opcode */
5001 || *(RTGCINTPTR *)(&temp[1]) != displ
5002 )
5003 {
5004 Log(("PATMR3DisablePatch: Can't disable a patch who's guest code has changed!!\n"));
5005 STAM_COUNTER_INC(&pVM->patm.s.StatOverwritten);
5006 /* Remove it completely */
5007 pPatch->uState = PATCH_DISABLED; /* don't call PATMR3DisablePatch again */
5008 rc = PATMR3RemovePatch(pVM, pInstrGC);
5009 AssertRC(rc);
5010 return VWRN_PATCH_REMOVED;
5011 }
5012 }
5013 patmRemoveJumpToPatch(pVM, pPatch);
5014
5015 }
5016 else
5017 {
5018 Log(("PATMR3DisablePatch: unable to disable patch -> mark PATCH_DISABLE_PENDING\n"));
5019 pPatch->uState = PATCH_DISABLE_PENDING;
5020 }
5021 }
5022 else
5023 {
5024 AssertMsgFailed(("Patch was refused!\n"));
5025 return VERR_PATCH_ALREADY_DISABLED;
5026 }
5027 }
5028 else
5029 if (pPatch->flags & (PATMFL_INT3_REPLACEMENT|PATMFL_INT3_REPLACEMENT_BLOCK))
5030 {
5031 uint8_t temp[16];
5032
5033 Assert(pPatch->cbPatchJump < sizeof(temp));
5034
5035 /* Let's first check if the guest code is still the same. */
5036 rc = PGMPhysReadGCPtr(pVM, temp, pPatch->pPrivInstrGC, pPatch->cbPatchJump);
5037 Assert(rc == VINF_SUCCESS || rc == VERR_PAGE_TABLE_NOT_PRESENT || rc == VERR_PAGE_NOT_PRESENT);
5038 if (rc == VINF_SUCCESS)
5039 {
5040 if (temp[0] != 0xCC)
5041 {
5042 Log(("PATMR3DisablePatch: Can't disable a patch who's guest code has changed!!\n"));
5043 STAM_COUNTER_INC(&pVM->patm.s.StatOverwritten);
5044 /* Remove it completely */
5045 pPatch->uState = PATCH_DISABLED; /* don't call PATMR3DisablePatch again */
5046 rc = PATMR3RemovePatch(pVM, pInstrGC);
5047 AssertRC(rc);
5048 return VWRN_PATCH_REMOVED;
5049 }
5050 patmDeactivateInt3Patch(pVM, pPatch);
5051 }
5052 }
5053
5054 if (rc == VINF_SUCCESS)
5055 {
5056 /* Save old state and mark this one as disabled (so it can be enabled later on). */
5057 if (pPatch->uState == PATCH_DISABLE_PENDING)
5058 {
5059 /* Just to be safe, let's make sure this one can never be reused; the patch might be marked dirty already (int3 at start) */
5060 pPatch->uState = PATCH_UNUSABLE;
5061 }
5062 else
5063 if (pPatch->uState != PATCH_DIRTY)
5064 {
5065 pPatch->uOldState = pPatch->uState;
5066 pPatch->uState = PATCH_DISABLED;
5067 }
5068 STAM_COUNTER_ADD(&pVM->patm.s.StatDisabled, 1);
5069 }
5070
5071 Log(("PATMR3DisablePatch: disabled patch at %VGv\n", pInstrGC));
5072 return VINF_SUCCESS;
5073 }
5074 Log(("Patch not found!\n"));
5075 return VERR_PATCH_NOT_FOUND;
5076}
5077
5078/**
5079 * Permanently disable patch for privileged instruction at specified location
5080 *
5081 * @returns VBox status code.
5082 * @param pVM The VM to operate on.
5083 * @param pInstr Guest context instruction pointer
5084 * @param pConflictAddr Guest context pointer which conflicts with specified patch
5085 * @param pConflictPatch Conflicting patch
5086 *
5087 */
5088static int patmDisableUnusablePatch(PVM pVM, RTGCPTR pInstrGC, RTGCPTR pConflictAddr, PPATCHINFO pConflictPatch)
5089{
5090#ifdef PATM_RESOLVE_CONFLICTS_WITH_JUMP_PATCHES
5091 PATCHINFO patch = {0};
5092 DISCPUSTATE cpu;
5093 R3PTRTYPE(uint8_t *) pInstrHC;
5094 uint32_t opsize;
5095 bool disret;
5096 int rc;
5097
5098 pInstrHC = PATMGCVirtToHCVirt(pVM, &patch, pInstrGC);
5099 cpu.mode = (pConflictPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
5100 disret = PATMR3DISInstr(pVM, &patch, &cpu, pInstrGC, pInstrHC, &opsize, NULL);
5101 /*
5102 * If it's a 5 byte relative jump, then we can work around the problem by replacing the 32 bits relative offset
5103 * with one that jumps right into the conflict patch.
5104 * Otherwise we must disable the conflicting patch to avoid serious problems.
5105 */
5106 if ( disret == true
5107 && (pConflictPatch->flags & PATMFL_CODE32)
5108 && (cpu.pCurInstr->opcode == OP_JMP || (cpu.pCurInstr->optype & OPTYPE_COND_CONTROLFLOW))
5109 && (cpu.param1.flags & USE_IMMEDIATE32_REL))
5110 {
5111 /* Hint patches must be enabled first. */
5112 if (pConflictPatch->flags & PATMFL_INSTR_HINT)
5113 {
5114 Log(("Enabling HINTED patch %VGv\n", pConflictPatch->pPrivInstrGC));
5115 pConflictPatch->flags &= ~PATMFL_INSTR_HINT;
5116 rc = PATMR3EnablePatch(pVM, pConflictPatch->pPrivInstrGC);
5117 Assert(rc == VINF_SUCCESS || rc == VERR_PATCH_NOT_FOUND);
5118 /* Enabling might fail if the patched code has changed in the meantime. */
5119 if (rc != VINF_SUCCESS)
5120 return rc;
5121 }
5122
5123 rc = PATMR3InstallPatch(pVM, pInstrGC, PATMFL_CODE32 | PATMFL_JUMP_CONFLICT);
5124 if (VBOX_SUCCESS(rc))
5125 {
5126 Log(("PATM -> CONFLICT: Installed JMP patch for patch conflict at %VGv\n", pInstrGC));
5127 STAM_COUNTER_INC(&pVM->patm.s.StatFixedConflicts);
5128 return VINF_SUCCESS;
5129 }
5130 }
5131#endif
5132
5133 if (pConflictPatch->opcode == OP_CLI)
5134 {
5135 /* Turn it into an int3 patch; our GC trap handler will call the generated code manually. */
5136 Log(("PATM -> CONFLICT: Found active patch at instruction %VGv with target %VGv -> turn into int 3 patch!!\n", pInstrGC, pConflictPatch->pPrivInstrGC));
5137 int rc = PATMR3DisablePatch(pVM, pConflictPatch->pPrivInstrGC);
5138 if (rc == VWRN_PATCH_REMOVED)
5139 return VINF_SUCCESS;
5140 if (VBOX_SUCCESS(rc))
5141 {
5142 pConflictPatch->flags &= ~(PATMFL_MUST_INSTALL_PATCHJMP|PATMFL_INSTR_HINT);
5143 pConflictPatch->flags |= PATMFL_INT3_REPLACEMENT_BLOCK;
5144 rc = PATMR3EnablePatch(pVM, pConflictPatch->pPrivInstrGC);
5145 if (rc == VERR_PATCH_NOT_FOUND)
5146 return VINF_SUCCESS; /* removed already */
5147
5148 AssertRC(rc);
5149 if (VBOX_SUCCESS(rc))
5150 {
5151 STAM_COUNTER_INC(&pVM->patm.s.StatInt3Callable);
5152 return VINF_SUCCESS;
5153 }
5154 }
5155 /* else turned into unusable patch (see below) */
5156 }
5157 else
5158 {
5159 Log(("PATM -> CONFLICT: Found active patch at instruction %VGv with target %VGv -> DISABLING it!!\n", pInstrGC, pConflictPatch->pPrivInstrGC));
5160 int rc = PATMR3DisablePatch(pVM, pConflictPatch->pPrivInstrGC);
5161 if (rc == VWRN_PATCH_REMOVED)
5162 return VINF_SUCCESS;
5163 }
5164
5165 /* No need to monitor the code anymore. */
5166 if (pConflictPatch->flags & PATMFL_CODE_MONITORED)
5167 {
5168 int rc = patmRemovePatchPages(pVM, pConflictPatch);
5169 AssertRC(rc);
5170 }
5171 pConflictPatch->uState = PATCH_UNUSABLE;
5172 STAM_COUNTER_INC(&pVM->patm.s.StatUnusable);
5173 return VERR_PATCH_DISABLED;
5174}
5175
5176/**
5177 * Enable patch for privileged instruction at specified location
5178 *
5179 * @returns VBox status code.
5180 * @param pVM The VM to operate on.
5181 * @param pInstr Guest context point to privileged instruction
5182 *
5183 * @note returns failure if patching is not allowed or possible
5184 *
5185 */
5186PATMR3DECL(int) PATMR3EnablePatch(PVM pVM, RTGCPTR pInstrGC)
5187{
5188 PPATMPATCHREC pPatchRec;
5189 PPATCHINFO pPatch;
5190
5191 Log(("PATMR3EnablePatch %VGv\n", pInstrGC));
5192 pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC);
5193 if (pPatchRec)
5194 {
5195 int rc = VINF_SUCCESS;
5196
5197 pPatch = &pPatchRec->patch;
5198
5199 if (pPatch->uState == PATCH_DISABLED)
5200 {
5201 if (pPatch->flags & PATMFL_MUST_INSTALL_PATCHJMP)
5202 {
5203 Assert(!(pPatch->flags & PATMFL_PATCHED_GUEST_CODE));
5204 /** @todo -> pPrivInstrHC is probably not valid anymore */
5205 rc = PGMPhysGCPtr2HCPtr(pVM, pPatchRec->patch.pPrivInstrGC, (PRTHCPTR)&pPatchRec->patch.pPrivInstrHC);
5206 if (rc == VINF_SUCCESS)
5207 {
5208#ifdef DEBUG
5209 DISCPUSTATE cpu;
5210 char szOutput[256];
5211 uint32_t opsize, i = 0;
5212#endif
5213 uint8_t temp[16];
5214
5215 Assert(pPatch->cbPatchJump < sizeof(temp));
5216
5217 // let's first check if the guest code is still the same
5218 int rc = PGMPhysReadGCPtr(pVM, temp, pPatch->pPrivInstrGC, pPatch->cbPatchJump);
5219 AssertRC(rc);
5220
5221 if (memcmp(temp, pPatch->aPrivInstr, pPatch->cbPatchJump))
5222 {
5223 Log(("PATMR3EnablePatch: Can't enable a patch who's guest code has changed!!\n"));
5224 STAM_COUNTER_INC(&pVM->patm.s.StatOverwritten);
5225 /* Remove it completely */
5226 PATMR3RemovePatch(pVM, pInstrGC);
5227 return VERR_PATCH_NOT_FOUND;
5228 }
5229
5230 rc = patmGenJumpToPatch(pVM, pPatch, false);
5231 AssertRC(rc);
5232 if (VBOX_FAILURE(rc))
5233 return rc;
5234
5235#ifdef DEBUG
5236 bool disret;
5237 i = 0;
5238 while(i < pPatch->cbPatchJump)
5239 {
5240 cpu.mode = (pPatch->flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
5241 disret = PATMR3DISInstr(pVM, pPatch, &cpu, pPatch->pPrivInstrGC + i, &pPatch->pPrivInstrHC[i], &opsize, szOutput);
5242 Log(("Renewed patch instr: %s", szOutput));
5243 i += opsize;
5244 }
5245#endif
5246 }
5247 }
5248 else
5249 if (pPatch->flags & (PATMFL_INT3_REPLACEMENT|PATMFL_INT3_REPLACEMENT_BLOCK))
5250 {
5251 uint8_t temp[16];
5252
5253 Assert(pPatch->cbPatchJump < sizeof(temp));
5254
5255 /* Let's first check if the guest code is still the same. */
5256 int rc = PGMPhysReadGCPtr(pVM, temp, pPatch->pPrivInstrGC, pPatch->cbPatchJump);
5257 AssertRC(rc);
5258
5259 if (memcmp(temp, pPatch->aPrivInstr, pPatch->cbPatchJump))
5260 {
5261 Log(("PATMR3EnablePatch: Can't enable a patch who's guest code has changed!!\n"));
5262 STAM_COUNTER_INC(&pVM->patm.s.StatOverwritten);
5263 PATMR3RemovePatch(pVM, pInstrGC);
5264 return VERR_PATCH_NOT_FOUND;
5265 }
5266
5267 rc = patmActivateInt3Patch(pVM, pPatch);
5268 if (VBOX_FAILURE(rc))
5269 return rc;
5270 }
5271
5272 pPatch->uState = pPatch->uOldState; //restore state
5273
5274 /* Restore the entry breakpoint with the original opcode (see PATMR3DisablePatch). */
5275 if (pPatch->pPatchBlockOffset)
5276 {
5277 *PATCHCODE_PTR_HC(pPatch) = pPatch->bDirtyOpcode;
5278 }
5279
5280 STAM_COUNTER_ADD(&pVM->patm.s.StatEnabled, 1);
5281 }
5282 else
5283 Log(("PATMR3EnablePatch: Unable to enable patch %VGv with state %d\n", pInstrGC, pPatch->uState));
5284
5285 return rc;
5286 }
5287 return VERR_PATCH_NOT_FOUND;
5288}
5289
5290/**
5291 * Remove patch for privileged instruction at specified location
5292 *
5293 * @returns VBox status code.
5294 * @param pVM The VM to operate on.
5295 * @param pPatchRec Patch record
5296 * @param fForceRemove Remove *all* patches
5297 */
5298int PATMRemovePatch(PVM pVM, PPATMPATCHREC pPatchRec, bool fForceRemove)
5299{
5300 PPATCHINFO pPatch;
5301
5302 pPatch = &pPatchRec->patch;
5303
5304 /* Strictly forbidden to remove such patches. There can be dependencies!! */
5305 AssertReturn(fForceRemove || !(pPatch->flags & (PATMFL_DUPLICATE_FUNCTION)), VERR_ACCESS_DENIED);
5306
5307 /** @note NEVER EVER REUSE PATCH MEMORY */
5308 /** @note PATMR3DisablePatch put a breakpoint (0xCC) at the entry of this patch */
5309
5310 if (pPatchRec->patch.pPatchBlockOffset)
5311 {
5312 PAVLOGCPTRNODECORE pNode;
5313
5314 pNode = RTAvloGCPtrRemove(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, pPatchRec->patch.pPatchBlockOffset);
5315 Assert(pNode);
5316 }
5317
5318 if (pPatchRec->patch.flags & PATMFL_CODE_MONITORED)
5319 {
5320 int rc = patmRemovePatchPages(pVM, &pPatchRec->patch);
5321 AssertRC(rc);
5322 }
5323
5324#ifdef VBOX_WITH_STATISTICS
5325 if (PATM_STAT_INDEX_IS_VALID(pPatchRec->patch.uPatchIdx))
5326 {
5327 STAMR3Deregister(pVM, &pPatchRec->patch);
5328#ifndef DEBUG_sandervl
5329 STAMR3Deregister(pVM, &pVM->patm.s.pStatsHC[pPatchRec->patch.uPatchIdx]);
5330 STAMR3Deregister(pVM, &pPatchRec->patch.cbPatchBlockSize);
5331 STAMR3Deregister(pVM, &pPatchRec->patch.cbPatchJump);
5332 STAMR3Deregister(pVM, &pPatchRec->patch.cbPrivInstr);
5333 STAMR3Deregister(pVM, &pPatchRec->patch.cCodeWrites);
5334 STAMR3Deregister(pVM, &pPatchRec->patch.cInvalidWrites);
5335 STAMR3Deregister(pVM, &pPatchRec->patch.cTraps);
5336 STAMR3Deregister(pVM, &pPatchRec->patch.flags);
5337 STAMR3Deregister(pVM, &pPatchRec->patch.nrJumpRecs);
5338 STAMR3Deregister(pVM, &pPatchRec->patch.nrFixups);
5339 STAMR3Deregister(pVM, &pPatchRec->patch.opcode);
5340 STAMR3Deregister(pVM, &pPatchRec->patch.uState);
5341 STAMR3Deregister(pVM, &pPatchRec->patch.uOldState);
5342 STAMR3Deregister(pVM, &pPatchRec->patch.uOpMode);
5343#endif
5344 }
5345#endif
5346
5347 /** @note no need to free Guest2PatchAddrTree as those records share memory with Patch2GuestAddrTree records. */
5348 patmEmptyTreeU32(pVM, &pPatch->Patch2GuestAddrTree);
5349 pPatch->nrPatch2GuestRecs = 0;
5350 Assert(pPatch->Patch2GuestAddrTree == 0);
5351
5352 patmEmptyTree(pVM, &pPatch->FixupTree);
5353 pPatch->nrFixups = 0;
5354 Assert(pPatch->FixupTree == 0);
5355
5356 if (pPatchRec->patch.pTempInfo)
5357 MMR3HeapFree(pPatchRec->patch.pTempInfo);
5358
5359 /** @note might fail, because it has already been removed (e.g. during reset). */
5360 RTAvloGCPtrRemove(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pPatchRec->Core.Key);
5361
5362 /* Free the patch record */
5363 MMHyperFree(pVM, pPatchRec);
5364 return VINF_SUCCESS;
5365}
5366
5367/**
5368 * Attempt to refresh the patch by recompiling its entire code block
5369 *
5370 * @returns VBox status code.
5371 * @param pVM The VM to operate on.
5372 * @param pPatchRec Patch record
5373 */
5374int patmR3RefreshPatch(PVM pVM, PPATMPATCHREC pPatchRec)
5375{
5376 PPATCHINFO pPatch;
5377 int rc;
5378 RTGCPTR pInstrGC = pPatchRec->patch.pPrivInstrGC;
5379
5380 Log(("patmR3RefreshPatch: attempt to refresh patch at %VGv\n", pInstrGC));
5381
5382 pPatch = &pPatchRec->patch;
5383 AssertReturn(pPatch->flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_IDTHANDLER|PATMFL_TRAPHANDLER), VERR_PATCHING_REFUSED);
5384 AssertReturn(!(pPatch->flags & PATMFL_EXTERNAL_JUMP_INSIDE), VERR_PATCHING_REFUSED);
5385
5386 /** Note: quite ugly to enable/disable/remove/insert old and new patches, but there's no easy way around it. */
5387
5388 rc = PATMR3DisablePatch(pVM, pInstrGC);
5389 AssertRC(rc);
5390
5391 /** Kick it out of the lookup tree to make sure PATMR3InstallPatch doesn't fail (hack alert) */
5392 RTAvloGCPtrRemove(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pPatchRec->Core.Key);
5393#ifdef VBOX_WITH_STATISTICS
5394 if (PATM_STAT_INDEX_IS_VALID(pPatchRec->patch.uPatchIdx))
5395 {
5396 STAMR3Deregister(pVM, &pPatchRec->patch);
5397#ifndef DEBUG_sandervl
5398 STAMR3Deregister(pVM, &pVM->patm.s.pStatsHC[pPatchRec->patch.uPatchIdx]);
5399 STAMR3Deregister(pVM, &pPatchRec->patch.cbPatchBlockSize);
5400 STAMR3Deregister(pVM, &pPatchRec->patch.cbPatchJump);
5401 STAMR3Deregister(pVM, &pPatchRec->patch.cbPrivInstr);
5402 STAMR3Deregister(pVM, &pPatchRec->patch.cCodeWrites);
5403 STAMR3Deregister(pVM, &pPatchRec->patch.cInvalidWrites);
5404 STAMR3Deregister(pVM, &pPatchRec->patch.cTraps);
5405 STAMR3Deregister(pVM, &pPatchRec->patch.flags);
5406 STAMR3Deregister(pVM, &pPatchRec->patch.nrJumpRecs);
5407 STAMR3Deregister(pVM, &pPatchRec->patch.nrFixups);
5408 STAMR3Deregister(pVM, &pPatchRec->patch.opcode);
5409 STAMR3Deregister(pVM, &pPatchRec->patch.uState);
5410 STAMR3Deregister(pVM, &pPatchRec->patch.uOldState);
5411 STAMR3Deregister(pVM, &pPatchRec->patch.uOpMode);
5412#endif
5413 }
5414#endif
5415
5416 /** Note: We don't attempt to reuse patch memory here as it's quite common that the new code block requires more memory. */
5417
5418 /* Attempt to install a new patch. */
5419 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));
5420 if (VBOX_SUCCESS(rc))
5421 {
5422 RTGCPTR pPatchTargetGC;
5423 PPATMPATCHREC pNewPatchRec;
5424
5425 /* Determine target address in new patch */
5426 pPatchTargetGC = PATMR3QueryPatchGCPtr(pVM, pInstrGC);
5427 Assert(pPatchTargetGC);
5428 if (!pPatchTargetGC)
5429 {
5430 rc = VERR_PATCHING_REFUSED;
5431 goto failure;
5432 }
5433
5434 /* Reset offset into patch memory to put the next code blocks right at the beginning. */
5435 pPatch->uCurPatchOffset = 0;
5436
5437 /* insert jump to new patch in old patch block */
5438 rc = patmPatchGenPatchJump(pVM, pPatch, pInstrGC, pPatchTargetGC, false /* no lookup record */);
5439 if (VBOX_FAILURE(rc))
5440 goto failure;
5441
5442 pNewPatchRec = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC);
5443 Assert(pNewPatchRec); /* can't fail */
5444
5445 /* Remove old patch (only do that when everything is finished) */
5446 int rc2 = PATMRemovePatch(pVM, pPatchRec, true /* force removal */);
5447 AssertRC(rc2);
5448
5449 /* Put the new patch back into the tree, because removing the old one kicked this one out. (hack alert) */
5450 RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTree, &pNewPatchRec->Core);
5451
5452 LogRel(("PATM: patmR3RefreshPatch: succeeded to refresh patch at %VGv \n", pInstrGC));
5453 STAM_COUNTER_INC(&pVM->patm.s.StatPatchRefreshSuccess);
5454 }
5455
5456failure:
5457 if (VBOX_FAILURE(rc))
5458 {
5459 LogRel(("PATM: patmR3RefreshPatch: failed to refresh patch at %VGv. Reactiving old one. \n", pInstrGC));
5460
5461 /* Remove the new inactive patch */
5462 rc = PATMR3RemovePatch(pVM, pInstrGC);
5463 AssertRC(rc);
5464
5465 /* Put the old patch back into the tree (or else it won't be saved) (hack alert) */
5466 RTAvloGCPtrInsert(&pVM->patm.s.PatchLookupTreeHC->PatchTree, &pPatchRec->Core);
5467
5468 /* Enable again in case the dirty instruction is near the end and there are safe code paths. */
5469 int rc2 = PATMR3EnablePatch(pVM, pInstrGC);
5470 AssertRC(rc2);
5471
5472 STAM_COUNTER_INC(&pVM->patm.s.StatPatchRefreshFailed);
5473 }
5474 return rc;
5475}
5476
5477/**
5478 * Find patch for privileged instruction at specified location
5479 *
5480 * @returns Patch structure pointer if found; else NULL
5481 * @param pVM The VM to operate on.
5482 * @param pInstr Guest context point to instruction that might lie within 5 bytes of an existing patch jump
5483 * @param fIncludeHints Include hinted patches or not
5484 *
5485 */
5486PPATCHINFO PATMFindActivePatchByEntrypoint(PVM pVM, RTGCPTR pInstrGC, bool fIncludeHints)
5487{
5488 PPATMPATCHREC pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC, false);
5489 /* 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! */
5490 if (pPatchRec)
5491 {
5492 if ( pPatchRec->patch.uState == PATCH_ENABLED
5493 && (pPatchRec->patch.flags & PATMFL_PATCHED_GUEST_CODE)
5494 && pInstrGC > pPatchRec->patch.pPrivInstrGC
5495 && pInstrGC < pPatchRec->patch.pPrivInstrGC + pPatchRec->patch.cbPatchJump)
5496 {
5497 Log(("Found active patch at %VGv (org %VGv)\n", pInstrGC, pPatchRec->patch.pPrivInstrGC));
5498 return &pPatchRec->patch;
5499 }
5500 else
5501 if ( fIncludeHints
5502 && pPatchRec->patch.uState == PATCH_DISABLED
5503 && (pPatchRec->patch.flags & PATMFL_INSTR_HINT)
5504 && pInstrGC > pPatchRec->patch.pPrivInstrGC
5505 && pInstrGC < pPatchRec->patch.pPrivInstrGC + pPatchRec->patch.cbPatchJump)
5506 {
5507 Log(("Found HINT patch at %VGv (org %VGv)\n", pInstrGC, pPatchRec->patch.pPrivInstrGC));
5508 return &pPatchRec->patch;
5509 }
5510 }
5511 return NULL;
5512}
5513
5514/**
5515 * Checks whether the GC address is inside a generated patch jump
5516 *
5517 * @returns true -> yes, false -> no
5518 * @param pVM The VM to operate on.
5519 * @param pAddr Guest context address
5520 * @param pPatchAddr Guest context patch address (if true)
5521 */
5522PATMR3DECL(bool) PATMR3IsInsidePatchJump(PVM pVM, RTGCPTR pAddr, PRTGCPTR pPatchAddr)
5523{
5524 RTGCPTR addr;
5525 PPATCHINFO pPatch;
5526
5527 if (PATMIsEnabled(pVM) == false)
5528 return false;
5529
5530 if (pPatchAddr == NULL)
5531 pPatchAddr = &addr;
5532
5533 *pPatchAddr = 0;
5534
5535 pPatch = PATMFindActivePatchByEntrypoint(pVM, pAddr);
5536 if (pPatch)
5537 {
5538 *pPatchAddr = pPatch->pPrivInstrGC;
5539 }
5540 return *pPatchAddr == 0 ? false : true;
5541}
5542
5543/**
5544 * Remove patch for privileged instruction at specified location
5545 *
5546 * @returns VBox status code.
5547 * @param pVM The VM to operate on.
5548 * @param pInstr Guest context point to privileged instruction
5549 *
5550 * @note returns failure if patching is not allowed or possible
5551 *
5552 */
5553PATMR3DECL(int) PATMR3RemovePatch(PVM pVM, RTGCPTR pInstrGC)
5554{
5555 PPATMPATCHREC pPatchRec;
5556
5557 pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC);
5558 if (pPatchRec)
5559 {
5560 int rc = PATMR3DisablePatch(pVM, pInstrGC);
5561 if (rc == VWRN_PATCH_REMOVED)
5562 return VINF_SUCCESS;
5563 return PATMRemovePatch(pVM, pPatchRec, false);
5564 }
5565 AssertFailed();
5566 return VERR_PATCH_NOT_FOUND;
5567}
5568
5569/**
5570 * Mark patch as dirty
5571 *
5572 * @returns VBox status code.
5573 * @param pVM The VM to operate on.
5574 * @param pPatch Patch record
5575 *
5576 * @note returns failure if patching is not allowed or possible
5577 *
5578 */
5579PATMR3DECL(int) PATMR3MarkDirtyPatch(PVM pVM, PPATCHINFO pPatch)
5580{
5581 if (pPatch->pPatchBlockOffset)
5582 {
5583 Log(("Invalidate patch at %VGv (HC=%VGv)\n", PATCHCODE_PTR_GC(pPatch), PATCHCODE_PTR_HC(pPatch)));
5584 pPatch->bDirtyOpcode = *PATCHCODE_PTR_HC(pPatch);
5585 *PATCHCODE_PTR_HC(pPatch) = 0xCC;
5586 }
5587
5588 STAM_COUNTER_INC(&pVM->patm.s.StatDirty);
5589 /* Put back the replaced instruction. */
5590 int rc = PATMR3DisablePatch(pVM, pPatch->pPrivInstrGC);
5591 if (rc == VWRN_PATCH_REMOVED)
5592 return VINF_SUCCESS;
5593
5594 /** @note we don't restore patch pages for patches that are not enabled! */
5595 /** @note be careful when changing this behaviour!! */
5596
5597 /* The patch pages are no longer marked for self-modifying code detection */
5598 if (pPatch->flags & PATMFL_CODE_MONITORED)
5599 {
5600 int rc = patmRemovePatchPages(pVM, pPatch);
5601 AssertRCReturn(rc, rc);
5602 }
5603 pPatch->uState = PATCH_DIRTY;
5604
5605 /* Paranoia; make sure this patch is not somewhere in the callchain, so prevent ret instructions from succeeding. */
5606 CTXSUFF(pVM->patm.s.pGCState)->Psp = PATM_STACK_SIZE;
5607
5608 return VINF_SUCCESS;
5609}
5610
5611/**
5612 * Query the corresponding GC instruction pointer from a pointer inside the patch block itself
5613 *
5614 * @returns VBox status code.
5615 * @param pVM The VM to operate on.
5616 * @param pPatch Patch block structure pointer
5617 * @param pPatchGC GC address in patch block
5618 */
5619RTGCPTR patmPatchGCPtr2GuestGCPtr(PVM pVM, PPATCHINFO pPatch, GCPTRTYPE(uint8_t *) pPatchGC)
5620{
5621 Assert(pPatch->Patch2GuestAddrTree);
5622 /* Get the closest record from below. */
5623 PRECPATCHTOGUEST pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatch->Patch2GuestAddrTree, pPatchGC - pVM->patm.s.pPatchMemGC, false);
5624 if (pPatchToGuestRec)
5625 return pPatchToGuestRec->pOrgInstrGC;
5626
5627 return 0;
5628}
5629
5630/* Converts Guest code GC ptr to Patch code GC ptr (if found)
5631 *
5632 * @returns corresponding GC pointer in patch block
5633 * @param pVM The VM to operate on.
5634 * @param pPatch Current patch block pointer
5635 * @param pInstrGC Guest context pointer to privileged instruction
5636 *
5637 */
5638RTGCPTR patmGuestGCPtrToPatchGCPtr(PVM pVM, PPATCHINFO pPatch, GCPTRTYPE(uint8_t*) pInstrGC)
5639{
5640 if (pPatch->Guest2PatchAddrTree)
5641 {
5642 PRECGUESTTOPATCH pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlGCPtrGet(&pPatch->Guest2PatchAddrTree, pInstrGC);
5643 if (pGuestToPatchRec)
5644 return pVM->patm.s.pPatchMemGC + pGuestToPatchRec->PatchOffset;
5645 }
5646
5647 return 0;
5648}
5649
5650/* Converts Guest code GC ptr to Patch code GC ptr (or nearest from below if no identical match)
5651 *
5652 * @returns corresponding GC pointer in patch block
5653 * @param pVM The VM to operate on.
5654 * @param pPatch Current patch block pointer
5655 * @param pInstrGC Guest context pointer to privileged instruction
5656 *
5657 */
5658RTGCPTR patmGuestGCPtrToClosestPatchGCPtr(PVM pVM, PPATCHINFO pPatch, GCPTRTYPE(uint8_t*) pInstrGC)
5659{
5660 PRECGUESTTOPATCH pGuestToPatchRec = (PRECGUESTTOPATCH)RTAvlGCPtrGetBestFit(&pPatch->Guest2PatchAddrTree, pInstrGC, false);
5661 if (pGuestToPatchRec)
5662 return pVM->patm.s.pPatchMemGC + pGuestToPatchRec->PatchOffset;
5663
5664 return 0;
5665}
5666
5667/* Converts Guest code GC ptr to Patch code GC ptr (if found)
5668 *
5669 * @returns corresponding GC pointer in patch block
5670 * @param pVM The VM to operate on.
5671 * @param pInstrGC Guest context pointer to privileged instruction
5672 *
5673 */
5674PATMR3DECL(RTGCPTR) PATMR3GuestGCPtrToPatchGCPtr(PVM pVM, GCPTRTYPE(uint8_t*) pInstrGC)
5675{
5676 PPATMPATCHREC pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pInstrGC, false);
5677 if (pPatchRec && pPatchRec->patch.uState == PATCH_ENABLED && pInstrGC >= pPatchRec->patch.pPrivInstrGC)
5678 {
5679 return patmGuestGCPtrToPatchGCPtr(pVM, &pPatchRec->patch, pInstrGC);
5680 }
5681 return 0;
5682}
5683
5684/**
5685 * Query the corresponding GC instruction pointer from a pointer inside the patch block itself
5686 *
5687 * @returns original GC instruction pointer or 0 if not found
5688 * @param pVM The VM to operate on.
5689 * @param pPatchGC GC address in patch block
5690 * @param pEnmState State of the translated address (out)
5691 *
5692 */
5693PATMR3DECL(RTGCPTR) PATMR3PatchToGCPtr(PVM pVM, RTGCPTR pPatchGC, PATMTRANSSTATE *pEnmState)
5694{
5695 PPATMPATCHREC pPatchRec;
5696 void *pvPatchCoreOffset;
5697 RTGCPTR pPrivInstrGC;
5698
5699 Assert(PATMIsPatchGCAddr(pVM, pPatchGC));
5700 pvPatchCoreOffset = RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, pPatchGC - pVM->patm.s.pPatchMemGC, false);
5701 if (pvPatchCoreOffset == 0)
5702 {
5703 Log(("PATMR3PatchToGCPtr failed for %VGv offset %x\n", pPatchGC, pPatchGC - pVM->patm.s.pPatchMemGC));
5704 return 0;
5705 }
5706 pPatchRec = PATM_PATCHREC_FROM_COREOFFSET(pvPatchCoreOffset);
5707 pPrivInstrGC = patmPatchGCPtr2GuestGCPtr(pVM, &pPatchRec->patch, pPatchGC);
5708 if (pEnmState)
5709 {
5710 AssertMsg(pPrivInstrGC && ( pPatchRec->patch.uState == PATCH_ENABLED
5711 || pPatchRec->patch.uState == PATCH_DIRTY
5712 || pPatchRec->patch.uState == PATCH_DISABLE_PENDING
5713 || pPatchRec->patch.uState == PATCH_UNUSABLE),
5714 ("pPrivInstrGC=%VGv uState=%d\n", pPrivInstrGC, pPatchRec->patch.uState));
5715
5716 if ( !pPrivInstrGC
5717 || pPatchRec->patch.uState == PATCH_UNUSABLE
5718 || pPatchRec->patch.uState == PATCH_REFUSED)
5719 {
5720 pPrivInstrGC = 0;
5721 *pEnmState = PATMTRANS_FAILED;
5722 }
5723 else
5724 if (pVM->patm.s.pGCStateHC->GCPtrInhibitInterrupts == pPrivInstrGC)
5725 {
5726 *pEnmState = PATMTRANS_INHIBITIRQ;
5727 }
5728 else
5729 if ( pPatchRec->patch.uState == PATCH_ENABLED
5730 && !(pPatchRec->patch.flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_IDTHANDLER|PATMFL_TRAMPOLINE))
5731 && pPrivInstrGC > pPatchRec->patch.pPrivInstrGC
5732 && pPrivInstrGC < pPatchRec->patch.pPrivInstrGC + pPatchRec->patch.cbPatchJump)
5733 {
5734 *pEnmState = PATMTRANS_OVERWRITTEN;
5735 }
5736 else
5737 if (PATMFindActivePatchByEntrypoint(pVM, pPrivInstrGC))
5738 {
5739 *pEnmState = PATMTRANS_OVERWRITTEN;
5740 }
5741 else
5742 if (pPrivInstrGC == pPatchRec->patch.pPrivInstrGC)
5743 {
5744 *pEnmState = PATMTRANS_PATCHSTART;
5745 }
5746 else
5747 *pEnmState = PATMTRANS_SAFE;
5748 }
5749 return pPrivInstrGC;
5750}
5751
5752/**
5753 * Returns the GC pointer of the patch for the specified GC address
5754 *
5755 * @returns VBox status code.
5756 * @param pVM The VM to operate on.
5757 * @param pAddrGC Guest context address
5758 */
5759PATMR3DECL(RTGCPTR) PATMR3QueryPatchGCPtr(PVM pVM, RTGCPTR pAddrGC)
5760{
5761 PPATMPATCHREC pPatchRec;
5762
5763 // Find the patch record
5764 pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGet(&pVM->patm.s.PatchLookupTreeHC->PatchTree, pAddrGC);
5765 /** @todo we should only use patches that are enabled! always did this, but it's incorrect! */
5766 if (pPatchRec && (pPatchRec->patch.uState == PATCH_ENABLED || pPatchRec->patch.uState == PATCH_DIRTY))
5767 return PATCHCODE_PTR_GC(&pPatchRec->patch);
5768
5769 return 0;
5770}
5771
5772/**
5773 * Attempt to recover dirty instructions
5774 *
5775 * @returns VBox status code.
5776 * @param pVM The VM to operate on.
5777 * @param pCtx CPU context
5778 * @param pPatch Patch record
5779 * @param pPatchToGuestRec Patch to guest address record
5780 * @param pEip GC pointer of trapping instruction
5781 */
5782static int patmR3HandleDirtyInstr(PVM pVM, PCPUMCTX pCtx, PPATMPATCHREC pPatch, PRECPATCHTOGUEST pPatchToGuestRec, RTGCPTR pEip)
5783{
5784 DISCPUSTATE CpuOld, CpuNew;
5785 uint8_t *pPatchInstrHC, *pCurPatchInstrHC;
5786 int rc;
5787 RTGCPTR pCurInstrGC, pCurPatchInstrGC;
5788 uint32_t cbDirty;
5789 PRECPATCHTOGUEST pRec;
5790
5791 Log(("patmR3HandleDirtyInstr: dirty instruction at %VGv (%VGv)\n", pEip, pPatchToGuestRec->pOrgInstrGC));
5792
5793 pRec = pPatchToGuestRec;
5794 pCurInstrGC = pPatchToGuestRec->pOrgInstrGC;
5795 pCurPatchInstrGC = pEip;
5796 cbDirty = 0;
5797 pPatchInstrHC = patmPatchGCPtr2PatchHCPtr(pVM, pCurPatchInstrGC);
5798
5799 /* Find all adjacent dirty instructions */
5800 while (true)
5801 {
5802 if (pRec->fJumpTarget)
5803 {
5804 LogRel(("PATM: patmR3HandleDirtyInstr: dirty instruction at %VGv (%VGv) ignored, because instruction in function was reused as target of jump\n", pEip, pPatchToGuestRec->pOrgInstrGC));
5805 pRec->fDirty = false;
5806 return VERR_PATCHING_REFUSED;
5807 }
5808
5809 /* Restore original instruction opcode byte so we can check if the write was indeed safe. */
5810 pCurPatchInstrHC = patmPatchGCPtr2PatchHCPtr(pVM, pCurPatchInstrGC);
5811 *pCurPatchInstrHC = pRec->u8DirtyOpcode;
5812
5813 /* Only harmless instructions are acceptable. */
5814 rc = CPUMR3DisasmInstrCPU(pVM, pCtx, pCurPatchInstrGC, &CpuOld, 0);
5815 if ( VBOX_FAILURE(rc)
5816 || !(CpuOld.pCurInstr->optype & OPTYPE_HARMLESS))
5817 break;
5818
5819#ifdef DEBUG
5820 char szBuf[256];
5821 szBuf[0] = '\0';
5822 DBGFR3DisasInstr(pVM, pCtx->cs, pCurPatchInstrGC, szBuf, sizeof(szBuf));
5823 Log(("DIRTY: %s\n", szBuf));
5824#endif
5825 /** Remove old lookup record. */
5826 patmr3RemoveP2GLookupRecord(pVM, &pPatch->patch, pCurPatchInstrGC);
5827
5828 pCurPatchInstrGC += CpuOld.opsize;
5829 cbDirty += CpuOld.opsize;
5830
5831 /* Mark as clean; if we fail we'll let it always fault. */
5832 pRec->fDirty = false;
5833
5834 /* Let's see if there's another dirty instruction right after. */
5835 pRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatch->patch.Patch2GuestAddrTree, pCurPatchInstrGC - pVM->patm.s.pPatchMemGC, true);
5836 if (!pRec || !pRec->fDirty)
5837 break; /* no more dirty instructions */
5838 }
5839
5840 if ( VBOX_SUCCESS(rc)
5841 && (CpuOld.pCurInstr->optype & OPTYPE_HARMLESS)
5842 )
5843 {
5844 uint32_t cbLeft;
5845
5846 pCurPatchInstrHC = pPatchInstrHC;
5847 pCurPatchInstrGC = pEip;
5848 cbLeft = cbDirty;
5849
5850 while (cbLeft && VBOX_SUCCESS(rc))
5851 {
5852 bool fValidInstr;
5853
5854 rc = CPUMR3DisasmInstrCPU(pVM, pCtx, pCurInstrGC, &CpuNew, 0);
5855
5856 fValidInstr = !!(CpuNew.pCurInstr->optype & OPTYPE_HARMLESS);
5857 if ( !fValidInstr
5858 && (CpuNew.pCurInstr->optype & OPTYPE_RELATIVE_CONTROLFLOW)
5859 )
5860 {
5861 RTGCPTR pTargetGC = PATMResolveBranch(&CpuNew, pCurInstrGC);
5862
5863 if ( pTargetGC >= pPatchToGuestRec->pOrgInstrGC
5864 && pTargetGC <= pPatchToGuestRec->pOrgInstrGC + cbDirty
5865 )
5866 {
5867 /* A relative jump to an instruction inside or to the end of the dirty block is acceptable. */
5868 fValidInstr = true;
5869 }
5870 }
5871
5872 /* If the instruction is completely harmless (which implies a 1:1 patch copy). */
5873 if ( rc == VINF_SUCCESS
5874 && CpuNew.opsize <= cbLeft /* must still fit */
5875 && fValidInstr
5876 )
5877 {
5878#ifdef DEBUG
5879 char szBuf[256];
5880 szBuf[0] = '\0';
5881 DBGFR3DisasInstr(pVM, pCtx->cs, pCurInstrGC, szBuf, sizeof(szBuf));
5882 Log(("NEW: %s\n", szBuf));
5883#endif
5884
5885 /* Copy the new instruction. */
5886 rc = PGMPhysReadGCPtr(pVM, pCurPatchInstrHC, pCurInstrGC, CpuNew.opsize);
5887 AssertRC(rc);
5888
5889 /* Add a new lookup record for the duplicated instruction. */
5890 patmr3AddP2GLookupRecord(pVM, &pPatch->patch, pCurPatchInstrHC, pCurInstrGC, PATM_LOOKUP_BOTHDIR);
5891 }
5892 else
5893 {
5894#ifdef DEBUG
5895 char szBuf[256];
5896 szBuf[0] = '\0';
5897 DBGFR3DisasInstr(pVM, pCtx->cs, pCurInstrGC, szBuf, sizeof(szBuf));
5898 Log(("NEW: %s (FAILED)\n", szBuf));
5899#endif
5900 /* Restore the old lookup record for the duplicated instruction. */
5901 patmr3AddP2GLookupRecord(pVM, &pPatch->patch, pCurPatchInstrHC, pCurInstrGC, PATM_LOOKUP_BOTHDIR);
5902
5903 /** @todo in theory we need to restore the lookup records for the remaining dirty instructions too! */
5904 rc = VERR_PATCHING_REFUSED;
5905 break;
5906 }
5907 pCurInstrGC += CpuNew.opsize;
5908 pCurPatchInstrHC += CpuNew.opsize;
5909 pCurPatchInstrGC += CpuNew.opsize;
5910 cbLeft -= CpuNew.opsize;
5911 }
5912 }
5913 else
5914 rc = VERR_PATCHING_REFUSED;
5915
5916 if (VBOX_SUCCESS(rc))
5917 {
5918 STAM_COUNTER_INC(&pVM->patm.s.StatInstrDirtyGood);
5919 }
5920 else
5921 {
5922 STAM_COUNTER_INC(&pVM->patm.s.StatInstrDirtyBad);
5923 /* Mark the whole instruction stream with breakpoints. */
5924 memset(pPatchInstrHC, 0xCC, cbDirty);
5925
5926 if ( pVM->patm.s.fOutOfMemory == false
5927 && (pPatch->patch.flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_IDTHANDLER|PATMFL_TRAPHANDLER)))
5928 {
5929 rc = patmR3RefreshPatch(pVM, pPatch);
5930 if (VBOX_FAILURE(rc))
5931 {
5932 LogRel(("PATM: Failed to refresh dirty patch at %VGv. Disabling it.\n", pPatch->patch.pPrivInstrGC));
5933 }
5934 /* Even if we succeed, we must go back to the original instruction as the patched one could be invalid. */
5935 rc = VERR_PATCHING_REFUSED;
5936 }
5937 }
5938 return rc;
5939}
5940
5941/**
5942 * Handle trap inside patch code
5943 *
5944 * @returns VBox status code.
5945 * @param pVM The VM to operate on.
5946 * @param pCtx CPU context
5947 * @param pEip GC pointer of trapping instruction
5948 * @param ppNewEip GC pointer to new instruction
5949 */
5950PATMR3DECL(int) PATMR3HandleTrap(PVM pVM, PCPUMCTX pCtx, RTGCPTR pEip, RTGCPTR *ppNewEip)
5951{
5952 PPATMPATCHREC pPatch = 0;
5953 void *pvPatchCoreOffset;
5954 RTGCUINTPTR offset;
5955 RTGCPTR pNewEip;
5956 int rc ;
5957 PRECPATCHTOGUEST pPatchToGuestRec = 0;
5958
5959 pNewEip = 0;
5960 *ppNewEip = 0;
5961
5962 STAM_PROFILE_ADV_START(&pVM->patm.s.StatHandleTrap, a);
5963
5964 /* Find the patch record. */
5965 /** @note there might not be a patch to guest translation record (global function) */
5966 offset = pEip - pVM->patm.s.pPatchMemGC;
5967 pvPatchCoreOffset = RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTreeByPatchAddr, offset, false);
5968 if (pvPatchCoreOffset)
5969 {
5970 pPatch = PATM_PATCHREC_FROM_COREOFFSET(pvPatchCoreOffset);
5971
5972 if (pPatch->patch.uState == PATCH_DIRTY)
5973 {
5974 Log(("PATMR3HandleTrap: trap in dirty patch at %VGv\n", pEip));
5975 if (pPatch->patch.flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_CALLABLE_AS_FUNCTION))
5976 {
5977 /* Function duplication patches set fPIF to 1 on entry */
5978 pVM->patm.s.pGCStateHC->fPIF = 1;
5979 }
5980 }
5981 else
5982 if (pPatch->patch.uState == PATCH_DISABLED)
5983 {
5984 Log(("PATMR3HandleTrap: trap in disabled patch at %VGv\n", pEip));
5985 if (pPatch->patch.flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_CALLABLE_AS_FUNCTION))
5986 {
5987 /* Function duplication patches set fPIF to 1 on entry */
5988 pVM->patm.s.pGCStateHC->fPIF = 1;
5989 }
5990 }
5991 else
5992 if (pPatch->patch.uState == PATCH_DISABLE_PENDING)
5993 {
5994 RTGCPTR pPrivInstrGC = pPatch->patch.pPrivInstrGC;
5995
5996 Log(("PATMR3HandleTrap: disable operation is pending for patch at %VGv\n", pPatch->patch.pPrivInstrGC));
5997 rc = PATMR3DisablePatch(pVM, pPatch->patch.pPrivInstrGC);
5998 AssertReleaseMsg(rc != VWRN_PATCH_REMOVED, ("PATMR3DisablePatch removed patch at %VGv\n", pPrivInstrGC));
5999 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));
6000 }
6001
6002 pPatchToGuestRec = (PRECPATCHTOGUEST)RTAvlU32GetBestFit(&pPatch->patch.Patch2GuestAddrTree, offset, false);
6003 AssertReleaseMsg(pPatchToGuestRec, ("PATMR3HandleTrap: Unable to find corresponding guest address for %VGv (offset %x)\n", pEip, offset));
6004
6005 pNewEip = pPatchToGuestRec->pOrgInstrGC;
6006 pPatch->patch.cTraps++;
6007 PATM_STAT_FAULT_INC(&pPatch->patch);
6008 }
6009 else
6010 AssertReleaseMsg(pVM->patm.s.pGCStateHC->fPIF == 0, ("PATMR3HandleTrap: Unable to find translation record for %VGv (PIF=0)\n", pEip));
6011
6012 /* Check if we were interrupted in PATM generated instruction code. */
6013 if (pVM->patm.s.pGCStateHC->fPIF == 0)
6014 {
6015 DISCPUSTATE Cpu;
6016 rc = CPUMR3DisasmInstrCPU(pVM, pCtx, pEip, &Cpu, "PIF Trap: ");
6017 AssertRC(rc);
6018
6019 if ( rc == VINF_SUCCESS
6020 && ( Cpu.pCurInstr->opcode == OP_PUSHF
6021 || Cpu.pCurInstr->opcode == OP_PUSH
6022 || Cpu.pCurInstr->opcode == OP_CALL)
6023 )
6024 {
6025 uint64_t fFlags;
6026
6027 STAM_COUNTER_INC(&pVM->patm.s.StatPushTrap);
6028
6029 if (Cpu.pCurInstr->opcode == OP_PUSH)
6030 {
6031 rc = PGMShwGetPage(pVM, pCtx->esp, &fFlags, NULL);
6032 if ( rc == VINF_SUCCESS
6033 && ((fFlags & (X86_PTE_P|X86_PTE_RW)) == (X86_PTE_P|X86_PTE_RW)) )
6034 {
6035 /* The stack address is fine, so the push argument is a pointer -> emulate this instruction */
6036
6037 /* Reset the PATM stack. */
6038 CTXSUFF(pVM->patm.s.pGCState)->Psp = PATM_STACK_SIZE;
6039
6040 pVM->patm.s.pGCStateHC->fPIF = 1;
6041
6042 Log(("Faulting push -> go back to the original instruction\n"));
6043
6044 /* continue at the original instruction */
6045 *ppNewEip = pNewEip - SELMToFlat(pVM, pCtx->eflags, pCtx->cs, &pCtx->csHid, 0);
6046 STAM_PROFILE_ADV_STOP(&pVM->patm.s.StatHandleTrap, a);
6047 return VINF_SUCCESS;
6048 }
6049 }
6050
6051 /* Typical pushf (most patches)/push (call patch) trap because of a monitored page. */
6052 rc = PGMShwModifyPage(pVM, pCtx->esp, 1, X86_PTE_RW, ~(uint64_t)X86_PTE_RW);
6053 AssertMsgRC(rc, ("PGMShwModifyPage -> rc=%Vrc\n", rc));
6054 if (rc == VINF_SUCCESS)
6055 {
6056
6057 /* The guest page *must* be present. */
6058 rc = PGMGstGetPage(pVM, pCtx->esp, &fFlags, NULL);
6059 if (rc == VINF_SUCCESS && (fFlags & X86_PTE_P))
6060 {
6061 STAM_PROFILE_ADV_STOP(&pVM->patm.s.StatHandleTrap, a);
6062 return VINF_PATCH_CONTINUE;
6063 }
6064 }
6065 }
6066
6067 char szBuf[256];
6068 szBuf[0] = '\0';
6069 DBGFR3DisasInstr(pVM, pCtx->cs, pEip, szBuf, sizeof(szBuf));
6070
6071 /* Very bad. We crashed in emitted code. Probably stack? */
6072 if (pPatch)
6073 {
6074 AssertReleaseMsg(pVM->patm.s.pGCStateHC->fPIF == 1,
6075 ("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));
6076 }
6077 else
6078 AssertReleaseMsg(pVM->patm.s.pGCStateHC->fPIF == 1,
6079 ("Crash in patch code %VGv (%VGv) esp=%RX32\n%s\n", pEip, pNewEip, CPUMGetGuestESP(pVM), szBuf));
6080 EMR3FatalError(pVM, VERR_INTERNAL_ERROR);
6081 }
6082
6083 /* From here on, we must have a valid patch to guest translation. */
6084 if (pvPatchCoreOffset == 0)
6085 {
6086 STAM_PROFILE_ADV_STOP(&pVM->patm.s.StatHandleTrap, a);
6087 AssertMsgFailed(("PATMR3HandleTrap: patch not found at address %VGv!!\n", pEip));
6088 return VERR_PATCH_NOT_FOUND; //fatal error
6089 }
6090
6091 /* Take care of dirty/changed instructions. */
6092 if (pPatchToGuestRec->fDirty)
6093 {
6094 Assert(pPatchToGuestRec->Core.Key == offset);
6095 Assert(pVM->patm.s.pGCStateHC->fPIF == 1);
6096
6097 rc = patmR3HandleDirtyInstr(pVM, pCtx, pPatch, pPatchToGuestRec, pEip);
6098 if (VBOX_SUCCESS(rc))
6099 {
6100 /* Retry the current instruction. */
6101 pNewEip = pEip;
6102 rc = VINF_PATCH_CONTINUE; /* Continue at current patch instruction. */
6103 }
6104 else
6105 {
6106 /* Reset the PATM stack. */
6107 CTXSUFF(pVM->patm.s.pGCState)->Psp = PATM_STACK_SIZE;
6108
6109 rc = VINF_SUCCESS; /* Continue at original instruction. */
6110 }
6111
6112 *ppNewEip = pNewEip - SELMToFlat(pVM, pCtx->eflags, pCtx->cs, &pCtx->csHid, 0);
6113 STAM_PROFILE_ADV_STOP(&pVM->patm.s.StatHandleTrap, a);
6114 return rc;
6115 }
6116
6117#ifdef VBOX_STRICT
6118 if (pPatch->patch.flags & PATMFL_DUPLICATE_FUNCTION)
6119 {
6120 DISCPUSTATE cpu;
6121 bool disret;
6122 uint32_t opsize;
6123
6124 cpu.mode = (pPatch->patch.flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
6125 disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pNewEip, PATMGCVirtToHCVirt(pVM, &pPatch->patch, pNewEip), &opsize, NULL, PATMREAD_RAWCODE);
6126 if (disret && cpu.pCurInstr->opcode == OP_RETN)
6127 {
6128 RTGCPTR retaddr;
6129 PCPUMCTX pCtx;
6130 int rc;
6131
6132 rc = CPUMQueryGuestCtxPtr(pVM, &pCtx);
6133 AssertRC(rc);
6134
6135 rc = PGMPhysReadGCPtr(pVM, &retaddr, pCtx->esp, sizeof(retaddr));
6136 AssertRC(rc);
6137
6138 Log(("Return failed at %VGv (%VGv)\n", pEip, pNewEip));
6139 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));
6140 }
6141 }
6142#endif
6143
6144 /* Return original address, correct by subtracting the CS base address. */
6145 *ppNewEip = pNewEip - SELMToFlat(pVM, pCtx->eflags, pCtx->cs, &pCtx->csHid, 0);
6146
6147 /* Reset the PATM stack. */
6148 CTXSUFF(pVM->patm.s.pGCState)->Psp = PATM_STACK_SIZE;
6149
6150 if (pVM->patm.s.pGCStateHC->GCPtrInhibitInterrupts == pNewEip)
6151 {
6152 /* Must be a faulting instruction after sti; currently only sysexit, hlt or iret */
6153 Log(("PATMR3HandleTrap %VGv -> inhibit irqs set!\n", pEip));
6154#ifdef VBOX_STRICT
6155 DISCPUSTATE cpu;
6156 bool disret;
6157 uint32_t opsize;
6158
6159 cpu.mode = (pPatch->patch.flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
6160 disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pNewEip, PATMGCVirtToHCVirt(pVM, &pPatch->patch, pNewEip), &opsize, NULL, PATMREAD_ORGCODE);
6161
6162 if (disret && (cpu.pCurInstr->opcode == OP_SYSEXIT || cpu.pCurInstr->opcode == OP_HLT || cpu.pCurInstr->opcode == OP_INT3))
6163 {
6164 cpu.mode = (pPatch->patch.flags & PATMFL_CODE32) ? CPUMODE_32BIT : CPUMODE_16BIT;
6165 disret = PATMR3DISInstr(pVM, &pPatch->patch, &cpu, pNewEip, PATMGCVirtToHCVirt(pVM, &pPatch->patch, pNewEip), &opsize, NULL, PATMREAD_RAWCODE);
6166
6167 Assert(cpu.pCurInstr->opcode == OP_SYSEXIT || cpu.pCurInstr->opcode == OP_HLT || cpu.pCurInstr->opcode == OP_IRET);
6168 }
6169#endif
6170 EMSetInhibitInterruptsPC(pVM, pNewEip);
6171 pVM->patm.s.pGCStateHC->GCPtrInhibitInterrupts = 0;
6172 }
6173
6174 Log2(("pPatchBlockGC %VGv - pEip %VGv corresponding GC address %VGv\n", PATCHCODE_PTR_GC(&pPatch->patch), pEip, pNewEip));
6175
6176 if (pNewEip >= pPatch->patch.pPrivInstrGC && pNewEip < pPatch->patch.pPrivInstrGC + pPatch->patch.cbPatchJump)
6177 {
6178 /* We can't jump back to code that we've overwritten with a 5 byte jump! */
6179 Log(("Disabling patch at location %VGv due to trap too close to the privileged instruction \n", pPatch->patch.pPrivInstrGC));
6180 PATMR3DisablePatch(pVM, pPatch->patch.pPrivInstrGC);
6181 STAM_PROFILE_ADV_STOP(&pVM->patm.s.StatHandleTrap, a);
6182 return VERR_PATCH_DISABLED;
6183 }
6184
6185#ifdef PATM_REMOVE_PATCH_ON_TOO_MANY_TRAPS
6186 /** @todo compare to nr of successful runs. add some aging algorithm and determine the best time to disable the patch */
6187 if (pPatch->patch.cTraps > MAX_PATCH_TRAPS)
6188 {
6189 Log(("Disabling patch at location %VGv due to too many traps inside patch code\n", pPatch->patch.pPrivInstrGC));
6190 //we are only wasting time, back out the patch
6191 PATMR3DisablePatch(pVM, pPatch->patch.pPrivInstrGC);
6192 pTrapRec->pNextPatchInstr = 0;
6193 STAM_PROFILE_ADV_STOP(&pVM->patm.s.StatHandleTrap, a);
6194 return VERR_PATCH_DISABLED;
6195 }
6196#endif
6197
6198 STAM_PROFILE_ADV_STOP(&pVM->patm.s.StatHandleTrap, a);
6199 return VINF_SUCCESS;
6200}
6201
6202
6203/**
6204 * Handle page-fault in monitored page
6205 *
6206 * @returns VBox status code.
6207 * @param pVM The VM to operate on.
6208 */
6209PATMR3DECL(int) PATMR3HandleMonitoredPage(PVM pVM)
6210{
6211 RTGCPTR addr = pVM->patm.s.pvFaultMonitor;
6212
6213 addr &= PAGE_BASE_GC_MASK;
6214
6215 int rc = PGMHandlerVirtualDeregister(pVM, addr);
6216 AssertRC(rc); NOREF(rc);
6217
6218 PPATMPATCHREC pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, addr, false);
6219 if (pPatchRec && pPatchRec->patch.uState == PATCH_ENABLED && PAGE_ADDRESS(pPatchRec->patch.pPrivInstrGC) == PAGE_ADDRESS(addr))
6220 {
6221 STAM_COUNTER_INC(&pVM->patm.s.StatMonitored);
6222 Log(("Renewing patch at %VGv\n", pPatchRec->patch.pPrivInstrGC));
6223 rc = PATMR3DisablePatch(pVM, pPatchRec->patch.pPrivInstrGC);
6224 if (rc == VWRN_PATCH_REMOVED)
6225 return VINF_SUCCESS;
6226
6227 PATMR3EnablePatch(pVM, pPatchRec->patch.pPrivInstrGC);
6228
6229 if (addr == pPatchRec->patch.pPrivInstrGC)
6230 addr++;
6231 }
6232
6233 for(;;)
6234 {
6235 pPatchRec = (PPATMPATCHREC)RTAvloGCPtrGetBestFit(&pVM->patm.s.PatchLookupTreeHC->PatchTree, addr, true);
6236
6237 if (!pPatchRec || PAGE_ADDRESS(pPatchRec->patch.pPrivInstrGC) != PAGE_ADDRESS(addr))
6238 break;
6239
6240 if (pPatchRec && pPatchRec->patch.uState == PATCH_ENABLED)
6241 {
6242 STAM_COUNTER_INC(&pVM->patm.s.StatMonitored);
6243 Log(("Renewing patch at %VGv\n", pPatchRec->patch.pPrivInstrGC));
6244 PATMR3DisablePatch(pVM, pPatchRec->patch.pPrivInstrGC);
6245 PATMR3EnablePatch(pVM, pPatchRec->patch.pPrivInstrGC);
6246 }
6247 addr = pPatchRec->patch.pPrivInstrGC + 1;
6248 }
6249
6250 pVM->patm.s.pvFaultMonitor = 0;
6251 return VINF_SUCCESS;
6252}
6253
6254
6255#ifdef VBOX_WITH_STATISTICS
6256
6257static const char *PATMPatchType(PVM pVM, PPATCHINFO pPatch)
6258{
6259 if (pPatch->flags & PATMFL_SYSENTER)
6260 {
6261 return "SYSENT";
6262 }
6263 else
6264 if (pPatch->flags & (PATMFL_TRAPHANDLER|PATMFL_INTHANDLER))
6265 {
6266 static char szTrap[16];
6267 uint32_t iGate;
6268
6269 iGate = TRPMR3QueryGateByHandler(pVM, PATCHCODE_PTR_GC(pPatch));
6270 if (iGate < 256)
6271 RTStrPrintf(szTrap, sizeof(szTrap), (pPatch->flags & PATMFL_INTHANDLER) ? "INT-%2X" : "TRAP-%2X", iGate);
6272 else
6273 RTStrPrintf(szTrap, sizeof(szTrap), (pPatch->flags & PATMFL_INTHANDLER) ? "INT-??" : "TRAP-??");
6274 return szTrap;
6275 }
6276 else
6277 if (pPatch->flags & (PATMFL_DUPLICATE_FUNCTION))
6278 return "DUPFUNC";
6279 else
6280 if (pPatch->flags & PATMFL_REPLACE_FUNCTION_CALL)
6281 return "FUNCCALL";
6282 else
6283 if (pPatch->flags & PATMFL_TRAMPOLINE)
6284 return "TRAMP";
6285 else
6286 return patmGetInstructionString(pPatch->opcode, pPatch->flags);
6287}
6288
6289static const char *PATMPatchState(PVM pVM, PPATCHINFO pPatch)
6290{
6291 switch(pPatch->uState)
6292 {
6293 case PATCH_ENABLED:
6294 return "ENA";
6295 case PATCH_DISABLED:
6296 return "DIS";
6297 case PATCH_DIRTY:
6298 return "DIR";
6299 case PATCH_UNUSABLE:
6300 return "UNU";
6301 case PATCH_REFUSED:
6302 return "REF";
6303 case PATCH_DISABLE_PENDING:
6304 return "DIP";
6305 default:
6306 AssertFailed();
6307 return " ";
6308 }
6309}
6310
6311/**
6312 * Resets the sample.
6313 * @param pVM The VM handle.
6314 * @param pvSample The sample registered using STAMR3RegisterCallback.
6315 */
6316static void patmResetStat(PVM pVM, void *pvSample)
6317{
6318 PPATCHINFO pPatch = (PPATCHINFO)pvSample;
6319 Assert(pPatch);
6320
6321 pVM->patm.s.pStatsHC[pPatch->uPatchIdx].u32A = 0;
6322 pVM->patm.s.pStatsHC[pPatch->uPatchIdx].u32B = 0;
6323}
6324
6325/**
6326 * Prints the sample into the buffer.
6327 *
6328 * @param pVM The VM handle.
6329 * @param pvSample The sample registered using STAMR3RegisterCallback.
6330 * @param pszBuf The buffer to print into.
6331 * @param cchBuf The size of the buffer.
6332 */
6333static void patmPrintStat(PVM pVM, void *pvSample, char *pszBuf, size_t cchBuf)
6334{
6335 PPATCHINFO pPatch = (PPATCHINFO)pvSample;
6336 Assert(pPatch);
6337
6338 Assert(pPatch->uState != PATCH_REFUSED);
6339 Assert(!(pPatch->flags & (PATMFL_REPLACE_FUNCTION_CALL|PATMFL_MMIO_ACCESS)));
6340
6341 RTStrPrintf(pszBuf, cchBuf, "size %04x ->%3s %8s - %08d - %08d",
6342 pPatch->cbPatchBlockSize, PATMPatchState(pVM, pPatch), PATMPatchType(pVM, pPatch),
6343 pVM->patm.s.pStatsHC[pPatch->uPatchIdx].u32A, pVM->patm.s.pStatsHC[pPatch->uPatchIdx].u32B);
6344}
6345
6346/**
6347 * Returns the GC address of the corresponding patch statistics counter
6348 *
6349 * @returns Stat address
6350 * @param pVM The VM to operate on.
6351 * @param pPatch Patch structure
6352 */
6353RTGCPTR patmPatchQueryStatAddress(PVM pVM, PPATCHINFO pPatch)
6354{
6355 Assert(pPatch->uPatchIdx != PATM_STAT_INDEX_NONE);
6356 return pVM->patm.s.pStatsGC + sizeof(STAMRATIOU32) * pPatch->uPatchIdx + RT_OFFSETOF(STAMRATIOU32, u32A);
6357}
6358
6359#endif /* VBOX_WITH_STATISTICS */
6360
6361#ifdef VBOX_WITH_DEBUGGER
6362/**
6363 * The '.patmoff' command.
6364 *
6365 * @returns VBox status.
6366 * @param pCmd Pointer to the command descriptor (as registered).
6367 * @param pCmdHlp Pointer to command helper functions.
6368 * @param pVM Pointer to the current VM (if any).
6369 * @param paArgs Pointer to (readonly) array of arguments.
6370 * @param cArgs Number of arguments in the array.
6371 */
6372static DECLCALLBACK(int) patmr3CmdOff(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
6373{
6374 /*
6375 * Validate input.
6376 */
6377 if (!pVM)
6378 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: The command requires VM to be selected.\n");
6379
6380 RTAvloGCPtrDoWithAll(&pVM->patm.s.PatchLookupTreeHC->PatchTree, true, DisableAllPatches, pVM);
6381 PATMR3AllowPatching(pVM, false);
6382 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Patching disabled\n");
6383}
6384
6385/**
6386 * The '.patmon' command.
6387 *
6388 * @returns VBox status.
6389 * @param pCmd Pointer to the command descriptor (as registered).
6390 * @param pCmdHlp Pointer to command helper functions.
6391 * @param pVM Pointer to the current VM (if any).
6392 * @param paArgs Pointer to (readonly) array of arguments.
6393 * @param cArgs Number of arguments in the array.
6394 */
6395static DECLCALLBACK(int) patmr3CmdOn(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PVM pVM, PCDBGCVAR paArgs, unsigned cArgs, PDBGCVAR pResult)
6396{
6397 /*
6398 * Validate input.
6399 */
6400 if (!pVM)
6401 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "error: The command requires VM to be selected.\n");
6402
6403 PATMR3AllowPatching(pVM, true);
6404 RTAvloGCPtrDoWithAll(&pVM->patm.s.PatchLookupTreeHC->PatchTree, true, EnableAllPatches, pVM);
6405 return pCmdHlp->pfnPrintf(pCmdHlp, NULL, "Patching enabled\n");
6406}
6407#endif
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette