VirtualBox

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

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

Do HMR3Init first in vmR3InitRing3 so the other components can skip raw-mode bits during init.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.1 KB
Line 
1/* $Id: HMAll.cpp 45618 2013-04-18 18:41:07Z 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/x86.h>
36
37
38
39/**
40 * Query HM state (enabled/disabled)
41 *
42 * @returns @c false if disabled, @c true if enabled.
43 * @param pVM The cross context VM structure.
44 * @sa HMIsEnabled, HMR3IsEnabled
45 * @internal
46 */
47VMMDECL(bool) HMIsEnabledNotMacro(PVM pVM)
48{
49 Assert(pVM->fHMEnabledFixed);
50 return pVM->fHMEnabled;
51}
52
53
54/**
55 * Queues a page for invalidation
56 *
57 * @returns VBox status code.
58 * @param pVCpu Pointer to the VMCPU.
59 * @param GCVirt Page to invalidate
60 */
61static void hmQueueInvlPage(PVMCPU pVCpu, RTGCPTR GCVirt)
62{
63 /* Nothing to do if a TLB flush is already pending */
64 if (VMCPU_FF_ISSET(pVCpu, VMCPU_FF_TLB_FLUSH))
65 return;
66#if 1
67 VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_FLUSH);
68 NOREF(GCVirt);
69#else
70 /* Be very careful when activating this code! */
71 if (iPage == RT_ELEMENTS(pVCpu->hm.s.TlbShootdown.aPages))
72 VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_FLUSH);
73 else
74 VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_SHOOTDOWN);
75#endif
76}
77
78/**
79 * Invalidates a guest page
80 *
81 * @returns VBox status code.
82 * @param pVCpu Pointer to the VMCPU.
83 * @param GCVirt Page to invalidate
84 */
85VMM_INT_DECL(int) HMInvalidatePage(PVMCPU pVCpu, RTGCPTR GCVirt)
86{
87 STAM_COUNTER_INC(&pVCpu->hm.s.StatFlushPageManual);
88#ifdef IN_RING0
89 PVM pVM = pVCpu->CTX_SUFF(pVM);
90 if (pVM->hm.s.vmx.fSupported)
91 return VMXR0InvalidatePage(pVM, pVCpu, GCVirt);
92
93 Assert(pVM->hm.s.svm.fSupported);
94 return SVMR0InvalidatePage(pVM, pVCpu, GCVirt);
95
96#else
97 hmQueueInvlPage(pVCpu, GCVirt);
98 return VINF_SUCCESS;
99#endif
100}
101
102/**
103 * Flushes the guest TLB
104 *
105 * @returns VBox status code.
106 * @param pVCpu Pointer to the VMCPU.
107 */
108VMM_INT_DECL(int) HMFlushTLB(PVMCPU pVCpu)
109{
110 LogFlow(("HMFlushTLB\n"));
111
112 VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_FLUSH);
113 STAM_COUNTER_INC(&pVCpu->hm.s.StatFlushTlbManual);
114 return VINF_SUCCESS;
115}
116
117#ifdef IN_RING0
118
119/**
120 * Dummy RTMpOnSpecific handler since RTMpPokeCpu couldn't be used.
121 *
122 */
123static DECLCALLBACK(void) hmFlushHandler(RTCPUID idCpu, void *pvUser1, void *pvUser2)
124{
125 NOREF(idCpu); NOREF(pvUser1); NOREF(pvUser2);
126 return;
127}
128
129/**
130 * Wrapper for RTMpPokeCpu to deal with VERR_NOT_SUPPORTED.
131 */
132static void hmR0PokeCpu(PVMCPU pVCpu, RTCPUID idHostCpu)
133{
134 uint32_t cWorldSwitchExits = ASMAtomicUoReadU32(&pVCpu->hm.s.cWorldSwitchExits);
135
136 STAM_PROFILE_ADV_START(&pVCpu->hm.s.StatPoke, x);
137 int rc = RTMpPokeCpu(idHostCpu);
138 STAM_PROFILE_ADV_STOP(&pVCpu->hm.s.StatPoke, x);
139
140 /* Not implemented on some platforms (Darwin, Linux kernel < 2.6.19); fall
141 back to a less efficient implementation (broadcast). */
142 if (rc == VERR_NOT_SUPPORTED)
143 {
144 STAM_PROFILE_ADV_START(&pVCpu->hm.s.StatSpinPoke, z);
145 /* synchronous. */
146 RTMpOnSpecific(idHostCpu, hmFlushHandler, 0, 0);
147 STAM_PROFILE_ADV_STOP(&pVCpu->hm.s.StatSpinPoke, z);
148 }
149 else
150 {
151 if (rc == VINF_SUCCESS)
152 STAM_PROFILE_ADV_START(&pVCpu->hm.s.StatSpinPoke, z);
153 else
154 STAM_PROFILE_ADV_START(&pVCpu->hm.s.StatSpinPokeFailed, z);
155
156/** @todo If more than one CPU is going to be poked, we could optimize this
157 * operation by poking them first and wait afterwards. Would require
158 * recording who to poke and their current cWorldSwitchExits values,
159 * that's something not suitable for stack... So, pVCpu->hm.s.something
160 * then. */
161 /* Spin until the VCPU has switched back (poking is async). */
162 while ( ASMAtomicUoReadBool(&pVCpu->hm.s.fCheckedTLBFlush)
163 && cWorldSwitchExits == ASMAtomicUoReadU32(&pVCpu->hm.s.cWorldSwitchExits))
164 ASMNopPause();
165
166 if (rc == VINF_SUCCESS)
167 STAM_PROFILE_ADV_STOP(&pVCpu->hm.s.StatSpinPoke, z);
168 else
169 STAM_PROFILE_ADV_STOP(&pVCpu->hm.s.StatSpinPokeFailed, z);
170 }
171}
172
173#endif /* IN_RING0 */
174#ifndef IN_RC
175
176/**
177 * Poke an EMT so it can perform the appropriate TLB shootdowns.
178 *
179 * @param pVCpu The handle of the virtual CPU to poke.
180 * @param fAccountFlushStat Whether to account the call to
181 * StatTlbShootdownFlush or StatTlbShootdown.
182 */
183static void hmPokeCpuForTlbFlush(PVMCPU pVCpu, bool fAccountFlushStat)
184{
185 if (ASMAtomicUoReadBool(&pVCpu->hm.s.fCheckedTLBFlush))
186 {
187 if (fAccountFlushStat)
188 STAM_COUNTER_INC(&pVCpu->hm.s.StatTlbShootdownFlush);
189 else
190 STAM_COUNTER_INC(&pVCpu->hm.s.StatTlbShootdown);
191#ifdef IN_RING0
192 RTCPUID idHostCpu = pVCpu->hm.s.idEnteredCpu;
193 if (idHostCpu != NIL_RTCPUID)
194 hmR0PokeCpu(pVCpu, idHostCpu);
195#else
196 VMR3NotifyCpuFFU(pVCpu->pUVCpu, VMNOTIFYFF_FLAGS_POKE);
197#endif
198 }
199 else
200 STAM_COUNTER_INC(&pVCpu->hm.s.StatFlushPageManual);
201}
202
203
204/**
205 * Invalidates a guest page on all VCPUs.
206 *
207 * @returns VBox status code.
208 * @param pVM Pointer to the VM.
209 * @param GCVirt Page to invalidate
210 */
211VMM_INT_DECL(int) HMInvalidatePageOnAllVCpus(PVM pVM, RTGCPTR GCPtr)
212{
213 VMCPUID idCurCpu = VMMGetCpuId(pVM);
214 STAM_COUNTER_INC(&pVM->aCpus[idCurCpu].hm.s.StatFlushPage);
215
216 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
217 {
218 PVMCPU pVCpu = &pVM->aCpus[idCpu];
219
220 /* Nothing to do if a TLB flush is already pending; the VCPU should
221 have already been poked if it were active. */
222 if (VMCPU_FF_ISSET(pVCpu, VMCPU_FF_TLB_FLUSH))
223 continue;
224
225 if (pVCpu->idCpu == idCurCpu)
226 HMInvalidatePage(pVCpu, GCPtr);
227 else
228 {
229 hmQueueInvlPage(pVCpu, GCPtr);
230 hmPokeCpuForTlbFlush(pVCpu, false /*fAccountFlushStat*/);
231 }
232 }
233
234 return VINF_SUCCESS;
235}
236
237
238/**
239 * Flush the TLBs of all VCPUs
240 *
241 * @returns VBox status code.
242 * @param pVM Pointer to the VM.
243 */
244VMM_INT_DECL(int) HMFlushTLBOnAllVCpus(PVM pVM)
245{
246 if (pVM->cCpus == 1)
247 return HMFlushTLB(&pVM->aCpus[0]);
248
249 VMCPUID idThisCpu = VMMGetCpuId(pVM);
250
251 STAM_COUNTER_INC(&pVM->aCpus[idThisCpu].hm.s.StatFlushTlb);
252
253 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
254 {
255 PVMCPU pVCpu = &pVM->aCpus[idCpu];
256
257 /* Nothing to do if a TLB flush is already pending; the VCPU should
258 have already been poked if it were active. */
259 if (!VMCPU_FF_ISSET(pVCpu, VMCPU_FF_TLB_FLUSH))
260 {
261 VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_FLUSH);
262 if (idThisCpu != idCpu)
263 hmPokeCpuForTlbFlush(pVCpu, true /*fAccountFlushStat*/);
264 }
265 }
266
267 return VINF_SUCCESS;
268}
269
270#endif /* !IN_RC */
271
272/**
273 * Checks if nested paging is enabled
274 *
275 * @returns boolean
276 * @param pVM Pointer to the VM.
277 */
278VMM_INT_DECL(bool) HMIsNestedPagingActive(PVM pVM)
279{
280 return HMIsEnabled(pVM) && pVM->hm.s.fNestedPaging;
281}
282
283/**
284 * Return the shadow paging mode for nested paging/ept
285 *
286 * @returns shadow paging mode
287 * @param pVM Pointer to the VM.
288 */
289VMM_INT_DECL(PGMMODE) HMGetShwPagingMode(PVM pVM)
290{
291 Assert(HMIsNestedPagingActive(pVM));
292 if (pVM->hm.s.svm.fSupported)
293 return PGMMODE_NESTED;
294
295 Assert(pVM->hm.s.vmx.fSupported);
296 return PGMMODE_EPT;
297}
298
299/**
300 * Invalidates a guest page by physical address
301 *
302 * NOTE: Assumes the current instruction references this physical page though a virtual address!!
303 *
304 * @returns VBox status code.
305 * @param pVM Pointer to the VM.
306 * @param GCPhys Page to invalidate
307 */
308VMM_INT_DECL(int) HMInvalidatePhysPage(PVM pVM, RTGCPHYS GCPhys)
309{
310 if (!HMIsNestedPagingActive(pVM))
311 return VINF_SUCCESS;
312
313#ifdef IN_RING0
314 if (pVM->hm.s.vmx.fSupported)
315 {
316 VMCPUID idThisCpu = VMMGetCpuId(pVM);
317
318 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
319 {
320 PVMCPU pVCpu = &pVM->aCpus[idCpu];
321
322 if (idThisCpu == idCpu)
323 {
324 /** @todo r=ramshankar: Intel does not support flushing by guest physical
325 * address either. See comment in VMXR0InvalidatePhysPage(). Fix this. */
326 VMXR0InvalidatePhysPage(pVM, pVCpu, GCPhys);
327 }
328 else
329 {
330 VMCPU_FF_SET(pVCpu, VMCPU_FF_TLB_FLUSH);
331 hmPokeCpuForTlbFlush(pVCpu, true /*fAccountFlushStat*/);
332 }
333 }
334 return VINF_SUCCESS;
335 }
336
337 /* AMD-V doesn't support invalidation with guest physical addresses; see
338 comment in SVMR0InvalidatePhysPage. */
339 Assert(pVM->hm.s.svm.fSupported);
340#else
341 NOREF(GCPhys);
342#endif
343
344 HMFlushTLBOnAllVCpus(pVM);
345 return VINF_SUCCESS;
346}
347
348/**
349 * Checks if an interrupt event is currently pending.
350 *
351 * @returns Interrupt event pending state.
352 * @param pVM Pointer to the VM.
353 */
354VMM_INT_DECL(bool) HMHasPendingIrq(PVM pVM)
355{
356 PVMCPU pVCpu = VMMGetCpu(pVM);
357 return !!pVCpu->hm.s.Event.fPending;
358}
359
360
361/**
362 * Return the PAE PDPE entries.
363 *
364 * @returns Pointer to the PAE PDPE array.
365 * @param pVCpu Pointer to the VMCPU.
366 */
367VMM_INT_DECL(PX86PDPE) HMGetPaePdpes(PVMCPU pVCpu)
368{
369 return &pVCpu->hm.s.aPdpes[0];
370}
371
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