xref: /illumos-gate/usr/src/uts/intel/io/vmm/io/vhpet.c (revision 7c8c0b8227679b4684566e408ccc96d6ef7175e9)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
5  * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31 
32 /*
33  * Copyright 2018 Joyent, Inc.
34  */
35 
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38 
39 #include <sys/param.h>
40 #include <sys/mutex.h>
41 #include <sys/kernel.h>
42 #include <sys/malloc.h>
43 #include <sys/systm.h>
44 
45 #include <dev/acpica/acpi_hpet.h>
46 
47 #include <machine/vmm.h>
48 #include <machine/vmm_dev.h>
49 
50 #include "vmm_lapic.h"
51 #include "vatpic.h"
52 #include "vioapic.h"
53 #include "vhpet.h"
54 
55 #include "vmm_ktr.h"
56 
57 static MALLOC_DEFINE(M_VHPET, "vhpet", "bhyve virtual hpet");
58 
59 #define	HPET_FREQ	16777216		/* 16.7 (2^24) Mhz */
60 #define	FS_PER_S	1000000000000000ul
61 
62 /* Timer N Configuration and Capabilities Register */
63 #define	HPET_TCAP_RO_MASK	(HPET_TCAP_INT_ROUTE	|	\
64 				HPET_TCAP_FSB_INT_DEL	|	\
65 				HPET_TCAP_SIZE		|	\
66 				HPET_TCAP_PER_INT)
67 /*
68  * HPET requires at least 3 timers and up to 32 timers per block.
69  */
70 #define	VHPET_NUM_TIMERS	8
71 CTASSERT(VHPET_NUM_TIMERS >= 3 && VHPET_NUM_TIMERS <= 32);
72 
73 struct vhpet_callout_arg {
74 	struct vhpet *vhpet;
75 	int timer_num;
76 };
77 
78 struct vhpet_timer {
79 	uint64_t	cap_config;	/* Configuration */
80 	uint64_t	msireg;		/* FSB interrupt routing */
81 	uint32_t	compval;	/* Comparator */
82 	uint32_t	comprate;
83 	struct callout	callout;
84 	hrtime_t	callout_expire;	/* time when counter==compval */
85 	struct vhpet_callout_arg arg;
86 };
87 
88 struct vhpet {
89 	struct vm	*vm;
90 	kmutex_t	lock;
91 
92 	uint64_t	config;		/* Configuration */
93 	uint64_t	isr;		/* Interrupt Status */
94 	uint32_t	base_count;	/* HPET counter base value */
95 	hrtime_t	base_time;	/* uptime corresponding to base value */
96 
97 	struct vhpet_timer timer[VHPET_NUM_TIMERS];
98 };
99 
100 #define	VHPET_LOCK(vhp)		mutex_enter(&((vhp)->lock))
101 #define	VHPET_UNLOCK(vhp)	mutex_exit(&((vhp)->lock))
102 
103 static void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter,
104     hrtime_t now);
105 
106 static uint64_t
107 vhpet_capabilities(void)
108 {
109 	uint64_t cap = 0;
110 
111 	cap |= 0x8086 << 16;			/* vendor id */
112 	cap |= (VHPET_NUM_TIMERS - 1) << 8;	/* number of timers */
113 	cap |= 1;				/* revision */
114 	cap &= ~HPET_CAP_COUNT_SIZE;		/* 32-bit timer */
115 
116 	cap &= 0xffffffff;
117 	cap |= (FS_PER_S / HPET_FREQ) << 32;	/* tick period in fs */
118 
119 	return (cap);
120 }
121 
122 static __inline bool
123 vhpet_counter_enabled(struct vhpet *vhpet)
124 {
125 
126 	return ((vhpet->config & HPET_CNF_ENABLE) ? true : false);
127 }
128 
129 static __inline bool
130 vhpet_timer_msi_enabled(struct vhpet *vhpet, int n)
131 {
132 	const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN;
133 
134 	if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable)
135 		return (true);
136 	else
137 		return (false);
138 }
139 
140 static __inline int
141 vhpet_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 	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
151 }
152 
153 static uint32_t
154 vhpet_counter(struct vhpet *vhpet, hrtime_t *nowptr)
155 {
156 	const hrtime_t now = gethrtime();
157 	uint32_t val = vhpet->base_count;
158 
159 	if (vhpet_counter_enabled(vhpet)) {
160 		const hrtime_t delta = now - vhpet->base_time;
161 
162 		ASSERT3S(delta, >=, 0);
163 		val += hrt_freq_count(delta, HPET_FREQ);
164 	} else {
165 		/* Value of the counter is meaningless when it is disabled */
166 	}
167 
168 	if (nowptr != NULL) {
169 		*nowptr = now;
170 	}
171 	return (val);
172 }
173 
174 static void
175 vhpet_timer_clear_isr(struct vhpet *vhpet, int n)
176 {
177 	int pin;
178 
179 	if (vhpet->isr & (1 << n)) {
180 		pin = vhpet_timer_ioapic_pin(vhpet, n);
181 		KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n));
182 		vioapic_deassert_irq(vhpet->vm, pin);
183 		vhpet->isr &= ~(1 << n);
184 	}
185 }
186 
187 static __inline bool
188 vhpet_periodic_timer(struct vhpet *vhpet, int n)
189 {
190 
191 	return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0);
192 }
193 
194 static __inline bool
195 vhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n)
196 {
197 
198 	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0);
199 }
200 
201 static __inline bool
202 vhpet_timer_edge_trig(struct vhpet *vhpet, int n)
203 {
204 
205 	KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: "
206 	    "timer %d is using MSI", n));
207 
208 	if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0)
209 		return (true);
210 	else
211 		return (false);
212 }
213 
214 static void
215 vhpet_timer_interrupt(struct vhpet *vhpet, int n)
216 {
217 	int pin;
218 
219 	/* If interrupts are not enabled for this timer then just return. */
220 	if (!vhpet_timer_interrupt_enabled(vhpet, n))
221 		return;
222 
223 	/*
224 	 * If a level triggered interrupt is already asserted then just return.
225 	 */
226 	if ((vhpet->isr & (1 << n)) != 0) {
227 		VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n);
228 		return;
229 	}
230 
231 	if (vhpet_timer_msi_enabled(vhpet, n)) {
232 		lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32,
233 		    vhpet->timer[n].msireg & 0xffffffff);
234 		return;
235 	}
236 
237 	pin = vhpet_timer_ioapic_pin(vhpet, n);
238 	if (pin == 0) {
239 		VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n);
240 		return;
241 	}
242 
243 	if (vhpet_timer_edge_trig(vhpet, n)) {
244 		vioapic_pulse_irq(vhpet->vm, pin);
245 	} else {
246 		vhpet->isr |= 1 << n;
247 		vioapic_assert_irq(vhpet->vm, pin);
248 	}
249 }
250 
251 static void
252 vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter)
253 {
254 	uint32_t compval, comprate, compnext;
255 
256 	KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n));
257 
258 	compval = vhpet->timer[n].compval;
259 	comprate = vhpet->timer[n].comprate;
260 
261 	/*
262 	 * Calculate the comparator value to be used for the next periodic
263 	 * interrupt.
264 	 *
265 	 * This function is commonly called from the callout handler.
266 	 * In this scenario the 'counter' is ahead of 'compval'. To find
267 	 * the next value to program into the accumulator we divide the
268 	 * number space between 'compval' and 'counter' into 'comprate'
269 	 * sized units. The 'compval' is rounded up such that is "ahead"
270 	 * of 'counter'.
271 	 */
272 	compnext = compval + ((counter - compval) / comprate + 1) * comprate;
273 
274 	vhpet->timer[n].compval = compnext;
275 }
276 
277 static void
278 vhpet_handler(void *a)
279 {
280 	int n;
281 	uint32_t counter;
282 	hrtime_t now;
283 	struct vhpet *vhpet;
284 	struct callout *callout;
285 	struct vhpet_callout_arg *arg;
286 
287 	arg = a;
288 	vhpet = arg->vhpet;
289 	n = arg->timer_num;
290 	callout = &vhpet->timer[n].callout;
291 
292 	VM_CTR1(vhpet->vm, "hpet t%d fired", n);
293 
294 	VHPET_LOCK(vhpet);
295 
296 	if (callout_pending(callout))		/* callout was reset */
297 		goto done;
298 
299 	if (!callout_active(callout))		/* callout was stopped */
300 		goto done;
301 
302 	callout_deactivate(callout);
303 
304 	if (!vhpet_counter_enabled(vhpet))
305 		panic("vhpet(%p) callout with counter disabled", vhpet);
306 
307 	counter = vhpet_counter(vhpet, &now);
308 	vhpet_start_timer(vhpet, n, counter, now);
309 	vhpet_timer_interrupt(vhpet, n);
310 done:
311 	VHPET_UNLOCK(vhpet);
312 }
313 
314 static void
315 vhpet_stop_timer(struct vhpet *vhpet, int n, hrtime_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_expire < 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 
335 static void
336 vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, hrtime_t now)
337 {
338 	struct vhpet_timer *timer = &vhpet->timer[n];
339 
340 	if (timer->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 	const hrtime_t delta = hrt_freq_interval(HPET_FREQ,
352 	    timer->compval - counter);
353 	timer->callout_expire = now + delta;
354 	callout_reset_hrtime(&timer->callout, timer->callout_expire,
355 	    vhpet_handler, &timer->arg, C_ABSOLUTE);
356 }
357 
358 static void
359 vhpet_start_counting(struct vhpet *vhpet)
360 {
361 	int i;
362 
363 	vhpet->base_time = gethrtime();
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->base_count,
370 		    vhpet->base_time);
371 	}
372 }
373 
374 static void
375 vhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, hrtime_t now)
376 {
377 	int i;
378 
379 	vhpet->base_count = counter;
380 	for (i = 0; i < VHPET_NUM_TIMERS; i++)
381 		vhpet_stop_timer(vhpet, i, now);
382 }
383 
384 static __inline void
385 update_register(uint64_t *regptr, uint64_t data, uint64_t mask)
386 {
387 
388 	*regptr &= ~mask;
389 	*regptr |= (data & mask);
390 }
391 
392 static void
393 vhpet_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 
469 int
470 vhpet_mmio_write(struct vm *vm, int vcpuid, uint64_t gpa, uint64_t val,
471     int size)
472 {
473 	struct vhpet *vhpet;
474 	uint64_t data, mask, oldval, val64;
475 	uint32_t isr_clear_mask, old_compval, old_comprate, counter;
476 	hrtime_t now;
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 'base_count' with the value right
516 		 * before it is disabled.
517 		 */
518 		counter = vhpet_counter(vhpet, &now);
519 		oldval = vhpet->config;
520 		update_register(&vhpet->config, data, mask);
521 
522 		/*
523 		 * LegacyReplacement Routing is not supported so clear the
524 		 * bit explicitly.
525 		 */
526 		vhpet->config &= ~HPET_CNF_LEG_RT;
527 
528 		if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) {
529 			if (vhpet_counter_enabled(vhpet)) {
530 				vhpet_start_counting(vhpet);
531 				VM_CTR0(vhpet->vm, "hpet enabled");
532 			} else {
533 				vhpet_stop_counting(vhpet, counter, now);
534 				VM_CTR0(vhpet->vm, "hpet disabled");
535 			}
536 		}
537 		goto done;
538 	}
539 
540 	if (offset == HPET_ISR || offset == HPET_ISR + 4) {
541 		isr_clear_mask = vhpet->isr & data;
542 		for (i = 0; i < VHPET_NUM_TIMERS; i++) {
543 			if ((isr_clear_mask & (1 << i)) != 0) {
544 				VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i);
545 				vhpet_timer_clear_isr(vhpet, i);
546 			}
547 		}
548 		goto done;
549 	}
550 
551 	if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
552 		/* Zero-extend the counter to 64-bits before updating it */
553 		val64 = vhpet_counter(vhpet, NULL);
554 		update_register(&val64, data, mask);
555 		vhpet->base_count = val64;
556 		if (vhpet_counter_enabled(vhpet))
557 			vhpet_start_counting(vhpet);
558 		goto done;
559 	}
560 
561 	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
562 		if (offset == HPET_TIMER_CAP_CNF(i) ||
563 		    offset == HPET_TIMER_CAP_CNF(i) + 4) {
564 			vhpet_timer_update_config(vhpet, i, data, mask);
565 			break;
566 		}
567 
568 		if (offset == HPET_TIMER_COMPARATOR(i) ||
569 		    offset == HPET_TIMER_COMPARATOR(i) + 4) {
570 			old_compval = vhpet->timer[i].compval;
571 			old_comprate = vhpet->timer[i].comprate;
572 			if (vhpet_periodic_timer(vhpet, i)) {
573 				/*
574 				 * In periodic mode writes to the comparator
575 				 * change the 'compval' register only if the
576 				 * HPET_TCNF_VAL_SET bit is set in the config
577 				 * register.
578 				 */
579 				val64 = vhpet->timer[i].comprate;
580 				update_register(&val64, data, mask);
581 				vhpet->timer[i].comprate = val64;
582 				if ((vhpet->timer[i].cap_config &
583 				    HPET_TCNF_VAL_SET) != 0) {
584 					vhpet->timer[i].compval = val64;
585 				}
586 			} else {
587 				KASSERT(vhpet->timer[i].comprate == 0,
588 				    ("vhpet one-shot timer %d has invalid "
589 				    "rate %u", i, vhpet->timer[i].comprate));
590 				val64 = vhpet->timer[i].compval;
591 				update_register(&val64, data, mask);
592 				vhpet->timer[i].compval = val64;
593 			}
594 			vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET;
595 
596 			if (vhpet->timer[i].compval != old_compval ||
597 			    vhpet->timer[i].comprate != old_comprate) {
598 				if (vhpet_counter_enabled(vhpet)) {
599 					counter = vhpet_counter(vhpet, &now);
600 					vhpet_start_timer(vhpet, i, counter,
601 					    now);
602 				}
603 			}
604 			break;
605 		}
606 
607 		if (offset == HPET_TIMER_FSB_VAL(i) ||
608 		    offset == HPET_TIMER_FSB_ADDR(i)) {
609 			update_register(&vhpet->timer[i].msireg, data, mask);
610 			break;
611 		}
612 	}
613 done:
614 	VHPET_UNLOCK(vhpet);
615 	return (0);
616 }
617 
618 int
619 vhpet_mmio_read(struct vm *vm, int vcpuid, uint64_t gpa, uint64_t *rval,
620     int size)
621 {
622 	int i, offset;
623 	struct vhpet *vhpet;
624 	uint64_t data;
625 
626 	vhpet = vm_hpet(vm);
627 	offset = gpa - VHPET_BASE;
628 
629 	VHPET_LOCK(vhpet);
630 
631 	/* Accesses to the HPET should be 4 or 8 bytes wide */
632 	if (size != 4 && size != 8) {
633 		VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
634 		    "offset 0x%08x, size %d", offset, size);
635 		data = 0;
636 		goto done;
637 	}
638 
639 	/* Access to the HPET should be naturally aligned to its width */
640 	if (offset & (size - 1)) {
641 		VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
642 		    "offset 0x%08x, size %d", offset, size);
643 		data = 0;
644 		goto done;
645 	}
646 
647 	if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) {
648 		data = vhpet_capabilities();
649 		goto done;
650 	}
651 
652 	if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
653 		data = vhpet->config;
654 		goto done;
655 	}
656 
657 	if (offset == HPET_ISR || offset == HPET_ISR + 4) {
658 		data = vhpet->isr;
659 		goto done;
660 	}
661 
662 	if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
663 		data = vhpet_counter(vhpet, NULL);
664 		goto done;
665 	}
666 
667 	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
668 		if (offset == HPET_TIMER_CAP_CNF(i) ||
669 		    offset == HPET_TIMER_CAP_CNF(i) + 4) {
670 			data = vhpet->timer[i].cap_config;
671 			break;
672 		}
673 
674 		if (offset == HPET_TIMER_COMPARATOR(i) ||
675 		    offset == HPET_TIMER_COMPARATOR(i) + 4) {
676 			data = vhpet->timer[i].compval;
677 			break;
678 		}
679 
680 		if (offset == HPET_TIMER_FSB_VAL(i) ||
681 		    offset == HPET_TIMER_FSB_ADDR(i)) {
682 			data = vhpet->timer[i].msireg;
683 			break;
684 		}
685 	}
686 
687 	if (i >= VHPET_NUM_TIMERS)
688 		data = 0;
689 done:
690 	VHPET_UNLOCK(vhpet);
691 
692 	if (size == 4) {
693 		if (offset & 0x4)
694 			data >>= 32;
695 	}
696 	*rval = data;
697 	return (0);
698 }
699 
700 struct vhpet *
701 vhpet_init(struct vm *vm)
702 {
703 	int i, pincount;
704 	struct vhpet *vhpet;
705 	uint64_t allowed_irqs;
706 	struct vhpet_callout_arg *arg;
707 
708 	vhpet = malloc(sizeof (struct vhpet), M_VHPET, M_WAITOK | M_ZERO);
709 	vhpet->vm = vm;
710 	mutex_init(&vhpet->lock, NULL, MUTEX_ADAPTIVE, NULL);
711 
712 	pincount = vioapic_pincount(vm);
713 	if (pincount >= 32)
714 		allowed_irqs = 0xff000000;	/* irqs 24-31 */
715 	else if (pincount >= 20)
716 		allowed_irqs = 0xf << (pincount - 4);	/* 4 upper irqs */
717 	else
718 		allowed_irqs = 0;
719 
720 	/*
721 	 * Initialize HPET timer hardware state.
722 	 */
723 	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
724 		vhpet->timer[i].cap_config = allowed_irqs << 32;
725 		vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT;
726 		vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL;
727 
728 		vhpet->timer[i].compval = 0xffffffff;
729 		callout_init(&vhpet->timer[i].callout, 1);
730 
731 		arg = &vhpet->timer[i].arg;
732 		arg->vhpet = vhpet;
733 		arg->timer_num = i;
734 	}
735 
736 	return (vhpet);
737 }
738 
739 void
740 vhpet_cleanup(struct vhpet *vhpet)
741 {
742 	int i;
743 
744 	for (i = 0; i < VHPET_NUM_TIMERS; i++)
745 		callout_drain(&vhpet->timer[i].callout);
746 
747 	mutex_destroy(&vhpet->lock);
748 	free(vhpet, M_VHPET);
749 }
750 
751 int
752 vhpet_getcap(struct vm_hpet_cap *cap)
753 {
754 
755 	cap->capabilities = vhpet_capabilities();
756 	return (0);
757 }
758 void
759 vhpet_localize_resources(struct vhpet *vhpet)
760 {
761 	for (uint_t i = 0; i < VHPET_NUM_TIMERS; i++) {
762 		vmm_glue_callout_localize(&vhpet->timer[i].callout);
763 	}
764 }
765