108e3ff32SNeel Natu /*- 208e3ff32SNeel Natu * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 308e3ff32SNeel Natu * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 408e3ff32SNeel Natu * All rights reserved. 508e3ff32SNeel Natu * 608e3ff32SNeel Natu * Redistribution and use in source and binary forms, with or without 708e3ff32SNeel Natu * modification, are permitted provided that the following conditions 808e3ff32SNeel Natu * are met: 908e3ff32SNeel Natu * 1. Redistributions of source code must retain the above copyright 1008e3ff32SNeel Natu * notice, this list of conditions and the following disclaimer. 1108e3ff32SNeel Natu * 2. Redistributions in binary form must reproduce the above copyright 1208e3ff32SNeel Natu * notice, this list of conditions and the following disclaimer in the 1308e3ff32SNeel Natu * documentation and/or other materials provided with the distribution. 1408e3ff32SNeel Natu * 1508e3ff32SNeel Natu * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 1608e3ff32SNeel Natu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1708e3ff32SNeel Natu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1808e3ff32SNeel Natu * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 1908e3ff32SNeel Natu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2008e3ff32SNeel Natu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2108e3ff32SNeel Natu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2208e3ff32SNeel Natu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2308e3ff32SNeel Natu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2408e3ff32SNeel Natu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2508e3ff32SNeel Natu * SUCH DAMAGE. 2608e3ff32SNeel Natu * 2708e3ff32SNeel Natu * $FreeBSD$ 2808e3ff32SNeel Natu */ 2908e3ff32SNeel Natu 3008e3ff32SNeel Natu #include <sys/cdefs.h> 3108e3ff32SNeel Natu __FBSDID("$FreeBSD$"); 3208e3ff32SNeel Natu 3308e3ff32SNeel Natu #include <sys/param.h> 3408e3ff32SNeel Natu #include <sys/lock.h> 3508e3ff32SNeel Natu #include <sys/mutex.h> 3608e3ff32SNeel Natu #include <sys/kernel.h> 3708e3ff32SNeel Natu #include <sys/malloc.h> 3808e3ff32SNeel Natu #include <sys/systm.h> 3908e3ff32SNeel Natu 4008e3ff32SNeel Natu #include <dev/acpica/acpi_hpet.h> 4108e3ff32SNeel Natu 4208e3ff32SNeel Natu #include <machine/vmm.h> 4308e3ff32SNeel Natu #include <machine/vmm_dev.h> 4408e3ff32SNeel Natu 4508e3ff32SNeel Natu #include "vmm_lapic.h" 46762fd208STycho Nightingale #include "vatpic.h" 4708e3ff32SNeel Natu #include "vioapic.h" 4808e3ff32SNeel Natu #include "vhpet.h" 4908e3ff32SNeel Natu 5008e3ff32SNeel Natu #include "vmm_ktr.h" 5108e3ff32SNeel Natu 5208e3ff32SNeel Natu static MALLOC_DEFINE(M_VHPET, "vhpet", "bhyve virtual hpet"); 5308e3ff32SNeel Natu 5408e3ff32SNeel Natu #define HPET_FREQ 10000000 /* 10.0 Mhz */ 5508e3ff32SNeel Natu #define FS_PER_S 1000000000000000ul 5608e3ff32SNeel Natu 5708e3ff32SNeel Natu /* Timer N Configuration and Capabilities Register */ 5808e3ff32SNeel Natu #define HPET_TCAP_RO_MASK (HPET_TCAP_INT_ROUTE | \ 5908e3ff32SNeel Natu HPET_TCAP_FSB_INT_DEL | \ 6008e3ff32SNeel Natu HPET_TCAP_SIZE | \ 6108e3ff32SNeel Natu HPET_TCAP_PER_INT) 6208e3ff32SNeel Natu /* 6308e3ff32SNeel Natu * HPET requires at least 3 timers and up to 32 timers per block. 6408e3ff32SNeel Natu */ 6508e3ff32SNeel Natu #define VHPET_NUM_TIMERS 8 6608e3ff32SNeel Natu CTASSERT(VHPET_NUM_TIMERS >= 3 && VHPET_NUM_TIMERS <= 32); 6708e3ff32SNeel Natu 6808e3ff32SNeel Natu struct vhpet_callout_arg { 6908e3ff32SNeel Natu struct vhpet *vhpet; 7008e3ff32SNeel Natu int timer_num; 7108e3ff32SNeel Natu }; 7208e3ff32SNeel Natu 7308e3ff32SNeel Natu struct vhpet { 7408e3ff32SNeel Natu struct vm *vm; 7508e3ff32SNeel Natu struct mtx mtx; 7608e3ff32SNeel Natu sbintime_t freq_sbt; 7708e3ff32SNeel Natu 7808e3ff32SNeel Natu uint64_t config; /* Configuration */ 7908e3ff32SNeel Natu uint64_t isr; /* Interrupt Status */ 800a9ae358SNeel Natu uint32_t countbase; /* HPET counter base value */ 810a9ae358SNeel Natu sbintime_t countbase_sbt; /* uptime corresponding to base value */ 8208e3ff32SNeel Natu 8308e3ff32SNeel Natu struct { 8408e3ff32SNeel Natu uint64_t cap_config; /* Configuration */ 8508e3ff32SNeel Natu uint64_t msireg; /* FSB interrupt routing */ 8608e3ff32SNeel Natu uint32_t compval; /* Comparator */ 8708e3ff32SNeel Natu uint32_t comprate; 8808e3ff32SNeel Natu struct callout callout; 890a9ae358SNeel Natu sbintime_t callout_sbt; /* time when counter==compval */ 9008e3ff32SNeel Natu struct vhpet_callout_arg arg; 9108e3ff32SNeel Natu } timer[VHPET_NUM_TIMERS]; 9208e3ff32SNeel Natu }; 9308e3ff32SNeel Natu 9408e3ff32SNeel Natu #define VHPET_LOCK(vhp) mtx_lock(&((vhp)->mtx)) 9508e3ff32SNeel Natu #define VHPET_UNLOCK(vhp) mtx_unlock(&((vhp)->mtx)) 9608e3ff32SNeel Natu 970a9ae358SNeel Natu static void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, 980a9ae358SNeel Natu sbintime_t now); 990a9ae358SNeel Natu 10008e3ff32SNeel Natu static uint64_t 10108e3ff32SNeel Natu vhpet_capabilities(void) 10208e3ff32SNeel Natu { 10308e3ff32SNeel Natu uint64_t cap = 0; 10408e3ff32SNeel Natu 10508e3ff32SNeel Natu cap |= 0x8086 << 16; /* vendor id */ 10608e3ff32SNeel Natu cap |= (VHPET_NUM_TIMERS - 1) << 8; /* number of timers */ 10708e3ff32SNeel Natu cap |= 1; /* revision */ 10808e3ff32SNeel Natu cap &= ~HPET_CAP_COUNT_SIZE; /* 32-bit timer */ 10908e3ff32SNeel Natu 11008e3ff32SNeel Natu cap &= 0xffffffff; 11108e3ff32SNeel Natu cap |= (FS_PER_S / HPET_FREQ) << 32; /* tick period in fs */ 11208e3ff32SNeel Natu 11308e3ff32SNeel Natu return (cap); 11408e3ff32SNeel Natu } 11508e3ff32SNeel Natu 11608e3ff32SNeel Natu static __inline bool 11708e3ff32SNeel Natu vhpet_counter_enabled(struct vhpet *vhpet) 11808e3ff32SNeel Natu { 11908e3ff32SNeel Natu 12008e3ff32SNeel Natu return ((vhpet->config & HPET_CNF_ENABLE) ? true : false); 12108e3ff32SNeel Natu } 12208e3ff32SNeel Natu 12308e3ff32SNeel Natu static __inline bool 12408e3ff32SNeel Natu vhpet_timer_msi_enabled(struct vhpet *vhpet, int n) 12508e3ff32SNeel Natu { 12608e3ff32SNeel Natu const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN; 12708e3ff32SNeel Natu 12808e3ff32SNeel Natu if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable) 12908e3ff32SNeel Natu return (true); 13008e3ff32SNeel Natu else 13108e3ff32SNeel Natu return (false); 13208e3ff32SNeel Natu } 13308e3ff32SNeel Natu 13408e3ff32SNeel Natu static __inline int 13508e3ff32SNeel Natu vhpet_timer_ioapic_pin(struct vhpet *vhpet, int n) 13608e3ff32SNeel Natu { 13708e3ff32SNeel Natu /* 13808e3ff32SNeel Natu * If the timer is configured to use MSI then treat it as if the 13908e3ff32SNeel Natu * timer is not connected to the ioapic. 14008e3ff32SNeel Natu */ 14108e3ff32SNeel Natu if (vhpet_timer_msi_enabled(vhpet, n)) 14208e3ff32SNeel Natu return (0); 14308e3ff32SNeel Natu 14408e3ff32SNeel Natu return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9); 14508e3ff32SNeel Natu } 14608e3ff32SNeel Natu 14708e3ff32SNeel Natu static uint32_t 1480a9ae358SNeel Natu vhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr) 14908e3ff32SNeel Natu { 15008e3ff32SNeel Natu uint32_t val; 1510a9ae358SNeel Natu sbintime_t now, delta; 15208e3ff32SNeel Natu 1530a9ae358SNeel Natu val = vhpet->countbase; 15408e3ff32SNeel Natu if (vhpet_counter_enabled(vhpet)) { 1550a9ae358SNeel Natu now = sbinuptime(); 1560a9ae358SNeel Natu delta = now - vhpet->countbase_sbt; 1570a9ae358SNeel Natu KASSERT(delta >= 0, ("vhpet_counter: uptime went backwards: " 1580a9ae358SNeel Natu "%#lx to %#lx", vhpet->countbase_sbt, now)); 1590a9ae358SNeel Natu val += delta / vhpet->freq_sbt; 1600a9ae358SNeel Natu if (nowptr != NULL) 1610a9ae358SNeel Natu *nowptr = now; 1620a9ae358SNeel Natu } else { 16308e3ff32SNeel Natu /* 1640a9ae358SNeel Natu * The sbinuptime corresponding to the 'countbase' is 1650a9ae358SNeel Natu * meaningless when the counter is disabled. Make sure 166*cef367e6SEitan Adler * that the caller doesn't want to use it. 16708e3ff32SNeel Natu */ 1680a9ae358SNeel Natu KASSERT(nowptr == NULL, ("vhpet_counter: nowptr must be NULL")); 16908e3ff32SNeel Natu } 17008e3ff32SNeel Natu return (val); 17108e3ff32SNeel Natu } 17208e3ff32SNeel Natu 17308e3ff32SNeel Natu static void 17408e3ff32SNeel Natu vhpet_timer_clear_isr(struct vhpet *vhpet, int n) 17508e3ff32SNeel Natu { 1760dafa5cdSNeel Natu int pin; 17708e3ff32SNeel Natu 17808e3ff32SNeel Natu if (vhpet->isr & (1 << n)) { 17908e3ff32SNeel Natu pin = vhpet_timer_ioapic_pin(vhpet, n); 18008e3ff32SNeel Natu KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n)); 18108e3ff32SNeel Natu vioapic_deassert_irq(vhpet->vm, pin); 18208e3ff32SNeel Natu vhpet->isr &= ~(1 << n); 18308e3ff32SNeel Natu } 18408e3ff32SNeel Natu } 18508e3ff32SNeel Natu 18608e3ff32SNeel Natu static __inline bool 18708e3ff32SNeel Natu vhpet_periodic_timer(struct vhpet *vhpet, int n) 18808e3ff32SNeel Natu { 18908e3ff32SNeel Natu 19008e3ff32SNeel Natu return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0); 19108e3ff32SNeel Natu } 19208e3ff32SNeel Natu 19308e3ff32SNeel Natu static __inline bool 19408e3ff32SNeel Natu vhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n) 19508e3ff32SNeel Natu { 19608e3ff32SNeel Natu 19708e3ff32SNeel Natu return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0); 19808e3ff32SNeel Natu } 19908e3ff32SNeel Natu 20008e3ff32SNeel Natu static __inline bool 20108e3ff32SNeel Natu vhpet_timer_edge_trig(struct vhpet *vhpet, int n) 20208e3ff32SNeel Natu { 20308e3ff32SNeel Natu 20408e3ff32SNeel Natu KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: " 20508e3ff32SNeel Natu "timer %d is using MSI", n)); 20608e3ff32SNeel Natu 20708e3ff32SNeel Natu if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0) 20808e3ff32SNeel Natu return (true); 20908e3ff32SNeel Natu else 21008e3ff32SNeel Natu return (false); 21108e3ff32SNeel Natu } 21208e3ff32SNeel Natu 21308e3ff32SNeel Natu static void 21408e3ff32SNeel Natu vhpet_timer_interrupt(struct vhpet *vhpet, int n) 21508e3ff32SNeel Natu { 2160dafa5cdSNeel Natu int pin; 21708e3ff32SNeel Natu 21808e3ff32SNeel Natu /* If interrupts are not enabled for this timer then just return. */ 21908e3ff32SNeel Natu if (!vhpet_timer_interrupt_enabled(vhpet, n)) 22008e3ff32SNeel Natu return; 22108e3ff32SNeel Natu 22208e3ff32SNeel Natu /* 22308e3ff32SNeel Natu * If a level triggered interrupt is already asserted then just return. 22408e3ff32SNeel Natu */ 22508e3ff32SNeel Natu if ((vhpet->isr & (1 << n)) != 0) { 22608e3ff32SNeel Natu VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n); 22708e3ff32SNeel Natu return; 22808e3ff32SNeel Natu } 22908e3ff32SNeel Natu 23008e3ff32SNeel Natu if (vhpet_timer_msi_enabled(vhpet, n)) { 2314f8be175SNeel Natu lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32, 2324f8be175SNeel Natu vhpet->timer[n].msireg & 0xffffffff); 23308e3ff32SNeel Natu return; 23408e3ff32SNeel Natu } 23508e3ff32SNeel Natu 23608e3ff32SNeel Natu pin = vhpet_timer_ioapic_pin(vhpet, n); 23708e3ff32SNeel Natu if (pin == 0) { 23808e3ff32SNeel Natu VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n); 23908e3ff32SNeel Natu return; 24008e3ff32SNeel Natu } 24108e3ff32SNeel Natu 24208e3ff32SNeel Natu if (vhpet_timer_edge_trig(vhpet, n)) { 24308e3ff32SNeel Natu vioapic_pulse_irq(vhpet->vm, pin); 24408e3ff32SNeel Natu } else { 24508e3ff32SNeel Natu vhpet->isr |= 1 << n; 24608e3ff32SNeel Natu vioapic_assert_irq(vhpet->vm, pin); 24708e3ff32SNeel Natu } 24808e3ff32SNeel Natu } 24908e3ff32SNeel Natu 25008e3ff32SNeel Natu static void 25108e3ff32SNeel Natu vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter) 25208e3ff32SNeel Natu { 25308e3ff32SNeel Natu uint32_t compval, comprate, compnext; 25408e3ff32SNeel Natu 25508e3ff32SNeel Natu KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n)); 25608e3ff32SNeel Natu 25708e3ff32SNeel Natu compval = vhpet->timer[n].compval; 25808e3ff32SNeel Natu comprate = vhpet->timer[n].comprate; 25908e3ff32SNeel Natu 26008e3ff32SNeel Natu /* 26108e3ff32SNeel Natu * Calculate the comparator value to be used for the next periodic 26208e3ff32SNeel Natu * interrupt. 26308e3ff32SNeel Natu * 26408e3ff32SNeel Natu * This function is commonly called from the callout handler. 26508e3ff32SNeel Natu * In this scenario the 'counter' is ahead of 'compval'. To find 26608e3ff32SNeel Natu * the next value to program into the accumulator we divide the 26708e3ff32SNeel Natu * number space between 'compval' and 'counter' into 'comprate' 26808e3ff32SNeel Natu * sized units. The 'compval' is rounded up such that is "ahead" 26908e3ff32SNeel Natu * of 'counter'. 27008e3ff32SNeel Natu */ 27108e3ff32SNeel Natu compnext = compval + ((counter - compval) / comprate + 1) * comprate; 27208e3ff32SNeel Natu 27308e3ff32SNeel Natu vhpet->timer[n].compval = compnext; 27408e3ff32SNeel Natu } 27508e3ff32SNeel Natu 27608e3ff32SNeel Natu static void 27708e3ff32SNeel Natu vhpet_handler(void *a) 27808e3ff32SNeel Natu { 27908e3ff32SNeel Natu int n; 28008e3ff32SNeel Natu uint32_t counter; 2810a9ae358SNeel Natu sbintime_t now; 28208e3ff32SNeel Natu struct vhpet *vhpet; 28308e3ff32SNeel Natu struct callout *callout; 28408e3ff32SNeel Natu struct vhpet_callout_arg *arg; 28508e3ff32SNeel Natu 28608e3ff32SNeel Natu arg = a; 28708e3ff32SNeel Natu vhpet = arg->vhpet; 28808e3ff32SNeel Natu n = arg->timer_num; 28908e3ff32SNeel Natu callout = &vhpet->timer[n].callout; 29008e3ff32SNeel Natu 29108e3ff32SNeel Natu VM_CTR1(vhpet->vm, "hpet t%d fired", n); 29208e3ff32SNeel Natu 29308e3ff32SNeel Natu VHPET_LOCK(vhpet); 29408e3ff32SNeel Natu 29508e3ff32SNeel Natu if (callout_pending(callout)) /* callout was reset */ 29608e3ff32SNeel Natu goto done; 29708e3ff32SNeel Natu 29808e3ff32SNeel Natu if (!callout_active(callout)) /* callout was stopped */ 29908e3ff32SNeel Natu goto done; 30008e3ff32SNeel Natu 30108e3ff32SNeel Natu callout_deactivate(callout); 30208e3ff32SNeel Natu 30308e3ff32SNeel Natu if (!vhpet_counter_enabled(vhpet)) 30408e3ff32SNeel Natu panic("vhpet(%p) callout with counter disabled", vhpet); 30508e3ff32SNeel Natu 3060a9ae358SNeel Natu counter = vhpet_counter(vhpet, &now); 3070a9ae358SNeel Natu vhpet_start_timer(vhpet, n, counter, now); 30808e3ff32SNeel Natu vhpet_timer_interrupt(vhpet, n); 30908e3ff32SNeel Natu done: 31008e3ff32SNeel Natu VHPET_UNLOCK(vhpet); 31108e3ff32SNeel Natu return; 31208e3ff32SNeel Natu } 31308e3ff32SNeel Natu 31408e3ff32SNeel Natu static void 3150a9ae358SNeel Natu vhpet_stop_timer(struct vhpet *vhpet, int n, sbintime_t now) 31608e3ff32SNeel Natu { 31708e3ff32SNeel Natu 3180a9ae358SNeel Natu VM_CTR1(vhpet->vm, "hpet t%d stopped", n); 31908e3ff32SNeel Natu callout_stop(&vhpet->timer[n].callout); 3200a9ae358SNeel Natu 3210a9ae358SNeel Natu /* 3220a9ae358SNeel Natu * If the callout was scheduled to expire in the past but hasn't 3230a9ae358SNeel Natu * had a chance to execute yet then trigger the timer interrupt 3240a9ae358SNeel Natu * here. Failing to do so will result in a missed timer interrupt 3250a9ae358SNeel Natu * in the guest. This is especially bad in one-shot mode because 3260a9ae358SNeel Natu * the next interrupt has to wait for the counter to wrap around. 3270a9ae358SNeel Natu */ 3280a9ae358SNeel Natu if (vhpet->timer[n].callout_sbt < now) { 3290a9ae358SNeel Natu VM_CTR1(vhpet->vm, "hpet t%d interrupt triggered after " 3300a9ae358SNeel Natu "stopping timer", n); 3310a9ae358SNeel Natu vhpet_timer_interrupt(vhpet, n); 3320a9ae358SNeel Natu } 33308e3ff32SNeel Natu } 33408e3ff32SNeel Natu 33508e3ff32SNeel Natu static void 3360a9ae358SNeel Natu vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, sbintime_t now) 33708e3ff32SNeel Natu { 3380a9ae358SNeel Natu sbintime_t delta, precision; 33908e3ff32SNeel Natu 34008e3ff32SNeel Natu if (vhpet->timer[n].comprate != 0) 34108e3ff32SNeel Natu vhpet_adjust_compval(vhpet, n, counter); 3420a9ae358SNeel Natu else { 34308e3ff32SNeel Natu /* 3440a9ae358SNeel Natu * In one-shot mode it is the guest's responsibility to make 3450a9ae358SNeel Natu * sure that the comparator value is not in the "past". The 3460a9ae358SNeel Natu * hardware doesn't have any belt-and-suspenders to deal with 3470a9ae358SNeel Natu * this so we don't either. 34808e3ff32SNeel Natu */ 34908e3ff32SNeel Natu } 35008e3ff32SNeel Natu 3510a9ae358SNeel Natu delta = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt; 3520a9ae358SNeel Natu precision = delta >> tc_precexp; 3530a9ae358SNeel Natu vhpet->timer[n].callout_sbt = now + delta; 3540a9ae358SNeel Natu callout_reset_sbt(&vhpet->timer[n].callout, vhpet->timer[n].callout_sbt, 3550a9ae358SNeel Natu precision, vhpet_handler, &vhpet->timer[n].arg, C_ABSOLUTE); 35608e3ff32SNeel Natu } 35708e3ff32SNeel Natu 35808e3ff32SNeel Natu static void 35908e3ff32SNeel Natu vhpet_start_counting(struct vhpet *vhpet) 36008e3ff32SNeel Natu { 36108e3ff32SNeel Natu int i; 36208e3ff32SNeel Natu 3630a9ae358SNeel Natu vhpet->countbase_sbt = sbinuptime(); 3640a9ae358SNeel Natu for (i = 0; i < VHPET_NUM_TIMERS; i++) { 3650a9ae358SNeel Natu /* 3660a9ae358SNeel Natu * Restart the timers based on the value of the main counter 3670a9ae358SNeel Natu * when it stopped counting. 3680a9ae358SNeel Natu */ 3690a9ae358SNeel Natu vhpet_start_timer(vhpet, i, vhpet->countbase, 3700a9ae358SNeel Natu vhpet->countbase_sbt); 3710a9ae358SNeel Natu } 37208e3ff32SNeel Natu } 37308e3ff32SNeel Natu 37408e3ff32SNeel Natu static void 3750a9ae358SNeel Natu vhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, sbintime_t now) 37608e3ff32SNeel Natu { 37708e3ff32SNeel Natu int i; 37808e3ff32SNeel Natu 3790a9ae358SNeel Natu vhpet->countbase = counter; 38008e3ff32SNeel Natu for (i = 0; i < VHPET_NUM_TIMERS; i++) 3810a9ae358SNeel Natu vhpet_stop_timer(vhpet, i, now); 38208e3ff32SNeel Natu } 38308e3ff32SNeel Natu 38408e3ff32SNeel Natu static __inline void 38508e3ff32SNeel Natu update_register(uint64_t *regptr, uint64_t data, uint64_t mask) 38608e3ff32SNeel Natu { 38708e3ff32SNeel Natu 38808e3ff32SNeel Natu *regptr &= ~mask; 38908e3ff32SNeel Natu *regptr |= (data & mask); 39008e3ff32SNeel Natu } 39108e3ff32SNeel Natu 39208e3ff32SNeel Natu static void 39308e3ff32SNeel Natu vhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data, 39408e3ff32SNeel Natu uint64_t mask) 39508e3ff32SNeel Natu { 39608e3ff32SNeel Natu bool clear_isr; 39708e3ff32SNeel Natu int old_pin, new_pin; 39808e3ff32SNeel Natu uint32_t allowed_irqs; 39908e3ff32SNeel Natu uint64_t oldval, newval; 40008e3ff32SNeel Natu 40108e3ff32SNeel Natu if (vhpet_timer_msi_enabled(vhpet, n) || 40208e3ff32SNeel Natu vhpet_timer_edge_trig(vhpet, n)) { 40308e3ff32SNeel Natu if (vhpet->isr & (1 << n)) 40408e3ff32SNeel Natu panic("vhpet timer %d isr should not be asserted", n); 40508e3ff32SNeel Natu } 40608e3ff32SNeel Natu old_pin = vhpet_timer_ioapic_pin(vhpet, n); 40708e3ff32SNeel Natu oldval = vhpet->timer[n].cap_config; 40808e3ff32SNeel Natu 40908e3ff32SNeel Natu newval = oldval; 41008e3ff32SNeel Natu update_register(&newval, data, mask); 41108e3ff32SNeel Natu newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE); 41208e3ff32SNeel Natu newval |= oldval & HPET_TCAP_RO_MASK; 41308e3ff32SNeel Natu 41408e3ff32SNeel Natu if (newval == oldval) 41508e3ff32SNeel Natu return; 41608e3ff32SNeel Natu 41708e3ff32SNeel Natu vhpet->timer[n].cap_config = newval; 41808e3ff32SNeel Natu VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval); 41908e3ff32SNeel Natu 42008e3ff32SNeel Natu /* 42108e3ff32SNeel Natu * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field. 42208e3ff32SNeel Natu * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set 42308e3ff32SNeel Natu * it to the default value of 0. 42408e3ff32SNeel Natu */ 42508e3ff32SNeel Natu allowed_irqs = vhpet->timer[n].cap_config >> 32; 42608e3ff32SNeel Natu new_pin = vhpet_timer_ioapic_pin(vhpet, n); 42708e3ff32SNeel Natu if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) { 42808e3ff32SNeel Natu VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, " 42908e3ff32SNeel Natu "allowed_irqs 0x%08x", n, new_pin, allowed_irqs); 43008e3ff32SNeel Natu new_pin = 0; 43108e3ff32SNeel Natu vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE; 43208e3ff32SNeel Natu } 43308e3ff32SNeel Natu 43408e3ff32SNeel Natu if (!vhpet_periodic_timer(vhpet, n)) 43508e3ff32SNeel Natu vhpet->timer[n].comprate = 0; 43608e3ff32SNeel Natu 43708e3ff32SNeel Natu /* 43808e3ff32SNeel Natu * If the timer's ISR bit is set then clear it in the following cases: 43908e3ff32SNeel Natu * - interrupt is disabled 44008e3ff32SNeel Natu * - interrupt type is changed from level to edge or fsb. 44108e3ff32SNeel Natu * - interrupt routing is changed 44208e3ff32SNeel Natu * 44308e3ff32SNeel Natu * This is to ensure that this timer's level triggered interrupt does 44408e3ff32SNeel Natu * not remain asserted forever. 44508e3ff32SNeel Natu */ 44608e3ff32SNeel Natu if (vhpet->isr & (1 << n)) { 44708e3ff32SNeel Natu KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d", 44808e3ff32SNeel Natu n, old_pin)); 44908e3ff32SNeel Natu if (!vhpet_timer_interrupt_enabled(vhpet, n)) 45008e3ff32SNeel Natu clear_isr = true; 45108e3ff32SNeel Natu else if (vhpet_timer_msi_enabled(vhpet, n)) 45208e3ff32SNeel Natu clear_isr = true; 45308e3ff32SNeel Natu else if (vhpet_timer_edge_trig(vhpet, n)) 45408e3ff32SNeel Natu clear_isr = true; 45508e3ff32SNeel Natu else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin) 45608e3ff32SNeel Natu clear_isr = true; 45708e3ff32SNeel Natu else 45808e3ff32SNeel Natu clear_isr = false; 45908e3ff32SNeel Natu 46008e3ff32SNeel Natu if (clear_isr) { 46108e3ff32SNeel Natu VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to " 46208e3ff32SNeel Natu "configuration change", n); 46308e3ff32SNeel Natu vioapic_deassert_irq(vhpet->vm, old_pin); 46408e3ff32SNeel Natu vhpet->isr &= ~(1 << n); 46508e3ff32SNeel Natu } 46608e3ff32SNeel Natu } 46708e3ff32SNeel Natu } 46808e3ff32SNeel Natu 46908e3ff32SNeel Natu int 47008e3ff32SNeel Natu vhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size, 47108e3ff32SNeel Natu void *arg) 47208e3ff32SNeel Natu { 47308e3ff32SNeel Natu struct vhpet *vhpet; 47408e3ff32SNeel Natu uint64_t data, mask, oldval, val64; 4750a9ae358SNeel Natu uint32_t isr_clear_mask, old_compval, old_comprate, counter; 4760a9ae358SNeel Natu sbintime_t now, *nowptr; 47708e3ff32SNeel Natu int i, offset; 47808e3ff32SNeel Natu 47908e3ff32SNeel Natu vhpet = vm_hpet(vm); 48008e3ff32SNeel Natu offset = gpa - VHPET_BASE; 48108e3ff32SNeel Natu 48208e3ff32SNeel Natu VHPET_LOCK(vhpet); 48308e3ff32SNeel Natu 48408e3ff32SNeel Natu /* Accesses to the HPET should be 4 or 8 bytes wide */ 48508e3ff32SNeel Natu switch (size) { 48608e3ff32SNeel Natu case 8: 48708e3ff32SNeel Natu mask = 0xffffffffffffffff; 48808e3ff32SNeel Natu data = val; 48908e3ff32SNeel Natu break; 49008e3ff32SNeel Natu case 4: 49108e3ff32SNeel Natu mask = 0xffffffff; 49208e3ff32SNeel Natu data = val; 49308e3ff32SNeel Natu if ((offset & 0x4) != 0) { 49408e3ff32SNeel Natu mask <<= 32; 49508e3ff32SNeel Natu data <<= 32; 49608e3ff32SNeel Natu } 49708e3ff32SNeel Natu break; 49808e3ff32SNeel Natu default: 49908e3ff32SNeel Natu VM_CTR2(vhpet->vm, "hpet invalid mmio write: " 50008e3ff32SNeel Natu "offset 0x%08x, size %d", offset, size); 50108e3ff32SNeel Natu goto done; 50208e3ff32SNeel Natu } 50308e3ff32SNeel Natu 50408e3ff32SNeel Natu /* Access to the HPET should be naturally aligned to its width */ 50508e3ff32SNeel Natu if (offset & (size - 1)) { 50608e3ff32SNeel Natu VM_CTR2(vhpet->vm, "hpet invalid mmio write: " 50708e3ff32SNeel Natu "offset 0x%08x, size %d", offset, size); 50808e3ff32SNeel Natu goto done; 50908e3ff32SNeel Natu } 51008e3ff32SNeel Natu 51108e3ff32SNeel Natu if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) { 5120a9ae358SNeel Natu /* 5130a9ae358SNeel Natu * Get the most recent value of the counter before updating 5140a9ae358SNeel Natu * the 'config' register. If the HPET is going to be disabled 5150a9ae358SNeel Natu * then we need to update 'countbase' with the value right 5160a9ae358SNeel Natu * before it is disabled. 5170a9ae358SNeel Natu */ 5180a9ae358SNeel Natu nowptr = vhpet_counter_enabled(vhpet) ? &now : NULL; 5190a9ae358SNeel Natu counter = vhpet_counter(vhpet, nowptr); 52008e3ff32SNeel Natu oldval = vhpet->config; 52108e3ff32SNeel Natu update_register(&vhpet->config, data, mask); 5220dafa5cdSNeel Natu 5230dafa5cdSNeel Natu /* 5240dafa5cdSNeel Natu * LegacyReplacement Routing is not supported so clear the 5250dafa5cdSNeel Natu * bit explicitly. 5260dafa5cdSNeel Natu */ 5270dafa5cdSNeel Natu vhpet->config &= ~HPET_CNF_LEG_RT; 5280dafa5cdSNeel Natu 52908e3ff32SNeel Natu if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) { 53008e3ff32SNeel Natu if (vhpet_counter_enabled(vhpet)) { 53108e3ff32SNeel Natu vhpet_start_counting(vhpet); 53208e3ff32SNeel Natu VM_CTR0(vhpet->vm, "hpet enabled"); 53308e3ff32SNeel Natu } else { 5340a9ae358SNeel Natu vhpet_stop_counting(vhpet, counter, now); 53508e3ff32SNeel Natu VM_CTR0(vhpet->vm, "hpet disabled"); 53608e3ff32SNeel Natu } 53708e3ff32SNeel Natu } 53808e3ff32SNeel Natu goto done; 53908e3ff32SNeel Natu } 54008e3ff32SNeel Natu 54108e3ff32SNeel Natu if (offset == HPET_ISR || offset == HPET_ISR + 4) { 54208e3ff32SNeel Natu isr_clear_mask = vhpet->isr & data; 54308e3ff32SNeel Natu for (i = 0; i < VHPET_NUM_TIMERS; i++) { 54408e3ff32SNeel Natu if ((isr_clear_mask & (1 << i)) != 0) { 54508e3ff32SNeel Natu VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i); 54608e3ff32SNeel Natu vhpet_timer_clear_isr(vhpet, i); 54708e3ff32SNeel Natu } 54808e3ff32SNeel Natu } 54908e3ff32SNeel Natu goto done; 55008e3ff32SNeel Natu } 55108e3ff32SNeel Natu 55208e3ff32SNeel Natu if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) { 55308e3ff32SNeel Natu /* Zero-extend the counter to 64-bits before updating it */ 5540a9ae358SNeel Natu val64 = vhpet_counter(vhpet, NULL); 55508e3ff32SNeel Natu update_register(&val64, data, mask); 5560a9ae358SNeel Natu vhpet->countbase = val64; 55708e3ff32SNeel Natu if (vhpet_counter_enabled(vhpet)) 55808e3ff32SNeel Natu vhpet_start_counting(vhpet); 55908e3ff32SNeel Natu goto done; 56008e3ff32SNeel Natu } 56108e3ff32SNeel Natu 56208e3ff32SNeel Natu for (i = 0; i < VHPET_NUM_TIMERS; i++) { 56308e3ff32SNeel Natu if (offset == HPET_TIMER_CAP_CNF(i) || 56408e3ff32SNeel Natu offset == HPET_TIMER_CAP_CNF(i) + 4) { 56508e3ff32SNeel Natu vhpet_timer_update_config(vhpet, i, data, mask); 56608e3ff32SNeel Natu break; 56708e3ff32SNeel Natu } 56808e3ff32SNeel Natu 56908e3ff32SNeel Natu if (offset == HPET_TIMER_COMPARATOR(i) || 57008e3ff32SNeel Natu offset == HPET_TIMER_COMPARATOR(i) + 4) { 57108e3ff32SNeel Natu old_compval = vhpet->timer[i].compval; 57208e3ff32SNeel Natu old_comprate = vhpet->timer[i].comprate; 57308e3ff32SNeel Natu if (vhpet_periodic_timer(vhpet, i)) { 57408e3ff32SNeel Natu /* 57508e3ff32SNeel Natu * In periodic mode writes to the comparator 57608e3ff32SNeel Natu * change the 'compval' register only if the 57708e3ff32SNeel Natu * HPET_TCNF_VAL_SET bit is set in the config 57808e3ff32SNeel Natu * register. 57908e3ff32SNeel Natu */ 58008e3ff32SNeel Natu val64 = vhpet->timer[i].comprate; 58108e3ff32SNeel Natu update_register(&val64, data, mask); 58208e3ff32SNeel Natu vhpet->timer[i].comprate = val64; 58308e3ff32SNeel Natu if ((vhpet->timer[i].cap_config & 58408e3ff32SNeel Natu HPET_TCNF_VAL_SET) != 0) { 58508e3ff32SNeel Natu vhpet->timer[i].compval = val64; 58608e3ff32SNeel Natu } 58708e3ff32SNeel Natu } else { 58808e3ff32SNeel Natu KASSERT(vhpet->timer[i].comprate == 0, 58908e3ff32SNeel Natu ("vhpet one-shot timer %d has invalid " 59008e3ff32SNeel Natu "rate %u", i, vhpet->timer[i].comprate)); 59108e3ff32SNeel Natu val64 = vhpet->timer[i].compval; 59208e3ff32SNeel Natu update_register(&val64, data, mask); 59308e3ff32SNeel Natu vhpet->timer[i].compval = val64; 59408e3ff32SNeel Natu } 59508e3ff32SNeel Natu vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET; 59608e3ff32SNeel Natu 59708e3ff32SNeel Natu if (vhpet->timer[i].compval != old_compval || 59808e3ff32SNeel Natu vhpet->timer[i].comprate != old_comprate) { 5990a9ae358SNeel Natu if (vhpet_counter_enabled(vhpet)) { 6000a9ae358SNeel Natu counter = vhpet_counter(vhpet, &now); 6010a9ae358SNeel Natu vhpet_start_timer(vhpet, i, counter, 6020a9ae358SNeel Natu now); 6030a9ae358SNeel Natu } 60408e3ff32SNeel Natu } 60508e3ff32SNeel Natu break; 60608e3ff32SNeel Natu } 60708e3ff32SNeel Natu 60808e3ff32SNeel Natu if (offset == HPET_TIMER_FSB_VAL(i) || 60908e3ff32SNeel Natu offset == HPET_TIMER_FSB_ADDR(i)) { 61008e3ff32SNeel Natu update_register(&vhpet->timer[i].msireg, data, mask); 61108e3ff32SNeel Natu break; 61208e3ff32SNeel Natu } 61308e3ff32SNeel Natu } 61408e3ff32SNeel Natu done: 61508e3ff32SNeel Natu VHPET_UNLOCK(vhpet); 61608e3ff32SNeel Natu return (0); 61708e3ff32SNeel Natu } 61808e3ff32SNeel Natu 61908e3ff32SNeel Natu int 62008e3ff32SNeel Natu vhpet_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, int size, 62108e3ff32SNeel Natu void *arg) 62208e3ff32SNeel Natu { 62308e3ff32SNeel Natu int i, offset; 62408e3ff32SNeel Natu struct vhpet *vhpet; 62508e3ff32SNeel Natu uint64_t data; 62608e3ff32SNeel Natu 62708e3ff32SNeel Natu vhpet = vm_hpet(vm); 62808e3ff32SNeel Natu offset = gpa - VHPET_BASE; 62908e3ff32SNeel Natu 63008e3ff32SNeel Natu VHPET_LOCK(vhpet); 63108e3ff32SNeel Natu 63208e3ff32SNeel Natu /* Accesses to the HPET should be 4 or 8 bytes wide */ 63308e3ff32SNeel Natu if (size != 4 && size != 8) { 63408e3ff32SNeel Natu VM_CTR2(vhpet->vm, "hpet invalid mmio read: " 63508e3ff32SNeel Natu "offset 0x%08x, size %d", offset, size); 63608e3ff32SNeel Natu data = 0; 63708e3ff32SNeel Natu goto done; 63808e3ff32SNeel Natu } 63908e3ff32SNeel Natu 64008e3ff32SNeel Natu /* Access to the HPET should be naturally aligned to its width */ 64108e3ff32SNeel Natu if (offset & (size - 1)) { 64208e3ff32SNeel Natu VM_CTR2(vhpet->vm, "hpet invalid mmio read: " 64308e3ff32SNeel Natu "offset 0x%08x, size %d", offset, size); 64408e3ff32SNeel Natu data = 0; 64508e3ff32SNeel Natu goto done; 64608e3ff32SNeel Natu } 64708e3ff32SNeel Natu 64808e3ff32SNeel Natu if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) { 64908e3ff32SNeel Natu data = vhpet_capabilities(); 65008e3ff32SNeel Natu goto done; 65108e3ff32SNeel Natu } 65208e3ff32SNeel Natu 65308e3ff32SNeel Natu if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) { 65408e3ff32SNeel Natu data = vhpet->config; 65508e3ff32SNeel Natu goto done; 65608e3ff32SNeel Natu } 65708e3ff32SNeel Natu 65808e3ff32SNeel Natu if (offset == HPET_ISR || offset == HPET_ISR + 4) { 65908e3ff32SNeel Natu data = vhpet->isr; 66008e3ff32SNeel Natu goto done; 66108e3ff32SNeel Natu } 66208e3ff32SNeel Natu 66308e3ff32SNeel Natu if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) { 6640a9ae358SNeel Natu data = vhpet_counter(vhpet, NULL); 66508e3ff32SNeel Natu goto done; 66608e3ff32SNeel Natu } 66708e3ff32SNeel Natu 66808e3ff32SNeel Natu for (i = 0; i < VHPET_NUM_TIMERS; i++) { 66908e3ff32SNeel Natu if (offset == HPET_TIMER_CAP_CNF(i) || 67008e3ff32SNeel Natu offset == HPET_TIMER_CAP_CNF(i) + 4) { 67108e3ff32SNeel Natu data = vhpet->timer[i].cap_config; 67208e3ff32SNeel Natu break; 67308e3ff32SNeel Natu } 67408e3ff32SNeel Natu 67508e3ff32SNeel Natu if (offset == HPET_TIMER_COMPARATOR(i) || 67608e3ff32SNeel Natu offset == HPET_TIMER_COMPARATOR(i) + 4) { 67708e3ff32SNeel Natu data = vhpet->timer[i].compval; 67808e3ff32SNeel Natu break; 67908e3ff32SNeel Natu } 68008e3ff32SNeel Natu 68108e3ff32SNeel Natu if (offset == HPET_TIMER_FSB_VAL(i) || 68208e3ff32SNeel Natu offset == HPET_TIMER_FSB_ADDR(i)) { 68308e3ff32SNeel Natu data = vhpet->timer[i].msireg; 68408e3ff32SNeel Natu break; 68508e3ff32SNeel Natu } 68608e3ff32SNeel Natu } 68708e3ff32SNeel Natu 68808e3ff32SNeel Natu if (i >= VHPET_NUM_TIMERS) 68908e3ff32SNeel Natu data = 0; 69008e3ff32SNeel Natu done: 69108e3ff32SNeel Natu VHPET_UNLOCK(vhpet); 69208e3ff32SNeel Natu 69308e3ff32SNeel Natu if (size == 4) { 69408e3ff32SNeel Natu if (offset & 0x4) 69508e3ff32SNeel Natu data >>= 32; 69608e3ff32SNeel Natu } 69708e3ff32SNeel Natu *rval = data; 69808e3ff32SNeel Natu return (0); 69908e3ff32SNeel Natu } 70008e3ff32SNeel Natu 70108e3ff32SNeel Natu struct vhpet * 70208e3ff32SNeel Natu vhpet_init(struct vm *vm) 70308e3ff32SNeel Natu { 704b5b28fc9SNeel Natu int i, pincount; 70508e3ff32SNeel Natu struct vhpet *vhpet; 706b5b28fc9SNeel Natu uint64_t allowed_irqs; 70708e3ff32SNeel Natu struct vhpet_callout_arg *arg; 70808e3ff32SNeel Natu struct bintime bt; 70908e3ff32SNeel Natu 71008e3ff32SNeel Natu vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO); 71108e3ff32SNeel Natu vhpet->vm = vm; 71208e3ff32SNeel Natu mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF); 71308e3ff32SNeel Natu 71408e3ff32SNeel Natu FREQ2BT(HPET_FREQ, &bt); 71508e3ff32SNeel Natu vhpet->freq_sbt = bttosbt(bt); 71608e3ff32SNeel Natu 717b5b28fc9SNeel Natu pincount = vioapic_pincount(vm); 718b5b28fc9SNeel Natu if (pincount >= 24) 719b5b28fc9SNeel Natu allowed_irqs = 0x00f00000; /* irqs 20, 21, 22 and 23 */ 720b5b28fc9SNeel Natu else 721b5b28fc9SNeel Natu allowed_irqs = 0; 722b5b28fc9SNeel Natu 72308e3ff32SNeel Natu /* 72408e3ff32SNeel Natu * Initialize HPET timer hardware state. 72508e3ff32SNeel Natu */ 72608e3ff32SNeel Natu for (i = 0; i < VHPET_NUM_TIMERS; i++) { 727b5b28fc9SNeel Natu vhpet->timer[i].cap_config = allowed_irqs << 32; 728b5b28fc9SNeel Natu vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT; 729b5b28fc9SNeel Natu vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL; 730b5b28fc9SNeel Natu 73108e3ff32SNeel Natu vhpet->timer[i].compval = 0xffffffff; 73208e3ff32SNeel Natu callout_init(&vhpet->timer[i].callout, 1); 73308e3ff32SNeel Natu 73408e3ff32SNeel Natu arg = &vhpet->timer[i].arg; 73508e3ff32SNeel Natu arg->vhpet = vhpet; 73608e3ff32SNeel Natu arg->timer_num = i; 73708e3ff32SNeel Natu } 73808e3ff32SNeel Natu 73908e3ff32SNeel Natu return (vhpet); 74008e3ff32SNeel Natu } 74108e3ff32SNeel Natu 74208e3ff32SNeel Natu void 74308e3ff32SNeel Natu vhpet_cleanup(struct vhpet *vhpet) 74408e3ff32SNeel Natu { 74508e3ff32SNeel Natu int i; 74608e3ff32SNeel Natu 74708e3ff32SNeel Natu for (i = 0; i < VHPET_NUM_TIMERS; i++) 74808e3ff32SNeel Natu callout_drain(&vhpet->timer[i].callout); 74908e3ff32SNeel Natu 75008e3ff32SNeel Natu free(vhpet, M_VHPET); 75108e3ff32SNeel Natu } 75208e3ff32SNeel Natu 75308e3ff32SNeel Natu int 75408e3ff32SNeel Natu vhpet_getcap(struct vm_hpet_cap *cap) 75508e3ff32SNeel Natu { 75608e3ff32SNeel Natu 75708e3ff32SNeel Natu cap->capabilities = vhpet_capabilities(); 75808e3ff32SNeel Natu return (0); 75908e3ff32SNeel Natu } 760