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