SUPDrvGip.cpp revision 00cd9478b4914c7bb2998114d794cb5ba3faa1b4
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * VBoxDrv - The VirtualBox Support Driver - Common code for GIP.
e64031e20c39650a7bc902a3e1aba613b9415deevboxsync * Copyright (C) 2006-2015 Oracle Corporation
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * available from http://www.virtualbox.org. This file is free software;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * you can redistribute it and/or modify it under the terms of the GNU
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * General Public License (GPL) as published by the Free Software
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
2f0d866e126dd288169fed591c259c1c6b4016e5vboxsync * The contents of this file may alternatively be used under the terms
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * of the Common Development and Distribution License Version 1.0
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * VirtualBox OSE distribution, in which case the provisions of the
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * CDDL are applicable instead of those of the GPL.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * 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.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/*******************************************************************************
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync* Header Files *
f5e53763b0a581b0299e98028c6c52192eb06785vboxsync*******************************************************************************/
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/*******************************************************************************
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync* Defined Constants And Macros *
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync*******************************************************************************/
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** The frequency by which we recalculate the u32UpdateHz and
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * u32UpdateIntervalNS GIP members. The value must be a power of 2.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Warning: Bumping this too high might overflow u32UpdateIntervalNS.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** A reserved TSC value used for synchronization as well as measurement of
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * TSC deltas. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** The number of TSC delta measurement loops in total (includes primer and
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * read-time loops). */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** The number of cache primer loops. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** The number of loops until we keep computing the minumum read time. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** @name Master / worker synchronization values.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** Stop measurement of TSC delta. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** Start measurement of TSC delta. */
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync/** Worker thread is ready for reading the TSC. */
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync/** Worker thread is done updating TSC delta info. */
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync/** When IPRT is isn't concurrent safe: Master is ready and will wait for worker
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync * with a timeout. */
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync#define GIP_TSC_DELTA_SYNC_PRESTART_MASTER UINT32_C(4)
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync/** When IPRT is isn't concurrent safe: Worker is ready after waiting for
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * master with a timeout. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** The TSC-refinement interval in seconds. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** The TSC-delta threshold for the SUPGIPUSETSCDELTA_PRACTICALLY_ZERO rating */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** The TSC-delta threshold for the SUPGIPUSETSCDELTA_ROUGHLY_ZERO rating */
2f0d866e126dd288169fed591c259c1c6b4016e5vboxsync/** The TSC delta value for the initial GIP master - 0 in regular builds.
2f0d866e126dd288169fed591c259c1c6b4016e5vboxsync * To test the delta code this can be set to a non-zero value. */
2f0d866e126dd288169fed591c259c1c6b4016e5vboxsync# define GIP_TSC_DELTA_INITIAL_MASTER_VALUE INT64_C(170139095182512) /* 0x00009abd9854acb0 */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync# define GIP_TSC_DELTA_INITIAL_MASTER_VALUE INT64_C(0)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncAssertCompile(GIP_TSC_DELTA_PRIMER_LOOPS < GIP_TSC_DELTA_READ_TIME_LOOPS);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncAssertCompile(GIP_TSC_DELTA_PRIMER_LOOPS + GIP_TSC_DELTA_READ_TIME_LOOPS < GIP_TSC_DELTA_LOOPS);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** @def VBOX_SVN_REV
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * The makefile should define this if it can. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync#if 0 /* Don't start the GIP timers. Useful when debugging the IPRT timer code. */
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync/*******************************************************************************
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync* Internal Functions *
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync*******************************************************************************/
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic DECLCALLBACK(void) supdrvGipSyncAndInvariantTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsyncstatic DECLCALLBACK(void) supdrvGipAsyncTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic void supdrvGipInitCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pCpu, uint64_t u64NanoTS, uint64_t uCpuHz);
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsyncstatic int supdrvTscDeltaThreadInit(PSUPDRVDEVEXT pDevExt);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic void supdrvTscDeltaTerm(PSUPDRVDEVEXT pDevExt);
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsyncstatic void supdrvTscDeltaThreadStartMeasurement(PSUPDRVDEVEXT pDevExt);
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsyncstatic int supdrvMeasureInitialTscDeltas(PSUPDRVDEVEXT pDevExt);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic int supdrvMeasureTscDeltaOne(PSUPDRVDEVEXT pDevExt, uint32_t idxWorker);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/*******************************************************************************
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync* Global Variables *
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync*******************************************************************************/
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncDECLEXPORT(PSUPGLOBALINFOPAGE) g_pSUPGlobalInfoPage = NULL;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Misc Common GIP Code
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Misc Common GIP Code
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Misc Common GIP Code
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Finds the GIP CPU index corresponding to @a idCpu.
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync * @returns GIP CPU array index, UINT32_MAX if not found.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pGip The GIP.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param idCpu The CPU ID.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic uint32_t supdrvGipFindCpuIndexForCpuId(PSUPGLOBALINFOPAGE pGip, RTCPUID idCpu)
9540eeb13face31ddc1c5f15338556fe46f18a77vboxsync * GIP Mapping and Unmapping Related Code.
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync * GIP Mapping and Unmapping Related Code.
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync * GIP Mapping and Unmapping Related Code.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * (Re-)initializes the per-cpu structure prior to starting or resuming the GIP
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * updating.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pGip Pointer to the GIP.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pGipCpu The per CPU structure for this CPU.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param u64NanoTS The current time.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic void supdrvGipReInitCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pGipCpu, uint64_t u64NanoTS)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Here we don't really care about applying the TSC delta. The re-initialization of this
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * value is not relevant especially while (re)starting the GIP as the first few ones will
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * be ignored anyway, see supdrvGipDoUpdateCpu().
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync pGipCpu->u64TSC = ASMReadTSC() - pGipCpu->u32UpdateIntervalTSC;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Set the current TSC and NanoTS value for the CPU.
9540eeb13face31ddc1c5f15338556fe46f18a77vboxsync * @param idCpu The CPU ID. Unused - we have to use the APIC ID.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pvUser1 Pointer to the ring-0 GIP mapping.
9540eeb13face31ddc1c5f15338556fe46f18a77vboxsync * @param pvUser2 Pointer to the variable holding the current time.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic DECLCALLBACK(void) supdrvGipReInitCpuCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync PSUPGLOBALINFOPAGE pGip = (PSUPGLOBALINFOPAGE)pvUser1;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync unsigned iCpu = pGip->aiCpuFromApicId[ASMGetApicId()];
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (RT_LIKELY(iCpu < pGip->cCpus && pGip->aCPUs[iCpu].idCpu == idCpu))
9540eeb13face31ddc1c5f15338556fe46f18a77vboxsync supdrvGipReInitCpu(pGip, &pGip->aCPUs[iCpu], *(uint64_t *)pvUser2);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * State structure for supdrvGipDetectGetGipCpuCallback.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /** Bitmap of APIC IDs that has been seen (initialized to zero).
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync * Used to detect duplicate APIC IDs (paranoia). */
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync /** Mask of supported GIP CPU getter methods (SUPGIPGETCPU_XXX) (all bits set
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * initially). The callback clears the methods not detected. */
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync /** The first callback detecting any kind of range issues (initialized to
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * NIL_RTCPUID). */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** Pointer to state structure for supdrvGipDetectGetGipCpuCallback. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsynctypedef SUPDRVGIPDETECTGETCPU *PSUPDRVGIPDETECTGETCPU;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Checks for alternative ways of getting the CPU ID.
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync * This also checks the APIC ID, CPU ID and CPU set index values against the
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync * GIP tables.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param idCpu The CPU ID. Unused - we have to use the APIC ID.
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync * @param pvUser1 Pointer to the state structure.
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync * @param pvUser2 Pointer to the GIP.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic DECLCALLBACK(void) supdrvGipDetectGetGipCpuCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync PSUPDRVGIPDETECTGETCPU pState = (PSUPDRVGIPDETECTGETCPU)pvUser1;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync PSUPGLOBALINFOPAGE pGip = (PSUPGLOBALINFOPAGE)pvUser2;
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync AssertMsg(idCpu == RTMpCpuId(), ("idCpu=%#x RTMpCpuId()=%#x\n", idCpu, RTMpCpuId())); /* paranoia^3 */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Check that the CPU ID and CPU set index are interchangable.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync AssertCompile(RT_IS_POWER_OF_TWO(RTCPUSET_MAX_CPUS));
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Check whether the IDTR.LIMIT contains a CPU number.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint16_t const cbIdt = sizeof(X86DESC64SYSTEM) * 256;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync fSupported |= SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Check whether RDTSCP is an option.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if ( ASMIsValidExtRange(ASMCpuId_EAX(UINT32_C(0x80000000)))
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync && (ASMCpuId_EDX(UINT32_C(0x80000001)) & X86_CPUID_EXT_FEATURE_EDX_RDTSCP) )
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync fSupported |= SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Check that the APIC ID is unique.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (RT_LIKELY( idApic < RT_ELEMENTS(pGip->aiCpuFromApicId)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync && !ASMAtomicBitTestAndSet(pState->bmApicId, idApic)))
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync AssertCompile(sizeof(pState->bmApicId) * 8 == RT_ELEMENTS(pGip->aiCpuFromApicId));
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync ASMAtomicCmpXchgU32(&pState->idCpuProblem, idCpu, NIL_RTCPUID);
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x - duplicate APIC ID.\n",
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync * Check that the iCpuSet is within the expected range.
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync || (unsigned)iCpuSet >= RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync ASMAtomicCmpXchgU32(&pState->idCpuProblem, idCpu, NIL_RTCPUID);
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x - CPU set index is out of range.\n",
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ASMAtomicCmpXchgU32(&pState->idCpuProblem, idCpu, NIL_RTCPUID);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x - CPU id/index roundtrip problem: %#x\n",
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Update the supported feature mask before we return.
af5224eb6b6676bc892a3f5abeb21f602547d31cvboxsync * Increase the timer freqency on hosts where this is possible (NT).
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * The idea is that more interrupts is better for us... Also, it's better than
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync * we increase the timer frequence, because we might end up getting inaccurate
af5224eb6b6676bc892a3f5abeb21f602547d31cvboxsync * callbacks if someone else does it.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pDevExt Sets u32SystemTimerGranularityGrant if increased.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic void supdrvGipRequestHigherTimerFrequencyFromSystem(PSUPDRVDEVEXT pDevExt)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if ( RT_SUCCESS_NP(RTTimerRequestSystemGranularity( 976563 /* 1024 HZ */, &u32SystemResolution))
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync || RT_SUCCESS_NP(RTTimerRequestSystemGranularity( 1000000 /* 1000 HZ */, &u32SystemResolution))
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync || RT_SUCCESS_NP(RTTimerRequestSystemGranularity( 1953125 /* 512 HZ */, &u32SystemResolution))
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync || RT_SUCCESS_NP(RTTimerRequestSystemGranularity( 2000000 /* 500 HZ */, &u32SystemResolution))
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync Assert(RTTimerGetSystemGranularity() <= u32SystemResolution);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync pDevExt->u32SystemTimerGranularityGrant = u32SystemResolution;
36fbf6dcd3e6b2e5891456b730577ff0eb355c9fvboxsync * Undoes supdrvGipRequestHigherTimerFrequencyFromSystem.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pDevExt Clears u32SystemTimerGranularityGrant.
36fbf6dcd3e6b2e5891456b730577ff0eb355c9fvboxsyncstatic void supdrvGipReleaseHigherTimerFrequencyFromSystem(PSUPDRVDEVEXT pDevExt)
36fbf6dcd3e6b2e5891456b730577ff0eb355c9fvboxsync int rc2 = RTTimerReleaseSystemGranularity(pDevExt->u32SystemTimerGranularityGrant);
36fbf6dcd3e6b2e5891456b730577ff0eb355c9fvboxsync * Maps the GIP into userspace and/or get the physical address of the GIP.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @returns IPRT status code.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pSession Session to which the GIP mapping should belong.
36fbf6dcd3e6b2e5891456b730577ff0eb355c9fvboxsync * @param ppGipR3 Where to store the address of the ring-3 mapping. (optional)
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync * @param pHCPhysGip Where to store the physical address. (optional)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @remark There is no reference counting on the mapping, so one call to this function
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * count globally as one reference. One call to SUPR0GipUnmap() is will unmap GIP
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * and remove the session as a GIP user.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncSUPR0DECL(int) SUPR0GipMap(PSUPDRVSESSION pSession, PRTR3PTR ppGipR3, PRTHCPHYS pHCPhysGip)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync LogFlow(("SUPR0GipMap: pSession=%p ppGipR3=%p pHCPhysGip=%p\n", pSession, ppGipR3, pHCPhysGip));
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync AssertPtrNullReturn(ppGipR3, VERR_INVALID_POINTER);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync AssertPtrNullReturn(pHCPhysGip, VERR_INVALID_POINTER);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync RTSemMutexRequest(pDevExt->mtxGip, RT_INDEFINITE_WAIT);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync rc = RTR0MemObjMapUser(&pSession->GipMapObjR3, pDevExt->GipMemObj, (RTR3PTR)-1, 0,
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync pGipR3 = RTR0MemObjAddressR3(pSession->GipMapObjR3);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Get physical address.
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync * Reference globally.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * GIP starts/resumes updating again. On windows we bump the
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * host timer frequency to make sure we don't get stuck in guest
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * mode and to get better timer (and possibly clock) accuracy.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync supdrvGipRequestHigherTimerFrequencyFromSystem(pDevExt);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * document me
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync if (pGipR0->aCPUs[0].u32TransactionId != 2 /* not the first time */)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync unsigned i;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ASMAtomicUoWriteU32(&pGipR0->aCPUs[i].u32TransactionId,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync (pGipR0->aCPUs[i].u32TransactionId + GIP_UPDATEHZ_RECALC_FREQ * 2)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ASMAtomicWriteU64(&pGipR0->u64NanoTSLastUpdateHz, 0);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * document me
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync u64NanoTS = RTTimeSystemNanoTS() - pGipR0->u32UpdateIntervalNS;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync supdrvGipReInitCpu(pGipR0, &pGipR0->aCPUs[0], u64NanoTS);
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync RTMpOnAll(supdrvGipReInitCpuCallback, pGipR0, &u64NanoTS);
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync * Detect alternative ways to figure the CPU ID in ring-3 and
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync * raw-mode context. Check the sanity of the APIC IDs, CPU IDs,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * and CPU set indexes while we're at it.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync RT_BZERO((void *)&DetectState.bmApicId, sizeof(DetectState.bmApicId));
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync rc = RTMpOnAll(supdrvGipDetectGetGipCpuCallback, &DetectState, pGipR0);
36fbf6dcd3e6b2e5891456b730577ff0eb355c9fvboxsync LogRel(("SUPR0GipMap: fGetGipCpu=%#x\n", DetectState.fSupported));
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync LogRel(("SUPR0GipMap: No supported ways of getting the APIC ID or CPU number in ring-3! (%#x)\n",
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync LogRel(("SUPR0GipMap: APIC ID, CPU ID or CPU set index problem detected on CPU #%u (%#x)!\n",
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync DetectState.idCpuProblem, DetectState.idCpuProblem));
36fbf6dcd3e6b2e5891456b730577ff0eb355c9fvboxsync * Start the GIP timer if all is well..
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync rc = RTTimerStart(pDevExt->pGipTimer, 0 /* fire ASAP */); AssertRC(rc);
36fbf6dcd3e6b2e5891456b730577ff0eb355c9fvboxsync * Bail out on error.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync int rc2 = RTR0MemObjFree(pSession->GipMapObjR3, false); AssertRC(rc2);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Write returns.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync OSDBGPRINT(("SUPR0GipMap: returns %d *pHCPhysGip=%lx pGipR3=%p\n", rc, (unsigned long)HCPhys, (void *)pGipR3));
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync LogFlow(( "SUPR0GipMap: returns %d *pHCPhysGip=%lx pGipR3=%p\n", rc, (unsigned long)HCPhys, (void *)pGipR3));
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync * Unmaps any user mapping of the GIP and terminates all GIP access
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * from this session.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @returns IPRT status code.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pSession Session to which the GIP mapping should belong.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncSUPR0DECL(int) SUPR0GipUnmap(PSUPDRVSESSION pSession)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync OSDBGPRINT(("SUPR0GipUnmap: pSession=%p pGip=%p GipMapObjR3=%p\n",
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync pSession->GipMapObjR3 != NIL_RTR0MEMOBJ ? RTR0MemObjAddress(pSession->GipMapObjR3) : NULL,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync LogFlow(("SUPR0GipUnmap: pSession=%p\n", pSession));
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync AssertReturn(SUP_IS_SESSION_VALID(pSession), VERR_INVALID_PARAMETER);
3614117c1132a61599e6190939e775cafe549411vboxsync RTSemMutexRequest(pDevExt->mtxGip, RT_INDEFINITE_WAIT);
3614117c1132a61599e6190939e775cafe549411vboxsync * Unmap anything?
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Dereference global GIP.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync LogFlow(("SUPR0GipUnmap: Suspends GIP updating\n"));
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync rc = RTTimerStop(pDevExt->pGipTimer); AssertRC(rc); rc = VINF_SUCCESS;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync supdrvGipReleaseHigherTimerFrequencyFromSystem(pDevExt);
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * Gets the GIP pointer.
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * @returns Pointer to the GIP or NULL.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * GIP Initialization, Termination and CPU Offline / Online Related Code.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * GIP Initialization, Termination and CPU Offline / Online Related Code.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * GIP Initialization, Termination and CPU Offline / Online Related Code.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Used by supdrvInitRefineInvariantTscFreqTimer and supdrvGipInitMeasureTscFreq
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * to update the TSC frequency related GIP variables.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pGip The GIP.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param nsElapsed The number of nano seconds elapsed.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param cElapsedTscTicks The corresponding number of TSC ticks.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param iTick The tick number for debugging.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic void supdrvGipInitSetCpuFreq(PSUPGLOBALINFOPAGE pGip, uint64_t nsElapsed, uint64_t cElapsedTscTicks, uint32_t iTick)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Calculate the frequency.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uCpuHz = ASMMultU64ByU32DivByU32(cElapsedTscTicks, RT_NS_1SEC, (uint32_t)nsElapsed);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync RTUInt128MulU64ByU64(&Tmp, cElapsedTscTicks, RT_NS_1SEC_64);
9540eeb13face31ddc1c5f15338556fe46f18a77vboxsync RTUInt128Div(&CpuHz, &Tmp, RTUInt128AssignU64(&Divisor, nsElapsed));
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Update the GIP.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ASMAtomicWriteU64(&pGip->aCPUs[0].u64CpuHz, uCpuHz);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* For inspecting the frequency calcs using tstGIP-2, debugger or similar. */
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync ASMAtomicWriteU64(&pGip->aCPUs[iTick + 1].u64CpuHz, uCpuHz);
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * Timer callback function for TSC frequency refinement in invariant GIP mode.
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * This is started during driver init and fires once
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * GIP_TSC_REFINE_PERIOD_IN_SECS seconds later.
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * @param pTimer The timer.
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * @param pvUser Opaque pointer to the device instance data.
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * @param iTick The timer tick.
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsyncstatic DECLCALLBACK(void) supdrvInitRefineInvariantTscFreqTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync /* Paranoia. */
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync AssertReturnVoid(pGip->u32Mode == SUPGIPMODE_INVARIANT_TSC);
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * If we got a power event, stop the refinement process.
fc81313f4240cea1b6db5eaf81f6b5aac0ad5840vboxsync * Try get close to the next clock tick as usual.
fc81313f4240cea1b6db5eaf81f6b5aac0ad5840vboxsync * PORTME: If timers are called from the clock interrupt handler, or
fc81313f4240cea1b6db5eaf81f6b5aac0ad5840vboxsync * an interrupt handler with higher priority than the clock
fc81313f4240cea1b6db5eaf81f6b5aac0ad5840vboxsync * interrupt, or spinning for ages in timer handlers is frowned
fc81313f4240cea1b6db5eaf81f6b5aac0ad5840vboxsync * upon, this loop must be disabled!
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync * Darwin, FreeBSD, Linux, Solaris, Windows 8.1+:
fc81313f4240cea1b6db5eaf81f6b5aac0ad5840vboxsync * High RTTimeSystemNanoTS resolution should prevent any noticable
fc81313f4240cea1b6db5eaf81f6b5aac0ad5840vboxsync * spinning her.
fc81313f4240cea1b6db5eaf81f6b5aac0ad5840vboxsync * Windows 8.0 and earlier:
fc81313f4240cea1b6db5eaf81f6b5aac0ad5840vboxsync * We're running in a DPC here, so we may trigger the DPC watchdog?
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * Timer callbacks are done in the clock interrupt, so skip it.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync cNsElapsed = nsNow - pDevExt->nsStartInvarTscRefine;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync cTscTicksElapsed = uTsc - pDevExt->uTscStartInvarTscRefine;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * If the above measurement was taken on a different CPU than the one we
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * started the process on, cTscTicksElapsed will need to be adjusted with
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * the TSC deltas of both the CPUs.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * We ASSUME that the delta calculation process takes less time than the
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * TSC frequency refinement timer. If it doesn't, we'll complain and
07f038fb9d3d3080465ba0ba2065a4208e53e0f6vboxsync * drop the frequency refinement.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Note! We cannot entirely trust enmUseTscDelta here because it's
9540eeb13face31ddc1c5f15338556fe46f18a77vboxsync * downgraded after each delta calculation.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync && pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t iStartCpuSet = RTMpCpuIdToSetIndex(pDevExt->idCpuInvarTscRefine);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint16_t iStartGipCpu = iStartCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ? pGip->aiCpuFromCpuSetIdx[iStartCpuSet] : UINT16_MAX;
07f038fb9d3d3080465ba0ba2065a4208e53e0f6vboxsync uint16_t iStopGipCpu = iStopCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ? pGip->aiCpuFromCpuSetIdx[iStopCpuSet] : UINT16_MAX;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync int64_t iStartTscDelta = iStartGipCpu < pGip->cCpus ? pGip->aCPUs[iStartGipCpu].i64TSCDelta : INT64_MAX;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync int64_t iStopTscDelta = iStopGipCpu < pGip->cCpus ? pGip->aCPUs[iStopGipCpu].i64TSCDelta : INT64_MAX;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (RT_LIKELY(iStartTscDelta != INT64_MAX && iStopTscDelta != INT64_MAX))
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync if (pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_PRACTICALLY_ZERO)
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync /* cTscTicksElapsed = (uTsc - iStopTscDelta) - (pDevExt->uTscStartInvarTscRefine - iStartTscDelta); */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync cTscTicksElapsed += iStartTscDelta - iStopTscDelta;
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * Allow 5 times the refinement period to elapse before we give up on the TSC delta
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * calculations.
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync else if (cNsElapsed > GIP_TSC_REFINE_PERIOD_IN_SECS * 5 * RT_NS_1SEC_64)
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync SUPR0Printf("vboxdrv: Failed to refine invariant TSC frequency because deltas are unavailable after %u (%u) seconds\n",
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync (uint32_t)(cNsElapsed / RT_NS_1SEC), GIP_TSC_REFINE_PERIOD_IN_SECS);
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync SUPR0Printf("vboxdrv: start: %u, %u, %#llx stop: %u, %u, %#llx\n",
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync iStartCpuSet, iStartGipCpu, iStartTscDelta, iStopCpuSet, iStopGipCpu, iStopTscDelta);
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * Calculate and update the CPU frequency variables in GIP.
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * If there is a GIP user already and we've already refined the frequency
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * a couple of times, don't update it as we want a stable frequency value
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * for all VMs.
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync supdrvGipInitSetCpuFreq(pGip, cNsElapsed, cTscTicksElapsed, iTick);
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync * Stop the timer once we've reached the defined refinement period.
ac6445a70a26cb69d08734f1d9dbc171cec86cd8vboxsync if (cNsElapsed > GIP_TSC_REFINE_PERIOD_IN_SECS * RT_NS_1SEC_64)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @callback_method_impl{FNRTPOWERNOTIFICATION}
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic DECLCALLBACK(void) supdrvGipPowerNotificationCallback(RTPOWEREVENT enmEvent, void *pvUser)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * If the TSC frequency refinement timer we need to cancel it so it doesn't screw
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * up the frequency after a long suspend.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ASMAtomicWriteBool(&pDevExt->fInvTscRefinePowerEvent, true);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Start the TSC-frequency refinment timer for the invariant TSC GIP mode.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * We cannot use this in the synchronous and asynchronous tsc GIP modes because
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * the CPU may change the TSC frequence between now and when the timer fires
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * (supdrvInitAsyncRefineTscTimer).
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pDevExt Pointer to the device instance data.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pGip Pointer to the GIP.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic void supdrvGipInitStartTimerForRefiningInvariantTscFreq(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Register a power management callback.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync rc = RTPowerNotificationRegister(supdrvGipPowerNotificationCallback, pDevExt);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Record the TSC and NanoTS as the starting anchor point for refinement
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * of the TSC. We try get as close to a clock tick as possible on systems
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * which does not provide high resolution time.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync pDevExt->nsStartInvarTscRefine = RTTimeSystemNanoTS();
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** @todo we need a power management callback that disables the timer if the
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Create a timer that runs on the same CPU so we won't have a depencency
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * on the TSC-delta and can run in parallel to it. On systems that does not
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * implement CPU specific timers we'll apply deltas in the timer callback,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * just like we do for CPUs going offline.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * The longer the refinement interval the better the accuracy, at least in
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * theory. If it's too long though, ring-3 may already be starting its
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * first VMs before we're done. On most systems we will be loading the
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * support driver during boot and VMs won't be started for a while yet,
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync * it is really only a problem during development (especially with
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * on-demand driver starting on windows).
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * To avoid wasting time doing a long supdrvGipInitMeasureTscFreq() call
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * to calculate the frequency during driver loading, the timer is set
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * to fire after 200 ms the first time. It will then reschedule itself
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * to fire every second until GIP_TSC_REFINE_PERIOD_IN_SECS has been
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * reached or it notices that there is a user land client with GIP
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * mapped (we want a stable frequency for all VMs).
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync rc = RTTimerCreateEx(&pDevExt->pInvarTscRefineTimer, RT_NS_1SEC,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync RTTIMER_FLAGS_CPU(RTMpCpuIdToSetIndex(pDevExt->idCpuInvarTscRefine)),
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync rc = RTTimerStart(pDevExt->pInvarTscRefineTimer, 2*RT_NS_100MS);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (rc == VERR_CPU_OFFLINE || rc == VERR_NOT_SUPPORTED)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync rc = RTTimerCreateEx(&pDevExt->pInvarTscRefineTimer, RT_NS_1SEC, RTTIMER_FLAGS_CPU_ANY,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync rc = RTTimerStart(pDevExt->pInvarTscRefineTimer, 2*RT_NS_100MS);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync OSDBGPRINT(("vboxdrv: Failed to create or start TSC frequency refinement timer: rc=%Rrc\n", rc));
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @callback_method_impl{PFNRTMPWORKER,
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * RTMpOnSpecific callback for reading TSC and time on the CPU we started
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * the measurements on.}
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncDECLCALLBACK(void) supdrvGipInitReadTscAndNanoTsOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync * Measures the TSC frequency of the system.
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync * The TSC frequency can vary on systems which are not reported as invariant.
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync * On such systems the object of this function is to find out what the nominal,
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync * maximum TSC frequency under 'normal' CPU operation.
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync * @returns VBox status code.
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync * @param pDevExt Pointer to the device instance.
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync * @param pGip Pointer to the GIP.
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync * @param fRough Set if we're doing the rough calculation that the
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync * TSC measuring code needs, where accuracy isn't all
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync * that important (too high is better than to low).
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync * When clear we try for best accuracy that we can
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync * achieve in reasonably short time.
af5224eb6b6676bc892a3f5abeb21f602547d31cvboxsyncstatic int supdrvGipInitMeasureTscFreq(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip, bool fRough)
9b7ab382b3f9667e8847020e1e58f7143c4d2334vboxsync uint32_t nsTimerIncr = RTTimerGetSystemGranularity();
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync while (cTriesLeft-- > 0)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Synchronize with the host OS clock tick on systems without high
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * resolution time API (older Windows version for example).
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Read the TSC and current time, noting which CPU we're on.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Delay for a while.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Sleep-wait since the TSC frequency is constant, it eases host load.
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync * Shorter interval produces more variance in the frequency (esp. Windows).
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint64_t msDelay = ( ((fRough ? 16 : 200) * RT_NS_1MS + nsTimerIncr - 1) / nsTimerIncr * nsTimerIncr - RT_NS_100US )
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync RTThreadSleep((RTMSINTERVAL)(msDelay - msElapsed));
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Busy-wait keeping the frequency up.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Read the TSC and time again.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * If the CPU changes things get a bit complicated and what we
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * can get away with depends on the GIP mode / TSC reliablity.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync bool fDoXCall = false;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Synchronous TSC mode: we're probably fine as it's unlikely
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * that we were rescheduled because of TSC throttling or power
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * management reasons, so just go ahead.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* Probably ok, maybe we should retry once?. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync Assert(pGip->enmUseTscDelta == SUPGIPUSETSCDELTA_NOT_APPLICABLE);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * If we're just doing the rough measurement, do the cross call and
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * get on with things (we don't have deltas!).
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Invariant TSC mode: It doesn't matter if we have delta available
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * for both CPUs. That is not something we can assume at this point.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Note! We cannot necessarily trust enmUseTscDelta here because it's
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * downgraded after each delta calculation and the delta
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * calculations may not be complete yet.
762a68c2bb3ccde807330e3d1cb05f8b244a5f72vboxsync else if (pGip->u32Mode == SUPGIPMODE_INVARIANT_TSC)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync/** @todo This section of code is never reached atm, consider dropping it later on... */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint32_t iStartCpuSet = RTMpCpuIdToSetIndex(idCpuStart);
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync uint32_t iStopCpuSet = RTMpCpuIdToSetIndex(idCpuStop);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint16_t iStartGipCpu = iStartCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync ? pGip->aiCpuFromCpuSetIdx[iStartCpuSet] : UINT16_MAX;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync uint16_t iStopGipCpu = iStopCpuSet < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ? pGip->aiCpuFromCpuSetIdx[iStopCpuSet] : UINT16_MAX;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync int64_t iStartTscDelta = iStartGipCpu < pGip->cCpus ? pGip->aCPUs[iStartGipCpu].i64TSCDelta : INT64_MAX;
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync int64_t iStopTscDelta = iStopGipCpu < pGip->cCpus ? pGip->aCPUs[iStopGipCpu].i64TSCDelta : INT64_MAX;
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync if (RT_LIKELY(iStartTscDelta != INT64_MAX && iStopTscDelta != INT64_MAX))
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_PRACTICALLY_ZERO)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Invalid CPU indexes are not caused by online/offline races, so
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * we have to trigger driver load failure if that happens as GIP
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * and IPRT assumptions are busted on this system.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync else if (iStopGipCpu >= pGip->cCpus || iStartGipCpu >= pGip->cCpus)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync SUPR0Printf("vboxdrv: Unexpected CPU index in supdrvGipInitMeasureTscFreq.\n");
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync SUPR0Printf("vboxdrv: start: %u, %u, %#llx stop: %u, %u, %#llx\n",
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync iStartCpuSet, iStartGipCpu, iStartTscDelta, iStopCpuSet, iStopGipCpu, iStopTscDelta);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * No valid deltas. We retry, if we're on our last retry
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * we do the cross call instead just to get a result. The
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * frequency will be refined in a few seconds anyways.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync else if (cTriesLeft > 0)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Asynchronous TSC mode: This is bad as the reason we usually
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * use this mode is to deal with variable TSC frequencies and
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * deltas. So, we need to get the TSC from the same CPU as
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * started it, we also need to keep that CPU busy. So, retry
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * and fall back to the cross call on the last attempt.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Try read the TSC and timestamp on the start CPU.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync int rc = RTMpOnSpecific(idCpuStart, supdrvGipInitReadTscAndNanoTsOnCpu, &uTscStop, &nsStop);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Calculate the TSC frequency and update it (shared with the refinement timer).
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync supdrvGipInitSetCpuFreq(pGip, nsStop - nsStart, uTscStop - uTscStart, 0);
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync * Finds our (@a idCpu) entry, or allocates a new one if not found.
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync * @returns Index of the CPU in the cache set.
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync * @param pGip The GIP.
23603ed361f22874964e3a841add2c58ec2bb1eavboxsync * @param idCpu The CPU ID.
23603ed361f22874964e3a841add2c58ec2bb1eavboxsyncstatic uint32_t supdrvGipFindOrAllocCpuIndexForCpuId(PSUPGLOBALINFOPAGE pGip, RTCPUID idCpu)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * ASSUMES that CPU IDs are constant.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ASMAtomicCmpXchgSize(&pGip->aCPUs[i].idCpu, idCpu, NIL_RTCPUID, fRc);
fe554d9c0e3a6de4ba221610ac95a44c7d288e01vboxsync return i - 1;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * The calling CPU should be accounted as online, update GIP accordingly.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * This is used by supdrvGipCreate() as well as supdrvGipMpEvent().
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pDevExt The device extension.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param idCpu The CPU ID.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic void supdrvGipMpEventOnlineOrInitOnCpu(PSUPDRVDEVEXT pDevExt, RTCPUID idCpu)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Do this behind a spinlock with interrupts disabled as this can fire
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * on all CPUs simultaneously, see @bugref{6110}.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Update the globals.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ASMAtomicWriteU16(&pGip->cPresentCpus, RTMpGetPresentCount());
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ASMAtomicWriteU16(&pGip->cOnlineCpus, RTMpGetOnlineCount());
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync Assert(RTCpuSetIsMemberByIndex(&pGip->PossibleCpuSet, iCpuSet));
9540eeb13face31ddc1c5f15338556fe46f18a77vboxsync * Update the entry.
9540eeb13face31ddc1c5f15338556fe46f18a77vboxsync u64NanoTS = RTTimeSystemNanoTS() - pGip->u32UpdateIntervalNS;
9540eeb13face31ddc1c5f15338556fe46f18a77vboxsync i = supdrvGipFindOrAllocCpuIndexForCpuId(pGip, idCpu);
9540eeb13face31ddc1c5f15338556fe46f18a77vboxsync supdrvGipInitCpu(pGip, &pGip->aCPUs[i], u64NanoTS, pGip->u64CpuHz);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ASMAtomicWriteS16(&pGip->aCPUs[i].iCpuSet, (int16_t)iCpuSet);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Update the APIC ID and CPU set index mappings.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ASMAtomicWriteU16(&pGip->aiCpuFromApicId[idApic], i);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ASMAtomicWriteU16(&pGip->aiCpuFromCpuSetIdx[iCpuSet], i);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* Add this CPU to this set of CPUs we need to calculate the TSC-delta for. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync RTCpuSetAddByIndex(&pDevExt->TscDeltaCpuSet, RTMpCpuIdToSetIndex(idCpu));
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* Update the Mp online/offline counter. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* Commit it. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ASMAtomicWriteSize(&pGip->aCPUs[i].enmState, SUPGIPCPUSTATE_ONLINE);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * RTMpOnSpecific callback wrapper for supdrvGipMpEventOnlineOrInitOnCpu().
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param idCpu The CPU ID we are running on.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pvUser1 Opaque pointer to the device instance data.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pvUser2 Not used.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic DECLCALLBACK(void) supdrvGipMpEventOnlineCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * The CPU should be accounted as offline, update the GIP accordingly.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * This is used by supdrvGipMpEvent.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pDevExt The device extension.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param idCpu The CPU ID.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic void supdrvGipMpEventOffline(PSUPDRVDEVEXT pDevExt, RTCPUID idCpu)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync unsigned i;
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync Assert(RTCpuSetIsMemberByIndex(&pGip->PossibleCpuSet, iCpuSet));
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* Update the Mp online/offline counter. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync if (pGip->enmUseTscDelta > SUPGIPUSETSCDELTA_ZERO_CLAIMED)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* Reset the TSC delta, we will recalculate it lazily. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ASMAtomicWriteS64(&pGip->aCPUs[i].i64TSCDelta, INT64_MAX);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* Remove this CPU from the set of CPUs that we have obtained the TSC deltas. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync RTCpuSetDelByIndex(&pDevExt->TscDeltaObtainedCpuSet, iCpuSet);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync /* Commit it. */
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync ASMAtomicWriteSize(&pGip->aCPUs[i].enmState, SUPGIPCPUSTATE_OFFLINE);
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * Multiprocessor event notification callback.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * This is used to make sure that the GIP master gets passed on to
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * another CPU. It also updates the associated CPU data.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param enmEvent The event.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param idCpu The cpu it applies to.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync * @param pvUser Pointer to the device extension.
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsyncstatic DECLCALLBACK(void) supdrvGipMpEvent(RTMPEVENT enmEvent, RTCPUID idCpu, void *pvUser)
016096e367cd20c3d3c3fd9a6650b55935c2e31dvboxsync RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
#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)
rc = RTMpOnSpecific(RTMpCpuIdFromSetIndex(iCpu), supdrvGipInitDetermineAsyncTscWorker, &CurTsc, NULL);
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;
} M1;
#ifdef GIP_TSC_DELTA_METHOD_2
bool fLagMaster;
bool fLagWorker;
bool volatile fQuitEarly;
} M2;
#ifdef TSCDELTA_VERIFY_WITH_STATS
# 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)))
ASMNopPause();
#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)))
if (fIsMaster)
if (RT_LIKELY(ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_GO, GIP_TSC_DELTA_SYNC2_STEADY)))
ASMNopPause();
if (!fIsMaster)
if (RT_LIKELY(ASMAtomicCmpXchgU32(&pOtherSync->uSyncVar, GIP_TSC_DELTA_SYNC2_GO, GIP_TSC_DELTA_SYNC2_STEADY)))
ASMNopPause();
ASMNopPause();
ASMNopPause();
static bool supdrvTscDeltaSync2_After(PSUPTSCDELTASYNC2 pMySync, PSUPTSCDELTASYNC2 pOtherSync, RTCCUINTREG fEFlags)
ASMNopPause();
ASMNopPause();
if (!ASMAtomicCmpXchgU32(&(a_pOtherSync)->uSyncVar, GIP_TSC_DELTA_SYNC2_READY, GIP_TSC_DELTA_SYNC2_GO)) \
if (!ASMAtomicCmpXchgU32(&(a_pOtherSync)->uSyncVar, GIP_TSC_DELTA_SYNC2_READY, GIP_TSC_DELTA_SYNC2_GO)) \
#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));
if (!fIsMaster)
return VINF_SUCCESS;
#ifdef GIP_TSC_DELTA_METHOD_2
cHits++;
/*SUPR0Printf("quitting early #1: hits=%#x iLoop=%d iBestDelta=%lld\n", cHits, iLoop, iBestDelta);*/
uint32_t const cHitsNeeded = GIP_TSC_DELTA_M2_LOOPS * RT_ELEMENTS(pArgs->M2.pMasterData->aResults) / 4; /* 25% */
pArgs->M2.cHits, cHits, cHitsNeeded, iLoop, iBestDelta);*/
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 (fIsMaster)
# if GIP_TSC_DELTA_M2_PRIMER_LOOPS > 0
supdrvTscDeltaMethod2CollectData(pArgs->M2.pMasterData, &pArgs->M2.pWorkerData->iCurSeqNo, pArgs->M2.fLagMaster);
# if GIP_TSC_DELTA_M2_PRIMER_LOOPS > 0
supdrvTscDeltaMethod2CollectData(pArgs->M2.pWorkerData, &pArgs->M2.pMasterData->iCurSeqNo, pArgs->M2.fLagWorker);
return rc;
SUPR0Printf("cHits=%d m=%d w=%d\n", pArgs->M2.cHits, pArgs->pMaster->idApic, pArgs->pWorker->idApic);
uint32_t i;
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)
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)
#ifdef GIP_TSC_DELTA_METHOD_1
if (fIsMaster)
iTry = 0;
iTry++;
if ( iTry == 0
ASMNopPause();
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 GIP_TSC_DELTA_METHOD_1
#ifdef GIP_TSC_DELTA_METHOD_1
#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;