vhpet.c revision 258579
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: head/sys/amd64/vmm/io/vhpet.c 258579 2013-11-25 19:04:51Z neel $ 28258579Sneel */ 29258579Sneel 30258579Sneel#include <sys/cdefs.h> 31258579Sneel__FBSDID("$FreeBSD: head/sys/amd64/vmm/io/vhpet.c 258579 2013-11-25 19:04:51Z 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" 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 */ 80258579Sneel uint32_t counter; /* HPET Counter */ 81258579Sneel sbintime_t counter_sbt; 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; 89258579Sneel struct vhpet_callout_arg arg; 90258579Sneel } timer[VHPET_NUM_TIMERS]; 91258579Sneel}; 92258579Sneel 93258579Sneel#define VHPET_LOCK(vhp) mtx_lock(&((vhp)->mtx)) 94258579Sneel#define VHPET_UNLOCK(vhp) mtx_unlock(&((vhp)->mtx)) 95258579Sneel 96258579Sneelstatic uint64_t 97258579Sneelvhpet_capabilities(void) 98258579Sneel{ 99258579Sneel uint64_t cap = 0; 100258579Sneel 101258579Sneel cap |= 0x8086 << 16; /* vendor id */ 102258579Sneel cap |= HPET_CAP_LEG_RT; /* legacy routing capable */ 103258579Sneel cap |= (VHPET_NUM_TIMERS - 1) << 8; /* number of timers */ 104258579Sneel cap |= 1; /* revision */ 105258579Sneel cap &= ~HPET_CAP_COUNT_SIZE; /* 32-bit timer */ 106258579Sneel 107258579Sneel cap &= 0xffffffff; 108258579Sneel cap |= (FS_PER_S / HPET_FREQ) << 32; /* tick period in fs */ 109258579Sneel 110258579Sneel return (cap); 111258579Sneel} 112258579Sneel 113258579Sneelstatic __inline bool 114258579Sneelvhpet_counter_enabled(struct vhpet *vhpet) 115258579Sneel{ 116258579Sneel 117258579Sneel return ((vhpet->config & HPET_CNF_ENABLE) ? true : false); 118258579Sneel} 119258579Sneel 120258579Sneelstatic __inline bool 121258579Sneelvhpet_timer_msi_enabled(struct vhpet *vhpet, int n) 122258579Sneel{ 123258579Sneel const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN; 124258579Sneel 125258579Sneel /* 126258579Sneel * LegacyReplacement Route configuration takes precedence over MSI 127258579Sneel * for timers 0 and 1. 128258579Sneel */ 129258579Sneel if (n == 0 || n == 1) { 130258579Sneel if (vhpet->config & HPET_CNF_LEG_RT) 131258579Sneel return (false); 132258579Sneel } 133258579Sneel 134258579Sneel if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable) 135258579Sneel return (true); 136258579Sneel else 137258579Sneel return (false); 138258579Sneel} 139258579Sneel 140258579Sneelstatic __inline int 141258579Sneelvhpet_timer_ioapic_pin(struct vhpet *vhpet, int n) 142258579Sneel{ 143258579Sneel /* 144258579Sneel * If the timer is configured to use MSI then treat it as if the 145258579Sneel * timer is not connected to the ioapic. 146258579Sneel */ 147258579Sneel if (vhpet_timer_msi_enabled(vhpet, n)) 148258579Sneel return (0); 149258579Sneel 150258579Sneel if (vhpet->config & HPET_CNF_LEG_RT) { 151258579Sneel /* 152258579Sneel * In "legacy routing" timers 0 and 1 are connected to 153258579Sneel * ioapic pins 2 and 8 respectively. 154258579Sneel */ 155258579Sneel switch (n) { 156258579Sneel case 0: 157258579Sneel return (2); 158258579Sneel case 1: 159258579Sneel return (8); 160258579Sneel } 161258579Sneel } 162258579Sneel 163258579Sneel return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9); 164258579Sneel} 165258579Sneel 166258579Sneelstatic uint32_t 167258579Sneelvhpet_counter(struct vhpet *vhpet, bool latch) 168258579Sneel{ 169258579Sneel uint32_t val; 170258579Sneel sbintime_t cur_sbt, delta_sbt; 171258579Sneel 172258579Sneel val = vhpet->counter; 173258579Sneel if (vhpet_counter_enabled(vhpet)) { 174258579Sneel cur_sbt = sbinuptime(); 175258579Sneel delta_sbt = cur_sbt - vhpet->counter_sbt; 176258579Sneel KASSERT(delta_sbt >= 0, 177258579Sneel ("vhpet counter went backwards: %#lx to %#lx", 178258579Sneel vhpet->counter_sbt, cur_sbt)); 179258579Sneel val += delta_sbt / vhpet->freq_sbt; 180258579Sneel 181258579Sneel /* 182258579Sneel * Keep track of the last value of the main counter that 183258579Sneel * was read by the guest. 184258579Sneel */ 185258579Sneel if (latch) { 186258579Sneel vhpet->counter = val; 187258579Sneel vhpet->counter_sbt = cur_sbt; 188258579Sneel } 189258579Sneel } 190258579Sneel 191258579Sneel return (val); 192258579Sneel} 193258579Sneel 194258579Sneelstatic void 195258579Sneelvhpet_timer_clear_isr(struct vhpet *vhpet, int n) 196258579Sneel{ 197258579Sneel int pin; 198258579Sneel 199258579Sneel if (vhpet->isr & (1 << n)) { 200258579Sneel pin = vhpet_timer_ioapic_pin(vhpet, n); 201258579Sneel KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n)); 202258579Sneel vioapic_deassert_irq(vhpet->vm, pin); 203258579Sneel vhpet->isr &= ~(1 << n); 204258579Sneel } 205258579Sneel} 206258579Sneel 207258579Sneelstatic __inline bool 208258579Sneelvhpet_periodic_timer(struct vhpet *vhpet, int n) 209258579Sneel{ 210258579Sneel 211258579Sneel return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0); 212258579Sneel} 213258579Sneel 214258579Sneelstatic __inline bool 215258579Sneelvhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n) 216258579Sneel{ 217258579Sneel 218258579Sneel return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0); 219258579Sneel} 220258579Sneel 221258579Sneelstatic __inline bool 222258579Sneelvhpet_timer_edge_trig(struct vhpet *vhpet, int n) 223258579Sneel{ 224258579Sneel 225258579Sneel KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: " 226258579Sneel "timer %d is using MSI", n)); 227258579Sneel 228258579Sneel /* The legacy replacement interrupts are always edge triggered */ 229258579Sneel if (vhpet->config & HPET_CNF_LEG_RT) { 230258579Sneel if (n == 0 || n == 1) 231258579Sneel return (true); 232258579Sneel } 233258579Sneel 234258579Sneel if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0) 235258579Sneel return (true); 236258579Sneel else 237258579Sneel return (false); 238258579Sneel} 239258579Sneel 240258579Sneelstatic void 241258579Sneelvhpet_timer_interrupt(struct vhpet *vhpet, int n) 242258579Sneel{ 243258579Sneel int apicid, vector, vcpuid, pin; 244258579Sneel cpuset_t dmask; 245258579Sneel 246258579Sneel /* If interrupts are not enabled for this timer then just return. */ 247258579Sneel if (!vhpet_timer_interrupt_enabled(vhpet, n)) 248258579Sneel return; 249258579Sneel 250258579Sneel /* 251258579Sneel * If a level triggered interrupt is already asserted then just return. 252258579Sneel */ 253258579Sneel if ((vhpet->isr & (1 << n)) != 0) { 254258579Sneel VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n); 255258579Sneel return; 256258579Sneel } 257258579Sneel 258258579Sneel if (vhpet_timer_msi_enabled(vhpet, n)) { 259258579Sneel /* 260258579Sneel * XXX should have an API 'vlapic_deliver_msi(vm, addr, data)' 261258579Sneel * - assuming physical delivery mode 262258579Sneel * - no need to interpret contents of 'msireg' here 263258579Sneel */ 264258579Sneel vector = vhpet->timer[n].msireg & 0xff; 265258579Sneel apicid = (vhpet->timer[n].msireg >> (32 + 12)) & 0xff; 266258579Sneel if (apicid != 0xff) { 267258579Sneel /* unicast */ 268258579Sneel vcpuid = vm_apicid2vcpuid(vhpet->vm, apicid); 269258579Sneel lapic_set_intr(vhpet->vm, vcpuid, vector); 270258579Sneel } else { 271258579Sneel /* broadcast */ 272258579Sneel dmask = vm_active_cpus(vhpet->vm); 273258579Sneel while ((vcpuid = CPU_FFS(&dmask)) != 0) { 274258579Sneel vcpuid--; 275258579Sneel CPU_CLR(vcpuid, &dmask); 276258579Sneel lapic_set_intr(vhpet->vm, vcpuid, vector); 277258579Sneel } 278258579Sneel } 279258579Sneel return; 280258579Sneel } 281258579Sneel 282258579Sneel pin = vhpet_timer_ioapic_pin(vhpet, n); 283258579Sneel if (pin == 0) { 284258579Sneel VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n); 285258579Sneel return; 286258579Sneel } 287258579Sneel 288258579Sneel if (vhpet_timer_edge_trig(vhpet, n)) { 289258579Sneel vioapic_pulse_irq(vhpet->vm, pin); 290258579Sneel } else { 291258579Sneel vhpet->isr |= 1 << n; 292258579Sneel vioapic_assert_irq(vhpet->vm, pin); 293258579Sneel } 294258579Sneel} 295258579Sneel 296258579Sneelstatic void 297258579Sneelvhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter) 298258579Sneel{ 299258579Sneel uint32_t compval, comprate, compnext; 300258579Sneel 301258579Sneel KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n)); 302258579Sneel 303258579Sneel compval = vhpet->timer[n].compval; 304258579Sneel comprate = vhpet->timer[n].comprate; 305258579Sneel 306258579Sneel /* 307258579Sneel * Calculate the comparator value to be used for the next periodic 308258579Sneel * interrupt. 309258579Sneel * 310258579Sneel * This function is commonly called from the callout handler. 311258579Sneel * In this scenario the 'counter' is ahead of 'compval'. To find 312258579Sneel * the next value to program into the accumulator we divide the 313258579Sneel * number space between 'compval' and 'counter' into 'comprate' 314258579Sneel * sized units. The 'compval' is rounded up such that is "ahead" 315258579Sneel * of 'counter'. 316258579Sneel */ 317258579Sneel compnext = compval + ((counter - compval) / comprate + 1) * comprate; 318258579Sneel 319258579Sneel vhpet->timer[n].compval = compnext; 320258579Sneel} 321258579Sneel 322258579Sneelstatic void 323258579Sneelvhpet_handler(void *a) 324258579Sneel{ 325258579Sneel int n; 326258579Sneel uint32_t counter; 327258579Sneel sbintime_t sbt; 328258579Sneel struct vhpet *vhpet; 329258579Sneel struct callout *callout; 330258579Sneel struct vhpet_callout_arg *arg; 331258579Sneel 332258579Sneel arg = a; 333258579Sneel vhpet = arg->vhpet; 334258579Sneel n = arg->timer_num; 335258579Sneel callout = &vhpet->timer[n].callout; 336258579Sneel 337258579Sneel VM_CTR1(vhpet->vm, "hpet t%d fired", n); 338258579Sneel 339258579Sneel VHPET_LOCK(vhpet); 340258579Sneel 341258579Sneel if (callout_pending(callout)) /* callout was reset */ 342258579Sneel goto done; 343258579Sneel 344258579Sneel if (!callout_active(callout)) /* callout was stopped */ 345258579Sneel goto done; 346258579Sneel 347258579Sneel callout_deactivate(callout); 348258579Sneel 349258579Sneel if (!vhpet_counter_enabled(vhpet)) 350258579Sneel panic("vhpet(%p) callout with counter disabled", vhpet); 351258579Sneel 352258579Sneel counter = vhpet_counter(vhpet, false); 353258579Sneel 354258579Sneel /* Update the accumulator for periodic timers */ 355258579Sneel if (vhpet->timer[n].comprate != 0) 356258579Sneel vhpet_adjust_compval(vhpet, n, counter); 357258579Sneel 358258579Sneel sbt = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt; 359258579Sneel callout_reset_sbt(callout, sbt, 0, vhpet_handler, arg, 0); 360258579Sneel vhpet_timer_interrupt(vhpet, n); 361258579Sneeldone: 362258579Sneel VHPET_UNLOCK(vhpet); 363258579Sneel return; 364258579Sneel} 365258579Sneel 366258579Sneelstatic void 367258579Sneelvhpet_stop_timer(struct vhpet *vhpet, int n) 368258579Sneel{ 369258579Sneel 370258579Sneel callout_stop(&vhpet->timer[n].callout); 371258579Sneel vhpet_timer_clear_isr(vhpet, n); 372258579Sneel} 373258579Sneel 374258579Sneelstatic void 375258579Sneelvhpet_start_timer(struct vhpet *vhpet, int n) 376258579Sneel{ 377258579Sneel uint32_t counter, delta, delta2; 378258579Sneel sbintime_t sbt; 379258579Sneel 380258579Sneel counter = vhpet_counter(vhpet, false); 381258579Sneel 382258579Sneel if (vhpet->timer[n].comprate != 0) 383258579Sneel vhpet_adjust_compval(vhpet, n, counter); 384258579Sneel 385258579Sneel delta = vhpet->timer[n].compval - counter; 386258579Sneel 387258579Sneel /* 388258579Sneel * In one-shot mode the guest will typically read the main counter 389258579Sneel * before programming the comparator. We can use this heuristic to 390258579Sneel * figure out whether the expiration time is in the past. If this 391258579Sneel * is the case we schedule the callout to fire immediately. 392258579Sneel */ 393258579Sneel if (!vhpet_periodic_timer(vhpet, n)) { 394258579Sneel delta2 = vhpet->timer[n].compval - vhpet->counter; 395258579Sneel if (delta > delta2) { 396258579Sneel VM_CTR3(vhpet->vm, "hpet t%d comparator value is in " 397258579Sneel "the past: %u/%u/%u", counter, 398258579Sneel vhpet->timer[n].compval, vhpet->counter); 399258579Sneel delta = 0; 400258579Sneel } 401258579Sneel } 402258579Sneel 403258579Sneel sbt = delta * vhpet->freq_sbt; 404258579Sneel callout_reset_sbt(&vhpet->timer[n].callout, sbt, 0, vhpet_handler, 405258579Sneel &vhpet->timer[n].arg, 0); 406258579Sneel} 407258579Sneel 408258579Sneelstatic void 409258579Sneelvhpet_start_counting(struct vhpet *vhpet) 410258579Sneel{ 411258579Sneel int i; 412258579Sneel 413258579Sneel vhpet->counter_sbt = sbinuptime(); 414258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) 415258579Sneel vhpet_start_timer(vhpet, i); 416258579Sneel} 417258579Sneel 418258579Sneelstatic void 419258579Sneelvhpet_stop_counting(struct vhpet *vhpet) 420258579Sneel{ 421258579Sneel int i; 422258579Sneel 423258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) 424258579Sneel vhpet_stop_timer(vhpet, i); 425258579Sneel} 426258579Sneel 427258579Sneelstatic __inline void 428258579Sneelupdate_register(uint64_t *regptr, uint64_t data, uint64_t mask) 429258579Sneel{ 430258579Sneel 431258579Sneel *regptr &= ~mask; 432258579Sneel *regptr |= (data & mask); 433258579Sneel} 434258579Sneel 435258579Sneelstatic void 436258579Sneelvhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data, 437258579Sneel uint64_t mask) 438258579Sneel{ 439258579Sneel bool clear_isr; 440258579Sneel int old_pin, new_pin; 441258579Sneel uint32_t allowed_irqs; 442258579Sneel uint64_t oldval, newval; 443258579Sneel 444258579Sneel if (vhpet_timer_msi_enabled(vhpet, n) || 445258579Sneel vhpet_timer_edge_trig(vhpet, n)) { 446258579Sneel if (vhpet->isr & (1 << n)) 447258579Sneel panic("vhpet timer %d isr should not be asserted", n); 448258579Sneel } 449258579Sneel old_pin = vhpet_timer_ioapic_pin(vhpet, n); 450258579Sneel oldval = vhpet->timer[n].cap_config; 451258579Sneel 452258579Sneel newval = oldval; 453258579Sneel update_register(&newval, data, mask); 454258579Sneel newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE); 455258579Sneel newval |= oldval & HPET_TCAP_RO_MASK; 456258579Sneel 457258579Sneel if (newval == oldval) 458258579Sneel return; 459258579Sneel 460258579Sneel vhpet->timer[n].cap_config = newval; 461258579Sneel VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval); 462258579Sneel 463258579Sneel /* 464258579Sneel * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field. 465258579Sneel * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set 466258579Sneel * it to the default value of 0. 467258579Sneel */ 468258579Sneel allowed_irqs = vhpet->timer[n].cap_config >> 32; 469258579Sneel new_pin = vhpet_timer_ioapic_pin(vhpet, n); 470258579Sneel if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) { 471258579Sneel VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, " 472258579Sneel "allowed_irqs 0x%08x", n, new_pin, allowed_irqs); 473258579Sneel new_pin = 0; 474258579Sneel vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE; 475258579Sneel } 476258579Sneel 477258579Sneel if (!vhpet_periodic_timer(vhpet, n)) 478258579Sneel vhpet->timer[n].comprate = 0; 479258579Sneel 480258579Sneel /* 481258579Sneel * If the timer's ISR bit is set then clear it in the following cases: 482258579Sneel * - interrupt is disabled 483258579Sneel * - interrupt type is changed from level to edge or fsb. 484258579Sneel * - interrupt routing is changed 485258579Sneel * 486258579Sneel * This is to ensure that this timer's level triggered interrupt does 487258579Sneel * not remain asserted forever. 488258579Sneel */ 489258579Sneel if (vhpet->isr & (1 << n)) { 490258579Sneel KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d", 491258579Sneel n, old_pin)); 492258579Sneel if (!vhpet_timer_interrupt_enabled(vhpet, n)) 493258579Sneel clear_isr = true; 494258579Sneel else if (vhpet_timer_msi_enabled(vhpet, n)) 495258579Sneel clear_isr = true; 496258579Sneel else if (vhpet_timer_edge_trig(vhpet, n)) 497258579Sneel clear_isr = true; 498258579Sneel else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin) 499258579Sneel clear_isr = true; 500258579Sneel else 501258579Sneel clear_isr = false; 502258579Sneel 503258579Sneel if (clear_isr) { 504258579Sneel VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to " 505258579Sneel "configuration change", n); 506258579Sneel vioapic_deassert_irq(vhpet->vm, old_pin); 507258579Sneel vhpet->isr &= ~(1 << n); 508258579Sneel } 509258579Sneel } 510258579Sneel} 511258579Sneel 512258579Sneelint 513258579Sneelvhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size, 514258579Sneel void *arg) 515258579Sneel{ 516258579Sneel struct vhpet *vhpet; 517258579Sneel uint64_t data, mask, oldval, val64; 518258579Sneel uint32_t isr_clear_mask, old_compval, old_comprate; 519258579Sneel int i, offset; 520258579Sneel 521258579Sneel vhpet = vm_hpet(vm); 522258579Sneel offset = gpa - VHPET_BASE; 523258579Sneel 524258579Sneel VHPET_LOCK(vhpet); 525258579Sneel 526258579Sneel /* Accesses to the HPET should be 4 or 8 bytes wide */ 527258579Sneel switch (size) { 528258579Sneel case 8: 529258579Sneel mask = 0xffffffffffffffff; 530258579Sneel data = val; 531258579Sneel break; 532258579Sneel case 4: 533258579Sneel mask = 0xffffffff; 534258579Sneel data = val; 535258579Sneel if ((offset & 0x4) != 0) { 536258579Sneel mask <<= 32; 537258579Sneel data <<= 32; 538258579Sneel } 539258579Sneel break; 540258579Sneel default: 541258579Sneel VM_CTR2(vhpet->vm, "hpet invalid mmio write: " 542258579Sneel "offset 0x%08x, size %d", offset, size); 543258579Sneel goto done; 544258579Sneel } 545258579Sneel 546258579Sneel /* Access to the HPET should be naturally aligned to its width */ 547258579Sneel if (offset & (size - 1)) { 548258579Sneel VM_CTR2(vhpet->vm, "hpet invalid mmio write: " 549258579Sneel "offset 0x%08x, size %d", offset, size); 550258579Sneel goto done; 551258579Sneel } 552258579Sneel 553258579Sneel if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) { 554258579Sneel oldval = vhpet->config; 555258579Sneel update_register(&vhpet->config, data, mask); 556258579Sneel if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) { 557258579Sneel if (vhpet_counter_enabled(vhpet)) { 558258579Sneel vhpet_start_counting(vhpet); 559258579Sneel VM_CTR0(vhpet->vm, "hpet enabled"); 560258579Sneel } else { 561258579Sneel vhpet_stop_counting(vhpet); 562258579Sneel VM_CTR0(vhpet->vm, "hpet disabled"); 563258579Sneel } 564258579Sneel } 565258579Sneel goto done; 566258579Sneel } 567258579Sneel 568258579Sneel if (offset == HPET_ISR || offset == HPET_ISR + 4) { 569258579Sneel isr_clear_mask = vhpet->isr & data; 570258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) { 571258579Sneel if ((isr_clear_mask & (1 << i)) != 0) { 572258579Sneel VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i); 573258579Sneel vhpet_timer_clear_isr(vhpet, i); 574258579Sneel } 575258579Sneel } 576258579Sneel goto done; 577258579Sneel } 578258579Sneel 579258579Sneel if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) { 580258579Sneel /* Zero-extend the counter to 64-bits before updating it */ 581258579Sneel val64 = vhpet->counter; 582258579Sneel update_register(&val64, data, mask); 583258579Sneel vhpet->counter = val64; 584258579Sneel if (vhpet_counter_enabled(vhpet)) 585258579Sneel vhpet_start_counting(vhpet); 586258579Sneel goto done; 587258579Sneel } 588258579Sneel 589258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) { 590258579Sneel if (offset == HPET_TIMER_CAP_CNF(i) || 591258579Sneel offset == HPET_TIMER_CAP_CNF(i) + 4) { 592258579Sneel vhpet_timer_update_config(vhpet, i, data, mask); 593258579Sneel break; 594258579Sneel } 595258579Sneel 596258579Sneel if (offset == HPET_TIMER_COMPARATOR(i) || 597258579Sneel offset == HPET_TIMER_COMPARATOR(i) + 4) { 598258579Sneel old_compval = vhpet->timer[i].compval; 599258579Sneel old_comprate = vhpet->timer[i].comprate; 600258579Sneel if (vhpet_periodic_timer(vhpet, i)) { 601258579Sneel /* 602258579Sneel * In periodic mode writes to the comparator 603258579Sneel * change the 'compval' register only if the 604258579Sneel * HPET_TCNF_VAL_SET bit is set in the config 605258579Sneel * register. 606258579Sneel */ 607258579Sneel val64 = vhpet->timer[i].comprate; 608258579Sneel update_register(&val64, data, mask); 609258579Sneel vhpet->timer[i].comprate = val64; 610258579Sneel if ((vhpet->timer[i].cap_config & 611258579Sneel HPET_TCNF_VAL_SET) != 0) { 612258579Sneel vhpet->timer[i].compval = val64; 613258579Sneel } 614258579Sneel } else { 615258579Sneel KASSERT(vhpet->timer[i].comprate == 0, 616258579Sneel ("vhpet one-shot timer %d has invalid " 617258579Sneel "rate %u", i, vhpet->timer[i].comprate)); 618258579Sneel val64 = vhpet->timer[i].compval; 619258579Sneel update_register(&val64, data, mask); 620258579Sneel vhpet->timer[i].compval = val64; 621258579Sneel } 622258579Sneel vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET; 623258579Sneel 624258579Sneel if (vhpet->timer[i].compval != old_compval || 625258579Sneel vhpet->timer[i].comprate != old_comprate) { 626258579Sneel if (vhpet_counter_enabled(vhpet)) 627258579Sneel vhpet_start_timer(vhpet, i); 628258579Sneel } 629258579Sneel break; 630258579Sneel } 631258579Sneel 632258579Sneel if (offset == HPET_TIMER_FSB_VAL(i) || 633258579Sneel offset == HPET_TIMER_FSB_ADDR(i)) { 634258579Sneel update_register(&vhpet->timer[i].msireg, data, mask); 635258579Sneel break; 636258579Sneel } 637258579Sneel } 638258579Sneeldone: 639258579Sneel VHPET_UNLOCK(vhpet); 640258579Sneel return (0); 641258579Sneel} 642258579Sneel 643258579Sneelint 644258579Sneelvhpet_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, int size, 645258579Sneel void *arg) 646258579Sneel{ 647258579Sneel int i, offset; 648258579Sneel struct vhpet *vhpet; 649258579Sneel uint64_t data; 650258579Sneel 651258579Sneel vhpet = vm_hpet(vm); 652258579Sneel offset = gpa - VHPET_BASE; 653258579Sneel 654258579Sneel VHPET_LOCK(vhpet); 655258579Sneel 656258579Sneel /* Accesses to the HPET should be 4 or 8 bytes wide */ 657258579Sneel if (size != 4 && size != 8) { 658258579Sneel VM_CTR2(vhpet->vm, "hpet invalid mmio read: " 659258579Sneel "offset 0x%08x, size %d", offset, size); 660258579Sneel data = 0; 661258579Sneel goto done; 662258579Sneel } 663258579Sneel 664258579Sneel /* Access to the HPET should be naturally aligned to its width */ 665258579Sneel if (offset & (size - 1)) { 666258579Sneel VM_CTR2(vhpet->vm, "hpet invalid mmio read: " 667258579Sneel "offset 0x%08x, size %d", offset, size); 668258579Sneel data = 0; 669258579Sneel goto done; 670258579Sneel } 671258579Sneel 672258579Sneel if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) { 673258579Sneel data = vhpet_capabilities(); 674258579Sneel goto done; 675258579Sneel } 676258579Sneel 677258579Sneel if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) { 678258579Sneel data = vhpet->config; 679258579Sneel goto done; 680258579Sneel } 681258579Sneel 682258579Sneel if (offset == HPET_ISR || offset == HPET_ISR + 4) { 683258579Sneel data = vhpet->isr; 684258579Sneel goto done; 685258579Sneel } 686258579Sneel 687258579Sneel if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) { 688258579Sneel data = vhpet_counter(vhpet, true); 689258579Sneel goto done; 690258579Sneel } 691258579Sneel 692258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) { 693258579Sneel if (offset == HPET_TIMER_CAP_CNF(i) || 694258579Sneel offset == HPET_TIMER_CAP_CNF(i) + 4) { 695258579Sneel data = vhpet->timer[i].cap_config; 696258579Sneel break; 697258579Sneel } 698258579Sneel 699258579Sneel if (offset == HPET_TIMER_COMPARATOR(i) || 700258579Sneel offset == HPET_TIMER_COMPARATOR(i) + 4) { 701258579Sneel data = vhpet->timer[i].compval; 702258579Sneel break; 703258579Sneel } 704258579Sneel 705258579Sneel if (offset == HPET_TIMER_FSB_VAL(i) || 706258579Sneel offset == HPET_TIMER_FSB_ADDR(i)) { 707258579Sneel data = vhpet->timer[i].msireg; 708258579Sneel break; 709258579Sneel } 710258579Sneel } 711258579Sneel 712258579Sneel if (i >= VHPET_NUM_TIMERS) 713258579Sneel data = 0; 714258579Sneeldone: 715258579Sneel VHPET_UNLOCK(vhpet); 716258579Sneel 717258579Sneel if (size == 4) { 718258579Sneel if (offset & 0x4) 719258579Sneel data >>= 32; 720258579Sneel } 721258579Sneel *rval = data; 722258579Sneel return (0); 723258579Sneel} 724258579Sneel 725258579Sneelstruct vhpet * 726258579Sneelvhpet_init(struct vm *vm) 727258579Sneel{ 728258579Sneel int i; 729258579Sneel struct vhpet *vhpet; 730258579Sneel struct vhpet_callout_arg *arg; 731258579Sneel struct bintime bt; 732258579Sneel 733258579Sneel vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO); 734258579Sneel vhpet->vm = vm; 735258579Sneel mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF); 736258579Sneel 737258579Sneel FREQ2BT(HPET_FREQ, &bt); 738258579Sneel vhpet->freq_sbt = bttosbt(bt); 739258579Sneel 740258579Sneel /* 741258579Sneel * Initialize HPET timer hardware state. 742258579Sneel */ 743258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) { 744258579Sneel vhpet->timer[i].cap_config = 0UL << 32 | 745258579Sneel HPET_TCAP_FSB_INT_DEL | HPET_TCAP_PER_INT; 746258579Sneel vhpet->timer[i].compval = 0xffffffff; 747258579Sneel callout_init(&vhpet->timer[i].callout, 1); 748258579Sneel 749258579Sneel arg = &vhpet->timer[i].arg; 750258579Sneel arg->vhpet = vhpet; 751258579Sneel arg->timer_num = i; 752258579Sneel } 753258579Sneel 754258579Sneel return (vhpet); 755258579Sneel} 756258579Sneel 757258579Sneelvoid 758258579Sneelvhpet_cleanup(struct vhpet *vhpet) 759258579Sneel{ 760258579Sneel int i; 761258579Sneel 762258579Sneel for (i = 0; i < VHPET_NUM_TIMERS; i++) 763258579Sneel callout_drain(&vhpet->timer[i].callout); 764258579Sneel 765258579Sneel free(vhpet, M_VHPET); 766258579Sneel} 767258579Sneel 768258579Sneelint 769258579Sneelvhpet_getcap(struct vm_hpet_cap *cap) 770258579Sneel{ 771258579Sneel 772258579Sneel cap->capabilities = vhpet_capabilities(); 773258579Sneel return (0); 774258579Sneel} 775