VirtualBox

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

Last change on this file since 58591 was 58123, checked in by vboxsync, 9 years ago

VMM: Made @param pVCpu more uniform and to the point.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 35.3 KB
Line 
1/* $Id: CPUMR0.cpp 58123 2015-10-08 18:09:45Z vboxsync $ */
2/** @file
3 * CPUM - Host Context Ring 0.
4 */
5
6/*
7 * Copyright (C) 2006-2015 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/vm.h>
26#include <VBox/err.h>
27#include <VBox/log.h>
28#include <VBox/vmm/hm.h>
29#include <iprt/assert.h>
30#include <iprt/asm-amd64-x86.h>
31#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
32# include <iprt/mem.h>
33# include <iprt/memobj.h>
34# include <VBox/apic.h>
35#endif
36#include <iprt/x86.h>
37
38
39/*********************************************************************************************************************************
40* Structures and Typedefs *
41*********************************************************************************************************************************/
42#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
43/**
44 * Local APIC mappings.
45 */
46typedef struct CPUMHOSTLAPIC
47{
48 /** Indicates that the entry is in use and have valid data. */
49 bool fEnabled;
50 /** Whether it's operating in X2APIC mode (EXTD). */
51 bool fX2Apic;
52 /** The APIC version number. */
53 uint32_t uVersion;
54 /** The physical address of the APIC registers. */
55 RTHCPHYS PhysBase;
56 /** The memory object entering the physical address. */
57 RTR0MEMOBJ hMemObj;
58 /** The mapping object for hMemObj. */
59 RTR0MEMOBJ hMapObj;
60 /** The mapping address APIC registers.
61 * @remarks Different CPUs may use the same physical address to map their
62 * APICs, so this pointer is only valid when on the CPU owning the
63 * APIC. */
64 void *pv;
65} CPUMHOSTLAPIC;
66#endif
67
68
69/*********************************************************************************************************************************
70* Global Variables *
71*********************************************************************************************************************************/
72#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
73static CPUMHOSTLAPIC g_aLApics[RTCPUSET_MAX_CPUS];
74#endif
75
76/**
77 * CPUID bits to unify among all cores.
78 */
79static struct
80{
81 uint32_t uLeaf; /**< Leaf to check. */
82 uint32_t uEcx; /**< which bits in ecx to unify between CPUs. */
83 uint32_t uEdx; /**< which bits in edx to unify between CPUs. */
84}
85const g_aCpuidUnifyBits[] =
86{
87 {
88 0x00000001,
89 X86_CPUID_FEATURE_ECX_CX16 | X86_CPUID_FEATURE_ECX_MONITOR,
90 X86_CPUID_FEATURE_EDX_CX8
91 }
92};
93
94
95
96/*********************************************************************************************************************************
97* Internal Functions *
98*********************************************************************************************************************************/
99#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
100static int cpumR0MapLocalApics(void);
101static void cpumR0UnmapLocalApics(void);
102#endif
103static int cpumR0SaveHostDebugState(PVMCPU pVCpu);
104
105
106/**
107 * Does the Ring-0 CPU initialization once during module load.
108 * XXX Host-CPU hot-plugging?
109 */
110VMMR0_INT_DECL(int) CPUMR0ModuleInit(void)
111{
112 int rc = VINF_SUCCESS;
113#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
114 rc = cpumR0MapLocalApics();
115#endif
116 return rc;
117}
118
119
120/**
121 * Terminate the module.
122 */
123VMMR0_INT_DECL(int) CPUMR0ModuleTerm(void)
124{
125#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
126 cpumR0UnmapLocalApics();
127#endif
128 return VINF_SUCCESS;
129}
130
131
132/**
133 *
134 *
135 * Check the CPUID features of this particular CPU and disable relevant features
136 * for the guest which do not exist on this CPU. We have seen systems where the
137 * X86_CPUID_FEATURE_ECX_MONITOR feature flag is only set on some host CPUs, see
138 * @bugref{5436}.
139 *
140 * @note This function might be called simultaneously on more than one CPU!
141 *
142 * @param idCpu The identifier for the CPU the function is called on.
143 * @param pvUser1 Pointer to the VM structure.
144 * @param pvUser2 Ignored.
145 */
146static DECLCALLBACK(void) cpumR0CheckCpuid(RTCPUID idCpu, void *pvUser1, void *pvUser2)
147{
148 PVM pVM = (PVM)pvUser1;
149
150 NOREF(idCpu); NOREF(pvUser2);
151 for (uint32_t i = 0; i < RT_ELEMENTS(g_aCpuidUnifyBits); i++)
152 {
153 /* Note! Cannot use cpumCpuIdGetLeaf from here because we're not
154 necessarily in the VM process context. So, we using the
155 legacy arrays as temporary storage. */
156
157 uint32_t uLeaf = g_aCpuidUnifyBits[i].uLeaf;
158 PCPUMCPUID pLegacyLeaf;
159 if (uLeaf < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmStd))
160 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmStd[uLeaf];
161 else if (uLeaf - UINT32_C(0x80000000) < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmExt))
162 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmExt[uLeaf - UINT32_C(0x80000000)];
163 else if (uLeaf - UINT32_C(0xc0000000) < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmCentaur))
164 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmCentaur[uLeaf - UINT32_C(0xc0000000)];
165 else
166 continue;
167
168 uint32_t eax, ebx, ecx, edx;
169 ASMCpuIdExSlow(uLeaf, 0, 0, 0, &eax, &ebx, &ecx, &edx);
170
171 ASMAtomicAndU32(&pLegacyLeaf->uEcx, ecx | ~g_aCpuidUnifyBits[i].uEcx);
172 ASMAtomicAndU32(&pLegacyLeaf->uEdx, edx | ~g_aCpuidUnifyBits[i].uEdx);
173 }
174}
175
176
177/**
178 * Does Ring-0 CPUM initialization.
179 *
180 * This is mainly to check that the Host CPU mode is compatible
181 * with VBox.
182 *
183 * @returns VBox status code.
184 * @param pVM The cross context VM structure.
185 */
186VMMR0_INT_DECL(int) CPUMR0InitVM(PVM pVM)
187{
188 LogFlow(("CPUMR0Init: %p\n", pVM));
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;
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 (ASMIsValidExtRange(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 * Unify/cross check some CPUID feature bits on all available CPU cores
272 * and threads. We've seen CPUs where the monitor support differed.
273 *
274 * Because the hyper heap isn't always mapped into ring-0, we cannot
275 * access it from a RTMpOnAll callback. We use the legacy CPUID arrays
276 * as temp ring-0 accessible memory instead, ASSUMING that they're all
277 * up to date when we get here.
278 */
279 RTMpOnAll(cpumR0CheckCpuid, pVM, NULL);
280
281 for (uint32_t i = 0; i < RT_ELEMENTS(g_aCpuidUnifyBits); i++)
282 {
283 bool fIgnored;
284 uint32_t uLeaf = g_aCpuidUnifyBits[i].uLeaf;
285 PCPUMCPUIDLEAF pLeaf = cpumCpuIdGetLeafEx(pVM, uLeaf, 0, &fIgnored);
286 if (pLeaf)
287 {
288 PCPUMCPUID pLegacyLeaf;
289 if (uLeaf < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmStd))
290 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmStd[uLeaf];
291 else if (uLeaf - UINT32_C(0x80000000) < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmExt))
292 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmExt[uLeaf - UINT32_C(0x80000000)];
293 else if (uLeaf - UINT32_C(0xc0000000) < RT_ELEMENTS(pVM->cpum.s.aGuestCpuIdPatmCentaur))
294 pLegacyLeaf = &pVM->cpum.s.aGuestCpuIdPatmCentaur[uLeaf - UINT32_C(0xc0000000)];
295 else
296 continue;
297
298 pLeaf->uEcx = pLegacyLeaf->uEcx;
299 pLeaf->uEdx = pLegacyLeaf->uEdx;
300 }
301 }
302
303 }
304
305
306 /*
307 * Check if debug registers are armed.
308 * This ASSUMES that DR7.GD is not set, or that it's handled transparently!
309 */
310 uint32_t u32DR7 = ASMGetDR7();
311 if (u32DR7 & X86_DR7_ENABLED_MASK)
312 {
313 for (VMCPUID i = 0; i < pVM->cCpus; i++)
314 pVM->aCpus[i].cpum.s.fUseFlags |= CPUM_USE_DEBUG_REGS_HOST;
315 Log(("CPUMR0Init: host uses debug registers (dr7=%x)\n", u32DR7));
316 }
317
318 return VINF_SUCCESS;
319}
320
321
322/**
323 * Trap handler for device-not-available fault (\#NM).
324 * Device not available, FP or (F)WAIT instruction.
325 *
326 * @returns VBox status code.
327 * @retval VINF_SUCCESS if the guest FPU state is loaded.
328 * @retval VINF_EM_RAW_GUEST_TRAP if it is a guest trap.
329 *
330 * @param pVM The cross context VM structure.
331 * @param pVCpu The cross context virtual CPU structure.
332 * @param pCtx Pointer to the guest-CPU context.
333 */
334VMMR0_INT_DECL(int) CPUMR0Trap07Handler(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
335{
336 Assert(pVM->cpum.s.HostFeatures.fFxSaveRstor);
337 Assert(ASMGetCR4() & X86_CR4_OSFXSR);
338
339 /* If the FPU state has already been loaded, then it's a guest trap. */
340 if (CPUMIsGuestFPUStateActive(pVCpu))
341 {
342 Assert( ((pCtx->cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS)) == (X86_CR0_MP | X86_CR0_TS))
343 || ((pCtx->cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS)) == (X86_CR0_MP | X86_CR0_TS | X86_CR0_EM)));
344 return VINF_EM_RAW_GUEST_TRAP;
345 }
346
347 /*
348 * There are two basic actions:
349 * 1. Save host fpu and restore guest fpu.
350 * 2. Generate guest trap.
351 *
352 * When entering the hypervisor we'll always enable MP (for proper wait
353 * trapping) and TS (for intercepting all fpu/mmx/sse stuff). The EM flag
354 * is taken from the guest OS in order to get proper SSE handling.
355 *
356 *
357 * Actions taken depending on the guest CR0 flags:
358 *
359 * 3 2 1
360 * TS | EM | MP | FPUInstr | WAIT :: VMM Action
361 * ------------------------------------------------------------------------
362 * 0 | 0 | 0 | Exec | Exec :: Clear TS & MP, Save HC, Load GC.
363 * 0 | 0 | 1 | Exec | Exec :: Clear TS, Save HC, Load GC.
364 * 0 | 1 | 0 | #NM | Exec :: Clear TS & MP, Save HC, Load GC.
365 * 0 | 1 | 1 | #NM | Exec :: Clear TS, Save HC, Load GC.
366 * 1 | 0 | 0 | #NM | Exec :: Clear MP, Save HC, Load GC. (EM is already cleared.)
367 * 1 | 0 | 1 | #NM | #NM :: Go to guest taking trap there.
368 * 1 | 1 | 0 | #NM | Exec :: Clear MP, Save HC, Load GC. (EM is already set.)
369 * 1 | 1 | 1 | #NM | #NM :: Go to guest taking trap there.
370 */
371
372 switch (pCtx->cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS))
373 {
374 case X86_CR0_MP | X86_CR0_TS:
375 case X86_CR0_MP | X86_CR0_TS | X86_CR0_EM:
376 return VINF_EM_RAW_GUEST_TRAP;
377 default:
378 break;
379 }
380
381 return CPUMR0LoadGuestFPU(pVM, pVCpu, pCtx);
382}
383
384
385/**
386 * Saves the host-FPU/XMM state and loads the guest-FPU state into the CPU.
387 *
388 * @returns VBox status code.
389 *
390 * @param pVM The cross context VM structure.
391 * @param pVCpu The cross context virtual CPU structure.
392 * @param pCtx Pointer to the guest-CPU context.
393 */
394VMMR0_INT_DECL(int) CPUMR0LoadGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
395{
396 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
397#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS)
398 if (CPUMIsGuestInLongModeEx(pCtx))
399 {
400 Assert(!(pVCpu->cpum.s.fUseFlags & CPUM_SYNC_FPU_STATE));
401
402 /* Save the host state and record the fact (CPUM_USED_FPU | CPUM_USED_FPU_SINCE_REM). */
403 cpumR0SaveHostFPUState(&pVCpu->cpum.s);
404
405 /* Restore the state on entry as we need to be in 64-bit mode to access the full state. */
406 pVCpu->cpum.s.fUseFlags |= CPUM_SYNC_FPU_STATE;
407 }
408 else
409#endif
410 {
411 NOREF(pCtx);
412 Assert(!(pVCpu->cpum.s.fUseFlags & CPUM_USED_MANUAL_XMM_RESTORE));
413 /** @todo Move the FFXR handling down into
414 * cpumR0SaveHostRestoreGuestFPUState to optimize the
415 * VBOX_WITH_KERNEL_USING_XMM handling. */
416 /* Clear MSR_K6_EFER_FFXSR or else we'll be unable to save/restore the XMM state with fxsave/fxrstor. */
417 uint64_t uHostEfer = 0;
418 bool fRestoreEfer = false;
419 if (pVM->cpum.s.HostFeatures.fLeakyFxSR)
420 {
421 /** @todo r=ramshankar: Can't we used a cached value here
422 * instead of reading the MSR? host EFER doesn't usually
423 * change. */
424 uHostEfer = ASMRdMsr(MSR_K6_EFER);
425 if (uHostEfer & MSR_K6_EFER_FFXSR)
426 {
427 ASMWrMsr(MSR_K6_EFER, uHostEfer & ~MSR_K6_EFER_FFXSR);
428 pVCpu->cpum.s.fUseFlags |= CPUM_USED_MANUAL_XMM_RESTORE;
429 fRestoreEfer = true;
430 }
431 }
432
433 /* Do the job and record that we've switched FPU state. */
434 cpumR0SaveHostRestoreGuestFPUState(&pVCpu->cpum.s);
435
436 /* Restore EFER. */
437 if (fRestoreEfer)
438 ASMWrMsr(MSR_K6_EFER, uHostEfer);
439 }
440
441 Assert((pVCpu->cpum.s.fUseFlags & (CPUM_USED_FPU | CPUM_USED_FPU_SINCE_REM)) == (CPUM_USED_FPU | CPUM_USED_FPU_SINCE_REM));
442 return VINF_SUCCESS;
443}
444
445
446/**
447 * Save guest FPU/XMM state
448 *
449 * @returns VBox status code.
450 * @param pVM The cross context VM structure.
451 * @param pVCpu The cross context virtual CPU structure.
452 * @param pCtx Pointer to the guest CPU context.
453 */
454VMMR0_INT_DECL(int) CPUMR0SaveGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
455{
456 Assert(pVM->cpum.s.HostFeatures.fFxSaveRstor);
457 Assert(ASMGetCR4() & X86_CR4_OSFXSR);
458 AssertReturn((pVCpu->cpum.s.fUseFlags & CPUM_USED_FPU), VINF_SUCCESS);
459 NOREF(pVM); NOREF(pCtx);
460
461#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS)
462 if (CPUMIsGuestInLongModeEx(pCtx))
463 {
464 if (!(pVCpu->cpum.s.fUseFlags & CPUM_SYNC_FPU_STATE))
465 {
466 HMR0SaveFPUState(pVM, pVCpu, pCtx);
467 cpumR0RestoreHostFPUState(&pVCpu->cpum.s);
468 }
469 /* else nothing to do; we didn't perform a world switch */
470 }
471 else
472#endif
473 {
474#ifdef VBOX_WITH_KERNEL_USING_XMM
475 /*
476 * We've already saved the XMM registers in the assembly wrapper, so
477 * we have to save them before saving the entire FPU state and put them
478 * back afterwards.
479 */
480 /** @todo This could be skipped if MSR_K6_EFER_FFXSR is set, but
481 * I'm not able to test such an optimization tonight.
482 * We could just all this in assembly. */
483 uint128_t aGuestXmmRegs[16];
484 memcpy(&aGuestXmmRegs[0], &pVCpu->cpum.s.Guest.CTX_SUFF(pXState)->x87.aXMM[0], sizeof(aGuestXmmRegs));
485#endif
486
487 /* Clear MSR_K6_EFER_FFXSR or else we'll be unable to save/restore the XMM state with fxsave/fxrstor. */
488 uint64_t uHostEfer = 0;
489 bool fRestoreEfer = false;
490 if (pVCpu->cpum.s.fUseFlags & CPUM_USED_MANUAL_XMM_RESTORE)
491 {
492 uHostEfer = ASMRdMsr(MSR_K6_EFER);
493 if (uHostEfer & MSR_K6_EFER_FFXSR)
494 {
495 ASMWrMsr(MSR_K6_EFER, uHostEfer & ~MSR_K6_EFER_FFXSR);
496 fRestoreEfer = true;
497 }
498 }
499
500 cpumR0SaveGuestRestoreHostFPUState(&pVCpu->cpum.s);
501
502 /* Restore EFER MSR */
503 if (fRestoreEfer)
504 ASMWrMsr(MSR_K6_EFER, uHostEfer | MSR_K6_EFER_FFXSR);
505
506#ifdef VBOX_WITH_KERNEL_USING_XMM
507 memcpy(&pVCpu->cpum.s.Guest.CTX_SUFF(pXState)->x87.aXMM[0], &aGuestXmmRegs[0], sizeof(aGuestXmmRegs));
508#endif
509 }
510
511 pVCpu->cpum.s.fUseFlags &= ~(CPUM_USED_FPU | CPUM_SYNC_FPU_STATE | CPUM_USED_MANUAL_XMM_RESTORE);
512 return VINF_SUCCESS;
513}
514
515
516/**
517 * Saves the host debug state, setting CPUM_USED_HOST_DEBUG_STATE and loading
518 * DR7 with safe values.
519 *
520 * @returns VBox status code.
521 * @param pVCpu The cross context virtual CPU structure.
522 */
523static int cpumR0SaveHostDebugState(PVMCPU pVCpu)
524{
525 /*
526 * Save the host state.
527 */
528 pVCpu->cpum.s.Host.dr0 = ASMGetDR0();
529 pVCpu->cpum.s.Host.dr1 = ASMGetDR1();
530 pVCpu->cpum.s.Host.dr2 = ASMGetDR2();
531 pVCpu->cpum.s.Host.dr3 = ASMGetDR3();
532 pVCpu->cpum.s.Host.dr6 = ASMGetDR6();
533 /** @todo dr7 might already have been changed to 0x400; don't care right now as it's harmless. */
534 pVCpu->cpum.s.Host.dr7 = ASMGetDR7();
535
536 /* Preemption paranoia. */
537 ASMAtomicOrU32(&pVCpu->cpum.s.fUseFlags, CPUM_USED_DEBUG_REGS_HOST);
538
539 /*
540 * Make sure DR7 is harmless or else we could trigger breakpoints when
541 * load guest or hypervisor DRx values later.
542 */
543 if (pVCpu->cpum.s.Host.dr7 != X86_DR7_INIT_VAL)
544 ASMSetDR7(X86_DR7_INIT_VAL);
545
546 return VINF_SUCCESS;
547}
548
549
550/**
551 * Saves the guest DRx state residing in host registers and restore the host
552 * register values.
553 *
554 * The guest DRx state is only saved if CPUMR0LoadGuestDebugState was called,
555 * since it's assumed that we're shadowing the guest DRx register values
556 * accurately when using the combined hypervisor debug register values
557 * (CPUMR0LoadHyperDebugState).
558 *
559 * @returns true if either guest or hypervisor debug registers were loaded.
560 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
561 * @param fDr6 Whether to include DR6 or not.
562 * @thread EMT(pVCpu)
563 */
564VMMR0_INT_DECL(bool) CPUMR0DebugStateMaybeSaveGuestAndRestoreHost(PVMCPU pVCpu, bool fDr6)
565{
566 Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
567 bool const fDrXLoaded = RT_BOOL(pVCpu->cpum.s.fUseFlags & (CPUM_USED_DEBUG_REGS_GUEST | CPUM_USED_DEBUG_REGS_HYPER));
568
569 /*
570 * Do we need to save the guest DRx registered loaded into host registers?
571 * (DR7 and DR6 (if fDr6 is true) are left to the caller.)
572 */
573 if (pVCpu->cpum.s.fUseFlags & CPUM_USED_DEBUG_REGS_GUEST)
574 {
575#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS)
576 if (CPUMIsGuestInLongModeEx(&pVCpu->cpum.s.Guest))
577 {
578 uint64_t uDr6 = pVCpu->cpum.s.Guest.dr[6];
579 HMR0SaveDebugState(pVCpu->CTX_SUFF(pVM), pVCpu, &pVCpu->cpum.s.Guest);
580 if (!fDr6)
581 pVCpu->cpum.s.Guest.dr[6] = uDr6;
582 }
583 else
584#endif
585 {
586 pVCpu->cpum.s.Guest.dr[0] = ASMGetDR0();
587 pVCpu->cpum.s.Guest.dr[1] = ASMGetDR1();
588 pVCpu->cpum.s.Guest.dr[2] = ASMGetDR2();
589 pVCpu->cpum.s.Guest.dr[3] = ASMGetDR3();
590 if (fDr6)
591 pVCpu->cpum.s.Guest.dr[6] = ASMGetDR6();
592 }
593 }
594 ASMAtomicAndU32(&pVCpu->cpum.s.fUseFlags, ~( CPUM_USED_DEBUG_REGS_GUEST | CPUM_USED_DEBUG_REGS_HYPER
595 | CPUM_SYNC_DEBUG_REGS_GUEST | CPUM_SYNC_DEBUG_REGS_HYPER));
596
597 /*
598 * Restore the host's debug state. DR0-3, DR6 and only then DR7!
599 */
600 if (pVCpu->cpum.s.fUseFlags & CPUM_USED_DEBUG_REGS_HOST)
601 {
602 /* A bit of paranoia first... */
603 uint64_t uCurDR7 = ASMGetDR7();
604 if (uCurDR7 != X86_DR7_INIT_VAL)
605 ASMSetDR7(X86_DR7_INIT_VAL);
606
607 ASMSetDR0(pVCpu->cpum.s.Host.dr0);
608 ASMSetDR1(pVCpu->cpum.s.Host.dr1);
609 ASMSetDR2(pVCpu->cpum.s.Host.dr2);
610 ASMSetDR3(pVCpu->cpum.s.Host.dr3);
611 /** @todo consider only updating if they differ, esp. DR6. Need to figure how
612 * expensive DRx reads are over DRx writes. */
613 ASMSetDR6(pVCpu->cpum.s.Host.dr6);
614 ASMSetDR7(pVCpu->cpum.s.Host.dr7);
615
616 ASMAtomicAndU32(&pVCpu->cpum.s.fUseFlags, ~CPUM_USED_DEBUG_REGS_HOST);
617 }
618
619 return fDrXLoaded;
620}
621
622
623/**
624 * Saves the guest DRx state if it resides host registers.
625 *
626 * This does NOT clear any use flags, so the host registers remains loaded with
627 * the guest DRx state upon return. The purpose is only to make sure the values
628 * in the CPU context structure is up to date.
629 *
630 * @returns true if the host registers contains guest values, false if not.
631 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
632 * @param fDr6 Whether to include DR6 or not.
633 * @thread EMT(pVCpu)
634 */
635VMMR0_INT_DECL(bool) CPUMR0DebugStateMaybeSaveGuest(PVMCPU pVCpu, bool fDr6)
636{
637 /*
638 * Do we need to save the guest DRx registered loaded into host registers?
639 * (DR7 and DR6 (if fDr6 is true) are left to the caller.)
640 */
641 if (pVCpu->cpum.s.fUseFlags & CPUM_USED_DEBUG_REGS_GUEST)
642 {
643#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS)
644 if (CPUMIsGuestInLongModeEx(&pVCpu->cpum.s.Guest))
645 {
646 uint64_t uDr6 = pVCpu->cpum.s.Guest.dr[6];
647 HMR0SaveDebugState(pVCpu->CTX_SUFF(pVM), pVCpu, &pVCpu->cpum.s.Guest);
648 if (!fDr6)
649 pVCpu->cpum.s.Guest.dr[6] = uDr6;
650 }
651 else
652#endif
653 {
654 pVCpu->cpum.s.Guest.dr[0] = ASMGetDR0();
655 pVCpu->cpum.s.Guest.dr[1] = ASMGetDR1();
656 pVCpu->cpum.s.Guest.dr[2] = ASMGetDR2();
657 pVCpu->cpum.s.Guest.dr[3] = ASMGetDR3();
658 if (fDr6)
659 pVCpu->cpum.s.Guest.dr[6] = ASMGetDR6();
660 }
661 return true;
662 }
663 return false;
664}
665
666
667/**
668 * Lazily sync in the debug state.
669 *
670 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
671 * @param fDr6 Whether to include DR6 or not.
672 * @thread EMT(pVCpu)
673 */
674VMMR0_INT_DECL(void) CPUMR0LoadGuestDebugState(PVMCPU pVCpu, bool fDr6)
675{
676 /*
677 * Save the host state and disarm all host BPs.
678 */
679 cpumR0SaveHostDebugState(pVCpu);
680 Assert(ASMGetDR7() == X86_DR7_INIT_VAL);
681
682 /*
683 * Activate the guest state DR0-3.
684 * DR7 and DR6 (if fDr6 is true) are left to the caller.
685 */
686#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS)
687 if (CPUMIsGuestInLongModeEx(&pVCpu->cpum.s.Guest))
688 ASMAtomicOrU32(&pVCpu->cpum.s.fUseFlags, CPUM_SYNC_DEBUG_REGS_GUEST); /* Postpone it to the world switch. */
689 else
690#endif
691 {
692 ASMSetDR0(pVCpu->cpum.s.Guest.dr[0]);
693 ASMSetDR1(pVCpu->cpum.s.Guest.dr[1]);
694 ASMSetDR2(pVCpu->cpum.s.Guest.dr[2]);
695 ASMSetDR3(pVCpu->cpum.s.Guest.dr[3]);
696 if (fDr6)
697 ASMSetDR6(pVCpu->cpum.s.Guest.dr[6]);
698
699 ASMAtomicOrU32(&pVCpu->cpum.s.fUseFlags, CPUM_USED_DEBUG_REGS_GUEST);
700 }
701}
702
703
704/**
705 * Lazily sync in the hypervisor debug state
706 *
707 * @returns VBox status code.
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) CPUMR0LoadHyperDebugState(PVMCPU 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 * Make sure the hypervisor values are up to date.
722 */
723 CPUMRecalcHyperDRx(pVCpu, UINT8_MAX /* no loading, please */, true);
724
725 /*
726 * Activate the guest state DR0-3.
727 * DR7 and DR6 (if fDr6 is true) are left to the caller.
728 */
729#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS)
730 if (CPUMIsGuestInLongModeEx(&pVCpu->cpum.s.Guest))
731 ASMAtomicOrU32(&pVCpu->cpum.s.fUseFlags, CPUM_SYNC_DEBUG_REGS_HYPER); /* Postpone it. */
732 else
733#endif
734 {
735 ASMSetDR0(pVCpu->cpum.s.Hyper.dr[0]);
736 ASMSetDR1(pVCpu->cpum.s.Hyper.dr[1]);
737 ASMSetDR2(pVCpu->cpum.s.Hyper.dr[2]);
738 ASMSetDR3(pVCpu->cpum.s.Hyper.dr[3]);
739 if (fDr6)
740 ASMSetDR6(X86_DR6_INIT_VAL);
741
742 ASMAtomicOrU32(&pVCpu->cpum.s.fUseFlags, CPUM_USED_DEBUG_REGS_HYPER);
743 }
744}
745
746#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
747
748/**
749 * Per-CPU callback that probes the CPU for APIC support.
750 *
751 * @param idCpu The identifier for the CPU the function is called on.
752 * @param pvUser1 Ignored.
753 * @param pvUser2 Ignored.
754 */
755static DECLCALLBACK(void) cpumR0MapLocalApicCpuProber(RTCPUID idCpu, void *pvUser1, void *pvUser2)
756{
757 NOREF(pvUser1); NOREF(pvUser2);
758 int iCpu = RTMpCpuIdToSetIndex(idCpu);
759 AssertReturnVoid(iCpu >= 0 && (unsigned)iCpu < RT_ELEMENTS(g_aLApics));
760
761 /*
762 * Check for APIC support.
763 */
764 uint32_t uMaxLeaf, u32EBX, u32ECX, u32EDX;
765 ASMCpuId(0, &uMaxLeaf, &u32EBX, &u32ECX, &u32EDX);
766 if ( ( ASMIsIntelCpuEx(u32EBX, u32ECX, u32EDX)
767 || ASMIsAmdCpuEx(u32EBX, u32ECX, u32EDX)
768 || ASMIsViaCentaurCpuEx(u32EBX, u32ECX, u32EDX))
769 && ASMIsValidStdRange(uMaxLeaf))
770 {
771 uint32_t uDummy;
772 ASMCpuId(1, &uDummy, &u32EBX, &u32ECX, &u32EDX);
773 if ( (u32EDX & X86_CPUID_FEATURE_EDX_APIC)
774 && (u32EDX & X86_CPUID_FEATURE_EDX_MSR))
775 {
776 /*
777 * Safe to access the MSR. Read it and calc the BASE (a little complicated).
778 */
779 uint64_t u64ApicBase = ASMRdMsr(MSR_IA32_APICBASE);
780 uint64_t u64Mask = MSR_IA32_APICBASE_BASE_MIN;
781
782 /* see Intel Manual: Local APIC Status and Location: MAXPHYADDR default is bit 36 */
783 uint32_t uMaxExtLeaf;
784 ASMCpuId(0x80000000, &uMaxExtLeaf, &u32EBX, &u32ECX, &u32EDX);
785 if ( uMaxExtLeaf >= UINT32_C(0x80000008)
786 && ASMIsValidExtRange(uMaxExtLeaf))
787 {
788 uint32_t u32PhysBits;
789 ASMCpuId(0x80000008, &u32PhysBits, &u32EBX, &u32ECX, &u32EDX);
790 u32PhysBits &= 0xff;
791 u64Mask = ((UINT64_C(1) << u32PhysBits) - 1) & UINT64_C(0xfffffffffffff000);
792 }
793
794 AssertCompile(sizeof(g_aLApics[iCpu].PhysBase) == sizeof(u64ApicBase));
795 g_aLApics[iCpu].PhysBase = u64ApicBase & u64Mask;
796 g_aLApics[iCpu].fEnabled = RT_BOOL(u64ApicBase & MSR_IA32_APICBASE_EN);
797 g_aLApics[iCpu].fX2Apic = (u64ApicBase & (MSR_IA32_APICBASE_EXTD | MSR_IA32_APICBASE_EN))
798 == (MSR_IA32_APICBASE_EXTD | MSR_IA32_APICBASE_EN);
799 }
800 }
801}
802
803
804
805/**
806 * Per-CPU callback that verifies our APIC expectations.
807 *
808 * @param idCpu The identifier for the CPU the function is called on.
809 * @param pvUser1 Ignored.
810 * @param pvUser2 Ignored.
811 */
812static DECLCALLBACK(void) cpumR0MapLocalApicCpuChecker(RTCPUID idCpu, void *pvUser1, void *pvUser2)
813{
814 NOREF(pvUser1); NOREF(pvUser2);
815
816 int iCpu = RTMpCpuIdToSetIndex(idCpu);
817 AssertReturnVoid(iCpu >= 0 && (unsigned)iCpu < RT_ELEMENTS(g_aLApics));
818 if (!g_aLApics[iCpu].fEnabled)
819 return;
820
821 /*
822 * 0x0X 82489 external APIC
823 * 0x1X Local APIC
824 * 0x2X..0xFF reserved
825 */
826 uint32_t uApicVersion;
827 if (g_aLApics[iCpu].fX2Apic)
828 uApicVersion = ApicX2RegRead32(APIC_REG_VERSION);
829 else
830 uApicVersion = ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_VERSION);
831 if ((APIC_REG_VERSION_GET_VER(uApicVersion) & 0xF0) == 0x10)
832 {
833 g_aLApics[iCpu].uVersion = uApicVersion;
834
835#if 0 /* enable if you need it. */
836 if (g_aLApics[iCpu].fX2Apic)
837 SUPR0Printf("CPUM: X2APIC %02u - ver %#010x, lint0=%#07x lint1=%#07x pc=%#07x thmr=%#07x cmci=%#07x\n",
838 iCpu, uApicVersion,
839 ApicX2RegRead32(APIC_REG_LVT_LINT0), ApicX2RegRead32(APIC_REG_LVT_LINT1),
840 ApicX2RegRead32(APIC_REG_LVT_PC), ApicX2RegRead32(APIC_REG_LVT_THMR),
841 ApicX2RegRead32(APIC_REG_LVT_CMCI));
842 else
843 {
844 SUPR0Printf("CPUM: APIC %02u at %RGp (mapped at %p) - ver %#010x, lint0=%#07x lint1=%#07x pc=%#07x thmr=%#07x cmci=%#07x\n",
845 iCpu, g_aLApics[iCpu].PhysBase, g_aLApics[iCpu].pv, uApicVersion,
846 ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_LVT_LINT0), ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_LVT_LINT1),
847 ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_LVT_PC), ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_LVT_THMR),
848 ApicRegRead(g_aLApics[iCpu].pv, APIC_REG_LVT_CMCI));
849 if (uApicVersion & 0x80000000)
850 {
851 uint32_t uExtFeatures = ApicRegRead(g_aLApics[iCpu].pv, 0x400);
852 uint32_t cEiLvt = (uExtFeatures >> 16) & 0xff;
853 SUPR0Printf("CPUM: APIC %02u: ExtSpace available. extfeat=%08x eilvt[0..3]=%08x %08x %08x %08x\n",
854 iCpu,
855 ApicRegRead(g_aLApics[iCpu].pv, 0x400),
856 cEiLvt >= 1 ? ApicRegRead(g_aLApics[iCpu].pv, 0x500) : 0,
857 cEiLvt >= 2 ? ApicRegRead(g_aLApics[iCpu].pv, 0x510) : 0,
858 cEiLvt >= 3 ? ApicRegRead(g_aLApics[iCpu].pv, 0x520) : 0,
859 cEiLvt >= 4 ? ApicRegRead(g_aLApics[iCpu].pv, 0x530) : 0);
860 }
861 }
862#endif
863 }
864 else
865 {
866 g_aLApics[iCpu].fEnabled = false;
867 g_aLApics[iCpu].fX2Apic = false;
868 SUPR0Printf("VBox/CPUM: Unsupported APIC version %#x (iCpu=%d)\n", uApicVersion, iCpu);
869 }
870}
871
872
873/**
874 * Map the MMIO page of each local APIC in the system.
875 */
876static int cpumR0MapLocalApics(void)
877{
878 /*
879 * Check that we'll always stay within the array bounds.
880 */
881 if (RTMpGetArraySize() > RT_ELEMENTS(g_aLApics))
882 {
883 LogRel(("CPUM: Too many real CPUs/cores/threads - %u, max %u\n", RTMpGetArraySize(), RT_ELEMENTS(g_aLApics)));
884 return VERR_TOO_MANY_CPUS;
885 }
886
887 /*
888 * Create mappings for all online CPUs we think have legacy APICs.
889 */
890 int rc = RTMpOnAll(cpumR0MapLocalApicCpuProber, NULL, NULL);
891
892 for (unsigned iCpu = 0; RT_SUCCESS(rc) && iCpu < RT_ELEMENTS(g_aLApics); iCpu++)
893 {
894 if (g_aLApics[iCpu].fEnabled && !g_aLApics[iCpu].fX2Apic)
895 {
896 rc = RTR0MemObjEnterPhys(&g_aLApics[iCpu].hMemObj, g_aLApics[iCpu].PhysBase,
897 PAGE_SIZE, RTMEM_CACHE_POLICY_MMIO);
898 if (RT_SUCCESS(rc))
899 {
900 rc = RTR0MemObjMapKernel(&g_aLApics[iCpu].hMapObj, g_aLApics[iCpu].hMemObj, (void *)-1,
901 PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
902 if (RT_SUCCESS(rc))
903 {
904 g_aLApics[iCpu].pv = RTR0MemObjAddress(g_aLApics[iCpu].hMapObj);
905 continue;
906 }
907 RTR0MemObjFree(g_aLApics[iCpu].hMemObj, true /* fFreeMappings */);
908 }
909 g_aLApics[iCpu].fEnabled = false;
910 }
911 g_aLApics[iCpu].pv = NULL;
912 }
913
914 /*
915 * Check the APICs.
916 */
917 if (RT_SUCCESS(rc))
918 rc = RTMpOnAll(cpumR0MapLocalApicCpuChecker, NULL, NULL);
919
920 if (RT_FAILURE(rc))
921 {
922 cpumR0UnmapLocalApics();
923 return rc;
924 }
925
926#ifdef LOG_ENABLED
927 /*
928 * Log the result (pretty useless, requires enabling CPUM in VBoxDrv
929 * and !VBOX_WITH_R0_LOGGING).
930 */
931 if (LogIsEnabled())
932 {
933 uint32_t cEnabled = 0;
934 uint32_t cX2Apics = 0;
935 for (unsigned iCpu = 0; iCpu < RT_ELEMENTS(g_aLApics); iCpu++)
936 if (g_aLApics[iCpu].fEnabled)
937 {
938 cEnabled++;
939 cX2Apics += g_aLApics[iCpu].fX2Apic;
940 }
941 Log(("CPUM: %u APICs, %u X2APICs\n", cEnabled, cX2Apics));
942 }
943#endif
944
945 return VINF_SUCCESS;
946}
947
948
949/**
950 * Unmap the Local APIC of all host CPUs.
951 */
952static void cpumR0UnmapLocalApics(void)
953{
954 for (unsigned iCpu = RT_ELEMENTS(g_aLApics); iCpu-- > 0;)
955 {
956 if (g_aLApics[iCpu].pv)
957 {
958 RTR0MemObjFree(g_aLApics[iCpu].hMapObj, true /* fFreeMappings */);
959 RTR0MemObjFree(g_aLApics[iCpu].hMemObj, true /* fFreeMappings */);
960 g_aLApics[iCpu].hMapObj = NIL_RTR0MEMOBJ;
961 g_aLApics[iCpu].hMemObj = NIL_RTR0MEMOBJ;
962 g_aLApics[iCpu].fEnabled = false;
963 g_aLApics[iCpu].fX2Apic = false;
964 g_aLApics[iCpu].pv = NULL;
965 }
966 }
967}
968
969
970/**
971 * Updates CPUMCPU::pvApicBase and CPUMCPU::fX2Apic prior to world switch.
972 *
973 * Writes the Local APIC mapping address of the current host CPU to CPUMCPU so
974 * the world switchers can access the APIC registers for the purpose of
975 * disabling and re-enabling the NMIs. Must be called with disabled preemption
976 * or disabled interrupts!
977 *
978 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
979 * @param iHostCpuSet The CPU set index of the current host CPU.
980 */
981VMMR0_INT_DECL(void) CPUMR0SetLApic(PVMCPU pVCpu, uint32_t iHostCpuSet)
982{
983 Assert(iHostCpuSet <= RT_ELEMENTS(g_aLApics));
984 pVCpu->cpum.s.pvApicBase = g_aLApics[iHostCpuSet].pv;
985 pVCpu->cpum.s.fX2Apic = g_aLApics[iHostCpuSet].fX2Apic;
986// Log6(("CPUMR0SetLApic: pvApicBase=%p fX2Apic=%d\n", g_aLApics[idxCpu].pv, g_aLApics[idxCpu].fX2Apic));
987}
988
989#endif /* VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI */
990
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