TRPM.cpp revision 5607eca3e8bb9dc0cd19c5fa58396e30375ad196
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * TRPM - The Trap Monitor.
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * Copyright (C) 2006-2007 Oracle Corporation
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * available from http://www.virtualbox.org. This file is free software;
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * you can redistribute it and/or modify it under the terms of the GNU
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * General Public License (GPL) as published by the Free Software
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync/** @page pg_trpm TRPM - The Trap Monitor
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * The Trap Monitor (TRPM) is responsible for all trap and interrupt handling in
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * the VMM. It plays a major role in raw-mode execution and a lesser one in the
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * hardware assisted mode.
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * Note first, the following will use trap as a collective term for faults,
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * aborts and traps.
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * @see grp_trpm
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * @section sec_trpm_rc Raw-Mode Context
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * When executing in the raw-mode context, TRPM will be managing the IDT and
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * processing all traps and interrupts. It will also monitor the guest IDT
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * because CSAM wishes to know about changes to it (trap/interrupt/syscall
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * handler patching) and TRPM needs to keep the \#BP gate in sync (ring-3
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * considerations). See TRPMR3SyncIDT and CSAMR3CheckGates.
84c4a51d2a3c1a5c3f7d6902a8ac8c2eb267f4e0vboxsync * External interrupts will be forwarded to the host context by the quickest
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * possible route where they will be reasserted. The other events will be
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * categorized into virtualization traps, genuine guest traps and hypervisor
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * traps. The latter group may be recoverable depending on when they happen and
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * whether there is a handler for it, otherwise it will cause a guru meditation.
84c4a51d2a3c1a5c3f7d6902a8ac8c2eb267f4e0vboxsync * TRPM distinguishes the between the first two (virt and guest traps) and the
84c4a51d2a3c1a5c3f7d6902a8ac8c2eb267f4e0vboxsync * latter (hyper) by checking the CPL of the trapping code, if CPL == 0 then
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * it's a hyper trap otherwise it's a virt/guest trap. There are three trap
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * dispatcher tables, one ad-hoc for one time traps registered via
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * TRPMGCSetTempHandler(), one for hyper traps and one for virt/guest traps.
84c4a51d2a3c1a5c3f7d6902a8ac8c2eb267f4e0vboxsync * The latter two live in TRPMGCHandlersA.asm, the former in the VM structure.
84c4a51d2a3c1a5c3f7d6902a8ac8c2eb267f4e0vboxsync * The raw-mode context trap handlers found in TRPMGCHandlers.cpp (for the most
84c4a51d2a3c1a5c3f7d6902a8ac8c2eb267f4e0vboxsync * part), will call up the other VMM sub-systems depending on what it things
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * happens. The two most busy traps are page faults (\#PF) and general
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * protection fault/trap (\#GP).
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * Before resuming guest code after having taken a virtualization trap or
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * injected a guest trap, TRPM will check for pending forced action and
ac1200e63b58a0451085add197084e5a4a1bb4cavboxsync * every now and again let TM check for timed out timers. This allows code that
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * is being executed as part of virtualization traps to signal ring-3 exits,
84c4a51d2a3c1a5c3f7d6902a8ac8c2eb267f4e0vboxsync * page table resyncs and similar without necessarily using the status code. It
84c4a51d2a3c1a5c3f7d6902a8ac8c2eb267f4e0vboxsync * also make sure we're more responsive to timers and requests from other
84c4a51d2a3c1a5c3f7d6902a8ac8c2eb267f4e0vboxsync * threads (necessarily running on some different core/cpu in most cases).
d960e43fb3727c884b5f7eca3c920c9549839881vboxsync * @section sec_trpm_all All Contexts
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync * TRPM will also dispatch / inject interrupts and traps to the guest, both when
84c4a51d2a3c1a5c3f7d6902a8ac8c2eb267f4e0vboxsync * in raw-mode and when in hardware assisted mode. See TRPMInject().
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync/*******************************************************************************
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync* Header Files *
84c4a51d2a3c1a5c3f7d6902a8ac8c2eb267f4e0vboxsync*******************************************************************************/
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync/*******************************************************************************
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync* Structures and Typedefs *
c40afa339b10a23d6fffcbeb7d4572bb494685f0vboxsync*******************************************************************************/
typedef enum TRPMHANDLER
TRPM_HANDLER_INT = 0,
#define IDTE_RESERVED() { 0, 0, 0, 0, 0, 0, 0, 0 }
#ifdef VBOX_WITH_NMI
IDTE_TRAP_GEN(), /* 10 - #MF - F - N - x86 FPU Floating-Point Error (Math fault), FP or (F)WAIT instruction. */
#define TRPM_TRACK_GUEST_IDT_CHANGES
#define TRPM_TRACK_SHADOW_IDT_CHANGES
static DECLCALLBACK(int) trpmR3GuestIDTWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
if (pTRPMNode)
return rc;
STAM_REG(pVM, &pVM->trpm.s.StatRCWriteGuestIDTFault, STAMTYPE_COUNTER, "/TRPM/RC/IDTWritesFault", STAMUNIT_OCCURENCES, "Guest IDT writes the we returned to R3 to handle.");
STAM_REG(pVM, &pVM->trpm.s.StatRCWriteGuestIDTHandled, STAMTYPE_COUNTER, "/TRPM/RC/IDTWritesHandled", STAMUNIT_OCCURENCES, "Guest IDT writes that we handled successfully.");
STAM_REG(pVM, &pVM->trpm.s.StatSyncIDT, STAMTYPE_PROFILE, "/PROF/TRPM/SyncIDT", STAMUNIT_TICKS_PER_CALL, "Profiling of TRPMR3SyncIDT().");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x00], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/00", STAMUNIT_TICKS_PER_CALL, "#DE - Divide error.");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x01], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/01", STAMUNIT_TICKS_PER_CALL, "#DB - Debug (single step and more).");
//STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x02], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/02", STAMUNIT_TICKS_PER_CALL, "NMI");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x03], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/03", STAMUNIT_TICKS_PER_CALL, "#BP - Breakpoint.");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x04], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/04", STAMUNIT_TICKS_PER_CALL, "#OF - Overflow.");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x05], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/05", STAMUNIT_TICKS_PER_CALL, "#BR - Bound range exceeded.");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x06], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/06", STAMUNIT_TICKS_PER_CALL, "#UD - Undefined opcode.");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x07], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/07", STAMUNIT_TICKS_PER_CALL, "#NM - Device not available (FPU).");
//STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x08], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/08", STAMUNIT_TICKS_PER_CALL, "#DF - Double fault.");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x09], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/09", STAMUNIT_TICKS_PER_CALL, "#?? - Coprocessor segment overrun (obsolete).");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x0a], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/0a", STAMUNIT_TICKS_PER_CALL, "#TS - Task switch fault.");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x0b], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/0b", STAMUNIT_TICKS_PER_CALL, "#NP - Segment not present.");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x0c], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/0c", STAMUNIT_TICKS_PER_CALL, "#SS - Stack segment fault.");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x0d], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/0d", STAMUNIT_TICKS_PER_CALL, "#GP - General protection fault.");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x0e], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/0e", STAMUNIT_TICKS_PER_CALL, "#PF - Page fault.");
//STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x0f], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/0f", STAMUNIT_TICKS_PER_CALL, "Reserved.");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x10], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/10", STAMUNIT_TICKS_PER_CALL, "#MF - Math fault..");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x11], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/11", STAMUNIT_TICKS_PER_CALL, "#AC - Alignment check.");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x12], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/12", STAMUNIT_TICKS_PER_CALL, "#MC - Machine check.");
STAM_REG(pVM, &pVM->trpm.s.aStatGCTraps[0x13], STAMTYPE_PROFILE_ADV, "/TRPM/GC/Traps/13", STAMUNIT_TICKS_PER_CALL, "#XF - SIMD Floating-Point Exception.");
#ifdef VBOX_WITH_STATISTICS
rc = MMHyperAlloc(pVM, sizeof(STAMCOUNTER) * 256, sizeof(STAMCOUNTER), MM_TAG_TRPM, (void **)&pVM->trpm.s.paStatForwardedIRQR3);
STAMR3RegisterF(pVM, &pVM->trpm.s.paStatForwardedIRQR3[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "Forwarded interrupts.",
rc = MMHyperAlloc(pVM, sizeof(STAMCOUNTER) * 256, sizeof(STAMCOUNTER), MM_TAG_TRPM, (void **)&pVM->trpm.s.paStatHostIrqR3);
STAMR3RegisterF(pVM, &pVM->trpm.s.paStatHostIrqR3[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
STAM_REG(pVM, &pVM->trpm.s.StatForwardProfR3, STAMTYPE_PROFILE_ADV, "/TRPM/ForwardRaw/ProfR3", STAMUNIT_TICKS_PER_CALL, "Profiling TRPMForwardTrap.");
STAM_REG(pVM, &pVM->trpm.s.StatForwardProfRZ, STAMTYPE_PROFILE_ADV, "/TRPM/ForwardRaw/ProfRZ", STAMUNIT_TICKS_PER_CALL, "Profiling TRPMForwardTrap.");
STAM_REG(pVM, &pVM->trpm.s.StatForwardFailNoHandler, STAMTYPE_COUNTER, "/TRPM/ForwardRaw/FailNoHandler", STAMUNIT_OCCURENCES,"Failure to forward interrupt in raw mode.");
STAM_REG(pVM, &pVM->trpm.s.StatForwardFailPatchAddr, STAMTYPE_COUNTER, "/TRPM/ForwardRaw/FailPatchAddr", STAMUNIT_OCCURENCES,"Failure to forward interrupt in raw mode.");
STAM_REG(pVM, &pVM->trpm.s.StatForwardFailR3, STAMTYPE_COUNTER, "/TRPM/ForwardRaw/FailR3", STAMUNIT_OCCURENCES, "Failure to forward interrupt in raw mode.");
STAM_REG(pVM, &pVM->trpm.s.StatForwardFailRZ, STAMTYPE_COUNTER, "/TRPM/ForwardRaw/FailRZ", STAMUNIT_OCCURENCES, "Failure to forward interrupt in raw mode.");
STAM_REG(pVM, &pVM->trpm.s.StatTrap0dDisasm, STAMTYPE_PROFILE, "/TRPM/RC/Traps/0d/Disasm", STAMUNIT_TICKS_PER_CALL, "Profiling disassembly part of trpmGCTrap0dHandler.");
STAM_REG(pVM, &pVM->trpm.s.StatTrap0dRdTsc, STAMTYPE_COUNTER, "/TRPM/RC/Traps/0d/RdTsc", STAMUNIT_OCCURENCES, "Number of RDTSC #GPs.");
int rc = PDMR3LdrGetSymbolRC(pVM, VMMGC_MAIN_MODULE_NAME, "TRPMGCHandlerInterupt", &aRCPtrs[TRPM_HANDLER_INT]);
rc = PDMR3LdrGetSymbolRC(pVM, VMMGC_MAIN_MODULE_NAME, "TRPMGCHandlerGeneric", &aRCPtrs[TRPM_HANDLER_TRAP]);
rc = PDMR3LdrGetSymbolRC(pVM, VMMGC_MAIN_MODULE_NAME, "TRPMGCHandlerTrap08", &aRCPtrs[TRPM_HANDLER_TRAP_08]);
rc = PDMR3LdrGetSymbolRC(pVM, VMMGC_MAIN_MODULE_NAME, "TRPMGCHandlerTrap12", &aRCPtrs[TRPM_HANDLER_TRAP_12]);
case TRPM_HANDLER_INT:
case TRPM_HANDLER_TRAP:
case TRPM_HANDLER_TRAP_12:
case TRPM_HANDLER_TRAP_08:
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_HYPERVISOR, pVM->trpm.s.pvMonShwIdtRC, pVM->trpm.s.pvMonShwIdtRC + sizeof(pVM->trpm.s.aIdt) - 1,
/* Relocate IDT handlers for forwarding guest traps/interrupts. */
Log(("TRPMR3Relocate: iGate=%2X Handler %RRv -> %RRv\n", iTrap, pVM->trpm.s.aGuestTrapHandler[iTrap], pVM->trpm.s.aGuestTrapHandler[iTrap] + offDelta));
#ifdef VBOX_WITH_STATISTICS
#ifdef TRPM_TRACK_GUEST_IDT_CHANGES
return VERR_SYMBOL_NOT_FOUND;
return VINF_SUCCESS;
return rc;
if (fSyncIDT)
return rc;
return rc;
return rc;
return VINF_SUCCESS;
int rc;
#ifdef TRPM_TRACK_GUEST_IDT_CHANGES
Log(("TRPMR3UpdateFromCPUM: Guest's IDT is changed to pIdt=%08X cbIdt=%08X\n", IDTR.pIdt, IDTR.cbIdt));
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, IDTR.pIdt, IDTR.pIdt + IDTR.cbIdt /* already inclusive */,
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, IDTR.pIdt, IDTR.pIdt + IDTR.cbIdt /* already inclusive */,
if (fRawRing0)
return VINF_SUCCESS;
#ifdef TRPM_TRACK_GUEST_IDT_CHANGES
static DECLCALLBACK(int) trpmR3GuestIDTWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf,
Log(("trpmR3GuestIDTWriteHandler: write to %RGv size %d\n", GCPtr, cbBuf)); NOREF(GCPtr); NOREF(cbBuf);
return VINF_PGM_HANDLER_DO_DEFAULT;
int rc;
rc = PDMR3LdrGetSymbolRC(pVM, VMMGC_MAIN_MODULE_NAME, "TRPMGCHandlerInterupt", &aGCPtrs[TRPM_HANDLER_INT]);
return VERR_INVALID_PARAMETER;
return VINF_SUCCESS;
return iTrap;
return iTrap;
return VERR_INVALID_PARAMETER;
AssertReturn(pHandler == TRPM_INVALID_HANDLER || PATMIsPatchGCAddr(pVM, pHandler), VERR_INVALID_PARAMETER);
int rc = PGMPhysSimpleReadGCPtr(pVCpu, &GuestIdte, GCPtrIDT + iTrap * sizeof(GuestIdte), sizeof(GuestIdte));
return rc;
return VINF_SUCCESS;
return VINF_SUCCESS;
return VERR_INVALID_PARAMETER;
if (!cEntries)
pIDTE++;
GCPtrIDTE = RT_ALIGN_T(GCPtrIDTE, PAGE_SIZE, RTGCPTR) + PAGE_SIZE + (GCPtrIDTE & (sizeof(VBOXIDTE) - 1));
#ifdef VBOX_WITH_REM
#ifdef TRPM_FORWARD_TRAPS_IN_GC
# ifdef LOG_ENABLED
Log(("TRPMR3InjectEvent: CPU%d u8Interrupt=%d (%#x) rc=%Rrc\n", pVCpu->idCpu, u8Interrupt, u8Interrupt, rc));
# ifndef IEM_VERIFICATION_MODE
Log(("TRPMR3InjectEvent: recheck gate %x -> valid=%d\n", u8Interrupt, TRPMR3GetGuestTrapHandler(pVM, u8Interrupt) != TRPM_INVALID_HANDLER));
rc = TRPMForwardTrap(pVCpu, CPUMCTX2CORE(pCtx), u8Interrupt, 0, TRPM_TRAP_NO_ERRORCODE, enmEvent, -1);
Assert(!VMCPU_FF_ISPENDING(pVCpu, VMCPU_FF_SELM_SYNC_GDT | VMCPU_FF_SELM_SYNC_LDT | VMCPU_FF_TRPM_SYNC_IDT | VMCPU_FF_SELM_SYNC_TSS));
return VINF_EM_RESCHEDULE_RAW;
# ifdef VBOX_WITH_REM
return HMR3IsActive(pVCpu) ? VINF_EM_RESCHEDULE_HM : VINF_EM_RESCHEDULE_REM; /* (Heed the halted state if this is changed!) */
return VINF_EM_RESCHEDULE_HM;
* this implies a safe state in translated instructions and should take sti successors into account (instruction fusing)