VirtualBox

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

Last change on this file since 15741 was 15439, checked in by vboxsync, 16 years ago

Enable 64 bits guest support on 32 bits hosts. Only use rem64 if the guest OS type is 64 bits.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 14.3 KB
Line 
1/* $Id: CPUMR0.cpp 15439 2008-12-13 12:48:22Z vboxsync $ */
2/** @file
3 * CPUM - Host Context Ring 0.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_CPUM
27#include <VBox/cpum.h>
28#include "CPUMInternal.h"
29#include <VBox/vm.h>
30#include <VBox/x86.h>
31#include <VBox/err.h>
32#include <VBox/log.h>
33#include <VBox/hwaccm.h>
34#include <iprt/assert.h>
35#include <iprt/asm.h>
36
37
38
39/**
40 * Does Ring-0 CPUM initialization.
41 *
42 * This is mainly to check that the Host CPU mode is compatible
43 * with VBox.
44 *
45 * @returns VBox status code.
46 * @param pVM The VM to operate on.
47 */
48VMMR0DECL(int) CPUMR0Init(PVM pVM)
49{
50 LogFlow(("CPUMR0Init: %p\n", pVM));
51
52 /*
53 * Check CR0 & CR4 flags.
54 */
55 uint32_t u32CR0 = ASMGetCR0();
56 if ((u32CR0 & (X86_CR0_PE | X86_CR0_PG)) != (X86_CR0_PE | X86_CR0_PG)) /* a bit paranoid perhaps.. */
57 {
58 Log(("CPUMR0Init: PE or PG not set. cr0=%#x\n", u32CR0));
59 return VERR_UNSUPPORTED_CPU_MODE;
60 }
61
62 /*
63 * Check for sysenter if it's used.
64 */
65 if (ASMHasCpuId())
66 {
67 uint32_t u32CpuVersion;
68 uint32_t u32Dummy;
69 uint32_t u32Features;
70 ASMCpuId(1, &u32CpuVersion, &u32Dummy, &u32Dummy, &u32Features);
71 uint32_t u32Family = u32CpuVersion >> 8;
72 uint32_t u32Model = (u32CpuVersion >> 4) & 0xF;
73 uint32_t u32Stepping = u32CpuVersion & 0xF;
74
75 /*
76 * Intel docs claim you should test both the flag and family, model & stepping.
77 * Some Pentium Pro cpus have the SEP cpuid flag set, but don't support it.
78 */
79 if ( (u32Features & X86_CPUID_FEATURE_EDX_SEP)
80 && !(u32Family == 6 && u32Model < 3 && u32Stepping < 3))
81 {
82 /*
83 * Read the MSR and see if it's in use or not.
84 */
85 uint32_t u32 = ASMRdMsr_Low(MSR_IA32_SYSENTER_CS);
86 if (u32)
87 {
88 for (unsigned i=0;i<pVM->cCPUs;i++)
89 pVM->aCpus[i].cpum.s.fUseFlags |= CPUM_USE_SYSENTER;
90
91 Log(("CPUMR0Init: host uses sysenter cs=%08x%08x\n", ASMRdMsr_High(MSR_IA32_SYSENTER_CS), u32));
92 }
93 }
94
95 /** @todo check for AMD and syscall!!!!!! */
96 }
97
98
99 /*
100 * Check if debug registers are armed.
101 * This ASSUMES that DR7.GD is not set, or that it's handled transparently!
102 */
103 uint32_t u32DR7 = ASMGetDR7();
104 if (u32DR7 & X86_DR7_ENABLED_MASK)
105 {
106 for (unsigned i=0;i<pVM->cCPUs;i++)
107 pVM->aCpus[i].cpum.s.fUseFlags |= CPUM_USE_DEBUG_REGS_HOST;
108 Log(("CPUMR0Init: host uses debug registers (dr7=%x)\n", u32DR7));
109 }
110
111 return VINF_SUCCESS;
112}
113
114
115/**
116 * Lazily sync in the FPU/XMM state
117 *
118 * @returns VBox status code.
119 * @param pVM VM handle.
120 * @param pVCpu VMCPU handle.
121 * @param pCtx CPU context
122 */
123VMMR0DECL(int) CPUMR0LoadGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
124{
125 Assert(pVM->cpum.s.CPUFeatures.edx.u1FXSR);
126 Assert(ASMGetCR4() & X86_CR4_OSFSXR);
127
128 /* If the FPU state has already been loaded, then it's a guest trap. */
129 if (pVCpu->cpum.s.fUseFlags & CPUM_USED_FPU)
130 {
131 Assert( ((pCtx->cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS)) == (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS))
132 || ((pCtx->cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS)) == (X86_CR0_MP | X86_CR0_TS)));
133 return VINF_EM_RAW_GUEST_TRAP;
134 }
135
136 /*
137 * There are two basic actions:
138 * 1. Save host fpu and restore guest fpu.
139 * 2. Generate guest trap.
140 *
141 * When entering the hypervisor we'll always enable MP (for proper wait
142 * trapping) and TS (for intercepting all fpu/mmx/sse stuff). The EM flag
143 * is taken from the guest OS in order to get proper SSE handling.
144 *
145 *
146 * Actions taken depending on the guest CR0 flags:
147 *
148 * 3 2 1
149 * TS | EM | MP | FPUInstr | WAIT :: VMM Action
150 * ------------------------------------------------------------------------
151 * 0 | 0 | 0 | Exec | Exec :: Clear TS & MP, Save HC, Load GC.
152 * 0 | 0 | 1 | Exec | Exec :: Clear TS, Save HC, Load GC.
153 * 0 | 1 | 0 | #NM | Exec :: Clear TS & MP, Save HC, Load GC.
154 * 0 | 1 | 1 | #NM | Exec :: Clear TS, Save HC, Load GC.
155 * 1 | 0 | 0 | #NM | Exec :: Clear MP, Save HC, Load GC. (EM is already cleared.)
156 * 1 | 0 | 1 | #NM | #NM :: Go to guest taking trap there.
157 * 1 | 1 | 0 | #NM | Exec :: Clear MP, Save HC, Load GC. (EM is already set.)
158 * 1 | 1 | 1 | #NM | #NM :: Go to guest taking trap there.
159 */
160
161 switch (pCtx->cr0 & (X86_CR0_MP | X86_CR0_EM | X86_CR0_TS))
162 {
163 case X86_CR0_MP | X86_CR0_TS:
164 case X86_CR0_MP | X86_CR0_EM | X86_CR0_TS:
165 return VINF_EM_RAW_GUEST_TRAP;
166 default:
167 break;
168 }
169
170#if HC_ARCH_BITS == 32 && defined(VBOX_ENABLE_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
171 if (CPUMIsGuestInLongModeEx(pCtx))
172 {
173 /* Restore the state on entry as we need to be in 64 bits mode to access the full state. */
174 pVCpu->cpum.s.fUseFlags |= CPUM_SYNC_FPU_STATE;
175 }
176 else
177#endif
178 {
179#ifndef CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE
180 uint64_t oldMsrEFERHost = 0;
181 uint32_t oldCR0 = ASMGetCR0();
182
183 /* Clear MSR_K6_EFER_FFXSR or else we'll be unable to save/restore the XMM state with fxsave/fxrstor. */
184 if (pVM->cpum.s.CPUFeaturesExt.edx & X86_CPUID_AMD_FEATURE_EDX_FFXSR)
185 {
186 /** @todo Do we really need to read this every time?? The host could change this on the fly though.
187 * bird: what about starting by skipping the ASMWrMsr below if we didn't
188 * change anything? Ditto for the stuff in CPUMR0SaveGuestFPU. */
189 oldMsrEFERHost = ASMRdMsr(MSR_K6_EFER);
190 if (oldMsrEFERHost & MSR_K6_EFER_FFXSR)
191 {
192 ASMWrMsr(MSR_K6_EFER, oldMsrEFERHost & ~MSR_K6_EFER_FFXSR);
193 pVCpu->cpum.s.fUseFlags |= CPUM_MANUAL_XMM_RESTORE;
194 }
195 }
196
197 /* If we sync the FPU/XMM state on-demand, then we can continue execution as if nothing has happened. */
198 int rc = CPUMHandleLazyFPU(pVM, pVCpu);
199 AssertRC(rc);
200 Assert(CPUMIsGuestFPUStateActive(pVCpu));
201
202 /* Restore EFER MSR */
203 if (pVCpu->cpum.s.fUseFlags & CPUM_MANUAL_XMM_RESTORE)
204 ASMWrMsr(MSR_K6_EFER, oldMsrEFERHost);
205
206 /* CPUMHandleLazyFPU could have changed CR0; restore it. */
207 ASMSetCR0(oldCR0);
208
209#else /* CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE */
210
211 /*
212 * Save the FPU control word and MXCSR, so we can restore the state properly afterwards.
213 * We don't want the guest to be able to trigger floating point/SSE exceptions on the host.
214 */
215 pVCpu->cpum.s.Host.fpu.FCW = CPUMGetFCW();
216 if (pVM->cpum.s.CPUFeatures.edx.u1SSE)
217 pVCpu->cpum.s.Host.fpu.MXCSR = CPUMGetMXCSR();
218
219 cpumR0LoadFPU(pCtx);
220
221 /*
222 * The MSR_K6_EFER_FFXSR feature is AMD only so far, but check the cpuid just in case Intel adds it in the future.
223 *
224 * MSR_K6_EFER_FFXSR changes the behaviour of fxsave and fxrstore: the XMM state isn't saved/restored
225 */
226 if (pVM->cpum.s.CPUFeaturesExt.edx & X86_CPUID_AMD_FEATURE_EDX_FFXSR)
227 {
228 /** @todo Do we really need to read this every time?? The host could change this on the fly though. */
229 uint64_t msrEFERHost = ASMRdMsr(MSR_K6_EFER);
230
231 if (msrEFERHost & MSR_K6_EFER_FFXSR)
232 {
233 /* fxrstor doesn't restore the XMM state! */
234 cpumR0LoadXMM(pCtx);
235 pVCpu->cpum.s.fUseFlags |= CPUM_MANUAL_XMM_RESTORE;
236 }
237 }
238#endif /* CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE */
239 }
240
241 pVCpu->cpum.s.fUseFlags |= CPUM_USED_FPU;
242 return VINF_SUCCESS;
243}
244
245
246/**
247 * Save guest FPU/XMM state
248 *
249 * @returns VBox status code.
250 * @param pVM VM handle.
251 * @param pVCpu VMCPU handle.
252 * @param pCtx CPU context
253 */
254VMMR0DECL(int) CPUMR0SaveGuestFPU(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx)
255{
256 Assert(pVM->cpum.s.CPUFeatures.edx.u1FXSR);
257 Assert(ASMGetCR4() & X86_CR4_OSFSXR);
258 AssertReturn((pVCpu->cpum.s.fUseFlags & CPUM_USED_FPU), VINF_SUCCESS);
259
260#if HC_ARCH_BITS == 32 && defined(VBOX_ENABLE_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
261 if (CPUMIsGuestInLongModeEx(pCtx))
262 {
263 if (!(pVCpu->cpum.s.fUseFlags & CPUM_SYNC_FPU_STATE))
264 HWACCMR0SaveFPUState(pVM, pVCpu, pCtx);
265
266 cpumR0RestoreHostFPUState(&pVCpu->cpum.s);
267 }
268 else
269#endif
270 {
271#ifndef CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE
272 uint64_t oldMsrEFERHost = 0;
273
274 /* Clear MSR_K6_EFER_FFXSR or else we'll be unable to save/restore the XMM state with fxsave/fxrstor. */
275 if (pVCpu->cpum.s.fUseFlags & CPUM_MANUAL_XMM_RESTORE)
276 {
277 oldMsrEFERHost = ASMRdMsr(MSR_K6_EFER);
278 ASMWrMsr(MSR_K6_EFER, oldMsrEFERHost & ~MSR_K6_EFER_FFXSR);
279 }
280 cpumR0SaveGuestRestoreHostFPUState(&pVCpu->cpum.s);
281
282 /* Restore EFER MSR */
283 if (pVCpu->cpum.s.fUseFlags & CPUM_MANUAL_XMM_RESTORE)
284 ASMWrMsr(MSR_K6_EFER, oldMsrEFERHost | MSR_K6_EFER_FFXSR);
285
286#else /* CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE */
287 cpumR0SaveFPU(pCtx);
288 if (pVCpu->cpum.s.fUseFlags & CPUM_MANUAL_XMM_RESTORE)
289 {
290 /* fxsave doesn't save the XMM state! */
291 cpumR0SaveXMM(pCtx);
292 }
293
294 /*
295 * Restore the original FPU control word and MXCSR.
296 * We don't want the guest to be able to trigger floating point/SSE exceptions on the host.
297 */
298 cpumR0SetFCW(pVCpu->cpum.s.Host.fpu.FCW);
299 if (pVM->cpum.s.CPUFeatures.edx.u1SSE)
300 cpumR0SetMXCSR(pVCpu->cpum.s.Host.fpu.MXCSR);
301#endif /* CPUM_CAN_HANDLE_NM_TRAPS_IN_KERNEL_MODE */
302 }
303
304 pVCpu->cpum.s.fUseFlags &= ~(CPUM_USED_FPU | CPUM_MANUAL_XMM_RESTORE);
305 return VINF_SUCCESS;
306}
307
308
309/**
310 * Save guest debug state
311 *
312 * @returns VBox status code.
313 * @param pVM VM handle.
314 * @param pVCpu VMCPU handle.
315 * @param pCtx CPU context
316 * @param fDR6 Include DR6 or not
317 */
318VMMR0DECL(int) CPUMR0SaveGuestDebugState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, bool fDR6)
319{
320 Assert(pVCpu->cpum.s.fUseFlags & CPUM_USE_DEBUG_REGS);
321
322 /* Save the guest's debug state. The caller is responsible for DR7. */
323#if HC_ARCH_BITS == 32 && defined(VBOX_ENABLE_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
324 if (CPUMIsGuestInLongModeEx(pCtx))
325 {
326 if (!(pVCpu->cpum.s.fUseFlags & CPUM_SYNC_DEBUG_STATE))
327 {
328 uint64_t dr6 = pCtx->dr[6];
329
330 HWACCMR0SaveDebugState(pVM, pVCpu, pCtx);
331 if (!fDR6) /* dr6 was already up-to-date */
332 pCtx->dr[6] = dr6;
333 }
334 }
335 else
336#endif
337 {
338#ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
339 cpumR0SaveDRx(&pCtx->dr[0]);
340#else
341 pCtx->dr[0] = ASMGetDR0();
342 pCtx->dr[1] = ASMGetDR1();
343 pCtx->dr[2] = ASMGetDR2();
344 pCtx->dr[3] = ASMGetDR3();
345#endif
346 if (fDR6)
347 pCtx->dr[6] = ASMGetDR6();
348 }
349
350 /*
351 * Restore the host's debug state. DR0-3, DR6 and only then DR7!
352 * DR7 contains 0x400 right now.
353 */
354#ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
355 AssertCompile((uintptr_t)&pVCpu->cpum.s.Host.dr3 - (uintptr_t)&pVCpu->cpum.s.Host.dr0 == sizeof(uint64_t) * 3);
356 cpumR0LoadDRx(&pVCpu->cpum.s.Host.dr0);
357#else
358 ASMSetDR0(pVCpu->cpum.s.Host.dr0);
359 ASMSetDR1(pVCpu->cpum.s.Host.dr1);
360 ASMSetDR2(pVCpu->cpum.s.Host.dr2);
361 ASMSetDR3(pVCpu->cpum.s.Host.dr3);
362#endif
363 ASMSetDR6(pVCpu->cpum.s.Host.dr6);
364 ASMSetDR7(pVCpu->cpum.s.Host.dr7);
365
366 pVCpu->cpum.s.fUseFlags &= ~CPUM_USE_DEBUG_REGS;
367 return VINF_SUCCESS;
368}
369
370
371/**
372 * Lazily sync in the debug state
373 *
374 * @returns VBox status code.
375 * @param pVM VM handle.
376 * @param pVCpu VMCPU handle.
377 * @param pCtx CPU context
378 * @param fDR6 Include DR6 or not
379 */
380VMMR0DECL(int) CPUMR0LoadGuestDebugState(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, bool fDR6)
381{
382 /* Save the host state. */
383#ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
384 AssertCompile((uintptr_t)&pVCpu->cpum.s.Host.dr3 - (uintptr_t)&pVCpu->cpum.s.Host.dr0 == sizeof(uint64_t) * 3);
385 cpumR0SaveDRx(&pVCpu->cpum.s.Host.dr0);
386#else
387 pVCpu->cpum.s.Host.dr0 = ASMGetDR0();
388 pVCpu->cpum.s.Host.dr1 = ASMGetDR1();
389 pVCpu->cpum.s.Host.dr2 = ASMGetDR2();
390 pVCpu->cpum.s.Host.dr3 = ASMGetDR3();
391#endif
392 pVCpu->cpum.s.Host.dr6 = ASMGetDR6();
393 /** @todo dr7 might already have been changed to 0x400; don't care right now as it's harmless. */
394 pVCpu->cpum.s.Host.dr7 = ASMGetDR7();
395 /* Make sure DR7 is harmless or else we could trigger breakpoints when restoring dr0-3 (!) */
396 ASMSetDR7(X86_DR7_INIT_VAL);
397
398 /* Activate the guest state DR0-3; DR7 is left to the caller. */
399#if HC_ARCH_BITS == 32 && defined(VBOX_ENABLE_64_BITS_GUESTS) && !defined(VBOX_WITH_HYBRID_32BIT_KERNEL)
400 if (CPUMIsGuestInLongModeEx(pCtx))
401 {
402 /* Restore the state on entry as we need to be in 64 bits mode to access the full state. */
403 pVCpu->cpum.s.fUseFlags |= CPUM_SYNC_DEBUG_STATE;
404 }
405 else
406#endif
407 {
408#ifdef VBOX_WITH_HYBRID_32BIT_KERNEL
409 cpumR0LoadDRx(&pCtx->dr[0]);
410#else
411 ASMSetDR0(pCtx->dr[0]);
412 ASMSetDR1(pCtx->dr[1]);
413 ASMSetDR2(pCtx->dr[2]);
414 ASMSetDR3(pCtx->dr[3]);
415#endif
416 if (fDR6)
417 ASMSetDR6(pCtx->dr[6]);
418 }
419
420 pVCpu->cpum.s.fUseFlags |= CPUM_USE_DEBUG_REGS;
421 return VINF_SUCCESS;
422}
423
424
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