SUPDrvGip.cpp revision b0deb52d71d46987430324c2e39de46a2791a738
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * VBoxDrv - The VirtualBox Support Driver - Common code for GIP.
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * Copyright (C) 2006-2015 Oracle Corporation
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * available from http://www.virtualbox.org. This file is free software;
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * you can redistribute it and/or modify it under the terms of the GNU
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * General Public License (GPL) as published by the Free Software
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * The contents of this file may alternatively be used under the terms
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * of the Common Development and Distribution License Version 1.0
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * VirtualBox OSE distribution, in which case the provisions of the
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * CDDL are applicable instead of those of the GPL.
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * You may elect to license modified versions of this file under the
5f2b03bf7695dabd71222dba123532a3f76828c1vboxsync * terms and conditions of either the GPL or the CDDL or both.
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync/*******************************************************************************
9523921c89c66f4bececdbd5ac95aed0039eda1bvboxsync* Header Files *
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync*******************************************************************************/
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsync#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync/*******************************************************************************
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync* Defined Constants And Macros *
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync*******************************************************************************/
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync/** The frequency by which we recalculate the u32UpdateHz and
6b9d50a0f466bd5a61458ed53925480ab28a3c17vboxsync * u32UpdateIntervalNS GIP members. The value must be a power of 2.
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync * Warning: Bumping this too high might overflow u32UpdateIntervalNS.
88cc9bf61296bc5526344415167bb2625ae1dd99vboxsync/** A reserved TSC value used for synchronization as well as measurement of
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync * TSC deltas. */
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync/** The number of TSC delta measurement loops in total (includes primer and
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync * read-time loops). */
6b9d50a0f466bd5a61458ed53925480ab28a3c17vboxsync/** The number of cache primer loops. */
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync/** The number of loops until we keep computing the minumum read time. */
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync/** @name Master / worker synchronization values.
88cc9bf61296bc5526344415167bb2625ae1dd99vboxsync/** Stop measurement of TSC delta. */
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync/** Start measurement of TSC delta. */
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync/** Worker thread is ready for reading the TSC. */
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync/** Worker thread is done updating TSC delta info. */
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync/** When IPRT is isn't concurrent safe: Master is ready and will wait for worker
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync * with a timeout. */
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync#define GIP_TSC_DELTA_SYNC_PRESTART_MASTER UINT32_C(4)
6b9d50a0f466bd5a61458ed53925480ab28a3c17vboxsync/** When IPRT is isn't concurrent safe: Worker is ready after waiting for
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync * master with a timeout. */
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync/** The TSC-refinement interval in seconds. */
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync/** The TSC-delta threshold for the SUPGIPUSETSCDELTA_PRACTICALLY_ZERO rating */
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync/** The TSC-delta threshold for the SUPGIPUSETSCDELTA_ROUGHLY_ZERO rating */
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync/** The TSC delta value for the initial GIP master - 0 in regular builds.
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync * To test the delta code this can be set to a non-zero value. */
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync# define GIP_TSC_DELTA_INITIAL_MASTER_VALUE INT64_C(170139095182512) /* 0x00009abd9854acb0 */
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync# define GIP_TSC_DELTA_INITIAL_MASTER_VALUE INT64_C(0)
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsyncAssertCompile(GIP_TSC_DELTA_PRIMER_LOOPS < GIP_TSC_DELTA_READ_TIME_LOOPS);
88cc9bf61296bc5526344415167bb2625ae1dd99vboxsyncAssertCompile(GIP_TSC_DELTA_PRIMER_LOOPS + GIP_TSC_DELTA_READ_TIME_LOOPS < GIP_TSC_DELTA_LOOPS);
88cc9bf61296bc5526344415167bb2625ae1dd99vboxsync/** @def VBOX_SVN_REV
88cc9bf61296bc5526344415167bb2625ae1dd99vboxsync * The makefile should define this if it can. */
88cc9bf61296bc5526344415167bb2625ae1dd99vboxsync#if 0 /* Don't start the GIP timers. Useful when debugging the IPRT timer code. */
88cc9bf61296bc5526344415167bb2625ae1dd99vboxsync/*******************************************************************************
88cc9bf61296bc5526344415167bb2625ae1dd99vboxsync* Internal Functions *
88cc9bf61296bc5526344415167bb2625ae1dd99vboxsync*******************************************************************************/
88cc9bf61296bc5526344415167bb2625ae1dd99vboxsyncstatic DECLCALLBACK(void) supdrvGipSyncAndInvariantTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
88cc9bf61296bc5526344415167bb2625ae1dd99vboxsyncstatic DECLCALLBACK(void) supdrvGipAsyncTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
88cc9bf61296bc5526344415167bb2625ae1dd99vboxsyncstatic void supdrvGipInitCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pCpu, uint64_t u64NanoTS, uint64_t uCpuHz);
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsyncstatic int supdrvTscDeltaThreadInit(PSUPDRVDEVEXT pDevExt);
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsyncstatic void supdrvTscDeltaTerm(PSUPDRVDEVEXT pDevExt);
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsyncstatic void supdrvTscDeltaThreadStartMeasurement(PSUPDRVDEVEXT pDevExt);
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsyncstatic int supdrvMeasureInitialTscDeltas(PSUPDRVDEVEXT pDevExt);
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsyncstatic int supdrvMeasureTscDeltaOne(PSUPDRVDEVEXT pDevExt, uint32_t idxWorker);
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsync/*******************************************************************************
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsync* Global Variables *
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsync*******************************************************************************/
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsyncDECLEXPORT(PSUPGLOBALINFOPAGE) g_pSUPGlobalInfoPage = NULL;
7c3bf57b1f6df237b1075e7cae6b188db6fc636avboxsync * Misc Common GIP Code
7c3bf57b1f6df237b1075e7cae6b188db6fc636avboxsync * Misc Common GIP Code
7c3bf57b1f6df237b1075e7cae6b188db6fc636avboxsync * Misc Common GIP Code
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsync * Finds the GIP CPU index corresponding to @a idCpu.
7c3bf57b1f6df237b1075e7cae6b188db6fc636avboxsync * @returns GIP CPU array index, UINT32_MAX if not found.
7c3bf57b1f6df237b1075e7cae6b188db6fc636avboxsync * @param pGip The GIP.
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsync * @param idCpu The CPU ID.
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsyncstatic uint32_t supdrvGipFindCpuIndexForCpuId(PSUPGLOBALINFOPAGE pGip, RTCPUID idCpu)
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsync * GIP Mapping and Unmapping Related Code.
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsync * GIP Mapping and Unmapping Related Code.
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsync * GIP Mapping and Unmapping Related Code.
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsync * (Re-)initializes the per-cpu structure prior to starting or resuming the GIP
88cc9bf61296bc5526344415167bb2625ae1dd99vboxsync * updating.
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsync * @param pGip Pointer to the GIP.
88cc9bf61296bc5526344415167bb2625ae1dd99vboxsync * @param pGipCpu The per CPU structure for this CPU.
cd059a6642b11828bd0ad8b3108f5f7f611d144fvboxsync * @param u64NanoTS The current time.
88cc9bf61296bc5526344415167bb2625ae1dd99vboxsyncstatic void supdrvGipReInitCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pGipCpu, uint64_t u64NanoTS)
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * Here we don't really care about applying the TSC delta. The re-initialization of this
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * value is not relevant especially while (re)starting the GIP as the first few ones will
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * be ignored anyway, see supdrvGipDoUpdateCpu().
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync pGipCpu->u64TSC = ASMReadTSC() - pGipCpu->u32UpdateIntervalTSC;
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * Set the current TSC and NanoTS value for the CPU.
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * @param idCpu The CPU ID. Unused - we have to use the APIC ID.
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * @param pvUser1 Pointer to the ring-0 GIP mapping.
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * @param pvUser2 Pointer to the variable holding the current time.
687794577e2e35c3cae67e692a7f2130d1262a82vboxsyncstatic DECLCALLBACK(void) supdrvGipReInitCpuCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync PSUPGLOBALINFOPAGE pGip = (PSUPGLOBALINFOPAGE)pvUser1;
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync unsigned iCpu = pGip->aiCpuFromApicId[ASMGetApicId()];
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync if (RT_LIKELY(iCpu < pGip->cCpus && pGip->aCPUs[iCpu].idCpu == idCpu))
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync supdrvGipReInitCpu(pGip, &pGip->aCPUs[iCpu], *(uint64_t *)pvUser2);
06ea6bcf23874b662d499b3f130024c98b2dd7a6vboxsync * State structure for supdrvGipDetectGetGipCpuCallback.
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync /** Bitmap of APIC IDs that has been seen (initialized to zero).
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * Used to detect duplicate APIC IDs (paranoia). */
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync /** Mask of supported GIP CPU getter methods (SUPGIPGETCPU_XXX) (all bits set
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * initially). The callback clears the methods not detected. */
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync /** The first callback detecting any kind of range issues (initialized to
0fd108a555ae02f2fb557d5f2c40281999b60d15vboxsync * NIL_RTCPUID). */
9523921c89c66f4bececdbd5ac95aed0039eda1bvboxsync/** Pointer to state structure for supdrvGipDetectGetGipCpuCallback. */
8bc8d66f188d5357155b8340e2d489573be2b607vboxsynctypedef SUPDRVGIPDETECTGETCPU *PSUPDRVGIPDETECTGETCPU;
06ea6bcf23874b662d499b3f130024c98b2dd7a6vboxsync * Checks for alternative ways of getting the CPU ID.
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync * This also checks the APIC ID, CPU ID and CPU set index values against the
702a8ee2dc1de96f2f77e97135015d3e243186fdvboxsync * GIP tables.
9523921c89c66f4bececdbd5ac95aed0039eda1bvboxsync * @param idCpu The CPU ID. Unused - we have to use the APIC ID.
92e624e40b06b4dc6d0a8222e1de33bd3e879a63vboxsync * @param pvUser1 Pointer to the state structure.
9523921c89c66f4bececdbd5ac95aed0039eda1bvboxsync * @param pvUser2 Pointer to the GIP.
9523921c89c66f4bececdbd5ac95aed0039eda1bvboxsyncstatic DECLCALLBACK(void) supdrvGipDetectGetGipCpuCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
9523921c89c66f4bececdbd5ac95aed0039eda1bvboxsync PSUPDRVGIPDETECTGETCPU pState = (PSUPDRVGIPDETECTGETCPU)pvUser1;
06ea6bcf23874b662d499b3f130024c98b2dd7a6vboxsync PSUPGLOBALINFOPAGE pGip = (PSUPGLOBALINFOPAGE)pvUser2;
06ea6bcf23874b662d499b3f130024c98b2dd7a6vboxsync AssertMsg(idCpu == RTMpCpuId(), ("idCpu=%#x RTMpCpuId()=%#x\n", idCpu, RTMpCpuId())); /* paranoia^3 */
d4a9d525e6f2111d462d2d96462dced6b9ec00efvboxsync * Check that the CPU ID and CPU set index are interchangable.
9523921c89c66f4bececdbd5ac95aed0039eda1bvboxsync AssertCompile(RT_IS_POWER_OF_TWO(RTCPUSET_MAX_CPUS));
7862f4bd000f1eb6c86289f5ac2849e9cf943ca9vboxsync * Check whether the IDTR.LIMIT contains a CPU number.
cc1ef2ef9bbc6a0ff964928d61b7298e5bfcce5fvboxsync uint16_t const cbIdt = sizeof(X86DESC64SYSTEM) * 256;
04f6f18325971f796623469adcf39ba2b2939ed3vboxsync fSupported |= SUPGIPGETCPU_IDTR_LIMIT_MASK_MAX_SET_CPUS;
e378dfdadd62aadc0a012c9953322d979d7606e6vboxsync * Check whether RDTSCP is an option.
40c1a23e86c79b24a917a43c186b2e54504d12c1vboxsync if ( ASMIsValidExtRange(ASMCpuId_EAX(UINT32_C(0x80000000)))
04f6f18325971f796623469adcf39ba2b2939ed3vboxsync && (ASMCpuId_EDX(UINT32_C(0x80000001)) & X86_CPUID_EXT_FEATURE_EDX_RDTSCP) )
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync fSupported |= SUPGIPGETCPU_RDTSCP_MASK_MAX_SET_CPUS;
02f7c106d33c45f99ec412a5fe0adde868f700fcvboxsync * Check that the APIC ID is unique.
d4a9d525e6f2111d462d2d96462dced6b9ec00efvboxsync if (RT_LIKELY( idApic < RT_ELEMENTS(pGip->aiCpuFromApicId)
d4a9d525e6f2111d462d2d96462dced6b9ec00efvboxsync && !ASMAtomicBitTestAndSet(pState->bmApicId, idApic)))
5f2b03bf7695dabd71222dba123532a3f76828c1vboxsync AssertCompile(sizeof(pState->bmApicId) * 8 == RT_ELEMENTS(pGip->aiCpuFromApicId));
d4a9d525e6f2111d462d2d96462dced6b9ec00efvboxsync ASMAtomicCmpXchgU32(&pState->idCpuProblem, idCpu, NIL_RTCPUID);
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x - duplicate APIC ID.\n",
02f7c106d33c45f99ec412a5fe0adde868f700fcvboxsync * Check that the iCpuSet is within the expected range.
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync || (unsigned)iCpuSet >= RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx)))
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync ASMAtomicCmpXchgU32(&pState->idCpuProblem, idCpu, NIL_RTCPUID);
02f7c106d33c45f99ec412a5fe0adde868f700fcvboxsync LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x - CPU set index is out of range.\n",
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync ASMAtomicCmpXchgU32(&pState->idCpuProblem, idCpu, NIL_RTCPUID);
2f3883b126a405f92b19e829472f614c7352b4f9vboxsync LogRel(("supdrvGipDetectGetGipCpuCallback: idCpu=%#x iCpuSet=%d idApic=%#x - CPU id/index roundtrip problem: %#x\n",
int rc;
#ifdef SUPDRV_USE_MUTEX_FOR_GIP
if (ppGipR3)
#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)
#if !defined(RT_OS_OS2)
ASMNopPause();
int64_t iStartTscDelta = iStartGipCpu < pGip->cCpus ? pGip->aCPUs[iStartGipCpu].i64TSCDelta : INT64_MAX;
int64_t iStopTscDelta = iStopGipCpu < pGip->cCpus ? pGip->aCPUs[iStopGipCpu].i64TSCDelta : INT64_MAX;
/* cTscTicksElapsed = (uTsc - iStopTscDelta) - (pDevExt->uTscStartInvarTscRefine - iStartTscDelta); */
SUPR0Printf("vboxdrv: Failed to refine invariant TSC frequency because deltas are unavailable after %u (%u) seconds\n",
static void supdrvGipInitStartTimerForRefiningInvariantTscFreq(PSUPDRVDEVEXT pDevExt, PSUPGLOBALINFOPAGE pGip)
int rc;
ASMNopPause();
while (cTriesLeft-- > 0)
ASMNopPause();
uint64_t msDelay = ( ((fRough ? 16 : 200) * RT_NS_1MS + nsTimerIncr - 1) / nsTimerIncr * nsTimerIncr - RT_NS_100US )
/ RT_NS_1MS;
ASMNopPause();
ASMNopPause();
bool fDoXCall = false;
else if (fRough)
fDoXCall = true;
int64_t iStartTscDelta = iStartGipCpu < pGip->cCpus ? pGip->aCPUs[iStartGipCpu].i64TSCDelta : INT64_MAX;
int64_t iStopTscDelta = iStopGipCpu < pGip->cCpus ? pGip->aCPUs[iStopGipCpu].i64TSCDelta : INT64_MAX;
return VERR_INVALID_CPU_INDEX;
else if (cTriesLeft > 0)
fDoXCall = true;
if (cTriesLeft > 0)
fDoXCall = true;
if (fDoXCall)
return VINF_SUCCESS;
cTries = 0;
bool fRc;
if (fRc)
int iCpuSet = 0;
uint32_t i = 0;
if (iCpuSet >= 0)
static DECLCALLBACK(void) supdrvGipMpEventOnlineCallback(RTCPUID idCpu, void *pvUser1, void *pvUser2)
int iCpuSet;
if (pGip)
switch (enmEvent)
case RTMPEVENT_ONLINE:
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
case RTMPEVENT_OFFLINE:
bool fIgnored;
for (i = 0; i < RTCPUSET_MAX_CPUS; i++)
static DECLCALLBACK(void) supdrvGipInitDetermineAsyncTscWorker(RTCPUID idCpu, void *pvUser1, void *pvUser2)
int iCpu;
bool fAsync = false;
while (cLoops-- > 0)
fAsync = true;
if (poffMin)
return fAsync;
bool fInvariantTsc = false;
if (ASMHasCpuId())
fInvariantTsc = true;
return SUPGIPMODE_ASYNC_TSC;
if (fInvariantTsc)
return SUPGIPMODE_INVARIANT_TSC;
return SUPGIPMODE_ASYNC_TSC;
return SUPGIPMODE_ASYNC_TSC;
return SUPGIPMODE_SYNC_TSC;
static void supdrvGipInitCpu(PSUPGLOBALINFOPAGE pGip, PSUPGIPCPU pCpu, uint64_t u64NanoTS, uint64_t uCpuHz)
if (!uCpuHz)
#ifdef DEBUG_DARWIN_GIP
OSDBGPRINT(("supdrvGipInit: pGip=%p HCPhys=%lx u64NanoTS=%llu uUpdateHz=%d cCpus=%u\n", pGip, (long)HCPhys, u64NanoTS, uUpdateHz, cCpus));
LogFlow(("supdrvGipInit: pGip=%p HCPhys=%lx u64NanoTS=%llu uUpdateHz=%d cCpus=%u\n", pGip, (long)HCPhys, u64NanoTS, uUpdateHz, cCpus));
for (i = 0; i < cCpus; i++)
unsigned cCpus;
int rc;
#ifdef SUPDRV_USE_MUTEX_FOR_GIP
SUPR0Printf("VBoxDrv: Too many CPUs (%u) for the GIP (max %u)\n", cCpus, RT_MIN(RTCPUSET_MAX_CPUS, 256));
return VERR_TOO_MANY_CPUS;
rc = RTR0MemObjAllocCont(&pDevExt->GipMemObj, RT_UOFFSETOF(SUPGLOBALINFOPAGE, aCPUs[cCpus]), false /*fExecutable*/);
return rc;
if (uMod)
supdrvGipInit(pDevExt, pGip, HCPhysGip, RTTimeSystemNanoTS(), RT_NS_1SEC / u32Interval /*=Hz*/, u32Interval, cCpus);
/* Basically, invariant Windows boxes, should never be detected as async (i.e. TSC-deltas should be 0). */
OSDBGPRINT(("supdrvGipCreate: The TSC-deltas should be normalized by the host OS, but verifying shows it's not!\n"));
return VERR_INTERNAL_ERROR_2;
* RTMpOnAll/supdrvGipInitOnCpu call below).
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
} while (--cTries > 0);
AssertMsg(!pGip->aCPUs[iCpu].i64TSCDelta, ("iCpu=%u %lld mode=%d\n", iCpu, pGip->aCPUs[iCpu].i64TSCDelta, pGip->u32Mode));
return VINF_SUCCESS;
OSDBGPRINT(("supdrvGipCreate: failed create GIP timer at %u ns interval. rc=%Rrc\n", u32Interval, rc));
return rc;
int rc;
#ifdef DEBUG_DARWIN_GIP
#ifdef SUPDRV_USE_TSC_DELTA_THREAD
static void supdrvGipDoUpdateCpu(PSUPDRVDEVEXT pDevExt, PSUPGIPCPU pGipCpu, uint64_t u64NanoTS, uint64_t u64TSC, uint64_t iTick)
unsigned iTSCHistoryHead;
ASMAtomicWriteU32(&pGipCpu->u32UpdateIntervalTSC, u32UpdateIntervalTSC + u32UpdateIntervalTSCSlack);
static void supdrvGipUpdate(PSUPDRVDEVEXT pDevExt, uint64_t u64NanoTS, uint64_t u64TSC, RTCPUID idCpu, uint64_t iTick)
/* this can happen on win32 if we're taking to long and there are more CPUs around. shouldn't happen though. */
if ( pGip->u32Mode != SUPGIPMODE_INVARIANT_TSC /* cuz we're not recalculating the frequency on invariants hosts. */
static DECLCALLBACK(void) supdrvGipSyncAndInvariantTimer(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
* Solaris timers fire on the CPU they were registered/started on.
# define GIP_TSC_DELTA_METHOD_1
# define GIP_TSC_DELTA_METHOD_2
typedef struct SUPDRVTSCDELTAMETHOD2ENTRY
typedef struct SUPDRVTSCDELTAMETHOD2
typedef struct SUPTSCDELTASYNC2
typedef struct SUPDRVGIPTSCDELTARGS
bool volatile fAbortSetup;
} M1;
#ifdef GIP_TSC_DELTA_METHOD_2
bool fLagMaster;
bool fLagWorker;
} M2;
bool volatile fTimedOut;
#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)))
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)
return VINF_SUCCESS;
#ifdef GIP_TSC_DELTA_METHOD_2
# define GIP_TSC_DELTA_M2_PRIMER_LOOPS 0
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->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)
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 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;