VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/HMSVMAll.cpp@ 75573

Last change on this file since 75573 was 75440, checked in by vboxsync, 6 years ago

VMM: Nested VMX: bugref:9180 Separate the VMX/SVM CPU spec. functions into CPUM as they can be called from IEM/REM/CPUM as well as HM.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.6 KB
Line 
1/* $Id: HMSVMAll.cpp 75440 2018-11-14 06:23:13Z vboxsync $ */
2/** @file
3 * HM SVM (AMD-V) - All contexts.
4 */
5
6/*
7 * Copyright (C) 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#define VMCPU_INCL_CPUM_GST_CTX
24#include "HMInternal.h"
25#include <VBox/vmm/apic.h>
26#include <VBox/vmm/gim.h>
27#include <VBox/vmm/iem.h>
28#include <VBox/vmm/vm.h>
29
30
31#ifndef IN_RC
32
33/**
34 * Emulates a simple MOV TPR (CR8) instruction.
35 *
36 * Used for TPR patching on 32-bit guests. This simply looks up the patch record
37 * at EIP and does the required.
38 *
39 * This VMMCALL is used a fallback mechanism when mov to/from cr8 isn't exactly
40 * like how we want it to be (e.g. not followed by shr 4 as is usually done for
41 * TPR). See hmR3ReplaceTprInstr() for the details.
42 *
43 * @returns VBox status code.
44 * @retval VINF_SUCCESS if the access was handled successfully, RIP + RFLAGS updated.
45 * @retval VERR_NOT_FOUND if no patch record for this RIP could be found.
46 * @retval VERR_SVM_UNEXPECTED_PATCH_TYPE if the found patch type is invalid.
47 *
48 * @param pVCpu The cross context virtual CPU structure.
49 * @param pCtx Pointer to the guest-CPU context.
50 */
51int hmSvmEmulateMovTpr(PVMCPU pVCpu)
52{
53 PCPUMCTX pCtx = &pVCpu->cpum.GstCtx;
54 Log4(("Emulated VMMCall TPR access replacement at RIP=%RGv\n", pCtx->rip));
55
56 /*
57 * We do this in a loop as we increment the RIP after a successful emulation
58 * and the new RIP may be a patched instruction which needs emulation as well.
59 */
60 bool fPatchFound = false;
61 PVM pVM = pVCpu->CTX_SUFF(pVM);
62 for (;;)
63 {
64 PHMTPRPATCH pPatch = (PHMTPRPATCH)RTAvloU32Get(&pVM->hm.s.PatchTree, (AVLOU32KEY)pCtx->eip);
65 if (!pPatch)
66 break;
67 fPatchFound = true;
68
69 uint8_t u8Tpr;
70 switch (pPatch->enmType)
71 {
72 case HMTPRINSTR_READ:
73 {
74 bool fPending;
75 int rc = APICGetTpr(pVCpu, &u8Tpr, &fPending, NULL /* pu8PendingIrq */);
76 AssertRC(rc);
77
78 rc = DISWriteReg32(CPUMCTX2CORE(pCtx), pPatch->uDstOperand, u8Tpr);
79 AssertRC(rc);
80 pCtx->rip += pPatch->cbOp;
81 pCtx->eflags.Bits.u1RF = 0;
82 break;
83 }
84
85 case HMTPRINSTR_WRITE_REG:
86 case HMTPRINSTR_WRITE_IMM:
87 {
88 if (pPatch->enmType == HMTPRINSTR_WRITE_REG)
89 {
90 uint32_t u32Val;
91 int rc = DISFetchReg32(CPUMCTX2CORE(pCtx), pPatch->uSrcOperand, &u32Val);
92 AssertRC(rc);
93 u8Tpr = u32Val;
94 }
95 else
96 u8Tpr = (uint8_t)pPatch->uSrcOperand;
97
98 int rc2 = APICSetTpr(pVCpu, u8Tpr);
99 AssertRC(rc2);
100 pCtx->rip += pPatch->cbOp;
101 pCtx->eflags.Bits.u1RF = 0;
102 ASMAtomicUoOrU64(&pVCpu->hm.s.fCtxChanged, HM_CHANGED_GUEST_APIC_TPR
103 | HM_CHANGED_GUEST_RIP
104 | HM_CHANGED_GUEST_RFLAGS);
105 break;
106 }
107
108 default:
109 {
110 AssertMsgFailed(("Unexpected patch type %d\n", pPatch->enmType));
111 pVCpu->hm.s.u32HMError = pPatch->enmType;
112 return VERR_SVM_UNEXPECTED_PATCH_TYPE;
113 }
114 }
115 }
116
117 return fPatchFound ? VINF_SUCCESS : VERR_NOT_FOUND;
118}
119
120# ifdef VBOX_WITH_NESTED_HWVIRT_SVM
121/**
122 * Notification callback for when a \#VMEXIT happens outside SVM R0 code (e.g.
123 * in IEM).
124 *
125 * @param pVCpu The cross context virtual CPU structure.
126 * @param pCtx Pointer to the guest-CPU context.
127 *
128 * @sa hmR0SvmVmRunCacheVmcb.
129 */
130VMM_INT_DECL(void) HMSvmNstGstVmExitNotify(PVMCPU pVCpu, PCPUMCTX pCtx)
131{
132 PSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
133 if (pVmcbNstGstCache->fCacheValid)
134 {
135 /*
136 * Restore fields as our own code might look at the VMCB controls as part
137 * of the #VMEXIT handling in IEM. Otherwise, strictly speaking we don't need to
138 * restore these fields because currently none of them are written back to memory
139 * by a physical CPU on #VMEXIT.
140 */
141 PSVMVMCBCTRL pVmcbNstGstCtrl = &pCtx->hwvirt.svm.CTX_SUFF(pVmcb)->ctrl;
142 pVmcbNstGstCtrl->u16InterceptRdCRx = pVmcbNstGstCache->u16InterceptRdCRx;
143 pVmcbNstGstCtrl->u16InterceptWrCRx = pVmcbNstGstCache->u16InterceptWrCRx;
144 pVmcbNstGstCtrl->u16InterceptRdDRx = pVmcbNstGstCache->u16InterceptRdDRx;
145 pVmcbNstGstCtrl->u16InterceptWrDRx = pVmcbNstGstCache->u16InterceptWrDRx;
146 pVmcbNstGstCtrl->u16PauseFilterThreshold = pVmcbNstGstCache->u16PauseFilterThreshold;
147 pVmcbNstGstCtrl->u16PauseFilterCount = pVmcbNstGstCache->u16PauseFilterCount;
148 pVmcbNstGstCtrl->u32InterceptXcpt = pVmcbNstGstCache->u32InterceptXcpt;
149 pVmcbNstGstCtrl->u64InterceptCtrl = pVmcbNstGstCache->u64InterceptCtrl;
150 pVmcbNstGstCtrl->u64TSCOffset = pVmcbNstGstCache->u64TSCOffset;
151 pVmcbNstGstCtrl->IntCtrl.n.u1VIntrMasking = pVmcbNstGstCache->fVIntrMasking;
152 pVmcbNstGstCtrl->NestedPagingCtrl.n.u1NestedPaging = pVmcbNstGstCache->fNestedPaging;
153 pVmcbNstGstCtrl->LbrVirt.n.u1LbrVirt = pVmcbNstGstCache->fLbrVirt;
154 pVmcbNstGstCache->fCacheValid = false;
155 }
156
157 /*
158 * Transitions to ring-3 flag a full CPU-state change except if we transition to ring-3
159 * in response to a physical CPU interrupt as no changes to the guest-CPU state are
160 * expected (see VINF_EM_RAW_INTERRUPT handling in hmR0SvmExitToRing3).
161 *
162 * However, with nested-guests, the state -can- change on trips to ring-3 for we might
163 * try to inject a nested-guest physical interrupt and cause a SVM_EXIT_INTR #VMEXIT for
164 * the nested-guest from ring-3. Import the complete state here as we will be swapping
165 * to the guest VMCB after the #VMEXIT.
166 */
167 CPUMImportGuestStateOnDemand(pVCpu, CPUMCTX_EXTRN_ALL);
168 AssertMsg(!(pVCpu->cpum.GstCtx.fExtrn & CPUMCTX_EXTRN_ALL),
169 ("fExtrn=%#RX64 fExtrnMbz=%#RX64\n", pVCpu->cpum.GstCtx.fExtrn, CPUMCTX_EXTRN_ALL));
170 ASMAtomicUoOrU64(&pVCpu->hm.s.fCtxChanged, HM_CHANGED_ALL_GUEST);
171}
172# endif
173
174/**
175 * Checks if the Virtual GIF (Global Interrupt Flag) feature is supported and
176 * enabled for the VM.
177 *
178 * @returns @c true if VGIF is enabled, @c false otherwise.
179 * @param pVM The cross context VM structure.
180 *
181 * @remarks This value returned by this functions is expected by the callers not
182 * to change throughout the lifetime of the VM.
183 */
184VMM_INT_DECL(bool) HMSvmIsVGifActive(PVM pVM)
185{
186 bool const fVGif = RT_BOOL(pVM->hm.s.svm.u32Features & X86_CPUID_SVM_FEATURE_EDX_VGIF);
187 bool const fUseVGif = fVGif && pVM->hm.s.svm.fVGif;
188
189 return HMIsEnabled(pVM) && fVGif && fUseVGif;
190}
191
192
193/**
194 * Applies the TSC offset of an SVM nested-guest if any and returns the new TSC
195 * value for the nested-guest.
196 *
197 * @returns The TSC offset after applying any nested-guest TSC offset.
198 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
199 * @param uTicks The guest TSC.
200 *
201 * @remarks This function looks at the VMCB cache rather than directly at the
202 * nested-guest VMCB. The latter may have been modified for executing
203 * using hardware-assisted SVM.
204 *
205 * @note If you make any changes to this function, please check if
206 * hmR0SvmNstGstUndoTscOffset() needs adjusting.
207 *
208 * @sa CPUMApplyNestedGuestTscOffset(), hmR0SvmNstGstUndoTscOffset().
209 */
210VMM_INT_DECL(uint64_t) HMSvmNstGstApplyTscOffset(PVMCPU pVCpu, uint64_t uTicks)
211{
212 PCCPUMCTX pCtx = &pVCpu->cpum.GstCtx;
213 Assert(CPUMIsGuestInSvmNestedHwVirtMode(pCtx)); RT_NOREF(pCtx);
214 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
215 Assert(pVmcbNstGstCache->fCacheValid);
216 return uTicks + pVmcbNstGstCache->u64TSCOffset;
217}
218
219
220/**
221 * Interface used by IEM to handle patched TPR accesses.
222 *
223 * @returns VBox status code
224 * @retval VINF_SUCCESS if hypercall was handled, RIP + RFLAGS all dealt with.
225 * @retval VERR_NOT_FOUND if hypercall was _not_ handled.
226 * @retval VERR_SVM_UNEXPECTED_PATCH_TYPE on IPE.
227 *
228 * @param pVCpu The cross context virtual CPU structure.
229 */
230VMM_INT_DECL(int) HMHCSvmMaybeMovTprHypercall(PVMCPU pVCpu)
231{
232 PVM pVM = pVCpu->CTX_SUFF(pVM);
233 if (pVM->hm.s.fTprPatchingAllowed)
234 {
235 int rc = hmSvmEmulateMovTpr(pVCpu);
236 if (RT_SUCCESS(rc))
237 return VINF_SUCCESS;
238 return rc;
239 }
240 return VERR_NOT_FOUND;
241}
242
243
244/**
245 * Checks if the current AMD CPU is subject to erratum 170 "In SVM mode,
246 * incorrect code bytes may be fetched after a world-switch".
247 *
248 * @param pu32Family Where to store the CPU family (can be NULL).
249 * @param pu32Model Where to store the CPU model (can be NULL).
250 * @param pu32Stepping Where to store the CPU stepping (can be NULL).
251 * @returns true if the erratum applies, false otherwise.
252 */
253VMM_INT_DECL(int) HMSvmIsSubjectToErratum170(uint32_t *pu32Family, uint32_t *pu32Model, uint32_t *pu32Stepping)
254{
255 /*
256 * Erratum 170 which requires a forced TLB flush for each world switch:
257 * See AMD spec. "Revision Guide for AMD NPT Family 0Fh Processors".
258 *
259 * All BH-G1/2 and DH-G1/2 models include a fix:
260 * Athlon X2: 0x6b 1/2
261 * 0x68 1/2
262 * Athlon 64: 0x7f 1
263 * 0x6f 2
264 * Sempron: 0x7f 1/2
265 * 0x6f 2
266 * 0x6c 2
267 * 0x7c 2
268 * Turion 64: 0x68 2
269 */
270 uint32_t u32Dummy;
271 uint32_t u32Version, u32Family, u32Model, u32Stepping, u32BaseFamily;
272 ASMCpuId(1, &u32Version, &u32Dummy, &u32Dummy, &u32Dummy);
273 u32BaseFamily = (u32Version >> 8) & 0xf;
274 u32Family = u32BaseFamily + (u32BaseFamily == 0xf ? ((u32Version >> 20) & 0x7f) : 0);
275 u32Model = ((u32Version >> 4) & 0xf);
276 u32Model = u32Model | ((u32BaseFamily == 0xf ? (u32Version >> 16) & 0x0f : 0) << 4);
277 u32Stepping = u32Version & 0xf;
278
279 bool fErratumApplies = false;
280 if ( u32Family == 0xf
281 && !((u32Model == 0x68 || u32Model == 0x6b || u32Model == 0x7f) && u32Stepping >= 1)
282 && !((u32Model == 0x6f || u32Model == 0x6c || u32Model == 0x7c) && u32Stepping >= 2))
283 {
284 fErratumApplies = true;
285 }
286
287 if (pu32Family)
288 *pu32Family = u32Family;
289 if (pu32Model)
290 *pu32Model = u32Model;
291 if (pu32Stepping)
292 *pu32Stepping = u32Stepping;
293
294 return fErratumApplies;
295}
296
297#endif /* !IN_RC */
298
299
300/**
301 * Converts an SVM event type to a TRPM event type.
302 *
303 * @returns The TRPM event type.
304 * @retval TRPM_32BIT_HACK if the specified type of event isn't among the set
305 * of recognized trap types.
306 *
307 * @param pEvent Pointer to the SVM event.
308 */
309VMM_INT_DECL(TRPMEVENT) HMSvmEventToTrpmEventType(PCSVMEVENT pEvent)
310{
311 uint8_t const uType = pEvent->n.u3Type;
312 switch (uType)
313 {
314 case SVM_EVENT_EXTERNAL_IRQ: return TRPM_HARDWARE_INT;
315 case SVM_EVENT_SOFTWARE_INT: return TRPM_SOFTWARE_INT;
316 case SVM_EVENT_EXCEPTION:
317 case SVM_EVENT_NMI: return TRPM_TRAP;
318 default:
319 break;
320 }
321 AssertMsgFailed(("HMSvmEventToTrpmEvent: Invalid pending-event type %#x\n", uType));
322 return TRPM_32BIT_HACK;
323}
324
325
326/**
327 * Returns whether HM has cached the nested-guest VMCB.
328 *
329 * If the VMCB is cached by HM, it means HM may have potentially modified the
330 * VMCB for execution using hardware-assisted SVM.
331 *
332 * @returns true if HM has cached the nested-guest VMCB, false otherwise.
333 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
334 */
335VMM_INT_DECL(bool) HMHasGuestSvmVmcbCached(PVMCPU pVCpu)
336{
337 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
338 return pVmcbNstGstCache->fCacheValid;
339}
340
341
342/**
343 * Checks if the nested-guest VMCB has the specified ctrl/instruction intercept
344 * active.
345 *
346 * @returns @c true if in intercept is set, @c false otherwise.
347 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
348 * @param fIntercept The SVM control/instruction intercept, see
349 * SVM_CTRL_INTERCEPT_*.
350 */
351VMM_INT_DECL(bool) HMIsGuestSvmCtrlInterceptSet(PVMCPU pVCpu, uint64_t fIntercept)
352{
353 Assert(HMHasGuestSvmVmcbCached(pVCpu));
354 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
355 return RT_BOOL(pVmcbNstGstCache->u64InterceptCtrl & fIntercept);
356}
357
358
359/**
360 * Checks if the nested-guest VMCB has the specified CR read intercept active.
361 *
362 * @returns @c true if in intercept is set, @c false otherwise.
363 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
364 * @param uCr The CR register number (0 to 15).
365 */
366VMM_INT_DECL(bool) HMIsGuestSvmReadCRxInterceptSet(PVMCPU pVCpu, uint8_t uCr)
367{
368 Assert(uCr < 16);
369 Assert(HMHasGuestSvmVmcbCached(pVCpu));
370 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
371 return RT_BOOL(pVmcbNstGstCache->u16InterceptRdCRx & (1 << uCr));
372}
373
374
375/**
376 * Checks if the nested-guest VMCB has the specified CR write intercept active.
377 *
378 * @returns @c true if in intercept is set, @c false otherwise.
379 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
380 * @param uCr The CR register number (0 to 15).
381 */
382VMM_INT_DECL(bool) HMIsGuestSvmWriteCRxInterceptSet(PVMCPU pVCpu, uint8_t uCr)
383{
384 Assert(uCr < 16);
385 Assert(HMHasGuestSvmVmcbCached(pVCpu));
386 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
387 return RT_BOOL(pVmcbNstGstCache->u16InterceptWrCRx & (1 << uCr));
388}
389
390
391/**
392 * Checks if the nested-guest VMCB has the specified DR read intercept active.
393 *
394 * @returns @c true if in intercept is set, @c false otherwise.
395 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
396 * @param uDr The DR register number (0 to 15).
397 */
398VMM_INT_DECL(bool) HMIsGuestSvmReadDRxInterceptSet(PVMCPU pVCpu, uint8_t uDr)
399{
400 Assert(uDr < 16);
401 Assert(HMHasGuestSvmVmcbCached(pVCpu));
402 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
403 return RT_BOOL(pVmcbNstGstCache->u16InterceptRdDRx & (1 << uDr));
404}
405
406
407/**
408 * Checks if the nested-guest VMCB has the specified DR write intercept active.
409 *
410 * @returns @c true if in intercept is set, @c false otherwise.
411 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
412 * @param uDr The DR register number (0 to 15).
413 */
414VMM_INT_DECL(bool) HMIsGuestSvmWriteDRxInterceptSet(PVMCPU pVCpu, uint8_t uDr)
415{
416 Assert(uDr < 16);
417 Assert(HMHasGuestSvmVmcbCached(pVCpu));
418 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
419 return RT_BOOL(pVmcbNstGstCache->u16InterceptWrDRx & (1 << uDr));
420}
421
422
423/**
424 * Checks if the nested-guest VMCB has the specified exception intercept active.
425 *
426 * @returns true if in intercept is active, false otherwise.
427 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
428 * @param uVector The exception / interrupt vector.
429 */
430VMM_INT_DECL(bool) HMIsGuestSvmXcptInterceptSet(PVMCPU pVCpu, uint8_t uVector)
431{
432 Assert(uVector < 32);
433 Assert(HMHasGuestSvmVmcbCached(pVCpu));
434 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
435 return RT_BOOL(pVmcbNstGstCache->u32InterceptXcpt & (1 << uVector));
436}
437
438
439/**
440 * Checks if the nested-guest VMCB has virtual-interrupts masking enabled.
441 *
442 * @returns true if virtual-interrupts are masked, @c false otherwise.
443 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
444 */
445VMM_INT_DECL(bool) HMIsGuestSvmVirtIntrMasking(PVMCPU pVCpu)
446{
447 Assert(HMHasGuestSvmVmcbCached(pVCpu));
448 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
449 return pVmcbNstGstCache->fVIntrMasking;
450}
451
452
453/**
454 * Checks if the nested-guest VMCB has nested-paging enabled.
455 *
456 * @returns true if nested-paging is enabled, @c false otherwise.
457 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
458 */
459VMM_INT_DECL(bool) HMIsGuestSvmNestedPagingEnabled(PVMCPU pVCpu)
460{
461 Assert(HMHasGuestSvmVmcbCached(pVCpu));
462 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
463 return pVmcbNstGstCache->fNestedPaging;
464}
465
466
467/**
468 * Returns the nested-guest VMCB pause-filter count.
469 *
470 * @returns The pause-filter count.
471 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
472 */
473VMM_INT_DECL(uint16_t) HMGetGuestSvmPauseFilterCount(PVMCPU pVCpu)
474{
475 Assert(HMHasGuestSvmVmcbCached(pVCpu));
476 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
477 return pVmcbNstGstCache->u16PauseFilterCount;
478}
479
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette