VirtualBox

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

Last change on this file since 3723 was 3020, checked in by vboxsync, 17 years ago

Added missing space after ')' in macro invocations so VCC doesn't mess up the precompiler output.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 52.1 KB
Line 
1/* $Id: PATMPatch.cpp 3020 2007-06-04 12:01:53Z 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 innotek 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 /* include prefix byte to make sure we don't use the incorrect selector register. */
694 if (pCpu->prefix & PREFIX_SEG)
695 pPB[offset++] = DISQuerySegPrefixByte(pCpu);
696 pPB[offset++] = 0xFF; // push r/m32
697 pPB[offset++] = MAKE_MODRM(MODRM_MOD(pCpu->ModRM), 6 /* group 5 */, MODRM_RM(pCpu->ModRM));
698 i = 2; /* standard offset of modrm bytes */
699 if (pCpu->prefix & PREFIX_OPSIZE)
700 i++; //skip operand prefix
701 if (pCpu->prefix & PREFIX_SEG)
702 i++; //skip segment prefix
703
704 rc = patmPatchReadBytes(pVM, &pPB[offset], (RTGCPTR)((RTGCUINTPTR)pCurInstrGC + i), pCpu->opsize - i);
705 AssertRCReturn(rc, rc);
706 offset += (pCpu->opsize - i);
707 }
708 else
709 {
710 AssertMsg(PATMIsPatchGCAddr(pVM, pTargetGC) == false, ("Target is already a patch address (%VGv)?!?\n", pTargetGC));
711 Assert(pTargetGC);
712 Assert(OP_PARM_VTYPE(pCpu->pCurInstr->param1) == OP_PARM_J);
713
714 /** @todo wasting memory as the complex search is overkill and we need only one lookup slot... */
715
716 /* Relative call to patch code (patch to patch -> no fixup). */
717 Log(("PatchGenCall from %VGv (next=%VGv) to %VGv\n", pCurInstrGC, pCurInstrGC + pCpu->opsize, pTargetGC));
718
719 /* We push it onto the stack here, so the guest's context isn't ruined when this happens to cause
720 * a page fault. The assembly code restores the stack afterwards.
721 */
722 offset = 0;
723 pPB[offset++] = 0x68; // push %Iv
724 *(RTGCPTR *)&pPB[offset] = pTargetGC;
725 offset += sizeof(RTGCPTR);
726 }
727
728 /* align this block properly to make sure the jump table will not be misaligned. */
729 size = (RTHCUINTPTR)&pPB[offset] & 3;
730 if (size)
731 size = 4 - size;
732
733 for (i=0;i<size;i++)
734 {
735 pPB[offset++] = 0x90; /* nop */
736 }
737 PATCHGEN_EPILOG(pPatch, offset);
738
739 /* 3: Generate code to lookup address in our local cache; call hypervisor PATM code if it can't be located. */
740 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
741 callInfo.pReturnGC = pCurInstrGC + pCpu->opsize;
742 callInfo.pTargetGC = (fIndirect) ? 0xDEADBEEF : pTargetGC;
743 size = patmPatchGenCode(pVM, pPatch, pPB, (fIndirect) ? &PATMCallIndirectRecord : &PATMCallRecord, 0, false, &callInfo);
744 PATCHGEN_EPILOG(pPatch, size);
745
746 /* Need to set PATM_INTERRUPTFLAG after the patched ret returns here. */
747 rc = patmPatchGenSetPIF(pVM, pPatch, pCurInstrGC);
748 if (rc == VERR_NO_MEMORY)
749 return rc;
750 AssertRCReturn(rc, rc);
751
752 STAM_COUNTER_INC(&pVM->patm.s.StatGenCall);
753 return VINF_SUCCESS;
754}
755
756/**
757 * Generate indirect jump to unknown destination
758 *
759 * @returns VBox status code.
760 * @param pVM The VM to operate on.
761 * @param pPatch Patch record
762 * @param pCpu Disassembly state
763 * @param pCurInstrGC Current instruction address
764 */
765int patmPatchGenJump(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu, RTGCPTR pCurInstrGC)
766{
767 PATMCALLINFO callInfo;
768 uint32_t offset;
769 uint32_t i, size;
770 int rc;
771
772 /* 1: Clear PATM interrupt flag on entry. */
773 rc = patmPatchGenClearPIF(pVM, pPatch, pCurInstrGC);
774 if (rc == VERR_NO_MEMORY)
775 return rc;
776 AssertRCReturn(rc, rc);
777
778 PATCHGEN_PROLOG(pVM, pPatch);
779 /* 2: We must push the target address onto the stack before appending the indirect call code. */
780
781 Log(("patmPatchGenIndirectJump\n"));
782 Assert(pCpu->param1.size == 4);
783 Assert(OP_PARM_VTYPE(pCpu->pCurInstr->param1) != OP_PARM_J);
784
785 /* We push it onto the stack here, so the guest's context isn't ruined when this happens to cause
786 * a page fault. The assembly code restores the stack afterwards.
787 */
788 offset = 0;
789 /* include prefix byte to make sure we don't use the incorrect selector register. */
790 if (pCpu->prefix & PREFIX_SEG)
791 pPB[offset++] = DISQuerySegPrefixByte(pCpu);
792
793 pPB[offset++] = 0xFF; // push r/m32
794 pPB[offset++] = MAKE_MODRM(MODRM_MOD(pCpu->ModRM), 6 /* group 5 */, MODRM_RM(pCpu->ModRM));
795 i = 2; /* standard offset of modrm bytes */
796 if (pCpu->prefix & PREFIX_OPSIZE)
797 i++; //skip operand prefix
798 if (pCpu->prefix & PREFIX_SEG)
799 i++; //skip segment prefix
800
801 rc = patmPatchReadBytes(pVM, &pPB[offset], (RTGCPTR)((RTGCUINTPTR)pCurInstrGC + i), pCpu->opsize - i);
802 AssertRCReturn(rc, rc);
803 offset += (pCpu->opsize - i);
804
805 /* align this block properly to make sure the jump table will not be misaligned. */
806 size = (RTHCUINTPTR)&pPB[offset] & 3;
807 if (size)
808 size = 4 - size;
809
810 for (i=0;i<size;i++)
811 {
812 pPB[offset++] = 0x90; /* nop */
813 }
814 PATCHGEN_EPILOG(pPatch, offset);
815
816 /* 3: Generate code to lookup address in our local cache; call hypervisor PATM code if it can't be located. */
817 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
818 callInfo.pReturnGC = pCurInstrGC + pCpu->opsize;
819 callInfo.pTargetGC = 0xDEADBEEF;
820 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMJumpIndirectRecord, 0, false, &callInfo);
821 PATCHGEN_EPILOG(pPatch, size);
822
823 STAM_COUNTER_INC(&pVM->patm.s.StatGenJump);
824 return VINF_SUCCESS;
825}
826
827/**
828 * Generate return instruction
829 *
830 * @returns VBox status code.
831 * @param pVM The VM to operate on.
832 * @param pPatch Patch structure
833 * @param pCpu Disassembly struct
834 * @param pCurInstrGC Current instruction pointer
835 *
836 */
837int patmPatchGenRet(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu, GCPTRTYPE(uint8_t *) pCurInstrGC)
838{
839 int size = 0, rc;
840 RTGCPTR pPatchRetInstrGC;
841
842 /* Remember start of this patch for below. */
843 pPatchRetInstrGC = PATCHCODE_PTR_GC(pPatch) + pPatch->uCurPatchOffset;
844
845 Log(("patmPatchGenRet %VGv\n", pCurInstrGC));
846
847 /** @note optimization: multiple identical ret instruction in a single patch can share a single patched ret. */
848 if ( pPatch->pTempInfo->pPatchRetInstrGC
849 && pPatch->pTempInfo->uPatchRetParam1 == (uint32_t)pCpu->param1.parval) /* nr of bytes popped off the stack should be identical of course! */
850 {
851 Assert(pCpu->pCurInstr->opcode == OP_RETN);
852 STAM_COUNTER_INC(&pVM->patm.s.StatGenRetReused);
853
854 return patmPatchGenPatchJump(pVM, pPatch, pCurInstrGC, pPatch->pTempInfo->pPatchRetInstrGC);
855 }
856
857 /* Jump back to the original instruction if IF is set again. */
858 Assert(!PATMFindActivePatchByEntrypoint(pVM, pCurInstrGC));
859 rc = patmPatchGenCheckIF(pVM, pPatch, pCurInstrGC);
860 AssertRCReturn(rc, rc);
861
862 /* align this block properly to make sure the jump table will not be misaligned. */
863 PATCHGEN_PROLOG(pVM, pPatch);
864 size = (RTHCUINTPTR)pPB & 3;
865 if (size)
866 size = 4 - size;
867
868 for (int i=0;i<size;i++)
869 pPB[i] = 0x90; /* nop */
870 PATCHGEN_EPILOG(pPatch, size);
871
872 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
873 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMRetRecord, 0, false);
874 PATCHGEN_EPILOG(pPatch, size);
875
876 STAM_COUNTER_INC(&pVM->patm.s.StatGenRet);
877 /* Duplicate the ret or ret n instruction; it will use the PATM return address */
878 rc = patmPatchGenDuplicate(pVM, pPatch, pCpu, pCurInstrGC);
879
880 if (rc == VINF_SUCCESS)
881 {
882 pPatch->pTempInfo->pPatchRetInstrGC = pPatchRetInstrGC;
883 pPatch->pTempInfo->uPatchRetParam1 = pCpu->param1.parval;
884 }
885 return rc;
886}
887
888/**
889 * Generate all global patm functions
890 *
891 * @returns VBox status code.
892 * @param pVM The VM to operate on.
893 * @param pPatch Patch structure
894 *
895 */
896int patmPatchGenGlobalFunctions(PVM pVM, PPATCHINFO pPatch)
897{
898 int size = 0;
899
900 pVM->patm.s.pfnHelperCallGC = PATCHCODE_PTR_GC(pPatch) + pPatch->uCurPatchOffset;
901 PATCHGEN_PROLOG(pVM, pPatch);
902 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMLookupAndCallRecord, 0, false);
903 PATCHGEN_EPILOG(pPatch, size);
904
905 /* Round to next 8 byte boundary. */
906 pPatch->uCurPatchOffset = RT_ALIGN_32(pPatch->uCurPatchOffset, 8);
907
908 pVM->patm.s.pfnHelperRetGC = PATCHCODE_PTR_GC(pPatch) + pPatch->uCurPatchOffset;
909 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
910 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMRetFunctionRecord, 0, false);
911 PATCHGEN_EPILOG(pPatch, size);
912
913 /* Round to next 8 byte boundary. */
914 pPatch->uCurPatchOffset = RT_ALIGN_32(pPatch->uCurPatchOffset, 8);
915
916 pVM->patm.s.pfnHelperJumpGC = PATCHCODE_PTR_GC(pPatch) + pPatch->uCurPatchOffset;
917 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
918 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMLookupAndJumpRecord, 0, false);
919 PATCHGEN_EPILOG(pPatch, size);
920
921 /* Round to next 8 byte boundary. */
922 pPatch->uCurPatchOffset = RT_ALIGN_32(pPatch->uCurPatchOffset, 8);
923
924 pVM->patm.s.pfnHelperIretGC = PATCHCODE_PTR_GC(pPatch) + pPatch->uCurPatchOffset;
925 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
926 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMIretFunctionRecord, 0, false);
927 PATCHGEN_EPILOG(pPatch, size);
928
929 Log(("pfnHelperCallGC %VGv\n", pVM->patm.s.pfnHelperCallGC));
930 Log(("pfnHelperRetGC %VGv\n", pVM->patm.s.pfnHelperRetGC));
931 Log(("pfnHelperJumpGC %VGv\n", pVM->patm.s.pfnHelperJumpGC));
932 Log(("pfnHelperIretGC %VGv\n", pVM->patm.s.pfnHelperIretGC));
933
934 return VINF_SUCCESS;
935}
936
937/**
938 * Generate illegal instruction (int 3)
939 *
940 * @returns VBox status code.
941 * @param pVM The VM to operate on.
942 * @param pPatch Patch structure
943 *
944 */
945int patmPatchGenIllegalInstr(PVM pVM, PPATCHINFO pPatch)
946{
947 PATCHGEN_PROLOG(pVM, pPatch);
948
949 pPB[0] = 0xCC;
950
951 PATCHGEN_EPILOG(pPatch, 1);
952 return VINF_SUCCESS;
953}
954
955/**
956 * Check virtual IF flag and jump back to original guest code if set
957 *
958 * @returns VBox status code.
959 * @param pVM The VM to operate on.
960 * @param pPatch Patch structure
961 * @param pCurInstrGC Guest context pointer to the current instruction
962 *
963 */
964int patmPatchGenCheckIF(PVM pVM, PPATCHINFO pPatch, RTGCPTR pCurInstrGC)
965{
966 uint32_t size;
967
968 PATCHGEN_PROLOG(pVM, pPatch);
969
970 /* Add lookup record for patch to guest address translation */
971 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pCurInstrGC, PATM_LOOKUP_PATCH2GUEST);
972
973 /* Generate code to check for IF=1 before executing the call to the duplicated function. */
974 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMCheckIFRecord, pCurInstrGC, true);
975
976 PATCHGEN_EPILOG(pPatch, size);
977 return VINF_SUCCESS;
978}
979
980/**
981 * Set PATM interrupt flag
982 *
983 * @returns VBox status code.
984 * @param pVM The VM to operate on.
985 * @param pPatch Patch structure
986 * @param pInstrGC Corresponding guest instruction
987 *
988 */
989int patmPatchGenSetPIF(PVM pVM, PPATCHINFO pPatch, RTGCPTR pInstrGC)
990{
991 PATCHGEN_PROLOG(pVM, pPatch);
992
993 /* Add lookup record for patch to guest address translation */
994 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pInstrGC, PATM_LOOKUP_PATCH2GUEST);
995
996 int size = patmPatchGenCode(pVM, pPatch, pPB, &PATMSetPIFRecord, 0, false);
997 PATCHGEN_EPILOG(pPatch, size);
998 return VINF_SUCCESS;
999}
1000
1001/**
1002 * Clear PATM interrupt flag
1003 *
1004 * @returns VBox status code.
1005 * @param pVM The VM to operate on.
1006 * @param pPatch Patch structure
1007 * @param pInstrGC Corresponding guest instruction
1008 *
1009 */
1010int patmPatchGenClearPIF(PVM pVM, PPATCHINFO pPatch, RTGCPTR pInstrGC)
1011{
1012 PATCHGEN_PROLOG(pVM, pPatch);
1013
1014 /* Add lookup record for patch to guest address translation */
1015 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pInstrGC, PATM_LOOKUP_PATCH2GUEST);
1016
1017 int size = patmPatchGenCode(pVM, pPatch, pPB, &PATMClearPIFRecord, 0, false);
1018 PATCHGEN_EPILOG(pPatch, size);
1019 return VINF_SUCCESS;
1020}
1021
1022
1023/**
1024 * Clear PATM inhibit irq flag
1025 *
1026 * @returns VBox status code.
1027 * @param pVM The VM to operate on.
1028 * @param pPatch Patch structure
1029 * @param pNextInstrGC Next guest instruction
1030 */
1031int patmPatchGenClearInhibitIRQ(PVM pVM, PPATCHINFO pPatch, RTGCPTR pNextInstrGC)
1032{
1033 int size;
1034 PATMCALLINFO callInfo;
1035
1036 PATCHGEN_PROLOG(pVM, pPatch);
1037
1038 Assert((pPatch->flags & (PATMFL_GENERATE_JUMPTOGUEST|PATMFL_DUPLICATE_FUNCTION)) != (PATMFL_GENERATE_JUMPTOGUEST|PATMFL_DUPLICATE_FUNCTION));
1039
1040 /* Add lookup record for patch to guest address translation */
1041 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pNextInstrGC, PATM_LOOKUP_PATCH2GUEST);
1042
1043 callInfo.pNextInstrGC = pNextInstrGC;
1044
1045 if (pPatch->flags & PATMFL_DUPLICATE_FUNCTION)
1046 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMClearInhibitIRQContIF0Record, 0, false, &callInfo);
1047 else
1048 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMClearInhibitIRQFaultIF0Record, 0, false, &callInfo);
1049
1050 PATCHGEN_EPILOG(pPatch, size);
1051 return VINF_SUCCESS;
1052}
1053
1054/**
1055 * Generate an interrupt handler entrypoint
1056 *
1057 * @returns VBox status code.
1058 * @param pVM The VM to operate on.
1059 * @param pPatch Patch record
1060 * @param pIntHandlerGC IDT handler address
1061 *
1062 ** @todo must check if virtual IF is already cleared on entry!!!!!!!!!!!!!!!!!!!!!!!
1063 */
1064int patmPatchGenIntEntry(PVM pVM, PPATCHINFO pPatch, RTGCPTR pIntHandlerGC)
1065{
1066 uint32_t size;
1067 int rc = VINF_SUCCESS;
1068
1069 PATCHGEN_PROLOG(pVM, pPatch);
1070
1071 /* Add lookup record for patch to guest address translation */
1072 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pIntHandlerGC, PATM_LOOKUP_PATCH2GUEST);
1073
1074 /* Generate entrypoint for the interrupt handler (correcting CS in the interrupt stack frame) */
1075 size = patmPatchGenCode(pVM, pPatch, pPB,
1076 (pPatch->flags & PATMFL_INTHANDLER_WITH_ERRORCODE) ? &PATMIntEntryRecordErrorCode : &PATMIntEntryRecord,
1077 0, false);
1078
1079 PATCHGEN_EPILOG(pPatch, size);
1080
1081 // Interrupt gates set IF to 0
1082 rc = patmPatchGenCli(pVM, pPatch);
1083 AssertRCReturn(rc, rc);
1084
1085 return rc;
1086}
1087
1088/**
1089 * Generate a trap handler entrypoint
1090 *
1091 * @returns VBox status code.
1092 * @param pVM The VM to operate on.
1093 * @param pPatch Patch record
1094 * @param pTrapHandlerGC IDT handler address
1095 */
1096int patmPatchGenTrapEntry(PVM pVM, PPATCHINFO pPatch, RTGCPTR pTrapHandlerGC)
1097{
1098 uint32_t size;
1099
1100 PATCHGEN_PROLOG(pVM, pPatch);
1101
1102 /* Add lookup record for patch to guest address translation */
1103 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pTrapHandlerGC, PATM_LOOKUP_PATCH2GUEST);
1104
1105 /* Generate entrypoint for the trap handler (correcting CS in the interrupt stack frame) */
1106 size = patmPatchGenCode(pVM, pPatch, pPB,
1107 (pPatch->flags & PATMFL_TRAPHANDLER_WITH_ERRORCODE) ? &PATMTrapEntryRecordErrorCode : &PATMTrapEntryRecord,
1108 pTrapHandlerGC, true);
1109 PATCHGEN_EPILOG(pPatch, size);
1110
1111 return VINF_SUCCESS;
1112}
1113
1114#ifdef VBOX_WITH_STATISTICS
1115int patmPatchGenStats(PVM pVM, PPATCHINFO pPatch, RTGCPTR pInstrGC)
1116{
1117 uint32_t size;
1118
1119 PATCHGEN_PROLOG(pVM, pPatch);
1120
1121 /* Add lookup record for stats code -> guest handler. */
1122 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pInstrGC, PATM_LOOKUP_PATCH2GUEST);
1123
1124 /* Generate code to keep calling statistics for this patch */
1125 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMStatsRecord, pInstrGC, false);
1126 PATCHGEN_EPILOG(pPatch, size);
1127
1128 return VINF_SUCCESS;
1129}
1130#endif
1131
1132/**
1133 * Debug register moves to or from general purpose registers
1134 * mov GPR, DRx
1135 * mov DRx, GPR
1136 *
1137 * @todo: if we ever want to support hardware debug registers natively, then
1138 * this will need to be changed!
1139 */
1140int patmPatchGenMovDebug(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu)
1141{
1142 int rc = VINF_SUCCESS;
1143 int reg, mod, rm, dbgreg;
1144 uint32_t offset;
1145
1146 PATCHGEN_PROLOG(pVM, pPatch);
1147
1148 mod = 0; //effective address (only)
1149 rm = 5; //disp32
1150 if (pCpu->pCurInstr->param1 == OP_PARM_Dd)
1151 {
1152 Assert(0); // You not come here. Illegal!
1153
1154 // mov DRx, GPR
1155 pPB[0] = 0x89; //mov disp32, GPR
1156 Assert(pCpu->param1.flags & USE_REG_DBG);
1157 Assert(pCpu->param2.flags & USE_REG_GEN32);
1158
1159 dbgreg = pCpu->param1.base.reg_dbg;
1160 reg = pCpu->param2.base.reg_gen32;
1161 }
1162 else
1163 {
1164 // mov GPR, DRx
1165 Assert(pCpu->param1.flags & USE_REG_GEN32);
1166 Assert(pCpu->param2.flags & USE_REG_DBG);
1167
1168 pPB[0] = 0x8B; // mov GPR, disp32
1169 reg = pCpu->param1.base.reg_gen32;
1170 dbgreg = pCpu->param2.base.reg_dbg;
1171 }
1172
1173 pPB[1] = MAKE_MODRM(mod, reg, rm);
1174
1175 /// @todo: make this an array in the context structure
1176 switch (dbgreg)
1177 {
1178 case USE_REG_DR0:
1179 offset = RT_OFFSETOF(CPUMCTX, dr0);
1180 break;
1181 case USE_REG_DR1:
1182 offset = RT_OFFSETOF(CPUMCTX, dr1);
1183 break;
1184 case USE_REG_DR2:
1185 offset = RT_OFFSETOF(CPUMCTX, dr2);
1186 break;
1187 case USE_REG_DR3:
1188 offset = RT_OFFSETOF(CPUMCTX, dr3);
1189 break;
1190 case USE_REG_DR4:
1191 offset = RT_OFFSETOF(CPUMCTX, dr4);
1192 break;
1193 case USE_REG_DR5:
1194 offset = RT_OFFSETOF(CPUMCTX, dr5);
1195 break;
1196 case USE_REG_DR6:
1197 offset = RT_OFFSETOF(CPUMCTX, dr6);
1198 break;
1199 case USE_REG_DR7:
1200 offset = RT_OFFSETOF(CPUMCTX, dr7);
1201 break;
1202 default: /* Shut up compiler warning. */
1203 AssertFailed();
1204 offset = 0;
1205 break;
1206 }
1207 *(RTGCPTR *)&pPB[2] = pVM->patm.s.pCPUMCtxGC + offset;
1208 patmPatchAddReloc32(pVM, pPatch, &pPB[2], FIXUP_ABSOLUTE);
1209
1210 PATCHGEN_EPILOG(pPatch, 2 + sizeof(RTGCPTR));
1211 return rc;
1212}
1213
1214/*
1215 * Control register moves to or from general purpose registers
1216 * mov GPR, CRx
1217 * mov CRx, GPR
1218 */
1219int patmPatchGenMovControl(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu)
1220{
1221 int rc = VINF_SUCCESS;
1222 int reg, mod, rm, ctrlreg;
1223 uint32_t offset;
1224
1225 PATCHGEN_PROLOG(pVM, pPatch);
1226
1227 mod = 0; //effective address (only)
1228 rm = 5; //disp32
1229 if (pCpu->pCurInstr->param1 == OP_PARM_Cd)
1230 {
1231 Assert(0); // You not come here. Illegal!
1232
1233 // mov CRx, GPR
1234 pPB[0] = 0x89; //mov disp32, GPR
1235 ctrlreg = pCpu->param1.base.reg_ctrl;
1236 reg = pCpu->param2.base.reg_gen32;
1237 Assert(pCpu->param1.flags & USE_REG_CR);
1238 Assert(pCpu->param2.flags & USE_REG_GEN32);
1239 }
1240 else
1241 {
1242 // mov GPR, DRx
1243 Assert(pCpu->param1.flags & USE_REG_GEN32);
1244 Assert(pCpu->param2.flags & USE_REG_CR);
1245
1246 pPB[0] = 0x8B; // mov GPR, disp32
1247 reg = pCpu->param1.base.reg_gen32;
1248 ctrlreg = pCpu->param2.base.reg_ctrl;
1249 }
1250
1251 pPB[1] = MAKE_MODRM(mod, reg, rm);
1252
1253 /// @todo: make this an array in the context structure
1254 switch (ctrlreg)
1255 {
1256 case USE_REG_CR0:
1257 offset = RT_OFFSETOF(CPUMCTX, cr0);
1258 break;
1259 case USE_REG_CR2:
1260 offset = RT_OFFSETOF(CPUMCTX, cr2);
1261 break;
1262 case USE_REG_CR3:
1263 offset = RT_OFFSETOF(CPUMCTX, cr3);
1264 break;
1265 case USE_REG_CR4:
1266 offset = RT_OFFSETOF(CPUMCTX, cr4);
1267 break;
1268 default: /* Shut up compiler warning. */
1269 AssertFailed();
1270 offset = 0;
1271 break;
1272 }
1273 *(RTGCPTR *)&pPB[2] = pVM->patm.s.pCPUMCtxGC + offset;
1274 patmPatchAddReloc32(pVM, pPatch, &pPB[2], FIXUP_ABSOLUTE);
1275
1276 PATCHGEN_EPILOG(pPatch, 2 + sizeof(RTGCPTR));
1277 return rc;
1278}
1279
1280/*
1281 * mov GPR, SS
1282 */
1283int patmPatchGenMovFromSS(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu, RTGCPTR pCurInstrGC)
1284{
1285 uint32_t size, offset;
1286
1287 Log(("patmPatchGenMovFromSS %VGv\n", pCurInstrGC));
1288
1289 Assert(pPatch->flags & PATMFL_CODE32);
1290
1291 PATCHGEN_PROLOG(pVM, pPatch);
1292 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMClearPIFRecord, 0, false);
1293 PATCHGEN_EPILOG(pPatch, size);
1294
1295 /* push ss */
1296 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
1297 offset = 0;
1298 if (pCpu->prefix & PREFIX_OPSIZE)
1299 pPB[offset++] = 0x66; /* size override -> 16 bits push */
1300 pPB[offset++] = 0x16;
1301 PATCHGEN_EPILOG(pPatch, offset);
1302
1303 /* checks and corrects RPL of pushed ss*/
1304 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
1305 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMMovFromSSRecord, 0, false);
1306 PATCHGEN_EPILOG(pPatch, size);
1307
1308 /* pop general purpose register */
1309 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
1310 offset = 0;
1311 if (pCpu->prefix & PREFIX_OPSIZE)
1312 pPB[offset++] = 0x66; /* size override -> 16 bits pop */
1313 pPB[offset++] = 0x58 + pCpu->param1.base.reg_gen32;
1314 PATCHGEN_EPILOG(pPatch, offset);
1315
1316
1317 PATCHGEN_PROLOG_NODEF(pVM, pPatch);
1318 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMSetPIFRecord, 0, false);
1319 PATCHGEN_EPILOG(pPatch, size);
1320
1321 return VINF_SUCCESS;
1322}
1323
1324
1325/**
1326 * Generate an sldt or str patch instruction
1327 *
1328 * @returns VBox status code.
1329 * @param pVM The VM to operate on.
1330 * @param pPatch Patch record
1331 * @param pCpu Disassembly state
1332 * @param pCurInstrGC Guest instruction address
1333 */
1334int patmPatchGenSldtStr(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu, RTGCPTR pCurInstrGC)
1335{
1336 // sldt %Ew
1337 int rc = VINF_SUCCESS;
1338 uint32_t offset = 0;
1339 uint32_t i;
1340
1341 /** @todo segment prefix (untested) */
1342 Assert(pCpu->prefix == PREFIX_NONE || pCpu->prefix == PREFIX_OPSIZE);
1343
1344 PATCHGEN_PROLOG(pVM, pPatch);
1345
1346 if (pCpu->param1.flags == USE_REG_GEN32 || pCpu->param1.flags == USE_REG_GEN16)
1347 {
1348 /* Register operand */
1349 // 8B 15 [32 bits addr] mov edx, CPUMCTX.tr/ldtr
1350
1351 if (pCpu->prefix == PREFIX_OPSIZE)
1352 pPB[offset++] = 0x66;
1353
1354 pPB[offset++] = 0x8B; // mov destreg, CPUMCTX.tr/ldtr
1355 /* Modify REG part according to destination of original instruction */
1356 pPB[offset++] = MAKE_MODRM(0, pCpu->param1.base.reg_gen32, 5);
1357 if (pCpu->pCurInstr->opcode == OP_STR)
1358 {
1359 *(RTGCPTR *)&pPB[offset] = pVM->patm.s.pCPUMCtxGC + RT_OFFSETOF(CPUMCTX, tr);
1360 }
1361 else
1362 {
1363 *(RTGCPTR *)&pPB[offset] = pVM->patm.s.pCPUMCtxGC + RT_OFFSETOF(CPUMCTX, ldtr);
1364 }
1365 patmPatchAddReloc32(pVM, pPatch, &pPB[offset], FIXUP_ABSOLUTE);
1366 offset += sizeof(RTGCPTR);
1367 }
1368 else
1369 {
1370 /* Memory operand */
1371 //50 push eax
1372 //52 push edx
1373 //8D 15 48 7C 42 00 lea edx, dword ptr [dest]
1374 //66 A1 48 7C 42 00 mov ax, CPUMCTX.tr/ldtr
1375 //66 89 02 mov word ptr [edx],ax
1376 //5A pop edx
1377 //58 pop eax
1378
1379 pPB[offset++] = 0x50; // push eax
1380 pPB[offset++] = 0x52; // push edx
1381
1382 if (pCpu->prefix == PREFIX_SEG)
1383 {
1384 pPB[offset++] = DISQuerySegPrefixByte(pCpu);
1385 }
1386 pPB[offset++] = 0x8D; // lea edx, dword ptr [dest]
1387 // duplicate and modify modrm byte and additional bytes if present (e.g. direct address)
1388 pPB[offset++] = MAKE_MODRM(MODRM_MOD(pCpu->ModRM), USE_REG_EDX, MODRM_RM(pCpu->ModRM));
1389
1390 i = 3; /* standard offset of modrm bytes */
1391 if (pCpu->prefix == PREFIX_OPSIZE)
1392 i++; //skip operand prefix
1393 if (pCpu->prefix == PREFIX_SEG)
1394 i++; //skip segment prefix
1395
1396 rc = patmPatchReadBytes(pVM, &pPB[offset], (RTGCPTR)((RTGCUINTPTR)pCurInstrGC + i), pCpu->opsize - i);
1397 AssertRCReturn(rc, rc);
1398 offset += (pCpu->opsize - i);
1399
1400 pPB[offset++] = 0x66; // mov ax, CPUMCTX.tr/ldtr
1401 pPB[offset++] = 0xA1;
1402 if (pCpu->pCurInstr->opcode == OP_STR)
1403 {
1404 *(RTGCPTR *)&pPB[offset] = pVM->patm.s.pCPUMCtxGC + RT_OFFSETOF(CPUMCTX, tr);
1405 }
1406 else
1407 {
1408 *(RTGCPTR *)&pPB[offset] = pVM->patm.s.pCPUMCtxGC + RT_OFFSETOF(CPUMCTX, ldtr);
1409 }
1410 patmPatchAddReloc32(pVM, pPatch, &pPB[offset], FIXUP_ABSOLUTE);
1411 offset += sizeof(RTGCPTR);
1412
1413 pPB[offset++] = 0x66; // mov word ptr [edx],ax
1414 pPB[offset++] = 0x89;
1415 pPB[offset++] = 0x02;
1416
1417 pPB[offset++] = 0x5A; // pop edx
1418 pPB[offset++] = 0x58; // pop eax
1419 }
1420
1421 PATCHGEN_EPILOG(pPatch, offset);
1422
1423 return rc;
1424}
1425
1426/**
1427 * Generate an sgdt or sidt patch instruction
1428 *
1429 * @returns VBox status code.
1430 * @param pVM The VM to operate on.
1431 * @param pPatch Patch record
1432 * @param pCpu Disassembly state
1433 * @param pCurInstrGC Guest instruction address
1434 */
1435int patmPatchGenSxDT(PVM pVM, PPATCHINFO pPatch, DISCPUSTATE *pCpu, RTGCPTR pCurInstrGC)
1436{
1437 int rc = VINF_SUCCESS;
1438 uint32_t offset = 0, offset_base, offset_limit;
1439 uint32_t i;
1440
1441 /* @todo segment prefix (untested) */
1442 Assert(pCpu->prefix == PREFIX_NONE);
1443
1444 // sgdt %Ms
1445 // sidt %Ms
1446
1447 switch (pCpu->pCurInstr->opcode)
1448 {
1449 case OP_SGDT:
1450 offset_base = RT_OFFSETOF(CPUMCTX, gdtr.pGdt);
1451 offset_limit = RT_OFFSETOF(CPUMCTX, gdtr.cbGdt);
1452 break;
1453
1454 case OP_SIDT:
1455 offset_base = RT_OFFSETOF(CPUMCTX, idtr.pIdt);
1456 offset_limit = RT_OFFSETOF(CPUMCTX, idtr.cbIdt);
1457 break;
1458
1459 default:
1460 return VERR_INVALID_PARAMETER;
1461 }
1462
1463//50 push eax
1464//52 push edx
1465//8D 15 48 7C 42 00 lea edx, dword ptr [dest]
1466//66 A1 48 7C 42 00 mov ax, CPUMCTX.gdtr.limit
1467//66 89 02 mov word ptr [edx],ax
1468//A1 48 7C 42 00 mov eax, CPUMCTX.gdtr.base
1469//89 42 02 mov dword ptr [edx+2],eax
1470//5A pop edx
1471//58 pop eax
1472
1473 PATCHGEN_PROLOG(pVM, pPatch);
1474 pPB[offset++] = 0x50; // push eax
1475 pPB[offset++] = 0x52; // push edx
1476
1477 if (pCpu->prefix == PREFIX_SEG)
1478 {
1479 pPB[offset++] = DISQuerySegPrefixByte(pCpu);
1480 }
1481 pPB[offset++] = 0x8D; // lea edx, dword ptr [dest]
1482 // duplicate and modify modrm byte and additional bytes if present (e.g. direct address)
1483 pPB[offset++] = MAKE_MODRM(MODRM_MOD(pCpu->ModRM), USE_REG_EDX, MODRM_RM(pCpu->ModRM));
1484
1485 i = 3; /* standard offset of modrm bytes */
1486 if (pCpu->prefix == PREFIX_OPSIZE)
1487 i++; //skip operand prefix
1488 if (pCpu->prefix == PREFIX_SEG)
1489 i++; //skip segment prefix
1490 rc = patmPatchReadBytes(pVM, &pPB[offset], (RTGCPTR)((RTGCUINTPTR)pCurInstrGC + i), pCpu->opsize - i);
1491 AssertRCReturn(rc, rc);
1492 offset += (pCpu->opsize - i);
1493
1494 pPB[offset++] = 0x66; // mov ax, CPUMCTX.gdtr.limit
1495 pPB[offset++] = 0xA1;
1496 *(RTGCPTR *)&pPB[offset] = pVM->patm.s.pCPUMCtxGC + offset_limit;
1497 patmPatchAddReloc32(pVM, pPatch, &pPB[offset], FIXUP_ABSOLUTE);
1498 offset += sizeof(RTGCPTR);
1499
1500 pPB[offset++] = 0x66; // mov word ptr [edx],ax
1501 pPB[offset++] = 0x89;
1502 pPB[offset++] = 0x02;
1503
1504 pPB[offset++] = 0xA1; // mov eax, CPUMCTX.gdtr.base
1505 *(RTGCPTR *)&pPB[offset] = pVM->patm.s.pCPUMCtxGC + offset_base;
1506 patmPatchAddReloc32(pVM, pPatch, &pPB[offset], FIXUP_ABSOLUTE);
1507 offset += sizeof(RTGCPTR);
1508
1509 pPB[offset++] = 0x89; // mov dword ptr [edx+2],eax
1510 pPB[offset++] = 0x42;
1511 pPB[offset++] = 0x02;
1512
1513 pPB[offset++] = 0x5A; // pop edx
1514 pPB[offset++] = 0x58; // pop eax
1515
1516 PATCHGEN_EPILOG(pPatch, offset);
1517
1518 return rc;
1519}
1520
1521/**
1522 * Generate a cpuid patch instruction
1523 *
1524 * @returns VBox status code.
1525 * @param pVM The VM to operate on.
1526 * @param pPatch Patch record
1527 * @param pCurInstrGC Guest instruction address
1528 */
1529int patmPatchGenCpuid(PVM pVM, PPATCHINFO pPatch, RTGCPTR pCurInstrGC)
1530{
1531 uint32_t size;
1532 PATCHGEN_PROLOG(pVM, pPatch);
1533
1534 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMCpuidRecord, 0, false);
1535
1536 PATCHGEN_EPILOG(pPatch, size);
1537 return VINF_SUCCESS;
1538}
1539
1540/**
1541 * Generate the jump from guest to patch code
1542 *
1543 * @returns VBox status code.
1544 * @param pVM The VM to operate on.
1545 * @param pPatch Patch record
1546 * @param pTargetGC Guest target jump
1547 * @param fClearInhibitIRQs Clear inhibit irq flag
1548 */
1549int patmPatchGenJumpToGuest(PVM pVM, PPATCHINFO pPatch, GCPTRTYPE(uint8_t *) pReturnAddrGC, bool fClearInhibitIRQs)
1550{
1551 int rc = VINF_SUCCESS;
1552 uint32_t size;
1553
1554 if (fClearInhibitIRQs)
1555 {
1556 rc = patmPatchGenClearInhibitIRQ(pVM, pPatch, pReturnAddrGC);
1557 if (rc == VERR_NO_MEMORY)
1558 return rc;
1559 AssertRCReturn(rc, rc);
1560 }
1561
1562 PATCHGEN_PROLOG(pVM, pPatch);
1563
1564 /* Add lookup record for patch to guest address translation */
1565 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pReturnAddrGC, PATM_LOOKUP_PATCH2GUEST);
1566
1567 /* Generate code to jump to guest code if IF=1, else fault. */
1568 size = patmPatchGenCode(pVM, pPatch, pPB, &PATMJumpToGuest_IF1Record, pReturnAddrGC, true);
1569 PATCHGEN_EPILOG(pPatch, size);
1570
1571 return rc;
1572}
1573
1574/*
1575 * Relative jump from patch code to patch code (no fixup required)
1576 */
1577int patmPatchGenPatchJump(PVM pVM, PPATCHINFO pPatch, RTGCPTR pCurInstrGC, GCPTRTYPE(uint8_t *) pPatchAddrGC, bool fAddLookupRecord)
1578{
1579 int32_t displ;
1580 int rc = VINF_SUCCESS;
1581
1582 Assert(PATMIsPatchGCAddr(pVM, pPatchAddrGC));
1583 PATCHGEN_PROLOG(pVM, pPatch);
1584
1585 if (fAddLookupRecord)
1586 {
1587 /* Add lookup record for patch to guest address translation */
1588 patmr3AddP2GLookupRecord(pVM, pPatch, pPB, pCurInstrGC, PATM_LOOKUP_PATCH2GUEST);
1589 }
1590
1591 pPB[0] = 0xE9; //JMP
1592
1593 displ = pPatchAddrGC - (PATCHCODE_PTR_GC(pPatch) + pPatch->uCurPatchOffset + SIZEOF_NEARJUMP32);
1594
1595 *(uint32_t *)&pPB[1] = displ;
1596
1597 PATCHGEN_EPILOG(pPatch, SIZEOF_NEARJUMP32);
1598
1599 return rc;
1600}
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