VirtualBox

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

Last change on this file since 84044 was 82968, checked in by vboxsync, 4 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.6 KB
Line 
1/* $Id: HMSVMAll.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * HM SVM (AMD-V) - All contexts.
4 */
5
6/*
7 * Copyright (C) 2017-2020 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
32
33
34/**
35 * Emulates a simple MOV TPR (CR8) instruction.
36 *
37 * Used for TPR patching on 32-bit guests. This simply looks up the patch record
38 * at EIP and does the required.
39 *
40 * This VMMCALL is used a fallback mechanism when mov to/from cr8 isn't exactly
41 * like how we want it to be (e.g. not followed by shr 4 as is usually done for
42 * TPR). See hmR3ReplaceTprInstr() for the details.
43 *
44 * @returns VBox status code.
45 * @retval VINF_SUCCESS if the access was handled successfully, RIP + RFLAGS updated.
46 * @retval VERR_NOT_FOUND if no patch record for this RIP could be found.
47 * @retval VERR_SVM_UNEXPECTED_PATCH_TYPE if the found patch type is invalid.
48 *
49 * @param pVM The cross context VM structure.
50 * @param pVCpu The cross context virtual CPU structure.
51 */
52VMM_INT_DECL(int) hmEmulateSvmMovTpr(PVMCC pVM, PVMCPUCC pVCpu)
53{
54 PCPUMCTX pCtx = &pVCpu->cpum.GstCtx;
55 Log4(("Emulated VMMCall TPR access replacement at RIP=%RGv\n", pCtx->rip));
56
57 /*
58 * We do this in a loop as we increment the RIP after a successful emulation
59 * and the new RIP may be a patched instruction which needs emulation as well.
60 */
61 bool fPatchFound = false;
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) HMNotifySvmNstGstVmexit(PVMCPUCC 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 CPUM_ASSERT_NOT_EXTRN(pVCpu, CPUMCTX_EXTRN_ALL);
169 ASMAtomicUoOrU64(&pVCpu->hm.s.fCtxChanged, HM_CHANGED_ALL_GUEST);
170}
171#endif
172
173/**
174 * Checks if the Virtual GIF (Global Interrupt Flag) feature is supported and
175 * enabled for the VM.
176 *
177 * @returns @c true if VGIF is enabled, @c false otherwise.
178 * @param pVM The cross context VM structure.
179 *
180 * @remarks This value returned by this functions is expected by the callers not
181 * to change throughout the lifetime of the VM.
182 */
183VMM_INT_DECL(bool) HMIsSvmVGifActive(PCVM pVM)
184{
185 bool const fVGif = RT_BOOL(pVM->hm.s.svm.u32Features & X86_CPUID_SVM_FEATURE_EDX_VGIF);
186 bool const fUseVGif = fVGif && pVM->hm.s.svm.fVGif;
187 return fVGif && fUseVGif;
188}
189
190
191/**
192 * Interface used by IEM to handle patched TPR accesses.
193 *
194 * @returns VBox status code
195 * @retval VINF_SUCCESS if hypercall was handled, RIP + RFLAGS all dealt with.
196 * @retval VERR_NOT_FOUND if hypercall was _not_ handled.
197 * @retval VERR_SVM_UNEXPECTED_PATCH_TYPE on IPE.
198 *
199 * @param pVM The cross context VM structure.
200 * @param pVCpu The cross context virtual CPU structure.
201 */
202VMM_INT_DECL(int) HMHCMaybeMovTprSvmHypercall(PVMCC pVM, PVMCPUCC pVCpu)
203{
204 if (pVM->hm.s.fTprPatchingAllowed)
205 {
206 int rc = hmEmulateSvmMovTpr(pVM, pVCpu);
207 if (RT_SUCCESS(rc))
208 return VINF_SUCCESS;
209 return rc;
210 }
211 return VERR_NOT_FOUND;
212}
213
214
215/**
216 * Checks if the current AMD CPU is subject to erratum 170 "In SVM mode,
217 * incorrect code bytes may be fetched after a world-switch".
218 *
219 * @param pu32Family Where to store the CPU family (can be NULL).
220 * @param pu32Model Where to store the CPU model (can be NULL).
221 * @param pu32Stepping Where to store the CPU stepping (can be NULL).
222 * @returns true if the erratum applies, false otherwise.
223 */
224VMM_INT_DECL(int) HMIsSubjectToSvmErratum170(uint32_t *pu32Family, uint32_t *pu32Model, uint32_t *pu32Stepping)
225{
226 /*
227 * Erratum 170 which requires a forced TLB flush for each world switch:
228 * See AMD spec. "Revision Guide for AMD NPT Family 0Fh Processors".
229 *
230 * All BH-G1/2 and DH-G1/2 models include a fix:
231 * Athlon X2: 0x6b 1/2
232 * 0x68 1/2
233 * Athlon 64: 0x7f 1
234 * 0x6f 2
235 * Sempron: 0x7f 1/2
236 * 0x6f 2
237 * 0x6c 2
238 * 0x7c 2
239 * Turion 64: 0x68 2
240 */
241 uint32_t u32Dummy;
242 uint32_t u32Version, u32Family, u32Model, u32Stepping, u32BaseFamily;
243 ASMCpuId(1, &u32Version, &u32Dummy, &u32Dummy, &u32Dummy);
244 u32BaseFamily = (u32Version >> 8) & 0xf;
245 u32Family = u32BaseFamily + (u32BaseFamily == 0xf ? ((u32Version >> 20) & 0x7f) : 0);
246 u32Model = ((u32Version >> 4) & 0xf);
247 u32Model = u32Model | ((u32BaseFamily == 0xf ? (u32Version >> 16) & 0x0f : 0) << 4);
248 u32Stepping = u32Version & 0xf;
249
250 bool fErratumApplies = false;
251 if ( u32Family == 0xf
252 && !((u32Model == 0x68 || u32Model == 0x6b || u32Model == 0x7f) && u32Stepping >= 1)
253 && !((u32Model == 0x6f || u32Model == 0x6c || u32Model == 0x7c) && u32Stepping >= 2))
254 {
255 fErratumApplies = true;
256 }
257
258 if (pu32Family)
259 *pu32Family = u32Family;
260 if (pu32Model)
261 *pu32Model = u32Model;
262 if (pu32Stepping)
263 *pu32Stepping = u32Stepping;
264
265 return fErratumApplies;
266}
267
268
269
270/**
271 * Converts an SVM event type to a TRPM event type.
272 *
273 * @returns The TRPM event type.
274 * @retval TRPM_32BIT_HACK if the specified type of event isn't among the set
275 * of recognized trap types.
276 *
277 * @param pEvent Pointer to the SVM event.
278 * @param uVector The vector associated with the event.
279 */
280VMM_INT_DECL(TRPMEVENT) HMSvmEventToTrpmEventType(PCSVMEVENT pEvent, uint8_t uVector)
281{
282 uint8_t const uType = pEvent->n.u3Type;
283 switch (uType)
284 {
285 case SVM_EVENT_EXTERNAL_IRQ: return TRPM_HARDWARE_INT;
286 case SVM_EVENT_SOFTWARE_INT: return TRPM_SOFTWARE_INT;
287 case SVM_EVENT_NMI: return TRPM_TRAP;
288 case SVM_EVENT_EXCEPTION:
289 {
290 if ( uVector == X86_XCPT_BP
291 || uVector == X86_XCPT_OF)
292 return TRPM_SOFTWARE_INT;
293 return TRPM_TRAP;
294 }
295 default:
296 break;
297 }
298 AssertMsgFailed(("HMSvmEventToTrpmEvent: Invalid pending-event type %#x\n", uType));
299 return TRPM_32BIT_HACK;
300}
301
302
303/**
304 * Gets the SVM nested-guest control intercepts if cached by HM.
305 *
306 * @returns @c true on success, @c false otherwise.
307 * @param pVCpu The cross context virtual CPU structure of the calling
308 * EMT.
309 * @param pu64Intercepts Where to store the control intercepts. Only updated when
310 * @c true is returned.
311 */
312VMM_INT_DECL(bool) HMGetGuestSvmCtrlIntercepts(PCVMCPU pVCpu, uint64_t *pu64Intercepts)
313{
314 Assert(pu64Intercepts);
315 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
316 if (pVmcbNstGstCache->fCacheValid)
317 {
318 *pu64Intercepts = pVmcbNstGstCache->u64InterceptCtrl;
319 return true;
320 }
321 return false;
322}
323
324
325/**
326 * Gets the SVM nested-guest CRx-read intercepts if cached by HM.
327 *
328 * @returns @c true on success, @c false otherwise.
329 * @param pVCpu The cross context virtual CPU structure of the calling
330 * EMT.
331 * @param pu16Intercepts Where to store the CRx-read intercepts. Only updated
332 * when @c true is returned.
333 */
334VMM_INT_DECL(bool) HMGetGuestSvmReadCRxIntercepts(PCVMCPU pVCpu, uint16_t *pu16Intercepts)
335{
336 Assert(pu16Intercepts);
337 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
338 if (pVmcbNstGstCache->fCacheValid)
339 {
340 *pu16Intercepts = pVmcbNstGstCache->u16InterceptRdCRx;
341 return true;
342 }
343 return false;
344}
345
346
347/**
348 * Gets the SVM nested-guest CRx-write intercepts if cached by HM.
349 *
350 * @returns @c true on success, @c false otherwise.
351 * @param pVCpu The cross context virtual CPU structure of the calling
352 * EMT.
353 * @param pu16Intercepts Where to store the CRx-write intercepts. Only updated
354 * when @c true is returned.
355 */
356VMM_INT_DECL(bool) HMGetGuestSvmWriteCRxIntercepts(PCVMCPU pVCpu, uint16_t *pu16Intercepts)
357{
358 Assert(pu16Intercepts);
359 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
360 if (pVmcbNstGstCache->fCacheValid)
361 {
362 *pu16Intercepts = pVmcbNstGstCache->u16InterceptWrCRx;
363 return true;
364 }
365 return false;
366}
367
368
369/**
370 * Gets the SVM nested-guest DRx-read intercepts if cached by HM.
371 *
372 * @returns @c true on success, @c false otherwise.
373 * @param pVCpu The cross context virtual CPU structure of the calling
374 * EMT.
375 * @param pu16Intercepts Where to store the DRx-read intercepts. Only updated
376 * when @c true is returned.
377 */
378VMM_INT_DECL(bool) HMGetGuestSvmReadDRxIntercepts(PCVMCPU pVCpu, uint16_t *pu16Intercepts)
379{
380 Assert(pu16Intercepts);
381 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
382 if (pVmcbNstGstCache->fCacheValid)
383 {
384 *pu16Intercepts = pVmcbNstGstCache->u16InterceptRdDRx;
385 return true;
386 }
387 return false;
388}
389
390
391/**
392 * Gets the SVM nested-guest DRx-write intercepts if cached by HM.
393 *
394 * @returns @c true on success, @c false otherwise.
395 * @param pVCpu The cross context virtual CPU structure of the calling
396 * EMT.
397 * @param pu16Intercepts Where to store the DRx-write intercepts. Only updated
398 * when @c true is returned.
399 */
400VMM_INT_DECL(bool) HMGetGuestSvmWriteDRxIntercepts(PCVMCPU pVCpu, uint16_t *pu16Intercepts)
401{
402 Assert(pu16Intercepts);
403 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
404 if (pVmcbNstGstCache->fCacheValid)
405 {
406 *pu16Intercepts = pVmcbNstGstCache->u16InterceptWrDRx;
407 return true;
408 }
409 return false;
410}
411
412
413/**
414 * Gets the SVM nested-guest exception intercepts if cached by HM.
415 *
416 * @returns @c true on success, @c false otherwise.
417 * @param pVCpu The cross context virtual CPU structure of the calling
418 * EMT.
419 * @param pu32Intercepts Where to store the exception intercepts. Only updated
420 * when @c true is returned.
421 */
422VMM_INT_DECL(bool) HMGetGuestSvmXcptIntercepts(PCVMCPU pVCpu, uint32_t *pu32Intercepts)
423{
424 Assert(pu32Intercepts);
425 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
426 if (pVmcbNstGstCache->fCacheValid)
427 {
428 *pu32Intercepts = pVmcbNstGstCache->u32InterceptXcpt;
429 return true;
430 }
431 return false;
432}
433
434
435/**
436 * Checks if the nested-guest VMCB has virtual-interrupts masking enabled.
437 *
438 * @returns @c true on success, @c false otherwise.
439 * @param pVCpu The cross context virtual CPU structure of the calling
440 * EMT.
441 * @param pfVIntrMasking Where to store the virtual-interrupt masking bit.
442 * Updated only when @c true is returned.
443 */
444VMM_INT_DECL(bool) HMGetGuestSvmVirtIntrMasking(PCVMCPU pVCpu, bool *pfVIntrMasking)
445{
446 Assert(pfVIntrMasking);
447 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
448 if (pVmcbNstGstCache->fCacheValid)
449 {
450 *pfVIntrMasking = pVmcbNstGstCache->fVIntrMasking;
451 return true;
452 }
453 return false;
454}
455
456
457/**
458 * Gets the SVM nested-guest nested-paging bit if cached by HM.
459 *
460 * @returns @c true on success, @c false otherwise.
461 * @param pVCpu The cross context virtual CPU structure of the
462 * calling EMT.
463 * @param pfNestedPaging Where to store the nested-paging bit. Updated only
464 * when @c true is returned.
465 */
466VMM_INT_DECL(bool) HMGetGuestSvmNestedPaging(PCVMCPU pVCpu, bool *pfNestedPaging)
467{
468 Assert(pfNestedPaging);
469 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
470 if (pVmcbNstGstCache->fCacheValid)
471 {
472 *pfNestedPaging = pVmcbNstGstCache->fNestedPaging;
473 return true;
474 }
475 return false;
476}
477
478
479/**
480 * Returns the nested-guest VMCB pause-filter count.
481 *
482 * @returns @c true on success, @c false otherwise.
483 * @param pVCpu The cross context virtual CPU structure of the
484 * calling EMT.
485 * @param pu16PauseFilterCount Where to store the pause-filter count. Only
486 * updated @c true is returned.
487 */
488VMM_INT_DECL(bool) HMGetGuestSvmPauseFilterCount(PCVMCPU pVCpu, uint16_t *pu16PauseFilterCount)
489{
490 Assert(pu16PauseFilterCount);
491 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
492 if (pVmcbNstGstCache->fCacheValid)
493 {
494 *pu16PauseFilterCount = pVmcbNstGstCache->u16PauseFilterCount;
495 return true;
496 }
497 return false;
498}
499
500
501/**
502 * Returns the SVM nested-guest TSC offset if cached by HM.
503 *
504 * @returns The TSC offset after applying any nested-guest TSC offset.
505 * @param pVCpu The cross context virtual CPU structure of the calling
506 * EMT.
507 * @param pu64TscOffset Where to store the TSC offset. Only updated when @c
508 * true is returned.
509 */
510VMM_INT_DECL(bool) HMGetGuestSvmTscOffset(PCVMCPU pVCpu, uint64_t *pu64TscOffset)
511{
512 Assert(pu64TscOffset);
513 PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
514 if (pVmcbNstGstCache->fCacheValid)
515 {
516 *pu64TscOffset = pVmcbNstGstCache->u64TSCOffset;
517 return true;
518 }
519 return false;
520}
521
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use