VirtualBox

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

Last change on this file since 72255 was 71910, checked in by vboxsync, 7 years ago

VMM/HM: Clean up and also fix HMR0EnsureCompleteBasicContext for AMD-V which also selectively re-loads guest state back into the VMCB.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.2 KB
Line 
1/* $Id: HMAll.cpp 71910 2018-04-19 08:54:23Z vboxsync $ */
2/** @file
3 * HM - All contexts.
4 */
5
6/*
7 * Copyright (C) 2006-2017 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 * Checks whether HM (VT-x/AMD-V) is being used by this VM.
42 *
43 * @retval true if used.
44 * @retval false if software virtualization (raw-mode) is used.
45 * @param pVM The cross context VM structure.
46 * @sa HMIsEnabled, HMR3IsEnabled
47 * @internal
48 */
49VMMDECL(bool) HMIsEnabledNotMacro(PVM pVM)
50{
51 Assert(pVM->bMainExecutionEngine != VM_EXEC_ENGINE_NOT_SET);
52 return pVM->fHMEnabled;
53}
54
55
56/**
57 * Queues a guest page for invalidation.
58 *
59 * @returns VBox status code.
60 * @param pVCpu The cross context virtual CPU structure.
61 * @param GCVirt Page to invalidate.
62 */
63static void hmQueueInvlPage(PVMCPU pVCpu, RTGCPTR GCVirt)
64{
65 /* Nothing to do if a TLB flush is already pending */
66 if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_TLB_FLUSH))
67 return;
68 VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_FLUSH);
69 NOREF(GCVirt);
70}
71
72
73/**
74 * Invalidates a guest page.
75 *
76 * @returns VBox status code.
77 * @param pVCpu The cross context virtual CPU structure.
78 * @param GCVirt Page to invalidate.
79 */
80VMM_INT_DECL(int) HMInvalidatePage(PVMCPU pVCpu, RTGCPTR GCVirt)
81{
82 STAM_COUNTER_INC(&pVCpu->hm.s.StatFlushPageManual);
83#ifdef IN_RING0
84 PVM pVM = pVCpu->CTX_SUFF(pVM);
85 if (pVM->hm.s.vmx.fSupported)
86 return VMXR0InvalidatePage(pVM, pVCpu, GCVirt);
87
88 Assert(pVM->hm.s.svm.fSupported);
89 return SVMR0InvalidatePage(pVM, pVCpu, GCVirt);
90
91#else
92 hmQueueInvlPage(pVCpu, GCVirt);
93 return VINF_SUCCESS;
94#endif
95}
96
97
98#ifdef IN_RING0
99
100/**
101 * Dummy RTMpOnSpecific handler since RTMpPokeCpu couldn't be used.
102 *
103 */
104static DECLCALLBACK(void) hmFlushHandler(RTCPUID idCpu, void *pvUser1, void *pvUser2)
105{
106 NOREF(idCpu); NOREF(pvUser1); NOREF(pvUser2);
107 return;
108}
109
110
111/**
112 * Wrapper for RTMpPokeCpu to deal with VERR_NOT_SUPPORTED.
113 */
114static void hmR0PokeCpu(PVMCPU pVCpu, RTCPUID idHostCpu)
115{
116 uint32_t cWorldSwitchExits = ASMAtomicUoReadU32(&pVCpu->hm.s.cWorldSwitchExits);
117
118 STAM_PROFILE_ADV_START(&pVCpu->hm.s.StatPoke, x);
119 int rc = RTMpPokeCpu(idHostCpu);
120 STAM_PROFILE_ADV_STOP(&pVCpu->hm.s.StatPoke, x);
121
122 /* Not implemented on some platforms (Darwin, Linux kernel < 2.6.19); fall
123 back to a less efficient implementation (broadcast). */
124 if (rc == VERR_NOT_SUPPORTED)
125 {
126 STAM_PROFILE_ADV_START(&pVCpu->hm.s.StatSpinPoke, z);
127 /* synchronous. */
128 RTMpOnSpecific(idHostCpu, hmFlushHandler, 0, 0);
129 STAM_PROFILE_ADV_STOP(&pVCpu->hm.s.StatSpinPoke, z);
130 }
131 else
132 {
133 if (rc == VINF_SUCCESS)
134 STAM_PROFILE_ADV_START(&pVCpu->hm.s.StatSpinPoke, z);
135 else
136 STAM_PROFILE_ADV_START(&pVCpu->hm.s.StatSpinPokeFailed, z);
137
138/** @todo If more than one CPU is going to be poked, we could optimize this
139 * operation by poking them first and wait afterwards. Would require
140 * recording who to poke and their current cWorldSwitchExits values,
141 * that's something not suitable for stack... So, pVCpu->hm.s.something
142 * then. */
143 /* Spin until the VCPU has switched back (poking is async). */
144 while ( ASMAtomicUoReadBool(&pVCpu->hm.s.fCheckedTLBFlush)
145 && cWorldSwitchExits == ASMAtomicUoReadU32(&pVCpu->hm.s.cWorldSwitchExits))
146 ASMNopPause();
147
148 if (rc == VINF_SUCCESS)
149 STAM_PROFILE_ADV_STOP(&pVCpu->hm.s.StatSpinPoke, z);
150 else
151 STAM_PROFILE_ADV_STOP(&pVCpu->hm.s.StatSpinPokeFailed, z);
152 }
153}
154
155#endif /* IN_RING0 */
156#ifndef IN_RC
157/**
158 * Flushes the guest TLB.
159 *
160 * @returns VBox status code.
161 * @param pVCpu The cross context virtual CPU structure.
162 */
163VMM_INT_DECL(int) HMFlushTLB(PVMCPU pVCpu)
164{
165 LogFlow(("HMFlushTLB\n"));
166
167 VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_FLUSH);
168 STAM_COUNTER_INC(&pVCpu->hm.s.StatFlushTlbManual);
169 return VINF_SUCCESS;
170}
171
172/**
173 * Poke an EMT so it can perform the appropriate TLB shootdowns.
174 *
175 * @param pVCpu The cross context virtual CPU structure of the
176 * EMT poke.
177 * @param fAccountFlushStat Whether to account the call to
178 * StatTlbShootdownFlush or StatTlbShootdown.
179 */
180static void hmPokeCpuForTlbFlush(PVMCPU pVCpu, bool fAccountFlushStat)
181{
182 if (ASMAtomicUoReadBool(&pVCpu->hm.s.fCheckedTLBFlush))
183 {
184 if (fAccountFlushStat)
185 STAM_COUNTER_INC(&pVCpu->hm.s.StatTlbShootdownFlush);
186 else
187 STAM_COUNTER_INC(&pVCpu->hm.s.StatTlbShootdown);
188#ifdef IN_RING0
189 RTCPUID idHostCpu = pVCpu->hm.s.idEnteredCpu;
190 if (idHostCpu != NIL_RTCPUID)
191 hmR0PokeCpu(pVCpu, idHostCpu);
192#else
193 VMR3NotifyCpuFFU(pVCpu->pUVCpu, VMNOTIFYFF_FLAGS_POKE);
194#endif
195 }
196 else
197 STAM_COUNTER_INC(&pVCpu->hm.s.StatFlushPageManual);
198}
199
200
201/**
202 * Invalidates a guest page on all VCPUs.
203 *
204 * @returns VBox status code.
205 * @param pVM The cross context VM structure.
206 * @param GCVirt Page to invalidate.
207 */
208VMM_INT_DECL(int) HMInvalidatePageOnAllVCpus(PVM pVM, RTGCPTR GCVirt)
209{
210 /*
211 * The VT-x/AMD-V code will be flushing TLB each time a VCPU migrates to a different
212 * host CPU, see hmR0VmxFlushTaggedTlbBoth() and hmR0SvmFlushTaggedTlb().
213 *
214 * This is the reason why we do not care about thread preemption here and just
215 * execute HMInvalidatePage() assuming it might be the 'right' CPU.
216 */
217 VMCPUID idCurCpu = VMMGetCpuId(pVM);
218 STAM_COUNTER_INC(&pVM->aCpus[idCurCpu].hm.s.StatFlushPage);
219
220 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
221 {
222 PVMCPU pVCpu = &pVM->aCpus[idCpu];
223
224 /* Nothing to do if a TLB flush is already pending; the VCPU should
225 have already been poked if it were active. */
226 if (VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_TLB_FLUSH))
227 continue;
228
229 if (pVCpu->idCpu == idCurCpu)
230 HMInvalidatePage(pVCpu, GCVirt);
231 else
232 {
233 hmQueueInvlPage(pVCpu, GCVirt);
234 hmPokeCpuForTlbFlush(pVCpu, false /* fAccountFlushStat */);
235 }
236 }
237
238 return VINF_SUCCESS;
239}
240
241
242/**
243 * Flush the TLBs of all VCPUs.
244 *
245 * @returns VBox status code.
246 * @param pVM The cross context VM structure.
247 */
248VMM_INT_DECL(int) HMFlushTLBOnAllVCpus(PVM pVM)
249{
250 if (pVM->cCpus == 1)
251 return HMFlushTLB(&pVM->aCpus[0]);
252
253 VMCPUID idThisCpu = VMMGetCpuId(pVM);
254
255 STAM_COUNTER_INC(&pVM->aCpus[idThisCpu].hm.s.StatFlushTlb);
256
257 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
258 {
259 PVMCPU pVCpu = &pVM->aCpus[idCpu];
260
261 /* Nothing to do if a TLB flush is already pending; the VCPU should
262 have already been poked if it were active. */
263 if (!VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_TLB_FLUSH))
264 {
265 VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_FLUSH);
266 if (idThisCpu != idCpu)
267 hmPokeCpuForTlbFlush(pVCpu, true /* fAccountFlushStat */);
268 }
269 }
270
271 return VINF_SUCCESS;
272}
273
274
275/**
276 * Invalidates a guest page by physical address.
277 *
278 * @returns VBox status code.
279 * @param pVM The cross context VM structure.
280 * @param GCPhys Page to invalidate.
281 *
282 * @remarks Assumes the current instruction references this physical page
283 * though a virtual address!
284 */
285VMM_INT_DECL(int) HMInvalidatePhysPage(PVM pVM, RTGCPHYS GCPhys)
286{
287 if (!HMIsNestedPagingActive(pVM))
288 return VINF_SUCCESS;
289
290#ifdef IN_RING0
291 if (pVM->hm.s.vmx.fSupported)
292 {
293 VMCPUID idThisCpu = VMMGetCpuId(pVM);
294
295 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
296 {
297 PVMCPU pVCpu = &pVM->aCpus[idCpu];
298
299 if (idThisCpu == idCpu)
300 {
301 /** @todo r=ramshankar: Intel does not support flushing by guest physical
302 * address either. See comment in VMXR0InvalidatePhysPage(). Fix this. */
303 VMXR0InvalidatePhysPage(pVM, pVCpu, GCPhys);
304 }
305 else
306 {
307 VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_FLUSH);
308 hmPokeCpuForTlbFlush(pVCpu, true /*fAccountFlushStat*/);
309 }
310 }
311 return VINF_SUCCESS;
312 }
313
314 /* AMD-V doesn't support invalidation with guest physical addresses; see
315 comment in SVMR0InvalidatePhysPage. */
316 Assert(pVM->hm.s.svm.fSupported);
317#else
318 NOREF(GCPhys);
319#endif
320
321 HMFlushTLBOnAllVCpus(pVM);
322 return VINF_SUCCESS;
323}
324
325
326/**
327 * Checks if nested paging is enabled.
328 *
329 * @returns true if nested paging is active, false otherwise.
330 * @param pVM The cross context VM structure.
331 *
332 * @remarks Works before hmR3InitFinalizeR0.
333 */
334VMM_INT_DECL(bool) HMIsNestedPagingActive(PVM pVM)
335{
336 return HMIsEnabled(pVM) && pVM->hm.s.fNestedPaging;
337}
338
339
340/**
341 * Checks if both nested paging and unhampered guest execution are enabled.
342 *
343 * The almost complete guest execution in hardware is only applicable to VT-x.
344 *
345 * @returns true if we have both enabled, otherwise false.
346 * @param pVM The cross context VM structure.
347 *
348 * @remarks Works before hmR3InitFinalizeR0.
349 */
350VMM_INT_DECL(bool) HMAreNestedPagingAndFullGuestExecEnabled(PVM pVM)
351{
352 return HMIsEnabled(pVM)
353 && pVM->hm.s.fNestedPaging
354 && ( pVM->hm.s.vmx.fUnrestrictedGuest
355 || pVM->hm.s.svm.fSupported);
356}
357
358
359/**
360 * Checks if this VM is long-mode capable.
361 *
362 * @returns true if long mode is allowed, false otherwise.
363 * @param pVM The cross context VM structure.
364 */
365/** @todo NEM: Check users of HMIsLongModeAllowed */
366VMM_INT_DECL(bool) HMIsLongModeAllowed(PVM pVM)
367{
368 return HMIsEnabled(pVM) && pVM->hm.s.fAllow64BitGuests;
369}
370
371
372/**
373 * Checks if MSR bitmaps are available. It is assumed that when it's available
374 * it will be used as well.
375 *
376 * @returns true if MSR bitmaps are available, false otherwise.
377 * @param pVM The cross context VM structure.
378 */
379VMM_INT_DECL(bool) HMAreMsrBitmapsAvailable(PVM pVM)
380{
381 if (HMIsEnabled(pVM))
382 {
383 if (pVM->hm.s.svm.fSupported)
384 return true;
385
386 if ( pVM->hm.s.vmx.fSupported
387 && (pVM->hm.s.vmx.Msrs.VmxProcCtls.n.allowed1 & VMX_VMCS_CTRL_PROC_EXEC_USE_MSR_BITMAPS))
388 {
389 return true;
390 }
391 }
392 return false;
393}
394
395
396/**
397 * Return the shadow paging mode for nested paging/ept
398 *
399 * @returns shadow paging mode
400 * @param pVM The cross context VM structure.
401 */
402VMM_INT_DECL(PGMMODE) HMGetShwPagingMode(PVM pVM)
403{
404 Assert(HMIsNestedPagingActive(pVM));
405 if (pVM->hm.s.svm.fSupported)
406 return PGMMODE_NESTED;
407
408 Assert(pVM->hm.s.vmx.fSupported);
409 return PGMMODE_EPT;
410}
411#endif /* !IN_RC */
412
413
414/**
415 * Checks if an interrupt event is currently pending.
416 *
417 * @returns Interrupt event pending state.
418 * @param pVM The cross context VM structure.
419 */
420VMM_INT_DECL(bool) HMHasPendingIrq(PVM pVM)
421{
422 PVMCPU pVCpu = VMMGetCpu(pVM);
423 return !!pVCpu->hm.s.Event.fPending;
424}
425
426
427/**
428 * Return the PAE PDPE entries.
429 *
430 * @returns Pointer to the PAE PDPE array.
431 * @param pVCpu The cross context virtual CPU structure.
432 */
433VMM_INT_DECL(PX86PDPE) HMGetPaePdpes(PVMCPU pVCpu)
434{
435 return &pVCpu->hm.s.aPdpes[0];
436}
437
438
439/**
440 * Checks if the current AMD CPU is subject to erratum 170 "In SVM mode,
441 * incorrect code bytes may be fetched after a world-switch".
442 *
443 * @param pu32Family Where to store the CPU family (can be NULL).
444 * @param pu32Model Where to store the CPU model (can be NULL).
445 * @param pu32Stepping Where to store the CPU stepping (can be NULL).
446 * @returns true if the erratum applies, false otherwise.
447 */
448VMM_INT_DECL(int) HMAmdIsSubjectToErratum170(uint32_t *pu32Family, uint32_t *pu32Model, uint32_t *pu32Stepping)
449{
450 /*
451 * Erratum 170 which requires a forced TLB flush for each world switch:
452 * See AMD spec. "Revision Guide for AMD NPT Family 0Fh Processors".
453 *
454 * All BH-G1/2 and DH-G1/2 models include a fix:
455 * Athlon X2: 0x6b 1/2
456 * 0x68 1/2
457 * Athlon 64: 0x7f 1
458 * 0x6f 2
459 * Sempron: 0x7f 1/2
460 * 0x6f 2
461 * 0x6c 2
462 * 0x7c 2
463 * Turion 64: 0x68 2
464 */
465 uint32_t u32Dummy;
466 uint32_t u32Version, u32Family, u32Model, u32Stepping, u32BaseFamily;
467 ASMCpuId(1, &u32Version, &u32Dummy, &u32Dummy, &u32Dummy);
468 u32BaseFamily = (u32Version >> 8) & 0xf;
469 u32Family = u32BaseFamily + (u32BaseFamily == 0xf ? ((u32Version >> 20) & 0x7f) : 0);
470 u32Model = ((u32Version >> 4) & 0xf);
471 u32Model = u32Model | ((u32BaseFamily == 0xf ? (u32Version >> 16) & 0x0f : 0) << 4);
472 u32Stepping = u32Version & 0xf;
473
474 bool fErratumApplies = false;
475 if ( u32Family == 0xf
476 && !((u32Model == 0x68 || u32Model == 0x6b || u32Model == 0x7f) && u32Stepping >= 1)
477 && !((u32Model == 0x6f || u32Model == 0x6c || u32Model == 0x7c) && u32Stepping >= 2))
478 {
479 fErratumApplies = true;
480 }
481
482 if (pu32Family)
483 *pu32Family = u32Family;
484 if (pu32Model)
485 *pu32Model = u32Model;
486 if (pu32Stepping)
487 *pu32Stepping = u32Stepping;
488
489 return fErratumApplies;
490}
491
492
493/**
494 * Sets or clears the single instruction flag.
495 *
496 * When set, HM will try its best to return to ring-3 after executing a single
497 * instruction. This can be used for debugging. See also
498 * EMR3HmSingleInstruction.
499 *
500 * @returns The old flag state.
501 * @param pVM The cross context VM structure.
502 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
503 * @param fEnable The new flag state.
504 */
505VMM_INT_DECL(bool) HMSetSingleInstruction(PVM pVM, PVMCPU pVCpu, bool fEnable)
506{
507 VMCPU_ASSERT_EMT(pVCpu);
508 bool fOld = pVCpu->hm.s.fSingleInstruction;
509 pVCpu->hm.s.fSingleInstruction = fEnable;
510 pVCpu->hm.s.fUseDebugLoop = fEnable || pVM->hm.s.fUseDebugLoop;
511 return fOld;
512}
513
514
515/**
516 * Notifies HM that paravirtualized hypercalls are now enabled.
517 *
518 * @param pVCpu The cross context virtual CPU structure.
519 */
520VMM_INT_DECL(void) HMHypercallsEnable(PVMCPU pVCpu)
521{
522 pVCpu->hm.s.fHypercallsEnabled = true;
523}
524
525
526/**
527 * Notifies HM that paravirtualized hypercalls are now disabled.
528 *
529 * @param pVCpu The cross context virtual CPU structure.
530 */
531VMM_INT_DECL(void) HMHypercallsDisable(PVMCPU pVCpu)
532{
533 pVCpu->hm.s.fHypercallsEnabled = false;
534}
535
536
537/**
538 * Notifies HM that GIM provider wants to trap \#UD.
539 *
540 * @param pVCpu The cross context virtual CPU structure.
541 */
542VMM_INT_DECL(void) HMTrapXcptUDForGIMEnable(PVMCPU pVCpu)
543{
544 pVCpu->hm.s.fGIMTrapXcptUD = true;
545 HMCPU_CF_SET(pVCpu, HM_CHANGED_VMM_GUEST_XCPT_INTERCEPTS);
546}
547
548
549/**
550 * Notifies HM that GIM provider no longer wants to trap \#UD.
551 *
552 * @param pVCpu The cross context virtual CPU structure.
553 */
554VMM_INT_DECL(void) HMTrapXcptUDForGIMDisable(PVMCPU pVCpu)
555{
556 pVCpu->hm.s.fGIMTrapXcptUD = false;
557 HMCPU_CF_SET(pVCpu, HM_CHANGED_VMM_GUEST_XCPT_INTERCEPTS);
558}
559
560
561/**
562 * VMX nested-guest VM-exit handler.
563 *
564 * @param pVCpu The cross context virtual CPU structure.
565 * @param uBasicExitReason The basic exit reason.
566 */
567VMM_INT_DECL(void) HMNstGstVmxVmExit(PVMCPU pVCpu, uint16_t uBasicExitReason)
568{
569 RT_NOREF2(pVCpu, uBasicExitReason);
570}
571
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