vhpet.c revision 266477
189580Smsmith/*-
2139749Simp * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
3129449Sscottl * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
4129449Sscottl * All rights reserved.
5129449Sscottl *
689580Smsmith * Redistribution and use in source and binary forms, with or without
789580Smsmith * modification, are permitted provided that the following conditions
889580Smsmith * are met:
989580Smsmith * 1. Redistributions of source code must retain the above copyright
1089580Smsmith *    notice, this list of conditions and the following disclaimer.
1189580Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1289580Smsmith *    notice, this list of conditions and the following disclaimer in the
1389580Smsmith *    documentation and/or other materials provided with the distribution.
1489580Smsmith *
1589580Smsmith * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
1689580Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1789580Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1889580Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
1989580Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2089580Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2189580Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2289580Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2389580Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2489580Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2589580Smsmith * SUCH DAMAGE.
2689580Smsmith *
2789580Smsmith * $FreeBSD: stable/10/sys/amd64/vmm/io/vhpet.c 266477 2014-05-20 21:05:36Z jhb $
2889580Smsmith */
2989580Smsmith
3089580Smsmith#include <sys/cdefs.h>
3189580Smsmith__FBSDID("$FreeBSD: stable/10/sys/amd64/vmm/io/vhpet.c 266477 2014-05-20 21:05:36Z jhb $");
3289580Smsmith
3389580Smsmith#include <sys/param.h>
3489580Smsmith#include <sys/lock.h>
3589580Smsmith#include <sys/mutex.h>
3689580Smsmith#include <sys/kernel.h>
3789580Smsmith#include <sys/malloc.h>
38120477Sscottl#include <sys/systm.h>
3989580Smsmith#include <sys/cpuset.h>
4089580Smsmith
4189580Smsmith#include <dev/acpica/acpi_hpet.h>
4289580Smsmith
43120477Sscottl#include <machine/vmm.h>
44129449Sscottl#include <machine/vmm_dev.h>
4589580Smsmith
4689580Smsmith#include "vmm_lapic.h"
4789580Smsmith#include "vioapic.h"
4889580Smsmith#include "vhpet.h"
4989580Smsmith
50143063Sjoerg#include "vmm_ktr.h"
51143063Sjoerg
52143063Sjoergstatic MALLOC_DEFINE(M_VHPET, "vhpet", "bhyve virtual hpet");
53143063Sjoerg
5489580Smsmith#define	HPET_FREQ	10000000		/* 10.0 Mhz */
55129449Sscottl#define	FS_PER_S	1000000000000000ul
5689580Smsmith
57120477Sscottl/* Timer N Configuration and Capabilities Register */
58120477Sscottl#define	HPET_TCAP_RO_MASK	(HPET_TCAP_INT_ROUTE 	|		\
59120477Sscottl				 HPET_TCAP_FSB_INT_DEL	|		\
60120477Sscottl				 HPET_TCAP_SIZE		|		\
6189580Smsmith				 HPET_TCAP_PER_INT)
6289580Smsmith/*
6389580Smsmith * HPET requires at least 3 timers and up to 32 timers per block.
6489580Smsmith */
6589580Smsmith#define	VHPET_NUM_TIMERS	8
66254379SjkimCTASSERT(VHPET_NUM_TIMERS >= 3 && VHPET_NUM_TIMERS <= 32);
6789580Smsmith
6889580Smsmithstruct vhpet_callout_arg {
6989580Smsmith	struct vhpet *vhpet;
7089580Smsmith	int timer_num;
7189580Smsmith};
7289580Smsmith
7389580Smsmithstruct vhpet {
7489580Smsmith	struct vm	*vm;
7589580Smsmith	struct mtx	mtx;
7689580Smsmith	sbintime_t	freq_sbt;
7789580Smsmith
7889580Smsmith	uint64_t	config;		/* Configuration */
7989580Smsmith	uint64_t	isr;		/* Interrupt Status */
8089580Smsmith	uint32_t	countbase;	/* HPET counter base value */
8189580Smsmith	sbintime_t	countbase_sbt;	/* uptime corresponding to base value */
8289580Smsmith
8389580Smsmith	struct {
8489580Smsmith		uint64_t	cap_config;	/* Configuration */
8589580Smsmith		uint64_t	msireg;		/* FSB interrupt routing */
8689580Smsmith		uint32_t	compval;	/* Comparator */
8789580Smsmith		uint32_t	comprate;
8889580Smsmith		struct callout	callout;
8989580Smsmith		sbintime_t	callout_sbt;	/* time when counter==compval */
9089580Smsmith		struct vhpet_callout_arg arg;
9189580Smsmith	} timer[VHPET_NUM_TIMERS];
9289580Smsmith};
9389580Smsmith
9489580Smsmith#define	VHPET_LOCK(vhp)		mtx_lock(&((vhp)->mtx))
9589580Smsmith#define	VHPET_UNLOCK(vhp)	mtx_unlock(&((vhp)->mtx))
9689580Smsmith
9789580Smsmithstatic void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter,
9889580Smsmith    sbintime_t now);
9989580Smsmith
10089580Smsmithstatic uint64_t
10189580Smsmithvhpet_capabilities(void)
10289580Smsmith{
10389580Smsmith	uint64_t cap = 0;
10489580Smsmith
10589580Smsmith	cap |= 0x8086 << 16;			/* vendor id */
10689580Smsmith	cap |= HPET_CAP_LEG_RT;			/* legacy routing capable */
10789580Smsmith	cap |= (VHPET_NUM_TIMERS - 1) << 8;	/* number of timers */
10889580Smsmith	cap |= 1;				/* revision */
10989580Smsmith	cap &= ~HPET_CAP_COUNT_SIZE;		/* 32-bit timer */
11089580Smsmith
11189580Smsmith	cap &= 0xffffffff;
11289580Smsmith	cap |= (FS_PER_S / HPET_FREQ) << 32;	/* tick period in fs */
11389580Smsmith
11489580Smsmith	return (cap);
11589580Smsmith}
11689580Smsmith
11789580Smsmithstatic __inline bool
11889580Smsmithvhpet_counter_enabled(struct vhpet *vhpet)
11989580Smsmith{
12089580Smsmith
12189580Smsmith	return ((vhpet->config & HPET_CNF_ENABLE) ? true : false);
12289580Smsmith}
12389580Smsmith
12489580Smsmithstatic __inline bool
12589580Smsmithvhpet_timer_msi_enabled(struct vhpet *vhpet, int n)
12689580Smsmith{
12789580Smsmith	const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN;
12889580Smsmith
12989580Smsmith	/*
13089580Smsmith	 * LegacyReplacement Route configuration takes precedence over MSI
13189580Smsmith	 * for timers 0 and 1.
13289580Smsmith	 */
13389580Smsmith	if (n == 0 || n == 1) {
13489580Smsmith		if (vhpet->config & HPET_CNF_LEG_RT)
13589580Smsmith			return (false);
13689580Smsmith	}
13789580Smsmith
13889580Smsmith	if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable)
13989580Smsmith		return (true);
14089580Smsmith	else
14189580Smsmith		return (false);
14289580Smsmith}
14389580Smsmith
14489580Smsmithstatic __inline int
14589580Smsmithvhpet_timer_ioapic_pin(struct vhpet *vhpet, int n)
14689580Smsmith{
14789580Smsmith	/*
14889580Smsmith	 * If the timer is configured to use MSI then treat it as if the
14989580Smsmith	 * timer is not connected to the ioapic.
15089580Smsmith	 */
15189580Smsmith	if (vhpet_timer_msi_enabled(vhpet, n))
152114001Sscottl		return (0);
15389580Smsmith
15489580Smsmith	if (vhpet->config & HPET_CNF_LEG_RT) {
15589580Smsmith		/*
15689580Smsmith		 * In "legacy routing" timers 0 and 1 are connected to
15789580Smsmith		 * ioapic pins 2 and 8 respectively.
15889580Smsmith		 */
159129449Sscottl		switch (n) {
160129449Sscottl		case 0:
161129449Sscottl			return (2);
162129449Sscottl		case 1:
16389580Smsmith			return (8);
16489580Smsmith		}
16589580Smsmith	}
16689580Smsmith
16789580Smsmith	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
16889580Smsmith}
16989580Smsmith
17089580Smsmithstatic uint32_t
17189580Smsmithvhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr)
17289580Smsmith{
17389580Smsmith	uint32_t val;
17489580Smsmith	sbintime_t now, delta;
17589580Smsmith
17689580Smsmith	val = vhpet->countbase;
17789580Smsmith	if (vhpet_counter_enabled(vhpet)) {
17889580Smsmith		now = sbinuptime();
17989580Smsmith		delta = now - vhpet->countbase_sbt;
18089580Smsmith		KASSERT(delta >= 0, ("vhpet_counter: uptime went backwards: "
18189580Smsmith		    "%#lx to %#lx", vhpet->countbase_sbt, now));
18289580Smsmith		val += delta / vhpet->freq_sbt;
18389580Smsmith		if (nowptr != NULL)
18489580Smsmith			*nowptr = now;
18589580Smsmith	} else {
18689580Smsmith		/*
18789580Smsmith		 * The sbinuptime corresponding to the 'countbase' is
18889580Smsmith		 * meaningless when the counter is disabled. Make sure
18989580Smsmith		 * that the the caller doesn't want to use it.
19089580Smsmith		 */
19189580Smsmith		KASSERT(nowptr == NULL, ("vhpet_counter: nowptr must be NULL"));
19289580Smsmith	}
19389580Smsmith	return (val);
19489580Smsmith}
19589580Smsmith
19689580Smsmithstatic void
19789580Smsmithvhpet_timer_clear_isr(struct vhpet *vhpet, int n)
19889580Smsmith{
19989580Smsmith	int pin;
20089580Smsmith
20189580Smsmith	if (vhpet->isr & (1 << n)) {
20289580Smsmith		pin = vhpet_timer_ioapic_pin(vhpet, n);
20389580Smsmith		KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n));
20489580Smsmith		vioapic_deassert_irq(vhpet->vm, pin);
20589580Smsmith		vhpet->isr &= ~(1 << n);
20689580Smsmith	}
20789580Smsmith}
20889580Smsmith
20989580Smsmithstatic __inline bool
21089580Smsmithvhpet_periodic_timer(struct vhpet *vhpet, int n)
21189580Smsmith{
21289580Smsmith
21389580Smsmith	return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0);
21489580Smsmith}
21589580Smsmith
21689580Smsmithstatic __inline bool
21789580Smsmithvhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n)
21889580Smsmith{
21989580Smsmith
22089580Smsmith	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0);
22189580Smsmith}
22289580Smsmith
22389580Smsmithstatic __inline bool
22489580Smsmithvhpet_timer_edge_trig(struct vhpet *vhpet, int n)
22589580Smsmith{
22689580Smsmith
22789580Smsmith	KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: "
22889580Smsmith	    "timer %d is using MSI", n));
22989580Smsmith
23089580Smsmith	/* The legacy replacement interrupts are always edge triggered */
23189580Smsmith	if (vhpet->config & HPET_CNF_LEG_RT) {
23289580Smsmith		if (n == 0 || n == 1)
23389580Smsmith			return (true);
23489580Smsmith	}
23589580Smsmith
23689580Smsmith	if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0)
23789580Smsmith		return (true);
23889580Smsmith	else
23989580Smsmith		return (false);
24089580Smsmith}
24189580Smsmith
24289580Smsmithstatic void
24389580Smsmithvhpet_timer_interrupt(struct vhpet *vhpet, int n)
24489580Smsmith{
24589580Smsmith	int pin;
24689580Smsmith
24789580Smsmith	/* If interrupts are not enabled for this timer then just return. */
24889580Smsmith	if (!vhpet_timer_interrupt_enabled(vhpet, n))
24989580Smsmith		return;
25089580Smsmith
25189580Smsmith	/*
25289580Smsmith	 * If a level triggered interrupt is already asserted then just return.
25389580Smsmith	 */
25489580Smsmith	if ((vhpet->isr & (1 << n)) != 0) {
25589580Smsmith		VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n);
25689580Smsmith		return;
25789580Smsmith	}
25889580Smsmith
25989580Smsmith	if (vhpet_timer_msi_enabled(vhpet, n)) {
26089580Smsmith		lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32,
26189580Smsmith		    vhpet->timer[n].msireg & 0xffffffff);
26289580Smsmith		return;
26389580Smsmith	}
26489580Smsmith
26589580Smsmith	pin = vhpet_timer_ioapic_pin(vhpet, n);
26689580Smsmith	if (pin == 0) {
26789580Smsmith		VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n);
26889580Smsmith		return;
26989580Smsmith	}
27089580Smsmith
27189580Smsmith	if (vhpet_timer_edge_trig(vhpet, n)) {
27289580Smsmith		vioapic_pulse_irq(vhpet->vm, pin);
27389580Smsmith	} else {
27489580Smsmith		vhpet->isr |= 1 << n;
27589580Smsmith		vioapic_assert_irq(vhpet->vm, pin);
27689580Smsmith	}
27789580Smsmith}
27889580Smsmith
27989580Smsmithstatic void
28089580Smsmithvhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter)
28189580Smsmith{
28289580Smsmith	uint32_t compval, comprate, compnext;
28389580Smsmith
28489580Smsmith	KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n));
28589580Smsmith
28689580Smsmith	compval = vhpet->timer[n].compval;
28789580Smsmith	comprate = vhpet->timer[n].comprate;
28889580Smsmith
28989580Smsmith	/*
29089580Smsmith	 * Calculate the comparator value to be used for the next periodic
29189580Smsmith	 * interrupt.
29289580Smsmith	 *
29389580Smsmith	 * This function is commonly called from the callout handler.
29489580Smsmith	 * In this scenario the 'counter' is ahead of 'compval'. To find
29589580Smsmith	 * the next value to program into the accumulator we divide the
29689580Smsmith	 * number space between 'compval' and 'counter' into 'comprate'
29789580Smsmith	 * sized units. The 'compval' is rounded up such that is "ahead"
29889580Smsmith	 * of 'counter'.
29989580Smsmith	 */
30089580Smsmith	compnext = compval + ((counter - compval) / comprate + 1) * comprate;
30189580Smsmith
30289580Smsmith	vhpet->timer[n].compval = compnext;
30389580Smsmith}
30489580Smsmith
30589580Smsmithstatic void
30689580Smsmithvhpet_handler(void *a)
30789580Smsmith{
30889580Smsmith	int n;
30989580Smsmith	uint32_t counter;
31089580Smsmith	sbintime_t now;
31189580Smsmith	struct vhpet *vhpet;
31289580Smsmith	struct callout *callout;
31389580Smsmith	struct vhpet_callout_arg *arg;
31489580Smsmith
31589580Smsmith	arg = a;
31689580Smsmith	vhpet = arg->vhpet;
31789580Smsmith	n = arg->timer_num;
31889580Smsmith	callout = &vhpet->timer[n].callout;
31989580Smsmith
32089580Smsmith	VM_CTR1(vhpet->vm, "hpet t%d fired", n);
32189580Smsmith
32289580Smsmith	VHPET_LOCK(vhpet);
32389580Smsmith
32489580Smsmith	if (callout_pending(callout))		/* callout was reset */
32589580Smsmith		goto done;
32689580Smsmith
32789580Smsmith	if (!callout_active(callout))		/* callout was stopped */
32889580Smsmith		goto done;
32989580Smsmith
33089580Smsmith	callout_deactivate(callout);
33189580Smsmith
33289580Smsmith	if (!vhpet_counter_enabled(vhpet))
33389580Smsmith		panic("vhpet(%p) callout with counter disabled", vhpet);
33489580Smsmith
33589580Smsmith	counter = vhpet_counter(vhpet, &now);
33689580Smsmith	vhpet_start_timer(vhpet, n, counter, now);
33789580Smsmith	vhpet_timer_interrupt(vhpet, n);
33889580Smsmithdone:
33989580Smsmith	VHPET_UNLOCK(vhpet);
34089580Smsmith	return;
34189580Smsmith}
34289580Smsmith
34389580Smsmithstatic void
34489580Smsmithvhpet_stop_timer(struct vhpet *vhpet, int n, sbintime_t now)
34589580Smsmith{
34689580Smsmith
34789580Smsmith	VM_CTR1(vhpet->vm, "hpet t%d stopped", n);
34889580Smsmith	callout_stop(&vhpet->timer[n].callout);
34989580Smsmith
35089580Smsmith	/*
35189580Smsmith	 * If the callout was scheduled to expire in the past but hasn't
35289580Smsmith	 * had a chance to execute yet then trigger the timer interrupt
35389580Smsmith	 * here. Failing to do so will result in a missed timer interrupt
35489580Smsmith	 * in the guest. This is especially bad in one-shot mode because
35589580Smsmith	 * the next interrupt has to wait for the counter to wrap around.
35689580Smsmith	 */
35789580Smsmith	if (vhpet->timer[n].callout_sbt < now) {
35889580Smsmith		VM_CTR1(vhpet->vm, "hpet t%d interrupt triggered after "
35989580Smsmith		    "stopping timer", n);
36089580Smsmith		vhpet_timer_interrupt(vhpet, n);
36189580Smsmith	}
36289580Smsmith}
36389580Smsmith
36489580Smsmithstatic void
36589580Smsmithvhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, sbintime_t now)
36689580Smsmith{
36789580Smsmith	sbintime_t delta, precision;
36889580Smsmith
36989580Smsmith	if (vhpet->timer[n].comprate != 0)
37089580Smsmith		vhpet_adjust_compval(vhpet, n, counter);
37189580Smsmith	else {
37289580Smsmith		/*
37389580Smsmith		 * In one-shot mode it is the guest's responsibility to make
37489580Smsmith		 * sure that the comparator value is not in the "past". The
37589580Smsmith		 * hardware doesn't have any belt-and-suspenders to deal with
37689580Smsmith		 * this so we don't either.
37789580Smsmith		 */
37889580Smsmith	}
37989580Smsmith
38089580Smsmith	delta = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt;
38189580Smsmith	precision = delta >> tc_precexp;
38289580Smsmith	vhpet->timer[n].callout_sbt = now + delta;
38389580Smsmith	callout_reset_sbt(&vhpet->timer[n].callout, vhpet->timer[n].callout_sbt,
38489580Smsmith	    precision, vhpet_handler, &vhpet->timer[n].arg, C_ABSOLUTE);
38589580Smsmith}
38689580Smsmith
38789580Smsmithstatic void
38889580Smsmithvhpet_start_counting(struct vhpet *vhpet)
38989580Smsmith{
39089580Smsmith	int i;
39189580Smsmith
39289580Smsmith	vhpet->countbase_sbt = sbinuptime();
39389580Smsmith	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
39489580Smsmith		/*
39589580Smsmith		 * Restart the timers based on the value of the main counter
39689580Smsmith		 * when it stopped counting.
39789580Smsmith		 */
39889580Smsmith		vhpet_start_timer(vhpet, i, vhpet->countbase,
39989580Smsmith		    vhpet->countbase_sbt);
40089580Smsmith	}
40189580Smsmith}
40289580Smsmith
40389580Smsmithstatic void
40489580Smsmithvhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, sbintime_t now)
40589580Smsmith{
40689580Smsmith	int i;
40789580Smsmith
40889580Smsmith	vhpet->countbase = counter;
40989580Smsmith	for (i = 0; i < VHPET_NUM_TIMERS; i++)
41089580Smsmith		vhpet_stop_timer(vhpet, i, now);
41189580Smsmith}
41289580Smsmith
41389580Smsmithstatic __inline void
41489580Smsmithupdate_register(uint64_t *regptr, uint64_t data, uint64_t mask)
41589580Smsmith{
41689580Smsmith
41789580Smsmith	*regptr &= ~mask;
41889580Smsmith	*regptr |= (data & mask);
41989580Smsmith}
42089580Smsmith
42189580Smsmithstatic void
42289580Smsmithvhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data,
42389580Smsmith    uint64_t mask)
42489580Smsmith{
42589580Smsmith	bool clear_isr;
42689580Smsmith	int old_pin, new_pin;
42789580Smsmith	uint32_t allowed_irqs;
42889580Smsmith	uint64_t oldval, newval;
42989580Smsmith
43089580Smsmith	if (vhpet_timer_msi_enabled(vhpet, n) ||
43189580Smsmith	    vhpet_timer_edge_trig(vhpet, n)) {
43289580Smsmith		if (vhpet->isr & (1 << n))
43389580Smsmith			panic("vhpet timer %d isr should not be asserted", n);
43489580Smsmith	}
43589580Smsmith	old_pin = vhpet_timer_ioapic_pin(vhpet, n);
43689580Smsmith	oldval = vhpet->timer[n].cap_config;
43789580Smsmith
43889580Smsmith	newval = oldval;
43989580Smsmith	update_register(&newval, data, mask);
44089580Smsmith	newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE);
44189580Smsmith	newval |= oldval & HPET_TCAP_RO_MASK;
44289580Smsmith
44389580Smsmith	if (newval == oldval)
44489580Smsmith		return;
44589580Smsmith
44689580Smsmith	vhpet->timer[n].cap_config = newval;
44789580Smsmith	VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval);
44889580Smsmith
44989580Smsmith	/*
45089580Smsmith	 * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field.
45189580Smsmith	 * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set
45289580Smsmith	 * it to the default value of 0.
45389580Smsmith	 */
45489580Smsmith	allowed_irqs = vhpet->timer[n].cap_config >> 32;
45589580Smsmith	new_pin = vhpet_timer_ioapic_pin(vhpet, n);
45689580Smsmith	if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) {
45789580Smsmith		VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, "
45889580Smsmith		    "allowed_irqs 0x%08x", n, new_pin, allowed_irqs);
45989580Smsmith		new_pin = 0;
46089580Smsmith		vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE;
46189580Smsmith	}
46289580Smsmith
46389580Smsmith	if (!vhpet_periodic_timer(vhpet, n))
46489580Smsmith		vhpet->timer[n].comprate = 0;
46589580Smsmith
46689580Smsmith	/*
467114001Sscottl	 * If the timer's ISR bit is set then clear it in the following cases:
468114001Sscottl	 * - interrupt is disabled
469114001Sscottl	 * - interrupt type is changed from level to edge or fsb.
470114001Sscottl	 * - interrupt routing is changed
471114001Sscottl	 *
472114001Sscottl	 * This is to ensure that this timer's level triggered interrupt does
473114001Sscottl	 * not remain asserted forever.
474114001Sscottl	 */
475114001Sscottl	if (vhpet->isr & (1 << n)) {
476114001Sscottl		KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d",
477114001Sscottl		    n, old_pin));
478114001Sscottl		if (!vhpet_timer_interrupt_enabled(vhpet, n))
479114001Sscottl			clear_isr = true;
480114001Sscottl		else if (vhpet_timer_msi_enabled(vhpet, n))
481114001Sscottl			clear_isr = true;
482114001Sscottl		else if (vhpet_timer_edge_trig(vhpet, n))
483114001Sscottl			clear_isr = true;
484114001Sscottl		else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin)
485114001Sscottl			clear_isr = true;
486114001Sscottl		else
487114001Sscottl			clear_isr = false;
488114001Sscottl
489114001Sscottl		if (clear_isr) {
490114001Sscottl			VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to "
491114001Sscottl			    "configuration change", n);
492114001Sscottl			vioapic_deassert_irq(vhpet->vm, old_pin);
493114001Sscottl			vhpet->isr &= ~(1 << n);
494114001Sscottl		}
495114001Sscottl	}
496114001Sscottl}
497114001Sscottl
498114001Sscottlint
499114001Sscottlvhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size,
500114001Sscottl    void *arg)
501114001Sscottl{
502114001Sscottl	struct vhpet *vhpet;
503114001Sscottl	uint64_t data, mask, oldval, val64;
504114001Sscottl	uint32_t isr_clear_mask, old_compval, old_comprate, counter;
505114001Sscottl	sbintime_t now, *nowptr;
506114001Sscottl	int i, offset;
507114001Sscottl
508114001Sscottl	vhpet = vm_hpet(vm);
509114001Sscottl	offset = gpa - VHPET_BASE;
510114001Sscottl
511114001Sscottl	VHPET_LOCK(vhpet);
512114001Sscottl
51389580Smsmith	/* Accesses to the HPET should be 4 or 8 bytes wide */
51489580Smsmith	switch (size) {
51589580Smsmith	case 8:
51689580Smsmith		mask = 0xffffffffffffffff;
51789580Smsmith		data = val;
51889580Smsmith		break;
51989580Smsmith	case 4:
52089580Smsmith		mask = 0xffffffff;
52189580Smsmith		data = val;
52289580Smsmith		if ((offset & 0x4) != 0) {
52389580Smsmith			mask <<= 32;
52489580Smsmith			data <<= 32;
52589580Smsmith		}
52689580Smsmith		break;
52789580Smsmith	default:
52889580Smsmith		VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
52989580Smsmith		    "offset 0x%08x, size %d", offset, size);
53089580Smsmith		goto done;
53189580Smsmith	}
53289580Smsmith
53389580Smsmith	/* Access to the HPET should be naturally aligned to its width */
53489580Smsmith	if (offset & (size - 1)) {
53589580Smsmith		VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
53689580Smsmith		    "offset 0x%08x, size %d", offset, size);
53789580Smsmith		goto done;
53889580Smsmith	}
53989580Smsmith
54089580Smsmith	if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
54189580Smsmith		/*
54289580Smsmith		 * Get the most recent value of the counter before updating
54389580Smsmith		 * the 'config' register. If the HPET is going to be disabled
54489580Smsmith		 * then we need to update 'countbase' with the value right
54589580Smsmith		 * before it is disabled.
54689580Smsmith		 */
54789580Smsmith		nowptr = vhpet_counter_enabled(vhpet) ? &now : NULL;
54889580Smsmith		counter = vhpet_counter(vhpet, nowptr);
54989580Smsmith		oldval = vhpet->config;
55089580Smsmith		update_register(&vhpet->config, data, mask);
55189580Smsmith		if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) {
55289580Smsmith			if (vhpet_counter_enabled(vhpet)) {
55389580Smsmith				vhpet_start_counting(vhpet);
55489580Smsmith				VM_CTR0(vhpet->vm, "hpet enabled");
55589580Smsmith			} else {
55689580Smsmith				vhpet_stop_counting(vhpet, counter, now);
55789580Smsmith				VM_CTR0(vhpet->vm, "hpet disabled");
55889580Smsmith			}
55989580Smsmith		}
56089580Smsmith		goto done;
56189580Smsmith	}
56289580Smsmith
56389580Smsmith	if (offset == HPET_ISR || offset == HPET_ISR + 4) {
56489580Smsmith		isr_clear_mask = vhpet->isr & data;
56589580Smsmith		for (i = 0; i < VHPET_NUM_TIMERS; i++) {
56689580Smsmith			if ((isr_clear_mask & (1 << i)) != 0) {
56789580Smsmith				VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i);
56889580Smsmith				vhpet_timer_clear_isr(vhpet, i);
56989580Smsmith			}
57089580Smsmith		}
57189580Smsmith		goto done;
57289580Smsmith	}
57389580Smsmith
57489580Smsmith	if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
57589580Smsmith		/* Zero-extend the counter to 64-bits before updating it */
57689580Smsmith		val64 = vhpet_counter(vhpet, NULL);
57789580Smsmith		update_register(&val64, data, mask);
57889580Smsmith		vhpet->countbase = val64;
57989580Smsmith		if (vhpet_counter_enabled(vhpet))
58089580Smsmith			vhpet_start_counting(vhpet);
58189580Smsmith		goto done;
58289580Smsmith	}
58389580Smsmith
58489580Smsmith	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
58589580Smsmith		if (offset == HPET_TIMER_CAP_CNF(i) ||
58689580Smsmith		    offset == HPET_TIMER_CAP_CNF(i) + 4) {
58789580Smsmith			vhpet_timer_update_config(vhpet, i, data, mask);
58889580Smsmith			break;
58989580Smsmith		}
59089580Smsmith
59189580Smsmith		if (offset == HPET_TIMER_COMPARATOR(i) ||
59289580Smsmith		    offset == HPET_TIMER_COMPARATOR(i) + 4) {
593170872Sscottl			old_compval = vhpet->timer[i].compval;
594275975Ssmh			old_comprate = vhpet->timer[i].comprate;
59589580Smsmith			if (vhpet_periodic_timer(vhpet, i)) {
59689580Smsmith				/*
59789580Smsmith				 * In periodic mode writes to the comparator
59889580Smsmith				 * change the 'compval' register only if the
59989580Smsmith				 * HPET_TCNF_VAL_SET bit is set in the config
60089580Smsmith				 * register.
60189580Smsmith				 */
602114001Sscottl				val64 = vhpet->timer[i].comprate;
60389580Smsmith				update_register(&val64, data, mask);
60489580Smsmith				vhpet->timer[i].comprate = val64;
60589580Smsmith				if ((vhpet->timer[i].cap_config &
60689580Smsmith				    HPET_TCNF_VAL_SET) != 0) {
60789580Smsmith					vhpet->timer[i].compval = val64;
60889580Smsmith				}
60989580Smsmith			} else {
61089580Smsmith				KASSERT(vhpet->timer[i].comprate == 0,
611130585Sphk				    ("vhpet one-shot timer %d has invalid "
612275975Ssmh				    "rate %u", i, vhpet->timer[i].comprate));
61389580Smsmith				val64 = vhpet->timer[i].compval;
61489580Smsmith				update_register(&val64, data, mask);
615156139Sscottl				vhpet->timer[i].compval = val64;
616156139Sscottl			}
617156139Sscottl			vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET;
61889580Smsmith
61989580Smsmith			if (vhpet->timer[i].compval != old_compval ||
620156139Sscottl			    vhpet->timer[i].comprate != old_comprate) {
62189580Smsmith				if (vhpet_counter_enabled(vhpet)) {
62289580Smsmith					counter = vhpet_counter(vhpet, &now);
62389580Smsmith					vhpet_start_timer(vhpet, i, counter,
62489580Smsmith					    now);
62589580Smsmith				}
62689580Smsmith			}
62789580Smsmith			break;
62889580Smsmith		}
62989580Smsmith
63089580Smsmith		if (offset == HPET_TIMER_FSB_VAL(i) ||
63189580Smsmith		    offset == HPET_TIMER_FSB_ADDR(i)) {
63289580Smsmith			update_register(&vhpet->timer[i].msireg, data, mask);
63389580Smsmith			break;
63489580Smsmith		}
63589580Smsmith	}
63689580Smsmithdone:
63789580Smsmith	VHPET_UNLOCK(vhpet);
63889580Smsmith	return (0);
63989580Smsmith}
64089580Smsmith
64189580Smsmithint
64289580Smsmithvhpet_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, int size,
64389580Smsmith    void *arg)
64489580Smsmith{
64589580Smsmith	int i, offset;
64689580Smsmith	struct vhpet *vhpet;
64789580Smsmith	uint64_t data;
64889580Smsmith
64989580Smsmith	vhpet = vm_hpet(vm);
65089580Smsmith	offset = gpa - VHPET_BASE;
65189580Smsmith
65289580Smsmith	VHPET_LOCK(vhpet);
65389580Smsmith
65489580Smsmith	/* Accesses to the HPET should be 4 or 8 bytes wide */
65589580Smsmith	if (size != 4 && size != 8) {
65689580Smsmith		VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
65789580Smsmith		    "offset 0x%08x, size %d", offset, size);
65889580Smsmith		data = 0;
65989580Smsmith		goto done;
66089580Smsmith	}
661114001Sscottl
66289580Smsmith	/* Access to the HPET should be naturally aligned to its width */
66389580Smsmith	if (offset & (size - 1)) {
66489580Smsmith		VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
66589580Smsmith		    "offset 0x%08x, size %d", offset, size);
66692739Salfred		data = 0;
66792739Salfred		goto done;
66892739Salfred	}
66992739Salfred
67092739Salfred	if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) {
67192739Salfred		data = vhpet_capabilities();
67289580Smsmith		goto done;
67389580Smsmith	}
67489580Smsmith
67589580Smsmith	if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
67689580Smsmith		data = vhpet->config;
67789580Smsmith		goto done;
67889580Smsmith	}
67989580Smsmith
68089580Smsmith	if (offset == HPET_ISR || offset == HPET_ISR + 4) {
681156139Sscottl		data = vhpet->isr;
682156139Sscottl		goto done;
68389580Smsmith	}
68489580Smsmith
68589580Smsmith	if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
686275975Ssmh		data = vhpet_counter(vhpet, NULL);
68789580Smsmith		goto done;
68889580Smsmith	}
68989580Smsmith
69089580Smsmith	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
69189580Smsmith		if (offset == HPET_TIMER_CAP_CNF(i) ||
69289580Smsmith		    offset == HPET_TIMER_CAP_CNF(i) + 4) {
69389580Smsmith			data = vhpet->timer[i].cap_config;
69489580Smsmith			break;
69589580Smsmith		}
696156139Sscottl
697156139Sscottl		if (offset == HPET_TIMER_COMPARATOR(i) ||
69889580Smsmith		    offset == HPET_TIMER_COMPARATOR(i) + 4) {
69989580Smsmith			data = vhpet->timer[i].compval;
70089580Smsmith			break;
70189580Smsmith		}
70292739Salfred
70392739Salfred		if (offset == HPET_TIMER_FSB_VAL(i) ||
70492739Salfred		    offset == HPET_TIMER_FSB_ADDR(i)) {
70592739Salfred			data = vhpet->timer[i].msireg;
70689580Smsmith			break;
707143063Sjoerg		}
70889580Smsmith	}
70992739Salfred
71092739Salfred	if (i >= VHPET_NUM_TIMERS)
71192739Salfred		data = 0;
71292739Salfreddone:
71389580Smsmith	VHPET_UNLOCK(vhpet);
71489580Smsmith
715154648Srwatson	if (size == 4) {
71689580Smsmith		if (offset & 0x4)
71789580Smsmith			data >>= 32;
71889580Smsmith	}
71989580Smsmith	*rval = data;
72089580Smsmith	return (0);
721154648Srwatson}
72289580Smsmith
72389580Smsmithstruct vhpet *
72489580Smsmithvhpet_init(struct vm *vm)
72589580Smsmith{
72689580Smsmith	int i, pincount;
727154648Srwatson	struct vhpet *vhpet;
72889580Smsmith	uint64_t allowed_irqs;
729114001Sscottl	struct vhpet_callout_arg *arg;
73089580Smsmith	struct bintime bt;
73189580Smsmith
73289580Smsmith	vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO);
733154648Srwatson        vhpet->vm = vm;
73489580Smsmith	mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF);
735114001Sscottl
73689580Smsmith	FREQ2BT(HPET_FREQ, &bt);
73789580Smsmith	vhpet->freq_sbt = bttosbt(bt);
73889580Smsmith
73989580Smsmith	pincount = vioapic_pincount(vm);
74089580Smsmith	if (pincount >= 24)
741275975Ssmh		allowed_irqs = 0x00f00000;	/* irqs 20, 21, 22 and 23 */
742130585Sphk	else
74389580Smsmith		allowed_irqs = 0;
74489580Smsmith
74589580Smsmith	/*
746275975Ssmh	 * Initialize HPET timer hardware state.
74789580Smsmith	 */
74889580Smsmith	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
74989580Smsmith		vhpet->timer[i].cap_config = allowed_irqs << 32;
75089580Smsmith		vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT;
75189580Smsmith		vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL;
75289580Smsmith
753		vhpet->timer[i].compval = 0xffffffff;
754		callout_init(&vhpet->timer[i].callout, 1);
755
756		arg = &vhpet->timer[i].arg;
757		arg->vhpet = vhpet;
758		arg->timer_num = i;
759	}
760
761	return (vhpet);
762}
763
764void
765vhpet_cleanup(struct vhpet *vhpet)
766{
767	int i;
768
769	for (i = 0; i < VHPET_NUM_TIMERS; i++)
770		callout_drain(&vhpet->timer[i].callout);
771
772	free(vhpet, M_VHPET);
773}
774
775int
776vhpet_getcap(struct vm_hpet_cap *cap)
777{
778
779	cap->capabilities = vhpet_capabilities();
780	return (0);
781}
782