VirtualBox

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

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

Changed counting of patch page writes.

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