VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMGC/TRPMGCHandlers.cpp@ 9176

Last change on this file since 9176 was 8985, checked in by vboxsync, 17 years ago

Added an extra argument to TRPMForwardTrap so the trpm profiling started in the trap handler assembly code are stopped correctly. Enabled the #UD forwarding for ring-0 traps (dtrace experiment).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 42.0 KB
Line 
1/* $Id: TRPMGCHandlers.cpp 8985 2008-05-20 21:03:33Z vboxsync $ */
2/** @file
3 * TRPM - Guest Context Trap Handlers, CPP part
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_TRPM
27#include <VBox/selm.h>
28#include <VBox/iom.h>
29#include <VBox/pgm.h>
30#include <VBox/pdm.h>
31#include <VBox/dbgf.h>
32#include <VBox/em.h>
33#include <VBox/csam.h>
34#include <VBox/patm.h>
35#include <VBox/mm.h>
36#include <VBox/cpum.h>
37#include "TRPMInternal.h"
38#include <VBox/vm.h>
39#include <VBox/param.h>
40
41#include <VBox/err.h>
42#include <VBox/dis.h>
43#include <VBox/disopcode.h>
44#include <VBox/x86.h>
45#include <VBox/log.h>
46#include <VBox/tm.h>
47#include <iprt/asm.h>
48#include <iprt/assert.h>
49
50/*******************************************************************************
51* Defined Constants And Macros *
52*******************************************************************************/
53/* still here. MODR/M byte parsing */
54#define X86_OPCODE_MODRM_MOD_MASK 0xc0
55#define X86_OPCODE_MODRM_REG_MASK 0x38
56#define X86_OPCODE_MODRM_RM_MASK 0x07
57
58/** @todo fix/remove/permanent-enable this when DIS/PATM handles invalid lock sequences. */
59#define DTRACE_EXPERIMENT
60
61
62/*******************************************************************************
63* Structures and Typedefs *
64*******************************************************************************/
65/** Pointer to a readonly hypervisor trap record. */
66typedef const struct TRPMGCHYPER *PCTRPMGCHYPER;
67
68/**
69 * A hypervisor trap record.
70 * This contains information about a handler for a instruction range.
71 *
72 * @remark This must match what TRPM_HANDLER outputs.
73 */
74typedef struct TRPMGCHYPER
75{
76 /** The start address. */
77 uintptr_t uStartEIP;
78 /** The end address. (exclusive)
79 * If NULL the it's only for the instruction at pvStartEIP. */
80 uintptr_t uEndEIP;
81 /**
82 * The handler.
83 *
84 * @returns VBox status code
85 * VINF_SUCCESS means we've handled the trap.
86 * Any other error code means returning to the host context.
87 * @param pVM The VM handle.
88 * @param pRegFrame The register frame.
89 * @param uUser The user argument.
90 */
91 DECLGCCALLBACKMEMBER(int, pfnHandler, (PVM pVM, PCPUMCTXCORE pRegFrame, uintptr_t uUser));
92 /** Whatever the handler desires to put here. */
93 uintptr_t uUser;
94} TRPMGCHYPER;
95
96
97/*******************************************************************************
98* Global Variables *
99*******************************************************************************/
100__BEGIN_DECLS
101/** Defined in VMMGC0.asm or VMMGC99.asm.
102 * @{ */
103extern const TRPMGCHYPER g_aTrap0bHandlers[1];
104extern const TRPMGCHYPER g_aTrap0bHandlersEnd[1];
105extern const TRPMGCHYPER g_aTrap0dHandlers[1];
106extern const TRPMGCHYPER g_aTrap0dHandlersEnd[1];
107extern const TRPMGCHYPER g_aTrap0eHandlers[1];
108extern const TRPMGCHYPER g_aTrap0eHandlersEnd[1];
109/** @} */
110__END_DECLS
111
112
113/*******************************************************************************
114* Internal Functions *
115*******************************************************************************/
116__BEGIN_DECLS /* addressed from asm (not called so no DECLASM). */
117DECLCALLBACK(int) trpmGCTrapInGeneric(PVM pVM, PCPUMCTXCORE pRegFrame, uintptr_t uUser);
118__END_DECLS
119
120
121
122/**
123 * Exits the trap, called when exiting a trap handler.
124 *
125 * Will reset the trap if it's not a guest trap or the trap
126 * is already handled. Will process resume guest FFs.
127 *
128 * @returns rc.
129 * @param pVM VM handle.
130 * @param rc The VBox status code to return.
131 * @param pRegFrame Pointer to the register frame for the trap.
132 */
133static int trpmGCExitTrap(PVM pVM, int rc, PCPUMCTXCORE pRegFrame)
134{
135 uint32_t uOldActiveVector = pVM->trpm.s.uActiveVector;
136 NOREF(uOldActiveVector);
137
138 /* Reset trap? */
139 if ( rc != VINF_EM_RAW_GUEST_TRAP
140 && rc != VINF_EM_RAW_RING_SWITCH_INT)
141 pVM->trpm.s.uActiveVector = ~0;
142
143#ifdef VBOX_HIGH_RES_TIMERS_HACK
144 /*
145 * Occationally we should poll timers.
146 * We must *NOT* do this too frequently as it adds a significant overhead
147 * and it'll kill us if the trap load is high. (See #1354.)
148 * (The heuristic is not very intelligent, we should really check trap
149 * frequency etc. here, but alas, we lack any such information atm.)
150 */
151 static unsigned s_iTimerPoll = 0;
152 if (rc == VINF_SUCCESS)
153 {
154 if (!(++s_iTimerPoll & 0xf))
155 {
156 uint64_t cTicks = TMTimerPoll(pVM); NOREF(cTicks);
157 Log2(("TMTimerPoll at %VGv returned %RX64 (VM_FF_TIMER=%d)\n", pRegFrame->eip, cTicks, VM_FF_ISPENDING(pVM, VM_FF_TIMER)));
158 }
159 }
160 else
161 s_iTimerPoll = 0;
162#endif
163
164 /* Clear pending inhibit interrupt state if required. (necessary for dispatching interrupts later on) */
165 if (VM_FF_ISSET(pVM, VM_FF_INHIBIT_INTERRUPTS))
166 {
167 Log2(("VM_FF_INHIBIT_INTERRUPTS at %VGv successor %VGv\n", pRegFrame->eip, EMGetInhibitInterruptsPC(pVM)));
168 if (pRegFrame->eip != EMGetInhibitInterruptsPC(pVM))
169 {
170 /** @note we intentionally don't clear VM_FF_INHIBIT_INTERRUPTS here if the eip is the same as the inhibited instr address.
171 * Before we are able to execute this instruction in raw mode (iret to guest code) an external interrupt might
172 * force a world switch again. Possibly allowing a guest interrupt to be dispatched in the process. This could
173 * break the guest. Sounds very unlikely, but such timing sensitive problem are not as rare as you might think.
174 */
175 VM_FF_CLEAR(pVM, VM_FF_INHIBIT_INTERRUPTS);
176 }
177 }
178
179 /*
180 * Pending resume-guest-FF?
181 * Or pending (A)PIC interrupt? Windows XP will crash if we delay APIC interrupts.
182 */
183 if ( rc == VINF_SUCCESS
184 && VM_FF_ISPENDING(pVM, VM_FF_TO_R3 | VM_FF_TIMER | VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC | VM_FF_PGM_SYNC_CR3 | VM_FF_PGM_SYNC_CR3_NON_GLOBAL | VM_FF_REQUEST))
185 {
186 /* Pending Ring-3 action. */
187 if (VM_FF_ISPENDING(pVM, VM_FF_TO_R3))
188 {
189 VM_FF_CLEAR(pVM, VM_FF_TO_R3);
190 rc = VINF_EM_RAW_TO_R3;
191 }
192 /* Pending timer action. */
193 else if (VM_FF_ISPENDING(pVM, VM_FF_TIMER))
194 rc = VINF_EM_RAW_TIMER_PENDING;
195 /* Pending interrupt: dispatch it. */
196 else if ( VM_FF_ISPENDING(pVM, VM_FF_INTERRUPT_APIC | VM_FF_INTERRUPT_PIC)
197 && !VM_FF_ISSET(pVM, VM_FF_INHIBIT_INTERRUPTS)
198 && PATMAreInterruptsEnabledByCtxCore(pVM, pRegFrame)
199 )
200 {
201 uint8_t u8Interrupt;
202 rc = PDMGetInterrupt(pVM, &u8Interrupt);
203 Log(("trpmGCExitTrap: u8Interrupt=%d (%#x) rc=%Vrc\n", u8Interrupt, u8Interrupt, rc));
204 AssertFatalMsgRC(rc, ("PDMGetInterrupt failed with %Vrc\n", rc));
205 rc = TRPMForwardTrap(pVM, pRegFrame, (uint32_t)u8Interrupt, 0, TRPM_TRAP_NO_ERRORCODE, TRPM_HARDWARE_INT, uOldActiveVector);
206 /* can't return if successful */
207 Assert(rc != VINF_SUCCESS);
208
209 /* Stop the profile counter that was started in TRPMGCHandlersA.asm */
210 Assert(uOldActiveVector <= 16);
211 STAM_PROFILE_ADV_STOP(&pVM->trpm.s.aStatGCTraps[uOldActiveVector], a);
212
213 /* Assert the trap and go to the recompiler to dispatch it. */
214 TRPMAssertTrap(pVM, u8Interrupt, TRPM_HARDWARE_INT);
215
216 STAM_PROFILE_ADV_START(&pVM->trpm.s.aStatGCTraps[uOldActiveVector], a);
217 rc = VINF_EM_RAW_INTERRUPT_PENDING;
218 }
219 /*
220 * Try sync CR3?
221 * This ASSUMES that the MOV CRx, x emulation doesn't return with VINF_PGM_SYNC_CR3. (a bit hackish)
222 */
223 else if (VM_FF_ISPENDING(pVM, VM_FF_PGM_SYNC_CR3 | VM_FF_PGM_SYNC_CR3_NON_GLOBAL))
224#if 1
225 rc = PGMSyncCR3(pVM, CPUMGetGuestCR0(pVM), CPUMGetGuestCR3(pVM), CPUMGetGuestCR4(pVM), VM_FF_ISSET(pVM, VM_FF_PGM_SYNC_CR3));
226#else
227 rc = VINF_PGM_SYNC_CR3;
228#endif
229 /* Pending request packets might contain actions that need immediate attention, such as pending hardware interrupts. */
230 else if (VM_FF_ISPENDING(pVM, VM_FF_REQUEST))
231 rc = VINF_EM_PENDING_REQUEST;
232 }
233
234 AssertMsg( rc != VINF_SUCCESS
235 || ( pRegFrame->eflags.Bits.u1IF
236 && ( pRegFrame->eflags.Bits.u2IOPL < (unsigned)(pRegFrame->ss & X86_SEL_RPL) || pRegFrame->eflags.Bits.u1VM))
237 , ("rc = %VGv\neflags=%RX32 ss=%RTsel IOPL=%d\n", rc, pRegFrame->eflags.u32, pRegFrame->ss, pRegFrame->eflags.Bits.u2IOPL));
238 return rc;
239}
240
241
242/**
243 * \#DB (Debug event) handler.
244 *
245 * @returns VBox status code.
246 * VINF_SUCCESS means we completely handled this trap,
247 * other codes are passed execution to host context.
248 *
249 * @param pTrpm Pointer to TRPM data (within VM).
250 * @param pRegFrame Pointer to the register frame for the trap.
251 * @internal
252 */
253DECLASM(int) TRPMGCTrap01Handler(PTRPM pTrpm, PCPUMCTXCORE pRegFrame)
254{
255 RTGCUINTREG uDr6 = ASMGetAndClearDR6();
256 PVM pVM = TRPM2VM(pTrpm);
257 LogFlow(("TRPMGCTrap01Handler: cs:eip=%04x:%08x uDr6=%RTreg\n", pRegFrame->cs, pRegFrame->eip, uDr6));
258
259 /*
260 * We currently don't make sure of the X86_DR7_GD bit, but
261 * there might come a time when we do.
262 */
263 if ((uDr6 & X86_DR6_BD) == X86_DR6_BD)
264 {
265 AssertReleaseMsgFailed(("X86_DR6_BD isn't used, but it's set! dr7=%RTreg(%RTreg) dr6=%RTreg\n",
266 ASMGetDR7(), CPUMGetHyperDR7(pVM), uDr6));
267 return VERR_NOT_IMPLEMENTED;
268 }
269
270 AssertReleaseMsg(!(uDr6 & X86_DR6_BT), ("X86_DR6_BT is impossible!\n"));
271
272 /*
273 * Now leave the rest to the DBGF.
274 */
275 int rc = DBGFGCTrap01Handler(pVM, pRegFrame, uDr6);
276 if (rc == VINF_EM_RAW_GUEST_TRAP)
277 CPUMSetGuestDR6(pVM, uDr6);
278
279 return trpmGCExitTrap(pVM, rc, pRegFrame);
280}
281
282
283
284/**
285 * NMI handler, for when we are using NMIs to debug things.
286 *
287 * @returns VBox status code.
288 * VINF_SUCCESS means we completely handled this trap,
289 * other codes are passed execution to host context.
290 *
291 * @param pTrpm Pointer to TRPM data (within VM).
292 * @param pRegFrame Pointer to the register frame for the trap.
293 * @internal
294 * @remark This is not hooked up unless you're building with VBOX_WITH_NMI defined.
295 */
296DECLASM(int) TRPMGCTrap02Handler(PTRPM pTrpm, PCPUMCTXCORE pRegFrame)
297{
298 LogFlow(("TRPMGCTrap02Handler: cs:eip=%04x:%08x\n", pRegFrame->cs, pRegFrame->eip));
299 RTLogComPrintf("TRPMGCTrap02Handler: cs:eip=%04x:%08x\n", pRegFrame->cs, pRegFrame->eip);
300 return VERR_TRPM_DONT_PANIC;
301}
302
303
304/**
305 * \#BP (Breakpoint) handler.
306 *
307 * @returns VBox status code.
308 * VINF_SUCCESS means we completely handled this trap,
309 * other codes are passed execution to host context.
310 *
311 * @param pTrpm Pointer to TRPM data (within VM).
312 * @param pRegFrame Pointer to the register frame for the trap.
313 * @internal
314 */
315DECLASM(int) TRPMGCTrap03Handler(PTRPM pTrpm, PCPUMCTXCORE pRegFrame)
316{
317 LogFlow(("TRPMGCTrap03Handler: cs:eip=%04x:%08x\n", pRegFrame->cs, pRegFrame->eip));
318 PVM pVM = TRPM2VM(pTrpm);
319 int rc;
320
321 /*
322 * Both PATM are using INT3s, let them have a go first.
323 */
324 if ( (pRegFrame->ss & X86_SEL_RPL) == 1
325 && !pRegFrame->eflags.Bits.u1VM)
326 {
327 rc = PATMHandleInt3PatchTrap(pVM, pRegFrame);
328 if (rc == VINF_SUCCESS || rc == VINF_EM_RAW_EMULATE_INSTR || rc == VINF_PATM_PATCH_INT3 || rc == VINF_PATM_DUPLICATE_FUNCTION)
329 return trpmGCExitTrap(pVM, rc, pRegFrame);
330 }
331 rc = DBGFGCTrap03Handler(pVM, pRegFrame);
332 /* anything we should do with this? Schedule it in GC? */
333 return trpmGCExitTrap(pVM, rc, pRegFrame);
334}
335
336
337/**
338 * Trap handler for illegal opcode fault (\#UD).
339 *
340 * @returns VBox status code.
341 * VINF_SUCCESS means we completely handled this trap,
342 * other codes are passed execution to host context.
343 *
344 * @param pTrpm Pointer to TRPM data (within VM).
345 * @param pRegFrame Pointer to the register frame for the trap.
346 * @internal
347 */
348DECLASM(int) TRPMGCTrap06Handler(PTRPM pTrpm, PCPUMCTXCORE pRegFrame)
349{
350 PVM pVM = TRPM2VM(pTrpm);
351 int rc;
352
353 LogFlow(("TRPMGCTrap06Handler %VGv eflags=%x\n", pRegFrame->eip, pRegFrame->eflags.u32));
354
355 if (CPUMGetGuestCPL(pVM, pRegFrame) == 0)
356 {
357 /*
358 * Decode the instruction.
359 */
360 RTGCPTR PC;
361 rc = SELMValidateAndConvertCSAddr(pVM, pRegFrame->eflags, pRegFrame->ss, pRegFrame->cs, &pRegFrame->csHid, (RTGCPTR)pRegFrame->eip, &PC);
362 if (VBOX_FAILURE(rc))
363 {
364 Log(("TRPMGCTrap06Handler: Failed to convert %RTsel:%RX32 (cpl=%d) - rc=%Vrc !!\n", pRegFrame->cs, pRegFrame->eip, pRegFrame->ss & X86_SEL_RPL, rc));
365 return trpmGCExitTrap(pVM, VINF_EM_RAW_GUEST_TRAP, pRegFrame);
366 }
367
368 DISCPUSTATE Cpu;
369 uint32_t cbOp;
370 rc = EMInterpretDisasOneEx(pVM, (RTGCUINTPTR)PC, pRegFrame, &Cpu, &cbOp);
371 if (VBOX_FAILURE(rc))
372 return trpmGCExitTrap(pVM, VINF_EM_RAW_EMULATE_INSTR, pRegFrame);
373
374 /*
375 * UD2 in a patch?
376 */
377 if ( Cpu.pCurInstr->opcode == OP_ILLUD2
378 && PATMIsPatchGCAddr(pVM, (RTGCPTR)pRegFrame->eip))
379 {
380 rc = PATMGCHandleIllegalInstrTrap(pVM, pRegFrame);
381 if ( rc == VINF_SUCCESS
382 || rc == VINF_EM_RAW_EMULATE_INSTR
383 || rc == VINF_PATM_DUPLICATE_FUNCTION
384 || rc == VINF_PATM_PENDING_IRQ_AFTER_IRET
385 || rc == VINF_EM_RESCHEDULE)
386 return trpmGCExitTrap(pVM, rc, pRegFrame);
387 }
388 /*
389 * Speed up dtrace and don't entrust invalid lock sequences to the recompiler.
390 */
391 else if (Cpu.prefix & PREFIX_LOCK)
392 {
393 Log(("TRPMGCTrap06Handler: pc=%RGv op=%d\n", pRegFrame->eip, Cpu.pCurInstr->opcode));
394#ifdef DTRACE_EXPERIMENT /** @todo fix/remove/permanent-enable this when DIS/PATM handles invalid lock sequences. */
395 Assert(!PATMIsPatchGCAddr(pVM, (RTGCPTR)pRegFrame->eip));
396 rc = TRPMForwardTrap(pVM, pRegFrame, 0x6, 0, TRPM_TRAP_NO_ERRORCODE, TRPM_TRAP, 0x6);
397 Assert(rc == VINF_EM_RAW_GUEST_TRAP);
398#else
399 rc = VINF_EM_RAW_EMULATE_INSTR;
400#endif
401 }
402 /*
403 * Handle MONITOR - it causes an #UD exception instead of #GP when not executed in ring 0.
404 */
405 else if (Cpu.pCurInstr->opcode == OP_MONITOR)
406 {
407 uint32_t cbIgnored;
408 rc = EMInterpretInstructionCPU(pVM, &Cpu, pRegFrame, PC, &cbIgnored);
409 if (RT_LIKELY(VBOX_SUCCESS(rc)))
410 pRegFrame->eip += Cpu.opsize;
411 }
412 /* Never generate a raw trap here; it might be an instruction, that requires emulation. */
413 else
414 rc = VINF_EM_RAW_EMULATE_INSTR;
415 }
416 else
417 {
418 rc = TRPMForwardTrap(pVM, pRegFrame, 0x6, 0, TRPM_TRAP_NO_ERRORCODE, TRPM_TRAP, 0x6);
419 Assert(rc == VINF_EM_RAW_GUEST_TRAP);
420 }
421
422 return trpmGCExitTrap(pVM, rc, pRegFrame);
423}
424
425
426/**
427 * Trap handler for device not present fault (\#NM).
428 *
429 * Device not available, FP or (F)WAIT instruction.
430 *
431 * @returns VBox status code.
432 * VINF_SUCCESS means we completely handled this trap,
433 * other codes are passed execution to host context.
434 *
435 * @param pTrpm Pointer to TRPM data (within VM).
436 * @param pRegFrame Pointer to the register frame for the trap.
437 * @internal
438 */
439DECLASM(int) TRPMGCTrap07Handler(PTRPM pTrpm, PCPUMCTXCORE pRegFrame)
440{
441 PVM pVM = TRPM2VM(pTrpm);
442
443 LogFlow(("TRPMTrap07HandlerGC: eip=%VGv\n", pRegFrame->eip));
444 return CPUMHandleLazyFPU(pVM);
445}
446
447
448/**
449 * \#NP ((segment) Not Present) handler.
450 *
451 * @returns VBox status code.
452 * VINF_SUCCESS means we completely handled this trap,
453 * other codes are passed execution to host context.
454 *
455 * @param pTrpm Pointer to TRPM data (within VM).
456 * @param pRegFrame Pointer to the register frame for the trap.
457 * @internal
458 */
459DECLASM(int) TRPMGCTrap0bHandler(PTRPM pTrpm, PCPUMCTXCORE pRegFrame)
460{
461 LogFlow(("TRPMGCTrap0bHandler: eip=%VGv\n", pRegFrame->eip));
462 PVM pVM = TRPM2VM(pTrpm);
463
464 /*
465 * Try to detect instruction by opcode which caused trap.
466 * XXX note: this code may cause \#PF (trap e) or \#GP (trap d) while
467 * accessing user code. need to handle it somehow in future!
468 */
469 uint8_t *pu8Code;
470 if (SELMValidateAndConvertCSAddr(pVM, pRegFrame->eflags, pRegFrame->ss, pRegFrame->cs, &pRegFrame->csHid, (RTGCPTR)pRegFrame->eip, (PRTGCPTR)&pu8Code) == VINF_SUCCESS)
471 {
472 /*
473 * First skip possible instruction prefixes, such as:
474 * OS, AS
475 * CS:, DS:, ES:, SS:, FS:, GS:
476 * REPE, REPNE
477 *
478 * note: Currently we supports only up to 4 prefixes per opcode, more
479 * prefixes (normally not used anyway) will cause trap d in guest.
480 * note: Instruction length in IA-32 may be up to 15 bytes, we dont
481 * check this issue, its too hard.
482 */
483 for (unsigned i = 0; i < 4; i++)
484 {
485 if ( pu8Code[0] != 0xf2 /* REPNE/REPNZ */
486 && pu8Code[0] != 0xf3 /* REP/REPE/REPZ */
487 && pu8Code[0] != 0x2e /* CS: */
488 && pu8Code[0] != 0x36 /* SS: */
489 && pu8Code[0] != 0x3e /* DS: */
490 && pu8Code[0] != 0x26 /* ES: */
491 && pu8Code[0] != 0x64 /* FS: */
492 && pu8Code[0] != 0x65 /* GS: */
493 && pu8Code[0] != 0x66 /* OS */
494 && pu8Code[0] != 0x67 /* AS */
495 )
496 break;
497 pu8Code++;
498 }
499
500 /*
501 * Detect right switch using a callgate.
502 *
503 * We recognize the following causes for the trap 0b:
504 * CALL FAR, CALL FAR []
505 * JMP FAR, JMP FAR []
506 * IRET (may cause a task switch)
507 *
508 * Note: we can't detect whether the trap was caused by a call to a
509 * callgate descriptor or it is a real trap 0b due to a bad selector.
510 * In both situations we'll pass execution to our recompiler so we don't
511 * have to worry.
512 * If we wanted to do better detection, we have set GDT entries to callgate
513 * descriptors pointing to our own handlers.
514 */
515 /** @todo not sure about IRET, may generate Trap 0d (\#GP), NEED TO CHECK! */
516 if ( pu8Code[0] == 0x9a /* CALL FAR */
517 || ( pu8Code[0] == 0xff /* CALL FAR [] */
518 && (pu8Code[1] & X86_OPCODE_MODRM_REG_MASK) == 0x18)
519 || pu8Code[0] == 0xea /* JMP FAR */
520 || ( pu8Code[0] == 0xff /* JMP FAR [] */
521 && (pu8Code[1] & X86_OPCODE_MODRM_REG_MASK) == 0x28)
522 || pu8Code[0] == 0xcf /* IRET */
523 )
524 {
525 /*
526 * Got potential call to callgate.
527 * We simply return execution to the recompiler to do emulation
528 * starting from the instruction which caused the trap.
529 */
530 pTrpm->uActiveVector = ~0;
531 return VINF_EM_RAW_RING_SWITCH;
532 }
533 }
534
535 /*
536 * Pass trap 0b as is to the recompiler in all other cases.
537 */
538 return VINF_EM_RAW_GUEST_TRAP;
539}
540
541
542/**
543 * \#GP (General Protection Fault) handler for Ring-0 privileged instructions.
544 *
545 * @returns VBox status code.
546 * VINF_SUCCESS means we completely handled this trap,
547 * other codes are passed execution to host context.
548 *
549 * @param pVM The VM handle.
550 * @param pRegFrame Pointer to the register frame for the trap.
551 * @param pCpu The opcode info.
552 * @param PC The program counter corresponding to cs:eip in pRegFrame.
553 */
554static int trpmGCTrap0dHandlerRing0(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu, RTGCPTR PC)
555{
556 int rc;
557
558 /*
559 * Try handle it here, if not return to HC and emulate/interpret it there.
560 */
561 switch (pCpu->pCurInstr->opcode)
562 {
563 case OP_INT3:
564 /*
565 * Little hack to make the code below not fail
566 */
567 pCpu->param1.flags = USE_IMMEDIATE8;
568 pCpu->param1.parval = 3;
569 /* fallthru */
570 case OP_INT:
571 {
572 Assert(pCpu->param1.flags & USE_IMMEDIATE8);
573 Assert(!(PATMIsPatchGCAddr(pVM, PC)));
574 if (pCpu->param1.parval == 3)
575 {
576 /* Int 3 replacement patch? */
577 if (PATMHandleInt3PatchTrap(pVM, pRegFrame) == VINF_SUCCESS)
578 {
579 AssertFailed();
580 return trpmGCExitTrap(pVM, VINF_SUCCESS, pRegFrame);
581 }
582 }
583 rc = TRPMForwardTrap(pVM, pRegFrame, (uint32_t)pCpu->param1.parval, pCpu->opsize, TRPM_TRAP_NO_ERRORCODE, TRPM_SOFTWARE_INT, 0xd);
584 if (VBOX_SUCCESS(rc) && rc != VINF_EM_RAW_GUEST_TRAP)
585 return trpmGCExitTrap(pVM, VINF_SUCCESS, pRegFrame);
586
587 pVM->trpm.s.uActiveVector = (pVM->trpm.s.uActiveErrorCode & X86_TRAP_ERR_SEL_MASK) >> X86_TRAP_ERR_SEL_SHIFT;
588 pVM->trpm.s.enmActiveType = TRPM_SOFTWARE_INT;
589 return trpmGCExitTrap(pVM, VINF_EM_RAW_RING_SWITCH_INT, pRegFrame);
590 }
591
592#ifdef PATM_EMULATE_SYSENTER
593 case OP_SYSEXIT:
594 case OP_SYSRET:
595 rc = PATMSysCall(pVM, pRegFrame, pCpu);
596 return trpmGCExitTrap(pVM, rc, pRegFrame);
597#endif
598
599 case OP_HLT:
600 /* If it's in patch code, defer to ring-3. */
601 if (PATMIsPatchGCAddr(pVM, PC))
602 break;
603
604 pRegFrame->eip += pCpu->opsize;
605 return trpmGCExitTrap(pVM, VINF_EM_HALT, pRegFrame);
606
607
608 /*
609 * These instructions are used by PATM and CASM for finding
610 * dangerous non-trapping instructions. Thus, since all
611 * scanning and patching is done in ring-3 we'll have to
612 * return to ring-3 on the first encounter of these instructions.
613 */
614 case OP_MOV_CR:
615 case OP_MOV_DR:
616 /* We can safely emulate control/debug register move instructions in patched code. */
617 if ( !PATMIsPatchGCAddr(pVM, PC)
618 && !CSAMIsKnownDangerousInstr(pVM, PC))
619 break;
620 case OP_INVLPG:
621 case OP_LLDT:
622 case OP_STI:
623 case OP_RDTSC: /* just in case */
624 case OP_CLTS:
625 {
626 uint32_t cbIgnored;
627 rc = EMInterpretInstructionCPU(pVM, pCpu, pRegFrame, PC, &cbIgnored);
628 if (VBOX_SUCCESS(rc))
629 pRegFrame->eip += pCpu->opsize;
630 else if (rc == VERR_EM_INTERPRETER)
631 rc = VINF_EM_RAW_EXCEPTION_PRIVILEGED;
632 return trpmGCExitTrap(pVM, rc, pRegFrame);
633 }
634 }
635
636 return trpmGCExitTrap(pVM, VINF_EM_RAW_EXCEPTION_PRIVILEGED, pRegFrame);
637}
638
639
640/**
641 * \#GP (General Protection Fault) handler for Ring-3.
642 *
643 * @returns VBox status code.
644 * VINF_SUCCESS means we completely handled this trap,
645 * other codes are passed execution to host context.
646 *
647 * @param pVM The VM handle.
648 * @param pRegFrame Pointer to the register frame for the trap.
649 * @param pCpu The opcode info.
650 * @param PC The program counter corresponding to cs:eip in pRegFrame.
651 */
652static int trpmGCTrap0dHandlerRing3(PVM pVM, PCPUMCTXCORE pRegFrame, PDISCPUSTATE pCpu, RTGCPTR PC)
653{
654 int rc;
655
656 Assert(!pRegFrame->eflags.Bits.u1VM);
657
658 switch (pCpu->pCurInstr->opcode)
659 {
660 /*
661 * INT3 and INT xx are ring-switching.
662 * (The shadow IDT will have set the entries to DPL=0, that's why we're here.)
663 */
664 case OP_INT3:
665 /*
666 * Little hack to make the code below not fail
667 */
668 pCpu->param1.flags = USE_IMMEDIATE8;
669 pCpu->param1.parval = 3;
670 /* fall thru */
671 case OP_INT:
672 {
673 Assert(pCpu->param1.flags & USE_IMMEDIATE8);
674 rc = TRPMForwardTrap(pVM, pRegFrame, (uint32_t)pCpu->param1.parval, pCpu->opsize, TRPM_TRAP_NO_ERRORCODE, TRPM_SOFTWARE_INT, 0xd);
675 if (VBOX_SUCCESS(rc) && rc != VINF_EM_RAW_GUEST_TRAP)
676 return trpmGCExitTrap(pVM, VINF_SUCCESS, pRegFrame);
677
678 pVM->trpm.s.uActiveVector = (pVM->trpm.s.uActiveErrorCode & X86_TRAP_ERR_SEL_MASK) >> X86_TRAP_ERR_SEL_SHIFT;
679 pVM->trpm.s.enmActiveType = TRPM_SOFTWARE_INT;
680 return trpmGCExitTrap(pVM, VINF_EM_RAW_RING_SWITCH_INT, pRegFrame);
681 }
682
683 /*
684 * SYSCALL, SYSENTER, INTO and BOUND are also ring-switchers.
685 */
686 case OP_SYSCALL:
687 case OP_SYSENTER:
688#ifdef PATM_EMULATE_SYSENTER
689 rc = PATMSysCall(pVM, pRegFrame, pCpu);
690 if (rc == VINF_SUCCESS)
691 return trpmGCExitTrap(pVM, VINF_SUCCESS, pRegFrame);
692 /* else no break; */
693#endif
694 case OP_BOUND:
695 case OP_INTO:
696 pVM->trpm.s.uActiveVector = ~0;
697 return trpmGCExitTrap(pVM, VINF_EM_RAW_RING_SWITCH, pRegFrame);
698
699 /*
700 * Handle virtualized TSC reads, just in case.
701 */
702 case OP_RDTSC:
703 {
704 uint32_t cbIgnored;
705 rc = EMInterpretInstructionCPU(pVM, pCpu, pRegFrame, PC, &cbIgnored);
706 if (VBOX_SUCCESS(rc))
707 pRegFrame->eip += pCpu->opsize;
708 else if (rc == VERR_EM_INTERPRETER)
709 rc = VINF_EM_RAW_EXCEPTION_PRIVILEGED;
710 return trpmGCExitTrap(pVM, rc, pRegFrame);
711 }
712
713 /*
714 * STI and CLI are I/O privileged, i.e. if IOPL
715 */
716 case OP_STI:
717 case OP_CLI:
718 {
719 uint32_t efl = CPUMRawGetEFlags(pVM, pRegFrame);
720 if (X86_EFL_GET_IOPL(efl) >= (unsigned)(pRegFrame->ss & X86_SEL_RPL))
721 {
722 LogFlow(("trpmGCTrap0dHandlerRing3: CLI/STI -> REM\n"));
723 return trpmGCExitTrap(pVM, VINF_EM_RESCHEDULE_REM, pRegFrame);
724 }
725 LogFlow(("trpmGCTrap0dHandlerRing3: CLI/STI -> #GP(0)\n"));
726 break;
727 }
728 }
729
730 /*
731 * A genuine guest fault.
732 */
733 return trpmGCExitTrap(pVM, VINF_EM_RAW_GUEST_TRAP, pRegFrame);
734}
735
736
737/**
738 * Emulates RDTSC for the \#GP handler.
739 *
740 * @returns VINF_SUCCESS or VINF_EM_RAW_EMULATE_INSTR.
741 *
742 * @param pVM Pointer to the shared VM structure.
743 * @param pRegFrame Pointer to the registre frame for the trap.
744 * This will be updated on successful return.
745 */
746DECLINLINE(int) trpmGCTrap0dHandlerRdTsc(PVM pVM, PCPUMCTXCORE pRegFrame)
747{
748 STAM_COUNTER_INC(&pVM->trpm.s.StatTrap0dRdTsc);
749
750 if (CPUMGetGuestCR4(pVM) & X86_CR4_TSD)
751 return trpmGCExitTrap(pVM, VINF_EM_RAW_EMULATE_INSTR, pRegFrame); /* will trap (optimize later). */
752
753 uint64_t uTicks = TMCpuTickGet(pVM);
754 pRegFrame->eax = uTicks;
755 pRegFrame->edx = uTicks >> 32;
756 pRegFrame->eip += 2;
757 return trpmGCExitTrap(pVM, VINF_SUCCESS, pRegFrame);
758}
759
760
761/**
762 * \#GP (General Protection Fault) handler.
763 *
764 * @returns VBox status code.
765 * VINF_SUCCESS means we completely handled this trap,
766 * other codes are passed execution to host context.
767 *
768 * @param pVM The VM handle.
769 * @param pTrpm Pointer to TRPM data (within VM).
770 * @param pRegFrame Pointer to the register frame for the trap.
771 */
772static int trpmGCTrap0dHandler(PVM pVM, PTRPM pTrpm, PCPUMCTXCORE pRegFrame)
773{
774 LogFlow(("trpmGCTrap0dHandler: cs:eip=%RTsel:%VGv uErr=%RX32\n", pRegFrame->ss, pRegFrame->eip, pTrpm->uActiveErrorCode));
775
776 /*
777 * Convert and validate CS.
778 */
779 STAM_PROFILE_START(&pVM->trpm.s.StatTrap0dDisasm, a);
780 RTGCPTR PC;
781 uint32_t cBits;
782 int rc = SELMValidateAndConvertCSAddrGCTrap(pVM, pRegFrame->eflags, pRegFrame->ss, pRegFrame->cs,
783 (RTGCPTR)pRegFrame->eip, &PC, &cBits);
784 if (RT_FAILURE(rc))
785 {
786 Log(("trpmGCTrap0dHandler: Failed to convert %RTsel:%RX32 (cpl=%d) - rc=%Rrc !!\n",
787 pRegFrame->cs, pRegFrame->eip, pRegFrame->ss & X86_SEL_RPL, rc));
788 STAM_PROFILE_STOP(&pVM->trpm.s.StatTrap0dDisasm, a);
789 return trpmGCExitTrap(pVM, VINF_EM_RAW_EMULATE_INSTR, pRegFrame);
790 }
791
792 /*
793 * Optimize RDTSC traps.
794 * Some guests (like Solaris) is using RDTSC all over the place and
795 * will end up trapping a *lot* because of that.
796 */
797 if ( !pRegFrame->eflags.Bits.u1VM
798 && ((uint8_t *)PC)[0] == 0x0f
799 && ((uint8_t *)PC)[1] == 0x31)
800 {
801 STAM_PROFILE_STOP(&pVM->trpm.s.StatTrap0dDisasm, a);
802 return trpmGCTrap0dHandlerRdTsc(pVM, pRegFrame);
803 }
804
805 /*
806 * Disassemble the instruction.
807 */
808 DISCPUSTATE Cpu;
809 uint32_t cbOp;
810 rc = DISCoreOneEx((RTGCUINTPTR)PC, cBits == 32 ? CPUMODE_32BIT : cBits == 16 ? CPUMODE_16BIT : CPUMODE_64BIT,
811 NULL, NULL, &Cpu, &cbOp);
812 if (RT_FAILURE(rc))
813 {
814 AssertMsgFailed(("DISCoreOneEx failed to PC=%VGv rc=%Vrc\n", PC, rc));
815 STAM_PROFILE_STOP(&pVM->trpm.s.StatTrap0dDisasm, a);
816 return trpmGCExitTrap(pVM, VINF_EM_RAW_EMULATE_INSTR, pRegFrame);
817 }
818 STAM_PROFILE_STOP(&pVM->trpm.s.StatTrap0dDisasm, a);
819
820 /*
821 * Deal with I/O port access.
822 */
823 if ( pVM->trpm.s.uActiveErrorCode == 0
824 && (Cpu.pCurInstr->optype & OPTYPE_PORTIO))
825 {
826 rc = EMInterpretPortIO(pVM, pRegFrame, &Cpu, cbOp);
827 return trpmGCExitTrap(pVM, rc, pRegFrame);
828 }
829
830 /*
831 * Deal with Ring-0 (privileged instructions)
832 */
833 if ( (pRegFrame->ss & X86_SEL_RPL) <= 1
834 && !pRegFrame->eflags.Bits.u1VM)
835 return trpmGCTrap0dHandlerRing0(pVM, pRegFrame, &Cpu, PC);
836
837 /*
838 * Deal with Ring-3 GPs.
839 */
840 if (!pRegFrame->eflags.Bits.u1VM)
841 return trpmGCTrap0dHandlerRing3(pVM, pRegFrame, &Cpu, PC);
842
843 /*
844 * Deal with v86 code.
845 *
846 * We always set IOPL to zero which makes e.g. pushf fault in V86
847 * mode. The guest might use IOPL=3 and therefore not expect a #GP.
848 * Simply fall back to the recompiler to emulate this instruction if
849 * that's the case. To get the correct we must use CPUMRawGetEFlags.
850 */
851 X86EFLAGS eflags;
852 eflags.u32 = CPUMRawGetEFlags(pVM, pRegFrame); /* Get the correct value. */
853 if (eflags.Bits.u2IOPL != 3)
854 {
855 Assert(eflags.Bits.u2IOPL == 0);
856
857 int rc = TRPMForwardTrap(pVM, pRegFrame, 0xD, 0, TRPM_TRAP_HAS_ERRORCODE, TRPM_TRAP, 0xd);
858 Assert(rc == VINF_EM_RAW_GUEST_TRAP);
859 return trpmGCExitTrap(pVM, rc, pRegFrame);
860 }
861 return trpmGCExitTrap(pVM, VINF_EM_RAW_EMULATE_INSTR, pRegFrame);
862}
863
864
865/**
866 * \#GP (General Protection Fault) handler.
867 *
868 * @returns VBox status code.
869 * VINF_SUCCESS means we completely handled this trap,
870 * other codes are passed execution to host context.
871 *
872 * @param pTrpm Pointer to TRPM data (within VM).
873 * @param pRegFrame Pointer to the register frame for the trap.
874 * @internal
875 */
876DECLASM(int) TRPMGCTrap0dHandler(PTRPM pTrpm, PCPUMCTXCORE pRegFrame)
877{
878 LogFlow(("TRPMGCTrap0dHandler: eip=%RGv\n", pRegFrame->eip));
879 PVM pVM = TRPM2VM(pTrpm);
880
881 int rc = trpmGCTrap0dHandler(pVM, pTrpm, pRegFrame);
882 switch (rc)
883 {
884 case VINF_EM_RAW_GUEST_TRAP:
885 case VINF_EM_RAW_EXCEPTION_PRIVILEGED:
886 if (PATMIsPatchGCAddr(pVM, (RTGCPTR)pRegFrame->eip))
887 rc = VINF_PATM_PATCH_TRAP_GP;
888 break;
889
890 case VINF_EM_RAW_INTERRUPT_PENDING:
891 Assert(TRPMHasTrap(pVM));
892 /* no break; */
893 case VINF_PGM_SYNC_CR3: /** @todo Check this with Sander. */
894 case VINF_EM_RAW_EMULATE_INSTR:
895 case VINF_IOM_HC_IOPORT_READ:
896 case VINF_IOM_HC_IOPORT_WRITE:
897 case VINF_IOM_HC_MMIO_WRITE:
898 case VINF_IOM_HC_MMIO_READ:
899 case VINF_IOM_HC_MMIO_READ_WRITE:
900 case VINF_PATM_PATCH_INT3:
901 case VINF_EM_RAW_TO_R3:
902 case VINF_EM_RAW_TIMER_PENDING:
903 case VINF_EM_PENDING_REQUEST:
904 case VINF_EM_HALT:
905 case VINF_SUCCESS:
906 break;
907
908 default:
909 AssertMsg(PATMIsPatchGCAddr(pVM, (RTGCPTR)pRegFrame->eip) == false, ("return code %d\n", rc));
910 break;
911 }
912 return rc;
913}
914
915/**
916 * \#PF (Page Fault) handler.
917 *
918 * Calls PGM which does the actual handling.
919 *
920 *
921 * @returns VBox status code.
922 * VINF_SUCCESS means we completely handled this trap,
923 * other codes are passed execution to host context.
924 *
925 * @param pTrpm Pointer to TRPM data (within VM).
926 * @param pRegFrame Pointer to the register frame for the trap.
927 * @internal
928 */
929DECLASM(int) TRPMGCTrap0eHandler(PTRPM pTrpm, PCPUMCTXCORE pRegFrame)
930{
931 LogBird(("TRPMGCTrap0eHandler: eip=%RGv\n", pRegFrame->eip));
932 PVM pVM = TRPM2VM(pTrpm);
933
934 /*
935 * This is all PGM stuff.
936 */
937 int rc = PGMTrap0eHandler(pVM, pTrpm->uActiveErrorCode, pRegFrame, (RTGCPTR)pTrpm->uActiveCR2);
938
939 switch (rc)
940 {
941 case VINF_EM_RAW_EMULATE_INSTR:
942 case VINF_EM_RAW_EMULATE_INSTR_PD_FAULT:
943 case VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT:
944 case VINF_EM_RAW_EMULATE_INSTR_TSS_FAULT:
945 case VINF_EM_RAW_EMULATE_INSTR_LDT_FAULT:
946 case VINF_EM_RAW_EMULATE_INSTR_IDT_FAULT:
947 if (PATMIsPatchGCAddr(pVM, (RTGCPTR)pRegFrame->eip))
948 rc = VINF_PATCH_EMULATE_INSTR;
949 break;
950
951 case VINF_EM_RAW_GUEST_TRAP:
952 if (PATMIsPatchGCAddr(pVM, (RTGCPTR)pRegFrame->eip))
953 return VINF_PATM_PATCH_TRAP_PF;
954
955 rc = TRPMForwardTrap(pVM, pRegFrame, 0xE, 0, TRPM_TRAP_HAS_ERRORCODE, TRPM_TRAP, 0xe);
956 Assert(rc == VINF_EM_RAW_GUEST_TRAP);
957 break;
958
959 case VINF_EM_RAW_INTERRUPT_PENDING:
960 Assert(TRPMHasTrap(pVM));
961 /* no break; */
962 case VINF_IOM_HC_MMIO_READ:
963 case VINF_IOM_HC_MMIO_WRITE:
964 case VINF_IOM_HC_MMIO_READ_WRITE:
965 case VINF_PATM_HC_MMIO_PATCH_READ:
966 case VINF_PATM_HC_MMIO_PATCH_WRITE:
967 case VINF_SUCCESS:
968 case VINF_EM_RAW_TO_R3:
969 case VINF_EM_PENDING_REQUEST:
970 case VINF_EM_RAW_TIMER_PENDING:
971 case VINF_CSAM_PENDING_ACTION:
972 case VINF_PGM_SYNC_CR3: /** @todo Check this with Sander. */
973 break;
974
975 default:
976 AssertMsg(PATMIsPatchGCAddr(pVM, (RTGCPTR)pRegFrame->eip) == false, ("Patch address for return code %d. eip=%08x\n", rc, pRegFrame->eip));
977 break;
978 }
979 return trpmGCExitTrap(pVM, rc, pRegFrame);
980}
981
982
983/**
984 * Scans for the EIP in the specified array of trap handlers.
985 *
986 * If we don't fine the EIP, we'll panic.
987 *
988 * @returns VBox status code.
989 *
990 * @param pVM The VM handle.
991 * @param pRegFrame Pointer to the register frame for the trap.
992 * @param paHandlers The array of trap handler records.
993 * @param pEndRecord The end record (exclusive).
994 */
995static int trpmGCHyperGeneric(PVM pVM, PCPUMCTXCORE pRegFrame, PCTRPMGCHYPER paHandlers, PCTRPMGCHYPER pEndRecord)
996{
997 uintptr_t uEip = (uintptr_t)pRegFrame->eip;
998 Assert(paHandlers <= pEndRecord);
999
1000 Log(("trpmGCHyperGeneric: uEip=%x %p-%p\n", uEip, paHandlers, pEndRecord));
1001
1002#if 0 /// @todo later
1003 /*
1004 * Start by doing a kind of binary search.
1005 */
1006 unsigned iStart = 0;
1007 unsigned iEnd = pEndRecord - paHandlers;
1008 unsigned i = iEnd / 2;
1009#endif
1010
1011 /*
1012 * Do a linear search now (in case the array wasn't properly sorted).
1013 */
1014 for (PCTRPMGCHYPER pCur = paHandlers; pCur < pEndRecord; pCur++)
1015 {
1016 if ( pCur->uStartEIP <= uEip
1017 && (pCur->uEndEIP ? pCur->uEndEIP > uEip : pCur->uStartEIP == uEip))
1018 return pCur->pfnHandler(pVM, pRegFrame, pCur->uUser);
1019 }
1020
1021 return VERR_TRPM_DONT_PANIC;
1022}
1023
1024
1025/**
1026 * Hypervisor \#NP ((segment) Not Present) handler.
1027 *
1028 * Scans for the EIP in the registered trap handlers.
1029 *
1030 * @returns VBox status code.
1031 * VINF_SUCCESS means we completely handled this trap,
1032 * other codes are passed back to host context.
1033 *
1034 * @param pTrpm Pointer to TRPM data (within VM).
1035 * @param pRegFrame Pointer to the register frame for the trap.
1036 * @internal
1037 */
1038DECLASM(int) TRPMGCHyperTrap0bHandler(PTRPM pTrpm, PCPUMCTXCORE pRegFrame)
1039{
1040 return trpmGCHyperGeneric(TRPM2VM(pTrpm), pRegFrame, g_aTrap0bHandlers, g_aTrap0bHandlersEnd);
1041}
1042
1043
1044/**
1045 * Hypervisor \#GP (General Protection Fault) handler.
1046 *
1047 * Scans for the EIP in the registered trap handlers.
1048 *
1049 * @returns VBox status code.
1050 * VINF_SUCCESS means we completely handled this trap,
1051 * other codes are passed back to host context.
1052 *
1053 * @param pTrpm Pointer to TRPM data (within VM).
1054 * @param pRegFrame Pointer to the register frame for the trap.
1055 * @internal
1056 */
1057DECLASM(int) TRPMGCHyperTrap0dHandler(PTRPM pTrpm, PCPUMCTXCORE pRegFrame)
1058{
1059 return trpmGCHyperGeneric(TRPM2VM(pTrpm), pRegFrame, g_aTrap0dHandlers, g_aTrap0dHandlersEnd);
1060}
1061
1062
1063/**
1064 * Hypervisor \#PF (Page Fault) handler.
1065 *
1066 * Scans for the EIP in the registered trap handlers.
1067 *
1068 * @returns VBox status code.
1069 * VINF_SUCCESS means we completely handled this trap,
1070 * other codes are passed back to host context.
1071 *
1072 * @param pTrpm Pointer to TRPM data (within VM).
1073 * @param pRegFrame Pointer to the register frame for the trap.
1074 * @internal
1075 */
1076DECLASM(int) TRPMGCHyperTrap0eHandler(PTRPM pTrpm, PCPUMCTXCORE pRegFrame)
1077{
1078 return trpmGCHyperGeneric(TRPM2VM(pTrpm), pRegFrame, g_aTrap0dHandlers, g_aTrap0dHandlersEnd);
1079}
1080
1081
1082/**
1083 * Deal with hypervisor traps occuring when resuming execution on a trap.
1084 *
1085 * @returns VBox status code.
1086 * @param pVM The VM handle.
1087 * @param pRegFrame Register frame.
1088 * @param uUser User arg.
1089 */
1090DECLCALLBACK(int) trpmGCTrapInGeneric(PVM pVM, PCPUMCTXCORE pRegFrame, uintptr_t uUser)
1091{
1092 Log(("********************************************************\n"));
1093 Log(("trpmGCTrapInGeneric: eip=%RX32 uUser=%#x\n", pRegFrame->eip, uUser));
1094 Log(("********************************************************\n"));
1095
1096 if (uUser & TRPM_TRAP_IN_HYPER)
1097 {
1098 /*
1099 * Check that there is still some stack left, if not we'll flag
1100 * a guru meditation (the alternative is a triple fault).
1101 */
1102 RTGCUINTPTR cbStackUsed = (RTGCUINTPTR)VMMGetStackGC(pVM) - pRegFrame->esp;
1103 if (cbStackUsed > VMM_STACK_SIZE - _1K)
1104 {
1105 LogRel(("trpmGCTrapInGeneric: ran out of stack: esp=#x cbStackUsed=%#x\n", pRegFrame->esp, cbStackUsed));
1106 return VERR_TRPM_DONT_PANIC;
1107 }
1108
1109 /*
1110 * Just zero the register containing the selector in question.
1111 * We'll deal with the actual stale or troublesome selector value in
1112 * the outermost trap frame.
1113 */
1114 switch (uUser & TRPM_TRAP_IN_OP_MASK)
1115 {
1116 case TRPM_TRAP_IN_MOV_GS:
1117 pRegFrame->eax = 0;
1118 pRegFrame->gs = 0; /* prevent recursive trouble. */
1119 break;
1120 case TRPM_TRAP_IN_MOV_FS:
1121 pRegFrame->eax = 0;
1122 pRegFrame->fs = 0; /* prevent recursive trouble. */
1123 return VINF_SUCCESS;
1124
1125 default:
1126 AssertMsgFailed(("Invalid uUser=%#x\n", uUser));
1127 return VERR_INTERNAL_ERROR;
1128 }
1129 }
1130 else
1131 {
1132 /*
1133 * Reconstruct the guest context and switch to the recompiler.
1134 * We ASSUME we're only at
1135 */
1136 CPUMCTXCORE CtxCore = *pRegFrame;
1137 uint32_t *pEsp = (uint32_t *)pRegFrame->esp;
1138 int rc;
1139
1140 switch (uUser)
1141 {
1142 /*
1143 * This will only occur when resuming guest code in a trap handler!
1144 */
1145 /* @note ASSUMES esp points to the temporary guest CPUMCTXCORE!!! */
1146 case TRPM_TRAP_IN_MOV_GS:
1147 case TRPM_TRAP_IN_MOV_FS:
1148 case TRPM_TRAP_IN_MOV_ES:
1149 case TRPM_TRAP_IN_MOV_DS:
1150 {
1151 PCPUMCTXCORE pTempGuestCtx = (PCPUMCTXCORE)pEsp;
1152
1153 /* Just copy the whole thing; several selector registers, eip (etc) and eax are not yet in pRegFrame. */
1154 CtxCore = *pTempGuestCtx;
1155 rc = VINF_EM_RAW_STALE_SELECTOR;
1156 break;
1157 }
1158
1159 /*
1160 * This will only occur when resuming guest code!
1161 */
1162 case TRPM_TRAP_IN_IRET:
1163 CtxCore.eip = *pEsp++;
1164 CtxCore.cs = (RTSEL)*pEsp++;
1165 CtxCore.eflags.u32 = *pEsp++;
1166 CtxCore.esp = *pEsp++;
1167 CtxCore.ss = (RTSEL)*pEsp++;
1168 rc = VINF_EM_RAW_IRET_TRAP;
1169 break;
1170
1171 /*
1172 * This will only occur when resuming V86 guest code!
1173 */
1174 case TRPM_TRAP_IN_IRET | TRPM_TRAP_IN_V86:
1175 CtxCore.eip = *pEsp++;
1176 CtxCore.cs = (RTSEL)*pEsp++;
1177 CtxCore.eflags.u32 = *pEsp++;
1178 CtxCore.esp = *pEsp++;
1179 CtxCore.ss = (RTSEL)*pEsp++;
1180 CtxCore.es = (RTSEL)*pEsp++;
1181 CtxCore.ds = (RTSEL)*pEsp++;
1182 CtxCore.fs = (RTSEL)*pEsp++;
1183 CtxCore.gs = (RTSEL)*pEsp++;
1184 rc = VINF_EM_RAW_IRET_TRAP;
1185 break;
1186
1187 default:
1188 AssertMsgFailed(("Invalid uUser=%#x\n", uUser));
1189 return VERR_INTERNAL_ERROR;
1190 }
1191
1192
1193 CPUMSetGuestCtxCore(pVM, &CtxCore);
1194 TRPMGCHyperReturnToHost(pVM, rc);
1195 }
1196
1197 AssertMsgFailed(("Impossible!\n"));
1198 return VERR_INTERNAL_ERROR;
1199}
1200
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