VirtualBox

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

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

PATMRawLeave: VINF_EM_RAW_GUEST_TRAP is probably fine too

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