VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR0/CPUMR0.cpp@ 93705

Last change on this file since 93705 was 93554, checked in by vboxsync, 3 years ago

VMM: Changed PAGE_SIZE -> GUEST_PAGE_SIZE / HOST_PAGE_SIZE, PAGE_SHIFT -> GUEST_PAGE_SHIFT / HOST_PAGE_SHIFT, and PAGE_OFFSET_MASK -> GUEST_PAGE_OFFSET_MASK / HOST_PAGE_OFFSET_MASK. Also removed most usage of ASMMemIsZeroPage and ASMMemZeroPage since the host and guest page size doesn't need to be the same any more. Some work left to do in the page pool code. bugref:9898

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 35.8 KB
Line 
1/* $Id: CPUMR0.cpp 93554 2022-02-02 22:57:02Z vboxsync $ */
2/** @file
3 * CPUM - Host Context Ring 0.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_CPUM
23#include <VBox/vmm/cpum.h>
24#include "CPUMInternal.h"
25#include <VBox/vmm/vmcc.h>
26#include <VBox/vmm/gvm.h>
27#include <VBox/err.h>
28#include <VBox/log.h>
29#include <VBox/vmm/hm.h>
30#include <iprt/assert.h>
31#include <iprt/asm-amd64-x86.h>
32#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
33# include <iprt/mem.h>
34# include <iprt/memobj.h>
35# include <VBox/apic.h>
36#endif
37#include <iprt/x86.h>
38
39
40/*********************************************************************************************************************************
41* Structures and Typedefs *
42*********************************************************************************************************************************/
43#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
44/**
45 * Local APIC mappings.
46 */
47typedef struct CPUMHOSTLAPIC
48{
49 /** Indicates that the entry is in use and have valid data. */
50 bool fEnabled;
51 /** Whether it's operating in X2APIC mode (EXTD). */
52 bool fX2Apic;
53 /** The APIC version number. */
54 uint32_t uVersion;
55 /** The physical address of the APIC registers. */
56 RTHCPHYS PhysBase;
57 /** The memory object entering the physical address. */
58 RTR0MEMOBJ hMemObj;
59 /** The mapping object for hMemObj. */
60 RTR0MEMOBJ hMapObj;
61 /** The mapping address APIC registers.
62 * @remarks Different CPUs may use the same physical address to map their
63 * APICs, so this pointer is only valid when on the CPU owning the
64 * APIC. */
65 void *pv;
66} CPUMHOSTLAPIC;
67#endif
68
69
70/*********************************************************************************************************************************
71* Global Variables *
72*********************************************************************************************************************************/
73#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
74static CPUMHOSTLAPIC g_aLApics[RTCPUSET_MAX_CPUS];
75#endif
76
77/**
78 * CPUID bits to unify among all cores.
79 */
80static struct
81{
82 uint32_t uLeaf; /**< Leaf to check. */
83 uint32_t uEcx; /**< which bits in ecx to unify between CPUs. */
84 uint32_t uEdx; /**< which bits in edx to unify between CPUs. */
85}
86const g_aCpuidUnifyBits[] =
87{
88 {
89 0x00000001,
90 X86_CPUID_FEATURE_ECX_CX16 | X86_CPUID_FEATURE_ECX_MONITOR,
91 X86_CPUID_FEATURE_EDX_CX8
92 }
93};
94
95
96
97/*********************************************************************************************************************************
98* Internal Functions *
99*********************************************************************************************************************************/
100#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
101static int cpumR0MapLocalApics(void);
102static void cpumR0UnmapLocalApics(void);
103#endif
104static int cpumR0SaveHostDebugState(PVMCPUCC pVCpu);
105
106
107/**
108 * Does the Ring-0 CPU initialization once during module load.
109 * XXX Host-CPU hot-plugging?
110 */
111VMMR0_INT_DECL(int) CPUMR0ModuleInit(void)
112{
113 int rc = VINF_SUCCESS;
114#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
115 rc = cpumR0MapLocalApics();
116#endif
117 return rc;
118}
119
120
121/**
122 * Terminate the module.
123 */
124VMMR0_INT_DECL(int) CPUMR0ModuleTerm(void)
125{
126#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
127 cpumR0UnmapLocalApics();
128#endif
129 return VINF_SUCCESS;
130}
131
132
133/**
134 * Check the CPUID features of this particular CPU and disable relevant features
135 * for the guest which do not exist on this CPU. We have seen systems where the
136 * X86_CPUID_FEATURE_ECX_MONITOR feature flag is only set on some host CPUs, see
137 * @bugref{5436}.
138 *
139 * @note This function might be called simultaneously on more than one CPU!
140 *
141 * @param idCpu The identifier for the CPU the function is called on.
142 * @param pvUser1 Pointer to the VM structure.
143 * @param pvUser2 Ignored.
144 */
145static DECLCALLBACK(void) cpumR0CheckCpuid(RTCPUID idCpu, void *pvUser1, void *pvUser2)
146{
147 PVMCC pVM = (PVMCC)pvUser1;
148
149 NOREF(idCpu); NOREF(pvUser2);
150 for (uint32_t i = 0; i < RT_ELEMENTS(g_aCpuidUnifyBits); i++)
151 {
152 /* Note! Cannot use cpumCpuIdGetLeaf from here because we're not
153 necessarily in the VM process context. So, we using the
154 legacy arrays as temporary storage. */
155
156 uint32_t uLeaf = g_aCpuidUnifyBits[i].uLeaf;
157 PCPUMCPUID pLegacyLeaf;
158 if (uLeaf < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmStd))
159 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmStd[uLeaf];
160 else if (uLeaf - UINT32_C(0x80000000) < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmExt))
161 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmExt[uLeaf - UINT32_C(0x80000000)];
162 else if (uLeaf - UINT32_C(0xc0000000) < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmCentaur))
163 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmCentaur[uLeaf - UINT32_C(0xc0000000)];
164 else
165 continue;
166
167 uint32_t eax, ebx, ecx, edx;
168 ASMCpuIdExSlow(uLeaf, 0, 0, 0, &eax, &ebx, &ecx, &edx);
169
170 ASMAtomicAndU32(&pLegacyLeaf->uEcx, ecx | ~g_aCpuidUnifyBits[i].uEcx);
171 ASMAtomicAndU32(&pLegacyLeaf->uEdx, edx | ~g_aCpuidUnifyBits[i].uEdx);
172 }
173}
174
175
176/**
177 * Does Ring-0 CPUM initialization.
178 *
179 * This is mainly to check that the Host CPU mode is compatible
180 * with VBox.
181 *
182 * @returns VBox status code.
183 * @param pVM The cross context VM structure.
184 */
185VMMR0_INT_DECL(int) CPUMR0InitVM(PVMCC pVM)
186{
187 LogFlow(("CPUMR0Init: %p\n", pVM));
188 AssertCompile(sizeof(pVM->aCpus[0].cpum.s.Host.abXState) >= sizeof(pVM->aCpus[0].cpum.s.Guest.abXState));
189
190 /*
191 * Check CR0 & CR4 flags.
192 */
193 uint32_t u32CR0 = ASMGetCR0();
194 if ((u32CR0 & (X86_CR0_PE | X86_CR0_PG)) != (X86_CR0_PE | X86_CR0_PG)) /* a bit paranoid perhaps.. */
195 {
196 Log(("CPUMR0Init: PE or PG not set. cr0=%#x\n", u32CR0));
197 return VERR_UNSUPPORTED_CPU_MODE;
198 }
199
200 /*
201 * Check for sysenter and syscall usage.
202 */
203 if (ASMHasCpuId())
204 {
205 /*
206 * SYSENTER/SYSEXIT
207 *
208 * Intel docs claim you should test both the flag and family, model &
209 * stepping because some Pentium Pro CPUs have the SEP cpuid flag set,
210 * but don't support it. AMD CPUs may support this feature in legacy
211 * mode, they've banned it from long mode. Since we switch to 32-bit
212 * mode when entering raw-mode context the feature would become
213 * accessible again on AMD CPUs, so we have to check regardless of
214 * host bitness.
215 */
216 uint32_t u32CpuVersion;
217 uint32_t u32Dummy;
218 uint32_t fFeatures; /* (Used further down to check for MSRs, so don't clobber.) */
219 ASMCpuId(1, &u32CpuVersion, &u32Dummy, &u32Dummy, &fFeatures);
220 uint32_t const u32Family = u32CpuVersion >> 8;
221 uint32_t const u32Model = (u32CpuVersion >> 4) & 0xF;
222 uint32_t const u32Stepping = u32CpuVersion & 0xF;
223 if ( (fFeatures & X86_CPUID_FEATURE_EDX_SEP)
224 && ( u32Family != 6 /* (> pentium pro) */
225 || u32Model >= 3
226 || u32Stepping >= 3
227 || !ASMIsIntelCpu())
228 )
229 {
230 /*
231 * Read the MSR and see if it's in use or not.
232 */
233 uint32_t u32 = ASMRdMsr_Low(MSR_IA32_SYSENTER_CS);
234 if (u32)
235 {
236 pVM->cpum.s.fHostUseFlags |= CPUM_USE_SYSENTER;
237 Log(("CPUMR0Init: host uses sysenter cs=%08x%08x\n", ASMRdMsr_High(MSR_IA32_SYSENTER_CS), u32));
238 }
239 }
240
241 /*
242 * SYSCALL/SYSRET
243 *
244 * This feature is indicated by the SEP bit returned in EDX by CPUID
245 * function 0x80000001. Intel CPUs only supports this feature in
246 * long mode. Since we're not running 64-bit guests in raw-mode there
247 * are no issues with 32-bit intel hosts.
248 */
249 uint32_t cExt = 0;
250 ASMCpuId(0x80000000, &cExt, &u32Dummy, &u32Dummy, &u32Dummy);
251 if (RTX86IsValidExtRange(cExt))
252 {
253 uint32_t fExtFeaturesEDX = ASMCpuId_EDX(0x80000001);
254 if (fExtFeaturesEDX & X86_CPUID_EXT_FEATURE_EDX_SYSCALL)
255 {
256#ifdef RT_ARCH_X86
257 if (!ASMIsIntelCpu())
258#endif
259 {
260 uint64_t fEfer = ASMRdMsr(MSR_K6_EFER);
261 if (fEfer & MSR_K6_EFER_SCE)
262 {
263 pVM->cpum.s.fHostUseFlags |= CPUM_USE_SYSCALL;
264 Log(("CPUMR0Init: host uses syscall\n"));
265 }
266 }
267 }
268 }
269
270 /*
271 * Copy MSR_IA32_ARCH_CAPABILITIES bits over into the host and guest feature
272 * structure and as well as the guest MSR.
273 * Note! we assume this happens after the CPUMR3Init is done, so CPUID bits are settled.
274 */
275 pVM->cpum.s.HostFeatures.fArchRdclNo = 0;
276 pVM->cpum.s.HostFeatures.fArchIbrsAll = 0;
277 pVM->cpum.s.HostFeatures.fArchRsbOverride = 0;
278 pVM->cpum.s.HostFeatures.fArchVmmNeedNotFlushL1d = 0;
279 pVM->cpum.s.HostFeatures.fArchMdsNo = 0;
280 uint32_t const cStdRange = ASMCpuId_EAX(0);
281 if ( RTX86IsValidStdRange(cStdRange)
282 && cStdRange >= 7)
283 {
284 uint32_t fEdxFeatures = ASMCpuId_EDX(7);
285 if ( (fEdxFeatures & X86_CPUID_STEXT_FEATURE_EDX_ARCHCAP)
286 && (fFeatures & X86_CPUID_FEATURE_EDX_MSR))
287 {
288 /* Host: */
289 uint64_t fArchVal = ASMRdMsr(MSR_IA32_ARCH_CAPABILITIES);
290 pVM->cpum.s.HostFeatures.fArchRdclNo = RT_BOOL(fArchVal & MSR_IA32_ARCH_CAP_F_RDCL_NO);
291 pVM->cpum.s.HostFeatures.fArchIbrsAll = RT_BOOL(fArchVal & MSR_IA32_ARCH_CAP_F_IBRS_ALL);
292 pVM->cpum.s.HostFeatures.fArchRsbOverride = RT_BOOL(fArchVal & MSR_IA32_ARCH_CAP_F_RSBO);
293 pVM->cpum.s.HostFeatures.fArchVmmNeedNotFlushL1d = RT_BOOL(fArchVal & MSR_IA32_ARCH_CAP_F_VMM_NEED_NOT_FLUSH_L1D);
294 pVM->cpum.s.HostFeatures.fArchMdsNo = RT_BOOL(fArchVal & MSR_IA32_ARCH_CAP_F_MDS_NO);
295
296 /* guest: */
297 if (!pVM->cpum.s.GuestFeatures.fArchCap)
298 fArchVal = 0;
299 else if (!pVM->cpum.s.GuestFeatures.fIbrs)
300 fArchVal &= ~MSR_IA32_ARCH_CAP_F_IBRS_ALL;
301 VMCC_FOR_EACH_VMCPU_STMT(pVM, pVCpu->cpum.s.GuestMsrs.msr.ArchCaps = fArchVal);
302 pVM->cpum.s.GuestFeatures.fArchRdclNo = RT_BOOL(fArchVal & MSR_IA32_ARCH_CAP_F_RDCL_NO);
303 pVM->cpum.s.GuestFeatures.fArchIbrsAll = RT_BOOL(fArchVal & MSR_IA32_ARCH_CAP_F_IBRS_ALL);
304 pVM->cpum.s.GuestFeatures.fArchRsbOverride = RT_BOOL(fArchVal & MSR_IA32_ARCH_CAP_F_RSBO);
305 pVM->cpum.s.GuestFeatures.fArchVmmNeedNotFlushL1d = RT_BOOL(fArchVal & MSR_IA32_ARCH_CAP_F_VMM_NEED_NOT_FLUSH_L1D);
306 pVM->cpum.s.GuestFeatures.fArchMdsNo = RT_BOOL(fArchVal & MSR_IA32_ARCH_CAP_F_MDS_NO);
307 }
308 else
309 pVM->cpum.s.HostFeatures.fArchCap = 0;
310 }
311
312 /*
313 * Unify/cross check some CPUID feature bits on all available CPU cores
314 * and threads. We've seen CPUs where the monitor support differed.
315 *
316 * Because the hyper heap isn't always mapped into ring-0, we cannot
317 * access it from a RTMpOnAll callback. We use the legacy CPUID arrays
318 * as temp ring-0 accessible memory instead, ASSUMING that they're all
319 * up to date when we get here.
320 */
321 RTMpOnAll(cpumR0CheckCpuid, pVM, NULL);
322
323 for (uint32_t i = 0; i < RT_ELEMENTS(g_aCpuidUnifyBits); i++)
324 {
325 bool fIgnored;
326 uint32_t uLeaf = g_aCpuidUnifyBits[i].uLeaf;
327 PCPUMCPUIDLEAF pLeaf = cpumCpuIdGetLeafEx(pVM, uLeaf, 0, &fIgnored);
328 if (pLeaf)
329 {
330 PCPUMCPUID pLegacyLeaf;
331 if (uLeaf < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmStd))
332 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmStd[uLeaf];
333 else if (uLeaf - UINT32_C(0x80000000) < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmExt))
334 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmExt[uLeaf - UINT32_C(0x80000000)];
335 else if (uLeaf - UINT32_C(0xc0000000) < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmCentaur))
336 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmCentaur[uLeaf - UINT32_C(0xc0000000)];
337 else
338 continue;
339
340 pLeaf->uEcx = pLegacyLeaf->uEcx;
341 pLeaf->uEdx = pLegacyLeaf->uEdx;
342 }
343 }
344
345 }
346
347
348 /*
349 * Check if debug registers are armed.
350 * This ASSUMES that DR7.GD is not set, or that it's handled transparently!
351 */
352 uint32_t u32DR7 = ASMGetDR7();
353 if (u32DR7 & X86_DR7_ENABLED_MASK)
354 {
355 VMCC_FOR_EACH_VMCPU_STMT(pVM, pVCpu->cpum.s.fUseFlags |= CPUM_USE_DEBUG_REGS_HOST);
356 Log(("CPUMR0Init: host uses debug registers (dr7=%x)\n", u32DR7));
357 }
358
359 return VINF_SUCCESS;
360}
361
362
363/**
364 * Trap handler for device-not-available fault (\#NM).
365 * Device not available, FP or (F)WAIT instruction.
366 *
367 * @returns VBox status code.
368 * @retval VINF_SUCCESS if the guest FPU state is loaded.
369 * @retval VINF_EM_RAW_GUEST_TRAP if it is a guest trap.
370 * @retval VINF_CPUM_HOST_CR0_MODIFIED if we modified the host CR0.
371 *
372 * @param pVM The cross context VM structure.
373 * @param pVCpu The cross context virtual CPU structure.
374 */
375VMMR0_INT_DECL(int) CPUMR0Trap07Handler(PVMCC pVM, PVMCPUCC pVCpu)
376{
377 Assert(pVM->cpum.s.HostFeatures.fFxSaveRstor);
378 Assert(ASMGetCR4() & X86_CR4_OSFXSR);
379
380 /* If the FPU state has already been loaded, then it's a guest trap. */
381 if (CPUMIsGuestFPUStateActive(pVCpu))
382 {
383 Assert( ((pVCpu->cpum.s.Guest.cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS)) == (X86_CR0_MP | X86_CR0_TS))
384 || ((pVCpu->cpum.s.Guest.cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS)) == (X86_CR0_MP | X86_CR0_TS | X86_CR0_EM)));
385 return VINF_EM_RAW_GUEST_TRAP;
386 }
387
388 /*
389 * There are two basic actions:
390 * 1. Save host fpu and restore guest fpu.
391 * 2. Generate guest trap.
392 *
393 * When entering the hypervisor we'll always enable MP (for proper wait
394 * trapping) and TS (for intercepting all fpu/mmx/sse stuff). The EM flag
395 * is taken from the guest OS in order to get proper SSE handling.
396 *
397 *
398 * Actions taken depending on the guest CR0 flags:
399 *
400 * 3 2 1
401 * TS | EM | MP | FPUInstr | WAIT :: VMM Action
402 * ------------------------------------------------------------------------
403 * 0 | 0 | 0 | Exec | Exec :: Clear TS & MP, Save HC, Load GC.
404 * 0 | 0 | 1 | Exec | Exec :: Clear TS, Save HC, Load GC.
405 * 0 | 1 | 0 | #NM | Exec :: Clear TS & MP, Save HC, Load GC.
406 * 0 | 1 | 1 | #NM | Exec :: Clear TS, Save HC, Load GC.
407 * 1 | 0 | 0 | #NM | Exec :: Clear MP, Save HC, Load GC. (EM is already cleared.)
408 * 1 | 0 | 1 | #NM | #NM :: Go to guest taking trap there.
409 * 1 | 1 | 0 | #NM | Exec :: Clear MP, Save HC, Load GC. (EM is already set.)
410 * 1 | 1 | 1 | #NM | #NM :: Go to guest taking trap there.
411 */
412
413 switch (pVCpu->cpum.s.Guest.cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS))
414 {
415 case X86_CR0_MP | X86_CR0_TS:
416 case X86_CR0_MP | X86_CR0_TS | X86_CR0_EM:
417 return VINF_EM_RAW_GUEST_TRAP;
418 default:
419 break;
420 }
421
422 return CPUMR0LoadGuestFPU(pVM, pVCpu);
423}
424
425
426/**
427 * Saves the host-FPU/XMM state (if necessary) and (always) loads the guest-FPU
428 * state into the CPU.
429 *
430 * @returns VINF_SUCCESS on success, host CR0 unmodified.
431 * @returns VINF_CPUM_HOST_CR0_MODIFIED on success when the host CR0 was
432 * modified and VT-x needs to update the value in the VMCS.
433 *
434 * @param pVM The cross context VM structure.
435 * @param pVCpu The cross context virtual CPU structure.
436 */
437VMMR0_INT_DECL(int) CPUMR0LoadGuestFPU(PVMCC pVM, PVMCPUCC pVCpu)
438{
439 int rc;
440 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
441 Assert(!(pVCpu->cpum.s.fUseFlags & CPUM_USED_FPU_GUEST));
442
443 if (!pVM->cpum.s.HostFeatures.fLeakyFxSR)
444 {
445 Assert(!(pVCpu->cpum.s.fUseFlags & CPUM_USED_MANUAL_XMM_RESTORE));
446 rc = cpumR0SaveHostRestoreGuestFPUState(&pVCpu->cpum.s);
447 }
448 else
449 {
450 Assert(!(pVCpu->cpum.s.fUseFlags & CPUM_USED_MANUAL_XMM_RESTORE) || (pVCpu->cpum.s.fUseFlags & CPUM_USED_FPU_HOST));
451 /** @todo r=ramshankar: Can't we used a cached value here
452 * instead of reading the MSR? host EFER doesn't usually
453 * change. */
454 uint64_t uHostEfer = ASMRdMsr(MSR_K6_EFER);
455 if (!(uHostEfer & MSR_K6_EFER_FFXSR))
456 rc = cpumR0SaveHostRestoreGuestFPUState(&pVCpu->cpum.s);
457 else
458 {
459 RTCCUINTREG const uSavedFlags = ASMIntDisableFlags();
460 pVCpu->cpum.s.fUseFlags |= CPUM_USED_MANUAL_XMM_RESTORE;
461 ASMWrMsr(MSR_K6_EFER, uHostEfer & ~MSR_K6_EFER_FFXSR);
462 rc = cpumR0SaveHostRestoreGuestFPUState(&pVCpu->cpum.s);
463 ASMWrMsr(MSR_K6_EFER, uHostEfer | MSR_K6_EFER_FFXSR);
464 ASMSetFlags(uSavedFlags);
465 }
466 }
467 Assert( (pVCpu->cpum.s.fUseFlags & (CPUM_USED_FPU_GUEST | CPUM_USED_FPU_HOST | CPUM_USED_FPU_SINCE_REM))
468 == (CPUM_USED_FPU_GUEST | CPUM_USED_FPU_HOST | CPUM_USED_FPU_SINCE_REM));
469 Assert(pVCpu->cpum.s.Guest.fUsedFpuGuest);
470 return rc;
471}
472
473
474/**
475 * Saves the guest FPU/XMM state if needed, restores the host FPU/XMM state as
476 * needed.
477 *
478 * @returns true if we saved the guest state.
479 * @param pVCpu The cross context virtual CPU structure.
480 */
481VMMR0_INT_DECL(bool) CPUMR0FpuStateMaybeSaveGuestAndRestoreHost(PVMCPUCC pVCpu)
482{
483 bool fSavedGuest;
484 Assert(pVCpu->CTX_SUFF(pVM)->cpum.s.HostFeatures.fFxSaveRstor);
485 Assert(ASMGetCR4() & X86_CR4_OSFXSR);
486 if (pVCpu->cpum.s.fUseFlags & (CPUM_USED_FPU_GUEST | CPUM_USED_FPU_HOST))
487 {
488 fSavedGuest = RT_BOOL(pVCpu->cpum.s.fUseFlags & CPUM_USED_FPU_GUEST);
489 Assert(fSavedGuest == pVCpu->cpum.s.Guest.fUsedFpuGuest);
490 if (!(pVCpu->cpum.s.fUseFlags & CPUM_USED_MANUAL_XMM_RESTORE))
491 cpumR0SaveGuestRestoreHostFPUState(&pVCpu->cpum.s);
492 else
493 {
494 /* Temporarily clear MSR_K6_EFER_FFXSR or else we'll be unable to
495 save/restore the XMM state with fxsave/fxrstor. */
496 uint64_t uHostEfer = ASMRdMsr(MSR_K6_EFER);
497 if (uHostEfer & MSR_K6_EFER_FFXSR)
498 {
499 RTCCUINTREG const uSavedFlags = ASMIntDisableFlags();
500 ASMWrMsr(MSR_K6_EFER, uHostEfer & ~MSR_K6_EFER_FFXSR);
501 cpumR0SaveGuestRestoreHostFPUState(&pVCpu->cpum.s);
502 ASMWrMsr(MSR_K6_EFER, uHostEfer | MSR_K6_EFER_FFXSR);
503 ASMSetFlags(uSavedFlags);
504 }
505 else
506 cpumR0SaveGuestRestoreHostFPUState(&pVCpu->cpum.s);
507 pVCpu->cpum.s.fUseFlags &= ~CPUM_USED_MANUAL_XMM_RESTORE;
508 }
509 }
510 else
511 fSavedGuest = false;
512 Assert(!( pVCpu->cpum.s.fUseFlags
513 & (CPUM_USED_FPU_GUEST | CPUM_USED_FPU_HOST | CPUM_USED_MANUAL_XMM_RESTORE)));
514 Assert(!pVCpu->cpum.s.Guest.fUsedFpuGuest);
515 return fSavedGuest;
516}
517
518
519/**
520 * Saves the host debug state, setting CPUM_USED_HOST_DEBUG_STATE and loading
521 * DR7 with safe values.
522 *
523 * @returns VBox status code.
524 * @param pVCpu The cross context virtual CPU structure.
525 */
526static int cpumR0SaveHostDebugState(PVMCPUCC pVCpu)
527{
528 /*
529 * Save the host state.
530 */
531 pVCpu->cpum.s.Host.dr0 = ASMGetDR0();
532 pVCpu->cpum.s.Host.dr1 = ASMGetDR1();
533 pVCpu->cpum.s.Host.dr2 = ASMGetDR2();
534 pVCpu->cpum.s.Host.dr3 = ASMGetDR3();
535 pVCpu->cpum.s.Host.dr6 = ASMGetDR6();
536 /** @todo dr7 might already have been changed to 0x400; don't care right now as it's harmless. */
537 pVCpu->cpum.s.Host.dr7 = ASMGetDR7();
538
539 /* Preemption paranoia. */
540 ASMAtomicOrU32(&pVCpu->cpum.s.fUseFlags, CPUM_USED_DEBUG_REGS_HOST);
541
542 /*
543 * Make sure DR7 is harmless or else we could trigger breakpoints when
544 * load guest or hypervisor DRx values later.
545 */
546 if (pVCpu->cpum.s.Host.dr7 != X86_DR7_INIT_VAL)
547 ASMSetDR7(X86_DR7_INIT_VAL);
548
549 return VINF_SUCCESS;
550}
551
552
553/**
554 * Saves the guest DRx state residing in host registers and restore the host
555 * register values.
556 *
557 * The guest DRx state is only saved if CPUMR0LoadGuestDebugState was called,
558 * since it's assumed that we're shadowing the guest DRx register values
559 * accurately when using the combined hypervisor debug register values
560 * (CPUMR0LoadHyperDebugState).
561 *
562 * @returns true if either guest or hypervisor debug registers were loaded.
563 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
564 * @param fDr6 Whether to include DR6 or not.
565 * @thread EMT(pVCpu)
566 */
567VMMR0_INT_DECL(bool) CPUMR0DebugStateMaybeSaveGuestAndRestoreHost(PVMCPUCC pVCpu, bool fDr6)
568{
569 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
570 bool const fDrXLoaded = RT_BOOL(pVCpu->cpum.s.fUseFlags & (CPUM_USED_DEBUG_REGS_GUEST | CPUM_USED_DEBUG_REGS_HYPER));
571
572 /*
573 * Do we need to save the guest DRx registered loaded into host registers?
574 * (DR7 and DR6 (if fDr6 is true) are left to the caller.)
575 */
576 if (pVCpu->cpum.s.fUseFlags & CPUM_USED_DEBUG_REGS_GUEST)
577 {
578 pVCpu->cpum.s.Guest.dr[0] = ASMGetDR0();
579 pVCpu->cpum.s.Guest.dr[1] = ASMGetDR1();
580 pVCpu->cpum.s.Guest.dr[2] = ASMGetDR2();
581 pVCpu->cpum.s.Guest.dr[3] = ASMGetDR3();
582 if (fDr6)
583 pVCpu->cpum.s.Guest.dr[6] = ASMGetDR6();
584 }
585 ASMAtomicAndU32(&pVCpu->cpum.s.fUseFlags, ~(CPUM_USED_DEBUG_REGS_GUEST | CPUM_USED_DEBUG_REGS_HYPER));
586
587 /*
588 * Restore the host's debug state. DR0-3, DR6 and only then DR7!
589 */
590 if (pVCpu->cpum.s.fUseFlags & CPUM_USED_DEBUG_REGS_HOST)
591 {
592 /* A bit of paranoia first... */
593 uint64_t uCurDR7 = ASMGetDR7();
594 if (uCurDR7 != X86_DR7_INIT_VAL)
595 ASMSetDR7(X86_DR7_INIT_VAL);
596
597 ASMSetDR0(pVCpu->cpum.s.Host.dr0);
598 ASMSetDR1(pVCpu->cpum.s.Host.dr1);
599 ASMSetDR2(pVCpu->cpum.s.Host.dr2);
600 ASMSetDR3(pVCpu->cpum.s.Host.dr3);
601 /** @todo consider only updating if they differ, esp. DR6. Need to figure how
602 * expensive DRx reads are over DRx writes. */
603 ASMSetDR6(pVCpu->cpum.s.Host.dr6);
604 ASMSetDR7(pVCpu->cpum.s.Host.dr7);
605
606 ASMAtomicAndU32(&pVCpu->cpum.s.fUseFlags, ~CPUM_USED_DEBUG_REGS_HOST);
607 }
608
609 return fDrXLoaded;
610}
611
612
613/**
614 * Saves the guest DRx state if it resides host registers.
615 *
616 * This does NOT clear any use flags, so the host registers remains loaded with
617 * the guest DRx state upon return. The purpose is only to make sure the values
618 * in the CPU context structure is up to date.
619 *
620 * @returns true if the host registers contains guest values, false if not.
621 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
622 * @param fDr6 Whether to include DR6 or not.
623 * @thread EMT(pVCpu)
624 */
625VMMR0_INT_DECL(bool) CPUMR0DebugStateMaybeSaveGuest(PVMCPUCC pVCpu, bool fDr6)
626{
627 /*
628 * Do we need to save the guest DRx registered loaded into host registers?
629 * (DR7 and DR6 (if fDr6 is true) are left to the caller.)
630 */
631 if (pVCpu->cpum.s.fUseFlags & CPUM_USED_DEBUG_REGS_GUEST)
632 {
633 pVCpu->cpum.s.Guest.dr[0] = ASMGetDR0();
634 pVCpu->cpum.s.Guest.dr[1] = ASMGetDR1();
635 pVCpu->cpum.s.Guest.dr[2] = ASMGetDR2();
636 pVCpu->cpum.s.Guest.dr[3] = ASMGetDR3();
637 if (fDr6)
638 pVCpu->cpum.s.Guest.dr[6] = ASMGetDR6();
639 return true;
640 }
641 return false;
642}
643
644
645/**
646 * Lazily sync in the debug state.
647 *
648 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
649 * @param fDr6 Whether to include DR6 or not.
650 * @thread EMT(pVCpu)
651 */
652VMMR0_INT_DECL(void) CPUMR0LoadGuestDebugState(PVMCPUCC pVCpu, bool fDr6)
653{
654 /*
655 * Save the host state and disarm all host BPs.
656 */
657 cpumR0SaveHostDebugState(pVCpu);
658 Assert(ASMGetDR7() == X86_DR7_INIT_VAL);
659
660 /*
661 * Activate the guest state DR0-3.
662 * DR7 and DR6 (if fDr6 is true) are left to the caller.
663 */
664 ASMSetDR0(pVCpu->cpum.s.Guest.dr[0]);
665 ASMSetDR1(pVCpu->cpum.s.Guest.dr[1]);
666 ASMSetDR2(pVCpu->cpum.s.Guest.dr[2]);
667 ASMSetDR3(pVCpu->cpum.s.Guest.dr[3]);
668 if (fDr6)
669 ASMSetDR6(pVCpu->cpum.s.Guest.dr[6]);
670
671 ASMAtomicOrU32(&pVCpu->cpum.s.fUseFlags, CPUM_USED_DEBUG_REGS_GUEST);
672}
673
674
675/**
676 * Lazily sync in the hypervisor debug state
677 *
678 * @returns VBox status code.
679 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
680 * @param fDr6 Whether to include DR6 or not.
681 * @thread EMT(pVCpu)
682 */
683VMMR0_INT_DECL(void) CPUMR0LoadHyperDebugState(PVMCPUCC pVCpu, bool fDr6)
684{
685 /*
686 * Save the host state and disarm all host BPs.
687 */
688 cpumR0SaveHostDebugState(pVCpu);
689 Assert(ASMGetDR7() == X86_DR7_INIT_VAL);
690
691 /*
692 * Make sure the hypervisor values are up to date.
693 */
694 CPUMRecalcHyperDRx(pVCpu, UINT8_MAX /* no loading, please */);
695
696 /*
697 * Activate the guest state DR0-3.
698 * DR7 and DR6 (if fDr6 is true) are left to the caller.
699 */
700 ASMSetDR0(pVCpu->cpum.s.Hyper.dr[0]);
701 ASMSetDR1(pVCpu->cpum.s.Hyper.dr[1]);
702 ASMSetDR2(pVCpu->cpum.s.Hyper.dr[2]);
703 ASMSetDR3(pVCpu->cpum.s.Hyper.dr[3]);
704 if (fDr6)
705 ASMSetDR6(X86_DR6_INIT_VAL);
706
707 ASMAtomicOrU32(&pVCpu->cpum.s.fUseFlags, CPUM_USED_DEBUG_REGS_HYPER);
708}
709
710#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
711
712/**
713 * Per-CPU callback that probes the CPU for APIC support.
714 *
715 * @param idCpu The identifier for the CPU the function is called on.
716 * @param pvUser1 Ignored.
717 * @param pvUser2 Ignored.
718 */
719static DECLCALLBACK(void) cpumR0MapLocalApicCpuProber(RTCPUID idCpu, void *pvUser1, void *pvUser2)
720{
721 NOREF(pvUser1); NOREF(pvUser2);
722 int iCpu = RTMpCpuIdToSetIndex(idCpu);
723 AssertReturnVoid(iCpu >= 0 && (unsigned)iCpu < RT_ELEMENTS(g_aLApics));
724
725 /*
726 * Check for APIC support.
727 */
728 uint32_t uMaxLeaf, u32EBX, u32ECX, u32EDX;
729 ASMCpuId(0, &uMaxLeaf, &u32EBX, &u32ECX, &u32EDX);
730 if ( ( RTX86IsIntelCpu(u32EBX, u32ECX, u32EDX)
731 || RTX86IsAmdCpu(u32EBX, u32ECX, u32EDX)
732 || RTX86IsViaCentaurCpu(u32EBX, u32ECX, u32EDX)
733 || RTX86IsShanghaiCpu(u32EBX, u32ECX, u32EDX)
734 || RTX86IsHygonCpu(u32EBX, u32ECX, u32EDX))
735 && RTX86IsValidStdRange(uMaxLeaf))
736 {
737 uint32_t uDummy;
738 ASMCpuId(1, &uDummy, &u32EBX, &u32ECX, &u32EDX);
739 if ( (u32EDX & X86_CPUID_FEATURE_EDX_APIC)
740 && (u32EDX & X86_CPUID_FEATURE_EDX_MSR))
741 {
742 /*
743 * Safe to access the MSR. Read it and calc the BASE (a little complicated).
744 */
745 uint64_t u64ApicBase = ASMRdMsr(MSR_IA32_APICBASE);
746 uint64_t u64Mask = MSR_IA32_APICBASE_BASE_MIN;
747
748 /* see Intel Manual: Local APIC Status and Location: MAXPHYADDR default is bit 36 */
749 uint32_t uMaxExtLeaf;
750 ASMCpuId(0x80000000, &uMaxExtLeaf, &u32EBX, &u32ECX, &u32EDX);
751 if ( uMaxExtLeaf >= UINT32_C(0x80000008)
752 && RTX86IsValidExtRange(uMaxExtLeaf))
753 {
754 uint32_t u32PhysBits;
755 ASMCpuId(0x80000008, &u32PhysBits, &u32EBX, &u32ECX, &u32EDX);
756 u32PhysBits &= 0xff;
757 u64Mask = ((UINT64_C(1) << u32PhysBits) - 1) & UINT64_C(0xfffffffffffff000);
758 }
759
760 AssertCompile(sizeof(g_aLApics[iCpu].PhysBase) == sizeof(u64ApicBase));
761 g_aLApics[iCpu].PhysBase = u64ApicBase & u64Mask;
762 g_aLApics[iCpu].fEnabled = RT_BOOL(u64ApicBase & MSR_IA32_APICBASE_EN);
763 g_aLApics[iCpu].fX2Apic = (u64ApicBase & (MSR_IA32_APICBASE_EXTD | MSR_IA32_APICBASE_EN))
764 == (MSR_IA32_APICBASE_EXTD | MSR_IA32_APICBASE_EN);
765 }
766 }
767}
768
769
770
771/**
772 * Per-CPU callback that verifies our APIC expectations.
773 *
774 * @param idCpu The identifier for the CPU the function is called on.
775 * @param pvUser1 Ignored.
776 * @param pvUser2 Ignored.
777 */
778static DECLCALLBACK(void) cpumR0MapLocalApicCpuChecker(RTCPUID idCpu, void *pvUser1, void *pvUser2)
779{
780 NOREF(pvUser1); NOREF(pvUser2);
781
782 int iCpu = RTMpCpuIdToSetIndex(idCpu);
783 AssertReturnVoid(iCpu >= 0 && (unsigned)iCpu < RT_ELEMENTS(g_aLApics));
784 if (!g_aLApics[iCpu].fEnabled)
785 return;
786
787 /*
788 * 0x0X 82489 external APIC
789 * 0x1X Local APIC
790 * 0x2X..0xFF reserved
791 */
792 uint32_t uApicVersion;
793 if (g_aLApics[iCpu].fX2Apic)
794 uApicVersion = ApicX2RegRead32(APIC_REG_VERSION);
795 else
796 uApicVersion = ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_VERSION);
797 if ((APIC_REG_VERSION_GET_VER(uApicVersion) & 0xF0) == 0x10)
798 {
799 g_aLApics[iCpu].uVersion = uApicVersion;
800
801# if 0 /* enable if you need it. */
802 if (g_aLApics[iCpu].fX2Apic)
803 SUPR0Printf("CPUM: X2APIC %02u - ver %#010x, lint0=%#07x lint1=%#07x pc=%#07x thmr=%#07x cmci=%#07x\n",
804 iCpu, uApicVersion,
805 ApicX2RegRead32(APIC_REG_LVT_LINT0), ApicX2RegRead32(APIC_REG_LVT_LINT1),
806 ApicX2RegRead32(APIC_REG_LVT_PC), ApicX2RegRead32(APIC_REG_LVT_THMR),
807 ApicX2RegRead32(APIC_REG_LVT_CMCI));
808 else
809 {
810 SUPR0Printf("CPUM: APIC %02u at %RGp (mapped at %p) - ver %#010x, lint0=%#07x lint1=%#07x pc=%#07x thmr=%#07x cmci=%#07x\n",
811 iCpu, g_aLApics[iCpu].PhysBase, g_aLApics[iCpu].pv, uApicVersion,
812 ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_LVT_LINT0), ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_LVT_LINT1),
813 ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_LVT_PC), ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_LVT_THMR),
814 ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_LVT_CMCI));
815 if (uApicVersion & 0x80000000)
816 {
817 uint32_t uExtFeatures = ApicRegRead(g_aLApics[iCpu].pv, 0x400);
818 uint32_t cEiLvt = (uExtFeatures >> 16) & 0xff;
819 SUPR0Printf("CPUM: APIC %02u: ExtSpace available. extfeat=%08x eilvt[0..3]=%08x %08x %08x %08x\n",
820 iCpu,
821 ApicRegRead(g_aLApics[iCpu].pv, 0x400),
822 cEiLvt >= 1 ? ApicRegRead(g_aLApics[iCpu].pv, 0x500) : 0,
823 cEiLvt >= 2 ? ApicRegRead(g_aLApics[iCpu].pv, 0x510) : 0,
824 cEiLvt >= 3 ? ApicRegRead(g_aLApics[iCpu].pv, 0x520) : 0,
825 cEiLvt >= 4 ? ApicRegRead(g_aLApics[iCpu].pv, 0x530) : 0);
826 }
827 }
828# endif
829 }
830 else
831 {
832 g_aLApics[iCpu].fEnabled = false;
833 g_aLApics[iCpu].fX2Apic = false;
834 SUPR0Printf("VBox/CPUM: Unsupported APIC version %#x (iCpu=%d)\n", uApicVersion, iCpu);
835 }
836}
837
838
839/**
840 * Map the MMIO page of each local APIC in the system.
841 */
842static int cpumR0MapLocalApics(void)
843{
844 /*
845 * Check that we'll always stay within the array bounds.
846 */
847 if (RTMpGetArraySize() > RT_ELEMENTS(g_aLApics))
848 {
849 LogRel(("CPUM: Too many real CPUs/cores/threads - %u, max %u\n", RTMpGetArraySize(), RT_ELEMENTS(g_aLApics)));
850 return VERR_TOO_MANY_CPUS;
851 }
852
853 /*
854 * Create mappings for all online CPUs we think have legacy APICs.
855 */
856 int rc = RTMpOnAll(cpumR0MapLocalApicCpuProber, NULL, NULL);
857
858 for (unsigned iCpu = 0; RT_SUCCESS(rc) && iCpu < RT_ELEMENTS(g_aLApics); iCpu++)
859 {
860 if (g_aLApics[iCpu].fEnabled && !g_aLApics[iCpu].fX2Apic)
861 {
862 rc = RTR0MemObjEnterPhys(&g_aLApics[iCpu].hMemObj, g_aLApics[iCpu].PhysBase,
863 HOST_PAGE_SIZE, RTMEM_CACHE_POLICY_MMIO);
864 if (RT_SUCCESS(rc))
865 {
866 rc = RTR0MemObjMapKernel(&g_aLApics[iCpu].hMapObj, g_aLApics[iCpu].hMemObj, (void *)-1,
867 HOST_PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
868 if (RT_SUCCESS(rc))
869 {
870 g_aLApics[iCpu].pv = RTR0MemObjAddress(g_aLApics[iCpu].hMapObj);
871 continue;
872 }
873 RTR0MemObjFree(g_aLApics[iCpu].hMemObj, true /* fFreeMappings */);
874 }
875 g_aLApics[iCpu].fEnabled = false;
876 }
877 g_aLApics[iCpu].pv = NULL;
878 }
879
880 /*
881 * Check the APICs.
882 */
883 if (RT_SUCCESS(rc))
884 rc = RTMpOnAll(cpumR0MapLocalApicCpuChecker, NULL, NULL);
885
886 if (RT_FAILURE(rc))
887 {
888 cpumR0UnmapLocalApics();
889 return rc;
890 }
891
892# ifdef LOG_ENABLED
893 /*
894 * Log the result (pretty useless, requires enabling CPUM in VBoxDrv
895 * and !VBOX_WITH_R0_LOGGING).
896 */
897 if (LogIsEnabled())
898 {
899 uint32_t cEnabled = 0;
900 uint32_t cX2Apics = 0;
901 for (unsigned iCpu = 0; iCpu < RT_ELEMENTS(g_aLApics); iCpu++)
902 if (g_aLApics[iCpu].fEnabled)
903 {
904 cEnabled++;
905 cX2Apics += g_aLApics[iCpu].fX2Apic;
906 }
907 Log(("CPUM: %u APICs, %u X2APICs\n", cEnabled, cX2Apics));
908 }
909# endif
910
911 return VINF_SUCCESS;
912}
913
914
915/**
916 * Unmap the Local APIC of all host CPUs.
917 */
918static void cpumR0UnmapLocalApics(void)
919{
920 for (unsigned iCpu = RT_ELEMENTS(g_aLApics); iCpu-- > 0;)
921 {
922 if (g_aLApics[iCpu].pv)
923 {
924 RTR0MemObjFree(g_aLApics[iCpu].hMapObj, true /* fFreeMappings */);
925 RTR0MemObjFree(g_aLApics[iCpu].hMemObj, true /* fFreeMappings */);
926 g_aLApics[iCpu].hMapObj = NIL_RTR0MEMOBJ;
927 g_aLApics[iCpu].hMemObj = NIL_RTR0MEMOBJ;
928 g_aLApics[iCpu].fEnabled = false;
929 g_aLApics[iCpu].fX2Apic = false;
930 g_aLApics[iCpu].pv = NULL;
931 }
932 }
933}
934
935
936/**
937 * Updates CPUMCPU::pvApicBase and CPUMCPU::fX2Apic prior to world switch.
938 *
939 * Writes the Local APIC mapping address of the current host CPU to CPUMCPU so
940 * the world switchers can access the APIC registers for the purpose of
941 * disabling and re-enabling the NMIs. Must be called with disabled preemption
942 * or disabled interrupts!
943 *
944 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
945 * @param iHostCpuSet The CPU set index of the current host CPU.
946 */
947VMMR0_INT_DECL(void) CPUMR0SetLApic(PVMCPUCC pVCpu, uint32_t iHostCpuSet)
948{
949 Assert(iHostCpuSet <= RT_ELEMENTS(g_aLApics));
950 pVCpu->cpum.s.pvApicBase = g_aLApics[iHostCpuSet].pv;
951 pVCpu->cpum.s.fX2Apic = g_aLApics[iHostCpuSet].fX2Apic;
952// Log6(("CPUMR0SetLApic: pvApicBase=%p fX2Apic=%d\n", g_aLApics[idxCpu].pv, g_aLApics[idxCpu].fX2Apic));
953}
954
955#endif /* VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI */
956
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette