VirtualBox

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

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

Forward iret to V86 code to our trap handler for emulation.

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