VirtualBox

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

Last change on this file since 45103 was 44399, checked in by vboxsync, 12 years ago

DBGF,DBGC,++: PVM -> PUVM. Some refactoring and cleanup as well.

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