VirtualBox

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

Last change on this file since 61675 was 61544, checked in by vboxsync, 9 years ago

VMM/GIM: Fix up hypercall rc handling and also a bug while disassembling hypercalls. IEM/REM being able to handle VMCALL/VMMCALL to hook into GIM is still a todo but this change temporarily works around by adding new GIM and GIM provider interfaces to kinda do the same work. The Xcpt handlers were not re-used as they check for whether or not the provider requested UD trapping in the first place.

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