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