vhpet.c revision 258579
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 258579 2013-11-25 19:04:51Z neel $
28258579Sneel */
29258579Sneel
30258579Sneel#include <sys/cdefs.h>
31258579Sneel__FBSDID("$FreeBSD: head/sys/amd64/vmm/io/vhpet.c 258579 2013-11-25 19:04:51Z 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"
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
54258579Sneel#define	HPET_FREQ	10000000		/* 10.0 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 */
80258579Sneel	uint32_t	counter;	/* HPET Counter */
81258579Sneel	sbintime_t	counter_sbt;
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;
89258579Sneel		struct vhpet_callout_arg arg;
90258579Sneel	} timer[VHPET_NUM_TIMERS];
91258579Sneel};
92258579Sneel
93258579Sneel#define	VHPET_LOCK(vhp)		mtx_lock(&((vhp)->mtx))
94258579Sneel#define	VHPET_UNLOCK(vhp)	mtx_unlock(&((vhp)->mtx))
95258579Sneel
96258579Sneelstatic uint64_t
97258579Sneelvhpet_capabilities(void)
98258579Sneel{
99258579Sneel	uint64_t cap = 0;
100258579Sneel
101258579Sneel	cap |= 0x8086 << 16;			/* vendor id */
102258579Sneel	cap |= HPET_CAP_LEG_RT;			/* legacy routing capable */
103258579Sneel	cap |= (VHPET_NUM_TIMERS - 1) << 8;	/* number of timers */
104258579Sneel	cap |= 1;				/* revision */
105258579Sneel	cap &= ~HPET_CAP_COUNT_SIZE;		/* 32-bit timer */
106258579Sneel
107258579Sneel	cap &= 0xffffffff;
108258579Sneel	cap |= (FS_PER_S / HPET_FREQ) << 32;	/* tick period in fs */
109258579Sneel
110258579Sneel	return (cap);
111258579Sneel}
112258579Sneel
113258579Sneelstatic __inline bool
114258579Sneelvhpet_counter_enabled(struct vhpet *vhpet)
115258579Sneel{
116258579Sneel
117258579Sneel	return ((vhpet->config & HPET_CNF_ENABLE) ? true : false);
118258579Sneel}
119258579Sneel
120258579Sneelstatic __inline bool
121258579Sneelvhpet_timer_msi_enabled(struct vhpet *vhpet, int n)
122258579Sneel{
123258579Sneel	const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN;
124258579Sneel
125258579Sneel	/*
126258579Sneel	 * LegacyReplacement Route configuration takes precedence over MSI
127258579Sneel	 * for timers 0 and 1.
128258579Sneel	 */
129258579Sneel	if (n == 0 || n == 1) {
130258579Sneel		if (vhpet->config & HPET_CNF_LEG_RT)
131258579Sneel			return (false);
132258579Sneel	}
133258579Sneel
134258579Sneel	if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable)
135258579Sneel		return (true);
136258579Sneel	else
137258579Sneel		return (false);
138258579Sneel}
139258579Sneel
140258579Sneelstatic __inline int
141258579Sneelvhpet_timer_ioapic_pin(struct vhpet *vhpet, int n)
142258579Sneel{
143258579Sneel	/*
144258579Sneel	 * If the timer is configured to use MSI then treat it as if the
145258579Sneel	 * timer is not connected to the ioapic.
146258579Sneel	 */
147258579Sneel	if (vhpet_timer_msi_enabled(vhpet, n))
148258579Sneel		return (0);
149258579Sneel
150258579Sneel	if (vhpet->config & HPET_CNF_LEG_RT) {
151258579Sneel		/*
152258579Sneel		 * In "legacy routing" timers 0 and 1 are connected to
153258579Sneel		 * ioapic pins 2 and 8 respectively.
154258579Sneel		 */
155258579Sneel		switch (n) {
156258579Sneel		case 0:
157258579Sneel			return (2);
158258579Sneel		case 1:
159258579Sneel			return (8);
160258579Sneel		}
161258579Sneel	}
162258579Sneel
163258579Sneel	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
164258579Sneel}
165258579Sneel
166258579Sneelstatic uint32_t
167258579Sneelvhpet_counter(struct vhpet *vhpet, bool latch)
168258579Sneel{
169258579Sneel	uint32_t val;
170258579Sneel	sbintime_t cur_sbt, delta_sbt;
171258579Sneel
172258579Sneel	val = vhpet->counter;
173258579Sneel	if (vhpet_counter_enabled(vhpet)) {
174258579Sneel		cur_sbt = sbinuptime();
175258579Sneel		delta_sbt = cur_sbt - vhpet->counter_sbt;
176258579Sneel		KASSERT(delta_sbt >= 0,
177258579Sneel		    ("vhpet counter went backwards: %#lx to %#lx",
178258579Sneel		    vhpet->counter_sbt, cur_sbt));
179258579Sneel		val += delta_sbt / vhpet->freq_sbt;
180258579Sneel
181258579Sneel		/*
182258579Sneel		 * Keep track of the last value of the main counter that
183258579Sneel		 * was read by the guest.
184258579Sneel		 */
185258579Sneel		if (latch) {
186258579Sneel			vhpet->counter = val;
187258579Sneel			vhpet->counter_sbt = cur_sbt;
188258579Sneel		}
189258579Sneel	}
190258579Sneel
191258579Sneel	return (val);
192258579Sneel}
193258579Sneel
194258579Sneelstatic void
195258579Sneelvhpet_timer_clear_isr(struct vhpet *vhpet, int n)
196258579Sneel{
197258579Sneel	int pin;
198258579Sneel
199258579Sneel	if (vhpet->isr & (1 << n)) {
200258579Sneel		pin = vhpet_timer_ioapic_pin(vhpet, n);
201258579Sneel		KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n));
202258579Sneel		vioapic_deassert_irq(vhpet->vm, pin);
203258579Sneel		vhpet->isr &= ~(1 << n);
204258579Sneel	}
205258579Sneel}
206258579Sneel
207258579Sneelstatic __inline bool
208258579Sneelvhpet_periodic_timer(struct vhpet *vhpet, int n)
209258579Sneel{
210258579Sneel
211258579Sneel	return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0);
212258579Sneel}
213258579Sneel
214258579Sneelstatic __inline bool
215258579Sneelvhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n)
216258579Sneel{
217258579Sneel
218258579Sneel	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0);
219258579Sneel}
220258579Sneel
221258579Sneelstatic __inline bool
222258579Sneelvhpet_timer_edge_trig(struct vhpet *vhpet, int n)
223258579Sneel{
224258579Sneel
225258579Sneel	KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: "
226258579Sneel	    "timer %d is using MSI", n));
227258579Sneel
228258579Sneel	/* The legacy replacement interrupts are always edge triggered */
229258579Sneel	if (vhpet->config & HPET_CNF_LEG_RT) {
230258579Sneel		if (n == 0 || n == 1)
231258579Sneel			return (true);
232258579Sneel	}
233258579Sneel
234258579Sneel	if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0)
235258579Sneel		return (true);
236258579Sneel	else
237258579Sneel		return (false);
238258579Sneel}
239258579Sneel
240258579Sneelstatic void
241258579Sneelvhpet_timer_interrupt(struct vhpet *vhpet, int n)
242258579Sneel{
243258579Sneel	int apicid, vector, vcpuid, pin;
244258579Sneel	cpuset_t dmask;
245258579Sneel
246258579Sneel	/* If interrupts are not enabled for this timer then just return. */
247258579Sneel	if (!vhpet_timer_interrupt_enabled(vhpet, n))
248258579Sneel		return;
249258579Sneel
250258579Sneel	/*
251258579Sneel	 * If a level triggered interrupt is already asserted then just return.
252258579Sneel	 */
253258579Sneel	if ((vhpet->isr & (1 << n)) != 0) {
254258579Sneel		VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n);
255258579Sneel		return;
256258579Sneel	}
257258579Sneel
258258579Sneel	if (vhpet_timer_msi_enabled(vhpet, n)) {
259258579Sneel		/*
260258579Sneel		 * XXX should have an API 'vlapic_deliver_msi(vm, addr, data)'
261258579Sneel		 * - assuming physical delivery mode
262258579Sneel		 * - no need to interpret contents of 'msireg' here
263258579Sneel		 */
264258579Sneel		vector = vhpet->timer[n].msireg & 0xff;
265258579Sneel		apicid = (vhpet->timer[n].msireg >> (32 + 12)) & 0xff;
266258579Sneel		if (apicid != 0xff) {
267258579Sneel			/* unicast */
268258579Sneel			vcpuid = vm_apicid2vcpuid(vhpet->vm, apicid);
269258579Sneel			lapic_set_intr(vhpet->vm, vcpuid, vector);
270258579Sneel		} else {
271258579Sneel			/* broadcast */
272258579Sneel			dmask = vm_active_cpus(vhpet->vm);
273258579Sneel			while ((vcpuid = CPU_FFS(&dmask)) != 0) {
274258579Sneel				vcpuid--;
275258579Sneel				CPU_CLR(vcpuid, &dmask);
276258579Sneel				lapic_set_intr(vhpet->vm, vcpuid, vector);
277258579Sneel			}
278258579Sneel		}
279258579Sneel		return;
280258579Sneel	}
281258579Sneel
282258579Sneel	pin = vhpet_timer_ioapic_pin(vhpet, n);
283258579Sneel	if (pin == 0) {
284258579Sneel		VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n);
285258579Sneel		return;
286258579Sneel	}
287258579Sneel
288258579Sneel	if (vhpet_timer_edge_trig(vhpet, n)) {
289258579Sneel		vioapic_pulse_irq(vhpet->vm, pin);
290258579Sneel	} else {
291258579Sneel		vhpet->isr |= 1 << n;
292258579Sneel		vioapic_assert_irq(vhpet->vm, pin);
293258579Sneel	}
294258579Sneel}
295258579Sneel
296258579Sneelstatic void
297258579Sneelvhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter)
298258579Sneel{
299258579Sneel	uint32_t compval, comprate, compnext;
300258579Sneel
301258579Sneel	KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n));
302258579Sneel
303258579Sneel	compval = vhpet->timer[n].compval;
304258579Sneel	comprate = vhpet->timer[n].comprate;
305258579Sneel
306258579Sneel	/*
307258579Sneel	 * Calculate the comparator value to be used for the next periodic
308258579Sneel	 * interrupt.
309258579Sneel	 *
310258579Sneel	 * This function is commonly called from the callout handler.
311258579Sneel	 * In this scenario the 'counter' is ahead of 'compval'. To find
312258579Sneel	 * the next value to program into the accumulator we divide the
313258579Sneel	 * number space between 'compval' and 'counter' into 'comprate'
314258579Sneel	 * sized units. The 'compval' is rounded up such that is "ahead"
315258579Sneel	 * of 'counter'.
316258579Sneel	 */
317258579Sneel	compnext = compval + ((counter - compval) / comprate + 1) * comprate;
318258579Sneel
319258579Sneel	vhpet->timer[n].compval = compnext;
320258579Sneel}
321258579Sneel
322258579Sneelstatic void
323258579Sneelvhpet_handler(void *a)
324258579Sneel{
325258579Sneel	int n;
326258579Sneel	uint32_t counter;
327258579Sneel	sbintime_t sbt;
328258579Sneel	struct vhpet *vhpet;
329258579Sneel	struct callout *callout;
330258579Sneel	struct vhpet_callout_arg *arg;
331258579Sneel
332258579Sneel	arg = a;
333258579Sneel	vhpet = arg->vhpet;
334258579Sneel	n = arg->timer_num;
335258579Sneel	callout = &vhpet->timer[n].callout;
336258579Sneel
337258579Sneel	VM_CTR1(vhpet->vm, "hpet t%d fired", n);
338258579Sneel
339258579Sneel	VHPET_LOCK(vhpet);
340258579Sneel
341258579Sneel	if (callout_pending(callout))		/* callout was reset */
342258579Sneel		goto done;
343258579Sneel
344258579Sneel	if (!callout_active(callout))		/* callout was stopped */
345258579Sneel		goto done;
346258579Sneel
347258579Sneel	callout_deactivate(callout);
348258579Sneel
349258579Sneel	if (!vhpet_counter_enabled(vhpet))
350258579Sneel		panic("vhpet(%p) callout with counter disabled", vhpet);
351258579Sneel
352258579Sneel	counter = vhpet_counter(vhpet, false);
353258579Sneel
354258579Sneel	/* Update the accumulator for periodic timers */
355258579Sneel	if (vhpet->timer[n].comprate != 0)
356258579Sneel		vhpet_adjust_compval(vhpet, n, counter);
357258579Sneel
358258579Sneel	sbt = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt;
359258579Sneel	callout_reset_sbt(callout, sbt, 0, vhpet_handler, arg, 0);
360258579Sneel	vhpet_timer_interrupt(vhpet, n);
361258579Sneeldone:
362258579Sneel	VHPET_UNLOCK(vhpet);
363258579Sneel	return;
364258579Sneel}
365258579Sneel
366258579Sneelstatic void
367258579Sneelvhpet_stop_timer(struct vhpet *vhpet, int n)
368258579Sneel{
369258579Sneel
370258579Sneel	callout_stop(&vhpet->timer[n].callout);
371258579Sneel	vhpet_timer_clear_isr(vhpet, n);
372258579Sneel}
373258579Sneel
374258579Sneelstatic void
375258579Sneelvhpet_start_timer(struct vhpet *vhpet, int n)
376258579Sneel{
377258579Sneel	uint32_t counter, delta, delta2;
378258579Sneel	sbintime_t sbt;
379258579Sneel
380258579Sneel	counter = vhpet_counter(vhpet, false);
381258579Sneel
382258579Sneel	if (vhpet->timer[n].comprate != 0)
383258579Sneel		vhpet_adjust_compval(vhpet, n, counter);
384258579Sneel
385258579Sneel	delta = vhpet->timer[n].compval - counter;
386258579Sneel
387258579Sneel	/*
388258579Sneel	 * In one-shot mode the guest will typically read the main counter
389258579Sneel	 * before programming the comparator. We can use this heuristic to
390258579Sneel	 * figure out whether the expiration time is in the past. If this
391258579Sneel	 * is the case we schedule the callout to fire immediately.
392258579Sneel	 */
393258579Sneel	if (!vhpet_periodic_timer(vhpet, n)) {
394258579Sneel		delta2 = vhpet->timer[n].compval - vhpet->counter;
395258579Sneel		if (delta > delta2) {
396258579Sneel			VM_CTR3(vhpet->vm, "hpet t%d comparator value is in "
397258579Sneel			    "the past: %u/%u/%u", counter,
398258579Sneel			    vhpet->timer[n].compval, vhpet->counter);
399258579Sneel			delta = 0;
400258579Sneel		}
401258579Sneel	}
402258579Sneel
403258579Sneel	sbt = delta * vhpet->freq_sbt;
404258579Sneel	callout_reset_sbt(&vhpet->timer[n].callout, sbt, 0, vhpet_handler,
405258579Sneel	    &vhpet->timer[n].arg, 0);
406258579Sneel}
407258579Sneel
408258579Sneelstatic void
409258579Sneelvhpet_start_counting(struct vhpet *vhpet)
410258579Sneel{
411258579Sneel	int i;
412258579Sneel
413258579Sneel	vhpet->counter_sbt = sbinuptime();
414258579Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++)
415258579Sneel		vhpet_start_timer(vhpet, i);
416258579Sneel}
417258579Sneel
418258579Sneelstatic void
419258579Sneelvhpet_stop_counting(struct vhpet *vhpet)
420258579Sneel{
421258579Sneel	int i;
422258579Sneel
423258579Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++)
424258579Sneel		vhpet_stop_timer(vhpet, i);
425258579Sneel}
426258579Sneel
427258579Sneelstatic __inline void
428258579Sneelupdate_register(uint64_t *regptr, uint64_t data, uint64_t mask)
429258579Sneel{
430258579Sneel
431258579Sneel	*regptr &= ~mask;
432258579Sneel	*regptr |= (data & mask);
433258579Sneel}
434258579Sneel
435258579Sneelstatic void
436258579Sneelvhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data,
437258579Sneel    uint64_t mask)
438258579Sneel{
439258579Sneel	bool clear_isr;
440258579Sneel	int old_pin, new_pin;
441258579Sneel	uint32_t allowed_irqs;
442258579Sneel	uint64_t oldval, newval;
443258579Sneel
444258579Sneel	if (vhpet_timer_msi_enabled(vhpet, n) ||
445258579Sneel	    vhpet_timer_edge_trig(vhpet, n)) {
446258579Sneel		if (vhpet->isr & (1 << n))
447258579Sneel			panic("vhpet timer %d isr should not be asserted", n);
448258579Sneel	}
449258579Sneel	old_pin = vhpet_timer_ioapic_pin(vhpet, n);
450258579Sneel	oldval = vhpet->timer[n].cap_config;
451258579Sneel
452258579Sneel	newval = oldval;
453258579Sneel	update_register(&newval, data, mask);
454258579Sneel	newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE);
455258579Sneel	newval |= oldval & HPET_TCAP_RO_MASK;
456258579Sneel
457258579Sneel	if (newval == oldval)
458258579Sneel		return;
459258579Sneel
460258579Sneel	vhpet->timer[n].cap_config = newval;
461258579Sneel	VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval);
462258579Sneel
463258579Sneel	/*
464258579Sneel	 * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field.
465258579Sneel	 * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set
466258579Sneel	 * it to the default value of 0.
467258579Sneel	 */
468258579Sneel	allowed_irqs = vhpet->timer[n].cap_config >> 32;
469258579Sneel	new_pin = vhpet_timer_ioapic_pin(vhpet, n);
470258579Sneel	if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) {
471258579Sneel		VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, "
472258579Sneel		    "allowed_irqs 0x%08x", n, new_pin, allowed_irqs);
473258579Sneel		new_pin = 0;
474258579Sneel		vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE;
475258579Sneel	}
476258579Sneel
477258579Sneel	if (!vhpet_periodic_timer(vhpet, n))
478258579Sneel		vhpet->timer[n].comprate = 0;
479258579Sneel
480258579Sneel	/*
481258579Sneel	 * If the timer's ISR bit is set then clear it in the following cases:
482258579Sneel	 * - interrupt is disabled
483258579Sneel	 * - interrupt type is changed from level to edge or fsb.
484258579Sneel	 * - interrupt routing is changed
485258579Sneel	 *
486258579Sneel	 * This is to ensure that this timer's level triggered interrupt does
487258579Sneel	 * not remain asserted forever.
488258579Sneel	 */
489258579Sneel	if (vhpet->isr & (1 << n)) {
490258579Sneel		KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d",
491258579Sneel		    n, old_pin));
492258579Sneel		if (!vhpet_timer_interrupt_enabled(vhpet, n))
493258579Sneel			clear_isr = true;
494258579Sneel		else if (vhpet_timer_msi_enabled(vhpet, n))
495258579Sneel			clear_isr = true;
496258579Sneel		else if (vhpet_timer_edge_trig(vhpet, n))
497258579Sneel			clear_isr = true;
498258579Sneel		else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin)
499258579Sneel			clear_isr = true;
500258579Sneel		else
501258579Sneel			clear_isr = false;
502258579Sneel
503258579Sneel		if (clear_isr) {
504258579Sneel			VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to "
505258579Sneel			    "configuration change", n);
506258579Sneel			vioapic_deassert_irq(vhpet->vm, old_pin);
507258579Sneel			vhpet->isr &= ~(1 << n);
508258579Sneel		}
509258579Sneel	}
510258579Sneel}
511258579Sneel
512258579Sneelint
513258579Sneelvhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size,
514258579Sneel    void *arg)
515258579Sneel{
516258579Sneel	struct vhpet *vhpet;
517258579Sneel	uint64_t data, mask, oldval, val64;
518258579Sneel	uint32_t isr_clear_mask, old_compval, old_comprate;
519258579Sneel	int i, offset;
520258579Sneel
521258579Sneel	vhpet = vm_hpet(vm);
522258579Sneel	offset = gpa - VHPET_BASE;
523258579Sneel
524258579Sneel	VHPET_LOCK(vhpet);
525258579Sneel
526258579Sneel	/* Accesses to the HPET should be 4 or 8 bytes wide */
527258579Sneel	switch (size) {
528258579Sneel	case 8:
529258579Sneel		mask = 0xffffffffffffffff;
530258579Sneel		data = val;
531258579Sneel		break;
532258579Sneel	case 4:
533258579Sneel		mask = 0xffffffff;
534258579Sneel		data = val;
535258579Sneel		if ((offset & 0x4) != 0) {
536258579Sneel			mask <<= 32;
537258579Sneel			data <<= 32;
538258579Sneel		}
539258579Sneel		break;
540258579Sneel	default:
541258579Sneel		VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
542258579Sneel		    "offset 0x%08x, size %d", offset, size);
543258579Sneel		goto done;
544258579Sneel	}
545258579Sneel
546258579Sneel	/* Access to the HPET should be naturally aligned to its width */
547258579Sneel	if (offset & (size - 1)) {
548258579Sneel		VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
549258579Sneel		    "offset 0x%08x, size %d", offset, size);
550258579Sneel		goto done;
551258579Sneel	}
552258579Sneel
553258579Sneel	if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
554258579Sneel		oldval = vhpet->config;
555258579Sneel		update_register(&vhpet->config, data, mask);
556258579Sneel		if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) {
557258579Sneel			if (vhpet_counter_enabled(vhpet)) {
558258579Sneel				vhpet_start_counting(vhpet);
559258579Sneel				VM_CTR0(vhpet->vm, "hpet enabled");
560258579Sneel			} else {
561258579Sneel				vhpet_stop_counting(vhpet);
562258579Sneel				VM_CTR0(vhpet->vm, "hpet disabled");
563258579Sneel			}
564258579Sneel		}
565258579Sneel		goto done;
566258579Sneel	}
567258579Sneel
568258579Sneel	if (offset == HPET_ISR || offset == HPET_ISR + 4) {
569258579Sneel		isr_clear_mask = vhpet->isr & data;
570258579Sneel		for (i = 0; i < VHPET_NUM_TIMERS; i++) {
571258579Sneel			if ((isr_clear_mask & (1 << i)) != 0) {
572258579Sneel				VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i);
573258579Sneel				vhpet_timer_clear_isr(vhpet, i);
574258579Sneel			}
575258579Sneel		}
576258579Sneel		goto done;
577258579Sneel	}
578258579Sneel
579258579Sneel	if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
580258579Sneel		/* Zero-extend the counter to 64-bits before updating it */
581258579Sneel		val64 = vhpet->counter;
582258579Sneel		update_register(&val64, data, mask);
583258579Sneel		vhpet->counter = val64;
584258579Sneel		if (vhpet_counter_enabled(vhpet))
585258579Sneel			vhpet_start_counting(vhpet);
586258579Sneel		goto done;
587258579Sneel	}
588258579Sneel
589258579Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
590258579Sneel		if (offset == HPET_TIMER_CAP_CNF(i) ||
591258579Sneel		    offset == HPET_TIMER_CAP_CNF(i) + 4) {
592258579Sneel			vhpet_timer_update_config(vhpet, i, data, mask);
593258579Sneel			break;
594258579Sneel		}
595258579Sneel
596258579Sneel		if (offset == HPET_TIMER_COMPARATOR(i) ||
597258579Sneel		    offset == HPET_TIMER_COMPARATOR(i) + 4) {
598258579Sneel			old_compval = vhpet->timer[i].compval;
599258579Sneel			old_comprate = vhpet->timer[i].comprate;
600258579Sneel			if (vhpet_periodic_timer(vhpet, i)) {
601258579Sneel				/*
602258579Sneel				 * In periodic mode writes to the comparator
603258579Sneel				 * change the 'compval' register only if the
604258579Sneel				 * HPET_TCNF_VAL_SET bit is set in the config
605258579Sneel				 * register.
606258579Sneel				 */
607258579Sneel				val64 = vhpet->timer[i].comprate;
608258579Sneel				update_register(&val64, data, mask);
609258579Sneel				vhpet->timer[i].comprate = val64;
610258579Sneel				if ((vhpet->timer[i].cap_config &
611258579Sneel				    HPET_TCNF_VAL_SET) != 0) {
612258579Sneel					vhpet->timer[i].compval = val64;
613258579Sneel				}
614258579Sneel			} else {
615258579Sneel				KASSERT(vhpet->timer[i].comprate == 0,
616258579Sneel				    ("vhpet one-shot timer %d has invalid "
617258579Sneel				    "rate %u", i, vhpet->timer[i].comprate));
618258579Sneel				val64 = vhpet->timer[i].compval;
619258579Sneel				update_register(&val64, data, mask);
620258579Sneel				vhpet->timer[i].compval = val64;
621258579Sneel			}
622258579Sneel			vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET;
623258579Sneel
624258579Sneel			if (vhpet->timer[i].compval != old_compval ||
625258579Sneel			    vhpet->timer[i].comprate != old_comprate) {
626258579Sneel				if (vhpet_counter_enabled(vhpet))
627258579Sneel					vhpet_start_timer(vhpet, i);
628258579Sneel			}
629258579Sneel			break;
630258579Sneel		}
631258579Sneel
632258579Sneel		if (offset == HPET_TIMER_FSB_VAL(i) ||
633258579Sneel		    offset == HPET_TIMER_FSB_ADDR(i)) {
634258579Sneel			update_register(&vhpet->timer[i].msireg, data, mask);
635258579Sneel			break;
636258579Sneel		}
637258579Sneel	}
638258579Sneeldone:
639258579Sneel	VHPET_UNLOCK(vhpet);
640258579Sneel	return (0);
641258579Sneel}
642258579Sneel
643258579Sneelint
644258579Sneelvhpet_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, int size,
645258579Sneel    void *arg)
646258579Sneel{
647258579Sneel	int i, offset;
648258579Sneel	struct vhpet *vhpet;
649258579Sneel	uint64_t data;
650258579Sneel
651258579Sneel	vhpet = vm_hpet(vm);
652258579Sneel	offset = gpa - VHPET_BASE;
653258579Sneel
654258579Sneel	VHPET_LOCK(vhpet);
655258579Sneel
656258579Sneel	/* Accesses to the HPET should be 4 or 8 bytes wide */
657258579Sneel	if (size != 4 && size != 8) {
658258579Sneel		VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
659258579Sneel		    "offset 0x%08x, size %d", offset, size);
660258579Sneel		data = 0;
661258579Sneel		goto done;
662258579Sneel	}
663258579Sneel
664258579Sneel	/* Access to the HPET should be naturally aligned to its width */
665258579Sneel	if (offset & (size - 1)) {
666258579Sneel		VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
667258579Sneel		    "offset 0x%08x, size %d", offset, size);
668258579Sneel		data = 0;
669258579Sneel		goto done;
670258579Sneel	}
671258579Sneel
672258579Sneel	if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) {
673258579Sneel		data = vhpet_capabilities();
674258579Sneel		goto done;
675258579Sneel	}
676258579Sneel
677258579Sneel	if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
678258579Sneel		data = vhpet->config;
679258579Sneel		goto done;
680258579Sneel	}
681258579Sneel
682258579Sneel	if (offset == HPET_ISR || offset == HPET_ISR + 4) {
683258579Sneel		data = vhpet->isr;
684258579Sneel		goto done;
685258579Sneel	}
686258579Sneel
687258579Sneel	if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
688258579Sneel		data = vhpet_counter(vhpet, true);
689258579Sneel		goto done;
690258579Sneel	}
691258579Sneel
692258579Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
693258579Sneel		if (offset == HPET_TIMER_CAP_CNF(i) ||
694258579Sneel		    offset == HPET_TIMER_CAP_CNF(i) + 4) {
695258579Sneel			data = vhpet->timer[i].cap_config;
696258579Sneel			break;
697258579Sneel		}
698258579Sneel
699258579Sneel		if (offset == HPET_TIMER_COMPARATOR(i) ||
700258579Sneel		    offset == HPET_TIMER_COMPARATOR(i) + 4) {
701258579Sneel			data = vhpet->timer[i].compval;
702258579Sneel			break;
703258579Sneel		}
704258579Sneel
705258579Sneel		if (offset == HPET_TIMER_FSB_VAL(i) ||
706258579Sneel		    offset == HPET_TIMER_FSB_ADDR(i)) {
707258579Sneel			data = vhpet->timer[i].msireg;
708258579Sneel			break;
709258579Sneel		}
710258579Sneel	}
711258579Sneel
712258579Sneel	if (i >= VHPET_NUM_TIMERS)
713258579Sneel		data = 0;
714258579Sneeldone:
715258579Sneel	VHPET_UNLOCK(vhpet);
716258579Sneel
717258579Sneel	if (size == 4) {
718258579Sneel		if (offset & 0x4)
719258579Sneel			data >>= 32;
720258579Sneel	}
721258579Sneel	*rval = data;
722258579Sneel	return (0);
723258579Sneel}
724258579Sneel
725258579Sneelstruct vhpet *
726258579Sneelvhpet_init(struct vm *vm)
727258579Sneel{
728258579Sneel	int i;
729258579Sneel	struct vhpet *vhpet;
730258579Sneel	struct vhpet_callout_arg *arg;
731258579Sneel	struct bintime bt;
732258579Sneel
733258579Sneel	vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO);
734258579Sneel        vhpet->vm = vm;
735258579Sneel	mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF);
736258579Sneel
737258579Sneel	FREQ2BT(HPET_FREQ, &bt);
738258579Sneel	vhpet->freq_sbt = bttosbt(bt);
739258579Sneel
740258579Sneel	/*
741258579Sneel	 * Initialize HPET timer hardware state.
742258579Sneel	 */
743258579Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
744258579Sneel		vhpet->timer[i].cap_config = 0UL << 32 |
745258579Sneel		    HPET_TCAP_FSB_INT_DEL | HPET_TCAP_PER_INT;
746258579Sneel		vhpet->timer[i].compval = 0xffffffff;
747258579Sneel		callout_init(&vhpet->timer[i].callout, 1);
748258579Sneel
749258579Sneel		arg = &vhpet->timer[i].arg;
750258579Sneel		arg->vhpet = vhpet;
751258579Sneel		arg->timer_num = i;
752258579Sneel	}
753258579Sneel
754258579Sneel	return (vhpet);
755258579Sneel}
756258579Sneel
757258579Sneelvoid
758258579Sneelvhpet_cleanup(struct vhpet *vhpet)
759258579Sneel{
760258579Sneel	int i;
761258579Sneel
762258579Sneel	for (i = 0; i < VHPET_NUM_TIMERS; i++)
763258579Sneel		callout_drain(&vhpet->timer[i].callout);
764258579Sneel
765258579Sneel	free(vhpet, M_VHPET);
766258579Sneel}
767258579Sneel
768258579Sneelint
769258579Sneelvhpet_getcap(struct vm_hpet_cap *cap)
770258579Sneel{
771258579Sneel
772258579Sneel	cap->capabilities = vhpet_capabilities();
773258579Sneel	return (0);
774258579Sneel}
775