xref: /illumos-gate/usr/src/uts/intel/io/vmm/io/vhpet.c (revision 32640292)
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