1258579Sneel/*-
2258579Sneel * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
3258579Sneel * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
4258579Sneel * All rights reserved.
5258579Sneel *
6258579Sneel * Redistribution and use in source and binary forms, with or without
7258579Sneel * modification, are permitted provided that the following conditions
8258579Sneel * are met:
9258579Sneel * 1. Redistributions of source code must retain the above copyright
10258579Sneel *    notice, this list of conditions and the following disclaimer.
11258579Sneel * 2. Redistributions in binary form must reproduce the above copyright
12258579Sneel *    notice, this list of conditions and the following disclaimer in the
13258579Sneel *    documentation and/or other materials provided with the distribution.
14258579Sneel *
15258579Sneel * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
16258579Sneel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17258579Sneel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18258579Sneel * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
19258579Sneel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20258579Sneel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21258579Sneel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22258579Sneel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23258579Sneel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24258579Sneel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25258579Sneel * SUCH DAMAGE.
26258579Sneel *
27258579Sneel * $FreeBSD: stable/11/sys/amd64/vmm/io/vhpet.c 327651 2018-01-06 22:56:48Z ian $
28258579Sneel */
29258579Sneel
30258579Sneel#include <sys/cdefs.h>
31258579Sneel__FBSDID("$FreeBSD: stable/11/sys/amd64/vmm/io/vhpet.c 327651 2018-01-06 22:56:48Z ian $");
32258579Sneel
33258579Sneel#include <sys/param.h>
34258579Sneel#include <sys/lock.h>
35258579Sneel#include <sys/mutex.h>
36258579Sneel#include <sys/kernel.h>
37258579Sneel#include <sys/malloc.h>
38258579Sneel#include <sys/systm.h>
39258579Sneel
40258579Sneel#include <dev/acpica/acpi_hpet.h>
41258579Sneel
42258579Sneel#include <machine/vmm.h>
43258579Sneel#include <machine/vmm_dev.h>
44258579Sneel
45258579Sneel#include "vmm_lapic.h"
46263035Stychon#include "vatpic.h"
47258579Sneel#include "vioapic.h"
48258579Sneel#include "vhpet.h"
49258579Sneel
50258579Sneel#include "vmm_ktr.h"
51258579Sneel
52258579Sneelstatic MALLOC_DEFINE(M_VHPET, "vhpet", "bhyve virtual hpet");
53258579Sneel
54327651Sian#define	HPET_FREQ	16777216		/* 16.7 (2^24) Mhz */
55258579Sneel#define	FS_PER_S	1000000000000000ul
56258579Sneel
57258579Sneel/* Timer N Configuration and Capabilities Register */
58258579Sneel#define	HPET_TCAP_RO_MASK	(HPET_TCAP_INT_ROUTE 	|		\
59258579Sneel				 HPET_TCAP_FSB_INT_DEL	|		\
60258579Sneel				 HPET_TCAP_SIZE		|		\
61258579Sneel				 HPET_TCAP_PER_INT)
62258579Sneel/*
63258579Sneel * HPET requires at least 3 timers and up to 32 timers per block.
64258579Sneel */
65258579Sneel#define	VHPET_NUM_TIMERS	8
66258579SneelCTASSERT(VHPET_NUM_TIMERS >= 3 && VHPET_NUM_TIMERS <= 32);
67258579Sneel
68258579Sneelstruct vhpet_callout_arg {
69258579Sneel	struct vhpet *vhpet;
70258579Sneel	int timer_num;
71258579Sneel};
72258579Sneel
73258579Sneelstruct vhpet {
74258579Sneel	struct vm	*vm;
75258579Sneel	struct mtx	mtx;
76258579Sneel	sbintime_t	freq_sbt;
77258579Sneel
78258579Sneel	uint64_t	config;		/* Configuration */
79258579Sneel	uint64_t	isr;		/* Interrupt Status */
80260237Sneel	uint32_t	countbase;	/* HPET counter base value */
81260237Sneel	sbintime_t	countbase_sbt;	/* uptime corresponding to base value */
82258579Sneel
83258579Sneel	struct {
84258579Sneel		uint64_t	cap_config;	/* Configuration */
85258579Sneel		uint64_t	msireg;		/* FSB interrupt routing */
86258579Sneel		uint32_t	compval;	/* Comparator */
87258579Sneel		uint32_t	comprate;
88258579Sneel		struct callout	callout;
89260237Sneel		sbintime_t	callout_sbt;	/* time when counter==compval */
90258579Sneel		struct vhpet_callout_arg arg;
91258579Sneel	} timer[VHPET_NUM_TIMERS];
92258579Sneel};
93258579Sneel
94258579Sneel#define	VHPET_LOCK(vhp)		mtx_lock(&((vhp)->mtx))
95258579Sneel#define	VHPET_UNLOCK(vhp)	mtx_unlock(&((vhp)->mtx))
96258579Sneel
97260237Sneelstatic void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter,
98260237Sneel    sbintime_t now);
99260237Sneel
100258579Sneelstatic uint64_t
101258579Sneelvhpet_capabilities(void)
102258579Sneel{
103258579Sneel	uint64_t cap = 0;
104258579Sneel
105258579Sneel	cap |= 0x8086 << 16;			/* vendor id */
106258579Sneel	cap |= (VHPET_NUM_TIMERS - 1) << 8;	/* number of timers */
107258579Sneel	cap |= 1;				/* revision */
108258579Sneel	cap &= ~HPET_CAP_COUNT_SIZE;		/* 32-bit timer */
109258579Sneel
110258579Sneel	cap &= 0xffffffff;
111258579Sneel	cap |= (FS_PER_S / HPET_FREQ) << 32;	/* tick period in fs */
112258579Sneel
113258579Sneel	return (cap);
114258579Sneel}
115258579Sneel
116258579Sneelstatic __inline bool
117258579Sneelvhpet_counter_enabled(struct vhpet *vhpet)
118258579Sneel{
119258579Sneel
120258579Sneel	return ((vhpet->config & HPET_CNF_ENABLE) ? true : false);
121258579Sneel}
122258579Sneel
123258579Sneelstatic __inline bool
124258579Sneelvhpet_timer_msi_enabled(struct vhpet *vhpet, int n)
125258579Sneel{
126258579Sneel	const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN;
127258579Sneel
128258579Sneel	if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable)
129258579Sneel		return (true);
130258579Sneel	else
131258579Sneel		return (false);
132258579Sneel}
133258579Sneel
134258579Sneelstatic __inline int
135258579Sneelvhpet_timer_ioapic_pin(struct vhpet *vhpet, int n)
136258579Sneel{
137258579Sneel	/*
138258579Sneel	 * If the timer is configured to use MSI then treat it as if the
139258579Sneel	 * timer is not connected to the ioapic.
140258579Sneel	 */
141258579Sneel	if (vhpet_timer_msi_enabled(vhpet, n))
142258579Sneel		return (0);
143258579Sneel
144258579Sneel	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
145258579Sneel}
146258579Sneel
147258579Sneelstatic uint32_t
148260237Sneelvhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr)
149258579Sneel{
150258579Sneel	uint32_t val;
151260237Sneel	sbintime_t now, delta;
152258579Sneel
153260237Sneel	val = vhpet->countbase;
154258579Sneel	if (vhpet_counter_enabled(vhpet)) {
155260237Sneel		now = sbinuptime();
156260237Sneel		delta = now - vhpet->countbase_sbt;
157260237Sneel		KASSERT(delta >= 0, ("vhpet_counter: uptime went backwards: "
158260237Sneel		    "%#lx to %#lx", vhpet->countbase_sbt, now));
159260237Sneel		val += delta / vhpet->freq_sbt;
160260237Sneel		if (nowptr != NULL)
161260237Sneel			*nowptr = now;
162260237Sneel	} else {
163258579Sneel		/*
164260237Sneel		 * The sbinuptime corresponding to the 'countbase' is
165260237Sneel		 * meaningless when the counter is disabled. Make sure
166300050Seadler		 * that the caller doesn't want to use it.
167258579Sneel		 */
168260237Sneel		KASSERT(nowptr == NULL, ("vhpet_counter: nowptr must be NULL"));
169258579Sneel	}
170258579Sneel	return (val);
171258579Sneel}
172258579Sneel
173258579Sneelstatic void
174258579Sneelvhpet_timer_clear_isr(struct vhpet *vhpet, int n)
175258579Sneel{
176276428Sneel	int pin;
177258579Sneel
178258579Sneel	if (vhpet->isr & (1 << n)) {
179258579Sneel		pin = vhpet_timer_ioapic_pin(vhpet, n);
180258579Sneel		KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n));
181258579Sneel		vioapic_deassert_irq(vhpet->vm, pin);
182258579Sneel		vhpet->isr &= ~(1 << n);
183258579Sneel	}
184258579Sneel}
185258579Sneel
186258579Sneelstatic __inline bool
187258579Sneelvhpet_periodic_timer(struct vhpet *vhpet, int n)
188258579Sneel{
189258579Sneel
190258579Sneel	return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0);
191258579Sneel}
192258579Sneel
193258579Sneelstatic __inline bool
194258579Sneelvhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n)
195258579Sneel{
196258579Sneel
197258579Sneel	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0);
198258579Sneel}
199258579Sneel
200258579Sneelstatic __inline bool
201258579Sneelvhpet_timer_edge_trig(struct vhpet *vhpet, int n)
202258579Sneel{
203258579Sneel
204258579Sneel	KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: "
205258579Sneel	    "timer %d is using MSI", n));
206258579Sneel
207258579Sneel	if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0)
208258579Sneel		return (true);
209258579Sneel	else
210258579Sneel		return (false);
211258579Sneel}
212258579Sneel
213258579Sneelstatic void
214258579Sneelvhpet_timer_interrupt(struct vhpet *vhpet, int n)
215258579Sneel{
216276428Sneel	int pin;
217258579Sneel
218258579Sneel	/* If interrupts are not enabled for this timer then just return. */
219258579Sneel	if (!vhpet_timer_interrupt_enabled(vhpet, n))
220258579Sneel		return;
221258579Sneel
222258579Sneel	/*
223258579Sneel	 * If a level triggered interrupt is already asserted then just return.
224258579Sneel	 */
225258579Sneel	if ((vhpet->isr & (1 << n)) != 0) {
226258579Sneel		VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n);
227258579Sneel		return;
228258579Sneel	}
229258579Sneel
230258579Sneel	if (vhpet_timer_msi_enabled(vhpet, n)) {
231259482Sneel		lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32,
232259482Sneel		    vhpet->timer[n].msireg & 0xffffffff);
233258579Sneel		return;
234258579Sneel	}
235258579Sneel
236258579Sneel	pin = vhpet_timer_ioapic_pin(vhpet, n);
237258579Sneel	if (pin == 0) {
238258579Sneel		VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n);
239258579Sneel		return;
240258579Sneel	}
241258579Sneel
242258579Sneel	if (vhpet_timer_edge_trig(vhpet, n)) {
243258579Sneel		vioapic_pulse_irq(vhpet->vm, pin);
244258579Sneel	} else {
245258579Sneel		vhpet->isr |= 1 << n;
246258579Sneel		vioapic_assert_irq(vhpet->vm, pin);
247258579Sneel	}
248258579Sneel}
249258579Sneel
250258579Sneelstatic void
251258579Sneelvhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter)
252258579Sneel{
253258579Sneel	uint32_t compval, comprate, compnext;
254258579Sneel
255258579Sneel	KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n));
256258579Sneel
257258579Sneel	compval = vhpet->timer[n].compval;
258258579Sneel	comprate = vhpet->timer[n].comprate;
259258579Sneel
260258579Sneel	/*
261258579Sneel	 * Calculate the comparator value to be used for the next periodic
262258579Sneel	 * interrupt.
263258579Sneel	 *
264258579Sneel	 * This function is commonly called from the callout handler.
265258579Sneel	 * In this scenario the 'counter' is ahead of 'compval'. To find
266258579Sneel	 * the next value to program into the accumulator we divide the
267258579Sneel	 * number space between 'compval' and 'counter' into 'comprate'
268258579Sneel	 * sized units. The 'compval' is rounded up such that is "ahead"
269258579Sneel	 * of 'counter'.
270258579Sneel	 */
271258579Sneel	compnext = compval + ((counter - compval) / comprate + 1) * comprate;
272258579Sneel
273258579Sneel	vhpet->timer[n].compval = compnext;
274258579Sneel}
275258579Sneel
276258579Sneelstatic void
277258579Sneelvhpet_handler(void *a)
278258579Sneel{
279258579Sneel	int n;
280258579Sneel	uint32_t counter;
281260237Sneel	sbintime_t now;
282258579Sneel	struct vhpet *vhpet;
283258579Sneel	struct callout *callout;
284258579Sneel	struct vhpet_callout_arg *arg;
285258579Sneel
286258579Sneel	arg = a;
287258579Sneel	vhpet = arg->vhpet;
288258579Sneel	n = arg->timer_num;
289258579Sneel	callout = &vhpet->timer[n].callout;
290258579Sneel
291258579Sneel	VM_CTR1(vhpet->vm, "hpet t%d fired", n);
292258579Sneel
293258579Sneel	VHPET_LOCK(vhpet);
294258579Sneel
295258579Sneel	if (callout_pending(callout))		/* callout was reset */
296258579Sneel		goto done;
297258579Sneel
298258579Sneel	if (!callout_active(callout))		/* callout was stopped */
299258579Sneel		goto done;
300258579Sneel
301258579Sneel	callout_deactivate(callout);
302258579Sneel
303258579Sneel	if (!vhpet_counter_enabled(vhpet))
304258579Sneel		panic("vhpet(%p) callout with counter disabled", vhpet);
305258579Sneel
306260237Sneel	counter = vhpet_counter(vhpet, &now);
307260237Sneel	vhpet_start_timer(vhpet, n, counter, now);
308258579Sneel	vhpet_timer_interrupt(vhpet, n);
309258579Sneeldone:
310258579Sneel	VHPET_UNLOCK(vhpet);
311258579Sneel	return;
312258579Sneel}
313258579Sneel
314258579Sneelstatic void
315260237Sneelvhpet_stop_timer(struct vhpet *vhpet, int n, sbintime_t now)
316258579Sneel{
317258579Sneel
318260237Sneel	VM_CTR1(vhpet->vm, "hpet t%d stopped", n);
319258579Sneel	callout_stop(&vhpet->timer[n].callout);
320260237Sneel
321260237Sneel	/*
322260237Sneel	 * If the callout was scheduled to expire in the past but hasn't
323260237Sneel	 * had a chance to execute yet then trigger the timer interrupt
324260237Sneel	 * here. Failing to do so will result in a missed timer interrupt
325260237Sneel	 * in the guest. This is especially bad in one-shot mode because
326260237Sneel	 * the next interrupt has to wait for the counter to wrap around.
327260237Sneel	 */
328260237Sneel	if (vhpet->timer[n].callout_sbt < now) {
329260237Sneel		VM_CTR1(vhpet->vm, "hpet t%d interrupt triggered after "
330260237Sneel		    "stopping timer", n);
331260237Sneel		vhpet_timer_interrupt(vhpet, n);
332260237Sneel	}
333258579Sneel}
334258579Sneel
335258579Sneelstatic void
336260237Sneelvhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, sbintime_t now)
337258579Sneel{
338260237Sneel	sbintime_t delta, precision;
339258579Sneel
340258579Sneel	if (vhpet->timer[n].comprate != 0)
341258579Sneel		vhpet_adjust_compval(vhpet, n, counter);
342260237Sneel	else {
343260237Sneel		/*
344260237Sneel		 * In one-shot mode it is the guest's responsibility to make
345260237Sneel		 * sure that the comparator value is not in the "past". The
346260237Sneel		 * hardware doesn't have any belt-and-suspenders to deal with
347260237Sneel		 * this so we don't either.
348260237Sneel		 */
349258579Sneel	}
350258579Sneel
351260237Sneel	delta = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt;
352260237Sneel	precision = delta >> tc_precexp;
353260237Sneel	vhpet->timer[n].callout_sbt = now + delta;
354260237Sneel	callout_reset_sbt(&vhpet->timer[n].callout, vhpet->timer[n].callout_sbt,
355260237Sneel	    precision, vhpet_handler, &vhpet->timer[n].arg, C_ABSOLUTE);
356258579Sneel}
357258579Sneel
358258579Sneelstatic void
359258579Sneelvhpet_start_counting(struct vhpet *vhpet)
360258579Sneel{
361258579Sneel	int i;
362258579Sneel
363260237Sneel	vhpet->countbase_sbt = sbinuptime();
364260237Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
365260237Sneel		/*
366260237Sneel		 * Restart the timers based on the value of the main counter
367260237Sneel		 * when it stopped counting.
368260237Sneel		 */
369260237Sneel		vhpet_start_timer(vhpet, i, vhpet->countbase,
370260237Sneel		    vhpet->countbase_sbt);
371260237Sneel	}
372258579Sneel}
373258579Sneel
374258579Sneelstatic void
375260237Sneelvhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, sbintime_t now)
376258579Sneel{
377258579Sneel	int i;
378258579Sneel
379260237Sneel	vhpet->countbase = counter;
380258579Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++)
381260237Sneel		vhpet_stop_timer(vhpet, i, now);
382258579Sneel}
383258579Sneel
384258579Sneelstatic __inline void
385258579Sneelupdate_register(uint64_t *regptr, uint64_t data, uint64_t mask)
386258579Sneel{
387258579Sneel
388258579Sneel	*regptr &= ~mask;
389258579Sneel	*regptr |= (data & mask);
390258579Sneel}
391258579Sneel
392258579Sneelstatic void
393258579Sneelvhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data,
394258579Sneel    uint64_t mask)
395258579Sneel{
396258579Sneel	bool clear_isr;
397258579Sneel	int old_pin, new_pin;
398258579Sneel	uint32_t allowed_irqs;
399258579Sneel	uint64_t oldval, newval;
400258579Sneel
401258579Sneel	if (vhpet_timer_msi_enabled(vhpet, n) ||
402258579Sneel	    vhpet_timer_edge_trig(vhpet, n)) {
403258579Sneel		if (vhpet->isr & (1 << n))
404258579Sneel			panic("vhpet timer %d isr should not be asserted", n);
405258579Sneel	}
406258579Sneel	old_pin = vhpet_timer_ioapic_pin(vhpet, n);
407258579Sneel	oldval = vhpet->timer[n].cap_config;
408258579Sneel
409258579Sneel	newval = oldval;
410258579Sneel	update_register(&newval, data, mask);
411258579Sneel	newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE);
412258579Sneel	newval |= oldval & HPET_TCAP_RO_MASK;
413258579Sneel
414258579Sneel	if (newval == oldval)
415258579Sneel		return;
416258579Sneel
417258579Sneel	vhpet->timer[n].cap_config = newval;
418258579Sneel	VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval);
419258579Sneel
420258579Sneel	/*
421258579Sneel	 * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field.
422258579Sneel	 * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set
423258579Sneel	 * it to the default value of 0.
424258579Sneel	 */
425258579Sneel	allowed_irqs = vhpet->timer[n].cap_config >> 32;
426258579Sneel	new_pin = vhpet_timer_ioapic_pin(vhpet, n);
427258579Sneel	if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) {
428258579Sneel		VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, "
429258579Sneel		    "allowed_irqs 0x%08x", n, new_pin, allowed_irqs);
430258579Sneel		new_pin = 0;
431258579Sneel		vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE;
432258579Sneel	}
433258579Sneel
434258579Sneel	if (!vhpet_periodic_timer(vhpet, n))
435258579Sneel		vhpet->timer[n].comprate = 0;
436258579Sneel
437258579Sneel	/*
438258579Sneel	 * If the timer's ISR bit is set then clear it in the following cases:
439258579Sneel	 * - interrupt is disabled
440258579Sneel	 * - interrupt type is changed from level to edge or fsb.
441258579Sneel	 * - interrupt routing is changed
442258579Sneel	 *
443258579Sneel	 * This is to ensure that this timer's level triggered interrupt does
444258579Sneel	 * not remain asserted forever.
445258579Sneel	 */
446258579Sneel	if (vhpet->isr & (1 << n)) {
447258579Sneel		KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d",
448258579Sneel		    n, old_pin));
449258579Sneel		if (!vhpet_timer_interrupt_enabled(vhpet, n))
450258579Sneel			clear_isr = true;
451258579Sneel		else if (vhpet_timer_msi_enabled(vhpet, n))
452258579Sneel			clear_isr = true;
453258579Sneel		else if (vhpet_timer_edge_trig(vhpet, n))
454258579Sneel			clear_isr = true;
455258579Sneel		else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin)
456258579Sneel			clear_isr = true;
457258579Sneel		else
458258579Sneel			clear_isr = false;
459258579Sneel
460258579Sneel		if (clear_isr) {
461258579Sneel			VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to "
462258579Sneel			    "configuration change", n);
463258579Sneel			vioapic_deassert_irq(vhpet->vm, old_pin);
464258579Sneel			vhpet->isr &= ~(1 << n);
465258579Sneel		}
466258579Sneel	}
467258579Sneel}
468258579Sneel
469258579Sneelint
470258579Sneelvhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size,
471258579Sneel    void *arg)
472258579Sneel{
473258579Sneel	struct vhpet *vhpet;
474258579Sneel	uint64_t data, mask, oldval, val64;
475260237Sneel	uint32_t isr_clear_mask, old_compval, old_comprate, counter;
476260237Sneel	sbintime_t now, *nowptr;
477258579Sneel	int i, offset;
478258579Sneel
479258579Sneel	vhpet = vm_hpet(vm);
480258579Sneel	offset = gpa - VHPET_BASE;
481258579Sneel
482258579Sneel	VHPET_LOCK(vhpet);
483258579Sneel
484258579Sneel	/* Accesses to the HPET should be 4 or 8 bytes wide */
485258579Sneel	switch (size) {
486258579Sneel	case 8:
487258579Sneel		mask = 0xffffffffffffffff;
488258579Sneel		data = val;
489258579Sneel		break;
490258579Sneel	case 4:
491258579Sneel		mask = 0xffffffff;
492258579Sneel		data = val;
493258579Sneel		if ((offset & 0x4) != 0) {
494258579Sneel			mask <<= 32;
495258579Sneel			data <<= 32;
496258579Sneel		}
497258579Sneel		break;
498258579Sneel	default:
499258579Sneel		VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
500258579Sneel		    "offset 0x%08x, size %d", offset, size);
501258579Sneel		goto done;
502258579Sneel	}
503258579Sneel
504258579Sneel	/* Access to the HPET should be naturally aligned to its width */
505258579Sneel	if (offset & (size - 1)) {
506258579Sneel		VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
507258579Sneel		    "offset 0x%08x, size %d", offset, size);
508258579Sneel		goto done;
509258579Sneel	}
510258579Sneel
511258579Sneel	if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
512260237Sneel		/*
513260237Sneel		 * Get the most recent value of the counter before updating
514260237Sneel		 * the 'config' register. If the HPET is going to be disabled
515260237Sneel		 * then we need to update 'countbase' with the value right
516260237Sneel		 * before it is disabled.
517260237Sneel		 */
518260237Sneel		nowptr = vhpet_counter_enabled(vhpet) ? &now : NULL;
519260237Sneel		counter = vhpet_counter(vhpet, nowptr);
520258579Sneel		oldval = vhpet->config;
521258579Sneel		update_register(&vhpet->config, data, mask);
522276428Sneel
523276428Sneel		/*
524276428Sneel		 * LegacyReplacement Routing is not supported so clear the
525276428Sneel		 * bit explicitly.
526276428Sneel		 */
527276428Sneel		vhpet->config &= ~HPET_CNF_LEG_RT;
528276428Sneel
529258579Sneel		if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) {
530258579Sneel			if (vhpet_counter_enabled(vhpet)) {
531258579Sneel				vhpet_start_counting(vhpet);
532258579Sneel				VM_CTR0(vhpet->vm, "hpet enabled");
533258579Sneel			} else {
534260237Sneel				vhpet_stop_counting(vhpet, counter, now);
535258579Sneel				VM_CTR0(vhpet->vm, "hpet disabled");
536258579Sneel			}
537258579Sneel		}
538258579Sneel		goto done;
539258579Sneel	}
540258579Sneel
541258579Sneel	if (offset == HPET_ISR || offset == HPET_ISR + 4) {
542258579Sneel		isr_clear_mask = vhpet->isr & data;
543258579Sneel		for (i = 0; i < VHPET_NUM_TIMERS; i++) {
544258579Sneel			if ((isr_clear_mask & (1 << i)) != 0) {
545258579Sneel				VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i);
546258579Sneel				vhpet_timer_clear_isr(vhpet, i);
547258579Sneel			}
548258579Sneel		}
549258579Sneel		goto done;
550258579Sneel	}
551258579Sneel
552258579Sneel	if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
553258579Sneel		/* Zero-extend the counter to 64-bits before updating it */
554260237Sneel		val64 = vhpet_counter(vhpet, NULL);
555258579Sneel		update_register(&val64, data, mask);
556260237Sneel		vhpet->countbase = val64;
557258579Sneel		if (vhpet_counter_enabled(vhpet))
558258579Sneel			vhpet_start_counting(vhpet);
559258579Sneel		goto done;
560258579Sneel	}
561258579Sneel
562258579Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
563258579Sneel		if (offset == HPET_TIMER_CAP_CNF(i) ||
564258579Sneel		    offset == HPET_TIMER_CAP_CNF(i) + 4) {
565258579Sneel			vhpet_timer_update_config(vhpet, i, data, mask);
566258579Sneel			break;
567258579Sneel		}
568258579Sneel
569258579Sneel		if (offset == HPET_TIMER_COMPARATOR(i) ||
570258579Sneel		    offset == HPET_TIMER_COMPARATOR(i) + 4) {
571258579Sneel			old_compval = vhpet->timer[i].compval;
572258579Sneel			old_comprate = vhpet->timer[i].comprate;
573258579Sneel			if (vhpet_periodic_timer(vhpet, i)) {
574258579Sneel				/*
575258579Sneel				 * In periodic mode writes to the comparator
576258579Sneel				 * change the 'compval' register only if the
577258579Sneel				 * HPET_TCNF_VAL_SET bit is set in the config
578258579Sneel				 * register.
579258579Sneel				 */
580258579Sneel				val64 = vhpet->timer[i].comprate;
581258579Sneel				update_register(&val64, data, mask);
582258579Sneel				vhpet->timer[i].comprate = val64;
583258579Sneel				if ((vhpet->timer[i].cap_config &
584258579Sneel				    HPET_TCNF_VAL_SET) != 0) {
585258579Sneel					vhpet->timer[i].compval = val64;
586258579Sneel				}
587258579Sneel			} else {
588258579Sneel				KASSERT(vhpet->timer[i].comprate == 0,
589258579Sneel				    ("vhpet one-shot timer %d has invalid "
590258579Sneel				    "rate %u", i, vhpet->timer[i].comprate));
591258579Sneel				val64 = vhpet->timer[i].compval;
592258579Sneel				update_register(&val64, data, mask);
593258579Sneel				vhpet->timer[i].compval = val64;
594258579Sneel			}
595258579Sneel			vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET;
596258579Sneel
597258579Sneel			if (vhpet->timer[i].compval != old_compval ||
598258579Sneel			    vhpet->timer[i].comprate != old_comprate) {
599260237Sneel				if (vhpet_counter_enabled(vhpet)) {
600260237Sneel					counter = vhpet_counter(vhpet, &now);
601260237Sneel					vhpet_start_timer(vhpet, i, counter,
602260237Sneel					    now);
603260237Sneel				}
604258579Sneel			}
605258579Sneel			break;
606258579Sneel		}
607258579Sneel
608258579Sneel		if (offset == HPET_TIMER_FSB_VAL(i) ||
609258579Sneel		    offset == HPET_TIMER_FSB_ADDR(i)) {
610258579Sneel			update_register(&vhpet->timer[i].msireg, data, mask);
611258579Sneel			break;
612258579Sneel		}
613258579Sneel	}
614258579Sneeldone:
615258579Sneel	VHPET_UNLOCK(vhpet);
616258579Sneel	return (0);
617258579Sneel}
618258579Sneel
619258579Sneelint
620258579Sneelvhpet_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, int size,
621258579Sneel    void *arg)
622258579Sneel{
623258579Sneel	int i, offset;
624258579Sneel	struct vhpet *vhpet;
625258579Sneel	uint64_t data;
626258579Sneel
627258579Sneel	vhpet = vm_hpet(vm);
628258579Sneel	offset = gpa - VHPET_BASE;
629258579Sneel
630258579Sneel	VHPET_LOCK(vhpet);
631258579Sneel
632258579Sneel	/* Accesses to the HPET should be 4 or 8 bytes wide */
633258579Sneel	if (size != 4 && size != 8) {
634258579Sneel		VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
635258579Sneel		    "offset 0x%08x, size %d", offset, size);
636258579Sneel		data = 0;
637258579Sneel		goto done;
638258579Sneel	}
639258579Sneel
640258579Sneel	/* Access to the HPET should be naturally aligned to its width */
641258579Sneel	if (offset & (size - 1)) {
642258579Sneel		VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
643258579Sneel		    "offset 0x%08x, size %d", offset, size);
644258579Sneel		data = 0;
645258579Sneel		goto done;
646258579Sneel	}
647258579Sneel
648258579Sneel	if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) {
649258579Sneel		data = vhpet_capabilities();
650258579Sneel		goto done;
651258579Sneel	}
652258579Sneel
653258579Sneel	if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
654258579Sneel		data = vhpet->config;
655258579Sneel		goto done;
656258579Sneel	}
657258579Sneel
658258579Sneel	if (offset == HPET_ISR || offset == HPET_ISR + 4) {
659258579Sneel		data = vhpet->isr;
660258579Sneel		goto done;
661258579Sneel	}
662258579Sneel
663258579Sneel	if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
664260237Sneel		data = vhpet_counter(vhpet, NULL);
665258579Sneel		goto done;
666258579Sneel	}
667258579Sneel
668258579Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
669258579Sneel		if (offset == HPET_TIMER_CAP_CNF(i) ||
670258579Sneel		    offset == HPET_TIMER_CAP_CNF(i) + 4) {
671258579Sneel			data = vhpet->timer[i].cap_config;
672258579Sneel			break;
673258579Sneel		}
674258579Sneel
675258579Sneel		if (offset == HPET_TIMER_COMPARATOR(i) ||
676258579Sneel		    offset == HPET_TIMER_COMPARATOR(i) + 4) {
677258579Sneel			data = vhpet->timer[i].compval;
678258579Sneel			break;
679258579Sneel		}
680258579Sneel
681258579Sneel		if (offset == HPET_TIMER_FSB_VAL(i) ||
682258579Sneel		    offset == HPET_TIMER_FSB_ADDR(i)) {
683258579Sneel			data = vhpet->timer[i].msireg;
684258579Sneel			break;
685258579Sneel		}
686258579Sneel	}
687258579Sneel
688258579Sneel	if (i >= VHPET_NUM_TIMERS)
689258579Sneel		data = 0;
690258579Sneeldone:
691258579Sneel	VHPET_UNLOCK(vhpet);
692258579Sneel
693258579Sneel	if (size == 4) {
694258579Sneel		if (offset & 0x4)
695258579Sneel			data >>= 32;
696258579Sneel	}
697258579Sneel	*rval = data;
698258579Sneel	return (0);
699258579Sneel}
700258579Sneel
701258579Sneelstruct vhpet *
702258579Sneelvhpet_init(struct vm *vm)
703258579Sneel{
704258699Sneel	int i, pincount;
705258579Sneel	struct vhpet *vhpet;
706258699Sneel	uint64_t allowed_irqs;
707258579Sneel	struct vhpet_callout_arg *arg;
708258579Sneel	struct bintime bt;
709258579Sneel
710258579Sneel	vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO);
711258579Sneel        vhpet->vm = vm;
712258579Sneel	mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF);
713258579Sneel
714258579Sneel	FREQ2BT(HPET_FREQ, &bt);
715258579Sneel	vhpet->freq_sbt = bttosbt(bt);
716258579Sneel
717258699Sneel	pincount = vioapic_pincount(vm);
718321414Smav	if (pincount >= 32)
719321414Smav		allowed_irqs = 0xff000000;	/* irqs 24-31 */
720321414Smav	else if (pincount >= 20)
721321414Smav		allowed_irqs = 0xf << (pincount - 4);	/* 4 upper irqs */
722258699Sneel	else
723258699Sneel		allowed_irqs = 0;
724258699Sneel
725258579Sneel	/*
726258579Sneel	 * Initialize HPET timer hardware state.
727258579Sneel	 */
728258579Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
729258699Sneel		vhpet->timer[i].cap_config = allowed_irqs << 32;
730258699Sneel		vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT;
731258699Sneel		vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL;
732258699Sneel
733258579Sneel		vhpet->timer[i].compval = 0xffffffff;
734258579Sneel		callout_init(&vhpet->timer[i].callout, 1);
735258579Sneel
736258579Sneel		arg = &vhpet->timer[i].arg;
737258579Sneel		arg->vhpet = vhpet;
738258579Sneel		arg->timer_num = i;
739258579Sneel	}
740258579Sneel
741258579Sneel	return (vhpet);
742258579Sneel}
743258579Sneel
744258579Sneelvoid
745258579Sneelvhpet_cleanup(struct vhpet *vhpet)
746258579Sneel{
747258579Sneel	int i;
748258579Sneel
749258579Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++)
750258579Sneel		callout_drain(&vhpet->timer[i].callout);
751258579Sneel
752258579Sneel	free(vhpet, M_VHPET);
753258579Sneel}
754258579Sneel
755258579Sneelint
756258579Sneelvhpet_getcap(struct vm_hpet_cap *cap)
757258579Sneel{
758258579Sneel
759258579Sneel	cap->capabilities = vhpet_capabilities();
760258579Sneel	return (0);
761258579Sneel}
762