xref: /freebsd-src/sys/amd64/vmm/io/vrtc.c (revision f3754afd5901857787271e73f9c34d3b9069a03f)
10dafa5cdSNeel Natu /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3ebc3c37cSMarcelo Araujo  *
40dafa5cdSNeel Natu  * Copyright (c) 2014, Neel Natu (neel@freebsd.org)
50dafa5cdSNeel Natu  * All rights reserved.
60dafa5cdSNeel Natu  *
70dafa5cdSNeel Natu  * Redistribution and use in source and binary forms, with or without
80dafa5cdSNeel Natu  * modification, are permitted provided that the following conditions
90dafa5cdSNeel Natu  * are met:
100dafa5cdSNeel Natu  * 1. Redistributions of source code must retain the above copyright
110dafa5cdSNeel Natu  *    notice unmodified, this list of conditions, and the following
120dafa5cdSNeel Natu  *    disclaimer.
130dafa5cdSNeel Natu  * 2. Redistributions in binary form must reproduce the above copyright
140dafa5cdSNeel Natu  *    notice, this list of conditions and the following disclaimer in the
150dafa5cdSNeel Natu  *    documentation and/or other materials provided with the distribution.
160dafa5cdSNeel Natu  *
170dafa5cdSNeel Natu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
180dafa5cdSNeel Natu  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
190dafa5cdSNeel Natu  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
200dafa5cdSNeel Natu  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
210dafa5cdSNeel Natu  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
220dafa5cdSNeel Natu  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
230dafa5cdSNeel Natu  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
240dafa5cdSNeel Natu  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
250dafa5cdSNeel Natu  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
260dafa5cdSNeel Natu  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
270dafa5cdSNeel Natu  */
280dafa5cdSNeel Natu 
290dafa5cdSNeel Natu #include <sys/cdefs.h>
30483d953aSJohn Baldwin #include "opt_bhyve_snapshot.h"
31483d953aSJohn Baldwin 
320dafa5cdSNeel Natu #include <sys/param.h>
330dafa5cdSNeel Natu #include <sys/systm.h>
340dafa5cdSNeel Natu #include <sys/queue.h>
350dafa5cdSNeel Natu #include <sys/kernel.h>
360dafa5cdSNeel Natu #include <sys/malloc.h>
370dafa5cdSNeel Natu #include <sys/lock.h>
380dafa5cdSNeel Natu #include <sys/mutex.h>
390dafa5cdSNeel Natu #include <sys/clock.h>
400dafa5cdSNeel Natu #include <sys/sysctl.h>
410dafa5cdSNeel Natu 
420dafa5cdSNeel Natu #include <machine/vmm.h>
43483d953aSJohn Baldwin #include <machine/vmm_snapshot.h>
440dafa5cdSNeel Natu 
450dafa5cdSNeel Natu #include <isa/rtc.h>
460dafa5cdSNeel Natu 
47*3ccb0233SMark Johnston #include <dev/vmm/vmm_ktr.h>
48*3ccb0233SMark Johnston 
490dafa5cdSNeel Natu #include "vatpic.h"
500dafa5cdSNeel Natu #include "vioapic.h"
510dafa5cdSNeel Natu #include "vrtc.h"
520dafa5cdSNeel Natu 
530dafa5cdSNeel Natu /* Register layout of the RTC */
540dafa5cdSNeel Natu struct rtcdev {
550dafa5cdSNeel Natu 	uint8_t	sec;
560dafa5cdSNeel Natu 	uint8_t	alarm_sec;
570dafa5cdSNeel Natu 	uint8_t	min;
580dafa5cdSNeel Natu 	uint8_t	alarm_min;
590dafa5cdSNeel Natu 	uint8_t	hour;
600dafa5cdSNeel Natu 	uint8_t	alarm_hour;
610dafa5cdSNeel Natu 	uint8_t	day_of_week;
620dafa5cdSNeel Natu 	uint8_t	day_of_month;
630dafa5cdSNeel Natu 	uint8_t	month;
640dafa5cdSNeel Natu 	uint8_t	year;
650dafa5cdSNeel Natu 	uint8_t	reg_a;
660dafa5cdSNeel Natu 	uint8_t	reg_b;
670dafa5cdSNeel Natu 	uint8_t	reg_c;
680dafa5cdSNeel Natu 	uint8_t	reg_d;
69f39630c2SNeel Natu 	uint8_t	nvram[36];
70f39630c2SNeel Natu 	uint8_t	century;
71f39630c2SNeel Natu 	uint8_t	nvram2[128 - 51];
720dafa5cdSNeel Natu } __packed;
730dafa5cdSNeel Natu CTASSERT(sizeof(struct rtcdev) == 128);
74f39630c2SNeel Natu CTASSERT(offsetof(struct rtcdev, century) == RTC_CENTURY);
750dafa5cdSNeel Natu 
760dafa5cdSNeel Natu struct vrtc {
770dafa5cdSNeel Natu 	struct vm	*vm;
780dafa5cdSNeel Natu 	struct mtx	mtx;
790dafa5cdSNeel Natu 	struct callout	callout;
800dafa5cdSNeel Natu 	u_int		addr;		/* RTC register to read or write */
810dafa5cdSNeel Natu 	sbintime_t	base_uptime;
820dafa5cdSNeel Natu 	time_t		base_rtctime;
830dafa5cdSNeel Natu 	struct rtcdev	rtcdev;
840dafa5cdSNeel Natu };
850dafa5cdSNeel Natu 
860dafa5cdSNeel Natu #define	VRTC_LOCK(vrtc)		mtx_lock(&((vrtc)->mtx))
870dafa5cdSNeel Natu #define	VRTC_UNLOCK(vrtc)	mtx_unlock(&((vrtc)->mtx))
880dafa5cdSNeel Natu #define	VRTC_LOCKED(vrtc)	mtx_owned(&((vrtc)->mtx))
890dafa5cdSNeel Natu 
900dafa5cdSNeel Natu /*
910dafa5cdSNeel Natu  * RTC time is considered "broken" if:
920dafa5cdSNeel Natu  * - RTC updates are halted by the guest
930dafa5cdSNeel Natu  * - RTC date/time fields have invalid values
940dafa5cdSNeel Natu  */
950dafa5cdSNeel Natu #define	VRTC_BROKEN_TIME	((time_t)-1)
960dafa5cdSNeel Natu 
970dafa5cdSNeel Natu #define	RTC_IRQ			8
980dafa5cdSNeel Natu #define	RTCSB_BIN		0x04
990dafa5cdSNeel Natu #define	RTCSB_ALL_INTRS		(RTCSB_UINTR | RTCSB_AINTR | RTCSB_PINTR)
1000dafa5cdSNeel Natu #define	rtc_halted(vrtc)	((vrtc->rtcdev.reg_b & RTCSB_HALT) != 0)
1010dafa5cdSNeel Natu #define	aintr_enabled(vrtc)	(((vrtc)->rtcdev.reg_b & RTCSB_AINTR) != 0)
1020dafa5cdSNeel Natu #define	pintr_enabled(vrtc)	(((vrtc)->rtcdev.reg_b & RTCSB_PINTR) != 0)
1030dafa5cdSNeel Natu #define	uintr_enabled(vrtc)	(((vrtc)->rtcdev.reg_b & RTCSB_UINTR) != 0)
1040dafa5cdSNeel Natu 
1050dafa5cdSNeel Natu static void vrtc_callout_handler(void *arg);
1060dafa5cdSNeel Natu static void vrtc_set_reg_c(struct vrtc *vrtc, uint8_t newval);
1070dafa5cdSNeel Natu 
1080dafa5cdSNeel Natu static MALLOC_DEFINE(M_VRTC, "vrtc", "bhyve virtual rtc");
1090dafa5cdSNeel Natu 
1100dafa5cdSNeel Natu SYSCTL_DECL(_hw_vmm);
111b40598c5SPawel Biernacki SYSCTL_NODE(_hw_vmm, OID_AUTO, vrtc, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL,
112b40598c5SPawel Biernacki     NULL);
1130dafa5cdSNeel Natu 
1140dafa5cdSNeel Natu static int rtc_flag_broken_time = 1;
1150dafa5cdSNeel Natu SYSCTL_INT(_hw_vmm_vrtc, OID_AUTO, flag_broken_time, CTLFLAG_RDTUN,
1160dafa5cdSNeel Natu     &rtc_flag_broken_time, 0, "Stop guest when invalid RTC time is detected");
1170dafa5cdSNeel Natu 
1180dafa5cdSNeel Natu static __inline bool
1190dafa5cdSNeel Natu divider_enabled(int reg_a)
1200dafa5cdSNeel Natu {
1210dafa5cdSNeel Natu 	/*
1220dafa5cdSNeel Natu 	 * The RTC is counting only when dividers are not held in reset.
1230dafa5cdSNeel Natu 	 */
1240dafa5cdSNeel Natu 	return ((reg_a & 0x70) == 0x20);
1250dafa5cdSNeel Natu }
1260dafa5cdSNeel Natu 
1270dafa5cdSNeel Natu static __inline bool
1280dafa5cdSNeel Natu update_enabled(struct vrtc *vrtc)
1290dafa5cdSNeel Natu {
1300dafa5cdSNeel Natu 	/*
1310dafa5cdSNeel Natu 	 * RTC date/time can be updated only if:
1320dafa5cdSNeel Natu 	 * - divider is not held in reset
1330dafa5cdSNeel Natu 	 * - guest has not disabled updates
1340dafa5cdSNeel Natu 	 * - the date/time fields have valid contents
1350dafa5cdSNeel Natu 	 */
1360dafa5cdSNeel Natu 	if (!divider_enabled(vrtc->rtcdev.reg_a))
1370dafa5cdSNeel Natu 		return (false);
1380dafa5cdSNeel Natu 
1390dafa5cdSNeel Natu 	if (rtc_halted(vrtc))
1400dafa5cdSNeel Natu 		return (false);
1410dafa5cdSNeel Natu 
1420dafa5cdSNeel Natu 	if (vrtc->base_rtctime == VRTC_BROKEN_TIME)
1430dafa5cdSNeel Natu 		return (false);
1440dafa5cdSNeel Natu 
1450dafa5cdSNeel Natu 	return (true);
1460dafa5cdSNeel Natu }
1470dafa5cdSNeel Natu 
1480dafa5cdSNeel Natu static time_t
149787fb3d0SNeel Natu vrtc_curtime(struct vrtc *vrtc, sbintime_t *basetime)
1500dafa5cdSNeel Natu {
1510dafa5cdSNeel Natu 	sbintime_t now, delta;
152787fb3d0SNeel Natu 	time_t t, secs;
1530dafa5cdSNeel Natu 
1540dafa5cdSNeel Natu 	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
1550dafa5cdSNeel Natu 
1560dafa5cdSNeel Natu 	t = vrtc->base_rtctime;
157787fb3d0SNeel Natu 	*basetime = vrtc->base_uptime;
1580dafa5cdSNeel Natu 	if (update_enabled(vrtc)) {
1590dafa5cdSNeel Natu 		now = sbinuptime();
1600dafa5cdSNeel Natu 		delta = now - vrtc->base_uptime;
1610dafa5cdSNeel Natu 		KASSERT(delta >= 0, ("vrtc_curtime: uptime went backwards: "
1620dafa5cdSNeel Natu 		    "%#lx to %#lx", vrtc->base_uptime, now));
163787fb3d0SNeel Natu 		secs = delta / SBT_1S;
164787fb3d0SNeel Natu 		t += secs;
165787fb3d0SNeel Natu 		*basetime += secs * SBT_1S;
1660dafa5cdSNeel Natu 	}
1670dafa5cdSNeel Natu 	return (t);
1680dafa5cdSNeel Natu }
1690dafa5cdSNeel Natu 
1700dafa5cdSNeel Natu static __inline uint8_t
1710dafa5cdSNeel Natu rtcset(struct rtcdev *rtc, int val)
1720dafa5cdSNeel Natu {
1730dafa5cdSNeel Natu 
1740dafa5cdSNeel Natu 	KASSERT(val >= 0 && val < 100, ("%s: invalid bin2bcd index %d",
1750dafa5cdSNeel Natu 	    __func__, val));
1760dafa5cdSNeel Natu 
1770dafa5cdSNeel Natu 	return ((rtc->reg_b & RTCSB_BIN) ? val : bin2bcd_data[val]);
1780dafa5cdSNeel Natu }
1790dafa5cdSNeel Natu 
1800dafa5cdSNeel Natu static void
1810dafa5cdSNeel Natu secs_to_rtc(time_t rtctime, struct vrtc *vrtc, int force_update)
1820dafa5cdSNeel Natu {
1830dafa5cdSNeel Natu 	struct clocktime ct;
1840dafa5cdSNeel Natu 	struct timespec ts;
1850dafa5cdSNeel Natu 	struct rtcdev *rtc;
1860dafa5cdSNeel Natu 	int hour;
1870dafa5cdSNeel Natu 
1880dafa5cdSNeel Natu 	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
1890dafa5cdSNeel Natu 
1900dafa5cdSNeel Natu 	if (rtctime < 0) {
1910dafa5cdSNeel Natu 		KASSERT(rtctime == VRTC_BROKEN_TIME,
1920dafa5cdSNeel Natu 		    ("%s: invalid vrtc time %#lx", __func__, rtctime));
1930dafa5cdSNeel Natu 		return;
1940dafa5cdSNeel Natu 	}
1950dafa5cdSNeel Natu 
1960dafa5cdSNeel Natu 	/*
1970dafa5cdSNeel Natu 	 * If the RTC is halted then the guest has "ownership" of the
1980dafa5cdSNeel Natu 	 * date/time fields. Don't update the RTC date/time fields in
1990dafa5cdSNeel Natu 	 * this case (unless forced).
2000dafa5cdSNeel Natu 	 */
2010dafa5cdSNeel Natu 	if (rtc_halted(vrtc) && !force_update)
2020dafa5cdSNeel Natu 		return;
2030dafa5cdSNeel Natu 
2040dafa5cdSNeel Natu 	ts.tv_sec = rtctime;
2050dafa5cdSNeel Natu 	ts.tv_nsec = 0;
2060dafa5cdSNeel Natu 	clock_ts_to_ct(&ts, &ct);
2070dafa5cdSNeel Natu 
2080dafa5cdSNeel Natu 	KASSERT(ct.sec >= 0 && ct.sec <= 59, ("invalid clocktime sec %d",
2090dafa5cdSNeel Natu 	    ct.sec));
2100dafa5cdSNeel Natu 	KASSERT(ct.min >= 0 && ct.min <= 59, ("invalid clocktime min %d",
2110dafa5cdSNeel Natu 	    ct.min));
2120dafa5cdSNeel Natu 	KASSERT(ct.hour >= 0 && ct.hour <= 23, ("invalid clocktime hour %d",
2130dafa5cdSNeel Natu 	    ct.hour));
2140dafa5cdSNeel Natu 	KASSERT(ct.dow >= 0 && ct.dow <= 6, ("invalid clocktime wday %d",
2150dafa5cdSNeel Natu 	    ct.dow));
2160dafa5cdSNeel Natu 	KASSERT(ct.day >= 1 && ct.day <= 31, ("invalid clocktime mday %d",
2170dafa5cdSNeel Natu 	    ct.day));
2180dafa5cdSNeel Natu 	KASSERT(ct.mon >= 1 && ct.mon <= 12, ("invalid clocktime month %d",
2190dafa5cdSNeel Natu 	    ct.mon));
2200dafa5cdSNeel Natu 	KASSERT(ct.year >= POSIX_BASE_YEAR, ("invalid clocktime year %d",
2210dafa5cdSNeel Natu 	    ct.year));
2220dafa5cdSNeel Natu 
2230dafa5cdSNeel Natu 	rtc = &vrtc->rtcdev;
2240dafa5cdSNeel Natu 	rtc->sec = rtcset(rtc, ct.sec);
2250dafa5cdSNeel Natu 	rtc->min = rtcset(rtc, ct.min);
2260dafa5cdSNeel Natu 
227f213ae0bSNeel Natu 	if (rtc->reg_b & RTCSB_24HR) {
2280dafa5cdSNeel Natu 		hour = ct.hour;
229f213ae0bSNeel Natu 	} else {
230f213ae0bSNeel Natu 		/*
231f213ae0bSNeel Natu 		 * Convert to the 12-hour format.
232f213ae0bSNeel Natu 		 */
233f213ae0bSNeel Natu 		switch (ct.hour) {
234f213ae0bSNeel Natu 		case 0:			/* 12 AM */
235f213ae0bSNeel Natu 		case 12:		/* 12 PM */
236f213ae0bSNeel Natu 			hour = 12;
237f213ae0bSNeel Natu 			break;
238f213ae0bSNeel Natu 		default:
239f213ae0bSNeel Natu 			/*
240f213ae0bSNeel Natu 			 * The remaining 'ct.hour' values are interpreted as:
241f213ae0bSNeel Natu 			 * [1  - 11] ->  1 - 11 AM
242f213ae0bSNeel Natu 			 * [13 - 23] ->  1 - 11 PM
243f213ae0bSNeel Natu 			 */
244f213ae0bSNeel Natu 			hour = ct.hour % 12;
245f213ae0bSNeel Natu 			break;
246f213ae0bSNeel Natu 		}
247f213ae0bSNeel Natu 	}
2480dafa5cdSNeel Natu 
2490dafa5cdSNeel Natu 	rtc->hour = rtcset(rtc, hour);
2500dafa5cdSNeel Natu 
2510dafa5cdSNeel Natu 	if ((rtc->reg_b & RTCSB_24HR) == 0 && ct.hour >= 12)
2520dafa5cdSNeel Natu 		rtc->hour |= 0x80;	    /* set MSB to indicate PM */
2530dafa5cdSNeel Natu 
2540dafa5cdSNeel Natu 	rtc->day_of_week = rtcset(rtc, ct.dow + 1);
2550dafa5cdSNeel Natu 	rtc->day_of_month = rtcset(rtc, ct.day);
2560dafa5cdSNeel Natu 	rtc->month = rtcset(rtc, ct.mon);
2570dafa5cdSNeel Natu 	rtc->year = rtcset(rtc, ct.year % 100);
258f39630c2SNeel Natu 	rtc->century = rtcset(rtc, ct.year / 100);
2590dafa5cdSNeel Natu }
2600dafa5cdSNeel Natu 
2610dafa5cdSNeel Natu static int
2620dafa5cdSNeel Natu rtcget(struct rtcdev *rtc, int val, int *retval)
2630dafa5cdSNeel Natu {
2640dafa5cdSNeel Natu 	uint8_t upper, lower;
2650dafa5cdSNeel Natu 
2660dafa5cdSNeel Natu 	if (rtc->reg_b & RTCSB_BIN) {
2670dafa5cdSNeel Natu 		*retval = val;
2680dafa5cdSNeel Natu 		return (0);
2690dafa5cdSNeel Natu 	}
2700dafa5cdSNeel Natu 
2710dafa5cdSNeel Natu 	lower = val & 0xf;
2720dafa5cdSNeel Natu 	upper = (val >> 4) & 0xf;
2730dafa5cdSNeel Natu 
2740dafa5cdSNeel Natu 	if (lower > 9 || upper > 9)
2750dafa5cdSNeel Natu 		return (-1);
2760dafa5cdSNeel Natu 
2770dafa5cdSNeel Natu 	*retval = upper * 10 + lower;
2780dafa5cdSNeel Natu 	return (0);
2790dafa5cdSNeel Natu }
2800dafa5cdSNeel Natu 
2810dafa5cdSNeel Natu static time_t
2820dafa5cdSNeel Natu rtc_to_secs(struct vrtc *vrtc)
2830dafa5cdSNeel Natu {
2840dafa5cdSNeel Natu 	struct clocktime ct;
2850dafa5cdSNeel Natu 	struct timespec ts;
2860dafa5cdSNeel Natu 	struct rtcdev *rtc;
287572edd3dSJohn Baldwin #ifdef KTR
2886a33ecdcSJohn Baldwin 	struct vm *vm = vrtc->vm;
289572edd3dSJohn Baldwin #endif
290f39630c2SNeel Natu 	int century, error, hour, pm, year;
2910dafa5cdSNeel Natu 
2920dafa5cdSNeel Natu 	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
2930dafa5cdSNeel Natu 
2940dafa5cdSNeel Natu 	rtc = &vrtc->rtcdev;
2950dafa5cdSNeel Natu 
2960dafa5cdSNeel Natu 	bzero(&ct, sizeof(struct clocktime));
2970dafa5cdSNeel Natu 
2980dafa5cdSNeel Natu 	error = rtcget(rtc, rtc->sec, &ct.sec);
2990dafa5cdSNeel Natu 	if (error || ct.sec < 0 || ct.sec > 59) {
3000dafa5cdSNeel Natu 		VM_CTR2(vm, "Invalid RTC sec %#x/%d", rtc->sec, ct.sec);
3010dafa5cdSNeel Natu 		goto fail;
3020dafa5cdSNeel Natu 	}
3030dafa5cdSNeel Natu 
3040dafa5cdSNeel Natu 	error = rtcget(rtc, rtc->min, &ct.min);
3050dafa5cdSNeel Natu 	if (error || ct.min < 0 || ct.min > 59) {
3060dafa5cdSNeel Natu 		VM_CTR2(vm, "Invalid RTC min %#x/%d", rtc->min, ct.min);
3070dafa5cdSNeel Natu 		goto fail;
3080dafa5cdSNeel Natu 	}
3090dafa5cdSNeel Natu 
3100dafa5cdSNeel Natu 	pm = 0;
3110dafa5cdSNeel Natu 	hour = rtc->hour;
3120dafa5cdSNeel Natu 	if ((rtc->reg_b & RTCSB_24HR) == 0) {
3130dafa5cdSNeel Natu 		if (hour & 0x80) {
3140dafa5cdSNeel Natu 			hour &= ~0x80;
3150dafa5cdSNeel Natu 			pm = 1;
3160dafa5cdSNeel Natu 		}
3170dafa5cdSNeel Natu 	}
3180dafa5cdSNeel Natu 	error = rtcget(rtc, hour, &ct.hour);
3190dafa5cdSNeel Natu 	if ((rtc->reg_b & RTCSB_24HR) == 0) {
320f213ae0bSNeel Natu 		if (ct.hour >= 1 && ct.hour <= 12) {
321f213ae0bSNeel Natu 			/*
322f213ae0bSNeel Natu 			 * Convert from 12-hour format to internal 24-hour
323f213ae0bSNeel Natu 			 * representation as follows:
324f213ae0bSNeel Natu 			 *
325f213ae0bSNeel Natu 			 *    12-hour format		ct.hour
326f213ae0bSNeel Natu 			 *	12	AM		0
327f213ae0bSNeel Natu 			 *	1 - 11	AM		1 - 11
328f213ae0bSNeel Natu 			 *	12	PM		12
329f213ae0bSNeel Natu 			 *	1 - 11	PM		13 - 23
330f213ae0bSNeel Natu 			 */
331f213ae0bSNeel Natu 			if (ct.hour == 12)
332f213ae0bSNeel Natu 				ct.hour = 0;
3330dafa5cdSNeel Natu 			if (pm)
3340dafa5cdSNeel Natu 				ct.hour += 12;
335f213ae0bSNeel Natu 		} else {
336f213ae0bSNeel Natu 			VM_CTR2(vm, "Invalid RTC 12-hour format %#x/%d",
337f213ae0bSNeel Natu 			    rtc->hour, ct.hour);
338f213ae0bSNeel Natu 			goto fail;
339f213ae0bSNeel Natu 		}
3400dafa5cdSNeel Natu 	}
3410dafa5cdSNeel Natu 
3420dafa5cdSNeel Natu 	if (error || ct.hour < 0 || ct.hour > 23) {
3430dafa5cdSNeel Natu 		VM_CTR2(vm, "Invalid RTC hour %#x/%d", rtc->hour, ct.hour);
3440dafa5cdSNeel Natu 		goto fail;
3450dafa5cdSNeel Natu 	}
3460dafa5cdSNeel Natu 
3470dafa5cdSNeel Natu 	/*
3480dafa5cdSNeel Natu 	 * Ignore 'rtc->dow' because some guests like Linux don't bother
3490dafa5cdSNeel Natu 	 * setting it at all while others like OpenBSD/i386 set it incorrectly.
3500dafa5cdSNeel Natu 	 *
3510dafa5cdSNeel Natu 	 * clock_ct_to_ts() does not depend on 'ct.dow' anyways so ignore it.
3520dafa5cdSNeel Natu 	 */
3530dafa5cdSNeel Natu 	ct.dow = -1;
3540dafa5cdSNeel Natu 
3550dafa5cdSNeel Natu 	error = rtcget(rtc, rtc->day_of_month, &ct.day);
3560dafa5cdSNeel Natu 	if (error || ct.day < 1 || ct.day > 31) {
3570dafa5cdSNeel Natu 		VM_CTR2(vm, "Invalid RTC mday %#x/%d", rtc->day_of_month,
3580dafa5cdSNeel Natu 		    ct.day);
3590dafa5cdSNeel Natu 		goto fail;
3600dafa5cdSNeel Natu 	}
3610dafa5cdSNeel Natu 
3620dafa5cdSNeel Natu 	error = rtcget(rtc, rtc->month, &ct.mon);
3630dafa5cdSNeel Natu 	if (error || ct.mon < 1 || ct.mon > 12) {
3640dafa5cdSNeel Natu 		VM_CTR2(vm, "Invalid RTC month %#x/%d", rtc->month, ct.mon);
3650dafa5cdSNeel Natu 		goto fail;
3660dafa5cdSNeel Natu 	}
3670dafa5cdSNeel Natu 
3680dafa5cdSNeel Natu 	error = rtcget(rtc, rtc->year, &year);
3690dafa5cdSNeel Natu 	if (error || year < 0 || year > 99) {
3700dafa5cdSNeel Natu 		VM_CTR2(vm, "Invalid RTC year %#x/%d", rtc->year, year);
3710dafa5cdSNeel Natu 		goto fail;
3720dafa5cdSNeel Natu 	}
373f39630c2SNeel Natu 
374f39630c2SNeel Natu 	error = rtcget(rtc, rtc->century, &century);
375f39630c2SNeel Natu 	ct.year = century * 100 + year;
376f39630c2SNeel Natu 	if (error || ct.year < POSIX_BASE_YEAR) {
377f39630c2SNeel Natu 		VM_CTR2(vm, "Invalid RTC century %#x/%d", rtc->century,
378f39630c2SNeel Natu 		    ct.year);
379f39630c2SNeel Natu 		goto fail;
380f39630c2SNeel Natu 	}
3810dafa5cdSNeel Natu 
3820dafa5cdSNeel Natu 	error = clock_ct_to_ts(&ct, &ts);
3830dafa5cdSNeel Natu 	if (error || ts.tv_sec < 0) {
3840dafa5cdSNeel Natu 		VM_CTR3(vm, "Invalid RTC clocktime.date %04d-%02d-%02d",
3850dafa5cdSNeel Natu 		    ct.year, ct.mon, ct.day);
3860dafa5cdSNeel Natu 		VM_CTR3(vm, "Invalid RTC clocktime.time %02d:%02d:%02d",
3870dafa5cdSNeel Natu 		    ct.hour, ct.min, ct.sec);
3880dafa5cdSNeel Natu 		goto fail;
3890dafa5cdSNeel Natu 	}
3900dafa5cdSNeel Natu 	return (ts.tv_sec);		/* success */
3910dafa5cdSNeel Natu fail:
392f39630c2SNeel Natu 	/*
393f39630c2SNeel Natu 	 * Stop updating the RTC if the date/time fields programmed by
394f39630c2SNeel Natu 	 * the guest are invalid.
395f39630c2SNeel Natu 	 */
396f39630c2SNeel Natu 	VM_CTR0(vrtc->vm, "Invalid RTC date/time programming detected");
397f39630c2SNeel Natu 	return (VRTC_BROKEN_TIME);
3980dafa5cdSNeel Natu }
3990dafa5cdSNeel Natu 
4000dafa5cdSNeel Natu static int
401787fb3d0SNeel Natu vrtc_time_update(struct vrtc *vrtc, time_t newtime, sbintime_t newbase)
4020dafa5cdSNeel Natu {
4030dafa5cdSNeel Natu 	struct rtcdev *rtc;
4040dafa5cdSNeel Natu 	time_t oldtime;
4050dafa5cdSNeel Natu 	uint8_t alarm_sec, alarm_min, alarm_hour;
4060dafa5cdSNeel Natu 
4070dafa5cdSNeel Natu 	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
4080dafa5cdSNeel Natu 
4090dafa5cdSNeel Natu 	rtc = &vrtc->rtcdev;
4100dafa5cdSNeel Natu 	alarm_sec = rtc->alarm_sec;
4110dafa5cdSNeel Natu 	alarm_min = rtc->alarm_min;
4120dafa5cdSNeel Natu 	alarm_hour = rtc->alarm_hour;
4130dafa5cdSNeel Natu 
4140dafa5cdSNeel Natu 	oldtime = vrtc->base_rtctime;
415787fb3d0SNeel Natu 	VM_CTR2(vrtc->vm, "Updating RTC secs from %#lx to %#lx",
4160dafa5cdSNeel Natu 	    oldtime, newtime);
4170dafa5cdSNeel Natu 
418787fb3d0SNeel Natu 	VM_CTR2(vrtc->vm, "Updating RTC base uptime from %#lx to %#lx",
419572edd3dSJohn Baldwin 	    vrtc->base_uptime, newbase);
420787fb3d0SNeel Natu 	vrtc->base_uptime = newbase;
421787fb3d0SNeel Natu 
4220dafa5cdSNeel Natu 	if (newtime == oldtime)
4230dafa5cdSNeel Natu 		return (0);
4240dafa5cdSNeel Natu 
4250dafa5cdSNeel Natu 	/*
4260dafa5cdSNeel Natu 	 * If 'newtime' indicates that RTC updates are disabled then just
4270dafa5cdSNeel Natu 	 * record that and return. There is no need to do alarm interrupt
428787fb3d0SNeel Natu 	 * processing in this case.
4290dafa5cdSNeel Natu 	 */
4300dafa5cdSNeel Natu 	if (newtime == VRTC_BROKEN_TIME) {
4310dafa5cdSNeel Natu 		vrtc->base_rtctime = VRTC_BROKEN_TIME;
4320dafa5cdSNeel Natu 		return (0);
4330dafa5cdSNeel Natu 	}
4340dafa5cdSNeel Natu 
4350dafa5cdSNeel Natu 	/*
4360dafa5cdSNeel Natu 	 * Return an error if RTC updates are halted by the guest.
4370dafa5cdSNeel Natu 	 */
4380dafa5cdSNeel Natu 	if (rtc_halted(vrtc)) {
4390dafa5cdSNeel Natu 		VM_CTR0(vrtc->vm, "RTC update halted by guest");
4400dafa5cdSNeel Natu 		return (EBUSY);
4410dafa5cdSNeel Natu 	}
4420dafa5cdSNeel Natu 
4430dafa5cdSNeel Natu 	do {
4440dafa5cdSNeel Natu 		/*
4450dafa5cdSNeel Natu 		 * If the alarm interrupt is enabled and 'oldtime' is valid
4460dafa5cdSNeel Natu 		 * then visit all the seconds between 'oldtime' and 'newtime'
4470dafa5cdSNeel Natu 		 * to check for the alarm condition.
4480dafa5cdSNeel Natu 		 *
4490dafa5cdSNeel Natu 		 * Otherwise move the RTC time forward directly to 'newtime'.
4500dafa5cdSNeel Natu 		 */
4510dafa5cdSNeel Natu 		if (aintr_enabled(vrtc) && oldtime != VRTC_BROKEN_TIME)
4520dafa5cdSNeel Natu 			vrtc->base_rtctime++;
4530dafa5cdSNeel Natu 		else
4540dafa5cdSNeel Natu 			vrtc->base_rtctime = newtime;
4550dafa5cdSNeel Natu 
4560dafa5cdSNeel Natu 		if (aintr_enabled(vrtc)) {
4570dafa5cdSNeel Natu 			/*
4580dafa5cdSNeel Natu 			 * Update the RTC date/time fields before checking
4590dafa5cdSNeel Natu 			 * if the alarm conditions are satisfied.
4600dafa5cdSNeel Natu 			 */
4610dafa5cdSNeel Natu 			secs_to_rtc(vrtc->base_rtctime, vrtc, 0);
4620dafa5cdSNeel Natu 
4630dafa5cdSNeel Natu 			if ((alarm_sec >= 0xC0 || alarm_sec == rtc->sec) &&
4640dafa5cdSNeel Natu 			    (alarm_min >= 0xC0 || alarm_min == rtc->min) &&
4650dafa5cdSNeel Natu 			    (alarm_hour >= 0xC0 || alarm_hour == rtc->hour)) {
4660dafa5cdSNeel Natu 				vrtc_set_reg_c(vrtc, rtc->reg_c | RTCIR_ALARM);
4670dafa5cdSNeel Natu 			}
4680dafa5cdSNeel Natu 		}
4690dafa5cdSNeel Natu 	} while (vrtc->base_rtctime != newtime);
4700dafa5cdSNeel Natu 
4710dafa5cdSNeel Natu 	if (uintr_enabled(vrtc))
4720dafa5cdSNeel Natu 		vrtc_set_reg_c(vrtc, rtc->reg_c | RTCIR_UPDATE);
4730dafa5cdSNeel Natu 
4740dafa5cdSNeel Natu 	return (0);
4750dafa5cdSNeel Natu }
4760dafa5cdSNeel Natu 
4770dafa5cdSNeel Natu static sbintime_t
4780dafa5cdSNeel Natu vrtc_freq(struct vrtc *vrtc)
4790dafa5cdSNeel Natu {
4800dafa5cdSNeel Natu 	int ratesel;
4810dafa5cdSNeel Natu 
4820dafa5cdSNeel Natu 	static sbintime_t pf[16] = {
4830dafa5cdSNeel Natu 		0,
4840dafa5cdSNeel Natu 		SBT_1S / 256,
4850dafa5cdSNeel Natu 		SBT_1S / 128,
4860dafa5cdSNeel Natu 		SBT_1S / 8192,
4870dafa5cdSNeel Natu 		SBT_1S / 4096,
4880dafa5cdSNeel Natu 		SBT_1S / 2048,
4890dafa5cdSNeel Natu 		SBT_1S / 1024,
4900dafa5cdSNeel Natu 		SBT_1S / 512,
4910dafa5cdSNeel Natu 		SBT_1S / 256,
4920dafa5cdSNeel Natu 		SBT_1S / 128,
4930dafa5cdSNeel Natu 		SBT_1S / 64,
4940dafa5cdSNeel Natu 		SBT_1S / 32,
4950dafa5cdSNeel Natu 		SBT_1S / 16,
4960dafa5cdSNeel Natu 		SBT_1S / 8,
4970dafa5cdSNeel Natu 		SBT_1S / 4,
4980dafa5cdSNeel Natu 		SBT_1S / 2,
4990dafa5cdSNeel Natu 	};
5000dafa5cdSNeel Natu 
5010dafa5cdSNeel Natu 	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
5020dafa5cdSNeel Natu 
5030dafa5cdSNeel Natu 	/*
5040dafa5cdSNeel Natu 	 * If both periodic and alarm interrupts are enabled then use the
5050dafa5cdSNeel Natu 	 * periodic frequency to drive the callout. The minimum periodic
5060dafa5cdSNeel Natu 	 * frequency (2 Hz) is higher than the alarm frequency (1 Hz) so
5070dafa5cdSNeel Natu 	 * piggyback the alarm on top of it. The same argument applies to
5080dafa5cdSNeel Natu 	 * the update interrupt.
5090dafa5cdSNeel Natu 	 */
5100dafa5cdSNeel Natu 	if (pintr_enabled(vrtc) && divider_enabled(vrtc->rtcdev.reg_a)) {
5110dafa5cdSNeel Natu 		ratesel = vrtc->rtcdev.reg_a & 0xf;
5120dafa5cdSNeel Natu 		return (pf[ratesel]);
5130dafa5cdSNeel Natu 	} else if (aintr_enabled(vrtc) && update_enabled(vrtc)) {
5140dafa5cdSNeel Natu 		return (SBT_1S);
5150dafa5cdSNeel Natu 	} else if (uintr_enabled(vrtc) && update_enabled(vrtc)) {
5160dafa5cdSNeel Natu 		return (SBT_1S);
5170dafa5cdSNeel Natu 	} else {
5180dafa5cdSNeel Natu 		return (0);
5190dafa5cdSNeel Natu 	}
5200dafa5cdSNeel Natu }
5210dafa5cdSNeel Natu 
5220dafa5cdSNeel Natu static void
5230dafa5cdSNeel Natu vrtc_callout_reset(struct vrtc *vrtc, sbintime_t freqsbt)
5240dafa5cdSNeel Natu {
5250dafa5cdSNeel Natu 
5260dafa5cdSNeel Natu 	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
5270dafa5cdSNeel Natu 
5280dafa5cdSNeel Natu 	if (freqsbt == 0) {
5290dafa5cdSNeel Natu 		if (callout_active(&vrtc->callout)) {
5300dafa5cdSNeel Natu 			VM_CTR0(vrtc->vm, "RTC callout stopped");
5310dafa5cdSNeel Natu 			callout_stop(&vrtc->callout);
5320dafa5cdSNeel Natu 		}
5330dafa5cdSNeel Natu 		return;
5340dafa5cdSNeel Natu 	}
5350dafa5cdSNeel Natu 	VM_CTR1(vrtc->vm, "RTC callout frequency %d hz", SBT_1S / freqsbt);
5360dafa5cdSNeel Natu 	callout_reset_sbt(&vrtc->callout, freqsbt, 0, vrtc_callout_handler,
5370dafa5cdSNeel Natu 	    vrtc, 0);
5380dafa5cdSNeel Natu }
5390dafa5cdSNeel Natu 
5400dafa5cdSNeel Natu static void
5410dafa5cdSNeel Natu vrtc_callout_handler(void *arg)
5420dafa5cdSNeel Natu {
5430dafa5cdSNeel Natu 	struct vrtc *vrtc = arg;
544787fb3d0SNeel Natu 	sbintime_t freqsbt, basetime;
5450dafa5cdSNeel Natu 	time_t rtctime;
5462062ce99SRobert Wing 	int error __diagused;
5470dafa5cdSNeel Natu 
5480dafa5cdSNeel Natu 	VM_CTR0(vrtc->vm, "vrtc callout fired");
5490dafa5cdSNeel Natu 
5500dafa5cdSNeel Natu 	VRTC_LOCK(vrtc);
5510dafa5cdSNeel Natu 	if (callout_pending(&vrtc->callout))	/* callout was reset */
5520dafa5cdSNeel Natu 		goto done;
5530dafa5cdSNeel Natu 
5540dafa5cdSNeel Natu 	if (!callout_active(&vrtc->callout))	/* callout was stopped */
5550dafa5cdSNeel Natu 		goto done;
5560dafa5cdSNeel Natu 
5570dafa5cdSNeel Natu 	callout_deactivate(&vrtc->callout);
5580dafa5cdSNeel Natu 
5590dafa5cdSNeel Natu 	KASSERT((vrtc->rtcdev.reg_b & RTCSB_ALL_INTRS) != 0,
5600dafa5cdSNeel Natu 	    ("gratuitous vrtc callout"));
5610dafa5cdSNeel Natu 
5620dafa5cdSNeel Natu 	if (pintr_enabled(vrtc))
5630dafa5cdSNeel Natu 		vrtc_set_reg_c(vrtc, vrtc->rtcdev.reg_c | RTCIR_PERIOD);
5640dafa5cdSNeel Natu 
5650dafa5cdSNeel Natu 	if (aintr_enabled(vrtc) || uintr_enabled(vrtc)) {
566787fb3d0SNeel Natu 		rtctime = vrtc_curtime(vrtc, &basetime);
567787fb3d0SNeel Natu 		error = vrtc_time_update(vrtc, rtctime, basetime);
5680dafa5cdSNeel Natu 		KASSERT(error == 0, ("%s: vrtc_time_update error %d",
5690dafa5cdSNeel Natu 		    __func__, error));
5700dafa5cdSNeel Natu 	}
5710dafa5cdSNeel Natu 
5720dafa5cdSNeel Natu 	freqsbt = vrtc_freq(vrtc);
5730dafa5cdSNeel Natu 	KASSERT(freqsbt != 0, ("%s: vrtc frequency cannot be zero", __func__));
5740dafa5cdSNeel Natu 	vrtc_callout_reset(vrtc, freqsbt);
5750dafa5cdSNeel Natu done:
5760dafa5cdSNeel Natu 	VRTC_UNLOCK(vrtc);
5770dafa5cdSNeel Natu }
5780dafa5cdSNeel Natu 
5790dafa5cdSNeel Natu static __inline void
5800dafa5cdSNeel Natu vrtc_callout_check(struct vrtc *vrtc, sbintime_t freq)
5810dafa5cdSNeel Natu {
5822062ce99SRobert Wing 	int active __diagused;
5830dafa5cdSNeel Natu 
5840dafa5cdSNeel Natu 	active = callout_active(&vrtc->callout) ? 1 : 0;
5850dafa5cdSNeel Natu 	KASSERT((freq == 0 && !active) || (freq != 0 && active),
5860dafa5cdSNeel Natu 	    ("vrtc callout %s with frequency %#lx",
5870dafa5cdSNeel Natu 	    active ? "active" : "inactive", freq));
5880dafa5cdSNeel Natu }
5890dafa5cdSNeel Natu 
5900dafa5cdSNeel Natu static void
5910dafa5cdSNeel Natu vrtc_set_reg_c(struct vrtc *vrtc, uint8_t newval)
5920dafa5cdSNeel Natu {
5930dafa5cdSNeel Natu 	struct rtcdev *rtc;
5940dafa5cdSNeel Natu 	int oldirqf, newirqf;
5950dafa5cdSNeel Natu 	uint8_t oldval, changed;
5960dafa5cdSNeel Natu 
5970dafa5cdSNeel Natu 	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
5980dafa5cdSNeel Natu 
5990dafa5cdSNeel Natu 	rtc = &vrtc->rtcdev;
6000dafa5cdSNeel Natu 	newval &= RTCIR_ALARM | RTCIR_PERIOD | RTCIR_UPDATE;
6010dafa5cdSNeel Natu 
6020dafa5cdSNeel Natu 	oldirqf = rtc->reg_c & RTCIR_INT;
6030dafa5cdSNeel Natu 	if ((aintr_enabled(vrtc) && (newval & RTCIR_ALARM) != 0) ||
6040dafa5cdSNeel Natu 	    (pintr_enabled(vrtc) && (newval & RTCIR_PERIOD) != 0) ||
6050dafa5cdSNeel Natu 	    (uintr_enabled(vrtc) && (newval & RTCIR_UPDATE) != 0)) {
6060dafa5cdSNeel Natu 		newirqf = RTCIR_INT;
6070dafa5cdSNeel Natu 	} else {
6080dafa5cdSNeel Natu 		newirqf = 0;
6090dafa5cdSNeel Natu 	}
6100dafa5cdSNeel Natu 
6110dafa5cdSNeel Natu 	oldval = rtc->reg_c;
6120dafa5cdSNeel Natu 	rtc->reg_c = newirqf | newval;
6130dafa5cdSNeel Natu 	changed = oldval ^ rtc->reg_c;
6140dafa5cdSNeel Natu 	if (changed) {
6150dafa5cdSNeel Natu 		VM_CTR2(vrtc->vm, "RTC reg_c changed from %#x to %#x",
6160dafa5cdSNeel Natu 		    oldval, rtc->reg_c);
6170dafa5cdSNeel Natu 	}
6180dafa5cdSNeel Natu 
6190dafa5cdSNeel Natu 	if (!oldirqf && newirqf) {
6200dafa5cdSNeel Natu 		VM_CTR1(vrtc->vm, "RTC irq %d asserted", RTC_IRQ);
6210dafa5cdSNeel Natu 		vatpic_pulse_irq(vrtc->vm, RTC_IRQ);
6220dafa5cdSNeel Natu 		vioapic_pulse_irq(vrtc->vm, RTC_IRQ);
6230dafa5cdSNeel Natu 	} else if (oldirqf && !newirqf) {
6240dafa5cdSNeel Natu 		VM_CTR1(vrtc->vm, "RTC irq %d deasserted", RTC_IRQ);
6250dafa5cdSNeel Natu 	}
6260dafa5cdSNeel Natu }
6270dafa5cdSNeel Natu 
6280dafa5cdSNeel Natu static int
6290dafa5cdSNeel Natu vrtc_set_reg_b(struct vrtc *vrtc, uint8_t newval)
6300dafa5cdSNeel Natu {
6310dafa5cdSNeel Natu 	struct rtcdev *rtc;
632787fb3d0SNeel Natu 	sbintime_t oldfreq, newfreq, basetime;
6330dafa5cdSNeel Natu 	time_t curtime, rtctime;
6342062ce99SRobert Wing 	int error __diagused;
6350dafa5cdSNeel Natu 	uint8_t oldval, changed;
6360dafa5cdSNeel Natu 
6370dafa5cdSNeel Natu 	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
6380dafa5cdSNeel Natu 
6390dafa5cdSNeel Natu 	rtc = &vrtc->rtcdev;
6400dafa5cdSNeel Natu 	oldval = rtc->reg_b;
6410dafa5cdSNeel Natu 	oldfreq = vrtc_freq(vrtc);
6420dafa5cdSNeel Natu 
6430dafa5cdSNeel Natu 	rtc->reg_b = newval;
6440dafa5cdSNeel Natu 	changed = oldval ^ newval;
6450dafa5cdSNeel Natu 	if (changed) {
6460dafa5cdSNeel Natu 		VM_CTR2(vrtc->vm, "RTC reg_b changed from %#x to %#x",
6470dafa5cdSNeel Natu 		    oldval, newval);
6480dafa5cdSNeel Natu 	}
6490dafa5cdSNeel Natu 
6500dafa5cdSNeel Natu 	if (changed & RTCSB_HALT) {
6510dafa5cdSNeel Natu 		if ((newval & RTCSB_HALT) == 0) {
6520dafa5cdSNeel Natu 			rtctime = rtc_to_secs(vrtc);
653787fb3d0SNeel Natu 			basetime = sbinuptime();
6540dafa5cdSNeel Natu 			if (rtctime == VRTC_BROKEN_TIME) {
6550dafa5cdSNeel Natu 				if (rtc_flag_broken_time)
6560dafa5cdSNeel Natu 					return (-1);
6570dafa5cdSNeel Natu 			}
6580dafa5cdSNeel Natu 		} else {
659787fb3d0SNeel Natu 			curtime = vrtc_curtime(vrtc, &basetime);
6600dafa5cdSNeel Natu 			KASSERT(curtime == vrtc->base_rtctime, ("%s: mismatch "
6610dafa5cdSNeel Natu 			    "between vrtc basetime (%#lx) and curtime (%#lx)",
6620dafa5cdSNeel Natu 			    __func__, vrtc->base_rtctime, curtime));
6630dafa5cdSNeel Natu 
6640dafa5cdSNeel Natu 			/*
6650dafa5cdSNeel Natu 			 * Force a refresh of the RTC date/time fields so
6660dafa5cdSNeel Natu 			 * they reflect the time right before the guest set
6670dafa5cdSNeel Natu 			 * the HALT bit.
6680dafa5cdSNeel Natu 			 */
6690dafa5cdSNeel Natu 			secs_to_rtc(curtime, vrtc, 1);
6700dafa5cdSNeel Natu 
6710dafa5cdSNeel Natu 			/*
6720dafa5cdSNeel Natu 			 * Updates are halted so mark 'base_rtctime' to denote
6730dafa5cdSNeel Natu 			 * that the RTC date/time is in flux.
6740dafa5cdSNeel Natu 			 */
6750dafa5cdSNeel Natu 			rtctime = VRTC_BROKEN_TIME;
6760dafa5cdSNeel Natu 			rtc->reg_b &= ~RTCSB_UINTR;
6770dafa5cdSNeel Natu 		}
678787fb3d0SNeel Natu 		error = vrtc_time_update(vrtc, rtctime, basetime);
6790dafa5cdSNeel Natu 		KASSERT(error == 0, ("vrtc_time_update error %d", error));
6800dafa5cdSNeel Natu 	}
6810dafa5cdSNeel Natu 
6820dafa5cdSNeel Natu 	/*
6830dafa5cdSNeel Natu 	 * Side effect of changes to the interrupt enable bits.
6840dafa5cdSNeel Natu 	 */
6850dafa5cdSNeel Natu 	if (changed & RTCSB_ALL_INTRS)
6860dafa5cdSNeel Natu 		vrtc_set_reg_c(vrtc, vrtc->rtcdev.reg_c);
6870dafa5cdSNeel Natu 
6880dafa5cdSNeel Natu 	/*
6890dafa5cdSNeel Natu 	 * Change the callout frequency if it has changed.
6900dafa5cdSNeel Natu 	 */
6910dafa5cdSNeel Natu 	newfreq = vrtc_freq(vrtc);
6920dafa5cdSNeel Natu 	if (newfreq != oldfreq)
6930dafa5cdSNeel Natu 		vrtc_callout_reset(vrtc, newfreq);
6940dafa5cdSNeel Natu 	else
6950dafa5cdSNeel Natu 		vrtc_callout_check(vrtc, newfreq);
6960dafa5cdSNeel Natu 
6970dafa5cdSNeel Natu 	/*
6980dafa5cdSNeel Natu 	 * The side effect of bits that control the RTC date/time format
6990dafa5cdSNeel Natu 	 * is handled lazily when those fields are actually read.
7000dafa5cdSNeel Natu 	 */
7010dafa5cdSNeel Natu 	return (0);
7020dafa5cdSNeel Natu }
7030dafa5cdSNeel Natu 
7040dafa5cdSNeel Natu static void
7050dafa5cdSNeel Natu vrtc_set_reg_a(struct vrtc *vrtc, uint8_t newval)
7060dafa5cdSNeel Natu {
7070dafa5cdSNeel Natu 	sbintime_t oldfreq, newfreq;
7080dafa5cdSNeel Natu 	uint8_t oldval, changed;
7090dafa5cdSNeel Natu 
7100dafa5cdSNeel Natu 	KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
7110dafa5cdSNeel Natu 
7120dafa5cdSNeel Natu 	newval &= ~RTCSA_TUP;
7130dafa5cdSNeel Natu 	oldval = vrtc->rtcdev.reg_a;
7140dafa5cdSNeel Natu 	oldfreq = vrtc_freq(vrtc);
7150dafa5cdSNeel Natu 
7160dafa5cdSNeel Natu 	if (divider_enabled(oldval) && !divider_enabled(newval)) {
7170dafa5cdSNeel Natu 		VM_CTR2(vrtc->vm, "RTC divider held in reset at %#lx/%#lx",
7180dafa5cdSNeel Natu 		    vrtc->base_rtctime, vrtc->base_uptime);
7190dafa5cdSNeel Natu 	} else if (!divider_enabled(oldval) && divider_enabled(newval)) {
7200dafa5cdSNeel Natu 		/*
7210dafa5cdSNeel Natu 		 * If the dividers are coming out of reset then update
7220dafa5cdSNeel Natu 		 * 'base_uptime' before this happens. This is done to
7230dafa5cdSNeel Natu 		 * maintain the illusion that the RTC date/time was frozen
7240dafa5cdSNeel Natu 		 * while the dividers were disabled.
7250dafa5cdSNeel Natu 		 */
7260dafa5cdSNeel Natu 		vrtc->base_uptime = sbinuptime();
7270dafa5cdSNeel Natu 		VM_CTR2(vrtc->vm, "RTC divider out of reset at %#lx/%#lx",
7280dafa5cdSNeel Natu 		    vrtc->base_rtctime, vrtc->base_uptime);
7290dafa5cdSNeel Natu 	} else {
7300dafa5cdSNeel Natu 		/* NOTHING */
7310dafa5cdSNeel Natu 	}
7320dafa5cdSNeel Natu 
7330dafa5cdSNeel Natu 	vrtc->rtcdev.reg_a = newval;
7340dafa5cdSNeel Natu 	changed = oldval ^ newval;
7350dafa5cdSNeel Natu 	if (changed) {
7360dafa5cdSNeel Natu 		VM_CTR2(vrtc->vm, "RTC reg_a changed from %#x to %#x",
7370dafa5cdSNeel Natu 		    oldval, newval);
7380dafa5cdSNeel Natu 	}
7390dafa5cdSNeel Natu 
7400dafa5cdSNeel Natu 	/*
7410dafa5cdSNeel Natu 	 * Side effect of changes to rate select and divider enable bits.
7420dafa5cdSNeel Natu 	 */
7430dafa5cdSNeel Natu 	newfreq = vrtc_freq(vrtc);
7440dafa5cdSNeel Natu 	if (newfreq != oldfreq)
7450dafa5cdSNeel Natu 		vrtc_callout_reset(vrtc, newfreq);
7460dafa5cdSNeel Natu 	else
7470dafa5cdSNeel Natu 		vrtc_callout_check(vrtc, newfreq);
7480dafa5cdSNeel Natu }
7490dafa5cdSNeel Natu 
7500dafa5cdSNeel Natu int
7510dafa5cdSNeel Natu vrtc_set_time(struct vm *vm, time_t secs)
7520dafa5cdSNeel Natu {
7530dafa5cdSNeel Natu 	struct vrtc *vrtc;
7540dafa5cdSNeel Natu 	int error;
7550dafa5cdSNeel Natu 
7560dafa5cdSNeel Natu 	vrtc = vm_rtc(vm);
7570dafa5cdSNeel Natu 	VRTC_LOCK(vrtc);
758787fb3d0SNeel Natu 	error = vrtc_time_update(vrtc, secs, sbinuptime());
7590dafa5cdSNeel Natu 	VRTC_UNLOCK(vrtc);
7600dafa5cdSNeel Natu 
7610dafa5cdSNeel Natu 	if (error) {
7620dafa5cdSNeel Natu 		VM_CTR2(vrtc->vm, "Error %d setting RTC time to %#lx", error,
7630dafa5cdSNeel Natu 		    secs);
7640dafa5cdSNeel Natu 	} else {
7650dafa5cdSNeel Natu 		VM_CTR1(vrtc->vm, "RTC time set to %#lx", secs);
7660dafa5cdSNeel Natu 	}
7670dafa5cdSNeel Natu 
7680dafa5cdSNeel Natu 	return (error);
7690dafa5cdSNeel Natu }
7700dafa5cdSNeel Natu 
7710dafa5cdSNeel Natu time_t
7720dafa5cdSNeel Natu vrtc_get_time(struct vm *vm)
7730dafa5cdSNeel Natu {
7740dafa5cdSNeel Natu 	struct vrtc *vrtc;
775787fb3d0SNeel Natu 	sbintime_t basetime;
7760dafa5cdSNeel Natu 	time_t t;
7770dafa5cdSNeel Natu 
7780dafa5cdSNeel Natu 	vrtc = vm_rtc(vm);
7790dafa5cdSNeel Natu 	VRTC_LOCK(vrtc);
780787fb3d0SNeel Natu 	t = vrtc_curtime(vrtc, &basetime);
7810dafa5cdSNeel Natu 	VRTC_UNLOCK(vrtc);
7820dafa5cdSNeel Natu 
7830dafa5cdSNeel Natu 	return (t);
7840dafa5cdSNeel Natu }
7850dafa5cdSNeel Natu 
7860dafa5cdSNeel Natu int
7870dafa5cdSNeel Natu vrtc_nvram_write(struct vm *vm, int offset, uint8_t value)
7880dafa5cdSNeel Natu {
7890dafa5cdSNeel Natu 	struct vrtc *vrtc;
7900dafa5cdSNeel Natu 	uint8_t *ptr;
7910dafa5cdSNeel Natu 
7920dafa5cdSNeel Natu 	vrtc = vm_rtc(vm);
7930dafa5cdSNeel Natu 
7940dafa5cdSNeel Natu 	/*
7950dafa5cdSNeel Natu 	 * Don't allow writes to RTC control registers or the date/time fields.
7960dafa5cdSNeel Natu 	 */
7970dafa5cdSNeel Natu 	if (offset < offsetof(struct rtcdev, nvram[0]) ||
798f39630c2SNeel Natu 	    offset == RTC_CENTURY || offset >= sizeof(struct rtcdev)) {
7990dafa5cdSNeel Natu 		VM_CTR1(vrtc->vm, "RTC nvram write to invalid offset %d",
8000dafa5cdSNeel Natu 		    offset);
8010dafa5cdSNeel Natu 		return (EINVAL);
8020dafa5cdSNeel Natu 	}
8030dafa5cdSNeel Natu 
8040dafa5cdSNeel Natu 	VRTC_LOCK(vrtc);
8050dafa5cdSNeel Natu 	ptr = (uint8_t *)(&vrtc->rtcdev);
8060dafa5cdSNeel Natu 	ptr[offset] = value;
8070dafa5cdSNeel Natu 	VM_CTR2(vrtc->vm, "RTC nvram write %#x to offset %#x", value, offset);
8080dafa5cdSNeel Natu 	VRTC_UNLOCK(vrtc);
8090dafa5cdSNeel Natu 
8100dafa5cdSNeel Natu 	return (0);
8110dafa5cdSNeel Natu }
8120dafa5cdSNeel Natu 
8130dafa5cdSNeel Natu int
8140dafa5cdSNeel Natu vrtc_nvram_read(struct vm *vm, int offset, uint8_t *retval)
8150dafa5cdSNeel Natu {
8160dafa5cdSNeel Natu 	struct vrtc *vrtc;
817787fb3d0SNeel Natu 	sbintime_t basetime;
8180dafa5cdSNeel Natu 	time_t curtime;
8190dafa5cdSNeel Natu 	uint8_t *ptr;
8200dafa5cdSNeel Natu 
8210dafa5cdSNeel Natu 	/*
8220dafa5cdSNeel Natu 	 * Allow all offsets in the RTC to be read.
8230dafa5cdSNeel Natu 	 */
8240dafa5cdSNeel Natu 	if (offset < 0 || offset >= sizeof(struct rtcdev))
8250dafa5cdSNeel Natu 		return (EINVAL);
8260dafa5cdSNeel Natu 
8270dafa5cdSNeel Natu 	vrtc = vm_rtc(vm);
8280dafa5cdSNeel Natu 	VRTC_LOCK(vrtc);
8290dafa5cdSNeel Natu 
8300dafa5cdSNeel Natu 	/*
8310dafa5cdSNeel Natu 	 * Update RTC date/time fields if necessary.
8320dafa5cdSNeel Natu 	 */
833f39630c2SNeel Natu 	if (offset < 10 || offset == RTC_CENTURY) {
834787fb3d0SNeel Natu 		curtime = vrtc_curtime(vrtc, &basetime);
8350dafa5cdSNeel Natu 		secs_to_rtc(curtime, vrtc, 0);
8360dafa5cdSNeel Natu 	}
8370dafa5cdSNeel Natu 
8380dafa5cdSNeel Natu 	ptr = (uint8_t *)(&vrtc->rtcdev);
8390dafa5cdSNeel Natu 	*retval = ptr[offset];
8400dafa5cdSNeel Natu 
8410dafa5cdSNeel Natu 	VRTC_UNLOCK(vrtc);
8420dafa5cdSNeel Natu 	return (0);
8430dafa5cdSNeel Natu }
8440dafa5cdSNeel Natu 
8450dafa5cdSNeel Natu int
8469388bc1eSJohn Baldwin vrtc_addr_handler(struct vm *vm, bool in, int port, int bytes, uint32_t *val)
8470dafa5cdSNeel Natu {
8480dafa5cdSNeel Natu 	struct vrtc *vrtc;
8490dafa5cdSNeel Natu 
8500dafa5cdSNeel Natu 	vrtc = vm_rtc(vm);
8510dafa5cdSNeel Natu 
8520dafa5cdSNeel Natu 	if (bytes != 1)
8530dafa5cdSNeel Natu 		return (-1);
8540dafa5cdSNeel Natu 
8550dafa5cdSNeel Natu 	if (in) {
8560dafa5cdSNeel Natu 		*val = 0xff;
8570dafa5cdSNeel Natu 		return (0);
8580dafa5cdSNeel Natu 	}
8590dafa5cdSNeel Natu 
8600dafa5cdSNeel Natu 	VRTC_LOCK(vrtc);
8610dafa5cdSNeel Natu 	vrtc->addr = *val & 0x7f;
8620dafa5cdSNeel Natu 	VRTC_UNLOCK(vrtc);
8630dafa5cdSNeel Natu 
8640dafa5cdSNeel Natu 	return (0);
8650dafa5cdSNeel Natu }
8660dafa5cdSNeel Natu 
8670dafa5cdSNeel Natu int
8689388bc1eSJohn Baldwin vrtc_data_handler(struct vm *vm, bool in, int port, int bytes, uint32_t *val)
8690dafa5cdSNeel Natu {
8700dafa5cdSNeel Natu 	struct vrtc *vrtc;
8710dafa5cdSNeel Natu 	struct rtcdev *rtc;
872787fb3d0SNeel Natu 	sbintime_t basetime;
8730dafa5cdSNeel Natu 	time_t curtime;
8740dafa5cdSNeel Natu 	int error, offset;
8750dafa5cdSNeel Natu 
8760dafa5cdSNeel Natu 	vrtc = vm_rtc(vm);
8770dafa5cdSNeel Natu 	rtc = &vrtc->rtcdev;
8780dafa5cdSNeel Natu 
8790dafa5cdSNeel Natu 	if (bytes != 1)
8800dafa5cdSNeel Natu 		return (-1);
8810dafa5cdSNeel Natu 
8820dafa5cdSNeel Natu 	VRTC_LOCK(vrtc);
8830dafa5cdSNeel Natu 	offset = vrtc->addr;
8840dafa5cdSNeel Natu 	if (offset >= sizeof(struct rtcdev)) {
8850dafa5cdSNeel Natu 		VRTC_UNLOCK(vrtc);
8860dafa5cdSNeel Natu 		return (-1);
8870dafa5cdSNeel Natu 	}
8880dafa5cdSNeel Natu 
8890dafa5cdSNeel Natu 	error = 0;
890787fb3d0SNeel Natu 	curtime = vrtc_curtime(vrtc, &basetime);
891787fb3d0SNeel Natu 	vrtc_time_update(vrtc, curtime, basetime);
8920dafa5cdSNeel Natu 
8930dafa5cdSNeel Natu 	/*
8940dafa5cdSNeel Natu 	 * Update RTC date/time fields if necessary.
895f39630c2SNeel Natu 	 *
896f39630c2SNeel Natu 	 * This is not just for reads of the RTC. The side-effect of writing
897f39630c2SNeel Natu 	 * the century byte requires other RTC date/time fields (e.g. sec)
898f39630c2SNeel Natu 	 * to be updated here.
8990dafa5cdSNeel Natu 	 */
900f39630c2SNeel Natu 	if (offset < 10 || offset == RTC_CENTURY)
9010dafa5cdSNeel Natu 		secs_to_rtc(curtime, vrtc, 0);
9020dafa5cdSNeel Natu 
903f39630c2SNeel Natu 	if (in) {
9040dafa5cdSNeel Natu 		if (offset == 12) {
9050dafa5cdSNeel Natu 			/*
9060dafa5cdSNeel Natu 			 * XXX
9070dafa5cdSNeel Natu 			 * reg_c interrupt flags are updated only if the
9080dafa5cdSNeel Natu 			 * corresponding interrupt enable bit in reg_b is set.
9090dafa5cdSNeel Natu 			 */
9100dafa5cdSNeel Natu 			*val = vrtc->rtcdev.reg_c;
9110dafa5cdSNeel Natu 			vrtc_set_reg_c(vrtc, 0);
9120dafa5cdSNeel Natu 		} else {
9130dafa5cdSNeel Natu 			*val = *((uint8_t *)rtc + offset);
9140dafa5cdSNeel Natu 		}
9159388bc1eSJohn Baldwin 		VM_CTR2(vm, "Read value %#x from RTC offset %#x",
9160dafa5cdSNeel Natu 		    *val, offset);
9170dafa5cdSNeel Natu 	} else {
9180dafa5cdSNeel Natu 		switch (offset) {
9190dafa5cdSNeel Natu 		case 10:
9209388bc1eSJohn Baldwin 			VM_CTR1(vm, "RTC reg_a set to %#x", *val);
9210dafa5cdSNeel Natu 			vrtc_set_reg_a(vrtc, *val);
9220dafa5cdSNeel Natu 			break;
9230dafa5cdSNeel Natu 		case 11:
9249388bc1eSJohn Baldwin 			VM_CTR1(vm, "RTC reg_b set to %#x", *val);
9250dafa5cdSNeel Natu 			error = vrtc_set_reg_b(vrtc, *val);
9260dafa5cdSNeel Natu 			break;
9270dafa5cdSNeel Natu 		case 12:
9289388bc1eSJohn Baldwin 			VM_CTR1(vm, "RTC reg_c set to %#x (ignored)",
9290dafa5cdSNeel Natu 			    *val);
9300dafa5cdSNeel Natu 			break;
9310dafa5cdSNeel Natu 		case 13:
9329388bc1eSJohn Baldwin 			VM_CTR1(vm, "RTC reg_d set to %#x (ignored)",
9330dafa5cdSNeel Natu 			    *val);
9340dafa5cdSNeel Natu 			break;
9350dafa5cdSNeel Natu 		case 0:
9360dafa5cdSNeel Natu 			/*
9370dafa5cdSNeel Natu 			 * High order bit of 'seconds' is readonly.
9380dafa5cdSNeel Natu 			 */
9390dafa5cdSNeel Natu 			*val &= 0x7f;
9400dafa5cdSNeel Natu 			/* FALLTHRU */
9410dafa5cdSNeel Natu 		default:
9429388bc1eSJohn Baldwin 			VM_CTR2(vm, "RTC offset %#x set to %#x",
9430dafa5cdSNeel Natu 			    offset, *val);
9440dafa5cdSNeel Natu 			*((uint8_t *)rtc + offset) = *val;
9450dafa5cdSNeel Natu 			break;
9460dafa5cdSNeel Natu 		}
947f39630c2SNeel Natu 
948f39630c2SNeel Natu 		/*
949f39630c2SNeel Natu 		 * XXX some guests (e.g. OpenBSD) write the century byte
950f39630c2SNeel Natu 		 * outside of RTCSB_HALT so re-calculate the RTC date/time.
951f39630c2SNeel Natu 		 */
952f39630c2SNeel Natu 		if (offset == RTC_CENTURY && !rtc_halted(vrtc)) {
953f39630c2SNeel Natu 			curtime = rtc_to_secs(vrtc);
954787fb3d0SNeel Natu 			error = vrtc_time_update(vrtc, curtime, sbinuptime());
955f39630c2SNeel Natu 			KASSERT(!error, ("vrtc_time_update error %d", error));
956f39630c2SNeel Natu 			if (curtime == VRTC_BROKEN_TIME && rtc_flag_broken_time)
957f39630c2SNeel Natu 				error = -1;
958f39630c2SNeel Natu 		}
9590dafa5cdSNeel Natu 	}
9600dafa5cdSNeel Natu 	VRTC_UNLOCK(vrtc);
9610dafa5cdSNeel Natu 	return (error);
9620dafa5cdSNeel Natu }
9630dafa5cdSNeel Natu 
9640dafa5cdSNeel Natu void
9650dafa5cdSNeel Natu vrtc_reset(struct vrtc *vrtc)
9660dafa5cdSNeel Natu {
9670dafa5cdSNeel Natu 	struct rtcdev *rtc;
9680dafa5cdSNeel Natu 
9690dafa5cdSNeel Natu 	VRTC_LOCK(vrtc);
9700dafa5cdSNeel Natu 
9710dafa5cdSNeel Natu 	rtc = &vrtc->rtcdev;
9720dafa5cdSNeel Natu 	vrtc_set_reg_b(vrtc, rtc->reg_b & ~(RTCSB_ALL_INTRS | RTCSB_SQWE));
9730dafa5cdSNeel Natu 	vrtc_set_reg_c(vrtc, 0);
9740dafa5cdSNeel Natu 	KASSERT(!callout_active(&vrtc->callout), ("rtc callout still active"));
9750dafa5cdSNeel Natu 
9760dafa5cdSNeel Natu 	VRTC_UNLOCK(vrtc);
9770dafa5cdSNeel Natu }
9780dafa5cdSNeel Natu 
9790dafa5cdSNeel Natu struct vrtc *
9800dafa5cdSNeel Natu vrtc_init(struct vm *vm)
9810dafa5cdSNeel Natu {
9820dafa5cdSNeel Natu 	struct vrtc *vrtc;
9830dafa5cdSNeel Natu 	struct rtcdev *rtc;
9840dafa5cdSNeel Natu 	time_t curtime;
9850dafa5cdSNeel Natu 
9860dafa5cdSNeel Natu 	vrtc = malloc(sizeof(struct vrtc), M_VRTC, M_WAITOK | M_ZERO);
9870dafa5cdSNeel Natu 	vrtc->vm = vm;
9880dafa5cdSNeel Natu 	mtx_init(&vrtc->mtx, "vrtc lock", NULL, MTX_DEF);
9890dafa5cdSNeel Natu 	callout_init(&vrtc->callout, 1);
9900dafa5cdSNeel Natu 
9910dafa5cdSNeel Natu 	/* Allow dividers to keep time but disable everything else */
9920dafa5cdSNeel Natu 	rtc = &vrtc->rtcdev;
9930dafa5cdSNeel Natu 	rtc->reg_a = 0x20;
9940dafa5cdSNeel Natu 	rtc->reg_b = RTCSB_24HR;
9950dafa5cdSNeel Natu 	rtc->reg_c = 0;
9960dafa5cdSNeel Natu 	rtc->reg_d = RTCSD_PWR;
9970dafa5cdSNeel Natu 
9980dafa5cdSNeel Natu 	/* Reset the index register to a safe value. */
9990dafa5cdSNeel Natu 	vrtc->addr = RTC_STATUSD;
10000dafa5cdSNeel Natu 
10010dafa5cdSNeel Natu 	/*
10020dafa5cdSNeel Natu 	 * Initialize RTC time to 00:00:00 Jan 1, 1970.
10030dafa5cdSNeel Natu 	 */
10040dafa5cdSNeel Natu 	curtime = 0;
10050dafa5cdSNeel Natu 
10060dafa5cdSNeel Natu 	VRTC_LOCK(vrtc);
10070dafa5cdSNeel Natu 	vrtc->base_rtctime = VRTC_BROKEN_TIME;
1008787fb3d0SNeel Natu 	vrtc_time_update(vrtc, curtime, sbinuptime());
10090dafa5cdSNeel Natu 	secs_to_rtc(curtime, vrtc, 0);
10100dafa5cdSNeel Natu 	VRTC_UNLOCK(vrtc);
10110dafa5cdSNeel Natu 
10120dafa5cdSNeel Natu 	return (vrtc);
10130dafa5cdSNeel Natu }
10140dafa5cdSNeel Natu 
10150dafa5cdSNeel Natu void
10160dafa5cdSNeel Natu vrtc_cleanup(struct vrtc *vrtc)
10170dafa5cdSNeel Natu {
10180dafa5cdSNeel Natu 
10190dafa5cdSNeel Natu 	callout_drain(&vrtc->callout);
102008ebb360SJohn Baldwin 	mtx_destroy(&vrtc->mtx);
10210dafa5cdSNeel Natu 	free(vrtc, M_VRTC);
10220dafa5cdSNeel Natu }
1023483d953aSJohn Baldwin 
1024483d953aSJohn Baldwin #ifdef BHYVE_SNAPSHOT
1025483d953aSJohn Baldwin int
1026483d953aSJohn Baldwin vrtc_snapshot(struct vrtc *vrtc, struct vm_snapshot_meta *meta)
1027483d953aSJohn Baldwin {
1028483d953aSJohn Baldwin 	int ret;
1029483d953aSJohn Baldwin 
1030483d953aSJohn Baldwin 	VRTC_LOCK(vrtc);
1031483d953aSJohn Baldwin 
1032483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->addr, meta, ret, done);
1033483d953aSJohn Baldwin 	if (meta->op == VM_SNAPSHOT_RESTORE)
1034483d953aSJohn Baldwin 		vrtc->base_uptime = sbinuptime();
1035483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->base_rtctime, meta, ret, done);
1036483d953aSJohn Baldwin 
1037483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.sec, meta, ret, done);
1038483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.alarm_sec, meta, ret, done);
1039483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.min, meta, ret, done);
1040483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.alarm_min, meta, ret, done);
1041483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.hour, meta, ret, done);
1042483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.alarm_hour, meta, ret, done);
1043483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.day_of_week, meta, ret, done);
1044483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.day_of_month, meta, ret, done);
1045483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.month, meta, ret, done);
1046483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.year, meta, ret, done);
1047483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.reg_a, meta, ret, done);
1048483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.reg_b, meta, ret, done);
1049483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.reg_c, meta, ret, done);
1050483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.reg_d, meta, ret, done);
1051483d953aSJohn Baldwin 	SNAPSHOT_BUF_OR_LEAVE(vrtc->rtcdev.nvram, sizeof(vrtc->rtcdev.nvram),
1052483d953aSJohn Baldwin 			      meta, ret, done);
1053483d953aSJohn Baldwin 	SNAPSHOT_VAR_OR_LEAVE(vrtc->rtcdev.century, meta, ret, done);
1054483d953aSJohn Baldwin 	SNAPSHOT_BUF_OR_LEAVE(vrtc->rtcdev.nvram2, sizeof(vrtc->rtcdev.nvram2),
1055483d953aSJohn Baldwin 			      meta, ret, done);
1056483d953aSJohn Baldwin 
1057483d953aSJohn Baldwin 	vrtc_callout_reset(vrtc, vrtc_freq(vrtc));
1058483d953aSJohn Baldwin 
1059483d953aSJohn Baldwin 	VRTC_UNLOCK(vrtc);
1060483d953aSJohn Baldwin 
1061483d953aSJohn Baldwin done:
1062483d953aSJohn Baldwin 	return (ret);
1063483d953aSJohn Baldwin }
1064483d953aSJohn Baldwin #endif
1065