TRPM.cpp revision 0ee0973d45375de2c19b5801ee3cf8fe09c45182
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * TRPM - The Trap Monitor
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * Copyright (C) 2006 InnoTek Systemberatung GmbH
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * available from http://www.virtualbox.org. This file is free software;
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * you can redistribute it and/or modify it under the terms of the GNU
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * General Public License as published by the Free Software Foundation,
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * distribution. VirtualBox OSE is distributed in the hope that it will
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * be useful, but WITHOUT ANY WARRANTY of any kind.
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * If you received this file as part of a commercial VirtualBox
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * distribution, then only the terms of your commercial VirtualBox
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * license agreement apply instead of the previous paragraph.
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync/** @page pg_trpm TRPM - The Trap Monitor
bc830c4bf23fbfb4373b949a1d408b4a1c67017dvboxsync * The Trap Monitor (TRPM) is responsible for all trap and interrupt
bc830c4bf23fbfb4373b949a1d408b4a1c67017dvboxsync * handling in the VMM.
bc830c4bf23fbfb4373b949a1d408b4a1c67017dvboxsync * Interrupts occuring in GC will be routed to the HC and reassert there. TRPM
bc830c4bf23fbfb4373b949a1d408b4a1c67017dvboxsync * makes the assumption that the VMM or Guest will not cause hardware
bc830c4bf23fbfb4373b949a1d408b4a1c67017dvboxsync * interrupts to occur.
bc830c4bf23fbfb4373b949a1d408b4a1c67017dvboxsync * Traps will be passed to a list of registered trap handlers which will
bc830c4bf23fbfb4373b949a1d408b4a1c67017dvboxsync * check and see if they are the responsible part for the trap. If no handler
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * was found the default action is to pass the trap on the Guest OS. Trap
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * handlers may raise a Guest OS trap as a result of the trap handling.
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * Statistics will be maintained so the trap handler list can be resorted
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * every now and then to examin handlers in the optimal order.
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * If a trap happens inside the VMM (Guest Context) the TRPM will take the
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * shortest path back to Ring-3 Host Context and brutally destroy the VM.
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * The TRPM will have interfaces to enable devices to assert interrupts
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * in the guest, these interfaces are multithreaded and availble from
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * all contexts. This is to allow devices to have use worker threads.
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync/*******************************************************************************
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync* Header Files *
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync*******************************************************************************/
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) trpmGuestIDTWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
return rc;
STAM_REG(pVM, &pVM->trpm.s.StatGCWriteGuestIDTFault, STAMTYPE_COUNTER, "/TRPM/GC/Write/IDT/Fault", STAMUNIT_OCCURENCES, "The number of writes to the Guest IDT.");
STAM_REG(pVM, &pVM->trpm.s.StatGCWriteGuestIDTHandled, STAMTYPE_COUNTER, "/TRPM/GC/Write/IDT/Handled", STAMUNIT_OCCURENCES, "The number of writes to the Guest IDT.");
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 - Segemnt 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) * 255, 8, MM_TAG_STAM, (void **)&pVM->trpm.s.paStatForwardedIRQR3);
STAMR3RegisterF(pVM, &pVM->trpm.s.paStatForwardedIRQR3[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, "Forwarded interrupts.",
STAM_REG(pVM, &pVM->trpm.s.StatForwardFailNoHandler, STAMTYPE_COUNTER, "/TRPM/ForwardRaw/Fail/NoHandler", STAMUNIT_OCCURENCES, "Failure to forward interrupt in raw mode.");
STAM_REG(pVM, &pVM->trpm.s.StatForwardFailPatchAddr, STAMTYPE_COUNTER, "/TRPM/ForwardRaw/Fail/PatchAddr", STAMUNIT_OCCURENCES, "Failure to forward interrupt in raw mode.");
STAM_REG(pVM, &pVM->trpm.s.StatForwardFailGC, STAMTYPE_COUNTER, "/TRPM/ForwardRaw/Fail/GC", STAMUNIT_OCCURENCES, "Failure to forward interrupt in raw mode.");
STAM_REG(pVM, &pVM->trpm.s.StatForwardFailHC, STAMTYPE_COUNTER, "/TRPM/ForwardRaw/Fail/HC", STAMUNIT_OCCURENCES, "Failure to forward interrupt in raw mode.");
STAM_REG(pVM, &pVM->trpm.s.StatForwardProfGC, STAMTYPE_PROFILE_ADV, "/TRPM/ForwardRaw/Prof/GC", STAMUNIT_TICKS_PER_CALL, "Profiling TRPMForwardTrap.");
STAM_REG(pVM, &pVM->trpm.s.StatForwardProfHC, STAMTYPE_PROFILE_ADV, "/TRPM/ForwardRaw/Prof/HC", STAMUNIT_TICKS_PER_CALL, "Profiling TRPMForwardTrap.");
STAM_REG(pVM, &pVM->trpm.s.StatTrap0dDisasm, STAMTYPE_PROFILE_ADV, "/TRPM/Trap0d/Prof/Disasm", STAMUNIT_TICKS_PER_CALL, "Profiling trpmGCTrap0dHandler.");
int rc;
rc = PDMR3GetSymbolGC(pVM, VMMGC_MAIN_MODULE_NAME, "TRPMGCHandlerInterupt", &aGCPtrs[TRPM_HANDLER_INT]);
rc = PDMR3GetSymbolGC(pVM, VMMGC_MAIN_MODULE_NAME, "TRPMGCHandlerGeneric", &aGCPtrs[TRPM_HANDLER_TRAP]);
rc = PDMR3GetSymbolGC(pVM, VMMGC_MAIN_MODULE_NAME, "TRPMGCHandlerTrap08", &aGCPtrs[TRPM_HANDLER_TRAP_08]);
rc = PDMR3GetSymbolGC(pVM, VMMGC_MAIN_MODULE_NAME, "TRPMGCHandlerTrap12", &aGCPtrs[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.GCPtrIdt, pVM->trpm.s.GCPtrIdt + sizeof(pVM->trpm.s.aIdt) - 1,
/* Relocate IDT handlers for forwarding guest traps/interrupts. */
Log(("TRPMR3Relocate: iGate=%2X Handler %VGv -> %VGv\n", iTrap, pVM->trpm.s.aGuestTrapHandler[iTrap], pVM->trpm.s.aGuestTrapHandler[iTrap] + offDelta));
#ifdef TRPM_TRACK_GUEST_IDT_CHANGES
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 */,
if (fRawRing0)
return VINF_SUCCESS;
#ifdef TRPM_TRACK_GUEST_IDT_CHANGES
static DECLCALLBACK(int) trpmGuestIDTWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
return VINF_PGM_HANDLER_DO_DEFAULT;
int rc;
rc = PDMR3GetSymbolGC(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 = PGMPhysReadGCPtr(pVM, &GuestIdte, GCPtrIDT + iTrap * sizeof(GuestIdte), sizeof(GuestIdte));
return rc;
* Only replace the 0x2E handler; others need to be called indirectly via a trampoline in our GC handlers
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));
int rc;
#ifdef TRPM_FORWARD_TRAPS_IN_GC
# ifdef LOG_ENABLED
return VINF_EM_RESCHEDULE_HWACC;
Log(("TRPMR3InjectEvent: recheck gate %x -> valid=%d\n", u8Interrupt, TRPMR3GetGuestTrapHandler(pVM, u8Interrupt) != TRPM_INVALID_HANDLER));
return VINF_EM_RESCHEDULE_RAW;
return VINF_EM_RESCHEDULE_HWACC;
* this implies a safe state in translated instructions and should take sti successors into account (instruction fusing)