/* $Id: SELMAll.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */ /** @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 . * * SPDX-License-Identifier: GPL-3.0-only */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP LOG_GROUP_SELM #include #include #include #include #include #include #include #include "SELMInternal.h" #include #include #include #include #include #include #include /** * 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; }