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