VirtualBox

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

Last change on this file since 1237 was 742, checked in by vboxsync, 18 years ago

Updates for logging

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