diff options
Diffstat (limited to 'src/VBox/VMM/VMMRC/TRPMRC.cpp')
-rw-r--r-- | src/VBox/VMM/VMMRC/TRPMRC.cpp | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMRC/TRPMRC.cpp b/src/VBox/VMM/VMMRC/TRPMRC.cpp new file mode 100644 index 000000000..9d4c5a988 --- /dev/null +++ b/src/VBox/VMM/VMMRC/TRPMRC.cpp @@ -0,0 +1,194 @@ +/* $Id: TRPMRC.cpp 35346 2010-12-27 16:13:13Z vboxsync $ */ +/** @file + * TRPM - The Trap Monitor, Guest Context + */ + +/* + * Copyright (C) 2006-2007 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_TRPM +#include <VBox/vmm/trpm.h> +#include <VBox/vmm/cpum.h> +#include <VBox/vmm/vmm.h> +#include "TRPMInternal.h" +#include <VBox/vmm/vm.h> + +#include <VBox/err.h> +#include <VBox/x86.h> +#include <VBox/vmm/em.h> +#include <iprt/assert.h> +#include <iprt/asm.h> +#include <VBox/log.h> +#include <VBox/vmm/selm.h> + + + +/** + * Arms a temporary trap handler for traps in Hypervisor code. + * + * The operation is similar to a System V signal handler. I.e. when the handler + * is called it is first set to default action. So, if you need to handler more + * than one trap, you must reinstall the handler. + * + * To uninstall the temporary handler, call this function with pfnHandler set to NULL. + * + * @returns VBox status. + * @param pVM VM handle. + * @param iTrap Trap number to install handler [0..255]. + * @param pfnHandler Pointer to the handler. Use NULL for uninstalling the handler. + */ +VMMRCDECL(int) TRPMGCSetTempHandler(PVM pVM, unsigned iTrap, PFNTRPMGCTRAPHANDLER pfnHandler) +{ + /* + * Validate input. + */ + if (iTrap >= RT_ELEMENTS(pVM->trpm.s.aTmpTrapHandlers)) + { + AssertMsgFailed(("Trap handler iTrap=%u is out of range!\n", iTrap)); + return VERR_INVALID_PARAMETER; + } + + /* + * Install handler. + */ + pVM->trpm.s.aTmpTrapHandlers[iTrap] = (RTRCPTR)(uintptr_t)pfnHandler; + return VINF_SUCCESS; +} + + +/** + * Return to host context from a hypervisor trap handler. + * + * This function will *never* return. + * It will also reset any traps that are pending. + * + * @param pVM The VM handle. + * @param rc The return code for host context. + */ +VMMRCDECL(void) TRPMGCHyperReturnToHost(PVM pVM, int rc) +{ + PVMCPU pVCpu = VMMGetCpu0(pVM); + + LogFlow(("TRPMGCHyperReturnToHost: rc=%Rrc\n", rc)); + TRPMResetTrap(pVCpu); + CPUMHyperSetCtxCore(pVCpu, NULL); + VMMGCGuestToHost(pVM, rc); + AssertReleaseFailed(); +} + + +/** + * \#PF Virtual Handler callback for Guest write access to the Guest's own current IDT. + * + * @returns VBox status code (appropriate for trap handling and GC return). + * @param pVM VM Handle. + * @param uErrorCode CPU Error code. + * @param pRegFrame Trap register frame. + * @param pvFault The fault address (cr2). + * @param pvRange The base address of the handled virtual range. + * @param offRange The offset of the access into this range. + * (If it's a EIP range this is the EIP, if not it's pvFault.) + */ +VMMRCDECL(int) trpmRCGuestIDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange) +{ + PVMCPU pVCpu = VMMGetCpu0(pVM); + uint16_t cbIDT; + RTGCPTR GCPtrIDT = (RTGCPTR)CPUMGetGuestIDTR(pVCpu, &cbIDT); +#ifdef VBOX_STRICT + RTGCPTR GCPtrIDTEnd = (RTGCPTR)((RTGCUINTPTR)GCPtrIDT + cbIDT + 1); +#endif + uint32_t iGate = ((RTGCUINTPTR)pvFault - (RTGCUINTPTR)GCPtrIDT)/sizeof(VBOXIDTE); + + AssertMsg(offRange < (uint32_t)cbIDT+1, ("pvFault=%RGv GCPtrIDT=%RGv-%RGv pvRange=%RGv\n", pvFault, GCPtrIDT, GCPtrIDTEnd, pvRange)); + Assert((RTGCPTR)(RTRCUINTPTR)pvRange == GCPtrIDT); + +#if 0 + /* Note! this causes problems in Windows XP as instructions following the update can be dangerous (str eax has been seen) */ + /* Note! not going back to ring 3 could make the code scanner miss them. */ + /* Check if we can handle the write here. */ + if ( iGate != 3 /* Gate 3 is handled differently; could do it here as well, but let ring 3 handle this case for now. */ + && !ASMBitTest(&pVM->trpm.s.au32IdtPatched[0], iGate)) /* Passthru gates need special attention too. */ + { + uint32_t cb; + int rc = EMInterpretInstruction(pVM, pVCpu, pRegFrame, pvFault, &cb); + if (RT_SUCCESS(rc) && cb) + { + uint32_t iGate1 = (offRange + cb - 1)/sizeof(VBOXIDTE); + + Log(("trpmRCGuestIDTWriteHandler: write to gate %x (%x) offset %x cb=%d\n", iGate, iGate1, offRange, cb)); + + trpmClearGuestTrapHandler(pVM, iGate); + if (iGate != iGate1) + trpmClearGuestTrapHandler(pVM, iGate1); + + STAM_COUNTER_INC(&pVM->trpm.s.StatRCWriteGuestIDTHandled); + return VINF_SUCCESS; + } + } +#else + NOREF(iGate); +#endif + + Log(("trpmRCGuestIDTWriteHandler: eip=%RGv write to gate %x offset %x\n", pRegFrame->eip, iGate, offRange)); + + /** @todo Check which IDT entry and keep the update cost low in TRPMR3SyncIDT() and CSAMCheckGates(). */ + VMCPU_FF_SET(pVCpu, VMCPU_FF_TRPM_SYNC_IDT); + + STAM_COUNTER_INC(&pVM->trpm.s.StatRCWriteGuestIDTFault); + return VINF_EM_RAW_EMULATE_INSTR_IDT_FAULT; +} + + +/** + * \#PF Virtual Handler callback for Guest write access to the VBox shadow IDT. + * + * @returns VBox status code (appropriate for trap handling and GC return). + * @param pVM VM Handle. + * @param uErrorCode CPU Error code. + * @param pRegFrame Trap register frame. + * @param pvFault The fault address (cr2). + * @param pvRange The base address of the handled virtual range. + * @param offRange The offset of the access into this range. + * (If it's a EIP range this is the EIP, if not it's pvFault.) + */ +VMMRCDECL(int) trpmRCShadowIDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange) +{ + PVMCPU pVCpu = VMMGetCpu0(pVM); + LogRel(("FATAL ERROR: trpmRCShadowIDTWriteHandler: eip=%08X pvFault=%RGv pvRange=%08X\r\n", pRegFrame->eip, pvFault, pvRange)); + + /* If we ever get here, then the guest has executed an sidt instruction that we failed to patch. In theory this could be very bad, but + * there are nasty applications out there that install device drivers that mess with the guest's IDT. In those cases, it's quite ok + * to simply ignore the writes and pretend success. + */ + RTGCPTR PC; + int rc = SELMValidateAndConvertCSAddr(pVM, pRegFrame->eflags, pRegFrame->ss, pRegFrame->cs, &pRegFrame->csHid, + (RTGCPTR)pRegFrame->eip, &PC); + if (rc == VINF_SUCCESS) + { + DISCPUSTATE Cpu; + uint32_t cbOp; + rc = EMInterpretDisasOneEx(pVM, pVCpu, (RTGCUINTPTR)PC, pRegFrame, &Cpu, &cbOp); + if (rc == VINF_SUCCESS) + { + /* Just ignore the write. */ + pRegFrame->eip += Cpu.opsize; + return VINF_SUCCESS; + } + } + + return VERR_TRPM_SHADOW_IDT_WRITE; +} + |