1bf21cd93STycho Nightingale /*-
2*32640292SAndy Fiddaman * SPDX-License-Identifier: BSD-2-Clause
34c87aefeSPatrick Mooney *
4bf21cd93STycho Nightingale * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
5bf21cd93STycho Nightingale * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
6bf21cd93STycho Nightingale * All rights reserved.
7bf21cd93STycho Nightingale *
8bf21cd93STycho Nightingale * Redistribution and use in source and binary forms, with or without
9bf21cd93STycho Nightingale * modification, are permitted provided that the following conditions
10bf21cd93STycho Nightingale * are met:
11bf21cd93STycho Nightingale * 1. Redistributions of source code must retain the above copyright
12bf21cd93STycho Nightingale * notice, this list of conditions and the following disclaimer.
13bf21cd93STycho Nightingale * 2. Redistributions in binary form must reproduce the above copyright
14bf21cd93STycho Nightingale * notice, this list of conditions and the following disclaimer in the
15bf21cd93STycho Nightingale * documentation and/or other materials provided with the distribution.
16bf21cd93STycho Nightingale *
17bf21cd93STycho Nightingale * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
18bf21cd93STycho Nightingale * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19bf21cd93STycho Nightingale * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20bf21cd93STycho Nightingale * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
21bf21cd93STycho Nightingale * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22bf21cd93STycho Nightingale * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23bf21cd93STycho Nightingale * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24bf21cd93STycho Nightingale * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25bf21cd93STycho Nightingale * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26bf21cd93STycho Nightingale * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27bf21cd93STycho Nightingale * SUCH DAMAGE.
284c87aefeSPatrick Mooney */
294c87aefeSPatrick Mooney
304c87aefeSPatrick Mooney /*
314c87aefeSPatrick Mooney * Copyright 2018 Joyent, Inc.
322cac0506SPatrick Mooney * Copyright 2022 Oxide Computer Company
33bf21cd93STycho Nightingale */
34bf21cd93STycho Nightingale
35bf21cd93STycho Nightingale #include <sys/cdefs.h>
36bf21cd93STycho Nightingale
37bf21cd93STycho Nightingale #include <sys/param.h>
38bf21cd93STycho Nightingale #include <sys/mutex.h>
39bf21cd93STycho Nightingale #include <sys/kernel.h>
408130f8e1SPatrick Mooney #include <sys/kmem.h>
41bf21cd93STycho Nightingale #include <sys/systm.h>
42bf21cd93STycho Nightingale
43bf21cd93STycho Nightingale #include <dev/acpica/acpi_hpet.h>
44bf21cd93STycho Nightingale
45bf21cd93STycho Nightingale #include <machine/vmm.h>
46bf21cd93STycho Nightingale #include <machine/vmm_dev.h>
47bf21cd93STycho Nightingale
48bf21cd93STycho Nightingale #include "vmm_lapic.h"
49bf21cd93STycho Nightingale #include "vatpic.h"
50bf21cd93STycho Nightingale #include "vioapic.h"
51bf21cd93STycho Nightingale #include "vhpet.h"
52bf21cd93STycho Nightingale
53bf21cd93STycho Nightingale
544c87aefeSPatrick Mooney #define HPET_FREQ 16777216 /* 16.7 (2^24) Mhz */
55bf21cd93STycho Nightingale #define FS_PER_S 1000000000000000ul
56bf21cd93STycho Nightingale
57bf21cd93STycho Nightingale /* Timer N Configuration and Capabilities Register */
5884971882SPatrick Mooney #define HPET_TCAP_RO_MASK (HPET_TCAP_INT_ROUTE | \
5984971882SPatrick Mooney HPET_TCAP_FSB_INT_DEL | \
6084971882SPatrick Mooney HPET_TCAP_SIZE | \
6184971882SPatrick Mooney HPET_TCAP_PER_INT)
62bf21cd93STycho Nightingale /*
63bf21cd93STycho Nightingale * HPET requires at least 3 timers and up to 32 timers per block.
64bf21cd93STycho Nightingale */
65bf21cd93STycho Nightingale #define VHPET_NUM_TIMERS 8
66bf21cd93STycho Nightingale CTASSERT(VHPET_NUM_TIMERS >= 3 && VHPET_NUM_TIMERS <= 32);
67bf21cd93STycho Nightingale
68bf21cd93STycho Nightingale struct vhpet_callout_arg {
69bf21cd93STycho Nightingale struct vhpet *vhpet;
70bf21cd93STycho Nightingale int timer_num;
71bf21cd93STycho Nightingale };
72bf21cd93STycho Nightingale
735103e761SPatrick Mooney struct vhpet_timer {
745103e761SPatrick Mooney uint64_t cap_config; /* Configuration */
755103e761SPatrick Mooney uint64_t msireg; /* FSB interrupt routing */
765103e761SPatrick Mooney uint32_t compval; /* Comparator */
775103e761SPatrick Mooney uint32_t comprate;
785103e761SPatrick Mooney struct callout callout;
795103e761SPatrick Mooney hrtime_t callout_expire; /* time when counter==compval */
805103e761SPatrick Mooney struct vhpet_callout_arg arg;
815103e761SPatrick Mooney };
825103e761SPatrick Mooney
83bf21cd93STycho Nightingale struct vhpet {
84bf21cd93STycho Nightingale struct vm *vm;
85fb29dee0SPatrick Mooney kmutex_t lock;
86bf21cd93STycho Nightingale
87bf21cd93STycho Nightingale uint64_t config; /* Configuration */
88bf21cd93STycho Nightingale uint64_t isr; /* Interrupt Status */
895103e761SPatrick Mooney uint32_t base_count; /* HPET counter base value */
905103e761SPatrick Mooney hrtime_t base_time; /* uptime corresponding to base value */
915103e761SPatrick Mooney
925103e761SPatrick Mooney struct vhpet_timer timer[VHPET_NUM_TIMERS];
93bf21cd93STycho Nightingale };
94bf21cd93STycho Nightingale
95fb29dee0SPatrick Mooney #define VHPET_LOCK(vhp) mutex_enter(&((vhp)->lock))
96fb29dee0SPatrick Mooney #define VHPET_UNLOCK(vhp) mutex_exit(&((vhp)->lock))
972cac0506SPatrick Mooney #define VHPET_LOCKED(vhp) MUTEX_HELD(&((vhp)->lock))
98bf21cd93STycho Nightingale
99bf21cd93STycho Nightingale static void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter,
1005103e761SPatrick Mooney hrtime_t now);
101bf21cd93STycho Nightingale
102bf21cd93STycho Nightingale static uint64_t
vhpet_capabilities(void)103bf21cd93STycho Nightingale vhpet_capabilities(void)
104bf21cd93STycho Nightingale {
105bf21cd93STycho Nightingale uint64_t cap = 0;
106bf21cd93STycho Nightingale
107bf21cd93STycho Nightingale cap |= 0x8086 << 16; /* vendor id */
108bf21cd93STycho Nightingale cap |= (VHPET_NUM_TIMERS - 1) << 8; /* number of timers */
109bf21cd93STycho Nightingale cap |= 1; /* revision */
110bf21cd93STycho Nightingale cap &= ~HPET_CAP_COUNT_SIZE; /* 32-bit timer */
111bf21cd93STycho Nightingale
112bf21cd93STycho Nightingale cap &= 0xffffffff;
113bf21cd93STycho Nightingale cap |= (FS_PER_S / HPET_FREQ) << 32; /* tick period in fs */
114bf21cd93STycho Nightingale
115bf21cd93STycho Nightingale return (cap);
116bf21cd93STycho Nightingale }
117bf21cd93STycho Nightingale
118bf21cd93STycho Nightingale static __inline bool
vhpet_counter_enabled(struct vhpet * vhpet)119bf21cd93STycho Nightingale vhpet_counter_enabled(struct vhpet *vhpet)
120bf21cd93STycho Nightingale {
121bf21cd93STycho Nightingale
122bf21cd93STycho Nightingale return ((vhpet->config & HPET_CNF_ENABLE) ? true : false);
123bf21cd93STycho Nightingale }
124bf21cd93STycho Nightingale
125bf21cd93STycho Nightingale static __inline bool
vhpet_timer_msi_enabled(struct vhpet * vhpet,int n)126bf21cd93STycho Nightingale vhpet_timer_msi_enabled(struct vhpet *vhpet, int n)
127bf21cd93STycho Nightingale {
128bf21cd93STycho Nightingale const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN;
129bf21cd93STycho Nightingale
130bf21cd93STycho Nightingale if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable)
131bf21cd93STycho Nightingale return (true);
132bf21cd93STycho Nightingale else
133bf21cd93STycho Nightingale return (false);
134bf21cd93STycho Nightingale }
135bf21cd93STycho Nightingale
136bf21cd93STycho Nightingale static __inline int
vhpet_timer_ioapic_pin(struct vhpet * vhpet,int n)137bf21cd93STycho Nightingale vhpet_timer_ioapic_pin(struct vhpet *vhpet, int n)
138bf21cd93STycho Nightingale {
139bf21cd93STycho Nightingale /*
140bf21cd93STycho Nightingale * If the timer is configured to use MSI then treat it as if the
141bf21cd93STycho Nightingale * timer is not connected to the ioapic.
142bf21cd93STycho Nightingale */
143bf21cd93STycho Nightingale if (vhpet_timer_msi_enabled(vhpet, n))
144bf21cd93STycho Nightingale return (0);
145bf21cd93STycho Nightingale
146bf21cd93STycho Nightingale return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
147bf21cd93STycho Nightingale }
148bf21cd93STycho Nightingale
149bf21cd93STycho Nightingale static uint32_t
vhpet_counter(struct vhpet * vhpet,hrtime_t * nowptr)1505103e761SPatrick Mooney vhpet_counter(struct vhpet *vhpet, hrtime_t *nowptr)
151bf21cd93STycho Nightingale {
1525103e761SPatrick Mooney const hrtime_t now = gethrtime();
1535103e761SPatrick Mooney uint32_t val = vhpet->base_count;
154bf21cd93STycho Nightingale
155bf21cd93STycho Nightingale if (vhpet_counter_enabled(vhpet)) {
1565103e761SPatrick Mooney const hrtime_t delta = now - vhpet->base_time;
1575103e761SPatrick Mooney
1585103e761SPatrick Mooney ASSERT3S(delta, >=, 0);
1595103e761SPatrick Mooney val += hrt_freq_count(delta, HPET_FREQ);
160bf21cd93STycho Nightingale } else {
1615103e761SPatrick Mooney /* Value of the counter is meaningless when it is disabled */
1625103e761SPatrick Mooney }
1635103e761SPatrick Mooney
1645103e761SPatrick Mooney if (nowptr != NULL) {
1655103e761SPatrick Mooney *nowptr = now;
166bf21cd93STycho Nightingale }
167bf21cd93STycho Nightingale return (val);
168bf21cd93STycho Nightingale }
169bf21cd93STycho Nightingale
170bf21cd93STycho Nightingale static void
vhpet_timer_clear_isr(struct vhpet * vhpet,int n)171bf21cd93STycho Nightingale vhpet_timer_clear_isr(struct vhpet *vhpet, int n)
172bf21cd93STycho Nightingale {
1734c87aefeSPatrick Mooney int pin;
174bf21cd93STycho Nightingale
175bf21cd93STycho Nightingale if (vhpet->isr & (1 << n)) {
176bf21cd93STycho Nightingale pin = vhpet_timer_ioapic_pin(vhpet, n);
177bf21cd93STycho Nightingale KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n));
178e0994bd2SPatrick Mooney (void) vioapic_deassert_irq(vhpet->vm, pin);
179bf21cd93STycho Nightingale vhpet->isr &= ~(1 << n);
180bf21cd93STycho Nightingale }
181bf21cd93STycho Nightingale }
182bf21cd93STycho Nightingale
183bf21cd93STycho Nightingale static __inline bool
vhpet_periodic_timer(struct vhpet * vhpet,int n)184bf21cd93STycho Nightingale vhpet_periodic_timer(struct vhpet *vhpet, int n)
185bf21cd93STycho Nightingale {
186bf21cd93STycho Nightingale
187bf21cd93STycho Nightingale return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0);
188bf21cd93STycho Nightingale }
189bf21cd93STycho Nightingale
190bf21cd93STycho Nightingale static __inline bool
vhpet_timer_interrupt_enabled(struct vhpet * vhpet,int n)191bf21cd93STycho Nightingale vhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n)
192bf21cd93STycho Nightingale {
193bf21cd93STycho Nightingale
194bf21cd93STycho Nightingale return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0);
195bf21cd93STycho Nightingale }
196bf21cd93STycho Nightingale
197bf21cd93STycho Nightingale static __inline bool
vhpet_timer_edge_trig(struct vhpet * vhpet,int n)198bf21cd93STycho Nightingale vhpet_timer_edge_trig(struct vhpet *vhpet, int n)
199bf21cd93STycho Nightingale {
200bf21cd93STycho Nightingale
201bf21cd93STycho Nightingale KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: "
202bf21cd93STycho Nightingale "timer %d is using MSI", n));
203bf21cd93STycho Nightingale
204bf21cd93STycho Nightingale if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0)
205bf21cd93STycho Nightingale return (true);
206bf21cd93STycho Nightingale else
207bf21cd93STycho Nightingale return (false);
208bf21cd93STycho Nightingale }
209bf21cd93STycho Nightingale
210bf21cd93STycho Nightingale static void
vhpet_timer_interrupt(struct vhpet * vhpet,int n)211bf21cd93STycho Nightingale vhpet_timer_interrupt(struct vhpet *vhpet, int n)
212bf21cd93STycho Nightingale {
2134c87aefeSPatrick Mooney int pin;
214bf21cd93STycho Nightingale
215bf21cd93STycho Nightingale /* If interrupts are not enabled for this timer then just return. */
216bf21cd93STycho Nightingale if (!vhpet_timer_interrupt_enabled(vhpet, n))
217bf21cd93STycho Nightingale return;
218bf21cd93STycho Nightingale
219bf21cd93STycho Nightingale /*
220bf21cd93STycho Nightingale * If a level triggered interrupt is already asserted then just return.
221bf21cd93STycho Nightingale */
222bf21cd93STycho Nightingale if ((vhpet->isr & (1 << n)) != 0) {
223bf21cd93STycho Nightingale return;
224bf21cd93STycho Nightingale }
225bf21cd93STycho Nightingale
226bf21cd93STycho Nightingale if (vhpet_timer_msi_enabled(vhpet, n)) {
227e0994bd2SPatrick Mooney (void) lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32,
228bf21cd93STycho Nightingale vhpet->timer[n].msireg & 0xffffffff);
229bf21cd93STycho Nightingale return;
23084971882SPatrick Mooney }
231bf21cd93STycho Nightingale
232bf21cd93STycho Nightingale pin = vhpet_timer_ioapic_pin(vhpet, n);
233bf21cd93STycho Nightingale if (pin == 0) {
234d4f59ae5SPatrick Mooney /* Interrupt is not routed to IOAPIC */
235bf21cd93STycho Nightingale return;
236bf21cd93STycho Nightingale }
237bf21cd93STycho Nightingale
238bf21cd93STycho Nightingale if (vhpet_timer_edge_trig(vhpet, n)) {
239e0994bd2SPatrick Mooney (void) vioapic_pulse_irq(vhpet->vm, pin);
240bf21cd93STycho Nightingale } else {
241bf21cd93STycho Nightingale vhpet->isr |= 1 << n;
242e0994bd2SPatrick Mooney (void) vioapic_assert_irq(vhpet->vm, pin);
243bf21cd93STycho Nightingale }
244bf21cd93STycho Nightingale }
245bf21cd93STycho Nightingale
246bf21cd93STycho Nightingale static void
vhpet_adjust_compval(struct vhpet * vhpet,int n,uint32_t counter)247bf21cd93STycho Nightingale vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter)
248bf21cd93STycho Nightingale {
249bf21cd93STycho Nightingale uint32_t compval, comprate, compnext;
250bf21cd93STycho Nightingale
251bf21cd93STycho Nightingale KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n));
252bf21cd93STycho Nightingale
253bf21cd93STycho Nightingale compval = vhpet->timer[n].compval;
254bf21cd93STycho Nightingale comprate = vhpet->timer[n].comprate;
255bf21cd93STycho Nightingale
256bf21cd93STycho Nightingale /*
257bf21cd93STycho Nightingale * Calculate the comparator value to be used for the next periodic
258bf21cd93STycho Nightingale * interrupt.
259bf21cd93STycho Nightingale *
260bf21cd93STycho Nightingale * This function is commonly called from the callout handler.
261bf21cd93STycho Nightingale * In this scenario the 'counter' is ahead of 'compval'. To find
262bf21cd93STycho Nightingale * the next value to program into the accumulator we divide the
263bf21cd93STycho Nightingale * number space between 'compval' and 'counter' into 'comprate'
264bf21cd93STycho Nightingale * sized units. The 'compval' is rounded up such that is "ahead"
265bf21cd93STycho Nightingale * of 'counter'.
266bf21cd93STycho Nightingale */
267bf21cd93STycho Nightingale compnext = compval + ((counter - compval) / comprate + 1) * comprate;
268bf21cd93STycho Nightingale
269bf21cd93STycho Nightingale vhpet->timer[n].compval = compnext;
270bf21cd93STycho Nightingale }
271bf21cd93STycho Nightingale
272bf21cd93STycho Nightingale static void
vhpet_handler(void * arg)2732cac0506SPatrick Mooney vhpet_handler(void *arg)
274bf21cd93STycho Nightingale {
2752cac0506SPatrick Mooney const struct vhpet_callout_arg *vca = arg;
2762cac0506SPatrick Mooney struct vhpet *vhpet = vca->vhpet;
2772cac0506SPatrick Mooney const int n = vca->timer_num;
2782cac0506SPatrick Mooney struct callout *callout = &vhpet->timer[n].callout;
279bf21cd93STycho Nightingale
280bf21cd93STycho Nightingale VHPET_LOCK(vhpet);
281bf21cd93STycho Nightingale
2822cac0506SPatrick Mooney if (callout_pending(callout) || !callout_active(callout)) {
2832cac0506SPatrick Mooney VHPET_UNLOCK(vhpet);
2842cac0506SPatrick Mooney return;
2852cac0506SPatrick Mooney }
286bf21cd93STycho Nightingale
287bf21cd93STycho Nightingale callout_deactivate(callout);
2882cac0506SPatrick Mooney ASSERT(vhpet_counter_enabled(vhpet));
289bf21cd93STycho Nightingale
2902cac0506SPatrick Mooney if (vhpet_periodic_timer(vhpet, n)) {
2912cac0506SPatrick Mooney hrtime_t now;
2922cac0506SPatrick Mooney uint32_t counter = vhpet_counter(vhpet, &now);
293bf21cd93STycho Nightingale
2942cac0506SPatrick Mooney vhpet_start_timer(vhpet, n, counter, now);
2952cac0506SPatrick Mooney } else {
2962cac0506SPatrick Mooney /*
2972cac0506SPatrick Mooney * Zero out the expiration time to distinguish a fired timer
2982cac0506SPatrick Mooney * from one which is held due to a VM pause.
2992cac0506SPatrick Mooney */
3002cac0506SPatrick Mooney vhpet->timer[n].callout_expire = 0;
3012cac0506SPatrick Mooney }
302bf21cd93STycho Nightingale vhpet_timer_interrupt(vhpet, n);
3032cac0506SPatrick Mooney
304bf21cd93STycho Nightingale VHPET_UNLOCK(vhpet);
305bf21cd93STycho Nightingale }
306bf21cd93STycho Nightingale
307bf21cd93STycho Nightingale static void
vhpet_stop_timer(struct vhpet * vhpet,int n,hrtime_t now)3085103e761SPatrick Mooney vhpet_stop_timer(struct vhpet *vhpet, int n, hrtime_t now)
309bf21cd93STycho Nightingale {
3102cac0506SPatrick Mooney ASSERT(VHPET_LOCKED(vhpet));
3112cac0506SPatrick Mooney
312bf21cd93STycho Nightingale callout_stop(&vhpet->timer[n].callout);
313bf21cd93STycho Nightingale
314bf21cd93STycho Nightingale /*
315bf21cd93STycho Nightingale * If the callout was scheduled to expire in the past but hasn't
316bf21cd93STycho Nightingale * had a chance to execute yet then trigger the timer interrupt
317bf21cd93STycho Nightingale * here. Failing to do so will result in a missed timer interrupt
318bf21cd93STycho Nightingale * in the guest. This is especially bad in one-shot mode because
319bf21cd93STycho Nightingale * the next interrupt has to wait for the counter to wrap around.
320bf21cd93STycho Nightingale */
3215103e761SPatrick Mooney if (vhpet->timer[n].callout_expire < now) {
322bf21cd93STycho Nightingale vhpet_timer_interrupt(vhpet, n);
323bf21cd93STycho Nightingale }
3242cac0506SPatrick Mooney vhpet->timer[n].callout_expire = 0;
325bf21cd93STycho Nightingale }
326bf21cd93STycho Nightingale
327bf21cd93STycho Nightingale static void
vhpet_start_timer(struct vhpet * vhpet,int n,uint32_t counter,hrtime_t now)3285103e761SPatrick Mooney vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, hrtime_t now)
329bf21cd93STycho Nightingale {
3305103e761SPatrick Mooney struct vhpet_timer *timer = &vhpet->timer[n];
331bf21cd93STycho Nightingale
3322cac0506SPatrick Mooney ASSERT(VHPET_LOCKED(vhpet));
3332cac0506SPatrick Mooney
3345103e761SPatrick Mooney if (timer->comprate != 0)
335bf21cd93STycho Nightingale vhpet_adjust_compval(vhpet, n, counter);
336bf21cd93STycho Nightingale else {
337bf21cd93STycho Nightingale /*
338bf21cd93STycho Nightingale * In one-shot mode it is the guest's responsibility to make
339bf21cd93STycho Nightingale * sure that the comparator value is not in the "past". The
340bf21cd93STycho Nightingale * hardware doesn't have any belt-and-suspenders to deal with
341bf21cd93STycho Nightingale * this so we don't either.
342bf21cd93STycho Nightingale */
343bf21cd93STycho Nightingale }
344bf21cd93STycho Nightingale
3455103e761SPatrick Mooney const hrtime_t delta = hrt_freq_interval(HPET_FREQ,
3465103e761SPatrick Mooney timer->compval - counter);
3475103e761SPatrick Mooney timer->callout_expire = now + delta;
3485103e761SPatrick Mooney callout_reset_hrtime(&timer->callout, timer->callout_expire,
3495103e761SPatrick Mooney vhpet_handler, &timer->arg, C_ABSOLUTE);
350bf21cd93STycho Nightingale }
351bf21cd93STycho Nightingale
352bf21cd93STycho Nightingale static void
vhpet_start_counting(struct vhpet * vhpet)353bf21cd93STycho Nightingale vhpet_start_counting(struct vhpet *vhpet)
354bf21cd93STycho Nightingale {
355bf21cd93STycho Nightingale int i;
356bf21cd93STycho Nightingale
3575103e761SPatrick Mooney vhpet->base_time = gethrtime();
358bf21cd93STycho Nightingale for (i = 0; i < VHPET_NUM_TIMERS; i++) {
359bf21cd93STycho Nightingale /*
360bf21cd93STycho Nightingale * Restart the timers based on the value of the main counter
361bf21cd93STycho Nightingale * when it stopped counting.
362bf21cd93STycho Nightingale */
3635103e761SPatrick Mooney vhpet_start_timer(vhpet, i, vhpet->base_count,
3645103e761SPatrick Mooney vhpet->base_time);
365bf21cd93STycho Nightingale }
366bf21cd93STycho Nightingale }
367bf21cd93STycho Nightingale
368bf21cd93STycho Nightingale static void
vhpet_stop_counting(struct vhpet * vhpet,uint32_t counter,hrtime_t now)3695103e761SPatrick Mooney vhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, hrtime_t now)
370bf21cd93STycho Nightingale {
371bf21cd93STycho Nightingale int i;
372bf21cd93STycho Nightingale
3735103e761SPatrick Mooney vhpet->base_count = counter;
374bf21cd93STycho Nightingale for (i = 0; i < VHPET_NUM_TIMERS; i++)
375bf21cd93STycho Nightingale vhpet_stop_timer(vhpet, i, now);
376bf21cd93STycho Nightingale }
377bf21cd93STycho Nightingale
378bf21cd93STycho Nightingale static __inline void
update_register(uint64_t * regptr,uint64_t data,uint64_t mask)379bf21cd93STycho Nightingale update_register(uint64_t *regptr, uint64_t data, uint64_t mask)
380bf21cd93STycho Nightingale {
381bf21cd93STycho Nightingale
382bf21cd93STycho Nightingale *regptr &= ~mask;
383bf21cd93STycho Nightingale *regptr |= (data & mask);
384bf21cd93STycho Nightingale }
385bf21cd93STycho Nightingale
386bf21cd93STycho Nightingale static void
vhpet_timer_update_config(struct vhpet * vhpet,int n,uint64_t data,uint64_t mask)387bf21cd93STycho Nightingale vhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data,
388bf21cd93STycho Nightingale uint64_t mask)
389bf21cd93STycho Nightingale {
390bf21cd93STycho Nightingale bool clear_isr;
391bf21cd93STycho Nightingale int old_pin, new_pin;
392bf21cd93STycho Nightingale uint32_t allowed_irqs;
393bf21cd93STycho Nightingale uint64_t oldval, newval;
394bf21cd93STycho Nightingale
395bf21cd93STycho Nightingale if (vhpet_timer_msi_enabled(vhpet, n) ||
396bf21cd93STycho Nightingale vhpet_timer_edge_trig(vhpet, n)) {
397bf21cd93STycho Nightingale if (vhpet->isr & (1 << n))
398bf21cd93STycho Nightingale panic("vhpet timer %d isr should not be asserted", n);
399bf21cd93STycho Nightingale }
400bf21cd93STycho Nightingale old_pin = vhpet_timer_ioapic_pin(vhpet, n);
401bf21cd93STycho Nightingale oldval = vhpet->timer[n].cap_config;
402bf21cd93STycho Nightingale
403bf21cd93STycho Nightingale newval = oldval;
404bf21cd93STycho Nightingale update_register(&newval, data, mask);
405bf21cd93STycho Nightingale newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE);
406bf21cd93STycho Nightingale newval |= oldval & HPET_TCAP_RO_MASK;
407bf21cd93STycho Nightingale
408bf21cd93STycho Nightingale if (newval == oldval)
409bf21cd93STycho Nightingale return;
410bf21cd93STycho Nightingale
411bf21cd93STycho Nightingale vhpet->timer[n].cap_config = newval;
412bf21cd93STycho Nightingale
413bf21cd93STycho Nightingale /*
414bf21cd93STycho Nightingale * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field.
415bf21cd93STycho Nightingale * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set
416bf21cd93STycho Nightingale * it to the default value of 0.
417bf21cd93STycho Nightingale */
418bf21cd93STycho Nightingale allowed_irqs = vhpet->timer[n].cap_config >> 32;
419bf21cd93STycho Nightingale new_pin = vhpet_timer_ioapic_pin(vhpet, n);
420bf21cd93STycho Nightingale if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) {
421d4f59ae5SPatrick Mooney /* Invalid IRQ configured */
422bf21cd93STycho Nightingale new_pin = 0;
423bf21cd93STycho Nightingale vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE;
424bf21cd93STycho Nightingale }
425bf21cd93STycho Nightingale
426bf21cd93STycho Nightingale if (!vhpet_periodic_timer(vhpet, n))
427bf21cd93STycho Nightingale vhpet->timer[n].comprate = 0;
428bf21cd93STycho Nightingale
429bf21cd93STycho Nightingale /*
430bf21cd93STycho Nightingale * If the timer's ISR bit is set then clear it in the following cases:
431bf21cd93STycho Nightingale * - interrupt is disabled
432bf21cd93STycho Nightingale * - interrupt type is changed from level to edge or fsb.
433bf21cd93STycho Nightingale * - interrupt routing is changed
434bf21cd93STycho Nightingale *
435bf21cd93STycho Nightingale * This is to ensure that this timer's level triggered interrupt does
436bf21cd93STycho Nightingale * not remain asserted forever.
437bf21cd93STycho Nightingale */
438bf21cd93STycho Nightingale if (vhpet->isr & (1 << n)) {
439bf21cd93STycho Nightingale KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d",
440bf21cd93STycho Nightingale n, old_pin));
441bf21cd93STycho Nightingale if (!vhpet_timer_interrupt_enabled(vhpet, n))
442bf21cd93STycho Nightingale clear_isr = true;
443bf21cd93STycho Nightingale else if (vhpet_timer_msi_enabled(vhpet, n))
444bf21cd93STycho Nightingale clear_isr = true;
445bf21cd93STycho Nightingale else if (vhpet_timer_edge_trig(vhpet, n))
446bf21cd93STycho Nightingale clear_isr = true;
447bf21cd93STycho Nightingale else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin)
448bf21cd93STycho Nightingale clear_isr = true;
449bf21cd93STycho Nightingale else
450bf21cd93STycho Nightingale clear_isr = false;
451bf21cd93STycho Nightingale
452bf21cd93STycho Nightingale if (clear_isr) {
453e0994bd2SPatrick Mooney (void) vioapic_deassert_irq(vhpet->vm, old_pin);
454bf21cd93STycho Nightingale vhpet->isr &= ~(1 << n);
455bf21cd93STycho Nightingale }
456bf21cd93STycho Nightingale }
457bf21cd93STycho Nightingale }
458bf21cd93STycho Nightingale
459bf21cd93STycho Nightingale int
vhpet_mmio_write(struct vm * vm,int vcpuid,uint64_t gpa,uint64_t val,int size)4603e1c5f3aSPatrick Mooney vhpet_mmio_write(struct vm *vm, int vcpuid, uint64_t gpa, uint64_t val,
4613e1c5f3aSPatrick Mooney int size)
462bf21cd93STycho Nightingale {
463bf21cd93STycho Nightingale struct vhpet *vhpet;
464bf21cd93STycho Nightingale uint64_t data, mask, oldval, val64;
465bf21cd93STycho Nightingale uint32_t isr_clear_mask, old_compval, old_comprate, counter;
4665103e761SPatrick Mooney hrtime_t now;
467bf21cd93STycho Nightingale int i, offset;
468bf21cd93STycho Nightingale
469bf21cd93STycho Nightingale vhpet = vm_hpet(vm);
470bf21cd93STycho Nightingale offset = gpa - VHPET_BASE;
471bf21cd93STycho Nightingale
472bf21cd93STycho Nightingale VHPET_LOCK(vhpet);
473bf21cd93STycho Nightingale
474bf21cd93STycho Nightingale /* Accesses to the HPET should be 4 or 8 bytes wide */
475bf21cd93STycho Nightingale switch (size) {
476bf21cd93STycho Nightingale case 8:
477bf21cd93STycho Nightingale mask = 0xffffffffffffffff;
478bf21cd93STycho Nightingale data = val;
479bf21cd93STycho Nightingale break;
480bf21cd93STycho Nightingale case 4:
481bf21cd93STycho Nightingale mask = 0xffffffff;
482bf21cd93STycho Nightingale data = val;
483bf21cd93STycho Nightingale if ((offset & 0x4) != 0) {
484bf21cd93STycho Nightingale mask <<= 32;
485bf21cd93STycho Nightingale data <<= 32;
48684971882SPatrick Mooney }
487bf21cd93STycho Nightingale break;
488bf21cd93STycho Nightingale default:
489d4f59ae5SPatrick Mooney /* Invalid MMIO write */
490bf21cd93STycho Nightingale goto done;
491bf21cd93STycho Nightingale }
492bf21cd93STycho Nightingale
493bf21cd93STycho Nightingale /* Access to the HPET should be naturally aligned to its width */
494bf21cd93STycho Nightingale if (offset & (size - 1)) {
495bf21cd93STycho Nightingale goto done;
496bf21cd93STycho Nightingale }
497bf21cd93STycho Nightingale
498bf21cd93STycho Nightingale if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
499bf21cd93STycho Nightingale /*
500bf21cd93STycho Nightingale * Get the most recent value of the counter before updating
501bf21cd93STycho Nightingale * the 'config' register. If the HPET is going to be disabled
5025103e761SPatrick Mooney * then we need to update 'base_count' with the value right
503bf21cd93STycho Nightingale * before it is disabled.
504bf21cd93STycho Nightingale */
5055103e761SPatrick Mooney counter = vhpet_counter(vhpet, &now);
506bf21cd93STycho Nightingale oldval = vhpet->config;
507bf21cd93STycho Nightingale update_register(&vhpet->config, data, mask);
5084c87aefeSPatrick Mooney
5094c87aefeSPatrick Mooney /*
5104c87aefeSPatrick Mooney * LegacyReplacement Routing is not supported so clear the
5114c87aefeSPatrick Mooney * bit explicitly.
5124c87aefeSPatrick Mooney */
5134c87aefeSPatrick Mooney vhpet->config &= ~HPET_CNF_LEG_RT;
5144c87aefeSPatrick Mooney
515bf21cd93STycho Nightingale if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) {
516bf21cd93STycho Nightingale if (vhpet_counter_enabled(vhpet)) {
517bf21cd93STycho Nightingale vhpet_start_counting(vhpet);
518bf21cd93STycho Nightingale } else {
519bf21cd93STycho Nightingale vhpet_stop_counting(vhpet, counter, now);
520bf21cd93STycho Nightingale }
521bf21cd93STycho Nightingale }
522bf21cd93STycho Nightingale goto done;
523bf21cd93STycho Nightingale }
524bf21cd93STycho Nightingale
525bf21cd93STycho Nightingale if (offset == HPET_ISR || offset == HPET_ISR + 4) {
526bf21cd93STycho Nightingale isr_clear_mask = vhpet->isr & data;
527bf21cd93STycho Nightingale for (i = 0; i < VHPET_NUM_TIMERS; i++) {
528bf21cd93STycho Nightingale if ((isr_clear_mask & (1 << i)) != 0) {
529bf21cd93STycho Nightingale vhpet_timer_clear_isr(vhpet, i);
530bf21cd93STycho Nightingale }
531bf21cd93STycho Nightingale }
532bf21cd93STycho Nightingale goto done;
533bf21cd93STycho Nightingale }
534bf21cd93STycho Nightingale
535bf21cd93STycho Nightingale if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
536bf21cd93STycho Nightingale /* Zero-extend the counter to 64-bits before updating it */
537bf21cd93STycho Nightingale val64 = vhpet_counter(vhpet, NULL);
538bf21cd93STycho Nightingale update_register(&val64, data, mask);
5395103e761SPatrick Mooney vhpet->base_count = val64;
540bf21cd93STycho Nightingale if (vhpet_counter_enabled(vhpet))
541bf21cd93STycho Nightingale vhpet_start_counting(vhpet);
542bf21cd93STycho Nightingale goto done;
543bf21cd93STycho Nightingale }
544bf21cd93STycho Nightingale
545bf21cd93STycho Nightingale for (i = 0; i < VHPET_NUM_TIMERS; i++) {
546bf21cd93STycho Nightingale if (offset == HPET_TIMER_CAP_CNF(i) ||
547bf21cd93STycho Nightingale offset == HPET_TIMER_CAP_CNF(i) + 4) {
548bf21cd93STycho Nightingale vhpet_timer_update_config(vhpet, i, data, mask);
549bf21cd93STycho Nightingale break;
550bf21cd93STycho Nightingale }
551bf21cd93STycho Nightingale
552bf21cd93STycho Nightingale if (offset == HPET_TIMER_COMPARATOR(i) ||
553bf21cd93STycho Nightingale offset == HPET_TIMER_COMPARATOR(i) + 4) {
554bf21cd93STycho Nightingale old_compval = vhpet->timer[i].compval;
555bf21cd93STycho Nightingale old_comprate = vhpet->timer[i].comprate;
556bf21cd93STycho Nightingale if (vhpet_periodic_timer(vhpet, i)) {
557bf21cd93STycho Nightingale /*
558bf21cd93STycho Nightingale * In periodic mode writes to the comparator
559bf21cd93STycho Nightingale * change the 'compval' register only if the
560bf21cd93STycho Nightingale * HPET_TCNF_VAL_SET bit is set in the config
561bf21cd93STycho Nightingale * register.
562bf21cd93STycho Nightingale */
563bf21cd93STycho Nightingale val64 = vhpet->timer[i].comprate;
564bf21cd93STycho Nightingale update_register(&val64, data, mask);
565bf21cd93STycho Nightingale vhpet->timer[i].comprate = val64;
566bf21cd93STycho Nightingale if ((vhpet->timer[i].cap_config &
567bf21cd93STycho Nightingale HPET_TCNF_VAL_SET) != 0) {
568bf21cd93STycho Nightingale vhpet->timer[i].compval = val64;
569bf21cd93STycho Nightingale }
570bf21cd93STycho Nightingale } else {
571bf21cd93STycho Nightingale KASSERT(vhpet->timer[i].comprate == 0,
572bf21cd93STycho Nightingale ("vhpet one-shot timer %d has invalid "
573bf21cd93STycho Nightingale "rate %u", i, vhpet->timer[i].comprate));
574bf21cd93STycho Nightingale val64 = vhpet->timer[i].compval;
575bf21cd93STycho Nightingale update_register(&val64, data, mask);
576bf21cd93STycho Nightingale vhpet->timer[i].compval = val64;
577bf21cd93STycho Nightingale }
578bf21cd93STycho Nightingale vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET;
579bf21cd93STycho Nightingale
580bf21cd93STycho Nightingale if (vhpet->timer[i].compval != old_compval ||
581bf21cd93STycho Nightingale vhpet->timer[i].comprate != old_comprate) {
582bf21cd93STycho Nightingale if (vhpet_counter_enabled(vhpet)) {
583bf21cd93STycho Nightingale counter = vhpet_counter(vhpet, &now);
584bf21cd93STycho Nightingale vhpet_start_timer(vhpet, i, counter,
585bf21cd93STycho Nightingale now);
586bf21cd93STycho Nightingale }
587bf21cd93STycho Nightingale }
588bf21cd93STycho Nightingale break;
589bf21cd93STycho Nightingale }
590bf21cd93STycho Nightingale
591bf21cd93STycho Nightingale if (offset == HPET_TIMER_FSB_VAL(i) ||
592bf21cd93STycho Nightingale offset == HPET_TIMER_FSB_ADDR(i)) {
593bf21cd93STycho Nightingale update_register(&vhpet->timer[i].msireg, data, mask);
594bf21cd93STycho Nightingale break;
595bf21cd93STycho Nightingale }
596bf21cd93STycho Nightingale }
597bf21cd93STycho Nightingale done:
598bf21cd93STycho Nightingale VHPET_UNLOCK(vhpet);
599bf21cd93STycho Nightingale return (0);
600bf21cd93STycho Nightingale }
601bf21cd93STycho Nightingale
602bf21cd93STycho Nightingale int
vhpet_mmio_read(struct vm * vm,int vcpuid,uint64_t gpa,uint64_t * rval,int size)6033e1c5f3aSPatrick Mooney vhpet_mmio_read(struct vm *vm, int vcpuid, uint64_t gpa, uint64_t *rval,
6043e1c5f3aSPatrick Mooney int size)
605bf21cd93STycho Nightingale {
606bf21cd93STycho Nightingale int i, offset;
607bf21cd93STycho Nightingale struct vhpet *vhpet;
608bf21cd93STycho Nightingale uint64_t data;
609bf21cd93STycho Nightingale
610bf21cd93STycho Nightingale vhpet = vm_hpet(vm);
611bf21cd93STycho Nightingale offset = gpa - VHPET_BASE;
612bf21cd93STycho Nightingale
613bf21cd93STycho Nightingale VHPET_LOCK(vhpet);
614bf21cd93STycho Nightingale
615bf21cd93STycho Nightingale /* Accesses to the HPET should be 4 or 8 bytes wide */
616bf21cd93STycho Nightingale if (size != 4 && size != 8) {
617bf21cd93STycho Nightingale data = 0;
618bf21cd93STycho Nightingale goto done;
619bf21cd93STycho Nightingale }
620bf21cd93STycho Nightingale
621bf21cd93STycho Nightingale /* Access to the HPET should be naturally aligned to its width */
622bf21cd93STycho Nightingale if (offset & (size - 1)) {
623bf21cd93STycho Nightingale data = 0;
624bf21cd93STycho Nightingale goto done;
625bf21cd93STycho Nightingale }
626bf21cd93STycho Nightingale
627bf21cd93STycho Nightingale if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) {
628bf21cd93STycho Nightingale data = vhpet_capabilities();
62984971882SPatrick Mooney goto done;
630bf21cd93STycho Nightingale }
631bf21cd93STycho Nightingale
632bf21cd93STycho Nightingale if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
633bf21cd93STycho Nightingale data = vhpet->config;
634bf21cd93STycho Nightingale goto done;
635bf21cd93STycho Nightingale }
636bf21cd93STycho Nightingale
637bf21cd93STycho Nightingale if (offset == HPET_ISR || offset == HPET_ISR + 4) {
638bf21cd93STycho Nightingale data = vhpet->isr;
639bf21cd93STycho Nightingale goto done;
640bf21cd93STycho Nightingale }
641bf21cd93STycho Nightingale
642bf21cd93STycho Nightingale if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
643bf21cd93STycho Nightingale data = vhpet_counter(vhpet, NULL);
644bf21cd93STycho Nightingale goto done;
645bf21cd93STycho Nightingale }
646bf21cd93STycho Nightingale
647bf21cd93STycho Nightingale for (i = 0; i < VHPET_NUM_TIMERS; i++) {
648bf21cd93STycho Nightingale if (offset == HPET_TIMER_CAP_CNF(i) ||
649bf21cd93STycho Nightingale offset == HPET_TIMER_CAP_CNF(i) + 4) {
650bf21cd93STycho Nightingale data = vhpet->timer[i].cap_config;
651bf21cd93STycho Nightingale break;
652bf21cd93STycho Nightingale }
653bf21cd93STycho Nightingale
654bf21cd93STycho Nightingale if (offset == HPET_TIMER_COMPARATOR(i) ||
655bf21cd93STycho Nightingale offset == HPET_TIMER_COMPARATOR(i) + 4) {
656bf21cd93STycho Nightingale data = vhpet->timer[i].compval;
657bf21cd93STycho Nightingale break;
658bf21cd93STycho Nightingale }
659bf21cd93STycho Nightingale
660bf21cd93STycho Nightingale if (offset == HPET_TIMER_FSB_VAL(i) ||
661bf21cd93STycho Nightingale offset == HPET_TIMER_FSB_ADDR(i)) {
662bf21cd93STycho Nightingale data = vhpet->timer[i].msireg;
663bf21cd93STycho Nightingale break;
664bf21cd93STycho Nightingale }
665bf21cd93STycho Nightingale }
666bf21cd93STycho Nightingale
667bf21cd93STycho Nightingale if (i >= VHPET_NUM_TIMERS)
668bf21cd93STycho Nightingale data = 0;
669bf21cd93STycho Nightingale done:
670bf21cd93STycho Nightingale VHPET_UNLOCK(vhpet);
671bf21cd93STycho Nightingale
672bf21cd93STycho Nightingale if (size == 4) {
673bf21cd93STycho Nightingale if (offset & 0x4)
674bf21cd93STycho Nightingale data >>= 32;
675bf21cd93STycho Nightingale }
676bf21cd93STycho Nightingale *rval = data;
677bf21cd93STycho Nightingale return (0);
678bf21cd93STycho Nightingale }
679bf21cd93STycho Nightingale
680bf21cd93STycho Nightingale struct vhpet *
vhpet_init(struct vm * vm)681bf21cd93STycho Nightingale vhpet_init(struct vm *vm)
682bf21cd93STycho Nightingale {
683bf21cd93STycho Nightingale int i, pincount;
684bf21cd93STycho Nightingale struct vhpet *vhpet;
685bf21cd93STycho Nightingale uint64_t allowed_irqs;
686bf21cd93STycho Nightingale struct vhpet_callout_arg *arg;
687bf21cd93STycho Nightingale
6888130f8e1SPatrick Mooney vhpet = kmem_zalloc(sizeof (struct vhpet), KM_SLEEP);
6892699b94cSPatrick Mooney vhpet->vm = vm;
690fb29dee0SPatrick Mooney mutex_init(&vhpet->lock, NULL, MUTEX_ADAPTIVE, NULL);
691bf21cd93STycho Nightingale
692bf21cd93STycho Nightingale pincount = vioapic_pincount(vm);
6934c87aefeSPatrick Mooney if (pincount >= 32)
6944c87aefeSPatrick Mooney allowed_irqs = 0xff000000; /* irqs 24-31 */
6954c87aefeSPatrick Mooney else if (pincount >= 20)
6964c87aefeSPatrick Mooney allowed_irqs = 0xf << (pincount - 4); /* 4 upper irqs */
697bf21cd93STycho Nightingale else
698bf21cd93STycho Nightingale allowed_irqs = 0;
699bf21cd93STycho Nightingale
700bf21cd93STycho Nightingale /*
701bf21cd93STycho Nightingale * Initialize HPET timer hardware state.
702bf21cd93STycho Nightingale */
703bf21cd93STycho Nightingale for (i = 0; i < VHPET_NUM_TIMERS; i++) {
704bf21cd93STycho Nightingale vhpet->timer[i].cap_config = allowed_irqs << 32;
705bf21cd93STycho Nightingale vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT;
706bf21cd93STycho Nightingale vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL;
707bf21cd93STycho Nightingale
708bf21cd93STycho Nightingale vhpet->timer[i].compval = 0xffffffff;
709bf21cd93STycho Nightingale callout_init(&vhpet->timer[i].callout, 1);
710bf21cd93STycho Nightingale
711bf21cd93STycho Nightingale arg = &vhpet->timer[i].arg;
712bf21cd93STycho Nightingale arg->vhpet = vhpet;
713bf21cd93STycho Nightingale arg->timer_num = i;
714bf21cd93STycho Nightingale }
715bf21cd93STycho Nightingale
716bf21cd93STycho Nightingale return (vhpet);
717bf21cd93STycho Nightingale }
718bf21cd93STycho Nightingale
719bf21cd93STycho Nightingale void
vhpet_cleanup(struct vhpet * vhpet)720bf21cd93STycho Nightingale vhpet_cleanup(struct vhpet *vhpet)
721bf21cd93STycho Nightingale {
722bf21cd93STycho Nightingale int i;
723bf21cd93STycho Nightingale
724bf21cd93STycho Nightingale for (i = 0; i < VHPET_NUM_TIMERS; i++)
725bf21cd93STycho Nightingale callout_drain(&vhpet->timer[i].callout);
726bf21cd93STycho Nightingale
727fb29dee0SPatrick Mooney mutex_destroy(&vhpet->lock);
7288130f8e1SPatrick Mooney kmem_free(vhpet, sizeof (*vhpet));
729bf21cd93STycho Nightingale }
730bf21cd93STycho Nightingale
731bf21cd93STycho Nightingale int
vhpet_getcap(struct vm_hpet_cap * cap)732bf21cd93STycho Nightingale vhpet_getcap(struct vm_hpet_cap *cap)
733bf21cd93STycho Nightingale {
734bf21cd93STycho Nightingale
735bf21cd93STycho Nightingale cap->capabilities = vhpet_capabilities();
736bf21cd93STycho Nightingale return (0);
737bf21cd93STycho Nightingale }
7384c87aefeSPatrick Mooney void
vhpet_localize_resources(struct vhpet * vhpet)7394c87aefeSPatrick Mooney vhpet_localize_resources(struct vhpet *vhpet)
7404c87aefeSPatrick Mooney {
7414c87aefeSPatrick Mooney for (uint_t i = 0; i < VHPET_NUM_TIMERS; i++) {
7424c87aefeSPatrick Mooney vmm_glue_callout_localize(&vhpet->timer[i].callout);
7434c87aefeSPatrick Mooney }
7444c87aefeSPatrick Mooney }
745d515dd77SPatrick Mooney
7462cac0506SPatrick Mooney void
vhpet_pause(struct vhpet * vhpet)7472cac0506SPatrick Mooney vhpet_pause(struct vhpet *vhpet)
7482cac0506SPatrick Mooney {
7492cac0506SPatrick Mooney VHPET_LOCK(vhpet);
7502cac0506SPatrick Mooney for (uint_t i = 0; i < VHPET_NUM_TIMERS; i++) {
7512cac0506SPatrick Mooney struct vhpet_timer *timer = &vhpet->timer[i];
7522cac0506SPatrick Mooney
7532cac0506SPatrick Mooney callout_stop(&timer->callout);
7542cac0506SPatrick Mooney }
7552cac0506SPatrick Mooney VHPET_UNLOCK(vhpet);
7562cac0506SPatrick Mooney }
7572cac0506SPatrick Mooney
7582cac0506SPatrick Mooney void
vhpet_resume(struct vhpet * vhpet)7592cac0506SPatrick Mooney vhpet_resume(struct vhpet *vhpet)
7602cac0506SPatrick Mooney {
7612cac0506SPatrick Mooney VHPET_LOCK(vhpet);
7622cac0506SPatrick Mooney for (uint_t i = 0; i < VHPET_NUM_TIMERS; i++) {
7632cac0506SPatrick Mooney struct vhpet_timer *timer = &vhpet->timer[i];
7642cac0506SPatrick Mooney
7652cac0506SPatrick Mooney if (timer->callout_expire != 0) {
7662cac0506SPatrick Mooney callout_reset_hrtime(&timer->callout,
7672cac0506SPatrick Mooney timer->callout_expire, vhpet_handler,
7682cac0506SPatrick Mooney &timer->arg, C_ABSOLUTE);
7692cac0506SPatrick Mooney }
7702cac0506SPatrick Mooney }
7712cac0506SPatrick Mooney VHPET_UNLOCK(vhpet);
7722cac0506SPatrick Mooney }
7732cac0506SPatrick Mooney
774d515dd77SPatrick Mooney static int
vhpet_data_read(void * datap,const vmm_data_req_t * req)775d515dd77SPatrick Mooney vhpet_data_read(void *datap, const vmm_data_req_t *req)
776d515dd77SPatrick Mooney {
777d515dd77SPatrick Mooney VERIFY3U(req->vdr_class, ==, VDC_HPET);
778d515dd77SPatrick Mooney VERIFY3U(req->vdr_version, ==, 1);
779a77feb92SPatrick Mooney VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_hpet_v1));
780d515dd77SPatrick Mooney
781d515dd77SPatrick Mooney struct vhpet *vhpet = datap;
782d515dd77SPatrick Mooney struct vdi_hpet_v1 *out = req->vdr_data;
783d515dd77SPatrick Mooney
784d515dd77SPatrick Mooney VHPET_LOCK(vhpet);
785d515dd77SPatrick Mooney out->vh_config = vhpet->config;
786d515dd77SPatrick Mooney out->vh_isr = vhpet->isr;
787d515dd77SPatrick Mooney out->vh_count_base = vhpet->base_count;
788d515dd77SPatrick Mooney out->vh_time_base = vm_normalize_hrtime(vhpet->vm, vhpet->base_time);
789d515dd77SPatrick Mooney for (uint_t i = 0; i < 8; i++) {
790d515dd77SPatrick Mooney const struct vhpet_timer *timer = &vhpet->timer[i];
791d515dd77SPatrick Mooney struct vdi_hpet_timer_v1 *timer_out = &out->vh_timers[i];
792d515dd77SPatrick Mooney
793d515dd77SPatrick Mooney timer_out->vht_config = timer->cap_config;
794d515dd77SPatrick Mooney timer_out->vht_msi = timer->msireg;
795d515dd77SPatrick Mooney timer_out->vht_comp_val = timer->compval;
796d515dd77SPatrick Mooney timer_out->vht_comp_rate = timer->comprate;
7972cac0506SPatrick Mooney if (timer->callout_expire != 0) {
798d515dd77SPatrick Mooney timer_out->vht_time_target =
799d515dd77SPatrick Mooney vm_normalize_hrtime(vhpet->vm,
800d515dd77SPatrick Mooney timer->callout_expire);
801d515dd77SPatrick Mooney } else {
802d515dd77SPatrick Mooney timer_out->vht_time_target = 0;
803d515dd77SPatrick Mooney }
804d515dd77SPatrick Mooney }
805d515dd77SPatrick Mooney VHPET_UNLOCK(vhpet);
806d515dd77SPatrick Mooney
807d515dd77SPatrick Mooney return (0);
808d515dd77SPatrick Mooney }
809d515dd77SPatrick Mooney
810d515dd77SPatrick Mooney enum vhpet_validation_error {
811d515dd77SPatrick Mooney VVE_OK,
812d515dd77SPatrick Mooney VVE_BAD_CONFIG,
813d515dd77SPatrick Mooney VVE_BAD_BASE_TIME,
814d515dd77SPatrick Mooney VVE_BAD_ISR,
815d515dd77SPatrick Mooney VVE_BAD_TIMER_CONFIG,
816d515dd77SPatrick Mooney VVE_BAD_TIMER_ISR,
817d515dd77SPatrick Mooney VVE_BAD_TIMER_TIME,
818d515dd77SPatrick Mooney };
819d515dd77SPatrick Mooney
820d515dd77SPatrick Mooney static enum vhpet_validation_error
vhpet_data_validate(const vmm_data_req_t * req,struct vm * vm)821d515dd77SPatrick Mooney vhpet_data_validate(const vmm_data_req_t *req, struct vm *vm)
822d515dd77SPatrick Mooney {
823d515dd77SPatrick Mooney ASSERT(req->vdr_version == 1 &&
824a77feb92SPatrick Mooney req->vdr_len >= sizeof (struct vdi_hpet_v1));
825d515dd77SPatrick Mooney const struct vdi_hpet_v1 *src = req->vdr_data;
826d515dd77SPatrick Mooney
827d515dd77SPatrick Mooney /* LegacyReplacement Routing is not supported */
828d515dd77SPatrick Mooney if ((src->vh_config & HPET_CNF_LEG_RT) != 0) {
829d515dd77SPatrick Mooney return (VVE_BAD_CONFIG);
830d515dd77SPatrick Mooney }
831d515dd77SPatrick Mooney
832d515dd77SPatrick Mooney /* A base time in the future makes no sense */
833d515dd77SPatrick Mooney const hrtime_t base_time = vm_denormalize_hrtime(vm, src->vh_time_base);
834d515dd77SPatrick Mooney if (base_time > gethrtime()) {
835d515dd77SPatrick Mooney return (VVE_BAD_BASE_TIME);
836d515dd77SPatrick Mooney }
837d515dd77SPatrick Mooney
838d515dd77SPatrick Mooney /* All asserted ISRs must be associated with an existing timer */
839d515dd77SPatrick Mooney if ((src->vh_isr & ~(uint64_t)((1 << VHPET_NUM_TIMERS) - 1)) != 0) {
840d515dd77SPatrick Mooney return (VVE_BAD_ISR);
841d515dd77SPatrick Mooney }
842d515dd77SPatrick Mooney
843d515dd77SPatrick Mooney for (uint_t i = 0; i < 8; i++) {
844d515dd77SPatrick Mooney const struct vdi_hpet_timer_v1 *timer = &src->vh_timers[i];
845d515dd77SPatrick Mooney
846d515dd77SPatrick Mooney const bool msi_enabled =
847d515dd77SPatrick Mooney (timer->vht_config & HPET_TCNF_FSB_EN) != 0;
848d515dd77SPatrick Mooney const bool level_triggered =
849d515dd77SPatrick Mooney (timer->vht_config & HPET_TCNF_INT_TYPE) != 0;
850d515dd77SPatrick Mooney const bool irq_asserted = (src->vh_isr & (1 << i)) != 0;
851d515dd77SPatrick Mooney const uint32_t allowed_irqs = (timer->vht_config >> 32);
852d515dd77SPatrick Mooney const uint32_t irq_pin =
853d515dd77SPatrick Mooney (timer->vht_config & HPET_TCNF_INT_ROUTE) >> 9;
854d515dd77SPatrick Mooney
855d515dd77SPatrick Mooney if (msi_enabled) {
856d515dd77SPatrick Mooney if (level_triggered) {
857d515dd77SPatrick Mooney return (VVE_BAD_TIMER_CONFIG);
858d515dd77SPatrick Mooney }
859d515dd77SPatrick Mooney } else {
860d515dd77SPatrick Mooney /*
861d515dd77SPatrick Mooney * Ensure interrupt route is valid as ensured by the
862d515dd77SPatrick Mooney * logic in vhpet_timer_update_config.
863d515dd77SPatrick Mooney */
864d515dd77SPatrick Mooney if (irq_pin != 0 &&
865d515dd77SPatrick Mooney (allowed_irqs & (1 << irq_pin)) == 0) {
866d515dd77SPatrick Mooney return (VVE_BAD_TIMER_CONFIG);
867d515dd77SPatrick Mooney }
868d515dd77SPatrick Mooney }
869d515dd77SPatrick Mooney if (irq_asserted && !level_triggered) {
870d515dd77SPatrick Mooney return (VVE_BAD_TIMER_ISR);
871d515dd77SPatrick Mooney }
872d515dd77SPatrick Mooney
873d515dd77SPatrick Mooney if (timer->vht_time_target != 0) {
874d515dd77SPatrick Mooney /*
875d515dd77SPatrick Mooney * A timer scheduled earlier than the base time of the
876d515dd77SPatrick Mooney * entire HPET makes no sense.
877d515dd77SPatrick Mooney */
878d515dd77SPatrick Mooney const uint64_t timer_target =
879d515dd77SPatrick Mooney vm_denormalize_hrtime(vm, timer->vht_time_target);
880d515dd77SPatrick Mooney if (timer_target < base_time) {
881d515dd77SPatrick Mooney return (VVE_BAD_TIMER_TIME);
882d515dd77SPatrick Mooney }
883d515dd77SPatrick Mooney }
884d515dd77SPatrick Mooney }
885d515dd77SPatrick Mooney
886d515dd77SPatrick Mooney return (VVE_OK);
887d515dd77SPatrick Mooney }
888d515dd77SPatrick Mooney
889d515dd77SPatrick Mooney static int
vhpet_data_write(void * datap,const vmm_data_req_t * req)890d515dd77SPatrick Mooney vhpet_data_write(void *datap, const vmm_data_req_t *req)
891d515dd77SPatrick Mooney {
892d515dd77SPatrick Mooney VERIFY3U(req->vdr_class, ==, VDC_HPET);
893d515dd77SPatrick Mooney VERIFY3U(req->vdr_version, ==, 1);
894a77feb92SPatrick Mooney VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_hpet_v1));
895d515dd77SPatrick Mooney
896d515dd77SPatrick Mooney struct vhpet *vhpet = datap;
897d515dd77SPatrick Mooney
898d515dd77SPatrick Mooney if (vhpet_data_validate(req, vhpet->vm) != VVE_OK) {
899d515dd77SPatrick Mooney return (EINVAL);
900d515dd77SPatrick Mooney }
901d515dd77SPatrick Mooney const struct vdi_hpet_v1 *src = req->vdr_data;
902d515dd77SPatrick Mooney
903d515dd77SPatrick Mooney VHPET_LOCK(vhpet);
904d515dd77SPatrick Mooney vhpet->config = src->vh_config;
905d515dd77SPatrick Mooney vhpet->isr = src->vh_isr;
906d515dd77SPatrick Mooney vhpet->base_count = src->vh_count_base;
907d515dd77SPatrick Mooney vhpet->base_time = vm_denormalize_hrtime(vhpet->vm, src->vh_time_base);
908d515dd77SPatrick Mooney
909d515dd77SPatrick Mooney for (uint_t i = 0; i < 8; i++) {
910d515dd77SPatrick Mooney struct vhpet_timer *timer = &vhpet->timer[i];
911d515dd77SPatrick Mooney const struct vdi_hpet_timer_v1 *timer_src = &src->vh_timers[i];
912d515dd77SPatrick Mooney
913d515dd77SPatrick Mooney timer->cap_config = timer_src->vht_config;
914d515dd77SPatrick Mooney timer->msireg = timer_src->vht_msi;
915d515dd77SPatrick Mooney timer->compval = timer_src->vht_comp_val;
916d515dd77SPatrick Mooney timer->comprate = timer_src->vht_comp_rate;
917d515dd77SPatrick Mooney
918d515dd77SPatrick Mooney /*
919d515dd77SPatrick Mooney * For now, any state associating an IOAPIC pin with a given
920d515dd77SPatrick Mooney * timer is not kept in sync. (We will not increment or
921d515dd77SPatrick Mooney * decrement a pin level based on the timer state.) It is left
922d515dd77SPatrick Mooney * to the consumer to keep those pin levels maintained if
923d515dd77SPatrick Mooney * modifying either the HPET or the IOAPIC.
924d515dd77SPatrick Mooney *
925d515dd77SPatrick Mooney * If both the HPET and IOAPIC are exported and then imported,
926d515dd77SPatrick Mooney * this will occur naturally, as any asserted IOAPIC pin level
927d515dd77SPatrick Mooney * from the HPET would come along for the ride.
928d515dd77SPatrick Mooney */
929d515dd77SPatrick Mooney
930d515dd77SPatrick Mooney if (timer_src->vht_time_target != 0) {
931d515dd77SPatrick Mooney timer->callout_expire = vm_denormalize_hrtime(vhpet->vm,
932d515dd77SPatrick Mooney timer_src->vht_time_target);
9332cac0506SPatrick Mooney
9342cac0506SPatrick Mooney if (!vm_is_paused(vhpet->vm)) {
9352cac0506SPatrick Mooney callout_reset_hrtime(&timer->callout,
9362cac0506SPatrick Mooney timer->callout_expire, vhpet_handler,
9372cac0506SPatrick Mooney &timer->arg, C_ABSOLUTE);
9382cac0506SPatrick Mooney }
939d515dd77SPatrick Mooney } else {
940d515dd77SPatrick Mooney timer->callout_expire = 0;
941d515dd77SPatrick Mooney }
942d515dd77SPatrick Mooney }
943d515dd77SPatrick Mooney VHPET_UNLOCK(vhpet);
944d515dd77SPatrick Mooney return (0);
945d515dd77SPatrick Mooney }
946d515dd77SPatrick Mooney
947d515dd77SPatrick Mooney static const vmm_data_version_entry_t hpet_v1 = {
948d515dd77SPatrick Mooney .vdve_class = VDC_HPET,
949d515dd77SPatrick Mooney .vdve_version = 1,
950d515dd77SPatrick Mooney .vdve_len_expect = sizeof (struct vdi_hpet_v1),
951d515dd77SPatrick Mooney .vdve_readf = vhpet_data_read,
952d515dd77SPatrick Mooney .vdve_writef = vhpet_data_write,
953d515dd77SPatrick Mooney };
954d515dd77SPatrick Mooney VMM_DATA_VERSION(hpet_v1);
955