summaryrefslogtreecommitdiffstats
path: root/src/VBox/VMM/VMMAll/SELMAll.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/VMM/VMMAll/SELMAll.cpp')
-rw-r--r--src/VBox/VMM/VMMAll/SELMAll.cpp392
1 files changed, 392 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMAll/SELMAll.cpp b/src/VBox/VMM/VMMAll/SELMAll.cpp
new file mode 100644
index 00000000..c4320022
--- /dev/null
+++ b/src/VBox/VMM/VMMAll/SELMAll.cpp
@@ -0,0 +1,392 @@
+/* $Id: SELMAll.cpp $ */
+/** @file
+ * SELM All contexts.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SELM
+#include <VBox/vmm/selm.h>
+#include <VBox/vmm/stam.h>
+#include <VBox/vmm/em.h>
+#include <VBox/vmm/mm.h>
+#include <VBox/vmm/hm.h>
+#include <VBox/vmm/pgm.h>
+#include <VBox/vmm/hm.h>
+#include "SELMInternal.h"
+#include <VBox/vmm/vmcc.h>
+#include <VBox/err.h>
+#include <VBox/param.h>
+#include <iprt/assert.h>
+#include <VBox/vmm/vmm.h>
+#include <iprt/x86.h>
+#include <iprt/string.h>
+
+
+
+/**
+ * Converts a GC selector based address to a flat address.
+ *
+ * No limit checks are done. Use the SELMToFlat*() or SELMValidate*() functions
+ * for that.
+ *
+ * @returns Flat address.
+ * @param pVCpu The cross context virtual CPU structure.
+ * @param idxSeg The selector register to use (X86_SREG_XXX).
+ * @param pCtx Pointer to the register context for the CPU.
+ * @param Addr Address part.
+ */
+VMMDECL(RTGCPTR) SELMToFlat(PVMCPUCC pVCpu, unsigned idxSeg, PCPUMCTX pCtx, RTGCPTR Addr)
+{
+ Assert(idxSeg < RT_ELEMENTS(pCtx->aSRegs));
+ PCPUMSELREG pSReg = &pCtx->aSRegs[idxSeg];
+
+ /*
+ * Deal with real & v86 mode first.
+ */
+ if ( pCtx->eflags.Bits.u1VM
+ || CPUMIsGuestInRealMode(pVCpu))
+ {
+ uint32_t uFlat = (uint32_t)Addr & 0xffff;
+ if (CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSReg))
+ uFlat += (uint32_t)pSReg->u64Base;
+ else
+ uFlat += (uint32_t)pSReg->Sel << 4;
+ return (RTGCPTR)uFlat;
+ }
+
+ Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSReg));
+ Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, &pCtx->cs));
+
+ /* 64 bits mode: CS, DS, ES and SS are treated as if each segment base is 0
+ (Intel(r) 64 and IA-32 Architectures Software Developer's Manual: 3.4.2.1). */
+ if ( pCtx->cs.Attr.n.u1Long
+ && CPUMIsGuestInLongMode(pVCpu))
+ {
+ switch (idxSeg)
+ {
+ case X86_SREG_FS:
+ case X86_SREG_GS:
+ return (RTGCPTR)(pSReg->u64Base + Addr);
+
+ default:
+ return Addr; /* base 0 */
+ }
+ }
+
+ /* AMD64 manual: compatibility mode ignores the high 32 bits when calculating an effective address. */
+ Assert(pSReg->u64Base <= 0xffffffff);
+ return (uint32_t)pSReg->u64Base + (uint32_t)Addr;
+}
+
+
+/**
+ * Converts a GC selector based address to a flat address.
+ *
+ * Some basic checking is done, but not all kinds yet.
+ *
+ * @returns VBox status
+ * @param pVCpu The cross context virtual CPU structure.
+ * @param idxSeg The selector register to use (X86_SREG_XXX).
+ * @param pCtx Pointer to the register context for the CPU.
+ * @param Addr Address part.
+ * @param fFlags SELMTOFLAT_FLAGS_*
+ * GDT entires are valid.
+ * @param ppvGC Where to store the GC flat address.
+ */
+VMMDECL(int) SELMToFlatEx(PVMCPU pVCpu, unsigned idxSeg, PCPUMCTX pCtx, RTGCPTR Addr, uint32_t fFlags, PRTGCPTR ppvGC)
+{
+ AssertReturn(idxSeg < RT_ELEMENTS(pCtx->aSRegs), VERR_INVALID_PARAMETER);
+ PCPUMSELREG pSReg = &pCtx->aSRegs[idxSeg];
+
+ /*
+ * Deal with real & v86 mode first.
+ */
+ if ( pCtx->eflags.Bits.u1VM
+ || CPUMIsGuestInRealMode(pVCpu))
+ {
+ if (ppvGC)
+ {
+ uint32_t uFlat = (uint32_t)Addr & 0xffff;
+ if (CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSReg))
+ *ppvGC = (uint32_t)pSReg->u64Base + uFlat;
+ else
+ *ppvGC = ((uint32_t)pSReg->Sel << 4) + uFlat;
+ }
+ return VINF_SUCCESS;
+ }
+
+ Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSReg));
+ Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, &pCtx->cs));
+
+ /* 64 bits mode: CS, DS, ES and SS are treated as if each segment base is 0
+ (Intel(r) 64 and IA-32 Architectures Software Developer's Manual: 3.4.2.1). */
+ RTGCPTR pvFlat;
+ bool fCheckLimit = true;
+ if ( pCtx->cs.Attr.n.u1Long
+ && CPUMIsGuestInLongMode(pVCpu))
+ {
+ fCheckLimit = false;
+ switch (idxSeg)
+ {
+ case X86_SREG_FS:
+ case X86_SREG_GS:
+ pvFlat = pSReg->u64Base + Addr;
+ break;
+
+ default:
+ pvFlat = Addr;
+ break;
+ }
+ }
+ else
+ {
+ /* AMD64 manual: compatibility mode ignores the high 32 bits when calculating an effective address. */
+ Assert(pSReg->u64Base <= UINT32_C(0xffffffff));
+ pvFlat = (uint32_t)pSReg->u64Base + (uint32_t)Addr;
+ Assert(pvFlat <= UINT32_MAX);
+ }
+
+ /*
+ * Check type if present.
+ */
+ if (pSReg->Attr.n.u1Present)
+ {
+ switch (pSReg->Attr.n.u4Type)
+ {
+ /* Read only selector type. */
+ case X86_SEL_TYPE_RO:
+ case X86_SEL_TYPE_RO_ACC:
+ case X86_SEL_TYPE_RW:
+ case X86_SEL_TYPE_RW_ACC:
+ case X86_SEL_TYPE_EO:
+ case X86_SEL_TYPE_EO_ACC:
+ case X86_SEL_TYPE_ER:
+ case X86_SEL_TYPE_ER_ACC:
+ if (!(fFlags & SELMTOFLAT_FLAGS_NO_PL))
+ {
+ /** @todo fix this mess */
+ }
+ /* check limit. */
+ if (fCheckLimit && Addr > pSReg->u32Limit)
+ return VERR_OUT_OF_SELECTOR_BOUNDS;
+ /* ok */
+ if (ppvGC)
+ *ppvGC = pvFlat;
+ return VINF_SUCCESS;
+
+ case X86_SEL_TYPE_EO_CONF:
+ case X86_SEL_TYPE_EO_CONF_ACC:
+ case X86_SEL_TYPE_ER_CONF:
+ case X86_SEL_TYPE_ER_CONF_ACC:
+ if (!(fFlags & SELMTOFLAT_FLAGS_NO_PL))
+ {
+ /** @todo fix this mess */
+ }
+ /* check limit. */
+ if (fCheckLimit && Addr > pSReg->u32Limit)
+ return VERR_OUT_OF_SELECTOR_BOUNDS;
+ /* ok */
+ if (ppvGC)
+ *ppvGC = pvFlat;
+ return VINF_SUCCESS;
+
+ case X86_SEL_TYPE_RO_DOWN:
+ case X86_SEL_TYPE_RO_DOWN_ACC:
+ case X86_SEL_TYPE_RW_DOWN:
+ case X86_SEL_TYPE_RW_DOWN_ACC:
+ if (!(fFlags & SELMTOFLAT_FLAGS_NO_PL))
+ {
+ /** @todo fix this mess */
+ }
+ /* check limit. */
+ if (fCheckLimit)
+ {
+ if (!pSReg->Attr.n.u1Granularity && Addr > UINT32_C(0xffff))
+ return VERR_OUT_OF_SELECTOR_BOUNDS;
+ if (Addr <= pSReg->u32Limit)
+ return VERR_OUT_OF_SELECTOR_BOUNDS;
+ }
+ /* ok */
+ if (ppvGC)
+ *ppvGC = pvFlat;
+ return VINF_SUCCESS;
+
+ default:
+ return VERR_INVALID_SELECTOR;
+
+ }
+ }
+ return VERR_SELECTOR_NOT_PRESENT;
+}
+
+
+
+/**
+ * Validates and converts a GC selector based code address to a flat
+ * address when in real or v8086 mode.
+ *
+ * @returns VINF_SUCCESS.
+ * @param pVCpu The cross context virtual CPU structure.
+ * @param SelCS Selector part.
+ * @param pSReg The hidden CS register part. Optional.
+ * @param Addr Address part.
+ * @param ppvFlat Where to store the flat address.
+ */
+DECLINLINE(int) selmValidateAndConvertCSAddrRealMode(PVMCPU pVCpu, RTSEL SelCS, PCCPUMSELREGHID pSReg, RTGCPTR Addr,
+ PRTGCPTR ppvFlat)
+{
+ NOREF(pVCpu);
+ uint32_t uFlat = Addr & 0xffff;
+ if (!pSReg || !CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSReg))
+ uFlat += (uint32_t)SelCS << 4;
+ else
+ uFlat += (uint32_t)pSReg->u64Base;
+ *ppvFlat = uFlat;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Validates and converts a GC selector based code address to a flat address
+ * when in protected/long mode using the standard hidden selector registers
+ *
+ * @returns VBox status code.
+ * @param pVCpu The cross context virtual CPU structure.
+ * @param SelCPL Current privilege level. Get this from SS - CS might be
+ * conforming! A full selector can be passed, we'll only
+ * use the RPL part.
+ * @param SelCS Selector part.
+ * @param pSRegCS The full CS selector register.
+ * @param Addr The address (think IP/EIP/RIP).
+ * @param ppvFlat Where to store the flat address upon successful return.
+ */
+DECLINLINE(int) selmValidateAndConvertCSAddrHidden(PVMCPU pVCpu, RTSEL SelCPL, RTSEL SelCS, PCCPUMSELREGHID pSRegCS,
+ RTGCPTR Addr, PRTGCPTR ppvFlat)
+{
+ NOREF(SelCPL); NOREF(SelCS);
+
+ /*
+ * Check if present.
+ */
+ if (pSRegCS->Attr.n.u1Present)
+ {
+ /*
+ * Type check.
+ */
+ if ( pSRegCS->Attr.n.u1DescType == 1
+ && (pSRegCS->Attr.n.u4Type & X86_SEL_TYPE_CODE))
+ {
+ /* 64 bits mode: CS, DS, ES and SS are treated as if each segment base is 0
+ (Intel(r) 64 and IA-32 Architectures Software Developer's Manual: 3.4.2.1). */
+ if ( pSRegCS->Attr.n.u1Long
+ && CPUMIsGuestInLongMode(pVCpu))
+ {
+ *ppvFlat = Addr;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Limit check. Note that the limit in the hidden register is the
+ * final value. The granularity bit was included in its calculation.
+ */
+ uint32_t u32Limit = pSRegCS->u32Limit;
+ if ((uint32_t)Addr <= u32Limit)
+ {
+ *ppvFlat = (uint32_t)Addr + (uint32_t)pSRegCS->u64Base;
+ return VINF_SUCCESS;
+ }
+
+ return VERR_OUT_OF_SELECTOR_BOUNDS;
+ }
+ return VERR_NOT_CODE_SELECTOR;
+ }
+ return VERR_SELECTOR_NOT_PRESENT;
+}
+
+
+/**
+ * Validates and converts a GC selector based code address to a flat address.
+ *
+ * @returns VBox status code.
+ * @param pVCpu The cross context virtual CPU structure.
+ * @param fEFlags Current EFLAGS.
+ * @param SelCPL Current privilege level. Get this from SS - CS might be
+ * conforming! A full selector can be passed, we'll only
+ * use the RPL part.
+ * @param SelCS Selector part.
+ * @param pSRegCS The full CS selector register.
+ * @param Addr The address (think IP/EIP/RIP).
+ * @param ppvFlat Where to store the flat address upon successful return.
+ */
+VMMDECL(int) SELMValidateAndConvertCSAddr(PVMCPU pVCpu, uint32_t fEFlags, RTSEL SelCPL, RTSEL SelCS, PCPUMSELREG pSRegCS,
+ RTGCPTR Addr, PRTGCPTR ppvFlat)
+{
+ if ( (fEFlags & X86_EFL_VM)
+ || CPUMIsGuestInRealMode(pVCpu))
+ return selmValidateAndConvertCSAddrRealMode(pVCpu, SelCS, pSRegCS, Addr, ppvFlat);
+
+ Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSRegCS));
+ Assert(pSRegCS->Sel == SelCS);
+
+ return selmValidateAndConvertCSAddrHidden(pVCpu, SelCPL, SelCS, pSRegCS, Addr, ppvFlat);
+}
+
+
+/**
+ * Gets info about the current TSS.
+ *
+ * @returns VBox status code.
+ * @retval VINF_SUCCESS if we've got a TSS loaded.
+ * @retval VERR_SELM_NO_TSS if we haven't got a TSS (rather unlikely).
+ *
+ * @param pVM The cross context VM structure.
+ * @param pVCpu The cross context virtual CPU structure.
+ * @param pGCPtrTss Where to store the TSS address.
+ * @param pcbTss Where to store the TSS size limit.
+ * @param pfCanHaveIOBitmap Where to store the can-have-I/O-bitmap indicator. (optional)
+ */
+VMMDECL(int) SELMGetTSSInfo(PVM pVM, PVMCPU pVCpu, PRTGCUINTPTR pGCPtrTss, PRTGCUINTPTR pcbTss, bool *pfCanHaveIOBitmap)
+{
+ NOREF(pVM);
+
+ /*
+ * The TR hidden register is always valid.
+ */
+ CPUMSELREGHID trHid;
+ RTSEL tr = CPUMGetGuestTR(pVCpu, &trHid);
+ if (!(tr & X86_SEL_MASK_OFF_RPL))
+ return VERR_SELM_NO_TSS;
+
+ *pGCPtrTss = trHid.u64Base;
+ *pcbTss = trHid.u32Limit + (trHid.u32Limit != UINT32_MAX); /* be careful. */
+ if (pfCanHaveIOBitmap)
+ *pfCanHaveIOBitmap = trHid.Attr.n.u4Type == X86_SEL_TYPE_SYS_386_TSS_AVAIL
+ || trHid.Attr.n.u4Type == X86_SEL_TYPE_SYS_386_TSS_BUSY;
+ return VINF_SUCCESS;
+}
+