2 * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
3 * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.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>
41 #include <dev/acpica/acpi_hpet.h>
43 #include <machine/vmm.h>
44 #include <machine/vmm_dev.h>
46 #include "vmm_lapic.h"
52 static MALLOC_DEFINE(M_VHPET, "vhpet", "bhyve virtual hpet");
54 #define HPET_FREQ 10000000 /* 10.0 Mhz */
55 #define FS_PER_S 1000000000000000ul
57 /* Timer N Configuration and Capabilities Register */
58 #define HPET_TCAP_RO_MASK (HPET_TCAP_INT_ROUTE | \
59 HPET_TCAP_FSB_INT_DEL | \
63 * HPET requires at least 3 timers and up to 32 timers per block.
65 #define VHPET_NUM_TIMERS 8
66 CTASSERT(VHPET_NUM_TIMERS >= 3 && VHPET_NUM_TIMERS <= 32);
68 struct vhpet_callout_arg {
78 uint64_t config; /* Configuration */
79 uint64_t isr; /* Interrupt Status */
80 uint32_t counter; /* HPET Counter */
81 sbintime_t counter_sbt;
84 uint64_t cap_config; /* Configuration */
85 uint64_t msireg; /* FSB interrupt routing */
86 uint32_t compval; /* Comparator */
88 struct callout callout;
89 struct vhpet_callout_arg arg;
90 } timer[VHPET_NUM_TIMERS];
93 #define VHPET_LOCK(vhp) mtx_lock(&((vhp)->mtx))
94 #define VHPET_UNLOCK(vhp) mtx_unlock(&((vhp)->mtx))
97 vhpet_capabilities(void)
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 */
108 cap |= (FS_PER_S / HPET_FREQ) << 32; /* tick period in fs */
114 vhpet_counter_enabled(struct vhpet *vhpet)
117 return ((vhpet->config & HPET_CNF_ENABLE) ? true : false);
121 vhpet_timer_msi_enabled(struct vhpet *vhpet, int n)
123 const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN;
126 * LegacyReplacement Route configuration takes precedence over MSI
127 * for timers 0 and 1.
129 if (n == 0 || n == 1) {
130 if (vhpet->config & HPET_CNF_LEG_RT)
134 if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable)
141 vhpet_timer_ioapic_pin(struct vhpet *vhpet, int n)
144 * If the timer is configured to use MSI then treat it as if the
145 * timer is not connected to the ioapic.
147 if (vhpet_timer_msi_enabled(vhpet, n))
150 if (vhpet->config & HPET_CNF_LEG_RT) {
152 * In "legacy routing" timers 0 and 1 are connected to
153 * ioapic pins 2 and 8 respectively.
163 return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
167 vhpet_counter(struct vhpet *vhpet, bool latch)
170 sbintime_t cur_sbt, delta_sbt;
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;
182 * Keep track of the last value of the main counter that
183 * was read by the guest.
186 vhpet->counter = val;
187 vhpet->counter_sbt = cur_sbt;
195 vhpet_timer_clear_isr(struct vhpet *vhpet, int n)
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);
208 vhpet_periodic_timer(struct vhpet *vhpet, int n)
211 return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0);
215 vhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n)
218 return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0);
222 vhpet_timer_edge_trig(struct vhpet *vhpet, int n)
225 KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: "
226 "timer %d is using MSI", n));
228 /* The legacy replacement interrupts are always edge triggered */
229 if (vhpet->config & HPET_CNF_LEG_RT) {
230 if (n == 0 || n == 1)
234 if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0)
241 vhpet_timer_interrupt(struct vhpet *vhpet, int n)
243 int apicid, vector, vcpuid, pin;
246 /* If interrupts are not enabled for this timer then just return. */
247 if (!vhpet_timer_interrupt_enabled(vhpet, n))
251 * If a level triggered interrupt is already asserted then just return.
253 if ((vhpet->isr & (1 << n)) != 0) {
254 VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n);
258 if (vhpet_timer_msi_enabled(vhpet, n)) {
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
264 vector = vhpet->timer[n].msireg & 0xff;
265 apicid = (vhpet->timer[n].msireg >> (32 + 12)) & 0xff;
266 if (apicid != 0xff) {
268 vcpuid = vm_apicid2vcpuid(vhpet->vm, apicid);
269 lapic_set_intr(vhpet->vm, vcpuid, vector);
272 dmask = vm_active_cpus(vhpet->vm);
273 while ((vcpuid = CPU_FFS(&dmask)) != 0) {
275 CPU_CLR(vcpuid, &dmask);
276 lapic_set_intr(vhpet->vm, vcpuid, vector);
282 pin = vhpet_timer_ioapic_pin(vhpet, n);
284 VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n);
288 if (vhpet_timer_edge_trig(vhpet, n)) {
289 vioapic_pulse_irq(vhpet->vm, pin);
291 vhpet->isr |= 1 << n;
292 vioapic_assert_irq(vhpet->vm, pin);
297 vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter)
299 uint32_t compval, comprate, compnext;
301 KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n));
303 compval = vhpet->timer[n].compval;
304 comprate = vhpet->timer[n].comprate;
307 * Calculate the comparator value to be used for the next periodic
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"
317 compnext = compval + ((counter - compval) / comprate + 1) * comprate;
319 vhpet->timer[n].compval = compnext;
323 vhpet_handler(void *a)
329 struct callout *callout;
330 struct vhpet_callout_arg *arg;
335 callout = &vhpet->timer[n].callout;
337 VM_CTR1(vhpet->vm, "hpet t%d fired", n);
341 if (callout_pending(callout)) /* callout was reset */
344 if (!callout_active(callout)) /* callout was stopped */
347 callout_deactivate(callout);
349 if (!vhpet_counter_enabled(vhpet))
350 panic("vhpet(%p) callout with counter disabled", vhpet);
352 counter = vhpet_counter(vhpet, false);
354 /* Update the accumulator for periodic timers */
355 if (vhpet->timer[n].comprate != 0)
356 vhpet_adjust_compval(vhpet, n, counter);
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);
367 vhpet_stop_timer(struct vhpet *vhpet, int n)
370 callout_stop(&vhpet->timer[n].callout);
371 vhpet_timer_clear_isr(vhpet, n);
375 vhpet_start_timer(struct vhpet *vhpet, int n)
377 uint32_t counter, delta, delta2;
380 counter = vhpet_counter(vhpet, false);
382 if (vhpet->timer[n].comprate != 0)
383 vhpet_adjust_compval(vhpet, n, counter);
385 delta = vhpet->timer[n].compval - counter;
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.
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);
403 sbt = delta * vhpet->freq_sbt;
404 callout_reset_sbt(&vhpet->timer[n].callout, sbt, 0, vhpet_handler,
405 &vhpet->timer[n].arg, 0);
409 vhpet_start_counting(struct vhpet *vhpet)
413 vhpet->counter_sbt = sbinuptime();
414 for (i = 0; i < VHPET_NUM_TIMERS; i++)
415 vhpet_start_timer(vhpet, i);
419 vhpet_stop_counting(struct vhpet *vhpet)
423 for (i = 0; i < VHPET_NUM_TIMERS; i++)
424 vhpet_stop_timer(vhpet, i);
428 update_register(uint64_t *regptr, uint64_t data, uint64_t mask)
432 *regptr |= (data & mask);
436 vhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data,
440 int old_pin, new_pin;
441 uint32_t allowed_irqs;
442 uint64_t oldval, newval;
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);
449 old_pin = vhpet_timer_ioapic_pin(vhpet, n);
450 oldval = vhpet->timer[n].cap_config;
453 update_register(&newval, data, mask);
454 newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE);
455 newval |= oldval & HPET_TCAP_RO_MASK;
457 if (newval == oldval)
460 vhpet->timer[n].cap_config = newval;
461 VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval);
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.
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);
474 vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE;
477 if (!vhpet_periodic_timer(vhpet, n))
478 vhpet->timer[n].comprate = 0;
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
486 * This is to ensure that this timer's level triggered interrupt does
487 * not remain asserted forever.
489 if (vhpet->isr & (1 << n)) {
490 KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d",
492 if (!vhpet_timer_interrupt_enabled(vhpet, n))
494 else if (vhpet_timer_msi_enabled(vhpet, n))
496 else if (vhpet_timer_edge_trig(vhpet, n))
498 else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin)
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);
513 vhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size,
517 uint64_t data, mask, oldval, val64;
518 uint32_t isr_clear_mask, old_compval, old_comprate;
522 offset = gpa - VHPET_BASE;
526 /* Accesses to the HPET should be 4 or 8 bytes wide */
529 mask = 0xffffffffffffffff;
535 if ((offset & 0x4) != 0) {
541 VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
542 "offset 0x%08x, size %d", offset, size);
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);
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");
561 vhpet_stop_counting(vhpet);
562 VM_CTR0(vhpet->vm, "hpet disabled");
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);
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);
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);
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)) {
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
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;
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;
622 vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET;
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);
632 if (offset == HPET_TIMER_FSB_VAL(i) ||
633 offset == HPET_TIMER_FSB_ADDR(i)) {
634 update_register(&vhpet->timer[i].msireg, data, mask);
644 vhpet_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, int size,
652 offset = gpa - VHPET_BASE;
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);
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);
672 if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) {
673 data = vhpet_capabilities();
677 if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
678 data = vhpet->config;
682 if (offset == HPET_ISR || offset == HPET_ISR + 4) {
687 if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
688 data = vhpet_counter(vhpet, true);
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;
699 if (offset == HPET_TIMER_COMPARATOR(i) ||
700 offset == HPET_TIMER_COMPARATOR(i) + 4) {
701 data = vhpet->timer[i].compval;
705 if (offset == HPET_TIMER_FSB_VAL(i) ||
706 offset == HPET_TIMER_FSB_ADDR(i)) {
707 data = vhpet->timer[i].msireg;
712 if (i >= VHPET_NUM_TIMERS)
726 vhpet_init(struct vm *vm)
730 struct vhpet_callout_arg *arg;
733 vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO);
735 mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF);
737 FREQ2BT(HPET_FREQ, &bt);
738 vhpet->freq_sbt = bttosbt(bt);
741 * Initialize HPET timer hardware state.
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);
749 arg = &vhpet->timer[i].arg;
758 vhpet_cleanup(struct vhpet *vhpet)
762 for (i = 0; i < VHPET_NUM_TIMERS; i++)
763 callout_drain(&vhpet->timer[i].callout);
765 free(vhpet, M_VHPET);
769 vhpet_getcap(struct vm_hpet_cap *cap)
772 cap->capabilities = vhpet_capabilities();