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