VirtualBox

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

Last change on this file since 19167 was 19141, checked in by vboxsync, 16 years ago

Action flags breakup.
Fixed PGM saved state loading of 2.2.2 images.
Reduced hacks in PATM state loading (fixups).

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