vhpet.c revision 266477
189580Smsmith/*- 2139749Simp * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 3129449Sscottl * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 4129449Sscottl * All rights reserved. 5129449Sscottl * 689580Smsmith * Redistribution and use in source and binary forms, with or without 789580Smsmith * modification, are permitted provided that the following conditions 889580Smsmith * are met: 989580Smsmith * 1. Redistributions of source code must retain the above copyright 1089580Smsmith * notice, this list of conditions and the following disclaimer. 1189580Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1289580Smsmith * notice, this list of conditions and the following disclaimer in the 1389580Smsmith * documentation and/or other materials provided with the distribution. 1489580Smsmith * 1589580Smsmith * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 1689580Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1789580Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1889580Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 1989580Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2089580Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2189580Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2289580Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2389580Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2489580Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2589580Smsmith * SUCH DAMAGE. 2689580Smsmith * 2789580Smsmith * $FreeBSD: stable/10/sys/amd64/vmm/io/vhpet.c 266477 2014-05-20 21:05:36Z jhb $ 2889580Smsmith */ 2989580Smsmith 3089580Smsmith#include <sys/cdefs.h> 3189580Smsmith__FBSDID("$FreeBSD: stable/10/sys/amd64/vmm/io/vhpet.c 266477 2014-05-20 21:05:36Z jhb $"); 3289580Smsmith 3389580Smsmith#include <sys/param.h> 3489580Smsmith#include <sys/lock.h> 3589580Smsmith#include <sys/mutex.h> 3689580Smsmith#include <sys/kernel.h> 3789580Smsmith#include <sys/malloc.h> 38120477Sscottl#include <sys/systm.h> 3989580Smsmith#include <sys/cpuset.h> 4089580Smsmith 4189580Smsmith#include <dev/acpica/acpi_hpet.h> 4289580Smsmith 43120477Sscottl#include <machine/vmm.h> 44129449Sscottl#include <machine/vmm_dev.h> 4589580Smsmith 4689580Smsmith#include "vmm_lapic.h" 4789580Smsmith#include "vioapic.h" 4889580Smsmith#include "vhpet.h" 4989580Smsmith 50143063Sjoerg#include "vmm_ktr.h" 51143063Sjoerg 52143063Sjoergstatic MALLOC_DEFINE(M_VHPET, "vhpet", "bhyve virtual hpet"); 53143063Sjoerg 5489580Smsmith#define HPET_FREQ 10000000 /* 10.0 Mhz */ 55129449Sscottl#define FS_PER_S 1000000000000000ul 5689580Smsmith 57120477Sscottl/* Timer N Configuration and Capabilities Register */ 58120477Sscottl#define HPET_TCAP_RO_MASK (HPET_TCAP_INT_ROUTE | \ 59120477Sscottl HPET_TCAP_FSB_INT_DEL | \ 60120477Sscottl HPET_TCAP_SIZE | \ 6189580Smsmith HPET_TCAP_PER_INT) 6289580Smsmith/* 6389580Smsmith * HPET requires at least 3 timers and up to 32 timers per block. 6489580Smsmith */ 6589580Smsmith#define VHPET_NUM_TIMERS 8 66254379SjkimCTASSERT(VHPET_NUM_TIMERS >= 3 && VHPET_NUM_TIMERS <= 32); 6789580Smsmith 6889580Smsmithstruct vhpet_callout_arg { 6989580Smsmith struct vhpet *vhpet; 7089580Smsmith int timer_num; 7189580Smsmith}; 7289580Smsmith 7389580Smsmithstruct vhpet { 7489580Smsmith struct vm *vm; 7589580Smsmith struct mtx mtx; 7689580Smsmith sbintime_t freq_sbt; 7789580Smsmith 7889580Smsmith uint64_t config; /* Configuration */ 7989580Smsmith uint64_t isr; /* Interrupt Status */ 8089580Smsmith uint32_t countbase; /* HPET counter base value */ 8189580Smsmith sbintime_t countbase_sbt; /* uptime corresponding to base value */ 8289580Smsmith 8389580Smsmith struct { 8489580Smsmith uint64_t cap_config; /* Configuration */ 8589580Smsmith uint64_t msireg; /* FSB interrupt routing */ 8689580Smsmith uint32_t compval; /* Comparator */ 8789580Smsmith uint32_t comprate; 8889580Smsmith struct callout callout; 8989580Smsmith sbintime_t callout_sbt; /* time when counter==compval */ 9089580Smsmith struct vhpet_callout_arg arg; 9189580Smsmith } timer[VHPET_NUM_TIMERS]; 9289580Smsmith}; 9389580Smsmith 9489580Smsmith#define VHPET_LOCK(vhp) mtx_lock(&((vhp)->mtx)) 9589580Smsmith#define VHPET_UNLOCK(vhp) mtx_unlock(&((vhp)->mtx)) 9689580Smsmith 9789580Smsmithstatic void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, 9889580Smsmith sbintime_t now); 9989580Smsmith 10089580Smsmithstatic uint64_t 10189580Smsmithvhpet_capabilities(void) 10289580Smsmith{ 10389580Smsmith uint64_t cap = 0; 10489580Smsmith 10589580Smsmith cap |= 0x8086 << 16; /* vendor id */ 10689580Smsmith cap |= HPET_CAP_LEG_RT; /* legacy routing capable */ 10789580Smsmith cap |= (VHPET_NUM_TIMERS - 1) << 8; /* number of timers */ 10889580Smsmith cap |= 1; /* revision */ 10989580Smsmith cap &= ~HPET_CAP_COUNT_SIZE; /* 32-bit timer */ 11089580Smsmith 11189580Smsmith cap &= 0xffffffff; 11289580Smsmith cap |= (FS_PER_S / HPET_FREQ) << 32; /* tick period in fs */ 11389580Smsmith 11489580Smsmith return (cap); 11589580Smsmith} 11689580Smsmith 11789580Smsmithstatic __inline bool 11889580Smsmithvhpet_counter_enabled(struct vhpet *vhpet) 11989580Smsmith{ 12089580Smsmith 12189580Smsmith return ((vhpet->config & HPET_CNF_ENABLE) ? true : false); 12289580Smsmith} 12389580Smsmith 12489580Smsmithstatic __inline bool 12589580Smsmithvhpet_timer_msi_enabled(struct vhpet *vhpet, int n) 12689580Smsmith{ 12789580Smsmith const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN; 12889580Smsmith 12989580Smsmith /* 13089580Smsmith * LegacyReplacement Route configuration takes precedence over MSI 13189580Smsmith * for timers 0 and 1. 13289580Smsmith */ 13389580Smsmith if (n == 0 || n == 1) { 13489580Smsmith if (vhpet->config & HPET_CNF_LEG_RT) 13589580Smsmith return (false); 13689580Smsmith } 13789580Smsmith 13889580Smsmith if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable) 13989580Smsmith return (true); 14089580Smsmith else 14189580Smsmith return (false); 14289580Smsmith} 14389580Smsmith 14489580Smsmithstatic __inline int 14589580Smsmithvhpet_timer_ioapic_pin(struct vhpet *vhpet, int n) 14689580Smsmith{ 14789580Smsmith /* 14889580Smsmith * If the timer is configured to use MSI then treat it as if the 14989580Smsmith * timer is not connected to the ioapic. 15089580Smsmith */ 15189580Smsmith if (vhpet_timer_msi_enabled(vhpet, n)) 152114001Sscottl return (0); 15389580Smsmith 15489580Smsmith if (vhpet->config & HPET_CNF_LEG_RT) { 15589580Smsmith /* 15689580Smsmith * In "legacy routing" timers 0 and 1 are connected to 15789580Smsmith * ioapic pins 2 and 8 respectively. 15889580Smsmith */ 159129449Sscottl switch (n) { 160129449Sscottl case 0: 161129449Sscottl return (2); 162129449Sscottl case 1: 16389580Smsmith return (8); 16489580Smsmith } 16589580Smsmith } 16689580Smsmith 16789580Smsmith return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9); 16889580Smsmith} 16989580Smsmith 17089580Smsmithstatic uint32_t 17189580Smsmithvhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr) 17289580Smsmith{ 17389580Smsmith uint32_t val; 17489580Smsmith sbintime_t now, delta; 17589580Smsmith 17689580Smsmith val = vhpet->countbase; 17789580Smsmith if (vhpet_counter_enabled(vhpet)) { 17889580Smsmith now = sbinuptime(); 17989580Smsmith delta = now - vhpet->countbase_sbt; 18089580Smsmith KASSERT(delta >= 0, ("vhpet_counter: uptime went backwards: " 18189580Smsmith "%#lx to %#lx", vhpet->countbase_sbt, now)); 18289580Smsmith val += delta / vhpet->freq_sbt; 18389580Smsmith if (nowptr != NULL) 18489580Smsmith *nowptr = now; 18589580Smsmith } else { 18689580Smsmith /* 18789580Smsmith * The sbinuptime corresponding to the 'countbase' is 18889580Smsmith * meaningless when the counter is disabled. Make sure 18989580Smsmith * that the the caller doesn't want to use it. 19089580Smsmith */ 19189580Smsmith KASSERT(nowptr == NULL, ("vhpet_counter: nowptr must be NULL")); 19289580Smsmith } 19389580Smsmith return (val); 19489580Smsmith} 19589580Smsmith 19689580Smsmithstatic void 19789580Smsmithvhpet_timer_clear_isr(struct vhpet *vhpet, int n) 19889580Smsmith{ 19989580Smsmith int pin; 20089580Smsmith 20189580Smsmith if (vhpet->isr & (1 << n)) { 20289580Smsmith pin = vhpet_timer_ioapic_pin(vhpet, n); 20389580Smsmith KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n)); 20489580Smsmith vioapic_deassert_irq(vhpet->vm, pin); 20589580Smsmith vhpet->isr &= ~(1 << n); 20689580Smsmith } 20789580Smsmith} 20889580Smsmith 20989580Smsmithstatic __inline bool 21089580Smsmithvhpet_periodic_timer(struct vhpet *vhpet, int n) 21189580Smsmith{ 21289580Smsmith 21389580Smsmith return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0); 21489580Smsmith} 21589580Smsmith 21689580Smsmithstatic __inline bool 21789580Smsmithvhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n) 21889580Smsmith{ 21989580Smsmith 22089580Smsmith return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0); 22189580Smsmith} 22289580Smsmith 22389580Smsmithstatic __inline bool 22489580Smsmithvhpet_timer_edge_trig(struct vhpet *vhpet, int n) 22589580Smsmith{ 22689580Smsmith 22789580Smsmith KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: " 22889580Smsmith "timer %d is using MSI", n)); 22989580Smsmith 23089580Smsmith /* The legacy replacement interrupts are always edge triggered */ 23189580Smsmith if (vhpet->config & HPET_CNF_LEG_RT) { 23289580Smsmith if (n == 0 || n == 1) 23389580Smsmith return (true); 23489580Smsmith } 23589580Smsmith 23689580Smsmith if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0) 23789580Smsmith return (true); 23889580Smsmith else 23989580Smsmith return (false); 24089580Smsmith} 24189580Smsmith 24289580Smsmithstatic void 24389580Smsmithvhpet_timer_interrupt(struct vhpet *vhpet, int n) 24489580Smsmith{ 24589580Smsmith int pin; 24689580Smsmith 24789580Smsmith /* If interrupts are not enabled for this timer then just return. */ 24889580Smsmith if (!vhpet_timer_interrupt_enabled(vhpet, n)) 24989580Smsmith return; 25089580Smsmith 25189580Smsmith /* 25289580Smsmith * If a level triggered interrupt is already asserted then just return. 25389580Smsmith */ 25489580Smsmith if ((vhpet->isr & (1 << n)) != 0) { 25589580Smsmith VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n); 25689580Smsmith return; 25789580Smsmith } 25889580Smsmith 25989580Smsmith if (vhpet_timer_msi_enabled(vhpet, n)) { 26089580Smsmith lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32, 26189580Smsmith vhpet->timer[n].msireg & 0xffffffff); 26289580Smsmith return; 26389580Smsmith } 26489580Smsmith 26589580Smsmith pin = vhpet_timer_ioapic_pin(vhpet, n); 26689580Smsmith if (pin == 0) { 26789580Smsmith VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n); 26889580Smsmith return; 26989580Smsmith } 27089580Smsmith 27189580Smsmith if (vhpet_timer_edge_trig(vhpet, n)) { 27289580Smsmith vioapic_pulse_irq(vhpet->vm, pin); 27389580Smsmith } else { 27489580Smsmith vhpet->isr |= 1 << n; 27589580Smsmith vioapic_assert_irq(vhpet->vm, pin); 27689580Smsmith } 27789580Smsmith} 27889580Smsmith 27989580Smsmithstatic void 28089580Smsmithvhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter) 28189580Smsmith{ 28289580Smsmith uint32_t compval, comprate, compnext; 28389580Smsmith 28489580Smsmith KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n)); 28589580Smsmith 28689580Smsmith compval = vhpet->timer[n].compval; 28789580Smsmith comprate = vhpet->timer[n].comprate; 28889580Smsmith 28989580Smsmith /* 29089580Smsmith * Calculate the comparator value to be used for the next periodic 29189580Smsmith * interrupt. 29289580Smsmith * 29389580Smsmith * This function is commonly called from the callout handler. 29489580Smsmith * In this scenario the 'counter' is ahead of 'compval'. To find 29589580Smsmith * the next value to program into the accumulator we divide the 29689580Smsmith * number space between 'compval' and 'counter' into 'comprate' 29789580Smsmith * sized units. The 'compval' is rounded up such that is "ahead" 29889580Smsmith * of 'counter'. 29989580Smsmith */ 30089580Smsmith compnext = compval + ((counter - compval) / comprate + 1) * comprate; 30189580Smsmith 30289580Smsmith vhpet->timer[n].compval = compnext; 30389580Smsmith} 30489580Smsmith 30589580Smsmithstatic void 30689580Smsmithvhpet_handler(void *a) 30789580Smsmith{ 30889580Smsmith int n; 30989580Smsmith uint32_t counter; 31089580Smsmith sbintime_t now; 31189580Smsmith struct vhpet *vhpet; 31289580Smsmith struct callout *callout; 31389580Smsmith struct vhpet_callout_arg *arg; 31489580Smsmith 31589580Smsmith arg = a; 31689580Smsmith vhpet = arg->vhpet; 31789580Smsmith n = arg->timer_num; 31889580Smsmith callout = &vhpet->timer[n].callout; 31989580Smsmith 32089580Smsmith VM_CTR1(vhpet->vm, "hpet t%d fired", n); 32189580Smsmith 32289580Smsmith VHPET_LOCK(vhpet); 32389580Smsmith 32489580Smsmith if (callout_pending(callout)) /* callout was reset */ 32589580Smsmith goto done; 32689580Smsmith 32789580Smsmith if (!callout_active(callout)) /* callout was stopped */ 32889580Smsmith goto done; 32989580Smsmith 33089580Smsmith callout_deactivate(callout); 33189580Smsmith 33289580Smsmith if (!vhpet_counter_enabled(vhpet)) 33389580Smsmith panic("vhpet(%p) callout with counter disabled", vhpet); 33489580Smsmith 33589580Smsmith counter = vhpet_counter(vhpet, &now); 33689580Smsmith vhpet_start_timer(vhpet, n, counter, now); 33789580Smsmith vhpet_timer_interrupt(vhpet, n); 33889580Smsmithdone: 33989580Smsmith VHPET_UNLOCK(vhpet); 34089580Smsmith return; 34189580Smsmith} 34289580Smsmith 34389580Smsmithstatic void 34489580Smsmithvhpet_stop_timer(struct vhpet *vhpet, int n, sbintime_t now) 34589580Smsmith{ 34689580Smsmith 34789580Smsmith VM_CTR1(vhpet->vm, "hpet t%d stopped", n); 34889580Smsmith callout_stop(&vhpet->timer[n].callout); 34989580Smsmith 35089580Smsmith /* 35189580Smsmith * If the callout was scheduled to expire in the past but hasn't 35289580Smsmith * had a chance to execute yet then trigger the timer interrupt 35389580Smsmith * here. Failing to do so will result in a missed timer interrupt 35489580Smsmith * in the guest. This is especially bad in one-shot mode because 35589580Smsmith * the next interrupt has to wait for the counter to wrap around. 35689580Smsmith */ 35789580Smsmith if (vhpet->timer[n].callout_sbt < now) { 35889580Smsmith VM_CTR1(vhpet->vm, "hpet t%d interrupt triggered after " 35989580Smsmith "stopping timer", n); 36089580Smsmith vhpet_timer_interrupt(vhpet, n); 36189580Smsmith } 36289580Smsmith} 36389580Smsmith 36489580Smsmithstatic void 36589580Smsmithvhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, sbintime_t now) 36689580Smsmith{ 36789580Smsmith sbintime_t delta, precision; 36889580Smsmith 36989580Smsmith if (vhpet->timer[n].comprate != 0) 37089580Smsmith vhpet_adjust_compval(vhpet, n, counter); 37189580Smsmith else { 37289580Smsmith /* 37389580Smsmith * In one-shot mode it is the guest's responsibility to make 37489580Smsmith * sure that the comparator value is not in the "past". The 37589580Smsmith * hardware doesn't have any belt-and-suspenders to deal with 37689580Smsmith * this so we don't either. 37789580Smsmith */ 37889580Smsmith } 37989580Smsmith 38089580Smsmith delta = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt; 38189580Smsmith precision = delta >> tc_precexp; 38289580Smsmith vhpet->timer[n].callout_sbt = now + delta; 38389580Smsmith callout_reset_sbt(&vhpet->timer[n].callout, vhpet->timer[n].callout_sbt, 38489580Smsmith precision, vhpet_handler, &vhpet->timer[n].arg, C_ABSOLUTE); 38589580Smsmith} 38689580Smsmith 38789580Smsmithstatic void 38889580Smsmithvhpet_start_counting(struct vhpet *vhpet) 38989580Smsmith{ 39089580Smsmith int i; 39189580Smsmith 39289580Smsmith vhpet->countbase_sbt = sbinuptime(); 39389580Smsmith for (i = 0; i < VHPET_NUM_TIMERS; i++) { 39489580Smsmith /* 39589580Smsmith * Restart the timers based on the value of the main counter 39689580Smsmith * when it stopped counting. 39789580Smsmith */ 39889580Smsmith vhpet_start_timer(vhpet, i, vhpet->countbase, 39989580Smsmith vhpet->countbase_sbt); 40089580Smsmith } 40189580Smsmith} 40289580Smsmith 40389580Smsmithstatic void 40489580Smsmithvhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, sbintime_t now) 40589580Smsmith{ 40689580Smsmith int i; 40789580Smsmith 40889580Smsmith vhpet->countbase = counter; 40989580Smsmith for (i = 0; i < VHPET_NUM_TIMERS; i++) 41089580Smsmith vhpet_stop_timer(vhpet, i, now); 41189580Smsmith} 41289580Smsmith 41389580Smsmithstatic __inline void 41489580Smsmithupdate_register(uint64_t *regptr, uint64_t data, uint64_t mask) 41589580Smsmith{ 41689580Smsmith 41789580Smsmith *regptr &= ~mask; 41889580Smsmith *regptr |= (data & mask); 41989580Smsmith} 42089580Smsmith 42189580Smsmithstatic void 42289580Smsmithvhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data, 42389580Smsmith uint64_t mask) 42489580Smsmith{ 42589580Smsmith bool clear_isr; 42689580Smsmith int old_pin, new_pin; 42789580Smsmith uint32_t allowed_irqs; 42889580Smsmith uint64_t oldval, newval; 42989580Smsmith 43089580Smsmith if (vhpet_timer_msi_enabled(vhpet, n) || 43189580Smsmith vhpet_timer_edge_trig(vhpet, n)) { 43289580Smsmith if (vhpet->isr & (1 << n)) 43389580Smsmith panic("vhpet timer %d isr should not be asserted", n); 43489580Smsmith } 43589580Smsmith old_pin = vhpet_timer_ioapic_pin(vhpet, n); 43689580Smsmith oldval = vhpet->timer[n].cap_config; 43789580Smsmith 43889580Smsmith newval = oldval; 43989580Smsmith update_register(&newval, data, mask); 44089580Smsmith newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE); 44189580Smsmith newval |= oldval & HPET_TCAP_RO_MASK; 44289580Smsmith 44389580Smsmith if (newval == oldval) 44489580Smsmith return; 44589580Smsmith 44689580Smsmith vhpet->timer[n].cap_config = newval; 44789580Smsmith VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval); 44889580Smsmith 44989580Smsmith /* 45089580Smsmith * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field. 45189580Smsmith * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set 45289580Smsmith * it to the default value of 0. 45389580Smsmith */ 45489580Smsmith allowed_irqs = vhpet->timer[n].cap_config >> 32; 45589580Smsmith new_pin = vhpet_timer_ioapic_pin(vhpet, n); 45689580Smsmith if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) { 45789580Smsmith VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, " 45889580Smsmith "allowed_irqs 0x%08x", n, new_pin, allowed_irqs); 45989580Smsmith new_pin = 0; 46089580Smsmith vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE; 46189580Smsmith } 46289580Smsmith 46389580Smsmith if (!vhpet_periodic_timer(vhpet, n)) 46489580Smsmith vhpet->timer[n].comprate = 0; 46589580Smsmith 46689580Smsmith /* 467114001Sscottl * If the timer's ISR bit is set then clear it in the following cases: 468114001Sscottl * - interrupt is disabled 469114001Sscottl * - interrupt type is changed from level to edge or fsb. 470114001Sscottl * - interrupt routing is changed 471114001Sscottl * 472114001Sscottl * This is to ensure that this timer's level triggered interrupt does 473114001Sscottl * not remain asserted forever. 474114001Sscottl */ 475114001Sscottl if (vhpet->isr & (1 << n)) { 476114001Sscottl KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d", 477114001Sscottl n, old_pin)); 478114001Sscottl if (!vhpet_timer_interrupt_enabled(vhpet, n)) 479114001Sscottl clear_isr = true; 480114001Sscottl else if (vhpet_timer_msi_enabled(vhpet, n)) 481114001Sscottl clear_isr = true; 482114001Sscottl else if (vhpet_timer_edge_trig(vhpet, n)) 483114001Sscottl clear_isr = true; 484114001Sscottl else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin) 485114001Sscottl clear_isr = true; 486114001Sscottl else 487114001Sscottl clear_isr = false; 488114001Sscottl 489114001Sscottl if (clear_isr) { 490114001Sscottl VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to " 491114001Sscottl "configuration change", n); 492114001Sscottl vioapic_deassert_irq(vhpet->vm, old_pin); 493114001Sscottl vhpet->isr &= ~(1 << n); 494114001Sscottl } 495114001Sscottl } 496114001Sscottl} 497114001Sscottl 498114001Sscottlint 499114001Sscottlvhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size, 500114001Sscottl void *arg) 501114001Sscottl{ 502114001Sscottl struct vhpet *vhpet; 503114001Sscottl uint64_t data, mask, oldval, val64; 504114001Sscottl uint32_t isr_clear_mask, old_compval, old_comprate, counter; 505114001Sscottl sbintime_t now, *nowptr; 506114001Sscottl int i, offset; 507114001Sscottl 508114001Sscottl vhpet = vm_hpet(vm); 509114001Sscottl offset = gpa - VHPET_BASE; 510114001Sscottl 511114001Sscottl VHPET_LOCK(vhpet); 512114001Sscottl 51389580Smsmith /* Accesses to the HPET should be 4 or 8 bytes wide */ 51489580Smsmith switch (size) { 51589580Smsmith case 8: 51689580Smsmith mask = 0xffffffffffffffff; 51789580Smsmith data = val; 51889580Smsmith break; 51989580Smsmith case 4: 52089580Smsmith mask = 0xffffffff; 52189580Smsmith data = val; 52289580Smsmith if ((offset & 0x4) != 0) { 52389580Smsmith mask <<= 32; 52489580Smsmith data <<= 32; 52589580Smsmith } 52689580Smsmith break; 52789580Smsmith default: 52889580Smsmith VM_CTR2(vhpet->vm, "hpet invalid mmio write: " 52989580Smsmith "offset 0x%08x, size %d", offset, size); 53089580Smsmith goto done; 53189580Smsmith } 53289580Smsmith 53389580Smsmith /* Access to the HPET should be naturally aligned to its width */ 53489580Smsmith if (offset & (size - 1)) { 53589580Smsmith VM_CTR2(vhpet->vm, "hpet invalid mmio write: " 53689580Smsmith "offset 0x%08x, size %d", offset, size); 53789580Smsmith goto done; 53889580Smsmith } 53989580Smsmith 54089580Smsmith if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) { 54189580Smsmith /* 54289580Smsmith * Get the most recent value of the counter before updating 54389580Smsmith * the 'config' register. If the HPET is going to be disabled 54489580Smsmith * then we need to update 'countbase' with the value right 54589580Smsmith * before it is disabled. 54689580Smsmith */ 54789580Smsmith nowptr = vhpet_counter_enabled(vhpet) ? &now : NULL; 54889580Smsmith counter = vhpet_counter(vhpet, nowptr); 54989580Smsmith oldval = vhpet->config; 55089580Smsmith update_register(&vhpet->config, data, mask); 55189580Smsmith if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) { 55289580Smsmith if (vhpet_counter_enabled(vhpet)) { 55389580Smsmith vhpet_start_counting(vhpet); 55489580Smsmith VM_CTR0(vhpet->vm, "hpet enabled"); 55589580Smsmith } else { 55689580Smsmith vhpet_stop_counting(vhpet, counter, now); 55789580Smsmith VM_CTR0(vhpet->vm, "hpet disabled"); 55889580Smsmith } 55989580Smsmith } 56089580Smsmith goto done; 56189580Smsmith } 56289580Smsmith 56389580Smsmith if (offset == HPET_ISR || offset == HPET_ISR + 4) { 56489580Smsmith isr_clear_mask = vhpet->isr & data; 56589580Smsmith for (i = 0; i < VHPET_NUM_TIMERS; i++) { 56689580Smsmith if ((isr_clear_mask & (1 << i)) != 0) { 56789580Smsmith VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i); 56889580Smsmith vhpet_timer_clear_isr(vhpet, i); 56989580Smsmith } 57089580Smsmith } 57189580Smsmith goto done; 57289580Smsmith } 57389580Smsmith 57489580Smsmith if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) { 57589580Smsmith /* Zero-extend the counter to 64-bits before updating it */ 57689580Smsmith val64 = vhpet_counter(vhpet, NULL); 57789580Smsmith update_register(&val64, data, mask); 57889580Smsmith vhpet->countbase = val64; 57989580Smsmith if (vhpet_counter_enabled(vhpet)) 58089580Smsmith vhpet_start_counting(vhpet); 58189580Smsmith goto done; 58289580Smsmith } 58389580Smsmith 58489580Smsmith for (i = 0; i < VHPET_NUM_TIMERS; i++) { 58589580Smsmith if (offset == HPET_TIMER_CAP_CNF(i) || 58689580Smsmith offset == HPET_TIMER_CAP_CNF(i) + 4) { 58789580Smsmith vhpet_timer_update_config(vhpet, i, data, mask); 58889580Smsmith break; 58989580Smsmith } 59089580Smsmith 59189580Smsmith if (offset == HPET_TIMER_COMPARATOR(i) || 59289580Smsmith offset == HPET_TIMER_COMPARATOR(i) + 4) { 593170872Sscottl old_compval = vhpet->timer[i].compval; 594275975Ssmh old_comprate = vhpet->timer[i].comprate; 59589580Smsmith if (vhpet_periodic_timer(vhpet, i)) { 59689580Smsmith /* 59789580Smsmith * In periodic mode writes to the comparator 59889580Smsmith * change the 'compval' register only if the 59989580Smsmith * HPET_TCNF_VAL_SET bit is set in the config 60089580Smsmith * register. 60189580Smsmith */ 602114001Sscottl val64 = vhpet->timer[i].comprate; 60389580Smsmith update_register(&val64, data, mask); 60489580Smsmith vhpet->timer[i].comprate = val64; 60589580Smsmith if ((vhpet->timer[i].cap_config & 60689580Smsmith HPET_TCNF_VAL_SET) != 0) { 60789580Smsmith vhpet->timer[i].compval = val64; 60889580Smsmith } 60989580Smsmith } else { 61089580Smsmith KASSERT(vhpet->timer[i].comprate == 0, 611130585Sphk ("vhpet one-shot timer %d has invalid " 612275975Ssmh "rate %u", i, vhpet->timer[i].comprate)); 61389580Smsmith val64 = vhpet->timer[i].compval; 61489580Smsmith update_register(&val64, data, mask); 615156139Sscottl vhpet->timer[i].compval = val64; 616156139Sscottl } 617156139Sscottl vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET; 61889580Smsmith 61989580Smsmith if (vhpet->timer[i].compval != old_compval || 620156139Sscottl vhpet->timer[i].comprate != old_comprate) { 62189580Smsmith if (vhpet_counter_enabled(vhpet)) { 62289580Smsmith counter = vhpet_counter(vhpet, &now); 62389580Smsmith vhpet_start_timer(vhpet, i, counter, 62489580Smsmith now); 62589580Smsmith } 62689580Smsmith } 62789580Smsmith break; 62889580Smsmith } 62989580Smsmith 63089580Smsmith if (offset == HPET_TIMER_FSB_VAL(i) || 63189580Smsmith offset == HPET_TIMER_FSB_ADDR(i)) { 63289580Smsmith update_register(&vhpet->timer[i].msireg, data, mask); 63389580Smsmith break; 63489580Smsmith } 63589580Smsmith } 63689580Smsmithdone: 63789580Smsmith VHPET_UNLOCK(vhpet); 63889580Smsmith return (0); 63989580Smsmith} 64089580Smsmith 64189580Smsmithint 64289580Smsmithvhpet_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, int size, 64389580Smsmith void *arg) 64489580Smsmith{ 64589580Smsmith int i, offset; 64689580Smsmith struct vhpet *vhpet; 64789580Smsmith uint64_t data; 64889580Smsmith 64989580Smsmith vhpet = vm_hpet(vm); 65089580Smsmith offset = gpa - VHPET_BASE; 65189580Smsmith 65289580Smsmith VHPET_LOCK(vhpet); 65389580Smsmith 65489580Smsmith /* Accesses to the HPET should be 4 or 8 bytes wide */ 65589580Smsmith if (size != 4 && size != 8) { 65689580Smsmith VM_CTR2(vhpet->vm, "hpet invalid mmio read: " 65789580Smsmith "offset 0x%08x, size %d", offset, size); 65889580Smsmith data = 0; 65989580Smsmith goto done; 66089580Smsmith } 661114001Sscottl 66289580Smsmith /* Access to the HPET should be naturally aligned to its width */ 66389580Smsmith if (offset & (size - 1)) { 66489580Smsmith VM_CTR2(vhpet->vm, "hpet invalid mmio read: " 66589580Smsmith "offset 0x%08x, size %d", offset, size); 66692739Salfred data = 0; 66792739Salfred goto done; 66892739Salfred } 66992739Salfred 67092739Salfred if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) { 67192739Salfred data = vhpet_capabilities(); 67289580Smsmith goto done; 67389580Smsmith } 67489580Smsmith 67589580Smsmith if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) { 67689580Smsmith data = vhpet->config; 67789580Smsmith goto done; 67889580Smsmith } 67989580Smsmith 68089580Smsmith if (offset == HPET_ISR || offset == HPET_ISR + 4) { 681156139Sscottl data = vhpet->isr; 682156139Sscottl goto done; 68389580Smsmith } 68489580Smsmith 68589580Smsmith if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) { 686275975Ssmh data = vhpet_counter(vhpet, NULL); 68789580Smsmith goto done; 68889580Smsmith } 68989580Smsmith 69089580Smsmith for (i = 0; i < VHPET_NUM_TIMERS; i++) { 69189580Smsmith if (offset == HPET_TIMER_CAP_CNF(i) || 69289580Smsmith offset == HPET_TIMER_CAP_CNF(i) + 4) { 69389580Smsmith data = vhpet->timer[i].cap_config; 69489580Smsmith break; 69589580Smsmith } 696156139Sscottl 697156139Sscottl if (offset == HPET_TIMER_COMPARATOR(i) || 69889580Smsmith offset == HPET_TIMER_COMPARATOR(i) + 4) { 69989580Smsmith data = vhpet->timer[i].compval; 70089580Smsmith break; 70189580Smsmith } 70292739Salfred 70392739Salfred if (offset == HPET_TIMER_FSB_VAL(i) || 70492739Salfred offset == HPET_TIMER_FSB_ADDR(i)) { 70592739Salfred data = vhpet->timer[i].msireg; 70689580Smsmith break; 707143063Sjoerg } 70889580Smsmith } 70992739Salfred 71092739Salfred if (i >= VHPET_NUM_TIMERS) 71192739Salfred data = 0; 71292739Salfreddone: 71389580Smsmith VHPET_UNLOCK(vhpet); 71489580Smsmith 715154648Srwatson if (size == 4) { 71689580Smsmith if (offset & 0x4) 71789580Smsmith data >>= 32; 71889580Smsmith } 71989580Smsmith *rval = data; 72089580Smsmith return (0); 721154648Srwatson} 72289580Smsmith 72389580Smsmithstruct vhpet * 72489580Smsmithvhpet_init(struct vm *vm) 72589580Smsmith{ 72689580Smsmith int i, pincount; 727154648Srwatson struct vhpet *vhpet; 72889580Smsmith uint64_t allowed_irqs; 729114001Sscottl struct vhpet_callout_arg *arg; 73089580Smsmith struct bintime bt; 73189580Smsmith 73289580Smsmith vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO); 733154648Srwatson vhpet->vm = vm; 73489580Smsmith mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF); 735114001Sscottl 73689580Smsmith FREQ2BT(HPET_FREQ, &bt); 73789580Smsmith vhpet->freq_sbt = bttosbt(bt); 73889580Smsmith 73989580Smsmith pincount = vioapic_pincount(vm); 74089580Smsmith if (pincount >= 24) 741275975Ssmh allowed_irqs = 0x00f00000; /* irqs 20, 21, 22 and 23 */ 742130585Sphk else 74389580Smsmith allowed_irqs = 0; 74489580Smsmith 74589580Smsmith /* 746275975Ssmh * Initialize HPET timer hardware state. 74789580Smsmith */ 74889580Smsmith for (i = 0; i < VHPET_NUM_TIMERS; i++) { 74989580Smsmith vhpet->timer[i].cap_config = allowed_irqs << 32; 75089580Smsmith vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT; 75189580Smsmith vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL; 75289580Smsmith 753 vhpet->timer[i].compval = 0xffffffff; 754 callout_init(&vhpet->timer[i].callout, 1); 755 756 arg = &vhpet->timer[i].arg; 757 arg->vhpet = vhpet; 758 arg->timer_num = i; 759 } 760 761 return (vhpet); 762} 763 764void 765vhpet_cleanup(struct vhpet *vhpet) 766{ 767 int i; 768 769 for (i = 0; i < VHPET_NUM_TIMERS; i++) 770 callout_drain(&vhpet->timer[i].callout); 771 772 free(vhpet, M_VHPET); 773} 774 775int 776vhpet_getcap(struct vm_hpet_cap *cap) 777{ 778 779 cap->capabilities = vhpet_capabilities(); 780 return (0); 781} 782