xref: /illumos-gate/usr/src/uts/intel/io/vmm/io/vhpet.c (revision 32640292)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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 
30 /*
31  * Copyright 2018 Joyent, Inc.
32  * Copyright 2022 Oxide Computer Company
33  */
34 
35 #include <sys/cdefs.h>
36 
37 #include <sys/param.h>
38 #include <sys/mutex.h>
39 #include <sys/kernel.h>
40 #include <sys/kmem.h>
41 #include <sys/systm.h>
42 
43 #include <dev/acpica/acpi_hpet.h>
44 
45 #include <machine/vmm.h>
46 #include <machine/vmm_dev.h>
47 
48 #include "vmm_lapic.h"
49 #include "vatpic.h"
50 #include "vioapic.h"
51 #include "vhpet.h"
52 
53 
54 #define	HPET_FREQ	16777216		/* 16.7 (2^24) 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_timer {
74 	uint64_t	cap_config;	/* Configuration */
75 	uint64_t	msireg;		/* FSB interrupt routing */
76 	uint32_t	compval;	/* Comparator */
77 	uint32_t	comprate;
78 	struct callout	callout;
79 	hrtime_t	callout_expire;	/* time when counter==compval */
80 	struct vhpet_callout_arg arg;
81 };
82 
83 struct vhpet {
84 	struct vm	*vm;
85 	kmutex_t	lock;
86 
87 	uint64_t	config;		/* Configuration */
88 	uint64_t	isr;		/* Interrupt Status */
89 	uint32_t	base_count;	/* HPET counter base value */
90 	hrtime_t	base_time;	/* uptime corresponding to base value */
91 
92 	struct vhpet_timer timer[VHPET_NUM_TIMERS];
93 };
94 
95 #define	VHPET_LOCK(vhp)		mutex_enter(&((vhp)->lock))
96 #define	VHPET_UNLOCK(vhp)	mutex_exit(&((vhp)->lock))
97 #define	VHPET_LOCKED(vhp)	MUTEX_HELD(&((vhp)->lock))
98 
99 static void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter,
100     hrtime_t now);
101 
102 static uint64_t
vhpet_capabilities(void)103 vhpet_capabilities(void)
104 {
105 	uint64_t cap = 0;
106 
107 	cap |= 0x8086 << 16;			/* vendor id */
108 	cap |= (VHPET_NUM_TIMERS - 1) << 8;	/* number of timers */
109 	cap |= 1;				/* revision */
110 	cap &= ~HPET_CAP_COUNT_SIZE;		/* 32-bit timer */
111 
112 	cap &= 0xffffffff;
113 	cap |= (FS_PER_S / HPET_FREQ) << 32;	/* tick period in fs */
114 
115 	return (cap);
116 }
117 
118 static __inline bool
vhpet_counter_enabled(struct vhpet * vhpet)119 vhpet_counter_enabled(struct vhpet *vhpet)
120 {
121 
122 	return ((vhpet->config & HPET_CNF_ENABLE) ? true : false);
123 }
124 
125 static __inline bool
vhpet_timer_msi_enabled(struct vhpet * vhpet,int n)126 vhpet_timer_msi_enabled(struct vhpet *vhpet, int n)
127 {
128 	const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN;
129 
130 	if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable)
131 		return (true);
132 	else
133 		return (false);
134 }
135 
136 static __inline int
vhpet_timer_ioapic_pin(struct vhpet * vhpet,int n)137 vhpet_timer_ioapic_pin(struct vhpet *vhpet, int n)
138 {
139 	/*
140 	 * If the timer is configured to use MSI then treat it as if the
141 	 * timer is not connected to the ioapic.
142 	 */
143 	if (vhpet_timer_msi_enabled(vhpet, n))
144 		return (0);
145 
146 	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
147 }
148 
149 static uint32_t
vhpet_counter(struct vhpet * vhpet,hrtime_t * nowptr)150 vhpet_counter(struct vhpet *vhpet, hrtime_t *nowptr)
151 {
152 	const hrtime_t now = gethrtime();
153 	uint32_t val = vhpet->base_count;
154 
155 	if (vhpet_counter_enabled(vhpet)) {
156 		const hrtime_t delta = now - vhpet->base_time;
157 
158 		ASSERT3S(delta, >=, 0);
159 		val += hrt_freq_count(delta, HPET_FREQ);
160 	} else {
161 		/* Value of the counter is meaningless when it is disabled */
162 	}
163 
164 	if (nowptr != NULL) {
165 		*nowptr = now;
166 	}
167 	return (val);
168 }
169 
170 static void
vhpet_timer_clear_isr(struct vhpet * vhpet,int n)171 vhpet_timer_clear_isr(struct vhpet *vhpet, int n)
172 {
173 	int pin;
174 
175 	if (vhpet->isr & (1 << n)) {
176 		pin = vhpet_timer_ioapic_pin(vhpet, n);
177 		KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n));
178 		(void) vioapic_deassert_irq(vhpet->vm, pin);
179 		vhpet->isr &= ~(1 << n);
180 	}
181 }
182 
183 static __inline bool
vhpet_periodic_timer(struct vhpet * vhpet,int n)184 vhpet_periodic_timer(struct vhpet *vhpet, int n)
185 {
186 
187 	return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0);
188 }
189 
190 static __inline bool
vhpet_timer_interrupt_enabled(struct vhpet * vhpet,int n)191 vhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n)
192 {
193 
194 	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0);
195 }
196 
197 static __inline bool
vhpet_timer_edge_trig(struct vhpet * vhpet,int n)198 vhpet_timer_edge_trig(struct vhpet *vhpet, int n)
199 {
200 
201 	KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: "
202 	    "timer %d is using MSI", n));
203 
204 	if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0)
205 		return (true);
206 	else
207 		return (false);
208 }
209 
210 static void
vhpet_timer_interrupt(struct vhpet * vhpet,int n)211 vhpet_timer_interrupt(struct vhpet *vhpet, int n)
212 {
213 	int pin;
214 
215 	/* If interrupts are not enabled for this timer then just return. */
216 	if (!vhpet_timer_interrupt_enabled(vhpet, n))
217 		return;
218 
219 	/*
220 	 * If a level triggered interrupt is already asserted then just return.
221 	 */
222 	if ((vhpet->isr & (1 << n)) != 0) {
223 		return;
224 	}
225 
226 	if (vhpet_timer_msi_enabled(vhpet, n)) {
227 		(void) lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32,
228 		    vhpet->timer[n].msireg & 0xffffffff);
229 		return;
230 	}
231 
232 	pin = vhpet_timer_ioapic_pin(vhpet, n);
233 	if (pin == 0) {
234 		/* Interrupt is not routed to IOAPIC */
235 		return;
236 	}
237 
238 	if (vhpet_timer_edge_trig(vhpet, n)) {
239 		(void) vioapic_pulse_irq(vhpet->vm, pin);
240 	} else {
241 		vhpet->isr |= 1 << n;
242 		(void) vioapic_assert_irq(vhpet->vm, pin);
243 	}
244 }
245 
246 static void
vhpet_adjust_compval(struct vhpet * vhpet,int n,uint32_t counter)247 vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter)
248 {
249 	uint32_t compval, comprate, compnext;
250 
251 	KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n));
252 
253 	compval = vhpet->timer[n].compval;
254 	comprate = vhpet->timer[n].comprate;
255 
256 	/*
257 	 * Calculate the comparator value to be used for the next periodic
258 	 * interrupt.
259 	 *
260 	 * This function is commonly called from the callout handler.
261 	 * In this scenario the 'counter' is ahead of 'compval'. To find
262 	 * the next value to program into the accumulator we divide the
263 	 * number space between 'compval' and 'counter' into 'comprate'
264 	 * sized units. The 'compval' is rounded up such that is "ahead"
265 	 * of 'counter'.
266 	 */
267 	compnext = compval + ((counter - compval) / comprate + 1) * comprate;
268 
269 	vhpet->timer[n].compval = compnext;
270 }
271 
272 static void
vhpet_handler(void * arg)273 vhpet_handler(void *arg)
274 {
275 	const struct vhpet_callout_arg *vca = arg;
276 	struct vhpet *vhpet = vca->vhpet;
277 	const int n = vca->timer_num;
278 	struct callout *callout = &vhpet->timer[n].callout;
279 
280 	VHPET_LOCK(vhpet);
281 
282 	if (callout_pending(callout) || !callout_active(callout)) {
283 		VHPET_UNLOCK(vhpet);
284 		return;
285 	}
286 
287 	callout_deactivate(callout);
288 	ASSERT(vhpet_counter_enabled(vhpet));
289 
290 	if (vhpet_periodic_timer(vhpet, n)) {
291 		hrtime_t now;
292 		uint32_t counter = vhpet_counter(vhpet, &now);
293 
294 		vhpet_start_timer(vhpet, n, counter, now);
295 	} else {
296 		/*
297 		 * Zero out the expiration time to distinguish a fired timer
298 		 * from one which is held due to a VM pause.
299 		 */
300 		vhpet->timer[n].callout_expire = 0;
301 	}
302 	vhpet_timer_interrupt(vhpet, n);
303 
304 	VHPET_UNLOCK(vhpet);
305 }
306 
307 static void
vhpet_stop_timer(struct vhpet * vhpet,int n,hrtime_t now)308 vhpet_stop_timer(struct vhpet *vhpet, int n, hrtime_t now)
309 {
310 	ASSERT(VHPET_LOCKED(vhpet));
311 
312 	callout_stop(&vhpet->timer[n].callout);
313 
314 	/*
315 	 * If the callout was scheduled to expire in the past but hasn't
316 	 * had a chance to execute yet then trigger the timer interrupt
317 	 * here. Failing to do so will result in a missed timer interrupt
318 	 * in the guest. This is especially bad in one-shot mode because
319 	 * the next interrupt has to wait for the counter to wrap around.
320 	 */
321 	if (vhpet->timer[n].callout_expire < now) {
322 		vhpet_timer_interrupt(vhpet, n);
323 	}
324 	vhpet->timer[n].callout_expire = 0;
325 }
326 
327 static void
vhpet_start_timer(struct vhpet * vhpet,int n,uint32_t counter,hrtime_t now)328 vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, hrtime_t now)
329 {
330 	struct vhpet_timer *timer = &vhpet->timer[n];
331 
332 	ASSERT(VHPET_LOCKED(vhpet));
333 
334 	if (timer->comprate != 0)
335 		vhpet_adjust_compval(vhpet, n, counter);
336 	else {
337 		/*
338 		 * In one-shot mode it is the guest's responsibility to make
339 		 * sure that the comparator value is not in the "past". The
340 		 * hardware doesn't have any belt-and-suspenders to deal with
341 		 * this so we don't either.
342 		 */
343 	}
344 
345 	const hrtime_t delta = hrt_freq_interval(HPET_FREQ,
346 	    timer->compval - counter);
347 	timer->callout_expire = now + delta;
348 	callout_reset_hrtime(&timer->callout, timer->callout_expire,
349 	    vhpet_handler, &timer->arg, C_ABSOLUTE);
350 }
351 
352 static void
vhpet_start_counting(struct vhpet * vhpet)353 vhpet_start_counting(struct vhpet *vhpet)
354 {
355 	int i;
356 
357 	vhpet->base_time = gethrtime();
358 	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
359 		/*
360 		 * Restart the timers based on the value of the main counter
361 		 * when it stopped counting.
362 		 */
363 		vhpet_start_timer(vhpet, i, vhpet->base_count,
364 		    vhpet->base_time);
365 	}
366 }
367 
368 static void
vhpet_stop_counting(struct vhpet * vhpet,uint32_t counter,hrtime_t now)369 vhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, hrtime_t now)
370 {
371 	int i;
372 
373 	vhpet->base_count = counter;
374 	for (i = 0; i < VHPET_NUM_TIMERS; i++)
375 		vhpet_stop_timer(vhpet, i, now);
376 }
377 
378 static __inline void
update_register(uint64_t * regptr,uint64_t data,uint64_t mask)379 update_register(uint64_t *regptr, uint64_t data, uint64_t mask)
380 {
381 
382 	*regptr &= ~mask;
383 	*regptr |= (data & mask);
384 }
385 
386 static void
vhpet_timer_update_config(struct vhpet * vhpet,int n,uint64_t data,uint64_t mask)387 vhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data,
388     uint64_t mask)
389 {
390 	bool clear_isr;
391 	int old_pin, new_pin;
392 	uint32_t allowed_irqs;
393 	uint64_t oldval, newval;
394 
395 	if (vhpet_timer_msi_enabled(vhpet, n) ||
396 	    vhpet_timer_edge_trig(vhpet, n)) {
397 		if (vhpet->isr & (1 << n))
398 			panic("vhpet timer %d isr should not be asserted", n);
399 	}
400 	old_pin = vhpet_timer_ioapic_pin(vhpet, n);
401 	oldval = vhpet->timer[n].cap_config;
402 
403 	newval = oldval;
404 	update_register(&newval, data, mask);
405 	newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE);
406 	newval |= oldval & HPET_TCAP_RO_MASK;
407 
408 	if (newval == oldval)
409 		return;
410 
411 	vhpet->timer[n].cap_config = newval;
412 
413 	/*
414 	 * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field.
415 	 * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set
416 	 * it to the default value of 0.
417 	 */
418 	allowed_irqs = vhpet->timer[n].cap_config >> 32;
419 	new_pin = vhpet_timer_ioapic_pin(vhpet, n);
420 	if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) {
421 		/* Invalid IRQ configured */
422 		new_pin = 0;
423 		vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE;
424 	}
425 
426 	if (!vhpet_periodic_timer(vhpet, n))
427 		vhpet->timer[n].comprate = 0;
428 
429 	/*
430 	 * If the timer's ISR bit is set then clear it in the following cases:
431 	 * - interrupt is disabled
432 	 * - interrupt type is changed from level to edge or fsb.
433 	 * - interrupt routing is changed
434 	 *
435 	 * This is to ensure that this timer's level triggered interrupt does
436 	 * not remain asserted forever.
437 	 */
438 	if (vhpet->isr & (1 << n)) {
439 		KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d",
440 		    n, old_pin));
441 		if (!vhpet_timer_interrupt_enabled(vhpet, n))
442 			clear_isr = true;
443 		else if (vhpet_timer_msi_enabled(vhpet, n))
444 			clear_isr = true;
445 		else if (vhpet_timer_edge_trig(vhpet, n))
446 			clear_isr = true;
447 		else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin)
448 			clear_isr = true;
449 		else
450 			clear_isr = false;
451 
452 		if (clear_isr) {
453 			(void) vioapic_deassert_irq(vhpet->vm, old_pin);
454 			vhpet->isr &= ~(1 << n);
455 		}
456 	}
457 }
458 
459 int
vhpet_mmio_write(struct vm * vm,int vcpuid,uint64_t gpa,uint64_t val,int size)460 vhpet_mmio_write(struct vm *vm, int vcpuid, uint64_t gpa, uint64_t val,
461     int size)
462 {
463 	struct vhpet *vhpet;
464 	uint64_t data, mask, oldval, val64;
465 	uint32_t isr_clear_mask, old_compval, old_comprate, counter;
466 	hrtime_t now;
467 	int i, offset;
468 
469 	vhpet = vm_hpet(vm);
470 	offset = gpa - VHPET_BASE;
471 
472 	VHPET_LOCK(vhpet);
473 
474 	/* Accesses to the HPET should be 4 or 8 bytes wide */
475 	switch (size) {
476 	case 8:
477 		mask = 0xffffffffffffffff;
478 		data = val;
479 		break;
480 	case 4:
481 		mask = 0xffffffff;
482 		data = val;
483 		if ((offset & 0x4) != 0) {
484 			mask <<= 32;
485 			data <<= 32;
486 		}
487 		break;
488 	default:
489 		/* Invalid MMIO write */
490 		goto done;
491 	}
492 
493 	/* Access to the HPET should be naturally aligned to its width */
494 	if (offset & (size - 1)) {
495 		goto done;
496 	}
497 
498 	if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
499 		/*
500 		 * Get the most recent value of the counter before updating
501 		 * the 'config' register. If the HPET is going to be disabled
502 		 * then we need to update 'base_count' with the value right
503 		 * before it is disabled.
504 		 */
505 		counter = vhpet_counter(vhpet, &now);
506 		oldval = vhpet->config;
507 		update_register(&vhpet->config, data, mask);
508 
509 		/*
510 		 * LegacyReplacement Routing is not supported so clear the
511 		 * bit explicitly.
512 		 */
513 		vhpet->config &= ~HPET_CNF_LEG_RT;
514 
515 		if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) {
516 			if (vhpet_counter_enabled(vhpet)) {
517 				vhpet_start_counting(vhpet);
518 			} else {
519 				vhpet_stop_counting(vhpet, counter, now);
520 			}
521 		}
522 		goto done;
523 	}
524 
525 	if (offset == HPET_ISR || offset == HPET_ISR + 4) {
526 		isr_clear_mask = vhpet->isr & data;
527 		for (i = 0; i < VHPET_NUM_TIMERS; i++) {
528 			if ((isr_clear_mask & (1 << i)) != 0) {
529 				vhpet_timer_clear_isr(vhpet, i);
530 			}
531 		}
532 		goto done;
533 	}
534 
535 	if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
536 		/* Zero-extend the counter to 64-bits before updating it */
537 		val64 = vhpet_counter(vhpet, NULL);
538 		update_register(&val64, data, mask);
539 		vhpet->base_count = val64;
540 		if (vhpet_counter_enabled(vhpet))
541 			vhpet_start_counting(vhpet);
542 		goto done;
543 	}
544 
545 	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
546 		if (offset == HPET_TIMER_CAP_CNF(i) ||
547 		    offset == HPET_TIMER_CAP_CNF(i) + 4) {
548 			vhpet_timer_update_config(vhpet, i, data, mask);
549 			break;
550 		}
551 
552 		if (offset == HPET_TIMER_COMPARATOR(i) ||
553 		    offset == HPET_TIMER_COMPARATOR(i) + 4) {
554 			old_compval = vhpet->timer[i].compval;
555 			old_comprate = vhpet->timer[i].comprate;
556 			if (vhpet_periodic_timer(vhpet, i)) {
557 				/*
558 				 * In periodic mode writes to the comparator
559 				 * change the 'compval' register only if the
560 				 * HPET_TCNF_VAL_SET bit is set in the config
561 				 * register.
562 				 */
563 				val64 = vhpet->timer[i].comprate;
564 				update_register(&val64, data, mask);
565 				vhpet->timer[i].comprate = val64;
566 				if ((vhpet->timer[i].cap_config &
567 				    HPET_TCNF_VAL_SET) != 0) {
568 					vhpet->timer[i].compval = val64;
569 				}
570 			} else {
571 				KASSERT(vhpet->timer[i].comprate == 0,
572 				    ("vhpet one-shot timer %d has invalid "
573 				    "rate %u", i, vhpet->timer[i].comprate));
574 				val64 = vhpet->timer[i].compval;
575 				update_register(&val64, data, mask);
576 				vhpet->timer[i].compval = val64;
577 			}
578 			vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET;
579 
580 			if (vhpet->timer[i].compval != old_compval ||
581 			    vhpet->timer[i].comprate != old_comprate) {
582 				if (vhpet_counter_enabled(vhpet)) {
583 					counter = vhpet_counter(vhpet, &now);
584 					vhpet_start_timer(vhpet, i, counter,
585 					    now);
586 				}
587 			}
588 			break;
589 		}
590 
591 		if (offset == HPET_TIMER_FSB_VAL(i) ||
592 		    offset == HPET_TIMER_FSB_ADDR(i)) {
593 			update_register(&vhpet->timer[i].msireg, data, mask);
594 			break;
595 		}
596 	}
597 done:
598 	VHPET_UNLOCK(vhpet);
599 	return (0);
600 }
601 
602 int
vhpet_mmio_read(struct vm * vm,int vcpuid,uint64_t gpa,uint64_t * rval,int size)603 vhpet_mmio_read(struct vm *vm, int vcpuid, uint64_t gpa, uint64_t *rval,
604     int size)
605 {
606 	int i, offset;
607 	struct vhpet *vhpet;
608 	uint64_t data;
609 
610 	vhpet = vm_hpet(vm);
611 	offset = gpa - VHPET_BASE;
612 
613 	VHPET_LOCK(vhpet);
614 
615 	/* Accesses to the HPET should be 4 or 8 bytes wide */
616 	if (size != 4 && size != 8) {
617 		data = 0;
618 		goto done;
619 	}
620 
621 	/* Access to the HPET should be naturally aligned to its width */
622 	if (offset & (size - 1)) {
623 		data = 0;
624 		goto done;
625 	}
626 
627 	if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) {
628 		data = vhpet_capabilities();
629 		goto done;
630 	}
631 
632 	if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
633 		data = vhpet->config;
634 		goto done;
635 	}
636 
637 	if (offset == HPET_ISR || offset == HPET_ISR + 4) {
638 		data = vhpet->isr;
639 		goto done;
640 	}
641 
642 	if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
643 		data = vhpet_counter(vhpet, NULL);
644 		goto done;
645 	}
646 
647 	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
648 		if (offset == HPET_TIMER_CAP_CNF(i) ||
649 		    offset == HPET_TIMER_CAP_CNF(i) + 4) {
650 			data = vhpet->timer[i].cap_config;
651 			break;
652 		}
653 
654 		if (offset == HPET_TIMER_COMPARATOR(i) ||
655 		    offset == HPET_TIMER_COMPARATOR(i) + 4) {
656 			data = vhpet->timer[i].compval;
657 			break;
658 		}
659 
660 		if (offset == HPET_TIMER_FSB_VAL(i) ||
661 		    offset == HPET_TIMER_FSB_ADDR(i)) {
662 			data = vhpet->timer[i].msireg;
663 			break;
664 		}
665 	}
666 
667 	if (i >= VHPET_NUM_TIMERS)
668 		data = 0;
669 done:
670 	VHPET_UNLOCK(vhpet);
671 
672 	if (size == 4) {
673 		if (offset & 0x4)
674 			data >>= 32;
675 	}
676 	*rval = data;
677 	return (0);
678 }
679 
680 struct vhpet *
vhpet_init(struct vm * vm)681 vhpet_init(struct vm *vm)
682 {
683 	int i, pincount;
684 	struct vhpet *vhpet;
685 	uint64_t allowed_irqs;
686 	struct vhpet_callout_arg *arg;
687 
688 	vhpet = kmem_zalloc(sizeof (struct vhpet), KM_SLEEP);
689 	vhpet->vm = vm;
690 	mutex_init(&vhpet->lock, NULL, MUTEX_ADAPTIVE, NULL);
691 
692 	pincount = vioapic_pincount(vm);
693 	if (pincount >= 32)
694 		allowed_irqs = 0xff000000;	/* irqs 24-31 */
695 	else if (pincount >= 20)
696 		allowed_irqs = 0xf << (pincount - 4);	/* 4 upper irqs */
697 	else
698 		allowed_irqs = 0;
699 
700 	/*
701 	 * Initialize HPET timer hardware state.
702 	 */
703 	for (i = 0; i < VHPET_NUM_TIMERS; i++) {
704 		vhpet->timer[i].cap_config = allowed_irqs << 32;
705 		vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT;
706 		vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL;
707 
708 		vhpet->timer[i].compval = 0xffffffff;
709 		callout_init(&vhpet->timer[i].callout, 1);
710 
711 		arg = &vhpet->timer[i].arg;
712 		arg->vhpet = vhpet;
713 		arg->timer_num = i;
714 	}
715 
716 	return (vhpet);
717 }
718 
719 void
vhpet_cleanup(struct vhpet * vhpet)720 vhpet_cleanup(struct vhpet *vhpet)
721 {
722 	int i;
723 
724 	for (i = 0; i < VHPET_NUM_TIMERS; i++)
725 		callout_drain(&vhpet->timer[i].callout);
726 
727 	mutex_destroy(&vhpet->lock);
728 	kmem_free(vhpet, sizeof (*vhpet));
729 }
730 
731 int
vhpet_getcap(struct vm_hpet_cap * cap)732 vhpet_getcap(struct vm_hpet_cap *cap)
733 {
734 
735 	cap->capabilities = vhpet_capabilities();
736 	return (0);
737 }
738 void
vhpet_localize_resources(struct vhpet * vhpet)739 vhpet_localize_resources(struct vhpet *vhpet)
740 {
741 	for (uint_t i = 0; i < VHPET_NUM_TIMERS; i++) {
742 		vmm_glue_callout_localize(&vhpet->timer[i].callout);
743 	}
744 }
745 
746 void
vhpet_pause(struct vhpet * vhpet)747 vhpet_pause(struct vhpet *vhpet)
748 {
749 	VHPET_LOCK(vhpet);
750 	for (uint_t i = 0; i < VHPET_NUM_TIMERS; i++) {
751 		struct vhpet_timer *timer = &vhpet->timer[i];
752 
753 		callout_stop(&timer->callout);
754 	}
755 	VHPET_UNLOCK(vhpet);
756 }
757 
758 void
vhpet_resume(struct vhpet * vhpet)759 vhpet_resume(struct vhpet *vhpet)
760 {
761 	VHPET_LOCK(vhpet);
762 	for (uint_t i = 0; i < VHPET_NUM_TIMERS; i++) {
763 		struct vhpet_timer *timer = &vhpet->timer[i];
764 
765 		if (timer->callout_expire != 0) {
766 			callout_reset_hrtime(&timer->callout,
767 			    timer->callout_expire, vhpet_handler,
768 			    &timer->arg, C_ABSOLUTE);
769 		}
770 	}
771 	VHPET_UNLOCK(vhpet);
772 }
773 
774 static int
vhpet_data_read(void * datap,const vmm_data_req_t * req)775 vhpet_data_read(void *datap, const vmm_data_req_t *req)
776 {
777 	VERIFY3U(req->vdr_class, ==, VDC_HPET);
778 	VERIFY3U(req->vdr_version, ==, 1);
779 	VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_hpet_v1));
780 
781 	struct vhpet *vhpet = datap;
782 	struct vdi_hpet_v1 *out = req->vdr_data;
783 
784 	VHPET_LOCK(vhpet);
785 	out->vh_config = vhpet->config;
786 	out->vh_isr = vhpet->isr;
787 	out->vh_count_base = vhpet->base_count;
788 	out->vh_time_base = vm_normalize_hrtime(vhpet->vm, vhpet->base_time);
789 	for (uint_t i = 0; i < 8; i++) {
790 		const struct vhpet_timer *timer = &vhpet->timer[i];
791 		struct vdi_hpet_timer_v1 *timer_out = &out->vh_timers[i];
792 
793 		timer_out->vht_config = timer->cap_config;
794 		timer_out->vht_msi = timer->msireg;
795 		timer_out->vht_comp_val = timer->compval;
796 		timer_out->vht_comp_rate = timer->comprate;
797 		if (timer->callout_expire != 0) {
798 			timer_out->vht_time_target =
799 			    vm_normalize_hrtime(vhpet->vm,
800 			    timer->callout_expire);
801 		} else {
802 			timer_out->vht_time_target = 0;
803 		}
804 	}
805 	VHPET_UNLOCK(vhpet);
806 
807 	return (0);
808 }
809 
810 enum vhpet_validation_error {
811 	VVE_OK,
812 	VVE_BAD_CONFIG,
813 	VVE_BAD_BASE_TIME,
814 	VVE_BAD_ISR,
815 	VVE_BAD_TIMER_CONFIG,
816 	VVE_BAD_TIMER_ISR,
817 	VVE_BAD_TIMER_TIME,
818 };
819 
820 static enum vhpet_validation_error
vhpet_data_validate(const vmm_data_req_t * req,struct vm * vm)821 vhpet_data_validate(const vmm_data_req_t *req, struct vm *vm)
822 {
823 	ASSERT(req->vdr_version == 1 &&
824 	    req->vdr_len >= sizeof (struct vdi_hpet_v1));
825 	const struct vdi_hpet_v1 *src = req->vdr_data;
826 
827 	/* LegacyReplacement Routing is not supported */
828 	if ((src->vh_config & HPET_CNF_LEG_RT) != 0) {
829 		return (VVE_BAD_CONFIG);
830 	}
831 
832 	/* A base time in the future makes no sense */
833 	const hrtime_t base_time = vm_denormalize_hrtime(vm, src->vh_time_base);
834 	if (base_time > gethrtime()) {
835 		return (VVE_BAD_BASE_TIME);
836 	}
837 
838 	/* All asserted ISRs must be associated with an existing timer */
839 	if ((src->vh_isr & ~(uint64_t)((1 << VHPET_NUM_TIMERS) - 1)) != 0) {
840 		return (VVE_BAD_ISR);
841 	}
842 
843 	for (uint_t i = 0; i < 8; i++) {
844 		const struct vdi_hpet_timer_v1 *timer = &src->vh_timers[i];
845 
846 		const bool msi_enabled =
847 		    (timer->vht_config & HPET_TCNF_FSB_EN) != 0;
848 		const bool level_triggered =
849 		    (timer->vht_config & HPET_TCNF_INT_TYPE) != 0;
850 		const bool irq_asserted = (src->vh_isr & (1 << i)) != 0;
851 		const uint32_t allowed_irqs = (timer->vht_config >> 32);
852 		const uint32_t irq_pin =
853 		    (timer->vht_config & HPET_TCNF_INT_ROUTE) >> 9;
854 
855 		if (msi_enabled) {
856 			if (level_triggered) {
857 				return (VVE_BAD_TIMER_CONFIG);
858 			}
859 		} else {
860 			/*
861 			 * Ensure interrupt route is valid as ensured by the
862 			 * logic in vhpet_timer_update_config.
863 			 */
864 			if (irq_pin != 0 &&
865 			    (allowed_irqs & (1 << irq_pin)) == 0) {
866 				return (VVE_BAD_TIMER_CONFIG);
867 			}
868 		}
869 		if (irq_asserted && !level_triggered) {
870 			return (VVE_BAD_TIMER_ISR);
871 		}
872 
873 		if (timer->vht_time_target != 0) {
874 			/*
875 			 * A timer scheduled earlier than the base time of the
876 			 * entire HPET makes no sense.
877 			 */
878 			const uint64_t timer_target =
879 			    vm_denormalize_hrtime(vm, timer->vht_time_target);
880 			if (timer_target < base_time) {
881 				return (VVE_BAD_TIMER_TIME);
882 			}
883 		}
884 	}
885 
886 	return (VVE_OK);
887 }
888 
889 static int
vhpet_data_write(void * datap,const vmm_data_req_t * req)890 vhpet_data_write(void *datap, const vmm_data_req_t *req)
891 {
892 	VERIFY3U(req->vdr_class, ==, VDC_HPET);
893 	VERIFY3U(req->vdr_version, ==, 1);
894 	VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_hpet_v1));
895 
896 	struct vhpet *vhpet = datap;
897 
898 	if (vhpet_data_validate(req, vhpet->vm) != VVE_OK) {
899 		return (EINVAL);
900 	}
901 	const struct vdi_hpet_v1 *src = req->vdr_data;
902 
903 	VHPET_LOCK(vhpet);
904 	vhpet->config = src->vh_config;
905 	vhpet->isr = src->vh_isr;
906 	vhpet->base_count = src->vh_count_base;
907 	vhpet->base_time = vm_denormalize_hrtime(vhpet->vm, src->vh_time_base);
908 
909 	for (uint_t i = 0; i < 8; i++) {
910 		struct vhpet_timer *timer = &vhpet->timer[i];
911 		const struct vdi_hpet_timer_v1 *timer_src = &src->vh_timers[i];
912 
913 		timer->cap_config = timer_src->vht_config;
914 		timer->msireg = timer_src->vht_msi;
915 		timer->compval = timer_src->vht_comp_val;
916 		timer->comprate = timer_src->vht_comp_rate;
917 
918 		/*
919 		 * For now, any state associating an IOAPIC pin with a given
920 		 * timer is not kept in sync. (We will not increment or
921 		 * decrement a pin level based on the timer state.)  It is left
922 		 * to the consumer to keep those pin levels maintained if
923 		 * modifying either the HPET or the IOAPIC.
924 		 *
925 		 * If both the HPET and IOAPIC are exported and then imported,
926 		 * this will occur naturally, as any asserted IOAPIC pin level
927 		 * from the HPET would come along for the ride.
928 		 */
929 
930 		if (timer_src->vht_time_target != 0) {
931 			timer->callout_expire = vm_denormalize_hrtime(vhpet->vm,
932 			    timer_src->vht_time_target);
933 
934 			if (!vm_is_paused(vhpet->vm)) {
935 				callout_reset_hrtime(&timer->callout,
936 				    timer->callout_expire, vhpet_handler,
937 				    &timer->arg, C_ABSOLUTE);
938 			}
939 		} else {
940 			timer->callout_expire = 0;
941 		}
942 	}
943 	VHPET_UNLOCK(vhpet);
944 	return (0);
945 }
946 
947 static const vmm_data_version_entry_t hpet_v1 = {
948 	.vdve_class = VDC_HPET,
949 	.vdve_version = 1,
950 	.vdve_len_expect = sizeof (struct vdi_hpet_v1),
951 	.vdve_readf = vhpet_data_read,
952 	.vdve_writef = vhpet_data_write,
953 };
954 VMM_DATA_VERSION(hpet_v1);
955