VirtualBox

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

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

Always check if the code selector is wide open. Refuse patching if that's not the case.

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