TMAllCpu.cpp revision 12c79195f773855bfca3ba75211cb2d3a1568605
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync/* $Id$ */
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync/** @file
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * TM - Timeout Manager, CPU Time, All Contexts.
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync */
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync/*
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * Copyright (C) 2006-2012 Oracle Corporation
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync *
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * available from http://www.virtualbox.org. This file is free software;
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * you can redistribute it and/or modify it under the terms of the GNU
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * General Public License (GPL) as published by the Free Software
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync */
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync/*******************************************************************************
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync* Header Files *
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync*******************************************************************************/
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync#define LOG_GROUP LOG_GROUP_TM
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync#include <VBox/vmm/tm.h>
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync#include <iprt/asm-amd64-x86.h> /* for SUPGetCpuHzFromGIP */
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync#include "TMInternal.h"
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync#include <VBox/vmm/vm.h>
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync#include <VBox/vmm/gim.h>
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync#include <VBox/sup.h>
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync#include <VBox/param.h>
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync#include <VBox/err.h>
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync#include <iprt/asm-math.h>
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync#include <iprt/assert.h>
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync#include <VBox/log.h>
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync/**
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * Gets the raw cpu tick from current virtual time.
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync */
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsyncDECLINLINE(uint64_t) tmCpuTickGetRawVirtual(PVM pVM, bool fCheckTimers)
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync{
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync uint64_t u64;
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync if (fCheckTimers)
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync u64 = TMVirtualSyncGet(pVM);
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync else
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync u64 = TMVirtualSyncGetNoCheck(pVM);
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync if (u64 != TMCLOCK_FREQ_VIRTUAL) /* what's the use of this test, document! */
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync u64 = ASMMultU64ByU32DivByU32(u64, pVM->tm.s.cTSCTicksPerSecond, TMCLOCK_FREQ_VIRTUAL);
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync return u64;
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync}
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync
14ebfaa1c57c3239ff757d0034253ff19647b276vboxsync/**
14ebfaa1c57c3239ff757d0034253ff19647b276vboxsync * Resumes the CPU timestamp counter ticking.
14ebfaa1c57c3239ff757d0034253ff19647b276vboxsync *
14ebfaa1c57c3239ff757d0034253ff19647b276vboxsync * @returns VBox status code.
14ebfaa1c57c3239ff757d0034253ff19647b276vboxsync * @param pVM Pointer to the VM.
14ebfaa1c57c3239ff757d0034253ff19647b276vboxsync * @param pVCpu Pointer to the VMCPU.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * @internal
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync */
14ebfaa1c57c3239ff757d0034253ff19647b276vboxsyncint tmCpuTickResume(PVM pVM, PVMCPU pVCpu)
14ebfaa1c57c3239ff757d0034253ff19647b276vboxsync{
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync if (!pVCpu->tm.s.fTSCTicking)
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync {
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync pVCpu->tm.s.fTSCTicking = true;
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync if (pVM->tm.s.fTSCVirtualized)
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync {
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync /** @todo Test that pausing and resuming doesn't cause lag! (I.e. that we're
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * unpaused before the virtual time and stopped after it. */
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync if (pVM->tm.s.fTSCUseRealTSC)
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync pVCpu->tm.s.offTSCRawSrc = ASMReadTSC() - pVCpu->tm.s.u64TSC;
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync else
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync pVCpu->tm.s.offTSCRawSrc = tmCpuTickGetRawVirtual(pVM, false /* don't check for pending timers */)
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync - pVCpu->tm.s.u64TSC;
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync }
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync return VINF_SUCCESS;
eeed648eeade937e46949231b47fa35b1c7829e2vboxsync }
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync AssertFailed();
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync return VERR_TM_TSC_ALREADY_TICKING;
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync}
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync/**
7dfb99974324af3e0dbbae9902d016927e113925vboxsync * Resumes the CPU timestamp counter ticking.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync *
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * @returns VINF_SUCCESS or VERR_TM_VIRTUAL_TICKING_IPE (asserted).
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * @param pVM Pointer to the VM.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync * @param pVCpu Pointer to the VCPU.
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync */
7dfb99974324af3e0dbbae9902d016927e113925vboxsyncint tmCpuTickResumeLocked(PVM pVM, PVMCPU pVCpu)
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync{
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync if (!pVCpu->tm.s.fTSCTicking)
7dfb99974324af3e0dbbae9902d016927e113925vboxsync {
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync /* TSC must be ticking before calling tmCpuTickGetRawVirtual()! */
7dfb99974324af3e0dbbae9902d016927e113925vboxsync pVCpu->tm.s.fTSCTicking = true;
7dfb99974324af3e0dbbae9902d016927e113925vboxsync if (pVM->tm.s.fTSCVirtualized)
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync {
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync uint32_t c = ASMAtomicIncU32(&pVM->tm.s.cTSCsTicking);
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync AssertMsgReturn(c <= pVM->cCpus, ("%u vs %u\n", c, pVM->cCpus), VERR_TM_VIRTUAL_TICKING_IPE);
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync if (c == 1)
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync {
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync /* The first VCPU to resume. */
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync uint64_t offTSCRawSrcOld = pVCpu->tm.s.offTSCRawSrc;
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync
7dfb99974324af3e0dbbae9902d016927e113925vboxsync STAM_COUNTER_INC(&pVM->tm.s.StatTSCResume);
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync /* When resuming, use the TSC value of the last stopped VCPU to avoid the TSC going back. */
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync if (pVM->tm.s.fTSCUseRealTSC)
7dfb99974324af3e0dbbae9902d016927e113925vboxsync pVCpu->tm.s.offTSCRawSrc = ASMReadTSC() - pVM->tm.s.u64LastPausedTSC;
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync else
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync pVCpu->tm.s.offTSCRawSrc = tmCpuTickGetRawVirtual(pVM, false /* don't check for pending timers */)
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync - pVM->tm.s.u64LastPausedTSC;
b1d7d513c459787311cd09c440524044fa7ff8a9vboxsync
b1d7d513c459787311cd09c440524044fa7ff8a9vboxsync /* Calculate the offset for other VCPUs to use. */
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync pVM->tm.s.offTSCPause = pVCpu->tm.s.offTSCRawSrc - offTSCRawSrcOld;
b1d7d513c459787311cd09c440524044fa7ff8a9vboxsync }
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync else
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync {
7dfb99974324af3e0dbbae9902d016927e113925vboxsync /* All other VCPUs (if any). */
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync pVCpu->tm.s.offTSCRawSrc += pVM->tm.s.offTSCPause;
7dfb99974324af3e0dbbae9902d016927e113925vboxsync }
7dfb99974324af3e0dbbae9902d016927e113925vboxsync }
45c4c3d2db1a166b7b1e92960adb823960821386vboxsync }
45c4c3d2db1a166b7b1e92960adb823960821386vboxsync return VINF_SUCCESS;
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync}
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync
7dfb99974324af3e0dbbae9902d016927e113925vboxsync/**
7dfb99974324af3e0dbbae9902d016927e113925vboxsync * Pauses the CPU timestamp counter ticking.
7dfb99974324af3e0dbbae9902d016927e113925vboxsync *
7dfb99974324af3e0dbbae9902d016927e113925vboxsync * @returns VBox status code.
b1d7d513c459787311cd09c440524044fa7ff8a9vboxsync * @param pVCpu Pointer to the VMCPU.
b1d7d513c459787311cd09c440524044fa7ff8a9vboxsync * @internal
7dfb99974324af3e0dbbae9902d016927e113925vboxsync */
7dfb99974324af3e0dbbae9902d016927e113925vboxsyncint tmCpuTickPause(PVMCPU pVCpu)
7dfb99974324af3e0dbbae9902d016927e113925vboxsync{
7dfb99974324af3e0dbbae9902d016927e113925vboxsync if (pVCpu->tm.s.fTSCTicking)
7dfb99974324af3e0dbbae9902d016927e113925vboxsync {
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync pVCpu->tm.s.u64TSC = TMCpuTickGetNoCheck(pVCpu);
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync pVCpu->tm.s.fTSCTicking = false;
7dfb99974324af3e0dbbae9902d016927e113925vboxsync return VINF_SUCCESS;
45c4c3d2db1a166b7b1e92960adb823960821386vboxsync }
45c4c3d2db1a166b7b1e92960adb823960821386vboxsync AssertFailed();
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync return VERR_TM_TSC_ALREADY_PAUSED;
7dfb99974324af3e0dbbae9902d016927e113925vboxsync}
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync/**
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * Pauses the CPU timestamp counter ticking.
b1d7d513c459787311cd09c440524044fa7ff8a9vboxsync *
b1d7d513c459787311cd09c440524044fa7ff8a9vboxsync * @returns VBox status code.
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * @param pVM Pointer to the VM.
b1d7d513c459787311cd09c440524044fa7ff8a9vboxsync * @param pVCpu Pointer to the VMCPU.
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * @internal
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync */
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsyncint tmCpuTickPauseLocked(PVM pVM, PVMCPU pVCpu)
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync{
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync if (pVCpu->tm.s.fTSCTicking)
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync {
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync pVCpu->tm.s.u64TSC = TMCpuTickGetNoCheck(pVCpu);
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync pVCpu->tm.s.fTSCTicking = false;
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync uint32_t c = ASMAtomicDecU32(&pVM->tm.s.cTSCsTicking);
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync AssertMsgReturn(c < pVM->cCpus, ("%u vs %u\n", c, pVM->cCpus), VERR_TM_VIRTUAL_TICKING_IPE);
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync if (c == 0)
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync {
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync /* When the last TSC stops, remember the value. */
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync STAM_COUNTER_INC(&pVM->tm.s.StatTSCPause);
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync pVM->tm.s.u64LastPausedTSC = pVCpu->tm.s.u64TSC;
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync }
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync return VINF_SUCCESS;
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync }
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync AssertFailed();
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync return VERR_TM_TSC_ALREADY_PAUSED;
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync}
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync
b41f65ee355ae1d4b24473c6d6d70c653497429avboxsync/**
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * Record why we refused to use offsetted TSC.
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync *
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * Used by TMCpuTickCanUseRealTSC and TMCpuTickGetDeadlineAndTscOffset.
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync *
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * @param pVM Pointer to the VM.
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync * @param pVCpu The current CPU.
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync */
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsyncDECLINLINE(void) tmCpuTickRecordOffsettedTscRefusal(PVM pVM, PVMCPU pVCpu)
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync{
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync /* Sample the reason for refusing. */
5f076b675789f7dd23040096fa3fdc636bd4ea10vboxsync if (!pVM->tm.s.fMaybeUseOffsettedHostTSC)
STAM_COUNTER_INC(&pVM->tm.s.StatTSCNotFixed);
else if (!pVCpu->tm.s.fTSCTicking)
STAM_COUNTER_INC(&pVM->tm.s.StatTSCNotTicking);
else if (!pVM->tm.s.fTSCUseRealTSC)
{
if (pVM->tm.s.fVirtualSyncCatchUp)
{
if (pVM->tm.s.u32VirtualSyncCatchUpPercentage <= 10)
STAM_COUNTER_INC(&pVM->tm.s.StatTSCCatchupLE010);
else if (pVM->tm.s.u32VirtualSyncCatchUpPercentage <= 25)
STAM_COUNTER_INC(&pVM->tm.s.StatTSCCatchupLE025);
else if (pVM->tm.s.u32VirtualSyncCatchUpPercentage <= 100)
STAM_COUNTER_INC(&pVM->tm.s.StatTSCCatchupLE100);
else
STAM_COUNTER_INC(&pVM->tm.s.StatTSCCatchupOther);
}
else if (!pVM->tm.s.fVirtualSyncTicking)
STAM_COUNTER_INC(&pVM->tm.s.StatTSCSyncNotTicking);
else if (pVM->tm.s.fVirtualWarpDrive)
STAM_COUNTER_INC(&pVM->tm.s.StatTSCWarp);
}
}
/**
* Checks if AMD-V / VT-x can use an offsetted hardware TSC or not.
*
* @returns true/false accordingly.
* @param pVCpu Pointer to the VMCPU.
* @param poffRealTSC The offset against the TSC of the current CPU.
* Can be NULL.
* @param pfParavirtTsc Where to store whether paravirt. TSC can be used or
* not.
* @thread EMT(pVCpu).
*/
VMM_INT_DECL(bool) TMCpuTickCanUseRealTSC(PVMCPU pVCpu, uint64_t *poffRealTSC, bool *pfParavirtTsc)
{
PVM pVM = pVCpu->CTX_SUFF(pVM);
bool fParavirtTsc = false;
/*
* We require:
* 1. Use of a paravirtualized TSC is enabled by the guest.
* (OR)
* 1. A fixed TSC, this is checked at init time.
* 2. That the TSC is ticking (we shouldn't be here if it isn't)
* 3. Either that we're using the real TSC as time source or
* a) we don't have any lag to catch up, and
* b) the virtual sync clock hasn't been halted by an expired timer, and
* c) we're not using warp drive (accelerated virtual guest time).
*/
if ( (*pfParavirtTsc = GIMIsParavirtTscEnabled(pVM)) == true
|| ( pVM->tm.s.fMaybeUseOffsettedHostTSC
&& RT_LIKELY(pVCpu->tm.s.fTSCTicking)
&& ( pVM->tm.s.fTSCUseRealTSC
|| ( !pVM->tm.s.fVirtualSyncCatchUp
&& RT_LIKELY(pVM->tm.s.fVirtualSyncTicking)
&& !pVM->tm.s.fVirtualWarpDrive))))
{
if (!pVM->tm.s.fTSCUseRealTSC)
{
/* The source is the timer synchronous virtual clock. */
Assert(pVM->tm.s.fTSCVirtualized);
if (poffRealTSC)
{
uint64_t u64Now = tmCpuTickGetRawVirtual(pVM, false /* don't check for pending timers */)
- pVCpu->tm.s.offTSCRawSrc;
/** @todo When we start collecting statistics on how much time we spend executing
* guest code before exiting, we should check this against the next virtual sync
* timer timeout. If it's lower than the avg. length, we should trap rdtsc to increase
* the chance that we'll get interrupted right after the timer expired. */
*poffRealTSC = u64Now - ASMReadTSC();
}
}
else if (poffRealTSC)
{
/* The source is the real TSC. */
if (pVM->tm.s.fTSCVirtualized)
*poffRealTSC = pVCpu->tm.s.offTSCRawSrc;
else
*poffRealTSC = 0;
}
/** @todo count this? */
return true;
}
#ifdef VBOX_WITH_STATISTICS
tmCpuTickRecordOffsettedTscRefusal(pVM, pVCpu);
#endif
return false;
}
/**
* Calculates the number of host CPU ticks till the next virtual sync deadline.
*
* @note To save work, this function will not bother calculating the accurate
* tick count for deadlines that are more than a second ahead.
*
* @returns The number of host cpu ticks to the next deadline. Max one second.
* @param cNsToDeadline The number of nano seconds to the next virtual
* sync deadline.
*/
DECLINLINE(uint64_t) tmCpuCalcTicksToDeadline(uint64_t cNsToDeadline)
{
AssertCompile(TMCLOCK_FREQ_VIRTUAL <= _4G);
if (RT_UNLIKELY(cNsToDeadline >= TMCLOCK_FREQ_VIRTUAL))
return SUPGetCpuHzFromGIP(g_pSUPGlobalInfoPage);
uint64_t cTicks = ASMMultU64ByU32DivByU32(SUPGetCpuHzFromGIP(g_pSUPGlobalInfoPage),
cNsToDeadline,
TMCLOCK_FREQ_VIRTUAL);
if (cTicks > 4000)
cTicks -= 4000; /* fudge to account for overhead */
else
cTicks >>= 1;
return cTicks;
}
/**
* Gets the next deadline in host CPU clock ticks and the TSC offset if we can
* use the raw TSC.
*
* @returns The number of host CPU clock ticks to the next timer deadline.
* @param pVCpu The current CPU.
* @param pfParavirtTsc Where to store whether paravirt. TSC can be used or
* not.
* @param poffRealTSC The offset against the TSC of the current CPU.
*
* @thread EMT(pVCpu).
* @remarks Superset of TMCpuTickCanUseRealTSC().
*/
VMM_INT_DECL(uint64_t) TMCpuTickGetDeadlineAndTscOffset(PVMCPU pVCpu, bool *pfOffsettedTsc, bool *pfParavirtTsc,
uint64_t *poffRealTSC)
{
PVM pVM = pVCpu->CTX_SUFF(pVM);
uint64_t cTicksToDeadline;
/*
* We require:
* 1. Use of a paravirtualized TSC is enabled by the guest.
* (OR)
* 1. A fixed TSC, this is checked at init time.
* 2. That the TSC is ticking (we shouldn't be here if it isn't)
* 3. Either that we're using the real TSC as time source or
* a) we don't have any lag to catch up, and
* b) the virtual sync clock hasn't been halted by an expired timer, and
* c) we're not using warp drive (accelerated virtual guest time).
*/
if ( (*pfParavirtTsc = GIMIsParavirtTscEnabled(pVM)) == true
|| ( pVM->tm.s.fMaybeUseOffsettedHostTSC
&& RT_LIKELY(pVCpu->tm.s.fTSCTicking)
&& ( pVM->tm.s.fTSCUseRealTSC
|| ( !pVM->tm.s.fVirtualSyncCatchUp
&& RT_LIKELY(pVM->tm.s.fVirtualSyncTicking)
&& !pVM->tm.s.fVirtualWarpDrive))))
{
*pfOffsettedTsc = true;
if (!pVM->tm.s.fTSCUseRealTSC)
{
/* The source is the timer synchronous virtual clock. */
Assert(pVM->tm.s.fTSCVirtualized);
uint64_t cNsToDeadline;
uint64_t u64NowVirtSync = TMVirtualSyncGetWithDeadlineNoCheck(pVM, &cNsToDeadline);
uint64_t u64Now = u64NowVirtSync != TMCLOCK_FREQ_VIRTUAL /* what's the use of this? */
? ASMMultU64ByU32DivByU32(u64NowVirtSync, pVM->tm.s.cTSCTicksPerSecond, TMCLOCK_FREQ_VIRTUAL)
: u64NowVirtSync;
u64Now -= pVCpu->tm.s.offTSCRawSrc;
*poffRealTSC = u64Now - ASMReadTSC();
cTicksToDeadline = tmCpuCalcTicksToDeadline(cNsToDeadline);
}
else
{
/* The source is the real TSC. */
if (pVM->tm.s.fTSCVirtualized)
*poffRealTSC = pVCpu->tm.s.offTSCRawSrc;
else
*poffRealTSC = 0;
cTicksToDeadline = tmCpuCalcTicksToDeadline(TMVirtualSyncGetNsToDeadline(pVM));
}
}
else
{
#ifdef VBOX_WITH_STATISTICS
tmCpuTickRecordOffsettedTscRefusal(pVM, pVCpu);
#endif
*pfOffsettedTsc = false;
*poffRealTSC = 0;
cTicksToDeadline = tmCpuCalcTicksToDeadline(TMVirtualSyncGetNsToDeadline(pVM));
}
return cTicksToDeadline;
}
/**
* Read the current CPU timestamp counter.
*
* @returns Gets the CPU tsc.
* @param pVCpu Pointer to the VMCPU.
*/
DECLINLINE(uint64_t) tmCpuTickGetInternal(PVMCPU pVCpu, bool fCheckTimers)
{
uint64_t u64;
if (RT_LIKELY(pVCpu->tm.s.fTSCTicking))
{
PVM pVM = pVCpu->CTX_SUFF(pVM);
if (pVM->tm.s.fTSCVirtualized)
{
if (pVM->tm.s.fTSCUseRealTSC)
u64 = ASMReadTSC();
else
u64 = tmCpuTickGetRawVirtual(pVM, fCheckTimers);
u64 -= pVCpu->tm.s.offTSCRawSrc;
}
else
u64 = ASMReadTSC();
/* Always return a value higher than what the guest has already seen. */
if (RT_LIKELY(u64 > pVCpu->tm.s.u64TSCLastSeen))
pVCpu->tm.s.u64TSCLastSeen = u64;
else
{
STAM_COUNTER_INC(&pVM->tm.s.StatTSCUnderflow);
pVCpu->tm.s.u64TSCLastSeen += 64; /* @todo choose a good increment here */
u64 = pVCpu->tm.s.u64TSCLastSeen;
}
}
else
u64 = pVCpu->tm.s.u64TSC;
return u64;
}
/**
* Read the current CPU timestamp counter.
*
* @returns Gets the CPU tsc.
* @param pVCpu Pointer to the VMCPU.
*/
VMMDECL(uint64_t) TMCpuTickGet(PVMCPU pVCpu)
{
return tmCpuTickGetInternal(pVCpu, true /* fCheckTimers */);
}
/**
* Read the current CPU timestamp counter, don't check for expired timers.
*
* @returns Gets the CPU tsc.
* @param pVCpu Pointer to the VMCPU.
*/
VMM_INT_DECL(uint64_t) TMCpuTickGetNoCheck(PVMCPU pVCpu)
{
return tmCpuTickGetInternal(pVCpu, false /* fCheckTimers */);
}
/**
* Sets the current CPU timestamp counter.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
* @param pVCpu Pointer to the VMCPU.
* @param u64Tick The new timestamp value.
*
* @thread EMT which TSC is to be set.
*/
VMM_INT_DECL(int) TMCpuTickSet(PVM pVM, PVMCPU pVCpu, uint64_t u64Tick)
{
VMCPU_ASSERT_EMT(pVCpu);
STAM_COUNTER_INC(&pVM->tm.s.StatTSCSet);
/*
* This is easier to do when the TSC is paused since resume will
* do all the calculations for us. Actually, we don't need to
* call tmCpuTickPause here since we overwrite u64TSC anyway.
*/
bool fTSCTicking = pVCpu->tm.s.fTSCTicking;
pVCpu->tm.s.fTSCTicking = false;
pVCpu->tm.s.u64TSC = u64Tick;
pVCpu->tm.s.u64TSCLastSeen = u64Tick;
if (fTSCTicking)
tmCpuTickResume(pVM, pVCpu);
/** @todo Try help synchronizing it better among the virtual CPUs? */
return VINF_SUCCESS;
}
/**
* Sets the last seen CPU timestamp counter.
*
* @returns VBox status code.
* @param pVCpu Pointer to the VMCPU.
* @param u64LastSeenTick The last seen timestamp value.
*
* @thread EMT which TSC is to be set.
*/
VMM_INT_DECL(int) TMCpuTickSetLastSeen(PVMCPU pVCpu, uint64_t u64LastSeenTick)
{
VMCPU_ASSERT_EMT(pVCpu);
LogFlow(("TMCpuTickSetLastSeen %RX64\n", u64LastSeenTick));
if (pVCpu->tm.s.u64TSCLastSeen < u64LastSeenTick)
pVCpu->tm.s.u64TSCLastSeen = u64LastSeenTick;
return VINF_SUCCESS;
}
/**
* Gets the last seen CPU timestamp counter of the guest.
*
* @returns the last seen TSC.
* @param pVCpu Pointer to the VMCPU.
*
* @thread EMT(pVCpu).
*/
VMM_INT_DECL(uint64_t) TMCpuTickGetLastSeen(PVMCPU pVCpu)
{
VMCPU_ASSERT_EMT(pVCpu);
return pVCpu->tm.s.u64TSCLastSeen;
}
/**
* Get the timestamp frequency.
*
* @returns Number of ticks per second.
* @param pVM The VM.
*/
VMMDECL(uint64_t) TMCpuTicksPerSecond(PVM pVM)
{
if (pVM->tm.s.fTSCUseRealTSC)
{
uint64_t cTSCTicksPerSecond = SUPGetCpuHzFromGIP(g_pSUPGlobalInfoPage);
if (RT_LIKELY(cTSCTicksPerSecond != ~(uint64_t)0))
return cTSCTicksPerSecond;
}
return pVM->tm.s.cTSCTicksPerSecond;
}