VirtualBox

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

Last change on this file since 11147 was 9344, checked in by vboxsync, 16 years ago

Types and string format specifiers.

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