VirtualBox

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

Last change on this file since 26616 was 26152, checked in by vboxsync, 15 years ago

VMM: pdm.h and @copydoc cleanups.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 52.3 KB
Line 
1/* $Id: PATMPatch.cpp 26152 2010-02-02 16:00:35Z vboxsync $ */
2/** @file
3 * PATMPatch - Dynamic Guest OS Instruction patches
4 *
5 * NOTE: CSAM assumes patch memory is never reused!!
6 */
7
8/*
9 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_PATM
28#include <VBox/patm.h>
29#include <VBox/stam.h>
30#include <VBox/pgm.h>
31#include <VBox/cpum.h>
32#include <VBox/mm.h>
33#include <VBox/trpm.h>
34#include <VBox/param.h>
35#include <iprt/avl.h>
36#include "PATMInternal.h"
37#include <VBox/vm.h>
38#include <VBox/csam.h>
39
40#include <VBox/dbg.h>
41#include <VBox/err.h>
42#include <VBox/log.h>
43#include <iprt/assert.h>
44#include <iprt/asm.h>
45#include <iprt/string.h>
46#include <VBox/dis.h>
47#include <VBox/disopcode.h>
48
49#include <stdlib.h>
50#include <stdio.h>
51#include "PATMA.h"
52#include "PATMPatch.h"
53
54/* internal structure for passing more information about call fixups to patmPatchGenCode */
55typedef struct
56{
57 RTRCPTR pTargetGC;
58 RTRCPTR pCurInstrGC;
59 RTRCPTR pNextInstrGC;
60 RTRCPTR pReturnGC;
61} PATMCALLINFO, *PPATMCALLINFO;
62
63int patmPatchAddReloc32(PVM pVM, PPATCHINFO pPatch, uint8_t *pRelocHC, uint32_t uType, RTRCPTR pSource, RTRCPTR pDest)
64{
65 PRELOCREC pRec;
66
67 Assert(uType == FIXUP_ABSOLUTE || ((uType == FIXUP_REL_JMPTOPATCH || uType == FIXUP_REL_JMPTOGUEST) && pSource && pDest));
68
69 LogFlow(("patmPatchAddReloc32 type=%d pRelocGC=%RRv source=%RRv dest=%RRv\n", uType, pRelocHC - pVM->patm.s.pPatchMemGC + pVM->patm.s.pPatchMemGC , pSource, pDest));
70
71 pRec = (PRELOCREC)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(*pRec));
72 Assert(pRec);
73 pRec->Core.Key = (AVLPVKEY)pRelocHC;
74 pRec->pRelocPos = pRelocHC; /* @todo redundant. */
75 pRec->pSource = pSource;
76 pRec->pDest = pDest;
77 pRec->uType = uType;
78
79 bool ret = RTAvlPVInsert(&pPatch->FixupTree, &pRec->Core);
80 Assert(ret); NOREF(ret);
81 pPatch->nrFixups++;
82
83 return VINF_SUCCESS;
84}
85
86int patmPatchAddJump(PVM pVM, PPATCHINFO pPatch, uint8_t *pJumpHC, uint32_t offset, RTRCPTR pTargetGC, uint32_t opcode)
87{
88 PJUMPREC pRec;
89
90 pRec = (PJUMPREC)MMR3HeapAllocZ(pVM, MM_TAG_PATM_PATCH, sizeof(*pRec));
91 Assert(pRec);
92
93 pRec->Core.Key = (AVLPVKEY)pJumpHC;
94 pRec->pJumpHC = pJumpHC; /* @todo redundant. */
95 pRec->offDispl = offset;
96 pRec->pTargetGC = pTargetGC;
97 pRec->opcode = opcode;
98
99 bool ret = RTAvlPVInsert(&pPatch->JumpTree, &pRec->Core);
100 Assert(ret); NOREF(ret);
101 pPatch->nrJumpRecs++;
102
103 return VINF_SUCCESS;
104}
105
106#define PATCHGEN_PROLOG_NODEF(pVM, pPatch) \
107 pPB = PATCHCODE_PTR_HC(pPatch) + pPatch->uCurPatchOffset; \
108 \
109 if (pPB + 256 >= pVM->patm.s.pPatchMemHC + pVM->patm.s.cbPatchMem) \
110 { \
111 pVM->patm.s.fOutOfMemory = true; \
112 Assert(pPB + 256 >= pVM->patm.s.pPatchMemHC + pVM->patm.s.cbPatchMem); \
113 return VERR_NO_MEMORY; \
114 }
115
116#define PATCHGEN_PROLOG(pVM, pPatch) \
117 uint8_t *pPB; \
118 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
119
120
121#define PATCHGEN_EPILOG(pPatch, size) \
122 Assert(size <= 640); \
123 pPatch->uCurPatchOffset += size;
124
125
126static uint32_t patmPatchGenCode(PVM pVM, PPATCHINFO pPatch, uint8_t *pPB, PPATCHASMRECORD pAsmRecord, RCPTRTYPE(uint8_t *) pReturnAddrGC, bool fGenJump,
127 PPATMCALLINFO pCallInfo = 0)
128{
129 uint32_t i, j;
130
131 Assert(fGenJump == false || pReturnAddrGC);
132 Assert(fGenJump == false || pAsmRecord->offJump);
133 Assert(pAsmRecord && pAsmRecord->size > sizeof(pAsmRecord->uReloc[0]));
134
135 // Copy the code block
136 memcpy(pPB, pAsmRecord->pFunction, pAsmRecord->size);
137
138 // Process all fixups
139 for (j=0,i=0;i<pAsmRecord->nrRelocs*2; i+=2)
140 {
141 for (;j<pAsmRecord->size;j++)
142 {
143 if (*(uint32_t*)&pPB[j] == pAsmRecord->uReloc[i])
144 {
145 RCPTRTYPE(uint32_t *) dest;
146
147#ifdef VBOX_STRICT
148 if (pAsmRecord->uReloc[i] == PATM_FIXUP)
149 Assert(pAsmRecord->uReloc[i+1] != 0);
150 else
151 Assert(pAsmRecord->uReloc[i+1] == 0);
152#endif
153
154 /**
155 * BE VERY CAREFUL WITH THESE FIXUPS. TAKE INTO ACCOUNT THAT PROBLEMS MAY ARISE WHEN RESTORING A SAVED STATE WITH
156 * A DIFFERENT HYPERVISOR LAYOUT.
157 */
158 switch (pAsmRecord->uReloc[i])
159 {
160 case PATM_VMFLAGS:
161 dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, uVMFlags);
162 break;
163
164 case PATM_PENDINGACTION:
165 dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, uPendingAction);
166 break;
167
168 case PATM_FIXUP:
169 /* Offset in uReloc[i+1] is from the base of the function. */
170 dest = (RTGCUINTPTR32)pVM->patm.s.pPatchMemGC + pAsmRecord->uReloc[i+1] + (RTGCUINTPTR32)(pPB - pVM->patm.s.pPatchMemHC);
171 break;
172#ifdef VBOX_WITH_STATISTICS
173 case PATM_ALLPATCHCALLS:
174 dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, uPatchCalls);
175 break;
176
177 case PATM_IRETEFLAGS:
178 dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, uIretEFlags);
179 break;
180
181 case PATM_IRETCS:
182 dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, uIretCS);
183 break;
184
185 case PATM_IRETEIP:
186 dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, uIretEIP);
187 break;
188
189 case PATM_PERPATCHCALLS:
190 dest = patmPatchQueryStatAddress(pVM, pPatch);
191 break;
192#endif
193 case PATM_STACKPTR:
194 dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, Psp);
195 break;
196
197 /* The first part of our PATM stack is used to store offsets of patch return addresses; the 2nd
198 * part to store the original return addresses.
199 */
200 case PATM_STACKBASE:
201 dest = pVM->patm.s.pGCStackGC;
202 break;
203
204 case PATM_STACKBASE_GUEST:
205 dest = pVM->patm.s.pGCStackGC + PATM_STACK_SIZE;
206 break;
207
208 case PATM_RETURNADDR: /* absolute guest address; no fixup required */
209 Assert(pCallInfo && pAsmRecord->uReloc[i] >= PATM_NO_FIXUP);
210 dest = pCallInfo->pReturnGC;
211 break;
212
213 case PATM_PATCHNEXTBLOCK: /* relative address of instruction following this block */
214 Assert(pCallInfo && pAsmRecord->uReloc[i] >= PATM_NO_FIXUP);
215
216 /** @note hardcoded assumption that we must return to the instruction following this block */
217 dest = (uintptr_t)pPB - (uintptr_t)pVM->patm.s.pPatchMemHC + pAsmRecord->size;
218 break;
219
220 case PATM_CALLTARGET: /* relative to patch address; no fixup requird */
221 Assert(pCallInfo && pAsmRecord->uReloc[i] >= PATM_NO_FIXUP);
222
223 /* Address must be filled in later. (see patmr3SetBranchTargets) */
224 patmPatchAddJump(pVM, pPatch, &pPB[j-1], 1, pCallInfo->pTargetGC, OP_CALL);
225 dest = PATM_ILLEGAL_DESTINATION;
226 break;
227
228 case PATM_PATCHBASE: /* Patch GC base address */
229 dest = pVM->patm.s.pPatchMemGC;
230 break;
231
232 case PATM_CPUID_STD_PTR:
233 /* @todo dirty hack when correcting this fixup (state restore) */
234 dest = CPUMR3GetGuestCpuIdStdRCPtr(pVM);
235 break;
236
237 case PATM_CPUID_EXT_PTR:
238 /* @todo dirty hack when correcting this fixup (state restore) */
239 dest = CPUMR3GetGuestCpuIdExtRCPtr(pVM);
240 break;
241
242 case PATM_CPUID_CENTAUR_PTR:
243 /* @todo dirty hack when correcting this fixup (state restore) */
244 dest = CPUMR3GetGuestCpuIdCentaurRCPtr(pVM);
245 break;
246
247 case PATM_CPUID_DEF_PTR:
248 /* @todo dirty hack when correcting this fixup (state restore) */
249 dest = CPUMR3GetGuestCpuIdDefRCPtr(pVM);
250 break;
251
252 case PATM_CPUID_STD_MAX:
253 dest = CPUMGetGuestCpuIdStdMax(pVM);
254 break;
255
256 case PATM_CPUID_EXT_MAX:
257 dest = CPUMGetGuestCpuIdExtMax(pVM);
258 break;
259
260 case PATM_CPUID_CENTAUR_MAX:
261 dest = CPUMGetGuestCpuIdCentaurMax(pVM);
262 break;
263
264 case PATM_INTERRUPTFLAG:
265 dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, fPIF);
266 break;
267
268 case PATM_INHIBITIRQADDR:
269 dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, GCPtrInhibitInterrupts);
270 break;
271
272 case PATM_NEXTINSTRADDR:
273 Assert(pCallInfo);
274 /* pNextInstrGC can be 0 if several instructions, that inhibit irqs, follow each other */
275 dest = pCallInfo->pNextInstrGC;
276 break;
277
278 case PATM_CURINSTRADDR:
279 Assert(pCallInfo);
280 dest = pCallInfo->pCurInstrGC;
281 break;
282
283 case PATM_VM_FORCEDACTIONS:
284 /* @todo dirty assumptions when correcting this fixup during saved state loading. */
285 dest = pVM->pVMRC + RT_OFFSETOF(VM, aCpus[0].fLocalForcedActions);
286 break;
287
288 case PATM_TEMP_EAX:
289 dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, Restore.uEAX);
290 break;
291 case PATM_TEMP_ECX:
292 dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, Restore.uECX);
293 break;
294 case PATM_TEMP_EDI:
295 dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, Restore.uEDI);
296 break;
297 case PATM_TEMP_EFLAGS:
298 dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, Restore.eFlags);
299 break;
300 case PATM_TEMP_RESTORE_FLAGS:
301 dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, Restore.uFlags);
302 break;
303 case PATM_CALL_PATCH_TARGET_ADDR:
304 dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, GCCallPatchTargetAddr);
305 break;
306 case PATM_CALL_RETURN_ADDR:
307 dest = pVM->patm.s.pGCStateGC + RT_OFFSETOF(PATMGCSTATE, GCCallReturnAddr);
308 break;
309
310 /* Relative address of global patm lookup and call function. */
311 case PATM_LOOKUP_AND_CALL_FUNCTION:
312 {
313 RTRCPTR pInstrAfterCall = pVM->patm.s.pPatchMemGC + (RTGCUINTPTR32)(&pPB[j] + sizeof(RTRCPTR) - pVM->patm.s.pPatchMemHC);
314 Assert(pVM->patm.s.pfnHelperCallGC);
315 Assert(sizeof(uint32_t) == sizeof(RTRCPTR));
316
317 /* Relative value is target minus address of instruction after the actual call instruction. */
318 dest = pVM->patm.s.pfnHelperCallGC - pInstrAfterCall;
319 break;
320 }
321
322 case PATM_RETURN_FUNCTION:
323 {
324 RTRCPTR pInstrAfterCall = pVM->patm.s.pPatchMemGC + (RTGCUINTPTR32)(&pPB[j] + sizeof(RTRCPTR) - pVM->patm.s.pPatchMemHC);
325 Assert(pVM->patm.s.pfnHelperRetGC);
326 Assert(sizeof(uint32_t) == sizeof(RTRCPTR));
327
328 /* Relative value is target minus address of instruction after the actual call instruction. */
329 dest = pVM->patm.s.pfnHelperRetGC - pInstrAfterCall;
330 break;
331 }
332
333 case PATM_IRET_FUNCTION:
334 {
335 RTRCPTR pInstrAfterCall = pVM->patm.s.pPatchMemGC + (RTGCUINTPTR32)(&pPB[j] + sizeof(RTRCPTR) - pVM->patm.s.pPatchMemHC);
336 Assert(pVM->patm.s.pfnHelperIretGC);
337 Assert(sizeof(uint32_t) == sizeof(RTRCPTR));
338
339 /* Relative value is target minus address of instruction after the actual call instruction. */
340 dest = pVM->patm.s.pfnHelperIretGC - pInstrAfterCall;
341 break;
342 }
343
344 case PATM_LOOKUP_AND_JUMP_FUNCTION:
345 {
346 RTRCPTR pInstrAfterCall = pVM->patm.s.pPatchMemGC + (RTGCUINTPTR32)(&pPB[j] + sizeof(RTRCPTR) - pVM->patm.s.pPatchMemHC);
347 Assert(pVM->patm.s.pfnHelperJumpGC);
348 Assert(sizeof(uint32_t) == sizeof(RTRCPTR));
349
350 /* Relative value is target minus address of instruction after the actual call instruction. */
351 dest = pVM->patm.s.pfnHelperJumpGC - pInstrAfterCall;
352 break;
353 }
354
355 default:
356 dest = PATM_ILLEGAL_DESTINATION;
357 AssertRelease(0);
358 break;
359 }
360
361 *(RTRCPTR *)&pPB[j] = dest;
362 if (pAsmRecord->uReloc[i] < PATM_NO_FIXUP)
363 {
364 patmPatchAddReloc32(pVM, pPatch, &pPB[j], FIXUP_ABSOLUTE);
365 }
366 break;
367 }
368 }
369 Assert(j < pAsmRecord->size);
370 }
371 Assert(pAsmRecord->uReloc[i] == 0xffffffff);
372
373 /* Add the jump back to guest code (if required) */
374 if (fGenJump)
375 {
376 int32_t displ = pReturnAddrGC - (PATCHCODE_PTR_GC(pPatch) + pPatch->uCurPatchOffset + pAsmRecord->offJump - 1 + SIZEOF_NEARJUMP32);
377
378 /* Add lookup record for patch to guest address translation */
379 Assert(pPB[pAsmRecord->offJump - 1] == 0xE9);
380 patmr3AddP2GLookupRecord(pVM, pPatch, &pPB[pAsmRecord->offJump - 1], pReturnAddrGC, PATM_LOOKUP_PATCH2GUEST);
381
382 *(uint32_t *)&pPB[pAsmRecord->offJump] = displ;
383 patmPatchAddReloc32(pVM, pPatch, &pPB[pAsmRecord->offJump], FIXUP_REL_JMPTOGUEST,
384 PATCHCODE_PTR_GC(pPatch) + pPatch->uCurPatchOffset + pAsmRecord->offJump - 1 + SIZEOF_NEARJUMP32,
385 pReturnAddrGC);
386 }
387
388 // Calculate the right size of this patch block
389 if ((fGenJump && pAsmRecord->offJump) || (!fGenJump && !pAsmRecord->offJump))
390 {
391 return pAsmRecord->size;
392 }
393 else {
394 // if a jump instruction is present and we don't want one, then subtract SIZEOF_NEARJUMP32
395 return pAsmRecord->size - SIZEOF_NEARJUMP32;
396 }
397}
398
399/* Read bytes and check for overwritten instructions. */
400static int patmPatchReadBytes(PVM pVM, uint8_t *pDest, RTRCPTR pSrc, uint32_t cb)
401{
402 int rc = PGMPhysSimpleReadGCPtr(&pVM->aCpus[0], pDest, pSrc, cb);
403 AssertRCReturn(rc, rc);
404 /*
405 * Could be patched already; make sure this is checked!
406 */
407 for (uint32_t i=0;i<cb;i++)
408 {
409 uint8_t temp;
410
411 int rc2 = PATMR3QueryOpcode(pVM, pSrc+i, &temp);
412 if (RT_SUCCESS(rc2))
413 {
414 pDest[i] = temp;
415 }
416 else
417 break; /* no more */
418 }
419 return VINF_SUCCESS;
420}
421
422int patmPatchGenDuplicate(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu, RCPTRTYPE(uint8_t *) pCurInstrGC)
423{
424 int rc = VINF_SUCCESS;
425 PATCHGEN_PROLOG(pVM, pPatch);
426
427 rc = patmPatchReadBytes(pVM, pPB, pCurInstrGC, pCpu->opsize);
428 AssertRC(rc);
429 PATCHGEN_EPILOG(pPatch, pCpu->opsize);
430 return rc;
431}
432
433int patmPatchGenIret(PVM pVM, PPATCHINFO pPatch, RTRCPTR pCurInstrGC, bool fSizeOverride)
434{
435 uint32_t size;
436 PATMCALLINFO callInfo;
437
438 PATCHGEN_PROLOG(pVM, pPatch);
439
440 AssertMsg(fSizeOverride == false, ("operand size override!!\n"));
441
442 callInfo.pCurInstrGC = pCurInstrGC;
443
444 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMIretRecord, 0, false, &callInfo);
445
446 PATCHGEN_EPILOG(pPatch, size);
447 return VINF_SUCCESS;
448}
449
450int patmPatchGenCli(PVM pVM, PPATCHINFO pPatch)
451{
452 uint32_t size;
453 PATCHGEN_PROLOG(pVM, pPatch);
454
455 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMCliRecord, 0, false);
456
457 PATCHGEN_EPILOG(pPatch, size);
458 return VINF_SUCCESS;
459}
460
461/*
462 * Generate an STI patch
463 */
464int patmPatchGenSti(PVM pVM, PPATCHINFO pPatch, RTRCPTR pCurInstrGC, RTRCPTR pNextInstrGC)
465{
466 PATMCALLINFO callInfo;
467 uint32_t size;
468
469 Log(("patmPatchGenSti at %RRv; next %RRv\n", pCurInstrGC, pNextInstrGC));
470 PATCHGEN_PROLOG(pVM, pPatch);
471 callInfo.pNextInstrGC = pNextInstrGC;
472 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMStiRecord, 0, false, &callInfo);
473 PATCHGEN_EPILOG(pPatch, size);
474
475 return VINF_SUCCESS;
476}
477
478
479int patmPatchGenPopf(PVM pVM, PPATCHINFO pPatch, RCPTRTYPE(uint8_t *) pReturnAddrGC, bool fSizeOverride, bool fGenJumpBack)
480{
481 uint32_t size;
482 PATMCALLINFO callInfo;
483
484 PATCHGEN_PROLOG(pVM, pPatch);
485
486 callInfo.pNextInstrGC = pReturnAddrGC;
487
488 Log(("patmPatchGenPopf at %RRv\n", pReturnAddrGC));
489
490 /* Note: keep IOPL in mind when changing any of this!! (see comments in PATMA.asm, PATMPopf32Replacement) */
491 if (fSizeOverride == true)
492 {
493 Log(("operand size override!!\n"));
494 size = patmPatchGenCode(pVM, pPatch, pPB, (fGenJumpBack) ? &PATMPopf16Record : &PATMPopf16Record_NoExit , pReturnAddrGC, fGenJumpBack, &callInfo);
495 }
496 else
497 {
498 size = patmPatchGenCode(pVM, pPatch, pPB, (fGenJumpBack) ? &PATMPopf32Record : &PATMPopf32Record_NoExit, pReturnAddrGC, fGenJumpBack, &callInfo);
499 }
500
501 PATCHGEN_EPILOG(pPatch, size);
502 STAM_COUNTER_INC(&pVM->patm.s.StatGenPopf);
503 return VINF_SUCCESS;
504}
505
506int patmPatchGenPushf(PVM pVM, PPATCHINFO pPatch, bool fSizeOverride)
507{
508 uint32_t size;
509 PATCHGEN_PROLOG(pVM, pPatch);
510
511 if (fSizeOverride == true)
512 {
513 Log(("operand size override!!\n"));
514 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMPushf16Record, 0, false);
515 }
516 else
517 {
518 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMPushf32Record, 0, false);
519 }
520
521 PATCHGEN_EPILOG(pPatch, size);
522 return VINF_SUCCESS;
523}
524
525int patmPatchGenPushCS(PVM pVM, PPATCHINFO pPatch)
526{
527 uint32_t size;
528 PATCHGEN_PROLOG(pVM, pPatch);
529 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMPushCSRecord, 0, false);
530 PATCHGEN_EPILOG(pPatch, size);
531 return VINF_SUCCESS;
532}
533
534int patmPatchGenLoop(PVM pVM, PPATCHINFO pPatch, RCPTRTYPE(uint8_t *) pTargetGC, uint32_t opcode, bool fSizeOverride)
535{
536 uint32_t size = 0;
537 PPATCHASMRECORD pPatchAsmRec;
538
539 PATCHGEN_PROLOG(pVM, pPatch);
540
541 switch (opcode)
542 {
543 case OP_LOOP:
544 pPatchAsmRec = &PATMLoopRecord;
545 break;
546 case OP_LOOPNE:
547 pPatchAsmRec = &PATMLoopNZRecord;
548 break;
549 case OP_LOOPE:
550 pPatchAsmRec = &PATMLoopZRecord;
551 break;
552 case OP_JECXZ:
553 pPatchAsmRec = &PATMJEcxRecord;
554 break;
555 default:
556 AssertMsgFailed(("PatchGenLoop: invalid opcode %d\n", opcode));
557 return VERR_INVALID_PARAMETER;
558 }
559 Assert(pPatchAsmRec->offSizeOverride && pPatchAsmRec->offRelJump);
560
561 Log(("PatchGenLoop %d jump %d to %08x offrel=%d\n", opcode, pPatch->nrJumpRecs, pTargetGC, pPatchAsmRec->offRelJump));
562
563 // Generate the patch code
564 size = patmPatchGenCode(pVM, pPatch, pPB, pPatchAsmRec, 0, false);
565
566 if (fSizeOverride)
567 {
568 pPB[pPatchAsmRec->offSizeOverride] = 0x66; // ecx -> cx or vice versa
569 }
570
571 *(RTRCPTR *)&pPB[pPatchAsmRec->offRelJump] = 0xDEADBEEF;
572
573 patmPatchAddJump(pVM, pPatch, &pPB[pPatchAsmRec->offRelJump - 1], 1, pTargetGC, opcode);
574
575 PATCHGEN_EPILOG(pPatch, size);
576 return VINF_SUCCESS;
577}
578
579int patmPatchGenRelJump(PVM pVM, PPATCHINFO pPatch, RCPTRTYPE(uint8_t *) pTargetGC, uint32_t opcode, bool fSizeOverride)
580{
581 uint32_t offset = 0;
582 PATCHGEN_PROLOG(pVM, pPatch);
583
584 // internal relative jumps from patch code to patch code; no relocation record required
585
586 Assert(PATMIsPatchGCAddr(pVM, pTargetGC) == false);
587
588 switch (opcode)
589 {
590 case OP_JO:
591 pPB[1] = 0x80;
592 break;
593 case OP_JNO:
594 pPB[1] = 0x81;
595 break;
596 case OP_JC:
597 pPB[1] = 0x82;
598 break;
599 case OP_JNC:
600 pPB[1] = 0x83;
601 break;
602 case OP_JE:
603 pPB[1] = 0x84;
604 break;
605 case OP_JNE:
606 pPB[1] = 0x85;
607 break;
608 case OP_JBE:
609 pPB[1] = 0x86;
610 break;
611 case OP_JNBE:
612 pPB[1] = 0x87;
613 break;
614 case OP_JS:
615 pPB[1] = 0x88;
616 break;
617 case OP_JNS:
618 pPB[1] = 0x89;
619 break;
620 case OP_JP:
621 pPB[1] = 0x8A;
622 break;
623 case OP_JNP:
624 pPB[1] = 0x8B;
625 break;
626 case OP_JL:
627 pPB[1] = 0x8C;
628 break;
629 case OP_JNL:
630 pPB[1] = 0x8D;
631 break;
632 case OP_JLE:
633 pPB[1] = 0x8E;
634 break;
635 case OP_JNLE:
636 pPB[1] = 0x8F;
637 break;
638
639 case OP_JMP:
640 /* If interrupted here, then jump to the target instruction. Used by PATM.cpp for jumping to known instructions. */
641 /* Add lookup record for patch to guest address translation */
642 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pTargetGC, PATM_LOOKUP_PATCH2GUEST);
643
644 pPB[0] = 0xE9;
645 break;
646
647 case OP_JECXZ:
648 case OP_LOOP:
649 case OP_LOOPNE:
650 case OP_LOOPE:
651 return patmPatchGenLoop(pVM, pPatch, pTargetGC, opcode, fSizeOverride);
652
653 default:
654 AssertMsg(0, ("Invalid jump opcode %d\n", opcode));
655 return VERR_PATCHING_REFUSED;
656 }
657 if (opcode != OP_JMP)
658 {
659 pPB[0] = 0xF;
660 offset += 2;
661 }
662 else offset++;
663
664 *(RTRCPTR *)&pPB[offset] = 0xDEADBEEF;
665
666 patmPatchAddJump(pVM, pPatch, pPB, offset, pTargetGC, opcode);
667
668 offset += sizeof(RTRCPTR);
669
670 PATCHGEN_EPILOG(pPatch, offset);
671 return VINF_SUCCESS;
672}
673
674/*
675 * Rewrite call to dynamic or currently unknown function (on-demand patching of function)
676 */
677int patmPatchGenCall(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu, RTRCPTR pCurInstrGC, RTRCPTR pTargetGC, bool fIndirect)
678{
679 PATMCALLINFO callInfo;
680 uint32_t offset;
681 uint32_t i, size;
682 int rc;
683
684 /** @note Don't check for IF=1 here. The ret instruction will do this. */
685 /** @note It's dangerous to do this for 'normal' patches. the jump target might be inside the generated patch jump. (seen this!) */
686
687 /* 1: Clear PATM interrupt flag on entry. */
688 rc = patmPatchGenClearPIF(pVM, pPatch, pCurInstrGC);
689 if (rc == VERR_NO_MEMORY)
690 return rc;
691 AssertRCReturn(rc, rc);
692
693 PATCHGEN_PROLOG(pVM, pPatch);
694 /* 2: We must push the target address onto the stack before appending the indirect call code. */
695
696 if (fIndirect)
697 {
698 Log(("patmPatchGenIndirectCall\n"));
699 Assert(pCpu->param1.size == 4);
700 Assert(OP_PARM_VTYPE(pCpu->pCurInstr->param1) != OP_PARM_J);
701
702 /* We push it onto the stack here, so the guest's context isn't ruined when this happens to cause
703 * a page fault. The assembly code restores the stack afterwards.
704 */
705 offset = 0;
706 /* include prefix byte to make sure we don't use the incorrect selector register. */
707 if (pCpu->prefix & PREFIX_SEG)
708 pPB[offset++] = DISQuerySegPrefixByte(pCpu);
709 pPB[offset++] = 0xFF; // push r/m32
710 pPB[offset++] = MAKE_MODRM(pCpu->ModRM.Bits.Mod, 6 /* group 5 */, pCpu->ModRM.Bits.Rm);
711 i = 2; /* standard offset of modrm bytes */
712 if (pCpu->prefix & PREFIX_OPSIZE)
713 i++; //skip operand prefix
714 if (pCpu->prefix & PREFIX_SEG)
715 i++; //skip segment prefix
716
717 rc = patmPatchReadBytes(pVM, &pPB[offset], (RTRCPTR)((RTGCUINTPTR32)pCurInstrGC + i), pCpu->opsize - i);
718 AssertRCReturn(rc, rc);
719 offset += (pCpu->opsize - i);
720 }
721 else
722 {
723 AssertMsg(PATMIsPatchGCAddr(pVM, pTargetGC) == false, ("Target is already a patch address (%RRv)?!?\n", pTargetGC));
724 Assert(pTargetGC);
725 Assert(OP_PARM_VTYPE(pCpu->pCurInstr->param1) == OP_PARM_J);
726
727 /** @todo wasting memory as the complex search is overkill and we need only one lookup slot... */
728
729 /* Relative call to patch code (patch to patch -> no fixup). */
730 Log(("PatchGenCall from %RRv (next=%RRv) to %RRv\n", pCurInstrGC, pCurInstrGC + pCpu->opsize, pTargetGC));
731
732 /* We push it onto the stack here, so the guest's context isn't ruined when this happens to cause
733 * a page fault. The assembly code restores the stack afterwards.
734 */
735 offset = 0;
736 pPB[offset++] = 0x68; // push %Iv
737 *(RTRCPTR *)&pPB[offset] = pTargetGC;
738 offset += sizeof(RTRCPTR);
739 }
740
741 /* align this block properly to make sure the jump table will not be misaligned. */
742 size = (RTHCUINTPTR)&pPB[offset] & 3;
743 if (size)
744 size = 4 - size;
745
746 for (i=0;i<size;i++)
747 {
748 pPB[offset++] = 0x90; /* nop */
749 }
750 PATCHGEN_EPILOG(pPatch, offset);
751
752 /* 3: Generate code to lookup address in our local cache; call hypervisor PATM code if it can't be located. */
753 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
754 callInfo.pReturnGC = pCurInstrGC + pCpu->opsize;
755 callInfo.pTargetGC = (fIndirect) ? 0xDEADBEEF : pTargetGC;
756 size = patmPatchGenCode(pVM, pPatch, pPB, (fIndirect) ? &PATMCallIndirectRecord : &PATMCallRecord, 0, false, &callInfo);
757 PATCHGEN_EPILOG(pPatch, size);
758
759 /* Need to set PATM_INTERRUPTFLAG after the patched ret returns here. */
760 rc = patmPatchGenSetPIF(pVM, pPatch, pCurInstrGC);
761 if (rc == VERR_NO_MEMORY)
762 return rc;
763 AssertRCReturn(rc, rc);
764
765 STAM_COUNTER_INC(&pVM->patm.s.StatGenCall);
766 return VINF_SUCCESS;
767}
768
769/**
770 * Generate indirect jump to unknown destination
771 *
772 * @returns VBox status code.
773 * @param pVM The VM to operate on.
774 * @param pPatch Patch record
775 * @param pCpu Disassembly state
776 * @param pCurInstrGC Current instruction address
777 */
778int patmPatchGenJump(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu, RTRCPTR pCurInstrGC)
779{
780 PATMCALLINFO callInfo;
781 uint32_t offset;
782 uint32_t i, size;
783 int rc;
784
785 /* 1: Clear PATM interrupt flag on entry. */
786 rc = patmPatchGenClearPIF(pVM, pPatch, pCurInstrGC);
787 if (rc == VERR_NO_MEMORY)
788 return rc;
789 AssertRCReturn(rc, rc);
790
791 PATCHGEN_PROLOG(pVM, pPatch);
792 /* 2: We must push the target address onto the stack before appending the indirect call code. */
793
794 Log(("patmPatchGenIndirectJump\n"));
795 Assert(pCpu->param1.size == 4);
796 Assert(OP_PARM_VTYPE(pCpu->pCurInstr->param1) != OP_PARM_J);
797
798 /* We push it onto the stack here, so the guest's context isn't ruined when this happens to cause
799 * a page fault. The assembly code restores the stack afterwards.
800 */
801 offset = 0;
802 /* include prefix byte to make sure we don't use the incorrect selector register. */
803 if (pCpu->prefix & PREFIX_SEG)
804 pPB[offset++] = DISQuerySegPrefixByte(pCpu);
805
806 pPB[offset++] = 0xFF; // push r/m32
807 pPB[offset++] = MAKE_MODRM(pCpu->ModRM.Bits.Mod, 6 /* group 5 */, pCpu->ModRM.Bits.Rm);
808 i = 2; /* standard offset of modrm bytes */
809 if (pCpu->prefix & PREFIX_OPSIZE)
810 i++; //skip operand prefix
811 if (pCpu->prefix & PREFIX_SEG)
812 i++; //skip segment prefix
813
814 rc = patmPatchReadBytes(pVM, &pPB[offset], (RTRCPTR)((RTGCUINTPTR32)pCurInstrGC + i), pCpu->opsize - i);
815 AssertRCReturn(rc, rc);
816 offset += (pCpu->opsize - i);
817
818 /* align this block properly to make sure the jump table will not be misaligned. */
819 size = (RTHCUINTPTR)&pPB[offset] & 3;
820 if (size)
821 size = 4 - size;
822
823 for (i=0;i<size;i++)
824 {
825 pPB[offset++] = 0x90; /* nop */
826 }
827 PATCHGEN_EPILOG(pPatch, offset);
828
829 /* 3: Generate code to lookup address in our local cache; call hypervisor PATM code if it can't be located. */
830 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
831 callInfo.pReturnGC = pCurInstrGC + pCpu->opsize;
832 callInfo.pTargetGC = 0xDEADBEEF;
833 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMJumpIndirectRecord, 0, false, &callInfo);
834 PATCHGEN_EPILOG(pPatch, size);
835
836 STAM_COUNTER_INC(&pVM->patm.s.StatGenJump);
837 return VINF_SUCCESS;
838}
839
840/**
841 * Generate return instruction
842 *
843 * @returns VBox status code.
844 * @param pVM The VM to operate on.
845 * @param pPatch Patch structure
846 * @param pCpu Disassembly struct
847 * @param pCurInstrGC Current instruction pointer
848 *
849 */
850int patmPatchGenRet(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu, RCPTRTYPE(uint8_t *) pCurInstrGC)
851{
852 int size = 0, rc;
853 RTRCPTR pPatchRetInstrGC;
854
855 /* Remember start of this patch for below. */
856 pPatchRetInstrGC = PATCHCODE_PTR_GC(pPatch) + pPatch->uCurPatchOffset;
857
858 Log(("patmPatchGenRet %RRv\n", pCurInstrGC));
859
860 /** @note optimization: multiple identical ret instruction in a single patch can share a single patched ret. */
861 if ( pPatch->pTempInfo->pPatchRetInstrGC
862 && pPatch->pTempInfo->uPatchRetParam1 == (uint32_t)pCpu->param1.parval) /* nr of bytes popped off the stack should be identical of course! */
863 {
864 Assert(pCpu->pCurInstr->opcode == OP_RETN);
865 STAM_COUNTER_INC(&pVM->patm.s.StatGenRetReused);
866
867 return patmPatchGenPatchJump(pVM, pPatch, pCurInstrGC, pPatch->pTempInfo->pPatchRetInstrGC);
868 }
869
870 /* Jump back to the original instruction if IF is set again. */
871 Assert(!PATMFindActivePatchByEntrypoint(pVM, pCurInstrGC));
872 rc = patmPatchGenCheckIF(pVM, pPatch, pCurInstrGC);
873 AssertRCReturn(rc, rc);
874
875 /* align this block properly to make sure the jump table will not be misaligned. */
876 PATCHGEN_PROLOG(pVM, pPatch);
877 size = (RTHCUINTPTR)pPB & 3;
878 if (size)
879 size = 4 - size;
880
881 for (int i=0;i<size;i++)
882 pPB[i] = 0x90; /* nop */
883 PATCHGEN_EPILOG(pPatch, size);
884
885 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
886 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMRetRecord, 0, false);
887 PATCHGEN_EPILOG(pPatch, size);
888
889 STAM_COUNTER_INC(&pVM->patm.s.StatGenRet);
890 /* Duplicate the ret or ret n instruction; it will use the PATM return address */
891 rc = patmPatchGenDuplicate(pVM, pPatch, pCpu, pCurInstrGC);
892
893 if (rc == VINF_SUCCESS)
894 {
895 pPatch->pTempInfo->pPatchRetInstrGC = pPatchRetInstrGC;
896 pPatch->pTempInfo->uPatchRetParam1 = pCpu->param1.parval;
897 }
898 return rc;
899}
900
901/**
902 * Generate all global patm functions
903 *
904 * @returns VBox status code.
905 * @param pVM The VM to operate on.
906 * @param pPatch Patch structure
907 *
908 */
909int patmPatchGenGlobalFunctions(PVM pVM, PPATCHINFO pPatch)
910{
911 int size = 0;
912
913 pVM->patm.s.pfnHelperCallGC = PATCHCODE_PTR_GC(pPatch) + pPatch->uCurPatchOffset;
914 PATCHGEN_PROLOG(pVM, pPatch);
915 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMLookupAndCallRecord, 0, false);
916 PATCHGEN_EPILOG(pPatch, size);
917
918 /* Round to next 8 byte boundary. */
919 pPatch->uCurPatchOffset = RT_ALIGN_32(pPatch->uCurPatchOffset, 8);
920
921 pVM->patm.s.pfnHelperRetGC = PATCHCODE_PTR_GC(pPatch) + pPatch->uCurPatchOffset;
922 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
923 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMRetFunctionRecord, 0, false);
924 PATCHGEN_EPILOG(pPatch, size);
925
926 /* Round to next 8 byte boundary. */
927 pPatch->uCurPatchOffset = RT_ALIGN_32(pPatch->uCurPatchOffset, 8);
928
929 pVM->patm.s.pfnHelperJumpGC = PATCHCODE_PTR_GC(pPatch) + pPatch->uCurPatchOffset;
930 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
931 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMLookupAndJumpRecord, 0, false);
932 PATCHGEN_EPILOG(pPatch, size);
933
934 /* Round to next 8 byte boundary. */
935 pPatch->uCurPatchOffset = RT_ALIGN_32(pPatch->uCurPatchOffset, 8);
936
937 pVM->patm.s.pfnHelperIretGC = PATCHCODE_PTR_GC(pPatch) + pPatch->uCurPatchOffset;
938 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
939 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMIretFunctionRecord, 0, false);
940 PATCHGEN_EPILOG(pPatch, size);
941
942 Log(("pfnHelperCallGC %RRv\n", pVM->patm.s.pfnHelperCallGC));
943 Log(("pfnHelperRetGC %RRv\n", pVM->patm.s.pfnHelperRetGC));
944 Log(("pfnHelperJumpGC %RRv\n", pVM->patm.s.pfnHelperJumpGC));
945 Log(("pfnHelperIretGC %RRv\n", pVM->patm.s.pfnHelperIretGC));
946
947 return VINF_SUCCESS;
948}
949
950/**
951 * Generate illegal instruction (int 3)
952 *
953 * @returns VBox status code.
954 * @param pVM The VM to operate on.
955 * @param pPatch Patch structure
956 *
957 */
958int patmPatchGenIllegalInstr(PVM pVM, PPATCHINFO pPatch)
959{
960 PATCHGEN_PROLOG(pVM, pPatch);
961
962 pPB[0] = 0xCC;
963
964 PATCHGEN_EPILOG(pPatch, 1);
965 return VINF_SUCCESS;
966}
967
968/**
969 * Check virtual IF flag and jump back to original guest code if set
970 *
971 * @returns VBox status code.
972 * @param pVM The VM to operate on.
973 * @param pPatch Patch structure
974 * @param pCurInstrGC Guest context pointer to the current instruction
975 *
976 */
977int patmPatchGenCheckIF(PVM pVM, PPATCHINFO pPatch, RTRCPTR pCurInstrGC)
978{
979 uint32_t size;
980
981 PATCHGEN_PROLOG(pVM, pPatch);
982
983 /* Add lookup record for patch to guest address translation */
984 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pCurInstrGC, PATM_LOOKUP_PATCH2GUEST);
985
986 /* Generate code to check for IF=1 before executing the call to the duplicated function. */
987 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMCheckIFRecord, pCurInstrGC, true);
988
989 PATCHGEN_EPILOG(pPatch, size);
990 return VINF_SUCCESS;
991}
992
993/**
994 * Set PATM interrupt flag
995 *
996 * @returns VBox status code.
997 * @param pVM The VM to operate on.
998 * @param pPatch Patch structure
999 * @param pInstrGC Corresponding guest instruction
1000 *
1001 */
1002int patmPatchGenSetPIF(PVM pVM, PPATCHINFO pPatch, RTRCPTR pInstrGC)
1003{
1004 PATCHGEN_PROLOG(pVM, pPatch);
1005
1006 /* Add lookup record for patch to guest address translation */
1007 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pInstrGC, PATM_LOOKUP_PATCH2GUEST);
1008
1009 int size = patmPatchGenCode(pVM, pPatch, pPB, &PATMSetPIFRecord, 0, false);
1010 PATCHGEN_EPILOG(pPatch, size);
1011 return VINF_SUCCESS;
1012}
1013
1014/**
1015 * Clear PATM interrupt flag
1016 *
1017 * @returns VBox status code.
1018 * @param pVM The VM to operate on.
1019 * @param pPatch Patch structure
1020 * @param pInstrGC Corresponding guest instruction
1021 *
1022 */
1023int patmPatchGenClearPIF(PVM pVM, PPATCHINFO pPatch, RTRCPTR pInstrGC)
1024{
1025 PATCHGEN_PROLOG(pVM, pPatch);
1026
1027 /* Add lookup record for patch to guest address translation */
1028 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pInstrGC, PATM_LOOKUP_PATCH2GUEST);
1029
1030 int size = patmPatchGenCode(pVM, pPatch, pPB, &PATMClearPIFRecord, 0, false);
1031 PATCHGEN_EPILOG(pPatch, size);
1032 return VINF_SUCCESS;
1033}
1034
1035
1036/**
1037 * Clear PATM inhibit irq flag
1038 *
1039 * @returns VBox status code.
1040 * @param pVM The VM to operate on.
1041 * @param pPatch Patch structure
1042 * @param pNextInstrGC Next guest instruction
1043 */
1044int patmPatchGenClearInhibitIRQ(PVM pVM, PPATCHINFO pPatch, RTRCPTR pNextInstrGC)
1045{
1046 int size;
1047 PATMCALLINFO callInfo;
1048
1049 PATCHGEN_PROLOG(pVM, pPatch);
1050
1051 Assert((pPatch->flags & (PATMFL_GENERATE_JUMPTOGUEST|PATMFL_DUPLICATE_FUNCTION)) != (PATMFL_GENERATE_JUMPTOGUEST|PATMFL_DUPLICATE_FUNCTION));
1052
1053 /* Add lookup record for patch to guest address translation */
1054 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pNextInstrGC, PATM_LOOKUP_PATCH2GUEST);
1055
1056 callInfo.pNextInstrGC = pNextInstrGC;
1057
1058 if (pPatch->flags & PATMFL_DUPLICATE_FUNCTION)
1059 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMClearInhibitIRQContIF0Record, 0, false, &callInfo);
1060 else
1061 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMClearInhibitIRQFaultIF0Record, 0, false, &callInfo);
1062
1063 PATCHGEN_EPILOG(pPatch, size);
1064 return VINF_SUCCESS;
1065}
1066
1067/**
1068 * Generate an interrupt handler entrypoint
1069 *
1070 * @returns VBox status code.
1071 * @param pVM The VM to operate on.
1072 * @param pPatch Patch record
1073 * @param pIntHandlerGC IDT handler address
1074 *
1075 ** @todo must check if virtual IF is already cleared on entry!!!!!!!!!!!!!!!!!!!!!!!
1076 */
1077int patmPatchGenIntEntry(PVM pVM, PPATCHINFO pPatch, RTRCPTR pIntHandlerGC)
1078{
1079 uint32_t size;
1080 int rc = VINF_SUCCESS;
1081
1082 PATCHGEN_PROLOG(pVM, pPatch);
1083
1084 /* Add lookup record for patch to guest address translation */
1085 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pIntHandlerGC, PATM_LOOKUP_PATCH2GUEST);
1086
1087 /* Generate entrypoint for the interrupt handler (correcting CS in the interrupt stack frame) */
1088 size = patmPatchGenCode(pVM, pPatch, pPB,
1089 (pPatch->flags & PATMFL_INTHANDLER_WITH_ERRORCODE) ? &PATMIntEntryRecordErrorCode : &PATMIntEntryRecord,
1090 0, false);
1091
1092 PATCHGEN_EPILOG(pPatch, size);
1093
1094 // Interrupt gates set IF to 0
1095 rc = patmPatchGenCli(pVM, pPatch);
1096 AssertRCReturn(rc, rc);
1097
1098 return rc;
1099}
1100
1101/**
1102 * Generate a trap handler entrypoint
1103 *
1104 * @returns VBox status code.
1105 * @param pVM The VM to operate on.
1106 * @param pPatch Patch record
1107 * @param pTrapHandlerGC IDT handler address
1108 */
1109int patmPatchGenTrapEntry(PVM pVM, PPATCHINFO pPatch, RTRCPTR pTrapHandlerGC)
1110{
1111 uint32_t size;
1112
1113 PATCHGEN_PROLOG(pVM, pPatch);
1114
1115 /* Add lookup record for patch to guest address translation */
1116 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pTrapHandlerGC, PATM_LOOKUP_PATCH2GUEST);
1117
1118 /* Generate entrypoint for the trap handler (correcting CS in the interrupt stack frame) */
1119 size = patmPatchGenCode(pVM, pPatch, pPB,
1120 (pPatch->flags & PATMFL_TRAPHANDLER_WITH_ERRORCODE) ? &PATMTrapEntryRecordErrorCode : &PATMTrapEntryRecord,
1121 pTrapHandlerGC, true);
1122 PATCHGEN_EPILOG(pPatch, size);
1123
1124 return VINF_SUCCESS;
1125}
1126
1127#ifdef VBOX_WITH_STATISTICS
1128int patmPatchGenStats(PVM pVM, PPATCHINFO pPatch, RTRCPTR pInstrGC)
1129{
1130 uint32_t size;
1131
1132 PATCHGEN_PROLOG(pVM, pPatch);
1133
1134 /* Add lookup record for stats code -> guest handler. */
1135 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pInstrGC, PATM_LOOKUP_PATCH2GUEST);
1136
1137 /* Generate code to keep calling statistics for this patch */
1138 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMStatsRecord, pInstrGC, false);
1139 PATCHGEN_EPILOG(pPatch, size);
1140
1141 return VINF_SUCCESS;
1142}
1143#endif
1144
1145/**
1146 * Debug register moves to or from general purpose registers
1147 * mov GPR, DRx
1148 * mov DRx, GPR
1149 *
1150 * @todo: if we ever want to support hardware debug registers natively, then
1151 * this will need to be changed!
1152 */
1153int patmPatchGenMovDebug(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu)
1154{
1155 int rc = VINF_SUCCESS;
1156 int reg, mod, rm, dbgreg;
1157 uint32_t offset;
1158
1159 PATCHGEN_PROLOG(pVM, pPatch);
1160
1161 mod = 0; //effective address (only)
1162 rm = 5; //disp32
1163 if (pCpu->pCurInstr->param1 == OP_PARM_Dd)
1164 {
1165 Assert(0); // You not come here. Illegal!
1166
1167 // mov DRx, GPR
1168 pPB[0] = 0x89; //mov disp32, GPR
1169 Assert(pCpu->param1.flags & USE_REG_DBG);
1170 Assert(pCpu->param2.flags & USE_REG_GEN32);
1171
1172 dbgreg = pCpu->param1.base.reg_dbg;
1173 reg = pCpu->param2.base.reg_gen;
1174 }
1175 else
1176 {
1177 // mov GPR, DRx
1178 Assert(pCpu->param1.flags & USE_REG_GEN32);
1179 Assert(pCpu->param2.flags & USE_REG_DBG);
1180
1181 pPB[0] = 0x8B; // mov GPR, disp32
1182 reg = pCpu->param1.base.reg_gen;
1183 dbgreg = pCpu->param2.base.reg_dbg;
1184 }
1185
1186 pPB[1] = MAKE_MODRM(mod, reg, rm);
1187
1188 AssertReturn(dbgreg <= USE_REG_DR7, VERR_INVALID_PARAMETER);
1189 offset = RT_OFFSETOF(CPUMCTX, dr[dbgreg]);
1190
1191 *(RTRCPTR *)&pPB[2] = pVM->patm.s.pCPUMCtxGC + offset;
1192 patmPatchAddReloc32(pVM, pPatch, &pPB[2], FIXUP_ABSOLUTE);
1193
1194 PATCHGEN_EPILOG(pPatch, 2 + sizeof(RTRCPTR));
1195 return rc;
1196}
1197
1198/*
1199 * Control register moves to or from general purpose registers
1200 * mov GPR, CRx
1201 * mov CRx, GPR
1202 */
1203int patmPatchGenMovControl(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu)
1204{
1205 int rc = VINF_SUCCESS;
1206 int reg, mod, rm, ctrlreg;
1207 uint32_t offset;
1208
1209 PATCHGEN_PROLOG(pVM, pPatch);
1210
1211 mod = 0; //effective address (only)
1212 rm = 5; //disp32
1213 if (pCpu->pCurInstr->param1 == OP_PARM_Cd)
1214 {
1215 Assert(0); // You not come here. Illegal!
1216
1217 // mov CRx, GPR
1218 pPB[0] = 0x89; //mov disp32, GPR
1219 ctrlreg = pCpu->param1.base.reg_ctrl;
1220 reg = pCpu->param2.base.reg_gen;
1221 Assert(pCpu->param1.flags & USE_REG_CR);
1222 Assert(pCpu->param2.flags & USE_REG_GEN32);
1223 }
1224 else
1225 {
1226 // mov GPR, DRx
1227 Assert(pCpu->param1.flags & USE_REG_GEN32);
1228 Assert(pCpu->param2.flags & USE_REG_CR);
1229
1230 pPB[0] = 0x8B; // mov GPR, disp32
1231 reg = pCpu->param1.base.reg_gen;
1232 ctrlreg = pCpu->param2.base.reg_ctrl;
1233 }
1234
1235 pPB[1] = MAKE_MODRM(mod, reg, rm);
1236
1237 /// @todo: make this an array in the context structure
1238 switch (ctrlreg)
1239 {
1240 case USE_REG_CR0:
1241 offset = RT_OFFSETOF(CPUMCTX, cr0);
1242 break;
1243 case USE_REG_CR2:
1244 offset = RT_OFFSETOF(CPUMCTX, cr2);
1245 break;
1246 case USE_REG_CR3:
1247 offset = RT_OFFSETOF(CPUMCTX, cr3);
1248 break;
1249 case USE_REG_CR4:
1250 offset = RT_OFFSETOF(CPUMCTX, cr4);
1251 break;
1252 default: /* Shut up compiler warning. */
1253 AssertFailed();
1254 offset = 0;
1255 break;
1256 }
1257 *(RTRCPTR *)&pPB[2] = pVM->patm.s.pCPUMCtxGC + offset;
1258 patmPatchAddReloc32(pVM, pPatch, &pPB[2], FIXUP_ABSOLUTE);
1259
1260 PATCHGEN_EPILOG(pPatch, 2 + sizeof(RTRCPTR));
1261 return rc;
1262}
1263
1264/*
1265 * mov GPR, SS
1266 */
1267int patmPatchGenMovFromSS(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu, RTRCPTR pCurInstrGC)
1268{
1269 uint32_t size, offset;
1270
1271 Log(("patmPatchGenMovFromSS %RRv\n", pCurInstrGC));
1272
1273 Assert(pPatch->flags & PATMFL_CODE32);
1274
1275 PATCHGEN_PROLOG(pVM, pPatch);
1276 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMClearPIFRecord, 0, false);
1277 PATCHGEN_EPILOG(pPatch, size);
1278
1279 /* push ss */
1280 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
1281 offset = 0;
1282 if (pCpu->prefix & PREFIX_OPSIZE)
1283 pPB[offset++] = 0x66; /* size override -> 16 bits push */
1284 pPB[offset++] = 0x16;
1285 PATCHGEN_EPILOG(pPatch, offset);
1286
1287 /* checks and corrects RPL of pushed ss*/
1288 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
1289 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMMovFromSSRecord, 0, false);
1290 PATCHGEN_EPILOG(pPatch, size);
1291
1292 /* pop general purpose register */
1293 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
1294 offset = 0;
1295 if (pCpu->prefix & PREFIX_OPSIZE)
1296 pPB[offset++] = 0x66; /* size override -> 16 bits pop */
1297 pPB[offset++] = 0x58 + pCpu->param1.base.reg_gen;
1298 PATCHGEN_EPILOG(pPatch, offset);
1299
1300
1301 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
1302 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMSetPIFRecord, 0, false);
1303 PATCHGEN_EPILOG(pPatch, size);
1304
1305 return VINF_SUCCESS;
1306}
1307
1308
1309/**
1310 * Generate an sldt or str patch instruction
1311 *
1312 * @returns VBox status code.
1313 * @param pVM The VM to operate on.
1314 * @param pPatch Patch record
1315 * @param pCpu Disassembly state
1316 * @param pCurInstrGC Guest instruction address
1317 */
1318int patmPatchGenSldtStr(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu, RTRCPTR pCurInstrGC)
1319{
1320 // sldt %Ew
1321 int rc = VINF_SUCCESS;
1322 uint32_t offset = 0;
1323 uint32_t i;
1324
1325 /** @todo segment prefix (untested) */
1326 Assert(pCpu->prefix == PREFIX_NONE || pCpu->prefix == PREFIX_OPSIZE);
1327
1328 PATCHGEN_PROLOG(pVM, pPatch);
1329
1330 if (pCpu->param1.flags == USE_REG_GEN32 || pCpu->param1.flags == USE_REG_GEN16)
1331 {
1332 /* Register operand */
1333 // 8B 15 [32 bits addr] mov edx, CPUMCTX.tr/ldtr
1334
1335 if (pCpu->prefix == PREFIX_OPSIZE)
1336 pPB[offset++] = 0x66;
1337
1338 pPB[offset++] = 0x8B; // mov destreg, CPUMCTX.tr/ldtr
1339 /* Modify REG part according to destination of original instruction */
1340 pPB[offset++] = MAKE_MODRM(0, pCpu->param1.base.reg_gen, 5);
1341 if (pCpu->pCurInstr->opcode == OP_STR)
1342 {
1343 *(RTRCPTR *)&pPB[offset] = pVM->patm.s.pCPUMCtxGC + RT_OFFSETOF(CPUMCTX, tr);
1344 }
1345 else
1346 {
1347 *(RTRCPTR *)&pPB[offset] = pVM->patm.s.pCPUMCtxGC + RT_OFFSETOF(CPUMCTX, ldtr);
1348 }
1349 patmPatchAddReloc32(pVM, pPatch, &pPB[offset], FIXUP_ABSOLUTE);
1350 offset += sizeof(RTRCPTR);
1351 }
1352 else
1353 {
1354 /* Memory operand */
1355 //50 push eax
1356 //52 push edx
1357 //8D 15 48 7C 42 00 lea edx, dword ptr [dest]
1358 //66 A1 48 7C 42 00 mov ax, CPUMCTX.tr/ldtr
1359 //66 89 02 mov word ptr [edx],ax
1360 //5A pop edx
1361 //58 pop eax
1362
1363 pPB[offset++] = 0x50; // push eax
1364 pPB[offset++] = 0x52; // push edx
1365
1366 if (pCpu->prefix == PREFIX_SEG)
1367 {
1368 pPB[offset++] = DISQuerySegPrefixByte(pCpu);
1369 }
1370 pPB[offset++] = 0x8D; // lea edx, dword ptr [dest]
1371 // duplicate and modify modrm byte and additional bytes if present (e.g. direct address)
1372 pPB[offset++] = MAKE_MODRM(pCpu->ModRM.Bits.Mod, USE_REG_EDX, pCpu->ModRM.Bits.Rm);
1373
1374 i = 3; /* standard offset of modrm bytes */
1375 if (pCpu->prefix == PREFIX_OPSIZE)
1376 i++; //skip operand prefix
1377 if (pCpu->prefix == PREFIX_SEG)
1378 i++; //skip segment prefix
1379
1380 rc = patmPatchReadBytes(pVM, &pPB[offset], (RTRCPTR)((RTGCUINTPTR32)pCurInstrGC + i), pCpu->opsize - i);
1381 AssertRCReturn(rc, rc);
1382 offset += (pCpu->opsize - i);
1383
1384 pPB[offset++] = 0x66; // mov ax, CPUMCTX.tr/ldtr
1385 pPB[offset++] = 0xA1;
1386 if (pCpu->pCurInstr->opcode == OP_STR)
1387 {
1388 *(RTRCPTR *)&pPB[offset] = pVM->patm.s.pCPUMCtxGC + RT_OFFSETOF(CPUMCTX, tr);
1389 }
1390 else
1391 {
1392 *(RTRCPTR *)&pPB[offset] = pVM->patm.s.pCPUMCtxGC + RT_OFFSETOF(CPUMCTX, ldtr);
1393 }
1394 patmPatchAddReloc32(pVM, pPatch, &pPB[offset], FIXUP_ABSOLUTE);
1395 offset += sizeof(RTRCPTR);
1396
1397 pPB[offset++] = 0x66; // mov word ptr [edx],ax
1398 pPB[offset++] = 0x89;
1399 pPB[offset++] = 0x02;
1400
1401 pPB[offset++] = 0x5A; // pop edx
1402 pPB[offset++] = 0x58; // pop eax
1403 }
1404
1405 PATCHGEN_EPILOG(pPatch, offset);
1406
1407 return rc;
1408}
1409
1410/**
1411 * Generate an sgdt or sidt patch instruction
1412 *
1413 * @returns VBox status code.
1414 * @param pVM The VM to operate on.
1415 * @param pPatch Patch record
1416 * @param pCpu Disassembly state
1417 * @param pCurInstrGC Guest instruction address
1418 */
1419int patmPatchGenSxDT(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu, RTRCPTR pCurInstrGC)
1420{
1421 int rc = VINF_SUCCESS;
1422 uint32_t offset = 0, offset_base, offset_limit;
1423 uint32_t i;
1424
1425 /* @todo segment prefix (untested) */
1426 Assert(pCpu->prefix == PREFIX_NONE);
1427
1428 // sgdt %Ms
1429 // sidt %Ms
1430
1431 switch (pCpu->pCurInstr->opcode)
1432 {
1433 case OP_SGDT:
1434 offset_base = RT_OFFSETOF(CPUMCTX, gdtr.pGdt);
1435 offset_limit = RT_OFFSETOF(CPUMCTX, gdtr.cbGdt);
1436 break;
1437
1438 case OP_SIDT:
1439 offset_base = RT_OFFSETOF(CPUMCTX, idtr.pIdt);
1440 offset_limit = RT_OFFSETOF(CPUMCTX, idtr.cbIdt);
1441 break;
1442
1443 default:
1444 return VERR_INVALID_PARAMETER;
1445 }
1446
1447//50 push eax
1448//52 push edx
1449//8D 15 48 7C 42 00 lea edx, dword ptr [dest]
1450//66 A1 48 7C 42 00 mov ax, CPUMCTX.gdtr.limit
1451//66 89 02 mov word ptr [edx],ax
1452//A1 48 7C 42 00 mov eax, CPUMCTX.gdtr.base
1453//89 42 02 mov dword ptr [edx+2],eax
1454//5A pop edx
1455//58 pop eax
1456
1457 PATCHGEN_PROLOG(pVM, pPatch);
1458 pPB[offset++] = 0x50; // push eax
1459 pPB[offset++] = 0x52; // push edx
1460
1461 if (pCpu->prefix == PREFIX_SEG)
1462 {
1463 pPB[offset++] = DISQuerySegPrefixByte(pCpu);
1464 }
1465 pPB[offset++] = 0x8D; // lea edx, dword ptr [dest]
1466 // duplicate and modify modrm byte and additional bytes if present (e.g. direct address)
1467 pPB[offset++] = MAKE_MODRM(pCpu->ModRM.Bits.Mod, USE_REG_EDX, pCpu->ModRM.Bits.Rm);
1468
1469 i = 3; /* standard offset of modrm bytes */
1470 if (pCpu->prefix == PREFIX_OPSIZE)
1471 i++; //skip operand prefix
1472 if (pCpu->prefix == PREFIX_SEG)
1473 i++; //skip segment prefix
1474 rc = patmPatchReadBytes(pVM, &pPB[offset], (RTRCPTR)((RTGCUINTPTR32)pCurInstrGC + i), pCpu->opsize - i);
1475 AssertRCReturn(rc, rc);
1476 offset += (pCpu->opsize - i);
1477
1478 pPB[offset++] = 0x66; // mov ax, CPUMCTX.gdtr.limit
1479 pPB[offset++] = 0xA1;
1480 *(RTRCPTR *)&pPB[offset] = pVM->patm.s.pCPUMCtxGC + offset_limit;
1481 patmPatchAddReloc32(pVM, pPatch, &pPB[offset], FIXUP_ABSOLUTE);
1482 offset += sizeof(RTRCPTR);
1483
1484 pPB[offset++] = 0x66; // mov word ptr [edx],ax
1485 pPB[offset++] = 0x89;
1486 pPB[offset++] = 0x02;
1487
1488 pPB[offset++] = 0xA1; // mov eax, CPUMCTX.gdtr.base
1489 *(RTRCPTR *)&pPB[offset] = pVM->patm.s.pCPUMCtxGC + offset_base;
1490 patmPatchAddReloc32(pVM, pPatch, &pPB[offset], FIXUP_ABSOLUTE);
1491 offset += sizeof(RTRCPTR);
1492
1493 pPB[offset++] = 0x89; // mov dword ptr [edx+2],eax
1494 pPB[offset++] = 0x42;
1495 pPB[offset++] = 0x02;
1496
1497 pPB[offset++] = 0x5A; // pop edx
1498 pPB[offset++] = 0x58; // pop eax
1499
1500 PATCHGEN_EPILOG(pPatch, offset);
1501
1502 return rc;
1503}
1504
1505/**
1506 * Generate a cpuid patch instruction
1507 *
1508 * @returns VBox status code.
1509 * @param pVM The VM to operate on.
1510 * @param pPatch Patch record
1511 * @param pCurInstrGC Guest instruction address
1512 */
1513int patmPatchGenCpuid(PVM pVM, PPATCHINFO pPatch, RTRCPTR pCurInstrGC)
1514{
1515 uint32_t size;
1516 PATCHGEN_PROLOG(pVM, pPatch);
1517
1518 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMCpuidRecord, 0, false);
1519
1520 PATCHGEN_EPILOG(pPatch, size);
1521 return VINF_SUCCESS;
1522}
1523
1524/**
1525 * Generate the jump from guest to patch code
1526 *
1527 * @returns VBox status code.
1528 * @param pVM The VM to operate on.
1529 * @param pPatch Patch record
1530 * @param pTargetGC Guest target jump
1531 * @param fClearInhibitIRQs Clear inhibit irq flag
1532 */
1533int patmPatchGenJumpToGuest(PVM pVM, PPATCHINFO pPatch, RCPTRTYPE(uint8_t *) pReturnAddrGC, bool fClearInhibitIRQs)
1534{
1535 int rc = VINF_SUCCESS;
1536 uint32_t size;
1537
1538 if (fClearInhibitIRQs)
1539 {
1540 rc = patmPatchGenClearInhibitIRQ(pVM, pPatch, pReturnAddrGC);
1541 if (rc == VERR_NO_MEMORY)
1542 return rc;
1543 AssertRCReturn(rc, rc);
1544 }
1545
1546 PATCHGEN_PROLOG(pVM, pPatch);
1547
1548 /* Add lookup record for patch to guest address translation */
1549 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pReturnAddrGC, PATM_LOOKUP_PATCH2GUEST);
1550
1551 /* Generate code to jump to guest code if IF=1, else fault. */
1552 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMJumpToGuest_IF1Record, pReturnAddrGC, true);
1553 PATCHGEN_EPILOG(pPatch, size);
1554
1555 return rc;
1556}
1557
1558/*
1559 * Relative jump from patch code to patch code (no fixup required)
1560 */
1561int patmPatchGenPatchJump(PVM pVM, PPATCHINFO pPatch, RTRCPTR pCurInstrGC, RCPTRTYPE(uint8_t *) pPatchAddrGC, bool fAddLookupRecord)
1562{
1563 int32_t displ;
1564 int rc = VINF_SUCCESS;
1565
1566 Assert(PATMIsPatchGCAddr(pVM, pPatchAddrGC));
1567 PATCHGEN_PROLOG(pVM, pPatch);
1568
1569 if (fAddLookupRecord)
1570 {
1571 /* Add lookup record for patch to guest address translation */
1572 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pCurInstrGC, PATM_LOOKUP_PATCH2GUEST);
1573 }
1574
1575 pPB[0] = 0xE9; //JMP
1576
1577 displ = pPatchAddrGC - (PATCHCODE_PTR_GC(pPatch) + pPatch->uCurPatchOffset + SIZEOF_NEARJUMP32);
1578
1579 *(uint32_t *)&pPB[1] = displ;
1580
1581 PATCHGEN_EPILOG(pPatch, SIZEOF_NEARJUMP32);
1582
1583 return rc;
1584}
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