summaryrefslogtreecommitdiffstats
path: root/src/VBox/VMM/VMMAll/HMSVMAll.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/VMM/VMMAll/HMSVMAll.cpp
parentInitial commit. (diff)
downloadvirtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz
virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/VMM/VMMAll/HMSVMAll.cpp')
-rw-r--r--src/VBox/VMM/VMMAll/HMSVMAll.cpp610
1 files changed, 610 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMAll/HMSVMAll.cpp b/src/VBox/VMM/VMMAll/HMSVMAll.cpp
new file mode 100644
index 00000000..e48baf82
--- /dev/null
+++ b/src/VBox/VMM/VMMAll/HMSVMAll.cpp
@@ -0,0 +1,610 @@
+/* $Id: HMSVMAll.cpp $ */
+/** @file
+ * HM SVM (AMD-V) - All contexts.
+ */
+
+/*
+ * Copyright (C) 2017-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_HM
+#define VMCPU_INCL_CPUM_GST_CTX
+#include "HMInternal.h"
+#include <VBox/vmm/apic.h>
+#include <VBox/vmm/gim.h>
+#include <VBox/vmm/iem.h>
+#include <VBox/vmm/vm.h>
+
+#include <VBox/err.h>
+
+
+#ifndef IN_RC
+
+/**
+ * Emulates a simple MOV TPR (CR8) instruction.
+ *
+ * Used for TPR patching on 32-bit guests. This simply looks up the patch record
+ * at EIP and does the required.
+ *
+ * This VMMCALL is used a fallback mechanism when mov to/from cr8 isn't exactly
+ * like how we want it to be (e.g. not followed by shr 4 as is usually done for
+ * TPR). See hmR3ReplaceTprInstr() for the details.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS if the access was handled successfully, RIP + RFLAGS updated.
+ * @retval VERR_NOT_FOUND if no patch record for this RIP could be found.
+ * @retval VERR_SVM_UNEXPECTED_PATCH_TYPE if the found patch type is invalid.
+ *
+ * @param pVCpu The cross context virtual CPU structure.
+ * @param pCtx Pointer to the guest-CPU context.
+ */
+int hmSvmEmulateMovTpr(PVMCPU pVCpu)
+{
+ PCPUMCTX pCtx = &pVCpu->cpum.GstCtx;
+ Log4(("Emulated VMMCall TPR access replacement at RIP=%RGv\n", pCtx->rip));
+
+ /*
+ * We do this in a loop as we increment the RIP after a successful emulation
+ * and the new RIP may be a patched instruction which needs emulation as well.
+ */
+ bool fPatchFound = false;
+ PVM pVM = pVCpu->CTX_SUFF(pVM);
+ for (;;)
+ {
+ PHMTPRPATCH pPatch = (PHMTPRPATCH)RTAvloU32Get(&pVM->hm.s.PatchTree, (AVLOU32KEY)pCtx->eip);
+ if (!pPatch)
+ break;
+ fPatchFound = true;
+
+ uint8_t u8Tpr;
+ switch (pPatch->enmType)
+ {
+ case HMTPRINSTR_READ:
+ {
+ bool fPending;
+ int rc = APICGetTpr(pVCpu, &u8Tpr, &fPending, NULL /* pu8PendingIrq */);
+ AssertRC(rc);
+
+ rc = DISWriteReg32(CPUMCTX2CORE(pCtx), pPatch->uDstOperand, u8Tpr);
+ AssertRC(rc);
+ pCtx->rip += pPatch->cbOp;
+ pCtx->eflags.Bits.u1RF = 0;
+ break;
+ }
+
+ case HMTPRINSTR_WRITE_REG:
+ case HMTPRINSTR_WRITE_IMM:
+ {
+ if (pPatch->enmType == HMTPRINSTR_WRITE_REG)
+ {
+ uint32_t u32Val;
+ int rc = DISFetchReg32(CPUMCTX2CORE(pCtx), pPatch->uSrcOperand, &u32Val);
+ AssertRC(rc);
+ u8Tpr = u32Val;
+ }
+ else
+ u8Tpr = (uint8_t)pPatch->uSrcOperand;
+
+ int rc2 = APICSetTpr(pVCpu, u8Tpr);
+ AssertRC(rc2);
+ pCtx->rip += pPatch->cbOp;
+ pCtx->eflags.Bits.u1RF = 0;
+ ASMAtomicUoOrU64(&pVCpu->hm.s.fCtxChanged, HM_CHANGED_GUEST_APIC_TPR
+ | HM_CHANGED_GUEST_RIP
+ | HM_CHANGED_GUEST_RFLAGS);
+ break;
+ }
+
+ default:
+ {
+ AssertMsgFailed(("Unexpected patch type %d\n", pPatch->enmType));
+ pVCpu->hm.s.u32HMError = pPatch->enmType;
+ return VERR_SVM_UNEXPECTED_PATCH_TYPE;
+ }
+ }
+ }
+
+ return fPatchFound ? VINF_SUCCESS : VERR_NOT_FOUND;
+}
+
+# ifdef VBOX_WITH_NESTED_HWVIRT_SVM
+/**
+ * Notification callback for when a \#VMEXIT happens outside SVM R0 code (e.g.
+ * in IEM).
+ *
+ * @param pVCpu The cross context virtual CPU structure.
+ * @param pCtx Pointer to the guest-CPU context.
+ *
+ * @sa hmR0SvmVmRunCacheVmcb.
+ */
+VMM_INT_DECL(void) HMNotifySvmNstGstVmexit(PVMCPU pVCpu, PCPUMCTX pCtx)
+{
+ PSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
+ if (pVmcbNstGstCache->fCacheValid)
+ {
+ /*
+ * Restore fields as our own code might look at the VMCB controls as part
+ * of the #VMEXIT handling in IEM. Otherwise, strictly speaking we don't need to
+ * restore these fields because currently none of them are written back to memory
+ * by a physical CPU on #VMEXIT.
+ */
+ PSVMVMCBCTRL pVmcbNstGstCtrl = &pCtx->hwvirt.svm.CTX_SUFF(pVmcb)->ctrl;
+ pVmcbNstGstCtrl->u16InterceptRdCRx = pVmcbNstGstCache->u16InterceptRdCRx;
+ pVmcbNstGstCtrl->u16InterceptWrCRx = pVmcbNstGstCache->u16InterceptWrCRx;
+ pVmcbNstGstCtrl->u16InterceptRdDRx = pVmcbNstGstCache->u16InterceptRdDRx;
+ pVmcbNstGstCtrl->u16InterceptWrDRx = pVmcbNstGstCache->u16InterceptWrDRx;
+ pVmcbNstGstCtrl->u16PauseFilterThreshold = pVmcbNstGstCache->u16PauseFilterThreshold;
+ pVmcbNstGstCtrl->u16PauseFilterCount = pVmcbNstGstCache->u16PauseFilterCount;
+ pVmcbNstGstCtrl->u32InterceptXcpt = pVmcbNstGstCache->u32InterceptXcpt;
+ pVmcbNstGstCtrl->u64InterceptCtrl = pVmcbNstGstCache->u64InterceptCtrl;
+ pVmcbNstGstCtrl->u64TSCOffset = pVmcbNstGstCache->u64TSCOffset;
+ pVmcbNstGstCtrl->IntCtrl.n.u1VIntrMasking = pVmcbNstGstCache->fVIntrMasking;
+ pVmcbNstGstCtrl->NestedPagingCtrl.n.u1NestedPaging = pVmcbNstGstCache->fNestedPaging;
+ pVmcbNstGstCtrl->LbrVirt.n.u1LbrVirt = pVmcbNstGstCache->fLbrVirt;
+ pVmcbNstGstCache->fCacheValid = false;
+ }
+
+ /*
+ * Transitions to ring-3 flag a full CPU-state change except if we transition to ring-3
+ * in response to a physical CPU interrupt as no changes to the guest-CPU state are
+ * expected (see VINF_EM_RAW_INTERRUPT handling in hmR0SvmExitToRing3).
+ *
+ * However, with nested-guests, the state -can- change on trips to ring-3 for we might
+ * try to inject a nested-guest physical interrupt and cause a SVM_EXIT_INTR #VMEXIT for
+ * the nested-guest from ring-3. Import the complete state here as we will be swapping
+ * to the guest VMCB after the #VMEXIT.
+ */
+ CPUMImportGuestStateOnDemand(pVCpu, CPUMCTX_EXTRN_ALL);
+ AssertMsg(!(pVCpu->cpum.GstCtx.fExtrn & CPUMCTX_EXTRN_ALL),
+ ("fExtrn=%#RX64 fExtrnMbz=%#RX64\n", pVCpu->cpum.GstCtx.fExtrn, CPUMCTX_EXTRN_ALL));
+ ASMAtomicUoOrU64(&pVCpu->hm.s.fCtxChanged, HM_CHANGED_ALL_GUEST);
+}
+# endif
+
+/**
+ * Checks if the Virtual GIF (Global Interrupt Flag) feature is supported and
+ * enabled for the VM.
+ *
+ * @returns @c true if VGIF is enabled, @c false otherwise.
+ * @param pVM The cross context VM structure.
+ *
+ * @remarks This value returned by this functions is expected by the callers not
+ * to change throughout the lifetime of the VM.
+ */
+VMM_INT_DECL(bool) HMIsSvmVGifActive(PVM pVM)
+{
+ bool const fVGif = RT_BOOL(pVM->hm.s.svm.u32Features & X86_CPUID_SVM_FEATURE_EDX_VGIF);
+ bool const fUseVGif = fVGif && pVM->hm.s.svm.fVGif;
+ return fVGif && fUseVGif;
+}
+
+
+/**
+ * Applies the TSC offset of an SVM nested-guest if any and returns the new TSC
+ * value for the nested-guest.
+ *
+ * @returns The TSC offset after applying any nested-guest TSC offset.
+ * @param pVCpu The cross context virtual CPU structure of the calling EMT.
+ * @param uTicks The guest TSC.
+ *
+ * @remarks This function looks at the VMCB cache rather than directly at the
+ * nested-guest VMCB. The latter may have been modified for executing
+ * using hardware-assisted SVM.
+ *
+ * @note If you make any changes to this function, please check if
+ * hmR0SvmNstGstUndoTscOffset() needs adjusting.
+ *
+ * @sa CPUMApplyNestedGuestTscOffset(), hmR0SvmNstGstUndoTscOffset().
+ */
+VMM_INT_DECL(uint64_t) HMApplySvmNstGstTscOffset(PVMCPU pVCpu, uint64_t uTicks)
+{
+ PCCPUMCTX pCtx = &pVCpu->cpum.GstCtx;
+ Assert(CPUMIsGuestInSvmNestedHwVirtMode(pCtx)); RT_NOREF(pCtx);
+ PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
+ Assert(pVmcbNstGstCache->fCacheValid);
+ return uTicks + pVmcbNstGstCache->u64TSCOffset;
+}
+
+
+/**
+ * Interface used by IEM to handle patched TPR accesses.
+ *
+ * @returns VBox status code
+ * @retval VINF_SUCCESS if hypercall was handled, RIP + RFLAGS all dealt with.
+ * @retval VERR_NOT_FOUND if hypercall was _not_ handled.
+ * @retval VERR_SVM_UNEXPECTED_PATCH_TYPE on IPE.
+ *
+ * @param pVCpu The cross context virtual CPU structure.
+ */
+VMM_INT_DECL(int) HMHCMaybeMovTprSvmHypercall(PVMCPU pVCpu)
+{
+ PVM pVM = pVCpu->CTX_SUFF(pVM);
+ if (pVM->hm.s.fTprPatchingAllowed)
+ {
+ int rc = hmSvmEmulateMovTpr(pVCpu);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ return rc;
+ }
+ return VERR_NOT_FOUND;
+}
+
+
+/**
+ * Checks if the current AMD CPU is subject to erratum 170 "In SVM mode,
+ * incorrect code bytes may be fetched after a world-switch".
+ *
+ * @param pu32Family Where to store the CPU family (can be NULL).
+ * @param pu32Model Where to store the CPU model (can be NULL).
+ * @param pu32Stepping Where to store the CPU stepping (can be NULL).
+ * @returns true if the erratum applies, false otherwise.
+ */
+VMM_INT_DECL(int) HMIsSubjectToSvmErratum170(uint32_t *pu32Family, uint32_t *pu32Model, uint32_t *pu32Stepping)
+{
+ /*
+ * Erratum 170 which requires a forced TLB flush for each world switch:
+ * See AMD spec. "Revision Guide for AMD NPT Family 0Fh Processors".
+ *
+ * All BH-G1/2 and DH-G1/2 models include a fix:
+ * Athlon X2: 0x6b 1/2
+ * 0x68 1/2
+ * Athlon 64: 0x7f 1
+ * 0x6f 2
+ * Sempron: 0x7f 1/2
+ * 0x6f 2
+ * 0x6c 2
+ * 0x7c 2
+ * Turion 64: 0x68 2
+ */
+ uint32_t u32Dummy;
+ uint32_t u32Version, u32Family, u32Model, u32Stepping, u32BaseFamily;
+ ASMCpuId(1, &u32Version, &u32Dummy, &u32Dummy, &u32Dummy);
+ u32BaseFamily = (u32Version >> 8) & 0xf;
+ u32Family = u32BaseFamily + (u32BaseFamily == 0xf ? ((u32Version >> 20) & 0x7f) : 0);
+ u32Model = ((u32Version >> 4) & 0xf);
+ u32Model = u32Model | ((u32BaseFamily == 0xf ? (u32Version >> 16) & 0x0f : 0) << 4);
+ u32Stepping = u32Version & 0xf;
+
+ bool fErratumApplies = false;
+ if ( u32Family == 0xf
+ && !((u32Model == 0x68 || u32Model == 0x6b || u32Model == 0x7f) && u32Stepping >= 1)
+ && !((u32Model == 0x6f || u32Model == 0x6c || u32Model == 0x7c) && u32Stepping >= 2))
+ {
+ fErratumApplies = true;
+ }
+
+ if (pu32Family)
+ *pu32Family = u32Family;
+ if (pu32Model)
+ *pu32Model = u32Model;
+ if (pu32Stepping)
+ *pu32Stepping = u32Stepping;
+
+ return fErratumApplies;
+}
+
+#endif /* !IN_RC */
+
+/**
+ * Gets the MSR permission bitmap byte and bit offset for the specified MSR.
+ *
+ * @returns VBox status code.
+ * @param idMsr The MSR being requested.
+ * @param pbOffMsrpm Where to store the byte offset in the MSR permission
+ * bitmap for @a idMsr.
+ * @param puMsrpmBit Where to store the bit offset starting at the byte
+ * returned in @a pbOffMsrpm.
+ */
+VMM_INT_DECL(int) HMGetSvmMsrpmOffsetAndBit(uint32_t idMsr, uint16_t *pbOffMsrpm, uint8_t *puMsrpmBit)
+{
+ Assert(pbOffMsrpm);
+ Assert(puMsrpmBit);
+
+ /*
+ * MSRPM Layout:
+ * Byte offset MSR range
+ * 0x000 - 0x7ff 0x00000000 - 0x00001fff
+ * 0x800 - 0xfff 0xc0000000 - 0xc0001fff
+ * 0x1000 - 0x17ff 0xc0010000 - 0xc0011fff
+ * 0x1800 - 0x1fff Reserved
+ *
+ * Each MSR is represented by 2 permission bits (read and write).
+ */
+ if (idMsr <= 0x00001fff)
+ {
+ /* Pentium-compatible MSRs. */
+ uint32_t const bitoffMsr = idMsr << 1;
+ *pbOffMsrpm = bitoffMsr >> 3;
+ *puMsrpmBit = bitoffMsr & 7;
+ return VINF_SUCCESS;
+ }
+
+ if ( idMsr >= 0xc0000000
+ && idMsr <= 0xc0001fff)
+ {
+ /* AMD Sixth Generation x86 Processor MSRs. */
+ uint32_t const bitoffMsr = (idMsr - 0xc0000000) << 1;
+ *pbOffMsrpm = 0x800 + (bitoffMsr >> 3);
+ *puMsrpmBit = bitoffMsr & 7;
+ return VINF_SUCCESS;
+ }
+
+ if ( idMsr >= 0xc0010000
+ && idMsr <= 0xc0011fff)
+ {
+ /* AMD Seventh and Eighth Generation Processor MSRs. */
+ uint32_t const bitoffMsr = (idMsr - 0xc0010000) << 1;
+ *pbOffMsrpm = 0x1000 + (bitoffMsr >> 3);
+ *puMsrpmBit = bitoffMsr & 7;
+ return VINF_SUCCESS;
+ }
+
+ *pbOffMsrpm = 0;
+ *puMsrpmBit = 0;
+ return VERR_OUT_OF_RANGE;
+}
+
+
+/**
+ * Determines whether an IOIO intercept is active for the nested-guest or not.
+ *
+ * @param pvIoBitmap Pointer to the nested-guest IO bitmap.
+ * @param u16Port The IO port being accessed.
+ * @param enmIoType The type of IO access.
+ * @param cbReg The IO operand size in bytes.
+ * @param cAddrSizeBits The address size bits (for 16, 32 or 64).
+ * @param iEffSeg The effective segment number.
+ * @param fRep Whether this is a repeating IO instruction (REP prefix).
+ * @param fStrIo Whether this is a string IO instruction.
+ * @param pIoExitInfo Pointer to the SVMIOIOEXITINFO struct to be filled.
+ * Optional, can be NULL.
+ */
+VMM_INT_DECL(bool) HMIsSvmIoInterceptActive(void *pvIoBitmap, uint16_t u16Port, SVMIOIOTYPE enmIoType, uint8_t cbReg,
+ uint8_t cAddrSizeBits, uint8_t iEffSeg, bool fRep, bool fStrIo,
+ PSVMIOIOEXITINFO pIoExitInfo)
+{
+ Assert(cAddrSizeBits == 16 || cAddrSizeBits == 32 || cAddrSizeBits == 64);
+ Assert(cbReg == 1 || cbReg == 2 || cbReg == 4 || cbReg == 8);
+
+ /*
+ * The IOPM layout:
+ * Each bit represents one 8-bit port. That makes a total of 0..65535 bits or
+ * two 4K pages.
+ *
+ * For IO instructions that access more than a single byte, the permission bits
+ * for all bytes are checked; if any bit is set to 1, the IO access is intercepted.
+ *
+ * Since it's possible to do a 32-bit IO access at port 65534 (accessing 4 bytes),
+ * we need 3 extra bits beyond the second 4K page.
+ */
+ static const uint16_t s_auSizeMasks[] = { 0, 1, 3, 0, 0xf, 0, 0, 0 };
+
+ uint16_t const offIopm = u16Port >> 3;
+ uint16_t const fSizeMask = s_auSizeMasks[(cAddrSizeBits >> SVM_IOIO_OP_SIZE_SHIFT) & 7];
+ uint8_t const cShift = u16Port - (offIopm << 3);
+ uint16_t const fIopmMask = (1 << cShift) | (fSizeMask << cShift);
+
+ uint8_t const *pbIopm = (uint8_t *)pvIoBitmap;
+ Assert(pbIopm);
+ pbIopm += offIopm;
+ uint16_t const u16Iopm = *(uint16_t *)pbIopm;
+ if (u16Iopm & fIopmMask)
+ {
+ if (pIoExitInfo)
+ {
+ static const uint32_t s_auIoOpSize[] =
+ { SVM_IOIO_32_BIT_OP, SVM_IOIO_8_BIT_OP, SVM_IOIO_16_BIT_OP, 0, SVM_IOIO_32_BIT_OP, 0, 0, 0 };
+
+ static const uint32_t s_auIoAddrSize[] =
+ { 0, SVM_IOIO_16_BIT_ADDR, SVM_IOIO_32_BIT_ADDR, 0, SVM_IOIO_64_BIT_ADDR, 0, 0, 0 };
+
+ pIoExitInfo->u = s_auIoOpSize[cbReg & 7];
+ pIoExitInfo->u |= s_auIoAddrSize[(cAddrSizeBits >> 4) & 7];
+ pIoExitInfo->n.u1Str = fStrIo;
+ pIoExitInfo->n.u1Rep = fRep;
+ pIoExitInfo->n.u3Seg = iEffSeg & 7;
+ pIoExitInfo->n.u1Type = enmIoType;
+ pIoExitInfo->n.u16Port = u16Port;
+ }
+ return true;
+ }
+
+ /** @todo remove later (for debugging as VirtualBox always traps all IO
+ * intercepts). */
+ AssertMsgFailed(("CPUMSvmIsIOInterceptActive: We expect an IO intercept here!\n"));
+ return false;
+}
+
+
+/**
+ * Converts an SVM event type to a TRPM event type.
+ *
+ * @returns The TRPM event type.
+ * @retval TRPM_32BIT_HACK if the specified type of event isn't among the set
+ * of recognized trap types.
+ *
+ * @param pEvent Pointer to the SVM event.
+ */
+VMM_INT_DECL(TRPMEVENT) HMSvmEventToTrpmEventType(PCSVMEVENT pEvent)
+{
+ uint8_t const uType = pEvent->n.u3Type;
+ switch (uType)
+ {
+ case SVM_EVENT_EXTERNAL_IRQ: return TRPM_HARDWARE_INT;
+ case SVM_EVENT_SOFTWARE_INT: return TRPM_SOFTWARE_INT;
+ case SVM_EVENT_EXCEPTION:
+ case SVM_EVENT_NMI: return TRPM_TRAP;
+ default:
+ break;
+ }
+ AssertMsgFailed(("HMSvmEventToTrpmEvent: Invalid pending-event type %#x\n", uType));
+ return TRPM_32BIT_HACK;
+}
+
+
+/**
+ * Returns whether HM has cached the nested-guest VMCB.
+ *
+ * If the VMCB is cached by HM, it means HM may have potentially modified the
+ * VMCB for execution using hardware-assisted SVM.
+ *
+ * @returns true if HM has cached the nested-guest VMCB, false otherwise.
+ * @param pVCpu The cross context virtual CPU structure of the calling EMT.
+ */
+VMM_INT_DECL(bool) HMHasGuestSvmVmcbCached(PVMCPU pVCpu)
+{
+ PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
+ return pVmcbNstGstCache->fCacheValid;
+}
+
+
+/**
+ * Checks if the nested-guest VMCB has the specified ctrl/instruction intercept
+ * active.
+ *
+ * @returns @c true if in intercept is set, @c false otherwise.
+ * @param pVCpu The cross context virtual CPU structure of the calling EMT.
+ * @param fIntercept The SVM control/instruction intercept, see
+ * SVM_CTRL_INTERCEPT_*.
+ */
+VMM_INT_DECL(bool) HMIsGuestSvmCtrlInterceptSet(PVMCPU pVCpu, uint64_t fIntercept)
+{
+ Assert(HMHasGuestSvmVmcbCached(pVCpu));
+ PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
+ return RT_BOOL(pVmcbNstGstCache->u64InterceptCtrl & fIntercept);
+}
+
+
+/**
+ * Checks if the nested-guest VMCB has the specified CR read intercept active.
+ *
+ * @returns @c true if in intercept is set, @c false otherwise.
+ * @param pVCpu The cross context virtual CPU structure of the calling EMT.
+ * @param uCr The CR register number (0 to 15).
+ */
+VMM_INT_DECL(bool) HMIsGuestSvmReadCRxInterceptSet(PVMCPU pVCpu, uint8_t uCr)
+{
+ Assert(uCr < 16);
+ Assert(HMHasGuestSvmVmcbCached(pVCpu));
+ PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
+ return RT_BOOL(pVmcbNstGstCache->u16InterceptRdCRx & (1 << uCr));
+}
+
+
+/**
+ * Checks if the nested-guest VMCB has the specified CR write intercept active.
+ *
+ * @returns @c true if in intercept is set, @c false otherwise.
+ * @param pVCpu The cross context virtual CPU structure of the calling EMT.
+ * @param uCr The CR register number (0 to 15).
+ */
+VMM_INT_DECL(bool) HMIsGuestSvmWriteCRxInterceptSet(PVMCPU pVCpu, uint8_t uCr)
+{
+ Assert(uCr < 16);
+ Assert(HMHasGuestSvmVmcbCached(pVCpu));
+ PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
+ return RT_BOOL(pVmcbNstGstCache->u16InterceptWrCRx & (1 << uCr));
+}
+
+
+/**
+ * Checks if the nested-guest VMCB has the specified DR read intercept active.
+ *
+ * @returns @c true if in intercept is set, @c false otherwise.
+ * @param pVCpu The cross context virtual CPU structure of the calling EMT.
+ * @param uDr The DR register number (0 to 15).
+ */
+VMM_INT_DECL(bool) HMIsGuestSvmReadDRxInterceptSet(PVMCPU pVCpu, uint8_t uDr)
+{
+ Assert(uDr < 16);
+ Assert(HMHasGuestSvmVmcbCached(pVCpu));
+ PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
+ return RT_BOOL(pVmcbNstGstCache->u16InterceptRdDRx & (1 << uDr));
+}
+
+
+/**
+ * Checks if the nested-guest VMCB has the specified DR write intercept active.
+ *
+ * @returns @c true if in intercept is set, @c false otherwise.
+ * @param pVCpu The cross context virtual CPU structure of the calling EMT.
+ * @param uDr The DR register number (0 to 15).
+ */
+VMM_INT_DECL(bool) HMIsGuestSvmWriteDRxInterceptSet(PVMCPU pVCpu, uint8_t uDr)
+{
+ Assert(uDr < 16);
+ Assert(HMHasGuestSvmVmcbCached(pVCpu));
+ PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
+ return RT_BOOL(pVmcbNstGstCache->u16InterceptWrDRx & (1 << uDr));
+}
+
+
+/**
+ * Checks if the nested-guest VMCB has the specified exception intercept active.
+ *
+ * @returns true if in intercept is active, false otherwise.
+ * @param pVCpu The cross context virtual CPU structure of the calling EMT.
+ * @param uVector The exception / interrupt vector.
+ */
+VMM_INT_DECL(bool) HMIsGuestSvmXcptInterceptSet(PVMCPU pVCpu, uint8_t uVector)
+{
+ Assert(uVector < 32);
+ Assert(HMHasGuestSvmVmcbCached(pVCpu));
+ PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
+ return RT_BOOL(pVmcbNstGstCache->u32InterceptXcpt & (1 << uVector));
+}
+
+
+/**
+ * Checks if the nested-guest VMCB has virtual-interrupts masking enabled.
+ *
+ * @returns true if virtual-interrupts are masked, @c false otherwise.
+ * @param pVCpu The cross context virtual CPU structure of the calling EMT.
+ */
+VMM_INT_DECL(bool) HMIsGuestSvmVirtIntrMasking(PVMCPU pVCpu)
+{
+ Assert(HMHasGuestSvmVmcbCached(pVCpu));
+ PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
+ return pVmcbNstGstCache->fVIntrMasking;
+}
+
+
+/**
+ * Checks if the nested-guest VMCB has nested-paging enabled.
+ *
+ * @returns true if nested-paging is enabled, @c false otherwise.
+ * @param pVCpu The cross context virtual CPU structure of the calling EMT.
+ */
+VMM_INT_DECL(bool) HMIsGuestSvmNestedPagingEnabled(PVMCPU pVCpu)
+{
+ Assert(HMHasGuestSvmVmcbCached(pVCpu));
+ PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
+ return pVmcbNstGstCache->fNestedPaging;
+}
+
+
+/**
+ * Returns the nested-guest VMCB pause-filter count.
+ *
+ * @returns The pause-filter count.
+ * @param pVCpu The cross context virtual CPU structure of the calling EMT.
+ */
+VMM_INT_DECL(uint16_t) HMGetGuestSvmPauseFilterCount(PVMCPU pVCpu)
+{
+ Assert(HMHasGuestSvmVmcbCached(pVCpu));
+ PCSVMNESTEDVMCBCACHE pVmcbNstGstCache = &pVCpu->hm.s.svm.NstGstVmcbCache;
+ return pVmcbNstGstCache->u16PauseFilterCount;
+}
+