1 /*- 2 * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> 3 * Copyright (c) 2013 Neel Natu <neel@freebsd.org> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/lock.h> 35 #include <sys/mutex.h> 36 #include <sys/kernel.h> 37 #include <sys/malloc.h> 38 #include <sys/systm.h> 39 #include <sys/cpuset.h> 40 41 #include <dev/acpica/acpi_hpet.h> 42 43 #include <machine/vmm.h> 44 #include <machine/vmm_dev.h> 45 46 #include "vmm_lapic.h" 47 #include "vioapic.h" 48 #include "vhpet.h" 49 50 #include "vmm_ktr.h" 51 52 static MALLOC_DEFINE(M_VHPET, "vhpet", "bhyve virtual hpet"); 53 54 #define HPET_FREQ 10000000 /* 10.0 Mhz */ 55 #define FS_PER_S 1000000000000000ul 56 57 /* Timer N Configuration and Capabilities Register */ 58 #define HPET_TCAP_RO_MASK (HPET_TCAP_INT_ROUTE | \ 59 HPET_TCAP_FSB_INT_DEL | \ 60 HPET_TCAP_SIZE | \ 61 HPET_TCAP_PER_INT) 62 /* 63 * HPET requires at least 3 timers and up to 32 timers per block. 64 */ 65 #define VHPET_NUM_TIMERS 8 66 CTASSERT(VHPET_NUM_TIMERS >= 3 && VHPET_NUM_TIMERS <= 32); 67 68 struct vhpet_callout_arg { 69 struct vhpet *vhpet; 70 int timer_num; 71 }; 72 73 struct vhpet { 74 struct vm *vm; 75 struct mtx mtx; 76 sbintime_t freq_sbt; 77 78 uint64_t config; /* Configuration */ 79 uint64_t isr; /* Interrupt Status */ 80 uint32_t countbase; /* HPET counter base value */ 81 sbintime_t countbase_sbt; /* uptime corresponding to base value */ 82 83 struct { 84 uint64_t cap_config; /* Configuration */ 85 uint64_t msireg; /* FSB interrupt routing */ 86 uint32_t compval; /* Comparator */ 87 uint32_t comprate; 88 struct callout callout; 89 sbintime_t callout_sbt; /* time when counter==compval */ 90 struct vhpet_callout_arg arg; 91 } timer[VHPET_NUM_TIMERS]; 92 }; 93 94 #define VHPET_LOCK(vhp) mtx_lock(&((vhp)->mtx)) 95 #define VHPET_UNLOCK(vhp) mtx_unlock(&((vhp)->mtx)) 96 97 static void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, 98 sbintime_t now); 99 100 static uint64_t 101 vhpet_capabilities(void) 102 { 103 uint64_t cap = 0; 104 105 cap |= 0x8086 << 16; /* vendor id */ 106 cap |= HPET_CAP_LEG_RT; /* legacy routing capable */ 107 cap |= (VHPET_NUM_TIMERS - 1) << 8; /* number of timers */ 108 cap |= 1; /* revision */ 109 cap &= ~HPET_CAP_COUNT_SIZE; /* 32-bit timer */ 110 111 cap &= 0xffffffff; 112 cap |= (FS_PER_S / HPET_FREQ) << 32; /* tick period in fs */ 113 114 return (cap); 115 } 116 117 static __inline bool 118 vhpet_counter_enabled(struct vhpet *vhpet) 119 { 120 121 return ((vhpet->config & HPET_CNF_ENABLE) ? true : false); 122 } 123 124 static __inline bool 125 vhpet_timer_msi_enabled(struct vhpet *vhpet, int n) 126 { 127 const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN; 128 129 /* 130 * LegacyReplacement Route configuration takes precedence over MSI 131 * for timers 0 and 1. 132 */ 133 if (n == 0 || n == 1) { 134 if (vhpet->config & HPET_CNF_LEG_RT) 135 return (false); 136 } 137 138 if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable) 139 return (true); 140 else 141 return (false); 142 } 143 144 static __inline int 145 vhpet_timer_ioapic_pin(struct vhpet *vhpet, int n) 146 { 147 /* 148 * If the timer is configured to use MSI then treat it as if the 149 * timer is not connected to the ioapic. 150 */ 151 if (vhpet_timer_msi_enabled(vhpet, n)) 152 return (0); 153 154 if (vhpet->config & HPET_CNF_LEG_RT) { 155 /* 156 * In "legacy routing" timers 0 and 1 are connected to 157 * ioapic pins 2 and 8 respectively. 158 */ 159 switch (n) { 160 case 0: 161 return (2); 162 case 1: 163 return (8); 164 } 165 } 166 167 return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9); 168 } 169 170 static uint32_t 171 vhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr) 172 { 173 uint32_t val; 174 sbintime_t now, delta; 175 176 val = vhpet->countbase; 177 if (vhpet_counter_enabled(vhpet)) { 178 now = sbinuptime(); 179 delta = now - vhpet->countbase_sbt; 180 KASSERT(delta >= 0, ("vhpet_counter: uptime went backwards: " 181 "%#lx to %#lx", vhpet->countbase_sbt, now)); 182 val += delta / vhpet->freq_sbt; 183 if (nowptr != NULL) 184 *nowptr = now; 185 } else { 186 /* 187 * The sbinuptime corresponding to the 'countbase' is 188 * meaningless when the counter is disabled. Make sure 189 * that the the caller doesn't want to use it. 190 */ 191 KASSERT(nowptr == NULL, ("vhpet_counter: nowptr must be NULL")); 192 } 193 return (val); 194 } 195 196 static void 197 vhpet_timer_clear_isr(struct vhpet *vhpet, int n) 198 { 199 int pin; 200 201 if (vhpet->isr & (1 << n)) { 202 pin = vhpet_timer_ioapic_pin(vhpet, n); 203 KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n)); 204 vioapic_deassert_irq(vhpet->vm, pin); 205 vhpet->isr &= ~(1 << n); 206 } 207 } 208 209 static __inline bool 210 vhpet_periodic_timer(struct vhpet *vhpet, int n) 211 { 212 213 return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0); 214 } 215 216 static __inline bool 217 vhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n) 218 { 219 220 return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0); 221 } 222 223 static __inline bool 224 vhpet_timer_edge_trig(struct vhpet *vhpet, int n) 225 { 226 227 KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: " 228 "timer %d is using MSI", n)); 229 230 /* The legacy replacement interrupts are always edge triggered */ 231 if (vhpet->config & HPET_CNF_LEG_RT) { 232 if (n == 0 || n == 1) 233 return (true); 234 } 235 236 if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0) 237 return (true); 238 else 239 return (false); 240 } 241 242 static void 243 vhpet_timer_interrupt(struct vhpet *vhpet, int n) 244 { 245 int pin; 246 247 /* If interrupts are not enabled for this timer then just return. */ 248 if (!vhpet_timer_interrupt_enabled(vhpet, n)) 249 return; 250 251 /* 252 * If a level triggered interrupt is already asserted then just return. 253 */ 254 if ((vhpet->isr & (1 << n)) != 0) { 255 VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n); 256 return; 257 } 258 259 if (vhpet_timer_msi_enabled(vhpet, n)) { 260 lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32, 261 vhpet->timer[n].msireg & 0xffffffff); 262 return; 263 } 264 265 pin = vhpet_timer_ioapic_pin(vhpet, n); 266 if (pin == 0) { 267 VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n); 268 return; 269 } 270 271 if (vhpet_timer_edge_trig(vhpet, n)) { 272 vioapic_pulse_irq(vhpet->vm, pin); 273 } else { 274 vhpet->isr |= 1 << n; 275 vioapic_assert_irq(vhpet->vm, pin); 276 } 277 } 278 279 static void 280 vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter) 281 { 282 uint32_t compval, comprate, compnext; 283 284 KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n)); 285 286 compval = vhpet->timer[n].compval; 287 comprate = vhpet->timer[n].comprate; 288 289 /* 290 * Calculate the comparator value to be used for the next periodic 291 * interrupt. 292 * 293 * This function is commonly called from the callout handler. 294 * In this scenario the 'counter' is ahead of 'compval'. To find 295 * the next value to program into the accumulator we divide the 296 * number space between 'compval' and 'counter' into 'comprate' 297 * sized units. The 'compval' is rounded up such that is "ahead" 298 * of 'counter'. 299 */ 300 compnext = compval + ((counter - compval) / comprate + 1) * comprate; 301 302 vhpet->timer[n].compval = compnext; 303 } 304 305 static void 306 vhpet_handler(void *a) 307 { 308 int n; 309 uint32_t counter; 310 sbintime_t now; 311 struct vhpet *vhpet; 312 struct callout *callout; 313 struct vhpet_callout_arg *arg; 314 315 arg = a; 316 vhpet = arg->vhpet; 317 n = arg->timer_num; 318 callout = &vhpet->timer[n].callout; 319 320 VM_CTR1(vhpet->vm, "hpet t%d fired", n); 321 322 VHPET_LOCK(vhpet); 323 324 if (callout_pending(callout)) /* callout was reset */ 325 goto done; 326 327 if (!callout_active(callout)) /* callout was stopped */ 328 goto done; 329 330 callout_deactivate(callout); 331 332 if (!vhpet_counter_enabled(vhpet)) 333 panic("vhpet(%p) callout with counter disabled", vhpet); 334 335 counter = vhpet_counter(vhpet, &now); 336 vhpet_start_timer(vhpet, n, counter, now); 337 vhpet_timer_interrupt(vhpet, n); 338 done: 339 VHPET_UNLOCK(vhpet); 340 return; 341 } 342 343 static void 344 vhpet_stop_timer(struct vhpet *vhpet, int n, sbintime_t now) 345 { 346 347 VM_CTR1(vhpet->vm, "hpet t%d stopped", n); 348 callout_stop(&vhpet->timer[n].callout); 349 350 /* 351 * If the callout was scheduled to expire in the past but hasn't 352 * had a chance to execute yet then trigger the timer interrupt 353 * here. Failing to do so will result in a missed timer interrupt 354 * in the guest. This is especially bad in one-shot mode because 355 * the next interrupt has to wait for the counter to wrap around. 356 */ 357 if (vhpet->timer[n].callout_sbt < now) { 358 VM_CTR1(vhpet->vm, "hpet t%d interrupt triggered after " 359 "stopping timer", n); 360 vhpet_timer_interrupt(vhpet, n); 361 } 362 } 363 364 static void 365 vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, sbintime_t now) 366 { 367 sbintime_t delta, precision; 368 369 if (vhpet->timer[n].comprate != 0) 370 vhpet_adjust_compval(vhpet, n, counter); 371 else { 372 /* 373 * In one-shot mode it is the guest's responsibility to make 374 * sure that the comparator value is not in the "past". The 375 * hardware doesn't have any belt-and-suspenders to deal with 376 * this so we don't either. 377 */ 378 } 379 380 delta = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt; 381 precision = delta >> tc_precexp; 382 vhpet->timer[n].callout_sbt = now + delta; 383 callout_reset_sbt(&vhpet->timer[n].callout, vhpet->timer[n].callout_sbt, 384 precision, vhpet_handler, &vhpet->timer[n].arg, C_ABSOLUTE); 385 } 386 387 static void 388 vhpet_start_counting(struct vhpet *vhpet) 389 { 390 int i; 391 392 vhpet->countbase_sbt = sbinuptime(); 393 for (i = 0; i < VHPET_NUM_TIMERS; i++) { 394 /* 395 * Restart the timers based on the value of the main counter 396 * when it stopped counting. 397 */ 398 vhpet_start_timer(vhpet, i, vhpet->countbase, 399 vhpet->countbase_sbt); 400 } 401 } 402 403 static void 404 vhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, sbintime_t now) 405 { 406 int i; 407 408 vhpet->countbase = counter; 409 for (i = 0; i < VHPET_NUM_TIMERS; i++) 410 vhpet_stop_timer(vhpet, i, now); 411 } 412 413 static __inline void 414 update_register(uint64_t *regptr, uint64_t data, uint64_t mask) 415 { 416 417 *regptr &= ~mask; 418 *regptr |= (data & mask); 419 } 420 421 static void 422 vhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data, 423 uint64_t mask) 424 { 425 bool clear_isr; 426 int old_pin, new_pin; 427 uint32_t allowed_irqs; 428 uint64_t oldval, newval; 429 430 if (vhpet_timer_msi_enabled(vhpet, n) || 431 vhpet_timer_edge_trig(vhpet, n)) { 432 if (vhpet->isr & (1 << n)) 433 panic("vhpet timer %d isr should not be asserted", n); 434 } 435 old_pin = vhpet_timer_ioapic_pin(vhpet, n); 436 oldval = vhpet->timer[n].cap_config; 437 438 newval = oldval; 439 update_register(&newval, data, mask); 440 newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE); 441 newval |= oldval & HPET_TCAP_RO_MASK; 442 443 if (newval == oldval) 444 return; 445 446 vhpet->timer[n].cap_config = newval; 447 VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval); 448 449 /* 450 * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field. 451 * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set 452 * it to the default value of 0. 453 */ 454 allowed_irqs = vhpet->timer[n].cap_config >> 32; 455 new_pin = vhpet_timer_ioapic_pin(vhpet, n); 456 if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) { 457 VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, " 458 "allowed_irqs 0x%08x", n, new_pin, allowed_irqs); 459 new_pin = 0; 460 vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE; 461 } 462 463 if (!vhpet_periodic_timer(vhpet, n)) 464 vhpet->timer[n].comprate = 0; 465 466 /* 467 * If the timer's ISR bit is set then clear it in the following cases: 468 * - interrupt is disabled 469 * - interrupt type is changed from level to edge or fsb. 470 * - interrupt routing is changed 471 * 472 * This is to ensure that this timer's level triggered interrupt does 473 * not remain asserted forever. 474 */ 475 if (vhpet->isr & (1 << n)) { 476 KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d", 477 n, old_pin)); 478 if (!vhpet_timer_interrupt_enabled(vhpet, n)) 479 clear_isr = true; 480 else if (vhpet_timer_msi_enabled(vhpet, n)) 481 clear_isr = true; 482 else if (vhpet_timer_edge_trig(vhpet, n)) 483 clear_isr = true; 484 else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin) 485 clear_isr = true; 486 else 487 clear_isr = false; 488 489 if (clear_isr) { 490 VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to " 491 "configuration change", n); 492 vioapic_deassert_irq(vhpet->vm, old_pin); 493 vhpet->isr &= ~(1 << n); 494 } 495 } 496 } 497 498 int 499 vhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size, 500 void *arg) 501 { 502 struct vhpet *vhpet; 503 uint64_t data, mask, oldval, val64; 504 uint32_t isr_clear_mask, old_compval, old_comprate, counter; 505 sbintime_t now, *nowptr; 506 int i, offset; 507 508 vhpet = vm_hpet(vm); 509 offset = gpa - VHPET_BASE; 510 511 VHPET_LOCK(vhpet); 512 513 /* Accesses to the HPET should be 4 or 8 bytes wide */ 514 switch (size) { 515 case 8: 516 mask = 0xffffffffffffffff; 517 data = val; 518 break; 519 case 4: 520 mask = 0xffffffff; 521 data = val; 522 if ((offset & 0x4) != 0) { 523 mask <<= 32; 524 data <<= 32; 525 } 526 break; 527 default: 528 VM_CTR2(vhpet->vm, "hpet invalid mmio write: " 529 "offset 0x%08x, size %d", offset, size); 530 goto done; 531 } 532 533 /* Access to the HPET should be naturally aligned to its width */ 534 if (offset & (size - 1)) { 535 VM_CTR2(vhpet->vm, "hpet invalid mmio write: " 536 "offset 0x%08x, size %d", offset, size); 537 goto done; 538 } 539 540 if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) { 541 /* 542 * Get the most recent value of the counter before updating 543 * the 'config' register. If the HPET is going to be disabled 544 * then we need to update 'countbase' with the value right 545 * before it is disabled. 546 */ 547 nowptr = vhpet_counter_enabled(vhpet) ? &now : NULL; 548 counter = vhpet_counter(vhpet, nowptr); 549 oldval = vhpet->config; 550 update_register(&vhpet->config, data, mask); 551 if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) { 552 if (vhpet_counter_enabled(vhpet)) { 553 vhpet_start_counting(vhpet); 554 VM_CTR0(vhpet->vm, "hpet enabled"); 555 } else { 556 vhpet_stop_counting(vhpet, counter, now); 557 VM_CTR0(vhpet->vm, "hpet disabled"); 558 } 559 } 560 goto done; 561 } 562 563 if (offset == HPET_ISR || offset == HPET_ISR + 4) { 564 isr_clear_mask = vhpet->isr & data; 565 for (i = 0; i < VHPET_NUM_TIMERS; i++) { 566 if ((isr_clear_mask & (1 << i)) != 0) { 567 VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i); 568 vhpet_timer_clear_isr(vhpet, i); 569 } 570 } 571 goto done; 572 } 573 574 if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) { 575 /* Zero-extend the counter to 64-bits before updating it */ 576 val64 = vhpet_counter(vhpet, NULL); 577 update_register(&val64, data, mask); 578 vhpet->countbase = val64; 579 if (vhpet_counter_enabled(vhpet)) 580 vhpet_start_counting(vhpet); 581 goto done; 582 } 583 584 for (i = 0; i < VHPET_NUM_TIMERS; i++) { 585 if (offset == HPET_TIMER_CAP_CNF(i) || 586 offset == HPET_TIMER_CAP_CNF(i) + 4) { 587 vhpet_timer_update_config(vhpet, i, data, mask); 588 break; 589 } 590 591 if (offset == HPET_TIMER_COMPARATOR(i) || 592 offset == HPET_TIMER_COMPARATOR(i) + 4) { 593 old_compval = vhpet->timer[i].compval; 594 old_comprate = vhpet->timer[i].comprate; 595 if (vhpet_periodic_timer(vhpet, i)) { 596 /* 597 * In periodic mode writes to the comparator 598 * change the 'compval' register only if the 599 * HPET_TCNF_VAL_SET bit is set in the config 600 * register. 601 */ 602 val64 = vhpet->timer[i].comprate; 603 update_register(&val64, data, mask); 604 vhpet->timer[i].comprate = val64; 605 if ((vhpet->timer[i].cap_config & 606 HPET_TCNF_VAL_SET) != 0) { 607 vhpet->timer[i].compval = val64; 608 } 609 } else { 610 KASSERT(vhpet->timer[i].comprate == 0, 611 ("vhpet one-shot timer %d has invalid " 612 "rate %u", i, vhpet->timer[i].comprate)); 613 val64 = vhpet->timer[i].compval; 614 update_register(&val64, data, mask); 615 vhpet->timer[i].compval = val64; 616 } 617 vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET; 618 619 if (vhpet->timer[i].compval != old_compval || 620 vhpet->timer[i].comprate != old_comprate) { 621 if (vhpet_counter_enabled(vhpet)) { 622 counter = vhpet_counter(vhpet, &now); 623 vhpet_start_timer(vhpet, i, counter, 624 now); 625 } 626 } 627 break; 628 } 629 630 if (offset == HPET_TIMER_FSB_VAL(i) || 631 offset == HPET_TIMER_FSB_ADDR(i)) { 632 update_register(&vhpet->timer[i].msireg, data, mask); 633 break; 634 } 635 } 636 done: 637 VHPET_UNLOCK(vhpet); 638 return (0); 639 } 640 641 int 642 vhpet_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, int size, 643 void *arg) 644 { 645 int i, offset; 646 struct vhpet *vhpet; 647 uint64_t data; 648 649 vhpet = vm_hpet(vm); 650 offset = gpa - VHPET_BASE; 651 652 VHPET_LOCK(vhpet); 653 654 /* Accesses to the HPET should be 4 or 8 bytes wide */ 655 if (size != 4 && size != 8) { 656 VM_CTR2(vhpet->vm, "hpet invalid mmio read: " 657 "offset 0x%08x, size %d", offset, size); 658 data = 0; 659 goto done; 660 } 661 662 /* Access to the HPET should be naturally aligned to its width */ 663 if (offset & (size - 1)) { 664 VM_CTR2(vhpet->vm, "hpet invalid mmio read: " 665 "offset 0x%08x, size %d", offset, size); 666 data = 0; 667 goto done; 668 } 669 670 if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) { 671 data = vhpet_capabilities(); 672 goto done; 673 } 674 675 if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) { 676 data = vhpet->config; 677 goto done; 678 } 679 680 if (offset == HPET_ISR || offset == HPET_ISR + 4) { 681 data = vhpet->isr; 682 goto done; 683 } 684 685 if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) { 686 data = vhpet_counter(vhpet, NULL); 687 goto done; 688 } 689 690 for (i = 0; i < VHPET_NUM_TIMERS; i++) { 691 if (offset == HPET_TIMER_CAP_CNF(i) || 692 offset == HPET_TIMER_CAP_CNF(i) + 4) { 693 data = vhpet->timer[i].cap_config; 694 break; 695 } 696 697 if (offset == HPET_TIMER_COMPARATOR(i) || 698 offset == HPET_TIMER_COMPARATOR(i) + 4) { 699 data = vhpet->timer[i].compval; 700 break; 701 } 702 703 if (offset == HPET_TIMER_FSB_VAL(i) || 704 offset == HPET_TIMER_FSB_ADDR(i)) { 705 data = vhpet->timer[i].msireg; 706 break; 707 } 708 } 709 710 if (i >= VHPET_NUM_TIMERS) 711 data = 0; 712 done: 713 VHPET_UNLOCK(vhpet); 714 715 if (size == 4) { 716 if (offset & 0x4) 717 data >>= 32; 718 } 719 *rval = data; 720 return (0); 721 } 722 723 struct vhpet * 724 vhpet_init(struct vm *vm) 725 { 726 int i, pincount; 727 struct vhpet *vhpet; 728 uint64_t allowed_irqs; 729 struct vhpet_callout_arg *arg; 730 struct bintime bt; 731 732 vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO); 733 vhpet->vm = vm; 734 mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF); 735 736 FREQ2BT(HPET_FREQ, &bt); 737 vhpet->freq_sbt = bttosbt(bt); 738 739 pincount = vioapic_pincount(vm); 740 if (pincount >= 24) 741 allowed_irqs = 0x00f00000; /* irqs 20, 21, 22 and 23 */ 742 else 743 allowed_irqs = 0; 744 745 /* 746 * Initialize HPET timer hardware state. 747 */ 748 for (i = 0; i < VHPET_NUM_TIMERS; i++) { 749 vhpet->timer[i].cap_config = allowed_irqs << 32; 750 vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT; 751 vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL; 752 753 vhpet->timer[i].compval = 0xffffffff; 754 callout_init(&vhpet->timer[i].callout, 1); 755 756 arg = &vhpet->timer[i].arg; 757 arg->vhpet = vhpet; 758 arg->timer_num = i; 759 } 760 761 return (vhpet); 762 } 763 764 void 765 vhpet_cleanup(struct vhpet *vhpet) 766 { 767 int i; 768 769 for (i = 0; i < VHPET_NUM_TIMERS; i++) 770 callout_drain(&vhpet->timer[i].callout); 771 772 free(vhpet, M_VHPET); 773 } 774 775 int 776 vhpet_getcap(struct vm_hpet_cap *cap) 777 { 778 779 cap->capabilities = vhpet_capabilities(); 780 return (0); 781 } 782