VirtualBox

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

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

dtrace experiments.

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

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