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