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