HMVMXR0.cpp revision 648a82ca2600b27248667fe20f553884153aa18a
/* $Id$ */
/** @file
* HM VMX (Intel VT-x) - Host Context Ring-0.
*/
/*
* Copyright (C) 2012-2013 Oracle Corporation
*
* 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.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_HM
#include <iprt/asm-amd64-x86.h>
#include "HMInternal.h"
#include "HWVMXR0.h"
#ifdef VBOX_WITH_REM
#endif
#ifdef DEBUG_ramshankar
#define HMVMX_SAVE_FULL_GUEST_STATE
#define HMVMX_SYNC_FULL_GUEST_STATE
#define HMVMX_ALWAYS_TRAP_ALL_XCPTS
#define HMVMX_ALWAYS_TRAP_PF
#endif
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#define HMVMXHCUINTREG RTHCUINTREG
#if defined(RT_ARCH_AMD64)
# define HMVMX_IS_64BIT_HOST_MODE() (true)
#elif defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
# define HMVMX_IS_64BIT_HOST_MODE() (g_fVMXIs64bitHost != 0)
# define HMVMXHCUINTREG uint64_t
#else
# define HMVMX_IS_64BIT_HOST_MODE() (false)
#endif
/** Use the function table. */
#define HMVMX_USE_FUNCTION_TABLE
/** This bit indicates the segment selector is unusable in VT-x. */
/** Determine which tagged-TLB flush handler to use. */
#define HMVMX_FLUSH_TAGGED_TLB_EPT_VPID 0
#define HMVMX_FLUSH_TAGGED_TLB_EPT 1
#define HMVMX_FLUSH_TAGGED_TLB_VPID 2
#define HMVMX_FLUSH_TAGGED_TLB_NONE 3
/** Updated-guest-state flags. */
#define HMVMX_UPDATED_GUEST_RIP RT_BIT(0)
#define HMVMX_UPDATED_GUEST_ALL ( HMVMX_UPDATED_GUEST_RIP \
/**
* Flags to skip redundant reads of some common VMCS fields that are not part of
* the guest-CPU state but are in the transient structure.
*/
#define HMVMX_UPDATED_TRANSIENT_IDT_VECTORING_INFO RT_BIT(0)
/**
* Exception bitmap mask for real-mode guests (real-on-v86). We need to intercept all exceptions manually (except #PF).
* #NM is also handled spearetely, see hmR0VmxLoadGuestControlRegs(). #PF need not be intercepted even in real-mode if
* we have Nested Paging support.
*/
#define HMVMX_REAL_MODE_XCPT_MASK ( RT_BIT(X86_XCPT_DE) | RT_BIT(X86_XCPT_DB) | RT_BIT(X86_XCPT_NMI) \
| RT_BIT(X86_XCPT_XF))
/**
* Exception bitmap mask for all contributory exceptions.
*/
#define HMVMX_CONTRIBUTORY_XCPT_MASK ( RT_BIT(X86_XCPT_GP) | RT_BIT(X86_XCPT_NP) | RT_BIT(X86_XCPT_SS) | RT_BIT(X86_XCPT_TS) \
| RT_BIT(X86_XCPT_DE))
/** Maximum VM-instruction error number. */
#define HMVMX_INSTR_ERROR_MAX 28
/** Profiling macro. */
#ifdef HM_PROFILE_EXIT_DISPATCH
#else
# define HMVMX_START_EXIT_DISPATCH_PROF() do { } while (0)
# define HMVMX_STOP_EXIT_DISPATCH_PROF() do { } while (0)
#endif
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* A state structure for holding miscellaneous information across
* VMX non-root operation and restored after the transition.
*/
typedef struct VMXTRANSIENT
{
#if HC_ARCH_BITS == 32
#endif
/** The guest's LSTAR MSR value used for TPR patching for 32-bit guests. */
/** The guest's TPR value used for TPR shadowing. */
/** Alignment. */
/** The basic VM-exit reason. */
/** Alignment. */
/** The VM-exit interruption error code. */
/** The VM-exit exit qualification. */
#if GC_ARCH_BITS == 32
/** Alignment. */
#endif
/** The VM-exit interruption-information field. */
/** The VM-exit instruction-length field. */
/** Whether the VM-entry failed or not. */
bool fVMEntryFailed;
/** Alignment. */
/** The VM-entry interruption-information field. */
/** The VM-entry exception error code field. */
/** The VM-entry instruction length field. */
/** IDT-vectoring information field. */
/** IDT-vectoring error code. */
/** Mask of currently read VMCS fields; HMVMX_UPDATED_TRANSIENT_*. */
/** Whether TSC-offsetting should be setup before VM-entry. */
/** Whether the VM-exit was caused by a page-fault during delivery of a
* contributary exception or a page-fault. */
bool fVectoringPF;
/**
* MSR-bitmap read permissions.
*/
typedef enum VMXMSREXITREAD
{
/** Reading this MSR causes a VM-exit. */
VMXMSREXIT_INTERCEPT_READ = 0xb,
/** Reading this MSR does not cause a VM-exit. */
/**
* MSR-bitmap write permissions.
*/
typedef enum VMXMSREXITWRITE
{
/** Writing to this MSR causes a VM-exit. */
VMXMSREXIT_INTERCEPT_WRITE = 0xd,
/** Writing to this MSR does not cause a VM-exit. */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static int hmR0VmxInjectEventVmcs(PVMCPU pVCpu, PCPUMCTX pMixedCtx, uint64_t u64IntrInfo, uint32_t cbInstr,
#endif
#ifndef HMVMX_USE_FUNCTION_TABLE
DECLINLINE(int) hmR0VmxHandleExit(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient, uint32_t rcReason);
#define HMVMX_EXIT_DECL static int
#else
#define HMVMX_EXIT_DECL static DECLCALLBACK(int)
#endif
HMVMX_EXIT_DECL hmR0VmxExitTripleFault(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
HMVMX_EXIT_DECL hmR0VmxExitInitSignal(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
HMVMX_EXIT_DECL hmR0VmxExitIntWindow(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
HMVMX_EXIT_DECL hmR0VmxExitNmiWindow(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
HMVMX_EXIT_DECL hmR0VmxExitTaskSwitch(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
HMVMX_EXIT_DECL hmR0VmxExitSetPendingXcptUD(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
HMVMX_EXIT_DECL hmR0VmxExitErrInvalidGuestState(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
HMVMX_EXIT_DECL hmR0VmxExitErrMsrLoad(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
HMVMX_EXIT_DECL hmR0VmxExitErrUndefined(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
HMVMX_EXIT_DECL hmR0VmxExitErrMachineCheck(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
HMVMX_EXIT_DECL hmR0VmxExitTprBelowThreshold(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
HMVMX_EXIT_DECL hmR0VmxExitApicAccess(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
HMVMX_EXIT_DECL hmR0VmxExitXdtrAccess(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
HMVMX_EXIT_DECL hmR0VmxExitXdtrAccess(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
HMVMX_EXIT_DECL hmR0VmxExitEptViolation(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
HMVMX_EXIT_DECL hmR0VmxExitEptMisconfig(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
HMVMX_EXIT_DECL hmR0VmxExitPreemptTimer(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
/*******************************************************************************
* Global Variables *
*******************************************************************************/
#ifdef HMVMX_USE_FUNCTION_TABLE
/**
* VM-exit handler.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required
* fields before using them.
* @param pVmxTransient Pointer to the VMX-transient structure.
*/
typedef DECLCALLBACK(int) FNVMEXITHANDLER(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient);
/** Pointer to VM-exit handler. */
typedef FNVMEXITHANDLER *const PFNVMEXITHANDLER;
/**
* VMX_EXIT dispatch table.
*/
{
/* 00 VMX_EXIT_XCPT_NMI */ hmR0VmxExitXcptNmi,
/* 01 VMX_EXIT_EXT_INT */ hmR0VmxExitExtInt,
/* 02 VMX_EXIT_TRIPLE_FAULT */ hmR0VmxExitTripleFault,
/* 03 VMX_EXIT_INIT_SIGNAL */ hmR0VmxExitInitSignal,
/* 04 VMX_EXIT_SIPI */ hmR0VmxExitSipi,
/* 05 VMX_EXIT_IO_SMI */ hmR0VmxExitIoSmi,
/* 06 VMX_EXIT_SMI */ hmR0VmxExitSmi,
/* 07 VMX_EXIT_INT_WINDOW */ hmR0VmxExitIntWindow,
/* 08 VMX_EXIT_NMI_WINDOW */ hmR0VmxExitNmiWindow,
/* 09 VMX_EXIT_TASK_SWITCH */ hmR0VmxExitTaskSwitch,
/* 10 VMX_EXIT_CPUID */ hmR0VmxExitCpuid,
/* 11 VMX_EXIT_GETSEC */ hmR0VmxExitGetsec,
/* 12 VMX_EXIT_HLT */ hmR0VmxExitHlt,
/* 13 VMX_EXIT_INVD */ hmR0VmxExitInvd,
/* 14 VMX_EXIT_INVLPG */ hmR0VmxExitInvlpg,
/* 15 VMX_EXIT_RDPMC */ hmR0VmxExitRdpmc,
/* 16 VMX_EXIT_RDTSC */ hmR0VmxExitRdtsc,
/* 17 VMX_EXIT_RSM */ hmR0VmxExitRsm,
/* 18 VMX_EXIT_VMCALL */ hmR0VmxExitSetPendingXcptUD,
/* 19 VMX_EXIT_VMCLEAR */ hmR0VmxExitSetPendingXcptUD,
/* 20 VMX_EXIT_VMLAUNCH */ hmR0VmxExitSetPendingXcptUD,
/* 21 VMX_EXIT_VMPTRLD */ hmR0VmxExitSetPendingXcptUD,
/* 22 VMX_EXIT_VMPTRST */ hmR0VmxExitSetPendingXcptUD,
/* 23 VMX_EXIT_VMREAD */ hmR0VmxExitSetPendingXcptUD,
/* 24 VMX_EXIT_VMRESUME */ hmR0VmxExitSetPendingXcptUD,
/* 25 VMX_EXIT_VMWRITE */ hmR0VmxExitSetPendingXcptUD,
/* 26 VMX_EXIT_VMXOFF */ hmR0VmxExitSetPendingXcptUD,
/* 27 VMX_EXIT_VMXON */ hmR0VmxExitSetPendingXcptUD,
/* 28 VMX_EXIT_MOV_CRX */ hmR0VmxExitMovCRx,
/* 29 VMX_EXIT_MOV_DRX */ hmR0VmxExitMovDRx,
/* 30 VMX_EXIT_IO_INSTR */ hmR0VmxExitIoInstr,
/* 31 VMX_EXIT_RDMSR */ hmR0VmxExitRdmsr,
/* 32 VMX_EXIT_WRMSR */ hmR0VmxExitWrmsr,
/* 33 VMX_EXIT_ERR_INVALID_GUEST_STATE */ hmR0VmxExitErrInvalidGuestState,
/* 34 VMX_EXIT_ERR_MSR_LOAD */ hmR0VmxExitErrMsrLoad,
/* 35 UNDEFINED */ hmR0VmxExitErrUndefined,
/* 36 VMX_EXIT_MWAIT */ hmR0VmxExitMwait,
/* 37 VMX_EXIT_MTF */ hmR0VmxExitMtf,
/* 38 UNDEFINED */ hmR0VmxExitErrUndefined,
/* 39 VMX_EXIT_MONITOR */ hmR0VmxExitMonitor,
/* 40 UNDEFINED */ hmR0VmxExitPause,
/* 41 VMX_EXIT_PAUSE */ hmR0VmxExitErrMachineCheck,
/* 42 VMX_EXIT_ERR_MACHINE_CHECK */ hmR0VmxExitErrUndefined,
/* 43 VMX_EXIT_TPR_BELOW_THRESHOLD */ hmR0VmxExitTprBelowThreshold,
/* 44 VMX_EXIT_APIC_ACCESS */ hmR0VmxExitApicAccess,
/* 45 UNDEFINED */ hmR0VmxExitErrUndefined,
/* 46 VMX_EXIT_XDTR_ACCESS */ hmR0VmxExitXdtrAccess,
/* 47 VMX_EXIT_TR_ACCESS */ hmR0VmxExitXdtrAccess,
/* 48 VMX_EXIT_EPT_VIOLATION */ hmR0VmxExitEptViolation,
/* 49 VMX_EXIT_EPT_MISCONFIG */ hmR0VmxExitEptMisconfig,
/* 50 VMX_EXIT_INVEPT */ hmR0VmxExitSetPendingXcptUD,
/* 51 VMX_EXIT_RDTSCP */ hmR0VmxExitRdtscp,
/* 52 VMX_EXIT_PREEMPT_TIMER */ hmR0VmxExitPreemptTimer,
/* 53 VMX_EXIT_INVVPID */ hmR0VmxExitSetPendingXcptUD,
/* 54 VMX_EXIT_WBINVD */ hmR0VmxExitWbinvd,
/* 55 VMX_EXIT_XSETBV */ hmR0VmxExitXsetbv,
/* 56 UNDEFINED */ hmR0VmxExitErrUndefined,
/* 57 VMX_EXIT_RDRAND */ hmR0VmxExitRdrand,
/* 58 VMX_EXIT_INVPCID */ hmR0VmxExitInvpcid,
/* 59 VMX_EXIT_VMFUNC */ hmR0VmxExitSetPendingXcptUD
};
#endif /* HMVMX_USE_FUNCTION_TABLE */
#ifdef VBOX_STRICT
{
/* 0 */ "(Not Used)",
/* 1 */ "VMCALL executed in VMX root operation.",
/* 2 */ "VMCLEAR with invalid physical address.",
/* 3 */ "VMCLEAR with VMXON pointer.",
/* 4 */ "VMLAUNCH with non-clear VMCS.",
/* 5 */ "VMRESUME with non-launched VMCS.",
/* 6 */ "VMRESUME after VMXOFF",
/* 7 */ "VM entry with invalid control fields.",
/* 8 */ "VM entry with invalid host state fields.",
/* 9 */ "VMPTRLD with invalid physical address.",
/* 10 */ "VMPTRLD with VMXON pointer.",
/* 11 */ "VMPTRLD with incorrect revision identifier.",
/* 13 */ "VMWRITE to read-only VMCS component.",
/* 14 */ "(Not Used)",
/* 15 */ "VMXON executed in VMX root operation.",
/* 16 */ "VM entry with invalid executive-VMCS pointer.",
/* 17 */ "VM entry with non-launched executing VMCS.",
/* 18 */ "VM entry with executive-VMCS pointer not VMXON pointer.",
/* 19 */ "VMCALL with non-clear VMCS.",
/* 20 */ "VMCALL with invalid VM-exit control fields.",
/* 21 */ "(Not Used)",
/* 22 */ "VMCALL with incorrect MSEG revision identifier.",
/* 23 */ "VMXOFF under dual monitor treatment of SMIs and SMM.",
/* 24 */ "VMCALL with invalid SMM-monitor features.",
/* 25 */ "VM entry with invalid VM-execution control fields in executive VMCS.",
/* 26 */ "VM entry with events blocked by MOV SS.",
/* 27 */ "(Not Used)",
};
#endif /* VBOX_STRICT */
/**
* Updates the VM's last error record. If there was a VMX instruction error,
* reads the error data from the VMCS and updates VCPU's last error record as
* well.
*
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU (can be NULL if @a rc is not
* VERR_VMX_UNABLE_TO_START_VM or
* VERR_VMX_INVALID_VMCS_FIELD).
* @param rc The error code.
*/
{
if ( rc == VERR_VMX_INVALID_VMCS_FIELD
|| rc == VERR_VMX_UNABLE_TO_START_VM)
{
}
}
/**
* Reads the VM-entry interruption-information field from the VMCS into the VMX
* transient structure.
*
* @returns VBox status code.
* @param pVmxTransient Pointer to the VMX transient structure.
*
* @remarks No-long-jump zone!!!
*/
{
return VINF_SUCCESS;
}
/**
* Reads the VM-entry exception error code field from the VMCS into
* the VMX transient structure.
*
* @returns VBox status code.
* @param pVmxTransient Pointer to the VMX transient structure.
*
* @remarks No-long-jump zone!!!
*/
{
int rc = VMXReadVmcs32(VMX_VMCS32_CTRL_ENTRY_EXCEPTION_ERRCODE, &pVmxTransient->uEntryXcptErrorCode);
return VINF_SUCCESS;
}
/**
* Reads the VM-entry exception error code field from the VMCS into
* the VMX transient structure.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pVmxTransient Pointer to the VMX transient structure.
*
* @remarks No-long-jump zone!!!
*/
{
return VINF_SUCCESS;
}
/**
* Reads the VM-exit interruption-information field from the VMCS into the VMX
* transient structure.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pVmxTransient Pointer to the VMX transient structure.
*/
{
{
}
return VINF_SUCCESS;
}
/**
* Reads the VM-exit interruption error code from the VMCS into the VMX
* transient structure.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pVmxTransient Pointer to the VMX transient structure.
*/
{
{
int rc = VMXReadVmcs32(VMX_VMCS32_RO_EXIT_INTERRUPTION_ERROR_CODE, &pVmxTransient->uExitIntrErrorCode);
}
return VINF_SUCCESS;
}
/**
* Reads the VM-exit instruction length field from the VMCS into the VMX
* transient structure.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pVmxTransient Pointer to the VMX transient structure.
*/
{
{
}
return VINF_SUCCESS;
}
/**
* Reads the exit qualification from the VMCS into the VMX transient structure.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pVmxTransient Pointer to the VMX transient structure.
*/
{
{
}
return VINF_SUCCESS;
}
/**
* Reads the IDT-vectoring information field from the VMCS into the VMX
* transient structure.
*
* @returns VBox status code.
* @param pVmxTransient Pointer to the VMX transient structure.
*
* @remarks No-long-jump zone!!!
*/
{
{
}
return VINF_SUCCESS;
}
/**
* Reads the IDT-vectoring error code from the VMCS into the VMX
* transient structure.
*
* @returns VBox status code.
* @param pVmxTransient Pointer to the VMX transient structure.
*/
{
{
}
return VINF_SUCCESS;
}
/**
* Enters VMX root mode operation on the current CPU.
*
* @returns VBox status code.
* @param pVM Pointer to the VM (optional, can be NULL, after
* a resume).
* @param HCPhysCpuPage Physical address of the VMXON region.
* @param pvCpuPage Pointer to the VMXON region.
*/
{
if (pVM)
{
/* Write the VMCS revision dword to the VMXON region. */
}
/* Enable the VMX bit in CR4 if necessary. */
if (!(uCr4 & X86_CR4_VMXE))
/* Enter VMX root mode. */
if (RT_FAILURE(rc))
return rc;
}
/**
* Exits VMX root mode operation on the current CPU.
*
* @returns VBox status code.
*/
static int hmR0VmxLeaveRootMode(void)
{
/* If we're for some reason not in VMX root mode, then don't leave it. */
if (ASMGetCR4() & X86_CR4_VMXE)
{
/* Exit VMX root mode and clear the VMX bit in CR4 */
VMXDisable();
return VINF_SUCCESS;
}
return VERR_VMX_NOT_IN_VMX_ROOT_MODE;
}
/**
* Allocates and maps one physically contiguous page. The allocated page is
* zero'd out. (Used by various VT-x structures).
*
* @returns IPRT status code.
* @param pMemObj Pointer to the ring-0 memory object.
* @param ppVirt Where to store the virtual address of the
* allocation.
* @param pPhys Where to store the physical address of the
* allocation.
*/
{
if (RT_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
/**
* Frees and unmaps an allocated physical page.
*
* @param pMemObj Pointer to the ring-0 memory object.
* @param ppVirt Where to re-initialize the virtual address of
* allocation as 0.
* @param pHCPhys Where to re-initialize the physical address of the
* allocation as 0.
*/
{
if (*pMemObj != NIL_RTR0MEMOBJ)
{
*ppVirt = 0;
*pHCPhys = 0;
}
}
/**
* Worker function to free VT-x related structures.
*
* @returns IPRT status code.
* @param pVM Pointer to the VM.
*/
{
{
hmR0VmxPageFree(&pVCpu->hm.s.vmx.hMemObjHostMsr, &pVCpu->hm.s.vmx.pvHostMsr, &pVCpu->hm.s.vmx.HCPhysHostMsr);
hmR0VmxPageFree(&pVCpu->hm.s.vmx.hMemObjGuestMsr, &pVCpu->hm.s.vmx.pvGuestMsr, &pVCpu->hm.s.vmx.HCPhysGuestMsr);
#endif
hmR0VmxPageFree(&pVCpu->hm.s.vmx.hMemObjMsrBitmap, &pVCpu->hm.s.vmx.pvMsrBitmap, &pVCpu->hm.s.vmx.HCPhysMsrBitmap);
hmR0VmxPageFree(&pVCpu->hm.s.vmx.hMemObjVirtApic, (PRTR0PTR)&pVCpu->hm.s.vmx.pbVirtApic, &pVCpu->hm.s.vmx.HCPhysVirtApic);
hmR0VmxPageFree(&pVCpu->hm.s.vmx.hMemObjVmcs, &pVCpu->hm.s.vmx.pvVmcs, &pVCpu->hm.s.vmx.HCPhysVmcs);
}
hmR0VmxPageFree(&pVM->hm.s.vmx.hMemObjApicAccess, (PRTR0PTR)&pVM->hm.s.vmx.pbApicAccess, &pVM->hm.s.vmx.HCPhysApicAccess);
#ifdef VBOX_WITH_CRASHDUMP_MAGIC
hmR0VmxPageFree(&pVM->hm.s.vmx.hMemObjScratch, &pVM->hm.s.vmx.pbScratch, &pVM->hm.s.vmx.HCPhysScratch);
#endif
}
/**
* Worker function to allocate VT-x related VM structures.
*
* @returns IPRT status code.
* @param pVM Pointer to the VM.
*/
{
/*
* Initialize members up-front so we can cleanup properly on allocation failure.
*/
#ifdef VBOX_WITH_CRASHDUMP_MAGIC
#endif
{
#endif
}
/*
* Allocate all the VT-x structures.
*/
int rc = VINF_SUCCESS;
#ifdef VBOX_WITH_CRASHDUMP_MAGIC
rc = hmR0VmxPageAllocZ(&pVM->hm.s.vmx.hMemObjScratch, &pVM->hm.s.vmx.pbScratch, &pVM->hm.s.vmx.HCPhysScratch);
if (RT_FAILURE(rc))
goto cleanup;
#endif
/* Allocate the APIC-access page for trapping APIC accesses from the guest. */
{
if (RT_FAILURE(rc))
goto cleanup;
}
/*
* Initialize per-VCPU VT-x structures.
*/
{
/* Allocate the VM control structure (VMCS). */
AssertReturn(MSR_IA32_VMX_BASIC_INFO_VMCS_SIZE(pVM->hm.s.vmx.msr.vmx_basic_info) <= PAGE_SIZE, VERR_INTERNAL_ERROR);
rc = hmR0VmxPageAllocZ(&pVCpu->hm.s.vmx.hMemObjVmcs, &pVCpu->hm.s.vmx.pvVmcs, &pVCpu->hm.s.vmx.HCPhysVmcs);
if (RT_FAILURE(rc))
goto cleanup;
/* Allocate the Virtual-APIC page for transparent TPR accesses. */
{
if (RT_FAILURE(rc))
goto cleanup;
}
/* Allocate the MSR-bitmap if supported by the CPU. The MSR-bitmap is for transparent accesses of specific MSRs. */
{
if (RT_FAILURE(rc))
goto cleanup;
}
/* Allocate the VM-entry MSR-load and VM-exit MSR-store page for the guest MSRs. */
rc = hmR0VmxPageAllocZ(&pVCpu->hm.s.vmx.hMemObjGuestMsr, &pVCpu->hm.s.vmx.pvGuestMsr, &pVCpu->hm.s.vmx.HCPhysGuestMsr);
if (RT_FAILURE(rc))
goto cleanup;
/* Allocate the VM-exit MSR-load page for the host MSRs. */
rc = hmR0VmxPageAllocZ(&pVCpu->hm.s.vmx.hMemObjHostMsr, &pVCpu->hm.s.vmx.pvHostMsr, &pVCpu->hm.s.vmx.HCPhysHostMsr);
if (RT_FAILURE(rc))
goto cleanup;
#endif
}
return VINF_SUCCESS;
return rc;
}
/**
* Does global VT-x initialization (called during module initialization).
*
* @returns VBox status code.
*/
VMMR0DECL(int) VMXR0GlobalInit(void)
{
#ifdef HMVMX_USE_FUNCTION_TABLE
# ifdef VBOX_STRICT
for (unsigned i = 0; i < RT_ELEMENTS(g_apfnVMExitHandlers); i++)
# endif
#endif
return VINF_SUCCESS;
}
/**
* Does global VT-x termination (called during module termination).
*/
VMMR0DECL(void) VMXR0GlobalTerm()
{
/* Nothing to do currently. */
}
/**
* Sets up and activates VT-x on the current CPU.
*
* @returns VBox status code.
* @param pCpu Pointer to the global CPU info struct.
* @param pVM Pointer to the VM (can be NULL after a host resume
* operation).
* @param pvCpuPage Pointer to the VMXON region (can be NULL if @a
* fEnabledByHost is true).
* @param HCPhysCpuPage Physical address of the VMXON region (can be 0 if
* @a fEnabledByHost is true).
* @param fEnabledByHost Set if SUPR0EnableVTx() or similar was used to
*/
VMMR0DECL(int) VMXR0EnableCpu(PHMGLOBLCPUINFO pCpu, PVM pVM, void *pvCpuPage, RTHCPHYS HCPhysCpuPage, bool fEnabledByHost)
{
if (!fEnabledByHost)
{
if (RT_FAILURE(rc))
return rc;
}
/*
* Flush all VPIDs (in case we or any other hypervisor have been using VPIDs) so that
* we can avoid an explicit flush while using new VPIDs. We would still need to flush
* each time while reusing a VPID after hitting the MaxASID limit once.
*/
if ( pVM
{
pCpu->fFlushAsidBeforeUse = false;
}
else
pCpu->fFlushAsidBeforeUse = true;
/* Ensure each VCPU scheduled on this CPU gets a new VPID on resume. See @bugref{6255}. */
++pCpu->cTlbFlushes;
return VINF_SUCCESS;
}
/**
* Deactivates VT-x on the current CPU.
*
* @returns VBox status code.
* @param pCpu Pointer to the global CPU info struct.
* @param pvCpuPage Pointer to the VMXON region.
* @param HCPhysCpuPage Physical address of the VMXON region.
*/
{
return VINF_SUCCESS;
}
/**
* Sets the permission bits for the specified MSR in the MSR bitmap.
*
* @param pVCpu Pointer to the VMCPU.
* @param uMSR The MSR value.
* @param enmRead Whether reading this MSR causes a VM-exit.
* @param enmWrite Whether writing this MSR causes a VM-exit.
*/
static void hmR0VmxSetMsrPermission(PVMCPU pVCpu, uint32_t uMsr, VMXMSREXITREAD enmRead, VMXMSREXITWRITE enmWrite)
{
/*
* Layout:
* 0x000 - 0x3ff - Low MSR read bits
* 0x400 - 0x7ff - High MSR read bits
* 0x800 - 0xbff - Low MSR write bits
* 0xc00 - 0xfff - High MSR write bits
*/
if (uMsr <= 0x00001FFF)
else if ( uMsr >= 0xC0000000
&& uMsr <= 0xC0001FFF)
{
pbMsrBitmap += 0x400;
}
else
{
return;
}
if (enmRead == VMXMSREXIT_INTERCEPT_READ)
else
if (enmWrite == VMXMSREXIT_INTERCEPT_WRITE)
else
}
/**
* Flushes the TLB using EPT.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param enmFlush Type of flush.
*/
{
AssertMsg(rc == VINF_SUCCESS, ("VMXR0InvEPT %#x %RGv failed with %Rrc\n", enmFlush, pVCpu->hm.s.vmx.HCPhysEPTP, rc));
}
/**
* Flushes the TLB using VPID.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU (can be NULL depending on @a
* enmFlush).
* @param enmFlush Type of flush.
* @param GCPtr Virtual address of the page to flush (can be 0 depending
* on @a enmFlush).
*/
{
if (enmFlush == VMX_FLUSH_VPID_ALL_CONTEXTS)
{
descriptor[0] = 0;
descriptor[1] = 0;
}
else
{
AssertMsg(pVCpu->hm.s.uCurrentAsid != 0, ("VMXR0InvVPID: invalid ASID %lu\n", pVCpu->hm.s.uCurrentAsid));
AssertMsg(pVCpu->hm.s.uCurrentAsid <= UINT16_MAX, ("VMXR0InvVPID: invalid ASID %lu\n", pVCpu->hm.s.uCurrentAsid));
}
("VMXR0InvVPID %#x %u %RGv failed with %d\n", enmFlush, pVCpu ? pVCpu->hm.s.uCurrentAsid : 0, GCPtr, rc));
if ( RT_SUCCESS(rc)
&& pVCpu)
{
}
}
/**
* Invalidates a guest page by guest virtual address. Only relevant for
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param GCVirt Guest virtual address of the page to invalidate.
*/
{
if (!fFlushPending)
{
/*
* We must invalidate the guest TLB entry in either case, we cannot ignore it even for the EPT case
* See @bugref{6043} and @bugref{6177}.
*
* Set the VMCPU_FF_TLB_FLUSH force flag and flush before VM-entry in hmR0VmxFlushTLB*() as this
* function maybe called in a loop with individual addresses.
*/
{
{
}
else
}
}
return VINF_SUCCESS;
}
/**
* otherwise there is nothing really to invalidate.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param GCPhys Guest physical address of the page to invalidate.
*/
{
/*
* We cannot flush a page by guest-physical address. invvpid takes only a linear address while invept only flushes
* by EPT not individual addresses. We update the force flag here and flush before the next VM-entry in hmR0VmxFlushTLB*().
* This function might be called in a loop.
*/
return VINF_SUCCESS;
}
/**
* Dummy placeholder for tagged-TLB flush handling before VM-entry. Used in the
* case where neither EPT nor VPID is supported by the CPU.
*
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
*
* @remarks Called with interrupts disabled.
*/
{
return;
}
/**
* Flushes the tagged-TLB entries for EPT+VPID CPUs as necessary.
*
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @remarks All references to "ASID" in this function pertains to "VPID" in
* Intel's nomenclature. The reason is, to avoid confusion in compare
* statements since the host-CPU copies are named "ASID".
*
* @remarks Called with interrupts disabled.
*/
{
("hmR0VmxFlushTaggedTlbBoth cannot be invoked unless NestedPaging & VPID are enabled."
/*
* Force a TLB flush for the first world-switch if the current CPU differs from the one we ran on last.
* This can happen both for start & resume due to long jumps back to ring-3.
* If the TLB flush count changed, another VM (VCPU rather) has hit the ASID limit while flushing the TLB
*/
bool fNewASID = false;
{
fNewASID = true;
}
/*
* Check for explicit TLB shootdowns.
*/
{
}
{
if (fNewASID)
{
++pCpu->uCurrentAsid;
{
pCpu->cTlbFlushes++;
pCpu->fFlushAsidBeforeUse = true;
}
if (pCpu->fFlushAsidBeforeUse)
}
else
{
else
}
}
else
{
("hm->uCurrentAsid=%lu hm->cTlbFlushes=%lu cpu->uCurrentAsid=%lu cpu->cTlbFlushes=%lu\n",
/** @todo We never set VMCPU_FF_TLB_SHOOTDOWN anywhere so this path should
* not be executed. See hmQueueInvlPage() where it is commented
* out. Support individual entry flushing someday. */
{
/*
* Flush individual guest entries using VPID from the TLB or as little as possible with EPT
* as supported by the CPU.
*/
{
}
else
}
else
}
("Flush count mismatch for cpu %d (%u vs %u)\n", pCpu->idCpu, pVCpu->hm.s.cTlbFlushes, pCpu->cTlbFlushes));
/* Update VMCS with the VPID. */
}
/**
* Flushes the tagged-TLB entries for EPT CPUs as necessary.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
*
* @remarks Called with interrupts disabled.
*/
{
AssertMsg(pVM->hm.s.fNestedPaging, ("hmR0VmxFlushTaggedTlbEpt cannot be invoked with NestedPaging disabled."));
/*
* Force a TLB flush for the first world-switch if the current CPU differs from the one we ran on last.
* This can happen both for start & resume due to long jumps back to ring-3.
*/
{
}
/* Check for explicit TLB shootdown flushes. */
{
}
{
}
else
{
/** @todo We never set VMCPU_FF_TLB_SHOOTDOWN anywhere so this path should
* not be executed. See hmQueueInvlPage() where it is commented
* out. Support individual entry flushing someday. */
{
/* We cannot flush individual entries without VPID support. Flush using EPT. */
}
else
}
}
/**
* Flushes the tagged-TLB entries for VPID CPUs as necessary.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
*
* @remarks Called with interrupts disabled.
*/
{
AssertMsg(!pVM->hm.s.fNestedPaging, ("hmR0VmxFlushTlbVpid cannot be invoked with NestedPaging enabled"));
/*
* Force a TLB flush for the first world switch if the current CPU differs from the one we ran on last.
* This can happen both for start & resume due to long jumps back to ring-3.
* If the TLB flush count changed, another VM (VCPU rather) has hit the ASID limit while flushing the TLB
*/
{
}
/* Check for explicit TLB shootdown flushes. */
{
/*
* If we ever support VPID flush combinations other than ALL or SINGLE-context (see hmR0VmxSetupTaggedTlb())
* we would need to explicitly flush in this case (add an fExplicitFlush = true here and change the
* pCpu->fFlushAsidBeforeUse check below to include fExplicitFlush's too) - an obscure corner case.
*/
}
{
++pCpu->uCurrentAsid;
{
pCpu->fFlushAsidBeforeUse = true;
pCpu->cTlbFlushes++;
}
if (pCpu->fFlushAsidBeforeUse)
}
else
{
("hm->uCurrentAsid=%lu hm->cTlbFlushes=%lu cpu->uCurrentAsid=%lu cpu->cTlbFlushes=%lu\n",
/** @todo We never set VMCPU_FF_TLB_SHOOTDOWN anywhere so this path should
* not be executed. See hmQueueInvlPage() where it is commented
* out. Support individual entry flushing someday. */
{
/* Flush individual guest entries using VPID or as little as possible with EPT as supported by the CPU. */
{
}
else
}
else
}
("Flush count mismatch for cpu %d (%u vs %u)\n", pCpu->idCpu, pVCpu->hm.s.cTlbFlushes, pCpu->cTlbFlushes));
}
/**
* Flushes the guest TLB entry based on CPU capabilities.
*
* @param pVCpu Pointer to the VMCPU.
*/
{
{
default:
AssertMsgFailed(("Invalid flush-tag function identifier\n"));
break;
}
}
/**
* Sets up the appropriate tagged TLB-flush level and handler for flushing guest
* TLB entries from the host TLB before VM-entry.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
*/
{
/*
* Determine optimal flush type for nested paging.
* We cannot ignore EPT if no suitable flush-types is supported by the CPU as we've already setup unrestricted
* guest execution (see hmR3InitFinalizeR0()).
*/
{
{
else
{
/* Shouldn't happen. EPT is supported but no suitable flush-types supported. */
}
/* Make sure the write-back cacheable memory type for EPT is supported. */
{
LogRel(("hmR0VmxSetupTaggedTlb: Unsupported EPTP memory type %#x.\n", pVM->hm.s.vmx.msr.vmx_ept_vpid_caps));
}
}
else
{
/* Shouldn't happen. EPT is supported but INVEPT instruction is not supported. */
}
}
/*
* Determine optimal flush type for VPID.
*/
{
{
else
{
/* Neither SINGLE nor ALL-context flush types for VPID is supported by the CPU. Ignore VPID capability. */
LogRel(("hmR0VmxSetupTaggedTlb: Only INDIV_ADDR supported. Ignoring VPID.\n"));
if (pVM->hm.s.vmx.msr.vmx_ept_vpid_caps & MSR_IA32_VMX_EPT_VPID_CAP_INVVPID_SINGLE_CONTEXT_RETAIN_GLOBALS)
LogRel(("hmR0VmxSetupTaggedTlb: Only SINGLE_CONTEXT_RETAIN_GLOBALS supported. Ignoring VPID.\n"));
}
}
else
{
/* Shouldn't happen. VPID is supported but INVVPID is not supported by the CPU. Ignore VPID capability. */
Log(("hmR0VmxSetupTaggedTlb: VPID supported without INVEPT support. Ignoring VPID.\n"));
}
}
/*
* Setup the handler for flushing tagged-TLBs.
*/
else
return VINF_SUCCESS;
}
/**
* Sets up pin-based VM-execution controls in the VMCS.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
*/
{
uint32_t val = pVM->hm.s.vmx.msr.vmx_pin_ctls.n.disallowed0; /* Bits set here must always be set. */
uint32_t zap = pVM->hm.s.vmx.msr.vmx_pin_ctls.n.allowed1; /* Bits cleared here must always be cleared. */
| VMX_VMCS_CTRL_PIN_EXEC_CONTROLS_NMI_EXIT; /* Non-maskable interrupts causes a VM-exit. */
/* Enable the VMX preemption timer. */
{
}
{
LogRel(("hmR0VmxSetupPinCtls: invalid pin-based VM-execution controls combo! cpu=%#RX64 val=%#RX64 zap=%#RX64\n",
}
/* Update VCPU with the currently set pin-based VM-execution controls. */
return rc;
}
/**
* Sets up processor-based VM-execution controls in the VMCS.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVMCPU Pointer to the VMCPU.
*/
{
int rc = VERR_INTERNAL_ERROR_5;
uint32_t val = pVM->hm.s.vmx.msr.vmx_proc_ctls.n.disallowed0; /* Bits set here must be set in the VMCS. */
uint32_t zap = pVM->hm.s.vmx.msr.vmx_proc_ctls.n.allowed1; /* Bits cleared here must be cleared in the VMCS. */
| VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_USE_TSC_OFFSETTING /* Use TSC-offsetting. */
| VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_MOV_DR_EXIT /* MOV DRx causes a VM-exit. */
| VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_UNCOND_IO_EXIT /* All IO instructions cause a VM-exit. */
| VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_RDPMC_EXIT /* RDPMC causes a VM-exit. */
| VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_MONITOR_EXIT /* MONITOR causes a VM-exit. */
| VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_MWAIT_EXIT; /* MWAIT causes a VM-exit. */
/* We toggle VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_MOV_DR_EXIT later, check if it's not -always- needed to be set or clear. */
{
LogRel(("hmR0VmxSetupProcCtls: unsupported VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_MOV_DR_EXIT combo!"));
}
/* Without nested paging, INVLPG (also affects INVPCID) and MOV CR3 instructions should cause VM-exits. */
{
}
/* Use TPR shadowing if supported by the CPU. */
{
/* CR8 writes causes a VM-exit based on TPR threshold. */
}
else
{
| VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_CR8_LOAD_EXIT; /* CR8 writes causes a VM-exit. */
}
/* Use MSR-bitmaps if supported by the CPU. */
{
/*
* The guest can access the following MSRs (read, write) without causing VM-exits; they are loaded/stored
*/
hmR0VmxSetMsrPermission(pVCpu, MSR_IA32_SYSENTER_CS, VMXMSREXIT_PASSTHRU_READ, VMXMSREXIT_PASSTHRU_WRITE);
hmR0VmxSetMsrPermission(pVCpu, MSR_IA32_SYSENTER_ESP, VMXMSREXIT_PASSTHRU_READ, VMXMSREXIT_PASSTHRU_WRITE);
hmR0VmxSetMsrPermission(pVCpu, MSR_IA32_SYSENTER_EIP, VMXMSREXIT_PASSTHRU_READ, VMXMSREXIT_PASSTHRU_WRITE);
hmR0VmxSetMsrPermission(pVCpu, MSR_K8_SF_MASK, VMXMSREXIT_PASSTHRU_READ, VMXMSREXIT_PASSTHRU_WRITE);
hmR0VmxSetMsrPermission(pVCpu, MSR_K8_KERNEL_GS_BASE, VMXMSREXIT_PASSTHRU_READ, VMXMSREXIT_PASSTHRU_WRITE);
hmR0VmxSetMsrPermission(pVCpu, MSR_K8_GS_BASE, VMXMSREXIT_PASSTHRU_READ, VMXMSREXIT_PASSTHRU_WRITE);
hmR0VmxSetMsrPermission(pVCpu, MSR_K8_FS_BASE, VMXMSREXIT_PASSTHRU_READ, VMXMSREXIT_PASSTHRU_WRITE);
}
/* Use the secondary processor-based VM-execution controls if supported by the CPU. */
{
LogRel(("hmR0VmxSetupProcCtls: invalid processor-based VM-execution controls combo! cpu=%#RX64 val=%#RX64 zap=%#RX64\n",
}
/* Update VCPU with the currently set processor-based VM-execution controls. */
/*
* Secondary processor-based VM-execution controls.
*/
{
zap = pVM->hm.s.vmx.msr.vmx_proc_ctls2.n.allowed1; /* Bits cleared here must be cleared in the VMCS. */
else
{
/*
* Without Nested Paging, INVPCID should cause a VM-exit. Enabling this bit causes the CPU to refer to
* VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_INVLPG_EXIT when INVPCID is executed by the guest.
* See Intel spec. 25.4 "Changes to instruction behaviour in VMX non-root operation".
*/
}
/* Enable Virtual-APIC page accesses if supported by the CPU. This is essentially where the TPR shadow resides. */
/** @todo VIRT_X2APIC support, it's mutually exclusive with this. So must be
* done dynamically. */
{
}
{
hmR0VmxSetMsrPermission(pVCpu, MSR_K8_TSC_AUX, VMXMSREXIT_PASSTHRU_READ, VMXMSREXIT_PASSTHRU_WRITE);
}
{
LogRel(("hmR0VmxSetupProcCtls: invalid secondary processor-based VM-execution controls combo! "
}
/* Update VCPU with the currently set secondary processor-based VM-execution controls. */
}
return VINF_SUCCESS;
}
/**
* Sets up miscellaneous (everything other than Pin & Processor-based
* VM-execution) control fields in the VMCS.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
*/
{
int rc = VERR_GENERAL_FAILURE;
/* All CR3 accesses cause VM-exits. Later we optimize CR3 accesses (see hmR0VmxLoadGuestControlRegs())*/
/*
* Set MASK & MATCH to 0. VMX checks if GuestPFErrCode & MASK == MATCH. If equal (in our case it always is)
* and if the X86_XCPT_PF bit in the exception bitmap is set it causes a VM-exit, if clear doesn't cause an exit.
* We thus use the exception bitmap to control it rather than use both.
*/
/** @todo Explore possibility of using IO-bitmaps. */
/* All IO & IOIO instructions cause VM-exits. */
/* Setup MSR autoloading/autostoring. */
#else
#endif
/* Set VMCS link pointer. Reserved for future use, must be -1. Intel spec. 24.4 "Guest-State Area". */
/* Setup debug controls */
rc |= VMXWriteVmcs64(VMX_VMCS64_GUEST_DEBUGCTL_FULL, 0); /** @todo We don't support IA32_DEBUGCTL MSR. Should we? */
return rc;
}
/**
* Sets up the initial exception bitmap in the VMCS based on static conditions
* (i.e. conditions that cannot ever change at runtime).
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
*/
{
uint32_t u32XcptBitmap = 0;
/* Without nested paging, #PF must cause a VM-exit so we can sync our shadow page tables. */
return rc;
}
/**
* Sets up the initial guest-state mask. The guest-state mask is consulted
* before reading guest-state fields from the VMCS as VMREADs can be expensive
* for the nested virtualization case (as it would cause a VM-exit).
*
* @param pVCpu Pointer to the VMCPU.
*/
{
/* Initially the guest-state is up-to-date as there is nothing in the VMCS. */
return VINF_SUCCESS;
}
/**
* Does per-VM VT-x initialization.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
*/
{
if (RT_FAILURE(rc))
{
return rc;
}
return VINF_SUCCESS;
}
/**
* Does per-VM VT-x termination.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
*/
{
#ifdef VBOX_WITH_CRASHDUMP_MAGIC
#endif
return VINF_SUCCESS;
}
/**
* Sets up the VM for execution under VT-x.
* This function is only called once per-VM during initalization.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
*/
{
/*
* Without UnrestrictedGuest, pRealModeTSS and pNonPagingModeEPTPageTable *must* always be allocated.
* We no longer support the highly unlikely case of UnrestrictedGuest without pRealModeTSS. See hmR3InitFinalizeR0().
*/
/* -XXX- change hmR3InitFinalizeR0() to fail if pRealModeTSS alloc fails. */
{
LogRel(("VMXR0SetupVM: invalid real-on-v86 state.\n"));
return VERR_INTERNAL_ERROR;
}
/* Initialize these always, see hmR3InitFinalizeR0().*/
/* Setup the tagged-TLB flush handlers. */
if (RT_FAILURE(rc))
{
return rc;
}
{
/* Set revision dword at the beginning of the VMCS structure. */
*(uint32_t *)pVCpu->hm.s.vmx.pvVmcs = MSR_IA32_VMX_BASIC_INFO_VMCS_ID(pVM->hm.s.vmx.msr.vmx_basic_info);
/* Initialize our VMCS region in memory, set the VMCS launch state to "clear". */
/* Load this VMCS as the current VMCS. */
AssertLogRelMsgRCReturnStmt(rc, ("VMXR0SetupVM: VMXActivateVMCS failed! rc=%Rrc (pVM=%p)\n", rc, pVM),
AssertLogRelMsgRCReturnStmt(rc, ("VMXR0SetupVM: hmR0VmxSetupPinCtls failed! rc=%Rrc (pVM=%p)\n", rc, pVM),
AssertLogRelMsgRCReturnStmt(rc, ("VMXR0SetupVM: hmR0VmxSetupProcCtls failed! rc=%Rrc (pVM=%p)\n", rc, pVM),
AssertLogRelMsgRCReturnStmt(rc, ("VMXR0SetupVM: hmR0VmxSetupMiscCtls failed! rc=%Rrc (pVM=%p)\n", rc, pVM),
AssertLogRelMsgRCReturnStmt(rc, ("VMXR0SetupVM: hmR0VmxInitXcptBitmap failed! rc=%Rrc (pVM=%p)\n", rc, pVM),
AssertLogRelMsgRCReturnStmt(rc, ("VMXR0SetupVM: hmR0VmxInitUpdatedGuestStateMask failed! rc=%Rrc (pVM=%p)\n", rc, pVM),
AssertLogRelMsgRCReturnStmt(rc, ("VMXR0SetupVM: hmR0VmxInitVmcsReadCache failed! rc=%Rrc (pVM=%p)\n", rc, pVM),
#endif
/* Re-sync the CPU's internal data into our VMCS memory region & reset the launch state to "clear". */
AssertLogRelMsgRCReturnStmt(rc, ("VMXR0SetupVM: VMXClearVMCS(2) failed! rc=%Rrc (pVM=%p)\n", rc, pVM),
}
return VINF_SUCCESS;
}
/**
* Saves the host control registers (CR0, CR3, CR4) into the host-state area in
* the VMCS.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
*/
{
/* For the darwin 32-bit hybrid kernel, we need the 64-bit CR3 as it uses 64-bit paging. */
if (HMVMX_IS_64BIT_HOST_MODE())
{
}
else
#endif
{
}
return rc;
}
/**
* Saves the host segment registers and GDTR, IDTR, (TR, GS and FS bases) into
* the host-state area in the VMCS.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
*/
{
int rc = VERR_INTERNAL_ERROR_5;
/*
* Host Selector registers.
*/
if (HMVMX_IS_64BIT_HOST_MODE())
{
}
else
{
/* Seems darwin uses the LDT (TI flag is set) in the CS & SS selectors which VT-x doesn't like. */
}
#else
#endif
/* Note: VT-x is picky about the RPL of the selectors here; we'll restore them manually. */
/* Verification based on Intel spec. 26.2.3 "Checks on Host Segment and Descriptor-Table Registers" */
/** @todo Verify if we have any platform that actually run with DS or ES with
* RPL != 0 in kernel space. */
/* Assertion is right but we would not have updated u32ExitCtls yet. */
#if 0
#endif
/* Write these host selector fields into the host-state area in the VMCS. */
/* Avoid the VMWRITEs as we set the following segments to 0 and the VMCS fields are already 0 (since g_HvmR0 is static) */
#if 0
#endif
/*
* Host GDTR and IDTR.
*/
/** @todo Despite VT-x -not- restoring the limits on GDTR and IDTR it should
* be safe to -not- save and restore GDTR and IDTR in the assembly
* code and just do it here and don't care if the limits are zapped on
* VM-exit. */
if (HMVMX_IS_64BIT_HOST_MODE())
{
}
else
#endif
{
ASMGetGDTR(&Gdtr);
ASMGetIDTR(&Idtr);
}
/*
* Host TR base. Verify that TR selector doesn't point past the GDT. Masking off the TI and RPL bits
* is effectively what the CPU does for "scaling by 8". TI is always 0 and RPL should be too in most cases.
*/
{
AssertMsgFailed(("hmR0VmxSaveHostSegmentRegs: TR selector exceeds limit.TR=%RTsel Gdtr.cbGdt=%#x\n", uSelTR, Gdtr.cbGdt));
return VERR_VMX_INVALID_HOST_STATE;
}
if (HMVMX_IS_64BIT_HOST_MODE())
{
/* We need the 64-bit TR base for hybrid darwin. */
}
else
#endif
{
#if HC_ARCH_BITS == 64
#else
#endif
}
/*
* Host FS base and GS base.
* would take care of the bases. In 64-bit, the MSRs come into play.
*/
if (HMVMX_IS_64BIT_HOST_MODE())
{
}
#endif
return rc;
}
/**
* Saves certain host MSRs in the VM-Exit MSR-load area and some in the
* host-state area of the VMCS. Theses MSRs will be automatically restored on
* the host after every successful VM exit.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
*/
{
int rc = VINF_SUCCESS;
{
pHostMsr->u32Reserved = 0;
# if HC_ARCH_BITS == 32 && defined(VBOX_ENABLE_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
if (CPUMIsGuestInLongMode(pVCpu))
{
/* Must match the EFER value in our 64 bits switcher. */
}
else
# endif
}
if (HMVMX_IS_64BIT_HOST_MODE())
{
pHostMsr->u32Reserved = 0;
pHostMsr->u32Reserved = 0;
pHostMsr->u32Reserved = 0;
pHostMsr->u32Reserved = 0;
}
# endif
/* Shouldn't ever happen but there -is- a number. We're well within the recommended 512. */
{
LogRel(("cHostMsrs=%u Cpu=%u\n", cHostMsrs, (unsigned)MSR_IA32_VMX_MISC_MAX_MSR(pVM->hm.s.vmx.msr.vmx_misc)));
}
#endif /* VBOX_WITH_AUTO_MSR_LOAD_RESTORE */
/*
* Host Sysenter MSRs.
*/
# ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
if (HMVMX_IS_64BIT_HOST_MODE())
{
}
else
{
}
# else
# endif
/** @todo IA32_PERF_GLOBALCTRL, IA32_PAT, IA32_EFER, also see
* hmR0VmxSetupExitCtls() !! */
return rc;
}
/**
* Sets up VM-entry controls in the VMCS. These controls can affect things done
* on VM-exit; e.g. "load debug controls", see Intel spec. 24.8.1 "VM-entry
* controls".
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
int rc = VINF_SUCCESS;
{
uint32_t val = pVM->hm.s.vmx.msr.vmx_entry.n.disallowed0; /* Bits set here must be set in the VMCS. */
uint32_t zap = pVM->hm.s.vmx.msr.vmx_entry.n.allowed1; /* Bits cleared here must be cleared in the VMCS. */
/* Load debug controls (DR7 & IA32_DEBUGCTL_MSR). The first VT-x capable CPUs only supports the 1-setting of this bit. */
else
/*
* The following should not be set (since we're not in SMM mode):
* - VMX_VMCS_CTRL_ENTRY_CONTROLS_ENTRY_SMM
* - VMX_VMCS_CTRL_ENTRY_CONTROLS_DEACTIVATE_DUALMON
*/
/** @todo VMX_VMCS_CTRL_ENTRY_CONTROLS_LOAD_GUEST_PERF_MSR,
* VMX_VMCS_CTRL_ENTRY_CONTROLS_LOAD_GUEST_PAT_MSR,
* VMX_VMCS_CTRL_ENTRY_CONTROLS_LOAD_GUEST_EFER_MSR */
{
LogRel(("hmR0VmxLoadGuestEntryCtls: invalid VM-entry controls combo! cpu=%RX64 val=%RX64 zap=%RX64\n",
}
/* Update VCPU with the currently set VM-exit controls. */
}
return rc;
}
/**
* Sets up the VM-exit controls in the VMCS.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks requires EFER.
*/
{
int rc = VINF_SUCCESS;
{
uint32_t val = pVM->hm.s.vmx.msr.vmx_exit.n.disallowed0; /* Bits set here must be set in the VMCS. */
uint32_t zap = pVM->hm.s.vmx.msr.vmx_exit.n.allowed1; /* Bits cleared here must be cleared in the VMCS. */
/* Save debug controls (DR7 & IA32_DEBUGCTL_MSR). The first VT-x CPUs only supported the 1-setting of this bit. */
/* Set the host long mode active (EFER.LMA) bit (which Intel calls "Host address-space size") if necessary. */
if (HMVMX_IS_64BIT_HOST_MODE())
else
else
#endif
/* Don't acknowledge external interrupts on VM-exit. We want to let the host do that. */
/** @todo VMX_VMCS_CTRL_EXIT_CONTROLS_LOAD_PERF_MSR,
* VMX_VMCS_CTRL_EXIT_CONTROLS_SAVE_GUEST_PAT_MSR,
* VMX_VMCS_CTRL_EXIT_CONTROLS_LOAD_HOST_PAT_MSR,
* VMX_VMCS_CTRL_EXIT_CONTROLS_SAVE_GUEST_EFER_MSR,
* VMX_VMCS_CTRL_EXIT_CONTROLS_LOAD_HOST_EFER_MSR. */
{
LogRel(("hmR0VmxSetupProcCtls: invalid VM-exit controls combo! cpu=%RX64 val=%RX64 zap=%RX64\n",
}
/* Update VCPU with the currently set VM-exit controls. */
}
return rc;
}
/**
* Loads the guest APIC and related state.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*/
{
int rc = VINF_SUCCESS;
{
/* Setup TPR shadowing. Also setup TPR patching for 32-bit guests. */
{
bool fPendingIntr = false;
uint8_t u8GuestTpr = 0;
/*
* If there are external interrupts pending but masked by the TPR value, apply the threshold so that if the guest
* lowers the TPR, it would cause a VM-exit and we can deliver the interrupt.
* If there are no external interrupts pending, set threshold to 0 to not cause a VM-exit. We will eventually deliver
* the interrupt when we VM-exit for other reasons.
*/
/* Bits 3-0 of the TPR threshold field correspond to bits 7-4 of the TPR (which is the Task-Priority Class). */
/* 32-bit guests uses LSTAR MSR for patching guest code which touches the TPR. */
{
{
/* If there are interrupts pending, intercept CR8 writes, otherwise don't intercept CR8 reads or writes. */
if (fPendingIntr)
else
}
}
}
}
return rc;
}
/**
* Gets the guest's interruptibility-state ("interrupt shadow" as AMD calls it).
*
* @returns
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
* @remarks Has side-effects with VMCPU_FF_INHIBIT_INTERRUPTS force-flag.
*/
{
/*
* Instructions like STI and MOV SS inhibit interrupts till the next instruction completes. Check if we should
* inhibit interrupts or clear any existing interrupt-inhibition.
*/
uint32_t uIntrState = 0;
{
/* If inhibition is active, RIP & RFLAGS should've been accessed (i.e. read previously from the VMCS or from ring-3). */
AssertMsg((pVCpu->hm.s.vmx.fUpdatedGuestState & (HMVMX_UPDATED_GUEST_RIP | HMVMX_UPDATED_GUEST_RFLAGS))
== (HMVMX_UPDATED_GUEST_RIP | HMVMX_UPDATED_GUEST_RFLAGS), ("%#x\n", pVCpu->hm.s.vmx.fUpdatedGuestState));
{
/*
* We can clear the inhibit force flag as even if we go back to the recompiler without executing guest code in
* VT-x the flag's condition to be cleared is met and thus the cleared state is correct.
*/
}
else
}
return uIntrState;
}
/**
* Loads the guest's interruptibility-state into the guest-state area in the
* VMCS.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param uIntrState The interruptibility-state to set.
*/
{
return rc;
}
/**
* Loads the guest's RIP into the guest-state area in the VMCS.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
int rc = VINF_SUCCESS;
{
}
return rc;
}
/**
* Loads the guest's RSP into the guest-state area in the VMCS.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
int rc = VINF_SUCCESS;
{
}
return rc;
}
/**
* Loads the guest's RFLAGS into the guest-state area in the VMCS.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
int rc = VINF_SUCCESS;
{
/* Intel spec. 2.3.1 "System Flags and Fields in IA-32e Mode" claims the upper 32-bits of RFLAGS are reserved (MBZ).
Let us assert it as such and use 32-bit VMWRITE. */
/*
* If we're emulating real-mode using Virtual 8086 mode, save the real-mode eflags so we can restore them on VM exit.
* Modify the real-mode guest's eflags so that VT-x can run the real-mode guest code under Virtual 8086 mode.
*/
{
pVCpu->hm.s.vmx.RealMode.eflags.u32 = uEFlags.u32; /* Save the original eflags of the real-mode guest. */
}
}
return rc;
}
/**
* Loads the guest RIP, RSP and RFLAGS into the guest-state area in the VMCS.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
return rc;
}
/**
* Loads the guest control registers (CR0, CR3, CR4) into the guest-state area
* in the VMCS.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
int rc = VINF_SUCCESS;
/*
* Guest CR0.
* Guest FPU.
*/
{
/* The guest's view (read access) of its CR0 is unblemished. */
/* Setup VT-x's view of the guest CR0. */
/* Minimize VM-exits due to CR3 changes when we have NestedPaging. */
{
{
/* The guest has paging enabled, let it access CR3 without causing a VM exit if supported. */
}
else
{
/* The guest doesn't have paging enabled, make CR3 access to cause VM exits to update our shadow. */
}
}
else
/*
* Guest FPU bits.
* Intel spec. 23.8 "Restrictions on VMX operation" mentions that CR0.NE bit must always be set on the first
* CPUs to support VT-x and no mention of with regards to UX in VM-entry checks.
*/
bool fInterceptNM = false;
{
fInterceptNM = false; /* Guest FPU active, no need to VM-exit on #NM. */
/* The guest should still get #NM exceptions when it expects it to, so we should not clear TS & MP bits here.
We're only concerned about -us- not intercepting #NMs when the guest-FPU is active. Not the guest itself! */
}
else
{
fInterceptNM = true; /* Guest FPU inactive, VM-exit on #NM for lazy FPU loading. */
| X86_CR0_MP; /* FWAIT/WAIT should not ignore CR0.TS and should generate #NM. */
}
/* Catch floating point exceptions if we need to report them to the guest in a different way. */
bool fInterceptMF = false;
fInterceptMF = true;
/* Finally, intercept all exceptions as we cannot directly inject them in real-mode, see hmR0VmxInjectEventVmcs(). */
{
fInterceptNM = true;
fInterceptMF = true;
}
else
if (fInterceptNM)
else
if (fInterceptMF)
else
/* Additional intercepts for debugging, define these yourself explicitly. */
#ifdef HMVMX_ALWAYS_TRAP_ALL_XCPTS
| RT_BIT(X86_XCPT_MF);
#elif defined(HMVMX_ALWAYS_TRAP_PF)
#endif
if (pVM->hm.s.vmx.fUnrestrictedGuest) /* Exceptions for unrestricted-guests for fixed CR0 bits (PE, PG). */
else
u32GuestCR0 |= uSetCR0;
u32GuestCR0 &= uZapCR0;
/* Write VT-x's view of the guest CR0 into the VMCS and update the exception bitmap. */
Log(("Load: VMX_VMCS_GUEST_CR0=%#RX32 (uSetCR0=%#RX32 uZapCR0=%#RX32)\n", u32GuestCR0, uSetCR0, uZapCR0));
/*
* CR0 is shared between host and guest along with a CR0 read shadow. Therefore, certain bits must not be changed
* by the guest because VT-x ignores saving/restoring them (namely CD, ET, NW) and for certain other bits
* we want to be notified immediately of guest CR0 changes (e.g. PG to update our shadow page tables).
*/
uint64_t u64CR0Mask = 0;
| X86_CR0_ET /* Bit ignored on VM-entry and VM-exit. Don't let the guest modify the host CR0.ET */
| X86_CR0_CD /* Bit ignored on VM-entry and VM-exit. Don't let the guest modify the host CR0.CD */
| X86_CR0_NW; /* Bit ignored on VM-entry and VM-exit. Don't let the guest modify the host CR0.NW */
u64CR0Mask &= ~X86_CR0_PE;
u64CR0Mask &= ~X86_CR0_WP;
/* If the guest FPU state is active, don't need to VM-exit on writes to FPU related bits in CR0. */
if (fInterceptNM)
else
/* Write the CR0 mask into the VMCS and update the VCPU's copy of the current CR0 mask. */
}
/*
* Guest CR2.
* It's always loaded in the assembler code. Nothing to do here.
*/
/*
* Guest CR3.
*/
{
{
/* Validate. See Intel spec. 28.2.2 "EPT Translation Mechanism" and 24.6.11 "Extended-Page-Table Pointer (EPTP)" */
/* VMX_EPT_MEMTYPE_WB support is already checked in hmR0VmxSetupTaggedTlb(). */
/* Validate. See Intel spec. 26.2.1 "Checks on VMX Controls" */
AssertMsg( ((pVCpu->hm.s.vmx.HCPhysEPTP >> 3) & 0x07) == 3 /* Bits 3:5 (EPT page walk length - 1) must be 3. */
{
/* If the guest is in PAE mode, pass the PDPEs to VT-x using the VMCS fields. */
if (CPUMIsGuestInPAEModeEx(pCtx))
{
}
/* The guest's view of its CR3 is unblemished with Nested Paging when the guest is using paging or we
have Unrestricted Execution to handle the guest when it's not using paging. */
}
else
{
/*
* The guest is not using paging, but the CPU (VT-x) has to. While the guest thinks it accesses physical memory
* directly, we use our identity-mapped page table to map guest-linear to guest-physical addresses.
* EPT takes care of translating it to host-physical addresses.
*/
/* We obtain it here every time as the guest could have relocated this PCI region. */
}
}
else
{
/* Non-nested paging case, just use the hypervisor's CR3. */
}
}
/*
* Guest CR4.
*/
{
/* The guest's view of its CR4 is unblemished. */
/* Setup VT-x's view of the guest CR4. */
/*
* If we're emulating real-mode using virtual-8086 mode, we want to redirect software interrupts to the 8086 program
* interrupt handler. Clear the VME bit (the interrupt redirection bitmap is already all 0, see hmR3InitFinalizeR0())
* See Intel spec. 20.2 "Software Interrupt Handling Methods While in Virtual-8086 Mode".
*/
{
u32GuestCR4 &= ~X86_CR4_VME;
}
{
if ( !CPUMIsGuestPagingEnabledEx(pCtx)
{
/* We use 4 MB pages in our identity mapping page table when the guest doesn't have paging. */
/* Our identity mapping is a 32 bits page directory. */
u32GuestCR4 &= ~X86_CR4_PAE;
}
/* else use guest CR4.*/
}
else
{
/*
* The shadow paging modes and guest paging modes are different, the shadow is in accordance with the host
* paging mode and thus we need to adjust VT-x's view of CR4 depending on our shadow page tables.
*/
{
case PGMMODE_REAL: /* Real-mode. */
case PGMMODE_PROTECTED: /* Protected mode without paging. */
case PGMMODE_32_BIT: /* 32-bit paging. */
{
u32GuestCR4 &= ~X86_CR4_PAE;
break;
}
case PGMMODE_PAE: /* PAE paging. */
case PGMMODE_PAE_NX: /* PAE paging with NX. */
{
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;
#endif
default:
AssertFailed();
}
}
/* We need to set and clear the CR4 specific bits here (mainly the X86_CR4_VMXE bit). */
u32GuestCR4 |= uSetCR4;
u32GuestCR4 &= uZapCR4;
/* Write VT-x's view of the guest CR4 into the VMCS. */
/* Setup CR4 mask. CR4 flags owned by the host, if the guest attempts to change them, that would cause a VM exit. */
uint64_t u64CR4Mask = 0;
| X86_CR4_VMXE;
}
return rc;
}
/**
* Loads the guest debug registers into the guest-state area in the VMCS.
* This also sets up whether #DB and MOV DRx accesses cause VM exits.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
return VINF_SUCCESS;
#ifdef VBOX_STRICT
/* Validate. Intel spec. 26.3.1.1 "Checks on Guest Controls Registers, Debug Registers, MSRs" */
{
/* Validate. Intel spec. 17.2 "Debug Registers", recompiler paranoia checks. */
}
#endif
int rc = VERR_INTERNAL_ERROR_5;
bool fInterceptDB = false;
bool fInterceptMovDRx = false;
if (DBGFIsStepping(pVCpu))
{
/* If the CPU supports the monitor trap flag, use it for single stepping in DBGF and avoid intercepting #DB. */
if (pVM->hm.s.vmx.msr.vmx_proc_ctls.n.allowed1 & VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_MONITOR_TRAP_FLAG)
{
Assert(fInterceptDB == false);
}
else
fInterceptDB = true;
}
{
if (!CPUMIsHyperDebugStateActive(pVCpu))
{
}
fInterceptMovDRx = true;
}
{
if (!CPUMIsGuestDebugStateActive(pVCpu))
{
}
Assert(fInterceptMovDRx == false);
}
else if (!CPUMIsGuestDebugStateActive(pVCpu))
{
/* For the first time we would need to intercept MOV DRx accesses even when the guest debug registers aren't loaded. */
fInterceptMovDRx = true;
}
/* Update the exception bitmap regarding intercepting #DB generated by the guest. */
if (fInterceptDB)
{
#ifndef HMVMX_ALWAYS_TRAP_ALL_XCPTS
#endif
}
/* Update the processor-based VM-execution controls regarding intercepting MOV DRx instructions. */
if (fInterceptMovDRx)
else
/* The guest's view of its DR7 is unblemished. */
return rc;
}
#ifdef VBOX_STRICT
/**
* Strict function to validate segment registers.
*
* @remarks Requires CR0.
*/
{
/* Validate segment registers. See Intel spec. 26.3.1.2 "Checks on Guest Segment Registers". */
&& ( !CPUMIsGuestInRealModeEx(pCtx)
&& !CPUMIsGuestInV86ModeEx(pCtx)))
{
/* Protected mode checks */
/* CS */
Assert(pCtx->cs.Attr.u && pCtx->cs.Attr.u != HMVMX_SEL_UNUSABLE); /* CS cannot be loaded with NULL in protected mode. */
else
/* SS */
{
}
{
}
/* DS, ES, FS, GS - only check for usable selectors, see hmR0VmxWriteSegmentReg(). */
{
}
{
}
{
}
{
}
/* 64-bit capable CPUs. */
# endif
}
else if ( CPUMIsGuestInV86ModeEx(pCtx)
|| ( CPUMIsGuestInRealModeEx(pCtx)
{
/* Real and v86 mode checks. */
/* hmR0VmxWriteSegmentReg() writes the modified in VMCS. We want what we're feeding to VT-x. */
{
u32CSAttr = 0xf3; u32SSAttr = 0xf3; u32DSAttr = 0xf3; u32ESAttr = 0xf3; u32FSAttr = 0xf3; u32GSAttr = 0xf3;
}
else
{
}
/* CS */
AssertMsg((pCtx->cs.u64Base == (uint64_t)pCtx->cs.Sel << 4), ("CS base %#x %#x\n", pCtx->cs.u64Base, pCtx->cs.Sel));
/* SS */
/* DS */
/* ES */
/* FS */
/* GS */
/* 64-bit capable CPUs. */
# endif
}
}
#endif /* VBOX_STRICT */
/**
* Writes a guest segment register into the guest-state area in the VMCS.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param idxSel Index of the selector in the VMCS.
* @param idxLimit Index of the segment limit in the VMCS.
* @param idxBase Index of the segment base in the VMCS.
* @param idxAccess Index of the access rights of the segment in the VMCS.
* @param pSelReg Pointer to the segment selector.
* @param pCtx Pointer to the guest-CPU context.
*
* @remarks No-long-jump zone!!!
*/
static int hmR0VmxWriteSegmentReg(PVMCPU pVCpu, uint32_t idxSel, uint32_t idxLimit, uint32_t idxBase,
{
int rc;
{
/* VT-x requires our real-using-v86 mode hack to override the segment access-right bits. */
u32Access = 0xf3;
}
else
{
/*
* The way to differentiate between whether this is really a null selector or was just a selector loaded with 0 in
* real-mode is using the segment attributes. A selector loaded in real-mode with the value 0 is valid and usable in
* protected-mode and we should -not- mark it as an unusable segment. Both the recompiler & VT-x ensures NULL selectors
* loaded in protected-mode have their attribute as 0.
*/
if (!u32Access)
}
/* Validate segment access rights. Refer to Intel spec. "26.3.1.2 Checks on Guest Segment Registers". */
("Access bit not set for usable segment. idx=%#x sel=%#x attr %#x\n", idxBase, pSelReg, pSelReg->Attr.u));
return rc;
}
/**
* Loads the guest segment registers, GDTR, IDTR, LDTR, (TR, FS and GS bases)
* into the guest-state area in the VMCS.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCPU Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks Requires CR0 (strict builds validation).
* @remarks No-long-jump zone!!!
*/
{
int rc = VERR_INTERNAL_ERROR_5;
/*
* Guest Segment registers: CS, SS, DS, ES, FS, GS.
*/
{
/* Save the segment attributes for real-on-v86 mode hack, so we can restore them on VM-exit. */
{
}
#ifdef VBOX_WITH_REM
{
{
/* Signal that the recompiler must flush its code-cache as the guest -may- rewrite code it will later execute
in real-mode (e.g. OpenBSD 4.0) */
Log(("Load: Switch to protected mode detected!\n"));
}
}
#endif
rc = hmR0VmxWriteSegmentReg(pVCpu, VMX_VMCS16_GUEST_FIELD_CS, VMX_VMCS32_GUEST_CS_LIMIT, VMX_VMCS_GUEST_CS_BASE,
rc |= hmR0VmxWriteSegmentReg(pVCpu, VMX_VMCS16_GUEST_FIELD_SS, VMX_VMCS32_GUEST_SS_LIMIT, VMX_VMCS_GUEST_SS_BASE,
rc |= hmR0VmxWriteSegmentReg(pVCpu, VMX_VMCS16_GUEST_FIELD_DS, VMX_VMCS32_GUEST_DS_LIMIT, VMX_VMCS_GUEST_DS_BASE,
rc |= hmR0VmxWriteSegmentReg(pVCpu, VMX_VMCS16_GUEST_FIELD_ES, VMX_VMCS32_GUEST_ES_LIMIT, VMX_VMCS_GUEST_ES_BASE,
rc |= hmR0VmxWriteSegmentReg(pVCpu, VMX_VMCS16_GUEST_FIELD_FS, VMX_VMCS32_GUEST_FS_LIMIT, VMX_VMCS_GUEST_FS_BASE,
rc |= hmR0VmxWriteSegmentReg(pVCpu, VMX_VMCS16_GUEST_FIELD_GS, VMX_VMCS32_GUEST_GS_LIMIT, VMX_VMCS_GUEST_GS_BASE,
#ifdef VBOX_STRICT
#endif
}
/*
* Guest TR.
*/
{
/*
* Real-mode emulation using virtual-8086 mode with CR4.VME. Interrupt redirection is achieved
* using the interrupt redirection bitmap (all bits cleared to let the guest handle INT-n's) in the TSS.
* See hmR3InitFinalizeR0() to see how pRealModeTSS is setup.
*/
uint32_t u32AccessRights = 0;
{
}
else
{
Assert(PDMVmmDevHeapIsEnabled(pVM)); /* Guaranteed by HMR3CanExecuteGuest() -XXX- what about inner loop changes? */
/* We obtain it here every time as PCI regions could be reconfigured in the guest, changing the VMMDev base. */
DescAttr.u = 0;
u16Sel = 0;
u32AccessRights = DescAttr.u;
}
/* Validate. */
|| (u32AccessRights & 0xf) == X86_SEL_TYPE_SYS_286_TSS_BUSY, ("TSS is not busy!? %#x\n", u32AccessRights));
AssertMsg(!(u32AccessRights & HMVMX_SEL_UNUSABLE), ("TR unusable bit is not clear!? %#x\n", u32AccessRights));
}
/*
* Guest GDTR.
*/
{
}
/*
* Guest LDTR.
*/
{
/* The unusable bit is specific to VT-x, if it's a null selector mark it as an unusable segment. */
else
/* Validate. */
if (!(u32Access & HMVMX_SEL_UNUSABLE))
{
}
}
/*
* Guest IDTR.
*/
{
}
return VINF_SUCCESS;
}
/**
* Loads certain guest MSRs into the VM-entry MSR-load and VM-exit MSR-store
* areas. These MSRs will automatically be loaded to the host CPU on every
* successful VM entry and stored from the host CPU on every successful VM exit.
* Also loads the sysenter MSRs into the guest-state area in the VMCS.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
/*
*/
int rc = VINF_SUCCESS;
{
uint32_t cGuestMsrs = 0;
/* See Intel spec. 4.1.4 "Enumeration of Paging Features by CPUID". */
if (fSupportsNX || fSupportsLongMode)
{
/** @todo support save IA32_EFER, i.e.
* VMX_VMCS_CTRL_EXIT_CONTROLS_SAVE_GUEST_EFER_MSR, in which case the
* guest EFER need not be part of the VM-entry MSR-load area. */
pGuestMsr->u32Reserved = 0;
/* VT-x will complain if only MSR_K6_EFER_LME is set. See Intel spec. 26.4 "Loading MSRs" for details. */
if (!CPUMIsGuestInLongModeEx(pMixedCtx))
pGuestMsr++; cGuestMsrs++;
if (fSupportsLongMode)
{
pGuestMsr->u32Reserved = 0;
pGuestMsr++; cGuestMsrs++;
pGuestMsr->u32Reserved = 0;
pGuestMsr++; cGuestMsrs++;
pGuestMsr->u32Reserved = 0;
pGuestMsr++; cGuestMsrs++;
pGuestMsr->u32Reserved = 0;
pGuestMsr++; cGuestMsrs++;
}
}
/*
* RDTSCP requires the TSC_AUX MSR. Host and guest share the physical MSR. So we have to
* load the guest's copy if the guest can execute RDTSCP without causing VM-exits.
*/
{
pGuestMsr->u32Reserved = 0;
pGuestMsr++; cGuestMsrs++;
}
/* Shouldn't ever happen but there -is- a number. We're well within the recommended 512. */
{
}
/* Update the VCPU's copy of the guest MSR count. */
#endif /* VBOX_WITH_AUTO_MSR_LOAD_RESTORE */
}
/*
* Guest Sysenter MSRs.
* These flags are only set when MSR-bitmaps are not supported by the CPU and we cause
* VM-exits on WRMSRs for these MSRs.
*/
{
}
{
}
{
}
return rc;
}
/**
* Loads the guest activity state into the guest-state area in the VMCS.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
/** @todo See if we can make use of other states, e.g.
* VMX_VMCS_GUEST_ACTIVITY_SHUTDOWN or HLT. */
int rc = VINF_SUCCESS;
{
}
return rc;
}
/**
* Sets up the appropriate function to run guest code.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
{
#ifndef VBOX_ENABLE_64_BITS_GUESTS
#endif
/* 32-bit host. We need to switch to 64-bit before running the 64-bit guest. */
#else
/* 64-bit host or hybrid host. */
#endif
}
else
{
/* Guest is not in long mode, use the 32-bit handler. */
}
return VINF_SUCCESS;
}
/**
* Wrapper for running the guest code in VT-x.
*
* @returns VBox strict status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pCtx Pointer to the guest-CPU context.
*
* @remarks No-long-jump zone!!!
*/
{
/*
* 64-bit Windows uses XMM registers in the kernel as the Microsoft compiler expresses floating-point operations
* using SSE instructions. Some XMM registers (XMM6-XMM15) are callee-saved and thus the need for this XMM wrapper.
* Refer MSDN docs. "Configuring Programs for 64-bit / x64 Software Conventions / Register Usage" for details.
*/
#ifdef VBOX_WITH_KERNEL_USING_XMM
return hmR0VMXStartVMWrapXMM(pVCpu->hm.s.fResumeVM, pCtx, &pVCpu->hm.s.vmx.VMCSCache, pVM, pVCpu, pVCpu->hm.s.vmx.pfnStartVM);
#else
return pVCpu->hm.s.vmx.pfnStartVM(pVCpu->hm.s.fResumeVM, pCtx, &pVCpu->hm.s.vmx.VMCSCache, pVM, pVCpu);
#endif
}
/**
* Report world-switch error and dump some useful debug info.
*
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pCtx Pointer to the guest-CPU context.
* @param pVmxTransient Pointer to the VMX transient structure (only
* exitReason updated).
*/
static void hmR0VmxReportWorldSwitchError(PVM pVM, PVMCPU pVCpu, int rcVMRun, PCPUMCTX pCtx, PVMXTRANSIENT pVmxTransient)
{
switch (rcVMRun)
{
AssertFailed();
break;
case VINF_SUCCESS: /* VMLAUNCH/VMRESUME succeeded but VM-entry failed... yeah, true story. */
case VERR_VMX_UNABLE_TO_START_VM: /* VMLAUNCH/VMRESUME itself failed. */
{
#ifdef VBOX_STRICT
else
/* VMX control bits. */
/* Guest bits. */
/* Host bits. */
{
}
{
}
{
}
{
}
{
}
{
}
{
}
if (HMVMX_IS_64BIT_HOST_MODE())
{
}
# endif
#endif /* VBOX_STRICT */
break;
}
default:
/* Impossible */
break;
}
}
#if HC_ARCH_BITS == 32 && defined(VBOX_ENABLE_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
#ifndef VMX_USE_CACHED_VMCS_ACCESSES
# error "VMX_USE_CACHED_VMCS_ACCESSES not defined when it should be!"
#endif
#ifdef VBOX_STRICT
{
switch (idxField)
{
case VMX_VMCS_GUEST_RIP:
case VMX_VMCS_GUEST_RSP:
case VMX_VMCS_GUEST_DR7:
case VMX_VMCS_GUEST_GDTR_BASE:
case VMX_VMCS_GUEST_IDTR_BASE:
case VMX_VMCS_GUEST_CS_BASE:
case VMX_VMCS_GUEST_DS_BASE:
case VMX_VMCS_GUEST_ES_BASE:
case VMX_VMCS_GUEST_FS_BASE:
case VMX_VMCS_GUEST_GS_BASE:
case VMX_VMCS_GUEST_SS_BASE:
case VMX_VMCS_GUEST_LDTR_BASE:
case VMX_VMCS_GUEST_TR_BASE:
case VMX_VMCS_GUEST_CR3:
return true;
}
return false;
}
{
switch (idxField)
{
/* Read-only fields. */
return true;
}
/* Remaining readable fields should also be writable. */
return hmR0VmxIsValidWriteField(idxField);
}
#endif /* VBOX_STRICT */
/**
* Executes the specified handler in 64-bit mode.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pCtx Pointer to the guest CPU context.
* @param enmOp The operation to perform.
* @param cbParam Number of parameters.
* @param paParam Array of 32-bit parameters.
*/
VMMR0DECL(int) VMXR0Execute64BitsHandler(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, HM64ON32OP enmOp, uint32_t cbParam,
{
Assert(pVCpu->hm.s.vmx.VMCSCache.Write.cValidEntries <= RT_ELEMENTS(pVCpu->hm.s.vmx.VMCSCache.Write.aField));
Assert(pVCpu->hm.s.vmx.VMCSCache.Read.cValidEntries <= RT_ELEMENTS(pVCpu->hm.s.vmx.VMCSCache.Read.aField));
#ifdef VBOX_STRICT
#endif
/* Disable interrupts. */
#endif
pCpu = HMR0GetCurrentCpu();
/* Clear VMCS. Marking it inactive, clearing implementation-specific data and writing VMCS data back to memory. */
/* Leave VMX Root Mode. */
VMXDisable();
for (int i = (int)cbParam - 1; i >= 0; i--)
/* Call the switcher. */
rc = pVM->hm.s.pfnHost32ToGuest64R0(pVM, RT_OFFSETOF(VM, aCpus[pVCpu->idCpu].cpum) - RT_OFFSETOF(VM, cpum));
/** @todo replace with hmR0VmxEnterRootMode() and LeaveRootMode(). */
/* Make sure the VMX instructions don't cause #UD faults. */
/* Re-enter VMX Root Mode */
if (RT_FAILURE(rc2))
{
return rc2;
}
return rc;
}
/**
* Prepares for and executes VMLAUNCH (64 bits guests) for 32-bit hosts
* supporting 64-bit guests.
*
* @returns VBox status code.
* @param fResume Whether to VMLAUNCH or VMRESUME.
* @param pCtx Pointer to the guest-CPU context.
* @param pCache Pointer to the VMCS cache.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
*/
DECLASM(int) VMXR0SwitcherStartVM64(RTHCUINT fResume, PCPUMCTX pCtx, PVMCSCACHE pCache, PVM pVM, PVMCPU pVCpu)
{
RTHCPHYS HCPhysCpuPage = 0;
int rc = VERR_INTERNAL_ERROR_5;
pCpu = HMR0GetCurrentCpu();
#ifdef VBOX_WITH_CRASHDUMP_MAGIC
#endif
#ifdef VBOX_STRICT
#endif
aParam[3] = (uint32_t)(pVCpu->hm.s.vmx.HCPhysVmcs >> 32); /* Param 2: VMCS physical address - Hi. */
aParam[5] = 0;
#ifdef VBOX_WITH_CRASHDUMP_MAGIC
#endif
#ifdef VBOX_WITH_CRASHDUMP_MAGIC
#endif
#ifdef VBOX_STRICT
AssertMsg(pCache->TestIn.HCPhysCpuPage == HCPhysCpuPage, ("%RHp vs %RHp\n", pCache->TestIn.HCPhysCpuPage, HCPhysCpuPage));
AssertMsg(pCache->TestIn.HCPhysVmcs == pVCpu->hm.s.vmx.HCPhysVmcs, ("%RHp vs %RHp\n", pCache->TestIn.HCPhysVmcs,
AssertMsg(pCache->TestIn.HCPhysVmcs == pCache->TestOut.HCPhysVmcs, ("%RHp vs %RHp\n", pCache->TestIn.HCPhysVmcs,
AssertMsg(pCache->TestIn.pCache == pCache->TestOut.pCache, ("%RGv vs %RGv\n", pCache->TestIn.pCache,
("%RGv vs %RGv\n", pCache->TestIn.pCache, VM_RC_ADDR(pVM, &pVM->aCpus[pVCpu->idCpu].hm.s.vmx.VMCSCache)));
#endif
return rc;
}
/**
* Initialize the VMCS-Read cache. The VMCS cache is used for 32-bit hosts
* running 64-bit guests (except 32-bit Darwin which runs with 64-bit paging in
* 32-bit mode) for 64-bit fields that cannot be accessed in 32-bit mode. Some
* 64-bit fields -can- be accessed (those that have a 32-bit FULL & HIGH part).
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
*/
{
{ \
++cReadFields; \
}
uint32_t cReadFields = 0;
/* Guest-natural selector base fields */
#if 0
/* These are 32-bit in practice. See Intel spec. 2.5 "Control Registers". */
#endif
#if 0
/* Unused natural width guest-state fields. */
#endif
/* 64-bit guest-state fields; unused as we use two 32-bit VMREADs for these 64-bit fields (using "FULL" and "HIGH" fields). */
#if 0
#endif
/* Natural width guest-state fields. */
#if 0
/* Currently unused field. */
#endif
{
AssertMsg(cReadFields == VMX_VMCS_MAX_NESTED_PAGING_CACHE_IDX, ("cReadFields=%u expected %u\n", cReadFields,
}
else
{
AssertMsg(cReadFields == VMX_VMCS_MAX_CACHE_IDX, ("cReadFields=%u expected %u\n", cReadFields, VMX_VMCS_MAX_CACHE_IDX));
}
return VINF_SUCCESS;
}
/**
* Writes a field into the VMCS. This can either directly invoke a VMWRITE or
* queue up the VMWRITE by using the VMCS write cache (on 32-bit hosts, except
* darwin, running 64-bit guests).
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param idxField The VMCS field encoding.
* @param u64Val 16, 32 or 64 bits value.
*/
{
int rc;
switch (idxField)
{
/*
* These fields consists of a "FULL" and a "HIGH" part which can be written to individually.
*/
/* 64-bit Control fields. */
/* 64-bit Guest-state fields. */
/* 64-bit Host-state fields. */
{
break;
}
/*
* These fields do not have high and low parts. Queue up the VMWRITE by using the VMCS write-cache (for 64-bit
* values). When we switch the host to 64-bit mode for running 64-bit guests, these VMWRITEs get executed then.
*/
/* Natural-width Guest-state fields. */
case VMX_VMCS_GUEST_CR3:
case VMX_VMCS_GUEST_ES_BASE:
case VMX_VMCS_GUEST_CS_BASE:
case VMX_VMCS_GUEST_SS_BASE:
case VMX_VMCS_GUEST_DS_BASE:
case VMX_VMCS_GUEST_FS_BASE:
case VMX_VMCS_GUEST_GS_BASE:
case VMX_VMCS_GUEST_LDTR_BASE:
case VMX_VMCS_GUEST_TR_BASE:
case VMX_VMCS_GUEST_GDTR_BASE:
case VMX_VMCS_GUEST_IDTR_BASE:
case VMX_VMCS_GUEST_DR7:
case VMX_VMCS_GUEST_RSP:
case VMX_VMCS_GUEST_RIP:
{
if (!(u64Val >> 32))
{
/* If this field is 64-bit, VT-x will zero out the top bits. */
}
else
{
/* Assert that only the 32->64 switcher case should ever come here. */
}
break;
}
default:
{
AssertMsgFailed(("VMXWriteVmcs64Ex: Invalid field %#RX32 (pVCpu=%p u64Val=%#RX64)\n", idxField, pVCpu, u64Val));
break;
}
}
return rc;
}
/**
* Queue up a VMWRITE by using the VMCS write cache. This is only used on 32-bit
* hosts (except darwin) for 64-bit guests.
*
* @param pVCpu Pointer to the VMCPU.
* @param idxField The VMCS field encoding.
* @param u64Val 16, 32 or 64 bits value.
*/
{
/* Make sure there are no duplicates. */
{
{
return VINF_SUCCESS;
}
}
return VINF_SUCCESS;
}
/* Enable later when the assembly code uses these as callbacks. */
#if 0
/*
* Loads the VMCS write-cache into the CPU (by executing VMWRITEs).
*
* @param pVCpu Pointer to the VMCPU.
* @param pCache Pointer to the VMCS cache.
*
* @remarks No-long-jump zone!!!
*/
{
{
}
}
/**
* Stores the VMCS read-cache from the CPU (by executing VMREADs).
*
* @param pVCpu Pointer to the VMCPU.
* @param pCache Pointer to the VMCS cache.
*
* @remarks No-long-jump zone!!!
*/
{
{
}
}
#endif
#endif /* HC_ARCH_BITS == 32 && defined(VBOX_ENABLE_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL) */
/**
* Sets up the usage of TSC-offsetting and updates the VMCS. If offsetting is
* not possible, cause VM-exits on RDTSC(P)s. Also sets up the VMX preemption
* timer.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
* @remarks No-long-jump zone!!!
*/
{
int rc = VERR_INTERNAL_ERROR_5;
bool fOffsettedTsc = false;
{
uint64_t cTicksToDeadline = TMCpuTickGetDeadlineAndTscOffset(pVCpu, &fOffsettedTsc, &pVCpu->hm.s.vmx.u64TSCOffset);
/* Make sure the returned values have sane upper and lower boundaries. */
}
else
if (fOffsettedTsc)
{
{
/* Note: VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_RDTSC_EXIT takes precedence over TSC_OFFSET, applies to RDTSCP too. */
}
else
{
/* VM-exit on RDTSC(P) as we would otherwise pass decreasing TSC values to the guest. */
}
}
else
{
/* We can't use TSC-offsetting (non-fixed TSC, warp drive active etc.), VM-exit on RDTSC(P). */
}
}
/**
* Determines if an exception is a contributory exception. Contributory
* exceptions are ones which can cause double-faults. Page-fault is
* intentionally not included here as it's a conditional contributory exception.
*
* @returns true if the exception is contributory, false otherwise.
* @param uVector The exception vector.
*/
{
switch (uVector)
{
case X86_XCPT_GP:
case X86_XCPT_SS:
case X86_XCPT_NP:
case X86_XCPT_TS:
case X86_XCPT_DE:
return true;
default:
break;
}
return false;
}
/**
* Sets an event as a pending event to be injected into the guest.
*
* @param pVCpu Pointer to the VMCPU.
* @param u32IntrInfo The VM-entry interruption-information field.
* @param cbInstr The VM-entry instruction length in bytes (for software
* interrupts, exceptions and privileged software
* exceptions).
* @param u32ErrCode The VM-entry exception error code.
* @param GCPtrFaultAddress The fault-address (CR2) in case it's a
* page-fault.
*/
DECLINLINE(void) hmR0VmxSetPendingEvent(PVMCPU pVCpu, uint32_t u32IntrInfo, uint32_t cbInstr, uint32_t u32ErrCode,
{
}
/**
* Sets a double-fault (#DF) exception as pending-for-injection into the VM.
*
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*/
{
/* Inject the double-fault. */
hmR0VmxSetPendingEvent(pVCpu, u32IntrInfo, 0 /* cbInstr */, 0 /* u32ErrCode */, 0 /* GCPtrFaultAddress */);
}
/**
* Handle a condition that occurred while delivering an event through the guest
* IDT.
*
* @returns VBox status code (informational error codes included).
* @retval VINF_SUCCESS if we should continue handling the VM-exit.
* @retval VINF_VMX_DOUBLE_FAULT if a #DF condition was detected and we ought to
* continue execution of the guest which will delivery the #DF.
* @retval VINF_EM_RESET if we detected a triple-fault condition.
*
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
* @param pVmxTransient Pointer to the VMX transient structure.
*
* @remarks No-long-jump zone!!!
*/
static int hmR0VmxCheckExitDueToEventDelivery(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
{
typedef enum
{
VMXREFLECTXCPT_XCPT, /* Reflect the exception to the guest or for further evaluation by VMM. */
VMXREFLECTXCPT_DF, /* Reflect the exception as a double-fault to the guest. */
VMXREFLECTXCPT_TF, /* Indicate a triple faulted state to the VMM. */
VMXREFLECTXCPT_NONE /* Nothing to reflect. */
/* See Intel spec. 30.7.1.1 "Reflecting Exceptions to Guest Software". */
{
#ifdef VBOX_STRICT
&& uExitVector == X86_XCPT_PF)
{
}
#endif
if ( uExitVector == X86_XCPT_PF
&& uIdtVector == X86_XCPT_PF)
{
pVmxTransient->fVectoringPF = true;
}
|| uIdtVector == X86_XCPT_PF))
{
}
else if (uIdtVector == X86_XCPT_DF)
}
else if ( uIntType != VMX_IDT_VECTORING_INFO_TYPE_SW_INT
{
/*
* Ignore software interrupts (INT n), software exceptions (#BP, #OF) and privileged software exception
* (whatever they are) as they reoccur when restarting the instruction.
*/
}
switch (enmReflect)
{
case VMXREFLECTXCPT_XCPT:
{
uint32_t u32ErrCode = 0;
{
}
/* If uExitVector is #PF, CR2 value will be updated from the VMCS if it's a guest #PF. See hmR0VmxExitXcptPF(). */
hmR0VmxSetPendingEvent(pVCpu, VMX_ENTRY_INTR_INFO_FROM_EXIT_IDT_INFO(pVmxTransient->uIdtVectoringInfo),
rc = VINF_SUCCESS;
break;
}
case VMXREFLECTXCPT_DF:
{
Log(("IDT: Pending vectoring #DF %#RX64 uIdtVector=%#x uExitVector=%#x\n", pVCpu->hm.s.Event.u64IntrInfo,
break;
}
case VMXREFLECTXCPT_TF:
{
rc = VINF_EM_RESET;
break;
}
default:
break;
}
}
return rc;
}
/**
* Saves the guest's CR0 register from the VMCS into the guest-CPU context.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
int rc = VINF_SUCCESS;
{
}
return rc;
}
/**
* Saves the guest's CR4 register from the VMCS into the guest-CPU context.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
int rc = VINF_SUCCESS;
{
}
return rc;
}
/**
* Saves the guest's RIP register from the VMCS into the guest-CPU context.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
int rc = VINF_SUCCESS;
{
RTGCUINTREG uVal = 0;
}
return rc;
}
/**
* Saves the guest's RSP register from the VMCS into the guest-CPU context.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
int rc = VINF_SUCCESS;
{
RTGCUINTREG uVal = 0;
}
return rc;
}
/**
* Saves the guest's RFLAGS from the VMCS into the guest-CPU context.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
int rc = VINF_SUCCESS;
{
/* Undo our real-on-v86-mode changes to eflags if necessary. */
{
}
}
return rc;
}
/**
* Wrapper for saving the guest's RIP, RSP and RFLAGS from the VMCS into the
* guest-CPU context.
*/
{
return rc;
}
/**
* Saves the guest's interruptibility-state ("interrupt shadow" as AMD calls it)
* from the guest-state area in the VMCS.
*
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
uint32_t uIntrState = 0;
if (!uIntrState)
else
{
}
}
/**
* Saves the guest's activity state.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
/* Nothing to do for now until we make use of different guest-CPU activity state. Just update the flag. */
return VINF_SUCCESS;
}
/**
* Saves the guest SYSENTER MSRs (SYSENTER_CS, SYSENTER_EIP, SYSENTER_ESP) from
* the current VMCS into the guest-CPU context.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
int rc = VINF_SUCCESS;
{
}
RTGCUINTREG uGCVal = 0;
{
}
{
}
return rc;
}
/**
* Saves the guest FS_BASE MSRs from the current VMCS into the guest-CPU
* context.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
int rc = VINF_SUCCESS;
{
RTGCUINTREG uVal = 0;
}
return rc;
}
/**
* Saves the guest GS_BASE MSRs from the current VMCS into the guest-CPU
* context.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
int rc = VINF_SUCCESS;
{
RTGCUINTREG uVal = 0;
}
return rc;
}
/**
* guest-CPU context. Currently these are LSTAR, STAR, SFMASK and TSC_AUX.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
return VINF_SUCCESS;
{
pMsr += i;
switch (pMsr->u32IndexMSR)
{
case MSR_K6_EFER: /* EFER can't be changed without causing a VM-exit. */ break;
default:
{
AssertFailed();
return VERR_HM_UNEXPECTED_LD_ST_MSR;
}
}
}
#endif
return VINF_SUCCESS;
}
/**
* Saves the guest control registers from the current VMCS into the guest-CPU
* context.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
/* Guest CR0. Guest FPU. */
/* Guest CR4. */
/* Guest CR3. Only changes with Nested Paging. This must be done -after- saving CR0 and CR4 from the guest! */
{
{
RTGCUINTREG uVal = 0;
{
/* Set the force flag to inform PGM about it when necessary. It is cleared by PGMUpdateCR3().*/
}
/* We require EFER to check PAE mode. */
/* If the guest is in PAE mode, sync back the PDPE's into the guest state. */
{
/* Set the force flag to inform PGM about it when necessary. It is cleared by PGMGstUpdatePaePdpes(). */
}
}
}
return rc;
}
/**
* Reads a guest segment register from the current VMCS into the guest-CPU
* context.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param idxSel Index of the selector in the VMCS.
* @param idxLimit Index of the segment limit in the VMCS.
* @param idxBase Index of the segment base in the VMCS.
* @param idxAccess Index of the access rights of the segment in the VMCS.
* @param pSelReg Pointer to the segment selector.
*
* @remarks No-long-jump zone!!!
* @remarks Never call this function directly. Use the VMXLOCAL_READ_SEG() macro
* as that takes care of whether to read from the VMCS cache or not.
*/
DECLINLINE(int) hmR0VmxReadSegmentReg(PVMCPU pVCpu, uint32_t idxSel, uint32_t idxLimit, uint32_t idxBase, uint32_t idxAccess,
{
RTGCUINTREG uGCVal = 0;
/*
* If VT-x marks the segment as unusable, the rest of the attributes are undefined.
* See Intel spec. 27.3.2 "Saving Segment Registers and Descriptor-Table Registers".
*/
{
/** @todo r=ramshankar: This can't be right for CS, SS which have exceptions for
* certain bits, they're not all undefined. Consider ORing
* HMVMX_SEL_UNUSABLE instead? */
}
return rc;
}
/**
* Saves the guest segment registers from the current VMCS into the guest-CPU
* context.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
#ifdef VMX_USE_CACHED_VMCS_ACCESSES
#else
#endif
int rc = VINF_SUCCESS;
/* Guest segment registers. */
{
/* Restore segment attributes for real-on-v86 mode hack. */
{
}
}
return rc;
}
/**
* Saves the guest descriptor table registers and task register from the current
* VMCS into the guest-CPU context.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
int rc = VINF_SUCCESS;
/* Guest LDTR. */
{
}
/* Guest GDTR. */
RTGCUINTREG uGCVal = 0;
{
}
/* Guest IDTR. */
{
}
/* Guest TR. */
{
/* For real-mode emulation using virtual-8086 mode we have the fake TSS (pRealModeTSS) in TR, don't save the fake one. */
}
return rc;
}
/**
* Saves the guest debug registers from the current VMCS into the guest-CPU
* context.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
int rc = VINF_SUCCESS;
{
}
return rc;
}
/**
* Saves the guest APIC state from the currentl VMCS into the guest-CPU context.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
/* Updating TPR is already done in hmR0VmxPostRunGuest(). Just update the flag. */
return VINF_SUCCESS;
}
/**
* Saves the entire guest state from the currently active VMCS into the
* guest-CPU context. This essentially VMREADs all guest-data.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*/
{
return VINF_SUCCESS;
LogFunc(("\n"));
AssertLogRelMsgRCReturn(rc, ("hmR0VmxSaveGuestRipRspRflags failed! rc=%Rrc (pVCpu=%p)\n", rc, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxSaveGuestControlRegs failed! rc=%Rrc (pVCpu=%p)\n", rc, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxSaveGuestSegmentRegs failed! rc=%Rrc (pVCpu=%p)\n", rc, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxSaveGuestTableRegs failed! rc=%Rrc (pVCpu=%p)\n", rc, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxSaveGuestDebugRegs failed! rc=%Rrc (pVCpu=%p)\n", rc, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxSaveGuestSysenterMsrs failed! rc=%Rrc (pVCpu=%p)\n", rc, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxSaveGuestFSBaseMsr failed! rc=%Rrc (pVCpu=%p)\n", rc, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxSaveGuestGSBaseMsr failed! rc=%Rrc (pVCpu=%p)\n", rc, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxSaveGuestAutoLoadStoreMsrs failed! rc=%Rrc (pVCpu=%p)\n", rc, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxSaveGuestActivityState failed! rc=%Rrc (pVCpu=%p)\n", rc, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxSaveGuestDebugRegs failed! rc=%Rrc (pVCpu=%p)\n", rc, pVCpu), rc);
("Missed guest state bits while saving state; residue %RX32\n", pVCpu->hm.s.vmx.fUpdatedGuestState));
return rc;
}
/**
* Check per-VM and per-VCPU force flag actions that require us to go back to
* ring-3 for one reason or another.
*
* @returns VBox status code (information status code included).
* @retval VINF_SUCCESS if we don't have any actions that require going back to
* ring-3.
* @retval VINF_PGM_SYNC_CR3 if we have pending PGM CR3 sync.
* @retval VINF_EM_PENDING_REQUEST if we have pending requests (like hardware
* interrupts)
* @retval VINF_PGM_POOL_FLUSH_PENDING if PGM is doing a pool flush and requires
* all EMTs to be in ring-3.
* @retval VINF_EM_RAW_TO_R3 if there is pending DMA requests.
* @retval VINF_EM_NO_MEMORY PGM is out of memory, we need to return
* to the EM loop.
*
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*/
{
int rc = VERR_INTERNAL_ERROR_5;
if ( VM_FF_IS_PENDING(pVM, VM_FF_HM_TO_R3_MASK | VM_FF_REQUEST | VM_FF_PGM_POOL_FLUSH_PENDING | VM_FF_PDM_DMA)
|| VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_HM_TO_R3_MASK | VMCPU_FF_PGM_SYNC_CR3 | VMCPU_FF_PGM_SYNC_CR3_NON_GLOBAL
{
/* We need the control registers now, make sure the guest-CPU context is updated. */
/* Pending HM CR3 sync. */
{
}
{
}
/* Pending PGM C3 sync. */
{
rc = PGMSyncCR3(pVCpu, pMixedCtx->cr0, pMixedCtx->cr3, pMixedCtx->cr4, VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_PGM_SYNC_CR3));
if (rc != VINF_SUCCESS)
{
return rc;
}
}
/* Pending HM-to-R3 operations (critsects, timers, EMT rendezvous etc.) */
/* -XXX- what was that about single stepping? */
{
rc = RT_UNLIKELY(VM_FF_IS_PENDING(pVM, VM_FF_PGM_NO_MEMORY)) ? VINF_EM_NO_MEMORY : VINF_EM_RAW_TO_R3;
return rc;
}
/* Pending VM request packets, such as hardware interrupts. */
{
Log(("hmR0VmxCheckForceFlags: Pending VM request forcing us back to ring-3\n"));
return VINF_EM_PENDING_REQUEST;
}
/* Pending PGM pool flushes. */
{
Log(("hmR0VmxCheckForceFlags: PGM pool flush pending forcing us back to ring-3\n"));
return VINF_PGM_POOL_FLUSH_PENDING;
}
/* Pending DMA requests. */
{
Log(("hmR0VmxCheckForceFlags: Pending DMA request forcing us back to ring-3\n"));
return VINF_EM_RAW_TO_R3;
}
}
/* Paranoia. */
return VINF_SUCCESS;
}
/**
* Converts any TRPM trap into a pending VMX event. This is typically used when
* entering from ring-3 (not longjmp returns).
*
* @param pVCpu Pointer to the VMCPU.
* @param pCtx Pointer to the guest-CPU context.
*/
{
if (!TRPMHasTrap(pVCpu))
{
return;
}
/* Refer Intel spec. 24.8.3 "VM-entry Controls for Event Injection" for the format of u32IntrInfo. */
if (enmTrpmEvent == TRPM_TRAP)
{
switch (uVector)
{
case X86_XCPT_BP:
case X86_XCPT_OF:
{
break;
}
case X86_XCPT_PF:
case X86_XCPT_DF:
case X86_XCPT_TS:
case X86_XCPT_NP:
case X86_XCPT_SS:
case X86_XCPT_GP:
case X86_XCPT_AC:
/* no break! */
default:
{
break;
}
}
}
else if (enmTrpmEvent == TRPM_HARDWARE_INT)
{
if (uVector != X86_XCPT_NMI)
else
}
else if (enmTrpmEvent == TRPM_SOFTWARE_INT)
else
Log(("Converting TRPM trap: u32IntrInfo=%#RX32 enmTrpmEvent=%d cbInstr=%u uErrCode=%#RX32 GCPtrFaultAddress=%#RGv\n",
}
/**
* Converts any pending VMX event into a TRPM trap. Typically used when leaving
* VT-x to execute any instruction.
*
* @param pvCpu Pointer to the VMCPU.
*/
{
{
/* If a trap was already pending, we did something wrong! */
Assert(TRPMQueryTrap(pVCpu, NULL /* pu8TrapNo */, NULL /* pEnmType */) == VERR_TRPM_NO_ACTIVE_TRAP);
switch (uVectorType)
{
break;
break;
case VMX_IDT_VECTORING_INFO_TYPE_SW_XCPT: /* #BP and #OF */
break;
default:
break;
}
Log(("Converting pending HM event to TRPM trap uVector=%#x enmTrapType=%d\n", uVector, enmTrapType));
if (fErrorCodeValid)
&& uVector == X86_XCPT_PF)
{
}
else if ( uVectorType == VMX_IDT_VECTORING_INFO_TYPE_SW_INT
{
}
}
}
/**
* Does the necessary state syncing before doing a longjmp to ring-3.
*
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
* @param rcExit The reason for exiting to ring-3. Can be
* VINF_VMM_UNKNOWN_RING3_CALL.
*
* @remarks No-long-jmp zone!!!
*/
{
/* Restore FPU state if necessary and resync on next R0 reentry .*/
{
}
/* Restore debug registers if necessary and resync on next R0 reentry. */
{
}
else if (CPUMIsHyperDebugStateActive(pVCpu))
{
}
}
/**
* An action requires us to go back to ring-3. This function does the necessary
* steps before we can safely return to ring-3. This is not the same as longjmps
* to ring-3, this is voluntary.
*
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
* @param rcExit The reason for exiting to ring-3. Can be
* VINF_VMM_UNKNOWN_RING3_CALL.
*/
{
{
/* We want to see what the guest-state was before VM-entry, don't resync here, as we won't continue guest execution. */
return;
}
{
return;
}
/* Please, no longjumps here (any logging shouldn't flush jump back to ring-3). NO LOGGING BEFORE THIS POINT! */
/* We need to do this only while truly exiting the "inner loop" back to ring-3 and -not- for any longjmp to ring3. */
/* Sync. the guest state. */
/* On our way back from ring-3 the following needs to be done. */
/** @todo This can change with preemption hooks. */
if (rcExit == VINF_EM_RAW_INTERRUPT)
else
}
/**
* VMMRZCallRing3 callback wrapper which saves the guest state before we
* longjump to ring-3 and possibly get preempted.
*
* @param pVCpu Pointer to the VMCPU.
* @param enmOperation The operation causing the ring-3 longjump.
* @param pvUser The user argument (pointer to the possibly
* out-of-date guest-CPU context).
*
* @remarks Must never be called with @a enmOperation ==
* VMMCALLRING3_VM_R0_ASSERTION.
*/
{
/* VMMRZCallRing3() already makes sure we never get called as a result of an longjmp due to an assertion, */
Log(("hmR0VmxCallRing3Callback->hmR0VmxLongJmpToRing3\n"));
}
/**
* Sets the interrupt-window exiting control in the VMCS which instructs VT-x to
* cause a VM-exit as soon as the guest is in a state to receive interrupts.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
*/
{
if (RT_LIKELY(pVCpu->CTX_SUFF(pVM)->hm.s.vmx.msr.vmx_proc_ctls.n.allowed1 & VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_INT_WINDOW_EXIT))
{
{
}
} /* else we will deliver interrupts whenever the guest exits next and is in a state to receive events. */
}
/**
* Injects any pending events into the guest if the guest is in a state to
* receive them.
*
* @returns VBox status code (informational status codes included).
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*/
{
/* Get the current interruptibility-state of the guest and then figure out what can be injected. */
Assert( !(uIntrState & VMX_VMCS_GUEST_INTERRUPTIBILITY_STATE_BLOCK_NMI) /* We don't support block-by-NMI and SMI yet.*/
Assert(!fBlockSti || pMixedCtx->eflags.Bits.u1IF); /* Cannot set block-by-STI when interrupts are disabled. */
int rc = VINF_SUCCESS;
{
bool fInject = true;
{
if ( fBlockInt
|| fBlockSti
|| fBlockMovSS)
{
fInject = false;
}
}
else if ( uIntrType == VMX_EXIT_INTERRUPTION_INFO_TYPE_NMI
&& ( fBlockMovSS
|| fBlockSti))
{
/* On some CPUs block-by-STI also blocks NMIs. See Intel spec. 26.3.1.5 "Checks On Guest Non-Register State". */
fInject = false;
}
if (fInject)
{
Log(("Injecting pending event\n"));
rc = hmR0VmxInjectEventVmcs(pVCpu, pMixedCtx, pVCpu->hm.s.Event.u64IntrInfo, pVCpu->hm.s.Event.cbInstr,
}
else
} /** @todo SMI. SMIs take priority over NMIs. */
else if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_INTERRUPT_NMI)) /* NMI. NMIs take priority over regular interrupts . */
{
/* On some CPUs block-by-STI also blocks NMIs. See Intel spec. 26.3.1.5 "Checks On Guest Non-Register State". */
if ( !fBlockMovSS
&& !fBlockSti)
{
Log(("Injecting NMI\n"));
0 /* GCPtrFaultAddress */, &uIntrState);
}
else
}
{
/* Check if there are guest external interrupts (PIC/APIC) pending and inject them if the guest can receive them. */
if ( !fBlockInt
&& !fBlockSti
&& !fBlockMovSS)
{
if (RT_SUCCESS(rc))
{
0 /* GCPtrFaultAddress */, &uIntrState);
}
else
{
/** @todo Does this actually happen? If not turn it into an assertion. */
rc = VINF_SUCCESS;
}
}
else
}
/*
* Delivery pending debug exception if the guest is single-stepping. The interruptibility-state could have been changed by
* hmR0VmxInjectEventVmcs() (e.g. real-on-v86 injecting software interrupts), re-evaluate it and set the BS bit.
*/
int rc2 = VINF_SUCCESS;
if ( fBlockSti
|| fBlockMovSS)
{
if (!DBGFIsStepping(pVCpu))
{
if (pMixedCtx->eflags.Bits.u1TF) /* We don't have any IA32_DEBUGCTL MSR for guests. Treat as all bits 0. */
{
/*
* The pending-debug exceptions field is cleared on all VM-exits except VMX_EXIT_TPR_BELOW_THRESHOLD, VMX_EXIT_MTF
* VMX_EXIT_APIC_WRITE, VMX_EXIT_VIRTUALIZED_EOI. See Intel spec. 27.3.4 "Saving Non-Register State".
*/
}
}
else
{
/* We are single-stepping in the hypervisor debugger, clear interrupt inhibition as setting the BS bit would mean
delivering a #DB to the guest upon VM-entry when it shouldn't be. */
uIntrState = 0;
}
}
/*
* There's no need to clear the VM entry-interruption information field here if we're not injecting anything.
* VT-x clears the valid bit on every VM-exit. See Intel spec. 24.8.3 "VM-Entry Controls for Event Injection".
*/
return rc;
}
/**
* Sets an invalid-opcode (#UD) exception as pending-for-injection into the VM.
*
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*/
{
hmR0VmxSetPendingEvent(pVCpu, u32IntrInfo, 0 /* cbInstr */, 0 /* u32ErrCode */, 0 /* GCPtrFaultAddress */);
}
/**
* Injects a double-fault (#DF) exception into the VM.
*
* @returns VBox status code (informational status code included).
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*/
{
return hmR0VmxInjectEventVmcs(pVCpu, pMixedCtx, u32IntrInfo, 0 /* cbInstr */, 0 /* u32ErrCode */, 0 /* GCPtrFaultAddress */,
}
/**
* Sets a debug (#DB) exception as pending-for-injection into the VM.
*
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*/
{
hmR0VmxSetPendingEvent(pVCpu, u32IntrInfo, 0 /* cbInstr */, 0 /* u32ErrCode */, 0 /* GCPtrFaultAddress */);
}
/**
* Sets an overflow (#OF) exception as pending-for-injection into the VM.
*
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
* @param cbInstr The value of RIP that is to be pushed on the guest
* stack.
*/
{
}
/**
* Injects a general-protection (#GP) fault into the VM.
*
* @returns VBox status code (informational status code included).
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
* @param u32ErrorCode The error code associated with the #GP.
*/
DECLINLINE(int) hmR0VmxInjectXcptGP(PVMCPU pVCpu, PCPUMCTX pMixedCtx, bool fErrorCodeValid, uint32_t u32ErrorCode,
{
if (fErrorCodeValid)
return hmR0VmxInjectEventVmcs(pVCpu, pMixedCtx, u32IntrInfo, 0 /* cbInstr */, u32ErrorCode, 0 /* GCPtrFaultAddress */,
}
/**
* Sets a software interrupt (INTn) as pending-for-injection into the VM.
*
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
* @param uVector The software interrupt vector number.
* @param cbInstr The value of RIP that is to be pushed on the guest
* stack.
*/
DECLINLINE(void) hmR0VmxSetPendingIntN(PVMCPU pVCpu, PCPUMCTX pMixedCtx, uint16_t uVector, uint32_t cbInstr)
{
if ( uVector == X86_XCPT_BP
|| uVector == X86_XCPT_OF)
{
}
else
}
/**
* Pushes a 2-byte value onto the real-mode (in virtual-8086 mode) guest's
* stack.
*
* @returns VBox status code (information status code included).
* @retval VINF_EM_RESET if pushing a value to the stack caused a triple-fault.
* @param pVM Pointer to the VM.
* @param pMixedCtx Pointer to the guest-CPU context.
* @param uValue The value to push to the guest stack.
*/
{
/*
* The stack limit is 0xffff in real-on-virtual 8086 mode. Real-mode with weird stack limits cannot be run in
* virtual 8086 mode in VT-x. See Intel spec. 26.3.1.2 "Checks on Guest Segment Registers".
* See Intel Instruction reference for PUSH and Intel spec. 22.33.1 "Segment Wraparound".
*/
return VINF_EM_RESET;
int rc = PGMPhysSimpleWriteGCPhys(pVM, pMixedCtx->ss.u64Base + pMixedCtx->sp, &uValue, sizeof(uint16_t));
return rc;
}
/**
* Injects an event into the guest upon VM-entry by updating the relevant fields
* in the VM-entry area in the VMCS.
*
* @returns VBox status code (informational error codes included).
* @retval VINF_SUCCESS if the event is successfully injected into the VMCS.
* @retval VINF_EM_RESET if event injection resulted in a triple-fault.
*
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may
* be out-of-sync. Make sure to update the required
* fields before using them.
* @param u64IntrInfo The VM-entry interruption-information field.
* @param cbInstr The VM-entry instruction length in bytes (for
* software interrupts, exceptions and privileged
* software exceptions).
* @param u32ErrCode The VM-entry exception error code.
* @param GCPtrFaultAddress The page-fault address for #PF exceptions.
* @param puIntrState Pointer to the current guest interruptibility-state.
* This interruptibility-state will be updated if
* necessary. This cannot not be NULL.
*
* @remarks No-long-jump zone!!!
* @remarks Requires CR0!
*/
static int hmR0VmxInjectEventVmcs(PVMCPU pVCpu, PCPUMCTX pMixedCtx, uint64_t u64IntrInfo, uint32_t cbInstr,
{
/* Intel spec. 24.8.3 "VM-Entry Controls for Event Injection" specifies the interruption-information field to be 32-bits. */
/* Cannot inject an NMI when block-by-MOV SS is in effect. */
/* We require CR0 to check if the guest is in real-mode. */
/*
* Hardware interrupts & exceptions cannot be delivered through the software interrupt redirection bitmap to the real
* mode task in virtual-8086 mode. We must jump to the interrupt handler in the (real-mode) guest.
* See Intel spec. 20.3 "Interrupt and Exception handling in Virtual-8086 Mode" for interrupt & exception classes.
* See Intel spec. 20.1.4 "Interrupt and Exception Handling" for real-mode interrupt handling.
*/
{
{
/* We require RIP, RSP, RFLAGS, CS, IDTR. Save the required ones from the VMCS. */
/* Check if the interrupt handler is present in the IVT (real-mode IDT). IDT limit is (4N - 1). */
{
/* If we are trying to inject a #DF with no valid IDT entry, return a triple-fault. */
if (uVector == X86_XCPT_DF)
return VINF_EM_RESET;
else if (uVector == X86_XCPT_GP)
{
/* If we're injecting a #GP with no valid IDT entry, inject a double-fault. */
}
/* If we're injecting an interrupt/exception with no valid IDT entry, inject a general-protection fault. */
/* No error codes for exceptions in real-mode. See Intel spec. 20.1.4 "Interrupt and Exception Handling" */
return hmR0VmxInjectXcptGP(pVCpu, pMixedCtx, false /* fErrCodeValid */, 0 /* u32ErrCode */, puIntrState);
}
/* Software exceptions (#BP and #OF exceptions thrown as a result of INT3 or INTO) */
{
/* #BP and #OF are both benign traps, we need to resume the next instruction. */
}
/* Get the code segment selector and offset from the IDT entry for the interrupt handler. */
uint16_t offIdtEntry = 0;
RTSEL selIdtEntry = 0;
if (rc == VINF_SUCCESS)
{
&& uVector == X86_XCPT_PF)
{
}
/* We're clearing interrupts, which means no block-by-STI interrupt-inhibition. */
{
Log(("Clearing inhibition due to STI.\n"));
}
Log(("Injecting real-mode: u32IntrInfo=%#x u32ErrCode=%#x instrlen=%#x\n", u32IntrInfo, u32ErrCode, cbInstr));
}
return rc;
}
else
{
/*
* For unrestricted execution enabled CPUs running real-mode guests, we must not set the deliver-error-code bit.
* See Intel spec. 26.2.1.3 "VM-Entry Control Fields".
*/
}
}
/* Validate. */
Assert(VMX_EXIT_INTERRUPTION_INFO_VALID(u32IntrInfo)); /* Bit 31 (Valid bit) must be set by caller. */
/* Inject. */
&& uVector == X86_XCPT_PF)
{
}
Log(("Injecting u32IntrInfo=%#x u32ErrCode=%#x cbInstr=%#x uCR2=%#RGv\n", u32IntrInfo, u32ErrCode, cbInstr, pMixedCtx->cr2));
return rc;
}
/**
* Enters the VT-x session.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pCpu Pointer to the CPU info struct.
*/
{
/* Make sure we're in VMX root mode. */
if (!(u32HostCR4 & X86_CR4_VMXE))
{
LogRel(("VMXR0Enter: X86_CR4_VMXE bit in CR4 is not set!\n"));
return VERR_VMX_X86_CR4_VMXE_CLEARED;
}
/* Load the active VMCS as the current one. */
if (RT_FAILURE(rc))
return rc;
/** @todo this will change with preemption hooks where can can VMRESUME as long
* as we're no preempted. */
return VINF_SUCCESS;
}
/**
* Leaves the VT-x session.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pCtx Pointer to the guest-CPU context.
*/
{
/** @todo this will change with preemption hooks where we only VMCLEAR when
* we are actually going to be preempted, not all the time like we
* currently do. */
/*
* Sync the current VMCS (writes back internal data back into the VMCS region in memory)
* and mark the VMCS launch-state as "clear".
*/
return rc;
}
/**
* Saves the host state in the VMCS host-state.
* Sets up the VM-exit MSR-load area.
*
* The CPU state will be loaded from these fields on every successful VM-exit.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
*
* @remarks No-long-jump zone!!!
*/
{
/* Nothing to do if the host-state-changed flag isn't set. This will later be optimized when preemption hooks are in place. */
return VINF_SUCCESS;
AssertLogRelMsgRCReturn(rc, ("hmR0VmxSaveHostControlRegisters failed! rc=%Rrc (pVM=%p pVCpu=%p)\n", rc, pVM, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxSaveHostSegmentRegisters failed! rc=%Rrc (pVM=%p pVCpu=%p)\n", rc, pVM, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxSaveHostMsrs failed! rc=%Rrc (pVM=%p pVCpu=%p)\n", rc, pVM, pVCpu), rc);
return rc;
}
/**
* Loads the guest state into the VMCS guest-state area. The CPU state will be
* loaded from these fields on every successful VM-entry.
*
* Sets up the VM-entry MSR-load and VM-exit MSR-store areas.
* Sets up the VM-entry controls.
* Sets up the appropriate VMX non-root function to execute guest code based on
* the guest CPU mode.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
*
* @remarks No-long-jump zone!!!
*/
{
/* Determine real-on-v86 mode. */
{
}
/*
* Load the guest-state into the VMCS.
* Any ordering dependency among the sub-functions below must be explicitly stated using comments.
* Ideally, assert that the cross-dependent bits are up to date at the point of using it.
*/
AssertLogRelMsgRCReturn(rc, ("hmR0VmxLoadGuestEntryCtls! rc=%Rrc (pVM=%p pVCpu=%p)\n", rc, pVM, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxSetupExitCtls failed! rc=%Rrc (pVM=%p pVCpu=%p)\n", rc, pVM, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxLoadGuestActivityState! rc=%Rrc (pVM=%p pVCpu=%p)\n", rc, pVM, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxLoadGuestControlRegs: rc=%Rrc (pVM=%p pVCpu=%p)\n", rc, pVM, pVCpu), rc);
/* Must be done after CR0 is loaded (strict builds require CR0 for segment register validation checks). */
AssertLogRelMsgRCReturn(rc, ("hmR0VmxLoadGuestSegmentRegs: rc=%Rrc (pVM=%p pVCpu=%p)\n", rc, pVM, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxLoadGuestDebugRegs: rc=%Rrc (pVM=%p pVCpu=%p)\n", rc, pVM, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxLoadGuestMsrs! rc=%Rrc (pVM=%p pVCpu=%p)\n", rc, pVM, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxLoadGuestApicState! rc=%Rrc (pVM=%p pVCpu=%p)\n", rc, pVM, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxLoadGuestGprs! rc=%Rrc (pVM=%p pVCpu=%p)\n", rc, pVM, pVCpu), rc);
AssertLogRelMsgRCReturn(rc, ("hmR0VmxSetupVMRunHandler! rc=%Rrc (pVM=%p pVCpu=%p)\n", rc, pVM, pVCpu), rc);
("Missed updating flags while loading guest state. pVM=%p pVCpu=%p fContextUseFlags=%#RX32\n",
return rc;
}
/**
* Does the preparations before executing guest code in VT-x.
*
* This may cause longjmps to ring-3 and may even result in rescheduling to the
* recompiler. We must be cautious what we do here regarding committing
* guest-state information into the the VMCS assuming we assuredly execute the
* guest in VT-x. If we fall back to the recompiler after updating VMCS and
* clearing the common-state (TRPM/forceflags), we must undo those changes so
* that the recompiler can (and should) use them when it resumes guest
* execution. Otherwise such operations must be done when we can no longer
* exit to ring-3.
*
* @returns VBox status code (informational status codes included).
* @retval VINF_SUCCESS if we can proceed with running the guest.
* @retval VINF_EM_RESET if a triple-fault occurs while injecting a double-fault
* into the guest.
* @retval VINF_* scheduling changes, we have to go back to ring-3.
*
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
* @param pVmxTransient Pointer to the VMX transient structure.
*
* @remarks Called with preemption disabled.
*/
DECLINLINE(int) hmR0VmxPreRunGuest(PVM pVM, PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
#endif
/* Check force flag actions that might require us to go back to ring-3. */
if (rc != VINF_SUCCESS)
return rc;
/* Setup the Virtualized APIC accesses. pMixedCtx->msrApicBase is always up-to-date. It's not part of the VMCS. */
{
/* Unalias any existing mapping. */
/* Map the HC APIC-access page into the GC space, this also updates the shadow page tables if necessary. */
rc = IOMMMIOMapMMIOHCPage(pVM, pVCpu, GCPhysApicBase, pVM->hm.s.vmx.HCPhysApicAccess, X86_PTE_RW | X86_PTE_P);
}
/* We disable interrupts so that we don't miss any interrupts that would flag preemption (IPI/timers etc.) */
{
/* Don't use VINF_EM_RAW_INTERRUPT_HYPER as we can't assume the host does kernel preemption. Maybe some day? */
return VINF_EM_RAW_INTERRUPT;
}
#endif
/*
* Evaluates and injects any pending events, toggling force-flags and updating the guest-interruptibility
* state (interrupt shadow) in the VMCS. This -can- potentially be reworked to be done before disabling
* interrupts and handle returning to ring-3 afterwards, but requires very careful state restoration.
*/
/** @todo Rework event evaluation and injection to be complete separate. */
return rc;
}
/**
* Prepares to run guest code in VT-x and we've committed to doing so. This
* means there is no backing out to ring-3 or anywhere else at this
* point.
*
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data may be
* out-of-sync. Make sure to update the required fields
* before using them.
* @param pVmxTransient Pointer to the VMX transient structure.
*
* @remarks Called with preemption disabled.
* @remarks No-long-jump zone!!!
*/
DECLINLINE(void) hmR0VmxPreRunGuestCommitted(PVM pVM, PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
/** @todo I don't see the point of this, VMMR0EntryFast() already disables interrupts for the entire period. */
#endif
/* Load the required guest state bits (for guest-state changes in the inner execution loop). */
#ifdef HMVMX_SYNC_FULL_GUEST_STATE
#endif
int rc = VINF_SUCCESS;
{
}
{
}
AssertMsg(!pVCpu->hm.s.fContextUseFlags, ("fContextUseFlags =%#x\n", pVCpu->hm.s.fContextUseFlags));
/* Cache the TPR-shadow for checking on every VM-exit if it might have changed. */
{
}
ASMAtomicWriteBool(&pVCpu->hm.s.fCheckedTLBFlush, true); /* Used for TLB-shootdowns, set this across the world switch. */
/*
* TPR patching (only active for 32-bit guests on 64-bit capable CPUs) when the CPU does not supported virtualizing
* APIC accesses feature (VMX_VMCS_CTRL_PROC_EXEC2_VIRT_APIC).
*/
{
/* Need guest's LSTAR MSR (which is part of the auto load/store MSRs in the VMCS), ensure we have the updated one. */
/* The patch code uses the LSTAR as it's not used by a guest in 32-bit mode implicitly (i.e. SYSCALL is 64-bit only). */
see hmR0VmxLoadGuestApicState(). */
}
#ifndef VBOX_WITH_AUTO_MSR_LOAD_RESTORE
/*
* Save the current Host TSC_AUX and write the guest TSC_AUX to the host, so that
* RDTSCPs (that don't cause exits) reads the guest MSR. See @bugref{3324}.
*/
{
uint64_t u64HostTscAux = 0;
}
#endif
to start executing. */
}
/**
* Performs some essential restoration of state after running guest code in
* VT-x.
*
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
* @param pVmxTransient Pointer to the VMX transient structure.
*
* @remarks Called with interrupts disabled.
* @remarks No-long-jump zone!!! This function will however re-enable longjmps
* unconditionally when it is safe to do so.
*/
DECLINLINE(void) hmR0VmxPostRunGuest(PVM pVM, PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient, int rcVMRun)
{
ASMAtomicWriteBool(&pVCpu->hm.s.fCheckedTLBFlush, false); /* See HMInvalidatePageOnAllVCpus(): used for TLB-shootdowns. */
ASMAtomicIncU32(&pVCpu->hm.s.cWorldSwitchExits); /* Initialized in vmR3CreateUVM(): used for TLB-shootdowns. */
pVCpu->hm.s.vmx.fUpdatedGuestState = 0; /* Exits/longjmps to ring-3 requires saving the guest state. */
{
#ifndef VBOX_WITH_AUTO_MSR_LOAD_RESTORE
/* Restore host's TSC_AUX. */
#endif
/** @todo Find a way to fix hardcoding a guestimate. */
}
/* Restore the effects of TPR patching if any. */
{
}
/* Save the basic VM-exit reason. Refer Intel spec. 24.9.1 "Basic VM-exit Information". */
{
Log(("VM-entry failure: rcVMRun=%Rrc fVMEntryFailed=%RTbool\n", rcVMRun, pVmxTransient->fVMEntryFailed));
return;
}
{
/* Update the guest interruptibility-state from the VMCS. */
#if defined(HMVMX_SYNC_FULL_GUEST_STATE) || defined(HMVMX_SAVE_FULL_GUEST_STATE)
#endif
/*
* If the TPR was raised by the guest, it wouldn't cause a VM-exit immediately. Instead we sync the TPR lazily whenever
* we eventually get a VM-exit for any reason. This maybe expensive as PDMApicSetTPR() can longjmp to ring-3; also why
* we do it outside of hmR0VmxSaveGuestState() which must never cause longjmps.
*/
{
}
}
}
/**
* Runs the guest code using VT-x.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param pCtx Pointer to the guest-CPU context.
*
* @remarks Called with preemption disabled.
*/
{
int rc = VERR_INTERNAL_ERROR_5;
for (;; cLoops++)
{
Assert(!HMR0SuspendPending());
/* Preparatory work for running guest code, this may return to ring-3 for some last minute updates. */
if (rc != VINF_SUCCESS)
break;
/*
* No longjmps to ring-3 from this point on!!!
* Asserts() will still longjmp to ring-3 (but won't return), which is intentional, better than a kernel panic.
* This also disables flushing of the R0-logger instance (if any).
*/
/* The guest-CPU context is now outdated, 'pCtx' is to be treated as 'pMixedCtx' from this point on!!! */
/*
* Restore any residual host-state and save any bits shared between host and guest into the guest-CPU state.
* This will also re-enable longjmps to ring-3 when it has reached a safe point!!!
*/
if (RT_UNLIKELY(rc != VINF_SUCCESS)) /* Check for errors with running the VM (VMLAUNCH/VMRESUME). */
{
return rc;
}
/* Handle the VM-exit. */
#ifdef HMVMX_USE_FUNCTION_TABLE
#else
#endif
if (rc != VINF_SUCCESS)
break;
{
break;
}
}
if (rc == VERR_EM_INTERPRETER)
else if (rc == VINF_EM_RESET)
return rc;
}
#ifndef HMVMX_USE_FUNCTION_TABLE
DECLINLINE(int) hmR0VmxHandleExit(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient, uint32_t rcReason)
{
int rc;
switch (rcReason)
{
case VMX_EXIT_TPR_BELOW_THRESHOLD: rc = hmR0VmxExitTprBelowThreshold(pVCpu, pMixedCtx, pVmxTransient); break;
case VMX_EXIT_ERR_INVALID_GUEST_STATE: rc = hmR0VmxExitErrInvalidGuestState(pVCpu, pMixedCtx, pVmxTransient); break;
case VMX_EXIT_ERR_MACHINE_CHECK: rc = hmR0VmxExitErrMachineCheck(pVCpu, pMixedCtx, pVmxTransient); break;
case VMX_EXIT_VMCALL:
case VMX_EXIT_VMCLEAR:
case VMX_EXIT_VMLAUNCH:
case VMX_EXIT_VMPTRLD:
case VMX_EXIT_VMPTRST:
case VMX_EXIT_VMREAD:
case VMX_EXIT_VMRESUME:
case VMX_EXIT_VMWRITE:
case VMX_EXIT_VMXOFF:
case VMX_EXIT_VMXON:
case VMX_EXIT_INVEPT:
case VMX_EXIT_INVVPID:
case VMX_EXIT_VMFUNC:
break;
default:
break;
}
return rc;
}
#endif
#ifdef DEBUG
# define VMX_ASSERT_PREEMPT_CPUID_VAR() \
# define VMX_ASSERT_PREEMPT_CPUID() \
do \
{ \
RTCPUID const idAssertCpuNow = RTThreadPreemptIsEnabled(NIL_RTTHREAD) ? NIL_RTCPUID : RTMpCpuId(); \
} while (0)
# define VMX_VALIDATE_EXIT_HANDLER_PARAMS() \
do { \
Assert(ASMIntAreEnabled()); \
LogFunc(("vcpu[%u] vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n", \
if (VMMR0IsLogFlushDisabled(pVCpu)) \
} while (0)
# define VMX_VALIDATE_EXIT_XCPT_HANDLER_PARAMS() \
do { \
LogFunc(("\n")); \
} while(0)
#else /* Release builds */
# define VMX_VALIDATE_EXIT_HANDLER_PARAMS() do { HMVMX_STOP_EXIT_DISPATCH_PROF(); } while(0)
# define VMX_VALIDATE_EXIT_XCPT_HANDLER_PARAMS() do { } while(0)
#endif
/**
* Advances the guest RIP after reading it from the VMCS.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param pMixedCtx Pointer to the guest-CPU context. The data maybe
* out-of-sync. Make sure to update the required fields
* before using them.
* @param pVmxTransient Pointer to the VMX transient structure.
*
* @remarks No-long-jump zone!!!
*/
DECLINLINE(int) hmR0VmxAdvanceGuestRip(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
return rc;
}
/* -=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- VM-exit handlers -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
/* -=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/**
* VM-exit handler for external interrupts (VMX_EXIT_EXT_INT).
*/
{
return VINF_SUCCESS;
#else
return VINF_EM_RAW_INTERRUPT;
#endif
}
/**
* VM-exit handler for exceptions and NMIs (VMX_EXIT_XCPT_NMI).
*/
{
{
return VINF_EM_RAW_INTERRUPT;
}
/* If this VM-exit occurred while delivering an event through the guest IDT, handle it accordingly. */
{
return VINF_SUCCESS;
}
{
return rc;
}
switch (uIntrType)
{
case VMX_EXIT_INTERRUPTION_INFO_TYPE_SW_XCPT: /* Software exception. (#BP or #OF) */
/* no break */
{
switch (uVector)
{
#ifdef HMVMX_ALWAYS_TRAP_ALL_XCPTS
#endif
default:
{
{
0 /* GCPtrFaultAddress */);
}
else
{
}
break;
}
}
break;
}
default:
{
AssertMsgFailed(("Unexpected interruption code %#x\n", VMX_EXIT_INTERRUPTION_INFO_TYPE(uExitIntrInfo)));
break;
}
}
return rc;
}
/**
* VM-exit handler for interrupt-window exiting (VMX_EXIT_INT_WINDOW).
*/
{
/* Indicate that we no longer need to VM-exit when the guest is ready to receive interrupts, it is now ready. */
/* Deliver the pending interrupt via hmR0VmxPreRunGuest()->hmR0VmxInjectEvent() and resume guest execution. */
return VINF_SUCCESS;
}
/**
* VM-exit handler for NMI-window exiting (VMX_EXIT_NMI_WINDOW).
*/
{
AssertMsgFailed(("Unexpected NMI-window exit.\n"));
return VERR_VMX_UNEXPECTED_EXIT_CODE;
}
/**
* VM-exit handler for WBINVD (VMX_EXIT_WBINVD). Conditional VM-exit.
*/
{
}
/**
* VM-exit handler for INVD (VMX_EXIT_INVD). Unconditional VM-exit.
*/
{
}
/**
* VM-exit handler for CPUID (VMX_EXIT_CPUID). Unconditional VM-exit.
*/
{
{
}
else
{
}
return rc;
}
/**
* VM-exit handler for GETSEC (VMX_EXIT_GETSEC). Unconditional VM-exit.
*/
{
return VINF_EM_RAW_EMULATE_INSTR;
AssertMsgFailed(("hmR0VmxExitGetsec: unexpected VM-exit when CR4.SMXE is 0.\n"));
return VERR_VMX_UNEXPECTED_EXIT_CODE;
}
/**
* VM-exit handler for RDTSC (VMX_EXIT_RDTSC). Conditional VM-exit.
*/
{
int rc = hmR0VmxSaveGuestCR4(pVCpu, pMixedCtx); /** @todo review if CR4 is really required by EM. */
{
/* If we get a spurious VM-exit when offsetting is enabled, we must reset offsetting on VM-reentry. See @bugref{6634}. */
}
else
{
}
return rc;
}
/**
* VM-exit handler for RDTSCP (VMX_EXIT_RDTSCP). Conditional VM-exit.
*/
{
int rc = hmR0VmxSaveGuestCR4(pVCpu, pMixedCtx); /** @todo review if CR4 is really required by EM. */
{
/* If we get a spurious VM-exit when offsetting is enabled, we must reset offsetting on VM-reentry. See @bugref{6634}. */
}
else
{
}
return rc;
}
/**
* VM-exit handler for RDPMC (VMX_EXIT_RDPMC). Conditional VM-exit.
*/
{
int rc = hmR0VmxSaveGuestCR4(pVCpu, pMixedCtx); /** @todo review if CR4 is really required by EM. */
{
}
else
{
}
return rc;
}
/**
* VM-exit handler for INVLPG (VMX_EXIT_INVLPG). Conditional VM-exit.
*/
{
VBOXSTRICTRC rc2 = EMInterpretInvlpg(pVM, pVCpu, CPUMCTX2CORE(pMixedCtx), pVmxTransient->uExitQualification);
else
{
AssertMsg(rc == VERR_EM_INTERPRETER, ("hmR0VmxExitInvlpg: EMInterpretInvlpg %#RGv failed with %Rrc\n",
}
return rc;
}
/**
* VM-exit handler for MONITOR (VMX_EXIT_MONITOR). Conditional VM-exit.
*/
{
else
{
AssertMsg(rc == VERR_EM_INTERPRETER, ("hmR0VmxExitMonitor: EMInterpretMonitor failed with %Rrc\n", rc));
}
return rc;
}
/**
* VM-exit handler for MWAIT (VMX_EXIT_MWAIT). Conditional VM-exit.
*/
{
|| rc == VINF_EM_HALT))
{
if ( rc == VINF_EM_HALT
{
rc = VINF_SUCCESS;
}
}
else
{
AssertMsg(rc == VERR_EM_INTERPRETER, ("hmR0VmxExitMwait: EMInterpretMWait failed with %Rrc\n", rc));
}
("hmR0VmxExitMwait: failed, invalid error code %Rrc\n", rc));
return rc;
}
/**
* VM-exit handler for RSM (VMX_EXIT_RSM). Unconditional VM-exit.
*/
{
/*
* Execution of RSM outside of SMM mode causes #UD regardless of VMX root or VMX non-root mode. In theory, we should never
* get this VM-exit. This can happen only if dual-monitor treatment of SMI and VMX is enabled, which can (only?) be done by
* executing VMCALL in VMX root operation. If we get here something funny is going on.
* See Intel spec. "33.15.5 Enabling the Dual-Monitor Treatment".
*/
return VERR_VMX_UNEXPECTED_EXIT_CODE;
}
/**
* VM-exit handler for SMI (VMX_EXIT_SMI). Unconditional VM-exit.
*/
{
/*
* This can only happen if we support dual-monitor treatment of SMI, which can be activated by executing VMCALL in VMX
* root operation. If we get there there is something funny going on.
* See Intel spec. "33.15.6 Activating the Dual-Monitor Treatment" and Intel spec. 25.3 "Other Causes of VM-Exits"
*/
return VERR_VMX_UNEXPECTED_EXIT_CODE;
}
/**
* VM-exit handler for IO SMI (VMX_EXIT_IO_SMI). Unconditional VM-exit.
*/
{
/* Same treatment as VMX_EXIT_SMI. See comment in hmR0VmxExitSmi(). */
return VERR_VMX_UNEXPECTED_EXIT_CODE;
}
/**
* VM-exit handler for SIPI (VMX_EXIT_SIPI). Conditional VM-exit.
*/
{
/*
* SIPI exits can only occur in VMX non-root operation when the "wait-for-SIPI" guest activity state is used. We currently
* don't make use of it (see hmR0VmxLoadGuestActivityState()) as our guests don't have direct access to the host LAPIC.
* See Intel spec. 25.3 "Other Causes of VM-exits".
*/
return VERR_VMX_UNEXPECTED_EXIT_CODE;
}
/**
* VM-exit handler for INIT signal (VMX_EXIT_INIT_SIGNAL). Unconditional
* VM-exit.
*/
HMVMX_EXIT_DECL hmR0VmxExitInitSignal(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
/*
* INIT signals are blocked in VMX root operation by VMXON and by SMI in SMM. See Intel spec. "33.14.1 Default Treatment of
* SMI Delivery" and "29.3 VMX Instructions" for "VMXON". It is -NOT- blocked in VMX non-root operation so we can potentially
* still get these exits. See Intel spec. "23.8 Restrictions on VMX operation".
*/
return VINF_SUCCESS; /** @todo r=ramshankar: correct?. */
}
/**
* VM-exit handler for triple faults (VMX_EXIT_TRIPLE_FAULT). Unconditional
* VM-exit.
*/
HMVMX_EXIT_DECL hmR0VmxExitTripleFault(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
return VINF_EM_RESET;
}
/**
* VM-exit handler for HLT (VMX_EXIT_HLT). Conditional VM-exit.
*/
{
rc = VINF_SUCCESS;
else
rc = VINF_EM_HALT;
return rc;
}
/**
* VM-exit handler for instructions that result in a #UD exception delivered to the guest.
*/
HMVMX_EXIT_DECL hmR0VmxExitSetPendingXcptUD(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
return VINF_SUCCESS;
}
/**
* VM-exit handler for expiry of the VMX preemption timer.
*/
HMVMX_EXIT_DECL hmR0VmxExitPreemptTimer(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
/* If the preemption-timer has expired, reinitialize the preemption timer on next VM-entry. */
/* If there are any timer events pending, fall back to ring-3, otherwise resume guest execution. */
}
/**
* VM-exit handler for XSETBV (VMX_EXIT_XSETBV). Unconditional VM-exit.
*/
{
/* We expose XSETBV to the guest, fallback to the recompiler for emulation. */
/** @todo check if XSETBV is supported by the recompiler. */
return VERR_EM_INTERPRETER;
}
/**
* VM-exit handler for INVPCID (VMX_EXIT_INVPCID). Conditional VM-exit.
*/
{
/* The guest should not invalidate the host CPU's TLBs, fallback to recompiler. */
/** @todo implement EMInterpretInvpcid() */
return VERR_EM_INTERPRETER;
}
/**
* VM-exit handler for invalid-guest-state (VMX_EXIT_ERR_INVALID_GUEST_STATE).
* Error VM-exit.
*/
HMVMX_EXIT_DECL hmR0VmxExitErrInvalidGuestState(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
return VERR_VMX_INVALID_GUEST_STATE;
}
/**
* VM-exit handler for VM-entry failure due to an MSR-load
* (VMX_EXIT_ERR_MSR_LOAD). Error VM-exit.
*/
HMVMX_EXIT_DECL hmR0VmxExitErrMsrLoad(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
return VERR_VMX_UNEXPECTED_EXIT_CODE;
}
/**
* VM-exit handler for VM-entry failure due to a machine-check event
* (VMX_EXIT_ERR_MACHINE_CHECK). Error VM-exit.
*/
HMVMX_EXIT_DECL hmR0VmxExitErrMachineCheck(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
AssertMsgFailed(("Unexpected machine-check event exit. pVCpu=%p pMixedCtx=%p\n", pVCpu, pMixedCtx));
return VERR_VMX_UNEXPECTED_EXIT_CODE;
}
/**
* VM-exit handler for all undefined reasons. Should never ever happen.. in
* theory.
*/
HMVMX_EXIT_DECL hmR0VmxExitErrUndefined(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
AssertMsgFailed(("Huh!? Undefined VM-exit reason %d. pVCpu=%p pMixedCtx=%p\n", pVmxTransient->uExitReason, pVCpu, pMixedCtx));
return VERR_VMX_UNDEFINED_EXIT_CODE;
}
/**
* VM-exit handler for XDTR (LGDT, SGDT, LIDT, SIDT) accesses
* (VMX_EXIT_XDTR_ACCESS) and LDT and TR access (LLDT, LTR, SLDT, STR).
* Conditional VM-exit.
*/
HMVMX_EXIT_DECL hmR0VmxExitXdtrAccess(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
/* By default, we don't enable VMX_VMCS_CTRL_PROC_EXEC2_DESCRIPTOR_TABLE_EXIT. */
return VERR_EM_INTERPRETER;
return VERR_VMX_UNEXPECTED_EXIT_CODE;
}
/**
* VM-exit handler for RDRAND (VMX_EXIT_RDRAND). Conditional VM-exit.
*/
{
/* By default, we don't enable VMX_VMCS_CTRL_PROC_EXEC2_RDRAND_EXIT. */
return VERR_EM_INTERPRETER;
return VERR_VMX_UNEXPECTED_EXIT_CODE;
}
/**
* VM-exit handler for RDMSR (VMX_EXIT_RDMSR).
*/
{
/* EMInterpretRdmsr() requires CR0, Eflags and SS segment register. */
("hmR0VmxExitRdmsr: failed, invalid error code %Rrc\n", rc));
{
}
return rc;
}
/**
* VM-exit handler for WRMSR (VMX_EXIT_WRMSR).
*/
{
int rc = VINF_SUCCESS;
/* If TPR patching is active, LSTAR holds the guest TPR, writes to it must be propagated to the APIC. */
{
{
}
return VINF_SUCCESS;
}
/* EMInterpretWrmsr() requires CR0, EFLAGS and SS segment register. */
AssertMsg(rc == VINF_SUCCESS || rc == VERR_EM_INTERPRETER, ("hmR0VmxExitWrmsr: failed, invalid error code %Rrc\n", rc));
{
/* If this is an X2APIC WRMSR access, update the APIC state as well. */
{
}
else if (pMixedCtx->ecx == MSR_K6_EFER) /* EFER is the only MSR we auto-load but don't allow write-passthrough. */
{
}
else if (pMixedCtx->ecx == MSR_IA32_TSC) /* Windows 7 does this during bootup. See @bugref{6398}. */
/* Update MSRs that are part of the VMCS when MSR-bitmaps are not supported. */
{
{
case MSR_IA32_SYSENTER_EIP: pVCpu->hm.s.fContextUseFlags |= HM_CHANGED_GUEST_SYSENTER_EIP_MSR; break;
case MSR_IA32_SYSENTER_ESP: pVCpu->hm.s.fContextUseFlags |= HM_CHANGED_GUEST_SYSENTER_ESP_MSR; break;
case MSR_K8_FS_BASE: /* no break */
/* MSR_K8_KERNEL_GS_BASE: Nothing to do as it's not part of the VMCS. Manually loaded each time on VM-entry. */
}
}
#ifdef VBOX_STRICT
else
{
/* Paranoia. Validate that MSRs in the MSR-bitmaps with write-passthru are not intercepted. */
{
case MSR_IA32_SYSENTER_CS:
case MSR_IA32_SYSENTER_EIP:
case MSR_IA32_SYSENTER_ESP:
case MSR_K8_FS_BASE:
case MSR_K8_GS_BASE:
{
return VERR_VMX_UNEXPECTED_EXIT_CODE;
}
case MSR_K8_LSTAR:
case MSR_K6_STAR:
case MSR_K8_SF_MASK:
case MSR_K8_TSC_AUX:
case MSR_K8_KERNEL_GS_BASE:
{
AssertMsgFailed(("Unexpected WRMSR for an MSR in the auto-load/store area in the VMCS. ecx=%#RX32\n",
return VERR_VMX_UNEXPECTED_EXIT_CODE;
}
}
}
#endif /* VBOX_STRICT */
}
return rc;
}
/**
* VM-exit handler for PAUSE (VMX_EXIT_PAUSE). Conditional VM-exit.
*/
{
/* By default, we don't enable VMX_VMCS_CTRL_PROC_EXEC_CONTROLS_PAUSE_EXIT. */
return VERR_EM_INTERPRETER;
return VERR_VMX_UNEXPECTED_EXIT_CODE;
}
/**
* VM-exit handler for when the TPR value is lowered below the specified
* threshold (VMX_EXIT_TPR_BELOW_THRESHOLD). Conditional VM-exit.
*/
HMVMX_EXIT_DECL hmR0VmxExitTprBelowThreshold(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
/*
* The TPR has already been updated, see hmR0VMXPostRunGuest(). RIP is also updated as part of the VM-exit by VT-x. Update
* the threshold in the VMCS, deliver the pending interrupt via hmR0VmxPreRunGuest()->hmR0VmxInjectEvent() and
* resume guest execution.
*/
return VINF_SUCCESS;
}
/**
* VM-exit handler for control-register accesses (VMX_EXIT_MOV_CRX). Conditional
* VM-exit.
*
* @retval VINF_SUCCESS when guest execution can continue.
* @retval VINF_PGM_CHANGE_MODE when shadow paging mode changed, back to ring-3.
* @retval VINF_PGM_SYNC_CR3 CR3 sync is required, back to ring-3.
* @retval VERR_EM_INTERPRETER when something unexpected happened, fallback to
* recompiler.
*/
{
switch (uAccessType)
{
case VMX_EXIT_QUALIFICATION_CRX_ACCESS_WRITE: /* MOV to CRx */
{
#if 0
/* EMInterpretCRxWrite() references a lot of guest state (EFER, RFLAGS, Segment Registers, etc.) Sync entire state */
#else
#endif
Assert(rc == VINF_SUCCESS || rc == VERR_EM_INTERPRETER || rc == VINF_PGM_CHANGE_MODE || rc == VINF_PGM_SYNC_CR3);
{
case 0: /* CR0 */
break;
case 2: /* C2 **/
/* Nothing to do here, CR2 it's not part of the VMCS. */
break;
case 3: /* CR3 */
break;
case 4: /* CR4 */
break;
case 8: /* CR8 */
/* CR8 contains the APIC TPR. Was updated by EMInterpretCRxWrite(). */
/* We don't need to update HM_CHANGED_VMX_GUEST_APIC_STATE here as this -cannot- happen with TPR shadowing. */
break;
default:
AssertMsgFailed(("Invalid CRx register %#x\n", VMX_EXIT_QUALIFICATION_CRX_REGISTER(uExitQualification)));
break;
}
STAM_COUNTER_INC(&pVCpu->hm.s.StatExitCRxWrite[VMX_EXIT_QUALIFICATION_CRX_REGISTER(uExitQualification)]);
break;
}
case VMX_EXIT_QUALIFICATION_CRX_ACCESS_READ: /* MOV from CRx */
{
/* EMInterpretCRxRead() requires EFER MSR, CS. */
/* CR8 reads only cause a VM-exit when the TPR shadow feature isn't enabled. */
STAM_COUNTER_INC(&pVCpu->hm.s.StatExitCRxRead[VMX_EXIT_QUALIFICATION_CRX_REGISTER(uExitQualification)]);
break;
}
case VMX_EXIT_QUALIFICATION_CRX_ACCESS_CLTS: /* CLTS (Clear Task-Switch Flag in CR0) */
{
break;
}
case VMX_EXIT_QUALIFICATION_CRX_ACCESS_LMSW: /* LMSW (Load Machine-Status Word into CR0) */
{
rc = EMInterpretLMSW(pVM, pVCpu, CPUMCTX2CORE(pMixedCtx), VMX_EXIT_QUALIFICATION_CRX_LMSW_DATA(uExitQualification));
break;
}
default:
{
}
}
/* Validate possible error codes. */
Assert(rc == VINF_SUCCESS || rc == VINF_PGM_CHANGE_MODE || rc == VERR_EM_INTERPRETER || rc == VINF_PGM_SYNC_CR3
|| rc == VERR_VMX_UNEXPECTED_EXCEPTION);
if (RT_SUCCESS(rc))
{
}
return rc;
}
/**
* VM-exit handler for I/O instructions (VMX_EXIT_IO_INSTR). Conditional
* VM-exit.
*/
{
rc |= hmR0VmxSaveGuestControlRegs(pVCpu, pMixedCtx); /* CR0 checks & PGM* in EMInterpretDisasCurrent(). */
rc |= hmR0VmxSaveGuestSegmentRegs(pVCpu, pMixedCtx); /* SELM checks in EMInterpretDisasCurrent(). */
/* EFER also required for longmode checks in EMInterpretDisasCurrent(), but it's always up-to-date. */
/* Refer Intel spec. 27-5. "Exit Qualifications for I/O Instructions" for the format. */
/* I/O operation lookup arrays. */
static const uint32_t s_aIOOpAnd[4] = { 0xff, 0xffff, 0, 0xffffffff }; /* AND masks for saving the result (in AL/AX/EAX). */
if (fIOString)
{
/** @todo for now manually disassemble later optimize by getting the fields from
* the VMCS. */
/** @todo VMX_VMCS_RO_EXIT_GUEST_LINEAR_ADDR contains the flat pointer
* operand of the instruction. VMX_VMCS32_RO_EXIT_INSTR_INFO contains
* segment prefix info. */
if (RT_SUCCESS(rc))
{
if (fIOWrite)
{
}
else
{
}
}
else
{
}
}
else
{
if (fIOWrite)
{
if (rc == VINF_IOM_R3_IOPORT_WRITE)
HMR0SavePendingIOPortWrite(pVCpu, pMixedCtx->rip, pMixedCtx->rip + cbInstr, uIOPort, uAndVal, cbSize);
}
else
{
if (IOM_SUCCESS(rc))
{
}
else if (rc == VINF_IOM_R3_IOPORT_READ)
HMR0SavePendingIOPortRead(pVCpu, pMixedCtx->rip, pMixedCtx->rip + cbInstr, uIOPort, uAndVal, cbSize);
}
}
if (IOM_SUCCESS(rc))
{
{
/* If any IO breakpoints are armed, then we should check if a debug trap needs to be generated. */
{
for (unsigned i = 0; i < 4; i++)
{
{
/* Clear all breakpoint status flags and set the one we just hit. */
/*
* Note: AMD64 Architecture Programmer's Manual 13.1:
* Bits 15:13 of the DR6 register is never cleared by the processor and must
* be cleared by software after the contents have been read.
*/
/* X86_DR7_GD will be cleared if DRx accesses should be trapped inside the guest. */
/* Paranoia. */
/* Resync DR7 */
/** @todo probably cheaper to just reload DR7, nothing else needs changing. */
/* Set #DB to be injected into the VM and continue guest execution. */
break;
}
}
}
}
}
#ifdef DEBUG
if (rc == VINF_IOM_R3_IOPORT_READ)
else if (rc == VINF_IOM_R3_IOPORT_WRITE)
else
{
|| rc == VINF_SUCCESS
|| rc == VINF_EM_RAW_EMULATE_INSTR
|| rc == VINF_EM_RAW_GUEST_TRAP
}
#endif
return rc;
}
/**
* VM-exit handler for task switches (VMX_EXIT_TASK_SWITCH). Unconditional
* VM-exit.
*/
HMVMX_EXIT_DECL hmR0VmxExitTaskSwitch(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
/* Check if this task-switch occurred while delivery an event through the guest IDT. */
if (VMX_EXIT_QUALIFICATION_TASK_SWITCH_TYPE(pVmxTransient->uExitQualification) == VMX_EXIT_QUALIFICATION_TASK_SWITCH_TYPE_IDT)
{
{
/* Software interrupts and exceptions will be regenerated when the recompiler restarts the instruction. */
{
bool fErrorCodeValid = !!VMX_IDT_VECTORING_INFO_ERROR_CODE_IS_VALID(pVmxTransient->uIdtVectoringInfo);
/* Save it as a pending event and it'll be converted to a TRPM event on the way out to ring-3. */
if (fErrorCodeValid)
else
&& uVector == X86_XCPT_PF)
{
}
}
}
}
/** @todo Emulate task switch someday, currently just going back to ring-3 for
* emulation. */
return VERR_EM_INTERPRETER;
}
/**
* VM-exit handler for monitor-trap-flag (VMX_EXIT_MTF). Conditional VM-exit.
*/
{
return VINF_EM_DBG_STOP;
}
/**
* VM-exit handler for APIC access (VMX_EXIT_APIC_ACCESS). Conditional VM-exit.
*/
HMVMX_EXIT_DECL hmR0VmxExitApicAccess(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
/* If this VM-exit occurred while delivering an event through the guest IDT, handle it accordingly. */
return VINF_SUCCESS;
return rc;
#if 0
/** @todo Investigate if IOMMMIOPhysHandler() requires a lot of state, for now
* just sync the whole thing. */
#else
/* Aggressive state sync. for now. */
#endif
/* See Intel spec. 27-6 "Exit Qualifications for APIC-access VM-exits from Linear Accesses & Guest-Phyiscal Addresses" */
switch (uAccessType)
{
{
{
AssertMsgFailed(("hmR0VmxExitApicAccess: can't access TPR offset while using TPR shadowing.\n"));
}
RTGCPHYS GCPhys = pMixedCtx->msrApicBase; /* Always up-to-date, msrApicBase is not part of the VMCS. */
if ( rc == VINF_SUCCESS
|| rc == VERR_PAGE_NOT_PRESENT)
{
pVCpu->hm.s.fContextUseFlags |= HM_CHANGED_GUEST_RIP | HM_CHANGED_GUEST_RSP | HM_CHANGED_GUEST_RFLAGS
rc = VINF_SUCCESS;
}
break;
}
default:
break;
}
return rc;
}
/**
* VM-exit handler for debug-register accesses (VMX_EXIT_MOV_DRX). Conditional
* VM-exit.
*/
{
/* We should -not- get this VM-exit if the guest is debugging. */
{
return VERR_VMX_UNEXPECTED_EXIT_CODE;
}
int rc = VERR_INTERNAL_ERROR_5;
if ( !DBGFIsStepping(pVCpu)
{
/* Don't intercept MOV DRx. */
/* Save the host & load the guest debug state, restart execution of the MOV DRx instruction. */
#ifdef VBOX_WITH_STATISTICS
if (VMX_EXIT_QUALIFICATION_DRX_DIRECTION(pVmxTransient->uExitQualification) == VMX_EXIT_QUALIFICATION_DRX_DIRECTION_WRITE)
else
#endif
return VINF_SUCCESS;
}
/*
* EMInterpretDRx[Write|Read]() calls CPUMIsGuestIn64BitCode() which requires EFER, CS. EFER is always up-to-date, see
* hmR0VmxSaveGuestAutoLoadStoreMsrs(). Update only the segment registers from the CPU.
*/
if (VMX_EXIT_QUALIFICATION_DRX_DIRECTION(pVmxTransient->uExitQualification) == VMX_EXIT_QUALIFICATION_DRX_DIRECTION_WRITE)
{
if (RT_SUCCESS(rc))
}
else
{
}
if (RT_SUCCESS(rc))
{
}
return rc;
}
/**
* VM-exit handler for EPT misconfiguration (VMX_EXIT_EPT_MISCONFIG).
* Conditional VM-exit.
*/
HMVMX_EXIT_DECL hmR0VmxExitEptMisconfig(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
/* If this VM-exit occurred while delivering an event through the guest IDT, handle it accordingly. */
return VINF_SUCCESS;
return rc;
#if 0
#else
/* Aggressive state sync. for now. */
#endif
/*
* If we succeed, resume guest execution.
* If we fail in interpreting the instruction because we couldn't get the guest physical address
* of the page containing the instruction via the guest's page tables (we would invalidate the guest page
* in the host TLB), resume execution which would cause a guest page fault to let the guest handle this
* weird case. See @bugref{6043}.
*/
VBOXSTRICTRC rc2 = PGMR0Trap0eHandlerNPMisconfig(pVM, pVCpu, PGMMODE_EPT, CPUMCTX2CORE(pMixedCtx), GCPhys, UINT32_MAX);
if ( rc == VINF_SUCCESS
|| rc == VERR_PAGE_NOT_PRESENT)
{
pVCpu->hm.s.fContextUseFlags |= HM_CHANGED_GUEST_RIP | HM_CHANGED_GUEST_RSP | HM_CHANGED_GUEST_RFLAGS
return VINF_SUCCESS;
}
return rc;
}
/**
* VM-exit handler for EPT violation (VMX_EXIT_EPT_VIOLATION). Conditional
* VM-exit.
*/
HMVMX_EXIT_DECL hmR0VmxExitEptViolation(PVMCPU pVCpu, PCPUMCTX pMixedCtx, PVMXTRANSIENT pVmxTransient)
{
/* If this VM-exit occurred while delivering an event through the guest IDT, handle it accordingly. */
return VINF_SUCCESS;
return rc;
#if 0
#else
/* Aggressive state sync. for now. */
#endif
/* Intel spec. Table 27-7 "Exit Qualifications for EPT violations". */
AssertMsg(((pVmxTransient->uExitQualification >> 7) & 3) != 2, ("%#RGv", pVmxTransient->uExitQualification));
RTGCUINT uErrorCode = 0;
Log(("EPT violation %#x at %#RGv ErrorCode %#x CS:EIP=%04x:%#RX64\n", (uint32_t)pVmxTransient->uExitQualification, GCPhys,
/* Handle the pagefault trap for the nested shadow table. */
rc = PGMR0Trap0eHandlerNestedPaging(pVM, pVCpu, PGMMODE_EPT, uErrorCode, CPUMCTX2CORE(pMixedCtx), GCPhys);
/* Same case as PGMR0Trap0eHandlerNPMisconfig(). See comment above, @bugref{6043}. */
if ( rc == VINF_SUCCESS
|| rc == VERR_PAGE_NOT_PRESENT)
{
/* Successfully synced our shadow page tables or emulation MMIO instruction. */
pVCpu->hm.s.fContextUseFlags |= HM_CHANGED_GUEST_RIP | HM_CHANGED_GUEST_RSP | HM_CHANGED_GUEST_RFLAGS
return VINF_SUCCESS;
}
Log(("EPT return to ring-3 rc=%d\n"));
return rc;
}
/* -=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/* -=-=-=-=-=-=-=-=-=- VM-exit Exception Handlers -=-=-=-=-=-=-=-=-=-=- */
/* -=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/**
* VM-exit exception handler for #MF (Math Fault: floating point exception).
*/
{
{
/* Old-style FPU error reporting needs some extra work. */
/** @todo don't fall back to the recompiler, but do it manually. */
return VERR_EM_INTERPRETER;
}
hmR0VmxSetPendingEvent(pVCpu, VMX_VMCS_CTRL_ENTRY_IRQ_INFO_FROM_EXIT_INT_INFO(pVmxTransient->uExitIntrInfo),
return rc;
}
/**
* VM-exit exception handler for #BP (Breakpoint exception).
*/
{
/** @todo Try optimize this by not saving the entire guest state unless
* really needed. */
if (rc == VINF_EM_RAW_GUEST_TRAP)
{
hmR0VmxSetPendingEvent(pVCpu, VMX_VMCS_CTRL_ENTRY_IRQ_INFO_FROM_EXIT_INT_INFO(pVmxTransient->uExitIntrInfo),
}
return rc;
}
/**
* VM-exit exception handler for #DB (Debug exception).
*/
{
/* Refer Intel spec. Table 27-1. "Exit Qualifications for debug exceptions" for the format. */
if (rc == VINF_EM_RAW_GUEST_TRAP)
{
/* DR6, DR7.GD and IA32_DEBUGCTL.LBR are not updated yet. See Intel spec. 27.1 "Architectural State before a VM-Exit". */
/* X86_DR7_GD will be cleared if DRx accesses should be trapped inside the guest. */
/* Paranoia. */
hmR0VmxSetPendingEvent(pVCpu, VMX_VMCS_CTRL_ENTRY_IRQ_INFO_FROM_EXIT_INT_INFO(pVmxTransient->uExitIntrInfo),
rc = VINF_SUCCESS;
}
return rc;
}
/**
* VM-exit exception handler for #NM (Device-not-available exception: floating
* point exception).
*/
{
#ifndef HMVMX_ALWAYS_TRAP_ALL_XCPTS
#endif
/* We require CR0 and EFER. EFER is always up-to-date. */
/* Lazy FPU loading; load the guest-FPU state transparently and continue execution of the guest. */
if (rc == VINF_SUCCESS)
{
return VINF_SUCCESS;
}
/* Forward #NM to the guest. */
hmR0VmxSetPendingEvent(pVCpu, VMX_VMCS_CTRL_ENTRY_IRQ_INFO_FROM_EXIT_INT_INFO(pVmxTransient->uExitIntrInfo),
return rc;
}
/**
* VM-exit exception handler for #GP (General-protection exception).
*
* @remarks Requires pVmxTransient->uExitIntrInfo to be up-to-date.
*/
{
int rc = VERR_INTERNAL_ERROR_5;
{
#ifdef HMVMX_ALWAYS_TRAP_ALL_XCPTS
/* If the guest is not in real-mode or we have unrestricted execution support, reflect #GP to the guest. */
Log(("#GP Gst: RIP %#RX64 ErrorCode=%#x CR0=%#RGv CPL=%u\n", pMixedCtx->rip, pVmxTransient->uExitIntrErrorCode,
hmR0VmxSetPendingEvent(pVCpu, VMX_VMCS_CTRL_ENTRY_IRQ_INFO_FROM_EXIT_INT_INFO(pVmxTransient->uExitIntrInfo),
return rc;
#else
/* We don't intercept #GP. */
AssertMsgFailed(("Unexpected VM-exit caused by #GP exception\n"));
return VERR_VMX_UNEXPECTED_EXCEPTION;
#endif
}
/* EMInterpretDisasCurrent() requires a lot of the state, save the entire state. */
if (RT_SUCCESS(rc))
{
rc = VINF_SUCCESS;
Log(("#GP Disas OpCode=%u CS:EIP %04x:%#RX64\n", pDis->pCurInstr->uOpcode, pMixedCtx->cs.Sel, pMixedCtx->rip));
{
case OP_CLI:
break;
case OP_STI:
break;
case OP_HLT:
rc = VINF_EM_HALT;
break;
case OP_POPF:
{
{
cbParm = 4;
uMask = 0xffffffff;
}
else
{
cbParm = 2;
uMask = 0xffff;
}
/* Get the stack pointer & pop the contents of the stack onto EFlags. */
RTGCPTR GCPtrStack = 0;
rc = SELMToFlatEx(pVCpu, DISSELREG_SS, CPUMCTX2CORE(pMixedCtx), pMixedCtx->esp & uMask, SELMTOFLAT_FLAGS_CPL0,
&GCPtrStack);
if (RT_SUCCESS(rc))
{
}
if (RT_FAILURE(rc))
{
break;
}
/* The RF bit is always cleared by POPF; see Intel Instruction reference for POPF. */
pVCpu->hm.s.fContextUseFlags |= HM_CHANGED_GUEST_RIP | HM_CHANGED_GUEST_RSP | HM_CHANGED_GUEST_RFLAGS;
break;
}
case OP_PUSHF:
{
{
cbParm = 4;
uMask = 0xffffffff;
}
else
{
cbParm = 2;
uMask = 0xffff;
}
/* Get the stack pointer & push the contents of eflags onto the stack. */
RTGCPTR GCPtrStack = 0;
if (RT_FAILURE(rc))
{
break;
}
/* The RF & VM bits are cleared on image stored on stack; see Intel Instruction reference for PUSHF. */
if (RT_FAILURE(rc))
{
break;
}
break;
}
case OP_IRET:
{
/** @todo Handle 32-bit operand sizes and check stack limits. See Intel
* instruction reference. */
RTGCPTR GCPtrStack = 0;
{
break;
}
rc = SELMToFlatEx(pVCpu, DISSELREG_SS, CPUMCTX2CORE(pMixedCtx), pMixedCtx->esp & uMask, SELMTOFLAT_FLAGS_CPL0,
&GCPtrStack);
if (RT_SUCCESS(rc))
if (RT_FAILURE(rc))
{
break;
}
pVCpu->hm.s.fContextUseFlags |= HM_CHANGED_GUEST_RIP | HM_CHANGED_GUEST_SEGMENT_REGS | HM_CHANGED_GUEST_RSP
break;
}
case OP_INT:
{
break;
}
case OP_INTO:
{
{
}
break;
}
default:
{
VBOXSTRICTRC rc2 = EMInterpretInstructionDisasState(pVCpu, pDis, CPUMCTX2CORE(pMixedCtx), 0 /* pvFault */,
break;
}
}
}
else
AssertMsg(rc == VINF_SUCCESS || rc == VERR_EM_INTERPRETER || rc == VINF_PGM_CHANGE_MODE || rc == VINF_EM_HALT,
("#GP Unexpected rc=%Rrc\n", rc));
return rc;
}
/**
* VM-exit exception handler wrapper for generic exceptions. Simply re-injects
* the exception reported in the VMX transient structure back into the VM.
*
* @remarks Requires uExitIntrInfo in the VMX transient structure to be
* up-to-date.
*/
{
/* Re-inject the exception into the guest. This cannot be a double-fault condition which would have been handled in
hmR0VmxCheckExitDueToEventDelivery(). */
hmR0VmxSetPendingEvent(pVCpu, VMX_VMCS_CTRL_ENTRY_IRQ_INFO_FROM_EXIT_INT_INFO(pVmxTransient->uExitIntrInfo),
return VINF_SUCCESS;
}
/**
* VM-exit exception handler for #PF (Page-fault exception).
*/
{
#if defined(HMVMX_ALWAYS_TRAP_ALL_XCPTS) || defined(HMVMX_ALWAYS_TRAP_PF)
{
{
pMixedCtx->cr2 = pVmxTransient->uExitQualification; /* Update here in case we go back to ring-3 before injection. */
hmR0VmxSetPendingEvent(pVCpu, VMX_VMCS_CTRL_ENTRY_IRQ_INFO_FROM_EXIT_INT_INFO(pVmxTransient->uExitIntrInfo),
}
else
{
/* A guest page-fault occurred during delivery of a page-fault. Inject #DF. */
Log(("Pending #DF due to vectoring #PF. NP\n"));
}
return rc;
}
#else
#endif
#ifdef VBOX_HM_WITH_GUEST_PATCHING
/* Shortcut for APIC TPR access, only for 32-bit guests. */
{
if ( rc == VINF_SUCCESS
&& GCPhys == GCPhysApicBase)
{
/* Only attempt to patch the instruction once. */
if (!pPatch)
return VINF_EM_HM_PATCH_TPR_INSTR;
}
}
#endif
Log(("#PF: cr2=%#RGv cs:rip=%#04x:%#RGv uErrCode %#RX32 cr3=%#RGv\n", pVmxTransient->uExitQualification, pMixedCtx->cs.Sel,
TRPMAssertXcptPF(pVCpu, pVmxTransient->uExitQualification, (RTGCUINT)pVmxTransient->uExitIntrErrorCode);
if (rc == VINF_SUCCESS)
{
/* Successfully synced shadow pages tables or emulated an MMIO instruction. */
/** @todo this isn't quite right, what if guest does lgdt with some MMIO
* memory? We don't update the whole state here... */
pVCpu->hm.s.fContextUseFlags |= HM_CHANGED_GUEST_RIP | HM_CHANGED_GUEST_RSP | HM_CHANGED_GUEST_RFLAGS
return rc;
}
else if (rc == VINF_EM_RAW_GUEST_TRAP)
{
if (!pVmxTransient->fVectoringPF)
{
/* It's a guest page fault and needs to be reflected to the guest. */
pMixedCtx->cr2 = pVmxTransient->uExitQualification; /* Update here in case we go back to ring-3 before injection. */
hmR0VmxSetPendingEvent(pVCpu, VMX_VMCS_CTRL_ENTRY_IRQ_INFO_FROM_EXIT_INT_INFO(pVmxTransient->uExitIntrInfo),
}
else
{
/* A guest page-fault occurred during delivery of a page-fault. Inject #DF. */
Log(("#PF: Pending #DF due to vectoring #PF\n"));
}
return VINF_SUCCESS;
}
return rc;
}