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