VirtualBox

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

Last change on this file since 11147 was 9675, checked in by vboxsync, 16 years ago

General cleanup of SELMToFlat.

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

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