VirtualBox

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

Last change on this file since 2009 was 1926, checked in by vboxsync, 18 years ago

Got rid of incorrect assertion.

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