VirtualBox

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

Last change on this file since 96860 was 96407, checked in by vboxsync, 21 months ago

scm copyright and license note update

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

© 2023 Oracle
ContactPrivacy policyTerms of Use