VirtualBox

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

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

Patch2GuestAddrTree / RECPATCHTOGUEST: changed from PV to U32 AVL tree. (first in a series of 64-bit fixes)

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette