VirtualBox

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

Last change on this file since 520 was 519, checked in by vboxsync, 18 years ago

disabled two HC pointer size assertions that are probably invalid.

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