diff options
Diffstat (limited to 'src/VBox/VMM/VMMAll/TRPMAll.cpp')
-rw-r--r-- | src/VBox/VMM/VMMAll/TRPMAll.cpp | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMAll/TRPMAll.cpp b/src/VBox/VMM/VMMAll/TRPMAll.cpp new file mode 100644 index 00000000..a7901d52 --- /dev/null +++ b/src/VBox/VMM/VMMAll/TRPMAll.cpp @@ -0,0 +1,438 @@ +/* $Id: TRPMAll.cpp $ */ +/** @file + * TRPM - Trap Monitor - Any Context. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_TRPM +#include <VBox/vmm/trpm.h> +#include <VBox/vmm/pgm.h> +#include <VBox/vmm/mm.h> +#include <VBox/vmm/hm.h> +#include <VBox/vmm/selm.h> +#include <VBox/vmm/stam.h> +#include <VBox/vmm/dbgf.h> +#include "TRPMInternal.h" +#include <VBox/vmm/vmcc.h> +#include <VBox/err.h> +#include <VBox/vmm/em.h> +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/asm.h> +#include <iprt/param.h> +#include <iprt/x86.h> + + + +/** + * Query info about the current active trap/interrupt. + * If no trap is active active an error code is returned. + * + * @returns VBox status code. + * @param pVCpu The cross context virtual CPU structure. + * @param pu8TrapNo Where to store the trap number. + * @param penmType Where to store the trap type + */ +VMMDECL(int) TRPMQueryTrap(PVMCPU pVCpu, uint8_t *pu8TrapNo, TRPMEVENT *penmType) +{ + /* + * Check if we have a trap at present. + */ + if (pVCpu->trpm.s.uActiveVector != ~0U) + { + if (pu8TrapNo) + *pu8TrapNo = (uint8_t)pVCpu->trpm.s.uActiveVector; + if (penmType) + *penmType = pVCpu->trpm.s.enmActiveType; + return VINF_SUCCESS; + } + + return VERR_TRPM_NO_ACTIVE_TRAP; +} + + +/** + * Gets the trap number for the current trap. + * + * The caller is responsible for making sure there is an active trap which + * takes an error code when making this request. + * + * @returns The current trap number. + * @param pVCpu The cross context virtual CPU structure. + */ +VMMDECL(uint8_t) TRPMGetTrapNo(PVMCPU pVCpu) +{ + AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n")); + return (uint8_t)pVCpu->trpm.s.uActiveVector; +} + + +/** + * Gets the error code for the current trap. + * + * The caller is responsible for making sure there is an active trap which + * takes an error code when making this request. + * + * @returns Error code. + * @param pVCpu The cross context virtual CPU structure. + */ +VMMDECL(uint32_t) TRPMGetErrorCode(PVMCPU pVCpu) +{ + AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n")); +#ifdef VBOX_STRICT + switch (pVCpu->trpm.s.uActiveVector) + { + case X86_XCPT_TS: + case X86_XCPT_NP: + case X86_XCPT_SS: + case X86_XCPT_GP: + case X86_XCPT_PF: + case X86_XCPT_AC: + case X86_XCPT_DF: + break; + default: + AssertMsgFailed(("This trap (%#x) doesn't have any error code\n", pVCpu->trpm.s.uActiveVector)); + break; + } +#endif + return pVCpu->trpm.s.uActiveErrorCode; +} + + +/** + * Gets the fault address for the current trap. + * + * The caller is responsible for making sure there is an active trap 0x0e when + * making this request. + * + * @returns Fault address associated with the trap. + * @param pVCpu The cross context virtual CPU structure. + */ +VMMDECL(RTGCUINTPTR) TRPMGetFaultAddress(PVMCPU pVCpu) +{ + AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n")); + AssertMsg(pVCpu->trpm.s.uActiveVector == X86_XCPT_PF, ("Not page-fault trap!\n")); + return pVCpu->trpm.s.uActiveCR2; +} + + +/** + * Gets the instruction-length for the current trap (only relevant for software + * interrupts and software exceptions \#BP and \#OF). + * + * The caller is responsible for making sure there is an active trap 0x0e when + * making this request. + * + * @returns Fault address associated with the trap. + * @param pVCpu The cross context virtual CPU structure. + */ +VMMDECL(uint8_t) TRPMGetInstrLength(PVMCPU pVCpu) +{ + AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n")); + return pVCpu->trpm.s.cbInstr; +} + + +/** + * Checks if the current \#DB exception is due to an INT1/ICEBP instruction. + * + * The caller is responsible for making sure there is an active trap. + * + * @returns @c true if it's due to INT1/ICEBP, @c false if not. + * + * @param pVCpu The cross context virtual CPU structure. + */ +VMMDECL(bool) TRPMIsTrapDueToIcebp(PVMCPU pVCpu) +{ + AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n")); + return pVCpu->trpm.s.fIcebp; +} + + +/** + * Clears the current active trap/exception/interrupt. + * + * The caller is responsible for making sure there is an active trap + * when making this request. + * + * @returns VBox status code. + * @param pVCpu The cross context virtual CPU structure. + */ +VMMDECL(int) TRPMResetTrap(PVMCPU pVCpu) +{ + /* + * Cannot reset non-existing trap! + */ + if (pVCpu->trpm.s.uActiveVector == ~0U) + { + AssertMsgFailed(("No active trap!\n")); + return VERR_TRPM_NO_ACTIVE_TRAP; + } + + /* + * Reset it. + */ + pVCpu->trpm.s.uActiveVector = ~0U; + return VINF_SUCCESS; +} + + +/** + * Assert trap/exception/interrupt. + * + * The caller is responsible for making sure there is no active trap + * when making this request. + * + * @returns VBox status code. + * @param pVCpu The cross context virtual CPU structure. + * @param u8TrapNo The trap vector to assert. + * @param enmType Trap type. + */ +VMMDECL(int) TRPMAssertTrap(PVMCPUCC pVCpu, uint8_t u8TrapNo, TRPMEVENT enmType) +{ + Log2(("TRPMAssertTrap: u8TrapNo=%02x type=%d\n", u8TrapNo, enmType)); + + /* + * Cannot assert a trap when one is already active. + */ + if (pVCpu->trpm.s.uActiveVector != ~0U) + { + AssertMsgFailed(("CPU%d: Active trap %#x\n", pVCpu->idCpu, pVCpu->trpm.s.uActiveVector)); + return VERR_TRPM_ACTIVE_TRAP; + } + + pVCpu->trpm.s.uActiveVector = u8TrapNo; + pVCpu->trpm.s.enmActiveType = enmType; + pVCpu->trpm.s.uActiveErrorCode = ~0U; + pVCpu->trpm.s.uActiveCR2 = 0xdeadface; + pVCpu->trpm.s.cbInstr = UINT8_MAX; + pVCpu->trpm.s.fIcebp = false; + return VINF_SUCCESS; +} + + +/** + * Assert a page-fault exception. + * + * The caller is responsible for making sure there is no active trap + * when making this request. + * + * @returns VBox status code. + * @param pVCpu The cross context virtual CPU structure. + * @param uCR2 The new fault address. + * @param uErrorCode The error code for the page-fault. + */ +VMMDECL(int) TRPMAssertXcptPF(PVMCPUCC pVCpu, RTGCUINTPTR uCR2, uint32_t uErrorCode) +{ + Log2(("TRPMAssertXcptPF: uCR2=%RGv uErrorCode=%#RX32\n", uCR2, uErrorCode)); + + /* + * Cannot assert a trap when one is already active. + */ + if (pVCpu->trpm.s.uActiveVector != ~0U) + { + AssertMsgFailed(("CPU%d: Active trap %#x\n", pVCpu->idCpu, pVCpu->trpm.s.uActiveVector)); + return VERR_TRPM_ACTIVE_TRAP; + } + + pVCpu->trpm.s.uActiveVector = X86_XCPT_PF; + pVCpu->trpm.s.enmActiveType = TRPM_TRAP; + pVCpu->trpm.s.uActiveErrorCode = uErrorCode; + pVCpu->trpm.s.uActiveCR2 = uCR2; + pVCpu->trpm.s.cbInstr = UINT8_MAX; + return VINF_SUCCESS; +} + + +/** + * Sets the error code of the current trap. + * (This function is for use in trap handlers and such.) + * + * The caller is responsible for making sure there is an active trap + * which takes an errorcode when making this request. + * + * @param pVCpu The cross context virtual CPU structure. + * @param uErrorCode The new error code. + */ +VMMDECL(void) TRPMSetErrorCode(PVMCPU pVCpu, uint32_t uErrorCode) +{ + Log2(("TRPMSetErrorCode: uErrorCode=%#RX32\n", uErrorCode)); + AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n")); + AssertMsg( pVCpu->trpm.s.enmActiveType == TRPM_TRAP + || ( pVCpu->trpm.s.enmActiveType == TRPM_SOFTWARE_INT && pVCpu->trpm.s.uActiveVector == X86_XCPT_DB), + ("Not hardware exception or privileged software exception (INT1/ICEBP)!\n")); + pVCpu->trpm.s.uActiveErrorCode = uErrorCode; +#ifdef VBOX_STRICT + if (pVCpu->trpm.s.enmActiveType == TRPM_TRAP) + { + switch (pVCpu->trpm.s.uActiveVector) + { + case X86_XCPT_TS: case X86_XCPT_NP: case X86_XCPT_SS: case X86_XCPT_GP: case X86_XCPT_PF: + AssertMsg(uErrorCode != ~0U, ("Invalid uErrorCode=%#x u8TrapNo=%u\n", uErrorCode, pVCpu->trpm.s.uActiveVector)); + break; + case X86_XCPT_AC: case X86_XCPT_DF: + AssertMsg(uErrorCode == 0, ("Invalid uErrorCode=%#x u8TrapNo=%u\n", uErrorCode, pVCpu->trpm.s.uActiveVector)); + break; + default: + AssertMsg(uErrorCode == ~0U, ("Invalid uErrorCode=%#x u8TrapNo=%u\n", uErrorCode, pVCpu->trpm.s.uActiveVector)); + break; + } + } +#endif +} + + +/** + * Sets the fault address of the current \#PF trap. (This function is for use in + * trap handlers and such.) + * + * The caller is responsible for making sure there is an active trap 0e + * when making this request. + * + * @param pVCpu The cross context virtual CPU structure. + * @param uCR2 The new fault address (cr2 register). + */ +VMMDECL(void) TRPMSetFaultAddress(PVMCPU pVCpu, RTGCUINTPTR uCR2) +{ + Log2(("TRPMSetFaultAddress: uCR2=%RGv\n", uCR2)); + AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n")); + AssertMsg(pVCpu->trpm.s.enmActiveType == TRPM_TRAP, ("Not hardware exception!\n")); + AssertMsg(pVCpu->trpm.s.uActiveVector == X86_XCPT_PF, ("Not trap 0e!\n")); + pVCpu->trpm.s.uActiveCR2 = uCR2; +} + + +/** + * Sets the instruction-length of the current trap (relevant for software + * interrupts and software exceptions like \#BP, \#OF). + * + * The caller is responsible for making sure there is an active trap when making + * this request. + * + * @param pVCpu The cross context virtual CPU structure. + * @param cbInstr The instruction length. + */ +VMMDECL(void) TRPMSetInstrLength(PVMCPU pVCpu, uint8_t cbInstr) +{ + Log2(("TRPMSetInstrLength: cbInstr=%u\n", cbInstr)); + AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n")); + AssertMsg( pVCpu->trpm.s.enmActiveType == TRPM_SOFTWARE_INT + || ( pVCpu->trpm.s.enmActiveType == TRPM_TRAP + && ( pVCpu->trpm.s.uActiveVector == X86_XCPT_BP + || pVCpu->trpm.s.uActiveVector == X86_XCPT_OF)), + ("Invalid trap type %#x\n", pVCpu->trpm.s.enmActiveType)); + pVCpu->trpm.s.cbInstr = cbInstr; +} + + +/** + * Sets if the current \#DB exception is due to an INT1/ICEBP instruction. + * + * The caller is responsible for making sure there is an active trap and it's a + * \#DB. + * + * @param pVCpu The cross context virtual CPU structure. + */ +VMMDECL(void) TRPMSetTrapDueToIcebp(PVMCPU pVCpu) +{ + AssertMsg(pVCpu->trpm.s.enmActiveType == TRPM_SOFTWARE_INT, ("Trap type for INT1/ICEBP invalid!")); + AssertMsg(pVCpu->trpm.s.uActiveVector == X86_XCPT_DB, ("INT1/ICEBP must be indicated by a #DB!\n")); + pVCpu->trpm.s.fIcebp = true; +} + + +/** + * Checks if the current active trap/interrupt/exception/fault/whatever is a software + * interrupt or not. + * + * The caller is responsible for making sure there is an active trap + * when making this request. + * + * @returns true if software interrupt, false if not. + * + * @param pVCpu The cross context virtual CPU structure. + */ +VMMDECL(bool) TRPMIsSoftwareInterrupt(PVMCPU pVCpu) +{ + AssertMsg(pVCpu->trpm.s.uActiveVector != ~0U, ("No active trap!\n")); + return (pVCpu->trpm.s.enmActiveType == TRPM_SOFTWARE_INT); +} + + +/** + * Check if there is an active trap. + * + * @returns true if trap active, false if not. + * @param pVCpu The cross context virtual CPU structure. + */ +VMMDECL(bool) TRPMHasTrap(PVMCPU pVCpu) +{ + return pVCpu->trpm.s.uActiveVector != ~0U; +} + + +/** + * Query all info about the current active trap/interrupt. + * If no trap is active active an error code is returned. + * + * @returns VBox status code. + * @param pVCpu The cross context virtual CPU structure. + * @param pu8TrapNo Where to store the trap number. + * @param pEnmType Where to store the trap type. + * @param puErrorCode Where to store the error code associated with some + * traps. ~0U is stored if the trap has no error code. + * @param puCR2 Where to store the CR2 associated with a trap 0E. + * @param pcbInstr Where to store the instruction-length associated with + * some traps. + * @param pfIcebp Where to store whether the trap is a \#DB caused by an + * INT1/ICEBP instruction. + */ +VMMDECL(int) TRPMQueryTrapAll(PVMCPU pVCpu, uint8_t *pu8TrapNo, TRPMEVENT *pEnmType, uint32_t *puErrorCode, PRTGCUINTPTR puCR2, + uint8_t *pcbInstr, bool *pfIcebp) +{ + /* + * Check if we have an active trap. + */ + if (pVCpu->trpm.s.uActiveVector == ~0U) + return VERR_TRPM_NO_ACTIVE_TRAP; + + if (pu8TrapNo) + *pu8TrapNo = (uint8_t)pVCpu->trpm.s.uActiveVector; + if (pEnmType) + *pEnmType = pVCpu->trpm.s.enmActiveType; + if (puErrorCode) + *puErrorCode = pVCpu->trpm.s.uActiveErrorCode; + if (puCR2) + *puCR2 = pVCpu->trpm.s.uActiveCR2; + if (pcbInstr) + *pcbInstr = pVCpu->trpm.s.cbInstr; + if (pfIcebp) + *pfIcebp = pVCpu->trpm.s.fIcebp; + return VINF_SUCCESS; +} + |