VirtualBox

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

Last change on this file since 107882 was 107854, checked in by vboxsync, 4 weeks ago

x86.h,VMM: More AMD CPUID bits; addressed some old todos related to these; fixed bugs in svn & vmx world switcher (sanity checks, ++). jiraref:VBP-947 bugref:10738

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