/* * hpet.c: HPET emulation for HVM guests. * Copyright (c) 2006, Intel Corporation. * Copyright (c) 2006, Keir Fraser * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307 USA. */ #include #include #include #include #include #include #define domain_vhpet(d) (&(d)->arch.hvm_domain.pl_time.vhpet) #define vcpu_vhpet(vcpu) (domain_vhpet((vcpu)->domain)) #define vhpet_domain(hpet) (container_of((hpet), struct domain, \ arch.hvm_domain.pl_time.vhpet)) #define vhpet_vcpu(hpet) (pt_global_vcpu_target(vhpet_domain(hpet))) #define HPET_BASE_ADDRESS 0xfed00000ULL #define HPET_MMAP_SIZE 1024 #define S_TO_NS 1000000000ULL /* 1s = 10^9 ns */ #define S_TO_FS 1000000000000000ULL /* 1s = 10^15 fs */ /* Frequency_of_Xen_systeme_time / frequency_of_HPET = 16 */ #define STIME_PER_HPET_TICK 16 #define guest_time_hpet(hpet) \ (hvm_get_guest_time(vhpet_vcpu(hpet)) / STIME_PER_HPET_TICK) #define HPET_ID 0x000 #define HPET_PERIOD 0x004 #define HPET_CFG 0x010 #define HPET_STATUS 0x020 #define HPET_COUNTER 0x0f0 #define HPET_T0_CFG 0x100 #define HPET_T0_CMP 0x108 #define HPET_T0_ROUTE 0x110 #define HPET_T1_CFG 0x120 #define HPET_T1_CMP 0x128 #define HPET_T1_ROUTE 0x130 #define HPET_T2_CFG 0x140 #define HPET_T2_CMP 0x148 #define HPET_T2_ROUTE 0x150 #define HPET_T3_CFG 0x160 #define HPET_CFG_ENABLE 0x001 #define HPET_CFG_LEGACY 0x002 #define HPET_TN_INT_TYPE_LEVEL 0x002 #define HPET_TN_ENABLE 0x004 #define HPET_TN_PERIODIC 0x008 #define HPET_TN_PERIODIC_CAP 0x010 #define HPET_TN_SIZE_CAP 0x020 #define HPET_TN_SETVAL 0x040 #define HPET_TN_32BIT 0x100 #define HPET_TN_INT_ROUTE_MASK 0x3e00 #define HPET_TN_INT_ROUTE_SHIFT 9 #define HPET_TN_INT_ROUTE_CAP_SHIFT 32 #define HPET_TN_CFG_BITS_READONLY_OR_RESERVED 0xffff80b1U /* can be routed to IOAPIC.redirect_table[23..20] */ #define HPET_TN_INT_ROUTE_CAP (0x00f00000ULL \ << HPET_TN_INT_ROUTE_CAP_SHIFT) #define HPET_TN_INT_ROUTE_CAP_MASK (0xffffffffULL \ << HPET_TN_INT_ROUTE_CAP_SHIFT) #define hpet_tick_to_ns(h, tick) \ ((s_time_t)((((tick) > (h)->hpet_to_ns_limit) ? \ ~0ULL : (tick) * (h)->hpet_to_ns_scale) >> 10)) #define timer_config(h, n) (h->hpet.timers[n].config) #define timer_enabled(h, n) (timer_config(h, n) & HPET_TN_ENABLE) #define timer_is_periodic(h, n) (timer_config(h, n) & HPET_TN_PERIODIC) #define timer_is_32bit(h, n) (timer_config(h, n) & HPET_TN_32BIT) #define hpet_enabled(h) (h->hpet.config & HPET_CFG_ENABLE) #define timer_level(h, n) (timer_config(h, n) & HPET_TN_INT_TYPE_LEVEL) #define timer_int_route(h, n) \ ((timer_config(h, n) & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT) #define timer_int_route_cap(h, n) \ ((timer_config(h, n) & HPET_TN_INT_ROUTE_CAP_MASK) \ >> HPET_TN_INT_ROUTE_CAP_SHIFT) static inline uint64_t hpet_read_maincounter(HPETState *h) { ASSERT(spin_is_locked(&h->lock)); if ( hpet_enabled(h) ) return guest_time_hpet(h) + h->mc_offset; else return h->hpet.mc64; } static uint64_t hpet_get_comparator(HPETState *h, unsigned int tn) { uint64_t comparator; uint64_t elapsed; comparator = h->hpet.comparator64[tn]; if ( timer_is_periodic(h, tn) ) { /* update comparator by number of periods elapsed since last update */ uint64_t period = h->hpet.period[tn]; if (period) { elapsed = hpet_read_maincounter(h) + period - 1 - comparator; comparator += (elapsed / period) * period; h->hpet.comparator64[tn] = comparator; } } /* truncate if timer is in 32 bit mode */ if ( timer_is_32bit(h, tn) ) comparator = (uint32_t)comparator; h->hpet.timers[tn].cmp = comparator; return comparator; } static inline uint64_t hpet_read64(HPETState *h, unsigned long addr) { addr &= ~7; switch ( addr ) { case HPET_ID: return h->hpet.capability; case HPET_CFG: return h->hpet.config; case HPET_STATUS: return h->hpet.isr; case HPET_COUNTER: return hpet_read_maincounter(h); case HPET_T0_CFG: case HPET_T1_CFG: case HPET_T2_CFG: return h->hpet.timers[(addr - HPET_T0_CFG) >> 5].config; case HPET_T0_CMP: case HPET_T1_CMP: case HPET_T2_CMP: return hpet_get_comparator(h, (addr - HPET_T0_CMP) >> 5); case HPET_T0_ROUTE: case HPET_T1_ROUTE: case HPET_T2_ROUTE: return h->hpet.timers[(addr - HPET_T0_ROUTE) >> 5].fsb; } return 0; } static inline int hpet_check_access_length( unsigned long addr, unsigned long len) { if ( (addr & (len - 1)) || (len > 8) ) { /* * According to ICH9 specification, unaligned accesses may result * in unexpected behaviour or master abort, but should not crash/hang. * Hence we read all-ones, drop writes, and log a warning. */ gdprintk(XENLOG_WARNING, "HPET: access across register boundary: " "%lx %lx\n", addr, len); return -EINVAL; } return 0; } static int hpet_read( struct vcpu *v, unsigned long addr, unsigned long length, unsigned long *pval) { HPETState *h = vcpu_vhpet(v); unsigned long result; uint64_t val; addr &= HPET_MMAP_SIZE-1; if ( hpet_check_access_length(addr, length) != 0 ) { result = ~0ul; goto out; } spin_lock(&h->lock); val = hpet_read64(h, addr); result = val; if ( length != 8 ) result = (val >> ((addr & 7) * 8)) & ((1ULL << (length * 8)) - 1); spin_unlock(&h->lock); out: *pval = result; return X86EMUL_OKAY; } static void hpet_stop_timer(HPETState *h, unsigned int tn) { ASSERT(tn < HPET_TIMER_NUM); ASSERT(spin_is_locked(&h->lock)); destroy_periodic_time(&h->pt[tn]); /* read the comparator to get it updated so a read while stopped will * return the expected value. */ hpet_get_comparator(h, tn); } /* the number of HPET tick that stands for * 1/(2^10) second, namely, 0.9765625 milliseconds */ #define HPET_TINY_TIME_SPAN ((h->stime_freq >> 10) / STIME_PER_HPET_TICK) static void hpet_set_timer(HPETState *h, unsigned int tn) { uint64_t tn_cmp, cur_tick, diff; unsigned int irq; unsigned int oneshot; ASSERT(tn < HPET_TIMER_NUM); ASSERT(spin_is_locked(&h->lock)); if ( (tn == 0) && (h->hpet.config & HPET_CFG_LEGACY) ) { /* HPET specification requires PIT shouldn't generate * interrupts if LegacyReplacementRoute is set
/**
 * \addtogroup timer
 * @{
 */

/**
 * \file
 * Timer library implementation.
 * \author
 * Adam Dunkels <adam@sics.se>
 */

/*
 * Copyright (c) 2004, Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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.
 *
 * This file is part of the uIP TCP/IP stack
 *
 * Author: Adam Dunkels <adam@sics.se>
 *
 * $Id: timer.c,v 1.2 2006/06/12 08:00:30 adam Exp $
 */

#include "clock.h"
#include "timer.h"

/*---------------------------------------------------------------------------*/
/**
 * Set a timer.
 *
 * This function is used to set a timer for a time sometime in the
 * future. The function timer_expired() will evaluate to true after
 * the timer has expired.
 *
 * \param t A pointer to the timer
 * \param interval The interval before the timer expires.
 *
 */
void
timer_set(struct timer *t, clock_time_t interval)
{
  t->interval = interval;
  t->start = clock_time();
}
/*---------------------------------------------------------------------------*/
/**
 * Reset the timer with the same interval.
 *
 * This function resets the timer with the same interval that was
 * given to the timer_set() function. The start point of the interval
 * is the exact time that the timer last expired. Therefore, this
 * function will cause the timer to be stable over time, unlike the
 * timer_restart() function.
 *
 * \param t A pointer to the timer.
 *
 * \sa timer_restart()
 */
void
timer_reset(struct timer *t)
{
  t->start += t->interval;
}
/*---------------------------------------------------------------------------*/
/**
 * Restart the timer from the current point in time
 *
 * This function restarts a timer with the same interval that was
 * given to the timer_set() function. The timer will start at the
 * current time.
 *
 * \note A periodic timer will drift if this function is used to reset
 * it. For periodic timers, use the timer_reset() function instead.
 *
 * \param t A pointer to the timer.
 *
 * \sa timer_reset()
 */
void
timer_restart(struct timer *t)
{
  t->start = clock_time();
}
/*---------------------------------------------------------------------------*/
/**
 * Check if a timer has expired.
 *
 * This function tests if a timer has expired and returns true or
 * false depending on its status.
 *
 * \param t A pointer to the timer
 *
 * \return Non-zero if the timer has expired, zero otherwise.
 *
 */
int
timer_expired(struct timer *t)
{
  return (clock_time_t)(clock_time() - t->start) >= (clock_time_t)t->interval;
}
/*---------------------------------------------------------------------------*/

/** @} */