108e3ff32SNeel Natu /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3c49761ddSPedro F. Giffuni *
408e3ff32SNeel Natu * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
508e3ff32SNeel Natu * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
608e3ff32SNeel Natu * All rights reserved.
708e3ff32SNeel Natu *
808e3ff32SNeel Natu * Redistribution and use in source and binary forms, with or without
908e3ff32SNeel Natu * modification, are permitted provided that the following conditions
1008e3ff32SNeel Natu * are met:
1108e3ff32SNeel Natu * 1. Redistributions of source code must retain the above copyright
1208e3ff32SNeel Natu * notice, this list of conditions and the following disclaimer.
1308e3ff32SNeel Natu * 2. Redistributions in binary form must reproduce the above copyright
1408e3ff32SNeel Natu * notice, this list of conditions and the following disclaimer in the
1508e3ff32SNeel Natu * documentation and/or other materials provided with the distribution.
1608e3ff32SNeel Natu *
1708e3ff32SNeel Natu * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
1808e3ff32SNeel Natu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1908e3ff32SNeel Natu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2008e3ff32SNeel Natu * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
2108e3ff32SNeel Natu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2208e3ff32SNeel Natu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2308e3ff32SNeel Natu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2408e3ff32SNeel Natu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2508e3ff32SNeel Natu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2608e3ff32SNeel Natu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2708e3ff32SNeel Natu * SUCH DAMAGE.
2808e3ff32SNeel Natu */
2908e3ff32SNeel Natu
3008e3ff32SNeel Natu #include <sys/cdefs.h>
31483d953aSJohn Baldwin #include "opt_bhyve_snapshot.h"
32483d953aSJohn Baldwin
3308e3ff32SNeel Natu #include <sys/param.h>
3408e3ff32SNeel Natu #include <sys/lock.h>
3508e3ff32SNeel Natu #include <sys/mutex.h>
3608e3ff32SNeel Natu #include <sys/kernel.h>
3708e3ff32SNeel Natu #include <sys/malloc.h>
3808e3ff32SNeel Natu #include <sys/systm.h>
3908e3ff32SNeel Natu
4008e3ff32SNeel Natu #include <machine/vmm.h>
4108e3ff32SNeel Natu #include <machine/vmm_dev.h>
42483d953aSJohn Baldwin #include <machine/vmm_snapshot.h>
4308e3ff32SNeel Natu
44*3ccb0233SMark Johnston #include <dev/acpica/acpi_hpet.h>
45*3ccb0233SMark Johnston #include <dev/vmm/vmm_ktr.h>
46*3ccb0233SMark Johnston
4708e3ff32SNeel Natu #include "vmm_lapic.h"
48762fd208STycho Nightingale #include "vatpic.h"
4908e3ff32SNeel Natu #include "vioapic.h"
5008e3ff32SNeel Natu #include "vhpet.h"
5108e3ff32SNeel Natu
5208e3ff32SNeel Natu static MALLOC_DEFINE(M_VHPET, "vhpet", "bhyve virtual hpet");
5308e3ff32SNeel Natu
545deb1573SIan Lepore #define HPET_FREQ 16777216 /* 16.7 (2^24) Mhz */
5508e3ff32SNeel Natu #define FS_PER_S 1000000000000000ul
5608e3ff32SNeel Natu
5708e3ff32SNeel Natu /* Timer N Configuration and Capabilities Register */
5808e3ff32SNeel Natu #define HPET_TCAP_RO_MASK (HPET_TCAP_INT_ROUTE | \
5908e3ff32SNeel Natu HPET_TCAP_FSB_INT_DEL | \
6008e3ff32SNeel Natu HPET_TCAP_SIZE | \
6108e3ff32SNeel Natu HPET_TCAP_PER_INT)
6208e3ff32SNeel Natu /*
6308e3ff32SNeel Natu * HPET requires at least 3 timers and up to 32 timers per block.
6408e3ff32SNeel Natu */
6508e3ff32SNeel Natu #define VHPET_NUM_TIMERS 8
6608e3ff32SNeel Natu CTASSERT(VHPET_NUM_TIMERS >= 3 && VHPET_NUM_TIMERS <= 32);
6708e3ff32SNeel Natu
6808e3ff32SNeel Natu struct vhpet_callout_arg {
6908e3ff32SNeel Natu struct vhpet *vhpet;
7008e3ff32SNeel Natu int timer_num;
7108e3ff32SNeel Natu };
7208e3ff32SNeel Natu
7308e3ff32SNeel Natu struct vhpet {
7408e3ff32SNeel Natu struct vm *vm;
7508e3ff32SNeel Natu struct mtx mtx;
7608e3ff32SNeel Natu sbintime_t freq_sbt;
7708e3ff32SNeel Natu
7808e3ff32SNeel Natu uint64_t config; /* Configuration */
7908e3ff32SNeel Natu uint64_t isr; /* Interrupt Status */
800a9ae358SNeel Natu uint32_t countbase; /* HPET counter base value */
810a9ae358SNeel Natu sbintime_t countbase_sbt; /* uptime corresponding to base value */
8208e3ff32SNeel Natu
8308e3ff32SNeel Natu struct {
8408e3ff32SNeel Natu uint64_t cap_config; /* Configuration */
8508e3ff32SNeel Natu uint64_t msireg; /* FSB interrupt routing */
8608e3ff32SNeel Natu uint32_t compval; /* Comparator */
8708e3ff32SNeel Natu uint32_t comprate;
8808e3ff32SNeel Natu struct callout callout;
890a9ae358SNeel Natu sbintime_t callout_sbt; /* time when counter==compval */
9008e3ff32SNeel Natu struct vhpet_callout_arg arg;
9108e3ff32SNeel Natu } timer[VHPET_NUM_TIMERS];
9208e3ff32SNeel Natu };
9308e3ff32SNeel Natu
9408e3ff32SNeel Natu #define VHPET_LOCK(vhp) mtx_lock(&((vhp)->mtx))
9508e3ff32SNeel Natu #define VHPET_UNLOCK(vhp) mtx_unlock(&((vhp)->mtx))
9608e3ff32SNeel Natu
970a9ae358SNeel Natu static void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter,
980a9ae358SNeel Natu sbintime_t now);
990a9ae358SNeel Natu
10008e3ff32SNeel Natu static uint64_t
vhpet_capabilities(void)10108e3ff32SNeel Natu vhpet_capabilities(void)
10208e3ff32SNeel Natu {
10308e3ff32SNeel Natu uint64_t cap = 0;
10408e3ff32SNeel Natu
10508e3ff32SNeel Natu cap |= 0x8086 << 16; /* vendor id */
10608e3ff32SNeel Natu cap |= (VHPET_NUM_TIMERS - 1) << 8; /* number of timers */
10708e3ff32SNeel Natu cap |= 1; /* revision */
10808e3ff32SNeel Natu cap &= ~HPET_CAP_COUNT_SIZE; /* 32-bit timer */
10908e3ff32SNeel Natu
11008e3ff32SNeel Natu cap &= 0xffffffff;
11108e3ff32SNeel Natu cap |= (FS_PER_S / HPET_FREQ) << 32; /* tick period in fs */
11208e3ff32SNeel Natu
11308e3ff32SNeel Natu return (cap);
11408e3ff32SNeel Natu }
11508e3ff32SNeel Natu
11608e3ff32SNeel Natu static __inline bool
vhpet_counter_enabled(struct vhpet * vhpet)11708e3ff32SNeel Natu vhpet_counter_enabled(struct vhpet *vhpet)
11808e3ff32SNeel Natu {
11908e3ff32SNeel Natu
12008e3ff32SNeel Natu return ((vhpet->config & HPET_CNF_ENABLE) ? true : false);
12108e3ff32SNeel Natu }
12208e3ff32SNeel Natu
12308e3ff32SNeel Natu static __inline bool
vhpet_timer_msi_enabled(struct vhpet * vhpet,int n)12408e3ff32SNeel Natu vhpet_timer_msi_enabled(struct vhpet *vhpet, int n)
12508e3ff32SNeel Natu {
12608e3ff32SNeel Natu const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN;
12708e3ff32SNeel Natu
12808e3ff32SNeel Natu if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable)
12908e3ff32SNeel Natu return (true);
13008e3ff32SNeel Natu else
13108e3ff32SNeel Natu return (false);
13208e3ff32SNeel Natu }
13308e3ff32SNeel Natu
13408e3ff32SNeel Natu static __inline int
vhpet_timer_ioapic_pin(struct vhpet * vhpet,int n)13508e3ff32SNeel Natu vhpet_timer_ioapic_pin(struct vhpet *vhpet, int n)
13608e3ff32SNeel Natu {
13708e3ff32SNeel Natu /*
13808e3ff32SNeel Natu * If the timer is configured to use MSI then treat it as if the
13908e3ff32SNeel Natu * timer is not connected to the ioapic.
14008e3ff32SNeel Natu */
14108e3ff32SNeel Natu if (vhpet_timer_msi_enabled(vhpet, n))
14208e3ff32SNeel Natu return (0);
14308e3ff32SNeel Natu
14408e3ff32SNeel Natu return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
14508e3ff32SNeel Natu }
14608e3ff32SNeel Natu
14708e3ff32SNeel Natu static uint32_t
vhpet_counter(struct vhpet * vhpet,sbintime_t * nowptr)1480a9ae358SNeel Natu vhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr)
14908e3ff32SNeel Natu {
15008e3ff32SNeel Natu uint32_t val;
1510a9ae358SNeel Natu sbintime_t now, delta;
15208e3ff32SNeel Natu
1530a9ae358SNeel Natu val = vhpet->countbase;
15408e3ff32SNeel Natu if (vhpet_counter_enabled(vhpet)) {
1550a9ae358SNeel Natu now = sbinuptime();
1560a9ae358SNeel Natu delta = now - vhpet->countbase_sbt;
1570a9ae358SNeel Natu KASSERT(delta >= 0, ("vhpet_counter: uptime went backwards: "
1580a9ae358SNeel Natu "%#lx to %#lx", vhpet->countbase_sbt, now));
1590a9ae358SNeel Natu val += delta / vhpet->freq_sbt;
1600a9ae358SNeel Natu if (nowptr != NULL)
1610a9ae358SNeel Natu *nowptr = now;
1620a9ae358SNeel Natu } else {
16308e3ff32SNeel Natu /*
1640a9ae358SNeel Natu * The sbinuptime corresponding to the 'countbase' is
1650a9ae358SNeel Natu * meaningless when the counter is disabled. Make sure
166cef367e6SEitan Adler * that the caller doesn't want to use it.
16708e3ff32SNeel Natu */
1680a9ae358SNeel Natu KASSERT(nowptr == NULL, ("vhpet_counter: nowptr must be NULL"));
16908e3ff32SNeel Natu }
17008e3ff32SNeel Natu return (val);
17108e3ff32SNeel Natu }
17208e3ff32SNeel Natu
17308e3ff32SNeel Natu static void
vhpet_timer_clear_isr(struct vhpet * vhpet,int n)17408e3ff32SNeel Natu vhpet_timer_clear_isr(struct vhpet *vhpet, int n)
17508e3ff32SNeel Natu {
1760dafa5cdSNeel Natu int pin;
17708e3ff32SNeel Natu
17808e3ff32SNeel Natu if (vhpet->isr & (1 << n)) {
17908e3ff32SNeel Natu pin = vhpet_timer_ioapic_pin(vhpet, n);
18008e3ff32SNeel Natu KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n));
18108e3ff32SNeel Natu vioapic_deassert_irq(vhpet->vm, pin);
18208e3ff32SNeel Natu vhpet->isr &= ~(1 << n);
18308e3ff32SNeel Natu }
18408e3ff32SNeel Natu }
18508e3ff32SNeel Natu
18608e3ff32SNeel Natu static __inline bool
vhpet_periodic_timer(struct vhpet * vhpet,int n)18708e3ff32SNeel Natu vhpet_periodic_timer(struct vhpet *vhpet, int n)
18808e3ff32SNeel Natu {
18908e3ff32SNeel Natu
19008e3ff32SNeel Natu return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0);
19108e3ff32SNeel Natu }
19208e3ff32SNeel Natu
19308e3ff32SNeel Natu static __inline bool
vhpet_timer_interrupt_enabled(struct vhpet * vhpet,int n)19408e3ff32SNeel Natu vhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n)
19508e3ff32SNeel Natu {
19608e3ff32SNeel Natu
19708e3ff32SNeel Natu return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0);
19808e3ff32SNeel Natu }
19908e3ff32SNeel Natu
20008e3ff32SNeel Natu static __inline bool
vhpet_timer_edge_trig(struct vhpet * vhpet,int n)20108e3ff32SNeel Natu vhpet_timer_edge_trig(struct vhpet *vhpet, int n)
20208e3ff32SNeel Natu {
20308e3ff32SNeel Natu
20408e3ff32SNeel Natu KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: "
20508e3ff32SNeel Natu "timer %d is using MSI", n));
20608e3ff32SNeel Natu
20708e3ff32SNeel Natu if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0)
20808e3ff32SNeel Natu return (true);
20908e3ff32SNeel Natu else
21008e3ff32SNeel Natu return (false);
21108e3ff32SNeel Natu }
21208e3ff32SNeel Natu
21308e3ff32SNeel Natu static void
vhpet_timer_interrupt(struct vhpet * vhpet,int n)21408e3ff32SNeel Natu vhpet_timer_interrupt(struct vhpet *vhpet, int n)
21508e3ff32SNeel Natu {
2160dafa5cdSNeel Natu int pin;
21708e3ff32SNeel Natu
21808e3ff32SNeel Natu /* If interrupts are not enabled for this timer then just return. */
21908e3ff32SNeel Natu if (!vhpet_timer_interrupt_enabled(vhpet, n))
22008e3ff32SNeel Natu return;
22108e3ff32SNeel Natu
22208e3ff32SNeel Natu /*
22308e3ff32SNeel Natu * If a level triggered interrupt is already asserted then just return.
22408e3ff32SNeel Natu */
22508e3ff32SNeel Natu if ((vhpet->isr & (1 << n)) != 0) {
22608e3ff32SNeel Natu VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n);
22708e3ff32SNeel Natu return;
22808e3ff32SNeel Natu }
22908e3ff32SNeel Natu
23008e3ff32SNeel Natu if (vhpet_timer_msi_enabled(vhpet, n)) {
2314f8be175SNeel Natu lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32,
2324f8be175SNeel Natu vhpet->timer[n].msireg & 0xffffffff);
23308e3ff32SNeel Natu return;
23408e3ff32SNeel Natu }
23508e3ff32SNeel Natu
23608e3ff32SNeel Natu pin = vhpet_timer_ioapic_pin(vhpet, n);
23708e3ff32SNeel Natu if (pin == 0) {
23808e3ff32SNeel Natu VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n);
23908e3ff32SNeel Natu return;
24008e3ff32SNeel Natu }
24108e3ff32SNeel Natu
24208e3ff32SNeel Natu if (vhpet_timer_edge_trig(vhpet, n)) {
24308e3ff32SNeel Natu vioapic_pulse_irq(vhpet->vm, pin);
24408e3ff32SNeel Natu } else {
24508e3ff32SNeel Natu vhpet->isr |= 1 << n;
24608e3ff32SNeel Natu vioapic_assert_irq(vhpet->vm, pin);
24708e3ff32SNeel Natu }
24808e3ff32SNeel Natu }
24908e3ff32SNeel Natu
25008e3ff32SNeel Natu static void
vhpet_adjust_compval(struct vhpet * vhpet,int n,uint32_t counter)25108e3ff32SNeel Natu vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter)
25208e3ff32SNeel Natu {
25308e3ff32SNeel Natu uint32_t compval, comprate, compnext;
25408e3ff32SNeel Natu
25508e3ff32SNeel Natu KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n));
25608e3ff32SNeel Natu
25708e3ff32SNeel Natu compval = vhpet->timer[n].compval;
25808e3ff32SNeel Natu comprate = vhpet->timer[n].comprate;
25908e3ff32SNeel Natu
26008e3ff32SNeel Natu /*
26108e3ff32SNeel Natu * Calculate the comparator value to be used for the next periodic
26208e3ff32SNeel Natu * interrupt.
26308e3ff32SNeel Natu *
26408e3ff32SNeel Natu * This function is commonly called from the callout handler.
26508e3ff32SNeel Natu * In this scenario the 'counter' is ahead of 'compval'. To find
26608e3ff32SNeel Natu * the next value to program into the accumulator we divide the
26708e3ff32SNeel Natu * number space between 'compval' and 'counter' into 'comprate'
26808e3ff32SNeel Natu * sized units. The 'compval' is rounded up such that is "ahead"
26908e3ff32SNeel Natu * of 'counter'.
27008e3ff32SNeel Natu */
27108e3ff32SNeel Natu compnext = compval + ((counter - compval) / comprate + 1) * comprate;
27208e3ff32SNeel Natu
27308e3ff32SNeel Natu vhpet->timer[n].compval = compnext;
27408e3ff32SNeel Natu }
27508e3ff32SNeel Natu
27608e3ff32SNeel Natu static void
vhpet_handler(void * a)27708e3ff32SNeel Natu vhpet_handler(void *a)
27808e3ff32SNeel Natu {
27908e3ff32SNeel Natu int n;
28008e3ff32SNeel Natu uint32_t counter;
2810a9ae358SNeel Natu sbintime_t now;
28208e3ff32SNeel Natu struct vhpet *vhpet;
28308e3ff32SNeel Natu struct callout *callout;
28408e3ff32SNeel Natu struct vhpet_callout_arg *arg;
28508e3ff32SNeel Natu
28608e3ff32SNeel Natu arg = a;
28708e3ff32SNeel Natu vhpet = arg->vhpet;
28808e3ff32SNeel Natu n = arg->timer_num;
28908e3ff32SNeel Natu callout = &vhpet->timer[n].callout;
29008e3ff32SNeel Natu
29108e3ff32SNeel Natu VM_CTR1(vhpet->vm, "hpet t%d fired", n);
29208e3ff32SNeel Natu
29308e3ff32SNeel Natu VHPET_LOCK(vhpet);
29408e3ff32SNeel Natu
29508e3ff32SNeel Natu if (callout_pending(callout)) /* callout was reset */
29608e3ff32SNeel Natu goto done;
29708e3ff32SNeel Natu
29808e3ff32SNeel Natu if (!callout_active(callout)) /* callout was stopped */
29908e3ff32SNeel Natu goto done;
30008e3ff32SNeel Natu
30108e3ff32SNeel Natu callout_deactivate(callout);
30208e3ff32SNeel Natu
30308e3ff32SNeel Natu if (!vhpet_counter_enabled(vhpet))
30408e3ff32SNeel Natu panic("vhpet(%p) callout with counter disabled", vhpet);
30508e3ff32SNeel Natu
3060a9ae358SNeel Natu counter = vhpet_counter(vhpet, &now);
3070a9ae358SNeel Natu vhpet_start_timer(vhpet, n, counter, now);
30808e3ff32SNeel Natu vhpet_timer_interrupt(vhpet, n);
30908e3ff32SNeel Natu done:
31008e3ff32SNeel Natu VHPET_UNLOCK(vhpet);
31108e3ff32SNeel Natu return;
31208e3ff32SNeel Natu }
31308e3ff32SNeel Natu
31408e3ff32SNeel Natu static void
vhpet_stop_timer(struct vhpet * vhpet,int n,sbintime_t now)3150a9ae358SNeel Natu vhpet_stop_timer(struct vhpet *vhpet, int n, sbintime_t now)
31608e3ff32SNeel Natu {
31708e3ff32SNeel Natu
3180a9ae358SNeel Natu VM_CTR1(vhpet->vm, "hpet t%d stopped", n);
31908e3ff32SNeel Natu callout_stop(&vhpet->timer[n].callout);
3200a9ae358SNeel Natu
3210a9ae358SNeel Natu /*
3220a9ae358SNeel Natu * If the callout was scheduled to expire in the past but hasn't
3230a9ae358SNeel Natu * had a chance to execute yet then trigger the timer interrupt
3240a9ae358SNeel Natu * here. Failing to do so will result in a missed timer interrupt
3250a9ae358SNeel Natu * in the guest. This is especially bad in one-shot mode because
3260a9ae358SNeel Natu * the next interrupt has to wait for the counter to wrap around.
3270a9ae358SNeel Natu */
3280a9ae358SNeel Natu if (vhpet->timer[n].callout_sbt < now) {
3290a9ae358SNeel Natu VM_CTR1(vhpet->vm, "hpet t%d interrupt triggered after "
3300a9ae358SNeel Natu "stopping timer", n);
3310a9ae358SNeel Natu vhpet_timer_interrupt(vhpet, n);
3320a9ae358SNeel Natu }
33308e3ff32SNeel Natu }
33408e3ff32SNeel Natu
33508e3ff32SNeel Natu static void
vhpet_start_timer(struct vhpet * vhpet,int n,uint32_t counter,sbintime_t now)3360a9ae358SNeel Natu vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, sbintime_t now)
33708e3ff32SNeel Natu {
3380a9ae358SNeel Natu sbintime_t delta, precision;
33908e3ff32SNeel Natu
34008e3ff32SNeel Natu if (vhpet->timer[n].comprate != 0)
34108e3ff32SNeel Natu vhpet_adjust_compval(vhpet, n, counter);
3420a9ae358SNeel Natu else {
34308e3ff32SNeel Natu /*
3440a9ae358SNeel Natu * In one-shot mode it is the guest's responsibility to make
3450a9ae358SNeel Natu * sure that the comparator value is not in the "past". The
3460a9ae358SNeel Natu * hardware doesn't have any belt-and-suspenders to deal with
3470a9ae358SNeel Natu * this so we don't either.
34808e3ff32SNeel Natu */
34908e3ff32SNeel Natu }
35008e3ff32SNeel Natu
3510a9ae358SNeel Natu delta = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt;
3520a9ae358SNeel Natu precision = delta >> tc_precexp;
3530a9ae358SNeel Natu vhpet->timer[n].callout_sbt = now + delta;
3540a9ae358SNeel Natu callout_reset_sbt(&vhpet->timer[n].callout, vhpet->timer[n].callout_sbt,
3550a9ae358SNeel Natu precision, vhpet_handler, &vhpet->timer[n].arg, C_ABSOLUTE);
35608e3ff32SNeel Natu }
35708e3ff32SNeel Natu
35808e3ff32SNeel Natu static void
vhpet_start_counting(struct vhpet * vhpet)35908e3ff32SNeel Natu vhpet_start_counting(struct vhpet *vhpet)
36008e3ff32SNeel Natu {
36108e3ff32SNeel Natu int i;
36208e3ff32SNeel Natu
3630a9ae358SNeel Natu vhpet->countbase_sbt = sbinuptime();
3640a9ae358SNeel Natu for (i = 0; i < VHPET_NUM_TIMERS; i++) {
3650a9ae358SNeel Natu /*
3660a9ae358SNeel Natu * Restart the timers based on the value of the main counter
3670a9ae358SNeel Natu * when it stopped counting.
3680a9ae358SNeel Natu */
3690a9ae358SNeel Natu vhpet_start_timer(vhpet, i, vhpet->countbase,
3700a9ae358SNeel Natu vhpet->countbase_sbt);
3710a9ae358SNeel Natu }
37208e3ff32SNeel Natu }
37308e3ff32SNeel Natu
37408e3ff32SNeel Natu static void
vhpet_stop_counting(struct vhpet * vhpet,uint32_t counter,sbintime_t now)3750a9ae358SNeel Natu vhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, sbintime_t now)
37608e3ff32SNeel Natu {
37708e3ff32SNeel Natu int i;
37808e3ff32SNeel Natu
3790a9ae358SNeel Natu vhpet->countbase = counter;
38008e3ff32SNeel Natu for (i = 0; i < VHPET_NUM_TIMERS; i++)
3810a9ae358SNeel Natu vhpet_stop_timer(vhpet, i, now);
38208e3ff32SNeel Natu }
38308e3ff32SNeel Natu
38408e3ff32SNeel Natu static __inline void
update_register(uint64_t * regptr,uint64_t data,uint64_t mask)38508e3ff32SNeel Natu update_register(uint64_t *regptr, uint64_t data, uint64_t mask)
38608e3ff32SNeel Natu {
38708e3ff32SNeel Natu
38808e3ff32SNeel Natu *regptr &= ~mask;
38908e3ff32SNeel Natu *regptr |= (data & mask);
39008e3ff32SNeel Natu }
39108e3ff32SNeel Natu
39208e3ff32SNeel Natu static void
vhpet_timer_update_config(struct vhpet * vhpet,int n,uint64_t data,uint64_t mask)39308e3ff32SNeel Natu vhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data,
39408e3ff32SNeel Natu uint64_t mask)
39508e3ff32SNeel Natu {
39608e3ff32SNeel Natu bool clear_isr;
39708e3ff32SNeel Natu int old_pin, new_pin;
39808e3ff32SNeel Natu uint32_t allowed_irqs;
39908e3ff32SNeel Natu uint64_t oldval, newval;
40008e3ff32SNeel Natu
40108e3ff32SNeel Natu if (vhpet_timer_msi_enabled(vhpet, n) ||
40208e3ff32SNeel Natu vhpet_timer_edge_trig(vhpet, n)) {
40308e3ff32SNeel Natu if (vhpet->isr & (1 << n))
40408e3ff32SNeel Natu panic("vhpet timer %d isr should not be asserted", n);
40508e3ff32SNeel Natu }
40608e3ff32SNeel Natu old_pin = vhpet_timer_ioapic_pin(vhpet, n);
40708e3ff32SNeel Natu oldval = vhpet->timer[n].cap_config;
40808e3ff32SNeel Natu
40908e3ff32SNeel Natu newval = oldval;
41008e3ff32SNeel Natu update_register(&newval, data, mask);
41108e3ff32SNeel Natu newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE);
41208e3ff32SNeel Natu newval |= oldval & HPET_TCAP_RO_MASK;
41308e3ff32SNeel Natu
41408e3ff32SNeel Natu if (newval == oldval)
41508e3ff32SNeel Natu return;
41608e3ff32SNeel Natu
41708e3ff32SNeel Natu vhpet->timer[n].cap_config = newval;
41808e3ff32SNeel Natu VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval);
41908e3ff32SNeel Natu
42008e3ff32SNeel Natu /*
42108e3ff32SNeel Natu * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field.
42208e3ff32SNeel Natu * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set
42308e3ff32SNeel Natu * it to the default value of 0.
42408e3ff32SNeel Natu */
42508e3ff32SNeel Natu allowed_irqs = vhpet->timer[n].cap_config >> 32;
42608e3ff32SNeel Natu new_pin = vhpet_timer_ioapic_pin(vhpet, n);
42708e3ff32SNeel Natu if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) {
42808e3ff32SNeel Natu VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, "
42908e3ff32SNeel Natu "allowed_irqs 0x%08x", n, new_pin, allowed_irqs);
43008e3ff32SNeel Natu new_pin = 0;
43108e3ff32SNeel Natu vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE;
43208e3ff32SNeel Natu }
43308e3ff32SNeel Natu
43408e3ff32SNeel Natu if (!vhpet_periodic_timer(vhpet, n))
43508e3ff32SNeel Natu vhpet->timer[n].comprate = 0;
43608e3ff32SNeel Natu
43708e3ff32SNeel Natu /*
43808e3ff32SNeel Natu * If the timer's ISR bit is set then clear it in the following cases:
43908e3ff32SNeel Natu * - interrupt is disabled
44008e3ff32SNeel Natu * - interrupt type is changed from level to edge or fsb.
44108e3ff32SNeel Natu * - interrupt routing is changed
44208e3ff32SNeel Natu *
44308e3ff32SNeel Natu * This is to ensure that this timer's level triggered interrupt does
44408e3ff32SNeel Natu * not remain asserted forever.
44508e3ff32SNeel Natu */
44608e3ff32SNeel Natu if (vhpet->isr & (1 << n)) {
44708e3ff32SNeel Natu KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d",
44808e3ff32SNeel Natu n, old_pin));
44908e3ff32SNeel Natu if (!vhpet_timer_interrupt_enabled(vhpet, n))
45008e3ff32SNeel Natu clear_isr = true;
45108e3ff32SNeel Natu else if (vhpet_timer_msi_enabled(vhpet, n))
45208e3ff32SNeel Natu clear_isr = true;
45308e3ff32SNeel Natu else if (vhpet_timer_edge_trig(vhpet, n))
45408e3ff32SNeel Natu clear_isr = true;
45508e3ff32SNeel Natu else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin)
45608e3ff32SNeel Natu clear_isr = true;
45708e3ff32SNeel Natu else
45808e3ff32SNeel Natu clear_isr = false;
45908e3ff32SNeel Natu
46008e3ff32SNeel Natu if (clear_isr) {
46108e3ff32SNeel Natu VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to "
46208e3ff32SNeel Natu "configuration change", n);
46308e3ff32SNeel Natu vioapic_deassert_irq(vhpet->vm, old_pin);
46408e3ff32SNeel Natu vhpet->isr &= ~(1 << n);
46508e3ff32SNeel Natu }
46608e3ff32SNeel Natu }
46708e3ff32SNeel Natu }
46808e3ff32SNeel Natu
46908e3ff32SNeel Natu int
vhpet_mmio_write(struct vcpu * vcpu,uint64_t gpa,uint64_t val,int size,void * arg)470d3956e46SJohn Baldwin vhpet_mmio_write(struct vcpu *vcpu, uint64_t gpa, uint64_t val, int size,
47108e3ff32SNeel Natu void *arg)
47208e3ff32SNeel Natu {
47308e3ff32SNeel Natu struct vhpet *vhpet;
47408e3ff32SNeel Natu uint64_t data, mask, oldval, val64;
4750a9ae358SNeel Natu uint32_t isr_clear_mask, old_compval, old_comprate, counter;
4760a9ae358SNeel Natu sbintime_t now, *nowptr;
47708e3ff32SNeel Natu int i, offset;
47808e3ff32SNeel Natu
479d3956e46SJohn Baldwin vhpet = vm_hpet(vcpu_vm(vcpu));
48008e3ff32SNeel Natu offset = gpa - VHPET_BASE;
48108e3ff32SNeel Natu
48208e3ff32SNeel Natu VHPET_LOCK(vhpet);
48308e3ff32SNeel Natu
48408e3ff32SNeel Natu /* Accesses to the HPET should be 4 or 8 bytes wide */
48508e3ff32SNeel Natu switch (size) {
48608e3ff32SNeel Natu case 8:
48708e3ff32SNeel Natu mask = 0xffffffffffffffff;
48808e3ff32SNeel Natu data = val;
48908e3ff32SNeel Natu break;
49008e3ff32SNeel Natu case 4:
49108e3ff32SNeel Natu mask = 0xffffffff;
49208e3ff32SNeel Natu data = val;
49308e3ff32SNeel Natu if ((offset & 0x4) != 0) {
49408e3ff32SNeel Natu mask <<= 32;
49508e3ff32SNeel Natu data <<= 32;
49608e3ff32SNeel Natu }
49708e3ff32SNeel Natu break;
49808e3ff32SNeel Natu default:
49908e3ff32SNeel Natu VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
50008e3ff32SNeel Natu "offset 0x%08x, size %d", offset, size);
50108e3ff32SNeel Natu goto done;
50208e3ff32SNeel Natu }
50308e3ff32SNeel Natu
50408e3ff32SNeel Natu /* Access to the HPET should be naturally aligned to its width */
50508e3ff32SNeel Natu if (offset & (size - 1)) {
50608e3ff32SNeel Natu VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
50708e3ff32SNeel Natu "offset 0x%08x, size %d", offset, size);
50808e3ff32SNeel Natu goto done;
50908e3ff32SNeel Natu }
51008e3ff32SNeel Natu
51108e3ff32SNeel Natu if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
5120a9ae358SNeel Natu /*
5130a9ae358SNeel Natu * Get the most recent value of the counter before updating
5140a9ae358SNeel Natu * the 'config' register. If the HPET is going to be disabled
5150a9ae358SNeel Natu * then we need to update 'countbase' with the value right
5160a9ae358SNeel Natu * before it is disabled.
5170a9ae358SNeel Natu */
5180a9ae358SNeel Natu nowptr = vhpet_counter_enabled(vhpet) ? &now : NULL;
5190a9ae358SNeel Natu counter = vhpet_counter(vhpet, nowptr);
52008e3ff32SNeel Natu oldval = vhpet->config;
52108e3ff32SNeel Natu update_register(&vhpet->config, data, mask);
5220dafa5cdSNeel Natu
5230dafa5cdSNeel Natu /*
5240dafa5cdSNeel Natu * LegacyReplacement Routing is not supported so clear the
5250dafa5cdSNeel Natu * bit explicitly.
5260dafa5cdSNeel Natu */
5270dafa5cdSNeel Natu vhpet->config &= ~HPET_CNF_LEG_RT;
5280dafa5cdSNeel Natu
52908e3ff32SNeel Natu if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) {
53008e3ff32SNeel Natu if (vhpet_counter_enabled(vhpet)) {
53108e3ff32SNeel Natu vhpet_start_counting(vhpet);
53208e3ff32SNeel Natu VM_CTR0(vhpet->vm, "hpet enabled");
53308e3ff32SNeel Natu } else {
5340a9ae358SNeel Natu vhpet_stop_counting(vhpet, counter, now);
53508e3ff32SNeel Natu VM_CTR0(vhpet->vm, "hpet disabled");
53608e3ff32SNeel Natu }
53708e3ff32SNeel Natu }
53808e3ff32SNeel Natu goto done;
53908e3ff32SNeel Natu }
54008e3ff32SNeel Natu
54108e3ff32SNeel Natu if (offset == HPET_ISR || offset == HPET_ISR + 4) {
54208e3ff32SNeel Natu isr_clear_mask = vhpet->isr & data;
54308e3ff32SNeel Natu for (i = 0; i < VHPET_NUM_TIMERS; i++) {
54408e3ff32SNeel Natu if ((isr_clear_mask & (1 << i)) != 0) {
54508e3ff32SNeel Natu VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i);
54608e3ff32SNeel Natu vhpet_timer_clear_isr(vhpet, i);
54708e3ff32SNeel Natu }
54808e3ff32SNeel Natu }
54908e3ff32SNeel Natu goto done;
55008e3ff32SNeel Natu }
55108e3ff32SNeel Natu
55208e3ff32SNeel Natu if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
55308e3ff32SNeel Natu /* Zero-extend the counter to 64-bits before updating it */
5540a9ae358SNeel Natu val64 = vhpet_counter(vhpet, NULL);
55508e3ff32SNeel Natu update_register(&val64, data, mask);
5560a9ae358SNeel Natu vhpet->countbase = val64;
55708e3ff32SNeel Natu if (vhpet_counter_enabled(vhpet))
55808e3ff32SNeel Natu vhpet_start_counting(vhpet);
55908e3ff32SNeel Natu goto done;
56008e3ff32SNeel Natu }
56108e3ff32SNeel Natu
56208e3ff32SNeel Natu for (i = 0; i < VHPET_NUM_TIMERS; i++) {
56308e3ff32SNeel Natu if (offset == HPET_TIMER_CAP_CNF(i) ||
56408e3ff32SNeel Natu offset == HPET_TIMER_CAP_CNF(i) + 4) {
56508e3ff32SNeel Natu vhpet_timer_update_config(vhpet, i, data, mask);
56608e3ff32SNeel Natu break;
56708e3ff32SNeel Natu }
56808e3ff32SNeel Natu
56908e3ff32SNeel Natu if (offset == HPET_TIMER_COMPARATOR(i) ||
57008e3ff32SNeel Natu offset == HPET_TIMER_COMPARATOR(i) + 4) {
57108e3ff32SNeel Natu old_compval = vhpet->timer[i].compval;
57208e3ff32SNeel Natu old_comprate = vhpet->timer[i].comprate;
57308e3ff32SNeel Natu if (vhpet_periodic_timer(vhpet, i)) {
57408e3ff32SNeel Natu /*
57508e3ff32SNeel Natu * In periodic mode writes to the comparator
57608e3ff32SNeel Natu * change the 'compval' register only if the
57708e3ff32SNeel Natu * HPET_TCNF_VAL_SET bit is set in the config
57808e3ff32SNeel Natu * register.
57908e3ff32SNeel Natu */
58008e3ff32SNeel Natu val64 = vhpet->timer[i].comprate;
58108e3ff32SNeel Natu update_register(&val64, data, mask);
58208e3ff32SNeel Natu vhpet->timer[i].comprate = val64;
58308e3ff32SNeel Natu if ((vhpet->timer[i].cap_config &
58408e3ff32SNeel Natu HPET_TCNF_VAL_SET) != 0) {
58508e3ff32SNeel Natu vhpet->timer[i].compval = val64;
58608e3ff32SNeel Natu }
58708e3ff32SNeel Natu } else {
58808e3ff32SNeel Natu KASSERT(vhpet->timer[i].comprate == 0,
58908e3ff32SNeel Natu ("vhpet one-shot timer %d has invalid "
59008e3ff32SNeel Natu "rate %u", i, vhpet->timer[i].comprate));
59108e3ff32SNeel Natu val64 = vhpet->timer[i].compval;
59208e3ff32SNeel Natu update_register(&val64, data, mask);
59308e3ff32SNeel Natu vhpet->timer[i].compval = val64;
59408e3ff32SNeel Natu }
59508e3ff32SNeel Natu vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET;
59608e3ff32SNeel Natu
59708e3ff32SNeel Natu if (vhpet->timer[i].compval != old_compval ||
59808e3ff32SNeel Natu vhpet->timer[i].comprate != old_comprate) {
5990a9ae358SNeel Natu if (vhpet_counter_enabled(vhpet)) {
6000a9ae358SNeel Natu counter = vhpet_counter(vhpet, &now);
6010a9ae358SNeel Natu vhpet_start_timer(vhpet, i, counter,
6020a9ae358SNeel Natu now);
6030a9ae358SNeel Natu }
60408e3ff32SNeel Natu }
60508e3ff32SNeel Natu break;
60608e3ff32SNeel Natu }
60708e3ff32SNeel Natu
60808e3ff32SNeel Natu if (offset == HPET_TIMER_FSB_VAL(i) ||
60908e3ff32SNeel Natu offset == HPET_TIMER_FSB_ADDR(i)) {
61008e3ff32SNeel Natu update_register(&vhpet->timer[i].msireg, data, mask);
61108e3ff32SNeel Natu break;
61208e3ff32SNeel Natu }
61308e3ff32SNeel Natu }
61408e3ff32SNeel Natu done:
61508e3ff32SNeel Natu VHPET_UNLOCK(vhpet);
61608e3ff32SNeel Natu return (0);
61708e3ff32SNeel Natu }
61808e3ff32SNeel Natu
61908e3ff32SNeel Natu int
vhpet_mmio_read(struct vcpu * vcpu,uint64_t gpa,uint64_t * rval,int size,void * arg)620d3956e46SJohn Baldwin vhpet_mmio_read(struct vcpu *vcpu, uint64_t gpa, uint64_t *rval, int size,
62108e3ff32SNeel Natu void *arg)
62208e3ff32SNeel Natu {
62308e3ff32SNeel Natu int i, offset;
62408e3ff32SNeel Natu struct vhpet *vhpet;
62508e3ff32SNeel Natu uint64_t data;
62608e3ff32SNeel Natu
627d3956e46SJohn Baldwin vhpet = vm_hpet(vcpu_vm(vcpu));
62808e3ff32SNeel Natu offset = gpa - VHPET_BASE;
62908e3ff32SNeel Natu
63008e3ff32SNeel Natu VHPET_LOCK(vhpet);
63108e3ff32SNeel Natu
63208e3ff32SNeel Natu /* Accesses to the HPET should be 4 or 8 bytes wide */
63308e3ff32SNeel Natu if (size != 4 && size != 8) {
63408e3ff32SNeel Natu VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
63508e3ff32SNeel Natu "offset 0x%08x, size %d", offset, size);
63608e3ff32SNeel Natu data = 0;
63708e3ff32SNeel Natu goto done;
63808e3ff32SNeel Natu }
63908e3ff32SNeel Natu
64008e3ff32SNeel Natu /* Access to the HPET should be naturally aligned to its width */
64108e3ff32SNeel Natu if (offset & (size - 1)) {
64208e3ff32SNeel Natu VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
64308e3ff32SNeel Natu "offset 0x%08x, size %d", offset, size);
64408e3ff32SNeel Natu data = 0;
64508e3ff32SNeel Natu goto done;
64608e3ff32SNeel Natu }
64708e3ff32SNeel Natu
64808e3ff32SNeel Natu if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) {
64908e3ff32SNeel Natu data = vhpet_capabilities();
65008e3ff32SNeel Natu goto done;
65108e3ff32SNeel Natu }
65208e3ff32SNeel Natu
65308e3ff32SNeel Natu if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
65408e3ff32SNeel Natu data = vhpet->config;
65508e3ff32SNeel Natu goto done;
65608e3ff32SNeel Natu }
65708e3ff32SNeel Natu
65808e3ff32SNeel Natu if (offset == HPET_ISR || offset == HPET_ISR + 4) {
65908e3ff32SNeel Natu data = vhpet->isr;
66008e3ff32SNeel Natu goto done;
66108e3ff32SNeel Natu }
66208e3ff32SNeel Natu
66308e3ff32SNeel Natu if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
6640a9ae358SNeel Natu data = vhpet_counter(vhpet, NULL);
66508e3ff32SNeel Natu goto done;
66608e3ff32SNeel Natu }
66708e3ff32SNeel Natu
66808e3ff32SNeel Natu for (i = 0; i < VHPET_NUM_TIMERS; i++) {
66908e3ff32SNeel Natu if (offset == HPET_TIMER_CAP_CNF(i) ||
67008e3ff32SNeel Natu offset == HPET_TIMER_CAP_CNF(i) + 4) {
67108e3ff32SNeel Natu data = vhpet->timer[i].cap_config;
67208e3ff32SNeel Natu break;
67308e3ff32SNeel Natu }
67408e3ff32SNeel Natu
67508e3ff32SNeel Natu if (offset == HPET_TIMER_COMPARATOR(i) ||
67608e3ff32SNeel Natu offset == HPET_TIMER_COMPARATOR(i) + 4) {
67708e3ff32SNeel Natu data = vhpet->timer[i].compval;
67808e3ff32SNeel Natu break;
67908e3ff32SNeel Natu }
68008e3ff32SNeel Natu
68108e3ff32SNeel Natu if (offset == HPET_TIMER_FSB_VAL(i) ||
68208e3ff32SNeel Natu offset == HPET_TIMER_FSB_ADDR(i)) {
68308e3ff32SNeel Natu data = vhpet->timer[i].msireg;
68408e3ff32SNeel Natu break;
68508e3ff32SNeel Natu }
68608e3ff32SNeel Natu }
68708e3ff32SNeel Natu
68808e3ff32SNeel Natu if (i >= VHPET_NUM_TIMERS)
68908e3ff32SNeel Natu data = 0;
69008e3ff32SNeel Natu done:
69108e3ff32SNeel Natu VHPET_UNLOCK(vhpet);
69208e3ff32SNeel Natu
69308e3ff32SNeel Natu if (size == 4) {
69408e3ff32SNeel Natu if (offset & 0x4)
69508e3ff32SNeel Natu data >>= 32;
69608e3ff32SNeel Natu }
69708e3ff32SNeel Natu *rval = data;
69808e3ff32SNeel Natu return (0);
69908e3ff32SNeel Natu }
70008e3ff32SNeel Natu
70108e3ff32SNeel Natu struct vhpet *
vhpet_init(struct vm * vm)70208e3ff32SNeel Natu vhpet_init(struct vm *vm)
70308e3ff32SNeel Natu {
704b5b28fc9SNeel Natu int i, pincount;
70508e3ff32SNeel Natu struct vhpet *vhpet;
706b5b28fc9SNeel Natu uint64_t allowed_irqs;
70708e3ff32SNeel Natu struct vhpet_callout_arg *arg;
70808e3ff32SNeel Natu struct bintime bt;
70908e3ff32SNeel Natu
71008e3ff32SNeel Natu vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO);
71108e3ff32SNeel Natu vhpet->vm = vm;
71208e3ff32SNeel Natu mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF);
71308e3ff32SNeel Natu
71408e3ff32SNeel Natu FREQ2BT(HPET_FREQ, &bt);
71508e3ff32SNeel Natu vhpet->freq_sbt = bttosbt(bt);
71608e3ff32SNeel Natu
717b5b28fc9SNeel Natu pincount = vioapic_pincount(vm);
7184cefe96cSAlexander Motin if (pincount >= 32)
7194cefe96cSAlexander Motin allowed_irqs = 0xff000000; /* irqs 24-31 */
7204cefe96cSAlexander Motin else if (pincount >= 20)
7214cefe96cSAlexander Motin allowed_irqs = 0xf << (pincount - 4); /* 4 upper irqs */
722b5b28fc9SNeel Natu else
723b5b28fc9SNeel Natu allowed_irqs = 0;
724b5b28fc9SNeel Natu
72508e3ff32SNeel Natu /*
72608e3ff32SNeel Natu * Initialize HPET timer hardware state.
72708e3ff32SNeel Natu */
72808e3ff32SNeel Natu for (i = 0; i < VHPET_NUM_TIMERS; i++) {
729b5b28fc9SNeel Natu vhpet->timer[i].cap_config = allowed_irqs << 32;
730b5b28fc9SNeel Natu vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT;
731b5b28fc9SNeel Natu vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL;
732b5b28fc9SNeel Natu
73308e3ff32SNeel Natu vhpet->timer[i].compval = 0xffffffff;
73408e3ff32SNeel Natu callout_init(&vhpet->timer[i].callout, 1);
73508e3ff32SNeel Natu
73608e3ff32SNeel Natu arg = &vhpet->timer[i].arg;
73708e3ff32SNeel Natu arg->vhpet = vhpet;
73808e3ff32SNeel Natu arg->timer_num = i;
73908e3ff32SNeel Natu }
74008e3ff32SNeel Natu
74108e3ff32SNeel Natu return (vhpet);
74208e3ff32SNeel Natu }
74308e3ff32SNeel Natu
74408e3ff32SNeel Natu void
vhpet_cleanup(struct vhpet * vhpet)74508e3ff32SNeel Natu vhpet_cleanup(struct vhpet *vhpet)
74608e3ff32SNeel Natu {
74708e3ff32SNeel Natu int i;
74808e3ff32SNeel Natu
74908e3ff32SNeel Natu for (i = 0; i < VHPET_NUM_TIMERS; i++)
75008e3ff32SNeel Natu callout_drain(&vhpet->timer[i].callout);
75108e3ff32SNeel Natu
75208ebb360SJohn Baldwin mtx_destroy(&vhpet->mtx);
75308e3ff32SNeel Natu free(vhpet, M_VHPET);
75408e3ff32SNeel Natu }
75508e3ff32SNeel Natu
75608e3ff32SNeel Natu int
vhpet_getcap(struct vm_hpet_cap * cap)75708e3ff32SNeel Natu vhpet_getcap(struct vm_hpet_cap *cap)
75808e3ff32SNeel Natu {
75908e3ff32SNeel Natu
76008e3ff32SNeel Natu cap->capabilities = vhpet_capabilities();
76108e3ff32SNeel Natu return (0);
76208e3ff32SNeel Natu }
763483d953aSJohn Baldwin
764483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT
765483d953aSJohn Baldwin int
vhpet_snapshot(struct vhpet * vhpet,struct vm_snapshot_meta * meta)766483d953aSJohn Baldwin vhpet_snapshot(struct vhpet *vhpet, struct vm_snapshot_meta *meta)
767483d953aSJohn Baldwin {
768483d953aSJohn Baldwin int i, ret;
769483d953aSJohn Baldwin uint32_t countbase;
770483d953aSJohn Baldwin
771483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vhpet->freq_sbt, meta, ret, done);
772483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vhpet->config, meta, ret, done);
773483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vhpet->isr, meta, ret, done);
774483d953aSJohn Baldwin
775483d953aSJohn Baldwin /* at restore time the countbase should have the value it had when the
776483d953aSJohn Baldwin * snapshot was created; since the value is not directly kept in
777483d953aSJohn Baldwin * vhpet->countbase, but rather computed relative to the current system
778ef764e48SElyes Haouas * uptime using countbase_sbt, save the value returned by vhpet_counter
779483d953aSJohn Baldwin */
780483d953aSJohn Baldwin if (meta->op == VM_SNAPSHOT_SAVE)
781483d953aSJohn Baldwin countbase = vhpet_counter(vhpet, NULL);
782483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(countbase, meta, ret, done);
783483d953aSJohn Baldwin if (meta->op == VM_SNAPSHOT_RESTORE)
784483d953aSJohn Baldwin vhpet->countbase = countbase;
785483d953aSJohn Baldwin
786483d953aSJohn Baldwin for (i = 0; i < nitems(vhpet->timer); i++) {
787483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vhpet->timer[i].cap_config,
788483d953aSJohn Baldwin meta, ret, done);
789483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vhpet->timer[i].msireg, meta, ret, done);
790483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vhpet->timer[i].compval, meta, ret, done);
791483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vhpet->timer[i].comprate, meta, ret, done);
792483d953aSJohn Baldwin SNAPSHOT_VAR_OR_LEAVE(vhpet->timer[i].callout_sbt,
793483d953aSJohn Baldwin meta, ret, done);
794483d953aSJohn Baldwin }
795483d953aSJohn Baldwin
796483d953aSJohn Baldwin done:
797483d953aSJohn Baldwin return (ret);
798483d953aSJohn Baldwin }
799483d953aSJohn Baldwin
800483d953aSJohn Baldwin int
vhpet_restore_time(struct vhpet * vhpet)801483d953aSJohn Baldwin vhpet_restore_time(struct vhpet *vhpet)
802483d953aSJohn Baldwin {
803483d953aSJohn Baldwin if (vhpet_counter_enabled(vhpet))
804483d953aSJohn Baldwin vhpet_start_counting(vhpet);
805483d953aSJohn Baldwin
806483d953aSJohn Baldwin return (0);
807483d953aSJohn Baldwin }
808483d953aSJohn Baldwin #endif
809