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