VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMRC/TRPMRCHandlers.cpp@ 47671

Last change on this file since 47671 was 47671, checked in by vboxsync, 12 years ago

VMM: More debugging related stuff.

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