SUPDrvGip.cpp revision d5d73d91f3aba093b9737fc51ccf3c7a31501d80
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * VBoxDrv - The VirtualBox Support Driver - Common code for GIP.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Copyright (C) 2006-2015 Oracle Corporation
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * available from http://www.virtualbox.org. This file is free software;
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * you can redistribute it and/or modify it under the terms of the GNU
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * General Public License (GPL) as published by the Free Software
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * The contents of this file may alternatively be used under the terms
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * of the Common Development and Distribution License Version 1.0
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * VirtualBox OSE distribution, in which case the provisions of the
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * CDDL are applicable instead of those of the GPL.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * You may elect to license modified versions of this file under the
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * terms and conditions of either the GPL or the CDDL or both.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync/*******************************************************************************
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync* Header Files *
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync*******************************************************************************/
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync/*******************************************************************************
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync* Defined Constants And Macros *
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync*******************************************************************************/
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync/** The frequency by which we recalculate the u32UpdateHz and
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * u32UpdateIntervalNS GIP members. The value must be a power of 2.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Warning: Bumping this too high might overflow u32UpdateIntervalNS.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync/** A reserved TSC value used for synchronization as well as measurement of
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * TSC deltas. */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync/** The number of TSC delta measurement loops in total (includes primer and
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * read-time loops). */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync/** The number of cache primer loops. */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync/** The number of loops until we keep computing the minumum read time. */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync/** The TSC frequency refinement period in seconds.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * The timer fires after 200ms, then every second, this value just says when
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * to stop it after that. */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync/** The TSC-delta threshold for the SUPGIPUSETSCDELTA_PRACTICALLY_ZERO rating */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync/** The TSC-delta threshold for the SUPGIPUSETSCDELTA_ROUGHLY_ZERO rating */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync/** The TSC delta value for the initial GIP master - 0 in regular builds.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * To test the delta code this can be set to a non-zero value. */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync# define GIP_TSC_DELTA_INITIAL_MASTER_VALUE INT64_C(170139095182512) /* 0x00009abd9854acb0 */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync# define GIP_TSC_DELTA_INITIAL_MASTER_VALUE INT64_C(0)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncAssertCompile(GIP_TSC_DELTA_PRIMER_LOOPS < GIP_TSC_DELTA_READ_TIME_LOOPS);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncAssertCompile(GIP_TSC_DELTA_PRIMER_LOOPS + GIP_TSC_DELTA_READ_TIME_LOOPS < GIP_TSC_DELTA_LOOPS);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync/** @def VBOX_SVN_REV
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * The makefile should define this if it can. */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync#if 0 /* Don't start the GIP timers. Useful when debugging the IPRT timer code. */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync/*******************************************************************************
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync* Internal Functions *
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync*******************************************************************************/
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic DECLCALLBACK(void) supdrvGipSyncAndInvariantTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic DECLCALLBACK(void) supdrvGipAsyncTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic void supdrvGipInitCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pCpu, uint64_t u64NanoTS, uint64_t uCpuHz);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic void supdrvTscResetSamples(PSUPDRVDEVEXT pDevExt, bool fClearDeltas);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic int supdrvTscDeltaThreadInit(PSUPDRVDEVEXT pDevExt);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic void supdrvTscDeltaTerm(PSUPDRVDEVEXT pDevExt);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic void supdrvTscDeltaThreadStartMeasurement(PSUPDRVDEVEXT pDevExt, bool fForceAll);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic int supdrvMeasureInitialTscDeltas(PSUPDRVDEVEXT pDevExt);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic int supdrvMeasureTscDeltaOne(PSUPDRVDEVEXT pDevExt, uint32_t idxWorker);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync/*******************************************************************************
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync* Global Variables *
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync*******************************************************************************/
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncDECLEXPORT(PSUPGLOBALINFOPAGE) g_pSUPGlobalInfoPage = NULL;
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Misc Common GIP Code
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Misc Common GIP Code
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Misc Common GIP Code
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Finds the GIP CPU index corresponding to @a idCpu.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @returns GIP CPU array index, UINT32_MAX if not found.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @param pGip The GIP.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @param idCpu The CPU ID.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic uint32_t supdrvGipFindCpuIndexForCpuId(PSUPGLOBALINFOPAGE pGip, RTCPUID idCpu)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * GIP Mapping and Unmapping Related Code.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * GIP Mapping and Unmapping Related Code.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * GIP Mapping and Unmapping Related Code.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * (Re-)initializes the per-cpu structure prior to starting or resuming the GIP
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * updating.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @param pGip Pointer to the GIP.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @param pGipCpu The per CPU structure for this CPU.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @param u64NanoTS The current time.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic void supdrvGipReInitCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pGipCpu, uint64_t u64NanoTS)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Here we don't really care about applying the TSC delta. The re-initialization of this
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * value is not relevant especially while (re)starting the GIP as the first few ones will
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * be ignored anyway, see supdrvGipDoUpdateCpu().
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync pGipCpu->u64TSC = ASMReadTSC() - pGipCpu->u32UpdateIntervalTSC;
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Set the current TSC and NanoTS value for the CPU.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @param idCpu The CPU ID. Unused - we have to use the APIC ID.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @param pvUser1 Pointer to the ring-0 GIP mapping.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @param pvUser2 Pointer to the variable holding the current time.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic DECLCALLBACK(void) supdrvGipReInitCpuCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync PSUPGLOBALINFOPAGE pGip = (PSUPGLOBALINFOPAGE)pvUser1;
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync unsigned iCpu = pGip->aiCpuFromApicId[ASMGetApicId()];
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync if (RT_LIKELY(iCpu < pGip->cCpus && pGip->aCPUs[iCpu].idCpu == idCpu))
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync supdrvGipReInitCpu(pGip, &pGip->aCPUs[iCpu], *(uint64_t *)pvUser2);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * State structure for supdrvGipDetectGetGipCpuCallback.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync /** Bitmap of APIC IDs that has been seen (initialized to zero).
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Used to detect duplicate APIC IDs (paranoia). */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync /** Mask of supported GIP CPU getter methods (SUPGIPGETCPU_XXX) (all bits set
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * initially). The callback clears the methods not detected. */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync /** The first callback detecting any kind of range issues (initialized to
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * NIL_RTCPUID). */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync/** Pointer to state structure for supdrvGipDetectGetGipCpuCallback. */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsynctypedef SUPDRVGIPDETECTGETCPU *PSUPDRVGIPDETECTGETCPU;
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Checks for alternative ways of getting the CPU ID.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * This also checks the APIC ID, CPU ID and CPU set index values against the
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * GIP tables.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @param idCpu The CPU ID. Unused - we have to use the APIC ID.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @param pvUser1 Pointer to the state structure.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @param pvUser2 Pointer to the GIP.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic DECLCALLBACK(void) supdrvGipDetectGetGipCpuCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync PSUPDRVGIPDETECTGETCPU pState = (PSUPDRVGIPDETECTGETCPU)pvUser1;
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync PSUPGLOBALINFOPAGE pGip = (PSUPGLOBALINFOPAGE)pvUser2;
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync AssertMsg(idCpu == RTMpCpuId(), ("idCpu=%#x RTMpCpuId()=%#x\n", idCpu, RTMpCpuId())); /* paranoia^3 */
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Check that the CPU ID and CPU set index are interchangable.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync AssertCompile(RT_IS_POWER_OF_TWO(RTCPUSET_MAX_CPUS));
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Check whether the IDTR.LIMIT contains a CPU number.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync uint16_t const cbIdt = sizeof(X86DESC64SYSTEM) * 256;
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync fSupported |= SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS;
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Check whether RDTSCP is an option.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync if ( ASMIsValidExtRange(ASMCpuId_EAX(UINT32_C(0x80000000)))
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync && (ASMCpuId_EDX(UINT32_C(0x80000001)) & X86_CPUID_EXT_FEATURE_EDX_RDTSCP) )
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync fSupported |= SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS;
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Check that the APIC ID is unique.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync if (RT_LIKELY( idApic < RT_ELEMENTS(pGip->aiCpuFromApicId)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync && !ASMAtomicBitTestAndSet(pState->bmApicId, idApic)))
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync AssertCompile(sizeof(pState->bmApicId) * 8 == RT_ELEMENTS(pGip->aiCpuFromApicId));
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync ASMAtomicCmpXchgU32(&pState->idCpuProblem, idCpu, NIL_RTCPUID);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x - duplicate APIC ID.\n",
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Check that the iCpuSet is within the expected range.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync || (unsigned)iCpuSet >= RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync ASMAtomicCmpXchgU32(&pState->idCpuProblem, idCpu, NIL_RTCPUID);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x - CPU set index is out of range.\n",
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync ASMAtomicCmpXchgU32(&pState->idCpuProblem, idCpu, NIL_RTCPUID);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x - CPU id/index roundtrip problem: %#x\n",
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Update the supported feature mask before we return.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Increase the timer freqency on hosts where this is possible (NT).
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * The idea is that more interrupts is better for us... Also, it's better than
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * we increase the timer frequence, because we might end up getting inaccurate
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * callbacks if someone else does it.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @param pDevExt Sets u32SystemTimerGranularityGrant if increased.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic void supdrvGipRequestHigherTimerFrequencyFromSystem(PSUPDRVDEVEXT pDevExt)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync if ( RT_SUCCESS_NP(RTTimerRequestSystemGranularity( 976563 /* 1024 HZ */, &u32SystemResolution))
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync || RT_SUCCESS_NP(RTTimerRequestSystemGranularity( 1000000 /* 1000 HZ */, &u32SystemResolution))
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync || RT_SUCCESS_NP(RTTimerRequestSystemGranularity( 1953125 /* 512 HZ */, &u32SystemResolution))
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync || RT_SUCCESS_NP(RTTimerRequestSystemGranularity( 2000000 /* 500 HZ */, &u32SystemResolution))
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync Assert(RTTimerGetSystemGranularity() <= u32SystemResolution);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync pDevExt->u32SystemTimerGranularityGrant = u32SystemResolution;
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Undoes supdrvGipRequestHigherTimerFrequencyFromSystem.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @param pDevExt Clears u32SystemTimerGranularityGrant.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncstatic void supdrvGipReleaseHigherTimerFrequencyFromSystem(PSUPDRVDEVEXT pDevExt)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync int rc2 = RTTimerReleaseSystemGranularity(pDevExt->u32SystemTimerGranularityGrant);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Maps the GIP into userspace and/or get the physical address of the GIP.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @returns IPRT status code.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @param pSession Session to which the GIP mapping should belong.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @param ppGipR3 Where to store the address of the ring-3 mapping. (optional)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @param pHCPhysGip Where to store the physical address. (optional)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * @remark There is no reference counting on the mapping, so one call to this function
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * count globally as one reference. One call to SUPR0GipUnmap() is will unmap GIP
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * and remove the session as a GIP user.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsyncSUPR0DECL(int) SUPR0GipMap(PSUPDRVSESSION pSession, PRTR3PTR ppGipR3, PRTHCPHYS pHCPhysGip)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync LogFlow(("SUPR0GipMap: pSession=%p ppGipR3=%p pHCPhysGip=%p\n", pSession, ppGipR3, pHCPhysGip));
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync AssertPtrNullReturn(ppGipR3, VERR_INVALID_POINTER);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync AssertPtrNullReturn(pHCPhysGip, VERR_INVALID_POINTER);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync RTSemMutexRequest(pDevExt->mtxGip, RT_INDEFINITE_WAIT);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync rc = RTR0MemObjMapUser(&pSession->GipMapObjR3, pDevExt->GipMemObj, (RTR3PTR)-1, 0,
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync pGipR3 = RTR0MemObjAddressR3(pSession->GipMapObjR3);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Get physical address.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * Reference globally.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * GIP starts/resumes updating again. On windows we bump the
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * host timer frequency to make sure we don't get stuck in guest
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * mode and to get better timer (and possibly clock) accuracy.
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync supdrvGipRequestHigherTimerFrequencyFromSystem(pDevExt);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * document me
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync if (pGipR0->aCPUs[0].u32TransactionId != 2 /* not the first time */)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync unsigned i;
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync ASMAtomicUoWriteU32(&pGipR0->aCPUs[i].u32TransactionId,
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync (pGipR0->aCPUs[i].u32TransactionId + GIP_UPDATEHZ_RECALC_FREQ * 2)
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync ASMAtomicWriteU64(&pGipR0->u64NanoTSLastUpdateHz, 0);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync * document me
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync u64NanoTS = RTTimeSystemNanoTS() - pGipR0->u32UpdateIntervalNS;
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync supdrvGipReInitCpu(pGipR0, &pGipR0->aCPUs[0], u64NanoTS);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync RTMpOnAll(supdrvGipReInitCpuCallback, pGipR0, &u64NanoTS);
597418a165dce3e1c1330096dff7ac95c90d7099vboxsync * Detect alternative ways to figure the CPU ID in ring-3 and
597418a165dce3e1c1330096dff7ac95c90d7099vboxsync * raw-mode context. Check the sanity of the APIC IDs, CPU IDs,
597418a165dce3e1c1330096dff7ac95c90d7099vboxsync * and CPU set indexes while we're at it.
597418a165dce3e1c1330096dff7ac95c90d7099vboxsync RT_BZERO((void *)&DetectState.bmApicId, sizeof(DetectState.bmApicId));
597418a165dce3e1c1330096dff7ac95c90d7099vboxsync rc = RTMpOnAll(supdrvGipDetectGetGipCpuCallback, &DetectState, pGipR0);
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync LogRel(("SUPR0GipMap: fGetGipCpu=%#x\n", DetectState.fSupported));
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync LogRel(("SUPR0GipMap: No supported ways of getting the APIC ID or CPU number in ring-3! (%#x)\n",
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync LogRel(("SUPR0GipMap: APIC ID, CPU ID or CPU set index problem detected on CPU #%u (%#x)!\n",
13493ab7596e827b8d0caab2c89e635dd65f78f9vboxsync DetectState.idCpuProblem, DetectState.idCpuProblem));
#ifndef DO_NOT_START_GIP
#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)
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",
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
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);
OSDBGPRINT(("supdrvGipCreate: Host-OS/user claims the TSC-deltas are zero but we detected async. TSC! Bad.\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 (fResetTscDeltas)
unsigned iCpu;
if (pidxMaster)
return VINF_SUCCESS;
return VERR_CPU_OFFLINE;
unsigned iCpu;
unsigned iOddEven;
return rc;
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;
switch (enmState)
cConsecutiveTimeouts = 0;
rc = RTSemEventSignal(pDevExt->hTscDeltaEvent); /* (Safe on windows as long as spinlock isn't IRQ safe.) */
cConsecutiveTimeouts = 0;
} while (cTries-- > 0);
unsigned iCpu;
Assert(rc != VERR_NOT_AVAILABLE); /* VERR_NOT_AVAILABLE is used as init value, see supdrvTscDeltaThreadInit(). */
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;
if (fForceAll)
&& fForceAll)
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;