vhpet.c revision 284894
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/10/sys/amd64/vmm/io/vhpet.c 284894 2015-06-27 22:48:22Z neel $ 28258579Sneel */ 29258579Sneel 30258579Sneel#include <sys/cdefs.h> 31258579Sneel__FBSDID("$FreeBSD: stable/10/sys/amd64/vmm/io/vhpet.c 284894 2015-06-27 22:48:22Z neel $"); 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#include <sys/cpuset.h> 40258579Sneel 41258579Sneel#include <dev/acpica/acpi_hpet.h> 42258579Sneel 43258579Sneel#include <machine/vmm.h> 44258579Sneel#include <machine/vmm_dev.h> 45258579Sneel 46258579Sneel#include "vmm_lapic.h" 47268891Sjhb#include "vatpic.h" 48258579Sneel#include "vioapic.h" 49258579Sneel#include "vhpet.h" 50258579Sneel 51258579Sneel#include "vmm_ktr.h" 52258579Sneel 53258579Sneelstatic MALLOC_DEFINE(M_VHPET, "vhpet", "bhyve virtual hpet"); 54258579Sneel 55258579Sneel#define HPET_FREQ 10000000 /* 10.0 Mhz */ 56258579Sneel#define FS_PER_S 1000000000000000ul 57258579Sneel 58258579Sneel/* Timer N Configuration and Capabilities Register */ 59258579Sneel#define HPET_TCAP_RO_MASK (HPET_TCAP_INT_ROUTE | \ 60258579Sneel HPET_TCAP_FSB_INT_DEL | \ 61258579Sneel HPET_TCAP_SIZE | \ 62258579Sneel HPET_TCAP_PER_INT) 63258579Sneel/* 64258579Sneel * HPET requires at least 3 timers and up to 32 timers per block. 65258579Sneel */ 66258579Sneel#define VHPET_NUM_TIMERS 8 67258579SneelCTASSERT(VHPET_NUM_TIMERS >= 3 && VHPET_NUM_TIMERS <= 32); 68258579Sneel 69258579Sneelstruct vhpet_callout_arg { 70258579Sneel struct vhpet *vhpet; 71258579Sneel int timer_num; 72258579Sneel}; 73258579Sneel 74258579Sneelstruct vhpet { 75258579Sneel struct vm *vm; 76258579Sneel struct mtx mtx; 77258579Sneel sbintime_t freq_sbt; 78258579Sneel 79258579Sneel uint64_t config; /* Configuration */ 80258579Sneel uint64_t isr; /* Interrupt Status */ 81266477Sjhb uint32_t countbase; /* HPET counter base value */ 82266477Sjhb sbintime_t countbase_sbt; /* uptime corresponding to base value */ 83258579Sneel 84258579Sneel struct { 85258579Sneel uint64_t cap_config; /* Configuration */ 86258579Sneel uint64_t msireg; /* FSB interrupt routing */ 87258579Sneel uint32_t compval; /* Comparator */ 88258579Sneel uint32_t comprate; 89258579Sneel struct callout callout; 90266477Sjhb sbintime_t callout_sbt; /* time when counter==compval */ 91258579Sneel struct vhpet_callout_arg arg; 92258579Sneel } timer[VHPET_NUM_TIMERS]; 93258579Sneel}; 94258579Sneel 95258579Sneel#define VHPET_LOCK(vhp) mtx_lock(&((vhp)->mtx)) 96258579Sneel#define VHPET_UNLOCK(vhp) mtx_unlock(&((vhp)->mtx)) 97258579Sneel 98266477Sjhbstatic void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, 99266477Sjhb sbintime_t now); 100266477Sjhb 101258579Sneelstatic uint64_t 102258579Sneelvhpet_capabilities(void) 103258579Sneel{ 104258579Sneel uint64_t cap = 0; 105258579Sneel 106258579Sneel cap |= 0x8086 << 16; /* vendor id */ 107258579Sneel cap |= (VHPET_NUM_TIMERS - 1) << 8; /* number of timers */ 108258579Sneel cap |= 1; /* revision */ 109258579Sneel cap &= ~HPET_CAP_COUNT_SIZE; /* 32-bit timer */ 110258579Sneel 111258579Sneel cap &= 0xffffffff; 112258579Sneel cap |= (FS_PER_S / HPET_FREQ) << 32; /* tick period in fs */ 113258579Sneel 114258579Sneel return (cap); 115258579Sneel} 116258579Sneel 117258579Sneelstatic __inline bool 118258579Sneelvhpet_counter_enabled(struct vhpet *vhpet) 119258579Sneel{ 120258579Sneel 121258579Sneel return ((vhpet->config & HPET_CNF_ENABLE) ? true : false); 122258579Sneel} 123258579Sneel 124258579Sneelstatic __inline bool 125258579Sneelvhpet_timer_msi_enabled(struct vhpet *vhpet, int n) 126258579Sneel{ 127258579Sneel const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN; 128258579Sneel 129258579Sneel if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable) 130258579Sneel return (true); 131258579Sneel else 132258579Sneel return (false); 133258579Sneel} 134258579Sneel 135258579Sneelstatic __inline int 136258579Sneelvhpet_timer_ioapic_pin(struct vhpet *vhpet, int n) 137258579Sneel{ 138258579Sneel /* 139258579Sneel * If the timer is configured to use MSI then treat it as if the 140258579Sneel * timer is not connected to the ioapic. 141258579Sneel */ 142258579Sneel if (vhpet_timer_msi_enabled(vhpet, n)) 143258579Sneel return (0); 144258579Sneel 145258579Sneel return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9); 146258579Sneel} 147258579Sneel 148258579Sneelstatic uint32_t 149266477Sjhbvhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr) 150258579Sneel{ 151258579Sneel uint32_t val; 152266477Sjhb sbintime_t now, delta; 153258579Sneel 154266477Sjhb val = vhpet->countbase; 155258579Sneel if (vhpet_counter_enabled(vhpet)) { 156266477Sjhb now = sbinuptime(); 157266477Sjhb delta = now - vhpet->countbase_sbt; 158266477Sjhb KASSERT(delta >= 0, ("vhpet_counter: uptime went backwards: " 159266477Sjhb "%#lx to %#lx", vhpet->countbase_sbt, now)); 160266477Sjhb val += delta / vhpet->freq_sbt; 161266477Sjhb if (nowptr != NULL) 162266477Sjhb *nowptr = now; 163266477Sjhb } else { 164258579Sneel /* 165266477Sjhb * The sbinuptime corresponding to the 'countbase' is 166266477Sjhb * meaningless when the counter is disabled. Make sure 167266477Sjhb * that the the caller doesn't want to use it. 168258579Sneel */ 169266477Sjhb KASSERT(nowptr == NULL, ("vhpet_counter: nowptr must be NULL")); 170258579Sneel } 171258579Sneel return (val); 172258579Sneel} 173258579Sneel 174258579Sneelstatic void 175258579Sneelvhpet_timer_clear_isr(struct vhpet *vhpet, int n) 176258579Sneel{ 177284894Sneel int pin; 178258579Sneel 179258579Sneel if (vhpet->isr & (1 << n)) { 180258579Sneel pin = vhpet_timer_ioapic_pin(vhpet, n); 181258579Sneel KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n)); 182258579Sneel vioapic_deassert_irq(vhpet->vm, pin); 183258579Sneel vhpet->isr &= ~(1 << n); 184258579Sneel } 185258579Sneel} 186258579Sneel 187258579Sneelstatic __inline bool 188258579Sneelvhpet_periodic_timer(struct vhpet *vhpet, int n) 189258579Sneel{ 190258579Sneel 191258579Sneel return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0); 192258579Sneel} 193258579Sneel 194258579Sneelstatic __inline bool 195258579Sneelvhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n) 196258579Sneel{ 197258579Sneel 198258579Sneel return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0); 199258579Sneel} 200258579Sneel 201258579Sneelstatic __inline bool 202258579Sneelvhpet_timer_edge_trig(struct vhpet *vhpet, int n) 203258579Sneel{ 204258579Sneel 205258579Sneel KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: " 206258579Sneel "timer %d is using MSI", n)); 207258579Sneel 208258579Sneel if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0) 209258579Sneel return (true); 210258579Sneel else 211258579Sneel return (false); 212258579Sneel} 213258579Sneel 214258579Sneelstatic void 215258579Sneelvhpet_timer_interrupt(struct vhpet *vhpet, int n) 216258579Sneel{ 217284894Sneel int pin; 218258579Sneel 219258579Sneel /* If interrupts are not enabled for this timer then just return. */ 220258579Sneel if (!vhpet_timer_interrupt_enabled(vhpet, n)) 221258579Sneel return; 222258579Sneel 223258579Sneel /* 224258579Sneel * If a level triggered interrupt is already asserted then just return. 225258579Sneel */ 226258579Sneel if ((vhpet->isr & (1 << n)) != 0) { 227258579Sneel VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n); 228258579Sneel return; 229258579Sneel } 230258579Sneel 231258579Sneel if (vhpet_timer_msi_enabled(vhpet, n)) { 232262350Sjhb lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32, 233262350Sjhb vhpet->timer[n].msireg & 0xffffffff); 234258579Sneel return; 235258579Sneel } 236258579Sneel 237258579Sneel pin = vhpet_timer_ioapic_pin(vhpet, n); 238258579Sneel if (pin == 0) { 239258579Sneel VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n); 240258579Sneel return; 241258579Sneel } 242258579Sneel 243258579Sneel if (vhpet_timer_edge_trig(vhpet, n)) { 244258579Sneel vioapic_pulse_irq(vhpet->vm, pin); 245258579Sneel } else { 246258579Sneel vhpet->isr |= 1 << n; 247258579Sneel vioapic_assert_irq(vhpet->vm, pin); 248258579Sneel } 249258579Sneel} 250258579Sneel 251258579Sneelstatic void 252258579Sneelvhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter) 253258579Sneel{ 254258579Sneel uint32_t compval, comprate, compnext; 255258579Sneel 256258579Sneel KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n)); 257258579Sneel 258258579Sneel compval = vhpet->timer[n].compval; 259258579Sneel comprate = vhpet->timer[n].comprate; 260258579Sneel 261258579Sneel /* 262258579Sneel * Calculate the comparator value to be used for the next periodic 263258579Sneel * interrupt. 264258579Sneel * 265258579Sneel * This function is commonly called from the callout handler. 266258579Sneel * In this scenario the 'counter' is ahead of 'compval'. To find 267258579Sneel * the next value to program into the accumulator we divide the 268258579Sneel * number space between 'compval' and 'counter' into 'comprate' 269258579Sneel * sized units. The 'compval' is rounded up such that is "ahead" 270258579Sneel * of 'counter'. 271258579Sneel */ 272258579Sneel compnext = compval + ((counter - compval) / comprate + 1) * comprate; 273258579Sneel 274258579Sneel vhpet->timer[n].compval = compnext; 275258579Sneel} 276258579Sneel 277258579Sneelstatic void 278258579Sneelvhpet_handler(void *a) 279258579Sneel{ 280258579Sneel int n; 281258579Sneel uint32_t counter; 282266477Sjhb sbintime_t now; 283258579Sneel struct vhpet *vhpet; 284258579Sneel struct callout *callout; 285258579Sneel struct vhpet_callout_arg *arg; 286258579Sneel 287258579Sneel arg = a; 288258579Sneel vhpet = arg->vhpet; 289258579Sneel n = arg->timer_num; 290258579Sneel callout = &vhpet->timer[n].callout; 291258579Sneel 292258579Sneel VM_CTR1(vhpet->vm, "hpet t%d fired", n); 293258579Sneel 294258579Sneel VHPET_LOCK(vhpet); 295258579Sneel 296258579Sneel if (callout_pending(callout)) /* callout was reset */ 297258579Sneel goto done; 298258579Sneel 299258579Sneel if (!callout_active(callout)) /* callout was stopped */ 300258579Sneel goto done; 301258579Sneel 302258579Sneel callout_deactivate(callout); 303258579Sneel 304258579Sneel if (!vhpet_counter_enabled(vhpet)) 305258579Sneel panic("vhpet(%p) callout with counter disabled", vhpet); 306258579Sneel 307266477Sjhb counter = vhpet_counter(vhpet, &now); 308266477Sjhb vhpet_start_timer(vhpet, n, counter, now); 309258579Sneel vhpet_timer_interrupt(vhpet, n); 310258579Sneeldone: 311258579Sneel VHPET_UNLOCK(vhpet); 312258579Sneel return; 313258579Sneel} 314258579Sneel 315258579Sneelstatic void 316266477Sjhbvhpet_stop_timer(struct vhpet *vhpet, int n, sbintime_t now) 317258579Sneel{ 318258579Sneel 319266477Sjhb VM_CTR1(vhpet->vm, "hpet t%d stopped", n); 320258579Sneel callout_stop(&vhpet->timer[n].callout); 321266477Sjhb 322266477Sjhb /* 323266477Sjhb * If the callout was scheduled to expire in the past but hasn't 324266477Sjhb * had a chance to execute yet then trigger the timer interrupt 325266477Sjhb * here. Failing to do so will result in a missed timer interrupt 326266477Sjhb * in the guest. This is especially bad in one-shot mode because 327266477Sjhb * the next interrupt has to wait for the counter to wrap around. 328266477Sjhb */ 329266477Sjhb if (vhpet->timer[n].callout_sbt < now) { 330266477Sjhb VM_CTR1(vhpet->vm, "hpet t%d interrupt triggered after " 331266477Sjhb "stopping timer", n); 332266477Sjhb vhpet_timer_interrupt(vhpet, n); 333266477Sjhb } 334258579Sneel} 335258579Sneel 336258579Sneelstatic void 337266477Sjhbvhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, sbintime_t now) 338258579Sneel{ 339266477Sjhb sbintime_t delta, precision; 340258579Sneel 341258579Sneel if (vhpet->timer[n].comprate != 0) 342258579Sneel vhpet_adjust_compval(vhpet, n, counter); 343266477Sjhb else { 344266477Sjhb /* 345266477Sjhb * In one-shot mode it is the guest's responsibility to make 346266477Sjhb * sure that the comparator value is not in the "past". The 347266477Sjhb * hardware doesn't have any belt-and-suspenders to deal with 348266477Sjhb * this so we don't either. 349266477Sjhb */ 350258579Sneel } 351258579Sneel 352266477Sjhb delta = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt; 353266477Sjhb precision = delta >> tc_precexp; 354266477Sjhb vhpet->timer[n].callout_sbt = now + delta; 355266477Sjhb callout_reset_sbt(&vhpet->timer[n].callout, vhpet->timer[n].callout_sbt, 356266477Sjhb precision, vhpet_handler, &vhpet->timer[n].arg, C_ABSOLUTE); 357258579Sneel} 358258579Sneel 359258579Sneelstatic void 360258579Sneelvhpet_start_counting(struct vhpet *vhpet) 361258579Sneel{ 362258579Sneel int i; 363258579Sneel 364266477Sjhb vhpet->countbase_sbt = sbinuptime(); 365266477Sjhb for (i = 0; i < VHPET_NUM_TIMERS; i++) { 366266477Sjhb /* 367266477Sjhb * Restart the timers based on the value of the main counter 368266477Sjhb * when it stopped counting. 369266477Sjhb */ 370266477Sjhb vhpet_start_timer(vhpet, i, vhpet->countbase, 371266477Sjhb vhpet->countbase_sbt); 372266477Sjhb } 373258579Sneel} 374258579Sneel 375258579Sneelstatic void 376266477Sjhbvhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, sbintime_t now) 377258579Sneel{ 378258579Sneel int i; 379258579Sneel 380266477Sjhb vhpet->countbase = counter; 381258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) 382266477Sjhb vhpet_stop_timer(vhpet, i, now); 383258579Sneel} 384258579Sneel 385258579Sneelstatic __inline void 386258579Sneelupdate_register(uint64_t *regptr, uint64_t data, uint64_t mask) 387258579Sneel{ 388258579Sneel 389258579Sneel *regptr &= ~mask; 390258579Sneel *regptr |= (data & mask); 391258579Sneel} 392258579Sneel 393258579Sneelstatic void 394258579Sneelvhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data, 395258579Sneel uint64_t mask) 396258579Sneel{ 397258579Sneel bool clear_isr; 398258579Sneel int old_pin, new_pin; 399258579Sneel uint32_t allowed_irqs; 400258579Sneel uint64_t oldval, newval; 401258579Sneel 402258579Sneel if (vhpet_timer_msi_enabled(vhpet, n) || 403258579Sneel vhpet_timer_edge_trig(vhpet, n)) { 404258579Sneel if (vhpet->isr & (1 << n)) 405258579Sneel panic("vhpet timer %d isr should not be asserted", n); 406258579Sneel } 407258579Sneel old_pin = vhpet_timer_ioapic_pin(vhpet, n); 408258579Sneel oldval = vhpet->timer[n].cap_config; 409258579Sneel 410258579Sneel newval = oldval; 411258579Sneel update_register(&newval, data, mask); 412258579Sneel newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE); 413258579Sneel newval |= oldval & HPET_TCAP_RO_MASK; 414258579Sneel 415258579Sneel if (newval == oldval) 416258579Sneel return; 417258579Sneel 418258579Sneel vhpet->timer[n].cap_config = newval; 419258579Sneel VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval); 420258579Sneel 421258579Sneel /* 422258579Sneel * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field. 423258579Sneel * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set 424258579Sneel * it to the default value of 0. 425258579Sneel */ 426258579Sneel allowed_irqs = vhpet->timer[n].cap_config >> 32; 427258579Sneel new_pin = vhpet_timer_ioapic_pin(vhpet, n); 428258579Sneel if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) { 429258579Sneel VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, " 430258579Sneel "allowed_irqs 0x%08x", n, new_pin, allowed_irqs); 431258579Sneel new_pin = 0; 432258579Sneel vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE; 433258579Sneel } 434258579Sneel 435258579Sneel if (!vhpet_periodic_timer(vhpet, n)) 436258579Sneel vhpet->timer[n].comprate = 0; 437258579Sneel 438258579Sneel /* 439258579Sneel * If the timer's ISR bit is set then clear it in the following cases: 440258579Sneel * - interrupt is disabled 441258579Sneel * - interrupt type is changed from level to edge or fsb. 442258579Sneel * - interrupt routing is changed 443258579Sneel * 444258579Sneel * This is to ensure that this timer's level triggered interrupt does 445258579Sneel * not remain asserted forever. 446258579Sneel */ 447258579Sneel if (vhpet->isr & (1 << n)) { 448258579Sneel KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d", 449258579Sneel n, old_pin)); 450258579Sneel if (!vhpet_timer_interrupt_enabled(vhpet, n)) 451258579Sneel clear_isr = true; 452258579Sneel else if (vhpet_timer_msi_enabled(vhpet, n)) 453258579Sneel clear_isr = true; 454258579Sneel else if (vhpet_timer_edge_trig(vhpet, n)) 455258579Sneel clear_isr = true; 456258579Sneel else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin) 457258579Sneel clear_isr = true; 458258579Sneel else 459258579Sneel clear_isr = false; 460258579Sneel 461258579Sneel if (clear_isr) { 462258579Sneel VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to " 463258579Sneel "configuration change", n); 464258579Sneel vioapic_deassert_irq(vhpet->vm, old_pin); 465258579Sneel vhpet->isr &= ~(1 << n); 466258579Sneel } 467258579Sneel } 468258579Sneel} 469258579Sneel 470258579Sneelint 471258579Sneelvhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size, 472258579Sneel void *arg) 473258579Sneel{ 474258579Sneel struct vhpet *vhpet; 475258579Sneel uint64_t data, mask, oldval, val64; 476266477Sjhb uint32_t isr_clear_mask, old_compval, old_comprate, counter; 477266477Sjhb sbintime_t now, *nowptr; 478258579Sneel int i, offset; 479258579Sneel 480258579Sneel vhpet = vm_hpet(vm); 481258579Sneel offset = gpa - VHPET_BASE; 482258579Sneel 483258579Sneel VHPET_LOCK(vhpet); 484258579Sneel 485258579Sneel /* Accesses to the HPET should be 4 or 8 bytes wide */ 486258579Sneel switch (size) { 487258579Sneel case 8: 488258579Sneel mask = 0xffffffffffffffff; 489258579Sneel data = val; 490258579Sneel break; 491258579Sneel case 4: 492258579Sneel mask = 0xffffffff; 493258579Sneel data = val; 494258579Sneel if ((offset & 0x4) != 0) { 495258579Sneel mask <<= 32; 496258579Sneel data <<= 32; 497258579Sneel } 498258579Sneel break; 499258579Sneel default: 500258579Sneel VM_CTR2(vhpet->vm, "hpet invalid mmio write: " 501258579Sneel "offset 0x%08x, size %d", offset, size); 502258579Sneel goto done; 503258579Sneel } 504258579Sneel 505258579Sneel /* Access to the HPET should be naturally aligned to its width */ 506258579Sneel if (offset & (size - 1)) { 507258579Sneel VM_CTR2(vhpet->vm, "hpet invalid mmio write: " 508258579Sneel "offset 0x%08x, size %d", offset, size); 509258579Sneel goto done; 510258579Sneel } 511258579Sneel 512258579Sneel if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) { 513266477Sjhb /* 514266477Sjhb * Get the most recent value of the counter before updating 515266477Sjhb * the 'config' register. If the HPET is going to be disabled 516266477Sjhb * then we need to update 'countbase' with the value right 517266477Sjhb * before it is disabled. 518266477Sjhb */ 519266477Sjhb nowptr = vhpet_counter_enabled(vhpet) ? &now : NULL; 520266477Sjhb counter = vhpet_counter(vhpet, nowptr); 521258579Sneel oldval = vhpet->config; 522258579Sneel update_register(&vhpet->config, data, mask); 523284894Sneel 524284894Sneel /* 525284894Sneel * LegacyReplacement Routing is not supported so clear the 526284894Sneel * bit explicitly. 527284894Sneel */ 528284894Sneel vhpet->config &= ~HPET_CNF_LEG_RT; 529284894Sneel 530258579Sneel if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) { 531258579Sneel if (vhpet_counter_enabled(vhpet)) { 532258579Sneel vhpet_start_counting(vhpet); 533258579Sneel VM_CTR0(vhpet->vm, "hpet enabled"); 534258579Sneel } else { 535266477Sjhb vhpet_stop_counting(vhpet, counter, now); 536258579Sneel VM_CTR0(vhpet->vm, "hpet disabled"); 537258579Sneel } 538258579Sneel } 539258579Sneel goto done; 540258579Sneel } 541258579Sneel 542258579Sneel if (offset == HPET_ISR || offset == HPET_ISR + 4) { 543258579Sneel isr_clear_mask = vhpet->isr & data; 544258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) { 545258579Sneel if ((isr_clear_mask & (1 << i)) != 0) { 546258579Sneel VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i); 547258579Sneel vhpet_timer_clear_isr(vhpet, i); 548258579Sneel } 549258579Sneel } 550258579Sneel goto done; 551258579Sneel } 552258579Sneel 553258579Sneel if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) { 554258579Sneel /* Zero-extend the counter to 64-bits before updating it */ 555266477Sjhb val64 = vhpet_counter(vhpet, NULL); 556258579Sneel update_register(&val64, data, mask); 557266477Sjhb vhpet->countbase = val64; 558258579Sneel if (vhpet_counter_enabled(vhpet)) 559258579Sneel vhpet_start_counting(vhpet); 560258579Sneel goto done; 561258579Sneel } 562258579Sneel 563258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) { 564258579Sneel if (offset == HPET_TIMER_CAP_CNF(i) || 565258579Sneel offset == HPET_TIMER_CAP_CNF(i) + 4) { 566258579Sneel vhpet_timer_update_config(vhpet, i, data, mask); 567258579Sneel break; 568258579Sneel } 569258579Sneel 570258579Sneel if (offset == HPET_TIMER_COMPARATOR(i) || 571258579Sneel offset == HPET_TIMER_COMPARATOR(i) + 4) { 572258579Sneel old_compval = vhpet->timer[i].compval; 573258579Sneel old_comprate = vhpet->timer[i].comprate; 574258579Sneel if (vhpet_periodic_timer(vhpet, i)) { 575258579Sneel /* 576258579Sneel * In periodic mode writes to the comparator 577258579Sneel * change the 'compval' register only if the 578258579Sneel * HPET_TCNF_VAL_SET bit is set in the config 579258579Sneel * register. 580258579Sneel */ 581258579Sneel val64 = vhpet->timer[i].comprate; 582258579Sneel update_register(&val64, data, mask); 583258579Sneel vhpet->timer[i].comprate = val64; 584258579Sneel if ((vhpet->timer[i].cap_config & 585258579Sneel HPET_TCNF_VAL_SET) != 0) { 586258579Sneel vhpet->timer[i].compval = val64; 587258579Sneel } 588258579Sneel } else { 589258579Sneel KASSERT(vhpet->timer[i].comprate == 0, 590258579Sneel ("vhpet one-shot timer %d has invalid " 591258579Sneel "rate %u", i, vhpet->timer[i].comprate)); 592258579Sneel val64 = vhpet->timer[i].compval; 593258579Sneel update_register(&val64, data, mask); 594258579Sneel vhpet->timer[i].compval = val64; 595258579Sneel } 596258579Sneel vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET; 597258579Sneel 598258579Sneel if (vhpet->timer[i].compval != old_compval || 599258579Sneel vhpet->timer[i].comprate != old_comprate) { 600266477Sjhb if (vhpet_counter_enabled(vhpet)) { 601266477Sjhb counter = vhpet_counter(vhpet, &now); 602266477Sjhb vhpet_start_timer(vhpet, i, counter, 603266477Sjhb now); 604266477Sjhb } 605258579Sneel } 606258579Sneel break; 607258579Sneel } 608258579Sneel 609258579Sneel if (offset == HPET_TIMER_FSB_VAL(i) || 610258579Sneel offset == HPET_TIMER_FSB_ADDR(i)) { 611258579Sneel update_register(&vhpet->timer[i].msireg, data, mask); 612258579Sneel break; 613258579Sneel } 614258579Sneel } 615258579Sneeldone: 616258579Sneel VHPET_UNLOCK(vhpet); 617258579Sneel return (0); 618258579Sneel} 619258579Sneel 620258579Sneelint 621258579Sneelvhpet_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, int size, 622258579Sneel void *arg) 623258579Sneel{ 624258579Sneel int i, offset; 625258579Sneel struct vhpet *vhpet; 626258579Sneel uint64_t data; 627258579Sneel 628258579Sneel vhpet = vm_hpet(vm); 629258579Sneel offset = gpa - VHPET_BASE; 630258579Sneel 631258579Sneel VHPET_LOCK(vhpet); 632258579Sneel 633258579Sneel /* Accesses to the HPET should be 4 or 8 bytes wide */ 634258579Sneel if (size != 4 && size != 8) { 635258579Sneel VM_CTR2(vhpet->vm, "hpet invalid mmio read: " 636258579Sneel "offset 0x%08x, size %d", offset, size); 637258579Sneel data = 0; 638258579Sneel goto done; 639258579Sneel } 640258579Sneel 641258579Sneel /* Access to the HPET should be naturally aligned to its width */ 642258579Sneel if (offset & (size - 1)) { 643258579Sneel VM_CTR2(vhpet->vm, "hpet invalid mmio read: " 644258579Sneel "offset 0x%08x, size %d", offset, size); 645258579Sneel data = 0; 646258579Sneel goto done; 647258579Sneel } 648258579Sneel 649258579Sneel if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) { 650258579Sneel data = vhpet_capabilities(); 651258579Sneel goto done; 652258579Sneel } 653258579Sneel 654258579Sneel if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) { 655258579Sneel data = vhpet->config; 656258579Sneel goto done; 657258579Sneel } 658258579Sneel 659258579Sneel if (offset == HPET_ISR || offset == HPET_ISR + 4) { 660258579Sneel data = vhpet->isr; 661258579Sneel goto done; 662258579Sneel } 663258579Sneel 664258579Sneel if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) { 665266477Sjhb data = vhpet_counter(vhpet, NULL); 666258579Sneel goto done; 667258579Sneel } 668258579Sneel 669258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) { 670258579Sneel if (offset == HPET_TIMER_CAP_CNF(i) || 671258579Sneel offset == HPET_TIMER_CAP_CNF(i) + 4) { 672258579Sneel data = vhpet->timer[i].cap_config; 673258579Sneel break; 674258579Sneel } 675258579Sneel 676258579Sneel if (offset == HPET_TIMER_COMPARATOR(i) || 677258579Sneel offset == HPET_TIMER_COMPARATOR(i) + 4) { 678258579Sneel data = vhpet->timer[i].compval; 679258579Sneel break; 680258579Sneel } 681258579Sneel 682258579Sneel if (offset == HPET_TIMER_FSB_VAL(i) || 683258579Sneel offset == HPET_TIMER_FSB_ADDR(i)) { 684258579Sneel data = vhpet->timer[i].msireg; 685258579Sneel break; 686258579Sneel } 687258579Sneel } 688258579Sneel 689258579Sneel if (i >= VHPET_NUM_TIMERS) 690258579Sneel data = 0; 691258579Sneeldone: 692258579Sneel VHPET_UNLOCK(vhpet); 693258579Sneel 694258579Sneel if (size == 4) { 695258579Sneel if (offset & 0x4) 696258579Sneel data >>= 32; 697258579Sneel } 698258579Sneel *rval = data; 699258579Sneel return (0); 700258579Sneel} 701258579Sneel 702258579Sneelstruct vhpet * 703258579Sneelvhpet_init(struct vm *vm) 704258579Sneel{ 705261088Sjhb int i, pincount; 706258579Sneel struct vhpet *vhpet; 707261088Sjhb uint64_t allowed_irqs; 708258579Sneel struct vhpet_callout_arg *arg; 709258579Sneel struct bintime bt; 710258579Sneel 711258579Sneel vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO); 712258579Sneel vhpet->vm = vm; 713258579Sneel mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF); 714258579Sneel 715258579Sneel FREQ2BT(HPET_FREQ, &bt); 716258579Sneel vhpet->freq_sbt = bttosbt(bt); 717258579Sneel 718261088Sjhb pincount = vioapic_pincount(vm); 719261088Sjhb if (pincount >= 24) 720261088Sjhb allowed_irqs = 0x00f00000; /* irqs 20, 21, 22 and 23 */ 721261088Sjhb else 722261088Sjhb allowed_irqs = 0; 723261088Sjhb 724258579Sneel /* 725258579Sneel * Initialize HPET timer hardware state. 726258579Sneel */ 727258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) { 728261088Sjhb vhpet->timer[i].cap_config = allowed_irqs << 32; 729261088Sjhb vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT; 730261088Sjhb vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL; 731261088Sjhb 732258579Sneel vhpet->timer[i].compval = 0xffffffff; 733258579Sneel callout_init(&vhpet->timer[i].callout, 1); 734258579Sneel 735258579Sneel arg = &vhpet->timer[i].arg; 736258579Sneel arg->vhpet = vhpet; 737258579Sneel arg->timer_num = i; 738258579Sneel } 739258579Sneel 740258579Sneel return (vhpet); 741258579Sneel} 742258579Sneel 743258579Sneelvoid 744258579Sneelvhpet_cleanup(struct vhpet *vhpet) 745258579Sneel{ 746258579Sneel int i; 747258579Sneel 748258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) 749258579Sneel callout_drain(&vhpet->timer[i].callout); 750258579Sneel 751258579Sneel free(vhpet, M_VHPET); 752258579Sneel} 753258579Sneel 754258579Sneelint 755258579Sneelvhpet_getcap(struct vm_hpet_cap *cap) 756258579Sneel{ 757258579Sneel 758258579Sneel cap->capabilities = vhpet_capabilities(); 759258579Sneel return (0); 760258579Sneel} 761