]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/vmm/io/vhpet.c
Add an API to deliver message signalled interrupts to vcpus. This allows
[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 pin;
244
245         /* If interrupts are not enabled for this timer then just return. */
246         if (!vhpet_timer_interrupt_enabled(vhpet, n))
247                 return;
248
249         /*
250          * If a level triggered interrupt is already asserted then just return.
251          */
252         if ((vhpet->isr & (1 << n)) != 0) {
253                 VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n);
254                 return;
255         }
256
257         if (vhpet_timer_msi_enabled(vhpet, n)) {
258                 lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32,
259                     vhpet->timer[n].msireg & 0xffffffff);
260                 return;
261         }       
262
263         pin = vhpet_timer_ioapic_pin(vhpet, n);
264         if (pin == 0) {
265                 VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n);
266                 return;
267         }
268
269         if (vhpet_timer_edge_trig(vhpet, n)) {
270                 vioapic_pulse_irq(vhpet->vm, pin);
271         } else {
272                 vhpet->isr |= 1 << n;
273                 vioapic_assert_irq(vhpet->vm, pin);
274         }
275 }
276
277 static void
278 vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter)
279 {
280         uint32_t compval, comprate, compnext;
281
282         KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n));
283
284         compval = vhpet->timer[n].compval;
285         comprate = vhpet->timer[n].comprate;
286
287         /*
288          * Calculate the comparator value to be used for the next periodic
289          * interrupt.
290          *
291          * This function is commonly called from the callout handler.
292          * In this scenario the 'counter' is ahead of 'compval'. To find
293          * the next value to program into the accumulator we divide the
294          * number space between 'compval' and 'counter' into 'comprate'
295          * sized units. The 'compval' is rounded up such that is "ahead"
296          * of 'counter'.
297          */
298         compnext = compval + ((counter - compval) / comprate + 1) * comprate;
299
300         vhpet->timer[n].compval = compnext;
301 }
302
303 static void
304 vhpet_handler(void *a)
305 {
306         int n;
307         uint32_t counter;
308         sbintime_t sbt;
309         struct vhpet *vhpet;
310         struct callout *callout;
311         struct vhpet_callout_arg *arg;
312
313         arg = a;
314         vhpet = arg->vhpet;
315         n = arg->timer_num;
316         callout = &vhpet->timer[n].callout;
317
318         VM_CTR1(vhpet->vm, "hpet t%d fired", n);
319
320         VHPET_LOCK(vhpet);
321
322         if (callout_pending(callout))           /* callout was reset */
323                 goto done;
324
325         if (!callout_active(callout))           /* callout was stopped */
326                 goto done;
327
328         callout_deactivate(callout);
329
330         if (!vhpet_counter_enabled(vhpet))
331                 panic("vhpet(%p) callout with counter disabled", vhpet);
332
333         counter = vhpet_counter(vhpet, false);
334
335         /* Update the accumulator for periodic timers */
336         if (vhpet->timer[n].comprate != 0)
337                 vhpet_adjust_compval(vhpet, n, counter);
338
339         sbt = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt;
340         callout_reset_sbt(callout, sbt, 0, vhpet_handler, arg, 0);
341         vhpet_timer_interrupt(vhpet, n);
342 done:
343         VHPET_UNLOCK(vhpet);
344         return;
345 }
346
347 static void
348 vhpet_stop_timer(struct vhpet *vhpet, int n)
349 {
350
351         callout_stop(&vhpet->timer[n].callout);
352         vhpet_timer_clear_isr(vhpet, n);
353 }
354
355 static void
356 vhpet_start_timer(struct vhpet *vhpet, int n)
357 {
358         uint32_t counter, delta, delta2;
359         sbintime_t sbt;
360
361         counter = vhpet_counter(vhpet, false);
362
363         if (vhpet->timer[n].comprate != 0)
364                 vhpet_adjust_compval(vhpet, n, counter);
365
366         delta = vhpet->timer[n].compval - counter;
367
368         /*
369          * In one-shot mode the guest will typically read the main counter
370          * before programming the comparator. We can use this heuristic to
371          * figure out whether the expiration time is in the past. If this
372          * is the case we schedule the callout to fire immediately.
373          */
374         if (!vhpet_periodic_timer(vhpet, n)) {
375                 delta2 = vhpet->timer[n].compval - vhpet->counter;
376                 if (delta > delta2) {
377                         VM_CTR3(vhpet->vm, "hpet t%d comparator value is in "
378                             "the past: %u/%u/%u", counter,
379                             vhpet->timer[n].compval, vhpet->counter);
380                         delta = 0;
381                 }
382         }
383
384         sbt = delta * vhpet->freq_sbt;
385         callout_reset_sbt(&vhpet->timer[n].callout, sbt, 0, vhpet_handler,
386             &vhpet->timer[n].arg, 0);
387 }
388
389 static void
390 vhpet_start_counting(struct vhpet *vhpet)
391 {
392         int i;
393
394         vhpet->counter_sbt = sbinuptime();
395         for (i = 0; i < VHPET_NUM_TIMERS; i++)
396                 vhpet_start_timer(vhpet, i);
397 }
398
399 static void
400 vhpet_stop_counting(struct vhpet *vhpet)
401 {
402         int i;
403
404         for (i = 0; i < VHPET_NUM_TIMERS; i++)
405                 vhpet_stop_timer(vhpet, i);
406 }
407
408 static __inline void
409 update_register(uint64_t *regptr, uint64_t data, uint64_t mask)
410 {
411
412         *regptr &= ~mask;
413         *regptr |= (data & mask);
414 }
415
416 static void
417 vhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data,
418     uint64_t mask)
419 {
420         bool clear_isr;
421         int old_pin, new_pin;
422         uint32_t allowed_irqs;
423         uint64_t oldval, newval;
424
425         if (vhpet_timer_msi_enabled(vhpet, n) ||
426             vhpet_timer_edge_trig(vhpet, n)) {
427                 if (vhpet->isr & (1 << n))
428                         panic("vhpet timer %d isr should not be asserted", n);
429         }
430         old_pin = vhpet_timer_ioapic_pin(vhpet, n);
431         oldval = vhpet->timer[n].cap_config;
432
433         newval = oldval;
434         update_register(&newval, data, mask);
435         newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE);
436         newval |= oldval & HPET_TCAP_RO_MASK;
437
438         if (newval == oldval)
439                 return;
440
441         vhpet->timer[n].cap_config = newval;
442         VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval);
443
444         /*
445          * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field.
446          * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set
447          * it to the default value of 0.
448          */
449         allowed_irqs = vhpet->timer[n].cap_config >> 32;
450         new_pin = vhpet_timer_ioapic_pin(vhpet, n);
451         if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) {
452                 VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, "
453                     "allowed_irqs 0x%08x", n, new_pin, allowed_irqs);
454                 new_pin = 0;
455                 vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE;
456         }
457
458         if (!vhpet_periodic_timer(vhpet, n))
459                 vhpet->timer[n].comprate = 0;
460
461         /*
462          * If the timer's ISR bit is set then clear it in the following cases:
463          * - interrupt is disabled
464          * - interrupt type is changed from level to edge or fsb.
465          * - interrupt routing is changed
466          *
467          * This is to ensure that this timer's level triggered interrupt does
468          * not remain asserted forever.
469          */
470         if (vhpet->isr & (1 << n)) {
471                 KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d",
472                     n, old_pin));
473                 if (!vhpet_timer_interrupt_enabled(vhpet, n))
474                         clear_isr = true;
475                 else if (vhpet_timer_msi_enabled(vhpet, n))
476                         clear_isr = true;
477                 else if (vhpet_timer_edge_trig(vhpet, n))
478                         clear_isr = true;
479                 else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin)
480                         clear_isr = true;
481                 else
482                         clear_isr = false;
483
484                 if (clear_isr) {
485                         VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to "
486                             "configuration change", n);
487                         vioapic_deassert_irq(vhpet->vm, old_pin);
488                         vhpet->isr &= ~(1 << n);
489                 }
490         }
491 }
492
493 int
494 vhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size,
495     void *arg)
496 {
497         struct vhpet *vhpet;
498         uint64_t data, mask, oldval, val64;
499         uint32_t isr_clear_mask, old_compval, old_comprate;
500         int i, offset;
501
502         vhpet = vm_hpet(vm);
503         offset = gpa - VHPET_BASE;
504
505         VHPET_LOCK(vhpet);
506
507         /* Accesses to the HPET should be 4 or 8 bytes wide */
508         switch (size) {
509         case 8:
510                 mask = 0xffffffffffffffff;
511                 data = val;
512                 break;
513         case 4:
514                 mask = 0xffffffff;
515                 data = val;
516                 if ((offset & 0x4) != 0) {
517                         mask <<= 32;
518                         data <<= 32;
519                 } 
520                 break;
521         default:
522                 VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
523                     "offset 0x%08x, size %d", offset, size);
524                 goto done;
525         }
526
527         /* Access to the HPET should be naturally aligned to its width */
528         if (offset & (size - 1)) {
529                 VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
530                     "offset 0x%08x, size %d", offset, size);
531                 goto done;
532         }
533
534         if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
535                 oldval = vhpet->config;
536                 update_register(&vhpet->config, data, mask);
537                 if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) {
538                         if (vhpet_counter_enabled(vhpet)) {
539                                 vhpet_start_counting(vhpet);
540                                 VM_CTR0(vhpet->vm, "hpet enabled");
541                         } else {
542                                 vhpet_stop_counting(vhpet);
543                                 VM_CTR0(vhpet->vm, "hpet disabled");
544                         }
545                 }
546                 goto done;
547         }
548
549         if (offset == HPET_ISR || offset == HPET_ISR + 4) {
550                 isr_clear_mask = vhpet->isr & data;
551                 for (i = 0; i < VHPET_NUM_TIMERS; i++) {
552                         if ((isr_clear_mask & (1 << i)) != 0) {
553                                 VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i);
554                                 vhpet_timer_clear_isr(vhpet, i);
555                         }
556                 }
557                 goto done;
558         }
559
560         if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
561                 /* Zero-extend the counter to 64-bits before updating it */
562                 val64 = vhpet->counter;
563                 update_register(&val64, data, mask);
564                 vhpet->counter = val64;
565                 if (vhpet_counter_enabled(vhpet))
566                         vhpet_start_counting(vhpet);
567                 goto done;
568         }
569
570         for (i = 0; i < VHPET_NUM_TIMERS; i++) {
571                 if (offset == HPET_TIMER_CAP_CNF(i) ||
572                     offset == HPET_TIMER_CAP_CNF(i) + 4) {
573                         vhpet_timer_update_config(vhpet, i, data, mask);
574                         break;
575                 }
576
577                 if (offset == HPET_TIMER_COMPARATOR(i) ||
578                     offset == HPET_TIMER_COMPARATOR(i) + 4) {
579                         old_compval = vhpet->timer[i].compval;
580                         old_comprate = vhpet->timer[i].comprate;
581                         if (vhpet_periodic_timer(vhpet, i)) {
582                                 /*
583                                  * In periodic mode writes to the comparator
584                                  * change the 'compval' register only if the
585                                  * HPET_TCNF_VAL_SET bit is set in the config
586                                  * register.
587                                  */
588                                 val64 = vhpet->timer[i].comprate;
589                                 update_register(&val64, data, mask);
590                                 vhpet->timer[i].comprate = val64;
591                                 if ((vhpet->timer[i].cap_config &
592                                     HPET_TCNF_VAL_SET) != 0) {
593                                         vhpet->timer[i].compval = val64;
594                                 }
595                         } else {
596                                 KASSERT(vhpet->timer[i].comprate == 0,
597                                     ("vhpet one-shot timer %d has invalid "
598                                     "rate %u", i, vhpet->timer[i].comprate));
599                                 val64 = vhpet->timer[i].compval;
600                                 update_register(&val64, data, mask);
601                                 vhpet->timer[i].compval = val64;
602                         }
603                         vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET;
604
605                         if (vhpet->timer[i].compval != old_compval ||
606                             vhpet->timer[i].comprate != old_comprate) {
607                                 if (vhpet_counter_enabled(vhpet))
608                                         vhpet_start_timer(vhpet, i);
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(void *vm, int vcpuid, 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(vm);
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, true);
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 >= 24)
724                 allowed_irqs = 0x00f00000;      /* irqs 20, 21, 22 and 23 */
725         else
726                 allowed_irqs = 0;
727
728         /*
729          * Initialize HPET timer hardware state.
730          */
731         for (i = 0; i < VHPET_NUM_TIMERS; i++) {
732                 vhpet->timer[i].cap_config = allowed_irqs << 32;
733                 vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT;
734                 vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL;
735
736                 vhpet->timer[i].compval = 0xffffffff;
737                 callout_init(&vhpet->timer[i].callout, 1);
738
739                 arg = &vhpet->timer[i].arg;
740                 arg->vhpet = vhpet;
741                 arg->timer_num = i;
742         }
743
744         return (vhpet);
745 }
746
747 void
748 vhpet_cleanup(struct vhpet *vhpet)
749 {
750         int i;
751
752         for (i = 0; i < VHPET_NUM_TIMERS; i++)
753                 callout_drain(&vhpet->timer[i].callout);
754
755         free(vhpet, M_VHPET);
756 }
757
758 int
759 vhpet_getcap(struct vm_hpet_cap *cap)
760 {
761
762         cap->capabilities = vhpet_capabilities();
763         return (0);
764 }