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