]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/vmm/io/vhpet.c
Add HPET device emulation to bhyve.
[FreeBSD/FreeBSD.git] / sys / amd64 / vmm / io / vhpet.c
1 /*-
2  * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
3  * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/lock.h>
35 #include <sys/mutex.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/systm.h>
39 #include <sys/cpuset.h>
40
41 #include <dev/acpica/acpi_hpet.h>
42
43 #include <machine/vmm.h>
44 #include <machine/vmm_dev.h>
45
46 #include "vmm_lapic.h"
47 #include "vioapic.h"
48 #include "vhpet.h"
49
50 #include "vmm_ktr.h"
51
52 static MALLOC_DEFINE(M_VHPET, "vhpet", "bhyve virtual hpet");
53
54 #define HPET_FREQ       10000000                /* 10.0 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 {
74         struct vm       *vm;
75         struct mtx      mtx;
76         sbintime_t      freq_sbt;
77
78         uint64_t        config;         /* Configuration */
79         uint64_t        isr;            /* Interrupt Status */
80         uint32_t        counter;        /* HPET Counter */
81         sbintime_t      counter_sbt;
82
83         struct {
84                 uint64_t        cap_config;     /* Configuration */
85                 uint64_t        msireg;         /* FSB interrupt routing */
86                 uint32_t        compval;        /* Comparator */
87                 uint32_t        comprate;
88                 struct callout  callout;
89                 struct vhpet_callout_arg arg;
90         } timer[VHPET_NUM_TIMERS];
91 };
92
93 #define VHPET_LOCK(vhp)         mtx_lock(&((vhp)->mtx))
94 #define VHPET_UNLOCK(vhp)       mtx_unlock(&((vhp)->mtx))
95
96 static uint64_t
97 vhpet_capabilities(void)
98 {
99         uint64_t cap = 0;
100
101         cap |= 0x8086 << 16;                    /* vendor id */
102         cap |= HPET_CAP_LEG_RT;                 /* legacy routing capable */
103         cap |= (VHPET_NUM_TIMERS - 1) << 8;     /* number of timers */
104         cap |= 1;                               /* revision */
105         cap &= ~HPET_CAP_COUNT_SIZE;            /* 32-bit timer */
106
107         cap &= 0xffffffff;
108         cap |= (FS_PER_S / HPET_FREQ) << 32;    /* tick period in fs */
109
110         return (cap);
111 }
112
113 static __inline bool
114 vhpet_counter_enabled(struct vhpet *vhpet)
115 {
116
117         return ((vhpet->config & HPET_CNF_ENABLE) ? true : false);
118 }
119
120 static __inline bool
121 vhpet_timer_msi_enabled(struct vhpet *vhpet, int n)
122 {
123         const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN;
124
125         /*
126          * LegacyReplacement Route configuration takes precedence over MSI
127          * for timers 0 and 1.
128          */
129         if (n == 0 || n == 1) {
130                 if (vhpet->config & HPET_CNF_LEG_RT)
131                         return (false);
132         }
133
134         if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable)
135                 return (true);
136         else
137                 return (false);
138 }
139
140 static __inline int
141 vhpet_timer_ioapic_pin(struct vhpet *vhpet, int n)
142 {
143         /*
144          * If the timer is configured to use MSI then treat it as if the
145          * timer is not connected to the ioapic.
146          */
147         if (vhpet_timer_msi_enabled(vhpet, n))
148                 return (0);
149
150         if (vhpet->config & HPET_CNF_LEG_RT) {
151                 /*
152                  * In "legacy routing" timers 0 and 1 are connected to
153                  * ioapic pins 2 and 8 respectively.
154                  */
155                 switch (n) {
156                 case 0:
157                         return (2);
158                 case 1:
159                         return (8);
160                 }
161         }
162
163         return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
164 }
165
166 static uint32_t
167 vhpet_counter(struct vhpet *vhpet, bool latch)
168 {
169         uint32_t val;
170         sbintime_t cur_sbt, delta_sbt;
171
172         val = vhpet->counter;
173         if (vhpet_counter_enabled(vhpet)) {
174                 cur_sbt = sbinuptime();
175                 delta_sbt = cur_sbt - vhpet->counter_sbt;
176                 KASSERT(delta_sbt >= 0,
177                     ("vhpet counter went backwards: %#lx to %#lx",
178                     vhpet->counter_sbt, cur_sbt));
179                 val += delta_sbt / vhpet->freq_sbt;
180
181                 /*
182                  * Keep track of the last value of the main counter that
183                  * was read by the guest.
184                  */
185                 if (latch) {
186                         vhpet->counter = val;
187                         vhpet->counter_sbt = cur_sbt;
188                 }
189         }
190
191         return (val);
192 }
193
194 static void
195 vhpet_timer_clear_isr(struct vhpet *vhpet, int n)
196 {
197         int pin;
198
199         if (vhpet->isr & (1 << n)) {
200                 pin = vhpet_timer_ioapic_pin(vhpet, n);
201                 KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n));
202                 vioapic_deassert_irq(vhpet->vm, pin);
203                 vhpet->isr &= ~(1 << n);
204         }
205 }
206
207 static __inline bool
208 vhpet_periodic_timer(struct vhpet *vhpet, int n)
209 {
210
211         return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0);
212 }
213
214 static __inline bool
215 vhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n)
216 {
217
218         return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0);
219 }
220
221 static __inline bool
222 vhpet_timer_edge_trig(struct vhpet *vhpet, int n)
223 {
224
225         KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: "
226             "timer %d is using MSI", n));
227
228         /* The legacy replacement interrupts are always edge triggered */
229         if (vhpet->config & HPET_CNF_LEG_RT) {
230                 if (n == 0 || n == 1)
231                         return (true);
232         }
233
234         if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0)
235                 return (true);
236         else
237                 return (false);
238 }
239
240 static void
241 vhpet_timer_interrupt(struct vhpet *vhpet, int n)
242 {
243         int apicid, vector, vcpuid, pin;
244         cpuset_t dmask;
245
246         /* If interrupts are not enabled for this timer then just return. */
247         if (!vhpet_timer_interrupt_enabled(vhpet, n))
248                 return;
249
250         /*
251          * If a level triggered interrupt is already asserted then just return.
252          */
253         if ((vhpet->isr & (1 << n)) != 0) {
254                 VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n);
255                 return;
256         }
257
258         if (vhpet_timer_msi_enabled(vhpet, n)) {
259                 /*
260                  * XXX should have an API 'vlapic_deliver_msi(vm, addr, data)'
261                  * - assuming physical delivery mode
262                  * - no need to interpret contents of 'msireg' here
263                  */
264                 vector = vhpet->timer[n].msireg & 0xff;
265                 apicid = (vhpet->timer[n].msireg >> (32 + 12)) & 0xff;
266                 if (apicid != 0xff) {
267                         /* unicast */
268                         vcpuid = vm_apicid2vcpuid(vhpet->vm, apicid);
269                         lapic_set_intr(vhpet->vm, vcpuid, vector);
270                 } else {
271                         /* broadcast */
272                         dmask = vm_active_cpus(vhpet->vm);
273                         while ((vcpuid = CPU_FFS(&dmask)) != 0) {
274                                 vcpuid--;
275                                 CPU_CLR(vcpuid, &dmask);
276                                 lapic_set_intr(vhpet->vm, vcpuid, vector);
277                         }
278                 }
279                 return;
280         }       
281
282         pin = vhpet_timer_ioapic_pin(vhpet, n);
283         if (pin == 0) {
284                 VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n);
285                 return;
286         }
287
288         if (vhpet_timer_edge_trig(vhpet, n)) {
289                 vioapic_pulse_irq(vhpet->vm, pin);
290         } else {
291                 vhpet->isr |= 1 << n;
292                 vioapic_assert_irq(vhpet->vm, pin);
293         }
294 }
295
296 static void
297 vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter)
298 {
299         uint32_t compval, comprate, compnext;
300
301         KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n));
302
303         compval = vhpet->timer[n].compval;
304         comprate = vhpet->timer[n].comprate;
305
306         /*
307          * Calculate the comparator value to be used for the next periodic
308          * interrupt.
309          *
310          * This function is commonly called from the callout handler.
311          * In this scenario the 'counter' is ahead of 'compval'. To find
312          * the next value to program into the accumulator we divide the
313          * number space between 'compval' and 'counter' into 'comprate'
314          * sized units. The 'compval' is rounded up such that is "ahead"
315          * of 'counter'.
316          */
317         compnext = compval + ((counter - compval) / comprate + 1) * comprate;
318
319         vhpet->timer[n].compval = compnext;
320 }
321
322 static void
323 vhpet_handler(void *a)
324 {
325         int n;
326         uint32_t counter;
327         sbintime_t sbt;
328         struct vhpet *vhpet;
329         struct callout *callout;
330         struct vhpet_callout_arg *arg;
331
332         arg = a;
333         vhpet = arg->vhpet;
334         n = arg->timer_num;
335         callout = &vhpet->timer[n].callout;
336
337         VM_CTR1(vhpet->vm, "hpet t%d fired", n);
338
339         VHPET_LOCK(vhpet);
340
341         if (callout_pending(callout))           /* callout was reset */
342                 goto done;
343
344         if (!callout_active(callout))           /* callout was stopped */
345                 goto done;
346
347         callout_deactivate(callout);
348
349         if (!vhpet_counter_enabled(vhpet))
350                 panic("vhpet(%p) callout with counter disabled", vhpet);
351
352         counter = vhpet_counter(vhpet, false);
353
354         /* Update the accumulator for periodic timers */
355         if (vhpet->timer[n].comprate != 0)
356                 vhpet_adjust_compval(vhpet, n, counter);
357
358         sbt = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt;
359         callout_reset_sbt(callout, sbt, 0, vhpet_handler, arg, 0);
360         vhpet_timer_interrupt(vhpet, n);
361 done:
362         VHPET_UNLOCK(vhpet);
363         return;
364 }
365
366 static void
367 vhpet_stop_timer(struct vhpet *vhpet, int n)
368 {
369
370         callout_stop(&vhpet->timer[n].callout);
371         vhpet_timer_clear_isr(vhpet, n);
372 }
373
374 static void
375 vhpet_start_timer(struct vhpet *vhpet, int n)
376 {
377         uint32_t counter, delta, delta2;
378         sbintime_t sbt;
379
380         counter = vhpet_counter(vhpet, false);
381
382         if (vhpet->timer[n].comprate != 0)
383                 vhpet_adjust_compval(vhpet, n, counter);
384
385         delta = vhpet->timer[n].compval - counter;
386
387         /*
388          * In one-shot mode the guest will typically read the main counter
389          * before programming the comparator. We can use this heuristic to
390          * figure out whether the expiration time is in the past. If this
391          * is the case we schedule the callout to fire immediately.
392          */
393         if (!vhpet_periodic_timer(vhpet, n)) {
394                 delta2 = vhpet->timer[n].compval - vhpet->counter;
395                 if (delta > delta2) {
396                         VM_CTR3(vhpet->vm, "hpet t%d comparator value is in "
397                             "the past: %u/%u/%u", counter,
398                             vhpet->timer[n].compval, vhpet->counter);
399                         delta = 0;
400                 }
401         }
402
403         sbt = delta * vhpet->freq_sbt;
404         callout_reset_sbt(&vhpet->timer[n].callout, sbt, 0, vhpet_handler,
405             &vhpet->timer[n].arg, 0);
406 }
407
408 static void
409 vhpet_start_counting(struct vhpet *vhpet)
410 {
411         int i;
412
413         vhpet->counter_sbt = sbinuptime();
414         for (i = 0; i < VHPET_NUM_TIMERS; i++)
415                 vhpet_start_timer(vhpet, i);
416 }
417
418 static void
419 vhpet_stop_counting(struct vhpet *vhpet)
420 {
421         int i;
422
423         for (i = 0; i < VHPET_NUM_TIMERS; i++)
424                 vhpet_stop_timer(vhpet, i);
425 }
426
427 static __inline void
428 update_register(uint64_t *regptr, uint64_t data, uint64_t mask)
429 {
430
431         *regptr &= ~mask;
432         *regptr |= (data & mask);
433 }
434
435 static void
436 vhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data,
437     uint64_t mask)
438 {
439         bool clear_isr;
440         int old_pin, new_pin;
441         uint32_t allowed_irqs;
442         uint64_t oldval, newval;
443
444         if (vhpet_timer_msi_enabled(vhpet, n) ||
445             vhpet_timer_edge_trig(vhpet, n)) {
446                 if (vhpet->isr & (1 << n))
447                         panic("vhpet timer %d isr should not be asserted", n);
448         }
449         old_pin = vhpet_timer_ioapic_pin(vhpet, n);
450         oldval = vhpet->timer[n].cap_config;
451
452         newval = oldval;
453         update_register(&newval, data, mask);
454         newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE);
455         newval |= oldval & HPET_TCAP_RO_MASK;
456
457         if (newval == oldval)
458                 return;
459
460         vhpet->timer[n].cap_config = newval;
461         VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval);
462
463         /*
464          * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field.
465          * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set
466          * it to the default value of 0.
467          */
468         allowed_irqs = vhpet->timer[n].cap_config >> 32;
469         new_pin = vhpet_timer_ioapic_pin(vhpet, n);
470         if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) {
471                 VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, "
472                     "allowed_irqs 0x%08x", n, new_pin, allowed_irqs);
473                 new_pin = 0;
474                 vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE;
475         }
476
477         if (!vhpet_periodic_timer(vhpet, n))
478                 vhpet->timer[n].comprate = 0;
479
480         /*
481          * If the timer's ISR bit is set then clear it in the following cases:
482          * - interrupt is disabled
483          * - interrupt type is changed from level to edge or fsb.
484          * - interrupt routing is changed
485          *
486          * This is to ensure that this timer's level triggered interrupt does
487          * not remain asserted forever.
488          */
489         if (vhpet->isr & (1 << n)) {
490                 KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d",
491                     n, old_pin));
492                 if (!vhpet_timer_interrupt_enabled(vhpet, n))
493                         clear_isr = true;
494                 else if (vhpet_timer_msi_enabled(vhpet, n))
495                         clear_isr = true;
496                 else if (vhpet_timer_edge_trig(vhpet, n))
497                         clear_isr = true;
498                 else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin)
499                         clear_isr = true;
500                 else
501                         clear_isr = false;
502
503                 if (clear_isr) {
504                         VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to "
505                             "configuration change", n);
506                         vioapic_deassert_irq(vhpet->vm, old_pin);
507                         vhpet->isr &= ~(1 << n);
508                 }
509         }
510 }
511
512 int
513 vhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size,
514     void *arg)
515 {
516         struct vhpet *vhpet;
517         uint64_t data, mask, oldval, val64;
518         uint32_t isr_clear_mask, old_compval, old_comprate;
519         int i, offset;
520
521         vhpet = vm_hpet(vm);
522         offset = gpa - VHPET_BASE;
523
524         VHPET_LOCK(vhpet);
525
526         /* Accesses to the HPET should be 4 or 8 bytes wide */
527         switch (size) {
528         case 8:
529                 mask = 0xffffffffffffffff;
530                 data = val;
531                 break;
532         case 4:
533                 mask = 0xffffffff;
534                 data = val;
535                 if ((offset & 0x4) != 0) {
536                         mask <<= 32;
537                         data <<= 32;
538                 } 
539                 break;
540         default:
541                 VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
542                     "offset 0x%08x, size %d", offset, size);
543                 goto done;
544         }
545
546         /* Access to the HPET should be naturally aligned to its width */
547         if (offset & (size - 1)) {
548                 VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
549                     "offset 0x%08x, size %d", offset, size);
550                 goto done;
551         }
552
553         if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
554                 oldval = vhpet->config;
555                 update_register(&vhpet->config, data, mask);
556                 if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) {
557                         if (vhpet_counter_enabled(vhpet)) {
558                                 vhpet_start_counting(vhpet);
559                                 VM_CTR0(vhpet->vm, "hpet enabled");
560                         } else {
561                                 vhpet_stop_counting(vhpet);
562                                 VM_CTR0(vhpet->vm, "hpet disabled");
563                         }
564                 }
565                 goto done;
566         }
567
568         if (offset == HPET_ISR || offset == HPET_ISR + 4) {
569                 isr_clear_mask = vhpet->isr & data;
570                 for (i = 0; i < VHPET_NUM_TIMERS; i++) {
571                         if ((isr_clear_mask & (1 << i)) != 0) {
572                                 VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i);
573                                 vhpet_timer_clear_isr(vhpet, i);
574                         }
575                 }
576                 goto done;
577         }
578
579         if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
580                 /* Zero-extend the counter to 64-bits before updating it */
581                 val64 = vhpet->counter;
582                 update_register(&val64, data, mask);
583                 vhpet->counter = val64;
584                 if (vhpet_counter_enabled(vhpet))
585                         vhpet_start_counting(vhpet);
586                 goto done;
587         }
588
589         for (i = 0; i < VHPET_NUM_TIMERS; i++) {
590                 if (offset == HPET_TIMER_CAP_CNF(i) ||
591                     offset == HPET_TIMER_CAP_CNF(i) + 4) {
592                         vhpet_timer_update_config(vhpet, i, data, mask);
593                         break;
594                 }
595
596                 if (offset == HPET_TIMER_COMPARATOR(i) ||
597                     offset == HPET_TIMER_COMPARATOR(i) + 4) {
598                         old_compval = vhpet->timer[i].compval;
599                         old_comprate = vhpet->timer[i].comprate;
600                         if (vhpet_periodic_timer(vhpet, i)) {
601                                 /*
602                                  * In periodic mode writes to the comparator
603                                  * change the 'compval' register only if the
604                                  * HPET_TCNF_VAL_SET bit is set in the config
605                                  * register.
606                                  */
607                                 val64 = vhpet->timer[i].comprate;
608                                 update_register(&val64, data, mask);
609                                 vhpet->timer[i].comprate = val64;
610                                 if ((vhpet->timer[i].cap_config &
611                                     HPET_TCNF_VAL_SET) != 0) {
612                                         vhpet->timer[i].compval = val64;
613                                 }
614                         } else {
615                                 KASSERT(vhpet->timer[i].comprate == 0,
616                                     ("vhpet one-shot timer %d has invalid "
617                                     "rate %u", i, vhpet->timer[i].comprate));
618                                 val64 = vhpet->timer[i].compval;
619                                 update_register(&val64, data, mask);
620                                 vhpet->timer[i].compval = val64;
621                         }
622                         vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET;
623
624                         if (vhpet->timer[i].compval != old_compval ||
625                             vhpet->timer[i].comprate != old_comprate) {
626                                 if (vhpet_counter_enabled(vhpet))
627                                         vhpet_start_timer(vhpet, i);
628                         }
629                         break;
630                 }
631
632                 if (offset == HPET_TIMER_FSB_VAL(i) ||
633                     offset == HPET_TIMER_FSB_ADDR(i)) {
634                         update_register(&vhpet->timer[i].msireg, data, mask);
635                         break;
636                 }
637         }
638 done:
639         VHPET_UNLOCK(vhpet);
640         return (0);
641 }
642
643 int
644 vhpet_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval, int size,
645     void *arg)
646 {
647         int i, offset;
648         struct vhpet *vhpet;
649         uint64_t data;
650
651         vhpet = vm_hpet(vm);
652         offset = gpa - VHPET_BASE;
653
654         VHPET_LOCK(vhpet);
655
656         /* Accesses to the HPET should be 4 or 8 bytes wide */
657         if (size != 4 && size != 8) {
658                 VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
659                     "offset 0x%08x, size %d", offset, size);
660                 data = 0;
661                 goto done;
662         }
663
664         /* Access to the HPET should be naturally aligned to its width */
665         if (offset & (size - 1)) {
666                 VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
667                     "offset 0x%08x, size %d", offset, size);
668                 data = 0;
669                 goto done;
670         }
671
672         if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) {
673                 data = vhpet_capabilities();
674                 goto done;      
675         }
676
677         if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
678                 data = vhpet->config;
679                 goto done;
680         }
681
682         if (offset == HPET_ISR || offset == HPET_ISR + 4) {
683                 data = vhpet->isr;
684                 goto done;
685         }
686
687         if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
688                 data = vhpet_counter(vhpet, true);
689                 goto done;
690         }
691
692         for (i = 0; i < VHPET_NUM_TIMERS; i++) {
693                 if (offset == HPET_TIMER_CAP_CNF(i) ||
694                     offset == HPET_TIMER_CAP_CNF(i) + 4) {
695                         data = vhpet->timer[i].cap_config;
696                         break;
697                 }
698
699                 if (offset == HPET_TIMER_COMPARATOR(i) ||
700                     offset == HPET_TIMER_COMPARATOR(i) + 4) {
701                         data = vhpet->timer[i].compval;
702                         break;
703                 }
704
705                 if (offset == HPET_TIMER_FSB_VAL(i) ||
706                     offset == HPET_TIMER_FSB_ADDR(i)) {
707                         data = vhpet->timer[i].msireg;
708                         break;
709                 }
710         }
711
712         if (i >= VHPET_NUM_TIMERS)
713                 data = 0;
714 done:
715         VHPET_UNLOCK(vhpet);
716
717         if (size == 4) {
718                 if (offset & 0x4)
719                         data >>= 32;
720         }
721         *rval = data;
722         return (0);
723 }
724
725 struct vhpet *
726 vhpet_init(struct vm *vm)
727 {
728         int i;
729         struct vhpet *vhpet;
730         struct vhpet_callout_arg *arg;
731         struct bintime bt;
732
733         vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO);
734         vhpet->vm = vm;
735         mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF);
736
737         FREQ2BT(HPET_FREQ, &bt);
738         vhpet->freq_sbt = bttosbt(bt);
739
740         /*
741          * Initialize HPET timer hardware state.
742          */
743         for (i = 0; i < VHPET_NUM_TIMERS; i++) {
744                 vhpet->timer[i].cap_config = 0UL << 32 |
745                     HPET_TCAP_FSB_INT_DEL | HPET_TCAP_PER_INT;
746                 vhpet->timer[i].compval = 0xffffffff;
747                 callout_init(&vhpet->timer[i].callout, 1);
748
749                 arg = &vhpet->timer[i].arg;
750                 arg->vhpet = vhpet;
751                 arg->timer_num = i;
752         }
753
754         return (vhpet);
755 }
756
757 void
758 vhpet_cleanup(struct vhpet *vhpet)
759 {
760         int i;
761
762         for (i = 0; i < VHPET_NUM_TIMERS; i++)
763                 callout_drain(&vhpet->timer[i].callout);
764
765         free(vhpet, M_VHPET);
766 }
767
768 int
769 vhpet_getcap(struct vm_hpet_cap *cap)
770 {
771
772         cap->capabilities = vhpet_capabilities();
773         return (0);
774 }