VirtualBox

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

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

tabs

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