VirtualBox

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

Last change on this file was 109210, checked in by vboxsync, 10 days ago

VMM/CPUM: Split the target specific code out of CPUM.cpp (and CPUM-armv8.cpp) to avoid code duplication and make sure we get the same host-specific initialization. jiraref:1653

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 28.0 KB
Line 
1/* $Id: CPUMR0.cpp 109210 2025-05-08 22:09:06Z vboxsync $ */
2/** @file
3 * CPUM - Host Context Ring 0, only targeting x86.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.215389.xyz.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_CPUM
33#define VBOX_VMM_TARGET_X86
34#define CPUM_WITH_NONCONST_HOST_FEATURES
35#include <VBox/vmm/cpum.h>
36#include <VBox/vmm/hm.h>
37#include "CPUMInternal.h"
38#include <VBox/vmm/vmcc.h>
39#include <VBox/vmm/gvm.h>
40#include <VBox/err.h>
41#include <VBox/log.h>
42#include <VBox/vmm/hm.h>
43
44#include <iprt/assert.h>
45#include <iprt/asm-amd64-x86.h>
46#include <iprt/mem.h>
47#include <iprt/string.h>
48#include <iprt/x86.h>
49
50
51/*********************************************************************************************************************************
52* Global Variables *
53*********************************************************************************************************************************/
54/** Host CPU features. */
55DECL_HIDDEN_DATA(CPUHOSTFEATURES) g_CpumHostFeatures;
56/** Static storage for host MSRs.
57 * @note this isn't really used beyond module init. */
58static SUPHWVIRTMSRS g_CpumHostHwvirtMsrs;
59
60/**
61 * CPUID bits to unify among all cores.
62 */
63static struct
64{
65 uint32_t uLeaf; /**< Leaf to check. */
66 uint32_t uEcx; /**< which bits in ecx to unify between CPUs. */
67 uint32_t uEdx; /**< which bits in edx to unify between CPUs. */
68}
69const g_aCpuidUnifyBits[] =
70{
71 {
72 0x00000001,
73 X86_CPUID_FEATURE_ECX_CX16 | X86_CPUID_FEATURE_ECX_MONITOR,
74 X86_CPUID_FEATURE_EDX_CX8
75 }
76};
77
78
79
80/*********************************************************************************************************************************
81* Internal Functions *
82*********************************************************************************************************************************/
83static int cpumR0SaveHostDebugState(PVMCPUCC pVCpu);
84
85
86/**
87 * Check the CPUID features of this particular CPU and disable relevant features
88 * for the guest which do not exist on this CPU.
89 *
90 * We have seen systems where the X86_CPUID_FEATURE_ECX_MONITOR feature flag is
91 * only set on some host CPUs, see @bugref{5436}.
92 *
93 * @note This function might be called simultaneously on more than one CPU!
94 *
95 * @param idCpu The identifier for the CPU the function is called on.
96 * @param pvUser1 Leaf array.
97 * @param pvUser2 Number of leaves.
98 */
99static DECLCALLBACK(void) cpumR0CheckCpuid(RTCPUID idCpu, void *pvUser1, void *pvUser2)
100{
101 PCPUMCPUIDLEAF const paLeaves = (PCPUMCPUIDLEAF)pvUser1;
102 uint32_t const cLeaves = (uint32_t)(uintptr_t)pvUser2;
103 RT_NOREF(idCpu);
104
105 for (uint32_t i = 0; i < RT_ELEMENTS(g_aCpuidUnifyBits); i++)
106 {
107 PCPUMCPUIDLEAF pLeaf = cpumCpuIdGetLeafInt(paLeaves, cLeaves, g_aCpuidUnifyBits[i].uLeaf, 0);
108 if (pLeaf)
109 {
110 uint32_t uEax, uEbx, uEcx, uEdx;
111 ASMCpuIdExSlow(g_aCpuidUnifyBits[i].uLeaf, 0, 0, 0, &uEax, &uEbx, &uEcx, &uEdx);
112
113 ASMAtomicAndU32(&pLeaf->uEcx, uEcx | ~g_aCpuidUnifyBits[i].uEcx);
114 ASMAtomicAndU32(&pLeaf->uEdx, uEdx | ~g_aCpuidUnifyBits[i].uEdx);
115 }
116 }
117}
118
119
120/**
121 * Does the Ring-0 CPU initialization once during module load.
122 * XXX Host-CPU hot-plugging?
123 */
124VMMR0_INT_DECL(int) CPUMR0ModuleInit(void)
125{
126 /*
127 * Query the hardware virtualization capabilities of the host CPU first.
128 */
129 uint32_t fHwCaps = 0;
130 int rc = SUPR0GetVTSupport(&fHwCaps);
131 AssertLogRelMsg(RT_SUCCESS(rc) || rc == VERR_UNSUPPORTED_CPU || rc == VERR_SVM_NO_SVM || rc == VERR_VMX_NO_VMX,
132 ("SUPR0GetHwvirtMsrs -> %Rrc\n", rc));
133 if (RT_SUCCESS(rc))
134 {
135 rc = SUPR0GetHwvirtMsrs(&g_CpumHostHwvirtMsrs, fHwCaps, false /*fIgnored*/);
136 AssertLogRelRC(rc);
137 if (RT_FAILURE(rc))
138 RT_ZERO(g_CpumHostHwvirtMsrs);
139 }
140
141 /*
142 * Collect CPUID leaves.
143 */
144 PCPUMCPUIDLEAF paLeaves;
145 uint32_t cLeaves;
146 rc = CPUMCpuIdCollectLeavesFromX86Host(&paLeaves, &cLeaves);
147 AssertLogRelRCReturn(rc, rc);
148
149 /*
150 * Unify/cross check some CPUID feature bits on all available CPU cores
151 * and threads. We've seen CPUs where the monitor support differed.
152 */
153 RTMpOnAll(cpumR0CheckCpuid, paLeaves, (void *)(uintptr_t)cLeaves);
154
155 /*
156 * Populate the host CPU feature global variable.
157 */
158 rc = cpumCpuIdExplodeFeaturesX86(paLeaves, cLeaves, &g_CpumHostFeatures.s);
159 RTMemFree(paLeaves);
160 AssertLogRelRCReturn(rc, rc);
161 if (g_CpumHostFeatures.s.fVmx)
162 cpumCpuIdExplodeFeaturesX86VmxFromSupMsrs(&g_CpumHostHwvirtMsrs, &g_CpumHostFeatures.s);
163
164 /*
165 * Get MSR_IA32_ARCH_CAPABILITIES and expand it into the host feature structure.
166 *
167 * AMD CPUs doesn't have this register, similar info is available in EBX in
168 * CPUID leaf 0x80000008
169 */
170 if (ASMHasCpuId())
171 {
172 uint32_t const cStdRange = ASMCpuId_EAX(0);
173 if ( RTX86IsValidStdRange(cStdRange)
174 && cStdRange >= 7)
175 {
176 uint32_t const fStdFeaturesEdx = ASMCpuId_EDX(1);
177 uint32_t fStdExtFeaturesEdx;
178 ASMCpuIdExSlow(7, 0, 0, 0, NULL, NULL, NULL, &fStdExtFeaturesEdx);
179 if ( (fStdExtFeaturesEdx & X86_CPUID_STEXT_FEATURE_EDX_ARCHCAP)
180 && (fStdFeaturesEdx & X86_CPUID_FEATURE_EDX_MSR))
181 cpumCpuIdExplodeArchCapabilities(&g_CpumHostFeatures.s, true, ASMRdMsr(MSR_IA32_ARCH_CAPABILITIES));
182 }
183 }
184
185 return VINF_SUCCESS;
186}
187
188
189/**
190 * Terminate the module.
191 */
192VMMR0_INT_DECL(int) CPUMR0ModuleTerm(void)
193{
194 return VINF_SUCCESS;
195}
196
197
198/**
199 * Initializes the CPUM data in the VM structure.
200 *
201 * @param pGVM The global VM structure.
202 */
203VMMR0_INT_DECL(void) CPUMR0InitPerVMData(PGVM pGVM)
204{
205 /* Copy the ring-0 host feature set to the shared part so ring-3 can pick it up. */
206 pGVM->cpum.s.HostFeatures.s = g_CpumHostFeatures.s;
207}
208
209
210/**
211 * Check the CPUID features of this particular CPU and disable relevant features
212 * for the guest which do not exist on this CPU. We have seen systems where the
213 * X86_CPUID_FEATURE_ECX_MONITOR feature flag is only set on some host CPUs, see
214 * @bugref{5436}.
215 *
216 * @note This function might be called simultaneously on more than one CPU!
217 *
218 * @param idCpu The identifier for the CPU the function is called on.
219 * @param pvUser1 Pointer to the VM structure.
220 * @param pvUser2 Ignored.
221 */
222static DECLCALLBACK(void) cpumR0CheckCpuidLegacy(RTCPUID idCpu, void *pvUser1, void *pvUser2)
223{
224 PVMCC pVM = (PVMCC)pvUser1;
225
226 NOREF(idCpu); NOREF(pvUser2);
227 for (uint32_t i = 0; i < RT_ELEMENTS(g_aCpuidUnifyBits); i++)
228 {
229 /* Note! Cannot use cpumCpuIdGetLeaf from here because we're not
230 necessarily in the VM process context. So, we using the
231 legacy arrays as temporary storage. */
232
233 uint32_t uLeaf = g_aCpuidUnifyBits[i].uLeaf;
234 PCPUMCPUID pLegacyLeaf;
235 if (uLeaf < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmStd))
236 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmStd[uLeaf];
237 else if (uLeaf - UINT32_C(0x80000000) < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmExt))
238 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmExt[uLeaf - UINT32_C(0x80000000)];
239 else if (uLeaf - UINT32_C(0xc0000000) < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmCentaur))
240 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmCentaur[uLeaf - UINT32_C(0xc0000000)];
241 else
242 continue;
243
244 uint32_t eax, ebx, ecx, edx;
245 ASMCpuIdExSlow(uLeaf, 0, 0, 0, &eax, &ebx, &ecx, &edx);
246
247 ASMAtomicAndU32(&pLegacyLeaf->uEcx, ecx | ~g_aCpuidUnifyBits[i].uEcx);
248 ASMAtomicAndU32(&pLegacyLeaf->uEdx, edx | ~g_aCpuidUnifyBits[i].uEdx);
249 }
250}
251
252
253/**
254 * Does Ring-0 CPUM initialization.
255 *
256 * This is mainly to check that the Host CPU mode is compatible
257 * with VBox.
258 *
259 * @returns VBox status code.
260 * @param pVM The cross context VM structure.
261 */
262VMMR0_INT_DECL(int) CPUMR0InitVM(PVMCC pVM)
263{
264 LogFlow(("CPUMR0Init: %p\n", pVM));
265 AssertCompile(sizeof(pVM->aCpus[0].cpum.s.Host.abXState) >= sizeof(pVM->aCpus[0].cpum.s.Guest.abXState));
266
267 /*
268 * Check CR0 & CR4 flags.
269 */
270 uint32_t u32CR0 = ASMGetCR0();
271 if ((u32CR0 & (X86_CR0_PE | X86_CR0_PG)) != (X86_CR0_PE | X86_CR0_PG)) /* a bit paranoid perhaps.. */
272 {
273 Log(("CPUMR0Init: PE or PG not set. cr0=%#x\n", u32CR0));
274 return VERR_UNSUPPORTED_CPU_MODE;
275 }
276
277 /*
278 * Check for sysenter and syscall usage.
279 */
280 if (ASMHasCpuId())
281 {
282 /*
283 * SYSENTER/SYSEXIT
284 *
285 * Intel docs claim you should test both the flag and family, model &
286 * stepping because some Pentium Pro CPUs have the SEP cpuid flag set,
287 * but don't support it. AMD CPUs may support this feature in legacy
288 * mode, they've banned it from long mode. Since we switch to 32-bit
289 * mode when entering raw-mode context the feature would become
290 * accessible again on AMD CPUs, so we have to check regardless of
291 * host bitness.
292 */
293 uint32_t u32CpuVersion;
294 uint32_t u32Dummy;
295 uint32_t fFeatures; /* (Used further down to check for MSRs, so don't clobber.) */
296 ASMCpuId(1, &u32CpuVersion, &u32Dummy, &u32Dummy, &fFeatures);
297 uint32_t const u32Family = u32CpuVersion >> 8;
298 uint32_t const u32Model = (u32CpuVersion >> 4) & 0xF;
299 uint32_t const u32Stepping = u32CpuVersion & 0xF;
300 if ( (fFeatures & X86_CPUID_FEATURE_EDX_SEP)
301 && ( u32Family != 6 /* (> pentium pro) */
302 || u32Model >= 3
303 || u32Stepping >= 3
304 || !ASMIsIntelCpu())
305 )
306 {
307 /*
308 * Read the MSR and see if it's in use or not.
309 */
310 uint32_t u32 = ASMRdMsr_Low(MSR_IA32_SYSENTER_CS);
311 if (u32)
312 {
313 pVM->cpum.s.fHostUseFlags |= CPUM_USE_SYSENTER;
314 Log(("CPUMR0Init: host uses sysenter cs=%08x%08x\n", ASMRdMsr_High(MSR_IA32_SYSENTER_CS), u32));
315 }
316 }
317
318 /*
319 * SYSCALL/SYSRET
320 *
321 * This feature is indicated by the SEP bit returned in EDX by CPUID
322 * function 0x80000001. Intel CPUs only supports this feature in
323 * long mode. Since we're not running 64-bit guests in raw-mode there
324 * are no issues with 32-bit intel hosts.
325 */
326 uint32_t cExt = 0;
327 ASMCpuId(0x80000000, &cExt, &u32Dummy, &u32Dummy, &u32Dummy);
328 if (RTX86IsValidExtRange(cExt))
329 {
330 uint32_t fExtFeaturesEDX = ASMCpuId_EDX(0x80000001);
331 if (fExtFeaturesEDX & X86_CPUID_EXT_FEATURE_EDX_SYSCALL)
332 {
333#ifdef RT_ARCH_X86
334 if (!ASMIsIntelCpu())
335#endif
336 {
337 uint64_t fEfer = ASMRdMsr(MSR_K6_EFER);
338 if (fEfer & MSR_K6_EFER_SCE)
339 {
340 pVM->cpum.s.fHostUseFlags |= CPUM_USE_SYSCALL;
341 Log(("CPUMR0Init: host uses syscall\n"));
342 }
343 }
344 }
345 }
346
347 /*
348 * Copy MSR_IA32_ARCH_CAPABILITIES bits over into the host and guest feature
349 * structure and as well as the guest MSR.
350 * Note! We assume this happens after the CPUMR3Init is done, so CPUID bits are settled.
351 */
352 /** @todo Should add this MSR to CPUMMSRS and expose it via SUPDrv... */
353 uint32_t const cStdRange = ASMCpuId_EAX(0);
354 if ( RTX86IsValidStdRange(cStdRange)
355 && cStdRange >= 7)
356 {
357 uint32_t fEdxFeatures;
358 ASMCpuId_Idx_ECX(7, 0, &u32Dummy, &u32Dummy, &u32Dummy, &fEdxFeatures);
359 if ( (fEdxFeatures & X86_CPUID_STEXT_FEATURE_EDX_ARCHCAP)
360 && (fFeatures & X86_CPUID_FEATURE_EDX_MSR))
361 CPUMCpuIdApplyX86HostArchCapabilities(pVM, true, ASMRdMsr(MSR_IA32_ARCH_CAPABILITIES));
362 }
363
364 /*
365 * Unify/cross check some CPUID feature bits on all available CPU cores
366 * and threads. We've seen CPUs where the monitor support differed.
367 *
368 * Because the hyper heap isn't always mapped into ring-0, we cannot
369 * access it from a RTMpOnAll callback. We use the legacy CPUID arrays
370 * as temp ring-0 accessible memory instead, ASSUMING that they're all
371 * up to date when we get here.
372 */
373 RTMpOnAll(cpumR0CheckCpuidLegacy, pVM, NULL);
374
375 for (uint32_t i = 0; i < RT_ELEMENTS(g_aCpuidUnifyBits); i++)
376 {
377 bool fIgnored;
378 uint32_t uLeaf = g_aCpuidUnifyBits[i].uLeaf;
379 PCPUMCPUIDLEAF pLeaf = cpumCpuIdGetLeafEx(pVM, uLeaf, 0, &fIgnored);
380 if (pLeaf)
381 {
382 PCPUMCPUID pLegacyLeaf;
383 if (uLeaf < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmStd))
384 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmStd[uLeaf];
385 else if (uLeaf - UINT32_C(0x80000000) < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmExt))
386 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmExt[uLeaf - UINT32_C(0x80000000)];
387 else if (uLeaf - UINT32_C(0xc0000000) < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmCentaur))
388 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmCentaur[uLeaf - UINT32_C(0xc0000000)];
389 else
390 continue;
391
392 pLeaf->uEcx = pLegacyLeaf->uEcx;
393 pLeaf->uEdx = pLegacyLeaf->uEdx;
394 }
395 }
396
397 }
398
399
400 /*
401 * Check if debug registers are armed.
402 * This ASSUMES that DR7.GD is not set, or that it's handled transparently!
403 */
404 uint32_t u32DR7 = ASMGetDR7();
405 if (u32DR7 & X86_DR7_ENABLED_MASK)
406 {
407 VMCC_FOR_EACH_VMCPU_STMT(pVM, pVCpu->cpum.s.fUseFlags |= CPUM_USE_DEBUG_REGS_HOST);
408 Log(("CPUMR0Init: host uses debug registers (dr7=%x)\n", u32DR7));
409 }
410
411 return VINF_SUCCESS;
412}
413
414
415/**
416 * Trap handler for device-not-available fault (\#NM).
417 * Device not available, FP or (F)WAIT instruction.
418 *
419 * @returns VBox status code.
420 * @retval VINF_SUCCESS if the guest FPU state is loaded.
421 * @retval VINF_EM_RAW_GUEST_TRAP if it is a guest trap.
422 * @retval VINF_CPUM_HOST_CR0_MODIFIED if we modified the host CR0.
423 *
424 * @param pVM The cross context VM structure.
425 * @param pVCpu The cross context virtual CPU structure.
426 */
427VMMR0_INT_DECL(int) CPUMR0Trap07Handler(PVMCC pVM, PVMCPUCC pVCpu)
428{
429 Assert(pVM->cpum.s.HostFeatures.s.fFxSaveRstor);
430 Assert(ASMGetCR4() & X86_CR4_OSFXSR);
431
432 /* If the FPU state has already been loaded, then it's a guest trap. */
433 if (CPUMIsGuestFPUStateActive(pVCpu))
434 {
435 Assert( ((pVCpu->cpum.s.Guest.cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS)) == (X86_CR0_MP | X86_CR0_TS))
436 || ((pVCpu->cpum.s.Guest.cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS)) == (X86_CR0_MP | X86_CR0_TS | X86_CR0_EM)));
437 return VINF_EM_RAW_GUEST_TRAP;
438 }
439
440 /*
441 * There are two basic actions:
442 * 1. Save host fpu and restore guest fpu.
443 * 2. Generate guest trap.
444 *
445 * When entering the hypervisor we'll always enable MP (for proper wait
446 * trapping) and TS (for intercepting all fpu/mmx/sse stuff). The EM flag
447 * is taken from the guest OS in order to get proper SSE handling.
448 *
449 *
450 * Actions taken depending on the guest CR0 flags:
451 *
452 * 3 2 1
453 * TS | EM | MP | FPUInstr | WAIT :: VMM Action
454 * ------------------------------------------------------------------------
455 * 0 | 0 | 0 | Exec | Exec :: Clear TS & MP, Save HC, Load GC.
456 * 0 | 0 | 1 | Exec | Exec :: Clear TS, Save HC, Load GC.
457 * 0 | 1 | 0 | #NM | Exec :: Clear TS & MP, Save HC, Load GC.
458 * 0 | 1 | 1 | #NM | Exec :: Clear TS, Save HC, Load GC.
459 * 1 | 0 | 0 | #NM | Exec :: Clear MP, Save HC, Load GC. (EM is already cleared.)
460 * 1 | 0 | 1 | #NM | #NM :: Go to guest taking trap there.
461 * 1 | 1 | 0 | #NM | Exec :: Clear MP, Save HC, Load GC. (EM is already set.)
462 * 1 | 1 | 1 | #NM | #NM :: Go to guest taking trap there.
463 */
464
465 switch (pVCpu->cpum.s.Guest.cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS))
466 {
467 case X86_CR0_MP | X86_CR0_TS:
468 case X86_CR0_MP | X86_CR0_TS | X86_CR0_EM:
469 return VINF_EM_RAW_GUEST_TRAP;
470 default:
471 break;
472 }
473
474 return CPUMR0LoadGuestFPU(pVM, pVCpu);
475}
476
477
478/**
479 * Saves the host-FPU/XMM state (if necessary) and (always) loads the guest-FPU
480 * state into the CPU.
481 *
482 * @returns VINF_SUCCESS on success, host CR0 unmodified.
483 * @returns VINF_CPUM_HOST_CR0_MODIFIED on success when the host CR0 was
484 * modified and VT-x needs to update the value in the VMCS.
485 *
486 * @param pVM The cross context VM structure.
487 * @param pVCpu The cross context virtual CPU structure.
488 */
489VMMR0_INT_DECL(int) CPUMR0LoadGuestFPU(PVMCC pVM, PVMCPUCC pVCpu)
490{
491 int rc;
492 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
493 Assert(!(pVCpu->cpum.s.fUseFlags & CPUM_USED_FPU_GUEST));
494
495 /* Notify the support driver prior to loading the guest-FPU register state. */
496 SUPR0FpuBegin(VMMR0ThreadCtxHookIsEnabled(pVCpu));
497 /** @todo use return value? Currently skipping that to be on the safe side
498 * wrt. extended state (linux). */
499
500 if (!pVM->cpum.s.HostFeatures.s.fLeakyFxSR)
501 {
502 Assert(!(pVCpu->cpum.s.fUseFlags & CPUM_USED_MANUAL_XMM_RESTORE));
503 rc = cpumR0SaveHostRestoreGuestFPUState(&pVCpu->cpum.s);
504 }
505 else
506 {
507 Assert(!(pVCpu->cpum.s.fUseFlags & CPUM_USED_MANUAL_XMM_RESTORE) || (pVCpu->cpum.s.fUseFlags & CPUM_USED_FPU_HOST));
508 /** @todo r=ramshankar: Can't we used a cached value here
509 * instead of reading the MSR? host EFER doesn't usually
510 * change. */
511 uint64_t uHostEfer = ASMRdMsr(MSR_K6_EFER);
512 if (!(uHostEfer & MSR_K6_EFER_FFXSR))
513 rc = cpumR0SaveHostRestoreGuestFPUState(&pVCpu->cpum.s);
514 else
515 {
516 RTCCUINTREG const uSavedFlags = ASMIntDisableFlags();
517 pVCpu->cpum.s.fUseFlags |= CPUM_USED_MANUAL_XMM_RESTORE;
518 ASMWrMsr(MSR_K6_EFER, uHostEfer & ~MSR_K6_EFER_FFXSR);
519 rc = cpumR0SaveHostRestoreGuestFPUState(&pVCpu->cpum.s);
520 ASMWrMsr(MSR_K6_EFER, uHostEfer | MSR_K6_EFER_FFXSR);
521 ASMSetFlags(uSavedFlags);
522 }
523 }
524 Assert( (pVCpu->cpum.s.fUseFlags & (CPUM_USED_FPU_GUEST | CPUM_USED_FPU_HOST | CPUM_USED_FPU_SINCE_REM))
525 == (CPUM_USED_FPU_GUEST | CPUM_USED_FPU_HOST | CPUM_USED_FPU_SINCE_REM));
526 Assert(pVCpu->cpum.s.Guest.fUsedFpuGuest);
527 return rc;
528}
529
530
531/**
532 * Saves the guest FPU/XMM state if needed, restores the host FPU/XMM state as
533 * needed.
534 *
535 * @returns true if we saved the guest state.
536 * @param pVCpu The cross context virtual CPU structure.
537 */
538VMMR0_INT_DECL(bool) CPUMR0FpuStateMaybeSaveGuestAndRestoreHost(PVMCPUCC pVCpu)
539{
540 bool fSavedGuest;
541 Assert(pVCpu->CTX_SUFF(pVM)->cpum.s.HostFeatures.s.fFxSaveRstor);
542 Assert(ASMGetCR4() & X86_CR4_OSFXSR);
543 if (pVCpu->cpum.s.fUseFlags & (CPUM_USED_FPU_GUEST | CPUM_USED_FPU_HOST))
544 {
545 fSavedGuest = RT_BOOL(pVCpu->cpum.s.fUseFlags & CPUM_USED_FPU_GUEST);
546 Assert(fSavedGuest == pVCpu->cpum.s.Guest.fUsedFpuGuest);
547 if (!(pVCpu->cpum.s.fUseFlags & CPUM_USED_MANUAL_XMM_RESTORE))
548 cpumR0SaveGuestRestoreHostFPUState(&pVCpu->cpum.s);
549 else
550 {
551 /* Temporarily clear MSR_K6_EFER_FFXSR or else we'll be unable to
552 save/restore the XMM state with fxsave/fxrstor. */
553 uint64_t uHostEfer = ASMRdMsr(MSR_K6_EFER);
554 if (uHostEfer & MSR_K6_EFER_FFXSR)
555 {
556 RTCCUINTREG const uSavedFlags = ASMIntDisableFlags();
557 ASMWrMsr(MSR_K6_EFER, uHostEfer & ~MSR_K6_EFER_FFXSR);
558 cpumR0SaveGuestRestoreHostFPUState(&pVCpu->cpum.s);
559 ASMWrMsr(MSR_K6_EFER, uHostEfer | MSR_K6_EFER_FFXSR);
560 ASMSetFlags(uSavedFlags);
561 }
562 else
563 cpumR0SaveGuestRestoreHostFPUState(&pVCpu->cpum.s);
564 pVCpu->cpum.s.fUseFlags &= ~CPUM_USED_MANUAL_XMM_RESTORE;
565 }
566
567 /* Notify the support driver after loading the host-FPU register state. */
568 SUPR0FpuEnd(VMMR0ThreadCtxHookIsEnabled(pVCpu));
569 }
570 else
571 fSavedGuest = false;
572 AssertMsg(!( pVCpu->cpum.s.fUseFlags
573 & (CPUM_USED_FPU_GUEST | CPUM_USED_FPU_HOST | CPUM_USED_MANUAL_XMM_RESTORE)), ("%#x\n", pVCpu->cpum.s.fUseFlags));
574 Assert(!pVCpu->cpum.s.Guest.fUsedFpuGuest);
575 return fSavedGuest;
576}
577
578
579/**
580 * Saves the host debug state, setting CPUM_USED_HOST_DEBUG_STATE and loading
581 * DR7 with safe values.
582 *
583 * @returns VBox status code.
584 * @param pVCpu The cross context virtual CPU structure.
585 */
586static int cpumR0SaveHostDebugState(PVMCPUCC pVCpu)
587{
588 /*
589 * Save the host state.
590 */
591 pVCpu->cpum.s.Host.dr0 = ASMGetDR0();
592 pVCpu->cpum.s.Host.dr1 = ASMGetDR1();
593 pVCpu->cpum.s.Host.dr2 = ASMGetDR2();
594 pVCpu->cpum.s.Host.dr3 = ASMGetDR3();
595 pVCpu->cpum.s.Host.dr6 = ASMGetDR6();
596 /** @todo dr7 might already have been changed to 0x400; don't care right now as it's harmless. */
597 pVCpu->cpum.s.Host.dr7 = ASMGetDR7();
598
599 /* Preemption paranoia. */
600 ASMAtomicOrU32(&pVCpu->cpum.s.fUseFlags, CPUM_USED_DEBUG_REGS_HOST);
601
602 /*
603 * Make sure DR7 is harmless or else we could trigger breakpoints when
604 * load guest or hypervisor DRx values later.
605 */
606 if (pVCpu->cpum.s.Host.dr7 != X86_DR7_INIT_VAL)
607 ASMSetDR7(X86_DR7_INIT_VAL);
608
609 return VINF_SUCCESS;
610}
611
612
613/**
614 * Saves the guest DRx state residing in host registers and restore the host
615 * register values.
616 *
617 * The guest DRx state is only saved if CPUMR0LoadGuestDebugState was called,
618 * since it's assumed that we're shadowing the guest DRx register values
619 * accurately when using the combined hypervisor debug register values
620 * (CPUMR0LoadHyperDebugState).
621 *
622 * @returns true if either guest or hypervisor debug registers were loaded.
623 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
624 * @param fDr6 Whether to include DR6 or not.
625 * @thread EMT(pVCpu)
626 */
627VMMR0_INT_DECL(bool) CPUMR0DebugStateMaybeSaveGuestAndRestoreHost(PVMCPUCC pVCpu, bool fDr6)
628{
629 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
630 bool const fDrXLoaded = RT_BOOL(pVCpu->cpum.s.fUseFlags & (CPUM_USED_DEBUG_REGS_GUEST | CPUM_USED_DEBUG_REGS_HYPER));
631
632 /*
633 * Do we need to save the guest DRx registered loaded into host registers?
634 * (DR7 and DR6 (if fDr6 is true) are left to the caller.)
635 */
636 if (pVCpu->cpum.s.fUseFlags & CPUM_USED_DEBUG_REGS_GUEST)
637 {
638 pVCpu->cpum.s.Guest.dr[0] = ASMGetDR0();
639 pVCpu->cpum.s.Guest.dr[1] = ASMGetDR1();
640 pVCpu->cpum.s.Guest.dr[2] = ASMGetDR2();
641 pVCpu->cpum.s.Guest.dr[3] = ASMGetDR3();
642 if (fDr6)
643 pVCpu->cpum.s.Guest.dr[6] = ASMGetDR6() | X86_DR6_RA1_MASK; /* ASSUMES no guest supprot for TSX-NI / RTM. */
644 }
645 ASMAtomicAndU32(&pVCpu->cpum.s.fUseFlags, ~(CPUM_USED_DEBUG_REGS_GUEST | CPUM_USED_DEBUG_REGS_HYPER));
646
647 /*
648 * Restore the host's debug state. DR0-3, DR6 and only then DR7!
649 */
650 if (pVCpu->cpum.s.fUseFlags & CPUM_USED_DEBUG_REGS_HOST)
651 {
652 /* A bit of paranoia first... */
653 uint64_t uCurDR7 = ASMGetDR7();
654 if (uCurDR7 != X86_DR7_INIT_VAL)
655 ASMSetDR7(X86_DR7_INIT_VAL);
656
657 ASMSetDR0(pVCpu->cpum.s.Host.dr0);
658 ASMSetDR1(pVCpu->cpum.s.Host.dr1);
659 ASMSetDR2(pVCpu->cpum.s.Host.dr2);
660 ASMSetDR3(pVCpu->cpum.s.Host.dr3);
661 /** @todo consider only updating if they differ, esp. DR6. Need to figure how
662 * expensive DRx reads are over DRx writes. */
663 ASMSetDR6(pVCpu->cpum.s.Host.dr6);
664 ASMSetDR7(pVCpu->cpum.s.Host.dr7);
665
666 ASMAtomicAndU32(&pVCpu->cpum.s.fUseFlags, ~CPUM_USED_DEBUG_REGS_HOST);
667 }
668
669 return fDrXLoaded;
670}
671
672
673/**
674 * Saves the guest DRx state if it resides host registers.
675 *
676 * This does NOT clear any use flags, so the host registers remains loaded with
677 * the guest DRx state upon return. The purpose is only to make sure the values
678 * in the CPU context structure is up to date.
679 *
680 * @returns true if the host registers contains guest values, false if not.
681 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
682 * @param fDr6 Whether to include DR6 or not.
683 * @thread EMT(pVCpu)
684 */
685VMMR0_INT_DECL(bool) CPUMR0DebugStateMaybeSaveGuest(PVMCPUCC pVCpu, bool fDr6)
686{
687 /*
688 * Do we need to save the guest DRx registered loaded into host registers?
689 * (DR7 and DR6 (if fDr6 is true) are left to the caller.)
690 */
691 if (pVCpu->cpum.s.fUseFlags & CPUM_USED_DEBUG_REGS_GUEST)
692 {
693 pVCpu->cpum.s.Guest.dr[0] = ASMGetDR0();
694 pVCpu->cpum.s.Guest.dr[1] = ASMGetDR1();
695 pVCpu->cpum.s.Guest.dr[2] = ASMGetDR2();
696 pVCpu->cpum.s.Guest.dr[3] = ASMGetDR3();
697 if (fDr6)
698 pVCpu->cpum.s.Guest.dr[6] = ASMGetDR6();
699 return true;
700 }
701 return false;
702}
703
704
705/**
706 * Lazily sync in the debug state.
707 *
708 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
709 * @param fDr6 Whether to include DR6 or not.
710 * @thread EMT(pVCpu)
711 */
712VMMR0_INT_DECL(void) CPUMR0LoadGuestDebugState(PVMCPUCC pVCpu, bool fDr6)
713{
714 /*
715 * Save the host state and disarm all host BPs.
716 */
717 cpumR0SaveHostDebugState(pVCpu);
718 Assert(ASMGetDR7() == X86_DR7_INIT_VAL);
719
720 /*
721 * Activate the guest state DR0-3.
722 * DR7 and DR6 (if fDr6 is true) are left to the caller.
723 */
724 ASMSetDR0(pVCpu->cpum.s.Guest.dr[0]);
725 ASMSetDR1(pVCpu->cpum.s.Guest.dr[1]);
726 ASMSetDR2(pVCpu->cpum.s.Guest.dr[2]);
727 ASMSetDR3(pVCpu->cpum.s.Guest.dr[3]);
728 if (fDr6)
729 ASMSetDR6(pVCpu->cpum.s.Guest.dr[6]);
730
731 ASMAtomicOrU32(&pVCpu->cpum.s.fUseFlags, CPUM_USED_DEBUG_REGS_GUEST);
732}
733
734
735/**
736 * Lazily sync in the hypervisor debug state
737 *
738 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
739 * @param fDr6 Whether to include DR6 or not.
740 * @thread EMT(pVCpu)
741 */
742VMMR0_INT_DECL(void) CPUMR0LoadHyperDebugState(PVMCPUCC pVCpu, bool fDr6)
743{
744 /*
745 * Save the host state and disarm all host BPs.
746 */
747 cpumR0SaveHostDebugState(pVCpu);
748 Assert(ASMGetDR7() == X86_DR7_INIT_VAL);
749
750 /*
751 * Make sure the hypervisor values are up to date.
752 */
753 CPUMRecalcHyperDRx(pVCpu, UINT8_MAX /* no loading, please */);
754
755 /*
756 * Activate the guest state DR0-3.
757 * DR7 and DR6 (if fDr6 is true) are left to the caller.
758 */
759 ASMSetDR0(pVCpu->cpum.s.Hyper.dr[0]);
760 ASMSetDR1(pVCpu->cpum.s.Hyper.dr[1]);
761 ASMSetDR2(pVCpu->cpum.s.Hyper.dr[2]);
762 ASMSetDR3(pVCpu->cpum.s.Hyper.dr[3]);
763 if (fDr6)
764 ASMSetDR6(X86_DR6_INIT_VAL);
765
766 ASMAtomicOrU32(&pVCpu->cpum.s.fUseFlags, CPUM_USED_DEBUG_REGS_HYPER);
767}
768
Note: See TracBrowser for help on using the repository browser.

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