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