VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/Support/SUPLibAll.cpp

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.6 KB
RevLine 
[54224]1/* $Id: SUPLibAll.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VirtualBox Support Library - All Contexts Code.
4 */
5
6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[54224]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
[54224]11 *
[96407]12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
[54224]25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
[96407]27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
[54224]29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
[96407]33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
[54224]35 */
36
[57358]37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
[54224]41#include <VBox/sup.h>
[54257]42#ifdef IN_RC
43# include <VBox/vmm/vm.h>
44# include <VBox/vmm/vmm.h>
45#endif
[54224]46#ifdef IN_RING0
47# include <iprt/mp.h>
48#endif
49#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
50# include <iprt/asm-amd64-x86.h>
51#endif
[76389]52#include <iprt/errcore.h>
[87700]53#if defined(IN_RING0) && defined(RT_OS_LINUX)
54# include "SUPDrvInternal.h"
55#endif
[54224]56
57
[87700]58
[54224]59#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
60/**
61 * The slow case for SUPReadTsc where we need to apply deltas.
62 *
63 * Must only be called when deltas are applicable, so please do not call it
64 * directly.
65 *
66 * @returns TSC with delta applied.
[54308]67 * @param pGip Pointer to the GIP.
[54224]68 *
69 * @remarks May be called with interrupts disabled in ring-0! This is why the
70 * ring-0 code doesn't attempt to figure the delta.
71 *
72 * @internal
73 */
[54335]74SUPDECL(uint64_t) SUPReadTscWithDelta(PSUPGLOBALINFOPAGE pGip)
[54224]75{
[54257]76 uint64_t uTsc;
77 uint16_t iGipCpu;
78 AssertCompile(RT_IS_POWER_OF_TWO(RTCPUSET_MAX_CPUS));
79 AssertCompile(RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx) >= RTCPUSET_MAX_CPUS);
[54308]80 Assert(pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_PRACTICALLY_ZERO);
[54224]81
82 /*
[54257]83 * Read the TSC and get the corresponding aCPUs index.
[54224]84 */
[54308]85#ifdef IN_RING3
[54257]86 if (pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS)
[54224]87 {
[54257]88 /* RDTSCP gives us all we need, no loops/cli. */
89 uint32_t iCpuSet;
90 uTsc = ASMReadTscWithAux(&iCpuSet);
91 iCpuSet &= RTCPUSET_MAX_CPUS - 1;
92 iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
93 }
94 else if (pGip->fGetGipCpu & SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS)
95 {
96 /* Storing the IDTR is normally very quick, but we need to loop. */
97 uint32_t cTries = 0;
98 for (;;)
[54224]99 {
[54257]100 uint16_t cbLim = ASMGetIdtrLimit();
101 uTsc = ASMReadTSC();
102 if (RT_LIKELY(ASMGetIdtrLimit() == cbLim))
103 {
104 uint16_t iCpuSet = cbLim - 256 * (ARCH_BITS == 64 ? 16 : 8);
105 iCpuSet &= RTCPUSET_MAX_CPUS - 1;
106 iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
107 break;
108 }
109 if (cTries >= 16)
110 {
111 iGipCpu = UINT16_MAX;
112 break;
113 }
114 cTries++;
115 }
116 }
[81071]117 else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_0B)
118 {
119 /* Get APIC ID / 0x1b via the slow CPUID instruction, requires looping. */
120 uint32_t cTries = 0;
121 for (;;)
122 {
123 uint32_t idApic = ASMGetApicIdExt0B();
124 uTsc = ASMReadTSC();
125 if (RT_LIKELY(ASMGetApicIdExt0B() == idApic))
126 {
127 iGipCpu = pGip->aiCpuFromApicId[idApic];
128 break;
129 }
130 if (cTries >= 16)
131 {
132 iGipCpu = UINT16_MAX;
133 break;
134 }
135 cTries++;
136 }
137 }
138 else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_8000001E)
139 {
140 /* Get APIC ID / 0x8000001e via the slow CPUID instruction, requires looping. */
141 uint32_t cTries = 0;
142 for (;;)
143 {
144 uint32_t idApic = ASMGetApicIdExt8000001E();
145 uTsc = ASMReadTSC();
146 if (RT_LIKELY(ASMGetApicIdExt8000001E() == idApic))
147 {
148 iGipCpu = pGip->aiCpuFromApicId[idApic];
149 break;
150 }
151 if (cTries >= 16)
152 {
153 iGipCpu = UINT16_MAX;
154 break;
155 }
156 cTries++;
157 }
158 }
[54257]159 else
160 {
[54308]161 /* Get APIC ID via the slow CPUID instruction, requires looping. */
[54257]162 uint32_t cTries = 0;
163 for (;;)
164 {
165 uint8_t idApic = ASMGetApicId();
166 uTsc = ASMReadTSC();
167 if (RT_LIKELY(ASMGetApicId() == idApic))
168 {
169 iGipCpu = pGip->aiCpuFromApicId[idApic];
170 break;
171 }
172 if (cTries >= 16)
173 {
174 iGipCpu = UINT16_MAX;
175 break;
176 }
177 cTries++;
178 }
[54308]179 }
180#elif defined(IN_RING0)
181 /* Ring-0: Use use RTMpCpuId(), no loops. */
182 RTCCUINTREG uFlags = ASMIntDisableFlags();
183 int iCpuSet = RTMpCpuIdToSetIndex(RTMpCpuId());
184 if (RT_LIKELY((unsigned)iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
185 iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
186 else
187 iGipCpu = UINT16_MAX;
188 uTsc = ASMReadTSC();
189 ASMSetFlags(uFlags);
[54224]190
[54257]191# elif defined(IN_RC)
[54308]192 /* Raw-mode context: We can get the host CPU set index via VMCPU, no loops. */
193 RTCCUINTREG uFlags = ASMIntDisableFlags(); /* Are already disable, but play safe. */
194 uint32_t iCpuSet = VMMGetCpu(&g_VM)->iHostCpuSet;
195 if (RT_LIKELY(iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
196 iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
197 else
198 iGipCpu = UINT16_MAX;
199 uTsc = ASMReadTSC();
200 ASMSetFlags(uFlags);
201#else
202# error "IN_RING3, IN_RC or IN_RING0 must be defined!"
203#endif
[54257]204
[54224]205 /*
[54257]206 * If the delta is valid, apply it.
[54224]207 */
[54257]208 if (RT_LIKELY(iGipCpu < pGip->cCpus))
209 {
210 int64_t iTscDelta = pGip->aCPUs[iGipCpu].i64TSCDelta;
211 if (RT_LIKELY(iTscDelta != INT64_MAX))
[55246]212 return uTsc - iTscDelta;
[54224]213
[54257]214# ifdef IN_RING3
215 /*
216 * The delta needs calculating, call supdrv to get the TSC.
217 */
218 int rc = SUPR3ReadTsc(&uTsc, NULL);
219 if (RT_SUCCESS(rc))
220 return uTsc;
221 AssertMsgFailed(("SUPR3ReadTsc -> %Rrc\n", rc));
222 uTsc = ASMReadTSC();
223# endif /* IN_RING3 */
224 }
225
[54224]226 /*
[54257]227 * This shouldn't happen, especially not in ring-3 and raw-mode context.
228 * But if it does, return something that's half useful.
[54224]229 */
[54257]230 AssertMsgFailed(("iGipCpu=%d (%#x) cCpus=%d fGetGipCpu=%#x\n", iGipCpu, iGipCpu, pGip->cCpus, pGip->fGetGipCpu));
[54224]231 return uTsc;
232}
[87702]233# ifdef SUPR0_EXPORT_SYMBOL
[87700]234SUPR0_EXPORT_SYMBOL(SUPReadTscWithDelta);
235# endif
[87235]236#endif /* RT_ARCH_AMD64 || RT_ARCH_X86 */
[54224]237
[54308]238
239/**
240 * Internal worker for getting the GIP CPU array index for the calling CPU.
241 *
242 * @returns Index into SUPGLOBALINFOPAGE::aCPUs or UINT16_MAX.
243 * @param pGip The GIP.
244 */
245DECLINLINE(uint16_t) supGetGipCpuIndex(PSUPGLOBALINFOPAGE pGip)
246{
247 uint16_t iGipCpu;
248#ifdef IN_RING3
[87235]249# if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
[54308]250 if (pGip->fGetGipCpu & SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS)
251 {
252 /* Storing the IDTR is normally very fast. */
253 uint16_t cbLim = ASMGetIdtrLimit();
254 uint16_t iCpuSet = cbLim - 256 * (ARCH_BITS == 64 ? 16 : 8);
255 iCpuSet &= RTCPUSET_MAX_CPUS - 1;
256 iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
257 }
258 else if (pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS)
259 {
260 /* RDTSCP gives us what need need and more. */
261 uint32_t iCpuSet;
262 ASMReadTscWithAux(&iCpuSet);
263 iCpuSet &= RTCPUSET_MAX_CPUS - 1;
264 iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
265 }
[81071]266 else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_0B)
267 {
268 /* Get APIC ID via the slow CPUID/0000000B instruction. */
269 uint32_t idApic = ASMGetApicIdExt0B();
270 iGipCpu = pGip->aiCpuFromApicId[idApic];
271 }
272 else if (pGip->fGetGipCpu & SUPGIPGETCPU_APIC_ID_EXT_8000001E)
273 {
274 /* Get APIC ID via the slow CPUID/8000001E instruction. */
275 uint32_t idApic = ASMGetApicIdExt8000001E();
276 iGipCpu = pGip->aiCpuFromApicId[idApic];
277 }
[54308]278 else
279 {
280 /* Get APIC ID via the slow CPUID instruction. */
281 uint8_t idApic = ASMGetApicId();
282 iGipCpu = pGip->aiCpuFromApicId[idApic];
283 }
[87235]284
285# else
286 int iCpuSet = RTMpCpuIdToSetIndex(RTMpCpuId());
287 if (RT_LIKELY((unsigned)iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
288 iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
289 else
290 iGipCpu = UINT16_MAX;
291# endif
292
[54308]293#elif defined(IN_RING0)
294 /* Ring-0: Use use RTMpCpuId() (disables cli to avoid host OS assertions about unsafe CPU number usage). */
295 RTCCUINTREG uFlags = ASMIntDisableFlags();
296 int iCpuSet = RTMpCpuIdToSetIndex(RTMpCpuId());
297 if (RT_LIKELY((unsigned)iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
298 iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
299 else
300 iGipCpu = UINT16_MAX;
301 ASMSetFlags(uFlags);
302
303# elif defined(IN_RC)
304 /* Raw-mode context: We can get the host CPU set index via VMCPU. */
305 uint32_t iCpuSet = VMMGetCpu(&g_VM)->iHostCpuSet;
306 if (RT_LIKELY(iCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
307 iGipCpu = pGip->aiCpuFromCpuSetIdx[iCpuSet];
308 else
309 iGipCpu = UINT16_MAX;
[87235]310
[54308]311#else
312# error "IN_RING3, IN_RC or IN_RING0 must be defined!"
313#endif
314 return iGipCpu;
315}
316
317
318/**
319 * Slow path in SUPGetTscDelta, don't call directly.
320 *
321 * @returns See SUPGetTscDelta.
322 * @param pGip The GIP.
323 * @internal
324 */
[85213]325SUPDECL(int64_t) SUPGetTscDeltaSlow(PSUPGLOBALINFOPAGE pGip)
[54308]326{
327 uint16_t iGipCpu = supGetGipCpuIndex(pGip);
328 if (RT_LIKELY(iGipCpu < pGip->cCpus))
329 {
330 int64_t iTscDelta = pGip->aCPUs[iGipCpu].i64TSCDelta;
331 if (iTscDelta != INT64_MAX)
332 return iTscDelta;
333 }
334 AssertFailed();
335 return 0;
336}
337
338
339/**
[92613]340 * SLow path in SUPGetGipCpuPtr, don't call directly.
341 *
342 * @returns Pointer to the CPU entry for the caller, NULL on failure.
343 * @param pGip The GIP.
344 */
345SUPDECL(PSUPGIPCPU) SUPGetGipCpuPtrForAsyncMode(PSUPGLOBALINFOPAGE pGip)
346{
347 uint16_t iGipCpu = supGetGipCpuIndex(pGip);
348 if (RT_LIKELY(iGipCpu < pGip->cCpus))
349 return &pGip->aCPUs[iGipCpu];
350 AssertFailed();
351 return NULL;
352}
353
354
355/**
[54308]356 * Slow path in SUPGetCpuHzFromGip, don't call directly.
357 *
358 * @returns See SUPGetCpuHzFromGip.
359 * @param pGip The GIP.
360 * @internal
361 */
362SUPDECL(uint64_t) SUPGetCpuHzFromGipForAsyncMode(PSUPGLOBALINFOPAGE pGip)
363{
364 uint16_t iGipCpu = supGetGipCpuIndex(pGip);
365 if (RT_LIKELY(iGipCpu < pGip->cCpus))
366 return pGip->aCPUs[iGipCpu].u64CpuHz;
367 AssertFailed();
368 return pGip->u64CpuHz;
369}
370
371
[87235]372
[57090]373/**
374 * Worker for SUPIsTscFreqCompatible().
375 *
376 * @returns true if it's compatible, false otherwise.
377 * @param uBaseCpuHz The reference CPU frequency of the system.
378 * @param uCpuHz The CPU frequency to compare with the base.
379 * @param fRelax Whether to use a more relaxed threshold (like
380 * for when running in a virtualized environment).
381 *
382 * @remarks Don't use directly, use SUPIsTscFreqCompatible() instead. This is
383 * to be used by tstGIP-2 or the like.
384 */
385SUPDECL(bool) SUPIsTscFreqCompatibleEx(uint64_t uBaseCpuHz, uint64_t uCpuHz, bool fRelax)
386{
387 if (uBaseCpuHz != uCpuHz)
388 {
389 /* Arbitrary tolerance threshold, tweak later if required, perhaps
390 more tolerance on lower frequencies and less tolerance on higher. */
[57218]391 uint16_t uFact = !fRelax ? 666 /* 0.15% */ : 125 /* 0.8% */;
392 uint64_t uThr = uBaseCpuHz / uFact;
393 uint64_t uLo = uBaseCpuHz - uThr;
394 uint64_t uHi = uBaseCpuHz + uThr;
[57090]395 if ( uCpuHz < uLo
396 || uCpuHz > uHi)
397 return false;
398 }
399 return true;
400}
401
402
403/**
404 * Checks if the provided TSC frequency is close enough to the computed TSC
405 * frequency of the host.
406 *
407 * @returns true if it's compatible, false otherwise.
408 * @param uCpuHz The TSC frequency to check.
409 * @param puGipCpuHz Where to store the GIP TSC frequency used
410 * during the compatibility test - optional.
411 * @param fRelax Whether to use a more relaxed threshold (like
412 * for when running in a virtualized environment).
413 */
414SUPDECL(bool) SUPIsTscFreqCompatible(uint64_t uCpuHz, uint64_t *puGipCpuHz, bool fRelax)
415{
416 PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
[57097]417 bool fCompat = false;
418 uint64_t uGipCpuHz = 0;
[57090]419 if ( pGip
420 && pGip->u32Mode != SUPGIPMODE_ASYNC_TSC)
421 {
[57097]422 uGipCpuHz = pGip->u64CpuHz;
423 fCompat = SUPIsTscFreqCompatibleEx(uGipCpuHz, uCpuHz, fRelax);
[57090]424 }
[57097]425 if (puGipCpuHz)
426 *puGipCpuHz = uGipCpuHz;
427 return fCompat;
[57090]428}
429
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use