1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 30 /* 31 * Copyright 2018 Joyent, Inc. 32 * Copyright 2022 Oxide Computer Company 33 */ 34 35 #include <sys/cdefs.h> 36 37 #include <sys/param.h> 38 #include <sys/mutex.h> 39 #include <sys/kernel.h> 40 #include <sys/kmem.h> 41 #include <sys/systm.h> 42 43 #include <dev/acpica/acpi_hpet.h> 44 45 #include <machine/vmm.h> 46 #include <machine/vmm_dev.h> 47 48 #include "vmm_lapic.h" 49 #include "vatpic.h" 50 #include "vioapic.h" 51 #include "vhpet.h" 52 53 54 #define HPET_FREQ 16777216 /* 16.7 (2^24) 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_timer { 74 uint64_t cap_config; /* Configuration */ 75 uint64_t msireg; /* FSB interrupt routing */ 76 uint32_t compval; /* Comparator */ 77 uint32_t comprate; 78 struct callout callout; 79 hrtime_t callout_expire; /* time when counter==compval */ 80 struct vhpet_callout_arg arg; 81 }; 82 83 struct vhpet { 84 struct vm *vm; 85 kmutex_t lock; 86 87 uint64_t config; /* Configuration */ 88 uint64_t isr; /* Interrupt Status */ 89 uint32_t base_count; /* HPET counter base value */ 90 hrtime_t base_time; /* uptime corresponding to base value */ 91 92 struct vhpet_timer timer[VHPET_NUM_TIMERS]; 93 }; 94 95 #define VHPET_LOCK(vhp) mutex_enter(&((vhp)->lock)) 96 #define VHPET_UNLOCK(vhp) mutex_exit(&((vhp)->lock)) 97 #define VHPET_LOCKED(vhp) MUTEX_HELD(&((vhp)->lock)) 98 99 static void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, 100 hrtime_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, hrtime_t *nowptr) 151 { 152 const hrtime_t now = gethrtime(); 153 uint32_t val = vhpet->base_count; 154 155 if (vhpet_counter_enabled(vhpet)) { 156 const hrtime_t delta = now - vhpet->base_time; 157 158 ASSERT3S(delta, >=, 0); 159 val += hrt_freq_count(delta, HPET_FREQ); 160 } else { 161 /* Value of the counter is meaningless when it is disabled */ 162 } 163 164 if (nowptr != NULL) { 165 *nowptr = now; 166 } 167 return (val); 168 } 169 170 static void 171 vhpet_timer_clear_isr(struct vhpet *vhpet, int n) 172 { 173 int pin; 174 175 if (vhpet->isr & (1 << n)) { 176 pin = vhpet_timer_ioapic_pin(vhpet, n); 177 KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n)); 178 (void) vioapic_deassert_irq(vhpet->vm, pin); 179 vhpet->isr &= ~(1 << n); 180 } 181 } 182 183 static __inline bool 184 vhpet_periodic_timer(struct vhpet *vhpet, int n) 185 { 186 187 return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0); 188 } 189 190 static __inline bool 191 vhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n) 192 { 193 194 return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0); 195 } 196 197 static __inline bool 198 vhpet_timer_edge_trig(struct vhpet *vhpet, int n) 199 { 200 201 KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: " 202 "timer %d is using MSI", n)); 203 204 if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0) 205 return (true); 206 else 207 return (false); 208 } 209 210 static void 211 vhpet_timer_interrupt(struct vhpet *vhpet, int n) 212 { 213 int pin; 214 215 /* If interrupts are not enabled for this timer then just return. */ 216 if (!vhpet_timer_interrupt_enabled(vhpet, n)) 217 return; 218 219 /* 220 * If a level triggered interrupt is already asserted then just return. 221 */ 222 if ((vhpet->isr & (1 << n)) != 0) { 223 return; 224 } 225 226 if (vhpet_timer_msi_enabled(vhpet, n)) { 227 (void) lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32, 228 vhpet->timer[n].msireg & 0xffffffff); 229 return; 230 } 231 232 pin = vhpet_timer_ioapic_pin(vhpet, n); 233 if (pin == 0) { 234 /* Interrupt is not routed to IOAPIC */ 235 return; 236 } 237 238 if (vhpet_timer_edge_trig(vhpet, n)) { 239 (void) vioapic_pulse_irq(vhpet->vm, pin); 240 } else { 241 vhpet->isr |= 1 << n; 242 (void) vioapic_assert_irq(vhpet->vm, pin); 243 } 244 } 245 246 static void 247 vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter) 248 { 249 uint32_t compval, comprate, compnext; 250 251 KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n)); 252 253 compval = vhpet->timer[n].compval; 254 comprate = vhpet->timer[n].comprate; 255 256 /* 257 * Calculate the comparator value to be used for the next periodic 258 * interrupt. 259 * 260 * This function is commonly called from the callout handler. 261 * In this scenario the 'counter' is ahead of 'compval'. To find 262 * the next value to program into the accumulator we divide the 263 * number space between 'compval' and 'counter' into 'comprate' 264 * sized units. The 'compval' is rounded up such that is "ahead" 265 * of 'counter'. 266 */ 267 compnext = compval + ((counter - compval) / comprate + 1) * comprate; 268 269 vhpet->timer[n].compval = compnext; 270 } 271 272 static void 273 vhpet_handler(void *arg) 274 { 275 const struct vhpet_callout_arg *vca = arg; 276 struct vhpet *vhpet = vca->vhpet; 277 const int n = vca->timer_num; 278 struct callout *callout = &vhpet->timer[n].callout; 279 280 VHPET_LOCK(vhpet); 281 282 if (callout_pending(callout) || !callout_active(callout)) { 283 VHPET_UNLOCK(vhpet); 284 return; 285 } 286 287 callout_deactivate(callout); 288 ASSERT(vhpet_counter_enabled(vhpet)); 289 290 if (vhpet_periodic_timer(vhpet, n)) { 291 hrtime_t now; 292 uint32_t counter = vhpet_counter(vhpet, &now); 293 294 vhpet_start_timer(vhpet, n, counter, now); 295 } else { 296 /* 297 * Zero out the expiration time to distinguish a fired timer 298 * from one which is held due to a VM pause. 299 */ 300 vhpet->timer[n].callout_expire = 0; 301 } 302 vhpet_timer_interrupt(vhpet, n); 303 304 VHPET_UNLOCK(vhpet); 305 } 306 307 static void 308 vhpet_stop_timer(struct vhpet *vhpet, int n, hrtime_t now) 309 { 310 ASSERT(VHPET_LOCKED(vhpet)); 311 312 callout_stop(&vhpet->timer[n].callout); 313 314 /* 315 * If the callout was scheduled to expire in the past but hasn't 316 * had a chance to execute yet then trigger the timer interrupt 317 * here. Failing to do so will result in a missed timer interrupt 318 * in the guest. This is especially bad in one-shot mode because 319 * the next interrupt has to wait for the counter to wrap around. 320 */ 321 if (vhpet->timer[n].callout_expire < now) { 322 vhpet_timer_interrupt(vhpet, n); 323 } 324 vhpet->timer[n].callout_expire = 0; 325 } 326 327 static void 328 vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, hrtime_t now) 329 { 330 struct vhpet_timer *timer = &vhpet->timer[n]; 331 332 ASSERT(VHPET_LOCKED(vhpet)); 333 334 if (timer->comprate != 0) 335 vhpet_adjust_compval(vhpet, n, counter); 336 else { 337 /* 338 * In one-shot mode it is the guest's responsibility to make 339 * sure that the comparator value is not in the "past". The 340 * hardware doesn't have any belt-and-suspenders to deal with 341 * this so we don't either. 342 */ 343 } 344 345 const hrtime_t delta = hrt_freq_interval(HPET_FREQ, 346 timer->compval - counter); 347 timer->callout_expire = now + delta; 348 callout_reset_hrtime(&timer->callout, timer->callout_expire, 349 vhpet_handler, &timer->arg, C_ABSOLUTE); 350 } 351 352 static void 353 vhpet_start_counting(struct vhpet *vhpet) 354 { 355 int i; 356 357 vhpet->base_time = gethrtime(); 358 for (i = 0; i < VHPET_NUM_TIMERS; i++) { 359 /* 360 * Restart the timers based on the value of the main counter 361 * when it stopped counting. 362 */ 363 vhpet_start_timer(vhpet, i, vhpet->base_count, 364 vhpet->base_time); 365 } 366 } 367 368 static void 369 vhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, hrtime_t now) 370 { 371 int i; 372 373 vhpet->base_count = counter; 374 for (i = 0; i < VHPET_NUM_TIMERS; i++) 375 vhpet_stop_timer(vhpet, i, now); 376 } 377 378 static __inline void 379 update_register(uint64_t *regptr, uint64_t data, uint64_t mask) 380 { 381 382 *regptr &= ~mask; 383 *regptr |= (data & mask); 384 } 385 386 static void 387 vhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data, 388 uint64_t mask) 389 { 390 bool clear_isr; 391 int old_pin, new_pin; 392 uint32_t allowed_irqs; 393 uint64_t oldval, newval; 394 395 if (vhpet_timer_msi_enabled(vhpet, n) || 396 vhpet_timer_edge_trig(vhpet, n)) { 397 if (vhpet->isr & (1 << n)) 398 panic("vhpet timer %d isr should not be asserted", n); 399 } 400 old_pin = vhpet_timer_ioapic_pin(vhpet, n); 401 oldval = vhpet->timer[n].cap_config; 402 403 newval = oldval; 404 update_register(&newval, data, mask); 405 newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE); 406 newval |= oldval & HPET_TCAP_RO_MASK; 407 408 if (newval == oldval) 409 return; 410 411 vhpet->timer[n].cap_config = newval; 412 413 /* 414 * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field. 415 * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set 416 * it to the default value of 0. 417 */ 418 allowed_irqs = vhpet->timer[n].cap_config >> 32; 419 new_pin = vhpet_timer_ioapic_pin(vhpet, n); 420 if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) { 421 /* Invalid IRQ configured */ 422 new_pin = 0; 423 vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE; 424 } 425 426 if (!vhpet_periodic_timer(vhpet, n)) 427 vhpet->timer[n].comprate = 0; 428 429 /* 430 * If the timer's ISR bit is set then clear it in the following cases: 431 * - interrupt is disabled 432 * - interrupt type is changed from level to edge or fsb. 433 * - interrupt routing is changed 434 * 435 * This is to ensure that this timer's level triggered interrupt does 436 * not remain asserted forever. 437 */ 438 if (vhpet->isr & (1 << n)) { 439 KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d", 440 n, old_pin)); 441 if (!vhpet_timer_interrupt_enabled(vhpet, n)) 442 clear_isr = true; 443 else if (vhpet_timer_msi_enabled(vhpet, n)) 444 clear_isr = true; 445 else if (vhpet_timer_edge_trig(vhpet, n)) 446 clear_isr = true; 447 else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin) 448 clear_isr = true; 449 else 450 clear_isr = false; 451 452 if (clear_isr) { 453 (void) vioapic_deassert_irq(vhpet->vm, old_pin); 454 vhpet->isr &= ~(1 << n); 455 } 456 } 457 } 458 459 int 460 vhpet_mmio_write(struct vm *vm, int vcpuid, uint64_t gpa, uint64_t val, 461 int size) 462 { 463 struct vhpet *vhpet; 464 uint64_t data, mask, oldval, val64; 465 uint32_t isr_clear_mask, old_compval, old_comprate, counter; 466 hrtime_t now; 467 int i, offset; 468 469 vhpet = vm_hpet(vm); 470 offset = gpa - VHPET_BASE; 471 472 VHPET_LOCK(vhpet); 473 474 /* Accesses to the HPET should be 4 or 8 bytes wide */ 475 switch (size) { 476 case 8: 477 mask = 0xffffffffffffffff; 478 data = val; 479 break; 480 case 4: 481 mask = 0xffffffff; 482 data = val; 483 if ((offset & 0x4) != 0) { 484 mask <<= 32; 485 data <<= 32; 486 } 487 break; 488 default: 489 /* Invalid MMIO write */ 490 goto done; 491 } 492 493 /* Access to the HPET should be naturally aligned to its width */ 494 if (offset & (size - 1)) { 495 goto done; 496 } 497 498 if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) { 499 /* 500 * Get the most recent value of the counter before updating 501 * the 'config' register. If the HPET is going to be disabled 502 * then we need to update 'base_count' with the value right 503 * before it is disabled. 504 */ 505 counter = vhpet_counter(vhpet, &now); 506 oldval = vhpet->config; 507 update_register(&vhpet->config, data, mask); 508 509 /* 510 * LegacyReplacement Routing is not supported so clear the 511 * bit explicitly. 512 */ 513 vhpet->config &= ~HPET_CNF_LEG_RT; 514 515 if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) { 516 if (vhpet_counter_enabled(vhpet)) { 517 vhpet_start_counting(vhpet); 518 } else { 519 vhpet_stop_counting(vhpet, counter, now); 520 } 521 } 522 goto done; 523 } 524 525 if (offset == HPET_ISR || offset == HPET_ISR + 4) { 526 isr_clear_mask = vhpet->isr & data; 527 for (i = 0; i < VHPET_NUM_TIMERS; i++) { 528 if ((isr_clear_mask & (1 << i)) != 0) { 529 vhpet_timer_clear_isr(vhpet, i); 530 } 531 } 532 goto done; 533 } 534 535 if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) { 536 /* Zero-extend the counter to 64-bits before updating it */ 537 val64 = vhpet_counter(vhpet, NULL); 538 update_register(&val64, data, mask); 539 vhpet->base_count = val64; 540 if (vhpet_counter_enabled(vhpet)) 541 vhpet_start_counting(vhpet); 542 goto done; 543 } 544 545 for (i = 0; i < VHPET_NUM_TIMERS; i++) { 546 if (offset == HPET_TIMER_CAP_CNF(i) || 547 offset == HPET_TIMER_CAP_CNF(i) + 4) { 548 vhpet_timer_update_config(vhpet, i, data, mask); 549 break; 550 } 551 552 if (offset == HPET_TIMER_COMPARATOR(i) || 553 offset == HPET_TIMER_COMPARATOR(i) + 4) { 554 old_compval = vhpet->timer[i].compval; 555 old_comprate = vhpet->timer[i].comprate; 556 if (vhpet_periodic_timer(vhpet, i)) { 557 /* 558 * In periodic mode writes to the comparator 559 * change the 'compval' register only if the 560 * HPET_TCNF_VAL_SET bit is set in the config 561 * register. 562 */ 563 val64 = vhpet->timer[i].comprate; 564 update_register(&val64, data, mask); 565 vhpet->timer[i].comprate = val64; 566 if ((vhpet->timer[i].cap_config & 567 HPET_TCNF_VAL_SET) != 0) { 568 vhpet->timer[i].compval = val64; 569 } 570 } else { 571 KASSERT(vhpet->timer[i].comprate == 0, 572 ("vhpet one-shot timer %d has invalid " 573 "rate %u", i, vhpet->timer[i].comprate)); 574 val64 = vhpet->timer[i].compval; 575 update_register(&val64, data, mask); 576 vhpet->timer[i].compval = val64; 577 } 578 vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET; 579 580 if (vhpet->timer[i].compval != old_compval || 581 vhpet->timer[i].comprate != old_comprate) { 582 if (vhpet_counter_enabled(vhpet)) { 583 counter = vhpet_counter(vhpet, &now); 584 vhpet_start_timer(vhpet, i, counter, 585 now); 586 } 587 } 588 break; 589 } 590 591 if (offset == HPET_TIMER_FSB_VAL(i) || 592 offset == HPET_TIMER_FSB_ADDR(i)) { 593 update_register(&vhpet->timer[i].msireg, data, mask); 594 break; 595 } 596 } 597 done: 598 VHPET_UNLOCK(vhpet); 599 return (0); 600 } 601 602 int 603 vhpet_mmio_read(struct vm *vm, int vcpuid, uint64_t gpa, uint64_t *rval, 604 int size) 605 { 606 int i, offset; 607 struct vhpet *vhpet; 608 uint64_t data; 609 610 vhpet = vm_hpet(vm); 611 offset = gpa - VHPET_BASE; 612 613 VHPET_LOCK(vhpet); 614 615 /* Accesses to the HPET should be 4 or 8 bytes wide */ 616 if (size != 4 && size != 8) { 617 data = 0; 618 goto done; 619 } 620 621 /* Access to the HPET should be naturally aligned to its width */ 622 if (offset & (size - 1)) { 623 data = 0; 624 goto done; 625 } 626 627 if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) { 628 data = vhpet_capabilities(); 629 goto done; 630 } 631 632 if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) { 633 data = vhpet->config; 634 goto done; 635 } 636 637 if (offset == HPET_ISR || offset == HPET_ISR + 4) { 638 data = vhpet->isr; 639 goto done; 640 } 641 642 if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) { 643 data = vhpet_counter(vhpet, NULL); 644 goto done; 645 } 646 647 for (i = 0; i < VHPET_NUM_TIMERS; i++) { 648 if (offset == HPET_TIMER_CAP_CNF(i) || 649 offset == HPET_TIMER_CAP_CNF(i) + 4) { 650 data = vhpet->timer[i].cap_config; 651 break; 652 } 653 654 if (offset == HPET_TIMER_COMPARATOR(i) || 655 offset == HPET_TIMER_COMPARATOR(i) + 4) { 656 data = vhpet->timer[i].compval; 657 break; 658 } 659 660 if (offset == HPET_TIMER_FSB_VAL(i) || 661 offset == HPET_TIMER_FSB_ADDR(i)) { 662 data = vhpet->timer[i].msireg; 663 break; 664 } 665 } 666 667 if (i >= VHPET_NUM_TIMERS) 668 data = 0; 669 done: 670 VHPET_UNLOCK(vhpet); 671 672 if (size == 4) { 673 if (offset & 0x4) 674 data >>= 32; 675 } 676 *rval = data; 677 return (0); 678 } 679 680 struct vhpet * 681 vhpet_init(struct vm *vm) 682 { 683 int i, pincount; 684 struct vhpet *vhpet; 685 uint64_t allowed_irqs; 686 struct vhpet_callout_arg *arg; 687 688 vhpet = kmem_zalloc(sizeof (struct vhpet), KM_SLEEP); 689 vhpet->vm = vm; 690 mutex_init(&vhpet->lock, NULL, MUTEX_ADAPTIVE, NULL); 691 692 pincount = vioapic_pincount(vm); 693 if (pincount >= 32) 694 allowed_irqs = 0xff000000; /* irqs 24-31 */ 695 else if (pincount >= 20) 696 allowed_irqs = 0xf << (pincount - 4); /* 4 upper irqs */ 697 else 698 allowed_irqs = 0; 699 700 /* 701 * Initialize HPET timer hardware state. 702 */ 703 for (i = 0; i < VHPET_NUM_TIMERS; i++) { 704 vhpet->timer[i].cap_config = allowed_irqs << 32; 705 vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT; 706 vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL; 707 708 vhpet->timer[i].compval = 0xffffffff; 709 callout_init(&vhpet->timer[i].callout, 1); 710 711 arg = &vhpet->timer[i].arg; 712 arg->vhpet = vhpet; 713 arg->timer_num = i; 714 } 715 716 return (vhpet); 717 } 718 719 void 720 vhpet_cleanup(struct vhpet *vhpet) 721 { 722 int i; 723 724 for (i = 0; i < VHPET_NUM_TIMERS; i++) 725 callout_drain(&vhpet->timer[i].callout); 726 727 mutex_destroy(&vhpet->lock); 728 kmem_free(vhpet, sizeof (*vhpet)); 729 } 730 731 int 732 vhpet_getcap(struct vm_hpet_cap *cap) 733 { 734 735 cap->capabilities = vhpet_capabilities(); 736 return (0); 737 } 738 void 739 vhpet_localize_resources(struct vhpet *vhpet) 740 { 741 for (uint_t i = 0; i < VHPET_NUM_TIMERS; i++) { 742 vmm_glue_callout_localize(&vhpet->timer[i].callout); 743 } 744 } 745 746 void 747 vhpet_pause(struct vhpet *vhpet) 748 { 749 VHPET_LOCK(vhpet); 750 for (uint_t i = 0; i < VHPET_NUM_TIMERS; i++) { 751 struct vhpet_timer *timer = &vhpet->timer[i]; 752 753 callout_stop(&timer->callout); 754 } 755 VHPET_UNLOCK(vhpet); 756 } 757 758 void 759 vhpet_resume(struct vhpet *vhpet) 760 { 761 VHPET_LOCK(vhpet); 762 for (uint_t i = 0; i < VHPET_NUM_TIMERS; i++) { 763 struct vhpet_timer *timer = &vhpet->timer[i]; 764 765 if (timer->callout_expire != 0) { 766 callout_reset_hrtime(&timer->callout, 767 timer->callout_expire, vhpet_handler, 768 &timer->arg, C_ABSOLUTE); 769 } 770 } 771 VHPET_UNLOCK(vhpet); 772 } 773 774 static int 775 vhpet_data_read(void *datap, const vmm_data_req_t *req) 776 { 777 VERIFY3U(req->vdr_class, ==, VDC_HPET); 778 VERIFY3U(req->vdr_version, ==, 1); 779 VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_hpet_v1)); 780 781 struct vhpet *vhpet = datap; 782 struct vdi_hpet_v1 *out = req->vdr_data; 783 784 VHPET_LOCK(vhpet); 785 out->vh_config = vhpet->config; 786 out->vh_isr = vhpet->isr; 787 out->vh_count_base = vhpet->base_count; 788 out->vh_time_base = vm_normalize_hrtime(vhpet->vm, vhpet->base_time); 789 for (uint_t i = 0; i < 8; i++) { 790 const struct vhpet_timer *timer = &vhpet->timer[i]; 791 struct vdi_hpet_timer_v1 *timer_out = &out->vh_timers[i]; 792 793 timer_out->vht_config = timer->cap_config; 794 timer_out->vht_msi = timer->msireg; 795 timer_out->vht_comp_val = timer->compval; 796 timer_out->vht_comp_rate = timer->comprate; 797 if (timer->callout_expire != 0) { 798 timer_out->vht_time_target = 799 vm_normalize_hrtime(vhpet->vm, 800 timer->callout_expire); 801 } else { 802 timer_out->vht_time_target = 0; 803 } 804 } 805 VHPET_UNLOCK(vhpet); 806 807 return (0); 808 } 809 810 enum vhpet_validation_error { 811 VVE_OK, 812 VVE_BAD_CONFIG, 813 VVE_BAD_BASE_TIME, 814 VVE_BAD_ISR, 815 VVE_BAD_TIMER_CONFIG, 816 VVE_BAD_TIMER_ISR, 817 VVE_BAD_TIMER_TIME, 818 }; 819 820 static enum vhpet_validation_error 821 vhpet_data_validate(const vmm_data_req_t *req, struct vm *vm) 822 { 823 ASSERT(req->vdr_version == 1 && 824 req->vdr_len >= sizeof (struct vdi_hpet_v1)); 825 const struct vdi_hpet_v1 *src = req->vdr_data; 826 827 /* LegacyReplacement Routing is not supported */ 828 if ((src->vh_config & HPET_CNF_LEG_RT) != 0) { 829 return (VVE_BAD_CONFIG); 830 } 831 832 /* A base time in the future makes no sense */ 833 const hrtime_t base_time = vm_denormalize_hrtime(vm, src->vh_time_base); 834 if (base_time > gethrtime()) { 835 return (VVE_BAD_BASE_TIME); 836 } 837 838 /* All asserted ISRs must be associated with an existing timer */ 839 if ((src->vh_isr & ~(uint64_t)((1 << VHPET_NUM_TIMERS) - 1)) != 0) { 840 return (VVE_BAD_ISR); 841 } 842 843 for (uint_t i = 0; i < 8; i++) { 844 const struct vdi_hpet_timer_v1 *timer = &src->vh_timers[i]; 845 846 const bool msi_enabled = 847 (timer->vht_config & HPET_TCNF_FSB_EN) != 0; 848 const bool level_triggered = 849 (timer->vht_config & HPET_TCNF_INT_TYPE) != 0; 850 const bool irq_asserted = (src->vh_isr & (1 << i)) != 0; 851 const uint32_t allowed_irqs = (timer->vht_config >> 32); 852 const uint32_t irq_pin = 853 (timer->vht_config & HPET_TCNF_INT_ROUTE) >> 9; 854 855 if (msi_enabled) { 856 if (level_triggered) { 857 return (VVE_BAD_TIMER_CONFIG); 858 } 859 } else { 860 /* 861 * Ensure interrupt route is valid as ensured by the 862 * logic in vhpet_timer_update_config. 863 */ 864 if (irq_pin != 0 && 865 (allowed_irqs & (1 << irq_pin)) == 0) { 866 return (VVE_BAD_TIMER_CONFIG); 867 } 868 } 869 if (irq_asserted && !level_triggered) { 870 return (VVE_BAD_TIMER_ISR); 871 } 872 873 if (timer->vht_time_target != 0) { 874 /* 875 * A timer scheduled earlier than the base time of the 876 * entire HPET makes no sense. 877 */ 878 const uint64_t timer_target = 879 vm_denormalize_hrtime(vm, timer->vht_time_target); 880 if (timer_target < base_time) { 881 return (VVE_BAD_TIMER_TIME); 882 } 883 } 884 } 885 886 return (VVE_OK); 887 } 888 889 static int 890 vhpet_data_write(void *datap, const vmm_data_req_t *req) 891 { 892 VERIFY3U(req->vdr_class, ==, VDC_HPET); 893 VERIFY3U(req->vdr_version, ==, 1); 894 VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_hpet_v1)); 895 896 struct vhpet *vhpet = datap; 897 898 if (vhpet_data_validate(req, vhpet->vm) != VVE_OK) { 899 return (EINVAL); 900 } 901 const struct vdi_hpet_v1 *src = req->vdr_data; 902 903 VHPET_LOCK(vhpet); 904 vhpet->config = src->vh_config; 905 vhpet->isr = src->vh_isr; 906 vhpet->base_count = src->vh_count_base; 907 vhpet->base_time = vm_denormalize_hrtime(vhpet->vm, src->vh_time_base); 908 909 for (uint_t i = 0; i < 8; i++) { 910 struct vhpet_timer *timer = &vhpet->timer[i]; 911 const struct vdi_hpet_timer_v1 *timer_src = &src->vh_timers[i]; 912 913 timer->cap_config = timer_src->vht_config; 914 timer->msireg = timer_src->vht_msi; 915 timer->compval = timer_src->vht_comp_val; 916 timer->comprate = timer_src->vht_comp_rate; 917 918 /* 919 * For now, any state associating an IOAPIC pin with a given 920 * timer is not kept in sync. (We will not increment or 921 * decrement a pin level based on the timer state.) It is left 922 * to the consumer to keep those pin levels maintained if 923 * modifying either the HPET or the IOAPIC. 924 * 925 * If both the HPET and IOAPIC are exported and then imported, 926 * this will occur naturally, as any asserted IOAPIC pin level 927 * from the HPET would come along for the ride. 928 */ 929 930 if (timer_src->vht_time_target != 0) { 931 timer->callout_expire = vm_denormalize_hrtime(vhpet->vm, 932 timer_src->vht_time_target); 933 934 if (!vm_is_paused(vhpet->vm)) { 935 callout_reset_hrtime(&timer->callout, 936 timer->callout_expire, vhpet_handler, 937 &timer->arg, C_ABSOLUTE); 938 } 939 } else { 940 timer->callout_expire = 0; 941 } 942 } 943 VHPET_UNLOCK(vhpet); 944 return (0); 945 } 946 947 static const vmm_data_version_entry_t hpet_v1 = { 948 .vdve_class = VDC_HPET, 949 .vdve_version = 1, 950 .vdve_len_expect = sizeof (struct vdi_hpet_v1), 951 .vdve_readf = vhpet_data_read, 952 .vdve_writef = vhpet_data_write, 953 }; 954 VMM_DATA_VERSION(hpet_v1); 955