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