VirtualBox

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

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

CSAMR3CheckCode added

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