xref: /freebsd/sys/amd64/vmm/io/vhpet.c (revision 84dfba8d183d31e3412639ecb4b8ad4433cf7e80)
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$
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
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 
52 static 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
66 CTASSERT(VHPET_NUM_TIMERS >= 3 && VHPET_NUM_TIMERS <= 32);
67 
68 struct vhpet_callout_arg {
69 	struct vhpet *vhpet;
70 	int timer_num;
71 };
72 
73 struct 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 
96 static uint64_t
97 vhpet_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 
113 static __inline bool
114 vhpet_counter_enabled(struct vhpet *vhpet)
115 {
116 
117 	return ((vhpet->config & HPET_CNF_ENABLE) ? true : false);
118 }
119 
120 static __inline bool
121 vhpet_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 
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 	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 
166 static uint32_t
167 vhpet_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 
194 static void
195 vhpet_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 
207 static __inline bool
208 vhpet_periodic_timer(struct vhpet *vhpet, int n)
209 {
210 
211 	return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0);
212 }
213 
214 static __inline bool
215 vhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n)
216 {
217 
218 	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0);
219 }
220 
221 static __inline bool
222 vhpet_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 
240 static void
241 vhpet_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_set_intr(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_set_intr(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 
296 static void
297 vhpet_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 
322 static void
323 vhpet_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);
361 done:
362 	VHPET_UNLOCK(vhpet);
363 	return;
364 }
365 
366 static void
367 vhpet_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 
374 static void
375 vhpet_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 
408 static void
409 vhpet_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 
418 static void
419 vhpet_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 
427 static __inline void
428 update_register(uint64_t *regptr, uint64_t data, uint64_t mask)
429 {
430 
431 	*regptr &= ~mask;
432 	*regptr |= (data & mask);
433 }
434 
435 static void
436 vhpet_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 
512 int
513 vhpet_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 	}
638 done:
639 	VHPET_UNLOCK(vhpet);
640 	return (0);
641 }
642 
643 int
644 vhpet_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;
714 done:
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 
725 struct vhpet *
726 vhpet_init(struct vm *vm)
727 {
728 	int i;
729 	struct vhpet *vhpet;
730 	struct vhpet_callout_arg *arg;
731 	struct bintime bt;
732 
733 	vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO);
734         vhpet->vm = vm;
735 	mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF);
736 
737 	FREQ2BT(HPET_FREQ, &bt);
738 	vhpet->freq_sbt = bttosbt(bt);
739 
740 	/*
741 	 * Initialize HPET timer hardware state.
742 	 */
743 	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
744 		vhpet->timer[i].cap_config = 0UL << 32 |
745 		    HPET_TCAP_FSB_INT_DEL | HPET_TCAP_PER_INT;
746 		vhpet->timer[i].compval = 0xffffffff;
747 		callout_init(&vhpet->timer[i].callout, 1);
748 
749 		arg = &vhpet->timer[i].arg;
750 		arg->vhpet = vhpet;
751 		arg->timer_num = i;
752 	}
753 
754 	return (vhpet);
755 }
756 
757 void
758 vhpet_cleanup(struct vhpet *vhpet)
759 {
760 	int i;
761 
762 	for (i = 0; i < VHPET_NUM_TIMERS; i++)
763 		callout_drain(&vhpet->timer[i].callout);
764 
765 	free(vhpet, M_VHPET);
766 }
767 
768 int
769 vhpet_getcap(struct vm_hpet_cap *cap)
770 {
771 
772 	cap->capabilities = vhpet_capabilities();
773 	return (0);
774 }
775