summaryrefslogtreecommitdiff
path: root/src/VBox/VMM/VMMR0/CPUMR0.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/VMM/VMMR0/CPUMR0.cpp')
-rw-r--r--src/VBox/VMM/VMMR0/CPUMR0.cpp796
1 files changed, 482 insertions, 314 deletions
diff --git a/src/VBox/VMM/VMMR0/CPUMR0.cpp b/src/VBox/VMM/VMMR0/CPUMR0.cpp
index 4fbebab4..d4289388 100644
--- a/src/VBox/VMM/VMMR0/CPUMR0.cpp
+++ b/src/VBox/VMM/VMMR0/CPUMR0.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2011 Oracle Corporation
+ * Copyright (C) 2006-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;
@@ -25,7 +25,7 @@
#include <VBox/vmm/vm.h>
#include <VBox/err.h>
#include <VBox/log.h>
-#include <VBox/vmm/hwaccm.h>
+#include <VBox/vmm/hm.h>
#include <iprt/assert.h>
#include <iprt/asm-amd64-x86.h>
#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
@@ -47,6 +47,10 @@ typedef struct CPUMHOSTLAPIC
{
/** Indicates that the entry is in use and have valid data. */
bool fEnabled;
+ /** Whether it's operating in X2APIC mode (EXTD). */
+ bool fX2Apic;
+ /** The APIC version number. */
+ uint32_t uVersion;
/** Has APIC_REG_LVT_THMR. Not used. */
uint32_t fHasThermal;
/** The physical address of the APIC registers. */
@@ -71,6 +75,25 @@ typedef struct CPUMHOSTLAPIC
static CPUMHOSTLAPIC g_aLApics[RTCPUSET_MAX_CPUS];
#endif
+/**
+ * CPUID bits to unify among all cores.
+ */
+static struct
+{
+ uint32_t uLeaf; /**< Leaf to check. */
+ uint32_t ecx; /**< which bits in ecx to unify between CPUs. */
+ uint32_t edx; /**< which bits in edx to unify between CPUs. */
+}
+const g_aCpuidUnifyBits[] =
+{
+ {
+ 0x00000001,
+ X86_CPUID_FEATURE_ECX_CX16 | X86_CPUID_FEATURE_ECX_MONITOR,
+ X86_CPUID_FEATURE_EDX_CX8
+ }
+};
+
+
/*******************************************************************************
* Internal Functions *
@@ -79,13 +102,14 @@ static CPUMHOSTLAPIC g_aLApics[RTCPUSET_MAX_CPUS];
static int cpumR0MapLocalApics(void);
static void cpumR0UnmapLocalApics(void);
#endif
+static int cpumR0SaveHostDebugState(PVMCPU pVCpu);
/**
* Does the Ring-0 CPU initialization once during module load.
* XXX Host-CPU hot-plugging?
*/
-VMMR0DECL(int) CPUMR0ModuleInit(void)
+VMMR0_INT_DECL(int) CPUMR0ModuleInit(void)
{
int rc = VINF_SUCCESS;
#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
@@ -98,7 +122,7 @@ VMMR0DECL(int) CPUMR0ModuleInit(void)
/**
* Terminate the module.
*/
-VMMR0DECL(int) CPUMR0ModuleTerm(void)
+VMMR0_INT_DECL(int) CPUMR0ModuleTerm(void)
{
#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
cpumR0UnmapLocalApics();
@@ -108,6 +132,52 @@ VMMR0DECL(int) CPUMR0ModuleTerm(void)
/**
+ *
+ *
+ * Check the CPUID features of this particular CPU and disable relevant features
+ * for the guest which do not exist on this CPU. We have seen systems where the
+ * X86_CPUID_FEATURE_ECX_MONITOR feature flag is only set on some host CPUs, see
+ * @bugref{5436}.
+ *
+ * @note This function might be called simultaneously on more than one CPU!
+ *
+ * @param idCpu The identifier for the CPU the function is called on.
+ * @param pvUser1 Pointer to the VM structure.
+ * @param pvUser2 Ignored.
+ */
+static DECLCALLBACK(void) cpumR0CheckCpuid(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ PVM pVM = (PVM)pvUser1;
+ PCPUM pCPUM = &pVM->cpum.s;
+
+ NOREF(idCpu); NOREF(pvUser2);
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_aCpuidUnifyBits); i++)
+ {
+ /* Note! Cannot use cpumCpuIdGetLeaf from here because we're not
+ necessarily in the VM process context. So, we using the
+ legacy arrays as temporary storage. */
+
+ uint32_t uLeaf = g_aCpuidUnifyBits[i].uLeaf;
+ PCPUMCPUID pLegacyLeaf;
+ if (uLeaf < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdStd))
+ pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdStd[uLeaf];
+ else if (uLeaf - UINT32_C(0x80000000) < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdExt))
+ pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdExt[uLeaf - UINT32_C(0x80000000)];
+ else if (uLeaf - UINT32_C(0xc0000000) < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdCentaur))
+ pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdCentaur[uLeaf - UINT32_C(0xc0000000)];
+ else
+ continue;
+
+ uint32_t eax, ebx, ecx, edx;
+ ASMCpuIdExSlow(uLeaf, 0, 0, 0, &eax, &ebx, &ecx, &edx);
+
+ ASMAtomicAndU32(&pLegacyLeaf->ecx, ecx | ~g_aCpuidUnifyBits[i].ecx);
+ ASMAtomicAndU32(&pLegacyLeaf->edx, edx | ~g_aCpuidUnifyBits[i].edx);
+ }
+}
+
+
+/**
* Does Ring-0 CPUM initialization.
*
* This is mainly to check that the Host CPU mode is compatible
@@ -116,7 +186,7 @@ VMMR0DECL(int) CPUMR0ModuleTerm(void)
* @returns VBox status code.
* @param pVM Pointer to the VM.
*/
-VMMR0DECL(int) CPUMR0Init(PVM pVM)
+VMMR0_INT_DECL(int) CPUMR0InitVM(PVM pVM)
{
LogFlow(("CPUMR0Init: %p\n", pVM));
@@ -150,9 +220,9 @@ VMMR0DECL(int) CPUMR0Init(PVM pVM)
uint32_t u32Dummy;
uint32_t fFeatures;
ASMCpuId(1, &u32CpuVersion, &u32Dummy, &u32Dummy, &fFeatures);
- uint32_t u32Family = u32CpuVersion >> 8;
- uint32_t u32Model = (u32CpuVersion >> 4) & 0xF;
- uint32_t u32Stepping = u32CpuVersion & 0xF;
+ uint32_t const u32Family = u32CpuVersion >> 8;
+ uint32_t const u32Model = (u32CpuVersion >> 4) & 0xF;
+ uint32_t const u32Stepping = u32CpuVersion & 0xF;
if ( (fFeatures & X86_CPUID_FEATURE_EDX_SEP)
&& ( u32Family != 6 /* (> pentium pro) */
|| u32Model >= 3
@@ -181,8 +251,7 @@ VMMR0DECL(int) CPUMR0Init(PVM pVM)
*/
uint32_t cExt = 0;
ASMCpuId(0x80000000, &cExt, &u32Dummy, &u32Dummy, &u32Dummy);
- if ( cExt >= 0x80000001
- && cExt <= 0x8000ffff)
+ if (ASMIsValidExtRange(cExt))
{
uint32_t fExtFeaturesEDX = ASMCpuId_EDX(0x80000001);
if (fExtFeaturesEDX & X86_CPUID_EXT_FEATURE_EDX_SYSCALL)
@@ -204,6 +273,39 @@ VMMR0DECL(int) CPUMR0Init(PVM pVM)
}
}
}
+
+ /*
+ * Unify/cross check some CPUID feature bits on all available CPU cores
+ * and threads. We've seen CPUs where the monitor support differed.
+ *
+ * Because the hyper heap isn't always mapped into ring-0, we cannot
+ * access it from a RTMpOnAll callback. We use the legacy CPUID arrays
+ * as temp ring-0 accessible memory instead, ASSUMING that they're all
+ * up to date when we get here.
+ */
+ RTMpOnAll(cpumR0CheckCpuid, pVM, NULL);
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_aCpuidUnifyBits); i++)
+ {
+ uint32_t uLeaf = g_aCpuidUnifyBits[i].uLeaf;
+ PCPUMCPUIDLEAF pLeaf = cpumCpuIdGetLeaf(pVM, uLeaf, 0);
+ if (pLeaf)
+ {
+ PCPUMCPUID pLegacyLeaf;
+ if (uLeaf < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdStd))
+ pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdStd[uLeaf];
+ else if (uLeaf - UINT32_C(0x80000000) < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdExt))
+ pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdExt[uLeaf - UINT32_C(0x80000000)];
+ else if (uLeaf - UINT32_C(0xc0000000) < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdCentaur))
+ pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdCentaur[uLeaf - UINT32_C(0xc0000000)];
+ else
+ continue;
+
+ pLeaf->uEcx = pLegacyLeaf->ecx;
+ pLeaf->uEdx = pLegacyLeaf->edx;
+ }
+ }
+
}
@@ -224,23 +326,27 @@ VMMR0DECL(int) CPUMR0Init(PVM pVM)
/**
- * Lazily sync in the FPU/XMM state
+ * Trap handler for device-not-available fault (#NM).
+ * Device not available, FP or (F)WAIT instruction.
*
* @returns VBox status code.
+ * @retval VINF_SUCCESS if the guest FPU state is loaded.
+ * @retval VINF_EM_RAW_GUEST_TRAP if it is a guest trap.
+ *
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
- * @param pCtx Pointer to the guest CPU context.
+ * @param pCtx Pointer to the guest-CPU context.
*/
-VMMR0DECL(int) CPUMR0LoadGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
+VMMR0_INT_DECL(int) CPUMR0Trap07Handler(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
{
Assert(pVM->cpum.s.CPUFeatures.edx.u1FXSR);
Assert(ASMGetCR4() & X86_CR4_OSFSXR);
/* If the FPU state has already been loaded, then it's a guest trap. */
- if (pVCpu->cpum.s.fUseFlags & CPUM_USED_FPU)
+ if (CPUMIsGuestFPUStateActive(pVCpu))
{
- Assert( ((pCtx->cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS)) == (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS))
- || ((pCtx->cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS)) == (X86_CR0_MP | X86_CR0_TS)));
+ Assert( ((pCtx->cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS)) == (X86_CR0_MP | X86_CR0_TS))
+ || ((pCtx->cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS)) == (X86_CR0_MP | X86_CR0_TS | X86_CR0_EM)));
return VINF_EM_RAW_GUEST_TRAP;
}
@@ -272,12 +378,28 @@ VMMR0DECL(int) CPUMR0LoadGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
switch (pCtx->cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS))
{
case X86_CR0_MP | X86_CR0_TS:
- case X86_CR0_MP | X86_CR0_EM | X86_CR0_TS:
+ case X86_CR0_MP | X86_CR0_TS | X86_CR0_EM:
return VINF_EM_RAW_GUEST_TRAP;
default:
break;
}
+ return CPUMR0LoadGuestFPU(pVM, pVCpu, pCtx);
+}
+
+
+/**
+ * Saves the host-FPU/XMM state and loads the guest-FPU state into the CPU.
+ *
+ * @returns VBox status code.
+ *
+ * @param pVM Pointer to the VM.
+ * @param pVCpu Pointer to the VMCPU.
+ * @param pCtx Pointer to the guest-CPU context.
+ */
+VMMR0_INT_DECL(int) CPUMR0LoadGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
+{
+ Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
if (CPUMIsGuestInLongModeEx(pCtx))
{
@@ -286,27 +408,27 @@ VMMR0DECL(int) CPUMR0LoadGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
/* Save the host state and record the fact (CPUM_USED_FPU | CPUM_USED_FPU_SINCE_REM). */
cpumR0SaveHostFPUState(&pVCpu->cpum.s);
- /* Restore the state on entry as we need to be in 64 bits mode to access the full state. */
+ /* Restore the state on entry as we need to be in 64-bit mode to access the full state. */
pVCpu->cpum.s.fUseFlags |= CPUM_SYNC_FPU_STATE;
}
else
#endif
{
-#ifndef CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE
-# if defined(VBOX_WITH_HYBRID_32BIT_KERNEL) || defined(VBOX_WITH_KERNEL_USING_XMM) /** @todo remove the #else here and move cpumHandleLazyFPUAsm back to VMMGC after branching out 3.0!!. */
- Assert(!(pVCpu->cpum.s.fUseFlags & CPUM_MANUAL_XMM_RESTORE));
+ Assert(!(pVCpu->cpum.s.fUseFlags & CPUM_USED_MANUAL_XMM_RESTORE));
/** @todo Move the FFXR handling down into
- * cpumR0SaveHostRestoreguestFPUState to optimize the
+ * cpumR0SaveHostRestoreGuestFPUState to optimize the
* VBOX_WITH_KERNEL_USING_XMM handling. */
/* Clear MSR_K6_EFER_FFXSR or else we'll be unable to save/restore the XMM state with fxsave/fxrstor. */
- uint64_t SavedEFER = 0;
+ uint64_t uHostEfer = 0;
+ bool fRestoreEfer = false;
if (pVM->cpum.s.CPUFeaturesExt.edx & X86_CPUID_AMD_FEATURE_EDX_FFXSR)
{
- SavedEFER = ASMRdMsr(MSR_K6_EFER);
- if (SavedEFER & MSR_K6_EFER_FFXSR)
+ uHostEfer = ASMRdMsr(MSR_K6_EFER);
+ if (uHostEfer & MSR_K6_EFER_FFXSR)
{
- ASMWrMsr(MSR_K6_EFER, SavedEFER & ~MSR_K6_EFER_FFXSR);
- pVCpu->cpum.s.fUseFlags |= CPUM_MANUAL_XMM_RESTORE;
+ ASMWrMsr(MSR_K6_EFER, uHostEfer & ~MSR_K6_EFER_FFXSR);
+ pVCpu->cpum.s.fUseFlags |= CPUM_USED_MANUAL_XMM_RESTORE;
+ fRestoreEfer = true;
}
}
@@ -314,71 +436,8 @@ VMMR0DECL(int) CPUMR0LoadGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
cpumR0SaveHostRestoreGuestFPUState(&pVCpu->cpum.s);
/* Restore EFER. */
- if (pVCpu->cpum.s.fUseFlags & CPUM_MANUAL_XMM_RESTORE)
- ASMWrMsr(MSR_K6_EFER, SavedEFER);
-
-# else
- uint64_t oldMsrEFERHost = 0;
- uint32_t oldCR0 = ASMGetCR0();
-
- /* Clear MSR_K6_EFER_FFXSR or else we'll be unable to save/restore the XMM state with fxsave/fxrstor. */
- if (pVM->cpum.s.CPUFeaturesExt.edx & X86_CPUID_AMD_FEATURE_EDX_FFXSR)
- {
- /** @todo Do we really need to read this every time?? The host could change this on the fly though.
- * bird: what about starting by skipping the ASMWrMsr below if we didn't
- * change anything? Ditto for the stuff in CPUMR0SaveGuestFPU. */
- oldMsrEFERHost = ASMRdMsr(MSR_K6_EFER);
- if (oldMsrEFERHost & MSR_K6_EFER_FFXSR)
- {
- ASMWrMsr(MSR_K6_EFER, oldMsrEFERHost & ~MSR_K6_EFER_FFXSR);
- pVCpu->cpum.s.fUseFlags |= CPUM_MANUAL_XMM_RESTORE;
- }
- }
-
- /* If we sync the FPU/XMM state on-demand, then we can continue execution as if nothing has happened. */
- int rc = CPUMHandleLazyFPU(pVCpu);
- AssertRC(rc);
- Assert(CPUMIsGuestFPUStateActive(pVCpu));
-
- /* Restore EFER MSR */
- if (pVCpu->cpum.s.fUseFlags & CPUM_MANUAL_XMM_RESTORE)
- ASMWrMsr(MSR_K6_EFER, oldMsrEFERHost);
-
- /* CPUMHandleLazyFPU could have changed CR0; restore it. */
- ASMSetCR0(oldCR0);
-# endif
-
-#else /* CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE */
-
- /*
- * Save the FPU control word and MXCSR, so we can restore the state properly afterwards.
- * We don't want the guest to be able to trigger floating point/SSE exceptions on the host.
- */
- pVCpu->cpum.s.Host.fpu.FCW = CPUMGetFCW();
- if (pVM->cpum.s.CPUFeatures.edx.u1SSE)
- pVCpu->cpum.s.Host.fpu.MXCSR = CPUMGetMXCSR();
-
- cpumR0LoadFPU(pCtx);
-
- /*
- * The MSR_K6_EFER_FFXSR feature is AMD only so far, but check the cpuid just in case Intel adds it in the future.
- *
- * MSR_K6_EFER_FFXSR changes the behaviour of fxsave and fxrstore: the XMM state isn't saved/restored
- */
- if (pVM->cpum.s.CPUFeaturesExt.edx & X86_CPUID_AMD_FEATURE_EDX_FFXSR)
- {
- /** @todo Do we really need to read this every time?? The host could change this on the fly though. */
- uint64_t msrEFERHost = ASMRdMsr(MSR_K6_EFER);
-
- if (msrEFERHost & MSR_K6_EFER_FFXSR)
- {
- /* fxrstor doesn't restore the XMM state! */
- cpumR0LoadXMM(pCtx);
- pVCpu->cpum.s.fUseFlags |= CPUM_MANUAL_XMM_RESTORE;
- }
- }
-
-#endif /* CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE */
+ if (fRestoreEfer)
+ ASMWrMsr(MSR_K6_EFER, uHostEfer);
}
Assert((pVCpu->cpum.s.fUseFlags & (CPUM_USED_FPU | CPUM_USED_FPU_SINCE_REM)) == (CPUM_USED_FPU | CPUM_USED_FPU_SINCE_REM));
@@ -394,7 +453,7 @@ VMMR0DECL(int) CPUMR0LoadGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
* @param pVCpu Pointer to the VMCPU.
* @param pCtx Pointer to the guest CPU context.
*/
-VMMR0DECL(int) CPUMR0SaveGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
+VMMR0_INT_DECL(int) CPUMR0SaveGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
{
Assert(pVM->cpum.s.CPUFeatures.edx.u1FXSR);
Assert(ASMGetCR4() & X86_CR4_OSFSXR);
@@ -406,7 +465,7 @@ VMMR0DECL(int) CPUMR0SaveGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
{
if (!(pVCpu->cpum.s.fUseFlags & CPUM_SYNC_FPU_STATE))
{
- HWACCMR0SaveFPUState(pVM, pVCpu, pCtx);
+ HMR0SaveFPUState(pVM, pVCpu, pCtx);
cpumR0RestoreHostFPUState(&pVCpu->cpum.s);
}
/* else nothing to do; we didn't perform a world switch */
@@ -414,8 +473,7 @@ VMMR0DECL(int) CPUMR0SaveGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
else
#endif
{
-#ifndef CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE
-# ifdef VBOX_WITH_KERNEL_USING_XMM
+#ifdef VBOX_WITH_KERNEL_USING_XMM
/*
* We've already saved the XMM registers in the assembly wrapper, so
* we have to save them before saving the entire FPU state and put them
@@ -426,203 +484,244 @@ VMMR0DECL(int) CPUMR0SaveGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
* We could just all this in assembly. */
uint128_t aGuestXmmRegs[16];
memcpy(&aGuestXmmRegs[0], &pVCpu->cpum.s.Guest.fpu.aXMM[0], sizeof(aGuestXmmRegs));
-# endif
+#endif
/* Clear MSR_K6_EFER_FFXSR or else we'll be unable to save/restore the XMM state with fxsave/fxrstor. */
- uint64_t oldMsrEFERHost = 0;
- if (pVCpu->cpum.s.fUseFlags & CPUM_MANUAL_XMM_RESTORE)
+ uint64_t uHostEfer = 0;
+ bool fRestoreEfer = false;
+ if (pVCpu->cpum.s.fUseFlags & CPUM_USED_MANUAL_XMM_RESTORE)
{
- oldMsrEFERHost = ASMRdMsr(MSR_K6_EFER);
- ASMWrMsr(MSR_K6_EFER, oldMsrEFERHost & ~MSR_K6_EFER_FFXSR);
+ uHostEfer = ASMRdMsr(MSR_K6_EFER);
+ if (uHostEfer & MSR_K6_EFER_FFXSR)
+ {
+ ASMWrMsr(MSR_K6_EFER, uHostEfer & ~MSR_K6_EFER_FFXSR);
+ fRestoreEfer = true;
+ }
}
+
cpumR0SaveGuestRestoreHostFPUState(&pVCpu->cpum.s);
/* Restore EFER MSR */
- if (pVCpu->cpum.s.fUseFlags & CPUM_MANUAL_XMM_RESTORE)
- ASMWrMsr(MSR_K6_EFER, oldMsrEFERHost | MSR_K6_EFER_FFXSR);
+ if (fRestoreEfer)
+ ASMWrMsr(MSR_K6_EFER, uHostEfer | MSR_K6_EFER_FFXSR);
-# ifdef VBOX_WITH_KERNEL_USING_XMM
+#ifdef VBOX_WITH_KERNEL_USING_XMM
memcpy(&pVCpu->cpum.s.Guest.fpu.aXMM[0], &aGuestXmmRegs[0], sizeof(aGuestXmmRegs));
-# endif
-
-#else /* CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE */
-# ifdef VBOX_WITH_KERNEL_USING_XMM
-# error "Fix all the NM_TRAPS_IN_KERNEL_MODE code path. I'm not going to fix unused code now."
-# endif
- cpumR0SaveFPU(pCtx);
- if (pVCpu->cpum.s.fUseFlags & CPUM_MANUAL_XMM_RESTORE)
- {
- /* fxsave doesn't save the XMM state! */
- cpumR0SaveXMM(pCtx);
- }
-
- /*
- * Restore the original FPU control word and MXCSR.
- * We don't want the guest to be able to trigger floating point/SSE exceptions on the host.
- */
- cpumR0SetFCW(pVCpu->cpum.s.Host.fpu.FCW);
- if (pVM->cpum.s.CPUFeatures.edx.u1SSE)
- cpumR0SetMXCSR(pVCpu->cpum.s.Host.fpu.MXCSR);
-#endif /* CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE */
+#endif
}
- pVCpu->cpum.s.fUseFlags &= ~(CPUM_USED_FPU | CPUM_SYNC_FPU_STATE | CPUM_MANUAL_XMM_RESTORE);
+ pVCpu->cpum.s.fUseFlags &= ~(CPUM_USED_FPU | CPUM_SYNC_FPU_STATE | CPUM_USED_MANUAL_XMM_RESTORE);
return VINF_SUCCESS;
}
/**
- * Save guest debug state
+ * Saves the host debug state, setting CPUM_USED_HOST_DEBUG_STATE and loading
+ * DR7 with safe values.
*
* @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 fDR6 Whether to include DR6 or not.
*/
-VMMR0DECL(int) CPUMR0SaveGuestDebugState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, bool fDR6)
+static int cpumR0SaveHostDebugState(PVMCPU pVCpu)
{
- Assert(pVCpu->cpum.s.fUseFlags & CPUM_USE_DEBUG_REGS);
-
- /* Save the guest's debug state. The caller is responsible for DR7. */
-#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
- if (CPUMIsGuestInLongModeEx(pCtx))
- {
- if (!(pVCpu->cpum.s.fUseFlags & CPUM_SYNC_DEBUG_STATE))
- {
- uint64_t dr6 = pCtx->dr[6];
-
- HWACCMR0SaveDebugState(pVM, pVCpu, pCtx);
- if (!fDR6) /* dr6 was already up-to-date */
- pCtx->dr[6] = dr6;
- }
- }
- else
-#endif
- {
+ /*
+ * Save the host state.
+ */
#ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
- cpumR0SaveDRx(&pCtx->dr[0]);
+ AssertCompile((uintptr_t)&pVCpu->cpum.s.Host.dr3 - (uintptr_t)&pVCpu->cpum.s.Host.dr0 == sizeof(uint64_t) * 3);
+ cpumR0SaveDRx(&pVCpu->cpum.s.Host.dr0);
#else
- pCtx->dr[0] = ASMGetDR0();
- pCtx->dr[1] = ASMGetDR1();
- pCtx->dr[2] = ASMGetDR2();
- pCtx->dr[3] = ASMGetDR3();
+ pVCpu->cpum.s.Host.dr0 = ASMGetDR0();
+ pVCpu->cpum.s.Host.dr1 = ASMGetDR1();
+ pVCpu->cpum.s.Host.dr2 = ASMGetDR2();
+ pVCpu->cpum.s.Host.dr3 = ASMGetDR3();
#endif
- if (fDR6)
- pCtx->dr[6] = ASMGetDR6();
- }
+ pVCpu->cpum.s.Host.dr6 = ASMGetDR6();
+ /** @todo dr7 might already have been changed to 0x400; don't care right now as it's harmless. */
+ pVCpu->cpum.s.Host.dr7 = ASMGetDR7();
+
+ /* Preemption paranoia. */
+ ASMAtomicOrU32(&pVCpu->cpum.s.fUseFlags, CPUM_USED_DEBUG_REGS_HOST);
/*
- * Restore the host's debug state. DR0-3, DR6 and only then DR7!
- * DR7 contains 0x400 right now.
+ * Make sure DR7 is harmless or else we could trigger breakpoints when
+ * load guest or hypervisor DRx values later.
*/
- CPUMR0LoadHostDebugState(pVM, pVCpu);
- Assert(!(pVCpu->cpum.s.fUseFlags & CPUM_USE_DEBUG_REGS));
+ if (pVCpu->cpum.s.Host.dr7 != X86_DR7_INIT_VAL)
+ ASMSetDR7(X86_DR7_INIT_VAL);
+
return VINF_SUCCESS;
}
/**
- * Lazily sync in the debug state
+ * Saves the guest DRx state residing in host registers and restore the host
+ * register values.
*
- * @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 fDR6 Whether to include DR6 or not.
+ * The guest DRx state is only saved if CPUMR0LoadGuestDebugState was called,
+ * since it's assumed that we're shadowing the guest DRx register values
+ * accurately when using the combined hypervisor debug register values
+ * (CPUMR0LoadHyperDebugState).
+ *
+ * @returns true if either guest or hypervisor debug registers were loaded.
+ * @param pVCpu The cross context CPU structure for the calling EMT.
+ * @param fDr6 Whether to include DR6 or not.
+ * @thread EMT(pVCpu)
*/
-VMMR0DECL(int) CPUMR0LoadGuestDebugState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, bool fDR6)
+VMMR0_INT_DECL(bool) CPUMR0DebugStateMaybeSaveGuestAndRestoreHost(PVMCPU pVCpu, bool fDr6)
{
- /* Save the host state. */
- CPUMR0SaveHostDebugState(pVM, pVCpu);
- Assert(ASMGetDR7() == X86_DR7_INIT_VAL);
+ Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+ bool const fDrXLoaded = RT_BOOL(pVCpu->cpum.s.fUseFlags & (CPUM_USED_DEBUG_REGS_GUEST | CPUM_USED_DEBUG_REGS_HYPER));
- /* Activate the guest state DR0-3; DR7 is left to the caller. */
-#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
- if (CPUMIsGuestInLongModeEx(pCtx))
+ /*
+ * Do we need to save the guest DRx registered loaded into host registers?
+ * (DR7 and DR6 (if fDr6 is true) are left to the caller.)
+ */
+ if (pVCpu->cpum.s.fUseFlags & CPUM_USED_DEBUG_REGS_GUEST)
{
- /* Restore the state on entry as we need to be in 64 bits mode to access the full state. */
- pVCpu->cpum.s.fUseFlags |= CPUM_SYNC_DEBUG_STATE;
- }
- else
+#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
+ if (CPUMIsGuestInLongModeEx(&pVCpu->cpum.s.Guest))
+ {
+ uint64_t uDr6 = pVCpu->cpum.s.Guest.dr[6];
+ HMR0SaveDebugState(pVCpu->CTX_SUFF(pVM), pVCpu, &pVCpu->cpum.s.Guest);
+ if (!fDr6)
+ pVCpu->cpum.s.Guest.dr[6] = uDr6;
+ }
+ else
+#endif
+ {
+#ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
+ cpumR0SaveDRx(&pVCpu->cpum.s.Guest.dr[0]);
+#else
+ pVCpu->cpum.s.Guest.dr[0] = ASMGetDR0();
+ pVCpu->cpum.s.Guest.dr[1] = ASMGetDR1();
+ pVCpu->cpum.s.Guest.dr[2] = ASMGetDR2();
+ pVCpu->cpum.s.Guest.dr[3] = ASMGetDR3();
#endif
+ if (fDr6)
+ pVCpu->cpum.s.Guest.dr[6] = ASMGetDR6();
+ }
+ }
+ ASMAtomicAndU32(&pVCpu->cpum.s.fUseFlags, ~( CPUM_USED_DEBUG_REGS_GUEST | CPUM_USED_DEBUG_REGS_HYPER
+ | CPUM_SYNC_DEBUG_REGS_GUEST | CPUM_SYNC_DEBUG_REGS_HYPER));
+
+ /*
+ * Restore the host's debug state. DR0-3, DR6 and only then DR7!
+ */
+ if (pVCpu->cpum.s.fUseFlags & CPUM_USED_DEBUG_REGS_HOST)
{
+ /* A bit of paranoia first... */
+ uint64_t uCurDR7 = ASMGetDR7();
+ if (uCurDR7 != X86_DR7_INIT_VAL)
+ ASMSetDR7(X86_DR7_INIT_VAL);
+
#ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
- cpumR0LoadDRx(&pCtx->dr[0]);
+ AssertCompile((uintptr_t)&pVCpu->cpum.s.Host.dr3 - (uintptr_t)&pVCpu->cpum.s.Host.dr0 == sizeof(uint64_t) * 3);
+ cpumR0LoadDRx(&pVCpu->cpum.s.Host.dr0);
#else
- ASMSetDR0(pCtx->dr[0]);
- ASMSetDR1(pCtx->dr[1]);
- ASMSetDR2(pCtx->dr[2]);
- ASMSetDR3(pCtx->dr[3]);
+ ASMSetDR0(pVCpu->cpum.s.Host.dr0);
+ ASMSetDR1(pVCpu->cpum.s.Host.dr1);
+ ASMSetDR2(pVCpu->cpum.s.Host.dr2);
+ ASMSetDR3(pVCpu->cpum.s.Host.dr3);
#endif
- if (fDR6)
- ASMSetDR6(pCtx->dr[6]);
+ /** @todo consider only updating if they differ, esp. DR6. Need to figure how
+ * expensive DRx reads are over DRx writes. */
+ ASMSetDR6(pVCpu->cpum.s.Host.dr6);
+ ASMSetDR7(pVCpu->cpum.s.Host.dr7);
+
+ ASMAtomicAndU32(&pVCpu->cpum.s.fUseFlags, ~CPUM_USED_DEBUG_REGS_HOST);
}
- pVCpu->cpum.s.fUseFlags |= CPUM_USE_DEBUG_REGS;
- return VINF_SUCCESS;
+ return fDrXLoaded;
}
+
/**
- * Save the host debug state
+ * Saves the guest DRx state if it resides host registers.
*
- * @returns VBox status code.
- * @param pVM Pointer to the VM.
- * @param pVCpu Pointer to the VMCPU.
+ * This does NOT clear any use flags, so the host registers remains loaded with
+ * the guest DRx state upon return. The purpose is only to make sure the values
+ * in the CPU context structure is up to date.
+ *
+ * @returns true if the host registers contains guest values, false if not.
+ * @param pVCpu The cross context CPU structure for the calling EMT.
+ * @param fDr6 Whether to include DR6 or not.
+ * @thread EMT(pVCpu)
*/
-VMMR0DECL(int) CPUMR0SaveHostDebugState(PVM pVM, PVMCPU pVCpu)
+VMMR0_INT_DECL(bool) CPUMR0DebugStateMaybeSaveGuest(PVMCPU pVCpu, bool fDr6)
{
- NOREF(pVM);
-
- /* Save the host state. */
+ /*
+ * Do we need to save the guest DRx registered loaded into host registers?
+ * (DR7 and DR6 (if fDr6 is true) are left to the caller.)
+ */
+ if (pVCpu->cpum.s.fUseFlags & CPUM_USED_DEBUG_REGS_GUEST)
+ {
+#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
+ if (CPUMIsGuestInLongModeEx(&pVCpu->cpum.s.Guest))
+ {
+ uint64_t uDr6 = pVCpu->cpum.s.Guest.dr[6];
+ HMR0SaveDebugState(pVCpu->CTX_SUFF(pVM), pVCpu, &pVCpu->cpum.s.Guest);
+ if (!fDr6)
+ pVCpu->cpum.s.Guest.dr[6] = uDr6;
+ }
+ else
+#endif
+ {
#ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
- AssertCompile((uintptr_t)&pVCpu->cpum.s.Host.dr3 - (uintptr_t)&pVCpu->cpum.s.Host.dr0 == sizeof(uint64_t) * 3);
- cpumR0SaveDRx(&pVCpu->cpum.s.Host.dr0);
+ cpumR0SaveDRx(&pVCpu->cpum.s.Guest.dr[0]);
#else
- pVCpu->cpum.s.Host.dr0 = ASMGetDR0();
- pVCpu->cpum.s.Host.dr1 = ASMGetDR1();
- pVCpu->cpum.s.Host.dr2 = ASMGetDR2();
- pVCpu->cpum.s.Host.dr3 = ASMGetDR3();
+ pVCpu->cpum.s.Guest.dr[0] = ASMGetDR0();
+ pVCpu->cpum.s.Guest.dr[1] = ASMGetDR1();
+ pVCpu->cpum.s.Guest.dr[2] = ASMGetDR2();
+ pVCpu->cpum.s.Guest.dr[3] = ASMGetDR3();
#endif
- pVCpu->cpum.s.Host.dr6 = ASMGetDR6();
- /** @todo dr7 might already have been changed to 0x400; don't care right now as it's harmless. */
- pVCpu->cpum.s.Host.dr7 = ASMGetDR7();
- /* Make sure DR7 is harmless or else we could trigger breakpoints when restoring dr0-3 (!) */
- ASMSetDR7(X86_DR7_INIT_VAL);
-
- return VINF_SUCCESS;
+ if (fDr6)
+ pVCpu->cpum.s.Guest.dr[6] = ASMGetDR6();
+ }
+ return true;
+ }
+ return false;
}
+
/**
- * Load the host debug state
+ * Lazily sync in the debug state.
*
- * @returns VBox status code.
- * @param pVM Pointer to the VM.
- * @param pVCpu Pointer to the VMCPU.
+ * @param pVCpu The cross context CPU structure for the calling EMT.
+ * @param fDr6 Whether to include DR6 or not.
+ * @thread EMT(pVCpu)
*/
-VMMR0DECL(int) CPUMR0LoadHostDebugState(PVM pVM, PVMCPU pVCpu)
+VMMR0_INT_DECL(void) CPUMR0LoadGuestDebugState(PVMCPU pVCpu, bool fDr6)
{
- Assert(pVCpu->cpum.s.fUseFlags & (CPUM_USE_DEBUG_REGS | CPUM_USE_DEBUG_REGS_HYPER));
- NOREF(pVM);
+ /*
+ * Save the host state and disarm all host BPs.
+ */
+ cpumR0SaveHostDebugState(pVCpu);
+ Assert(ASMGetDR7() == X86_DR7_INIT_VAL);
/*
- * Restore the host's debug state. DR0-3, DR6 and only then DR7!
- * DR7 contains 0x400 right now.
+ * Activate the guest state DR0-3.
+ * DR7 and DR6 (if fDr6 is true) are left to the caller.
*/
+#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
+ if (CPUMIsGuestInLongModeEx(&pVCpu->cpum.s.Guest))
+ ASMAtomicOrU32(&pVCpu->cpum.s.fUseFlags, CPUM_SYNC_DEBUG_REGS_GUEST); /* Postpone it to the world switch. */
+ else
+#endif
+ {
#ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
- AssertCompile((uintptr_t)&pVCpu->cpum.s.Host.dr3 - (uintptr_t)&pVCpu->cpum.s.Host.dr0 == sizeof(uint64_t) * 3);
- cpumR0LoadDRx(&pVCpu->cpum.s.Host.dr0);
+ cpumR0LoadDRx(&pVCpu->cpum.s.Guest.dr[0]);
#else
- ASMSetDR0(pVCpu->cpum.s.Host.dr0);
- ASMSetDR1(pVCpu->cpum.s.Host.dr1);
- ASMSetDR2(pVCpu->cpum.s.Host.dr2);
- ASMSetDR3(pVCpu->cpum.s.Host.dr3);
+ ASMSetDR0(pVCpu->cpum.s.Guest.dr[0]);
+ ASMSetDR1(pVCpu->cpum.s.Guest.dr[1]);
+ ASMSetDR2(pVCpu->cpum.s.Guest.dr[2]);
+ ASMSetDR3(pVCpu->cpum.s.Guest.dr[3]);
#endif
- ASMSetDR6(pVCpu->cpum.s.Host.dr6);
- ASMSetDR7(pVCpu->cpum.s.Host.dr7);
+ if (fDr6)
+ ASMSetDR6(pVCpu->cpum.s.Guest.dr[6]);
- pVCpu->cpum.s.fUseFlags &= ~(CPUM_USE_DEBUG_REGS | CPUM_USE_DEBUG_REGS_HYPER);
- return VINF_SUCCESS;
+ ASMAtomicOrU32(&pVCpu->cpum.s.fUseFlags, CPUM_USED_DEBUG_REGS_GUEST);
+ }
}
@@ -630,83 +729,89 @@ VMMR0DECL(int) CPUMR0LoadHostDebugState(PVM pVM, PVMCPU pVCpu)
* Lazily sync in the hypervisor debug state
*
* @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 fDR6 Whether to include DR6 or not.
+ * @param pVCpu The cross context CPU structure for the calling EMT.
+ * @param fDr6 Whether to include DR6 or not.
+ * @thread EMT(pVCpu)
*/
-VMMR0DECL(int) CPUMR0LoadHyperDebugState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, bool fDR6)
+VMMR0_INT_DECL(void) CPUMR0LoadHyperDebugState(PVMCPU pVCpu, bool fDr6)
{
- NOREF(pCtx);
-
- /* Save the host state. */
- CPUMR0SaveHostDebugState(pVM, pVCpu);
+ /*
+ * Save the host state and disarm all host BPs.
+ */
+ cpumR0SaveHostDebugState(pVCpu);
Assert(ASMGetDR7() == X86_DR7_INIT_VAL);
- /* Activate the guest state DR0-3; DR7 is left to the caller. */
+ /*
+ * Make sure the hypervisor values are up to date.
+ */
+ CPUMRecalcHyperDRx(pVCpu, UINT8_MAX /* no loading, please */, true);
+
+ /*
+ * Activate the guest state DR0-3.
+ * DR7 and DR6 (if fDr6 is true) are left to the caller.
+ */
#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
- if (CPUMIsGuestInLongModeEx(pCtx))
- {
- AssertFailed();
- return VERR_NOT_IMPLEMENTED;
- }
+ if (CPUMIsGuestInLongModeEx(&pVCpu->cpum.s.Guest))
+ ASMAtomicOrU32(&pVCpu->cpum.s.fUseFlags, CPUM_SYNC_DEBUG_REGS_HYPER); /* Postpone it. */
else
#endif
{
#ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
- AssertFailed();
- return VERR_NOT_IMPLEMENTED;
+ cpumR0LoadDRx(&pVCpu->cpum.s.Hyper.dr[0]);
#else
- ASMSetDR0(CPUMGetHyperDR0(pVCpu));
- ASMSetDR1(CPUMGetHyperDR1(pVCpu));
- ASMSetDR2(CPUMGetHyperDR2(pVCpu));
- ASMSetDR3(CPUMGetHyperDR3(pVCpu));
+ ASMSetDR0(pVCpu->cpum.s.Hyper.dr[0]);
+ ASMSetDR1(pVCpu->cpum.s.Hyper.dr[1]);
+ ASMSetDR2(pVCpu->cpum.s.Hyper.dr[2]);
+ ASMSetDR3(pVCpu->cpum.s.Hyper.dr[3]);
#endif
- if (fDR6)
- ASMSetDR6(CPUMGetHyperDR6(pVCpu));
- }
+ if (fDr6)
+ ASMSetDR6(X86_DR6_INIT_VAL);
- pVCpu->cpum.s.fUseFlags |= CPUM_USE_DEBUG_REGS_HYPER;
- return VINF_SUCCESS;
+ ASMAtomicOrU32(&pVCpu->cpum.s.fUseFlags, CPUM_USED_DEBUG_REGS_HYPER);
+ }
}
#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
/**
- * Worker for cpumR0MapLocalApics. Check each CPU for a present Local APIC.
- * Play safe and treat each CPU separate.
+ * Per-CPU callback that probes the CPU for APIC support.
+ *
+ * @param idCpu The identifier for the CPU the function is called on.
+ * @param pvUser1 Ignored.
+ * @param pvUser2 Ignored.
*/
-static DECLCALLBACK(void) cpumR0MapLocalApicWorker(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+static DECLCALLBACK(void) cpumR0MapLocalApicCpuProber(RTCPUID idCpu, void *pvUser1, void *pvUser2)
{
NOREF(pvUser1); NOREF(pvUser2);
int iCpu = RTMpCpuIdToSetIndex(idCpu);
AssertReturnVoid(iCpu >= 0 && (unsigned)iCpu < RT_ELEMENTS(g_aLApics));
- uint32_t u32MaxIdx, u32EBX, u32ECX, u32EDX;
- ASMCpuId(0, &u32MaxIdx, &u32EBX, &u32ECX, &u32EDX);
- if ( ( ( u32EBX == X86_CPUID_VENDOR_INTEL_EBX
- && u32ECX == X86_CPUID_VENDOR_INTEL_ECX
- && u32EDX == X86_CPUID_VENDOR_INTEL_EDX)
- || ( u32EBX == X86_CPUID_VENDOR_AMD_EBX
- && u32ECX == X86_CPUID_VENDOR_AMD_ECX
- && u32EDX == X86_CPUID_VENDOR_AMD_EDX)
- || ( u32EBX == X86_CPUID_VENDOR_VIA_EBX
- && u32ECX == X86_CPUID_VENDOR_VIA_ECX
- && u32EDX == X86_CPUID_VENDOR_VIA_EDX))
- && u32MaxIdx >= 1)
+ /*
+ * Check for APIC support.
+ */
+ uint32_t uMaxLeaf, u32EBX, u32ECX, u32EDX;
+ ASMCpuId(0, &uMaxLeaf, &u32EBX, &u32ECX, &u32EDX);
+ if ( ( ASMIsIntelCpuEx(u32EBX, u32ECX, u32EDX)
+ || ASMIsAmdCpuEx(u32EBX, u32ECX, u32EDX)
+ || ASMIsViaCentaurCpuEx(u32EBX, u32ECX, u32EDX))
+ && ASMIsValidStdRange(uMaxLeaf))
{
- ASMCpuId(1, &u32MaxIdx, &u32EBX, &u32ECX, &u32EDX);
+ uint32_t uDummy;
+ ASMCpuId(1, &uDummy, &u32EBX, &u32ECX, &u32EDX);
if ( (u32EDX & X86_CPUID_FEATURE_EDX_APIC)
&& (u32EDX & X86_CPUID_FEATURE_EDX_MSR))
{
+ /*
+ * Safe to access the MSR. Read it and calc the BASE (a little complicated).
+ */
uint64_t u64ApicBase = ASMRdMsr(MSR_IA32_APICBASE);
- uint64_t u64Mask = UINT64_C(0x0000000ffffff000);
+ uint64_t u64Mask = MSR_IA32_APICBASE_BASE_MIN;
/* see Intel Manual: Local APIC Status and Location: MAXPHYADDR default is bit 36 */
- uint32_t u32MaxExtIdx;
- ASMCpuId(0x80000000, &u32MaxExtIdx, &u32EBX, &u32ECX, &u32EDX);
- if ( u32MaxExtIdx >= UINT32_C(0x80000008)
- && u32MaxExtIdx < UINT32_C(0x8000ffff))
+ uint32_t uMaxExtLeaf;
+ ASMCpuId(0x80000000, &uMaxExtLeaf, &u32EBX, &u32ECX, &u32EDX);
+ if ( uMaxExtLeaf >= UINT32_C(0x80000008)
+ && ASMIsValidExtRange(uMaxExtLeaf))
{
uint32_t u32PhysBits;
ASMCpuId(0x80000008, &u32PhysBits, &u32EBX, &u32ECX, &u32EDX);
@@ -714,14 +819,68 @@ static DECLCALLBACK(void) cpumR0MapLocalApicWorker(RTCPUID idCpu, void *pvUser1,
u64Mask = ((UINT64_C(1) << u32PhysBits) - 1) & UINT64_C(0xfffffffffffff000);
}
- uint64_t const u64PhysBase = u64ApicBase & u64Mask;
- g_aLApics[iCpu].PhysBase = (RTHCPHYS)u64PhysBase;
- g_aLApics[iCpu].fEnabled = g_aLApics[iCpu].PhysBase == u64PhysBase;
+ AssertCompile(sizeof(g_aLApics[iCpu].PhysBase) == sizeof(u64ApicBase));
+ g_aLApics[iCpu].PhysBase = u64ApicBase & u64Mask;
+ g_aLApics[iCpu].fEnabled = RT_BOOL(u64ApicBase & MSR_IA32_APICBASE_EN);
+ g_aLApics[iCpu].fX2Apic = (u64ApicBase & (MSR_IA32_APICBASE_EXTD | MSR_IA32_APICBASE_EN))
+ == (MSR_IA32_APICBASE_EXTD | MSR_IA32_APICBASE_EN);
}
}
}
+
+/**
+ * Per-CPU callback that verifies our APIC expectations.
+ *
+ * @param idCpu The identifier for the CPU the function is called on.
+ * @param pvUser1 Ignored.
+ * @param pvUser2 Ignored.
+ */
+static DECLCALLBACK(void) cpumR0MapLocalApicCpuChecker(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ int iCpu = RTMpCpuIdToSetIndex(idCpu);
+ AssertReturnVoid(iCpu >= 0 && (unsigned)iCpu < RT_ELEMENTS(g_aLApics));
+ if (!g_aLApics[iCpu].fEnabled)
+ return;
+
+ /*
+ * 0x0X 82489 external APIC
+ * 0x1X Local APIC
+ * 0x2X..0xFF reserved
+ */
+ uint32_t uApicVersion;
+ if (g_aLApics[iCpu].fX2Apic)
+ uApicVersion = ApicX2RegRead32(APIC_REG_VERSION);
+ else
+ uApicVersion = ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_VERSION);
+ if ((APIC_REG_VERSION_GET_VER(uApicVersion) & 0xF0) == 0x10)
+ {
+ g_aLApics[iCpu].uVersion = uApicVersion;
+ g_aLApics[iCpu].fHasThermal = APIC_REG_VERSION_GET_MAX_LVT(uApicVersion) >= 5;
+
+#if 0 /* enable if you need it. */
+ if (g_aLApics[iCpu].fX2Apic)
+ SUPR0Printf("CPUM: X2APIC %02u - ver %#010x, lint0=%#07x lint1=%#07x pc=%#07x thmr=%#07x\n",
+ iCpu, uApicVersion,
+ ApicX2RegRead32(APIC_REG_LVT_LINT0), ApicX2RegRead32(APIC_REG_LVT_LINT1),
+ ApicX2RegRead32(APIC_REG_LVT_PC), ApicX2RegRead32(APIC_REG_LVT_THMR) );
+ else
+ SUPR0Printf("CPUM: APIC %02u at %RGp (mapped at %p) - ver %#010x, lint0=%#07x lint1=%#07x pc=%#07x thmr=%#07x\n",
+ iCpu, g_aLApics[iCpu].PhysBase, g_aLApics[iCpu].pv, uApicVersion,
+ ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_LVT_LINT0), ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_LVT_LINT1),
+ ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_LVT_PC), ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_LVT_THMR) );
+#endif
+ }
+ else
+ {
+ g_aLApics[iCpu].fEnabled = false;
+ g_aLApics[iCpu].fX2Apic = false;
+ SUPR0Printf("VBox/CPUM: Unsupported APIC version %#x (iCpu=%d)\n", uApicVersion, iCpu);
+ }
+}
+
+
/**
* Map the MMIO page of each local APIC in the system.
*/
@@ -737,15 +896,13 @@ static int cpumR0MapLocalApics(void)
}
/*
- * Create mappings for all online CPUs we think have APICs.
+ * Create mappings for all online CPUs we think have legacy APICs.
*/
- /** @todo r=bird: This code is not adequately handling CPUs that are
- * offline or unplugged at init time and later bought into action. */
- int rc = RTMpOnAll(cpumR0MapLocalApicWorker, NULL, NULL);
+ int rc = RTMpOnAll(cpumR0MapLocalApicCpuProber, NULL, NULL);
for (unsigned iCpu = 0; RT_SUCCESS(rc) && iCpu < RT_ELEMENTS(g_aLApics); iCpu++)
{
- if (g_aLApics[iCpu].fEnabled)
+ if (g_aLApics[iCpu].fEnabled && !g_aLApics[iCpu].fX2Apic)
{
rc = RTR0MemObjEnterPhys(&g_aLApics[iCpu].hMemObj, g_aLApics[iCpu].PhysBase,
PAGE_SIZE, RTMEM_CACHE_POLICY_MMIO);
@@ -755,43 +912,47 @@ static int cpumR0MapLocalApics(void)
PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
if (RT_SUCCESS(rc))
{
- void *pvApicBase = RTR0MemObjAddress(g_aLApics[iCpu].hMapObj);
-
- /*
- * 0x0X 82489 external APIC
- * 0x1X Local APIC
- * 0x2X..0xFF reserved
- */
- /** @todo r=bird: The local APIC is usually at the same address for all CPUs,
- * and therefore inaccessible by the other CPUs. */
- uint32_t ApicVersion = ApicRegRead(pvApicBase, APIC_REG_VERSION);
- if ((APIC_REG_VERSION_GET_VER(ApicVersion) & 0xF0) == 0x10)
- {
- g_aLApics[iCpu].fHasThermal = APIC_REG_VERSION_GET_MAX_LVT(ApicVersion) >= 5;
- g_aLApics[iCpu].pv = pvApicBase;
- Log(("CPUM: APIC %02u at %RGp (mapped at %p) - ver %#x, lint0=%#x lint1=%#x pc=%#x thmr=%#x\n",
- iCpu, g_aLApics[iCpu].PhysBase, g_aLApics[iCpu].pv, ApicVersion,
- ApicRegRead(pvApicBase, APIC_REG_LVT_LINT0),
- ApicRegRead(pvApicBase, APIC_REG_LVT_LINT1),
- ApicRegRead(pvApicBase, APIC_REG_LVT_PC),
- ApicRegRead(pvApicBase, APIC_REG_LVT_THMR)
- ));
- continue;
- }
-
- RTR0MemObjFree(g_aLApics[iCpu].hMapObj, true /* fFreeMappings */);
+ g_aLApics[iCpu].pv = RTR0MemObjAddress(g_aLApics[iCpu].hMapObj);
+ continue;
}
RTR0MemObjFree(g_aLApics[iCpu].hMemObj, true /* fFreeMappings */);
}
g_aLApics[iCpu].fEnabled = false;
}
+ g_aLApics[iCpu].pv = NULL;
}
+
+ /*
+ * Check the APICs.
+ */
+ if (RT_SUCCESS(rc))
+ rc = RTMpOnAll(cpumR0MapLocalApicCpuChecker, NULL, NULL);
+
if (RT_FAILURE(rc))
{
cpumR0UnmapLocalApics();
return rc;
}
+#ifdef LOG_ENABLED
+ /*
+ * Log the result (pretty useless, requires enabling CPUM in VBoxDrv
+ * and !VBOX_WITH_R0_LOGGING).
+ */
+ if (LogIsEnabled())
+ {
+ uint32_t cEnabled = 0;
+ uint32_t cX2Apics = 0;
+ for (unsigned iCpu = 0; iCpu < RT_ELEMENTS(g_aLApics); iCpu++)
+ if (g_aLApics[iCpu].fEnabled)
+ {
+ cEnabled++;
+ cX2Apics += g_aLApics[iCpu].fX2Apic;
+ }
+ Log(("CPUM: %u APICs, %u X2APICs\n", cEnabled, cX2Apics));
+ }
+#endif
+
return VINF_SUCCESS;
}
@@ -810,6 +971,7 @@ static void cpumR0UnmapLocalApics(void)
g_aLApics[iCpu].hMapObj = NIL_RTR0MEMOBJ;
g_aLApics[iCpu].hMemObj = NIL_RTR0MEMOBJ;
g_aLApics[iCpu].fEnabled = false;
+ g_aLApics[iCpu].fX2Apic = false;
g_aLApics[iCpu].pv = NULL;
}
}
@@ -817,17 +979,23 @@ static void cpumR0UnmapLocalApics(void)
/**
- * Write the Local APIC mapping address of the current host CPU to CPUM to be
- * able to access the APIC registers in the raw mode switcher for disabling/
- * re-enabling the NMI. Must be called with disabled preemption or disabled
- * interrupts!
+ * Updates CPUMCPU::pvApicBase and CPUMCPU::fX2Apic prior to world switch.
*
- * @param pVM Pointer to the VM.
+ * Writes the Local APIC mapping address of the current host CPU to CPUMCPU so
+ * the world switchers can access the APIC registers for the purpose of
+ * disabling and re-enabling the NMIs. Must be called with disabled preemption
+ * or disabled interrupts!
+ *
+ * @param pVCpu Pointer to the cross context CPU structure of the
+ * calling EMT.
* @param idHostCpu The ID of the current host CPU.
*/
-VMMR0DECL(void) CPUMR0SetLApic(PVM pVM, RTCPUID idHostCpu)
+VMMR0_INT_DECL(void) CPUMR0SetLApic(PVMCPU pVCpu, RTCPUID idHostCpu)
{
- pVM->cpum.s.pvApicBase = g_aLApics[RTMpCpuIdToSetIndex(idHostCpu)].pv;
+ int idxCpu = RTMpCpuIdToSetIndex(idHostCpu);
+ pVCpu->cpum.s.pvApicBase = g_aLApics[idxCpu].pv;
+ pVCpu->cpum.s.fX2Apic = g_aLApics[idxCpu].fX2Apic;
+// Log6(("CPUMR0SetLApic: pvApicBase=%p fX2Apic=%d\n", g_aLApics[idxCpu].pv, g_aLApics[idxCpu].fX2Apic));
}
#endif /* VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI */