VirtualBox

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

Last change on this file since 62338 was 62291, checked in by vboxsync, 8 years ago

Removed empty internal/pgm.h header file. (That stuff moved into VBox/vmm/pgm.h a long time ago. Internal APIs are using VMM_INT_DECL and similar now.)

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