VirtualBox

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

Last change on this file since 93725 was 93725, checked in by vboxsync, 2 years ago

VMM: More arm64 adjustments. bugref:9898

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

© 2023 Oracle
ContactPrivacy policyTerms of Use