/* $Id$ */
/** @file
* IPRT - Time using SUPLib, the C Code Template.
*/
/*
* Copyright (C) 2006-2015 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/**
* The C reference implementation of the assembly routines.
*
* Calculate NanoTS using the information in the global information page (GIP)
* which the support library (SUPLib) exports.
*
* This function guarantees that the returned timestamp is later (in time) than
* any previous calls in the same thread.
*
* @remark The way the ever increasing time guarantee is currently implemented means
* that if you call this function at a frequency higher than 1GHz you're in for
* trouble. We currently assume that no idiot will do that for real life purposes.
*
* @returns Nanosecond timestamp.
* @param pData Pointer to the data structure.
*/
{
#endif
for (;;)
{
#ifndef IN_RING3 /* This simplifies and improves everything. */
#endif
/*
* Check that the GIP is sane and that the premises for this worker function
* hasn't changed (CPU onlined with bad delta or missing features).
*/
#else
#endif
#endif
)
{
/*
* Resolve pGipCpu if needed. If the instruction is serializing, we
* read the transaction id first if possible.
*/
# if defined(IN_RING0)
# if TMPL_MODE != TMPL_MODE_ASYNC
# endif
# if TMPL_MODE != TMPL_MODE_ASYNC
# endif
# else
# error "What?"
# endif
{
#else
{
#endif
/*
* Get the transaction ID if necessary and we haven't already
* read it before a serializing instruction above. We can skip
* this for ASYNC_TSC mode in ring-0 and raw-mode context since
* we disable interrupts.
*/
#endif
/*
* Gather all the data we need. The mess at the end is to make
* sure all loads are done before we recheck the transaction ID
* without triggering serializing twice.
*/
#if TMPL_MODE == TMPL_MODE_ASYNC
#else
# endif
#endif
#else
TMPL_READ_FENCE(); /* Expensive (~30 ticks). Would like convincing argumentation that let us remove it. */
# endif
#endif
/*
* Check that we didn't change CPU.
*/
#if defined(IN_RING3) && ( TMPL_MODE == TMPL_MODE_ASYNC || TMPL_MODE == TMPL_MODE_SYNC_INVAR_WITH_DELTA )
# endif
#endif
{
/*
*/
# if TMPL_MODE == TMPL_MODE_ASYNC
# else
# endif
#endif
{
/*
* Apply the TSC delta. If the delta is invalid and the
* execution allows it, try trigger delta recalibration.
*/
#endif
{
# ifndef IN_RING3
# endif
u64Delta -= i64TscDelta;
#endif
/*
* Bingo! We've got a consistent set of data.
*/
#ifndef IN_RING3
#endif
/*
* Calc NanoTS delta.
*/
{ /* MSVC branch hint, probably pointless. */ }
else
{
/*
* We've expired the interval, cap it. If we're here for the 2nd
* time without any GIP update in-between, the checks against
* *pu64Prev below will force 1ns stepping.
*/
}
#if !defined(_MSC_VER) || !defined(RT_ARCH_X86) /* GCC makes very pretty code from these two inline calls, while MSC cannot. */
#else
{
}
#endif
/*
* Calculate the time and compare it with the previously returned value.
*/
if (RT_LIKELY( u64DeltaPrev > 0
{ /* Frequent - less than 24h since last call. */ }
{
/* Occasional - u64NanoTS is in the recent 'past' relative the previous call. */
}
else if (!u64PrevNanoTS)
/* We're resuming (see TMVirtualResume). */;
else
{
/* Something has gone bust, if negative offset it's real bad. */
}
/*
* Attempt updating the previous value, provided we're still ahead of it.
*
* There is no point in recalculating u64NanoTS because we got preempted or if
* we raced somebody while the GIP was updated, since these are events
* that might occur at any point in the return path as well.
*/
return u64NanoTS;
{
if (u64PrevNanoTS >= u64NanoTS)
break;
break;
ASMNopPause();
}
return u64NanoTS;
}
/*
* Call into the support driver to try make it recalculate the delta. We
* remember which GIP CPU structure we're probably working on so we won't
* end up in a loop if the driver for some reason cannot get the job done.
*/
else /* else is unecessary, but helps checking the preprocessor spaghetti. */
{
{
}
}
#endif
}
}
/*
* No joy must try again.
*/
#ifndef IN_RING3
#endif
ASMNopPause();
continue;
}
/*
* We've got a bad CPU or APIC index of some kind.
*/
else /* else is unecessary, but helps checking the preprocessor spaghetti. */
{
# ifndef IN_RING3
# endif
# else
# endif
}
#endif
}
/*
* Something changed in the GIP config or it was unmapped, figure out
* the right worker function to use now.
*/
#ifndef IN_RING3
#endif
}
}