vhpet.c revision 276428
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: head/sys/amd64/vmm/io/vhpet.c 276428 2014-12-30 22:19:34Z neel $
28258579Sneel */
29258579Sneel
30258579Sneel#include <sys/cdefs.h>
31258579Sneel__FBSDID("$FreeBSD: head/sys/amd64/vmm/io/vhpet.c 276428 2014-12-30 22:19:34Z neel $");
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#include <sys/cpuset.h>
40258579Sneel
41258579Sneel#include <dev/acpica/acpi_hpet.h>
42258579Sneel
43258579Sneel#include <machine/vmm.h>
44258579Sneel#include <machine/vmm_dev.h>
45258579Sneel
46258579Sneel#include "vmm_lapic.h"
47263035Stychon#include "vatpic.h"
48258579Sneel#include "vioapic.h"
49258579Sneel#include "vhpet.h"
50258579Sneel
51258579Sneel#include "vmm_ktr.h"
52258579Sneel
53258579Sneelstatic MALLOC_DEFINE(M_VHPET, "vhpet", "bhyve virtual hpet");
54258579Sneel
55258579Sneel#define	HPET_FREQ	10000000		/* 10.0 Mhz */
56258579Sneel#define	FS_PER_S	1000000000000000ul
57258579Sneel
58258579Sneel/* Timer N Configuration and Capabilities Register */
59258579Sneel#define	HPET_TCAP_RO_MASK	(HPET_TCAP_INT_ROUTE 	|		\
60258579Sneel				 HPET_TCAP_FSB_INT_DEL	|		\
61258579Sneel				 HPET_TCAP_SIZE		|		\
62258579Sneel				 HPET_TCAP_PER_INT)
63258579Sneel/*
64258579Sneel * HPET requires at least 3 timers and up to 32 timers per block.
65258579Sneel */
66258579Sneel#define	VHPET_NUM_TIMERS	8
67258579SneelCTASSERT(VHPET_NUM_TIMERS >= 3 && VHPET_NUM_TIMERS <= 32);
68258579Sneel
69258579Sneelstruct vhpet_callout_arg {
70258579Sneel	struct vhpet *vhpet;
71258579Sneel	int timer_num;
72258579Sneel};
73258579Sneel
74258579Sneelstruct vhpet {
75258579Sneel	struct vm	*vm;
76258579Sneel	struct mtx	mtx;
77258579Sneel	sbintime_t	freq_sbt;
78258579Sneel
79258579Sneel	uint64_t	config;		/* Configuration */
80258579Sneel	uint64_t	isr;		/* Interrupt Status */
81260237Sneel	uint32_t	countbase;	/* HPET counter base value */
82260237Sneel	sbintime_t	countbase_sbt;	/* uptime corresponding to base value */
83258579Sneel
84258579Sneel	struct {
85258579Sneel		uint64_t	cap_config;	/* Configuration */
86258579Sneel		uint64_t	msireg;		/* FSB interrupt routing */
87258579Sneel		uint32_t	compval;	/* Comparator */
88258579Sneel		uint32_t	comprate;
89258579Sneel		struct callout	callout;
90260237Sneel		sbintime_t	callout_sbt;	/* time when counter==compval */
91258579Sneel		struct vhpet_callout_arg arg;
92258579Sneel	} timer[VHPET_NUM_TIMERS];
93258579Sneel};
94258579Sneel
95258579Sneel#define	VHPET_LOCK(vhp)		mtx_lock(&((vhp)->mtx))
96258579Sneel#define	VHPET_UNLOCK(vhp)	mtx_unlock(&((vhp)->mtx))
97258579Sneel
98260237Sneelstatic void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter,
99260237Sneel    sbintime_t now);
100260237Sneel
101258579Sneelstatic uint64_t
102258579Sneelvhpet_capabilities(void)
103258579Sneel{
104258579Sneel	uint64_t cap = 0;
105258579Sneel
106258579Sneel	cap |= 0x8086 << 16;			/* vendor id */
107258579Sneel	cap |= (VHPET_NUM_TIMERS - 1) << 8;	/* number of timers */
108258579Sneel	cap |= 1;				/* revision */
109258579Sneel	cap &= ~HPET_CAP_COUNT_SIZE;		/* 32-bit timer */
110258579Sneel
111258579Sneel	cap &= 0xffffffff;
112258579Sneel	cap |= (FS_PER_S / HPET_FREQ) << 32;	/* tick period in fs */
113258579Sneel
114258579Sneel	return (cap);
115258579Sneel}
116258579Sneel
117258579Sneelstatic __inline bool
118258579Sneelvhpet_counter_enabled(struct vhpet *vhpet)
119258579Sneel{
120258579Sneel
121258579Sneel	return ((vhpet->config & HPET_CNF_ENABLE) ? true : false);
122258579Sneel}
123258579Sneel
124258579Sneelstatic __inline bool
125258579Sneelvhpet_timer_msi_enabled(struct vhpet *vhpet, int n)
126258579Sneel{
127258579Sneel	const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN;
128258579Sneel
129258579Sneel	if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable)
130258579Sneel		return (true);
131258579Sneel	else
132258579Sneel		return (false);
133258579Sneel}
134258579Sneel
135258579Sneelstatic __inline int
136258579Sneelvhpet_timer_ioapic_pin(struct vhpet *vhpet, int n)
137258579Sneel{
138258579Sneel	/*
139258579Sneel	 * If the timer is configured to use MSI then treat it as if the
140258579Sneel	 * timer is not connected to the ioapic.
141258579Sneel	 */
142258579Sneel	if (vhpet_timer_msi_enabled(vhpet, n))
143258579Sneel		return (0);
144258579Sneel
145258579Sneel	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
146258579Sneel}
147258579Sneel
148258579Sneelstatic uint32_t
149260237Sneelvhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr)
150258579Sneel{
151258579Sneel	uint32_t val;
152260237Sneel	sbintime_t now, delta;
153258579Sneel
154260237Sneel	val = vhpet->countbase;
155258579Sneel	if (vhpet_counter_enabled(vhpet)) {
156260237Sneel		now = sbinuptime();
157260237Sneel		delta = now - vhpet->countbase_sbt;
158260237Sneel		KASSERT(delta >= 0, ("vhpet_counter: uptime went backwards: "
159260237Sneel		    "%#lx to %#lx", vhpet->countbase_sbt, now));
160260237Sneel		val += delta / vhpet->freq_sbt;
161260237Sneel		if (nowptr != NULL)
162260237Sneel			*nowptr = now;
163260237Sneel	} else {
164258579Sneel		/*
165260237Sneel		 * The sbinuptime corresponding to the 'countbase' is
166260237Sneel		 * meaningless when the counter is disabled. Make sure
167260237Sneel		 * that the the caller doesn't want to use it.
168258579Sneel		 */
169260237Sneel		KASSERT(nowptr == NULL, ("vhpet_counter: nowptr must be NULL"));
170258579Sneel	}
171258579Sneel	return (val);
172258579Sneel}
173258579Sneel
174258579Sneelstatic void
175258579Sneelvhpet_timer_clear_isr(struct vhpet *vhpet, int n)
176258579Sneel{
177276428Sneel	int pin;
178258579Sneel
179258579Sneel	if (vhpet->isr & (1 << n)) {
180258579Sneel		pin = vhpet_timer_ioapic_pin(vhpet, n);
181258579Sneel		KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n));
182258579Sneel		vioapic_deassert_irq(vhpet->vm, pin);
183258579Sneel		vhpet->isr &= ~(1 << n);
184258579Sneel	}
185258579Sneel}
186258579Sneel
187258579Sneelstatic __inline bool
188258579Sneelvhpet_periodic_timer(struct vhpet *vhpet, int n)
189258579Sneel{
190258579Sneel
191258579Sneel	return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0);
192258579Sneel}
193258579Sneel
194258579Sneelstatic __inline bool
195258579Sneelvhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n)
196258579Sneel{
197258579Sneel
198258579Sneel	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0);
199258579Sneel}
200258579Sneel
201258579Sneelstatic __inline bool
202258579Sneelvhpet_timer_edge_trig(struct vhpet *vhpet, int n)
203258579Sneel{
204258579Sneel
205258579Sneel	KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: "
206258579Sneel	    "timer %d is using MSI", n));
207258579Sneel
208258579Sneel	if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0)
209258579Sneel		return (true);
210258579Sneel	else
211258579Sneel		return (false);
212258579Sneel}
213258579Sneel
214258579Sneelstatic void
215258579Sneelvhpet_timer_interrupt(struct vhpet *vhpet, int n)
216258579Sneel{
217276428Sneel	int pin;
218258579Sneel
219258579Sneel	/* If interrupts are not enabled for this timer then just return. */
220258579Sneel	if (!vhpet_timer_interrupt_enabled(vhpet, n))
221258579Sneel		return;
222258579Sneel
223258579Sneel	/*
224258579Sneel	 * If a level triggered interrupt is already asserted then just return.
225258579Sneel	 */
226258579Sneel	if ((vhpet->isr & (1 << n)) != 0) {
227258579Sneel		VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n);
228258579Sneel		return;
229258579Sneel	}
230258579Sneel
231258579Sneel	if (vhpet_timer_msi_enabled(vhpet, n)) {
232259482Sneel		lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32,
233259482Sneel		    vhpet->timer[n].msireg & 0xffffffff);
234258579Sneel		return;
235258579Sneel	}
236258579Sneel
237258579Sneel	pin = vhpet_timer_ioapic_pin(vhpet, n);
238258579Sneel	if (pin == 0) {
239258579Sneel		VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n);
240258579Sneel		return;
241258579Sneel	}
242258579Sneel
243258579Sneel	if (vhpet_timer_edge_trig(vhpet, n)) {
244258579Sneel		vioapic_pulse_irq(vhpet->vm, pin);
245258579Sneel	} else {
246258579Sneel		vhpet->isr |= 1 << n;
247258579Sneel		vioapic_assert_irq(vhpet->vm, pin);
248258579Sneel	}
249258579Sneel}
250258579Sneel
251258579Sneelstatic void
252258579Sneelvhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter)
253258579Sneel{
254258579Sneel	uint32_t compval, comprate, compnext;
255258579Sneel
256258579Sneel	KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n));
257258579Sneel
258258579Sneel	compval = vhpet->timer[n].compval;
259258579Sneel	comprate = vhpet->timer[n].comprate;
260258579Sneel
261258579Sneel	/*
262258579Sneel	 * Calculate the comparator value to be used for the next periodic
263258579Sneel	 * interrupt.
264258579Sneel	 *
265258579Sneel	 * This function is commonly called from the callout handler.
266258579Sneel	 * In this scenario the 'counter' is ahead of 'compval'. To find
267258579Sneel	 * the next value to program into the accumulator we divide the
268258579Sneel	 * number space between 'compval' and 'counter' into 'comprate'
269258579Sneel	 * sized units. The 'compval' is rounded up such that is "ahead"
270258579Sneel	 * of 'counter'.
271258579Sneel	 */
272258579Sneel	compnext = compval + ((counter - compval) / comprate + 1) * comprate;
273258579Sneel
274258579Sneel	vhpet->timer[n].compval = compnext;
275258579Sneel}
276258579Sneel
277258579Sneelstatic void
278258579Sneelvhpet_handler(void *a)
279258579Sneel{
280258579Sneel	int n;
281258579Sneel	uint32_t counter;
282260237Sneel	sbintime_t now;
283258579Sneel	struct vhpet *vhpet;
284258579Sneel	struct callout *callout;
285258579Sneel	struct vhpet_callout_arg *arg;
286258579Sneel
287258579Sneel	arg = a;
288258579Sneel	vhpet = arg->vhpet;
289258579Sneel	n = arg->timer_num;
290258579Sneel	callout = &vhpet->timer[n].callout;
291258579Sneel
292258579Sneel	VM_CTR1(vhpet->vm, "hpet t%d fired", n);
293258579Sneel
294258579Sneel	VHPET_LOCK(vhpet);
295258579Sneel
296258579Sneel	if (callout_pending(callout))		/* callout was reset */
297258579Sneel		goto done;
298258579Sneel
299258579Sneel	if (!callout_active(callout))		/* callout was stopped */
300258579Sneel		goto done;
301258579Sneel
302258579Sneel	callout_deactivate(callout);
303258579Sneel
304258579Sneel	if (!vhpet_counter_enabled(vhpet))
305258579Sneel		panic("vhpet(%p) callout with counter disabled", vhpet);
306258579Sneel
307260237Sneel	counter = vhpet_counter(vhpet, &now);
308260237Sneel	vhpet_start_timer(vhpet, n, counter, now);
309258579Sneel	vhpet_timer_interrupt(vhpet, n);
310258579Sneeldone:
311258579Sneel	VHPET_UNLOCK(vhpet);
312258579Sneel	return;
313258579Sneel}
314258579Sneel
315258579Sneelstatic void
316260237Sneelvhpet_stop_timer(struct vhpet *vhpet, int n, sbintime_t now)
317258579Sneel{
318258579Sneel
319260237Sneel	VM_CTR1(vhpet->vm, "hpet t%d stopped", n);
320258579Sneel	callout_stop(&vhpet->timer[n].callout);
321260237Sneel
322260237Sneel	/*
323260237Sneel	 * If the callout was scheduled to expire in the past but hasn't
324260237Sneel	 * had a chance to execute yet then trigger the timer interrupt
325260237Sneel	 * here. Failing to do so will result in a missed timer interrupt
326260237Sneel	 * in the guest. This is especially bad in one-shot mode because
327260237Sneel	 * the next interrupt has to wait for the counter to wrap around.
328260237Sneel	 */
329260237Sneel	if (vhpet->timer[n].callout_sbt < now) {
330260237Sneel		VM_CTR1(vhpet->vm, "hpet t%d interrupt triggered after "
331260237Sneel		    "stopping timer", n);
332260237Sneel		vhpet_timer_interrupt(vhpet, n);
333260237Sneel	}
334258579Sneel}
335258579Sneel
336258579Sneelstatic void
337260237Sneelvhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, sbintime_t now)
338258579Sneel{
339260237Sneel	sbintime_t delta, precision;
340258579Sneel
341258579Sneel	if (vhpet->timer[n].comprate != 0)
342258579Sneel		vhpet_adjust_compval(vhpet, n, counter);
343260237Sneel	else {
344260237Sneel		/*
345260237Sneel		 * In one-shot mode it is the guest's responsibility to make
346260237Sneel		 * sure that the comparator value is not in the "past". The
347260237Sneel		 * hardware doesn't have any belt-and-suspenders to deal with
348260237Sneel		 * this so we don't either.
349260237Sneel		 */
350258579Sneel	}
351258579Sneel
352260237Sneel	delta = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt;
353260237Sneel	precision = delta >> tc_precexp;
354260237Sneel	vhpet->timer[n].callout_sbt = now + delta;
355260237Sneel	callout_reset_sbt(&vhpet->timer[n].callout, vhpet->timer[n].callout_sbt,
356260237Sneel	    precision, vhpet_handler, &vhpet->timer[n].arg, C_ABSOLUTE);
357258579Sneel}
358258579Sneel
359258579Sneelstatic void
360258579Sneelvhpet_start_counting(struct vhpet *vhpet)
361258579Sneel{
362258579Sneel	int i;
363258579Sneel
364260237Sneel	vhpet->countbase_sbt = sbinuptime();
365260237Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
366260237Sneel		/*
367260237Sneel		 * Restart the timers based on the value of the main counter
368260237Sneel		 * when it stopped counting.
369260237Sneel		 */
370260237Sneel		vhpet_start_timer(vhpet, i, vhpet->countbase,
371260237Sneel		    vhpet->countbase_sbt);
372260237Sneel	}
373258579Sneel}
374258579Sneel
375258579Sneelstatic void
376260237Sneelvhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, sbintime_t now)
377258579Sneel{
378258579Sneel	int i;
379258579Sneel
380260237Sneel	vhpet->countbase = counter;
381258579Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++)
382260237Sneel		vhpet_stop_timer(vhpet, i, now);
383258579Sneel}
384258579Sneel
385258579Sneelstatic __inline void
386258579Sneelupdate_register(uint64_t *regptr, uint64_t data, uint64_t mask)
387258579Sneel{
388258579Sneel
389258579Sneel	*regptr &= ~mask;
390258579Sneel	*regptr |= (data & mask);
391258579Sneel}
392258579Sneel
393258579Sneelstatic void
394258579Sneelvhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data,
395258579Sneel    uint64_t mask)
396258579Sneel{
397258579Sneel	bool clear_isr;
398258579Sneel	int old_pin, new_pin;
399258579Sneel	uint32_t allowed_irqs;
400258579Sneel	uint64_t oldval, newval;
401258579Sneel
402258579Sneel	if (vhpet_timer_msi_enabled(vhpet, n) ||
403258579Sneel	    vhpet_timer_edge_trig(vhpet, n)) {
404258579Sneel		if (vhpet->isr & (1 << n))
405258579Sneel			panic("vhpet timer %d isr should not be asserted", n);
406258579Sneel	}
407258579Sneel	old_pin = vhpet_timer_ioapic_pin(vhpet, n);
408258579Sneel	oldval = vhpet->timer[n].cap_config;
409258579Sneel
410258579Sneel	newval = oldval;
411258579Sneel	update_register(&newval, data, mask);
412258579Sneel	newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE);
413258579Sneel	newval |= oldval & HPET_TCAP_RO_MASK;
414258579Sneel
415258579Sneel	if (newval == oldval)
416258579Sneel		return;
417258579Sneel
418258579Sneel	vhpet->timer[n].cap_config = newval;
419258579Sneel	VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval);
420258579Sneel
421258579Sneel	/*
422258579Sneel	 * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field.
423258579Sneel	 * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set
424258579Sneel	 * it to the default value of 0.
425258579Sneel	 */
426258579Sneel	allowed_irqs = vhpet->timer[n].cap_config >> 32;
427258579Sneel	new_pin = vhpet_timer_ioapic_pin(vhpet, n);
428258579Sneel	if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) {
429258579Sneel		VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, "
430258579Sneel		    "allowed_irqs 0x%08x", n, new_pin, allowed_irqs);
431258579Sneel		new_pin = 0;
432258579Sneel		vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE;
433258579Sneel	}
434258579Sneel
435258579Sneel	if (!vhpet_periodic_timer(vhpet, n))
436258579Sneel		vhpet->timer[n].comprate = 0;
437258579Sneel
438258579Sneel	/*
439258579Sneel	 * If the timer's ISR bit is set then clear it in the following cases:
440258579Sneel	 * - interrupt is disabled
441258579Sneel	 * - interrupt type is changed from level to edge or fsb.
442258579Sneel	 * - interrupt routing is changed
443258579Sneel	 *
444258579Sneel	 * This is to ensure that this timer's level triggered interrupt does
445258579Sneel	 * not remain asserted forever.
446258579Sneel	 */
447258579Sneel	if (vhpet->isr & (1 << n)) {
448258579Sneel		KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d",
449258579Sneel		    n, old_pin));
450258579Sneel		if (!vhpet_timer_interrupt_enabled(vhpet, n))
451258579Sneel			clear_isr = true;
452258579Sneel		else if (vhpet_timer_msi_enabled(vhpet, n))
453258579Sneel			clear_isr = true;
454258579Sneel		else if (vhpet_timer_edge_trig(vhpet, n))
455258579Sneel			clear_isr = true;
456258579Sneel		else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin)
457258579Sneel			clear_isr = true;
458258579Sneel		else
459258579Sneel			clear_isr = false;
460258579Sneel
461258579Sneel		if (clear_isr) {
462258579Sneel			VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to "
463258579Sneel			    "configuration change", n);
464258579Sneel			vioapic_deassert_irq(vhpet->vm, old_pin);
465258579Sneel			vhpet->isr &= ~(1 << n);
466258579Sneel		}
467258579Sneel	}
468258579Sneel}
469258579Sneel
470258579Sneelint
471258579Sneelvhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size,
472258579Sneel    void *arg)
473258579Sneel{
474258579Sneel	struct vhpet *vhpet;
475258579Sneel	uint64_t data, mask, oldval, val64;
476260237Sneel	uint32_t isr_clear_mask, old_compval, old_comprate, counter;
477260237Sneel	sbintime_t now, *nowptr;
478258579Sneel	int i, offset;
479258579Sneel
480258579Sneel	vhpet = vm_hpet(vm);
481258579Sneel	offset = gpa - VHPET_BASE;
482258579Sneel
483258579Sneel	VHPET_LOCK(vhpet);
484258579Sneel
485258579Sneel	/* Accesses to the HPET should be 4 or 8 bytes wide */
486258579Sneel	switch (size) {
487258579Sneel	case 8:
488258579Sneel		mask = 0xffffffffffffffff;
489258579Sneel		data = val;
490258579Sneel		break;
491258579Sneel	case 4:
492258579Sneel		mask = 0xffffffff;
493258579Sneel		data = val;
494258579Sneel		if ((offset & 0x4) != 0) {
495258579Sneel			mask <<= 32;
496258579Sneel			data <<= 32;
497258579Sneel		}
498258579Sneel		break;
499258579Sneel	default:
500258579Sneel		VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
501258579Sneel		    "offset 0x%08x, size %d", offset, size);
502258579Sneel		goto done;
503258579Sneel	}
504258579Sneel
505258579Sneel	/* Access to the HPET should be naturally aligned to its width */
506258579Sneel	if (offset & (size - 1)) {
507258579Sneel		VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
508258579Sneel		    "offset 0x%08x, size %d", offset, size);
509258579Sneel		goto done;
510258579Sneel	}
511258579Sneel
512258579Sneel	if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
513260237Sneel		/*
514260237Sneel		 * Get the most recent value of the counter before updating
515260237Sneel		 * the 'config' register. If the HPET is going to be disabled
516260237Sneel		 * then we need to update 'countbase' with the value right
517260237Sneel		 * before it is disabled.
518260237Sneel		 */
519260237Sneel		nowptr = vhpet_counter_enabled(vhpet) ? &now : NULL;
520260237Sneel		counter = vhpet_counter(vhpet, nowptr);
521258579Sneel		oldval = vhpet->config;
522258579Sneel		update_register(&vhpet->config, data, mask);
523276428Sneel
524276428Sneel		/*
525276428Sneel		 * LegacyReplacement Routing is not supported so clear the
526276428Sneel		 * bit explicitly.
527276428Sneel		 */
528276428Sneel		vhpet->config &= ~HPET_CNF_LEG_RT;
529276428Sneel
530258579Sneel		if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) {
531258579Sneel			if (vhpet_counter_enabled(vhpet)) {
532258579Sneel				vhpet_start_counting(vhpet);
533258579Sneel				VM_CTR0(vhpet->vm, "hpet enabled");
534258579Sneel			} else {
535260237Sneel				vhpet_stop_counting(vhpet, counter, now);
536258579Sneel				VM_CTR0(vhpet->vm, "hpet disabled");
537258579Sneel			}
538258579Sneel		}
539258579Sneel		goto done;
540258579Sneel	}
541258579Sneel
542258579Sneel	if (offset == HPET_ISR || offset == HPET_ISR + 4) {
543258579Sneel		isr_clear_mask = vhpet->isr & data;
544258579Sneel		for (i = 0; i < VHPET_NUM_TIMERS; i++) {
545258579Sneel			if ((isr_clear_mask & (1 << i)) != 0) {
546258579Sneel				VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i);
547258579Sneel				vhpet_timer_clear_isr(vhpet, i);
548258579Sneel			}
549258579Sneel		}
550258579Sneel		goto done;
551258579Sneel	}
552258579Sneel
553258579Sneel	if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
554258579Sneel		/* Zero-extend the counter to 64-bits before updating it */
555260237Sneel		val64 = vhpet_counter(vhpet, NULL);
556258579Sneel		update_register(&val64, data, mask);
557260237Sneel		vhpet->countbase = val64;
558258579Sneel		if (vhpet_counter_enabled(vhpet))
559258579Sneel			vhpet_start_counting(vhpet);
560258579Sneel		goto done;
561258579Sneel	}
562258579Sneel
563258579Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
564258579Sneel		if (offset == HPET_TIMER_CAP_CNF(i) ||
565258579Sneel		    offset == HPET_TIMER_CAP_CNF(i) + 4) {
566258579Sneel			vhpet_timer_update_config(vhpet, i, data, mask);
567258579Sneel			break;
568258579Sneel		}
569258579Sneel
570258579Sneel		if (offset == HPET_TIMER_COMPARATOR(i) ||
571258579Sneel		    offset == HPET_TIMER_COMPARATOR(i) + 4) {
572258579Sneel			old_compval = vhpet->timer[i].compval;
573258579Sneel			old_comprate = vhpet->timer[i].comprate;
574258579Sneel			if (vhpet_periodic_timer(vhpet, i)) {
575258579Sneel				/*
576258579Sneel				 * In periodic mode writes to the comparator
577258579Sneel				 * change the 'compval' register only if the
578258579Sneel				 * HPET_TCNF_VAL_SET bit is set in the config
579258579Sneel				 * register.
580258579Sneel				 */
581258579Sneel				val64 = vhpet->timer[i].comprate;
582258579Sneel				update_register(&val64, data, mask);
583258579Sneel				vhpet->timer[i].comprate = val64;
584258579Sneel				if ((vhpet->timer[i].cap_config &
585258579Sneel				    HPET_TCNF_VAL_SET) != 0) {
586258579Sneel					vhpet->timer[i].compval = val64;
587258579Sneel				}
588258579Sneel			} else {
589258579Sneel				KASSERT(vhpet->timer[i].comprate == 0,
590258579Sneel				    ("vhpet one-shot timer %d has invalid "
591258579Sneel				    "rate %u", i, vhpet->timer[i].comprate));
592258579Sneel				val64 = vhpet->timer[i].compval;
593258579Sneel				update_register(&val64, data, mask);
594258579Sneel				vhpet->timer[i].compval = val64;
595258579Sneel			}
596258579Sneel			vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET;
597258579Sneel
598258579Sneel			if (vhpet->timer[i].compval != old_compval ||
599258579Sneel			    vhpet->timer[i].comprate != old_comprate) {
600260237Sneel				if (vhpet_counter_enabled(vhpet)) {
601260237Sneel					counter = vhpet_counter(vhpet, &now);
602260237Sneel					vhpet_start_timer(vhpet, i, counter,
603260237Sneel					    now);
604260237Sneel				}
605258579Sneel			}
606258579Sneel			break;
607258579Sneel		}
608258579Sneel
609258579Sneel		if (offset == HPET_TIMER_FSB_VAL(i) ||
610258579Sneel		    offset == HPET_TIMER_FSB_ADDR(i)) {
611258579Sneel			update_register(&vhpet->timer[i].msireg, data, mask);
612258579Sneel			break;
613258579Sneel		}
614258579Sneel	}
615258579Sneeldone:
616258579Sneel	VHPET_UNLOCK(vhpet);
617258579Sneel	return (0);
618258579Sneel}
619258579Sneel
620258579Sneelint
621258579Sneelvhpet_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, int size,
622258579Sneel    void *arg)
623258579Sneel{
624258579Sneel	int i, offset;
625258579Sneel	struct vhpet *vhpet;
626258579Sneel	uint64_t data;
627258579Sneel
628258579Sneel	vhpet = vm_hpet(vm);
629258579Sneel	offset = gpa - VHPET_BASE;
630258579Sneel
631258579Sneel	VHPET_LOCK(vhpet);
632258579Sneel
633258579Sneel	/* Accesses to the HPET should be 4 or 8 bytes wide */
634258579Sneel	if (size != 4 && size != 8) {
635258579Sneel		VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
636258579Sneel		    "offset 0x%08x, size %d", offset, size);
637258579Sneel		data = 0;
638258579Sneel		goto done;
639258579Sneel	}
640258579Sneel
641258579Sneel	/* Access to the HPET should be naturally aligned to its width */
642258579Sneel	if (offset & (size - 1)) {
643258579Sneel		VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
644258579Sneel		    "offset 0x%08x, size %d", offset, size);
645258579Sneel		data = 0;
646258579Sneel		goto done;
647258579Sneel	}
648258579Sneel
649258579Sneel	if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) {
650258579Sneel		data = vhpet_capabilities();
651258579Sneel		goto done;
652258579Sneel	}
653258579Sneel
654258579Sneel	if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
655258579Sneel		data = vhpet->config;
656258579Sneel		goto done;
657258579Sneel	}
658258579Sneel
659258579Sneel	if (offset == HPET_ISR || offset == HPET_ISR + 4) {
660258579Sneel		data = vhpet->isr;
661258579Sneel		goto done;
662258579Sneel	}
663258579Sneel
664258579Sneel	if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
665260237Sneel		data = vhpet_counter(vhpet, NULL);
666258579Sneel		goto done;
667258579Sneel	}
668258579Sneel
669258579Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
670258579Sneel		if (offset == HPET_TIMER_CAP_CNF(i) ||
671258579Sneel		    offset == HPET_TIMER_CAP_CNF(i) + 4) {
672258579Sneel			data = vhpet->timer[i].cap_config;
673258579Sneel			break;
674258579Sneel		}
675258579Sneel
676258579Sneel		if (offset == HPET_TIMER_COMPARATOR(i) ||
677258579Sneel		    offset == HPET_TIMER_COMPARATOR(i) + 4) {
678258579Sneel			data = vhpet->timer[i].compval;
679258579Sneel			break;
680258579Sneel		}
681258579Sneel
682258579Sneel		if (offset == HPET_TIMER_FSB_VAL(i) ||
683258579Sneel		    offset == HPET_TIMER_FSB_ADDR(i)) {
684258579Sneel			data = vhpet->timer[i].msireg;
685258579Sneel			break;
686258579Sneel		}
687258579Sneel	}
688258579Sneel
689258579Sneel	if (i >= VHPET_NUM_TIMERS)
690258579Sneel		data = 0;
691258579Sneeldone:
692258579Sneel	VHPET_UNLOCK(vhpet);
693258579Sneel
694258579Sneel	if (size == 4) {
695258579Sneel		if (offset & 0x4)
696258579Sneel			data >>= 32;
697258579Sneel	}
698258579Sneel	*rval = data;
699258579Sneel	return (0);
700258579Sneel}
701258579Sneel
702258579Sneelstruct vhpet *
703258579Sneelvhpet_init(struct vm *vm)
704258579Sneel{
705258699Sneel	int i, pincount;
706258579Sneel	struct vhpet *vhpet;
707258699Sneel	uint64_t allowed_irqs;
708258579Sneel	struct vhpet_callout_arg *arg;
709258579Sneel	struct bintime bt;
710258579Sneel
711258579Sneel	vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO);
712258579Sneel        vhpet->vm = vm;
713258579Sneel	mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF);
714258579Sneel
715258579Sneel	FREQ2BT(HPET_FREQ, &bt);
716258579Sneel	vhpet->freq_sbt = bttosbt(bt);
717258579Sneel
718258699Sneel	pincount = vioapic_pincount(vm);
719258699Sneel	if (pincount >= 24)
720258699Sneel		allowed_irqs = 0x00f00000;	/* irqs 20, 21, 22 and 23 */
721258699Sneel	else
722258699Sneel		allowed_irqs = 0;
723258699Sneel
724258579Sneel	/*
725258579Sneel	 * Initialize HPET timer hardware state.
726258579Sneel	 */
727258579Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
728258699Sneel		vhpet->timer[i].cap_config = allowed_irqs << 32;
729258699Sneel		vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT;
730258699Sneel		vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL;
731258699Sneel
732258579Sneel		vhpet->timer[i].compval = 0xffffffff;
733258579Sneel		callout_init(&vhpet->timer[i].callout, 1);
734258579Sneel
735258579Sneel		arg = &vhpet->timer[i].arg;
736258579Sneel		arg->vhpet = vhpet;
737258579Sneel		arg->timer_num = i;
738258579Sneel	}
739258579Sneel
740258579Sneel	return (vhpet);
741258579Sneel}
742258579Sneel
743258579Sneelvoid
744258579Sneelvhpet_cleanup(struct vhpet *vhpet)
745258579Sneel{
746258579Sneel	int i;
747258579Sneel
748258579Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++)
749258579Sneel		callout_drain(&vhpet->timer[i].callout);
750258579Sneel
751258579Sneel	free(vhpet, M_VHPET);
752258579Sneel}
753258579Sneel
754258579Sneelint
755258579Sneelvhpet_getcap(struct vm_hpet_cap *cap)
756258579Sneel{
757258579Sneel
758258579Sneel	cap->capabilities = vhpet_capabilities();
759258579Sneel	return (0);
760258579Sneel}
761