vhpet.c revision 258699
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: head/sys/amd64/vmm/io/vhpet.c 258699 2013-11-27 22:18:08Z neel $
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/amd64/vmm/io/vhpet.c 258699 2013-11-27 22:18:08Z 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#include <sys/cpuset.h>
40
41#include <dev/acpica/acpi_hpet.h>
42
43#include <machine/vmm.h>
44#include <machine/vmm_dev.h>
45
46#include "vmm_lapic.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	counter;	/* HPET Counter */
81	sbintime_t	counter_sbt;
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		struct vhpet_callout_arg arg;
90	} timer[VHPET_NUM_TIMERS];
91};
92
93#define	VHPET_LOCK(vhp)		mtx_lock(&((vhp)->mtx))
94#define	VHPET_UNLOCK(vhp)	mtx_unlock(&((vhp)->mtx))
95
96static uint64_t
97vhpet_capabilities(void)
98{
99	uint64_t cap = 0;
100
101	cap |= 0x8086 << 16;			/* vendor id */
102	cap |= HPET_CAP_LEG_RT;			/* legacy routing capable */
103	cap |= (VHPET_NUM_TIMERS - 1) << 8;	/* number of timers */
104	cap |= 1;				/* revision */
105	cap &= ~HPET_CAP_COUNT_SIZE;		/* 32-bit timer */
106
107	cap &= 0xffffffff;
108	cap |= (FS_PER_S / HPET_FREQ) << 32;	/* tick period in fs */
109
110	return (cap);
111}
112
113static __inline bool
114vhpet_counter_enabled(struct vhpet *vhpet)
115{
116
117	return ((vhpet->config & HPET_CNF_ENABLE) ? true : false);
118}
119
120static __inline bool
121vhpet_timer_msi_enabled(struct vhpet *vhpet, int n)
122{
123	const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN;
124
125	/*
126	 * LegacyReplacement Route configuration takes precedence over MSI
127	 * for timers 0 and 1.
128	 */
129	if (n == 0 || n == 1) {
130		if (vhpet->config & HPET_CNF_LEG_RT)
131			return (false);
132	}
133
134	if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable)
135		return (true);
136	else
137		return (false);
138}
139
140static __inline int
141vhpet_timer_ioapic_pin(struct vhpet *vhpet, int n)
142{
143	/*
144	 * If the timer is configured to use MSI then treat it as if the
145	 * timer is not connected to the ioapic.
146	 */
147	if (vhpet_timer_msi_enabled(vhpet, n))
148		return (0);
149
150	if (vhpet->config & HPET_CNF_LEG_RT) {
151		/*
152		 * In "legacy routing" timers 0 and 1 are connected to
153		 * ioapic pins 2 and 8 respectively.
154		 */
155		switch (n) {
156		case 0:
157			return (2);
158		case 1:
159			return (8);
160		}
161	}
162
163	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
164}
165
166static uint32_t
167vhpet_counter(struct vhpet *vhpet, bool latch)
168{
169	uint32_t val;
170	sbintime_t cur_sbt, delta_sbt;
171
172	val = vhpet->counter;
173	if (vhpet_counter_enabled(vhpet)) {
174		cur_sbt = sbinuptime();
175		delta_sbt = cur_sbt - vhpet->counter_sbt;
176		KASSERT(delta_sbt >= 0,
177		    ("vhpet counter went backwards: %#lx to %#lx",
178		    vhpet->counter_sbt, cur_sbt));
179		val += delta_sbt / vhpet->freq_sbt;
180
181		/*
182		 * Keep track of the last value of the main counter that
183		 * was read by the guest.
184		 */
185		if (latch) {
186			vhpet->counter = val;
187			vhpet->counter_sbt = cur_sbt;
188		}
189	}
190
191	return (val);
192}
193
194static void
195vhpet_timer_clear_isr(struct vhpet *vhpet, int n)
196{
197	int pin;
198
199	if (vhpet->isr & (1 << n)) {
200		pin = vhpet_timer_ioapic_pin(vhpet, n);
201		KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n));
202		vioapic_deassert_irq(vhpet->vm, pin);
203		vhpet->isr &= ~(1 << n);
204	}
205}
206
207static __inline bool
208vhpet_periodic_timer(struct vhpet *vhpet, int n)
209{
210
211	return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0);
212}
213
214static __inline bool
215vhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n)
216{
217
218	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0);
219}
220
221static __inline bool
222vhpet_timer_edge_trig(struct vhpet *vhpet, int n)
223{
224
225	KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: "
226	    "timer %d is using MSI", n));
227
228	/* The legacy replacement interrupts are always edge triggered */
229	if (vhpet->config & HPET_CNF_LEG_RT) {
230		if (n == 0 || n == 1)
231			return (true);
232	}
233
234	if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0)
235		return (true);
236	else
237		return (false);
238}
239
240static void
241vhpet_timer_interrupt(struct vhpet *vhpet, int n)
242{
243	int apicid, vector, vcpuid, pin;
244	cpuset_t dmask;
245
246	/* If interrupts are not enabled for this timer then just return. */
247	if (!vhpet_timer_interrupt_enabled(vhpet, n))
248		return;
249
250	/*
251	 * If a level triggered interrupt is already asserted then just return.
252	 */
253	if ((vhpet->isr & (1 << n)) != 0) {
254		VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n);
255		return;
256	}
257
258	if (vhpet_timer_msi_enabled(vhpet, n)) {
259		/*
260		 * XXX should have an API 'vlapic_deliver_msi(vm, addr, data)'
261		 * - assuming physical delivery mode
262		 * - no need to interpret contents of 'msireg' here
263		 */
264		vector = vhpet->timer[n].msireg & 0xff;
265		apicid = (vhpet->timer[n].msireg >> (32 + 12)) & 0xff;
266		if (apicid != 0xff) {
267			/* unicast */
268			vcpuid = vm_apicid2vcpuid(vhpet->vm, apicid);
269			lapic_intr_edge(vhpet->vm, vcpuid, vector);
270		} else {
271			/* broadcast */
272			dmask = vm_active_cpus(vhpet->vm);
273			while ((vcpuid = CPU_FFS(&dmask)) != 0) {
274				vcpuid--;
275				CPU_CLR(vcpuid, &dmask);
276				lapic_intr_edge(vhpet->vm, vcpuid, vector);
277			}
278		}
279		return;
280	}
281
282	pin = vhpet_timer_ioapic_pin(vhpet, n);
283	if (pin == 0) {
284		VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n);
285		return;
286	}
287
288	if (vhpet_timer_edge_trig(vhpet, n)) {
289		vioapic_pulse_irq(vhpet->vm, pin);
290	} else {
291		vhpet->isr |= 1 << n;
292		vioapic_assert_irq(vhpet->vm, pin);
293	}
294}
295
296static void
297vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter)
298{
299	uint32_t compval, comprate, compnext;
300
301	KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n));
302
303	compval = vhpet->timer[n].compval;
304	comprate = vhpet->timer[n].comprate;
305
306	/*
307	 * Calculate the comparator value to be used for the next periodic
308	 * interrupt.
309	 *
310	 * This function is commonly called from the callout handler.
311	 * In this scenario the 'counter' is ahead of 'compval'. To find
312	 * the next value to program into the accumulator we divide the
313	 * number space between 'compval' and 'counter' into 'comprate'
314	 * sized units. The 'compval' is rounded up such that is "ahead"
315	 * of 'counter'.
316	 */
317	compnext = compval + ((counter - compval) / comprate + 1) * comprate;
318
319	vhpet->timer[n].compval = compnext;
320}
321
322static void
323vhpet_handler(void *a)
324{
325	int n;
326	uint32_t counter;
327	sbintime_t sbt;
328	struct vhpet *vhpet;
329	struct callout *callout;
330	struct vhpet_callout_arg *arg;
331
332	arg = a;
333	vhpet = arg->vhpet;
334	n = arg->timer_num;
335	callout = &vhpet->timer[n].callout;
336
337	VM_CTR1(vhpet->vm, "hpet t%d fired", n);
338
339	VHPET_LOCK(vhpet);
340
341	if (callout_pending(callout))		/* callout was reset */
342		goto done;
343
344	if (!callout_active(callout))		/* callout was stopped */
345		goto done;
346
347	callout_deactivate(callout);
348
349	if (!vhpet_counter_enabled(vhpet))
350		panic("vhpet(%p) callout with counter disabled", vhpet);
351
352	counter = vhpet_counter(vhpet, false);
353
354	/* Update the accumulator for periodic timers */
355	if (vhpet->timer[n].comprate != 0)
356		vhpet_adjust_compval(vhpet, n, counter);
357
358	sbt = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt;
359	callout_reset_sbt(callout, sbt, 0, vhpet_handler, arg, 0);
360	vhpet_timer_interrupt(vhpet, n);
361done:
362	VHPET_UNLOCK(vhpet);
363	return;
364}
365
366static void
367vhpet_stop_timer(struct vhpet *vhpet, int n)
368{
369
370	callout_stop(&vhpet->timer[n].callout);
371	vhpet_timer_clear_isr(vhpet, n);
372}
373
374static void
375vhpet_start_timer(struct vhpet *vhpet, int n)
376{
377	uint32_t counter, delta, delta2;
378	sbintime_t sbt;
379
380	counter = vhpet_counter(vhpet, false);
381
382	if (vhpet->timer[n].comprate != 0)
383		vhpet_adjust_compval(vhpet, n, counter);
384
385	delta = vhpet->timer[n].compval - counter;
386
387	/*
388	 * In one-shot mode the guest will typically read the main counter
389	 * before programming the comparator. We can use this heuristic to
390	 * figure out whether the expiration time is in the past. If this
391	 * is the case we schedule the callout to fire immediately.
392	 */
393	if (!vhpet_periodic_timer(vhpet, n)) {
394		delta2 = vhpet->timer[n].compval - vhpet->counter;
395		if (delta > delta2) {
396			VM_CTR3(vhpet->vm, "hpet t%d comparator value is in "
397			    "the past: %u/%u/%u", counter,
398			    vhpet->timer[n].compval, vhpet->counter);
399			delta = 0;
400		}
401	}
402
403	sbt = delta * vhpet->freq_sbt;
404	callout_reset_sbt(&vhpet->timer[n].callout, sbt, 0, vhpet_handler,
405	    &vhpet->timer[n].arg, 0);
406}
407
408static void
409vhpet_start_counting(struct vhpet *vhpet)
410{
411	int i;
412
413	vhpet->counter_sbt = sbinuptime();
414	for (i = 0; i < VHPET_NUM_TIMERS; i++)
415		vhpet_start_timer(vhpet, i);
416}
417
418static void
419vhpet_stop_counting(struct vhpet *vhpet)
420{
421	int i;
422
423	for (i = 0; i < VHPET_NUM_TIMERS; i++)
424		vhpet_stop_timer(vhpet, i);
425}
426
427static __inline void
428update_register(uint64_t *regptr, uint64_t data, uint64_t mask)
429{
430
431	*regptr &= ~mask;
432	*regptr |= (data & mask);
433}
434
435static void
436vhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data,
437    uint64_t mask)
438{
439	bool clear_isr;
440	int old_pin, new_pin;
441	uint32_t allowed_irqs;
442	uint64_t oldval, newval;
443
444	if (vhpet_timer_msi_enabled(vhpet, n) ||
445	    vhpet_timer_edge_trig(vhpet, n)) {
446		if (vhpet->isr & (1 << n))
447			panic("vhpet timer %d isr should not be asserted", n);
448	}
449	old_pin = vhpet_timer_ioapic_pin(vhpet, n);
450	oldval = vhpet->timer[n].cap_config;
451
452	newval = oldval;
453	update_register(&newval, data, mask);
454	newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE);
455	newval |= oldval & HPET_TCAP_RO_MASK;
456
457	if (newval == oldval)
458		return;
459
460	vhpet->timer[n].cap_config = newval;
461	VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval);
462
463	/*
464	 * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field.
465	 * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set
466	 * it to the default value of 0.
467	 */
468	allowed_irqs = vhpet->timer[n].cap_config >> 32;
469	new_pin = vhpet_timer_ioapic_pin(vhpet, n);
470	if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) {
471		VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, "
472		    "allowed_irqs 0x%08x", n, new_pin, allowed_irqs);
473		new_pin = 0;
474		vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE;
475	}
476
477	if (!vhpet_periodic_timer(vhpet, n))
478		vhpet->timer[n].comprate = 0;
479
480	/*
481	 * If the timer's ISR bit is set then clear it in the following cases:
482	 * - interrupt is disabled
483	 * - interrupt type is changed from level to edge or fsb.
484	 * - interrupt routing is changed
485	 *
486	 * This is to ensure that this timer's level triggered interrupt does
487	 * not remain asserted forever.
488	 */
489	if (vhpet->isr & (1 << n)) {
490		KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d",
491		    n, old_pin));
492		if (!vhpet_timer_interrupt_enabled(vhpet, n))
493			clear_isr = true;
494		else if (vhpet_timer_msi_enabled(vhpet, n))
495			clear_isr = true;
496		else if (vhpet_timer_edge_trig(vhpet, n))
497			clear_isr = true;
498		else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin)
499			clear_isr = true;
500		else
501			clear_isr = false;
502
503		if (clear_isr) {
504			VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to "
505			    "configuration change", n);
506			vioapic_deassert_irq(vhpet->vm, old_pin);
507			vhpet->isr &= ~(1 << n);
508		}
509	}
510}
511
512int
513vhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size,
514    void *arg)
515{
516	struct vhpet *vhpet;
517	uint64_t data, mask, oldval, val64;
518	uint32_t isr_clear_mask, old_compval, old_comprate;
519	int i, offset;
520
521	vhpet = vm_hpet(vm);
522	offset = gpa - VHPET_BASE;
523
524	VHPET_LOCK(vhpet);
525
526	/* Accesses to the HPET should be 4 or 8 bytes wide */
527	switch (size) {
528	case 8:
529		mask = 0xffffffffffffffff;
530		data = val;
531		break;
532	case 4:
533		mask = 0xffffffff;
534		data = val;
535		if ((offset & 0x4) != 0) {
536			mask <<= 32;
537			data <<= 32;
538		}
539		break;
540	default:
541		VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
542		    "offset 0x%08x, size %d", offset, size);
543		goto done;
544	}
545
546	/* Access to the HPET should be naturally aligned to its width */
547	if (offset & (size - 1)) {
548		VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
549		    "offset 0x%08x, size %d", offset, size);
550		goto done;
551	}
552
553	if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
554		oldval = vhpet->config;
555		update_register(&vhpet->config, data, mask);
556		if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) {
557			if (vhpet_counter_enabled(vhpet)) {
558				vhpet_start_counting(vhpet);
559				VM_CTR0(vhpet->vm, "hpet enabled");
560			} else {
561				vhpet_stop_counting(vhpet);
562				VM_CTR0(vhpet->vm, "hpet disabled");
563			}
564		}
565		goto done;
566	}
567
568	if (offset == HPET_ISR || offset == HPET_ISR + 4) {
569		isr_clear_mask = vhpet->isr & data;
570		for (i = 0; i < VHPET_NUM_TIMERS; i++) {
571			if ((isr_clear_mask & (1 << i)) != 0) {
572				VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i);
573				vhpet_timer_clear_isr(vhpet, i);
574			}
575		}
576		goto done;
577	}
578
579	if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
580		/* Zero-extend the counter to 64-bits before updating it */
581		val64 = vhpet->counter;
582		update_register(&val64, data, mask);
583		vhpet->counter = val64;
584		if (vhpet_counter_enabled(vhpet))
585			vhpet_start_counting(vhpet);
586		goto done;
587	}
588
589	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
590		if (offset == HPET_TIMER_CAP_CNF(i) ||
591		    offset == HPET_TIMER_CAP_CNF(i) + 4) {
592			vhpet_timer_update_config(vhpet, i, data, mask);
593			break;
594		}
595
596		if (offset == HPET_TIMER_COMPARATOR(i) ||
597		    offset == HPET_TIMER_COMPARATOR(i) + 4) {
598			old_compval = vhpet->timer[i].compval;
599			old_comprate = vhpet->timer[i].comprate;
600			if (vhpet_periodic_timer(vhpet, i)) {
601				/*
602				 * In periodic mode writes to the comparator
603				 * change the 'compval' register only if the
604				 * HPET_TCNF_VAL_SET bit is set in the config
605				 * register.
606				 */
607				val64 = vhpet->timer[i].comprate;
608				update_register(&val64, data, mask);
609				vhpet->timer[i].comprate = val64;
610				if ((vhpet->timer[i].cap_config &
611				    HPET_TCNF_VAL_SET) != 0) {
612					vhpet->timer[i].compval = val64;
613				}
614			} else {
615				KASSERT(vhpet->timer[i].comprate == 0,
616				    ("vhpet one-shot timer %d has invalid "
617				    "rate %u", i, vhpet->timer[i].comprate));
618				val64 = vhpet->timer[i].compval;
619				update_register(&val64, data, mask);
620				vhpet->timer[i].compval = val64;
621			}
622			vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET;
623
624			if (vhpet->timer[i].compval != old_compval ||
625			    vhpet->timer[i].comprate != old_comprate) {
626				if (vhpet_counter_enabled(vhpet))
627					vhpet_start_timer(vhpet, i);
628			}
629			break;
630		}
631
632		if (offset == HPET_TIMER_FSB_VAL(i) ||
633		    offset == HPET_TIMER_FSB_ADDR(i)) {
634			update_register(&vhpet->timer[i].msireg, data, mask);
635			break;
636		}
637	}
638done:
639	VHPET_UNLOCK(vhpet);
640	return (0);
641}
642
643int
644vhpet_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, int size,
645    void *arg)
646{
647	int i, offset;
648	struct vhpet *vhpet;
649	uint64_t data;
650
651	vhpet = vm_hpet(vm);
652	offset = gpa - VHPET_BASE;
653
654	VHPET_LOCK(vhpet);
655
656	/* Accesses to the HPET should be 4 or 8 bytes wide */
657	if (size != 4 && size != 8) {
658		VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
659		    "offset 0x%08x, size %d", offset, size);
660		data = 0;
661		goto done;
662	}
663
664	/* Access to the HPET should be naturally aligned to its width */
665	if (offset & (size - 1)) {
666		VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
667		    "offset 0x%08x, size %d", offset, size);
668		data = 0;
669		goto done;
670	}
671
672	if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) {
673		data = vhpet_capabilities();
674		goto done;
675	}
676
677	if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
678		data = vhpet->config;
679		goto done;
680	}
681
682	if (offset == HPET_ISR || offset == HPET_ISR + 4) {
683		data = vhpet->isr;
684		goto done;
685	}
686
687	if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
688		data = vhpet_counter(vhpet, true);
689		goto done;
690	}
691
692	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
693		if (offset == HPET_TIMER_CAP_CNF(i) ||
694		    offset == HPET_TIMER_CAP_CNF(i) + 4) {
695			data = vhpet->timer[i].cap_config;
696			break;
697		}
698
699		if (offset == HPET_TIMER_COMPARATOR(i) ||
700		    offset == HPET_TIMER_COMPARATOR(i) + 4) {
701			data = vhpet->timer[i].compval;
702			break;
703		}
704
705		if (offset == HPET_TIMER_FSB_VAL(i) ||
706		    offset == HPET_TIMER_FSB_ADDR(i)) {
707			data = vhpet->timer[i].msireg;
708			break;
709		}
710	}
711
712	if (i >= VHPET_NUM_TIMERS)
713		data = 0;
714done:
715	VHPET_UNLOCK(vhpet);
716
717	if (size == 4) {
718		if (offset & 0x4)
719			data >>= 32;
720	}
721	*rval = data;
722	return (0);
723}
724
725struct vhpet *
726vhpet_init(struct vm *vm)
727{
728	int i, pincount;
729	struct vhpet *vhpet;
730	uint64_t allowed_irqs;
731	struct vhpet_callout_arg *arg;
732	struct bintime bt;
733
734	vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO);
735        vhpet->vm = vm;
736	mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF);
737
738	FREQ2BT(HPET_FREQ, &bt);
739	vhpet->freq_sbt = bttosbt(bt);
740
741	pincount = vioapic_pincount(vm);
742	if (pincount >= 24)
743		allowed_irqs = 0x00f00000;	/* irqs 20, 21, 22 and 23 */
744	else
745		allowed_irqs = 0;
746
747	/*
748	 * Initialize HPET timer hardware state.
749	 */
750	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
751		vhpet->timer[i].cap_config = allowed_irqs << 32;
752		vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT;
753		vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL;
754
755		vhpet->timer[i].compval = 0xffffffff;
756		callout_init(&vhpet->timer[i].callout, 1);
757
758		arg = &vhpet->timer[i].arg;
759		arg->vhpet = vhpet;
760		arg->timer_num = i;
761	}
762
763	return (vhpet);
764}
765
766void
767vhpet_cleanup(struct vhpet *vhpet)
768{
769	int i;
770
771	for (i = 0; i < VHPET_NUM_TIMERS; i++)
772		callout_drain(&vhpet->timer[i].callout);
773
774	free(vhpet, M_VHPET);
775}
776
777int
778vhpet_getcap(struct vm_hpet_cap *cap)
779{
780
781	cap->capabilities = vhpet_capabilities();
782	return (0);
783}
784