SUPDrvGip.cpp revision b9efc54979b7ee83352754810d59d75543b474a1
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * VBoxDrv - The VirtualBox Support Driver - Common code for GIP.
5654aa8329bbe2838fa5733f28c1a0461c9e6453vboxsync * Copyright (C) 2006-2015 Oracle Corporation
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * available from http://www.virtualbox.org. This file is free software;
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * you can redistribute it and/or modify it under the terms of the GNU
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * General Public License (GPL) as published by the Free Software
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * The contents of this file may alternatively be used under the terms
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * of the Common Development and Distribution License Version 1.0
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync * VirtualBox OSE distribution, in which case the provisions of the
f84cd77241a1c4b9106a92280611c659243e10d1vboxsync * CDDL are applicable instead of those of the GPL.
43747b1f0bc8302a238fb35e55857a5e9aa1933dvboxsync * You may elect to license modified versions of this file under the
43747b1f0bc8302a238fb35e55857a5e9aa1933dvboxsync * terms and conditions of either the GPL or the CDDL or both.
43747b1f0bc8302a238fb35e55857a5e9aa1933dvboxsync/*******************************************************************************
0c437bb10c61b229407a7517efde04dfe3b1e4a1vboxsync* Header Files *
3609dfc9f2733f4dc836c6a6bb3745398f280fcevboxsync*******************************************************************************/
289060a0c3cb1d509f2cb01fca060796212376f6vboxsync#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
436b5c616e019c5e62053657c52d3ab5562ecbbfvboxsync/*******************************************************************************
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync* Defined Constants And Macros *
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync*******************************************************************************/
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync/** The frequency by which we recalculate the u32UpdateHz and
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync * u32UpdateIntervalNS GIP members. The value must be a power of 2.
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync * Warning: Bumping this too high might overflow u32UpdateIntervalNS.
436b5c616e019c5e62053657c52d3ab5562ecbbfvboxsync/** A reserved TSC value used for synchronization as well as measurement of
683371bbf37760161d1b8454ce978acf89bbb04fvboxsync * TSC deltas. */
436b5c616e019c5e62053657c52d3ab5562ecbbfvboxsync/** The number of TSC delta measurement loops in total (includes primer and
436b5c616e019c5e62053657c52d3ab5562ecbbfvboxsync * read-time loops). */
436b5c616e019c5e62053657c52d3ab5562ecbbfvboxsync/** The number of cache primer loops. */
43dff6077acb4176145b18bdb862eb73620182d2vboxsync/** The number of loops until we keep computing the minumum read time. */
436b5c616e019c5e62053657c52d3ab5562ecbbfvboxsync/** @name Master / worker synchronization values.
436b5c616e019c5e62053657c52d3ab5562ecbbfvboxsync/** Stop measurement of TSC delta. */
b40179b44fea65b72b2f226f62af1ed7bd3c48fcvboxsync/** Start measurement of TSC delta. */
43dff6077acb4176145b18bdb862eb73620182d2vboxsync/** Worker thread is ready for reading the TSC. */
436b5c616e019c5e62053657c52d3ab5562ecbbfvboxsync/** Worker thread is done updating TSC delta info. */
0d4bc23ca3867d6dbedd76d5b1e3725c766adb75vboxsync/** When IPRT is isn't concurrent safe: Master is ready and will wait for worker
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync * with a timeout. */
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync#define GIP_TSC_DELTA_SYNC_PRESTART_MASTER UINT32_C(4)
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync/** When IPRT is isn't concurrent safe: Worker is ready after waiting for
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync * master with a timeout. */
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync/** The TSC-refinement interval in seconds. */
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync/** The TSC-delta threshold for the SUPGIPUSETSCDELTA_PRACTICALLY_ZERO rating */
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync/** The TSC-delta threshold for the SUPGIPUSETSCDELTA_ROUGHLY_ZERO rating */
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync/** The TSC delta value for the initial GIP master - 0 in regular builds.
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync * To test the delta code this can be set to a non-zero value. */
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync# define GIP_TSC_DELTA_INITIAL_MASTER_VALUE INT64_C(170139095182512) /* 0x00009abd9854acb0 */
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync# define GIP_TSC_DELTA_INITIAL_MASTER_VALUE INT64_C(0)
ff78b877ed7acd25e2d384570a938441455d6a95vboxsyncAssertCompile(GIP_TSC_DELTA_PRIMER_LOOPS < GIP_TSC_DELTA_READ_TIME_LOOPS);
5654aa8329bbe2838fa5733f28c1a0461c9e6453vboxsyncAssertCompile(GIP_TSC_DELTA_PRIMER_LOOPS + GIP_TSC_DELTA_READ_TIME_LOOPS < GIP_TSC_DELTA_LOOPS);
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync/** @def VBOX_SVN_REV
5654aa8329bbe2838fa5733f28c1a0461c9e6453vboxsync * The makefile should define this if it can. */
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync#if 0 /* Don't start the GIP timers. Useful when debugging the IPRT timer code. */
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync/*******************************************************************************
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync* Internal Functions *
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync*******************************************************************************/
ff78b877ed7acd25e2d384570a938441455d6a95vboxsyncstatic DECLCALLBACK(void) supdrvGipSyncAndInvariantTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
b40179b44fea65b72b2f226f62af1ed7bd3c48fcvboxsyncstatic DECLCALLBACK(void) supdrvGipAsyncTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
ff78b877ed7acd25e2d384570a938441455d6a95vboxsyncstatic void supdrvGipInitCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pCpu, uint64_t u64NanoTS, uint64_t uCpuHz);
ff78b877ed7acd25e2d384570a938441455d6a95vboxsyncstatic int supdrvTscDeltaThreadInit(PSUPDRVDEVEXT pDevExt);
ff78b877ed7acd25e2d384570a938441455d6a95vboxsyncstatic void supdrvTscDeltaTerm(PSUPDRVDEVEXT pDevExt);
ff78b877ed7acd25e2d384570a938441455d6a95vboxsyncstatic void supdrvTscDeltaThreadStartMeasurement(PSUPDRVDEVEXT pDevExt);
ff78b877ed7acd25e2d384570a938441455d6a95vboxsyncstatic int supdrvMeasureInitialTscDeltas(PSUPDRVDEVEXT pDevExt);
ff78b877ed7acd25e2d384570a938441455d6a95vboxsyncstatic int supdrvMeasureTscDeltaOne(PSUPDRVDEVEXT pDevExt, uint32_t idxWorker);
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync/*******************************************************************************
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync* Global Variables *
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync*******************************************************************************/
ff78b877ed7acd25e2d384570a938441455d6a95vboxsyncDECLEXPORT(PSUPGLOBALINFOPAGE) g_pSUPGlobalInfoPage = NULL;
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync * Misc Common GIP Code
ff78b877ed7acd25e2d384570a938441455d6a95vboxsync * Misc Common GIP Code
436b5c616e019c5e62053657c52d3ab5562ecbbfvboxsync * Misc Common GIP Code
30868e719f5a45ec4689ecb2616767cb1fd02c28vboxsync * Finds the GIP CPU index corresponding to @a idCpu.
30868e719f5a45ec4689ecb2616767cb1fd02c28vboxsync * @returns GIP CPU array index, UINT32_MAX if not found.
30868e719f5a45ec4689ecb2616767cb1fd02c28vboxsync * @param pGip The GIP.
30868e719f5a45ec4689ecb2616767cb1fd02c28vboxsync * @param idCpu The CPU ID.
30868e719f5a45ec4689ecb2616767cb1fd02c28vboxsyncstatic uint32_t supdrvGipFindCpuIndexForCpuId(PSUPGLOBALINFOPAGE pGip, RTCPUID idCpu)
30868e719f5a45ec4689ecb2616767cb1fd02c28vboxsync * GIP Mapping and Unmapping Related Code.
30868e719f5a45ec4689ecb2616767cb1fd02c28vboxsync * GIP Mapping and Unmapping Related Code.
30868e719f5a45ec4689ecb2616767cb1fd02c28vboxsync * GIP Mapping and Unmapping Related Code.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * (Re-)initializes the per-cpu structure prior to starting or resuming the GIP
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * updating.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * @param pGip Pointer to the GIP.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * @param pGipCpu The per CPU structure for this CPU.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * @param u64NanoTS The current time.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsyncstatic void supdrvGipReInitCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pGipCpu, uint64_t u64NanoTS)
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * Here we don't really care about applying the TSC delta. The re-initialization of this
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * value is not relevant especially while (re)starting the GIP as the first few ones will
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * be ignored anyway, see supdrvGipDoUpdateCpu().
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync pGipCpu->u64TSC = ASMReadTSC() - pGipCpu->u32UpdateIntervalTSC;
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * Set the current TSC and NanoTS value for the CPU.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * @param idCpu The CPU ID. Unused - we have to use the APIC ID.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * @param pvUser1 Pointer to the ring-0 GIP mapping.
2afbe132eb7931e0125141eabe3a48e08f1ffab5vboxsync * @param pvUser2 Pointer to the variable holding the current time.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsyncstatic DECLCALLBACK(void) supdrvGipReInitCpuCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync PSUPGLOBALINFOPAGE pGip = (PSUPGLOBALINFOPAGE)pvUser1;
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync unsigned iCpu = pGip->aiCpuFromApicId[ASMGetApicId()];
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync if (RT_LIKELY(iCpu < pGip->cCpus && pGip->aCPUs[iCpu].idCpu == idCpu))
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync supdrvGipReInitCpu(pGip, &pGip->aCPUs[iCpu], *(uint64_t *)pvUser2);
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * State structure for supdrvGipDetectGetGipCpuCallback.
2508d15edddcae0b79002fae3fe103d6c4836810vboxsync /** Bitmap of APIC IDs that has been seen (initialized to zero).
2508d15edddcae0b79002fae3fe103d6c4836810vboxsync * Used to detect duplicate APIC IDs (paranoia). */
2508d15edddcae0b79002fae3fe103d6c4836810vboxsync /** Mask of supported GIP CPU getter methods (SUPGIPGETCPU_XXX) (all bits set
2508d15edddcae0b79002fae3fe103d6c4836810vboxsync * initially). The callback clears the methods not detected. */
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync /** The first callback detecting any kind of range issues (initialized to
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * NIL_RTCPUID). */
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync/** Pointer to state structure for supdrvGipDetectGetGipCpuCallback. */
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsynctypedef SUPDRVGIPDETECTGETCPU *PSUPDRVGIPDETECTGETCPU;
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * Checks for alternative ways of getting the CPU ID.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * This also checks the APIC ID, CPU ID and CPU set index values against the
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * GIP tables.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * @param idCpu The CPU ID. Unused - we have to use the APIC ID.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * @param pvUser1 Pointer to the state structure.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * @param pvUser2 Pointer to the GIP.
2508d15edddcae0b79002fae3fe103d6c4836810vboxsyncstatic DECLCALLBACK(void) supdrvGipDetectGetGipCpuCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
2508d15edddcae0b79002fae3fe103d6c4836810vboxsync PSUPDRVGIPDETECTGETCPU pState = (PSUPDRVGIPDETECTGETCPU)pvUser1;
2508d15edddcae0b79002fae3fe103d6c4836810vboxsync PSUPGLOBALINFOPAGE pGip = (PSUPGLOBALINFOPAGE)pvUser2;
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync AssertMsg(idCpu == RTMpCpuId(), ("idCpu=%#x RTMpCpuId()=%#x\n", idCpu, RTMpCpuId())); /* paranoia^3 */
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * Check that the CPU ID and CPU set index are interchangable.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync AssertCompile(RT_IS_POWER_OF_TWO(RTCPUSET_MAX_CPUS));
2afbe132eb7931e0125141eabe3a48e08f1ffab5vboxsync * Check whether the IDTR.LIMIT contains a CPU number.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync uint16_t const cbIdt = sizeof(X86DESC64SYSTEM) * 256;
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync fSupported |= SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS;
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * Check whether RDTSCP is an option.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync if ( ASMIsValidExtRange(ASMCpuId_EAX(UINT32_C(0x80000000)))
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync && (ASMCpuId_EDX(UINT32_C(0x80000001)) & X86_CPUID_EXT_FEATURE_EDX_RDTSCP) )
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync fSupported |= SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS;
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * Check that the APIC ID is unique.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync if (RT_LIKELY( idApic < RT_ELEMENTS(pGip->aiCpuFromApicId)
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync && !ASMAtomicBitTestAndSet(pState->bmApicId, idApic)))
1999ae03c34840fa4d712fd2e020120b2cb7182avboxsync AssertCompile(sizeof(pState->bmApicId) * 8 == RT_ELEMENTS(pGip->aiCpuFromApicId));
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync ASMAtomicCmpXchgU32(&pState->idCpuProblem, idCpu, NIL_RTCPUID);
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x - duplicate APIC ID.\n",
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * Check that the iCpuSet is within the expected range.
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync || (unsigned)iCpuSet >= RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync ASMAtomicCmpXchgU32(&pState->idCpuProblem, idCpu, NIL_RTCPUID);
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x - CPU set index is out of range.\n",
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync ASMAtomicCmpXchgU32(&pState->idCpuProblem, idCpu, NIL_RTCPUID);
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x - CPU id/index roundtrip problem: %#x\n",
e2bd93b4f9c38c9b01eb960ba7bc1fc9c4d38ce8vboxsync * Update the supported feature mask before we return.
fa8716d08ff627a8e1c14bcac56e8e3867b3f795vboxsync * Increase the timer freqency on hosts where this is possible (NT).
cc723cf07e365cd40b517b9c5da4f113e9469745vboxsync * The idea is that more interrupts is better for us... Also, it's better than
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * we increase the timer frequence, because we might end up getting inaccurate
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * callbacks if someone else does it.
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * @param pDevExt Sets u32SystemTimerGranularityGrant if increased.
cc723cf07e365cd40b517b9c5da4f113e9469745vboxsyncstatic void supdrvGipRequestHigherTimerFrequencyFromSystem(PSUPDRVDEVEXT pDevExt)
42c1972c22e09797b4b24afbd0ec114ed076c37cvboxsync if ( RT_SUCCESS_NP(RTTimerRequestSystemGranularity( 976563 /* 1024 HZ */, &u32SystemResolution))
42c1972c22e09797b4b24afbd0ec114ed076c37cvboxsync || RT_SUCCESS_NP(RTTimerRequestSystemGranularity( 1000000 /* 1000 HZ */, &u32SystemResolution))
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync || RT_SUCCESS_NP(RTTimerRequestSystemGranularity( 1953125 /* 512 HZ */, &u32SystemResolution))
289060a0c3cb1d509f2cb01fca060796212376f6vboxsync || RT_SUCCESS_NP(RTTimerRequestSystemGranularity( 2000000 /* 500 HZ */, &u32SystemResolution))
7c9a5eca233baf6ede345ace077a00bd0b7af1efvboxsync Assert(RTTimerGetSystemGranularity() <= u32SystemResolution);
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync pDevExt->u32SystemTimerGranularityGrant = u32SystemResolution;
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * Undoes supdrvGipRequestHigherTimerFrequencyFromSystem.
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * @param pDevExt Clears u32SystemTimerGranularityGrant.
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsyncstatic void supdrvGipReleaseHigherTimerFrequencyFromSystem(PSUPDRVDEVEXT pDevExt)
1986f56777969a25707ab214f8dd070804be666cvboxsync int rc2 = RTTimerReleaseSystemGranularity(pDevExt->u32SystemTimerGranularityGrant);
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * Maps the GIP into userspace and/or get the physical address of the GIP.
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * @returns IPRT status code.
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * @param pSession Session to which the GIP mapping should belong.
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * @param ppGipR3 Where to store the address of the ring-3 mapping. (optional)
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * @param pHCPhysGip Where to store the physical address. (optional)
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * @remark There is no reference counting on the mapping, so one call to this function
1986f56777969a25707ab214f8dd070804be666cvboxsync * count globally as one reference. One call to SUPR0GipUnmap() is will unmap GIP
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync * and remove the session as a GIP user.
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsyncSUPR0DECL(int) SUPR0GipMap(PSUPDRVSESSION pSession, PRTR3PTR ppGipR3, PRTHCPHYS pHCPhysGip)
1986f56777969a25707ab214f8dd070804be666cvboxsync LogFlow(("SUPR0GipMap: pSession=%p ppGipR3=%p pHCPhysGip=%p\n", pSession, ppGipR3, pHCPhysGip));
611910c4ba57eb6db5c0d508ca7b923efd654aecvboxsync AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
134a71c1528b56afe4db843ab63ec5a5b849535bvboxsync AssertPtrNullReturn(ppGipR3, VERR_INVALID_POINTER);
7c9a5eca233baf6ede345ace077a00bd0b7af1efvboxsync AssertPtrNullReturn(pHCPhysGip, VERR_INVALID_POINTER);
0bc7c910e57c78c68e89122e2244cc073d1ef06evboxsync RTSemMutexRequest(pDevExt->mtxGip, RT_INDEFINITE_WAIT);
7c9a5eca233baf6ede345ace077a00bd0b7af1efvboxsync rc = RTR0MemObjMapUser(&pSession->GipMapObjR3, pDevExt->GipMemObj, (RTR3PTR)-1, 0,
7c9a5eca233baf6ede345ace077a00bd0b7af1efvboxsync pGipR3 = RTR0MemObjAddressR3(pSession->GipMapObjR3);
7c9a5eca233baf6ede345ace077a00bd0b7af1efvboxsync * Get physical address.
0bc7c910e57c78c68e89122e2244cc073d1ef06evboxsync * Reference globally.
7c9a5eca233baf6ede345ace077a00bd0b7af1efvboxsync * GIP starts/resumes updating again. On windows we bump the
3c941112ffb137d71a8e457fcc3915f2d464ed2avboxsync * host timer frequency to make sure we don't get stuck in guest
3c941112ffb137d71a8e457fcc3915f2d464ed2avboxsync * mode and to get better timer (and possibly clock) accuracy.
1986f56777969a25707ab214f8dd070804be666cvboxsync supdrvGipRequestHigherTimerFrequencyFromSystem(pDevExt);
6f516ad9911d9037a18778742caa955fe362f8ffvboxsync * document me
cc723cf07e365cd40b517b9c5da4f113e9469745vboxsync if (pGipR0->aCPUs[0].u32TransactionId != 2 /* not the first time */)
cc723cf07e365cd40b517b9c5da4f113e9469745vboxsync unsigned i;
cc723cf07e365cd40b517b9c5da4f113e9469745vboxsync ASMAtomicUoWriteU32(&pGipR0->aCPUs[i].u32TransactionId,
4bfa7b58e362a1bca0628643c352c137900bf01avboxsync (pGipR0->aCPUs[i].u32TransactionId + GIP_UPDATEHZ_RECALC_FREQ * 2)
cc723cf07e365cd40b517b9c5da4f113e9469745vboxsync ASMAtomicWriteU64(&pGipR0->u64NanoTSLastUpdateHz, 0);
150283991b1a312acbe86c67d3420f6463b38878vboxsync * document me
cc723cf07e365cd40b517b9c5da4f113e9469745vboxsync u64NanoTS = RTTimeSystemNanoTS() - pGipR0->u32UpdateIntervalNS;
cc723cf07e365cd40b517b9c5da4f113e9469745vboxsync supdrvGipReInitCpu(pGipR0, &pGipR0->aCPUs[0], u64NanoTS);
cc723cf07e365cd40b517b9c5da4f113e9469745vboxsync RTMpOnAll(supdrvGipReInitCpuCallback, pGipR0, &u64NanoTS);
cc723cf07e365cd40b517b9c5da4f113e9469745vboxsync * Detect alternative ways to figure the CPU ID in ring-3 and
cc723cf07e365cd40b517b9c5da4f113e9469745vboxsync * raw-mode context. Check the sanity of the APIC IDs, CPU IDs,
cc723cf07e365cd40b517b9c5da4f113e9469745vboxsync * and CPU set indexes while we're at it.
6e4b0f4821f335d37975004f6a7badab8bc48b6fvboxsync RT_BZERO((void *)&DetectState.bmApicId, sizeof(DetectState.bmApicId));
6e4b0f4821f335d37975004f6a7badab8bc48b6fvboxsync rc = RTMpOnAll(supdrvGipDetectGetGipCpuCallback, &DetectState, pGipR0);
6e4b0f4821f335d37975004f6a7badab8bc48b6fvboxsync LogRel(("SUPR0GipMap: fGetGipCpu=%#x\n", DetectState.fSupported));
6e4b0f4821f335d37975004f6a7badab8bc48b6fvboxsync LogRel(("SUPR0GipMap: No supported ways of getting the APIC ID or CPU number in ring-3! (%#x)\n",
6e4b0f4821f335d37975004f6a7badab8bc48b6fvboxsync LogRel(("SUPR0GipMap: APIC ID, CPU ID or CPU set index problem detected on CPU #%u (%#x)!\n",
6e4b0f4821f335d37975004f6a7badab8bc48b6fvboxsync DetectState.idCpuProblem, DetectState.idCpuProblem));
6e4b0f4821f335d37975004f6a7badab8bc48b6fvboxsync * Start the GIP timer if all is well..
6e4b0f4821f335d37975004f6a7badab8bc48b6fvboxsync rc = RTTimerStart(pDevExt->pGipTimer, 0 /* fire ASAP */); AssertRC(rc);
cc723cf07e365cd40b517b9c5da4f113e9469745vboxsync * Bail out on error.
150283991b1a312acbe86c67d3420f6463b38878vboxsync int rc2 = RTR0MemObjFree(pSession->GipMapObjR3, false); AssertRC(rc2);
#ifdef SUPDRV_USE_MUTEX_FOR_GIP
if (pHCPhysGip)
if (ppGipR3)
#ifdef DEBUG_DARWIN_GIP
OSDBGPRINT(("SUPR0GipMap: returns %d *pHCPhysGip=%lx pGipR3=%p\n", rc, (unsigned long)HCPhys, (void *)pGipR3));
LogFlow(( "SUPR0GipMap: returns %d *pHCPhysGip=%lx pGipR3=%p\n", rc, (unsigned long)HCPhys, (void *)pGipR3));
return rc;
#ifdef DEBUG_DARWIN_GIP
#ifdef SUPDRV_USE_MUTEX_FOR_GIP
#ifndef DO_NOT_START_GIP
#ifdef SUPDRV_USE_MUTEX_FOR_GIP
return rc;
return g_pSUPGlobalInfoPage;
static void supdrvGipInitSetCpuFreq(PSUPGLOBALINFOPAGE pGip, uint64_t nsElapsed, uint64_t cElapsedTscTicks, uint32_t iTick)
static DECLCALLBACK(void) supdrvInitRefineInvariantTscFreqTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
#if !defined(RT_OS_OS2)
ASMNopPause();
int64_t iStartTscDelta = iStartGipCpu < pGip->cCpus ? pGip->aCPUs[iStartGipCpu].i64TSCDelta : INT64_MAX;
int64_t iStopTscDelta = iStopGipCpu < pGip->cCpus ? pGip->aCPUs[iStopGipCpu].i64TSCDelta : INT64_MAX;
/* cTscTicksElapsed = (uTsc - iStopTscDelta) - (pDevExt->uTscStartInvarTscRefine - iStartTscDelta); */
SUPR0Printf("vboxdrv: Failed to refine invariant TSC frequency because deltas are unavailable after %u (%u) seconds\n",
static void supdrvGipInitStartTimerForRefiningInvariantTscFreq(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip)
int rc;
ASMNopPause();
while (cTriesLeft-- > 0)
ASMNopPause();
uint64_t msDelay = ( ((fRough ? 16 : 200) * RT_NS_1MS + nsTimerIncr - 1) / nsTimerIncr * nsTimerIncr - RT_NS_100US )
/ RT_NS_1MS;
ASMNopPause();
ASMNopPause();
bool fDoXCall = false;
else if (fRough)
fDoXCall = true;
int64_t iStartTscDelta = iStartGipCpu < pGip->cCpus ? pGip->aCPUs[iStartGipCpu].i64TSCDelta : INT64_MAX;
int64_t iStopTscDelta = iStopGipCpu < pGip->cCpus ? pGip->aCPUs[iStopGipCpu].i64TSCDelta : INT64_MAX;
return VERR_INVALID_CPU_INDEX;
else if (cTriesLeft > 0)
fDoXCall = true;
if (cTriesLeft > 0)
fDoXCall = true;
if (fDoXCall)
return VINF_SUCCESS;
cTries = 0;
bool fRc;
if (fRc)
int iCpuSet = 0;
uint32_t i = 0;
if (iCpuSet >= 0)
static DECLCALLBACK(void) supdrvGipMpEventOnlineCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
int iCpuSet;
if (pGip)
switch (enmEvent)
case RTMPEVENT_ONLINE:
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
case RTMPEVENT_OFFLINE:
bool fIgnored;
for (i = 0; i < RTCPUSET_MAX_CPUS; i++)
static DECLCALLBACK(void) supdrvGipInitDetermineAsyncTscWorker(RTCPUID idCpu, void *pvUser1, void *pvUser2)
int iCpu;
bool fAsync = false;
while (cLoops-- > 0)
fAsync = true;
if (poffMin)
return fAsync;
bool fInvariantTsc = false;
if (ASMHasCpuId())
fInvariantTsc = true;
return SUPGIPMODE_ASYNC_TSC;
if (fInvariantTsc)
return SUPGIPMODE_INVARIANT_TSC;
return SUPGIPMODE_ASYNC_TSC;
return SUPGIPMODE_ASYNC_TSC;
return SUPGIPMODE_SYNC_TSC;
static void supdrvGipInitCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pCpu, uint64_t u64NanoTS, uint64_t uCpuHz)
if (!uCpuHz)
#ifdef DEBUG_DARWIN_GIP
OSDBGPRINT(("supdrvGipInit: pGip=%p HCPhys=%lx u64NanoTS=%llu uUpdateHz=%d cCpus=%u\n", pGip, (long)HCPhys, u64NanoTS, uUpdateHz, cCpus));
LogFlow(("supdrvGipInit: pGip=%p HCPhys=%lx u64NanoTS=%llu uUpdateHz=%d cCpus=%u\n", pGip, (long)HCPhys, u64NanoTS, uUpdateHz, cCpus));
for (i = 0; i < cCpus; i++)
unsigned cCpus;
int rc;
#ifdef SUPDRV_USE_MUTEX_FOR_GIP
SUPR0Printf("VBoxDrv: Too many CPUs (%u) for the GIP (max %u)\n", cCpus, RT_MIN(RTCPUSET_MAX_CPUS, 256));
return VERR_TOO_MANY_CPUS;
rc = RTR0MemObjAllocCont(&pDevExt->GipMemObj, RT_UOFFSETOF(SUPGLOBALINFOPAGE, aCPUs[cCpus]), false /*fExecutable*/);
return rc;
if (uMod)
supdrvGipInit(pDevExt, pGip, HCPhysGip, RTTimeSystemNanoTS(), RT_NS_1SEC / u32Interval /*=Hz*/, u32Interval, cCpus);
/* Basically, invariant Windows boxes, should never be detected as async (i.e. TSC-deltas should be 0). */
OSDBGPRINT(("supdrvGipCreate: The TSC-deltas should be normalized by the host OS, but verifying shows it's not!\n"));
return VERR_INTERNAL_ERROR_2;
* RTMpOnAll/supdrvGipInitOnCpu call below).
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
} while (--cTries > 0);
AssertMsg(!pGip->aCPUs[iCpu].i64TSCDelta, ("iCpu=%u %lld mode=%d\n", iCpu, pGip->aCPUs[iCpu].i64TSCDelta, pGip->u32Mode));
return VINF_SUCCESS;
OSDBGPRINT(("supdrvGipCreate: failed create GIP timer at %u ns interval. rc=%Rrc\n", u32Interval, rc));
return rc;
int rc;
#ifdef DEBUG_DARWIN_GIP
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
static void supdrvGipDoUpdateCpu(PSUPDRVDEVEXT pDevExt, PSUPGIPCPU pGipCpu, uint64_t u64NanoTS, uint64_t u64TSC, uint64_t iTick)
unsigned iTSCHistoryHead;
ASMAtomicWriteU32(&pGipCpu->u32UpdateIntervalTSC, u32UpdateIntervalTSC + u32UpdateIntervalTSCSlack);
static void supdrvGipUpdate(PSUPDRVDEVEXT pDevExt, uint64_t u64NanoTS, uint64_t u64TSC, RTCPUID idCpu, uint64_t iTick)
/* this can happen on win32 if we're taking to long and there are more CPUs around. shouldn't happen though. */
if ( pGip->u32Mode != SUPGIPMODE_INVARIANT_TSC /* cuz we're not recalculating the frequency on invariants hosts. */
static DECLCALLBACK(void) supdrvGipSyncAndInvariantTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
* Solaris timers fire on the CPU they were registered/started on.
# define GIP_TSC_DELTA_METHOD_1
# define GIP_TSC_DELTA_METHOD_2
typedef struct SUPDRVTSCDELTAMETHOD2ENTRY
typedef struct SUPDRVTSCDELTAMETHOD2
typedef struct SUPTSCDELTASYNC2
typedef struct SUPDRVGIPTSCDELTARGS
bool volatile fAbortSetup;
bool volatile fTimedOut;
} Verify;
bool fLag;
} M2;
} uMaster;
#ifdef TSCDELTA_VERIFY_WITH_STATS
} Verify;
bool fLag;
} M2;
} uWorker;
# define TSCDELTA_DBG_CHECK_LOOP() \
# define TSCDELTA_DBG_VARS() ((void)0)
# define TSCDELTA_DBG_START_LOOP() ((void)0)
# define TSCDELTA_DBG_CHECK_LOOP() ((void)0)
if (fIsMaster)
if (RT_LIKELY(ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_STEADY, GIP_TSC_DELTA_SYNC2_READY)))
TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #1 uSyncVar=%#x\n", fIsMaster ? "master" : "worker", pOtherSync->uSyncVar));
ASMNopPause();
TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #2 u32Tmp=%#x\n", fIsMaster ? "master" : "worker", u32Tmp));
#if 0 /* This is crazy, I know, but enable this code and the results are markedly better when enabled on the 1.4GHz AMD (debug). */
if (ASMAtomicCmpXchgU32(&pMySync->uSyncVar, GIP_TSC_DELTA_SYNC2_TIMEOUT, GIP_TSC_DELTA_SYNC2_READY))
ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_TIMEOUT, GIP_TSC_DELTA_SYNC2_STEADY);
iSync2Loops++;
if (!fIsMaster)
if (RT_LIKELY(ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_STEADY, GIP_TSC_DELTA_SYNC2_READY)))
TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #3 uSyncVar=%#x\n", fIsMaster ? "master" : "worker", pOtherSync->uSyncVar));
if (fIsMaster)
if (RT_LIKELY(ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_GO, GIP_TSC_DELTA_SYNC2_STEADY)))
TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #4 uSyncVar=%#x\n", fIsMaster ? "master" : "worker", pOtherSync->uSyncVar));
TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #5 u32Tmp=%#x\n", fIsMaster ? "master" : "worker", u32Tmp));
ASMNopPause();
if (!fIsMaster)
if (RT_LIKELY(ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_GO, GIP_TSC_DELTA_SYNC2_STEADY)))
TSCDELTA_DBG_SYNC_MSG(("sync/before/%s: #6 uSyncVar=%#x\n", fIsMaster ? "master" : "worker", pOtherSync->uSyncVar));
ASMNopPause();
ASMNopPause();
ASMNopPause();
if (RT_LIKELY(supdrvTscDeltaSync2_Before(a_pMySync, a_pOtherSync, true /*fIsMaster*/, a_pfEFlags, a_pArgs))) \
if (RT_LIKELY(supdrvTscDeltaSync2_Before(a_pMySync, a_pOtherSync, false /*fIsMaster*/, a_pfEFlags, a_pArgs))) \
ASMNopPause();
ASMNopPause();
if (RT_LIKELY(ASMAtomicCmpXchgU32(&(a_pOtherSync)->uSyncVar, GIP_TSC_DELTA_SYNC2_READY, GIP_TSC_DELTA_SYNC2_GO))) \
if (RT_LIKELY(ASMAtomicCmpXchgU32(&(a_pOtherSync)->uSyncVar, GIP_TSC_DELTA_SYNC2_READY, GIP_TSC_DELTA_SYNC2_GO))) \
if (RT_LIKELY(supdrvTscDeltaSync2_After(a_pMySync, a_pOtherSync, false /*fIsMaster*/, a_fEFlags))) \
#ifdef GIP_TSC_DELTA_METHOD_1
static void supdrvTscDeltaMethod1Loop(PSUPDRVGIPTSCDELTARGS pArgs, PSUPTSCDELTASYNC2 pMySync, PSUPTSCDELTASYNC2 pOtherSync,
unsigned iLoop;
if (fIsMaster)
pGipCpuMaster->u64TSCSample, pGipCpuMaster->idCpu, pGipCpuWorker->idCpu, pArgs->pDevExt->idGipMaster));
TSCDELTA_DBG_SYNC_MSG9(("sync/method1loop/%s: #92 iLoop=%u MyState=%#x\n", fIsMaster ? "master" : "worker", iLoop,
if (!fIsMaster)
#ifdef GIP_TSC_DELTA_METHOD_2
# define GIP_TSC_DELTA_M2_PRIMER_LOOPS 0
if (pArgs->uWorker.M2.Data.aResults[idxOther].iSeqOther == pArgs->uMaster.M2.Data.aResults[idxResult].iSeqMine)
cHits++;
static void supdrvTscDeltaMethod2CollectData(PSUPDRVTSCDELTAMETHOD2 pMyData, uint32_t volatile *piOtherSeqNo, bool fLag)
while (cLeft-- > 0)
pEntry++;
if (fLag)
ASMNopPause();
static void supdrvTscDeltaMethod2Loop(PSUPDRVGIPTSCDELTARGS pArgs, PSUPTSCDELTASYNC2 pMySync, PSUPTSCDELTASYNC2 pOtherSync,
unsigned iLoop;
if (fIsMaster)
# if GIP_TSC_DELTA_M2_PRIMER_LOOPS > 0
supdrvTscDeltaMethod2CollectData(&pArgs->uMaster.M2.Data, &pArgs->uWorker.M2.Data.iCurSeqNo, pArgs->uMaster.M2.fLag);
# if GIP_TSC_DELTA_M2_PRIMER_LOOPS > 0
supdrvTscDeltaMethod2CollectData(&pArgs->uWorker.M2.Data, &pArgs->uMaster.M2.Data.iCurSeqNo, pArgs->uWorker.M2.fLag);
uint32_t i;
AssertCompile(RT_ELEMENTS(pArgs->uMaster.Verify.auTscs) == RT_ELEMENTS(pArgs->uWorker.Verify.auTscs));
if (fIsMaster)
ASMNopPause();
ASMNopPause();
#ifdef TSCDELTA_VERIFY_WITH_STATS
uTscWorker = 0;
#ifdef TSCDELTA_VERIFY_WITH_STATS
if (iDiff < 0)
#ifdef TSCDELTA_VERIFY_WITH_STATS
#ifdef TSCDELTA_VERIFY_WITH_STATS
if (iDiff < 0)
#ifdef TSCDELTA_VERIFY_WITH_STATS
ASMNopPause();
ASMNopPause();
return VERR_TIMEOUT;
DECL_NO_INLINE(static, int)
supdrvMeasureTscDeltaCallbackAbortSyncSetup(PSUPDRVGIPTSCDELTARGS pArgs, PSUPTSCDELTASYNC2 pMySync, bool fIsMaster, bool fTimeout)
if (fTimeout)
ASMNopPause();
ASMNopPause();
ASMNopPause();
int rc;
if ( !fIsMaster
iTry = 0;
ASMNopPause();
ASMNopPause();
if (fIsMaster)
if (!ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_READY, GIP_TSC_DELTA_SYNC2_PRESTART_WAIT)) /* parnaoia */
iTry = 0;
ASMNopPause();
if ( fIsMaster
&& !ASMAtomicCmpXchgU32(&MySync.uSyncVar, GIP_TSC_DELTA_SYNC2_PRESTART_ABORT, GIP_TSC_DELTA_SYNC2_PRESTART_WAIT))
if (!fIsMaster)
if (!ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_READY, GIP_TSC_DELTA_SYNC2_PRESTART_WAIT)) /* race #1 */
rc = supdrvTscDeltaVerify(pArgs, &MySync, pOtherSync, fIsMaster, GIP_TSC_DELTA_INITIAL_MASTER_VALUE);
if (fIsMaster)
&& (fIsMaster || u32Tmp != GIP_TSC_DELTA_SYNC2_STEADY) /* worker may be late prepping for the next round */ )
TSCDELTA_DBG_SYNC_MSG(("sync/loop/%s: #0 iTry=%u MyState=%#x\n", fIsMaster ? "master" : "worker", iTry, u32Tmp));
#ifdef GIP_TSC_DELTA_METHOD_1
&& (fIsMaster || u32Tmp != GIP_TSC_DELTA_SYNC2_STEADY) /* worker may be late prepping for the next round */ )
if (fIsMaster)
if (fIsMaster)
if (fIsMaster)
iTry = 0;
iTry++;
if ( iTry == 0
ASMNopPause();
if (fIsMaster)
static DECLCALLBACK(void) supdrvMeasureTscDeltaCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
int rc;
int rc2;
return VINF_SUCCESS;
#ifdef SUPDRV_USE_MUTEX_FOR_GIP
return rc;
&& ASMHasCpuId()
&& !ASMIsAmdCpu()
uint32_t i;
if ( i != iGipCpuMaster
&& i != idxWorker
iGipCpuMaster = i;
if (pArgs)
#ifdef SUPDRV_USE_MUTEX_FOR_GIP
return rc;
unsigned iCpu;
if (fClearDeltas)
unsigned iCpu;
unsigned iOddEven;
return VINF_SUCCESS;
SUPR0Printf("supdrvMeasureTscDeltaOne failed. rc=%d CPU[%u].idCpu=%u Master[%u].idCpu=%u\n", rc, iCpu,
SUPR0Printf("One or more CPUs transitioned between online & offline states. I'm confused, retry...\n");
return rc;
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
static int supdrvTscDeltaThreadButchered(PSUPDRVDEVEXT pDevExt, bool fSpinlockHeld, const char *pszFailed, int rcFailed)
if (!fSpinlockHeld)
return rcFailed;
bool fInitialMeasurement = true;
switch (enmState)
cConsecutiveTimeouts = 0;
rc = RTSemEventSignal(pDevExt->hTscDeltaEvent); /* (Safe on windows as long as spinlock isn't IRQ safe.) */
cConsecutiveTimeouts = 0;
if (fInitialMeasurement)
fInitialMeasurement = false;
} while (cTries-- > 0);
unsigned iCpu;
return VINF_SUCCESS;
return supdrvTscDeltaThreadButchered(pDevExt, true /* fSpinlockHeld */, "Invalid state", VERR_INVALID_STATE);
return rc;
int rc;
OSDBGPRINT(("supdrvTscDeltaThreadWait: timed out state transition. enmState=%d enmNewState=%d\n", enmState,
enmNewState));
OSDBGPRINT(("supdrvTscDeltaThreadWait: invalid state transition from %d to %d, expected %d\n", enmCurState,
OSDBGPRINT(("supdrvTscDeltaThreadWait: invalid state transition from %d to %d\n", enmCurState, enmNewState));
return rc;
int rc;
while (--cTriesLeft > 0)
int rc;
rc = RTSpinlockCreate(&pDevExt->hTscDeltaSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, "VBoxTscSpnLck");
rc = supdrvTscDeltaThreadWait(pDevExt, kTscDeltaThreadState_Creating, kTscDeltaThreadState_Listening);
return rc;
return rc;
SUPR0DECL(int) SUPR0TscDeltaMeasureBySetIndex(PSUPDRVSESSION pSession, uint32_t iCpuSet, uint32_t fFlags,
int rc;
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
return VERR_WRONG_ORDER;
return VERR_INVALID_FLAGS;
return VINF_SUCCESS;
if (cTries == 0)
if (cMsWaitRetry == 0)
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
return VINF_SUCCESS;
return VINF_SUCCESS;
if (cMsWaitThread == 0)
return rc;
cTries--;
return rc;
int VBOXCALL supdrvIOCtl_TscDeltaMeasure(PSUPDRVDEVEXT pDevExt, PSUPDRVSESSION pSession, PSUPTSCDELTAMEASURE pReq)
return VERR_INVALID_CPU_ID;
return VERR_INVALID_CPU_ID;
fFlags = 0;
cTries);
int rc;
return VERR_WRONG_ORDER;
int iGipCpu;
int iGipCpu;
return rc;