From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/HostDrivers/Support/SUPLibAll.cpp | 429 +++++++++++++++++++++++++++++ 1 file changed, 429 insertions(+) create mode 100644 src/VBox/HostDrivers/Support/SUPLibAll.cpp (limited to 'src/VBox/HostDrivers/Support/SUPLibAll.cpp') diff --git a/src/VBox/HostDrivers/Support/SUPLibAll.cpp b/src/VBox/HostDrivers/Support/SUPLibAll.cpp new file mode 100644 index 00000000..939c4188 --- /dev/null +++ b/src/VBox/HostDrivers/Support/SUPLibAll.cpp @@ -0,0 +1,429 @@ +/* $Id: SUPLibAll.cpp $ */ +/** @file + * VirtualBox Support Library - All Contexts Code. + */ + +/* + * 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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#ifdef IN_RC +# include +# include +#endif +#ifdef IN_RING0 +# include +#endif +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +# include +#endif +#include +#if defined(IN_RING0) && defined(RT_OS_LINUX) +# include "SUPDrvInternal.h" +#endif + + + +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +/** + * The slow case for SUPReadTsc where we need to apply deltas. + * + * Must only be called when deltas are applicable, so please do not call it + * directly. + * + * @returns TSC with delta applied. + * @param pGip Pointer to the GIP. + * + * @remarks May be called with interrupts disabled in ring-0! This is why the + * ring-0 code doesn't attempt to figure the delta. + * + * @internal + */ +SUPDECL(uint64_t) SUPReadTscWithDelta(PSUPGLOBALINFOPAGE pGip) +{ + uint64_t uTsc; + uint16_t iGipCpu; + AssertCompile(RT_IS_POWER_OF_TWO(RTCPUSET_MAX_CPUS)); + AssertCompile(RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx) >= RTCPUSET_MAX_CPUS); + Assert(pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_PRACTICALLY_ZERO); + + /* + * Read the TSC and get the corresponding aCPUs index. + */ +#ifdef IN_RING3 + if (pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS) + { + /* RDTSCP gives us all we need, no loops/cli. */ + uint32_t iCpuSet; + uTsc = ASMReadTscWithAux(&iCpuSet); + iCpuSet &= RTCPUSET_MAX_CPUS - 1; + iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet]; + } + else if (pGip->fGetGipCpu & SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS) + { + /* Storing the IDTR is normally very quick, but we need to loop. */ + uint32_t cTries = 0; + for (;;) + { + uint16_t cbLim = ASMGetIdtrLimit(); + uTsc = ASMReadTSC(); + if (RT_LIKELY(ASMGetIdtrLimit() == cbLim)) + { + uint16_t iCpuSet = cbLim - 256 * (ARCH_BITS == 64 ? 16 : 8); + iCpuSet &= RTCPUSET_MAX_CPUS - 1; + iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet]; + break; + } + if (cTries >= 16) + { + iGipCpu = UINT16_MAX; + break; + } + cTries++; + } + } + else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_0B) + { + /* Get APIC ID / 0x1b via the slow CPUID instruction, requires looping. */ + uint32_t cTries = 0; + for (;;) + { + uint32_t idApic = ASMGetApicIdExt0B(); + uTsc = ASMReadTSC(); + if (RT_LIKELY(ASMGetApicIdExt0B() == idApic)) + { + iGipCpu = pGip->aiCpuFromApicId[idApic]; + break; + } + if (cTries >= 16) + { + iGipCpu = UINT16_MAX; + break; + } + cTries++; + } + } + else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_8000001E) + { + /* Get APIC ID / 0x8000001e via the slow CPUID instruction, requires looping. */ + uint32_t cTries = 0; + for (;;) + { + uint32_t idApic = ASMGetApicIdExt8000001E(); + uTsc = ASMReadTSC(); + if (RT_LIKELY(ASMGetApicIdExt8000001E() == idApic)) + { + iGipCpu = pGip->aiCpuFromApicId[idApic]; + break; + } + if (cTries >= 16) + { + iGipCpu = UINT16_MAX; + break; + } + cTries++; + } + } + else + { + /* Get APIC ID via the slow CPUID instruction, requires looping. */ + uint32_t cTries = 0; + for (;;) + { + uint8_t idApic = ASMGetApicId(); + uTsc = ASMReadTSC(); + if (RT_LIKELY(ASMGetApicId() == idApic)) + { + iGipCpu = pGip->aiCpuFromApicId[idApic]; + break; + } + if (cTries >= 16) + { + iGipCpu = UINT16_MAX; + break; + } + cTries++; + } + } +#elif defined(IN_RING0) + /* Ring-0: Use use RTMpCpuId(), no loops. */ + RTCCUINTREG uFlags = ASMIntDisableFlags(); + int iCpuSet = RTMpCpuIdToSetIndex(RTMpCpuId()); + if (RT_LIKELY((unsigned)iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx))) + iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet]; + else + iGipCpu = UINT16_MAX; + uTsc = ASMReadTSC(); + ASMSetFlags(uFlags); + +# elif defined(IN_RC) + /* Raw-mode context: We can get the host CPU set index via VMCPU, no loops. */ + RTCCUINTREG uFlags = ASMIntDisableFlags(); /* Are already disable, but play safe. */ + uint32_t iCpuSet = VMMGetCpu(&g_VM)->iHostCpuSet; + if (RT_LIKELY(iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx))) + iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet]; + else + iGipCpu = UINT16_MAX; + uTsc = ASMReadTSC(); + ASMSetFlags(uFlags); +#else +# error "IN_RING3, IN_RC or IN_RING0 must be defined!" +#endif + + /* + * If the delta is valid, apply it. + */ + if (RT_LIKELY(iGipCpu < pGip->cCpus)) + { + int64_t iTscDelta = pGip->aCPUs[iGipCpu].i64TSCDelta; + if (RT_LIKELY(iTscDelta != INT64_MAX)) + return uTsc - iTscDelta; + +# ifdef IN_RING3 + /* + * The delta needs calculating, call supdrv to get the TSC. + */ + int rc = SUPR3ReadTsc(&uTsc, NULL); + if (RT_SUCCESS(rc)) + return uTsc; + AssertMsgFailed(("SUPR3ReadTsc -> %Rrc\n", rc)); + uTsc = ASMReadTSC(); +# endif /* IN_RING3 */ + } + + /* + * This shouldn't happen, especially not in ring-3 and raw-mode context. + * But if it does, return something that's half useful. + */ + AssertMsgFailed(("iGipCpu=%d (%#x) cCpus=%d fGetGipCpu=%#x\n", iGipCpu, iGipCpu, pGip->cCpus, pGip->fGetGipCpu)); + return uTsc; +} +# ifdef SUPR0_EXPORT_SYMBOL +SUPR0_EXPORT_SYMBOL(SUPReadTscWithDelta); +# endif +#endif /* RT_ARCH_AMD64 || RT_ARCH_X86 */ + + +/** + * Internal worker for getting the GIP CPU array index for the calling CPU. + * + * @returns Index into SUPGLOBALINFOPAGE::aCPUs or UINT16_MAX. + * @param pGip The GIP. + */ +DECLINLINE(uint16_t) supGetGipCpuIndex(PSUPGLOBALINFOPAGE pGip) +{ + uint16_t iGipCpu; +#ifdef IN_RING3 +# if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) + if (pGip->fGetGipCpu & SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS) + { + /* Storing the IDTR is normally very fast. */ + uint16_t cbLim = ASMGetIdtrLimit(); + uint16_t iCpuSet = cbLim - 256 * (ARCH_BITS == 64 ? 16 : 8); + iCpuSet &= RTCPUSET_MAX_CPUS - 1; + iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet]; + } + else if (pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS) + { + /* RDTSCP gives us what need need and more. */ + uint32_t iCpuSet; + ASMReadTscWithAux(&iCpuSet); + iCpuSet &= RTCPUSET_MAX_CPUS - 1; + iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet]; + } + else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_0B) + { + /* Get APIC ID via the slow CPUID/0000000B instruction. */ + uint32_t idApic = ASMGetApicIdExt0B(); + iGipCpu = pGip->aiCpuFromApicId[idApic]; + } + else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_8000001E) + { + /* Get APIC ID via the slow CPUID/8000001E instruction. */ + uint32_t idApic = ASMGetApicIdExt8000001E(); + iGipCpu = pGip->aiCpuFromApicId[idApic]; + } + else + { + /* Get APIC ID via the slow CPUID instruction. */ + uint8_t idApic = ASMGetApicId(); + iGipCpu = pGip->aiCpuFromApicId[idApic]; + } + +# else + int iCpuSet = RTMpCpuIdToSetIndex(RTMpCpuId()); + if (RT_LIKELY((unsigned)iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx))) + iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet]; + else + iGipCpu = UINT16_MAX; +# endif + +#elif defined(IN_RING0) + /* Ring-0: Use use RTMpCpuId() (disables cli to avoid host OS assertions about unsafe CPU number usage). */ + RTCCUINTREG uFlags = ASMIntDisableFlags(); + int iCpuSet = RTMpCpuIdToSetIndex(RTMpCpuId()); + if (RT_LIKELY((unsigned)iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx))) + iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet]; + else + iGipCpu = UINT16_MAX; + ASMSetFlags(uFlags); + +# elif defined(IN_RC) + /* Raw-mode context: We can get the host CPU set index via VMCPU. */ + uint32_t iCpuSet = VMMGetCpu(&g_VM)->iHostCpuSet; + if (RT_LIKELY(iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx))) + iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet]; + else + iGipCpu = UINT16_MAX; + +#else +# error "IN_RING3, IN_RC or IN_RING0 must be defined!" +#endif + return iGipCpu; +} + + +/** + * Slow path in SUPGetTscDelta, don't call directly. + * + * @returns See SUPGetTscDelta. + * @param pGip The GIP. + * @internal + */ +SUPDECL(int64_t) SUPGetTscDeltaSlow(PSUPGLOBALINFOPAGE pGip) +{ + uint16_t iGipCpu = supGetGipCpuIndex(pGip); + if (RT_LIKELY(iGipCpu < pGip->cCpus)) + { + int64_t iTscDelta = pGip->aCPUs[iGipCpu].i64TSCDelta; + if (iTscDelta != INT64_MAX) + return iTscDelta; + } + AssertFailed(); + return 0; +} + + +/** + * SLow path in SUPGetGipCpuPtr, don't call directly. + * + * @returns Pointer to the CPU entry for the caller, NULL on failure. + * @param pGip The GIP. + */ +SUPDECL(PSUPGIPCPU) SUPGetGipCpuPtrForAsyncMode(PSUPGLOBALINFOPAGE pGip) +{ + uint16_t iGipCpu = supGetGipCpuIndex(pGip); + if (RT_LIKELY(iGipCpu < pGip->cCpus)) + return &pGip->aCPUs[iGipCpu]; + AssertFailed(); + return NULL; +} + + +/** + * Slow path in SUPGetCpuHzFromGip, don't call directly. + * + * @returns See SUPGetCpuHzFromGip. + * @param pGip The GIP. + * @internal + */ +SUPDECL(uint64_t) SUPGetCpuHzFromGipForAsyncMode(PSUPGLOBALINFOPAGE pGip) +{ + uint16_t iGipCpu = supGetGipCpuIndex(pGip); + if (RT_LIKELY(iGipCpu < pGip->cCpus)) + return pGip->aCPUs[iGipCpu].u64CpuHz; + AssertFailed(); + return pGip->u64CpuHz; +} + + + +/** + * Worker for SUPIsTscFreqCompatible(). + * + * @returns true if it's compatible, false otherwise. + * @param uBaseCpuHz The reference CPU frequency of the system. + * @param uCpuHz The CPU frequency to compare with the base. + * @param fRelax Whether to use a more relaxed threshold (like + * for when running in a virtualized environment). + * + * @remarks Don't use directly, use SUPIsTscFreqCompatible() instead. This is + * to be used by tstGIP-2 or the like. + */ +SUPDECL(bool) SUPIsTscFreqCompatibleEx(uint64_t uBaseCpuHz, uint64_t uCpuHz, bool fRelax) +{ + if (uBaseCpuHz != uCpuHz) + { + /* Arbitrary tolerance threshold, tweak later if required, perhaps + more tolerance on lower frequencies and less tolerance on higher. */ + uint16_t uFact = !fRelax ? 666 /* 0.15% */ : 125 /* 0.8% */; + uint64_t uThr = uBaseCpuHz / uFact; + uint64_t uLo = uBaseCpuHz - uThr; + uint64_t uHi = uBaseCpuHz + uThr; + if ( uCpuHz < uLo + || uCpuHz > uHi) + return false; + } + return true; +} + + +/** + * Checks if the provided TSC frequency is close enough to the computed TSC + * frequency of the host. + * + * @returns true if it's compatible, false otherwise. + * @param uCpuHz The TSC frequency to check. + * @param puGipCpuHz Where to store the GIP TSC frequency used + * during the compatibility test - optional. + * @param fRelax Whether to use a more relaxed threshold (like + * for when running in a virtualized environment). + */ +SUPDECL(bool) SUPIsTscFreqCompatible(uint64_t uCpuHz, uint64_t *puGipCpuHz, bool fRelax) +{ + PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage; + bool fCompat = false; + uint64_t uGipCpuHz = 0; + if ( pGip + && pGip->u32Mode != SUPGIPMODE_ASYNC_TSC) + { + uGipCpuHz = pGip->u64CpuHz; + fCompat = SUPIsTscFreqCompatibleEx(uGipCpuHz, uCpuHz, fRelax); + } + if (puGipCpuHz) + *puGipCpuHz = uGipCpuHz; + return fCompat; +} + -- cgit v1.2.3