VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/PATMPatch.cpp@ 39078

Last change on this file since 39078 was 39078, checked in by vboxsync, 13 years ago

VMM: -Wunused-parameter

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