1258579Sneel/*- 2258579Sneel * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 3258579Sneel * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 4258579Sneel * All rights reserved. 5258579Sneel * 6258579Sneel * Redistribution and use in source and binary forms, with or without 7258579Sneel * modification, are permitted provided that the following conditions 8258579Sneel * are met: 9258579Sneel * 1. Redistributions of source code must retain the above copyright 10258579Sneel * notice, this list of conditions and the following disclaimer. 11258579Sneel * 2. Redistributions in binary form must reproduce the above copyright 12258579Sneel * notice, this list of conditions and the following disclaimer in the 13258579Sneel * documentation and/or other materials provided with the distribution. 14258579Sneel * 15258579Sneel * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 16258579Sneel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17258579Sneel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18258579Sneel * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 19258579Sneel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20258579Sneel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21258579Sneel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22258579Sneel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23258579Sneel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24258579Sneel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25258579Sneel * SUCH DAMAGE. 26258579Sneel * 27258579Sneel * $FreeBSD$ 28258579Sneel */ 29258579Sneel 30258579Sneel#include <sys/cdefs.h> 31258579Sneel__FBSDID("$FreeBSD$"); 32258579Sneel 33258579Sneel#include <sys/param.h> 34258579Sneel#include <sys/lock.h> 35258579Sneel#include <sys/mutex.h> 36258579Sneel#include <sys/kernel.h> 37258579Sneel#include <sys/malloc.h> 38258579Sneel#include <sys/systm.h> 39258579Sneel 40258579Sneel#include <dev/acpica/acpi_hpet.h> 41258579Sneel 42258579Sneel#include <machine/vmm.h> 43258579Sneel#include <machine/vmm_dev.h> 44258579Sneel 45258579Sneel#include "vmm_lapic.h" 46268891Sjhb#include "vatpic.h" 47258579Sneel#include "vioapic.h" 48258579Sneel#include "vhpet.h" 49258579Sneel 50258579Sneel#include "vmm_ktr.h" 51258579Sneel 52258579Sneelstatic MALLOC_DEFINE(M_VHPET, "vhpet", "bhyve virtual hpet"); 53258579Sneel 54258579Sneel#define HPET_FREQ 10000000 /* 10.0 Mhz */ 55258579Sneel#define FS_PER_S 1000000000000000ul 56258579Sneel 57258579Sneel/* Timer N Configuration and Capabilities Register */ 58258579Sneel#define HPET_TCAP_RO_MASK (HPET_TCAP_INT_ROUTE | \ 59258579Sneel HPET_TCAP_FSB_INT_DEL | \ 60258579Sneel HPET_TCAP_SIZE | \ 61258579Sneel HPET_TCAP_PER_INT) 62258579Sneel/* 63258579Sneel * HPET requires at least 3 timers and up to 32 timers per block. 64258579Sneel */ 65258579Sneel#define VHPET_NUM_TIMERS 8 66258579SneelCTASSERT(VHPET_NUM_TIMERS >= 3 && VHPET_NUM_TIMERS <= 32); 67258579Sneel 68258579Sneelstruct vhpet_callout_arg { 69258579Sneel struct vhpet *vhpet; 70258579Sneel int timer_num; 71258579Sneel}; 72258579Sneel 73258579Sneelstruct vhpet { 74258579Sneel struct vm *vm; 75258579Sneel struct mtx mtx; 76258579Sneel sbintime_t freq_sbt; 77258579Sneel 78258579Sneel uint64_t config; /* Configuration */ 79258579Sneel uint64_t isr; /* Interrupt Status */ 80266477Sjhb uint32_t countbase; /* HPET counter base value */ 81266477Sjhb sbintime_t countbase_sbt; /* uptime corresponding to base value */ 82258579Sneel 83258579Sneel struct { 84258579Sneel uint64_t cap_config; /* Configuration */ 85258579Sneel uint64_t msireg; /* FSB interrupt routing */ 86258579Sneel uint32_t compval; /* Comparator */ 87258579Sneel uint32_t comprate; 88258579Sneel struct callout callout; 89266477Sjhb sbintime_t callout_sbt; /* time when counter==compval */ 90258579Sneel struct vhpet_callout_arg arg; 91258579Sneel } timer[VHPET_NUM_TIMERS]; 92258579Sneel}; 93258579Sneel 94258579Sneel#define VHPET_LOCK(vhp) mtx_lock(&((vhp)->mtx)) 95258579Sneel#define VHPET_UNLOCK(vhp) mtx_unlock(&((vhp)->mtx)) 96258579Sneel 97266477Sjhbstatic void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, 98266477Sjhb sbintime_t now); 99266477Sjhb 100258579Sneelstatic uint64_t 101258579Sneelvhpet_capabilities(void) 102258579Sneel{ 103258579Sneel uint64_t cap = 0; 104258579Sneel 105258579Sneel cap |= 0x8086 << 16; /* vendor id */ 106258579Sneel cap |= (VHPET_NUM_TIMERS - 1) << 8; /* number of timers */ 107258579Sneel cap |= 1; /* revision */ 108258579Sneel cap &= ~HPET_CAP_COUNT_SIZE; /* 32-bit timer */ 109258579Sneel 110258579Sneel cap &= 0xffffffff; 111258579Sneel cap |= (FS_PER_S / HPET_FREQ) << 32; /* tick period in fs */ 112258579Sneel 113258579Sneel return (cap); 114258579Sneel} 115258579Sneel 116258579Sneelstatic __inline bool 117258579Sneelvhpet_counter_enabled(struct vhpet *vhpet) 118258579Sneel{ 119258579Sneel 120258579Sneel return ((vhpet->config & HPET_CNF_ENABLE) ? true : false); 121258579Sneel} 122258579Sneel 123258579Sneelstatic __inline bool 124258579Sneelvhpet_timer_msi_enabled(struct vhpet *vhpet, int n) 125258579Sneel{ 126258579Sneel const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN; 127258579Sneel 128258579Sneel if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable) 129258579Sneel return (true); 130258579Sneel else 131258579Sneel return (false); 132258579Sneel} 133258579Sneel 134258579Sneelstatic __inline int 135258579Sneelvhpet_timer_ioapic_pin(struct vhpet *vhpet, int n) 136258579Sneel{ 137258579Sneel /* 138258579Sneel * If the timer is configured to use MSI then treat it as if the 139258579Sneel * timer is not connected to the ioapic. 140258579Sneel */ 141258579Sneel if (vhpet_timer_msi_enabled(vhpet, n)) 142258579Sneel return (0); 143258579Sneel 144258579Sneel return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9); 145258579Sneel} 146258579Sneel 147258579Sneelstatic uint32_t 148266477Sjhbvhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr) 149258579Sneel{ 150258579Sneel uint32_t val; 151266477Sjhb sbintime_t now, delta; 152258579Sneel 153266477Sjhb val = vhpet->countbase; 154258579Sneel if (vhpet_counter_enabled(vhpet)) { 155266477Sjhb now = sbinuptime(); 156266477Sjhb delta = now - vhpet->countbase_sbt; 157266477Sjhb KASSERT(delta >= 0, ("vhpet_counter: uptime went backwards: " 158266477Sjhb "%#lx to %#lx", vhpet->countbase_sbt, now)); 159266477Sjhb val += delta / vhpet->freq_sbt; 160266477Sjhb if (nowptr != NULL) 161266477Sjhb *nowptr = now; 162266477Sjhb } else { 163258579Sneel /* 164266477Sjhb * The sbinuptime corresponding to the 'countbase' is 165266477Sjhb * meaningless when the counter is disabled. Make sure 166266477Sjhb * that the the caller doesn't want to use it. 167258579Sneel */ 168266477Sjhb KASSERT(nowptr == NULL, ("vhpet_counter: nowptr must be NULL")); 169258579Sneel } 170258579Sneel return (val); 171258579Sneel} 172258579Sneel 173258579Sneelstatic void 174258579Sneelvhpet_timer_clear_isr(struct vhpet *vhpet, int n) 175258579Sneel{ 176284894Sneel int pin; 177258579Sneel 178258579Sneel if (vhpet->isr & (1 << n)) { 179258579Sneel pin = vhpet_timer_ioapic_pin(vhpet, n); 180258579Sneel KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n)); 181258579Sneel vioapic_deassert_irq(vhpet->vm, pin); 182258579Sneel vhpet->isr &= ~(1 << n); 183258579Sneel } 184258579Sneel} 185258579Sneel 186258579Sneelstatic __inline bool 187258579Sneelvhpet_periodic_timer(struct vhpet *vhpet, int n) 188258579Sneel{ 189258579Sneel 190258579Sneel return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0); 191258579Sneel} 192258579Sneel 193258579Sneelstatic __inline bool 194258579Sneelvhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n) 195258579Sneel{ 196258579Sneel 197258579Sneel return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0); 198258579Sneel} 199258579Sneel 200258579Sneelstatic __inline bool 201258579Sneelvhpet_timer_edge_trig(struct vhpet *vhpet, int n) 202258579Sneel{ 203258579Sneel 204258579Sneel KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: " 205258579Sneel "timer %d is using MSI", n)); 206258579Sneel 207258579Sneel if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0) 208258579Sneel return (true); 209258579Sneel else 210258579Sneel return (false); 211258579Sneel} 212258579Sneel 213258579Sneelstatic void 214258579Sneelvhpet_timer_interrupt(struct vhpet *vhpet, int n) 215258579Sneel{ 216284894Sneel int pin; 217258579Sneel 218258579Sneel /* If interrupts are not enabled for this timer then just return. */ 219258579Sneel if (!vhpet_timer_interrupt_enabled(vhpet, n)) 220258579Sneel return; 221258579Sneel 222258579Sneel /* 223258579Sneel * If a level triggered interrupt is already asserted then just return. 224258579Sneel */ 225258579Sneel if ((vhpet->isr & (1 << n)) != 0) { 226258579Sneel VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n); 227258579Sneel return; 228258579Sneel } 229258579Sneel 230258579Sneel if (vhpet_timer_msi_enabled(vhpet, n)) { 231262350Sjhb lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32, 232262350Sjhb vhpet->timer[n].msireg & 0xffffffff); 233258579Sneel return; 234258579Sneel } 235258579Sneel 236258579Sneel pin = vhpet_timer_ioapic_pin(vhpet, n); 237258579Sneel if (pin == 0) { 238258579Sneel VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n); 239258579Sneel return; 240258579Sneel } 241258579Sneel 242258579Sneel if (vhpet_timer_edge_trig(vhpet, n)) { 243258579Sneel vioapic_pulse_irq(vhpet->vm, pin); 244258579Sneel } else { 245258579Sneel vhpet->isr |= 1 << n; 246258579Sneel vioapic_assert_irq(vhpet->vm, pin); 247258579Sneel } 248258579Sneel} 249258579Sneel 250258579Sneelstatic void 251258579Sneelvhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter) 252258579Sneel{ 253258579Sneel uint32_t compval, comprate, compnext; 254258579Sneel 255258579Sneel KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n)); 256258579Sneel 257258579Sneel compval = vhpet->timer[n].compval; 258258579Sneel comprate = vhpet->timer[n].comprate; 259258579Sneel 260258579Sneel /* 261258579Sneel * Calculate the comparator value to be used for the next periodic 262258579Sneel * interrupt. 263258579Sneel * 264258579Sneel * This function is commonly called from the callout handler. 265258579Sneel * In this scenario the 'counter' is ahead of 'compval'. To find 266258579Sneel * the next value to program into the accumulator we divide the 267258579Sneel * number space between 'compval' and 'counter' into 'comprate' 268258579Sneel * sized units. The 'compval' is rounded up such that is "ahead" 269258579Sneel * of 'counter'. 270258579Sneel */ 271258579Sneel compnext = compval + ((counter - compval) / comprate + 1) * comprate; 272258579Sneel 273258579Sneel vhpet->timer[n].compval = compnext; 274258579Sneel} 275258579Sneel 276258579Sneelstatic void 277258579Sneelvhpet_handler(void *a) 278258579Sneel{ 279258579Sneel int n; 280258579Sneel uint32_t counter; 281266477Sjhb sbintime_t now; 282258579Sneel struct vhpet *vhpet; 283258579Sneel struct callout *callout; 284258579Sneel struct vhpet_callout_arg *arg; 285258579Sneel 286258579Sneel arg = a; 287258579Sneel vhpet = arg->vhpet; 288258579Sneel n = arg->timer_num; 289258579Sneel callout = &vhpet->timer[n].callout; 290258579Sneel 291258579Sneel VM_CTR1(vhpet->vm, "hpet t%d fired", n); 292258579Sneel 293258579Sneel VHPET_LOCK(vhpet); 294258579Sneel 295258579Sneel if (callout_pending(callout)) /* callout was reset */ 296258579Sneel goto done; 297258579Sneel 298258579Sneel if (!callout_active(callout)) /* callout was stopped */ 299258579Sneel goto done; 300258579Sneel 301258579Sneel callout_deactivate(callout); 302258579Sneel 303258579Sneel if (!vhpet_counter_enabled(vhpet)) 304258579Sneel panic("vhpet(%p) callout with counter disabled", vhpet); 305258579Sneel 306266477Sjhb counter = vhpet_counter(vhpet, &now); 307266477Sjhb vhpet_start_timer(vhpet, n, counter, now); 308258579Sneel vhpet_timer_interrupt(vhpet, n); 309258579Sneeldone: 310258579Sneel VHPET_UNLOCK(vhpet); 311258579Sneel return; 312258579Sneel} 313258579Sneel 314258579Sneelstatic void 315266477Sjhbvhpet_stop_timer(struct vhpet *vhpet, int n, sbintime_t now) 316258579Sneel{ 317258579Sneel 318266477Sjhb VM_CTR1(vhpet->vm, "hpet t%d stopped", n); 319258579Sneel callout_stop(&vhpet->timer[n].callout); 320266477Sjhb 321266477Sjhb /* 322266477Sjhb * If the callout was scheduled to expire in the past but hasn't 323266477Sjhb * had a chance to execute yet then trigger the timer interrupt 324266477Sjhb * here. Failing to do so will result in a missed timer interrupt 325266477Sjhb * in the guest. This is especially bad in one-shot mode because 326266477Sjhb * the next interrupt has to wait for the counter to wrap around. 327266477Sjhb */ 328266477Sjhb if (vhpet->timer[n].callout_sbt < now) { 329266477Sjhb VM_CTR1(vhpet->vm, "hpet t%d interrupt triggered after " 330266477Sjhb "stopping timer", n); 331266477Sjhb vhpet_timer_interrupt(vhpet, n); 332266477Sjhb } 333258579Sneel} 334258579Sneel 335258579Sneelstatic void 336266477Sjhbvhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, sbintime_t now) 337258579Sneel{ 338266477Sjhb sbintime_t delta, precision; 339258579Sneel 340258579Sneel if (vhpet->timer[n].comprate != 0) 341258579Sneel vhpet_adjust_compval(vhpet, n, counter); 342266477Sjhb else { 343266477Sjhb /* 344266477Sjhb * In one-shot mode it is the guest's responsibility to make 345266477Sjhb * sure that the comparator value is not in the "past". The 346266477Sjhb * hardware doesn't have any belt-and-suspenders to deal with 347266477Sjhb * this so we don't either. 348266477Sjhb */ 349258579Sneel } 350258579Sneel 351266477Sjhb delta = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt; 352266477Sjhb precision = delta >> tc_precexp; 353266477Sjhb vhpet->timer[n].callout_sbt = now + delta; 354266477Sjhb callout_reset_sbt(&vhpet->timer[n].callout, vhpet->timer[n].callout_sbt, 355266477Sjhb precision, vhpet_handler, &vhpet->timer[n].arg, C_ABSOLUTE); 356258579Sneel} 357258579Sneel 358258579Sneelstatic void 359258579Sneelvhpet_start_counting(struct vhpet *vhpet) 360258579Sneel{ 361258579Sneel int i; 362258579Sneel 363266477Sjhb vhpet->countbase_sbt = sbinuptime(); 364266477Sjhb for (i = 0; i < VHPET_NUM_TIMERS; i++) { 365266477Sjhb /* 366266477Sjhb * Restart the timers based on the value of the main counter 367266477Sjhb * when it stopped counting. 368266477Sjhb */ 369266477Sjhb vhpet_start_timer(vhpet, i, vhpet->countbase, 370266477Sjhb vhpet->countbase_sbt); 371266477Sjhb } 372258579Sneel} 373258579Sneel 374258579Sneelstatic void 375266477Sjhbvhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, sbintime_t now) 376258579Sneel{ 377258579Sneel int i; 378258579Sneel 379266477Sjhb vhpet->countbase = counter; 380258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) 381266477Sjhb vhpet_stop_timer(vhpet, i, now); 382258579Sneel} 383258579Sneel 384258579Sneelstatic __inline void 385258579Sneelupdate_register(uint64_t *regptr, uint64_t data, uint64_t mask) 386258579Sneel{ 387258579Sneel 388258579Sneel *regptr &= ~mask; 389258579Sneel *regptr |= (data & mask); 390258579Sneel} 391258579Sneel 392258579Sneelstatic void 393258579Sneelvhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data, 394258579Sneel uint64_t mask) 395258579Sneel{ 396258579Sneel bool clear_isr; 397258579Sneel int old_pin, new_pin; 398258579Sneel uint32_t allowed_irqs; 399258579Sneel uint64_t oldval, newval; 400258579Sneel 401258579Sneel if (vhpet_timer_msi_enabled(vhpet, n) || 402258579Sneel vhpet_timer_edge_trig(vhpet, n)) { 403258579Sneel if (vhpet->isr & (1 << n)) 404258579Sneel panic("vhpet timer %d isr should not be asserted", n); 405258579Sneel } 406258579Sneel old_pin = vhpet_timer_ioapic_pin(vhpet, n); 407258579Sneel oldval = vhpet->timer[n].cap_config; 408258579Sneel 409258579Sneel newval = oldval; 410258579Sneel update_register(&newval, data, mask); 411258579Sneel newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE); 412258579Sneel newval |= oldval & HPET_TCAP_RO_MASK; 413258579Sneel 414258579Sneel if (newval == oldval) 415258579Sneel return; 416258579Sneel 417258579Sneel vhpet->timer[n].cap_config = newval; 418258579Sneel VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval); 419258579Sneel 420258579Sneel /* 421258579Sneel * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field. 422258579Sneel * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set 423258579Sneel * it to the default value of 0. 424258579Sneel */ 425258579Sneel allowed_irqs = vhpet->timer[n].cap_config >> 32; 426258579Sneel new_pin = vhpet_timer_ioapic_pin(vhpet, n); 427258579Sneel if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) { 428258579Sneel VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, " 429258579Sneel "allowed_irqs 0x%08x", n, new_pin, allowed_irqs); 430258579Sneel new_pin = 0; 431258579Sneel vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE; 432258579Sneel } 433258579Sneel 434258579Sneel if (!vhpet_periodic_timer(vhpet, n)) 435258579Sneel vhpet->timer[n].comprate = 0; 436258579Sneel 437258579Sneel /* 438258579Sneel * If the timer's ISR bit is set then clear it in the following cases: 439258579Sneel * - interrupt is disabled 440258579Sneel * - interrupt type is changed from level to edge or fsb. 441258579Sneel * - interrupt routing is changed 442258579Sneel * 443258579Sneel * This is to ensure that this timer's level triggered interrupt does 444258579Sneel * not remain asserted forever. 445258579Sneel */ 446258579Sneel if (vhpet->isr & (1 << n)) { 447258579Sneel KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d", 448258579Sneel n, old_pin)); 449258579Sneel if (!vhpet_timer_interrupt_enabled(vhpet, n)) 450258579Sneel clear_isr = true; 451258579Sneel else if (vhpet_timer_msi_enabled(vhpet, n)) 452258579Sneel clear_isr = true; 453258579Sneel else if (vhpet_timer_edge_trig(vhpet, n)) 454258579Sneel clear_isr = true; 455258579Sneel else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin) 456258579Sneel clear_isr = true; 457258579Sneel else 458258579Sneel clear_isr = false; 459258579Sneel 460258579Sneel if (clear_isr) { 461258579Sneel VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to " 462258579Sneel "configuration change", n); 463258579Sneel vioapic_deassert_irq(vhpet->vm, old_pin); 464258579Sneel vhpet->isr &= ~(1 << n); 465258579Sneel } 466258579Sneel } 467258579Sneel} 468258579Sneel 469258579Sneelint 470258579Sneelvhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size, 471258579Sneel void *arg) 472258579Sneel{ 473258579Sneel struct vhpet *vhpet; 474258579Sneel uint64_t data, mask, oldval, val64; 475266477Sjhb uint32_t isr_clear_mask, old_compval, old_comprate, counter; 476266477Sjhb sbintime_t now, *nowptr; 477258579Sneel int i, offset; 478258579Sneel 479258579Sneel vhpet = vm_hpet(vm); 480258579Sneel offset = gpa - VHPET_BASE; 481258579Sneel 482258579Sneel VHPET_LOCK(vhpet); 483258579Sneel 484258579Sneel /* Accesses to the HPET should be 4 or 8 bytes wide */ 485258579Sneel switch (size) { 486258579Sneel case 8: 487258579Sneel mask = 0xffffffffffffffff; 488258579Sneel data = val; 489258579Sneel break; 490258579Sneel case 4: 491258579Sneel mask = 0xffffffff; 492258579Sneel data = val; 493258579Sneel if ((offset & 0x4) != 0) { 494258579Sneel mask <<= 32; 495258579Sneel data <<= 32; 496258579Sneel } 497258579Sneel break; 498258579Sneel default: 499258579Sneel VM_CTR2(vhpet->vm, "hpet invalid mmio write: " 500258579Sneel "offset 0x%08x, size %d", offset, size); 501258579Sneel goto done; 502258579Sneel } 503258579Sneel 504258579Sneel /* Access to the HPET should be naturally aligned to its width */ 505258579Sneel if (offset & (size - 1)) { 506258579Sneel VM_CTR2(vhpet->vm, "hpet invalid mmio write: " 507258579Sneel "offset 0x%08x, size %d", offset, size); 508258579Sneel goto done; 509258579Sneel } 510258579Sneel 511258579Sneel if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) { 512266477Sjhb /* 513266477Sjhb * Get the most recent value of the counter before updating 514266477Sjhb * the 'config' register. If the HPET is going to be disabled 515266477Sjhb * then we need to update 'countbase' with the value right 516266477Sjhb * before it is disabled. 517266477Sjhb */ 518266477Sjhb nowptr = vhpet_counter_enabled(vhpet) ? &now : NULL; 519266477Sjhb counter = vhpet_counter(vhpet, nowptr); 520258579Sneel oldval = vhpet->config; 521258579Sneel update_register(&vhpet->config, data, mask); 522284894Sneel 523284894Sneel /* 524284894Sneel * LegacyReplacement Routing is not supported so clear the 525284894Sneel * bit explicitly. 526284894Sneel */ 527284894Sneel vhpet->config &= ~HPET_CNF_LEG_RT; 528284894Sneel 529258579Sneel if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) { 530258579Sneel if (vhpet_counter_enabled(vhpet)) { 531258579Sneel vhpet_start_counting(vhpet); 532258579Sneel VM_CTR0(vhpet->vm, "hpet enabled"); 533258579Sneel } else { 534266477Sjhb vhpet_stop_counting(vhpet, counter, now); 535258579Sneel VM_CTR0(vhpet->vm, "hpet disabled"); 536258579Sneel } 537258579Sneel } 538258579Sneel goto done; 539258579Sneel } 540258579Sneel 541258579Sneel if (offset == HPET_ISR || offset == HPET_ISR + 4) { 542258579Sneel isr_clear_mask = vhpet->isr & data; 543258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) { 544258579Sneel if ((isr_clear_mask & (1 << i)) != 0) { 545258579Sneel VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i); 546258579Sneel vhpet_timer_clear_isr(vhpet, i); 547258579Sneel } 548258579Sneel } 549258579Sneel goto done; 550258579Sneel } 551258579Sneel 552258579Sneel if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) { 553258579Sneel /* Zero-extend the counter to 64-bits before updating it */ 554266477Sjhb val64 = vhpet_counter(vhpet, NULL); 555258579Sneel update_register(&val64, data, mask); 556266477Sjhb vhpet->countbase = val64; 557258579Sneel if (vhpet_counter_enabled(vhpet)) 558258579Sneel vhpet_start_counting(vhpet); 559258579Sneel goto done; 560258579Sneel } 561258579Sneel 562258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) { 563258579Sneel if (offset == HPET_TIMER_CAP_CNF(i) || 564258579Sneel offset == HPET_TIMER_CAP_CNF(i) + 4) { 565258579Sneel vhpet_timer_update_config(vhpet, i, data, mask); 566258579Sneel break; 567258579Sneel } 568258579Sneel 569258579Sneel if (offset == HPET_TIMER_COMPARATOR(i) || 570258579Sneel offset == HPET_TIMER_COMPARATOR(i) + 4) { 571258579Sneel old_compval = vhpet->timer[i].compval; 572258579Sneel old_comprate = vhpet->timer[i].comprate; 573258579Sneel if (vhpet_periodic_timer(vhpet, i)) { 574258579Sneel /* 575258579Sneel * In periodic mode writes to the comparator 576258579Sneel * change the 'compval' register only if the 577258579Sneel * HPET_TCNF_VAL_SET bit is set in the config 578258579Sneel * register. 579258579Sneel */ 580258579Sneel val64 = vhpet->timer[i].comprate; 581258579Sneel update_register(&val64, data, mask); 582258579Sneel vhpet->timer[i].comprate = val64; 583258579Sneel if ((vhpet->timer[i].cap_config & 584258579Sneel HPET_TCNF_VAL_SET) != 0) { 585258579Sneel vhpet->timer[i].compval = val64; 586258579Sneel } 587258579Sneel } else { 588258579Sneel KASSERT(vhpet->timer[i].comprate == 0, 589258579Sneel ("vhpet one-shot timer %d has invalid " 590258579Sneel "rate %u", i, vhpet->timer[i].comprate)); 591258579Sneel val64 = vhpet->timer[i].compval; 592258579Sneel update_register(&val64, data, mask); 593258579Sneel vhpet->timer[i].compval = val64; 594258579Sneel } 595258579Sneel vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET; 596258579Sneel 597258579Sneel if (vhpet->timer[i].compval != old_compval || 598258579Sneel vhpet->timer[i].comprate != old_comprate) { 599266477Sjhb if (vhpet_counter_enabled(vhpet)) { 600266477Sjhb counter = vhpet_counter(vhpet, &now); 601266477Sjhb vhpet_start_timer(vhpet, i, counter, 602266477Sjhb now); 603266477Sjhb } 604258579Sneel } 605258579Sneel break; 606258579Sneel } 607258579Sneel 608258579Sneel if (offset == HPET_TIMER_FSB_VAL(i) || 609258579Sneel offset == HPET_TIMER_FSB_ADDR(i)) { 610258579Sneel update_register(&vhpet->timer[i].msireg, data, mask); 611258579Sneel break; 612258579Sneel } 613258579Sneel } 614258579Sneeldone: 615258579Sneel VHPET_UNLOCK(vhpet); 616258579Sneel return (0); 617258579Sneel} 618258579Sneel 619258579Sneelint 620258579Sneelvhpet_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, int size, 621258579Sneel void *arg) 622258579Sneel{ 623258579Sneel int i, offset; 624258579Sneel struct vhpet *vhpet; 625258579Sneel uint64_t data; 626258579Sneel 627258579Sneel vhpet = vm_hpet(vm); 628258579Sneel offset = gpa - VHPET_BASE; 629258579Sneel 630258579Sneel VHPET_LOCK(vhpet); 631258579Sneel 632258579Sneel /* Accesses to the HPET should be 4 or 8 bytes wide */ 633258579Sneel if (size != 4 && size != 8) { 634258579Sneel VM_CTR2(vhpet->vm, "hpet invalid mmio read: " 635258579Sneel "offset 0x%08x, size %d", offset, size); 636258579Sneel data = 0; 637258579Sneel goto done; 638258579Sneel } 639258579Sneel 640258579Sneel /* Access to the HPET should be naturally aligned to its width */ 641258579Sneel if (offset & (size - 1)) { 642258579Sneel VM_CTR2(vhpet->vm, "hpet invalid mmio read: " 643258579Sneel "offset 0x%08x, size %d", offset, size); 644258579Sneel data = 0; 645258579Sneel goto done; 646258579Sneel } 647258579Sneel 648258579Sneel if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) { 649258579Sneel data = vhpet_capabilities(); 650258579Sneel goto done; 651258579Sneel } 652258579Sneel 653258579Sneel if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) { 654258579Sneel data = vhpet->config; 655258579Sneel goto done; 656258579Sneel } 657258579Sneel 658258579Sneel if (offset == HPET_ISR || offset == HPET_ISR + 4) { 659258579Sneel data = vhpet->isr; 660258579Sneel goto done; 661258579Sneel } 662258579Sneel 663258579Sneel if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) { 664266477Sjhb data = vhpet_counter(vhpet, NULL); 665258579Sneel goto done; 666258579Sneel } 667258579Sneel 668258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) { 669258579Sneel if (offset == HPET_TIMER_CAP_CNF(i) || 670258579Sneel offset == HPET_TIMER_CAP_CNF(i) + 4) { 671258579Sneel data = vhpet->timer[i].cap_config; 672258579Sneel break; 673258579Sneel } 674258579Sneel 675258579Sneel if (offset == HPET_TIMER_COMPARATOR(i) || 676258579Sneel offset == HPET_TIMER_COMPARATOR(i) + 4) { 677258579Sneel data = vhpet->timer[i].compval; 678258579Sneel break; 679258579Sneel } 680258579Sneel 681258579Sneel if (offset == HPET_TIMER_FSB_VAL(i) || 682258579Sneel offset == HPET_TIMER_FSB_ADDR(i)) { 683258579Sneel data = vhpet->timer[i].msireg; 684258579Sneel break; 685258579Sneel } 686258579Sneel } 687258579Sneel 688258579Sneel if (i >= VHPET_NUM_TIMERS) 689258579Sneel data = 0; 690258579Sneeldone: 691258579Sneel VHPET_UNLOCK(vhpet); 692258579Sneel 693258579Sneel if (size == 4) { 694258579Sneel if (offset & 0x4) 695258579Sneel data >>= 32; 696258579Sneel } 697258579Sneel *rval = data; 698258579Sneel return (0); 699258579Sneel} 700258579Sneel 701258579Sneelstruct vhpet * 702258579Sneelvhpet_init(struct vm *vm) 703258579Sneel{ 704261088Sjhb int i, pincount; 705258579Sneel struct vhpet *vhpet; 706261088Sjhb uint64_t allowed_irqs; 707258579Sneel struct vhpet_callout_arg *arg; 708258579Sneel struct bintime bt; 709258579Sneel 710258579Sneel vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO); 711258579Sneel vhpet->vm = vm; 712258579Sneel mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF); 713258579Sneel 714258579Sneel FREQ2BT(HPET_FREQ, &bt); 715258579Sneel vhpet->freq_sbt = bttosbt(bt); 716258579Sneel 717261088Sjhb pincount = vioapic_pincount(vm); 718261088Sjhb if (pincount >= 24) 719261088Sjhb allowed_irqs = 0x00f00000; /* irqs 20, 21, 22 and 23 */ 720261088Sjhb else 721261088Sjhb allowed_irqs = 0; 722261088Sjhb 723258579Sneel /* 724258579Sneel * Initialize HPET timer hardware state. 725258579Sneel */ 726258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) { 727261088Sjhb vhpet->timer[i].cap_config = allowed_irqs << 32; 728261088Sjhb vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT; 729261088Sjhb vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL; 730261088Sjhb 731258579Sneel vhpet->timer[i].compval = 0xffffffff; 732258579Sneel callout_init(&vhpet->timer[i].callout, 1); 733258579Sneel 734258579Sneel arg = &vhpet->timer[i].arg; 735258579Sneel arg->vhpet = vhpet; 736258579Sneel arg->timer_num = i; 737258579Sneel } 738258579Sneel 739258579Sneel return (vhpet); 740258579Sneel} 741258579Sneel 742258579Sneelvoid 743258579Sneelvhpet_cleanup(struct vhpet *vhpet) 744258579Sneel{ 745258579Sneel int i; 746258579Sneel 747258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) 748258579Sneel callout_drain(&vhpet->timer[i].callout); 749258579Sneel 750258579Sneel free(vhpet, M_VHPET); 751258579Sneel} 752258579Sneel 753258579Sneelint 754258579Sneelvhpet_getcap(struct vm_hpet_cap *cap) 755258579Sneel{ 756258579Sneel 757258579Sneel cap->capabilities = vhpet_capabilities(); 758258579Sneel return (0); 759258579Sneel} 760