VirtualBox

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

Last change on this file since 78981 was 78871, checked in by vboxsync, 6 years ago

VMM/HMSVM: Nested SVM: bugref:7243 Doxygen.

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