VirtualBox

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

Last change on this file since 5070 was 4071, checked in by vboxsync, 17 years ago

Biggest check-in ever. New source code headers for all (C) innotek files.

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