/* * Copyright (c) 2012 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall * not be construed as granting a license to any other intellectual * property including but not limited to intellectual property relating * to a hardware implementation of the functionality of the software * licensed hereunder. You may use the software subject to the license * terms below provided that you ensure that this notice is replicated * unmodified and in its entirety in all distributions of the software, * modified or unmodified, in source code or in binary form. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Andreas Sandberg */ #ifndef __CPU_KVM_TIMER_HH__ #define __CPU_KVM_TIMER_HH__ #include #include "cpu/kvm/perfevent.hh" #include "sim/core.hh" /** * Timer functions to interrupt VM execution after a number of * simulation ticks. The timer allows scaling of the host time to take * performance differences between the simulated and real CPU into * account. * * The performance scaling factor is ratio between the target's CPI * and the host's CPI. It is larger than 1 if the host is faster than * the target and lower than 1 if it is slower. * * When the timer times out, it sends a signal to the thread that * started the timer. The signal forces KVM to drop out of the system * call that started the guest and hands control to gem5. */ class BaseKvmTimer { public: /** * Setup basic timer functionality shared by all timer * implementations. * * @param signo Signal to deliver * @param hostFactor Performance scaling factor * @param hostFreq Clock frequency of the host */ BaseKvmTimer(int signo, float hostFactor, Tick hostFreq) : signo(signo), _resolution(0), hostFactor(hostFactor), hostFreq(hostFreq) {}; virtual ~BaseKvmTimer() {}; /** * Arm the timer so that it fires after a certain number of ticks. * * @note A timer implementation is free to convert between * simulation ticks and virtualized time using any method it * chooses. The accuracy of the timer therefore depends on what it * measures, an accurate timer implementation should measure the * number of cycles or instructions executed in the guest. If such * counters are unavailable, it may fallback to wall clock time. * * @param ticks Number of ticks until the timer fires */ virtual void arm(Tick ticks) = 0; /** * Disarm the timer. * * When this method has returned, the timer may no longer deliver * signals upon timeout. */ virtual void disarm() = 0; /** * Determine the resolution of the timer in ticks. This method is * mainly used to determine the smallest number of ticks the timer * can wait before triggering a signal. * * @return Minimum number of ticks the timer can resolve */ Tick resolution() { if (_resolution == 0) _resolution = calcResolution(); return _resolution; } /** * Convert cycles executed on the host into Ticks executed in the * simulator. Scales the results using the hostFactor to take CPU * performance differences into account. * * @return Host cycles executed in VM converted to simulation ticks */ Tick ticksFromHostCycles(uint64_t cycles) { return cycles * hostFactor * hostFreq; } /** * Convert nanoseconds executed on the host into Ticks executed in * the simulator. Scales the results using the hostFactor to take * CPU performance differences into account. * * @return Nanoseconds executed in VM converted to simulation ticks */ Tick ticksFromHostNs(uint64_t ns) { return ns * hostFactor * SimClock::Float::ns; } protected: /** * Calculate the timer resolution, used by resolution() which * caches the result. * * @return Minimum number of ticks the timer can resolve */ virtual Tick calcResolution() = 0; /** * Convert a time in simulator ticks to host nanoseconds. * * @return Simulation ticks converted into nanoseconds on the host */ uint64_t hostNs(Tick ticks) { return ticks / (SimClock::Float::ns * hostFactor); } /** * Convert a time in simulator ticks to host cycles * * * @return Simulation ticks converted into CPU cycles on the host */ uint64_t hostCycles(Tick ticks) { return ticks / (hostFreq * hostFactor); } /** Signal to deliver when the timer times out */ int signo; private: /** Cached resolution */ mutable Tick _resolution; /** Performance scaling factor */ float hostFactor; /** Host frequency */ Tick hostFreq; }; /** * Timer based on standard POSIX timers. The POSIX timer API supports * several different clock with different characteristics. * * @note It might be tempting to use * CLOCK_(THREAD|PROCESS)_CPUTIME_ID, however, this clock usually has * much lower resolution than the real-time clocks. */ class PosixKvmTimer : public BaseKvmTimer { public: /** * @param signo Signal to deliver * @param clockID ID of the clock to use * @param hostFactor Performance scaling factor * @param hostFreq Clock frequency of the host */ PosixKvmTimer(int signo, clockid_t clockID, float hostFactor, Tick hostFreq); ~PosixKvmTimer(); void arm(Tick ticks); void disarm(); protected: Tick calcResolution(); private: clockid_t clockID; timer_t timer; }; /** * PerfEvent based timer using the host's CPU cycle counter. * * @warning There is a known problem in some versions of the PerfEvent * API that prevents the counter overflow period from being updated * reliably, which might break this timer. See PerfKvmCounter::period() * for details. */ class PerfKvmTimer : public BaseKvmTimer { public: /** * Create a timer that uses an existing hardware cycle counter. * * @note The performance counter must be configured for overflow * sampling, which in practice means that it must have a non-zero * sample period. The initial sample period is ignored since * period will be updated when arm() is called. * * @param ctr Attached performance counter configured for overflow * reporting. * @param signo Signal to deliver * @param hostFactor Performance scaling factor * @param hostFreq Clock frequency of the host */ PerfKvmTimer(PerfKvmCounter &ctr, int signo, float hostFactor, Tick hostFreq); ~PerfKvmTimer(); void arm(Tick ticks); void disarm(); protected: Tick calcResolution(); private: PerfKvmCounter &hwOverflow; }; #endif