VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 2 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
Line 
1/* $Id: SUPLibAll.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VirtualBox Support Library - All Contexts Code.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
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 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
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
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.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <VBox/sup.h>
42#ifdef IN_RC
43# include <VBox/vmm/vm.h>
44# include <VBox/vmm/vmm.h>
45#endif
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
52#include <iprt/errcore.h>
53#if defined(IN_RING0) && defined(RT_OS_LINUX)
54# include "SUPDrvInternal.h"
55#endif
56
57
58
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.
67 * @param pGip Pointer to the GIP.
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 */
74SUPDECL(uint64_t) SUPReadTscWithDelta(PSUPGLOBALINFOPAGE pGip)
75{
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);
80 Assert(pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_PRACTICALLY_ZERO);
81
82 /*
83 * Read the TSC and get the corresponding aCPUs index.
84 */
85#ifdef IN_RING3
86 if (pGip->fGetGipCpu & SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS)
87 {
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 (;;)
99 {
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 }
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 }
159 else
160 {
161 /* Get APIC ID via the slow CPUID instruction, requires looping. */
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 }
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);
190
191# elif defined(IN_RC)
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
204
205 /*
206 * If the delta is valid, apply it.
207 */
208 if (RT_LIKELY(iGipCpu < pGip->cCpus))
209 {
210 int64_t iTscDelta = pGip->aCPUs[iGipCpu].i64TSCDelta;
211 if (RT_LIKELY(iTscDelta != INT64_MAX))
212 return uTsc - iTscDelta;
213
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
226 /*
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.
229 */
230 AssertMsgFailed(("iGipCpu=%d (%#x) cCpus=%d fGetGipCpu=%#x\n", iGipCpu, iGipCpu, pGip->cCpus, pGip->fGetGipCpu));
231 return uTsc;
232}
233# ifdef SUPR0_EXPORT_SYMBOL
234SUPR0_EXPORT_SYMBOL(SUPReadTscWithDelta);
235# endif
236#endif /* RT_ARCH_AMD64 || RT_ARCH_X86 */
237
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
249# if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
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 }
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 }
278 else
279 {
280 /* Get APIC ID via the slow CPUID instruction. */
281 uint8_t idApic = ASMGetApicId();
282 iGipCpu = pGip->aiCpuFromApicId[idApic];
283 }
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
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;
310
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 */
325SUPDECL(int64_t) SUPGetTscDeltaSlow(PSUPGLOBALINFOPAGE pGip)
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/**
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/**
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
372
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. */
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;
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;
417 bool fCompat = false;
418 uint64_t uGipCpuHz = 0;
419 if ( pGip
420 && pGip->u32Mode != SUPGIPMODE_ASYNC_TSC)
421 {
422 uGipCpuHz = pGip->u64CpuHz;
423 fCompat = SUPIsTscFreqCompatibleEx(uGipCpuHz, uCpuHz, fRelax);
424 }
425 if (puGipCpuHz)
426 *puGipCpuHz = uGipCpuHz;
427 return fCompat;
428}
429
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette