vrtc.c revision 280775
1276428Sneel/*-
2276428Sneel * Copyright (c) 2014, Neel Natu (neel@freebsd.org)
3276428Sneel * All rights reserved.
4276428Sneel *
5276428Sneel * Redistribution and use in source and binary forms, with or without
6276428Sneel * modification, are permitted provided that the following conditions
7276428Sneel * are met:
8276428Sneel * 1. Redistributions of source code must retain the above copyright
9276428Sneel *    notice unmodified, this list of conditions, and the following
10276428Sneel *    disclaimer.
11276428Sneel * 2. Redistributions in binary form must reproduce the above copyright
12276428Sneel *    notice, this list of conditions and the following disclaimer in the
13276428Sneel *    documentation and/or other materials provided with the distribution.
14276428Sneel *
15276428Sneel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16276428Sneel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17276428Sneel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18276428Sneel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19276428Sneel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20276428Sneel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21276428Sneel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22276428Sneel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23276428Sneel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24276428Sneel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25276428Sneel */
26276428Sneel
27276428Sneel#include <sys/cdefs.h>
28276428Sneel__FBSDID("$FreeBSD: head/sys/amd64/vmm/io/vrtc.c 280775 2015-03-28 02:55:16Z neel $");
29276428Sneel
30276428Sneel#include <sys/param.h>
31276428Sneel#include <sys/systm.h>
32276428Sneel#include <sys/queue.h>
33276428Sneel#include <sys/cpuset.h>
34276428Sneel#include <sys/kernel.h>
35276428Sneel#include <sys/malloc.h>
36276428Sneel#include <sys/lock.h>
37276428Sneel#include <sys/mutex.h>
38276428Sneel#include <sys/clock.h>
39276428Sneel#include <sys/sysctl.h>
40276428Sneel
41276428Sneel#include <machine/vmm.h>
42276428Sneel
43276428Sneel#include <isa/rtc.h>
44276428Sneel
45276428Sneel#include "vmm_ktr.h"
46276428Sneel#include "vatpic.h"
47276428Sneel#include "vioapic.h"
48276428Sneel#include "vrtc.h"
49276428Sneel
50276428Sneel/* Register layout of the RTC */
51276428Sneelstruct rtcdev {
52276428Sneel	uint8_t	sec;
53276428Sneel	uint8_t	alarm_sec;
54276428Sneel	uint8_t	min;
55276428Sneel	uint8_t	alarm_min;
56276428Sneel	uint8_t	hour;
57276428Sneel	uint8_t	alarm_hour;
58276428Sneel	uint8_t	day_of_week;
59276428Sneel	uint8_t	day_of_month;
60276428Sneel	uint8_t	month;
61276428Sneel	uint8_t	year;
62276428Sneel	uint8_t	reg_a;
63276428Sneel	uint8_t	reg_b;
64276428Sneel	uint8_t	reg_c;
65276428Sneel	uint8_t	reg_d;
66276428Sneel	uint8_t	nvram[128 - 14];
67276428Sneel} __packed;
68276428SneelCTASSERT(sizeof(struct rtcdev) == 128);
69276428Sneel
70276428Sneelstruct vrtc {
71276428Sneel	struct vm	*vm;
72276428Sneel	struct mtx	mtx;
73276428Sneel	struct callout	callout;
74276428Sneel	u_int		addr;		/* RTC register to read or write */
75276428Sneel	sbintime_t	base_uptime;
76276428Sneel	time_t		base_rtctime;
77276428Sneel	struct rtcdev	rtcdev;
78276428Sneel};
79276428Sneel
80276428Sneel#define	VRTC_LOCK(vrtc)		mtx_lock(&((vrtc)->mtx))
81276428Sneel#define	VRTC_UNLOCK(vrtc)	mtx_unlock(&((vrtc)->mtx))
82276428Sneel#define	VRTC_LOCKED(vrtc)	mtx_owned(&((vrtc)->mtx))
83276428Sneel
84276428Sneel/*
85276428Sneel * RTC time is considered "broken" if:
86276428Sneel * - RTC updates are halted by the guest
87276428Sneel * - RTC date/time fields have invalid values
88276428Sneel */
89276428Sneel#define	VRTC_BROKEN_TIME	((time_t)-1)
90276428Sneel
91276428Sneel#define	RTC_IRQ			8
92276428Sneel#define	RTCSB_BIN		0x04
93276428Sneel#define	RTCSB_ALL_INTRS		(RTCSB_UINTR | RTCSB_AINTR | RTCSB_PINTR)
94276428Sneel#define	rtc_halted(vrtc)	((vrtc->rtcdev.reg_b & RTCSB_HALT) != 0)
95276428Sneel#define	aintr_enabled(vrtc)	(((vrtc)->rtcdev.reg_b & RTCSB_AINTR) != 0)
96276428Sneel#define	pintr_enabled(vrtc)	(((vrtc)->rtcdev.reg_b & RTCSB_PINTR) != 0)
97276428Sneel#define	uintr_enabled(vrtc)	(((vrtc)->rtcdev.reg_b & RTCSB_UINTR) != 0)
98276428Sneel
99276428Sneelstatic void vrtc_callout_handler(void *arg);
100276428Sneelstatic void vrtc_set_reg_c(struct vrtc *vrtc, uint8_t newval);
101276428Sneel
102276428Sneelstatic MALLOC_DEFINE(M_VRTC, "vrtc", "bhyve virtual rtc");
103276428Sneel
104276428SneelSYSCTL_DECL(_hw_vmm);
105276428SneelSYSCTL_NODE(_hw_vmm, OID_AUTO, vrtc, CTLFLAG_RW, NULL, NULL);
106276428Sneel
107276428Sneelstatic int rtc_flag_broken_time = 1;
108276428SneelSYSCTL_INT(_hw_vmm_vrtc, OID_AUTO, flag_broken_time, CTLFLAG_RDTUN,
109276428Sneel    &rtc_flag_broken_time, 0, "Stop guest when invalid RTC time is detected");
110276428Sneel
111276428Sneelstatic __inline bool
112276428Sneeldivider_enabled(int reg_a)
113276428Sneel{
114276428Sneel	/*
115276428Sneel	 * The RTC is counting only when dividers are not held in reset.
116276428Sneel	 */
117276428Sneel	return ((reg_a & 0x70) == 0x20);
118276428Sneel}
119276428Sneel
120276428Sneelstatic __inline bool
121276428Sneelupdate_enabled(struct vrtc *vrtc)
122276428Sneel{
123276428Sneel	/*
124276428Sneel	 * RTC date/time can be updated only if:
125276428Sneel	 * - divider is not held in reset
126276428Sneel	 * - guest has not disabled updates
127276428Sneel	 * - the date/time fields have valid contents
128276428Sneel	 */
129276428Sneel	if (!divider_enabled(vrtc->rtcdev.reg_a))
130276428Sneel		return (false);
131276428Sneel
132276428Sneel	if (rtc_halted(vrtc))
133276428Sneel		return (false);
134276428Sneel
135276428Sneel	if (vrtc->base_rtctime == VRTC_BROKEN_TIME)
136276428Sneel		return (false);
137276428Sneel
138276428Sneel	return (true);
139276428Sneel}
140276428Sneel
141276428Sneelstatic time_t
142276428Sneelvrtc_curtime(struct vrtc *vrtc)
143276428Sneel{
144276428Sneel	sbintime_t now, delta;
145276428Sneel	time_t t;
146276428Sneel
147276428Sneel	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
148276428Sneel
149276428Sneel	t = vrtc->base_rtctime;
150276428Sneel	if (update_enabled(vrtc)) {
151276428Sneel		now = sbinuptime();
152276428Sneel		delta = now - vrtc->base_uptime;
153276428Sneel		KASSERT(delta >= 0, ("vrtc_curtime: uptime went backwards: "
154276428Sneel		    "%#lx to %#lx", vrtc->base_uptime, now));
155276428Sneel		t += delta / SBT_1S;
156276428Sneel	}
157276428Sneel	return (t);
158276428Sneel}
159276428Sneel
160276428Sneelstatic __inline uint8_t
161276428Sneelrtcset(struct rtcdev *rtc, int val)
162276428Sneel{
163276428Sneel
164276428Sneel	KASSERT(val >= 0 && val < 100, ("%s: invalid bin2bcd index %d",
165276428Sneel	    __func__, val));
166276428Sneel
167276428Sneel	return ((rtc->reg_b & RTCSB_BIN) ? val : bin2bcd_data[val]);
168276428Sneel}
169276428Sneel
170276428Sneelstatic void
171276428Sneelsecs_to_rtc(time_t rtctime, struct vrtc *vrtc, int force_update)
172276428Sneel{
173276428Sneel	struct clocktime ct;
174276428Sneel	struct timespec ts;
175276428Sneel	struct rtcdev *rtc;
176276428Sneel	int hour;
177276428Sneel
178276428Sneel	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
179276428Sneel
180276428Sneel	if (rtctime < 0) {
181276428Sneel		KASSERT(rtctime == VRTC_BROKEN_TIME,
182276428Sneel		    ("%s: invalid vrtc time %#lx", __func__, rtctime));
183276428Sneel		return;
184276428Sneel	}
185276428Sneel
186276428Sneel	/*
187276428Sneel	 * If the RTC is halted then the guest has "ownership" of the
188276428Sneel	 * date/time fields. Don't update the RTC date/time fields in
189276428Sneel	 * this case (unless forced).
190276428Sneel	 */
191276428Sneel	if (rtc_halted(vrtc) && !force_update)
192276428Sneel		return;
193276428Sneel
194276428Sneel	ts.tv_sec = rtctime;
195276428Sneel	ts.tv_nsec = 0;
196276428Sneel	clock_ts_to_ct(&ts, &ct);
197276428Sneel
198276428Sneel	KASSERT(ct.sec >= 0 && ct.sec <= 59, ("invalid clocktime sec %d",
199276428Sneel	    ct.sec));
200276428Sneel	KASSERT(ct.min >= 0 && ct.min <= 59, ("invalid clocktime min %d",
201276428Sneel	    ct.min));
202276428Sneel	KASSERT(ct.hour >= 0 && ct.hour <= 23, ("invalid clocktime hour %d",
203276428Sneel	    ct.hour));
204276428Sneel	KASSERT(ct.dow >= 0 && ct.dow <= 6, ("invalid clocktime wday %d",
205276428Sneel	    ct.dow));
206276428Sneel	KASSERT(ct.day >= 1 && ct.day <= 31, ("invalid clocktime mday %d",
207276428Sneel	    ct.day));
208276428Sneel	KASSERT(ct.mon >= 1 && ct.mon <= 12, ("invalid clocktime month %d",
209276428Sneel	    ct.mon));
210276428Sneel	KASSERT(ct.year >= POSIX_BASE_YEAR, ("invalid clocktime year %d",
211276428Sneel	    ct.year));
212276428Sneel
213276428Sneel	rtc = &vrtc->rtcdev;
214276428Sneel	rtc->sec = rtcset(rtc, ct.sec);
215276428Sneel	rtc->min = rtcset(rtc, ct.min);
216276428Sneel
217280775Sneel	if (rtc->reg_b & RTCSB_24HR) {
218280775Sneel		hour = ct.hour;
219280775Sneel	} else {
220280775Sneel		/*
221280775Sneel		 * Convert to the 12-hour format.
222280775Sneel		 */
223280775Sneel		switch (ct.hour) {
224280775Sneel		case 0:			/* 12 AM */
225280775Sneel		case 12:		/* 12 PM */
226280775Sneel			hour = 12;
227280775Sneel			break;
228280775Sneel		default:
229280775Sneel			/*
230280775Sneel			 * The remaining 'ct.hour' values are interpreted as:
231280775Sneel			 * [1  - 11] ->  1 - 11 AM
232280775Sneel			 * [13 - 23] ->  1 - 11 PM
233280775Sneel			 */
234280775Sneel			hour = ct.hour % 12;
235280775Sneel			break;
236280775Sneel		}
237280775Sneel	}
238276428Sneel
239276428Sneel	rtc->hour = rtcset(rtc, hour);
240276428Sneel
241276428Sneel	if ((rtc->reg_b & RTCSB_24HR) == 0 && ct.hour >= 12)
242276428Sneel		rtc->hour |= 0x80;	    /* set MSB to indicate PM */
243276428Sneel
244276428Sneel	rtc->day_of_week = rtcset(rtc, ct.dow + 1);
245276428Sneel	rtc->day_of_month = rtcset(rtc, ct.day);
246276428Sneel	rtc->month = rtcset(rtc, ct.mon);
247276428Sneel	rtc->year = rtcset(rtc, ct.year % 100);
248276428Sneel}
249276428Sneel
250276428Sneelstatic int
251276428Sneelrtcget(struct rtcdev *rtc, int val, int *retval)
252276428Sneel{
253276428Sneel	uint8_t upper, lower;
254276428Sneel
255276428Sneel	if (rtc->reg_b & RTCSB_BIN) {
256276428Sneel		*retval = val;
257276428Sneel		return (0);
258276428Sneel	}
259276428Sneel
260276428Sneel	lower = val & 0xf;
261276428Sneel	upper = (val >> 4) & 0xf;
262276428Sneel
263276428Sneel	if (lower > 9 || upper > 9)
264276428Sneel		return (-1);
265276428Sneel
266276428Sneel	*retval = upper * 10 + lower;
267276428Sneel	return (0);
268276428Sneel}
269276428Sneel
270276428Sneelstatic time_t
271276428Sneelrtc_to_secs(struct vrtc *vrtc)
272276428Sneel{
273276428Sneel	struct clocktime ct;
274276428Sneel	struct timespec ts;
275276428Sneel	struct rtcdev *rtc;
276276428Sneel	struct vm *vm;
277276428Sneel	int error, hour, pm, year;
278276428Sneel
279276428Sneel	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
280276428Sneel
281276428Sneel	vm = vrtc->vm;
282276428Sneel	rtc = &vrtc->rtcdev;
283276428Sneel
284276428Sneel	bzero(&ct, sizeof(struct clocktime));
285276428Sneel
286276428Sneel	error = rtcget(rtc, rtc->sec, &ct.sec);
287276428Sneel	if (error || ct.sec < 0 || ct.sec > 59) {
288276428Sneel		VM_CTR2(vm, "Invalid RTC sec %#x/%d", rtc->sec, ct.sec);
289276428Sneel		goto fail;
290276428Sneel	}
291276428Sneel
292276428Sneel	error = rtcget(rtc, rtc->min, &ct.min);
293276428Sneel	if (error || ct.min < 0 || ct.min > 59) {
294276428Sneel		VM_CTR2(vm, "Invalid RTC min %#x/%d", rtc->min, ct.min);
295276428Sneel		goto fail;
296276428Sneel	}
297276428Sneel
298276428Sneel	pm = 0;
299276428Sneel	hour = rtc->hour;
300276428Sneel	if ((rtc->reg_b & RTCSB_24HR) == 0) {
301276428Sneel		if (hour & 0x80) {
302276428Sneel			hour &= ~0x80;
303276428Sneel			pm = 1;
304276428Sneel		}
305276428Sneel	}
306276428Sneel	error = rtcget(rtc, hour, &ct.hour);
307276428Sneel	if ((rtc->reg_b & RTCSB_24HR) == 0) {
308280775Sneel		if (ct.hour >= 1 && ct.hour <= 12) {
309280775Sneel			/*
310280775Sneel			 * Convert from 12-hour format to internal 24-hour
311280775Sneel			 * representation as follows:
312280775Sneel			 *
313280775Sneel			 *    12-hour format		ct.hour
314280775Sneel			 *	12	AM		0
315280775Sneel			 *	1 - 11	AM		1 - 11
316280775Sneel			 *	12	PM		12
317280775Sneel			 *	1 - 11	PM		13 - 23
318280775Sneel			 */
319280775Sneel			if (ct.hour == 12)
320280775Sneel				ct.hour = 0;
321280775Sneel			if (pm)
322280775Sneel				ct.hour += 12;
323280775Sneel		} else {
324280775Sneel			VM_CTR2(vm, "Invalid RTC 12-hour format %#x/%d",
325280775Sneel			    rtc->hour, ct.hour);
326280775Sneel			goto fail;
327280775Sneel		}
328276428Sneel	}
329276428Sneel
330276428Sneel	if (error || ct.hour < 0 || ct.hour > 23) {
331276428Sneel		VM_CTR2(vm, "Invalid RTC hour %#x/%d", rtc->hour, ct.hour);
332276428Sneel		goto fail;
333276428Sneel	}
334276428Sneel
335276428Sneel	/*
336276428Sneel	 * Ignore 'rtc->dow' because some guests like Linux don't bother
337276428Sneel	 * setting it at all while others like OpenBSD/i386 set it incorrectly.
338276428Sneel	 *
339276428Sneel	 * clock_ct_to_ts() does not depend on 'ct.dow' anyways so ignore it.
340276428Sneel	 */
341276428Sneel	ct.dow = -1;
342276428Sneel
343276428Sneel	error = rtcget(rtc, rtc->day_of_month, &ct.day);
344276428Sneel	if (error || ct.day < 1 || ct.day > 31) {
345276428Sneel		VM_CTR2(vm, "Invalid RTC mday %#x/%d", rtc->day_of_month,
346276428Sneel		    ct.day);
347276428Sneel		goto fail;
348276428Sneel	}
349276428Sneel
350276428Sneel	error = rtcget(rtc, rtc->month, &ct.mon);
351276428Sneel	if (error || ct.mon < 1 || ct.mon > 12) {
352276428Sneel		VM_CTR2(vm, "Invalid RTC month %#x/%d", rtc->month, ct.mon);
353276428Sneel		goto fail;
354276428Sneel	}
355276428Sneel
356276428Sneel	error = rtcget(rtc, rtc->year, &year);
357276428Sneel	if (error || year < 0 || year > 99) {
358276428Sneel		VM_CTR2(vm, "Invalid RTC year %#x/%d", rtc->year, year);
359276428Sneel		goto fail;
360276428Sneel	}
361276428Sneel	if (year >= 70)
362276428Sneel		ct.year = 1900 + year;
363276428Sneel	else
364276428Sneel		ct.year = 2000 + year;
365276428Sneel
366276428Sneel	error = clock_ct_to_ts(&ct, &ts);
367276428Sneel	if (error || ts.tv_sec < 0) {
368276428Sneel		VM_CTR3(vm, "Invalid RTC clocktime.date %04d-%02d-%02d",
369276428Sneel		    ct.year, ct.mon, ct.day);
370276428Sneel		VM_CTR3(vm, "Invalid RTC clocktime.time %02d:%02d:%02d",
371276428Sneel		    ct.hour, ct.min, ct.sec);
372276428Sneel		goto fail;
373276428Sneel	}
374276428Sneel	return (ts.tv_sec);		/* success */
375276428Sneelfail:
376276428Sneel	return (VRTC_BROKEN_TIME);	/* failure */
377276428Sneel}
378276428Sneel
379276428Sneelstatic int
380276428Sneelvrtc_time_update(struct vrtc *vrtc, time_t newtime)
381276428Sneel{
382276428Sneel	struct rtcdev *rtc;
383276428Sneel	time_t oldtime;
384276428Sneel	uint8_t alarm_sec, alarm_min, alarm_hour;
385276428Sneel
386276428Sneel	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
387276428Sneel
388276428Sneel	rtc = &vrtc->rtcdev;
389276428Sneel	alarm_sec = rtc->alarm_sec;
390276428Sneel	alarm_min = rtc->alarm_min;
391276428Sneel	alarm_hour = rtc->alarm_hour;
392276428Sneel
393276428Sneel	oldtime = vrtc->base_rtctime;
394276428Sneel	VM_CTR2(vrtc->vm, "Updating RTC time from %#lx to %#lx",
395276428Sneel	    oldtime, newtime);
396276428Sneel
397276428Sneel	if (newtime == oldtime)
398276428Sneel		return (0);
399276428Sneel
400276428Sneel	/*
401276428Sneel	 * If 'newtime' indicates that RTC updates are disabled then just
402276428Sneel	 * record that and return. There is no need to do alarm interrupt
403276428Sneel	 * processing or update 'base_uptime' in this case.
404276428Sneel	 */
405276428Sneel	if (newtime == VRTC_BROKEN_TIME) {
406276428Sneel		vrtc->base_rtctime = VRTC_BROKEN_TIME;
407276428Sneel		return (0);
408276428Sneel	}
409276428Sneel
410276428Sneel	/*
411276428Sneel	 * Return an error if RTC updates are halted by the guest.
412276428Sneel	 */
413276428Sneel	if (rtc_halted(vrtc)) {
414276428Sneel		VM_CTR0(vrtc->vm, "RTC update halted by guest");
415276428Sneel		return (EBUSY);
416276428Sneel	}
417276428Sneel
418276428Sneel	do {
419276428Sneel		/*
420276428Sneel		 * If the alarm interrupt is enabled and 'oldtime' is valid
421276428Sneel		 * then visit all the seconds between 'oldtime' and 'newtime'
422276428Sneel		 * to check for the alarm condition.
423276428Sneel		 *
424276428Sneel		 * Otherwise move the RTC time forward directly to 'newtime'.
425276428Sneel		 */
426276428Sneel		if (aintr_enabled(vrtc) && oldtime != VRTC_BROKEN_TIME)
427276428Sneel			vrtc->base_rtctime++;
428276428Sneel		else
429276428Sneel			vrtc->base_rtctime = newtime;
430276428Sneel
431276428Sneel		if (aintr_enabled(vrtc)) {
432276428Sneel			/*
433276428Sneel			 * Update the RTC date/time fields before checking
434276428Sneel			 * if the alarm conditions are satisfied.
435276428Sneel			 */
436276428Sneel			secs_to_rtc(vrtc->base_rtctime, vrtc, 0);
437276428Sneel
438276428Sneel			if ((alarm_sec >= 0xC0 || alarm_sec == rtc->sec) &&
439276428Sneel			    (alarm_min >= 0xC0 || alarm_min == rtc->min) &&
440276428Sneel			    (alarm_hour >= 0xC0 || alarm_hour == rtc->hour)) {
441276428Sneel				vrtc_set_reg_c(vrtc, rtc->reg_c | RTCIR_ALARM);
442276428Sneel			}
443276428Sneel		}
444276428Sneel	} while (vrtc->base_rtctime != newtime);
445276428Sneel
446276428Sneel	if (uintr_enabled(vrtc))
447276428Sneel		vrtc_set_reg_c(vrtc, rtc->reg_c | RTCIR_UPDATE);
448276428Sneel
449276428Sneel	vrtc->base_uptime = sbinuptime();
450276428Sneel
451276428Sneel	return (0);
452276428Sneel}
453276428Sneel
454276428Sneelstatic sbintime_t
455276428Sneelvrtc_freq(struct vrtc *vrtc)
456276428Sneel{
457276428Sneel	int ratesel;
458276428Sneel
459276428Sneel	static sbintime_t pf[16] = {
460276428Sneel		0,
461276428Sneel		SBT_1S / 256,
462276428Sneel		SBT_1S / 128,
463276428Sneel		SBT_1S / 8192,
464276428Sneel		SBT_1S / 4096,
465276428Sneel		SBT_1S / 2048,
466276428Sneel		SBT_1S / 1024,
467276428Sneel		SBT_1S / 512,
468276428Sneel		SBT_1S / 256,
469276428Sneel		SBT_1S / 128,
470276428Sneel		SBT_1S / 64,
471276428Sneel		SBT_1S / 32,
472276428Sneel		SBT_1S / 16,
473276428Sneel		SBT_1S / 8,
474276428Sneel		SBT_1S / 4,
475276428Sneel		SBT_1S / 2,
476276428Sneel	};
477276428Sneel
478276428Sneel	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
479276428Sneel
480276428Sneel	/*
481276428Sneel	 * If both periodic and alarm interrupts are enabled then use the
482276428Sneel	 * periodic frequency to drive the callout. The minimum periodic
483276428Sneel	 * frequency (2 Hz) is higher than the alarm frequency (1 Hz) so
484276428Sneel	 * piggyback the alarm on top of it. The same argument applies to
485276428Sneel	 * the update interrupt.
486276428Sneel	 */
487276428Sneel	if (pintr_enabled(vrtc) && divider_enabled(vrtc->rtcdev.reg_a)) {
488276428Sneel		ratesel = vrtc->rtcdev.reg_a & 0xf;
489276428Sneel		return (pf[ratesel]);
490276428Sneel	} else if (aintr_enabled(vrtc) && update_enabled(vrtc)) {
491276428Sneel		return (SBT_1S);
492276428Sneel	} else if (uintr_enabled(vrtc) && update_enabled(vrtc)) {
493276428Sneel		return (SBT_1S);
494276428Sneel	} else {
495276428Sneel		return (0);
496276428Sneel	}
497276428Sneel}
498276428Sneel
499276428Sneelstatic void
500276428Sneelvrtc_callout_reset(struct vrtc *vrtc, sbintime_t freqsbt)
501276428Sneel{
502276428Sneel
503276428Sneel	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
504276428Sneel
505276428Sneel	if (freqsbt == 0) {
506276428Sneel		if (callout_active(&vrtc->callout)) {
507276428Sneel			VM_CTR0(vrtc->vm, "RTC callout stopped");
508276428Sneel			callout_stop(&vrtc->callout);
509276428Sneel		}
510276428Sneel		return;
511276428Sneel	}
512276428Sneel	VM_CTR1(vrtc->vm, "RTC callout frequency %d hz", SBT_1S / freqsbt);
513276428Sneel	callout_reset_sbt(&vrtc->callout, freqsbt, 0, vrtc_callout_handler,
514276428Sneel	    vrtc, 0);
515276428Sneel}
516276428Sneel
517276428Sneelstatic void
518276428Sneelvrtc_callout_handler(void *arg)
519276428Sneel{
520276428Sneel	struct vrtc *vrtc = arg;
521276428Sneel	sbintime_t freqsbt;
522276428Sneel	time_t rtctime;
523276428Sneel	int error;
524276428Sneel
525276428Sneel	VM_CTR0(vrtc->vm, "vrtc callout fired");
526276428Sneel
527276428Sneel	VRTC_LOCK(vrtc);
528276428Sneel	if (callout_pending(&vrtc->callout))	/* callout was reset */
529276428Sneel		goto done;
530276428Sneel
531276428Sneel	if (!callout_active(&vrtc->callout))	/* callout was stopped */
532276428Sneel		goto done;
533276428Sneel
534276428Sneel	callout_deactivate(&vrtc->callout);
535276428Sneel
536276428Sneel	KASSERT((vrtc->rtcdev.reg_b & RTCSB_ALL_INTRS) != 0,
537276428Sneel	    ("gratuitous vrtc callout"));
538276428Sneel
539276428Sneel	if (pintr_enabled(vrtc))
540276428Sneel		vrtc_set_reg_c(vrtc, vrtc->rtcdev.reg_c | RTCIR_PERIOD);
541276428Sneel
542276428Sneel	if (aintr_enabled(vrtc) || uintr_enabled(vrtc)) {
543276428Sneel		rtctime = vrtc_curtime(vrtc);
544276428Sneel		error = vrtc_time_update(vrtc, rtctime);
545276428Sneel		KASSERT(error == 0, ("%s: vrtc_time_update error %d",
546276428Sneel		    __func__, error));
547276428Sneel	}
548276428Sneel
549276428Sneel	freqsbt = vrtc_freq(vrtc);
550276428Sneel	KASSERT(freqsbt != 0, ("%s: vrtc frequency cannot be zero", __func__));
551276428Sneel	vrtc_callout_reset(vrtc, freqsbt);
552276428Sneeldone:
553276428Sneel	VRTC_UNLOCK(vrtc);
554276428Sneel}
555276428Sneel
556276428Sneelstatic __inline void
557276428Sneelvrtc_callout_check(struct vrtc *vrtc, sbintime_t freq)
558276428Sneel{
559276428Sneel	int active;
560276428Sneel
561276428Sneel	active = callout_active(&vrtc->callout) ? 1 : 0;
562276428Sneel	KASSERT((freq == 0 && !active) || (freq != 0 && active),
563276428Sneel	    ("vrtc callout %s with frequency %#lx",
564276428Sneel	    active ? "active" : "inactive", freq));
565276428Sneel}
566276428Sneel
567276428Sneelstatic void
568276428Sneelvrtc_set_reg_c(struct vrtc *vrtc, uint8_t newval)
569276428Sneel{
570276428Sneel	struct rtcdev *rtc;
571276428Sneel	int oldirqf, newirqf;
572276428Sneel	uint8_t oldval, changed;
573276428Sneel
574276428Sneel	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
575276428Sneel
576276428Sneel	rtc = &vrtc->rtcdev;
577276428Sneel	newval &= RTCIR_ALARM | RTCIR_PERIOD | RTCIR_UPDATE;
578276428Sneel
579276428Sneel	oldirqf = rtc->reg_c & RTCIR_INT;
580276428Sneel	if ((aintr_enabled(vrtc) && (newval & RTCIR_ALARM) != 0) ||
581276428Sneel	    (pintr_enabled(vrtc) && (newval & RTCIR_PERIOD) != 0) ||
582276428Sneel	    (uintr_enabled(vrtc) && (newval & RTCIR_UPDATE) != 0)) {
583276428Sneel		newirqf = RTCIR_INT;
584276428Sneel	} else {
585276428Sneel		newirqf = 0;
586276428Sneel	}
587276428Sneel
588276428Sneel	oldval = rtc->reg_c;
589276428Sneel	rtc->reg_c = newirqf | newval;
590276428Sneel	changed = oldval ^ rtc->reg_c;
591276428Sneel	if (changed) {
592276428Sneel		VM_CTR2(vrtc->vm, "RTC reg_c changed from %#x to %#x",
593276428Sneel		    oldval, rtc->reg_c);
594276428Sneel	}
595276428Sneel
596276428Sneel	if (!oldirqf && newirqf) {
597276428Sneel		VM_CTR1(vrtc->vm, "RTC irq %d asserted", RTC_IRQ);
598276428Sneel		vatpic_pulse_irq(vrtc->vm, RTC_IRQ);
599276428Sneel		vioapic_pulse_irq(vrtc->vm, RTC_IRQ);
600276428Sneel	} else if (oldirqf && !newirqf) {
601276428Sneel		VM_CTR1(vrtc->vm, "RTC irq %d deasserted", RTC_IRQ);
602276428Sneel	}
603276428Sneel}
604276428Sneel
605276428Sneelstatic int
606276428Sneelvrtc_set_reg_b(struct vrtc *vrtc, uint8_t newval)
607276428Sneel{
608276428Sneel	struct rtcdev *rtc;
609276428Sneel	sbintime_t oldfreq, newfreq;
610276428Sneel	time_t curtime, rtctime;
611276428Sneel	int error;
612276428Sneel	uint8_t oldval, changed;
613276428Sneel
614276428Sneel	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
615276428Sneel
616276428Sneel	rtc = &vrtc->rtcdev;
617276428Sneel	oldval = rtc->reg_b;
618276428Sneel	oldfreq = vrtc_freq(vrtc);
619276428Sneel
620276428Sneel	rtc->reg_b = newval;
621276428Sneel	changed = oldval ^ newval;
622276428Sneel	if (changed) {
623276428Sneel		VM_CTR2(vrtc->vm, "RTC reg_b changed from %#x to %#x",
624276428Sneel		    oldval, newval);
625276428Sneel	}
626276428Sneel
627276428Sneel	if (changed & RTCSB_HALT) {
628276428Sneel		if ((newval & RTCSB_HALT) == 0) {
629276428Sneel			rtctime = rtc_to_secs(vrtc);
630276428Sneel			if (rtctime == VRTC_BROKEN_TIME) {
631276428Sneel				/*
632276428Sneel				 * Stop updating the RTC if the date/time
633276428Sneel				 * programmed by the guest is not correct.
634276428Sneel				 */
635276428Sneel				VM_CTR0(vrtc->vm, "Invalid RTC date/time "
636276428Sneel				    "programming detected");
637276428Sneel
638276428Sneel				if (rtc_flag_broken_time)
639276428Sneel					return (-1);
640276428Sneel			}
641276428Sneel		} else {
642276428Sneel			curtime = vrtc_curtime(vrtc);
643276428Sneel			KASSERT(curtime == vrtc->base_rtctime, ("%s: mismatch "
644276428Sneel			    "between vrtc basetime (%#lx) and curtime (%#lx)",
645276428Sneel			    __func__, vrtc->base_rtctime, curtime));
646276428Sneel
647276428Sneel			/*
648276428Sneel			 * Force a refresh of the RTC date/time fields so
649276428Sneel			 * they reflect the time right before the guest set
650276428Sneel			 * the HALT bit.
651276428Sneel			 */
652276428Sneel			secs_to_rtc(curtime, vrtc, 1);
653276428Sneel
654276428Sneel			/*
655276428Sneel			 * Updates are halted so mark 'base_rtctime' to denote
656276428Sneel			 * that the RTC date/time is in flux.
657276428Sneel			 */
658276428Sneel			rtctime = VRTC_BROKEN_TIME;
659276428Sneel			rtc->reg_b &= ~RTCSB_UINTR;
660276428Sneel		}
661276428Sneel		error = vrtc_time_update(vrtc, rtctime);
662276428Sneel		KASSERT(error == 0, ("vrtc_time_update error %d", error));
663276428Sneel	}
664276428Sneel
665276428Sneel	/*
666276428Sneel	 * Side effect of changes to the interrupt enable bits.
667276428Sneel	 */
668276428Sneel	if (changed & RTCSB_ALL_INTRS)
669276428Sneel		vrtc_set_reg_c(vrtc, vrtc->rtcdev.reg_c);
670276428Sneel
671276428Sneel	/*
672276428Sneel	 * Change the callout frequency if it has changed.
673276428Sneel	 */
674276428Sneel	newfreq = vrtc_freq(vrtc);
675276428Sneel	if (newfreq != oldfreq)
676276428Sneel		vrtc_callout_reset(vrtc, newfreq);
677276428Sneel	else
678276428Sneel		vrtc_callout_check(vrtc, newfreq);
679276428Sneel
680276428Sneel	/*
681276428Sneel	 * The side effect of bits that control the RTC date/time format
682276428Sneel	 * is handled lazily when those fields are actually read.
683276428Sneel	 */
684276428Sneel	return (0);
685276428Sneel}
686276428Sneel
687276428Sneelstatic void
688276428Sneelvrtc_set_reg_a(struct vrtc *vrtc, uint8_t newval)
689276428Sneel{
690276428Sneel	sbintime_t oldfreq, newfreq;
691276428Sneel	uint8_t oldval, changed;
692276428Sneel
693276428Sneel	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
694276428Sneel
695276428Sneel	newval &= ~RTCSA_TUP;
696276428Sneel	oldval = vrtc->rtcdev.reg_a;
697276428Sneel	oldfreq = vrtc_freq(vrtc);
698276428Sneel
699276428Sneel	if (divider_enabled(oldval) && !divider_enabled(newval)) {
700276428Sneel		VM_CTR2(vrtc->vm, "RTC divider held in reset at %#lx/%#lx",
701276428Sneel		    vrtc->base_rtctime, vrtc->base_uptime);
702276428Sneel	} else if (!divider_enabled(oldval) && divider_enabled(newval)) {
703276428Sneel		/*
704276428Sneel		 * If the dividers are coming out of reset then update
705276428Sneel		 * 'base_uptime' before this happens. This is done to
706276428Sneel		 * maintain the illusion that the RTC date/time was frozen
707276428Sneel		 * while the dividers were disabled.
708276428Sneel		 */
709276428Sneel		vrtc->base_uptime = sbinuptime();
710276428Sneel		VM_CTR2(vrtc->vm, "RTC divider out of reset at %#lx/%#lx",
711276428Sneel		    vrtc->base_rtctime, vrtc->base_uptime);
712276428Sneel	} else {
713276428Sneel		/* NOTHING */
714276428Sneel	}
715276428Sneel
716276428Sneel	vrtc->rtcdev.reg_a = newval;
717276428Sneel	changed = oldval ^ newval;
718276428Sneel	if (changed) {
719276428Sneel		VM_CTR2(vrtc->vm, "RTC reg_a changed from %#x to %#x",
720276428Sneel		    oldval, newval);
721276428Sneel	}
722276428Sneel
723276428Sneel	/*
724276428Sneel	 * Side effect of changes to rate select and divider enable bits.
725276428Sneel	 */
726276428Sneel	newfreq = vrtc_freq(vrtc);
727276428Sneel	if (newfreq != oldfreq)
728276428Sneel		vrtc_callout_reset(vrtc, newfreq);
729276428Sneel	else
730276428Sneel		vrtc_callout_check(vrtc, newfreq);
731276428Sneel}
732276428Sneel
733276428Sneelint
734276428Sneelvrtc_set_time(struct vm *vm, time_t secs)
735276428Sneel{
736276428Sneel	struct vrtc *vrtc;
737276428Sneel	int error;
738276428Sneel
739276428Sneel	vrtc = vm_rtc(vm);
740276428Sneel	VRTC_LOCK(vrtc);
741276428Sneel	error = vrtc_time_update(vrtc, secs);
742276428Sneel	VRTC_UNLOCK(vrtc);
743276428Sneel
744276428Sneel	if (error) {
745276428Sneel		VM_CTR2(vrtc->vm, "Error %d setting RTC time to %#lx", error,
746276428Sneel		    secs);
747276428Sneel	} else {
748276428Sneel		VM_CTR1(vrtc->vm, "RTC time set to %#lx", secs);
749276428Sneel	}
750276428Sneel
751276428Sneel	return (error);
752276428Sneel}
753276428Sneel
754276428Sneeltime_t
755276428Sneelvrtc_get_time(struct vm *vm)
756276428Sneel{
757276428Sneel	struct vrtc *vrtc;
758276428Sneel	time_t t;
759276428Sneel
760276428Sneel	vrtc = vm_rtc(vm);
761276428Sneel	VRTC_LOCK(vrtc);
762276428Sneel	t = vrtc_curtime(vrtc);
763276428Sneel	VRTC_UNLOCK(vrtc);
764276428Sneel
765276428Sneel	return (t);
766276428Sneel}
767276428Sneel
768276428Sneelint
769276428Sneelvrtc_nvram_write(struct vm *vm, int offset, uint8_t value)
770276428Sneel{
771276428Sneel	struct vrtc *vrtc;
772276428Sneel	uint8_t *ptr;
773276428Sneel
774276428Sneel	vrtc = vm_rtc(vm);
775276428Sneel
776276428Sneel	/*
777276428Sneel	 * Don't allow writes to RTC control registers or the date/time fields.
778276428Sneel	 */
779276428Sneel	if (offset < offsetof(struct rtcdev, nvram[0]) ||
780276428Sneel	    offset >= sizeof(struct rtcdev)) {
781276428Sneel		VM_CTR1(vrtc->vm, "RTC nvram write to invalid offset %d",
782276428Sneel		    offset);
783276428Sneel		return (EINVAL);
784276428Sneel	}
785276428Sneel
786276428Sneel	VRTC_LOCK(vrtc);
787276428Sneel	ptr = (uint8_t *)(&vrtc->rtcdev);
788276428Sneel	ptr[offset] = value;
789276428Sneel	VM_CTR2(vrtc->vm, "RTC nvram write %#x to offset %#x", value, offset);
790276428Sneel	VRTC_UNLOCK(vrtc);
791276428Sneel
792276428Sneel	return (0);
793276428Sneel}
794276428Sneel
795276428Sneelint
796276428Sneelvrtc_nvram_read(struct vm *vm, int offset, uint8_t *retval)
797276428Sneel{
798276428Sneel	struct vrtc *vrtc;
799276428Sneel	time_t curtime;
800276428Sneel	uint8_t *ptr;
801276428Sneel
802276428Sneel	/*
803276428Sneel	 * Allow all offsets in the RTC to be read.
804276428Sneel	 */
805276428Sneel	if (offset < 0 || offset >= sizeof(struct rtcdev))
806276428Sneel		return (EINVAL);
807276428Sneel
808276428Sneel	vrtc = vm_rtc(vm);
809276428Sneel	VRTC_LOCK(vrtc);
810276428Sneel
811276428Sneel	/*
812276428Sneel	 * Update RTC date/time fields if necessary.
813276428Sneel	 */
814276428Sneel	if (offset < 10) {
815276428Sneel		curtime = vrtc_curtime(vrtc);
816276428Sneel		secs_to_rtc(curtime, vrtc, 0);
817276428Sneel	}
818276428Sneel
819276428Sneel	ptr = (uint8_t *)(&vrtc->rtcdev);
820276428Sneel	*retval = ptr[offset];
821276428Sneel
822276428Sneel	VRTC_UNLOCK(vrtc);
823276428Sneel	return (0);
824276428Sneel}
825276428Sneel
826276428Sneelint
827276428Sneelvrtc_addr_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes,
828276428Sneel    uint32_t *val)
829276428Sneel{
830276428Sneel	struct vrtc *vrtc;
831276428Sneel
832276428Sneel	vrtc = vm_rtc(vm);
833276428Sneel
834276428Sneel	if (bytes != 1)
835276428Sneel		return (-1);
836276428Sneel
837276428Sneel	if (in) {
838276428Sneel		*val = 0xff;
839276428Sneel		return (0);
840276428Sneel	}
841276428Sneel
842276428Sneel	VRTC_LOCK(vrtc);
843276428Sneel	vrtc->addr = *val & 0x7f;
844276428Sneel	VRTC_UNLOCK(vrtc);
845276428Sneel
846276428Sneel	return (0);
847276428Sneel}
848276428Sneel
849276428Sneelint
850276428Sneelvrtc_data_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes,
851276428Sneel    uint32_t *val)
852276428Sneel{
853276428Sneel	struct vrtc *vrtc;
854276428Sneel	struct rtcdev *rtc;
855276428Sneel	time_t curtime;
856276428Sneel	int error, offset;
857276428Sneel
858276428Sneel	vrtc = vm_rtc(vm);
859276428Sneel	rtc = &vrtc->rtcdev;
860276428Sneel
861276428Sneel	if (bytes != 1)
862276428Sneel		return (-1);
863276428Sneel
864276428Sneel	VRTC_LOCK(vrtc);
865276428Sneel	offset = vrtc->addr;
866276428Sneel	if (offset >= sizeof(struct rtcdev)) {
867276428Sneel		VRTC_UNLOCK(vrtc);
868276428Sneel		return (-1);
869276428Sneel	}
870276428Sneel
871276428Sneel	error = 0;
872276428Sneel	curtime = vrtc_curtime(vrtc);
873276428Sneel	vrtc_time_update(vrtc, curtime);
874276428Sneel
875276428Sneel	if (in) {
876276428Sneel		/*
877276428Sneel		 * Update RTC date/time fields if necessary.
878276428Sneel		 */
879276428Sneel		if (offset < 10)
880276428Sneel			secs_to_rtc(curtime, vrtc, 0);
881276428Sneel
882276428Sneel		if (offset == 12) {
883276428Sneel			/*
884276428Sneel			 * XXX
885276428Sneel			 * reg_c interrupt flags are updated only if the
886276428Sneel			 * corresponding interrupt enable bit in reg_b is set.
887276428Sneel			 */
888276428Sneel			*val = vrtc->rtcdev.reg_c;
889276428Sneel			vrtc_set_reg_c(vrtc, 0);
890276428Sneel		} else {
891276428Sneel			*val = *((uint8_t *)rtc + offset);
892276428Sneel		}
893276428Sneel		VCPU_CTR2(vm, vcpuid, "Read value %#x from RTC offset %#x",
894276428Sneel		    *val, offset);
895276428Sneel	} else {
896276428Sneel		switch (offset) {
897276428Sneel		case 10:
898276428Sneel			VCPU_CTR1(vm, vcpuid, "RTC reg_a set to %#x", *val);
899276428Sneel			vrtc_set_reg_a(vrtc, *val);
900276428Sneel			break;
901276428Sneel		case 11:
902276428Sneel			VCPU_CTR1(vm, vcpuid, "RTC reg_b set to %#x", *val);
903276428Sneel			error = vrtc_set_reg_b(vrtc, *val);
904276428Sneel			break;
905276428Sneel		case 12:
906276428Sneel			VCPU_CTR1(vm, vcpuid, "RTC reg_c set to %#x (ignored)",
907276428Sneel			    *val);
908276428Sneel			break;
909276428Sneel		case 13:
910276428Sneel			VCPU_CTR1(vm, vcpuid, "RTC reg_d set to %#x (ignored)",
911276428Sneel			    *val);
912276428Sneel			break;
913276428Sneel		case 0:
914276428Sneel			/*
915276428Sneel			 * High order bit of 'seconds' is readonly.
916276428Sneel			 */
917276428Sneel			*val &= 0x7f;
918276428Sneel			/* FALLTHRU */
919276428Sneel		default:
920276428Sneel			VCPU_CTR2(vm, vcpuid, "RTC offset %#x set to %#x",
921276428Sneel			    offset, *val);
922276428Sneel			*((uint8_t *)rtc + offset) = *val;
923276428Sneel			break;
924276428Sneel		}
925276428Sneel	}
926276428Sneel	VRTC_UNLOCK(vrtc);
927276428Sneel	return (error);
928276428Sneel}
929276428Sneel
930276428Sneelvoid
931276428Sneelvrtc_reset(struct vrtc *vrtc)
932276428Sneel{
933276428Sneel	struct rtcdev *rtc;
934276428Sneel
935276428Sneel	VRTC_LOCK(vrtc);
936276428Sneel
937276428Sneel	rtc = &vrtc->rtcdev;
938276428Sneel	vrtc_set_reg_b(vrtc, rtc->reg_b & ~(RTCSB_ALL_INTRS | RTCSB_SQWE));
939276428Sneel	vrtc_set_reg_c(vrtc, 0);
940276428Sneel	KASSERT(!callout_active(&vrtc->callout), ("rtc callout still active"));
941276428Sneel
942276428Sneel	VRTC_UNLOCK(vrtc);
943276428Sneel}
944276428Sneel
945276428Sneelstruct vrtc *
946276428Sneelvrtc_init(struct vm *vm)
947276428Sneel{
948276428Sneel	struct vrtc *vrtc;
949276428Sneel	struct rtcdev *rtc;
950276428Sneel	time_t curtime;
951276428Sneel
952276428Sneel	vrtc = malloc(sizeof(struct vrtc), M_VRTC, M_WAITOK | M_ZERO);
953276428Sneel	vrtc->vm = vm;
954276428Sneel	mtx_init(&vrtc->mtx, "vrtc lock", NULL, MTX_DEF);
955276428Sneel	callout_init(&vrtc->callout, 1);
956276428Sneel
957276428Sneel	/* Allow dividers to keep time but disable everything else */
958276428Sneel	rtc = &vrtc->rtcdev;
959276428Sneel	rtc->reg_a = 0x20;
960276428Sneel	rtc->reg_b = RTCSB_24HR;
961276428Sneel	rtc->reg_c = 0;
962276428Sneel	rtc->reg_d = RTCSD_PWR;
963276428Sneel
964276428Sneel	/* Reset the index register to a safe value. */
965276428Sneel	vrtc->addr = RTC_STATUSD;
966276428Sneel
967276428Sneel	/*
968276428Sneel	 * Initialize RTC time to 00:00:00 Jan 1, 1970.
969276428Sneel	 */
970276428Sneel	curtime = 0;
971276428Sneel
972276428Sneel	VRTC_LOCK(vrtc);
973276428Sneel	vrtc->base_rtctime = VRTC_BROKEN_TIME;
974276428Sneel	vrtc_time_update(vrtc, curtime);
975276428Sneel	secs_to_rtc(curtime, vrtc, 0);
976276428Sneel	VRTC_UNLOCK(vrtc);
977276428Sneel
978276428Sneel	return (vrtc);
979276428Sneel}
980276428Sneel
981276428Sneelvoid
982276428Sneelvrtc_cleanup(struct vrtc *vrtc)
983276428Sneel{
984276428Sneel
985276428Sneel	callout_drain(&vrtc->callout);
986276428Sneel	free(vrtc, M_VRTC);
987276428Sneel}
988