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