xref: /freebsd/sys/amd64/vmm/io/vhpet.c (revision f3754afd5901857787271e73f9c34d3b9069a03f)
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