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: stable/11/sys/amd64/vmm/io/vhpet.c 327651 2018-01-06 22:56:48Z ian $ 28258579Sneel */ 29258579Sneel 30258579Sneel#include <sys/cdefs.h> 31258579Sneel__FBSDID("$FreeBSD: stable/11/sys/amd64/vmm/io/vhpet.c 327651 2018-01-06 22:56:48Z ian $"); 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" 46263035Stychon#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 54327651Sian#define HPET_FREQ 16777216 /* 16.7 (2^24) 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 */ 80260237Sneel uint32_t countbase; /* HPET counter base value */ 81260237Sneel 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; 89260237Sneel 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 97260237Sneelstatic void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, 98260237Sneel sbintime_t now); 99260237Sneel 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 148260237Sneelvhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr) 149258579Sneel{ 150258579Sneel uint32_t val; 151260237Sneel sbintime_t now, delta; 152258579Sneel 153260237Sneel val = vhpet->countbase; 154258579Sneel if (vhpet_counter_enabled(vhpet)) { 155260237Sneel now = sbinuptime(); 156260237Sneel delta = now - vhpet->countbase_sbt; 157260237Sneel KASSERT(delta >= 0, ("vhpet_counter: uptime went backwards: " 158260237Sneel "%#lx to %#lx", vhpet->countbase_sbt, now)); 159260237Sneel val += delta / vhpet->freq_sbt; 160260237Sneel if (nowptr != NULL) 161260237Sneel *nowptr = now; 162260237Sneel } else { 163258579Sneel /* 164260237Sneel * The sbinuptime corresponding to the 'countbase' is 165260237Sneel * meaningless when the counter is disabled. Make sure 166300050Seadler * that the caller doesn't want to use it. 167258579Sneel */ 168260237Sneel 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{ 176276428Sneel 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{ 216276428Sneel 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)) { 231259482Sneel lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32, 232259482Sneel 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; 281260237Sneel 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 306260237Sneel counter = vhpet_counter(vhpet, &now); 307260237Sneel vhpet_start_timer(vhpet, n, counter, now); 308258579Sneel vhpet_timer_interrupt(vhpet, n); 309258579Sneeldone: 310258579Sneel VHPET_UNLOCK(vhpet); 311258579Sneel return; 312258579Sneel} 313258579Sneel 314258579Sneelstatic void 315260237Sneelvhpet_stop_timer(struct vhpet *vhpet, int n, sbintime_t now) 316258579Sneel{ 317258579Sneel 318260237Sneel VM_CTR1(vhpet->vm, "hpet t%d stopped", n); 319258579Sneel callout_stop(&vhpet->timer[n].callout); 320260237Sneel 321260237Sneel /* 322260237Sneel * If the callout was scheduled to expire in the past but hasn't 323260237Sneel * had a chance to execute yet then trigger the timer interrupt 324260237Sneel * here. Failing to do so will result in a missed timer interrupt 325260237Sneel * in the guest. This is especially bad in one-shot mode because 326260237Sneel * the next interrupt has to wait for the counter to wrap around. 327260237Sneel */ 328260237Sneel if (vhpet->timer[n].callout_sbt < now) { 329260237Sneel VM_CTR1(vhpet->vm, "hpet t%d interrupt triggered after " 330260237Sneel "stopping timer", n); 331260237Sneel vhpet_timer_interrupt(vhpet, n); 332260237Sneel } 333258579Sneel} 334258579Sneel 335258579Sneelstatic void 336260237Sneelvhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, sbintime_t now) 337258579Sneel{ 338260237Sneel sbintime_t delta, precision; 339258579Sneel 340258579Sneel if (vhpet->timer[n].comprate != 0) 341258579Sneel vhpet_adjust_compval(vhpet, n, counter); 342260237Sneel else { 343260237Sneel /* 344260237Sneel * In one-shot mode it is the guest's responsibility to make 345260237Sneel * sure that the comparator value is not in the "past". The 346260237Sneel * hardware doesn't have any belt-and-suspenders to deal with 347260237Sneel * this so we don't either. 348260237Sneel */ 349258579Sneel } 350258579Sneel 351260237Sneel delta = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt; 352260237Sneel precision = delta >> tc_precexp; 353260237Sneel vhpet->timer[n].callout_sbt = now + delta; 354260237Sneel callout_reset_sbt(&vhpet->timer[n].callout, vhpet->timer[n].callout_sbt, 355260237Sneel precision, vhpet_handler, &vhpet->timer[n].arg, C_ABSOLUTE); 356258579Sneel} 357258579Sneel 358258579Sneelstatic void 359258579Sneelvhpet_start_counting(struct vhpet *vhpet) 360258579Sneel{ 361258579Sneel int i; 362258579Sneel 363260237Sneel vhpet->countbase_sbt = sbinuptime(); 364260237Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) { 365260237Sneel /* 366260237Sneel * Restart the timers based on the value of the main counter 367260237Sneel * when it stopped counting. 368260237Sneel */ 369260237Sneel vhpet_start_timer(vhpet, i, vhpet->countbase, 370260237Sneel vhpet->countbase_sbt); 371260237Sneel } 372258579Sneel} 373258579Sneel 374258579Sneelstatic void 375260237Sneelvhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, sbintime_t now) 376258579Sneel{ 377258579Sneel int i; 378258579Sneel 379260237Sneel vhpet->countbase = counter; 380258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) 381260237Sneel 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; 475260237Sneel uint32_t isr_clear_mask, old_compval, old_comprate, counter; 476260237Sneel 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) { 512260237Sneel /* 513260237Sneel * Get the most recent value of the counter before updating 514260237Sneel * the 'config' register. If the HPET is going to be disabled 515260237Sneel * then we need to update 'countbase' with the value right 516260237Sneel * before it is disabled. 517260237Sneel */ 518260237Sneel nowptr = vhpet_counter_enabled(vhpet) ? &now : NULL; 519260237Sneel counter = vhpet_counter(vhpet, nowptr); 520258579Sneel oldval = vhpet->config; 521258579Sneel update_register(&vhpet->config, data, mask); 522276428Sneel 523276428Sneel /* 524276428Sneel * LegacyReplacement Routing is not supported so clear the 525276428Sneel * bit explicitly. 526276428Sneel */ 527276428Sneel vhpet->config &= ~HPET_CNF_LEG_RT; 528276428Sneel 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 { 534260237Sneel 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 */ 554260237Sneel val64 = vhpet_counter(vhpet, NULL); 555258579Sneel update_register(&val64, data, mask); 556260237Sneel 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) { 599260237Sneel if (vhpet_counter_enabled(vhpet)) { 600260237Sneel counter = vhpet_counter(vhpet, &now); 601260237Sneel vhpet_start_timer(vhpet, i, counter, 602260237Sneel now); 603260237Sneel } 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) { 664260237Sneel 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{ 704258699Sneel int i, pincount; 705258579Sneel struct vhpet *vhpet; 706258699Sneel 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 717258699Sneel pincount = vioapic_pincount(vm); 718321414Smav if (pincount >= 32) 719321414Smav allowed_irqs = 0xff000000; /* irqs 24-31 */ 720321414Smav else if (pincount >= 20) 721321414Smav allowed_irqs = 0xf << (pincount - 4); /* 4 upper irqs */ 722258699Sneel else 723258699Sneel allowed_irqs = 0; 724258699Sneel 725258579Sneel /* 726258579Sneel * Initialize HPET timer hardware state. 727258579Sneel */ 728258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) { 729258699Sneel vhpet->timer[i].cap_config = allowed_irqs << 32; 730258699Sneel vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT; 731258699Sneel vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL; 732258699Sneel 733258579Sneel vhpet->timer[i].compval = 0xffffffff; 734258579Sneel callout_init(&vhpet->timer[i].callout, 1); 735258579Sneel 736258579Sneel arg = &vhpet->timer[i].arg; 737258579Sneel arg->vhpet = vhpet; 738258579Sneel arg->timer_num = i; 739258579Sneel } 740258579Sneel 741258579Sneel return (vhpet); 742258579Sneel} 743258579Sneel 744258579Sneelvoid 745258579Sneelvhpet_cleanup(struct vhpet *vhpet) 746258579Sneel{ 747258579Sneel int i; 748258579Sneel 749258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) 750258579Sneel callout_drain(&vhpet->timer[i].callout); 751258579Sneel 752258579Sneel free(vhpet, M_VHPET); 753258579Sneel} 754258579Sneel 755258579Sneelint 756258579Sneelvhpet_getcap(struct vm_hpet_cap *cap) 757258579Sneel{ 758258579Sneel 759258579Sneel cap->capabilities = vhpet_capabilities(); 760258579Sneel return (0); 761258579Sneel} 762