vhpet.c revision 284900
1/*-
2 * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
3 * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: stable/10/sys/amd64/vmm/io/vhpet.c 284900 2015-06-28 03:22:26Z neel $
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: stable/10/sys/amd64/vmm/io/vhpet.c 284900 2015-06-28 03:22:26Z neel $");
32
33#include <sys/param.h>
34#include <sys/lock.h>
35#include <sys/mutex.h>
36#include <sys/kernel.h>
37#include <sys/malloc.h>
38#include <sys/systm.h>
39
40#include <dev/acpica/acpi_hpet.h>
41
42#include <machine/vmm.h>
43#include <machine/vmm_dev.h>
44
45#include "vmm_lapic.h"
46#include "vatpic.h"
47#include "vioapic.h"
48#include "vhpet.h"
49
50#include "vmm_ktr.h"
51
52static MALLOC_DEFINE(M_VHPET, "vhpet", "bhyve virtual hpet");
53
54#define	HPET_FREQ	10000000		/* 10.0 Mhz */
55#define	FS_PER_S	1000000000000000ul
56
57/* Timer N Configuration and Capabilities Register */
58#define	HPET_TCAP_RO_MASK	(HPET_TCAP_INT_ROUTE 	|		\
59				 HPET_TCAP_FSB_INT_DEL	|		\
60				 HPET_TCAP_SIZE		|		\
61				 HPET_TCAP_PER_INT)
62/*
63 * HPET requires at least 3 timers and up to 32 timers per block.
64 */
65#define	VHPET_NUM_TIMERS	8
66CTASSERT(VHPET_NUM_TIMERS >= 3 && VHPET_NUM_TIMERS <= 32);
67
68struct vhpet_callout_arg {
69	struct vhpet *vhpet;
70	int timer_num;
71};
72
73struct vhpet {
74	struct vm	*vm;
75	struct mtx	mtx;
76	sbintime_t	freq_sbt;
77
78	uint64_t	config;		/* Configuration */
79	uint64_t	isr;		/* Interrupt Status */
80	uint32_t	countbase;	/* HPET counter base value */
81	sbintime_t	countbase_sbt;	/* uptime corresponding to base value */
82
83	struct {
84		uint64_t	cap_config;	/* Configuration */
85		uint64_t	msireg;		/* FSB interrupt routing */
86		uint32_t	compval;	/* Comparator */
87		uint32_t	comprate;
88		struct callout	callout;
89		sbintime_t	callout_sbt;	/* time when counter==compval */
90		struct vhpet_callout_arg arg;
91	} timer[VHPET_NUM_TIMERS];
92};
93
94#define	VHPET_LOCK(vhp)		mtx_lock(&((vhp)->mtx))
95#define	VHPET_UNLOCK(vhp)	mtx_unlock(&((vhp)->mtx))
96
97static void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter,
98    sbintime_t now);
99
100static uint64_t
101vhpet_capabilities(void)
102{
103	uint64_t cap = 0;
104
105	cap |= 0x8086 << 16;			/* vendor id */
106	cap |= (VHPET_NUM_TIMERS - 1) << 8;	/* number of timers */
107	cap |= 1;				/* revision */
108	cap &= ~HPET_CAP_COUNT_SIZE;		/* 32-bit timer */
109
110	cap &= 0xffffffff;
111	cap |= (FS_PER_S / HPET_FREQ) << 32;	/* tick period in fs */
112
113	return (cap);
114}
115
116static __inline bool
117vhpet_counter_enabled(struct vhpet *vhpet)
118{
119
120	return ((vhpet->config & HPET_CNF_ENABLE) ? true : false);
121}
122
123static __inline bool
124vhpet_timer_msi_enabled(struct vhpet *vhpet, int n)
125{
126	const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN;
127
128	if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable)
129		return (true);
130	else
131		return (false);
132}
133
134static __inline int
135vhpet_timer_ioapic_pin(struct vhpet *vhpet, int n)
136{
137	/*
138	 * If the timer is configured to use MSI then treat it as if the
139	 * timer is not connected to the ioapic.
140	 */
141	if (vhpet_timer_msi_enabled(vhpet, n))
142		return (0);
143
144	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
145}
146
147static uint32_t
148vhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr)
149{
150	uint32_t val;
151	sbintime_t now, delta;
152
153	val = vhpet->countbase;
154	if (vhpet_counter_enabled(vhpet)) {
155		now = sbinuptime();
156		delta = now - vhpet->countbase_sbt;
157		KASSERT(delta >= 0, ("vhpet_counter: uptime went backwards: "
158		    "%#lx to %#lx", vhpet->countbase_sbt, now));
159		val += delta / vhpet->freq_sbt;
160		if (nowptr != NULL)
161			*nowptr = now;
162	} else {
163		/*
164		 * The sbinuptime corresponding to the 'countbase' is
165		 * meaningless when the counter is disabled. Make sure
166		 * that the the caller doesn't want to use it.
167		 */
168		KASSERT(nowptr == NULL, ("vhpet_counter: nowptr must be NULL"));
169	}
170	return (val);
171}
172
173static void
174vhpet_timer_clear_isr(struct vhpet *vhpet, int n)
175{
176	int pin;
177
178	if (vhpet->isr & (1 << n)) {
179		pin = vhpet_timer_ioapic_pin(vhpet, n);
180		KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n));
181		vioapic_deassert_irq(vhpet->vm, pin);
182		vhpet->isr &= ~(1 << n);
183	}
184}
185
186static __inline bool
187vhpet_periodic_timer(struct vhpet *vhpet, int n)
188{
189
190	return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0);
191}
192
193static __inline bool
194vhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n)
195{
196
197	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0);
198}
199
200static __inline bool
201vhpet_timer_edge_trig(struct vhpet *vhpet, int n)
202{
203
204	KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: "
205	    "timer %d is using MSI", n));
206
207	if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0)
208		return (true);
209	else
210		return (false);
211}
212
213static void
214vhpet_timer_interrupt(struct vhpet *vhpet, int n)
215{
216	int pin;
217
218	/* If interrupts are not enabled for this timer then just return. */
219	if (!vhpet_timer_interrupt_enabled(vhpet, n))
220		return;
221
222	/*
223	 * If a level triggered interrupt is already asserted then just return.
224	 */
225	if ((vhpet->isr & (1 << n)) != 0) {
226		VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n);
227		return;
228	}
229
230	if (vhpet_timer_msi_enabled(vhpet, n)) {
231		lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32,
232		    vhpet->timer[n].msireg & 0xffffffff);
233		return;
234	}
235
236	pin = vhpet_timer_ioapic_pin(vhpet, n);
237	if (pin == 0) {
238		VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n);
239		return;
240	}
241
242	if (vhpet_timer_edge_trig(vhpet, n)) {
243		vioapic_pulse_irq(vhpet->vm, pin);
244	} else {
245		vhpet->isr |= 1 << n;
246		vioapic_assert_irq(vhpet->vm, pin);
247	}
248}
249
250static void
251vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter)
252{
253	uint32_t compval, comprate, compnext;
254
255	KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n));
256
257	compval = vhpet->timer[n].compval;
258	comprate = vhpet->timer[n].comprate;
259
260	/*
261	 * Calculate the comparator value to be used for the next periodic
262	 * interrupt.
263	 *
264	 * This function is commonly called from the callout handler.
265	 * In this scenario the 'counter' is ahead of 'compval'. To find
266	 * the next value to program into the accumulator we divide the
267	 * number space between 'compval' and 'counter' into 'comprate'
268	 * sized units. The 'compval' is rounded up such that is "ahead"
269	 * of 'counter'.
270	 */
271	compnext = compval + ((counter - compval) / comprate + 1) * comprate;
272
273	vhpet->timer[n].compval = compnext;
274}
275
276static void
277vhpet_handler(void *a)
278{
279	int n;
280	uint32_t counter;
281	sbintime_t now;
282	struct vhpet *vhpet;
283	struct callout *callout;
284	struct vhpet_callout_arg *arg;
285
286	arg = a;
287	vhpet = arg->vhpet;
288	n = arg->timer_num;
289	callout = &vhpet->timer[n].callout;
290
291	VM_CTR1(vhpet->vm, "hpet t%d fired", n);
292
293	VHPET_LOCK(vhpet);
294
295	if (callout_pending(callout))		/* callout was reset */
296		goto done;
297
298	if (!callout_active(callout))		/* callout was stopped */
299		goto done;
300
301	callout_deactivate(callout);
302
303	if (!vhpet_counter_enabled(vhpet))
304		panic("vhpet(%p) callout with counter disabled", vhpet);
305
306	counter = vhpet_counter(vhpet, &now);
307	vhpet_start_timer(vhpet, n, counter, now);
308	vhpet_timer_interrupt(vhpet, n);
309done:
310	VHPET_UNLOCK(vhpet);
311	return;
312}
313
314static void
315vhpet_stop_timer(struct vhpet *vhpet, int n, sbintime_t now)
316{
317
318	VM_CTR1(vhpet->vm, "hpet t%d stopped", n);
319	callout_stop(&vhpet->timer[n].callout);
320
321	/*
322	 * If the callout was scheduled to expire in the past but hasn't
323	 * had a chance to execute yet then trigger the timer interrupt
324	 * here. Failing to do so will result in a missed timer interrupt
325	 * in the guest. This is especially bad in one-shot mode because
326	 * the next interrupt has to wait for the counter to wrap around.
327	 */
328	if (vhpet->timer[n].callout_sbt < now) {
329		VM_CTR1(vhpet->vm, "hpet t%d interrupt triggered after "
330		    "stopping timer", n);
331		vhpet_timer_interrupt(vhpet, n);
332	}
333}
334
335static void
336vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, sbintime_t now)
337{
338	sbintime_t delta, precision;
339
340	if (vhpet->timer[n].comprate != 0)
341		vhpet_adjust_compval(vhpet, n, counter);
342	else {
343		/*
344		 * In one-shot mode it is the guest's responsibility to make
345		 * sure that the comparator value is not in the "past". The
346		 * hardware doesn't have any belt-and-suspenders to deal with
347		 * this so we don't either.
348		 */
349	}
350
351	delta = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt;
352	precision = delta >> tc_precexp;
353	vhpet->timer[n].callout_sbt = now + delta;
354	callout_reset_sbt(&vhpet->timer[n].callout, vhpet->timer[n].callout_sbt,
355	    precision, vhpet_handler, &vhpet->timer[n].arg, C_ABSOLUTE);
356}
357
358static void
359vhpet_start_counting(struct vhpet *vhpet)
360{
361	int i;
362
363	vhpet->countbase_sbt = sbinuptime();
364	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
365		/*
366		 * Restart the timers based on the value of the main counter
367		 * when it stopped counting.
368		 */
369		vhpet_start_timer(vhpet, i, vhpet->countbase,
370		    vhpet->countbase_sbt);
371	}
372}
373
374static void
375vhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, sbintime_t now)
376{
377	int i;
378
379	vhpet->countbase = counter;
380	for (i = 0; i < VHPET_NUM_TIMERS; i++)
381		vhpet_stop_timer(vhpet, i, now);
382}
383
384static __inline void
385update_register(uint64_t *regptr, uint64_t data, uint64_t mask)
386{
387
388	*regptr &= ~mask;
389	*regptr |= (data & mask);
390}
391
392static void
393vhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data,
394    uint64_t mask)
395{
396	bool clear_isr;
397	int old_pin, new_pin;
398	uint32_t allowed_irqs;
399	uint64_t oldval, newval;
400
401	if (vhpet_timer_msi_enabled(vhpet, n) ||
402	    vhpet_timer_edge_trig(vhpet, n)) {
403		if (vhpet->isr & (1 << n))
404			panic("vhpet timer %d isr should not be asserted", n);
405	}
406	old_pin = vhpet_timer_ioapic_pin(vhpet, n);
407	oldval = vhpet->timer[n].cap_config;
408
409	newval = oldval;
410	update_register(&newval, data, mask);
411	newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE);
412	newval |= oldval & HPET_TCAP_RO_MASK;
413
414	if (newval == oldval)
415		return;
416
417	vhpet->timer[n].cap_config = newval;
418	VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval);
419
420	/*
421	 * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field.
422	 * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set
423	 * it to the default value of 0.
424	 */
425	allowed_irqs = vhpet->timer[n].cap_config >> 32;
426	new_pin = vhpet_timer_ioapic_pin(vhpet, n);
427	if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) {
428		VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, "
429		    "allowed_irqs 0x%08x", n, new_pin, allowed_irqs);
430		new_pin = 0;
431		vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE;
432	}
433
434	if (!vhpet_periodic_timer(vhpet, n))
435		vhpet->timer[n].comprate = 0;
436
437	/*
438	 * If the timer's ISR bit is set then clear it in the following cases:
439	 * - interrupt is disabled
440	 * - interrupt type is changed from level to edge or fsb.
441	 * - interrupt routing is changed
442	 *
443	 * This is to ensure that this timer's level triggered interrupt does
444	 * not remain asserted forever.
445	 */
446	if (vhpet->isr & (1 << n)) {
447		KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d",
448		    n, old_pin));
449		if (!vhpet_timer_interrupt_enabled(vhpet, n))
450			clear_isr = true;
451		else if (vhpet_timer_msi_enabled(vhpet, n))
452			clear_isr = true;
453		else if (vhpet_timer_edge_trig(vhpet, n))
454			clear_isr = true;
455		else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin)
456			clear_isr = true;
457		else
458			clear_isr = false;
459
460		if (clear_isr) {
461			VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to "
462			    "configuration change", n);
463			vioapic_deassert_irq(vhpet->vm, old_pin);
464			vhpet->isr &= ~(1 << n);
465		}
466	}
467}
468
469int
470vhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size,
471    void *arg)
472{
473	struct vhpet *vhpet;
474	uint64_t data, mask, oldval, val64;
475	uint32_t isr_clear_mask, old_compval, old_comprate, counter;
476	sbintime_t now, *nowptr;
477	int i, offset;
478
479	vhpet = vm_hpet(vm);
480	offset = gpa - VHPET_BASE;
481
482	VHPET_LOCK(vhpet);
483
484	/* Accesses to the HPET should be 4 or 8 bytes wide */
485	switch (size) {
486	case 8:
487		mask = 0xffffffffffffffff;
488		data = val;
489		break;
490	case 4:
491		mask = 0xffffffff;
492		data = val;
493		if ((offset & 0x4) != 0) {
494			mask <<= 32;
495			data <<= 32;
496		}
497		break;
498	default:
499		VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
500		    "offset 0x%08x, size %d", offset, size);
501		goto done;
502	}
503
504	/* Access to the HPET should be naturally aligned to its width */
505	if (offset & (size - 1)) {
506		VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
507		    "offset 0x%08x, size %d", offset, size);
508		goto done;
509	}
510
511	if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
512		/*
513		 * Get the most recent value of the counter before updating
514		 * the 'config' register. If the HPET is going to be disabled
515		 * then we need to update 'countbase' with the value right
516		 * before it is disabled.
517		 */
518		nowptr = vhpet_counter_enabled(vhpet) ? &now : NULL;
519		counter = vhpet_counter(vhpet, nowptr);
520		oldval = vhpet->config;
521		update_register(&vhpet->config, data, mask);
522
523		/*
524		 * LegacyReplacement Routing is not supported so clear the
525		 * bit explicitly.
526		 */
527		vhpet->config &= ~HPET_CNF_LEG_RT;
528
529		if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) {
530			if (vhpet_counter_enabled(vhpet)) {
531				vhpet_start_counting(vhpet);
532				VM_CTR0(vhpet->vm, "hpet enabled");
533			} else {
534				vhpet_stop_counting(vhpet, counter, now);
535				VM_CTR0(vhpet->vm, "hpet disabled");
536			}
537		}
538		goto done;
539	}
540
541	if (offset == HPET_ISR || offset == HPET_ISR + 4) {
542		isr_clear_mask = vhpet->isr & data;
543		for (i = 0; i < VHPET_NUM_TIMERS; i++) {
544			if ((isr_clear_mask & (1 << i)) != 0) {
545				VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i);
546				vhpet_timer_clear_isr(vhpet, i);
547			}
548		}
549		goto done;
550	}
551
552	if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
553		/* Zero-extend the counter to 64-bits before updating it */
554		val64 = vhpet_counter(vhpet, NULL);
555		update_register(&val64, data, mask);
556		vhpet->countbase = val64;
557		if (vhpet_counter_enabled(vhpet))
558			vhpet_start_counting(vhpet);
559		goto done;
560	}
561
562	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
563		if (offset == HPET_TIMER_CAP_CNF(i) ||
564		    offset == HPET_TIMER_CAP_CNF(i) + 4) {
565			vhpet_timer_update_config(vhpet, i, data, mask);
566			break;
567		}
568
569		if (offset == HPET_TIMER_COMPARATOR(i) ||
570		    offset == HPET_TIMER_COMPARATOR(i) + 4) {
571			old_compval = vhpet->timer[i].compval;
572			old_comprate = vhpet->timer[i].comprate;
573			if (vhpet_periodic_timer(vhpet, i)) {
574				/*
575				 * In periodic mode writes to the comparator
576				 * change the 'compval' register only if the
577				 * HPET_TCNF_VAL_SET bit is set in the config
578				 * register.
579				 */
580				val64 = vhpet->timer[i].comprate;
581				update_register(&val64, data, mask);
582				vhpet->timer[i].comprate = val64;
583				if ((vhpet->timer[i].cap_config &
584				    HPET_TCNF_VAL_SET) != 0) {
585					vhpet->timer[i].compval = val64;
586				}
587			} else {
588				KASSERT(vhpet->timer[i].comprate == 0,
589				    ("vhpet one-shot timer %d has invalid "
590				    "rate %u", i, vhpet->timer[i].comprate));
591				val64 = vhpet->timer[i].compval;
592				update_register(&val64, data, mask);
593				vhpet->timer[i].compval = val64;
594			}
595			vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET;
596
597			if (vhpet->timer[i].compval != old_compval ||
598			    vhpet->timer[i].comprate != old_comprate) {
599				if (vhpet_counter_enabled(vhpet)) {
600					counter = vhpet_counter(vhpet, &now);
601					vhpet_start_timer(vhpet, i, counter,
602					    now);
603				}
604			}
605			break;
606		}
607
608		if (offset == HPET_TIMER_FSB_VAL(i) ||
609		    offset == HPET_TIMER_FSB_ADDR(i)) {
610			update_register(&vhpet->timer[i].msireg, data, mask);
611			break;
612		}
613	}
614done:
615	VHPET_UNLOCK(vhpet);
616	return (0);
617}
618
619int
620vhpet_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, int size,
621    void *arg)
622{
623	int i, offset;
624	struct vhpet *vhpet;
625	uint64_t data;
626
627	vhpet = vm_hpet(vm);
628	offset = gpa - VHPET_BASE;
629
630	VHPET_LOCK(vhpet);
631
632	/* Accesses to the HPET should be 4 or 8 bytes wide */
633	if (size != 4 && size != 8) {
634		VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
635		    "offset 0x%08x, size %d", offset, size);
636		data = 0;
637		goto done;
638	}
639
640	/* Access to the HPET should be naturally aligned to its width */
641	if (offset & (size - 1)) {
642		VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
643		    "offset 0x%08x, size %d", offset, size);
644		data = 0;
645		goto done;
646	}
647
648	if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) {
649		data = vhpet_capabilities();
650		goto done;
651	}
652
653	if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
654		data = vhpet->config;
655		goto done;
656	}
657
658	if (offset == HPET_ISR || offset == HPET_ISR + 4) {
659		data = vhpet->isr;
660		goto done;
661	}
662
663	if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
664		data = vhpet_counter(vhpet, NULL);
665		goto done;
666	}
667
668	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
669		if (offset == HPET_TIMER_CAP_CNF(i) ||
670		    offset == HPET_TIMER_CAP_CNF(i) + 4) {
671			data = vhpet->timer[i].cap_config;
672			break;
673		}
674
675		if (offset == HPET_TIMER_COMPARATOR(i) ||
676		    offset == HPET_TIMER_COMPARATOR(i) + 4) {
677			data = vhpet->timer[i].compval;
678			break;
679		}
680
681		if (offset == HPET_TIMER_FSB_VAL(i) ||
682		    offset == HPET_TIMER_FSB_ADDR(i)) {
683			data = vhpet->timer[i].msireg;
684			break;
685		}
686	}
687
688	if (i >= VHPET_NUM_TIMERS)
689		data = 0;
690done:
691	VHPET_UNLOCK(vhpet);
692
693	if (size == 4) {
694		if (offset & 0x4)
695			data >>= 32;
696	}
697	*rval = data;
698	return (0);
699}
700
701struct vhpet *
702vhpet_init(struct vm *vm)
703{
704	int i, pincount;
705	struct vhpet *vhpet;
706	uint64_t allowed_irqs;
707	struct vhpet_callout_arg *arg;
708	struct bintime bt;
709
710	vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO);
711        vhpet->vm = vm;
712	mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF);
713
714	FREQ2BT(HPET_FREQ, &bt);
715	vhpet->freq_sbt = bttosbt(bt);
716
717	pincount = vioapic_pincount(vm);
718	if (pincount >= 24)
719		allowed_irqs = 0x00f00000;	/* irqs 20, 21, 22 and 23 */
720	else
721		allowed_irqs = 0;
722
723	/*
724	 * Initialize HPET timer hardware state.
725	 */
726	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
727		vhpet->timer[i].cap_config = allowed_irqs << 32;
728		vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT;
729		vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL;
730
731		vhpet->timer[i].compval = 0xffffffff;
732		callout_init(&vhpet->timer[i].callout, 1);
733
734		arg = &vhpet->timer[i].arg;
735		arg->vhpet = vhpet;
736		arg->timer_num = i;
737	}
738
739	return (vhpet);
740}
741
742void
743vhpet_cleanup(struct vhpet *vhpet)
744{
745	int i;
746
747	for (i = 0; i < VHPET_NUM_TIMERS; i++)
748		callout_drain(&vhpet->timer[i].callout);
749
750	free(vhpet, M_VHPET);
751}
752
753int
754vhpet_getcap(struct vm_hpet_cap *cap)
755{
756
757	cap->capabilities = vhpet_capabilities();
758	return (0);
759}
760