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 countbase; /* HPET counter base value */
81 sbintime_t countbase_sbt; /* uptime corresponding to base value */
84 uint64_t cap_config; /* Configuration */
85 uint64_t msireg; /* FSB interrupt routing */
86 uint32_t compval; /* Comparator */
88 struct callout callout;
89 sbintime_t callout_sbt; /* time when counter==compval */
90 struct vhpet_callout_arg arg;
91 } timer[VHPET_NUM_TIMERS];
94 #define VHPET_LOCK(vhp) mtx_lock(&((vhp)->mtx))
95 #define VHPET_UNLOCK(vhp) mtx_unlock(&((vhp)->mtx))
97 static void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter,
101 vhpet_capabilities(void)
105 cap |= 0x8086 << 16; /* vendor id */
106 cap |= HPET_CAP_LEG_RT; /* legacy routing capable */
107 cap |= (VHPET_NUM_TIMERS - 1) << 8; /* number of timers */
108 cap |= 1; /* revision */
109 cap &= ~HPET_CAP_COUNT_SIZE; /* 32-bit timer */
112 cap |= (FS_PER_S / HPET_FREQ) << 32; /* tick period in fs */
118 vhpet_counter_enabled(struct vhpet *vhpet)
121 return ((vhpet->config & HPET_CNF_ENABLE) ? true : false);
125 vhpet_timer_msi_enabled(struct vhpet *vhpet, int n)
127 const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN;
130 * LegacyReplacement Route configuration takes precedence over MSI
131 * for timers 0 and 1.
133 if (n == 0 || n == 1) {
134 if (vhpet->config & HPET_CNF_LEG_RT)
138 if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable)
145 vhpet_timer_ioapic_pin(struct vhpet *vhpet, int n)
148 * If the timer is configured to use MSI then treat it as if the
149 * timer is not connected to the ioapic.
151 if (vhpet_timer_msi_enabled(vhpet, n))
154 if (vhpet->config & HPET_CNF_LEG_RT) {
156 * In "legacy routing" timers 0 and 1 are connected to
157 * ioapic pins 2 and 8 respectively.
167 return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
171 vhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr)
174 sbintime_t now, delta;
176 val = vhpet->countbase;
177 if (vhpet_counter_enabled(vhpet)) {
179 delta = now - vhpet->countbase_sbt;
180 KASSERT(delta >= 0, ("vhpet_counter: uptime went backwards: "
181 "%#lx to %#lx", vhpet->countbase_sbt, now));
182 val += delta / vhpet->freq_sbt;
187 * The sbinuptime corresponding to the 'countbase' is
188 * meaningless when the counter is disabled. Make sure
189 * that the the caller doesn't want to use it.
191 KASSERT(nowptr == NULL, ("vhpet_counter: nowptr must be NULL"));
197 vhpet_timer_clear_isr(struct vhpet *vhpet, int n)
201 if (vhpet->isr & (1 << n)) {
202 pin = vhpet_timer_ioapic_pin(vhpet, n);
203 KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n));
204 vioapic_deassert_irq(vhpet->vm, pin);
205 vhpet->isr &= ~(1 << n);
210 vhpet_periodic_timer(struct vhpet *vhpet, int n)
213 return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0);
217 vhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n)
220 return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0);
224 vhpet_timer_edge_trig(struct vhpet *vhpet, int n)
227 KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: "
228 "timer %d is using MSI", n));
230 /* The legacy replacement interrupts are always edge triggered */
231 if (vhpet->config & HPET_CNF_LEG_RT) {
232 if (n == 0 || n == 1)
236 if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0)
243 vhpet_timer_interrupt(struct vhpet *vhpet, int n)
247 /* If interrupts are not enabled for this timer then just return. */
248 if (!vhpet_timer_interrupt_enabled(vhpet, n))
252 * If a level triggered interrupt is already asserted then just return.
254 if ((vhpet->isr & (1 << n)) != 0) {
255 VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n);
259 if (vhpet_timer_msi_enabled(vhpet, n)) {
260 lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32,
261 vhpet->timer[n].msireg & 0xffffffff);
265 pin = vhpet_timer_ioapic_pin(vhpet, n);
267 VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n);
271 if (vhpet_timer_edge_trig(vhpet, n)) {
272 vioapic_pulse_irq(vhpet->vm, pin);
274 vhpet->isr |= 1 << n;
275 vioapic_assert_irq(vhpet->vm, pin);
280 vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter)
282 uint32_t compval, comprate, compnext;
284 KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n));
286 compval = vhpet->timer[n].compval;
287 comprate = vhpet->timer[n].comprate;
290 * Calculate the comparator value to be used for the next periodic
293 * This function is commonly called from the callout handler.
294 * In this scenario the 'counter' is ahead of 'compval'. To find
295 * the next value to program into the accumulator we divide the
296 * number space between 'compval' and 'counter' into 'comprate'
297 * sized units. The 'compval' is rounded up such that is "ahead"
300 compnext = compval + ((counter - compval) / comprate + 1) * comprate;
302 vhpet->timer[n].compval = compnext;
306 vhpet_handler(void *a)
312 struct callout *callout;
313 struct vhpet_callout_arg *arg;
318 callout = &vhpet->timer[n].callout;
320 VM_CTR1(vhpet->vm, "hpet t%d fired", n);
324 if (callout_pending(callout)) /* callout was reset */
327 if (!callout_active(callout)) /* callout was stopped */
330 callout_deactivate(callout);
332 if (!vhpet_counter_enabled(vhpet))
333 panic("vhpet(%p) callout with counter disabled", vhpet);
335 counter = vhpet_counter(vhpet, &now);
336 vhpet_start_timer(vhpet, n, counter, now);
337 vhpet_timer_interrupt(vhpet, n);
344 vhpet_stop_timer(struct vhpet *vhpet, int n, sbintime_t now)
347 VM_CTR1(vhpet->vm, "hpet t%d stopped", n);
348 callout_stop(&vhpet->timer[n].callout);
351 * If the callout was scheduled to expire in the past but hasn't
352 * had a chance to execute yet then trigger the timer interrupt
353 * here. Failing to do so will result in a missed timer interrupt
354 * in the guest. This is especially bad in one-shot mode because
355 * the next interrupt has to wait for the counter to wrap around.
357 if (vhpet->timer[n].callout_sbt < now) {
358 VM_CTR1(vhpet->vm, "hpet t%d interrupt triggered after "
359 "stopping timer", n);
360 vhpet_timer_interrupt(vhpet, n);
365 vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, sbintime_t now)
367 sbintime_t delta, precision;
369 if (vhpet->timer[n].comprate != 0)
370 vhpet_adjust_compval(vhpet, n, counter);
373 * In one-shot mode it is the guest's responsibility to make
374 * sure that the comparator value is not in the "past". The
375 * hardware doesn't have any belt-and-suspenders to deal with
376 * this so we don't either.
380 delta = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt;
381 precision = delta >> tc_precexp;
382 vhpet->timer[n].callout_sbt = now + delta;
383 callout_reset_sbt(&vhpet->timer[n].callout, vhpet->timer[n].callout_sbt,
384 precision, vhpet_handler, &vhpet->timer[n].arg, C_ABSOLUTE);
388 vhpet_start_counting(struct vhpet *vhpet)
392 vhpet->countbase_sbt = sbinuptime();
393 for (i = 0; i < VHPET_NUM_TIMERS; i++) {
395 * Restart the timers based on the value of the main counter
396 * when it stopped counting.
398 vhpet_start_timer(vhpet, i, vhpet->countbase,
399 vhpet->countbase_sbt);
404 vhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, sbintime_t now)
408 vhpet->countbase = counter;
409 for (i = 0; i < VHPET_NUM_TIMERS; i++)
410 vhpet_stop_timer(vhpet, i, now);
414 update_register(uint64_t *regptr, uint64_t data, uint64_t mask)
418 *regptr |= (data & mask);
422 vhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data,
426 int old_pin, new_pin;
427 uint32_t allowed_irqs;
428 uint64_t oldval, newval;
430 if (vhpet_timer_msi_enabled(vhpet, n) ||
431 vhpet_timer_edge_trig(vhpet, n)) {
432 if (vhpet->isr & (1 << n))
433 panic("vhpet timer %d isr should not be asserted", n);
435 old_pin = vhpet_timer_ioapic_pin(vhpet, n);
436 oldval = vhpet->timer[n].cap_config;
439 update_register(&newval, data, mask);
440 newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE);
441 newval |= oldval & HPET_TCAP_RO_MASK;
443 if (newval == oldval)
446 vhpet->timer[n].cap_config = newval;
447 VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval);
450 * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field.
451 * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set
452 * it to the default value of 0.
454 allowed_irqs = vhpet->timer[n].cap_config >> 32;
455 new_pin = vhpet_timer_ioapic_pin(vhpet, n);
456 if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) {
457 VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, "
458 "allowed_irqs 0x%08x", n, new_pin, allowed_irqs);
460 vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE;
463 if (!vhpet_periodic_timer(vhpet, n))
464 vhpet->timer[n].comprate = 0;
467 * If the timer's ISR bit is set then clear it in the following cases:
468 * - interrupt is disabled
469 * - interrupt type is changed from level to edge or fsb.
470 * - interrupt routing is changed
472 * This is to ensure that this timer's level triggered interrupt does
473 * not remain asserted forever.
475 if (vhpet->isr & (1 << n)) {
476 KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d",
478 if (!vhpet_timer_interrupt_enabled(vhpet, n))
480 else if (vhpet_timer_msi_enabled(vhpet, n))
482 else if (vhpet_timer_edge_trig(vhpet, n))
484 else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin)
490 VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to "
491 "configuration change", n);
492 vioapic_deassert_irq(vhpet->vm, old_pin);
493 vhpet->isr &= ~(1 << n);
499 vhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size,
503 uint64_t data, mask, oldval, val64;
504 uint32_t isr_clear_mask, old_compval, old_comprate, counter;
505 sbintime_t now, *nowptr;
509 offset = gpa - VHPET_BASE;
513 /* Accesses to the HPET should be 4 or 8 bytes wide */
516 mask = 0xffffffffffffffff;
522 if ((offset & 0x4) != 0) {
528 VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
529 "offset 0x%08x, size %d", offset, size);
533 /* Access to the HPET should be naturally aligned to its width */
534 if (offset & (size - 1)) {
535 VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
536 "offset 0x%08x, size %d", offset, size);
540 if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
542 * Get the most recent value of the counter before updating
543 * the 'config' register. If the HPET is going to be disabled
544 * then we need to update 'countbase' with the value right
545 * before it is disabled.
547 nowptr = vhpet_counter_enabled(vhpet) ? &now : NULL;
548 counter = vhpet_counter(vhpet, nowptr);
549 oldval = vhpet->config;
550 update_register(&vhpet->config, data, mask);
551 if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) {
552 if (vhpet_counter_enabled(vhpet)) {
553 vhpet_start_counting(vhpet);
554 VM_CTR0(vhpet->vm, "hpet enabled");
556 vhpet_stop_counting(vhpet, counter, now);
557 VM_CTR0(vhpet->vm, "hpet disabled");
563 if (offset == HPET_ISR || offset == HPET_ISR + 4) {
564 isr_clear_mask = vhpet->isr & data;
565 for (i = 0; i < VHPET_NUM_TIMERS; i++) {
566 if ((isr_clear_mask & (1 << i)) != 0) {
567 VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i);
568 vhpet_timer_clear_isr(vhpet, i);
574 if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
575 /* Zero-extend the counter to 64-bits before updating it */
576 val64 = vhpet_counter(vhpet, NULL);
577 update_register(&val64, data, mask);
578 vhpet->countbase = val64;
579 if (vhpet_counter_enabled(vhpet))
580 vhpet_start_counting(vhpet);
584 for (i = 0; i < VHPET_NUM_TIMERS; i++) {
585 if (offset == HPET_TIMER_CAP_CNF(i) ||
586 offset == HPET_TIMER_CAP_CNF(i) + 4) {
587 vhpet_timer_update_config(vhpet, i, data, mask);
591 if (offset == HPET_TIMER_COMPARATOR(i) ||
592 offset == HPET_TIMER_COMPARATOR(i) + 4) {
593 old_compval = vhpet->timer[i].compval;
594 old_comprate = vhpet->timer[i].comprate;
595 if (vhpet_periodic_timer(vhpet, i)) {
597 * In periodic mode writes to the comparator
598 * change the 'compval' register only if the
599 * HPET_TCNF_VAL_SET bit is set in the config
602 val64 = vhpet->timer[i].comprate;
603 update_register(&val64, data, mask);
604 vhpet->timer[i].comprate = val64;
605 if ((vhpet->timer[i].cap_config &
606 HPET_TCNF_VAL_SET) != 0) {
607 vhpet->timer[i].compval = val64;
610 KASSERT(vhpet->timer[i].comprate == 0,
611 ("vhpet one-shot timer %d has invalid "
612 "rate %u", i, vhpet->timer[i].comprate));
613 val64 = vhpet->timer[i].compval;
614 update_register(&val64, data, mask);
615 vhpet->timer[i].compval = val64;
617 vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET;
619 if (vhpet->timer[i].compval != old_compval ||
620 vhpet->timer[i].comprate != old_comprate) {
621 if (vhpet_counter_enabled(vhpet)) {
622 counter = vhpet_counter(vhpet, &now);
623 vhpet_start_timer(vhpet, i, counter,
630 if (offset == HPET_TIMER_FSB_VAL(i) ||
631 offset == HPET_TIMER_FSB_ADDR(i)) {
632 update_register(&vhpet->timer[i].msireg, data, mask);
642 vhpet_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, int size,
650 offset = gpa - VHPET_BASE;
654 /* Accesses to the HPET should be 4 or 8 bytes wide */
655 if (size != 4 && size != 8) {
656 VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
657 "offset 0x%08x, size %d", offset, size);
662 /* Access to the HPET should be naturally aligned to its width */
663 if (offset & (size - 1)) {
664 VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
665 "offset 0x%08x, size %d", offset, size);
670 if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) {
671 data = vhpet_capabilities();
675 if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
676 data = vhpet->config;
680 if (offset == HPET_ISR || offset == HPET_ISR + 4) {
685 if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
686 data = vhpet_counter(vhpet, NULL);
690 for (i = 0; i < VHPET_NUM_TIMERS; i++) {
691 if (offset == HPET_TIMER_CAP_CNF(i) ||
692 offset == HPET_TIMER_CAP_CNF(i) + 4) {
693 data = vhpet->timer[i].cap_config;
697 if (offset == HPET_TIMER_COMPARATOR(i) ||
698 offset == HPET_TIMER_COMPARATOR(i) + 4) {
699 data = vhpet->timer[i].compval;
703 if (offset == HPET_TIMER_FSB_VAL(i) ||
704 offset == HPET_TIMER_FSB_ADDR(i)) {
705 data = vhpet->timer[i].msireg;
710 if (i >= VHPET_NUM_TIMERS)
724 vhpet_init(struct vm *vm)
728 uint64_t allowed_irqs;
729 struct vhpet_callout_arg *arg;
732 vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO);
734 mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF);
736 FREQ2BT(HPET_FREQ, &bt);
737 vhpet->freq_sbt = bttosbt(bt);
739 pincount = vioapic_pincount(vm);
741 allowed_irqs = 0x00f00000; /* irqs 20, 21, 22 and 23 */
746 * Initialize HPET timer hardware state.
748 for (i = 0; i < VHPET_NUM_TIMERS; i++) {
749 vhpet->timer[i].cap_config = allowed_irqs << 32;
750 vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT;
751 vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL;
753 vhpet->timer[i].compval = 0xffffffff;
754 callout_init(&vhpet->timer[i].callout, 1);
756 arg = &vhpet->timer[i].arg;
765 vhpet_cleanup(struct vhpet *vhpet)
769 for (i = 0; i < VHPET_NUM_TIMERS; i++)
770 callout_drain(&vhpet->timer[i].callout);
772 free(vhpet, M_VHPET);
776 vhpet_getcap(struct vm_hpet_cap *cap)
779 cap->capabilities = vhpet_capabilities();