VirtualBox

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

Last change on this file since 44077 was 44077, checked in by vboxsync, 12 years ago

build fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 30.8 KB
Line 
1/* $Id: CPUMR0.cpp 44077 2012-12-10 12:46:42Z vboxsync $ */
2/** @file
3 * CPUM - Host Context Ring 0.
4 */
5
6/*
7 * Copyright (C) 2006-2011 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 /** Has APIC_REG_LVT_THMR. Not used. */
51 uint32_t fHasThermal;
52 /** The physical address of the APIC registers. */
53 RTHCPHYS PhysBase;
54 /** The memory object entering the physical address. */
55 RTR0MEMOBJ hMemObj;
56 /** The mapping object for hMemObj. */
57 RTR0MEMOBJ hMapObj;
58 /** The mapping address APIC registers.
59 * @remarks Different CPUs may use the same physical address to map their
60 * APICs, so this pointer is only valid when on the CPU owning the
61 * APIC. */
62 void *pv;
63} CPUMHOSTLAPIC;
64#endif
65
66
67/*******************************************************************************
68* Global Variables *
69*******************************************************************************/
70#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
71static CPUMHOSTLAPIC g_aLApics[RTCPUSET_MAX_CPUS];
72#endif
73
74
75/*******************************************************************************
76* Internal Functions *
77*******************************************************************************/
78#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
79static int cpumR0MapLocalApics(void);
80static void cpumR0UnmapLocalApics(void);
81#endif
82
83
84/**
85 * Does the Ring-0 CPU initialization once during module load.
86 * XXX Host-CPU hot-plugging?
87 */
88VMMR0DECL(int) CPUMR0ModuleInit(void)
89{
90 int rc = VINF_SUCCESS;
91#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
92 rc = cpumR0MapLocalApics();
93#endif
94 return rc;
95}
96
97
98/**
99 * Terminate the module.
100 */
101VMMR0DECL(int) CPUMR0ModuleTerm(void)
102{
103#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
104 cpumR0UnmapLocalApics();
105#endif
106 return VINF_SUCCESS;
107}
108
109
110/**
111 * Check the CPUID features of this particular CPU and disable relevant features
112 * for the guest which do not exist on this CPU. We have seen systems where the
113 * X86_CPUID_FEATURE_ECX_MONITOR feature flag is only set on some host CPUs, see
114 * @{bugref 5436}.
115 *
116 * @param idCpu The identifier for the CPU the function is called on.
117 * @param pvUser1 Pointer to the VM structure.
118 * @param pvUser2 Ignored.
119 */
120static DECLCALLBACK(void) cpumR0CheckCpuid(RTCPUID idCpu, void *pvUser1, void *pvUser2)
121{
122 struct
123 {
124 uint32_t uLeave; /* leave to check */
125 uint32_t ecx; /* which bits in ecx to unify between CPUs */
126 uint32_t edx; /* which bits in edx to unify between CPUs */
127 } aCpuidUnify[]
128 =
129 {
130 { 0x00000001, X86_CPUID_FEATURE_ECX_CX16
131 | X86_CPUID_FEATURE_ECX_MONITOR,
132 X86_CPUID_FEATURE_EDX_CX8 }
133 };
134 PVM pVM = (PVM)pvUser1;
135 PCPUM pCPUM = &pVM->cpum.s;
136 for (uint32_t i = 0; i < RT_ELEMENTS(aCpuidUnify); i++)
137 {
138 uint32_t uLeave = aCpuidUnify[i].uLeave;
139 uint32_t eax, ebx, ecx, edx;
140
141 ASMCpuId_Idx_ECX(uLeave, 0, &eax, &ebx, &ecx, &edx);
142 PCPUMCPUID paLeaves;
143 if (uLeave < 0x80000000)
144 paLeaves = &pCPUM->aGuestCpuIdStd[uLeave - 0x00000000];
145 else if (uLeave < 0xc0000000)
146 paLeaves = &pCPUM->aGuestCpuIdExt[uLeave - 0x80000000];
147 else
148 paLeaves = &pCPUM->aGuestCpuIdCentaur[uLeave - 0xc0000000];
149 /* unify important bits */
150 paLeaves->ecx &= (ecx | ~aCpuidUnify[i].ecx);
151 paLeaves->edx &= (edx | ~aCpuidUnify[i].edx);
152 }
153}
154
155
156/**
157 * Does Ring-0 CPUM initialization.
158 *
159 * This is mainly to check that the Host CPU mode is compatible
160 * with VBox.
161 *
162 * @returns VBox status code.
163 * @param pVM Pointer to the VM.
164 */
165VMMR0DECL(int) CPUMR0Init(PVM pVM)
166{
167 LogFlow(("CPUMR0Init: %p\n", pVM));
168
169 /*
170 * Check CR0 & CR4 flags.
171 */
172 uint32_t u32CR0 = ASMGetCR0();
173 if ((u32CR0 & (X86_CR0_PE | X86_CR0_PG)) != (X86_CR0_PE | X86_CR0_PG)) /* a bit paranoid perhaps.. */
174 {
175 Log(("CPUMR0Init: PE or PG not set. cr0=%#x\n", u32CR0));
176 return VERR_UNSUPPORTED_CPU_MODE;
177 }
178
179 /*
180 * Check for sysenter and syscall usage.
181 */
182 if (ASMHasCpuId())
183 {
184 /*
185 * SYSENTER/SYSEXIT
186 *
187 * Intel docs claim you should test both the flag and family, model &
188 * stepping because some Pentium Pro CPUs have the SEP cpuid flag set,
189 * but don't support it. AMD CPUs may support this feature in legacy
190 * mode, they've banned it from long mode. Since we switch to 32-bit
191 * mode when entering raw-mode context the feature would become
192 * accessible again on AMD CPUs, so we have to check regardless of
193 * host bitness.
194 */
195 uint32_t u32CpuVersion;
196 uint32_t u32Dummy;
197 uint32_t fFeatures;
198 ASMCpuId(1, &u32CpuVersion, &u32Dummy, &u32Dummy, &fFeatures);
199 uint32_t u32Family = u32CpuVersion >> 8;
200 uint32_t u32Model = (u32CpuVersion >> 4) & 0xF;
201 uint32_t u32Stepping = u32CpuVersion & 0xF;
202 if ( (fFeatures & X86_CPUID_FEATURE_EDX_SEP)
203 && ( u32Family != 6 /* (> pentium pro) */
204 || u32Model >= 3
205 || u32Stepping >= 3
206 || !ASMIsIntelCpu())
207 )
208 {
209 /*
210 * Read the MSR and see if it's in use or not.
211 */
212 uint32_t u32 = ASMRdMsr_Low(MSR_IA32_SYSENTER_CS);
213 if (u32)
214 {
215 pVM->cpum.s.fHostUseFlags |= CPUM_USE_SYSENTER;
216 Log(("CPUMR0Init: host uses sysenter cs=%08x%08x\n", ASMRdMsr_High(MSR_IA32_SYSENTER_CS), u32));
217 }
218 }
219
220 /*
221 * SYSCALL/SYSRET
222 *
223 * This feature is indicated by the SEP bit returned in EDX by CPUID
224 * function 0x80000001. Intel CPUs only supports this feature in
225 * long mode. Since we're not running 64-bit guests in raw-mode there
226 * are no issues with 32-bit intel hosts.
227 */
228 uint32_t cExt = 0;
229 ASMCpuId(0x80000000, &cExt, &u32Dummy, &u32Dummy, &u32Dummy);
230 if ( cExt >= 0x80000001
231 && cExt <= 0x8000ffff)
232 {
233 uint32_t fExtFeaturesEDX = ASMCpuId_EDX(0x80000001);
234 if (fExtFeaturesEDX & X86_CPUID_EXT_FEATURE_EDX_SYSCALL)
235 {
236#ifdef RT_ARCH_X86
237# ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
238 if (fExtFeaturesEDX & X86_CPUID_EXT_FEATURE_EDX_LONG_MODE)
239# else
240 if (!ASMIsIntelCpu())
241# endif
242#endif
243 {
244 uint64_t fEfer = ASMRdMsr(MSR_K6_EFER);
245 if (fEfer & MSR_K6_EFER_SCE)
246 {
247 pVM->cpum.s.fHostUseFlags |= CPUM_USE_SYSCALL;
248 Log(("CPUMR0Init: host uses syscall\n"));
249 }
250 }
251 }
252 }
253
254 RTMpOnAll(cpumR0CheckCpuid, pVM, NULL);
255 }
256
257
258 /*
259 * Check if debug registers are armed.
260 * This ASSUMES that DR7.GD is not set, or that it's handled transparently!
261 */
262 uint32_t u32DR7 = ASMGetDR7();
263 if (u32DR7 & X86_DR7_ENABLED_MASK)
264 {
265 for (VMCPUID i = 0; i < pVM->cCpus; i++)
266 pVM->aCpus[i].cpum.s.fUseFlags |= CPUM_USE_DEBUG_REGS_HOST;
267 Log(("CPUMR0Init: host uses debug registers (dr7=%x)\n", u32DR7));
268 }
269
270 return VINF_SUCCESS;
271}
272
273
274/**
275 * Lazily sync in the FPU/XMM state
276 *
277 * @returns VBox status code.
278 * @param pVM Pointer to the VM.
279 * @param pVCpu Pointer to the VMCPU.
280 * @param pCtx Pointer to the guest CPU context.
281 */
282VMMR0DECL(int) CPUMR0LoadGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
283{
284 Assert(pVM->cpum.s.CPUFeatures.edx.u1FXSR);
285 Assert(ASMGetCR4() & X86_CR4_OSFSXR);
286
287 /* If the FPU state has already been loaded, then it's a guest trap. */
288 if (pVCpu->cpum.s.fUseFlags & CPUM_USED_FPU)
289 {
290 Assert( ((pCtx->cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS)) == (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS))
291 || ((pCtx->cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS)) == (X86_CR0_MP | X86_CR0_TS)));
292 return VINF_EM_RAW_GUEST_TRAP;
293 }
294
295 /*
296 * There are two basic actions:
297 * 1. Save host fpu and restore guest fpu.
298 * 2. Generate guest trap.
299 *
300 * When entering the hypervisor we'll always enable MP (for proper wait
301 * trapping) and TS (for intercepting all fpu/mmx/sse stuff). The EM flag
302 * is taken from the guest OS in order to get proper SSE handling.
303 *
304 *
305 * Actions taken depending on the guest CR0 flags:
306 *
307 * 3 2 1
308 * TS | EM | MP | FPUInstr | WAIT :: VMM Action
309 * ------------------------------------------------------------------------
310 * 0 | 0 | 0 | Exec | Exec :: Clear TS & MP, Save HC, Load GC.
311 * 0 | 0 | 1 | Exec | Exec :: Clear TS, Save HC, Load GC.
312 * 0 | 1 | 0 | #NM | Exec :: Clear TS & MP, Save HC, Load GC.
313 * 0 | 1 | 1 | #NM | Exec :: Clear TS, Save HC, Load GC.
314 * 1 | 0 | 0 | #NM | Exec :: Clear MP, Save HC, Load GC. (EM is already cleared.)
315 * 1 | 0 | 1 | #NM | #NM :: Go to guest taking trap there.
316 * 1 | 1 | 0 | #NM | Exec :: Clear MP, Save HC, Load GC. (EM is already set.)
317 * 1 | 1 | 1 | #NM | #NM :: Go to guest taking trap there.
318 */
319
320 switch (pCtx->cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS))
321 {
322 case X86_CR0_MP | X86_CR0_TS:
323 case X86_CR0_MP | X86_CR0_EM | X86_CR0_TS:
324 return VINF_EM_RAW_GUEST_TRAP;
325 default:
326 break;
327 }
328
329#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
330 if (CPUMIsGuestInLongModeEx(pCtx))
331 {
332 Assert(!(pVCpu->cpum.s.fUseFlags & CPUM_SYNC_FPU_STATE));
333
334 /* Save the host state and record the fact (CPUM_USED_FPU | CPUM_USED_FPU_SINCE_REM). */
335 cpumR0SaveHostFPUState(&pVCpu->cpum.s);
336
337 /* Restore the state on entry as we need to be in 64 bits mode to access the full state. */
338 pVCpu->cpum.s.fUseFlags |= CPUM_SYNC_FPU_STATE;
339 }
340 else
341#endif
342 {
343#ifndef CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE
344# if defined(VBOX_WITH_HYBRID_32BIT_KERNEL) || defined(VBOX_WITH_KERNEL_USING_XMM) /** @todo remove the #else here and move cpumHandleLazyFPUAsm back to VMMGC after branching out 3.0!!. */
345 Assert(!(pVCpu->cpum.s.fUseFlags & CPUM_MANUAL_XMM_RESTORE));
346 /** @todo Move the FFXR handling down into
347 * cpumR0SaveHostRestoreguestFPUState to optimize the
348 * VBOX_WITH_KERNEL_USING_XMM handling. */
349 /* Clear MSR_K6_EFER_FFXSR or else we'll be unable to save/restore the XMM state with fxsave/fxrstor. */
350 uint64_t SavedEFER = 0;
351 if (pVM->cpum.s.CPUFeaturesExt.edx & X86_CPUID_AMD_FEATURE_EDX_FFXSR)
352 {
353 SavedEFER = ASMRdMsr(MSR_K6_EFER);
354 if (SavedEFER & MSR_K6_EFER_FFXSR)
355 {
356 ASMWrMsr(MSR_K6_EFER, SavedEFER & ~MSR_K6_EFER_FFXSR);
357 pVCpu->cpum.s.fUseFlags |= CPUM_MANUAL_XMM_RESTORE;
358 }
359 }
360
361 /* Do the job and record that we've switched FPU state. */
362 cpumR0SaveHostRestoreGuestFPUState(&pVCpu->cpum.s);
363
364 /* Restore EFER. */
365 if (pVCpu->cpum.s.fUseFlags & CPUM_MANUAL_XMM_RESTORE)
366 ASMWrMsr(MSR_K6_EFER, SavedEFER);
367
368# else
369 uint64_t oldMsrEFERHost = 0;
370 uint32_t oldCR0 = ASMGetCR0();
371
372 /* Clear MSR_K6_EFER_FFXSR or else we'll be unable to save/restore the XMM state with fxsave/fxrstor. */
373 if (pVM->cpum.s.CPUFeaturesExt.edx & X86_CPUID_AMD_FEATURE_EDX_FFXSR)
374 {
375 /** @todo Do we really need to read this every time?? The host could change this on the fly though.
376 * bird: what about starting by skipping the ASMWrMsr below if we didn't
377 * change anything? Ditto for the stuff in CPUMR0SaveGuestFPU. */
378 oldMsrEFERHost = ASMRdMsr(MSR_K6_EFER);
379 if (oldMsrEFERHost & MSR_K6_EFER_FFXSR)
380 {
381 ASMWrMsr(MSR_K6_EFER, oldMsrEFERHost & ~MSR_K6_EFER_FFXSR);
382 pVCpu->cpum.s.fUseFlags |= CPUM_MANUAL_XMM_RESTORE;
383 }
384 }
385
386 /* If we sync the FPU/XMM state on-demand, then we can continue execution as if nothing has happened. */
387 int rc = CPUMHandleLazyFPU(pVCpu);
388 AssertRC(rc);
389 Assert(CPUMIsGuestFPUStateActive(pVCpu));
390
391 /* Restore EFER MSR */
392 if (pVCpu->cpum.s.fUseFlags & CPUM_MANUAL_XMM_RESTORE)
393 ASMWrMsr(MSR_K6_EFER, oldMsrEFERHost);
394
395 /* CPUMHandleLazyFPU could have changed CR0; restore it. */
396 ASMSetCR0(oldCR0);
397# endif
398
399#else /* CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE */
400
401 /*
402 * Save the FPU control word and MXCSR, so we can restore the state properly afterwards.
403 * We don't want the guest to be able to trigger floating point/SSE exceptions on the host.
404 */
405 pVCpu->cpum.s.Host.fpu.FCW = CPUMGetFCW();
406 if (pVM->cpum.s.CPUFeatures.edx.u1SSE)
407 pVCpu->cpum.s.Host.fpu.MXCSR = CPUMGetMXCSR();
408
409 cpumR0LoadFPU(pCtx);
410
411 /*
412 * The MSR_K6_EFER_FFXSR feature is AMD only so far, but check the cpuid just in case Intel adds it in the future.
413 *
414 * MSR_K6_EFER_FFXSR changes the behaviour of fxsave and fxrstore: the XMM state isn't saved/restored
415 */
416 if (pVM->cpum.s.CPUFeaturesExt.edx & X86_CPUID_AMD_FEATURE_EDX_FFXSR)
417 {
418 /** @todo Do we really need to read this every time?? The host could change this on the fly though. */
419 uint64_t msrEFERHost = ASMRdMsr(MSR_K6_EFER);
420
421 if (msrEFERHost & MSR_K6_EFER_FFXSR)
422 {
423 /* fxrstor doesn't restore the XMM state! */
424 cpumR0LoadXMM(pCtx);
425 pVCpu->cpum.s.fUseFlags |= CPUM_MANUAL_XMM_RESTORE;
426 }
427 }
428
429#endif /* CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE */
430 }
431
432 Assert((pVCpu->cpum.s.fUseFlags & (CPUM_USED_FPU | CPUM_USED_FPU_SINCE_REM)) == (CPUM_USED_FPU | CPUM_USED_FPU_SINCE_REM));
433 return VINF_SUCCESS;
434}
435
436
437/**
438 * Save guest FPU/XMM state
439 *
440 * @returns VBox status code.
441 * @param pVM Pointer to the VM.
442 * @param pVCpu Pointer to the VMCPU.
443 * @param pCtx Pointer to the guest CPU context.
444 */
445VMMR0DECL(int) CPUMR0SaveGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
446{
447 Assert(pVM->cpum.s.CPUFeatures.edx.u1FXSR);
448 Assert(ASMGetCR4() & X86_CR4_OSFSXR);
449 AssertReturn((pVCpu->cpum.s.fUseFlags & CPUM_USED_FPU), VINF_SUCCESS);
450 NOREF(pCtx);
451
452#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
453 if (CPUMIsGuestInLongModeEx(pCtx))
454 {
455 if (!(pVCpu->cpum.s.fUseFlags & CPUM_SYNC_FPU_STATE))
456 {
457 HMR0SaveFPUState(pVM, pVCpu, pCtx);
458 cpumR0RestoreHostFPUState(&pVCpu->cpum.s);
459 }
460 /* else nothing to do; we didn't perform a world switch */
461 }
462 else
463#endif
464 {
465#ifndef CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE
466# ifdef VBOX_WITH_KERNEL_USING_XMM
467 /*
468 * We've already saved the XMM registers in the assembly wrapper, so
469 * we have to save them before saving the entire FPU state and put them
470 * back afterwards.
471 */
472 /** @todo This could be skipped if MSR_K6_EFER_FFXSR is set, but
473 * I'm not able to test such an optimization tonight.
474 * We could just all this in assembly. */
475 uint128_t aGuestXmmRegs[16];
476 memcpy(&aGuestXmmRegs[0], &pVCpu->cpum.s.Guest.fpu.aXMM[0], sizeof(aGuestXmmRegs));
477# endif
478
479 /* Clear MSR_K6_EFER_FFXSR or else we'll be unable to save/restore the XMM state with fxsave/fxrstor. */
480 uint64_t oldMsrEFERHost = 0;
481 if (pVCpu->cpum.s.fUseFlags & CPUM_MANUAL_XMM_RESTORE)
482 {
483 oldMsrEFERHost = ASMRdMsr(MSR_K6_EFER);
484 ASMWrMsr(MSR_K6_EFER, oldMsrEFERHost & ~MSR_K6_EFER_FFXSR);
485 }
486 cpumR0SaveGuestRestoreHostFPUState(&pVCpu->cpum.s);
487
488 /* Restore EFER MSR */
489 if (pVCpu->cpum.s.fUseFlags & CPUM_MANUAL_XMM_RESTORE)
490 ASMWrMsr(MSR_K6_EFER, oldMsrEFERHost | MSR_K6_EFER_FFXSR);
491
492# ifdef VBOX_WITH_KERNEL_USING_XMM
493 memcpy(&pVCpu->cpum.s.Guest.fpu.aXMM[0], &aGuestXmmRegs[0], sizeof(aGuestXmmRegs));
494# endif
495
496#else /* CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE */
497# ifdef VBOX_WITH_KERNEL_USING_XMM
498# error "Fix all the NM_TRAPS_IN_KERNEL_MODE code path. I'm not going to fix unused code now."
499# endif
500 cpumR0SaveFPU(pCtx);
501 if (pVCpu->cpum.s.fUseFlags & CPUM_MANUAL_XMM_RESTORE)
502 {
503 /* fxsave doesn't save the XMM state! */
504 cpumR0SaveXMM(pCtx);
505 }
506
507 /*
508 * Restore the original FPU control word and MXCSR.
509 * We don't want the guest to be able to trigger floating point/SSE exceptions on the host.
510 */
511 cpumR0SetFCW(pVCpu->cpum.s.Host.fpu.FCW);
512 if (pVM->cpum.s.CPUFeatures.edx.u1SSE)
513 cpumR0SetMXCSR(pVCpu->cpum.s.Host.fpu.MXCSR);
514#endif /* CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE */
515 }
516
517 pVCpu->cpum.s.fUseFlags &= ~(CPUM_USED_FPU | CPUM_SYNC_FPU_STATE | CPUM_MANUAL_XMM_RESTORE);
518 return VINF_SUCCESS;
519}
520
521
522/**
523 * Save guest debug state
524 *
525 * @returns VBox status code.
526 * @param pVM Pointer to the VM.
527 * @param pVCpu Pointer to the VMCPU.
528 * @param pCtx Pointer to the guest CPU context.
529 * @param fDR6 Whether to include DR6 or not.
530 */
531VMMR0DECL(int) CPUMR0SaveGuestDebugState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, bool fDR6)
532{
533 Assert(pVCpu->cpum.s.fUseFlags & CPUM_USE_DEBUG_REGS);
534
535 /* Save the guest's debug state. The caller is responsible for DR7. */
536#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
537 if (CPUMIsGuestInLongModeEx(pCtx))
538 {
539 if (!(pVCpu->cpum.s.fUseFlags & CPUM_SYNC_DEBUG_STATE))
540 {
541 uint64_t dr6 = pCtx->dr[6];
542
543 HMR0SaveDebugState(pVM, pVCpu, pCtx);
544 if (!fDR6) /* dr6 was already up-to-date */
545 pCtx->dr[6] = dr6;
546 }
547 }
548 else
549#endif
550 {
551#ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
552 cpumR0SaveDRx(&pCtx->dr[0]);
553#else
554 pCtx->dr[0] = ASMGetDR0();
555 pCtx->dr[1] = ASMGetDR1();
556 pCtx->dr[2] = ASMGetDR2();
557 pCtx->dr[3] = ASMGetDR3();
558#endif
559 if (fDR6)
560 pCtx->dr[6] = ASMGetDR6();
561 }
562
563 /*
564 * Restore the host's debug state. DR0-3, DR6 and only then DR7!
565 * DR7 contains 0x400 right now.
566 */
567 CPUMR0LoadHostDebugState(pVM, pVCpu);
568 Assert(!(pVCpu->cpum.s.fUseFlags & CPUM_USE_DEBUG_REGS));
569 return VINF_SUCCESS;
570}
571
572
573/**
574 * Lazily sync in the debug state
575 *
576 * @returns VBox status code.
577 * @param pVM Pointer to the VM.
578 * @param pVCpu Pointer to the VMCPU.
579 * @param pCtx Pointer to the guest CPU context.
580 * @param fDR6 Whether to include DR6 or not.
581 */
582VMMR0DECL(int) CPUMR0LoadGuestDebugState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, bool fDR6)
583{
584 /* Save the host state. */
585 CPUMR0SaveHostDebugState(pVM, pVCpu);
586 Assert(ASMGetDR7() == X86_DR7_INIT_VAL);
587
588 /* Activate the guest state DR0-3; DR7 is left to the caller. */
589#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
590 if (CPUMIsGuestInLongModeEx(pCtx))
591 {
592 /* Restore the state on entry as we need to be in 64 bits mode to access the full state. */
593 pVCpu->cpum.s.fUseFlags |= CPUM_SYNC_DEBUG_STATE;
594 }
595 else
596#endif
597 {
598#ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
599 cpumR0LoadDRx(&pCtx->dr[0]);
600#else
601 ASMSetDR0(pCtx->dr[0]);
602 ASMSetDR1(pCtx->dr[1]);
603 ASMSetDR2(pCtx->dr[2]);
604 ASMSetDR3(pCtx->dr[3]);
605#endif
606 if (fDR6)
607 ASMSetDR6(pCtx->dr[6]);
608 }
609
610 pVCpu->cpum.s.fUseFlags |= CPUM_USE_DEBUG_REGS;
611 return VINF_SUCCESS;
612}
613
614/**
615 * Save the host debug state
616 *
617 * @returns VBox status code.
618 * @param pVM Pointer to the VM.
619 * @param pVCpu Pointer to the VMCPU.
620 */
621VMMR0DECL(int) CPUMR0SaveHostDebugState(PVM pVM, PVMCPU pVCpu)
622{
623 NOREF(pVM);
624
625 /* Save the host state. */
626#ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
627 AssertCompile((uintptr_t)&pVCpu->cpum.s.Host.dr3 - (uintptr_t)&pVCpu->cpum.s.Host.dr0 == sizeof(uint64_t) * 3);
628 cpumR0SaveDRx(&pVCpu->cpum.s.Host.dr0);
629#else
630 pVCpu->cpum.s.Host.dr0 = ASMGetDR0();
631 pVCpu->cpum.s.Host.dr1 = ASMGetDR1();
632 pVCpu->cpum.s.Host.dr2 = ASMGetDR2();
633 pVCpu->cpum.s.Host.dr3 = ASMGetDR3();
634#endif
635 pVCpu->cpum.s.Host.dr6 = ASMGetDR6();
636 /** @todo dr7 might already have been changed to 0x400; don't care right now as it's harmless. */
637 pVCpu->cpum.s.Host.dr7 = ASMGetDR7();
638 /* Make sure DR7 is harmless or else we could trigger breakpoints when restoring dr0-3 (!) */
639 ASMSetDR7(X86_DR7_INIT_VAL);
640
641 return VINF_SUCCESS;
642}
643
644/**
645 * Load the host debug state
646 *
647 * @returns VBox status code.
648 * @param pVM Pointer to the VM.
649 * @param pVCpu Pointer to the VMCPU.
650 */
651VMMR0DECL(int) CPUMR0LoadHostDebugState(PVM pVM, PVMCPU pVCpu)
652{
653 Assert(pVCpu->cpum.s.fUseFlags & (CPUM_USE_DEBUG_REGS | CPUM_USE_DEBUG_REGS_HYPER));
654 NOREF(pVM);
655
656 /*
657 * Restore the host's debug state. DR0-3, DR6 and only then DR7!
658 * DR7 contains 0x400 right now.
659 */
660#ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
661 AssertCompile((uintptr_t)&pVCpu->cpum.s.Host.dr3 - (uintptr_t)&pVCpu->cpum.s.Host.dr0 == sizeof(uint64_t) * 3);
662 cpumR0LoadDRx(&pVCpu->cpum.s.Host.dr0);
663#else
664 ASMSetDR0(pVCpu->cpum.s.Host.dr0);
665 ASMSetDR1(pVCpu->cpum.s.Host.dr1);
666 ASMSetDR2(pVCpu->cpum.s.Host.dr2);
667 ASMSetDR3(pVCpu->cpum.s.Host.dr3);
668#endif
669 ASMSetDR6(pVCpu->cpum.s.Host.dr6);
670 ASMSetDR7(pVCpu->cpum.s.Host.dr7);
671
672 pVCpu->cpum.s.fUseFlags &= ~(CPUM_USE_DEBUG_REGS | CPUM_USE_DEBUG_REGS_HYPER);
673 return VINF_SUCCESS;
674}
675
676
677/**
678 * Lazily sync in the hypervisor debug state
679 *
680 * @returns VBox status code.
681 * @param pVM Pointer to the VM.
682 * @param pVCpu Pointer to the VMCPU.
683 * @param pCtx Pointer to the guest CPU context.
684 * @param fDR6 Whether to include DR6 or not.
685 */
686VMMR0DECL(int) CPUMR0LoadHyperDebugState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, bool fDR6)
687{
688 NOREF(pCtx);
689
690 /* Save the host state. */
691 CPUMR0SaveHostDebugState(pVM, pVCpu);
692 Assert(ASMGetDR7() == X86_DR7_INIT_VAL);
693
694 /* Activate the guest state DR0-3; DR7 is left to the caller. */
695#if HC_ARCH_BITS == 32 && defined(VBOX_WITH_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
696 if (CPUMIsGuestInLongModeEx(pCtx))
697 {
698 AssertFailed();
699 return VERR_NOT_IMPLEMENTED;
700 }
701 else
702#endif
703 {
704#ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
705 AssertFailed();
706 return VERR_NOT_IMPLEMENTED;
707#else
708 ASMSetDR0(CPUMGetHyperDR0(pVCpu));
709 ASMSetDR1(CPUMGetHyperDR1(pVCpu));
710 ASMSetDR2(CPUMGetHyperDR2(pVCpu));
711 ASMSetDR3(CPUMGetHyperDR3(pVCpu));
712#endif
713 if (fDR6)
714 ASMSetDR6(CPUMGetHyperDR6(pVCpu));
715 }
716
717 pVCpu->cpum.s.fUseFlags |= CPUM_USE_DEBUG_REGS_HYPER;
718 return VINF_SUCCESS;
719}
720
721#ifdef VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI
722
723/**
724 * Worker for cpumR0MapLocalApics. Check each CPU for a present Local APIC.
725 * Play safe and treat each CPU separate.
726 *
727 * @param idCpu The identifier for the CPU the function is called on.
728 * @param pvUser1 Ignored.
729 * @param pvUser2 Ignored.
730 */
731static DECLCALLBACK(void) cpumR0MapLocalApicWorker(RTCPUID idCpu, void *pvUser1, void *pvUser2)
732{
733 NOREF(pvUser1); NOREF(pvUser2);
734 int iCpu = RTMpCpuIdToSetIndex(idCpu);
735 AssertReturnVoid(iCpu >= 0 && (unsigned)iCpu < RT_ELEMENTS(g_aLApics));
736
737 uint32_t u32MaxIdx, u32EBX, u32ECX, u32EDX;
738 ASMCpuId(0, &u32MaxIdx, &u32EBX, &u32ECX, &u32EDX);
739 if ( ( ( u32EBX == X86_CPUID_VENDOR_INTEL_EBX
740 && u32ECX == X86_CPUID_VENDOR_INTEL_ECX
741 && u32EDX == X86_CPUID_VENDOR_INTEL_EDX)
742 || ( u32EBX == X86_CPUID_VENDOR_AMD_EBX
743 && u32ECX == X86_CPUID_VENDOR_AMD_ECX
744 && u32EDX == X86_CPUID_VENDOR_AMD_EDX)
745 || ( u32EBX == X86_CPUID_VENDOR_VIA_EBX
746 && u32ECX == X86_CPUID_VENDOR_VIA_ECX
747 && u32EDX == X86_CPUID_VENDOR_VIA_EDX))
748 && u32MaxIdx >= 1)
749 {
750 ASMCpuId(1, &u32MaxIdx, &u32EBX, &u32ECX, &u32EDX);
751 if ( (u32EDX & X86_CPUID_FEATURE_EDX_APIC)
752 && (u32EDX & X86_CPUID_FEATURE_EDX_MSR))
753 {
754 uint64_t u64ApicBase = ASMRdMsr(MSR_IA32_APICBASE);
755 uint64_t u64Mask = UINT64_C(0x0000000ffffff000);
756
757 /* see Intel Manual: Local APIC Status and Location: MAXPHYADDR default is bit 36 */
758 uint32_t u32MaxExtIdx;
759 ASMCpuId(0x80000000, &u32MaxExtIdx, &u32EBX, &u32ECX, &u32EDX);
760 if ( u32MaxExtIdx >= UINT32_C(0x80000008)
761 && u32MaxExtIdx < UINT32_C(0x8000ffff))
762 {
763 uint32_t u32PhysBits;
764 ASMCpuId(0x80000008, &u32PhysBits, &u32EBX, &u32ECX, &u32EDX);
765 u32PhysBits &= 0xff;
766 u64Mask = ((UINT64_C(1) << u32PhysBits) - 1) & UINT64_C(0xfffffffffffff000);
767 }
768
769 uint64_t const u64PhysBase = u64ApicBase & u64Mask;
770 g_aLApics[iCpu].PhysBase = (RTHCPHYS)u64PhysBase;
771 g_aLApics[iCpu].fEnabled = g_aLApics[iCpu].PhysBase == u64PhysBase;
772 }
773 }
774}
775
776
777/**
778 * Map the MMIO page of each local APIC in the system.
779 */
780static int cpumR0MapLocalApics(void)
781{
782 /*
783 * Check that we'll always stay within the array bounds.
784 */
785 if (RTMpGetArraySize() > RT_ELEMENTS(g_aLApics))
786 {
787 LogRel(("CPUM: Too many real CPUs/cores/threads - %u, max %u\n", RTMpGetArraySize(), RT_ELEMENTS(g_aLApics)));
788 return VERR_TOO_MANY_CPUS;
789 }
790
791 /*
792 * Create mappings for all online CPUs we think have APICs.
793 */
794 /** @todo r=bird: This code is not adequately handling CPUs that are
795 * offline or unplugged at init time and later bought into action. */
796 int rc = RTMpOnAll(cpumR0MapLocalApicWorker, NULL, NULL);
797
798 for (unsigned iCpu = 0; RT_SUCCESS(rc) && iCpu < RT_ELEMENTS(g_aLApics); iCpu++)
799 {
800 if (g_aLApics[iCpu].fEnabled)
801 {
802 rc = RTR0MemObjEnterPhys(&g_aLApics[iCpu].hMemObj, g_aLApics[iCpu].PhysBase,
803 PAGE_SIZE, RTMEM_CACHE_POLICY_MMIO);
804 if (RT_SUCCESS(rc))
805 {
806 rc = RTR0MemObjMapKernel(&g_aLApics[iCpu].hMapObj, g_aLApics[iCpu].hMemObj, (void *)-1,
807 PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
808 if (RT_SUCCESS(rc))
809 {
810 void *pvApicBase = RTR0MemObjAddress(g_aLApics[iCpu].hMapObj);
811
812 /*
813 * 0x0X 82489 external APIC
814 * 0x1X Local APIC
815 * 0x2X..0xFF reserved
816 */
817 /** @todo r=bird: The local APIC is usually at the same address for all CPUs,
818 * and therefore inaccessible by the other CPUs. */
819 uint32_t ApicVersion = ApicRegRead(pvApicBase, APIC_REG_VERSION);
820 if ((APIC_REG_VERSION_GET_VER(ApicVersion) & 0xF0) == 0x10)
821 {
822 g_aLApics[iCpu].fHasThermal = APIC_REG_VERSION_GET_MAX_LVT(ApicVersion) >= 5;
823 g_aLApics[iCpu].pv = pvApicBase;
824 Log(("CPUM: APIC %02u at %RGp (mapped at %p) - ver %#x, lint0=%#x lint1=%#x pc=%#x thmr=%#x\n",
825 iCpu, g_aLApics[iCpu].PhysBase, g_aLApics[iCpu].pv, ApicVersion,
826 ApicRegRead(pvApicBase, APIC_REG_LVT_LINT0),
827 ApicRegRead(pvApicBase, APIC_REG_LVT_LINT1),
828 ApicRegRead(pvApicBase, APIC_REG_LVT_PC),
829 ApicRegRead(pvApicBase, APIC_REG_LVT_THMR)
830 ));
831 continue;
832 }
833
834 RTR0MemObjFree(g_aLApics[iCpu].hMapObj, true /* fFreeMappings */);
835 }
836 RTR0MemObjFree(g_aLApics[iCpu].hMemObj, true /* fFreeMappings */);
837 }
838 g_aLApics[iCpu].fEnabled = false;
839 }
840 }
841 if (RT_FAILURE(rc))
842 {
843 cpumR0UnmapLocalApics();
844 return rc;
845 }
846
847 return VINF_SUCCESS;
848}
849
850
851/**
852 * Unmap the Local APIC of all host CPUs.
853 */
854static void cpumR0UnmapLocalApics(void)
855{
856 for (unsigned iCpu = RT_ELEMENTS(g_aLApics); iCpu-- > 0;)
857 {
858 if (g_aLApics[iCpu].pv)
859 {
860 RTR0MemObjFree(g_aLApics[iCpu].hMapObj, true /* fFreeMappings */);
861 RTR0MemObjFree(g_aLApics[iCpu].hMemObj, true /* fFreeMappings */);
862 g_aLApics[iCpu].hMapObj = NIL_RTR0MEMOBJ;
863 g_aLApics[iCpu].hMemObj = NIL_RTR0MEMOBJ;
864 g_aLApics[iCpu].fEnabled = false;
865 g_aLApics[iCpu].pv = NULL;
866 }
867 }
868}
869
870
871/**
872 * Write the Local APIC mapping address of the current host CPU to CPUM to be
873 * able to access the APIC registers in the raw mode switcher for disabling/
874 * re-enabling the NMI. Must be called with disabled preemption or disabled
875 * interrupts!
876 *
877 * @param pVM Pointer to the VM.
878 * @param idHostCpu The ID of the current host CPU.
879 */
880VMMR0DECL(void) CPUMR0SetLApic(PVM pVM, RTCPUID idHostCpu)
881{
882 pVM->cpum.s.pvApicBase = g_aLApics[RTMpCpuIdToSetIndex(idHostCpu)].pv;
883}
884
885#endif /* VBOX_WITH_VMMR0_DISABLE_LAPIC_NMI */
886
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