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