VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/PATMAll.cpp@ 69222

Last change on this file since 69222 was 69111, checked in by vboxsync, 7 years ago

(C) year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 25.2 KB
Line 
1/* $Id: PATMAll.cpp 69111 2017-10-17 14:26:02Z vboxsync $ */
2/** @file
3 * PATM - The Patch Manager, all contexts.
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_PATM
23#include <VBox/vmm/patm.h>
24#include <VBox/vmm/cpum.h>
25#include <VBox/vmm/em.h>
26#include <VBox/vmm/hm.h>
27#include <VBox/vmm/selm.h>
28#include <VBox/vmm/mm.h>
29#include "PATMInternal.h"
30#include <VBox/vmm/vm.h>
31#include <VBox/vmm/vmm.h>
32#include "PATMA.h"
33
34#include <VBox/dis.h>
35#include <VBox/disopcode.h>
36#include <VBox/err.h>
37#include <VBox/log.h>
38#include <iprt/assert.h>
39#include <iprt/string.h>
40
41
42/**
43 * @callback_method_impl{FNPGMPHYSHANDLER, PATM all access handler callback.}
44 *
45 * @remarks The @a pvUser argument is the base address of the page being
46 * monitored.
47 */
48PGM_ALL_CB2_DECL(VBOXSTRICTRC)
49patmVirtPageHandler(PVM pVM, PVMCPU pVCpu, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf,
50 PGMACCESSTYPE enmAccessType, PGMACCESSORIGIN enmOrigin, void *pvUser)
51{
52 Assert(enmAccessType == PGMACCESSTYPE_WRITE); NOREF(enmAccessType);
53 NOREF(pvPtr); NOREF(pvBuf); NOREF(cbBuf); NOREF(enmOrigin); NOREF(pvUser);
54 RT_NOREF_PV(pVCpu);
55
56 Assert(pvUser);
57 Assert(!((uintptr_t)pvUser & PAGE_OFFSET_MASK));
58 Assert(((uintptr_t)pvUser + (GCPtr & PAGE_OFFSET_MASK)) == GCPtr);
59
60 pVM->patm.s.pvFaultMonitor = (RTRCPTR)GCPtr;
61#ifdef IN_RING3
62 PATMR3HandleMonitoredPage(pVM);
63 return VINF_PGM_HANDLER_DO_DEFAULT;
64#else
65 /* RC: Go handle this in ring-3. */
66 return VINF_PATM_CHECK_PATCH_PAGE;
67#endif
68}
69
70
71/**
72 * Load virtualized flags.
73 *
74 * This function is called from CPUMRawEnter(). It doesn't have to update the
75 * IF and IOPL eflags bits, the caller will enforce those to set and 0 respectively.
76 *
77 * @param pVM The cross context VM structure.
78 * @param pCtx The cpu context.
79 * @see pg_raw
80 */
81VMM_INT_DECL(void) PATMRawEnter(PVM pVM, PCPUMCTX pCtx)
82{
83 Assert(!HMIsEnabled(pVM));
84
85 /*
86 * Currently we don't bother to check whether PATM is enabled or not.
87 * For all cases where it isn't, IOPL will be safe and IF will be set.
88 */
89 uint32_t efl = pCtx->eflags.u32;
90 CTXSUFF(pVM->patm.s.pGCState)->uVMFlags = efl & PATM_VIRTUAL_FLAGS_MASK;
91
92 AssertMsg((efl & X86_EFL_IF) || PATMShouldUseRawMode(pVM, (RTRCPTR)pCtx->eip),
93 ("X86_EFL_IF is clear and PATM is disabled! (eip=%RRv eflags=%08x fPATM=%d pPATMGC=%RRv-%RRv\n",
94 pCtx->eip, pCtx->eflags.u32, PATMIsEnabled(pVM), pVM->patm.s.pPatchMemGC,
95 pVM->patm.s.pPatchMemGC + pVM->patm.s.cbPatchMem));
96
97 AssertReleaseMsg(CTXSUFF(pVM->patm.s.pGCState)->fPIF || PATMIsPatchGCAddr(pVM, pCtx->eip),
98 ("fPIF=%d eip=%RRv\n", pVM->patm.s.CTXSUFF(pGCState)->fPIF, pCtx->eip));
99
100 efl &= ~PATM_VIRTUAL_FLAGS_MASK;
101 efl |= X86_EFL_IF;
102 pCtx->eflags.u32 = efl;
103
104#ifdef IN_RING3
105# ifdef PATM_EMULATE_SYSENTER
106 PCPUMCTX pCtx;
107
108 /* Check if the sysenter handler has changed. */
109 pCtx = CPUMQueryGuestCtxPtr(pVM);
110 if ( pCtx->SysEnter.cs != 0
111 && pCtx->SysEnter.eip != 0
112 )
113 {
114 if (pVM->patm.s.pfnSysEnterGC != (RTRCPTR)pCtx->SysEnter.eip)
115 {
116 pVM->patm.s.pfnSysEnterPatchGC = 0;
117 pVM->patm.s.pfnSysEnterGC = 0;
118
119 Log2(("PATMRawEnter: installing sysenter patch for %RRv\n", pCtx->SysEnter.eip));
120 pVM->patm.s.pfnSysEnterPatchGC = PATMR3QueryPatchGCPtr(pVM, pCtx->SysEnter.eip);
121 if (pVM->patm.s.pfnSysEnterPatchGC == 0)
122 {
123 rc = PATMR3InstallPatch(pVM, pCtx->SysEnter.eip, PATMFL_SYSENTER | PATMFL_CODE32);
124 if (rc == VINF_SUCCESS)
125 {
126 pVM->patm.s.pfnSysEnterPatchGC = PATMR3QueryPatchGCPtr(pVM, pCtx->SysEnter.eip);
127 pVM->patm.s.pfnSysEnterGC = (RTRCPTR)pCtx->SysEnter.eip;
128 Assert(pVM->patm.s.pfnSysEnterPatchGC);
129 }
130 }
131 else
132 pVM->patm.s.pfnSysEnterGC = (RTRCPTR)pCtx->SysEnter.eip;
133 }
134 }
135 else
136 {
137 pVM->patm.s.pfnSysEnterPatchGC = 0;
138 pVM->patm.s.pfnSysEnterGC = 0;
139 }
140# endif /* PATM_EMULATE_SYSENTER */
141#endif
142}
143
144
145/**
146 * Restores virtualized flags.
147 *
148 * This function is called from CPUMRawLeave(). It will update the eflags register.
149 *
150 ** @note Only here we are allowed to switch back to guest code (without a special reason such as a trap in patch code)!!
151 *
152 * @param pVM The cross context VM structure.
153 * @param pCtx The cpu context.
154 * @param rawRC Raw mode return code
155 * @see @ref pg_raw
156 */
157VMM_INT_DECL(void) PATMRawLeave(PVM pVM, PCPUMCTX pCtx, int rawRC)
158{
159 Assert(!HMIsEnabled(pVM));
160 bool fPatchCode = PATMIsPatchGCAddr(pVM, pCtx->eip);
161
162 /*
163 * We will only be called if PATMRawEnter was previously called.
164 */
165 uint32_t efl = pCtx->eflags.u32;
166 efl = (efl & ~PATM_VIRTUAL_FLAGS_MASK) | (CTXSUFF(pVM->patm.s.pGCState)->uVMFlags & PATM_VIRTUAL_FLAGS_MASK);
167 pCtx->eflags.u32 = efl;
168 CTXSUFF(pVM->patm.s.pGCState)->uVMFlags = X86_EFL_IF;
169
170#ifdef IN_RING3
171 AssertReleaseMsg((efl & X86_EFL_IF) || fPatchCode || rawRC == VINF_PATM_PENDING_IRQ_AFTER_IRET
172 || rawRC == VINF_EM_RESCHEDULE || rawRC == VINF_EM_RESCHEDULE_REM
173 || rawRC == VINF_EM_RAW_GUEST_TRAP || RT_FAILURE(rawRC),
174 ("Inconsistent state at %RRv rc=%Rrc\n", pCtx->eip, rawRC));
175 AssertReleaseMsg(CTXSUFF(pVM->patm.s.pGCState)->fPIF || fPatchCode || RT_FAILURE(rawRC), ("fPIF=%d eip=%RRv rc=%Rrc\n", CTXSUFF(pVM->patm.s.pGCState)->fPIF, pCtx->eip, rawRC));
176 if ( (efl & X86_EFL_IF)
177 && fPatchCode)
178 {
179 if ( rawRC < VINF_PATM_LEAVE_RC_FIRST
180 || rawRC > VINF_PATM_LEAVE_RC_LAST)
181 {
182 /*
183 * Golden rules:
184 * - Don't interrupt special patch streams that replace special instructions
185 * - Don't break instruction fusing (sti, pop ss, mov ss)
186 * - Don't go back to an instruction that has been overwritten by a patch jump
187 * - Don't interrupt an idt handler on entry (1st instruction); technically incorrect
188 *
189 */
190 if (CTXSUFF(pVM->patm.s.pGCState)->fPIF == 1) /* consistent patch instruction state */
191 {
192 PATMTRANSSTATE enmState;
193 RTRCPTR pOrgInstrGC = PATMR3PatchToGCPtr(pVM, pCtx->eip, &enmState);
194
195 AssertRelease(pOrgInstrGC);
196
197 Assert(enmState != PATMTRANS_OVERWRITTEN);
198 if (enmState == PATMTRANS_SAFE)
199 {
200 Assert(!patmFindActivePatchByEntrypoint(pVM, pOrgInstrGC));
201 Log(("Switchback from %RRv to %RRv (Psp=%x)\n", pCtx->eip, pOrgInstrGC, CTXSUFF(pVM->patm.s.pGCState)->Psp));
202 STAM_COUNTER_INC(&pVM->patm.s.StatSwitchBack);
203 pCtx->eip = pOrgInstrGC;
204 fPatchCode = false; /* to reset the stack ptr */
205
206 CTXSUFF(pVM->patm.s.pGCState)->GCPtrInhibitInterrupts = 0; /* reset this pointer; safe otherwise the state would be PATMTRANS_INHIBITIRQ */
207 }
208 else
209 {
210 LogFlow(("Patch address %RRv can't be interrupted (state=%d)!\n", pCtx->eip, enmState));
211 STAM_COUNTER_INC(&pVM->patm.s.StatSwitchBackFail);
212 }
213 }
214 else
215 {
216 LogFlow(("Patch address %RRv can't be interrupted (fPIF=%d)!\n", pCtx->eip, CTXSUFF(pVM->patm.s.pGCState)->fPIF));
217 STAM_COUNTER_INC(&pVM->patm.s.StatSwitchBackFail);
218 }
219 }
220 }
221#else /* !IN_RING3 */
222 /*
223 * When leaving raw-mode state while IN_RC, it's generally for interpreting
224 * a single original guest instruction.
225 */
226 AssertMsg(!fPatchCode, ("eip=%RRv\n", pCtx->eip));
227 AssertReleaseMsg((efl & X86_EFL_IF) || fPatchCode || rawRC == VINF_PATM_PENDING_IRQ_AFTER_IRET || RT_FAILURE(rawRC), ("Inconsistent state at %RRv rc=%Rrc\n", pCtx->eip, rawRC));
228 AssertReleaseMsg(CTXSUFF(pVM->patm.s.pGCState)->fPIF || fPatchCode || RT_FAILURE(rawRC), ("fPIF=%d eip=%RRv rc=%Rrc\n", CTXSUFF(pVM->patm.s.pGCState)->fPIF, pCtx->eip, rawRC));
229#endif /* !IN_RING3 */
230
231 if (!fPatchCode)
232 {
233 if (CTXSUFF(pVM->patm.s.pGCState)->GCPtrInhibitInterrupts == (RTRCPTR)pCtx->eip)
234 {
235 EMSetInhibitInterruptsPC(VMMGetCpu0(pVM), pCtx->eip);
236 }
237 CTXSUFF(pVM->patm.s.pGCState)->GCPtrInhibitInterrupts = 0;
238
239 /* Reset the stack pointer to the top of the stack. */
240#ifdef DEBUG
241 if (CTXSUFF(pVM->patm.s.pGCState)->Psp != PATM_STACK_SIZE)
242 {
243 LogFlow(("PATMRawLeave: Reset PATM stack (Psp = %x)\n", CTXSUFF(pVM->patm.s.pGCState)->Psp));
244 }
245#endif
246 CTXSUFF(pVM->patm.s.pGCState)->Psp = PATM_STACK_SIZE;
247 }
248}
249
250/**
251 * Get the EFLAGS.
252 * This is a worker for CPUMRawGetEFlags().
253 *
254 * @returns The eflags.
255 * @param pVM The cross context VM structure.
256 * @param pCtx The guest cpu context.
257 */
258VMM_INT_DECL(uint32_t) PATMRawGetEFlags(PVM pVM, PCCPUMCTX pCtx)
259{
260 Assert(!HMIsEnabled(pVM));
261 uint32_t efl = pCtx->eflags.u32;
262 efl &= ~PATM_VIRTUAL_FLAGS_MASK;
263 efl |= pVM->patm.s.CTXSUFF(pGCState)->uVMFlags & PATM_VIRTUAL_FLAGS_MASK;
264 return efl;
265}
266
267/**
268 * Updates the EFLAGS.
269 * This is a worker for CPUMRawSetEFlags().
270 *
271 * @param pVM The cross context VM structure.
272 * @param pCtx The guest cpu context.
273 * @param efl The new EFLAGS value.
274 */
275VMM_INT_DECL(void) PATMRawSetEFlags(PVM pVM, PCPUMCTX pCtx, uint32_t efl)
276{
277 Assert(!HMIsEnabled(pVM));
278 pVM->patm.s.CTXSUFF(pGCState)->uVMFlags = efl & PATM_VIRTUAL_FLAGS_MASK;
279 efl &= ~PATM_VIRTUAL_FLAGS_MASK;
280 efl |= X86_EFL_IF;
281 pCtx->eflags.u32 = efl;
282}
283
284/**
285 * Check if we must use raw mode (patch code being executed)
286 *
287 * @param pVM The cross context VM structure.
288 * @param pAddrGC Guest context address
289 */
290VMM_INT_DECL(bool) PATMShouldUseRawMode(PVM pVM, RTRCPTR pAddrGC)
291{
292 return PATMIsEnabled(pVM)
293 && ( (RTRCUINTPTR)pAddrGC - (RTRCUINTPTR)pVM->patm.s.pPatchMemGC < pVM->patm.s.cbPatchMem
294 || (RTRCUINTPTR)pAddrGC - (RTRCUINTPTR)pVM->patm.s.pbPatchHelpersRC < pVM->patm.s.cbPatchHelpers);
295}
296
297/**
298 * Returns the guest context pointer and size of the GC context structure
299 *
300 * @returns VBox status code.
301 * @param pVM The cross context VM structure.
302 */
303VMM_INT_DECL(RCPTRTYPE(PPATMGCSTATE)) PATMGetGCState(PVM pVM)
304{
305 AssertReturn(!HMIsEnabled(pVM), NIL_RTRCPTR);
306 return pVM->patm.s.pGCStateGC;
307}
308
309/**
310 * Checks whether the GC address is part of our patch or helper regions.
311 *
312 * @returns VBox status code.
313 * @param pVM The cross context VM structure.
314 * @param uGCAddr Guest context address.
315 * @internal
316 */
317VMMDECL(bool) PATMIsPatchGCAddr(PVM pVM, RTRCUINTPTR uGCAddr)
318{
319 return PATMIsEnabled(pVM)
320 && ( uGCAddr - (RTRCUINTPTR)pVM->patm.s.pPatchMemGC < pVM->patm.s.cbPatchMem
321 || uGCAddr - (RTRCUINTPTR)pVM->patm.s.pbPatchHelpersRC < pVM->patm.s.cbPatchHelpers);
322}
323
324/**
325 * Checks whether the GC address is part of our patch region.
326 *
327 * @returns VBox status code.
328 * @param pVM The cross context VM structure.
329 * @param uGCAddr Guest context address.
330 * @internal
331 */
332VMMDECL(bool) PATMIsPatchGCAddrExclHelpers(PVM pVM, RTRCUINTPTR uGCAddr)
333{
334 return PATMIsEnabled(pVM)
335 && uGCAddr - (RTRCUINTPTR)pVM->patm.s.pPatchMemGC < pVM->patm.s.cbPatchMem;
336}
337
338/**
339 * Reads patch code.
340 *
341 * @retval VINF_SUCCESS on success.
342 * @retval VERR_PATCH_NOT_FOUND if the request is entirely outside the patch
343 * code.
344 *
345 * @param pVM The cross context VM structure.
346 * @param GCPtrPatchCode The patch address to start reading at.
347 * @param pvDst Where to return the patch code.
348 * @param cbToRead Number of bytes to read.
349 * @param pcbRead Where to return the actual number of bytes we've
350 * read. Optional.
351 */
352VMM_INT_DECL(int) PATMReadPatchCode(PVM pVM, RTGCPTR GCPtrPatchCode, void *pvDst, size_t cbToRead, size_t *pcbRead)
353{
354 /* Shortcut. */
355 if (!PATMIsEnabled(pVM))
356 return VERR_PATCH_NOT_FOUND;
357 Assert(!HMIsEnabled(pVM));
358
359 /*
360 * Check patch code and patch helper code. We assume the requested bytes
361 * are not in either.
362 */
363 RTGCPTR offPatchCode = GCPtrPatchCode - (RTGCPTR32)pVM->patm.s.pPatchMemGC;
364 if (offPatchCode >= pVM->patm.s.cbPatchMem)
365 {
366 offPatchCode = GCPtrPatchCode - (RTGCPTR32)pVM->patm.s.pbPatchHelpersRC;
367 if (offPatchCode >= pVM->patm.s.cbPatchHelpers)
368 return VERR_PATCH_NOT_FOUND;
369
370 /*
371 * Patch helper memory.
372 */
373 uint32_t cbMaxRead = pVM->patm.s.cbPatchHelpers - (uint32_t)offPatchCode;
374 if (cbToRead > cbMaxRead)
375 cbToRead = cbMaxRead;
376#ifdef IN_RC
377 memcpy(pvDst, pVM->patm.s.pbPatchHelpersRC + (uint32_t)offPatchCode, cbToRead);
378#else
379 memcpy(pvDst, pVM->patm.s.pbPatchHelpersR3 + (uint32_t)offPatchCode, cbToRead);
380#endif
381 }
382 else
383 {
384 /*
385 * Patch memory.
386 */
387 uint32_t cbMaxRead = pVM->patm.s.cbPatchMem - (uint32_t)offPatchCode;
388 if (cbToRead > cbMaxRead)
389 cbToRead = cbMaxRead;
390#ifdef IN_RC
391 memcpy(pvDst, pVM->patm.s.pPatchMemGC + (uint32_t)offPatchCode, cbToRead);
392#else
393 memcpy(pvDst, pVM->patm.s.pPatchMemHC + (uint32_t)offPatchCode, cbToRead);
394#endif
395 }
396
397 if (pcbRead)
398 *pcbRead = cbToRead;
399 return VINF_SUCCESS;
400}
401
402/**
403 * Set parameters for pending MMIO patch operation
404 *
405 * @returns VBox status code.
406 * @param pVM The cross context VM structure.
407 * @param GCPhys MMIO physical address.
408 * @param pCachedData RC pointer to cached data.
409 */
410VMM_INT_DECL(int) PATMSetMMIOPatchInfo(PVM pVM, RTGCPHYS GCPhys, RTRCPTR pCachedData)
411{
412 if (!HMIsEnabled(pVM))
413 {
414 pVM->patm.s.mmio.GCPhys = GCPhys;
415 pVM->patm.s.mmio.pCachedData = (RTRCPTR)pCachedData;
416 }
417
418 return VINF_SUCCESS;
419}
420
421/**
422 * Checks if the interrupt flag is enabled or not.
423 *
424 * @returns true if it's enabled.
425 * @returns false if it's disabled.
426 *
427 * @param pVM The cross context VM structure.
428 * @todo CPUM should wrap this, EM.cpp shouldn't call us.
429 */
430VMM_INT_DECL(bool) PATMAreInterruptsEnabled(PVM pVM)
431{
432 PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(VMMGetCpu(pVM));
433
434 return PATMAreInterruptsEnabledByCtx(pVM, pCtx);
435}
436
437/**
438 * Checks if the interrupt flag is enabled or not.
439 *
440 * @returns true if it's enabled.
441 * @returns false if it's disabled.
442 *
443 * @param pVM The cross context VM structure.
444 * @param pCtx The guest CPU context.
445 * @todo CPUM should wrap this, EM.cpp shouldn't call us.
446 */
447VMM_INT_DECL(bool) PATMAreInterruptsEnabledByCtx(PVM pVM, PCPUMCTX pCtx)
448{
449 if (PATMIsEnabled(pVM))
450 {
451 Assert(!HMIsEnabled(pVM));
452 if (PATMIsPatchGCAddr(pVM, pCtx->eip))
453 return false;
454 }
455 return !!(pCtx->eflags.u32 & X86_EFL_IF);
456}
457
458/**
459 * Check if the instruction is patched as a duplicated function
460 *
461 * @returns patch record
462 * @param pVM The cross context VM structure.
463 * @param pInstrGC Guest context point to the instruction
464 *
465 */
466PPATMPATCHREC patmQueryFunctionPatch(PVM pVM, RTRCPTR pInstrGC)
467{
468 PPATMPATCHREC pRec;
469
470 AssertCompile(sizeof(AVLOU32KEY) == sizeof(pInstrGC));
471 pRec = (PPATMPATCHREC)RTAvloU32Get(&CTXSUFF(pVM->patm.s.PatchLookupTree)->PatchTree, (AVLOU32KEY)pInstrGC);
472 if ( pRec
473 && (pRec->patch.uState == PATCH_ENABLED)
474 && (pRec->patch.flags & (PATMFL_DUPLICATE_FUNCTION|PATMFL_CALLABLE_AS_FUNCTION))
475 )
476 return pRec;
477 return 0;
478}
479
480/**
481 * Checks if the int 3 was caused by a patched instruction
482 *
483 * @returns VBox status
484 *
485 * @param pVM The cross context VM structure.
486 * @param pInstrGC Instruction pointer
487 * @param pOpcode Original instruction opcode (out, optional)
488 * @param pSize Original instruction size (out, optional)
489 */
490VMM_INT_DECL(bool) PATMIsInt3Patch(PVM pVM, RTRCPTR pInstrGC, uint32_t *pOpcode, uint32_t *pSize)
491{
492 PPATMPATCHREC pRec;
493 Assert(!HMIsEnabled(pVM));
494
495 pRec = (PPATMPATCHREC)RTAvloU32Get(&CTXSUFF(pVM->patm.s.PatchLookupTree)->PatchTree, (AVLOU32KEY)pInstrGC);
496 if ( pRec
497 && (pRec->patch.uState == PATCH_ENABLED)
498 && (pRec->patch.flags & (PATMFL_INT3_REPLACEMENT|PATMFL_INT3_REPLACEMENT_BLOCK))
499 )
500 {
501 if (pOpcode) *pOpcode = pRec->patch.opcode;
502 if (pSize) *pSize = pRec->patch.cbPrivInstr;
503 return true;
504 }
505 return false;
506}
507
508/**
509 * Emulate sysenter, sysexit and syscall instructions
510 *
511 * @returns VBox status
512 *
513 * @param pVM The cross context VM structure.
514 * @param pCtx The relevant guest cpu context.
515 * @param pCpu Disassembly state.
516 */
517VMMDECL(int) PATMSysCall(PVM pVM, PCPUMCTX pCtx, PDISCPUSTATE pCpu)
518{
519 Assert(CPUMQueryGuestCtxPtr(VMMGetCpu0(pVM)) == pCtx);
520 AssertReturn(!HMIsEnabled(pVM), VERR_PATM_HM_IPE);
521
522 if (pCpu->pCurInstr->uOpcode == OP_SYSENTER)
523 {
524 if ( pCtx->SysEnter.cs == 0
525 || pCtx->eflags.Bits.u1VM
526 || (pCtx->cs.Sel & X86_SEL_RPL) != 3
527 || pVM->patm.s.pfnSysEnterPatchGC == 0
528 || pVM->patm.s.pfnSysEnterGC != (RTRCPTR)(RTRCUINTPTR)pCtx->SysEnter.eip
529 || !(PATMRawGetEFlags(pVM, pCtx) & X86_EFL_IF))
530 goto end;
531
532 Log2(("PATMSysCall: sysenter from %RRv to %RRv\n", pCtx->eip, pVM->patm.s.pfnSysEnterPatchGC));
533 /** @todo the base and limit are forced to 0 & 4G-1 resp. We assume the selector is wide open here. */
534 /** @note The Intel manual suggests that the OS is responsible for this. */
535 pCtx->cs.Sel = (pCtx->SysEnter.cs & ~X86_SEL_RPL) | 1;
536 pCtx->eip = /** @todo ugly conversion! */(uint32_t)pVM->patm.s.pfnSysEnterPatchGC;
537 pCtx->ss.Sel = pCtx->cs.Sel + 8; /* SysEnter.cs + 8 */
538 pCtx->esp = pCtx->SysEnter.esp;
539 pCtx->eflags.u32 &= ~(X86_EFL_VM | X86_EFL_RF);
540 pCtx->eflags.u32 |= X86_EFL_IF;
541
542 /* Turn off interrupts. */
543 pVM->patm.s.CTXSUFF(pGCState)->uVMFlags &= ~X86_EFL_IF;
544
545 STAM_COUNTER_INC(&pVM->patm.s.StatSysEnter);
546
547 return VINF_SUCCESS;
548 }
549 if (pCpu->pCurInstr->uOpcode == OP_SYSEXIT)
550 {
551 if ( pCtx->SysEnter.cs == 0
552 || (pCtx->cs.Sel & X86_SEL_RPL) != 1
553 || pCtx->eflags.Bits.u1VM
554 || !(PATMRawGetEFlags(pVM, pCtx) & X86_EFL_IF))
555 goto end;
556
557 Log2(("PATMSysCall: sysexit from %RRv to %RRv\n", pCtx->eip, pCtx->edx));
558
559 pCtx->cs.Sel = ((pCtx->SysEnter.cs + 16) & ~X86_SEL_RPL) | 3;
560 pCtx->eip = pCtx->edx;
561 pCtx->ss.Sel = pCtx->cs.Sel + 8; /* SysEnter.cs + 24 */
562 pCtx->esp = pCtx->ecx;
563
564 STAM_COUNTER_INC(&pVM->patm.s.StatSysExit);
565
566 return VINF_SUCCESS;
567 }
568 if (pCpu->pCurInstr->uOpcode == OP_SYSCALL)
569 {
570 /** @todo implement syscall */
571 }
572 else
573 if (pCpu->pCurInstr->uOpcode == OP_SYSRET)
574 {
575 /** @todo implement sysret */
576 }
577
578end:
579 return VINF_EM_RAW_RING_SWITCH;
580}
581
582/**
583 * Adds branch pair to the lookup cache of the particular branch instruction
584 *
585 * @returns VBox status
586 * @param pVM The cross context VM structure.
587 * @param pJumpTableGC Pointer to branch instruction lookup cache
588 * @param pBranchTarget Original branch target
589 * @param pRelBranchPatch Relative duplicated function address
590 */
591int patmAddBranchToLookupCache(PVM pVM, RTRCPTR pJumpTableGC, RTRCPTR pBranchTarget, RTRCUINTPTR pRelBranchPatch)
592{
593 PPATCHJUMPTABLE pJumpTable;
594
595 Log(("PATMAddBranchToLookupCache: Adding (%RRv->%RRv (%RRv)) to table %RRv\n", pBranchTarget, pRelBranchPatch + pVM->patm.s.pPatchMemGC, pRelBranchPatch, pJumpTableGC));
596
597 AssertReturn(PATMIsPatchGCAddr(pVM, (RTRCUINTPTR)pJumpTableGC), VERR_INVALID_PARAMETER);
598
599#ifdef IN_RC
600 pJumpTable = (PPATCHJUMPTABLE) pJumpTableGC;
601#else
602 pJumpTable = (PPATCHJUMPTABLE) (pJumpTableGC - pVM->patm.s.pPatchMemGC + pVM->patm.s.pPatchMemHC);
603#endif
604 Log(("Nr addresses = %d, insert pos = %d\n", pJumpTable->cAddresses, pJumpTable->ulInsertPos));
605 if (pJumpTable->cAddresses < pJumpTable->nrSlots)
606 {
607 uint32_t i;
608
609 for (i=0;i<pJumpTable->nrSlots;i++)
610 {
611 if (pJumpTable->Slot[i].pInstrGC == 0)
612 {
613 pJumpTable->Slot[i].pInstrGC = pBranchTarget;
614 /* Relative address - eases relocation */
615 pJumpTable->Slot[i].pRelPatchGC = pRelBranchPatch;
616 pJumpTable->cAddresses++;
617 break;
618 }
619 }
620 AssertReturn(i < pJumpTable->nrSlots, VERR_INTERNAL_ERROR);
621#ifdef VBOX_WITH_STATISTICS
622 STAM_COUNTER_INC(&pVM->patm.s.StatFunctionLookupInsert);
623 if (pVM->patm.s.StatU32FunctionMaxSlotsUsed < i)
624 pVM->patm.s.StatU32FunctionMaxSlotsUsed = i + 1;
625#endif
626 }
627 else
628 {
629 /* Replace an old entry. */
630 /** @todo replacement strategy isn't really bright. change to something better if required. */
631 Assert(pJumpTable->ulInsertPos < pJumpTable->nrSlots);
632 Assert((pJumpTable->nrSlots & 1) == 0);
633
634 pJumpTable->ulInsertPos &= (pJumpTable->nrSlots-1);
635 pJumpTable->Slot[pJumpTable->ulInsertPos].pInstrGC = pBranchTarget;
636 /* Relative address - eases relocation */
637 pJumpTable->Slot[pJumpTable->ulInsertPos].pRelPatchGC = pRelBranchPatch;
638
639 pJumpTable->ulInsertPos = (pJumpTable->ulInsertPos+1) & (pJumpTable->nrSlots-1);
640
641 STAM_COUNTER_INC(&pVM->patm.s.StatFunctionLookupReplace);
642 }
643
644 return VINF_SUCCESS;
645}
646
647
648#if defined(VBOX_WITH_STATISTICS) || defined(LOG_ENABLED)
649/**
650 * Return the name of the patched instruction
651 *
652 * @returns instruction name
653 *
654 * @param opcode DIS instruction opcode
655 * @param fPatchFlags Patch flags
656 */
657const char *patmGetInstructionString(uint32_t opcode, uint32_t fPatchFlags)
658{
659 const char *pszInstr = NULL;
660
661 switch (opcode)
662 {
663 case OP_CLI:
664 pszInstr = "cli";
665 break;
666 case OP_PUSHF:
667 pszInstr = "pushf";
668 break;
669 case OP_POPF:
670 pszInstr = "popf";
671 break;
672 case OP_STR:
673 pszInstr = "str";
674 break;
675 case OP_LSL:
676 pszInstr = "lsl";
677 break;
678 case OP_LAR:
679 pszInstr = "lar";
680 break;
681 case OP_SGDT:
682 pszInstr = "sgdt";
683 break;
684 case OP_SLDT:
685 pszInstr = "sldt";
686 break;
687 case OP_SIDT:
688 pszInstr = "sidt";
689 break;
690 case OP_SMSW:
691 pszInstr = "smsw";
692 break;
693 case OP_VERW:
694 pszInstr = "verw";
695 break;
696 case OP_VERR:
697 pszInstr = "verr";
698 break;
699 case OP_CPUID:
700 pszInstr = "cpuid";
701 break;
702 case OP_JMP:
703 pszInstr = "jmp";
704 break;
705 case OP_JO:
706 pszInstr = "jo";
707 break;
708 case OP_JNO:
709 pszInstr = "jno";
710 break;
711 case OP_JC:
712 pszInstr = "jc";
713 break;
714 case OP_JNC:
715 pszInstr = "jnc";
716 break;
717 case OP_JE:
718 pszInstr = "je";
719 break;
720 case OP_JNE:
721 pszInstr = "jne";
722 break;
723 case OP_JBE:
724 pszInstr = "jbe";
725 break;
726 case OP_JNBE:
727 pszInstr = "jnbe";
728 break;
729 case OP_JS:
730 pszInstr = "js";
731 break;
732 case OP_JNS:
733 pszInstr = "jns";
734 break;
735 case OP_JP:
736 pszInstr = "jp";
737 break;
738 case OP_JNP:
739 pszInstr = "jnp";
740 break;
741 case OP_JL:
742 pszInstr = "jl";
743 break;
744 case OP_JNL:
745 pszInstr = "jnl";
746 break;
747 case OP_JLE:
748 pszInstr = "jle";
749 break;
750 case OP_JNLE:
751 pszInstr = "jnle";
752 break;
753 case OP_JECXZ:
754 pszInstr = "jecxz";
755 break;
756 case OP_LOOP:
757 pszInstr = "loop";
758 break;
759 case OP_LOOPNE:
760 pszInstr = "loopne";
761 break;
762 case OP_LOOPE:
763 pszInstr = "loope";
764 break;
765 case OP_MOV:
766 if (fPatchFlags & PATMFL_IDTHANDLER)
767 pszInstr = "mov (Int/Trap Handler)";
768 else
769 pszInstr = "mov (cs)";
770 break;
771 case OP_SYSENTER:
772 pszInstr = "sysenter";
773 break;
774 case OP_PUSH:
775 pszInstr = "push (cs)";
776 break;
777 case OP_CALL:
778 pszInstr = "call";
779 break;
780 case OP_IRET:
781 pszInstr = "iret";
782 break;
783 }
784 return pszInstr;
785}
786#endif
Note: See TracBrowser for help on using the repository browser.

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