VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/HMAll.cpp@ 47619

Last change on this file since 47619 was 47619, checked in by vboxsync, 11 years ago

EM: Started on HM single stepping for IEM verification purposes. Trying to fix the HM debugging in the proccess. VT-x only atm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.8 KB
Line 
1/* $Id: HMAll.cpp 47619 2013-08-08 19:06:45Z vboxsync $ */
2/** @file
3 * HM - All contexts.
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_HM
23#include <VBox/vmm/hm.h>
24#include <VBox/vmm/pgm.h>
25#include "HMInternal.h"
26#include <VBox/vmm/vm.h>
27#include <VBox/vmm/hm_vmx.h>
28#include <VBox/vmm/hm_svm.h>
29#include <VBox/err.h>
30#include <VBox/log.h>
31#include <iprt/param.h>
32#include <iprt/assert.h>
33#include <iprt/asm.h>
34#include <iprt/string.h>
35#include <iprt/thread.h>
36#include <iprt/x86.h>
37#include <iprt/asm-amd64-x86.h>
38
39
40
41/**
42 * Checks whether HM (VT-x/AMD-V) is being used by this VM.
43 *
44 * @retval @c true if used.
45 * @retval @c false if software virtualization (raw-mode) is used.
46 * @param pVM The cross context VM structure.
47 * @sa HMIsEnabled, HMR3IsEnabled
48 * @internal
49 */
50VMMDECL(bool) HMIsEnabledNotMacro(PVM pVM)
51{
52 Assert(pVM->fHMEnabledFixed);
53 return pVM->fHMEnabled;
54}
55
56
57/**
58 * Queues a page for invalidation
59 *
60 * @returns VBox status code.
61 * @param pVCpu Pointer to the VMCPU.
62 * @param GCVirt Page to invalidate
63 */
64static void hmQueueInvlPage(PVMCPU pVCpu, RTGCPTR GCVirt)
65{
66 /* Nothing to do if a TLB flush is already pending */
67 if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_TLB_FLUSH))
68 return;
69#if 1
70 VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_FLUSH);
71 NOREF(GCVirt);
72#else
73 /* Be very careful when activating this code! */
74 if (iPage == RT_ELEMENTS(pVCpu->hm.s.TlbShootdown.aPages))
75 VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_FLUSH);
76 else
77 VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_SHOOTDOWN);
78#endif
79}
80
81/**
82 * Invalidates a guest page
83 *
84 * @returns VBox status code.
85 * @param pVCpu Pointer to the VMCPU.
86 * @param GCVirt Page to invalidate
87 */
88VMM_INT_DECL(int) HMInvalidatePage(PVMCPU pVCpu, RTGCPTR GCVirt)
89{
90 STAM_COUNTER_INC(&pVCpu->hm.s.StatFlushPageManual);
91#ifdef IN_RING0
92 PVM pVM = pVCpu->CTX_SUFF(pVM);
93 if (pVM->hm.s.vmx.fSupported)
94 return VMXR0InvalidatePage(pVM, pVCpu, GCVirt);
95
96 Assert(pVM->hm.s.svm.fSupported);
97 return SVMR0InvalidatePage(pVM, pVCpu, GCVirt);
98
99#else
100 hmQueueInvlPage(pVCpu, GCVirt);
101 return VINF_SUCCESS;
102#endif
103}
104
105/**
106 * Flushes the guest TLB.
107 *
108 * @returns VBox status code.
109 * @param pVCpu Pointer to the VMCPU.
110 */
111VMM_INT_DECL(int) HMFlushTLB(PVMCPU pVCpu)
112{
113 LogFlow(("HMFlushTLB\n"));
114
115 VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_FLUSH);
116 STAM_COUNTER_INC(&pVCpu->hm.s.StatFlushTlbManual);
117 return VINF_SUCCESS;
118}
119
120#ifdef IN_RING0
121
122/**
123 * Dummy RTMpOnSpecific handler since RTMpPokeCpu couldn't be used.
124 *
125 */
126static DECLCALLBACK(void) hmFlushHandler(RTCPUID idCpu, void *pvUser1, void *pvUser2)
127{
128 NOREF(idCpu); NOREF(pvUser1); NOREF(pvUser2);
129 return;
130}
131
132/**
133 * Wrapper for RTMpPokeCpu to deal with VERR_NOT_SUPPORTED.
134 */
135static void hmR0PokeCpu(PVMCPU pVCpu, RTCPUID idHostCpu)
136{
137 uint32_t cWorldSwitchExits = ASMAtomicUoReadU32(&pVCpu->hm.s.cWorldSwitchExits);
138
139 STAM_PROFILE_ADV_START(&pVCpu->hm.s.StatPoke, x);
140 int rc = RTMpPokeCpu(idHostCpu);
141 STAM_PROFILE_ADV_STOP(&pVCpu->hm.s.StatPoke, x);
142
143 /* Not implemented on some platforms (Darwin, Linux kernel < 2.6.19); fall
144 back to a less efficient implementation (broadcast). */
145 if (rc == VERR_NOT_SUPPORTED)
146 {
147 STAM_PROFILE_ADV_START(&pVCpu->hm.s.StatSpinPoke, z);
148 /* synchronous. */
149 RTMpOnSpecific(idHostCpu, hmFlushHandler, 0, 0);
150 STAM_PROFILE_ADV_STOP(&pVCpu->hm.s.StatSpinPoke, z);
151 }
152 else
153 {
154 if (rc == VINF_SUCCESS)
155 STAM_PROFILE_ADV_START(&pVCpu->hm.s.StatSpinPoke, z);
156 else
157 STAM_PROFILE_ADV_START(&pVCpu->hm.s.StatSpinPokeFailed, z);
158
159/** @todo If more than one CPU is going to be poked, we could optimize this
160 * operation by poking them first and wait afterwards. Would require
161 * recording who to poke and their current cWorldSwitchExits values,
162 * that's something not suitable for stack... So, pVCpu->hm.s.something
163 * then. */
164 /* Spin until the VCPU has switched back (poking is async). */
165 while ( ASMAtomicUoReadBool(&pVCpu->hm.s.fCheckedTLBFlush)
166 && cWorldSwitchExits == ASMAtomicUoReadU32(&pVCpu->hm.s.cWorldSwitchExits))
167 ASMNopPause();
168
169 if (rc == VINF_SUCCESS)
170 STAM_PROFILE_ADV_STOP(&pVCpu->hm.s.StatSpinPoke, z);
171 else
172 STAM_PROFILE_ADV_STOP(&pVCpu->hm.s.StatSpinPokeFailed, z);
173 }
174}
175
176#endif /* IN_RING0 */
177#ifndef IN_RC
178
179/**
180 * Poke an EMT so it can perform the appropriate TLB shootdowns.
181 *
182 * @param pVCpu The handle of the virtual CPU to poke.
183 * @param fAccountFlushStat Whether to account the call to
184 * StatTlbShootdownFlush or StatTlbShootdown.
185 */
186static void hmPokeCpuForTlbFlush(PVMCPU pVCpu, bool fAccountFlushStat)
187{
188 if (ASMAtomicUoReadBool(&pVCpu->hm.s.fCheckedTLBFlush))
189 {
190 if (fAccountFlushStat)
191 STAM_COUNTER_INC(&pVCpu->hm.s.StatTlbShootdownFlush);
192 else
193 STAM_COUNTER_INC(&pVCpu->hm.s.StatTlbShootdown);
194#ifdef IN_RING0
195 RTCPUID idHostCpu = pVCpu->hm.s.idEnteredCpu;
196 if (idHostCpu != NIL_RTCPUID)
197 hmR0PokeCpu(pVCpu, idHostCpu);
198#else
199 VMR3NotifyCpuFFU(pVCpu->pUVCpu, VMNOTIFYFF_FLAGS_POKE);
200#endif
201 }
202 else
203 STAM_COUNTER_INC(&pVCpu->hm.s.StatFlushPageManual);
204}
205
206
207/**
208 * Invalidates a guest page on all VCPUs.
209 *
210 * @returns VBox status code.
211 * @param pVM Pointer to the VM.
212 * @param GCVirt Page to invalidate
213 */
214VMM_INT_DECL(int) HMInvalidatePageOnAllVCpus(PVM pVM, RTGCPTR GCPtr)
215{
216 VMCPUID idCurCpu = VMMGetCpuId(pVM);
217 STAM_COUNTER_INC(&pVM->aCpus[idCurCpu].hm.s.StatFlushPage);
218
219 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
220 {
221 PVMCPU pVCpu = &pVM->aCpus[idCpu];
222
223 /* Nothing to do if a TLB flush is already pending; the VCPU should
224 have already been poked if it were active. */
225 if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_TLB_FLUSH))
226 continue;
227
228 if (pVCpu->idCpu == idCurCpu)
229 HMInvalidatePage(pVCpu, GCPtr);
230 else
231 {
232 hmQueueInvlPage(pVCpu, GCPtr);
233 hmPokeCpuForTlbFlush(pVCpu, false /* fAccountFlushStat */);
234 }
235 }
236
237 return VINF_SUCCESS;
238}
239
240
241/**
242 * Flush the TLBs of all VCPUs.
243 *
244 * @returns VBox status code.
245 * @param pVM Pointer to the VM.
246 */
247VMM_INT_DECL(int) HMFlushTLBOnAllVCpus(PVM pVM)
248{
249 if (pVM->cCpus == 1)
250 return HMFlushTLB(&pVM->aCpus[0]);
251
252 VMCPUID idThisCpu = VMMGetCpuId(pVM);
253
254 STAM_COUNTER_INC(&pVM->aCpus[idThisCpu].hm.s.StatFlushTlb);
255
256 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
257 {
258 PVMCPU pVCpu = &pVM->aCpus[idCpu];
259
260 /* Nothing to do if a TLB flush is already pending; the VCPU should
261 have already been poked if it were active. */
262 if (!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_TLB_FLUSH))
263 {
264 VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_FLUSH);
265 if (idThisCpu != idCpu)
266 hmPokeCpuForTlbFlush(pVCpu, true /* fAccountFlushStat */);
267 }
268 }
269
270 return VINF_SUCCESS;
271}
272
273#endif /* !IN_RC */
274
275/**
276 * Checks if nested paging is enabled
277 *
278 * @returns boolean
279 * @param pVM Pointer to the VM.
280 */
281VMM_INT_DECL(bool) HMIsNestedPagingActive(PVM pVM)
282{
283 return HMIsEnabled(pVM) && pVM->hm.s.fNestedPaging;
284}
285
286/**
287 * Return the shadow paging mode for nested paging/ept
288 *
289 * @returns shadow paging mode
290 * @param pVM Pointer to the VM.
291 */
292VMM_INT_DECL(PGMMODE) HMGetShwPagingMode(PVM pVM)
293{
294 Assert(HMIsNestedPagingActive(pVM));
295 if (pVM->hm.s.svm.fSupported)
296 return PGMMODE_NESTED;
297
298 Assert(pVM->hm.s.vmx.fSupported);
299 return PGMMODE_EPT;
300}
301
302/**
303 * Invalidates a guest page by physical address
304 *
305 * NOTE: Assumes the current instruction references this physical page though a virtual address!!
306 *
307 * @returns VBox status code.
308 * @param pVM Pointer to the VM.
309 * @param GCPhys Page to invalidate
310 */
311VMM_INT_DECL(int) HMInvalidatePhysPage(PVM pVM, RTGCPHYS GCPhys)
312{
313 if (!HMIsNestedPagingActive(pVM))
314 return VINF_SUCCESS;
315
316#ifdef IN_RING0
317 if (pVM->hm.s.vmx.fSupported)
318 {
319 VMCPUID idThisCpu = VMMGetCpuId(pVM);
320
321 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
322 {
323 PVMCPU pVCpu = &pVM->aCpus[idCpu];
324
325 if (idThisCpu == idCpu)
326 {
327 /** @todo r=ramshankar: Intel does not support flushing by guest physical
328 * address either. See comment in VMXR0InvalidatePhysPage(). Fix this. */
329 VMXR0InvalidatePhysPage(pVM, pVCpu, GCPhys);
330 }
331 else
332 {
333 VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_FLUSH);
334 hmPokeCpuForTlbFlush(pVCpu, true /*fAccountFlushStat*/);
335 }
336 }
337 return VINF_SUCCESS;
338 }
339
340 /* AMD-V doesn't support invalidation with guest physical addresses; see
341 comment in SVMR0InvalidatePhysPage. */
342 Assert(pVM->hm.s.svm.fSupported);
343#else
344 NOREF(GCPhys);
345#endif
346
347 HMFlushTLBOnAllVCpus(pVM);
348 return VINF_SUCCESS;
349}
350
351/**
352 * Checks if an interrupt event is currently pending.
353 *
354 * @returns Interrupt event pending state.
355 * @param pVM Pointer to the VM.
356 */
357VMM_INT_DECL(bool) HMHasPendingIrq(PVM pVM)
358{
359 PVMCPU pVCpu = VMMGetCpu(pVM);
360 return !!pVCpu->hm.s.Event.fPending;
361}
362
363
364/**
365 * Return the PAE PDPE entries.
366 *
367 * @returns Pointer to the PAE PDPE array.
368 * @param pVCpu Pointer to the VMCPU.
369 */
370VMM_INT_DECL(PX86PDPE) HMGetPaePdpes(PVMCPU pVCpu)
371{
372 return &pVCpu->hm.s.aPdpes[0];
373}
374
375
376/**
377 * Checks if the current AMD CPU is subject to erratum 170 "In SVM mode,
378 * incorrect code bytes may be fetched after a world-switch".
379 *
380 * @param pu32Family Where to store the CPU family (can be NULL).
381 * @param pu32Model Where to store the CPU model (can be NULL).
382 * @param pu32Stepping Where to store the CPU stepping (can be NULL).
383 * @returns true if the erratum applies, false otherwise.
384 */
385VMM_INT_DECL(int) HMAmdIsSubjectToErratum170(uint32_t *pu32Family, uint32_t *pu32Model, uint32_t *pu32Stepping)
386{
387 /*
388 * Erratum 170 which requires a forced TLB flush for each world switch:
389 * See AMD spec. "Revision Guide for AMD NPT Family 0Fh Processors".
390 *
391 * All BH-G1/2 and DH-G1/2 models include a fix:
392 * Athlon X2: 0x6b 1/2
393 * 0x68 1/2
394 * Athlon 64: 0x7f 1
395 * 0x6f 2
396 * Sempron: 0x7f 1/2
397 * 0x6f 2
398 * 0x6c 2
399 * 0x7c 2
400 * Turion 64: 0x68 2
401 */
402 uint32_t u32Dummy;
403 uint32_t u32Version, u32Family, u32Model, u32Stepping, u32BaseFamily;
404 ASMCpuId(1, &u32Version, &u32Dummy, &u32Dummy, &u32Dummy);
405 u32BaseFamily = (u32Version >> 8) & 0xf;
406 u32Family = u32BaseFamily + (u32BaseFamily == 0xf ? ((u32Version >> 20) & 0x7f) : 0);
407 u32Model = ((u32Version >> 4) & 0xf);
408 u32Model = u32Model | ((u32BaseFamily == 0xf ? (u32Version >> 16) & 0x0f : 0) << 4);
409 u32Stepping = u32Version & 0xf;
410
411 bool fErratumApplies = false;
412 if ( u32Family == 0xf
413 && !((u32Model == 0x68 || u32Model == 0x6b || u32Model == 0x7f) && u32Stepping >= 1)
414 && !((u32Model == 0x6f || u32Model == 0x6c || u32Model == 0x7c) && u32Stepping >= 2))
415 {
416 fErratumApplies = true;
417 }
418
419 if (pu32Family)
420 *pu32Family = u32Family;
421 if (pu32Model)
422 *pu32Model = u32Model;
423 if (pu32Stepping)
424 *pu32Stepping = u32Stepping;
425
426 return fErratumApplies;
427}
428
429
430/**
431 * Sets or clears the single instruction flag.
432 *
433 * When set, HM will try its best to return to ring-3 after executing a single
434 * instruction. This can be used for debugging. See also
435 * EMR3HmSingleInstruction.
436 *
437 * @returns The old flag state.
438 * @param pVCpu Pointer to the cross context CPU structure of
439 * the calling EMT.
440 * @param fEnable The new flag state.
441 */
442VMM_INT_DECL(bool) HMSetSingleInstruction(PVMCPU pVCpu, bool fEnable)
443{
444 VMCPU_ASSERT_EMT(pVCpu);
445 bool fOld = pVCpu->hm.s.fSingleInstruction;
446 pVCpu->hm.s.fSingleInstruction = fEnable;
447 return fOld;
448}
449
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