HWVMXR0.cpp revision 06bff04f33aeff5331b664c379f31b4969efc6fc
/* $Id$ */
/** @file
* HWACCM VMX - Host Context Ring 0.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_HWACCM
#include "HWACCMInternal.h"
#include "HWVMXR0.h"
/* IO operation lookup arrays. */
{
if (rc == VERR_VMX_GENERIC)
{
}
}
/**
* Sets up and activates VT-x on the current CPU
*
* @returns VBox status code.
* @param pCpu CPU info struct
* @param pVM The VM to operate on.
* @param pvPageCpu Pointer to the global cpu page
* @param pPageCpuPhys Physical address of the global cpu page
*/
HWACCMR0DECL(int) VMXR0EnableCpu(PHWACCM_CPUINFO pCpu, PVM pVM, void *pvPageCpu, RTHCPHYS pPageCpuPhys)
{
/* Setup Intel VMX. */
#ifdef LOG_ENABLED
SUPR0Printf("VMXR0EnableCpu cpu %d page (%x) %x\n", pCpu->idCpu, pvPageCpu, (uint32_t)pPageCpuPhys);
#endif
/* Set revision dword at the beginning of the VMXON structure. */
/* @todo we should unmap the two pages from the virtual address space in order to prevent accidental corruption.
* (which can have very bad consequences!!!)
*/
/* Make sure the VMX instructions don't cause #UD faults. */
/* Enter VMX Root Mode */
if (VBOX_FAILURE(rc))
{
return VERR_VMX_VMXON_FAILED;
}
return VINF_SUCCESS;
}
/**
* Deactivates VT-x on the current CPU
*
* @returns VBox status code.
* @param pCpu CPU info struct
* @param pvPageCpu Pointer to the global cpu page
* @param pPageCpuPhys Physical address of the global cpu page
*/
{
/* Leave VMX Root Mode. */
VMXDisable();
/* And clear the X86_CR4_VMXE bit */
#ifdef LOG_ENABLED
#endif
return VINF_SUCCESS;
}
/**
* Does Ring-0 per VM VT-x init.
*
* @returns VBox status code.
* @param pVM The VM to operate on.
*/
{
int rc;
#ifdef LOG_ENABLED
#endif
/* Allocate one page for the VM control structure (VMCS). */
rc = RTR0MemObjAllocCont(&pVM->hwaccm.s.vmx.pMemObjVMCS, 1 << PAGE_SHIFT, true /* executable R0 mapping */);
if (RT_FAILURE(rc))
return rc;
/* Allocate one page for the TSS we need for real mode emulation. */
rc = RTR0MemObjAllocCont(&pVM->hwaccm.s.vmx.pMemObjRealModeTSS, 1 << PAGE_SHIFT, true /* executable R0 mapping */);
if (RT_FAILURE(rc))
return rc;
pVM->hwaccm.s.vmx.pRealModeTSSPhys = RTR0MemObjGetPagePhysAddr(pVM->hwaccm.s.vmx.pMemObjRealModeTSS, 0);
/* The I/O bitmap starts right after the virtual interrupt redirection bitmap. Outside the TSS on purpose; the CPU will not check it
* for I/O operations. */
/* Bit set to 0 means redirection enabled. */
memset(pVM->hwaccm.s.vmx.pRealModeTSS->IntRedirBitmap, 0x0, sizeof(pVM->hwaccm.s.vmx.pRealModeTSS->IntRedirBitmap));
if (pVM->hwaccm.s.vmx.msr.vmx_proc_ctls.n.allowed1 & VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_USE_TPR_SHADOW)
{
/* Allocate one page for the virtual APIC mmio cache. */
rc = RTR0MemObjAllocCont(&pVM->hwaccm.s.vmx.pMemObjAPIC, 1 << PAGE_SHIFT, true /* executable R0 mapping */);
if (RT_FAILURE(rc))
return rc;
}
else
{
}
#ifdef LOG_ENABLED
SUPR0Printf("VMXR0InitVM %x VMCS=%x (%x) RealModeTSS=%x (%x)\n", pVM, pVM->hwaccm.s.vmx.pVMCS, (uint32_t)pVM->hwaccm.s.vmx.pVMCSPhys, pVM->hwaccm.s.vmx.pRealModeTSS, (uint32_t)pVM->hwaccm.s.vmx.pRealModeTSSPhys);
#endif
return VINF_SUCCESS;
}
/**
* Does Ring-0 per VM VT-x termination.
*
* @returns VBox status code.
* @param pVM The VM to operate on.
*/
{
{
}
{
}
{
}
return VINF_SUCCESS;
}
/**
* Sets up VT-x for the specified VM
*
* @returns VBox status code.
* @param pVM The VM to operate on.
*/
{
int rc = VINF_SUCCESS;
/* Set revision dword at the beginning of the VMCS structure. */
*(uint32_t *)pVM->hwaccm.s.vmx.pVMCS = MSR_IA32_VMX_BASIC_INFO_VMCS_ID(pVM->hwaccm.s.vmx.msr.vmx_basic_info);
/* Clear VM Control Structure. */
if (VBOX_FAILURE(rc))
goto vmx_end;
/* Activate the VM Control Structure. */
if (VBOX_FAILURE(rc))
goto vmx_end;
/* VMX_VMCS_CTRL_PIN_EXEC_CONTROLS
* Set required bits to one and zero according to the MSR capabilities.
*/
/* External and non-maskable interrupts cause VM-exits. */
val = val | VMX_VMCS_CTRL_PIN_EXEC_CONTROLS_EXT_INT_EXIT | VMX_VMCS_CTRL_PIN_EXEC_CONTROLS_NMI_EXIT;
/* VMX_VMCS_CTRL_PROC_EXEC_CONTROLS
* Set required bits to one and zero according to the MSR capabilities.
*/
/* Program which event cause VM-exits and which features we want to use. */
| VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_MWAIT_EXIT; /* don't execute mwait or else we'll idle inside the guest (host thinks the cpu load is high) */
/** @note VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_MWAIT_EXIT might cause a vmlaunch failure with an invalid control fields error. (combined with some other exit reasons) */
#if HC_ARCH_BITS == 64
if (pVM->hwaccm.s.vmx.msr.vmx_proc_ctls.n.allowed1 & VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_USE_TPR_SHADOW)
{
/* CR8 reads from the APIC shadow page; writes cause an exit is they lower the TPR below the threshold */
}
else
/* Exit on CR8 reads & writes in case the TPR shadow feature isn't present. */
val |= VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_CR8_STORE_EXIT | VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_CR8_LOAD_EXIT;
#endif
/* Mask away the bits that the CPU doesn't support */
/** @todo make sure they don't conflict with the above requirements. */
/* VMX_VMCS_CTRL_CR3_TARGET_COUNT
* Set required bits to one and zero according to the MSR capabilities.
*/
/* VMX_VMCS_CTRL_EXIT_CONTROLS
* Set required bits to one and zero according to the MSR capabilities.
*/
#if HC_ARCH_BITS == 64
#else
/* else Must be zero when AMD64 is not available. */
#endif
/* Don't acknowledge external interrupts on VM-exit. */
/* Forward all exception except #NM & #PF to the guest.
* We always need to check pagefaults since our shadow page table can be out of sync.
* And we always lazily sync the FPU & XMM state.
*/
/*
* @todo Possible optimization:
* Keep the FPU and XMM state current in the EM thread. That way there's no need to
* lazily sync anything, but the downside is that we can't use the FPU stack or XMM
* registers ourselves of course.
*
* @note only possible if the current state is actually ours (X86_CR0_TS flag)
*/
/* Don't filter page faults; all of them should cause a switch. */
/* Init TSC offset to zero. */
#if HC_ARCH_BITS == 32
#endif
#if HC_ARCH_BITS == 32
#endif
#if HC_ARCH_BITS == 32
#endif
/* Clear MSR controls. */
if (pVM->hwaccm.s.vmx.msr.vmx_proc_ctls.n.allowed1 & VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_USE_MSR_BITMAPS)
{
/* Optional */
#if HC_ARCH_BITS == 32
#endif
}
#if HC_ARCH_BITS == 32
#endif
if (pVM->hwaccm.s.vmx.msr.vmx_proc_ctls.n.allowed1 & VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_USE_TPR_SHADOW)
{
/* Optional */
#if HC_ARCH_BITS == 32
#endif
}
/* Set link pointer to -1. Not currently used. */
#if HC_ARCH_BITS == 32
#else
#endif
/* Clear VM Control Structure. Marking it inactive, clearing implementation specific data and writing back VMCS data to memory. */
return rc;
}
/**
* Injects an event (trap or external interrupt)
*
* @returns VBox status code.
* @param pVM The VM to operate on.
* @param pCtx CPU Context
* @param intInfo VMX interrupt info
* @param cbInstr Opcode length of faulting instruction
* @param errCode Error code (optional)
*/
static int VMXR0InjectEvent(PVM pVM, CPUMCTX *pCtx, uint32_t intInfo, uint32_t cbInstr, uint32_t errCode)
{
int rc;
#ifdef VBOX_STRICT
if (iGate == 0xE)
Log2(("VMXR0InjectEvent: Injecting interrupt %d at %VGv error code=%08x CR2=%08x intInfo=%08x\n", iGate, pCtx->rip, errCode, pCtx->cr2, intInfo));
else
if (iGate < 0x20)
Log2(("VMXR0InjectEvent: Injecting interrupt %d at %VGv error code=%08x\n", iGate, pCtx->rip, errCode));
else
{
}
#endif
/* Set event injection state. */
);
return rc;
}
/**
* Checks for pending guest interrupts and injects them
*
* @returns VBox status code.
* @param pVM The VM to operate on.
* @param pCtx CPU Context
*/
{
int rc;
/* Dispatch any pending interrupts. (injected before, but a VM exit occurred prematurely) */
{
Log(("Reinjecting event %VX64 %08x at %VGv cr2=%RX64\n", pVM->hwaccm.s.Event.intInfo, pVM->hwaccm.s.Event.errCode, pCtx->rip, pCtx->cr2));
return VINF_SUCCESS;
}
/* When external interrupts are pending, we should exit the VM when IF is set. */
if ( !TRPMHasTrap(pVM)
{
{
Log2(("Enable irq window exit!\n"));
}
else
{
if (VBOX_SUCCESS(rc))
{
}
else
{
/* Can only happen in rare cases where a pending interrupt is cleared behind our back */
/* Just continue */
}
}
else
}
#ifdef VBOX_STRICT
if (TRPMHasTrap(pVM))
{
}
#endif
&& TRPMHasTrap(pVM)
)
{
int rc;
/* If a new event is pending, then dispatch it now. */
/* Clear the pending trap. */
{
switch (u8Vector) {
case 8:
case 10:
case 11:
case 12:
case 13:
case 14:
case 17:
/* Valid error codes. */
break;
default:
break;
}
else
}
else
} /* if (interrupts can be dispatched) */
return VINF_SUCCESS;
}
/**
* Save the host state
*
* @returns VBox status code.
* @param pVM The VM to operate on.
*/
{
int rc = VINF_SUCCESS;
/*
* Host CPU Context
*/
{
/* Control registers */
/* Selector registers. */
/** @note VMX is (again) very picky about the RPL of the selectors here; we'll restore them manually. */
#if HC_ARCH_BITS == 32
#endif
/* GDTR & IDTR */
ASMGetGDTR(&gdtr);
ASMGetIDTR(&idtr);
/* Save the base address of the TR selector. */
{
return VERR_VMX_INVALID_HOST_STATE;
}
#if HC_ARCH_BITS == 64
#else
#endif
/* FS and GS base. */
#if HC_ARCH_BITS == 64
#endif
/* Sysenter MSRs. */
/** @todo expensive!! */
#if HC_ARCH_BITS == 32
#else
#endif
}
return rc;
}
/**
* Loads the guest state
*
* NOTE: Don't do anything here that can cause a jump back to ring 3!!!!!
*
* @returns VBox status code.
* @param pVM The VM to operate on.
* @param pCtx Guest context
*/
{
int rc = VINF_SUCCESS;
/* Guest CPU context: ES, CS, SS, DS, FS, GS. */
{
/* The base values in the hidden fs & gs registers are not in sync with the msrs; they are cut to 32 bits. */
}
/* Guest CPU context: LDTR. */
{
{
/** @note vmlaunch will fail with 0 or just 0x02. No idea why. */
}
else
{
}
}
/* Guest CPU context: TR. */
{
/* Real mode emulation using v86 mode with CR4.VME (interrupt redirection using the int bitmap in the TSS) */
{
}
else
{
}
/* The TSS selector must be busy. */
else
/* Default even if no TR selector has been set (otherwise vmlaunch will fail!) */
}
/* Guest CPU context: GDTR. */
{
}
/* Guest CPU context: IDTR. */
{
}
/*
* Sysenter MSRs (unconditional)
*/
/* Control registers */
{
if (CPUMIsGuestFPUStateActive(pVM) == false)
{
}
else
{
/** @todo check if we support the old style mess correctly. */
if (!(val & X86_CR0_NE))
{
Log(("Forcing X86_CR0_NE!!!\n"));
/* Also catch floating point exceptions as we need to report them to the guest in a different way. */
{
}
}
val |= X86_CR0_NE; /* always turn on the native mechanism to report FPU errors (old style uses interrupts) */
}
/* Note: protected mode & paging are always enabled; we use them for emulating real and protected mode without paging too. */
/* Note: We must also set this as we rely on protecting various pages for which supervisor writes must be caught. */
val |= X86_CR0_WP;
/* CR0 flags owned by the host; if the guests attempts to change them, then
* the VM will exit.
*/
| X86_CR0_WP /* Must monitor this bit (it must always be enabled). */
| X86_CR0_PG /* Must monitor this bit (assumptions are made for real mode & protected mode without paging emulation) */
| X86_CR0_MP;
}
{
/* CR4 */
/* Set the required bits in cr4 too (currently X86_CR4_VMXE). */
{
case PGMMODE_REAL: /* Real mode -> emulated using v86 mode */
case PGMMODE_PROTECTED: /* Protected mode, no paging -> emulated using identity mapping. */
case PGMMODE_32_BIT: /* 32-bit paging. */
break;
case PGMMODE_PAE: /* PAE paging. */
case PGMMODE_PAE_NX: /* PAE paging with NX enabled. */
/** @todo use normal 32 bits paging */
val |= X86_CR4_PAE;
break;
case PGMMODE_AMD64: /* 64-bit AMD paging (long mode). */
case PGMMODE_AMD64_NX: /* 64-bit AMD paging (long mode) with NX enabled. */
#ifdef VBOX_ENABLE_64_BITS_GUESTS
break;
#else
AssertFailed();
#endif
default: /* shut up gcc */
AssertFailed();
}
/* Real mode emulation using v86 mode with CR4.VME (interrupt redirection using the int bitmap in the TSS) */
val |= X86_CR4_VME;
/* CR4 flags owned by the host; if the guests attempts to change them, then
* the VM will exit.
*/
| X86_CR4_VMXE;
}
{
/* Save our shadow CR3 register. */
}
/* Debug registers. */
{
/** @todo DR0-6 */
#ifdef VBOX_STRICT
val = 0x400;
#endif
/* IA32_DEBUGCTL MSR. */
/** @todo */
}
/* EIP, ESP and EFLAGS */
/* Bits 22-31, 15, 5 & 3 must be zero. Bit 1 must be 1. */
/* Real mode emulation using v86 mode with CR4.VME (interrupt redirection using the int bitmap in the TSS) */
{
}
/** TSC offset. */
{
/* Note: VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_RDTSC_EXIT takes precedence over TSC_OFFSET */
#if HC_ARCH_BITS == 64
#else
#endif
}
else
{
}
/* VMX_VMCS_CTRL_ENTRY_CONTROLS
* Set required bits to one and zero according to the MSR capabilities.
*/
/* 64 bits guest mode? */
/* else Must be zero when AMD64 is not available. */
/* Mask away the bits that the CPU doesn't support */
/* 64 bits guest mode? */
{
#else
#endif
/* Unconditionally update these as wrmsr might have changed them. */
}
else
{
}
/* Done. */
return rc;
}
/**
* Runs guest code in a VT-x VM.
*
* @note NEVER EVER turn on interrupts here. Due to our illegal entry into the kernel, it might mess things up. (XP kernel traps have been frequently observed)
*
* @returns VBox status code.
* @param pVM The VM to operate on.
* @param pCtx Guest context
*/
{
int rc = VINF_SUCCESS;
bool fGuestStateSynced = false;
unsigned cResume = 0;
#ifdef VBOX_STRICT
#endif
Log2(("\nE"));
#ifdef VBOX_STRICT
/* allowed zero */
if ((val & pVM->hwaccm.s.vmx.msr.vmx_pin_ctls.n.disallowed0) != pVM->hwaccm.s.vmx.msr.vmx_pin_ctls.n.disallowed0)
Log(("Invalid VMX_VMCS_CTRL_PIN_EXEC_CONTROLS: zero\n"));
/* allowed one */
Log(("Invalid VMX_VMCS_CTRL_PIN_EXEC_CONTROLS: one\n"));
/* allowed zero */
if ((val & pVM->hwaccm.s.vmx.msr.vmx_proc_ctls.n.disallowed0) != pVM->hwaccm.s.vmx.msr.vmx_proc_ctls.n.disallowed0)
Log(("Invalid VMX_VMCS_CTRL_PROC_EXEC_CONTROLS: zero\n"));
/* allowed one */
Log(("Invalid VMX_VMCS_CTRL_PROC_EXEC_CONTROLS: one\n"));
/* allowed zero */
if ((val & pVM->hwaccm.s.vmx.msr.vmx_entry.n.disallowed0) != pVM->hwaccm.s.vmx.msr.vmx_entry.n.disallowed0)
Log(("Invalid VMX_VMCS_CTRL_ENTRY_CONTROLS: zero\n"));
/* allowed one */
Log(("Invalid VMX_VMCS_CTRL_ENTRY_CONTROLS: one\n"));
/* allowed zero */
if ((val & pVM->hwaccm.s.vmx.msr.vmx_exit.n.disallowed0) != pVM->hwaccm.s.vmx.msr.vmx_exit.n.disallowed0)
Log(("Invalid VMX_VMCS_CTRL_EXIT_CONTROLS: zero\n"));
/* allowed one */
Log(("Invalid VMX_VMCS_CTRL_EXIT_CONTROLS: one\n"));
#endif
#if 0
/*
* Check if debug registers are armed.
*/
if (u32DR7 & X86_DR7_ENABLED_MASK)
{
}
else
#endif
/* We can jump to this point to resume execution after determining that a VM-exit is innocent.
*/
/* Safety precaution; looping for too long here can have a very bad effect on the host */
if (++cResume > HWACCM_MAX_RESUME_LOOPS)
{
goto end;
}
/* Check for irq inhibition due to instruction fusing (sti, mov ss). */
{
Log(("VM_FF_INHIBIT_INTERRUPTS at %VGv successor %VGv\n", pCtx->rip, EMGetInhibitInterruptsPC(pVM)));
{
/** @note we intentionally don't clear VM_FF_INHIBIT_INTERRUPTS here.
* Before we are able to execute this instruction in raw mode (iret to guest code) an external interrupt might
* force a world switch again. Possibly allowing a guest interrupt to be dispatched in the process. This could
* break the guest. Sounds very unlikely, but such timing sensitive problem are not as rare as you might think.
*/
/* Irq inhibition is no longer active; clear the corresponding VMX state. */
}
}
else
{
/* Irq inhibition is no longer active; clear the corresponding VMX state. */
}
/* Check for pending actions that force us to go back to ring 3. */
{
goto end;
}
/* Pending request packets might contain actions that need immediate attention, such as pending hardware interrupts. */
{
goto end;
}
/* When external interrupts are pending, we should exit the VM when IF is set. */
/** @note *after* VM_FF_INHIBIT_INTERRUPTS check!!! */
if (VBOX_FAILURE(rc))
{
goto end;
}
/** @todo check timers?? */
/* TPR caching using CR8 is only available in 64 bits mode */
/* Note the 32 bits exception for AMD (X86_CPUID_AMD_FEATURE_ECX_CR8L), but that appears missing in Intel CPUs */
/* Note: we can't do this in LoadGuestState as PDMApicGetTPR can jump back to ring 3 (lock)!!!!! */
/*
* @todo reduce overhead
*/
{
/* TPR caching in CR8 */
bool fPending;
/* The TPR can be found at offset 0x80 in the APIC mmio page. */
/* Two options here:
* - external interrupt pending, but masked by the TPR value.
* -> CR8 updates that lower the TPR value to below the current value should cause an exit
* - no pending interrupts
* -> We don't need to be explicitely notified. There are enough world switches for detecting pending interrupts.
*/
}
/*
* NOTE: DO NOT DO ANYTHING AFTER THIS POINT THAT MIGHT JUMP BACK TO RING 3!
* (until the actual world switch)
*/
#ifdef VBOX_STRICT
idCpuCheck = RTMpCpuId();
#endif
/* Save the host state first. */
if (rc != VINF_SUCCESS)
{
goto end;
}
/* Load the guest state */
if (rc != VINF_SUCCESS)
{
goto end;
}
fGuestStateSynced = true;
/* Non-register state Guest Context */
/** @todo change me according to cpu state */
/* Manual save and restore:
* - General purpose registers except RIP, RSP
*
* Trashed:
* - CR2 (we don't care)
* - LDTR (reset to 0)
* - DRx (presumably not changed at all)
* - DR7 (reset to 0x400)
* - EFLAGS (reset to RT_BIT(1); not relevant)
*
*/
/* All done! Let's start VM execution. */
#ifdef VBOX_STRICT
#endif
/* In case we execute a goto ResumeExecution later on. */
/**
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* IMPORTANT: WE CAN'T DO ANY LOGGING OR OPERATIONS THAT CAN DO A LONGJMP BACK TO RING 3 *BEFORE* WE'VE SYNCED BACK (MOST OF) THE GUEST STATE
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
switch (rc)
{
case VINF_SUCCESS:
break;
AssertFailed();
goto end;
{
#ifdef VBOX_STRICT
int rc1;
if (rc1 == VINF_SUCCESS)
{
ASMGetGDTR(&gdtr);
Log(("Unable to start/resume VM for reason: %x. Instruction error %x\n", (uint32_t)exitReason, (uint32_t)instrError));
{
}
{
}
{
}
{
}
{
}
{
}
{
}
#if HC_ARCH_BITS == 64
#endif
}
#endif /* VBOX_STRICT */
goto end;
}
default:
/* impossible */
AssertFailed();
goto end;
}
/* Success. Query the guest state and figure out what has happened. */
/* Investigate why there was a VM-exit. */
/* Let's first sync back eip, esp, and eflags. */
/* Update the APIC with the cached TPR value.
* @todo reduce overhead
*/
{
}
/* Take care of instruction fusing (sti, mov ss) */
if (uInterruptState != 0)
{
}
else
/* Real mode emulation using v86 mode with CR4.VME (interrupt redirection using the int bitmap in the TSS) */
{
/* Hide our emulation flags */
}
/* Control registers. */
/* Guest CPU context: ES, CS, SS, DS, FS, GS. */
/** @note NOW IT'S SAFE FOR LOGGING! */
/* Check if an injected event was interrupted prematurely. */
&& VMX_EXIT_INTERRUPTION_INFO_TYPE(pVM->hwaccm.s.Event.intInfo) != VMX_EXIT_INTERRUPTION_INFO_TYPE_SW)
{
/* Error code present? */
{
Log(("Pending inject %VX64 at %VGv exit=%08x intInfo=%08x exitQualification=%08x pending error=%RX64\n", pVM->hwaccm.s.Event.intInfo, pCtx->rip, exitReason, intInfo, exitQualification, val));
}
else
{
Log(("Pending inject %VX64 at %VGv exit=%08x intInfo=%08x exitQualification=%08x\n", pVM->hwaccm.s.Event.intInfo, pCtx->rip, exitReason, intInfo, exitQualification));
}
}
#ifdef VBOX_STRICT
#endif
/* Some cases don't need a complete resync of the guest CPU state; handle them here. */
switch (exitReason)
{
case VMX_EXIT_EXCEPTION: /* 0 Exception or non-maskable interrupt (NMI). */
case VMX_EXIT_EXTERNAL_IRQ: /* 1 External interrupt. */
{
{
/* External interrupt; leave to allow it to be dispatched again. */
break;
}
switch (VMX_EXIT_INTERRUPTION_INFO_TYPE(intInfo))
{
case VMX_EXIT_INTERRUPTION_INFO_TYPE_NMI: /* Non-maskable interrupt. */
/* External interrupt; leave to allow it to be dispatched again. */
break;
case VMX_EXIT_INTERRUPTION_INFO_TYPE_EXT: /* External hardware interrupt. */
AssertFailed(); /* can't come here; fails the first check. */
break;
case VMX_EXIT_INTERRUPTION_INFO_TYPE_SWEXCPT: /* Software exception. (#BP or #OF) */
/* no break */
case VMX_EXIT_INTERRUPTION_INFO_TYPE_HWEXCPT: /* Hardware exception. */
switch (vector)
{
case X86_XCPT_NM:
{
/** @todo don't intercept #NM exceptions anymore when we've activated the guest FPU state. */
/* If we sync the FPU/XMM state on-demand, then we can continue execution as if nothing has happened. */
if (rc == VINF_SUCCESS)
{
/* Continue execution. */
goto ResumeExecution;
}
Log(("Forward #NM fault to the guest\n"));
rc = VMXR0InjectEvent(pVM, pCtx, VMX_VMCS_CTRL_ENTRY_IRQ_INFO_FROM_EXIT_INT_INFO(intInfo), cbInstr, 0);
goto ResumeExecution;
}
case X86_XCPT_PF: /* Page fault */
{
/* Exit qualification contains the linear address of the page fault. */
/* Forward it to our trap handler first, in case our shadow pages are out of sync. */
if (rc == VINF_SUCCESS)
{ /* We've successfully synced our shadow pages, so let's just continue execution. */
Log2(("Shadow page fault at %VGv cr2=%VGv error code %x\n", pCtx->rip, exitQualification ,errCode));
goto ResumeExecution;
}
else
if (rc == VINF_EM_RAW_GUEST_TRAP)
{ /* A genuine pagefault.
* Forward the trap to the guest by injecting the exception and resuming execution.
*/
Log2(("Forward page fault to the guest\n"));
/* The error code might have been changed. */
/* Now we must update CR2. */
rc = VMXR0InjectEvent(pVM, pCtx, VMX_VMCS_CTRL_ENTRY_IRQ_INFO_FROM_EXIT_INT_INFO(intInfo), cbInstr, errCode);
goto ResumeExecution;
}
#ifdef VBOX_STRICT
if (rc != VINF_EM_RAW_EMULATE_INSTR)
#endif
/* Need to go back to the recompiler to emulate the instruction. */
break;
}
case X86_XCPT_MF: /* Floating point exception. */
{
{
/* old style FPU error reporting needs some extra work. */
/** @todo don't fall back to the recompiler, but do it manually. */
break;
}
rc = VMXR0InjectEvent(pVM, pCtx, VMX_VMCS_CTRL_ENTRY_IRQ_INFO_FROM_EXIT_INT_INFO(intInfo), cbInstr, errCode);
goto ResumeExecution;
}
#ifdef VBOX_STRICT
case X86_XCPT_GP: /* General protection failure exception.*/
case X86_XCPT_UD: /* Unknown opcode exception. */
case X86_XCPT_DE: /* Debug exception. */
case X86_XCPT_SS: /* Stack segment exception. */
case X86_XCPT_NP: /* Segment not present exception. */
{
switch(vector)
{
case X86_XCPT_DE:
break;
case X86_XCPT_UD:
break;
case X86_XCPT_SS:
break;
case X86_XCPT_NP:
break;
case X86_XCPT_GP:
break;
}
rc = VMXR0InjectEvent(pVM, pCtx, VMX_VMCS_CTRL_ENTRY_IRQ_INFO_FROM_EXIT_INT_INFO(intInfo), cbInstr, errCode);
goto ResumeExecution;
}
#endif
default:
break;
} /* switch (vector) */
break;
default:
AssertFailed();
break;
}
break;
}
case VMX_EXIT_IRQ_WINDOW: /* 7 Interrupt window. */
/* Clear VM-exit on IF=1 change. */
goto ResumeExecution; /* we check for pending guest interrupts there */
case VMX_EXIT_INVD: /* 13 Guest software attempted to execute INVD. */
/* Skip instruction and continue directly. */
/* Continue execution.*/
goto ResumeExecution;
case VMX_EXIT_CPUID: /* 10 Guest software attempted to execute CPUID. */
{
if (rc == VINF_SUCCESS)
{
/* Update EIP and continue execution. */
goto ResumeExecution;
}
break;
}
case VMX_EXIT_RDTSC: /* 16 Guest software attempted to execute RDTSC. */
{
Log2(("VMX: Rdtsc\n"));
if (rc == VINF_SUCCESS)
{
/* Update EIP and continue execution. */
goto ResumeExecution;
}
break;
}
case VMX_EXIT_INVPG: /* 14 Guest software attempted to execute INVPG. */
{
Log2(("VMX: invlpg\n"));
if (rc == VINF_SUCCESS)
{
/* Update EIP and continue execution. */
goto ResumeExecution;
}
AssertMsg(rc == VERR_EM_INTERPRETER, ("EMU: invlpg %VGv failed with %Vrc\n", exitQualification, rc));
break;
}
case VMX_EXIT_RDMSR: /* 31 RDMSR. Guest software attempted to execute RDMSR. */
case VMX_EXIT_WRMSR: /* 32 WRMSR. Guest software attempted to execute WRMSR. */
{
/* Note: the intel manual claims there's a REX version of RDMSR that's slightly different, so we play safe by completely disassembling the instruction. */
if (rc == VINF_SUCCESS)
{
/* EIP has been updated already. */
/* Only resume if successful. */
goto ResumeExecution;
}
AssertMsg(rc == VERR_EM_INTERPRETER, ("EMU: %s failed with %Vrc\n", (exitReason == VMX_EXIT_RDMSR) ? "rdmsr" : "wrmsr", rc));
break;
}
case VMX_EXIT_CRX_MOVE: /* 28 Control-register accesses. */
{
{
Log2(("VMX: %VGv mov cr%d, x\n", pCtx->rip, VMX_EXIT_QUALIFICATION_CRX_REGISTER(exitQualification)));
{
case 0:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 8:
/* CR8 contains the APIC TPR */
Assert(!(pVM->hwaccm.s.vmx.msr.vmx_proc_ctls.n.allowed1 & VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_USE_TPR_SHADOW));
break;
default:
AssertFailed();
break;
}
/* Check if a sync operation is pending. */
{
rc = PGMSyncCR3(pVM, CPUMGetGuestCR0(pVM), CPUMGetGuestCR3(pVM), CPUMGetGuestCR4(pVM), VM_FF_ISSET(pVM, VM_FF_PGM_SYNC_CR3));
}
break;
Log2(("VMX: mov x, crx\n"));
/* CR8 reads only cause an exit when the TPR shadow feature isn't present. */
Assert(VMX_EXIT_QUALIFICATION_CRX_REGISTER(exitQualification) != 8 || !(pVM->hwaccm.s.vmx.msr.vmx_proc_ctls.n.allowed1 & VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_USE_TPR_SHADOW));
break;
Log2(("VMX: clts\n"));
break;
break;
}
/* Update EIP if no error occurred. */
if (VBOX_SUCCESS(rc))
if (rc == VINF_SUCCESS)
{
/* Only resume if successful. */
goto ResumeExecution;
}
break;
}
case VMX_EXIT_DRX_MOVE: /* 29 Debug-register accesses. */
{
/** @todo clear VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_MOV_DR_EXIT after the first time and restore drx registers afterwards */
if (VMX_EXIT_QUALIFICATION_DRX_DIRECTION(exitQualification) == VMX_EXIT_QUALIFICATION_DRX_DIRECTION_WRITE)
{
Log2(("VMX: mov drx%d, genreg%d\n", VMX_EXIT_QUALIFICATION_DRX_REGISTER(exitQualification), VMX_EXIT_QUALIFICATION_DRX_GENREG(exitQualification)));
}
else
{
Log2(("VMX: mov x, drx\n"));
}
/* Update EIP if no error occurred. */
if (VBOX_SUCCESS(rc))
if (rc == VINF_SUCCESS)
{
/* Only resume if successful. */
goto ResumeExecution;
}
break;
}
/** @note We'll get a #GP if the IO instruction isn't allowed (IOPL or TSS bitmap); no need to double check. */
case VMX_EXIT_PORT_IO: /* 30 I/O instruction. */
{
bool fIOWrite = (VMX_EXIT_QUALIFICATION_IO_DIRECTION(exitQualification) == VMX_EXIT_QUALIFICATION_IO_DIRECTION_OUT);
/** @todo necessary to make the distinction? */
{
}
else
/* paranoia */
{
break;
}
{
prefix |= PREFIX_REP;
if (fIOWrite)
{
}
else
{
}
}
else
{
if (fIOWrite)
{
}
else
{
if (IOM_SUCCESS(rc))
{
/* Write back to the EAX register. */
}
}
}
/*
* Handled the I/O return codes.
* (The unhandled cases end up with rc == VINF_EM_RAW_EMULATE_INSTR.)
*/
if (IOM_SUCCESS(rc))
{
/* Update EIP and continue execution. */
{
goto ResumeExecution;
}
break;
}
#ifdef VBOX_STRICT
if (rc == VINF_IOM_HC_IOPORT_READ)
else if (rc == VINF_IOM_HC_IOPORT_WRITE)
else
AssertMsg(VBOX_FAILURE(rc) || rc == VINF_EM_RAW_EMULATE_INSTR || rc == VINF_EM_RAW_GUEST_TRAP || rc == VINF_TRPM_XCPT_DISPATCHED, ("%Vrc\n", rc));
#endif
break;
}
case VMX_EXIT_TPR: /* 43 TPR below threshold. Guest software executed MOV to CR8. */
LogFlow(("VMX_EXIT_TPR\n"));
/* RIP is already set to the next instruction and the TPR has been synced back. Just resume. */
goto ResumeExecution;
default:
/* The rest is handled after syncing the entire CPU state. */
break;
}
/* Note: the guest state isn't entirely synced back at this stage. */
/* Investigate why there was a VM-exit. (part 2) */
switch (exitReason)
{
case VMX_EXIT_EXCEPTION: /* 0 Exception or non-maskable interrupt (NMI). */
case VMX_EXIT_EXTERNAL_IRQ: /* 1 External interrupt. */
/* Already handled above. */
break;
case VMX_EXIT_TRIPLE_FAULT: /* 2 Triple fault. */
break;
case VMX_EXIT_INIT_SIGNAL: /* 3 INIT signal. */
case VMX_EXIT_SIPI: /* 4 Start-up IPI (SIPI). */
AssertFailed(); /* Can't happen. Yet. */
break;
case VMX_EXIT_IO_SMI_IRQ: /* 5 I/O system-management interrupt (SMI). */
case VMX_EXIT_SMI_IRQ: /* 6 Other SMI. */
AssertFailed(); /* Can't happen afaik. */
break;
case VMX_EXIT_TASK_SWITCH: /* 9 Task switch. */
break;
case VMX_EXIT_HLT: /* 12 Guest software attempted to execute HLT. */
/** Check if external interrupts are pending; if so, don't switch back. */
goto ResumeExecution;
rc = VINF_EM_HALT;
break;
case VMX_EXIT_RSM: /* 17 Guest software attempted to execute RSM in SMM. */
AssertFailed(); /* can't happen. */
break;
case VMX_EXIT_VMCALL: /* 18 Guest software executed VMCALL. */
case VMX_EXIT_VMCLEAR: /* 19 Guest software executed VMCLEAR. */
case VMX_EXIT_VMLAUNCH: /* 20 Guest software executed VMLAUNCH. */
case VMX_EXIT_VMPTRLD: /* 21 Guest software executed VMPTRLD. */
case VMX_EXIT_VMPTRST: /* 22 Guest software executed VMPTRST. */
case VMX_EXIT_VMREAD: /* 23 Guest software executed VMREAD. */
case VMX_EXIT_VMRESUME: /* 24 Guest software executed VMRESUME. */
case VMX_EXIT_VMWRITE: /* 25 Guest software executed VMWRITE. */
case VMX_EXIT_VMXOFF: /* 26 Guest software executed VMXOFF. */
case VMX_EXIT_VMXON: /* 27 Guest software executed VMXON. */
/** @todo inject #UD immediately */
break;
case VMX_EXIT_CPUID: /* 10 Guest software attempted to execute CPUID. */
case VMX_EXIT_RDTSC: /* 16 Guest software attempted to execute RDTSC. */
case VMX_EXIT_INVPG: /* 14 Guest software attempted to execute INVPG. */
case VMX_EXIT_CRX_MOVE: /* 28 Control-register accesses. */
case VMX_EXIT_DRX_MOVE: /* 29 Debug-register accesses. */
case VMX_EXIT_PORT_IO: /* 30 I/O instruction. */
/* already handled above */
|| rc == VINF_EM_RAW_INTERRUPT
|| rc == VERR_EM_INTERPRETER
|| rc == VINF_EM_RAW_EMULATE_INSTR
|| rc == VINF_PGM_SYNC_CR3
|| rc == VINF_IOM_HC_IOPORT_READ
|| rc == VINF_IOM_HC_IOPORT_WRITE
|| rc == VINF_EM_RAW_GUEST_TRAP
|| rc == VINF_TRPM_XCPT_DISPATCHED
|| rc == VINF_EM_RESCHEDULE_REM,
("rc = %d\n", rc));
break;
case VMX_EXIT_TPR: /* 43 TPR below threshold. Guest software executed MOV to CR8. */
case VMX_EXIT_RDMSR: /* 31 RDMSR. Guest software attempted to execute RDMSR. */
case VMX_EXIT_WRMSR: /* 32 WRMSR. Guest software attempted to execute WRMSR. */
/* Note: If we decide to emulate them here, then we must sync the MSRs that could have been changed (sysenter, fs/gs base)!!! */
break;
case VMX_EXIT_RDPMC: /* 15 Guest software attempted to execute RDPMC. */
case VMX_EXIT_MWAIT: /* 36 Guest software executed MWAIT. */
case VMX_EXIT_MONITOR: /* 39 Guest software attempted to execute MONITOR. */
case VMX_EXIT_PAUSE: /* 40 Guest software attempted to execute PAUSE. */
break;
case VMX_EXIT_IRQ_WINDOW: /* 7 Interrupt window. */
break;
case VMX_EXIT_ERR_INVALID_GUEST_STATE: /* 33 VM-entry failure due to invalid guest state. */
{
#ifdef VBOX_STRICT
Log(("VMX_EXIT_ERR_INVALID_GUEST_STATE\n"));
#endif /* VBOX_STRICT */
break;
}
case VMX_EXIT_ERR_MSR_LOAD: /* 34 VM-entry failure due to MSR loading. */
case VMX_EXIT_ERR_MACHINE_CHECK: /* 41 VM-entry failure due to machine-check. */
default:
break;
}
end:
if (fGuestStateSynced)
{
/* Remaining guest CPU context: TR, IDTR, GDTR, LDTR. */
/*
* System MSRs
*/
}
/* Signal changes for the recompiler. */
CPUMSetChangedFlags(pVM, CPUM_CHANGED_SYSENTER_MSR | CPUM_CHANGED_LDTR | CPUM_CHANGED_GDTR | CPUM_CHANGED_IDTR | CPUM_CHANGED_TR | CPUM_CHANGED_HIDDEN_SEL_REGS);
/* If we executed vmlaunch/vmresume and an external irq was pending, then we don't have to do a full sync the next time. */
if ( exitReason == VMX_EXIT_EXTERNAL_IRQ
{
/* On the next entry we'll only sync the host context. */
}
else
{
/* On the next entry we'll sync everything. */
/** @todo we can do better than this */
}
/* translate into a less severe return code */
if (rc == VERR_EM_INTERPRETER)
Log2(("X"));
return rc;
}
/**
* Enters the VT-x session
*
* @returns VBox status code.
* @param pVM The VM to operate on.
* @param pCpu CPU info struct
*/
{
if (!(cr4 & X86_CR4_VMXE))
{
AssertMsgFailed(("X86_CR4_VMXE should be set!\n"));
return VERR_VMX_X86_CR4_VMXE_CLEARED;
}
/* Activate the VM Control Structure. */
if (VBOX_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
/**
* Leaves the VT-x session
*
* @returns VBox status code.
* @param pVM The VM to operate on.
*/
{
/* Clear VM Control Structure. Marking it inactive, clearing implementation specific data and writing back VMCS data to memory. */
return VINF_SUCCESS;
}