VirtualBox

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

Last change on this file since 30045 was 29250, checked in by vboxsync, 15 years ago

iprt/asm*.h: split out asm-math.h, don't include asm-*.h from asm.h, don't include asm.h from sup.h. Fixed a couple file headers.

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