VirtualBox

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

Last change on this file since 13835 was 13830, checked in by vboxsync, 16 years ago

VMM: Disabled VM:pVMGC, removed VM_GUEST_ADDR.

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