/* $Id: IEMAll.cpp 108953 2025-04-11 23:49:15Z vboxsync $ */ /** @file * IEM - Interpreted Execution Manager - All Contexts. */ /* * Copyright (C) 2011-2024 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 . * * SPDX-License-Identifier: GPL-3.0-only */ /** @page pg_iem IEM - Interpreted Execution Manager * * The interpreted exeuction manager (IEM) is for executing short guest code * sequences that are causing too many exits / virtualization traps. It will * also be used to interpret single instructions, thus replacing the selective * interpreters in EM and IOM. * * Design goals: * - Relatively small footprint, although we favour speed and correctness * over size. * - Reasonably fast. * - Correctly handle lock prefixed instructions. * - Complete instruction set - eventually. * - Refactorable into a recompiler, maybe. * - Replace EMInterpret*. * * Using the existing disassembler has been considered, however this is thought * to conflict with speed as the disassembler chews things a bit too much while * leaving us with a somewhat complicated state to interpret afterwards. * * * The current code is very much work in progress. You've been warned! * * * @section sec_iem_fpu_instr FPU Instructions * * On x86 and AMD64 hosts, the FPU instructions are implemented by executing the * same or equivalent instructions on the host FPU. To make life easy, we also * let the FPU prioritize the unmasked exceptions for us. This however, only * works reliably when CR0.NE is set, i.e. when using \#MF instead the IRQ 13 * for FPU exception delivery, because with CR0.NE=0 there is a window where we * can trigger spurious FPU exceptions. * * The guest FPU state is not loaded into the host CPU and kept there till we * leave IEM because the calling conventions have declared an all year open * season on much of the FPU state. For instance an innocent looking call to * memcpy might end up using a whole bunch of XMM or MM registers if the * particular implementation finds it worthwhile. * * * @section sec_iem_logging Logging * * The IEM code uses the \"IEM\" log group for the main logging. The different * logging levels/flags are generally used for the following purposes: * - Level 1 (Log) : Errors, exceptions, interrupts and such major events. * - Flow (LogFlow) : Basic enter/exit IEM state info. * - Level 2 (Log2) : ? * - Level 3 (Log3) : More detailed enter/exit IEM state info. * - Level 4 (Log4) : Decoding mnemonics w/ EIP. * - Level 5 (Log5) : Decoding details. * - Level 6 (Log6) : Enables/disables the lockstep comparison with REM. * - Level 7 (Log7) : iret++ execution logging. * - Level 8 (Log8) : * - Level 9 (Log9) : * - Level 10 (Log10): TLBs. * - Level 11 (Log11): Unmasked FPU exceptions. * * The \"IEM_MEM\" log group covers most of memory related details logging, * except for errors and exceptions: * - Level 1 (Log) : Reads. * - Level 2 (Log2) : Read fallbacks. * - Level 3 (Log3) : MemMap read. * - Level 4 (Log4) : MemMap read fallbacks. * - Level 5 (Log5) : Writes * - Level 6 (Log6) : Write fallbacks. * - Level 7 (Log7) : MemMap writes and read-writes. * - Level 8 (Log8) : MemMap write and read-write fallbacks. * - Level 9 (Log9) : Stack reads. * - Level 10 (Log10): Stack read fallbacks. * - Level 11 (Log11): Stack writes. * - Level 12 (Log12): Stack write fallbacks. * - Flow (LogFlow) : * * The SVM (AMD-V) and VMX (VT-x) code has the following assignments: * - Level 1 (Log) : Errors and other major events. * - Flow (LogFlow) : Misc flow stuff (cleanup?) * - Level 2 (Log2) : VM exits. * * The syscall logging level assignments: * - Level 1: DOS and BIOS. * - Level 2: Windows 3.x * - Level 3: Linux. */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP LOG_GROUP_IEM #define VMCPU_INCL_CPUM_GST_CTX #ifdef IN_RING0 # define VBOX_VMM_TARGET_X86 #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef VBOX_WITH_NESTED_HWVIRT_SVM # include # include #endif #ifdef VBOX_WITH_NESTED_HWVIRT_VMX # include #endif #include #include #include #include "IEMInternal.h" #include #include #include #include #include #include #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) # include #elif defined(RT_ARCH_ARM64) || defined(RT_ARCH_ARM32) # include #endif #include #include #include #include "IEMInline.h" #include "IEMInlineExec.h" #ifdef VBOX_VMM_TARGET_X86 # include "target-x86/IEMInline-x86.h" # include "target-x86/IEMInlineDecode-x86.h" # include "target-x86/IEMInlineExec-x86.h" #elif defined(VBOX_VMM_TARGET_ARMV8) # include "target-armv8/IEMInline-armv8.h" # include "target-armv8/IEMAllIntprA64Tables-armv8.h" # include "target-armv8/IEMInlineExec-armv8.h" #endif /** * Initializes the decoder state. * * iemReInitDecoder is mostly a copy of this function. * * @param pVCpu The cross context virtual CPU structure of the * calling thread. * @param fExecOpts Optional execution flags: * - IEM_F_BYPASS_HANDLERS * - IEM_F_X86_DISREGARD_LOCK */ DECLINLINE(void) iemInitDecoder(PVMCPUCC pVCpu, uint32_t fExecOpts) { IEM_CTX_ASSERT(pVCpu, IEM_CPUMCTX_EXTRN_MUST_MASK); Assert(!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_IEM)); #ifdef VBOX_STRICT iemInitDecoderStrictTarget(pVCpu); #endif /* Execution state: */ uint32_t fExec; pVCpu->iem.s.fExec = fExec = iemCalcExecFlags(pVCpu) | fExecOpts; /* Decoder state: */ #ifdef VBOX_VMM_TARGET_X86 pVCpu->iem.s.enmDefAddrMode = fExec & IEM_F_MODE_X86_CPUMODE_MASK; /** @todo check if this is correct... */ pVCpu->iem.s.enmEffAddrMode = fExec & IEM_F_MODE_X86_CPUMODE_MASK; if ((fExec & IEM_F_MODE_X86_CPUMODE_MASK) != IEMMODE_64BIT) { pVCpu->iem.s.enmDefOpSize = fExec & IEM_F_MODE_X86_CPUMODE_MASK; /** @todo check if this is correct... */ pVCpu->iem.s.enmEffOpSize = fExec & IEM_F_MODE_X86_CPUMODE_MASK; } else { pVCpu->iem.s.enmDefOpSize = IEMMODE_32BIT; pVCpu->iem.s.enmEffOpSize = IEMMODE_32BIT; } pVCpu->iem.s.fPrefixes = 0; pVCpu->iem.s.uRexReg = 0; pVCpu->iem.s.uRexB = 0; pVCpu->iem.s.uRexIndex = 0; pVCpu->iem.s.idxPrefix = 0; pVCpu->iem.s.uVex3rdReg = 0; pVCpu->iem.s.uVexLength = 0; pVCpu->iem.s.fEvexStuff = 0; pVCpu->iem.s.iEffSeg = X86_SREG_DS; pVCpu->iem.s.offModRm = 0; #endif /* VBOX_VMM_TARGET_X86 */ #ifdef IEM_WITH_CODE_TLB pVCpu->iem.s.pbInstrBuf = NULL; pVCpu->iem.s.offInstrNextByte = 0; # ifdef VBOX_VMM_TARGET_X86 pVCpu->iem.s.offCurInstrStart = 0; # endif # ifdef IEM_WITH_CODE_TLB_AND_OPCODE_BUF pVCpu->iem.s.offOpcode = 0; # endif # ifdef VBOX_STRICT pVCpu->iem.s.GCPhysInstrBuf = NIL_RTGCPHYS; # ifdef VBOX_VMM_TARGET_X86 pVCpu->iem.s.cbInstrBuf = UINT16_MAX; # endif pVCpu->iem.s.cbInstrBufTotal = UINT16_MAX; pVCpu->iem.s.uInstrBufPc = UINT64_C(0xc0ffc0ffcff0c0ff); # endif #else /* !IEM_WITH_CODE_TLB */ pVCpu->iem.s.offOpcode = 0; pVCpu->iem.s.cbOpcode = 0; #endif /* !IEM_WITH_CODE_TLB */ pVCpu->iem.s.cActiveMappings = 0; pVCpu->iem.s.iNextMapping = 0; pVCpu->iem.s.rcPassUp = VINF_SUCCESS; #ifdef DBGFTRACE_ENABLED iemInitDecoderTraceTargetPc(pVCpu, fExec); #endif } /** * Reinitializes the decoder state 2nd+ loop of IEMExecLots. * * This is mostly a copy of iemInitDecoder. * * @param pVCpu The cross context virtual CPU structure of the calling EMT. */ DECLINLINE(void) iemReInitDecoder(PVMCPUCC pVCpu) { Assert(!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_IEM)); #ifdef VBOX_STRICT iemInitDecoderStrictTarget(pVCpu); #endif /* ASSUMES: Anyone changing CPU state affecting the fExec bits will update them! */ AssertMsg((pVCpu->iem.s.fExec & ~IEM_F_USER_OPTS) == iemCalcExecFlags(pVCpu), ("fExec=%#x iemCalcExecModeFlags=%#x\n", pVCpu->iem.s.fExec, iemCalcExecFlags(pVCpu))); #ifdef VBOX_VMM_TARGET_X86 IEMMODE const enmMode = IEM_GET_CPU_MODE(pVCpu); pVCpu->iem.s.enmDefAddrMode = enmMode; /** @todo check if this is correct... */ pVCpu->iem.s.enmEffAddrMode = enmMode; if (enmMode != IEMMODE_64BIT) { pVCpu->iem.s.enmDefOpSize = enmMode; /** @todo check if this is correct... */ pVCpu->iem.s.enmEffOpSize = enmMode; } else { pVCpu->iem.s.enmDefOpSize = IEMMODE_32BIT; pVCpu->iem.s.enmEffOpSize = IEMMODE_32BIT; } pVCpu->iem.s.fPrefixes = 0; pVCpu->iem.s.uRexReg = 0; pVCpu->iem.s.uRexB = 0; pVCpu->iem.s.uRexIndex = 0; pVCpu->iem.s.idxPrefix = 0; pVCpu->iem.s.uVex3rdReg = 0; pVCpu->iem.s.uVexLength = 0; pVCpu->iem.s.fEvexStuff = 0; pVCpu->iem.s.iEffSeg = X86_SREG_DS; pVCpu->iem.s.offModRm = 0; #endif #ifdef IEM_WITH_CODE_TLB if (pVCpu->iem.s.pbInstrBuf) { # ifdef VBOX_VMM_TARGET_X86 uint64_t off = (enmMode == IEMMODE_64BIT ? pVCpu->cpum.GstCtx.rip : pVCpu->cpum.GstCtx.eip + (uint32_t)pVCpu->cpum.GstCtx.cs.u64Base) - pVCpu->iem.s.uInstrBufPc; if (off < pVCpu->iem.s.cbInstrBufTotal) # elif defined(VBOX_VMM_TARGET_ARMV8) uint64_t const off = pVCpu->cpum.GstCtx.Pc.u64 - pVCpu->iem.s.uInstrBufPc; if (off + sizeof(uint32_t) <= pVCpu->iem.s.cbInstrBufTotal) # endif { pVCpu->iem.s.offInstrNextByte = (uint32_t)off; # ifdef VBOX_VMM_TARGET_X86 pVCpu->iem.s.offCurInstrStart = (uint16_t)off; if ((uint16_t)off + 15 <= pVCpu->iem.s.cbInstrBufTotal) pVCpu->iem.s.cbInstrBuf = (uint16_t)off + 15; else pVCpu->iem.s.cbInstrBuf = pVCpu->iem.s.cbInstrBufTotal; # endif } else { pVCpu->iem.s.pbInstrBuf = NULL; pVCpu->iem.s.offInstrNextByte = 0; # ifdef VBOX_VMM_TARGET_X86 pVCpu->iem.s.offCurInstrStart = 0; pVCpu->iem.s.cbInstrBuf = 0; # endif pVCpu->iem.s.cbInstrBufTotal = 0; pVCpu->iem.s.GCPhysInstrBuf = NIL_RTGCPHYS; } } else { pVCpu->iem.s.offInstrNextByte = 0; # ifdef VBOX_VMM_TARGET_X86 pVCpu->iem.s.offCurInstrStart = 0; pVCpu->iem.s.cbInstrBuf = 0; # endif pVCpu->iem.s.cbInstrBufTotal = 0; # ifdef VBOX_STRICT pVCpu->iem.s.GCPhysInstrBuf = NIL_RTGCPHYS; # endif } # ifdef IEM_WITH_CODE_TLB_AND_OPCODE_BUF pVCpu->iem.s.offOpcode = 0; # endif #else /* !IEM_WITH_CODE_TLB */ pVCpu->iem.s.cbOpcode = 0; pVCpu->iem.s.offOpcode = 0; #endif /* !IEM_WITH_CODE_TLB */ Assert(pVCpu->iem.s.cActiveMappings == 0); pVCpu->iem.s.iNextMapping = 0; Assert(pVCpu->iem.s.rcPassUp == VINF_SUCCESS); Assert(!(pVCpu->iem.s.fExec & IEM_F_BYPASS_HANDLERS)); #ifdef DBGFTRACE_ENABLED iemInitDecoderTraceTargetPc(pVCpu, pVCpu->iem.s.fExec); #endif } /** * Prefetch opcodes the first time when starting executing. * * @returns Strict VBox status code. * @param pVCpu The cross context virtual CPU structure of the * calling thread. * @param fExecOpts Optional execution flags: * - IEM_F_BYPASS_HANDLERS * - IEM_F_X86_DISREGARD_LOCK */ DECLINLINE(VBOXSTRICTRC) iemInitDecoderAndPrefetchOpcodes(PVMCPUCC pVCpu, uint32_t fExecOpts) RT_NOEXCEPT { iemInitDecoder(pVCpu, fExecOpts); #ifndef IEM_WITH_CODE_TLB return iemOpcodeFetchPrefetch(pVCpu); #else return VINF_SUCCESS; #endif } #ifdef LOG_ENABLED /** * Logs the current instruction. * @param pVCpu The cross context virtual CPU structure of the calling EMT. * @param pszFunction The IEM function doing the execution. */ static void iemLogCurInstr(PVMCPUCC pVCpu, const char *pszFunction) RT_NOEXCEPT { # ifdef IN_RING3 if (LogIs2Enabled()) { char szInstr[256]; uint32_t cbInstr = 0; DBGFR3DisasInstrEx(pVCpu->pVMR3->pUVM, pVCpu->idCpu, 0, 0, DBGF_DISAS_FLAGS_CURRENT_GUEST | DBGF_DISAS_FLAGS_DEFAULT_MODE, szInstr, sizeof(szInstr), &cbInstr); # ifdef VBOX_VMM_TARGET_X86 PCX86FXSTATE pFpuCtx = &pVCpu->cpum.GstCtx.XState.x87; Log2(("**** %s fExec=%x\n" " eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\n" " eip=%08x esp=%08x ebp=%08x iopl=%d tr=%04x\n" " cs=%04x ss=%04x ds=%04x es=%04x fs=%04x gs=%04x efl=%08x\n" " fsw=%04x fcw=%04x ftw=%02x mxcsr=%04x/%04x\n" " %s\n" , pszFunction, pVCpu->iem.s.fExec, pVCpu->cpum.GstCtx.eax, pVCpu->cpum.GstCtx.ebx, pVCpu->cpum.GstCtx.ecx, pVCpu->cpum.GstCtx.edx, pVCpu->cpum.GstCtx.esi, pVCpu->cpum.GstCtx.edi, pVCpu->cpum.GstCtx.eip, pVCpu->cpum.GstCtx.esp, pVCpu->cpum.GstCtx.ebp, pVCpu->cpum.GstCtx.eflags.Bits.u2IOPL, pVCpu->cpum.GstCtx.tr.Sel, pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.ss.Sel, pVCpu->cpum.GstCtx.ds.Sel, pVCpu->cpum.GstCtx.es.Sel, pVCpu->cpum.GstCtx.fs.Sel, pVCpu->cpum.GstCtx.gs.Sel, pVCpu->cpum.GstCtx.eflags.u, pFpuCtx->FSW, pFpuCtx->FCW, pFpuCtx->FTW, pFpuCtx->MXCSR, pFpuCtx->MXCSR_MASK, szInstr)); # elif defined(VBOX_VMM_TARGET_ARMV8) Log2(("**** %s fExec=%x\n" " x0=%016RX64 x1=%016RX64 x2=%016RX64 x3=%016RX64\n" " x4=%016RX64 x5=%016RX64 x6=%016RX64 x7=%016RX64\n" " x8=%016RX64 x9=%016RX64 x10=%016RX64 x11=%016RX64\n" " x12=%016RX64 x13=%016RX64 x14=%016RX64 x15=%016RX64\n" " x16=%016RX64 x17=%016RX64 x18=%016RX64 x19=%016RX64\n" " x20=%016RX64 x21=%016RX64 x22=%016RX64 x23=%016RX64\n" " x24=%016RX64 x25=%016RX64 x26=%016RX64 x27=%016RX64\n" " x28=%016RX64 bp=%016RX64 lr=%016RX64 sp=%016RX64\n" " pc=%016RX64 psr=%08RX64 EL%u\n" " %s\n" , pszFunction, pVCpu->iem.s.fExec, pVCpu->cpum.GstCtx.aGRegs[0], pVCpu->cpum.GstCtx.aGRegs[1], pVCpu->cpum.GstCtx.aGRegs[2], pVCpu->cpum.GstCtx.aGRegs[3], pVCpu->cpum.GstCtx.aGRegs[4], pVCpu->cpum.GstCtx.aGRegs[5], pVCpu->cpum.GstCtx.aGRegs[6], pVCpu->cpum.GstCtx.aGRegs[7], pVCpu->cpum.GstCtx.aGRegs[8], pVCpu->cpum.GstCtx.aGRegs[9], pVCpu->cpum.GstCtx.aGRegs[10], pVCpu->cpum.GstCtx.aGRegs[11], pVCpu->cpum.GstCtx.aGRegs[12], pVCpu->cpum.GstCtx.aGRegs[13], pVCpu->cpum.GstCtx.aGRegs[14], pVCpu->cpum.GstCtx.aGRegs[15], pVCpu->cpum.GstCtx.aGRegs[16], pVCpu->cpum.GstCtx.aGRegs[17], pVCpu->cpum.GstCtx.aGRegs[18], pVCpu->cpum.GstCtx.aGRegs[19], pVCpu->cpum.GstCtx.aGRegs[20], pVCpu->cpum.GstCtx.aGRegs[21], pVCpu->cpum.GstCtx.aGRegs[22], pVCpu->cpum.GstCtx.aGRegs[23], pVCpu->cpum.GstCtx.aGRegs[24], pVCpu->cpum.GstCtx.aGRegs[25], pVCpu->cpum.GstCtx.aGRegs[26], pVCpu->cpum.GstCtx.aGRegs[27], pVCpu->cpum.GstCtx.aGRegs[28], pVCpu->cpum.GstCtx.aGRegs[29], pVCpu->cpum.GstCtx.aGRegs[30], pVCpu->cpum.GstCtx.aSpReg[IEM_F_MODE_ARM_GET_EL(pVCpu->iem.s.fExec) > 0], pVCpu->cpum.GstCtx.Pc, pVCpu->cpum.GstCtx.fPState, IEM_F_MODE_ARM_GET_EL(pVCpu->iem.s.fExec), szInstr)); # else # error "port me" # endif /* This stuff sucks atm. as it fills the log with MSRs. */ //if (LogIs3Enabled()) // DBGFR3InfoEx(pVCpu->pVMR3->pUVM, pVCpu->idCpu, "cpumguest", "verbose", NULL); return; } # endif # ifdef VBOX_VMM_TARGET_X86 LogFlow(("%s: cs:rip=%04x:%08RX64 ss:rsp=%04x:%08RX64 EFL=%06x\n", pszFunction, pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, pVCpu->cpum.GstCtx.ss.Sel, pVCpu->cpum.GstCtx.rsp, pVCpu->cpum.GstCtx.eflags.u)); # define LOGFLOW_REG_STATE_EX(a_pszName, a_szExtraFmt, ...) \ LogFlow(("%s: cs:rip=%04x:%08RX64 ss:rsp=%04x:%08RX64 EFL=%06x" a_szExtraFmt "\n", \ (a_pszName), pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, pVCpu->cpum.GstCtx.ss.Sel, pVCpu->cpum.GstCtx.rsp, \ pVCpu->cpum.GstCtx.eflags.u, __VA_ARGS__)) # elif defined(VBOX_VMM_TARGET_ARMV8) LogFlow(("%s: pc=%08RX64 lr=%08RX64 sp=%08RX64 psr=%08RX64 EL%u\n", pszFunction, pVCpu->cpum.GstCtx.Pc, pVCpu->cpum.GstCtx.aGRegs[ARMV8_A64_REG_LR], pVCpu->cpum.GstCtx.aSpReg[IEM_F_MODE_ARM_GET_EL(pVCpu->iem.s.fExec) > 0], pVCpu->cpum.GstCtx.fPState, IEM_F_MODE_ARM_GET_EL(pVCpu->iem.s.fExec) )); # define LOGFLOW_REG_STATE_EX(a_pszName, a_szExtraFmt, ...) \ LogFlow(("%s: pc=%08RX64 lr=%08RX64 sp=%08RX64 psr=%08RX64 EL%u" a_szExtraFmt "\n", \ (a_pszName), pVCpu->cpum.GstCtx.Pc, pVCpu->cpum.GstCtx.aGRegs[ARMV8_A64_REG_LR], \ pVCpu->cpum.GstCtx.aSpReg[IEM_F_MODE_ARM_GET_EL(pVCpu->iem.s.fExec) > 0], pVCpu->cpum.GstCtx.fPState, \ IEM_F_MODE_ARM_GET_EL(pVCpu->iem.s.fExec), __VA_ARGS__)) LOGFLOW_REG_STATE_EX(pszFunction, "",1); # else # error "port me" # endif RT_NOREF_PV(pVCpu); } #endif /* LOG_ENABLED */ #ifdef VBOX_WITH_NESTED_HWVIRT_VMX /** * Deals with VMCPU_FF_VMX_APIC_WRITE, VMCPU_FF_VMX_MTF, VMCPU_FF_VMX_NMI_WINDOW, * VMCPU_FF_VMX_PREEMPT_TIMER and VMCPU_FF_VMX_INT_WINDOW. * * @returns Modified rcStrict. * @param pVCpu The cross context virtual CPU structure of the calling thread. * @param rcStrict The instruction execution status. */ static VBOXSTRICTRC iemHandleNestedInstructionBoundaryFFs(PVMCPUCC pVCpu, VBOXSTRICTRC rcStrict) RT_NOEXCEPT { Assert(CPUMIsGuestInVmxNonRootMode(IEM_GET_CTX(pVCpu))); if (!VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_VMX_APIC_WRITE | VMCPU_FF_VMX_MTF)) { /* VMX preemption timer takes priority over NMI-window exits. */ if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_VMX_PREEMPT_TIMER)) { rcStrict = iemVmxVmexitPreemptTimer(pVCpu); Assert(!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_VMX_PREEMPT_TIMER)); } /* * Check remaining intercepts. * * NMI-window and Interrupt-window VM-exits. * Interrupt shadow (block-by-STI and Mov SS) inhibits interrupts and may also block NMIs. * Event injection during VM-entry takes priority over NMI-window and interrupt-window VM-exits. * * See Intel spec. 26.7.6 "NMI-Window Exiting". * See Intel spec. 26.7.5 "Interrupt-Window Exiting and Virtual-Interrupt Delivery". */ else if ( VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_VMX_NMI_WINDOW | VMCPU_FF_VMX_INT_WINDOW) && !CPUMIsInInterruptShadow(&pVCpu->cpum.GstCtx) && !TRPMHasTrap(pVCpu)) { Assert(CPUMIsGuestVmxInterceptEvents(&pVCpu->cpum.GstCtx)); if ( VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_VMX_NMI_WINDOW) && CPUMIsGuestVmxVirtNmiBlocking(&pVCpu->cpum.GstCtx)) { rcStrict = iemVmxVmexit(pVCpu, VMX_EXIT_NMI_WINDOW, 0 /* u64ExitQual */); Assert(!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_VMX_NMI_WINDOW)); } else if ( VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_VMX_INT_WINDOW) && CPUMIsGuestVmxVirtIntrEnabled(&pVCpu->cpum.GstCtx)) { rcStrict = iemVmxVmexit(pVCpu, VMX_EXIT_INT_WINDOW, 0 /* u64ExitQual */); Assert(!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_VMX_INT_WINDOW)); } } } /* TPR-below threshold/APIC write has the highest priority. */ else if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_VMX_APIC_WRITE)) { rcStrict = iemVmxApicWriteEmulation(pVCpu); Assert(!CPUMIsInInterruptShadow(&pVCpu->cpum.GstCtx)); Assert(!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_VMX_APIC_WRITE)); } /* MTF takes priority over VMX-preemption timer. */ else { rcStrict = iemVmxVmexit(pVCpu, VMX_EXIT_MTF, 0 /* u64ExitQual */); Assert(!CPUMIsInInterruptShadow(&pVCpu->cpum.GstCtx)); Assert(!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_VMX_MTF)); } return rcStrict; } #endif /* VBOX_WITH_NESTED_HWVIRT_VMX */ /** * The actual code execution bits of IEMExecOne, IEMExecOneWithPrefetchedByPC, * IEMExecOneBypass and friends. * * Similar code is found in IEMExecLots. * * @return Strict VBox status code. * @param pVCpu The cross context virtual CPU structure of the * calling EMT. * @param pszFunction The calling function name. * @tparam a_fExecuteInhibit X86: If set, execute the instruction following * CLI, POP SS and MOV SS,GR. */ template DECLINLINE(VBOXSTRICTRC) iemExecOneInner(PVMCPUCC pVCpu, const char *pszFunction) { AssertMsg(pVCpu->iem.s.aMemMappings[0].fAccess == IEM_ACCESS_INVALID, ("0: %#x %RGp\n", pVCpu->iem.s.aMemMappings[0].fAccess, pVCpu->iem.s.aMemBbMappings[0].GCPhysFirst)); AssertMsg(pVCpu->iem.s.aMemMappings[1].fAccess == IEM_ACCESS_INVALID, ("1: %#x %RGp\n", pVCpu->iem.s.aMemMappings[1].fAccess, pVCpu->iem.s.aMemBbMappings[1].GCPhysFirst)); AssertMsg(pVCpu->iem.s.aMemMappings[2].fAccess == IEM_ACCESS_INVALID, ("2: %#x %RGp\n", pVCpu->iem.s.aMemMappings[2].fAccess, pVCpu->iem.s.aMemBbMappings[2].GCPhysFirst)); RT_NOREF_PV(pszFunction); VBOXSTRICTRC rcStrict; IEM_TRY_SETJMP(pVCpu, rcStrict) { rcStrict = iemExecDecodeAndInterpretTargetInstruction(pVCpu); } IEM_CATCH_LONGJMP_BEGIN(pVCpu, rcStrict); { pVCpu->iem.s.cLongJumps++; } IEM_CATCH_LONGJMP_END(pVCpu); if (rcStrict == VINF_SUCCESS) pVCpu->iem.s.cInstructions++; if (pVCpu->iem.s.cActiveMappings > 0) { Assert(rcStrict != VINF_SUCCESS); iemMemRollback(pVCpu); } AssertMsg(pVCpu->iem.s.aMemMappings[0].fAccess == IEM_ACCESS_INVALID, ("0: %#x %RGp\n", pVCpu->iem.s.aMemMappings[0].fAccess, pVCpu->iem.s.aMemBbMappings[0].GCPhysFirst)); AssertMsg(pVCpu->iem.s.aMemMappings[1].fAccess == IEM_ACCESS_INVALID, ("1: %#x %RGp\n", pVCpu->iem.s.aMemMappings[1].fAccess, pVCpu->iem.s.aMemBbMappings[1].GCPhysFirst)); AssertMsg(pVCpu->iem.s.aMemMappings[2].fAccess == IEM_ACCESS_INVALID, ("2: %#x %RGp\n", pVCpu->iem.s.aMemMappings[2].fAccess, pVCpu->iem.s.aMemBbMappings[2].GCPhysFirst)); //#ifdef DEBUG // AssertMsg(IEM_GET_INSTR_LEN(pVCpu) == cbInstr || rcStrict != VINF_SUCCESS, ("%u %u\n", IEM_GET_INSTR_LEN(pVCpu), cbInstr)); //#endif #ifdef VBOX_WITH_NESTED_HWVIRT_VMX /* * Perform any VMX nested-guest instruction boundary actions. * * If any of these causes a VM-exit, we must skip executing the next * instruction (would run into stale page tables). A VM-exit makes sure * there is no interrupt-inhibition, so that should ensure we don't go * to try execute the next instruction. Clearing a_fExecuteInhibit is * problematic because of the setjmp/longjmp clobbering above. */ if ( !VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_VMX_APIC_WRITE | VMCPU_FF_VMX_MTF | VMCPU_FF_VMX_PREEMPT_TIMER | VMCPU_FF_VMX_INT_WINDOW | VMCPU_FF_VMX_NMI_WINDOW) || rcStrict != VINF_SUCCESS) { /* likely */ } else rcStrict = iemHandleNestedInstructionBoundaryFFs(pVCpu, rcStrict); #endif #ifdef VBOX_VMM_TARGET_X86 /* Execute the next instruction as well if a cli, pop ss or mov ss, Gr has just completed successfully. */ if RT_CONSTEXPR_IF(a_fExecuteInhibit) { if ( rcStrict == VINF_SUCCESS && CPUMIsInInterruptShadow(&pVCpu->cpum.GstCtx)) { rcStrict = iemInitDecoderAndPrefetchOpcodes(pVCpu, pVCpu->iem.s.fExec & (IEM_F_BYPASS_HANDLERS | IEM_F_X86_DISREGARD_LOCK)); if (rcStrict == VINF_SUCCESS) { # ifdef LOG_ENABLED iemLogCurInstr(pVCpu, pszFunction); # endif IEM_TRY_SETJMP_AGAIN(pVCpu, rcStrict) { rcStrict = iemExecDecodeAndInterpretTargetInstruction(pVCpu); } IEM_CATCH_LONGJMP_BEGIN(pVCpu, rcStrict); { pVCpu->iem.s.cLongJumps++; } IEM_CATCH_LONGJMP_END(pVCpu); if (rcStrict == VINF_SUCCESS) { pVCpu->iem.s.cInstructions++; # ifdef VBOX_WITH_NESTED_HWVIRT_VMX if (!VMCPU_FF_IS_ANY_SET(pVCpu, VMCPU_FF_VMX_APIC_WRITE | VMCPU_FF_VMX_MTF | VMCPU_FF_VMX_PREEMPT_TIMER | VMCPU_FF_VMX_INT_WINDOW | VMCPU_FF_VMX_NMI_WINDOW)) { /* likely */ } else rcStrict = iemHandleNestedInstructionBoundaryFFs(pVCpu, rcStrict); # endif } if (pVCpu->iem.s.cActiveMappings > 0) { Assert(rcStrict != VINF_SUCCESS); iemMemRollback(pVCpu); } AssertMsg(pVCpu->iem.s.aMemMappings[0].fAccess == IEM_ACCESS_INVALID, ("0: %#x %RGp\n", pVCpu->iem.s.aMemMappings[0].fAccess, pVCpu->iem.s.aMemBbMappings[0].GCPhysFirst)); AssertMsg(pVCpu->iem.s.aMemMappings[1].fAccess == IEM_ACCESS_INVALID, ("1: %#x %RGp\n", pVCpu->iem.s.aMemMappings[1].fAccess, pVCpu->iem.s.aMemBbMappings[1].GCPhysFirst)); AssertMsg(pVCpu->iem.s.aMemMappings[2].fAccess == IEM_ACCESS_INVALID, ("2: %#x %RGp\n", pVCpu->iem.s.aMemMappings[2].fAccess, pVCpu->iem.s.aMemBbMappings[2].GCPhysFirst)); } else if (pVCpu->iem.s.cActiveMappings > 0) iemMemRollback(pVCpu); /** @todo drop this after we bake this change into RIP advancing. */ CPUMClearInterruptShadow(&pVCpu->cpum.GstCtx); /* hope this is correct for all exceptional cases... */ } } #endif /* VBOX_VMM_TARGET_X86 */ /* * Return value fiddling, statistics and sanity assertions. */ rcStrict = iemExecStatusCodeFiddling(pVCpu, rcStrict); #ifdef VBOX_STRICT iemInitExecTailStrictTarget(pVCpu); #endif return rcStrict; } /** * Execute one instruction. * * @return Strict VBox status code. * @param pVCpu The cross context virtual CPU structure of the calling EMT. */ VMM_INT_DECL(VBOXSTRICTRC) IEMExecOne(PVMCPUCC pVCpu) { AssertCompile(sizeof(pVCpu->iem.s) <= sizeof(pVCpu->iem.padding)); /* (tstVMStruct can't do it's job w/o instruction stats) */ #ifdef LOG_ENABLED iemLogCurInstr(pVCpu, "IEMExecOne"); #endif /* * Do the decoding and emulation. */ VBOXSTRICTRC rcStrict = iemInitDecoderAndPrefetchOpcodes(pVCpu, 0 /*fExecOpts*/); if (rcStrict == VINF_SUCCESS) rcStrict = iemExecOneInner(pVCpu, "IEMExecOne"); else if (pVCpu->iem.s.cActiveMappings > 0) iemMemRollback(pVCpu); #ifdef LOG_ENABLED if (rcStrict != VINF_SUCCESS) LOGFLOW_REG_STATE_EX("IEMExecOne", " - rcStrict=%Rrc", VBOXSTRICTRC_VAL(rcStrict)); #endif return rcStrict; } VMM_INT_DECL(VBOXSTRICTRC) IEMExecOneWithPrefetchedByPC(PVMCPUCC pVCpu, uint64_t OpcodeBytesPC, const void *pvOpcodeBytes, size_t cbOpcodeBytes) { VBOXSTRICTRC rcStrict; if ( cbOpcodeBytes && iemRegGetPC(pVCpu) == OpcodeBytesPC) { iemInitDecoder(pVCpu, 0 /*fExecOpts*/); #ifdef IEM_WITH_CODE_TLB pVCpu->iem.s.uInstrBufPc = OpcodeBytesPC; pVCpu->iem.s.pbInstrBuf = (uint8_t const *)pvOpcodeBytes; pVCpu->iem.s.cbInstrBufTotal = (uint16_t)RT_MIN(X86_PAGE_SIZE, cbOpcodeBytes); # ifdef VBOX_VMM_TARGET_X86 pVCpu->iem.s.offCurInstrStart = 0; pVCpu->iem.s.offInstrNextByte = 0; # endif pVCpu->iem.s.GCPhysInstrBuf = NIL_RTGCPHYS; #else pVCpu->iem.s.cbOpcode = (uint8_t)RT_MIN(cbOpcodeBytes, sizeof(pVCpu->iem.s.abOpcode)); memcpy(pVCpu->iem.s.abOpcode, pvOpcodeBytes, pVCpu->iem.s.cbOpcode); #endif rcStrict = VINF_SUCCESS; } else rcStrict = iemInitDecoderAndPrefetchOpcodes(pVCpu, 0 /*fExecOpts*/); if (rcStrict == VINF_SUCCESS) rcStrict = iemExecOneInner(pVCpu, "IEMExecOneWithPrefetchedByPC"); else if (pVCpu->iem.s.cActiveMappings > 0) iemMemRollback(pVCpu); return rcStrict; } VMM_INT_DECL(VBOXSTRICTRC) IEMExecOneBypass(PVMCPUCC pVCpu) { VBOXSTRICTRC rcStrict = iemInitDecoderAndPrefetchOpcodes(pVCpu, IEM_F_BYPASS_HANDLERS); if (rcStrict == VINF_SUCCESS) rcStrict = iemExecOneInner(pVCpu, "IEMExecOneBypass"); else if (pVCpu->iem.s.cActiveMappings > 0) iemMemRollback(pVCpu); return rcStrict; } VMM_INT_DECL(VBOXSTRICTRC) IEMExecOneBypassWithPrefetchedByPC(PVMCPUCC pVCpu, uint64_t OpcodeBytesPC, const void *pvOpcodeBytes, size_t cbOpcodeBytes) { VBOXSTRICTRC rcStrict; if ( cbOpcodeBytes && iemRegGetPC(pVCpu) == OpcodeBytesPC) { iemInitDecoder(pVCpu, IEM_F_BYPASS_HANDLERS); #ifdef IEM_WITH_CODE_TLB pVCpu->iem.s.uInstrBufPc = OpcodeBytesPC; pVCpu->iem.s.pbInstrBuf = (uint8_t const *)pvOpcodeBytes; pVCpu->iem.s.cbInstrBufTotal = (uint16_t)RT_MIN(X86_PAGE_SIZE, cbOpcodeBytes); # ifdef VBOX_VMM_TARGET_X86 pVCpu->iem.s.offCurInstrStart = 0; pVCpu->iem.s.offInstrNextByte = 0; # endif pVCpu->iem.s.GCPhysInstrBuf = NIL_RTGCPHYS; #else pVCpu->iem.s.cbOpcode = (uint8_t)RT_MIN(cbOpcodeBytes, sizeof(pVCpu->iem.s.abOpcode)); memcpy(pVCpu->iem.s.abOpcode, pvOpcodeBytes, pVCpu->iem.s.cbOpcode); #endif rcStrict = VINF_SUCCESS; } else rcStrict = iemInitDecoderAndPrefetchOpcodes(pVCpu, IEM_F_BYPASS_HANDLERS); if (rcStrict == VINF_SUCCESS) rcStrict = iemExecOneInner(pVCpu, "IEMExecOneBypassWithPrefetchedByPC"); else if (pVCpu->iem.s.cActiveMappings > 0) iemMemRollback(pVCpu); return rcStrict; } /** * For handling split cacheline lock operations when the host has split-lock * detection enabled. * * This will cause the interpreter to disregard the lock prefix and implicit * locking (xchg). * * @returns Strict VBox status code. * @param pVCpu The cross context virtual CPU structure of the calling EMT. */ VMM_INT_DECL(VBOXSTRICTRC) IEMExecOneIgnoreLock(PVMCPUCC pVCpu) { /* * Do the decoding and emulation. */ VBOXSTRICTRC rcStrict = iemInitDecoderAndPrefetchOpcodes(pVCpu, IEM_F_X86_DISREGARD_LOCK); if (rcStrict == VINF_SUCCESS) rcStrict = iemExecOneInner(pVCpu, "IEMExecOneIgnoreLock"); else if (pVCpu->iem.s.cActiveMappings > 0) iemMemRollback(pVCpu); #ifdef LOG_ENABLED if (rcStrict != VINF_SUCCESS) LOGFLOW_REG_STATE_EX("IEMExecOneIgnoreLock", " - rcStrict=%Rrc", VBOXSTRICTRC_VAL(rcStrict)); #endif return rcStrict; } /** * Code common to IEMExecLots and IEMExecRecompilerThreaded that attempts to * inject a pending TRPM trap. */ VBOXSTRICTRC iemExecInjectPendingTrap(PVMCPUCC pVCpu) { Assert(TRPMHasTrap(pVCpu)); #ifdef VBOX_VMM_TARGET_X86 if ( !CPUMIsInInterruptShadow(&pVCpu->cpum.GstCtx) && !CPUMAreInterruptsInhibitedByNmi(&pVCpu->cpum.GstCtx)) { /** @todo Can we centralize this under CPUMCanInjectInterrupt()? */ # if defined(VBOX_WITH_NESTED_HWVIRT_SVM) || defined(VBOX_WITH_NESTED_HWVIRT_VMX) bool fIntrEnabled = CPUMGetGuestGif(&pVCpu->cpum.GstCtx); if (fIntrEnabled) { if (!CPUMIsGuestInNestedHwvirtMode(IEM_GET_CTX(pVCpu))) fIntrEnabled = pVCpu->cpum.GstCtx.eflags.Bits.u1IF; else if (CPUMIsGuestInVmxNonRootMode(IEM_GET_CTX(pVCpu))) fIntrEnabled = CPUMIsGuestVmxPhysIntrEnabled(IEM_GET_CTX(pVCpu)); else { Assert(CPUMIsGuestInSvmNestedHwVirtMode(IEM_GET_CTX(pVCpu))); fIntrEnabled = CPUMIsGuestSvmPhysIntrEnabled(pVCpu, IEM_GET_CTX(pVCpu)); } } # else bool fIntrEnabled = pVCpu->cpum.GstCtx.eflags.Bits.u1IF; # endif if (fIntrEnabled) { uint8_t u8TrapNo; TRPMEVENT enmType; uint32_t uErrCode; RTGCPTR uCr2; int rc2 = TRPMQueryTrapAll(pVCpu, &u8TrapNo, &enmType, &uErrCode, &uCr2, NULL /*pu8InstLen*/, NULL /*fIcebp*/); AssertRC(rc2); Assert(enmType == TRPM_HARDWARE_INT); VBOXSTRICTRC rcStrict = IEMInjectTrap(pVCpu, u8TrapNo, enmType, (uint16_t)uErrCode, uCr2, 0 /*cbInstr*/); TRPMResetTrap(pVCpu); # if defined(VBOX_WITH_NESTED_HWVIRT_SVM) || defined(VBOX_WITH_NESTED_HWVIRT_VMX) /* Injecting an event may cause a VM-exit. */ if ( rcStrict != VINF_SUCCESS && rcStrict != VINF_IEM_RAISED_XCPT) return iemExecStatusCodeFiddling(pVCpu, rcStrict); # else NOREF(rcStrict); # endif } } return VINF_SUCCESS; #else /* !VBOX_VMM_TARGET_X86 */ RT_NOREF(pVCpu); AssertFailedReturn(VERR_NOT_IMPLEMENTED); #endif /* !VBOX_VMM_TARGET_X86 */ } VMM_INT_DECL(VBOXSTRICTRC) IEMExecLots(PVMCPUCC pVCpu, uint32_t cMaxInstructions, uint32_t cPollRate, uint32_t *pcInstructions) { uint32_t const cInstructionsAtStart = pVCpu->iem.s.cInstructions; AssertMsg(RT_IS_POWER_OF_TWO(cPollRate + 1), ("%#x\n", cPollRate)); Assert(cMaxInstructions > 0); /* * See if there is an interrupt pending in TRPM, inject it if we can. */ /** @todo What if we are injecting an exception and not an interrupt? Is that * possible here? For now we assert it is indeed only an interrupt. */ if (!TRPMHasTrap(pVCpu)) { /* likely */ } else { VBOXSTRICTRC rcStrict = iemExecInjectPendingTrap(pVCpu); if (RT_LIKELY(rcStrict == VINF_SUCCESS)) { /*likely */ } else return rcStrict; } /* * Initial decoder init w/ prefetch, then setup setjmp. */ VBOXSTRICTRC rcStrict = iemInitDecoderAndPrefetchOpcodes(pVCpu, 0 /*fExecOpts*/); if (rcStrict == VINF_SUCCESS) { pVCpu->iem.s.cActiveMappings = 0; /** @todo wtf? */ IEM_TRY_SETJMP(pVCpu, rcStrict) { /* * The run loop. We limit ourselves to 4096 instructions right now. */ uint32_t cMaxInstructionsGccStupidity = cMaxInstructions; PVMCC pVM = pVCpu->CTX_SUFF(pVM); for (;;) { /* * Log the state. */ #ifdef LOG_ENABLED iemLogCurInstr(pVCpu, "IEMExecLots"); #endif /* * Do the decoding and emulation. */ rcStrict = iemExecDecodeAndInterpretTargetInstruction(pVCpu); #if defined(VBOX_STRICT) && defined(VBOX_VMM_TARGET_X86) CPUMAssertGuestRFlagsCookie(pVM, pVCpu); #endif if (RT_LIKELY(rcStrict == VINF_SUCCESS)) { Assert(pVCpu->iem.s.cActiveMappings == 0); pVCpu->iem.s.cInstructions++; #ifdef VBOX_WITH_NESTED_HWVIRT_VMX /* Perform any VMX nested-guest instruction boundary actions. */ uint64_t fCpu = pVCpu->fLocalForcedActions; if (!(fCpu & ( VMCPU_FF_VMX_APIC_WRITE | VMCPU_FF_VMX_MTF | VMCPU_FF_VMX_PREEMPT_TIMER | VMCPU_FF_VMX_INT_WINDOW | VMCPU_FF_VMX_NMI_WINDOW))) { /* likely */ } else { rcStrict = iemHandleNestedInstructionBoundaryFFs(pVCpu, rcStrict); if (RT_LIKELY(rcStrict == VINF_SUCCESS)) fCpu = pVCpu->fLocalForcedActions; else { rcStrict = iemExecStatusCodeFiddling(pVCpu, rcStrict); break; } } #endif if (RT_LIKELY(pVCpu->iem.s.rcPassUp == VINF_SUCCESS)) { #ifndef VBOX_WITH_NESTED_HWVIRT_VMX uint64_t fCpu = pVCpu->fLocalForcedActions; #endif fCpu &= VMCPU_FF_ALL_MASK & ~( VMCPU_FF_PGM_SYNC_CR3 | VMCPU_FF_PGM_SYNC_CR3_NON_GLOBAL | VMCPU_FF_TLB_FLUSH | VMCPU_FF_UNHALT ); if (RT_LIKELY( iemExecLoopTargetCheckMaskedCpuFFs(pVCpu, fCpu) && !VM_FF_IS_ANY_SET(pVM, VM_FF_ALL_MASK) )) { if (--cMaxInstructionsGccStupidity > 0) { /* Poll timers every now an then according to the caller's specs. */ if ( (cMaxInstructionsGccStupidity & cPollRate) != 0 || !TMTimerPollBool(pVM, pVCpu)) { Assert(pVCpu->iem.s.cActiveMappings == 0); iemReInitDecoder(pVCpu); continue; } } } } Assert(pVCpu->iem.s.cActiveMappings == 0); } else if (pVCpu->iem.s.cActiveMappings > 0) iemMemRollback(pVCpu); rcStrict = iemExecStatusCodeFiddling(pVCpu, rcStrict); break; } } IEM_CATCH_LONGJMP_BEGIN(pVCpu, rcStrict); { if (pVCpu->iem.s.cActiveMappings > 0) iemMemRollback(pVCpu); #if defined(VBOX_WITH_NESTED_HWVIRT_SVM) || defined(VBOX_WITH_NESTED_HWVIRT_VMX) rcStrict = iemExecStatusCodeFiddling(pVCpu, rcStrict); #endif pVCpu->iem.s.cLongJumps++; } IEM_CATCH_LONGJMP_END(pVCpu); #ifdef VBOX_STRICT iemInitExecTailStrictTarget(pVCpu); #endif } else { if (pVCpu->iem.s.cActiveMappings > 0) iemMemRollback(pVCpu); #if defined(VBOX_WITH_NESTED_HWVIRT_SVM) || defined(VBOX_WITH_NESTED_HWVIRT_VMX) /* * When a nested-guest causes an exception intercept (e.g. #PF) when fetching * code as part of instruction execution, we need this to fix-up VINF_SVM_VMEXIT. */ rcStrict = iemExecStatusCodeFiddling(pVCpu, rcStrict); #endif } /* * Maybe re-enter raw-mode and log. */ #ifdef LOG_ENABLED if (rcStrict != VINF_SUCCESS) LOGFLOW_REG_STATE_EX("IEMExecLots", " - rcStrict=%Rrc", VBOXSTRICTRC_VAL(rcStrict)); #endif if (pcInstructions) *pcInstructions = pVCpu->iem.s.cInstructions - cInstructionsAtStart; return rcStrict; } /** * Interface used by EMExecuteExec, does exit statistics and limits. * * @returns Strict VBox status code. * @param pVCpu The cross context virtual CPU structure. * @param fWillExit To be defined. * @param cMinInstructions Minimum number of instructions to execute before checking for FFs. * @param cMaxInstructions Maximum number of instructions to execute. * @param cMaxInstructionsWithoutExits * The max number of instructions without exits. * @param pStats Where to return statistics. */ VMM_INT_DECL(VBOXSTRICTRC) IEMExecForExits(PVMCPUCC pVCpu, uint32_t fWillExit, uint32_t cMinInstructions, uint32_t cMaxInstructions, uint32_t cMaxInstructionsWithoutExits, PIEMEXECFOREXITSTATS pStats) { NOREF(fWillExit); /** @todo define flexible exit crits */ /* * Initialize return stats. */ pStats->cInstructions = 0; pStats->cExits = 0; pStats->cMaxExitDistance = 0; pStats->cReserved = 0; /* * Initial decoder init w/ prefetch, then setup setjmp. */ VBOXSTRICTRC rcStrict = iemInitDecoderAndPrefetchOpcodes(pVCpu, 0 /*fExecOpts*/); if (rcStrict == VINF_SUCCESS) { pVCpu->iem.s.cActiveMappings = 0; /** @todo wtf?!? */ IEM_TRY_SETJMP(pVCpu, rcStrict) { #ifdef IN_RING0 bool const fCheckPreemptionPending = !RTThreadPreemptIsPossible() || !RTThreadPreemptIsEnabled(NIL_RTTHREAD); #endif uint32_t cInstructionSinceLastExit = 0; /* * The run loop. We limit ourselves to 4096 instructions right now. */ PVM pVM = pVCpu->CTX_SUFF(pVM); for (;;) { /* * Log the state. */ #ifdef LOG_ENABLED iemLogCurInstr(pVCpu, "IEMExecForExits"); #endif /* * Do the decoding and emulation. */ uint32_t const cPotentialExits = pVCpu->iem.s.cPotentialExits; rcStrict = iemExecDecodeAndInterpretTargetInstruction(pVCpu); if ( cPotentialExits != pVCpu->iem.s.cPotentialExits && cInstructionSinceLastExit > 0 /* don't count the first */ ) { pStats->cExits += 1; if (cInstructionSinceLastExit > pStats->cMaxExitDistance) pStats->cMaxExitDistance = cInstructionSinceLastExit; cInstructionSinceLastExit = 0; } if (RT_LIKELY(rcStrict == VINF_SUCCESS)) { Assert(pVCpu->iem.s.cActiveMappings == 0); pVCpu->iem.s.cInstructions++; pStats->cInstructions++; cInstructionSinceLastExit++; #ifdef VBOX_WITH_NESTED_HWVIRT_VMX /* Perform any VMX nested-guest instruction boundary actions. */ uint64_t fCpu = pVCpu->fLocalForcedActions; if (!(fCpu & ( VMCPU_FF_VMX_APIC_WRITE | VMCPU_FF_VMX_MTF | VMCPU_FF_VMX_PREEMPT_TIMER | VMCPU_FF_VMX_INT_WINDOW | VMCPU_FF_VMX_NMI_WINDOW))) { /* likely */ } else { rcStrict = iemHandleNestedInstructionBoundaryFFs(pVCpu, rcStrict); if (RT_LIKELY(rcStrict == VINF_SUCCESS)) fCpu = pVCpu->fLocalForcedActions; else { rcStrict = iemExecStatusCodeFiddling(pVCpu, rcStrict); break; } } #endif if (RT_LIKELY(pVCpu->iem.s.rcPassUp == VINF_SUCCESS)) { #ifndef VBOX_WITH_NESTED_HWVIRT_VMX uint64_t fCpu = pVCpu->fLocalForcedActions; #endif fCpu &= VMCPU_FF_ALL_MASK & ~( VMCPU_FF_PGM_SYNC_CR3 | VMCPU_FF_PGM_SYNC_CR3_NON_GLOBAL | VMCPU_FF_TLB_FLUSH | VMCPU_FF_UNHALT ); if (RT_LIKELY( ( iemExecLoopTargetCheckMaskedCpuFFs(pVCpu, fCpu) && !VM_FF_IS_ANY_SET(pVM, VM_FF_ALL_MASK) ) || pStats->cInstructions < cMinInstructions)) { if (pStats->cInstructions < cMaxInstructions) { if (cInstructionSinceLastExit <= cMaxInstructionsWithoutExits) { #ifdef IN_RING0 if ( !fCheckPreemptionPending || !RTThreadPreemptIsPending(NIL_RTTHREAD)) #endif { Assert(pVCpu->iem.s.cActiveMappings == 0); iemReInitDecoder(pVCpu); continue; } #ifdef IN_RING0 rcStrict = VINF_EM_RAW_INTERRUPT; break; #endif } } } Assert(!(fCpu & VMCPU_FF_IEM)); } Assert(pVCpu->iem.s.cActiveMappings == 0); } else if (pVCpu->iem.s.cActiveMappings > 0) iemMemRollback(pVCpu); rcStrict = iemExecStatusCodeFiddling(pVCpu, rcStrict); break; } } IEM_CATCH_LONGJMP_BEGIN(pVCpu, rcStrict); { if (pVCpu->iem.s.cActiveMappings > 0) iemMemRollback(pVCpu); pVCpu->iem.s.cLongJumps++; } IEM_CATCH_LONGJMP_END(pVCpu); #ifdef VBOX_STRICT iemInitExecTailStrictTarget(pVCpu); #endif } else { if (pVCpu->iem.s.cActiveMappings > 0) iemMemRollback(pVCpu); #if defined(VBOX_WITH_NESTED_HWVIRT_SVM) || defined(VBOX_WITH_NESTED_HWVIRT_VMX) /* * When a nested-guest causes an exception intercept (e.g. #PF) when fetching * code as part of instruction execution, we need this to fix-up VINF_SVM_VMEXIT. */ rcStrict = iemExecStatusCodeFiddling(pVCpu, rcStrict); #endif } /* * Maybe re-enter raw-mode and log. */ #ifdef LOG_ENABLED if (rcStrict != VINF_SUCCESS) LOGFLOW_REG_STATE_EX("IEMExecLots", " - rcStrict=%Rrc; ins=%u exits=%u maxdist=%u", VBOXSTRICTRC_VAL(rcStrict), pStats->cInstructions, pStats->cExits, pStats->cMaxExitDistance); #endif return rcStrict; } /** * Injects a trap, fault, abort, software interrupt or external interrupt. * * The parameter list matches TRPMQueryTrapAll pretty closely. * * @returns Strict VBox status code. * @param pVCpu The cross context virtual CPU structure of the calling EMT. * @param u8TrapNo The trap number. * @param enmType What type is it (trap/fault/abort), software * interrupt or hardware interrupt. * @param uErrCode The error code if applicable. * @param uCr2 The CR2 value if applicable. * @param cbInstr The instruction length (only relevant for * software interrupts). * @note x86 specific, but difficult to move due to iemInitDecoder dep. */ VMM_INT_DECL(VBOXSTRICTRC) IEMInjectTrap(PVMCPUCC pVCpu, uint8_t u8TrapNo, TRPMEVENT enmType, uint16_t uErrCode, RTGCPTR uCr2, uint8_t cbInstr) { #ifdef VBOX_VMM_TARGET_X86 iemInitDecoder(pVCpu, 0 /*fExecOpts*/); /** @todo wrong init function! */ # ifdef DBGFTRACE_ENABLED RTTraceBufAddMsgF(pVCpu->CTX_SUFF(pVM)->CTX_SUFF(hTraceBuf), "IEMInjectTrap: %x %d %x %llx", u8TrapNo, enmType, uErrCode, uCr2); # endif uint32_t fFlags; switch (enmType) { case TRPM_HARDWARE_INT: Log(("IEMInjectTrap: %#4x ext\n", u8TrapNo)); fFlags = IEM_XCPT_FLAGS_T_EXT_INT; uErrCode = uCr2 = 0; break; case TRPM_SOFTWARE_INT: Log(("IEMInjectTrap: %#4x soft\n", u8TrapNo)); fFlags = IEM_XCPT_FLAGS_T_SOFT_INT; uErrCode = uCr2 = 0; break; case TRPM_TRAP: case TRPM_NMI: /** @todo Distinguish NMI from exception 2. */ Log(("IEMInjectTrap: %#4x trap err=%#x cr2=%#RGv\n", u8TrapNo, uErrCode, uCr2)); fFlags = IEM_XCPT_FLAGS_T_CPU_XCPT; if (u8TrapNo == X86_XCPT_PF) fFlags |= IEM_XCPT_FLAGS_CR2; switch (u8TrapNo) { case X86_XCPT_DF: case X86_XCPT_TS: case X86_XCPT_NP: case X86_XCPT_SS: case X86_XCPT_PF: case X86_XCPT_AC: case X86_XCPT_GP: fFlags |= IEM_XCPT_FLAGS_ERR; break; } break; IEM_NOT_REACHED_DEFAULT_CASE_RET(); } VBOXSTRICTRC rcStrict = iemRaiseXcptOrInt(pVCpu, cbInstr, u8TrapNo, fFlags, uErrCode, uCr2); if (pVCpu->iem.s.cActiveMappings > 0) iemMemRollback(pVCpu); return rcStrict; #else /* !VBOX_VMM_TARGET_X86 */ RT_NOREF(pVCpu, u8TrapNo, enmType, uErrCode, uCr2, cbInstr); AssertFailedReturn(VERR_NOT_IMPLEMENTED); #endif /* !VBOX_VMM_TARGET_X86 */ } /** * Injects the active TRPM event. * * @returns Strict VBox status code. * @param pVCpu The cross context virtual CPU structure. */ VMM_INT_DECL(VBOXSTRICTRC) IEMInjectTrpmEvent(PVMCPUCC pVCpu) { #ifndef IEM_IMPLEMENTS_TASKSWITCH IEM_RETURN_ASPECT_NOT_IMPLEMENTED_LOG(("Event injection\n")); #else uint8_t u8TrapNo; TRPMEVENT enmType; uint32_t uErrCode; RTGCUINTPTR uCr2; uint8_t cbInstr; int rc = TRPMQueryTrapAll(pVCpu, &u8TrapNo, &enmType, &uErrCode, &uCr2, &cbInstr, NULL /* fIcebp */); if (RT_FAILURE(rc)) return rc; /** @todo r=ramshankar: Pass ICEBP info. to IEMInjectTrap() below and handle * ICEBP \#DB injection as a special case. */ VBOXSTRICTRC rcStrict = IEMInjectTrap(pVCpu, u8TrapNo, enmType, uErrCode, uCr2, cbInstr); #ifdef VBOX_WITH_NESTED_HWVIRT_SVM if (rcStrict == VINF_SVM_VMEXIT) rcStrict = VINF_SUCCESS; #endif #ifdef VBOX_WITH_NESTED_HWVIRT_VMX if (rcStrict == VINF_VMX_VMEXIT) rcStrict = VINF_SUCCESS; #endif /** @todo Are there any other codes that imply the event was successfully * delivered to the guest? See @bugref{6607}. */ if ( rcStrict == VINF_SUCCESS || rcStrict == VINF_IEM_RAISED_XCPT) TRPMResetTrap(pVCpu); return rcStrict; #endif } VMM_INT_DECL(int) IEMBreakpointSet(PVM pVM, RTGCPTR GCPtrBp) { RT_NOREF_PV(pVM); RT_NOREF_PV(GCPtrBp); return VERR_NOT_IMPLEMENTED; } VMM_INT_DECL(int) IEMBreakpointClear(PVM pVM, RTGCPTR GCPtrBp) { RT_NOREF_PV(pVM); RT_NOREF_PV(GCPtrBp); return VERR_NOT_IMPLEMENTED; }