xref: /illumos-gate/usr/src/uts/intel/io/vmm/io/vrtc.c (revision 32640292)
14c87aefeSPatrick Mooney /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
34c87aefeSPatrick Mooney  *
44c87aefeSPatrick Mooney  * Copyright (c) 2014, Neel Natu (neel@freebsd.org)
54c87aefeSPatrick Mooney  * All rights reserved.
64c87aefeSPatrick Mooney  *
74c87aefeSPatrick Mooney  * Redistribution and use in source and binary forms, with or without
84c87aefeSPatrick Mooney  * modification, are permitted provided that the following conditions
94c87aefeSPatrick Mooney  * are met:
104c87aefeSPatrick Mooney  * 1. Redistributions of source code must retain the above copyright
114c87aefeSPatrick Mooney  *    notice unmodified, this list of conditions, and the following
124c87aefeSPatrick Mooney  *    disclaimer.
134c87aefeSPatrick Mooney  * 2. Redistributions in binary form must reproduce the above copyright
144c87aefeSPatrick Mooney  *    notice, this list of conditions and the following disclaimer in the
154c87aefeSPatrick Mooney  *    documentation and/or other materials provided with the distribution.
164c87aefeSPatrick Mooney  *
174c87aefeSPatrick Mooney  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
184c87aefeSPatrick Mooney  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
194c87aefeSPatrick Mooney  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
204c87aefeSPatrick Mooney  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
214c87aefeSPatrick Mooney  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
224c87aefeSPatrick Mooney  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
234c87aefeSPatrick Mooney  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
244c87aefeSPatrick Mooney  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
254c87aefeSPatrick Mooney  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
264c87aefeSPatrick Mooney  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
274c87aefeSPatrick Mooney  */
284c87aefeSPatrick Mooney 
294c87aefeSPatrick Mooney /*
304c87aefeSPatrick Mooney  * Copyright 2018 Joyent, Inc.
31a1d41cf9SPatrick Mooney  * Copyright 2023 Oxide Computer Company
324c87aefeSPatrick Mooney  */
334c87aefeSPatrick Mooney 
344c87aefeSPatrick Mooney #include <sys/cdefs.h>
354c87aefeSPatrick Mooney 
364c87aefeSPatrick Mooney #include <sys/param.h>
374c87aefeSPatrick Mooney #include <sys/systm.h>
384c87aefeSPatrick Mooney #include <sys/queue.h>
394c87aefeSPatrick Mooney #include <sys/kernel.h>
408130f8e1SPatrick Mooney #include <sys/kmem.h>
414c87aefeSPatrick Mooney #include <sys/mutex.h>
424c87aefeSPatrick Mooney #include <sys/clock.h>
434c87aefeSPatrick Mooney #include <sys/sysctl.h>
444c87aefeSPatrick Mooney 
454c87aefeSPatrick Mooney #include <machine/vmm.h>
464c87aefeSPatrick Mooney 
474c87aefeSPatrick Mooney #include <isa/rtc.h>
484c87aefeSPatrick Mooney 
494c87aefeSPatrick Mooney #include "vatpic.h"
504c87aefeSPatrick Mooney #include "vioapic.h"
514c87aefeSPatrick Mooney #include "vrtc.h"
524c87aefeSPatrick Mooney 
53a1d41cf9SPatrick Mooney /*
54a1d41cf9SPatrick Mooney  * Virtual RTC: Fashioned after the MC146818
55a1d41cf9SPatrick Mooney  *
56a1d41cf9SPatrick Mooney  * Current limitations:
57a1d41cf9SPatrick Mooney  * - Clock divider will only run at 32768Hz (not 1.x or 4.x MHz)
58a1d41cf9SPatrick Mooney  * - Date-times prior to 1970-01-01 are not supported
59a1d41cf9SPatrick Mooney  * - If date-time held in CMOS is not valid (such as a nonsensical month/day)
60a1d41cf9SPatrick Mooney  *   then updates to the time (hours/minutes/seconds) will not occur, even if
61a1d41cf9SPatrick Mooney  *   they are enabled through the divider and flags.
62a1d41cf9SPatrick Mooney  */
63a1d41cf9SPatrick Mooney 
644c87aefeSPatrick Mooney /* Register layout of the RTC */
654c87aefeSPatrick Mooney struct rtcdev {
664c87aefeSPatrick Mooney 	uint8_t	sec;
674c87aefeSPatrick Mooney 	uint8_t	alarm_sec;
684c87aefeSPatrick Mooney 	uint8_t	min;
694c87aefeSPatrick Mooney 	uint8_t	alarm_min;
704c87aefeSPatrick Mooney 	uint8_t	hour;
714c87aefeSPatrick Mooney 	uint8_t	alarm_hour;
724c87aefeSPatrick Mooney 	uint8_t	day_of_week;
734c87aefeSPatrick Mooney 	uint8_t	day_of_month;
744c87aefeSPatrick Mooney 	uint8_t	month;
754c87aefeSPatrick Mooney 	uint8_t	year;
764c87aefeSPatrick Mooney 	uint8_t	reg_a;
774c87aefeSPatrick Mooney 	uint8_t	reg_b;
784c87aefeSPatrick Mooney 	uint8_t	reg_c;
794c87aefeSPatrick Mooney 	uint8_t	reg_d;
804c87aefeSPatrick Mooney 	uint8_t	nvram[36];
814c87aefeSPatrick Mooney 	uint8_t	century;
824c87aefeSPatrick Mooney 	uint8_t	nvram2[128 - 51];
834c87aefeSPatrick Mooney } __packed;
842699b94cSPatrick Mooney CTASSERT(sizeof (struct rtcdev) == 128);
854c87aefeSPatrick Mooney CTASSERT(offsetof(struct rtcdev, century) == RTC_CENTURY);
864c87aefeSPatrick Mooney 
874c87aefeSPatrick Mooney struct vrtc {
884c87aefeSPatrick Mooney 	struct vm	*vm;
89fb29dee0SPatrick Mooney 	kmutex_t	lock;
904c87aefeSPatrick Mooney 	struct callout	callout;
91a1d41cf9SPatrick Mooney 
92a1d41cf9SPatrick Mooney 	/*
93a1d41cf9SPatrick Mooney 	 * Address within the RTC to access when reading/writing from the data
94a1d41cf9SPatrick Mooney 	 * IO port.
95a1d41cf9SPatrick Mooney 	 */
96a1d41cf9SPatrick Mooney 	uint8_t		addr;
97a1d41cf9SPatrick Mooney 
98a1d41cf9SPatrick Mooney 	/*
99a1d41cf9SPatrick Mooney 	 * Time base for RTC functionality driven from the output of the
100a1d41cf9SPatrick Mooney 	 * (emulated) divider.  Holds the hrtime at the edge of the last update
101a1d41cf9SPatrick Mooney 	 * to seconds, be that an "official" update of the running RTC, the
102a1d41cf9SPatrick Mooney 	 * divider being enabled by the guest (and thus implying a start 500ms
103a1d41cf9SPatrick Mooney 	 * earlier), or the time being set by a userspace consumer.
104a1d41cf9SPatrick Mooney 	 */
105a1d41cf9SPatrick Mooney 	hrtime_t	base_clock;
106a1d41cf9SPatrick Mooney 
107a1d41cf9SPatrick Mooney 	/*
108a1d41cf9SPatrick Mooney 	 * Time for most recent periodic-timer-driven event.  Should be kept in
109a1d41cf9SPatrick Mooney 	 * phase with base_clock as it relates to edge boundaries of seconds.
110a1d41cf9SPatrick Mooney 	 */
111a1d41cf9SPatrick Mooney 	hrtime_t	last_period;
112a1d41cf9SPatrick Mooney 
113a1d41cf9SPatrick Mooney 	/*
114a1d41cf9SPatrick Mooney 	 * (UNIX) Time at the last base_clock reading.
115a1d41cf9SPatrick Mooney 	 *
116a1d41cf9SPatrick Mooney 	 * If an invalid date/time is specified in the RTC fields, this will
117a1d41cf9SPatrick Mooney 	 * hold VRTC_BROKEN_TIME to indicate to the rest of the vRTC logic that
118a1d41cf9SPatrick Mooney 	 * further updates will not occur on divider ticks (until the RTC fields
119a1d41cf9SPatrick Mooney 	 * are updated to hold a valid date/time).
120a1d41cf9SPatrick Mooney 	 */
1214c87aefeSPatrick Mooney 	time_t		base_rtctime;
122a1d41cf9SPatrick Mooney 
1234c87aefeSPatrick Mooney 	struct rtcdev	rtcdev;
1244c87aefeSPatrick Mooney };
1254c87aefeSPatrick Mooney 
126fb29dee0SPatrick Mooney #define	VRTC_LOCK(vrtc)		mutex_enter(&((vrtc)->lock))
127fb29dee0SPatrick Mooney #define	VRTC_UNLOCK(vrtc)	mutex_exit(&((vrtc)->lock))
128fb29dee0SPatrick Mooney #define	VRTC_LOCKED(vrtc)	MUTEX_HELD(&((vrtc)->lock))
1294c87aefeSPatrick Mooney 
1304c87aefeSPatrick Mooney /*
1314c87aefeSPatrick Mooney  * RTC time is considered "broken" if:
1324c87aefeSPatrick Mooney  * - RTC updates are halted by the guest
1334c87aefeSPatrick Mooney  * - RTC date/time fields have invalid values
1344c87aefeSPatrick Mooney  */
1354c87aefeSPatrick Mooney #define	VRTC_BROKEN_TIME	((time_t)-1)
1364c87aefeSPatrick Mooney 
1374c87aefeSPatrick Mooney #define	RTC_IRQ			8
138a1d41cf9SPatrick Mooney 
139a1d41cf9SPatrick Mooney #define	RTCSA_DIVIDER_MASK	0x70
140a1d41cf9SPatrick Mooney #define	RTCSA_DIVIDER_32K	0x20
141a1d41cf9SPatrick Mooney #define	RTCSA_PERIOD_MASK	0x0f
1424c87aefeSPatrick Mooney #define	RTCSB_BIN		0x04
143a1d41cf9SPatrick Mooney #define	RTCSB_INTR_MASK		(RTCSB_UINTR | RTCSB_AINTR | RTCSB_PINTR)
144d515dd77SPatrick Mooney #define	RTCSC_MASK	(RTCIR_UPDATE | RTCIR_ALARM | RTCIR_PERIOD | RTCIR_INT)
1454c87aefeSPatrick Mooney 
146a1d41cf9SPatrick Mooney /*
147a1d41cf9SPatrick Mooney  * Setting the two high bits in the alarm fields indicates a "don't care"
148a1d41cf9SPatrick Mooney  * condition, where that alarm field is to match against any value residing in
149a1d41cf9SPatrick Mooney  * its associated time field.
150a1d41cf9SPatrick Mooney  */
151a1d41cf9SPatrick Mooney #define	ALARM_DONT_CARE(x)	(((x) & 0xc0) == 0xc0)
152a1d41cf9SPatrick Mooney 
153a1d41cf9SPatrick Mooney /* The high bit of the hour field indicates PM when in 12-hour mode */
154a1d41cf9SPatrick Mooney #define	HOUR_IS_PM		0x80
155a1d41cf9SPatrick Mooney 
156a1d41cf9SPatrick Mooney #define	SEC_PER_DAY	(24 * 60 * 60)
1574c87aefeSPatrick Mooney 
158a1d41cf9SPatrick Mooney #define	ROUNDDOWN(x, y)	(((x)/(y))*(y))
1594c87aefeSPatrick Mooney 
160a1d41cf9SPatrick Mooney static void vrtc_regc_update(struct vrtc *, uint8_t);
161a1d41cf9SPatrick Mooney static void vrtc_callout_reschedule(struct vrtc *);
1624c87aefeSPatrick Mooney 
1634c87aefeSPatrick Mooney static __inline bool
rtc_field_datetime(uint8_t off)164a1d41cf9SPatrick Mooney rtc_field_datetime(uint8_t off)
165a1d41cf9SPatrick Mooney {
166a1d41cf9SPatrick Mooney 	switch (off) {
167a1d41cf9SPatrick Mooney 	case RTC_SEC:
168a1d41cf9SPatrick Mooney 	case RTC_MIN:
169a1d41cf9SPatrick Mooney 	case RTC_HRS:
170a1d41cf9SPatrick Mooney 	case RTC_WDAY:
171a1d41cf9SPatrick Mooney 	case RTC_DAY:
172a1d41cf9SPatrick Mooney 	case RTC_MONTH:
173a1d41cf9SPatrick Mooney 	case RTC_YEAR:
174a1d41cf9SPatrick Mooney 	case RTC_CENTURY:
175a1d41cf9SPatrick Mooney 		return (true);
176a1d41cf9SPatrick Mooney 	default:
177a1d41cf9SPatrick Mooney 		return (false);
178a1d41cf9SPatrick Mooney 	}
179a1d41cf9SPatrick Mooney }
180a1d41cf9SPatrick Mooney 
181a1d41cf9SPatrick Mooney static __inline bool
rtc_field_ondemand(uint8_t off)182a1d41cf9SPatrick Mooney rtc_field_ondemand(uint8_t off)
183a1d41cf9SPatrick Mooney {
184a1d41cf9SPatrick Mooney 	switch (off) {
185a1d41cf9SPatrick Mooney 	case RTC_STATUSA:
186a1d41cf9SPatrick Mooney 	case RTC_STATUSB:
187a1d41cf9SPatrick Mooney 	case RTC_INTR:
188a1d41cf9SPatrick Mooney 	case RTC_STATUSD:
189a1d41cf9SPatrick Mooney 		return (true);
190a1d41cf9SPatrick Mooney 	default:
191a1d41cf9SPatrick Mooney 		return (rtc_field_datetime(off));
192a1d41cf9SPatrick Mooney 	}
193a1d41cf9SPatrick Mooney }
194a1d41cf9SPatrick Mooney 
195a1d41cf9SPatrick Mooney static __inline bool
rtc_halted(const struct vrtc * vrtc)196a1d41cf9SPatrick Mooney rtc_halted(const struct vrtc *vrtc)
197a1d41cf9SPatrick Mooney {
198a1d41cf9SPatrick Mooney 	return ((vrtc->rtcdev.reg_b & RTCSB_HALT) != 0);
199a1d41cf9SPatrick Mooney }
200a1d41cf9SPatrick Mooney 
201a1d41cf9SPatrick Mooney static __inline bool
rega_divider_en(uint8_t rega)202a1d41cf9SPatrick Mooney rega_divider_en(uint8_t rega)
2034c87aefeSPatrick Mooney {
2044c87aefeSPatrick Mooney 	/*
2054c87aefeSPatrick Mooney 	 * The RTC is counting only when dividers are not held in reset.
2064c87aefeSPatrick Mooney 	 */
207a1d41cf9SPatrick Mooney 	return ((rega & RTCSA_DIVIDER_MASK) == RTCSA_DIVIDER_32K);
208a1d41cf9SPatrick Mooney }
209a1d41cf9SPatrick Mooney 
210a1d41cf9SPatrick Mooney static __inline hrtime_t
rega_period(uint8_t rega)211a1d41cf9SPatrick Mooney rega_period(uint8_t rega)
212a1d41cf9SPatrick Mooney {
213a1d41cf9SPatrick Mooney 	const uint_t sel = rega & RTCSA_PERIOD_MASK;
214a1d41cf9SPatrick Mooney 	const hrtime_t rate_period[16] = {
215a1d41cf9SPatrick Mooney 		0,
216a1d41cf9SPatrick Mooney 		NANOSEC / 256,
217a1d41cf9SPatrick Mooney 		NANOSEC / 128,
218a1d41cf9SPatrick Mooney 		NANOSEC / 8192,
219a1d41cf9SPatrick Mooney 		NANOSEC / 4096,
220a1d41cf9SPatrick Mooney 		NANOSEC / 2048,
221a1d41cf9SPatrick Mooney 		NANOSEC / 1024,
222a1d41cf9SPatrick Mooney 		NANOSEC / 512,
223a1d41cf9SPatrick Mooney 		NANOSEC / 256,
224a1d41cf9SPatrick Mooney 		NANOSEC / 128,
225a1d41cf9SPatrick Mooney 		NANOSEC / 64,
226a1d41cf9SPatrick Mooney 		NANOSEC / 32,
227a1d41cf9SPatrick Mooney 		NANOSEC / 16,
228a1d41cf9SPatrick Mooney 		NANOSEC / 8,
229a1d41cf9SPatrick Mooney 		NANOSEC / 4,
230a1d41cf9SPatrick Mooney 		NANOSEC / 2,
231a1d41cf9SPatrick Mooney 	};
232a1d41cf9SPatrick Mooney 
233a1d41cf9SPatrick Mooney 	return (rate_period[sel]);
2344c87aefeSPatrick Mooney }
2354c87aefeSPatrick Mooney 
2364c87aefeSPatrick Mooney static __inline bool
vrtc_update_enabled(const struct vrtc * vrtc)237a1d41cf9SPatrick Mooney vrtc_update_enabled(const struct vrtc *vrtc)
2384c87aefeSPatrick Mooney {
2394c87aefeSPatrick Mooney 	/*
2404c87aefeSPatrick Mooney 	 * RTC date/time can be updated only if:
2414c87aefeSPatrick Mooney 	 * - divider is not held in reset
2424c87aefeSPatrick Mooney 	 * - guest has not disabled updates
2434c87aefeSPatrick Mooney 	 * - the date/time fields have valid contents
2444c87aefeSPatrick Mooney 	 */
245a1d41cf9SPatrick Mooney 	if (!rega_divider_en(vrtc->rtcdev.reg_a))
2464c87aefeSPatrick Mooney 		return (false);
2474c87aefeSPatrick Mooney 
2484c87aefeSPatrick Mooney 	if (rtc_halted(vrtc))
2494c87aefeSPatrick Mooney 		return (false);
2504c87aefeSPatrick Mooney 
2514c87aefeSPatrick Mooney 	if (vrtc->base_rtctime == VRTC_BROKEN_TIME)
2524c87aefeSPatrick Mooney 		return (false);
2534c87aefeSPatrick Mooney 
2544c87aefeSPatrick Mooney 	return (true);
2554c87aefeSPatrick Mooney }
2564c87aefeSPatrick Mooney 
257a1d41cf9SPatrick Mooney /*
258a1d41cf9SPatrick Mooney  * Calculate the current time held by the RTC.  If the RTC is running (divider
259a1d41cf9SPatrick Mooney  * enabled, and updates not halted) then this will account for any time has
260a1d41cf9SPatrick Mooney  * passed since the last update.
261a1d41cf9SPatrick Mooney  */
2624c87aefeSPatrick Mooney static time_t
vrtc_curtime(struct vrtc * vrtc,hrtime_t * basep,hrtime_t * phasep)263a1d41cf9SPatrick Mooney vrtc_curtime(struct vrtc *vrtc, hrtime_t *basep, hrtime_t *phasep)
2644c87aefeSPatrick Mooney {
2655103e761SPatrick Mooney 	time_t t = vrtc->base_rtctime;
266a1d41cf9SPatrick Mooney 	hrtime_t base = vrtc->base_clock;
267a1d41cf9SPatrick Mooney 	hrtime_t phase = 0;
2684c87aefeSPatrick Mooney 
269fb29dee0SPatrick Mooney 	ASSERT(VRTC_LOCKED(vrtc));
2704c87aefeSPatrick Mooney 
271a1d41cf9SPatrick Mooney 	if (vrtc_update_enabled(vrtc)) {
272a1d41cf9SPatrick Mooney 		const hrtime_t delta = gethrtime() - vrtc->base_clock;
2735103e761SPatrick Mooney 		const time_t sec = delta / NANOSEC;
2745103e761SPatrick Mooney 
2755103e761SPatrick Mooney 		ASSERT3S(delta, >=, 0);
2765103e761SPatrick Mooney 
2775103e761SPatrick Mooney 		t += sec;
2785103e761SPatrick Mooney 		base += sec * NANOSEC;
279a1d41cf9SPatrick Mooney 		phase = delta % NANOSEC;
2805103e761SPatrick Mooney 	}
281a1d41cf9SPatrick Mooney 	if (basep != NULL) {
282a1d41cf9SPatrick Mooney 		*basep = base;
283a1d41cf9SPatrick Mooney 	}
284a1d41cf9SPatrick Mooney 	if (phasep != NULL) {
285a1d41cf9SPatrick Mooney 		*phasep = phase;
2864c87aefeSPatrick Mooney 	}
2874c87aefeSPatrick Mooney 	return (t);
2884c87aefeSPatrick Mooney }
2894c87aefeSPatrick Mooney 
290a1d41cf9SPatrick Mooney /* Encode an RTC CMOS value, converting to BCD if necessary */
2914c87aefeSPatrick Mooney static __inline uint8_t
rtc_enc(const struct rtcdev * rtc,uint8_t val)292a1d41cf9SPatrick Mooney rtc_enc(const struct rtcdev *rtc, uint8_t val)
2934c87aefeSPatrick Mooney {
294a1d41cf9SPatrick Mooney 	const uint8_t bin2bcd_data[] = {
295a1d41cf9SPatrick Mooney 		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
296a1d41cf9SPatrick Mooney 		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
297a1d41cf9SPatrick Mooney 		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
298a1d41cf9SPatrick Mooney 		0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
299a1d41cf9SPatrick Mooney 		0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
300a1d41cf9SPatrick Mooney 		0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
301a1d41cf9SPatrick Mooney 		0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
302a1d41cf9SPatrick Mooney 		0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
303a1d41cf9SPatrick Mooney 		0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
304a1d41cf9SPatrick Mooney 		0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99
305a1d41cf9SPatrick Mooney 	};
3064c87aefeSPatrick Mooney 
307a1d41cf9SPatrick Mooney 	ASSERT3U(val, <, 100);
3084c87aefeSPatrick Mooney 
3094c87aefeSPatrick Mooney 	return ((rtc->reg_b & RTCSB_BIN) ? val : bin2bcd_data[val]);
3104c87aefeSPatrick Mooney }
3114c87aefeSPatrick Mooney 
312a1d41cf9SPatrick Mooney /*
313a1d41cf9SPatrick Mooney  * Write the date/time fields in the CMOS with the date represented by the
314a1d41cf9SPatrick Mooney  * internal RTC time (base_rtctime).  If the time is not valid, or updates of
315a1d41cf9SPatrick Mooney  * the RTC are disabled via register configuration (without force_update
316a1d41cf9SPatrick Mooney  * override), then the CMOS contents will not be changed.
317a1d41cf9SPatrick Mooney  */
3184c87aefeSPatrick Mooney static void
vrtc_time_to_cmos(struct vrtc * vrtc,bool force_update)319a1d41cf9SPatrick Mooney vrtc_time_to_cmos(struct vrtc *vrtc, bool force_update)
3204c87aefeSPatrick Mooney {
321a1d41cf9SPatrick Mooney 	struct rtcdev *rtc = &vrtc->rtcdev;
322a1d41cf9SPatrick Mooney 	struct timespec ts = {
323a1d41cf9SPatrick Mooney 		.tv_sec = vrtc->base_rtctime,
324a1d41cf9SPatrick Mooney 		.tv_nsec = 0,
325a1d41cf9SPatrick Mooney 	};
3264c87aefeSPatrick Mooney 
327fb29dee0SPatrick Mooney 	ASSERT(VRTC_LOCKED(vrtc));
3284c87aefeSPatrick Mooney 
329a1d41cf9SPatrick Mooney 	if (vrtc->base_rtctime < 0) {
330a1d41cf9SPatrick Mooney 		ASSERT3S(vrtc->base_rtctime, ==, VRTC_BROKEN_TIME);
3314c87aefeSPatrick Mooney 		return;
3324c87aefeSPatrick Mooney 	}
3334c87aefeSPatrick Mooney 
3344c87aefeSPatrick Mooney 	/*
3354c87aefeSPatrick Mooney 	 * If the RTC is halted then the guest has "ownership" of the
3364c87aefeSPatrick Mooney 	 * date/time fields. Don't update the RTC date/time fields in
3374c87aefeSPatrick Mooney 	 * this case (unless forced).
3384c87aefeSPatrick Mooney 	 */
339a1d41cf9SPatrick Mooney 	if (rtc_halted(vrtc) && !force_update) {
3404c87aefeSPatrick Mooney 		return;
341a1d41cf9SPatrick Mooney 	}
3424c87aefeSPatrick Mooney 
343a1d41cf9SPatrick Mooney 	struct clocktime ct;
3444c87aefeSPatrick Mooney 	clock_ts_to_ct(&ts, &ct);
3454c87aefeSPatrick Mooney 
346a1d41cf9SPatrick Mooney 	/*
347a1d41cf9SPatrick Mooney 	 * Check that output from clock_ts_to_ct() matches expectations.
348a1d41cf9SPatrick Mooney 	 * Although it closely resembles the requirements for the RTC CMOS
349a1d41cf9SPatrick Mooney 	 * fields, there are a few notable parts (day-of-week) which are
350a1d41cf9SPatrick Mooney 	 * different, and are thus subsequently adjusted for the CMOS output.
351a1d41cf9SPatrick Mooney 	 */
352a1d41cf9SPatrick Mooney 	ASSERT(ct.sec >= 0 && ct.sec <= 59);
353a1d41cf9SPatrick Mooney 	ASSERT(ct.min >= 0 && ct.min <= 59);
354a1d41cf9SPatrick Mooney 	ASSERT(ct.hour >= 0 && ct.hour <= 23);
355a1d41cf9SPatrick Mooney 	ASSERT(ct.dow >= 0 && ct.dow <= 6);
356a1d41cf9SPatrick Mooney 	ASSERT(ct.day >= 1 && ct.day <= 31);
357a1d41cf9SPatrick Mooney 	ASSERT(ct.mon >= 1 && ct.mon <= 12);
358a1d41cf9SPatrick Mooney 	ASSERT(ct.year >= POSIX_BASE_YEAR);
3594c87aefeSPatrick Mooney 
360a1d41cf9SPatrick Mooney 	rtc->sec = rtc_enc(rtc, ct.sec);
361a1d41cf9SPatrick Mooney 	rtc->min = rtc_enc(rtc, ct.min);
3624c87aefeSPatrick Mooney 
363a1d41cf9SPatrick Mooney 	int hour;
3644c87aefeSPatrick Mooney 	if (rtc->reg_b & RTCSB_24HR) {
3654c87aefeSPatrick Mooney 		hour = ct.hour;
3664c87aefeSPatrick Mooney 	} else {
3674c87aefeSPatrick Mooney 		/*
3684c87aefeSPatrick Mooney 		 * Convert to the 12-hour format.
3694c87aefeSPatrick Mooney 		 */
3704c87aefeSPatrick Mooney 		switch (ct.hour) {
3714c87aefeSPatrick Mooney 		case 0:			/* 12 AM */
3724c87aefeSPatrick Mooney 		case 12:		/* 12 PM */
3734c87aefeSPatrick Mooney 			hour = 12;
3744c87aefeSPatrick Mooney 			break;
3754c87aefeSPatrick Mooney 		default:
3764c87aefeSPatrick Mooney 			/*
3774c87aefeSPatrick Mooney 			 * The remaining 'ct.hour' values are interpreted as:
3784c87aefeSPatrick Mooney 			 * [1  - 11] ->  1 - 11 AM
3794c87aefeSPatrick Mooney 			 * [13 - 23] ->  1 - 11 PM
3804c87aefeSPatrick Mooney 			 */
3814c87aefeSPatrick Mooney 			hour = ct.hour % 12;
3824c87aefeSPatrick Mooney 			break;
3834c87aefeSPatrick Mooney 		}
3844c87aefeSPatrick Mooney 	}
3854c87aefeSPatrick Mooney 
386a1d41cf9SPatrick Mooney 	rtc->hour = rtc_enc(rtc, hour);
3874c87aefeSPatrick Mooney 
388a1d41cf9SPatrick Mooney 	if ((rtc->reg_b & RTCSB_24HR) == 0 && ct.hour >= 12) {
389a1d41cf9SPatrick Mooney 		/* set MSB to indicate PM */
390a1d41cf9SPatrick Mooney 		rtc->hour |= HOUR_IS_PM;
3914c87aefeSPatrick Mooney 	}
3924c87aefeSPatrick Mooney 
393a1d41cf9SPatrick Mooney 	rtc->day_of_week = rtc_enc(rtc, ct.dow + 1);
394a1d41cf9SPatrick Mooney 	rtc->day_of_month = rtc_enc(rtc, ct.day);
395a1d41cf9SPatrick Mooney 	rtc->month = rtc_enc(rtc, ct.mon);
396a1d41cf9SPatrick Mooney 	rtc->year = rtc_enc(rtc, ct.year % 100);
397a1d41cf9SPatrick Mooney 	rtc->century = rtc_enc(rtc, ct.year / 100);
3984c87aefeSPatrick Mooney }
3994c87aefeSPatrick Mooney 
400a1d41cf9SPatrick Mooney /* Decode an RTC CMOS value, converting from BCD if necessary */
401a1d41cf9SPatrick Mooney static uint8_t
rtc_dec(const struct rtcdev * rtc,uint8_t val,bool * errp)402a1d41cf9SPatrick Mooney rtc_dec(const struct rtcdev *rtc, uint8_t val, bool *errp)
4034c87aefeSPatrick Mooney {
404a1d41cf9SPatrick Mooney 	if ((rtc->reg_b & RTCSB_BIN) == 0) {
405a1d41cf9SPatrick Mooney 		const uint8_t lower = val & 0xf;
406a1d41cf9SPatrick Mooney 		const uint8_t upper = val >> 4;
4074c87aefeSPatrick Mooney 
408a1d41cf9SPatrick Mooney 		*errp = (lower > 9 || upper > 9);
4094c87aefeSPatrick Mooney 
410a1d41cf9SPatrick Mooney 		/*
411a1d41cf9SPatrick Mooney 		 * Output will be bogus if value is out of range, so it is on
412a1d41cf9SPatrick Mooney 		 * the caller to properly check `errp`.
413a1d41cf9SPatrick Mooney 		 */
414a1d41cf9SPatrick Mooney 		return ((upper * 10) + lower);
415a1d41cf9SPatrick Mooney 	} else {
416a1d41cf9SPatrick Mooney 		*errp = false;
417a1d41cf9SPatrick Mooney 		return (val);
4184c87aefeSPatrick Mooney 	}
419a1d41cf9SPatrick Mooney }
4204c87aefeSPatrick Mooney 
421a1d41cf9SPatrick Mooney /* Parse hour format from CMOS, accounting for any BCD and 12/24hr encoding */
422a1d41cf9SPatrick Mooney static uint8_t
rtc_parse_hour(const struct rtcdev * rtc,uint8_t hour,bool * errp)423a1d41cf9SPatrick Mooney rtc_parse_hour(const struct rtcdev *rtc, uint8_t hour, bool *errp)
424a1d41cf9SPatrick Mooney {
425a1d41cf9SPatrick Mooney 	bool pm = false;
4264c87aefeSPatrick Mooney 
4274c87aefeSPatrick Mooney 	if ((rtc->reg_b & RTCSB_24HR) == 0) {
428a1d41cf9SPatrick Mooney 		if ((hour & HOUR_IS_PM) != 0) {
429a1d41cf9SPatrick Mooney 			hour &= ~HOUR_IS_PM;
430a1d41cf9SPatrick Mooney 			pm = true;
4314c87aefeSPatrick Mooney 		}
4324c87aefeSPatrick Mooney 	}
433a1d41cf9SPatrick Mooney 	hour = rtc_dec(rtc, hour, errp);
434a1d41cf9SPatrick Mooney 
4354c87aefeSPatrick Mooney 	if ((rtc->reg_b & RTCSB_24HR) == 0) {
436a1d41cf9SPatrick Mooney 		if (hour >= 1 && hour <= 12) {
4374c87aefeSPatrick Mooney 			/*
4384c87aefeSPatrick Mooney 			 * Convert from 12-hour format to internal 24-hour
4394c87aefeSPatrick Mooney 			 * representation as follows:
4404c87aefeSPatrick Mooney 			 *
4414c87aefeSPatrick Mooney 			 *    12-hour format		ct.hour
4424c87aefeSPatrick Mooney 			 *	12	AM		0
4434c87aefeSPatrick Mooney 			 *	1 - 11	AM		1 - 11
4444c87aefeSPatrick Mooney 			 *	12	PM		12
4454c87aefeSPatrick Mooney 			 *	1 - 11	PM		13 - 23
4464c87aefeSPatrick Mooney 			 */
447a1d41cf9SPatrick Mooney 			if (hour == 12) {
448a1d41cf9SPatrick Mooney 				hour = 0;
449a1d41cf9SPatrick Mooney 			}
450a1d41cf9SPatrick Mooney 			if (pm) {
451a1d41cf9SPatrick Mooney 				hour += 12;
452a1d41cf9SPatrick Mooney 			}
4534c87aefeSPatrick Mooney 		} else {
4549dc804b9SPatrick Mooney 			/* invalid RTC 12-hour format */
455a1d41cf9SPatrick Mooney 			*errp = true;
456a1d41cf9SPatrick Mooney 		}
457a1d41cf9SPatrick Mooney 	}
458a1d41cf9SPatrick Mooney 
459a1d41cf9SPatrick Mooney 	if (hour > 23) {
460a1d41cf9SPatrick Mooney 		*errp = true;
461a1d41cf9SPatrick Mooney 	}
462a1d41cf9SPatrick Mooney 
463a1d41cf9SPatrick Mooney 	return (hour);
464a1d41cf9SPatrick Mooney }
465a1d41cf9SPatrick Mooney 
466a1d41cf9SPatrick Mooney /* Check if alarm fields in CMOS are valid. */
467a1d41cf9SPatrick Mooney static bool
vrtc_alarm_valid(const struct vrtc * vrtc)468a1d41cf9SPatrick Mooney vrtc_alarm_valid(const struct vrtc *vrtc)
469a1d41cf9SPatrick Mooney {
470a1d41cf9SPatrick Mooney 	const struct rtcdev *rtc = &vrtc->rtcdev;
471a1d41cf9SPatrick Mooney 	bool err;
472a1d41cf9SPatrick Mooney 	uint8_t val;
473a1d41cf9SPatrick Mooney 
474a1d41cf9SPatrick Mooney 	ASSERT(VRTC_LOCKED(vrtc));
475a1d41cf9SPatrick Mooney 
476a1d41cf9SPatrick Mooney 	/*
477a1d41cf9SPatrick Mooney 	 * For seconds, minutes, and hours fields of the alarm configuration,
478a1d41cf9SPatrick Mooney 	 * check that they can match against valid times, either by matching any
479a1d41cf9SPatrick Mooney 	 * value via the "don't care" mode, or holding a valid time component.
480a1d41cf9SPatrick Mooney 	 */
481a1d41cf9SPatrick Mooney 
482a1d41cf9SPatrick Mooney 	val = rtc->sec;
483a1d41cf9SPatrick Mooney 	if (!ALARM_DONT_CARE(val)) {
484a1d41cf9SPatrick Mooney 		val = rtc_dec(rtc, val, &err);
485a1d41cf9SPatrick Mooney 		if (err || val > 59) {
486a1d41cf9SPatrick Mooney 			return (false);
487a1d41cf9SPatrick Mooney 		}
488a1d41cf9SPatrick Mooney 	}
489a1d41cf9SPatrick Mooney 
490a1d41cf9SPatrick Mooney 	val = rtc->min;
491a1d41cf9SPatrick Mooney 	if (!ALARM_DONT_CARE(val)) {
492a1d41cf9SPatrick Mooney 		val = rtc_dec(rtc, val, &err);
493a1d41cf9SPatrick Mooney 		if (err || val > 59) {
494a1d41cf9SPatrick Mooney 			return (false);
4954c87aefeSPatrick Mooney 		}
4964c87aefeSPatrick Mooney 	}
4974c87aefeSPatrick Mooney 
498a1d41cf9SPatrick Mooney 	val = rtc->hour;
499a1d41cf9SPatrick Mooney 	if (!ALARM_DONT_CARE(val)) {
500a1d41cf9SPatrick Mooney 		(void) rtc_parse_hour(rtc, val, &err);
501a1d41cf9SPatrick Mooney 		if (err) {
502a1d41cf9SPatrick Mooney 			return (false);
503a1d41cf9SPatrick Mooney 		}
504a1d41cf9SPatrick Mooney 	}
505a1d41cf9SPatrick Mooney 
506a1d41cf9SPatrick Mooney 	/*
507a1d41cf9SPatrick Mooney 	 * The alarm fields hold a valid time representation, taking into
508a1d41cf9SPatrick Mooney 	 * consideration any potential "don't care" directives.
509a1d41cf9SPatrick Mooney 	 */
510a1d41cf9SPatrick Mooney 	return (true);
511a1d41cf9SPatrick Mooney }
512a1d41cf9SPatrick Mooney 
513a1d41cf9SPatrick Mooney /*
514a1d41cf9SPatrick Mooney  * Read the date/time fields from the CMOS and attempt to convert it to a valid
515a1d41cf9SPatrick Mooney  * UNIX timestamp.  VRTC_BROKEN_TIME will be emitted if those fields represent
516a1d41cf9SPatrick Mooney  * an invalid date.
517a1d41cf9SPatrick Mooney  *
518a1d41cf9SPatrick Mooney  * The day-of-week field is ignored for the purposes of validation since certain
519a1d41cf9SPatrick Mooney  * guests do not make use of it.
520a1d41cf9SPatrick Mooney  */
521a1d41cf9SPatrick Mooney static time_t
vrtc_cmos_to_secs(struct vrtc * vrtc)522a1d41cf9SPatrick Mooney vrtc_cmos_to_secs(struct vrtc *vrtc)
523a1d41cf9SPatrick Mooney {
524a1d41cf9SPatrick Mooney 	struct rtcdev *rtc = &vrtc->rtcdev;
525a1d41cf9SPatrick Mooney 	struct clocktime ct = { 0 };
526a1d41cf9SPatrick Mooney 	bool err;
527a1d41cf9SPatrick Mooney 
528a1d41cf9SPatrick Mooney 	ASSERT(VRTC_LOCKED(vrtc));
529a1d41cf9SPatrick Mooney 
530a1d41cf9SPatrick Mooney 	ct.sec = rtc_dec(rtc, rtc->sec, &err);
531a1d41cf9SPatrick Mooney 	if (err || ct.sec > 59) {
532a1d41cf9SPatrick Mooney 		/* invalid RTC seconds */
533a1d41cf9SPatrick Mooney 		goto fail;
534a1d41cf9SPatrick Mooney 	}
535a1d41cf9SPatrick Mooney 
536a1d41cf9SPatrick Mooney 	ct.min = rtc_dec(rtc, rtc->min, &err);
537a1d41cf9SPatrick Mooney 	if (err || ct.min > 59) {
538a1d41cf9SPatrick Mooney 		/* invalid RTC minutes */
539a1d41cf9SPatrick Mooney 		goto fail;
540a1d41cf9SPatrick Mooney 	}
541a1d41cf9SPatrick Mooney 
542a1d41cf9SPatrick Mooney 	ct.hour = rtc_parse_hour(rtc, rtc->hour, &err);
543a1d41cf9SPatrick Mooney 	if (err) {
5449dc804b9SPatrick Mooney 		/* invalid RTC hour */
5454c87aefeSPatrick Mooney 		goto fail;
5464c87aefeSPatrick Mooney 	}
5474c87aefeSPatrick Mooney 
5484c87aefeSPatrick Mooney 	/*
5494c87aefeSPatrick Mooney 	 * Ignore 'rtc->dow' because some guests like Linux don't bother
55084971882SPatrick Mooney 	 * setting it at all while others like OpenBSD/i386 set it incorrectly.
5514c87aefeSPatrick Mooney 	 *
5524c87aefeSPatrick Mooney 	 * clock_ct_to_ts() does not depend on 'ct.dow' anyways so ignore it.
5534c87aefeSPatrick Mooney 	 */
5544c87aefeSPatrick Mooney 	ct.dow = -1;
5554c87aefeSPatrick Mooney 
556a1d41cf9SPatrick Mooney 	ct.day = rtc_dec(rtc, rtc->day_of_month, &err);
557a1d41cf9SPatrick Mooney 	if (err || ct.day < 1 || ct.day > 31) {
558a1d41cf9SPatrick Mooney 		/* invalid RTC day-of-month */
5594c87aefeSPatrick Mooney 		goto fail;
5604c87aefeSPatrick Mooney 	}
5614c87aefeSPatrick Mooney 
562a1d41cf9SPatrick Mooney 	ct.mon = rtc_dec(rtc, rtc->month, &err);
563a1d41cf9SPatrick Mooney 	if (err || ct.mon < 1 || ct.mon > 12) {
5649dc804b9SPatrick Mooney 		/* invalid RTC month */
5654c87aefeSPatrick Mooney 		goto fail;
5664c87aefeSPatrick Mooney 	}
5674c87aefeSPatrick Mooney 
568a1d41cf9SPatrick Mooney 	const uint_t year = rtc_dec(rtc, rtc->year, &err);
569a1d41cf9SPatrick Mooney 	if (err || year > 99) {
5709dc804b9SPatrick Mooney 		/* invalid RTC year */
5714c87aefeSPatrick Mooney 		goto fail;
5724c87aefeSPatrick Mooney 	}
5734c87aefeSPatrick Mooney 
574a1d41cf9SPatrick Mooney 	const uint_t century = rtc_dec(rtc, rtc->century, &err);
5754c87aefeSPatrick Mooney 	ct.year = century * 100 + year;
576a1d41cf9SPatrick Mooney 	if (err || ct.year < POSIX_BASE_YEAR) {
5779dc804b9SPatrick Mooney 		/* invalid RTC century */
5784c87aefeSPatrick Mooney 		goto fail;
5794c87aefeSPatrick Mooney 	}
5804c87aefeSPatrick Mooney 
581a1d41cf9SPatrick Mooney 	struct timespec ts;
582a1d41cf9SPatrick Mooney 	if (clock_ct_to_ts(&ct, &ts) != 0 || ts.tv_sec < 0) {
5839dc804b9SPatrick Mooney 		/* invalid RTC clocktime */
5844c87aefeSPatrick Mooney 		goto fail;
5854c87aefeSPatrick Mooney 	}
5864c87aefeSPatrick Mooney 	return (ts.tv_sec);		/* success */
587a1d41cf9SPatrick Mooney 
5884c87aefeSPatrick Mooney fail:
5894c87aefeSPatrick Mooney 	/*
5904c87aefeSPatrick Mooney 	 * Stop updating the RTC if the date/time fields programmed by
5914c87aefeSPatrick Mooney 	 * the guest are invalid.
5924c87aefeSPatrick Mooney 	 */
5934c87aefeSPatrick Mooney 	return (VRTC_BROKEN_TIME);
5944c87aefeSPatrick Mooney }
5954c87aefeSPatrick Mooney 
596a1d41cf9SPatrick Mooney /*
597a1d41cf9SPatrick Mooney  * If the periodic timer is enabled, check if enough time has passed for it to
598a1d41cf9SPatrick Mooney  * generate an event.
599a1d41cf9SPatrick Mooney  */
600a1d41cf9SPatrick Mooney static void
vrtc_periodic_update(struct vrtc * vrtc)601a1d41cf9SPatrick Mooney vrtc_periodic_update(struct vrtc *vrtc)
6024c87aefeSPatrick Mooney {
603a1d41cf9SPatrick Mooney 	struct rtcdev *rtc = &vrtc->rtcdev;
6044c87aefeSPatrick Mooney 
605fb29dee0SPatrick Mooney 	ASSERT(VRTC_LOCKED(vrtc));
6064c87aefeSPatrick Mooney 
6074c87aefeSPatrick Mooney 	/*
608a1d41cf9SPatrick Mooney 	 * If the divider is disabled, or periodic interrupts are not
609a1d41cf9SPatrick Mooney 	 * configured, then no further work is required.
6104c87aefeSPatrick Mooney 	 */
611a1d41cf9SPatrick Mooney 	const hrtime_t period = rega_period(rtc->reg_a);
612a1d41cf9SPatrick Mooney 	if (!rega_divider_en(rtc->reg_a) || period == 0) {
613a1d41cf9SPatrick Mooney 		return;
6144c87aefeSPatrick Mooney 	}
6154c87aefeSPatrick Mooney 
6164c87aefeSPatrick Mooney 	/*
617a1d41cf9SPatrick Mooney 	 * Have we crossed the edge of a period-sized time interval since the
618a1d41cf9SPatrick Mooney 	 * last periodic event?
6194c87aefeSPatrick Mooney 	 */
620a1d41cf9SPatrick Mooney 	hrtime_t since_last = gethrtime() - vrtc->last_period;
621a1d41cf9SPatrick Mooney 	if (since_last > period) {
622a1d41cf9SPatrick Mooney 		vrtc_regc_update(vrtc, RTCIR_PERIOD);
6230c663092SPatrick Mooney 		vrtc->last_period += ROUNDDOWN(since_last, period);
6244c87aefeSPatrick Mooney 	}
6254c87aefeSPatrick Mooney }
6264c87aefeSPatrick Mooney 
627a1d41cf9SPatrick Mooney /*
628a1d41cf9SPatrick Mooney  * Update the internal contents of the RTC.  This processes any events which may
629a1d41cf9SPatrick Mooney  * have been generated by the passage of time (update/periodic/alarm), resulting
630a1d41cf9SPatrick Mooney  * in updates to register-C.  As part of that, it updates the internal time
631a1d41cf9SPatrick Mooney  * representation of the RTC, but is not required to render those changes (if
6320c663092SPatrick Mooney  * any) to the CMOS memory.  A separate call to vrtc_time_to_cmos() is needed if
633a1d41cf9SPatrick Mooney  * those fields are about to be accessed.
634a1d41cf9SPatrick Mooney  */
635a1d41cf9SPatrick Mooney static void
vrtc_update(struct vrtc * vrtc,uint8_t off)636a1d41cf9SPatrick Mooney vrtc_update(struct vrtc *vrtc, uint8_t off)
6374c87aefeSPatrick Mooney {
638a1d41cf9SPatrick Mooney 	struct rtcdev *rtc = &vrtc->rtcdev;
6394c87aefeSPatrick Mooney 
640fb29dee0SPatrick Mooney 	ASSERT(VRTC_LOCKED(vrtc));
6414c87aefeSPatrick Mooney 
6424c87aefeSPatrick Mooney 	/*
643a1d41cf9SPatrick Mooney 	 * If CMOS offset of interest is not one which is updated on-demand,
644a1d41cf9SPatrick Mooney 	 * then no update processing is required.
6454c87aefeSPatrick Mooney 	 */
646a1d41cf9SPatrick Mooney 	if (!rtc_field_ondemand(off)) {
647a1d41cf9SPatrick Mooney 		return;
6484c87aefeSPatrick Mooney 	}
6494c87aefeSPatrick Mooney 
650a1d41cf9SPatrick Mooney 	/*
651a1d41cf9SPatrick Mooney 	 * If the divider output is disabled, no events will be generated, and
652a1d41cf9SPatrick Mooney 	 * the time will not be updated.
653a1d41cf9SPatrick Mooney 	 */
654a1d41cf9SPatrick Mooney 	if (!rega_divider_en(rtc->reg_a)) {
655a1d41cf9SPatrick Mooney 		return;
656a1d41cf9SPatrick Mooney 	}
6574c87aefeSPatrick Mooney 
658a1d41cf9SPatrick Mooney 	/* Check for any periodic timer events requiring injection. */
659a1d41cf9SPatrick Mooney 	vrtc_periodic_update(vrtc);
6604c87aefeSPatrick Mooney 
661a1d41cf9SPatrick Mooney 	if (vrtc->base_rtctime == VRTC_BROKEN_TIME) {
662a1d41cf9SPatrick Mooney 		/*
663a1d41cf9SPatrick Mooney 		 * If the RTC is halted, or the time stored in CMOS is invalid,
664a1d41cf9SPatrick Mooney 		 * then neither alarm checks nor updates to the time stored in
665a1d41cf9SPatrick Mooney 		 * CMOS are performed.
666a1d41cf9SPatrick Mooney 		 */
667a1d41cf9SPatrick Mooney 		return;
668a1d41cf9SPatrick Mooney 	}
669a1d41cf9SPatrick Mooney 
670a1d41cf9SPatrick Mooney 	/*
671a1d41cf9SPatrick Mooney 	 * Calculate the new time and its corresponding second-granularity clock
672a1d41cf9SPatrick Mooney 	 * edge from the divider for base_clock.
673a1d41cf9SPatrick Mooney 	 */
674a1d41cf9SPatrick Mooney 	hrtime_t base_clock;
675a1d41cf9SPatrick Mooney 	const time_t newtime = vrtc_curtime(vrtc, &base_clock, NULL);
676a1d41cf9SPatrick Mooney 	if (vrtc->base_rtctime >= newtime) {
677a1d41cf9SPatrick Mooney 		/* Nothing more to do if the actual time is unchanged */
6784c87aefeSPatrick Mooney 		return;
6794c87aefeSPatrick Mooney 	}
680a1d41cf9SPatrick Mooney 	vrtc->base_clock = base_clock;
681a1d41cf9SPatrick Mooney 
682a1d41cf9SPatrick Mooney 	if (!vrtc_alarm_valid(vrtc) || (rtc->reg_c & RTCIR_ALARM) != 0) {
683a1d41cf9SPatrick Mooney 		/*
684a1d41cf9SPatrick Mooney 		 * If no valid alarm is configured, or the alarm event is
685a1d41cf9SPatrick Mooney 		 * already pending, there is no need to match the RTC time
686a1d41cf9SPatrick Mooney 		 * against it, since any additional assertion will be redundant
687a1d41cf9SPatrick Mooney 		 * until the flag is read/cleared.
688a1d41cf9SPatrick Mooney 		 */
689a1d41cf9SPatrick Mooney 		vrtc->base_rtctime = newtime;
690a1d41cf9SPatrick Mooney 	} else if ((newtime - vrtc->base_rtctime) >= SEC_PER_DAY) {
691a1d41cf9SPatrick Mooney 		/*
692a1d41cf9SPatrick Mooney 		 * If 24 hours (or more) has elapsed since the last update, the
693a1d41cf9SPatrick Mooney 		 * configured alarm is certain to fire.  Rather than spending
694a1d41cf9SPatrick Mooney 		 * considerable effort in the full matching logic in order to
695a1d41cf9SPatrick Mooney 		 * determine this certainty, just apply it now as a shortcut.
696a1d41cf9SPatrick Mooney 		 */
697a1d41cf9SPatrick Mooney 		vrtc_regc_update(vrtc, RTCIR_ALARM);
698a1d41cf9SPatrick Mooney 		vrtc->base_rtctime = newtime;
699a1d41cf9SPatrick Mooney 	} else {
700a1d41cf9SPatrick Mooney 		/*
701a1d41cf9SPatrick Mooney 		 * Check if any of the times (down to the second) between the
702a1d41cf9SPatrick Mooney 		 * old time and the new match against a configured alarm
703a1d41cf9SPatrick Mooney 		 * condition.
704a1d41cf9SPatrick Mooney 		 *
705a1d41cf9SPatrick Mooney 		 * This is not insignificant effort and could stand to be
706a1d41cf9SPatrick Mooney 		 * optimized at some point in the future.
707a1d41cf9SPatrick Mooney 		 */
708a1d41cf9SPatrick Mooney 		const uint8_t a_sec = rtc->alarm_sec;
709a1d41cf9SPatrick Mooney 		const uint8_t a_min = rtc->alarm_min;
710a1d41cf9SPatrick Mooney 		const uint8_t a_hour = rtc->alarm_hour;
711a1d41cf9SPatrick Mooney 		do {
712a1d41cf9SPatrick Mooney 			vrtc->base_rtctime++;
713a1d41cf9SPatrick Mooney 			vrtc_time_to_cmos(vrtc, false);
714a1d41cf9SPatrick Mooney 
715a1d41cf9SPatrick Mooney 			if ((ALARM_DONT_CARE(a_sec) || a_sec == rtc->sec) &&
716a1d41cf9SPatrick Mooney 			    (ALARM_DONT_CARE(a_min) || a_min == rtc->min) &&
717a1d41cf9SPatrick Mooney 			    (ALARM_DONT_CARE(a_hour) || a_hour == rtc->hour)) {
718a1d41cf9SPatrick Mooney 				vrtc_regc_update(vrtc, RTCIR_ALARM);
719a1d41cf9SPatrick Mooney 				/*
720a1d41cf9SPatrick Mooney 				 * Once the alarm triggers during this check, we
721a1d41cf9SPatrick Mooney 				 * can skip to the end, since subsequent firings
722a1d41cf9SPatrick Mooney 				 * would be redundant until the guest can
723a1d41cf9SPatrick Mooney 				 * read/clear the event in register-C.
724a1d41cf9SPatrick Mooney 				 */
725a1d41cf9SPatrick Mooney 				vrtc->base_rtctime = newtime;
726a1d41cf9SPatrick Mooney 			}
727a1d41cf9SPatrick Mooney 		} while (vrtc->base_rtctime != newtime);
728a1d41cf9SPatrick Mooney 	}
729a1d41cf9SPatrick Mooney 
730a1d41cf9SPatrick Mooney 	/* Reflect that the time underwent an update */
731a1d41cf9SPatrick Mooney 	vrtc_regc_update(vrtc, RTCIR_UPDATE);
7324c87aefeSPatrick Mooney }
7334c87aefeSPatrick Mooney 
7344c87aefeSPatrick Mooney static void
vrtc_callout_handler(void * arg)7354c87aefeSPatrick Mooney vrtc_callout_handler(void *arg)
7364c87aefeSPatrick Mooney {
7374c87aefeSPatrick Mooney 	struct vrtc *vrtc = arg;
7384c87aefeSPatrick Mooney 
7394c87aefeSPatrick Mooney 	VRTC_LOCK(vrtc);
740a1d41cf9SPatrick Mooney 	if (callout_pending(&vrtc->callout)) {
741a1d41cf9SPatrick Mooney 		/* callout was reset */
742a1d41cf9SPatrick Mooney 	} else if (!callout_active(&vrtc->callout)) {
743a1d41cf9SPatrick Mooney 		/* callout was stopped */
744a1d41cf9SPatrick Mooney 	} else {
745a1d41cf9SPatrick Mooney 		callout_deactivate(&vrtc->callout);
7464c87aefeSPatrick Mooney 
747a1d41cf9SPatrick Mooney 		/* Perform the actual update and reschedule (if needed) */
748a1d41cf9SPatrick Mooney 		vrtc_update(vrtc, RTC_INTR);
749a1d41cf9SPatrick Mooney 		vrtc_callout_reschedule(vrtc);
750a1d41cf9SPatrick Mooney 	}
751a1d41cf9SPatrick Mooney 	VRTC_UNLOCK(vrtc);
752a1d41cf9SPatrick Mooney }
7534c87aefeSPatrick Mooney 
754a1d41cf9SPatrick Mooney static void
vrtc_callout_reschedule(struct vrtc * vrtc)755a1d41cf9SPatrick Mooney vrtc_callout_reschedule(struct vrtc *vrtc)
756a1d41cf9SPatrick Mooney {
757a1d41cf9SPatrick Mooney 	struct rtcdev *rtc = &vrtc->rtcdev;
7584c87aefeSPatrick Mooney 
759a1d41cf9SPatrick Mooney 	ASSERT(VRTC_LOCKED(vrtc));
7604c87aefeSPatrick Mooney 
761a1d41cf9SPatrick Mooney 	hrtime_t period = 0;
762a1d41cf9SPatrick Mooney 	if ((rtc->reg_b & RTCSB_PINTR) != 0) {
763a1d41cf9SPatrick Mooney 		/*
764a1d41cf9SPatrick Mooney 		 * Calculate the next event edge using the periodic timer, since
765a1d41cf9SPatrick Mooney 		 * it will be more granular (2Hz or faster) than the 1Hz used by
766a1d41cf9SPatrick Mooney 		 * the alarm and update interrupts, and still in phase.
767a1d41cf9SPatrick Mooney 		 */
768a1d41cf9SPatrick Mooney 		period = rega_period(rtc->reg_a);
769a1d41cf9SPatrick Mooney 	}
770a1d41cf9SPatrick Mooney 	if (period == 0 && vrtc_update_enabled(vrtc)) {
771a1d41cf9SPatrick Mooney 		/*
772a1d41cf9SPatrick Mooney 		 * If RTC updates are enabled, there is potential for update or
773a1d41cf9SPatrick Mooney 		 * alarm interrupts on 1Hz intervals.
774a1d41cf9SPatrick Mooney 		 */
775a1d41cf9SPatrick Mooney 		period = NANOSEC;
776a1d41cf9SPatrick Mooney 	}
7774c87aefeSPatrick Mooney 
778a1d41cf9SPatrick Mooney 	/*
779a1d41cf9SPatrick Mooney 	 * RTC callouts are only required if interrupts are enabled, since all
780a1d41cf9SPatrick Mooney 	 * other side effects of time moving forward (such as setting of the
781a1d41cf9SPatrick Mooney 	 * event bits in register-C) can be conjured on-demand when those fields
782a1d41cf9SPatrick Mooney 	 * are read by the guest.  The same is true when an interrupt has been
783a1d41cf9SPatrick Mooney 	 * asserted and not yet handled.
784a1d41cf9SPatrick Mooney 	 */
785a1d41cf9SPatrick Mooney 	const bool intr_enabled = (rtc->reg_b & RTCSB_INTR_MASK) != 0;
786a1d41cf9SPatrick Mooney 	const bool intr_asserted = (rtc->reg_c & RTCIR_INT) != 0;
787a1d41cf9SPatrick Mooney 	if (period != 0 && intr_enabled && !intr_asserted) {
788a1d41cf9SPatrick Mooney 		/*
789a1d41cf9SPatrick Mooney 		 * Find the next edge of the specified period interval,
790a1d41cf9SPatrick Mooney 		 * referenced against the phase of base_clock.
791a1d41cf9SPatrick Mooney 		 */
792a1d41cf9SPatrick Mooney 		const hrtime_t delta = gethrtime() + period - vrtc->base_clock;
793a1d41cf9SPatrick Mooney 		const hrtime_t next =
794a1d41cf9SPatrick Mooney 		    ROUNDDOWN(delta, period) + vrtc->base_clock;
7955103e761SPatrick Mooney 
796a1d41cf9SPatrick Mooney 		callout_reset_hrtime(&vrtc->callout, next, vrtc_callout_handler,
797a1d41cf9SPatrick Mooney 		    vrtc, C_ABSOLUTE);
798a1d41cf9SPatrick Mooney 	} else {
799a1d41cf9SPatrick Mooney 		if (callout_active(&vrtc->callout)) {
800a1d41cf9SPatrick Mooney 			callout_stop(&vrtc->callout);
801a1d41cf9SPatrick Mooney 		}
8024c87aefeSPatrick Mooney 	}
8034c87aefeSPatrick Mooney }
8044c87aefeSPatrick Mooney 
805a1d41cf9SPatrick Mooney /*
806a1d41cf9SPatrick Mooney  * We can take some shortcuts in the register-B/register-C math since the
807a1d41cf9SPatrick Mooney  * interrupt-enable bits match their corresponding interrupt-present bits.
808a1d41cf9SPatrick Mooney  */
809a1d41cf9SPatrick Mooney CTASSERT(RTCIR_UPDATE == RTCSB_UINTR);
810a1d41cf9SPatrick Mooney CTASSERT(RTCIR_ALARM == RTCSB_AINTR);
811a1d41cf9SPatrick Mooney CTASSERT(RTCIR_PERIOD == RTCSB_PINTR);
8124c87aefeSPatrick Mooney 
813a1d41cf9SPatrick Mooney /*
814a1d41cf9SPatrick Mooney  * Update the contents of register-C either due to newly asserted events, or
815a1d41cf9SPatrick Mooney  * altered interrupt-enable flags.
816a1d41cf9SPatrick Mooney  */
8174c87aefeSPatrick Mooney static void
vrtc_regc_update(struct vrtc * vrtc,uint8_t events)818a1d41cf9SPatrick Mooney vrtc_regc_update(struct vrtc *vrtc, uint8_t events)
8194c87aefeSPatrick Mooney {
820a1d41cf9SPatrick Mooney 	struct rtcdev *rtc = &vrtc->rtcdev;
8214c87aefeSPatrick Mooney 
822fb29dee0SPatrick Mooney 	ASSERT(VRTC_LOCKED(vrtc));
823a1d41cf9SPatrick Mooney 	ASSERT0(events & ~(RTCSB_INTR_MASK));
8244c87aefeSPatrick Mooney 
825a1d41cf9SPatrick Mooney 	/*
826a1d41cf9SPatrick Mooney 	 * Regardless of which interrupt enable flags are set in register-B, the
827a1d41cf9SPatrick Mooney 	 * corresponding event flags are always set in register-C.
828a1d41cf9SPatrick Mooney 	 */
829a1d41cf9SPatrick Mooney 	rtc->reg_c |= events;
8304c87aefeSPatrick Mooney 
831a1d41cf9SPatrick Mooney 	const bool oldirq = (rtc->reg_c & RTCIR_INT) != 0;
832a1d41cf9SPatrick Mooney 	if ((rtc->reg_b & RTCSB_INTR_MASK & rtc->reg_c) != 0) {
833a1d41cf9SPatrick Mooney 		rtc->reg_c |= RTCIR_INT;
8344c87aefeSPatrick Mooney 	}
835a1d41cf9SPatrick Mooney 	const bool newirq = (rtc->reg_c & RTCIR_INT) != 0;
8364c87aefeSPatrick Mooney 
837a1d41cf9SPatrick Mooney 	/*
838a1d41cf9SPatrick Mooney 	 * Although this should probably be asserting level-triggered interrupt,
839a1d41cf9SPatrick Mooney 	 * the original logic from bhyve is event-triggered.  This may warrant
840a1d41cf9SPatrick Mooney 	 * additional consideration at some point.
841a1d41cf9SPatrick Mooney 	 */
842a1d41cf9SPatrick Mooney 	if (!oldirq && newirq) {
843d4f59ae5SPatrick Mooney 		/* IRQ asserted */
844e0994bd2SPatrick Mooney 		(void) vatpic_pulse_irq(vrtc->vm, RTC_IRQ);
845e0994bd2SPatrick Mooney 		(void) vioapic_pulse_irq(vrtc->vm, RTC_IRQ);
846a1d41cf9SPatrick Mooney 	} else if (oldirq && !newirq) {
847d4f59ae5SPatrick Mooney 		/* IRQ de-asserted */
8484c87aefeSPatrick Mooney 	}
8494c87aefeSPatrick Mooney }
8504c87aefeSPatrick Mooney 
851a1d41cf9SPatrick Mooney /*
852a1d41cf9SPatrick Mooney  * Emulate a read of register-C, emitting the contained value and clearing its
853a1d41cf9SPatrick Mooney  * contents for subsequent actions.
854a1d41cf9SPatrick Mooney  */
855a1d41cf9SPatrick Mooney static uint8_t
vrtc_regc_read(struct vrtc * vrtc)856a1d41cf9SPatrick Mooney vrtc_regc_read(struct vrtc *vrtc)
8574c87aefeSPatrick Mooney {
858a1d41cf9SPatrick Mooney 	struct rtcdev *rtc = &vrtc->rtcdev;
8594c87aefeSPatrick Mooney 
860fb29dee0SPatrick Mooney 	ASSERT(VRTC_LOCKED(vrtc));
8614c87aefeSPatrick Mooney 
862a1d41cf9SPatrick Mooney 	/* Clear the IRQ flag, and any asserted events */
863a1d41cf9SPatrick Mooney 	const uint8_t val = rtc->reg_c;
864a1d41cf9SPatrick Mooney 	rtc->reg_c = 0;
8654c87aefeSPatrick Mooney 
8667c224ea6SPatrick Mooney 	/*
8677c224ea6SPatrick Mooney 	 * Since callout scheduling is suppressed when the IRQ flag is asserted,
8687c224ea6SPatrick Mooney 	 * it may need to be re-scheduled when the flag is read/cleared.
8697c224ea6SPatrick Mooney 	 */
8707c224ea6SPatrick Mooney 	if ((val & RTCIR_INT) != 0) {
8717c224ea6SPatrick Mooney 		vrtc_callout_reschedule(vrtc);
8727c224ea6SPatrick Mooney 	}
8737c224ea6SPatrick Mooney 
874a1d41cf9SPatrick Mooney 	return (val);
875a1d41cf9SPatrick Mooney }
876a1d41cf9SPatrick Mooney 
877a1d41cf9SPatrick Mooney static void
vrtc_regb_write(struct vrtc * vrtc,uint8_t newval)878a1d41cf9SPatrick Mooney vrtc_regb_write(struct vrtc *vrtc, uint8_t newval)
879a1d41cf9SPatrick Mooney {
880a1d41cf9SPatrick Mooney 	struct rtcdev *rtc = &vrtc->rtcdev;
881a1d41cf9SPatrick Mooney 
882a1d41cf9SPatrick Mooney 	ASSERT(VRTC_LOCKED(vrtc));
883a1d41cf9SPatrick Mooney 
884a1d41cf9SPatrick Mooney 	uint8_t changed = rtc->reg_b ^ newval;
8854c87aefeSPatrick Mooney 	rtc->reg_b = newval;
8864c87aefeSPatrick Mooney 
8874c87aefeSPatrick Mooney 	if (changed & RTCSB_HALT) {
8884c87aefeSPatrick Mooney 		if ((newval & RTCSB_HALT) == 0) {
889a1d41cf9SPatrick Mooney 			/*
890a1d41cf9SPatrick Mooney 			 * RTC is coming out of a halted state.
891a1d41cf9SPatrick Mooney 			 *
892a1d41cf9SPatrick Mooney 			 * Push the base time (the clock from the divider)
893a1d41cf9SPatrick Mooney 			 * forward to the nearest second boundary so it may
894a1d41cf9SPatrick Mooney 			 * resume updates from the value set in the CMOS.
895a1d41cf9SPatrick Mooney 			 */
896a1d41cf9SPatrick Mooney 			vrtc->base_rtctime = vrtc_cmos_to_secs(vrtc);
897a1d41cf9SPatrick Mooney 
898a1d41cf9SPatrick Mooney 			/*
899a1d41cf9SPatrick Mooney 			 * Account for any time which has passed if the divider
900a1d41cf9SPatrick Mooney 			 * was left running while the RTC was in the halted
901a1d41cf9SPatrick Mooney 			 * state.  Any whole seconds which elapsed while the
902a1d41cf9SPatrick Mooney 			 * device was in such a state must be discarded.
903a1d41cf9SPatrick Mooney 			 *
904a1d41cf9SPatrick Mooney 			 * If this was not done, the RTC would play "catch-up"
905a1d41cf9SPatrick Mooney 			 * since the last update as recorded in `base_clock`.
906a1d41cf9SPatrick Mooney 			 * The phase of that clock is preserved, even if the
907a1d41cf9SPatrick Mooney 			 * time itself is discarded.
908a1d41cf9SPatrick Mooney 			 */
909a1d41cf9SPatrick Mooney 			if (rega_divider_en(vrtc->rtcdev.reg_a)) {
910a1d41cf9SPatrick Mooney 				const hrtime_t delta =
911a1d41cf9SPatrick Mooney 				    gethrtime() - vrtc->base_clock;
912a1d41cf9SPatrick Mooney 
913a1d41cf9SPatrick Mooney 				if (delta > NANOSEC) {
914a1d41cf9SPatrick Mooney 					vrtc->base_clock += delta / NANOSEC;
915a1d41cf9SPatrick Mooney 				}
916a1d41cf9SPatrick Mooney 			} else {
917a1d41cf9SPatrick Mooney 				/*
918a1d41cf9SPatrick Mooney 				 * If the divider is not running, then all of
919a1d41cf9SPatrick Mooney 				 * this will be taken care of if/when it is
920a1d41cf9SPatrick Mooney 				 * re-enabled by the guest.
921a1d41cf9SPatrick Mooney 				 */
9224c87aefeSPatrick Mooney 			}
9234c87aefeSPatrick Mooney 		} else {
9244c87aefeSPatrick Mooney 			/*
9254c87aefeSPatrick Mooney 			 * Force a refresh of the RTC date/time fields so
9264c87aefeSPatrick Mooney 			 * they reflect the time right before the guest set
9274c87aefeSPatrick Mooney 			 * the HALT bit.
9284c87aefeSPatrick Mooney 			 */
929a1d41cf9SPatrick Mooney 			vrtc_update(vrtc, RTC_STATUSB);
930a1d41cf9SPatrick Mooney 			vrtc_time_to_cmos(vrtc, true);
9314c87aefeSPatrick Mooney 
9324c87aefeSPatrick Mooney 			/*
9334c87aefeSPatrick Mooney 			 * Updates are halted so mark 'base_rtctime' to denote
9344c87aefeSPatrick Mooney 			 * that the RTC date/time is in flux.
935a1d41cf9SPatrick Mooney 			 *
936a1d41cf9SPatrick Mooney 			 * Since the HALT/RUN flag does not effect the actual
937a1d41cf9SPatrick Mooney 			 * phase of the clock emitted from the emulated divider,
938a1d41cf9SPatrick Mooney 			 * the base time will remain unchanged
939a1d41cf9SPatrick Mooney 			 */
940a1d41cf9SPatrick Mooney 			vrtc->base_rtctime = VRTC_BROKEN_TIME;
941a1d41cf9SPatrick Mooney 
942a1d41cf9SPatrick Mooney 			/*
943a1d41cf9SPatrick Mooney 			 * Per the specification, the UINTR bit must be cleared
944a1d41cf9SPatrick Mooney 			 * if the HALT bit is set.
9454c87aefeSPatrick Mooney 			 */
946a1d41cf9SPatrick Mooney 			if ((rtc->reg_b & RTCSB_UINTR) != 0) {
947a1d41cf9SPatrick Mooney 				rtc->reg_b &= ~RTCSB_UINTR;
948a1d41cf9SPatrick Mooney 				changed |= RTCSB_UINTR;
949a1d41cf9SPatrick Mooney 			}
9504c87aefeSPatrick Mooney 		}
9514c87aefeSPatrick Mooney 	}
9524c87aefeSPatrick Mooney 
953a1d41cf9SPatrick Mooney 	/* Side effect of changes to the interrupt enable bits.  */
954a1d41cf9SPatrick Mooney 	if (changed & RTCSB_INTR_MASK) {
955a1d41cf9SPatrick Mooney 		vrtc_regc_update(vrtc, 0);
956a1d41cf9SPatrick Mooney 	}
9574c87aefeSPatrick Mooney 
958a1d41cf9SPatrick Mooney 	vrtc_callout_reschedule(vrtc);
9594c87aefeSPatrick Mooney 
9604c87aefeSPatrick Mooney 	/*
9614c87aefeSPatrick Mooney 	 * The side effect of bits that control the RTC date/time format
9624c87aefeSPatrick Mooney 	 * is handled lazily when those fields are actually read.
9634c87aefeSPatrick Mooney 	 */
9644c87aefeSPatrick Mooney }
9654c87aefeSPatrick Mooney 
9664c87aefeSPatrick Mooney static void
vrtc_rega_write(struct vrtc * vrtc,uint8_t newval)967a1d41cf9SPatrick Mooney vrtc_rega_write(struct vrtc *vrtc, uint8_t newval)
9684c87aefeSPatrick Mooney {
969fb29dee0SPatrick Mooney 	ASSERT(VRTC_LOCKED(vrtc));
9704c87aefeSPatrick Mooney 
9710c663092SPatrick Mooney 	const hrtime_t now = gethrtime();
972a1d41cf9SPatrick Mooney 	const uint8_t oldval = vrtc->rtcdev.reg_a;
9730c663092SPatrick Mooney 	bool divider_restarted = false;
9740c663092SPatrick Mooney 
975a1d41cf9SPatrick Mooney 	if (rega_divider_en(oldval) && !rega_divider_en(newval)) {
976d4f59ae5SPatrick Mooney 		/* RTC divider held in reset */
977a1d41cf9SPatrick Mooney 	} else if (!rega_divider_en(oldval) && rega_divider_en(newval)) {
9784c87aefeSPatrick Mooney 		/*
979a1d41cf9SPatrick Mooney 		 * Divider is coming out of reset.  Updates of the reported time
980a1d41cf9SPatrick Mooney 		 * (if enabled) are expected to begin 500ms from now.
9814c87aefeSPatrick Mooney 		 */
982a1d41cf9SPatrick Mooney 		vrtc->base_rtctime = vrtc_cmos_to_secs(vrtc);
9830c663092SPatrick Mooney 		vrtc->base_clock = now - (NANOSEC / 2);
9840c663092SPatrick Mooney 		divider_restarted = true;
9850c663092SPatrick Mooney 	}
9860c663092SPatrick Mooney 
9870c663092SPatrick Mooney 	/*
9880c663092SPatrick Mooney 	 * If the frequency of the periodic timer was altered, or the divider
9890c663092SPatrick Mooney 	 * itself was just brought out of reset, we must re-calculate
9900c663092SPatrick Mooney 	 * 'last_period' in order to determine the next edge when the periodic
9910c663092SPatrick Mooney 	 * timer would fire.
9920c663092SPatrick Mooney 	 */
9930c663092SPatrick Mooney 	const hrtime_t period_old = rega_period(oldval);
9940c663092SPatrick Mooney 	const hrtime_t period_new = rega_period(newval);
9950c663092SPatrick Mooney 	if (period_old != period_new || divider_restarted) {
9960c663092SPatrick Mooney 		if (period_new != 0) {
9970c663092SPatrick Mooney 			/*
9980c663092SPatrick Mooney 			 * Since the periodic timer is derived from additional
9990c663092SPatrick Mooney 			 * division applied to the output of the main divider,
10000c663092SPatrick Mooney 			 * we determine the last edge based on the most recent
10010c663092SPatrick Mooney 			 * time update.
10020c663092SPatrick Mooney 			 */
10030c663092SPatrick Mooney 			const hrtime_t since_last = now - vrtc->base_clock;
10040c663092SPatrick Mooney 			vrtc->last_period = vrtc->base_clock;
10050c663092SPatrick Mooney 			vrtc->last_period += ROUNDDOWN(since_last, period_new);
10060c663092SPatrick Mooney 		} else {
10070c663092SPatrick Mooney 			/*
10080c663092SPatrick Mooney 			 * The timing of the edge does not matter if the
10090c663092SPatrick Mooney 			 * periodic timer is disabled
10100c663092SPatrick Mooney 			 */
10110c663092SPatrick Mooney 			vrtc->last_period = now;
10120c663092SPatrick Mooney 		}
10134c87aefeSPatrick Mooney 	}
10144c87aefeSPatrick Mooney 
10154c87aefeSPatrick Mooney 	/*
1016a1d41cf9SPatrick Mooney 	 * We never present the time-update bit as a device, nor is the consumer
1017a1d41cf9SPatrick Mooney 	 * allowed to set it during a write.
10184c87aefeSPatrick Mooney 	 */
1019a1d41cf9SPatrick Mooney 	vrtc->rtcdev.reg_a = newval & ~RTCSA_TUP;
1020a1d41cf9SPatrick Mooney 
1021a1d41cf9SPatrick Mooney 	vrtc_callout_reschedule(vrtc);
10224c87aefeSPatrick Mooney }
10234c87aefeSPatrick Mooney 
10244c87aefeSPatrick Mooney int
vrtc_set_time(struct vm * vm,const timespec_t * ts)1025a1d41cf9SPatrick Mooney vrtc_set_time(struct vm *vm, const timespec_t *ts)
10264c87aefeSPatrick Mooney {
1027a1d41cf9SPatrick Mooney 	struct vrtc *vrtc = vm_rtc(vm);
1028a1d41cf9SPatrick Mooney 
1029a1d41cf9SPatrick Mooney 	if (ts->tv_sec < 0 || ts->tv_nsec >= NANOSEC) {
1030a1d41cf9SPatrick Mooney 		/*
1031a1d41cf9SPatrick Mooney 		 * Times before the 1970 epoch, or with nonsensical nanosecond
1032a1d41cf9SPatrick Mooney 		 * counts are not supported
1033a1d41cf9SPatrick Mooney 		 */
1034a1d41cf9SPatrick Mooney 		return (EINVAL);
1035a1d41cf9SPatrick Mooney 	}
10364c87aefeSPatrick Mooney 
10374c87aefeSPatrick Mooney 	VRTC_LOCK(vrtc);
1038a1d41cf9SPatrick Mooney 	vrtc->base_rtctime = ts->tv_sec;
1039a1d41cf9SPatrick Mooney 	vrtc->base_clock = gethrtime() - ts->tv_nsec;
1040a1d41cf9SPatrick Mooney 	vrtc->last_period = vrtc->base_clock;
1041a1d41cf9SPatrick Mooney 	if (!vm_is_paused(vrtc->vm)) {
1042a1d41cf9SPatrick Mooney 		vrtc_callout_reschedule(vrtc);
1043a1d41cf9SPatrick Mooney 	}
10444c87aefeSPatrick Mooney 	VRTC_UNLOCK(vrtc);
10454c87aefeSPatrick Mooney 
1046a1d41cf9SPatrick Mooney 	return (0);
10474c87aefeSPatrick Mooney }
10484c87aefeSPatrick Mooney 
1049a1d41cf9SPatrick Mooney void
vrtc_get_time(struct vm * vm,timespec_t * ts)1050a1d41cf9SPatrick Mooney vrtc_get_time(struct vm *vm, timespec_t *ts)
10514c87aefeSPatrick Mooney {
1052a1d41cf9SPatrick Mooney 	struct vrtc *vrtc = vm_rtc(vm);
1053a1d41cf9SPatrick Mooney 	hrtime_t phase;
10544c87aefeSPatrick Mooney 
10554c87aefeSPatrick Mooney 	VRTC_LOCK(vrtc);
1056a1d41cf9SPatrick Mooney 	ts->tv_sec = vrtc_curtime(vrtc, NULL, &phase);
1057a1d41cf9SPatrick Mooney 	ts->tv_nsec = phase;
10584c87aefeSPatrick Mooney 	VRTC_UNLOCK(vrtc);
10594c87aefeSPatrick Mooney }
10604c87aefeSPatrick Mooney 
10614c87aefeSPatrick Mooney int
vrtc_nvram_write(struct vm * vm,int offset,uint8_t value)10624c87aefeSPatrick Mooney vrtc_nvram_write(struct vm *vm, int offset, uint8_t value)
10634c87aefeSPatrick Mooney {
1064a1d41cf9SPatrick Mooney 	struct vrtc *vrtc = vm_rtc(vm);
1065a1d41cf9SPatrick Mooney 	uint8_t *rtc_raw = (uint8_t *)&vrtc->rtcdev;
10664c87aefeSPatrick Mooney 
1067a1d41cf9SPatrick Mooney 	/* Write offset must be valid */
1068a1d41cf9SPatrick Mooney 	if (offset < 0 || offset >= sizeof (struct rtcdev)) {
1069a1d41cf9SPatrick Mooney 		return (EINVAL);
1070a1d41cf9SPatrick Mooney 	}
10714c87aefeSPatrick Mooney 
1072a1d41cf9SPatrick Mooney 	/* Disallow writes to RTC control registers or the date/time fields */
1073a1d41cf9SPatrick Mooney 	if (rtc_field_ondemand(offset)) {
10744c87aefeSPatrick Mooney 		return (EINVAL);
10754c87aefeSPatrick Mooney 	}
10764c87aefeSPatrick Mooney 
10774c87aefeSPatrick Mooney 	VRTC_LOCK(vrtc);
1078a1d41cf9SPatrick Mooney 	rtc_raw[offset] = value;
10794c87aefeSPatrick Mooney 	VRTC_UNLOCK(vrtc);
10804c87aefeSPatrick Mooney 
10814c87aefeSPatrick Mooney 	return (0);
10824c87aefeSPatrick Mooney }
10834c87aefeSPatrick Mooney 
10844c87aefeSPatrick Mooney int
vrtc_nvram_read(struct vm * vm,int offset,uint8_t * retval)10854c87aefeSPatrick Mooney vrtc_nvram_read(struct vm *vm, int offset, uint8_t *retval)
10864c87aefeSPatrick Mooney {
1087a1d41cf9SPatrick Mooney 	struct vrtc *vrtc = vm_rtc(vm);
1088a1d41cf9SPatrick Mooney 	const uint8_t *rtc_raw = (uint8_t *)&vrtc->rtcdev;
10894c87aefeSPatrick Mooney 
1090a1d41cf9SPatrick Mooney 	/* Read offset must be valid */
1091a1d41cf9SPatrick Mooney 	if (offset < 0 || offset >= sizeof (struct rtcdev)) {
10924c87aefeSPatrick Mooney 		return (EINVAL);
1093a1d41cf9SPatrick Mooney 	}
10944c87aefeSPatrick Mooney 
10954c87aefeSPatrick Mooney 	VRTC_LOCK(vrtc);
10964c87aefeSPatrick Mooney 
1097a1d41cf9SPatrick Mooney 	vrtc_update(vrtc, offset);
1098a1d41cf9SPatrick Mooney 	/* Render out the updated date/time if it is being accessed */
1099a1d41cf9SPatrick Mooney 	if (rtc_field_datetime(offset)) {
1100a1d41cf9SPatrick Mooney 		vrtc_time_to_cmos(vrtc, false);
11014c87aefeSPatrick Mooney 	}
1102a1d41cf9SPatrick Mooney 	*retval = rtc_raw[offset];
11034c87aefeSPatrick Mooney 
11044c87aefeSPatrick Mooney 	VRTC_UNLOCK(vrtc);
1105a1d41cf9SPatrick Mooney 
11064c87aefeSPatrick Mooney 	return (0);
11074c87aefeSPatrick Mooney }
11084c87aefeSPatrick Mooney 
11094c87aefeSPatrick Mooney int
vrtc_addr_handler(void * arg,bool in,uint16_t port,uint8_t bytes,uint32_t * val)11100e1453c3SPatrick Mooney vrtc_addr_handler(void *arg, bool in, uint16_t port, uint8_t bytes,
11110e1453c3SPatrick Mooney     uint32_t *val)
11124c87aefeSPatrick Mooney {
11130e1453c3SPatrick Mooney 	struct vrtc *vrtc = arg;
11144c87aefeSPatrick Mooney 
1115a1d41cf9SPatrick Mooney 	if (bytes != 1) {
11164c87aefeSPatrick Mooney 		return (-1);
1117a1d41cf9SPatrick Mooney 	}
11184c87aefeSPatrick Mooney 
11194c87aefeSPatrick Mooney 	if (in) {
11204c87aefeSPatrick Mooney 		*val = 0xff;
11214c87aefeSPatrick Mooney 		return (0);
11224c87aefeSPatrick Mooney 	}
11234c87aefeSPatrick Mooney 
11244c87aefeSPatrick Mooney 	VRTC_LOCK(vrtc);
11254c87aefeSPatrick Mooney 	vrtc->addr = *val & 0x7f;
11264c87aefeSPatrick Mooney 	VRTC_UNLOCK(vrtc);
11274c87aefeSPatrick Mooney 
11284c87aefeSPatrick Mooney 	return (0);
11294c87aefeSPatrick Mooney }
11304c87aefeSPatrick Mooney 
1131a1d41cf9SPatrick Mooney static uint8_t
vrtc_read(struct vrtc * vrtc,uint8_t offset)1132a1d41cf9SPatrick Mooney vrtc_read(struct vrtc *vrtc, uint8_t offset)
1133a1d41cf9SPatrick Mooney {
1134a1d41cf9SPatrick Mooney 	const uint8_t *rtc_raw = (uint8_t *)&vrtc->rtcdev;
1135a1d41cf9SPatrick Mooney 
1136a1d41cf9SPatrick Mooney 	ASSERT(VRTC_LOCKED(vrtc));
1137a1d41cf9SPatrick Mooney 	ASSERT(offset < sizeof (struct rtcdev));
1138a1d41cf9SPatrick Mooney 
1139a1d41cf9SPatrick Mooney 	switch (offset) {
1140a1d41cf9SPatrick Mooney 	case RTC_INTR:
1141a1d41cf9SPatrick Mooney 		return (vrtc_regc_read(vrtc));
1142a1d41cf9SPatrick Mooney 	default:
1143a1d41cf9SPatrick Mooney 		/*
1144a1d41cf9SPatrick Mooney 		 * Everything else can be read from the updated-on-demand data
1145a1d41cf9SPatrick Mooney 		 * stored in the emulated CMOS space.
1146a1d41cf9SPatrick Mooney 		 */
1147a1d41cf9SPatrick Mooney 		return (rtc_raw[offset]);
1148a1d41cf9SPatrick Mooney 	}
1149a1d41cf9SPatrick Mooney }
1150a1d41cf9SPatrick Mooney 
1151a1d41cf9SPatrick Mooney static void
vrtc_write(struct vrtc * vrtc,uint8_t offset,uint8_t val)1152a1d41cf9SPatrick Mooney vrtc_write(struct vrtc *vrtc, uint8_t offset, uint8_t val)
1153a1d41cf9SPatrick Mooney {
1154a1d41cf9SPatrick Mooney 	uint8_t *rtc_raw = (uint8_t *)&vrtc->rtcdev;
1155a1d41cf9SPatrick Mooney 
1156a1d41cf9SPatrick Mooney 	ASSERT(VRTC_LOCKED(vrtc));
1157a1d41cf9SPatrick Mooney 	ASSERT(offset < sizeof (struct rtcdev));
1158a1d41cf9SPatrick Mooney 
1159a1d41cf9SPatrick Mooney 	switch (offset) {
1160a1d41cf9SPatrick Mooney 	case RTC_STATUSA:
1161a1d41cf9SPatrick Mooney 		vrtc_rega_write(vrtc, val);
1162a1d41cf9SPatrick Mooney 		break;
1163a1d41cf9SPatrick Mooney 	case RTC_STATUSB:
1164a1d41cf9SPatrick Mooney 		vrtc_regb_write(vrtc, val);
1165a1d41cf9SPatrick Mooney 		break;
1166a1d41cf9SPatrick Mooney 	case RTC_INTR:
1167a1d41cf9SPatrick Mooney 		/* Ignored write to register-C */
1168a1d41cf9SPatrick Mooney 		break;
1169a1d41cf9SPatrick Mooney 	case RTC_STATUSD:
1170a1d41cf9SPatrick Mooney 		/* Ignored write to register-D */
1171a1d41cf9SPatrick Mooney 		break;
1172a1d41cf9SPatrick Mooney 	case RTC_SEC:
1173a1d41cf9SPatrick Mooney 		/* High order bit of 'seconds' is read-only.  */
1174a1d41cf9SPatrick Mooney 		rtc_raw[offset] = val & 0x7f;
1175a1d41cf9SPatrick Mooney 		break;
1176a1d41cf9SPatrick Mooney 	default:
1177a1d41cf9SPatrick Mooney 		rtc_raw[offset] = val;
1178a1d41cf9SPatrick Mooney 		break;
1179a1d41cf9SPatrick Mooney 	}
1180a1d41cf9SPatrick Mooney 
1181a1d41cf9SPatrick Mooney 	/*
1182a1d41cf9SPatrick Mooney 	 * Some guests may write to date/time fields (such as OpenBSD writing
1183a1d41cf9SPatrick Mooney 	 * the century byte) without first pausing updates with RTCSB_HALT.
1184a1d41cf9SPatrick Mooney 	 *
1185a1d41cf9SPatrick Mooney 	 * Keep our internal representation of the time updated should such
1186a1d41cf9SPatrick Mooney 	 * writes occur.
1187a1d41cf9SPatrick Mooney 	 */
1188a1d41cf9SPatrick Mooney 	if (rtc_field_datetime(offset) && !rtc_halted(vrtc)) {
1189a1d41cf9SPatrick Mooney 		vrtc->base_rtctime = vrtc_cmos_to_secs(vrtc);
1190a1d41cf9SPatrick Mooney 	}
1191a1d41cf9SPatrick Mooney 
1192a1d41cf9SPatrick Mooney }
1193a1d41cf9SPatrick Mooney 
11944c87aefeSPatrick Mooney int
vrtc_data_handler(void * arg,bool in,uint16_t port,uint8_t bytes,uint32_t * val)11950e1453c3SPatrick Mooney vrtc_data_handler(void *arg, bool in, uint16_t port, uint8_t bytes,
11960e1453c3SPatrick Mooney     uint32_t *val)
11974c87aefeSPatrick Mooney {
11980e1453c3SPatrick Mooney 	struct vrtc *vrtc = arg;
11994c87aefeSPatrick Mooney 
1200a1d41cf9SPatrick Mooney 	if (bytes != 1) {
12014c87aefeSPatrick Mooney 		return (-1);
1202a1d41cf9SPatrick Mooney 	}
12034c87aefeSPatrick Mooney 
12044c87aefeSPatrick Mooney 	VRTC_LOCK(vrtc);
1205a1d41cf9SPatrick Mooney 	const uint8_t offset = vrtc->addr;
12062699b94cSPatrick Mooney 	if (offset >= sizeof (struct rtcdev)) {
12074c87aefeSPatrick Mooney 		VRTC_UNLOCK(vrtc);
12084c87aefeSPatrick Mooney 		return (-1);
12094c87aefeSPatrick Mooney 	}
12104c87aefeSPatrick Mooney 
1211a1d41cf9SPatrick Mooney 	/* Ensure internal state of RTC is updated */
1212a1d41cf9SPatrick Mooney 	vrtc_update(vrtc, offset);
12134c87aefeSPatrick Mooney 
12144c87aefeSPatrick Mooney 	/*
1215a1d41cf9SPatrick Mooney 	 * Update RTC date/time CMOS fields, if necessary.
12164c87aefeSPatrick Mooney 	 *
1217a1d41cf9SPatrick Mooney 	 * While the necessity for reads is obvious, the need for it during
1218a1d41cf9SPatrick Mooney 	 * writes is slightly more subtle: A write to one of the date/time
1219a1d41cf9SPatrick Mooney 	 * fields will requiring (re)parsing them all in order to determine the
1220a1d41cf9SPatrick Mooney 	 * new working date/time for the RTC.
12214c87aefeSPatrick Mooney 	 */
1222a1d41cf9SPatrick Mooney 	if (rtc_field_datetime(offset)) {
1223a1d41cf9SPatrick Mooney 		vrtc_time_to_cmos(vrtc, false);
1224a1d41cf9SPatrick Mooney 	}
12254c87aefeSPatrick Mooney 
12264c87aefeSPatrick Mooney 	if (in) {
1227a1d41cf9SPatrick Mooney 		*val = vrtc_read(vrtc, offset);
12284c87aefeSPatrick Mooney 	} else {
1229a1d41cf9SPatrick Mooney 		vrtc_write(vrtc, offset, *val);
12304c87aefeSPatrick Mooney 	}
12314c87aefeSPatrick Mooney 	VRTC_UNLOCK(vrtc);
1232a1d41cf9SPatrick Mooney 	return (0);
12334c87aefeSPatrick Mooney }
12344c87aefeSPatrick Mooney 
12354c87aefeSPatrick Mooney void
vrtc_reset(struct vrtc * vrtc)12364c87aefeSPatrick Mooney vrtc_reset(struct vrtc *vrtc)
12374c87aefeSPatrick Mooney {
1238a1d41cf9SPatrick Mooney 	struct rtcdev *rtc = &vrtc->rtcdev;
12394c87aefeSPatrick Mooney 
12404c87aefeSPatrick Mooney 	VRTC_LOCK(vrtc);
12414c87aefeSPatrick Mooney 
1242a1d41cf9SPatrick Mooney 	vrtc_regb_write(vrtc, rtc->reg_b & ~(RTCSB_INTR_MASK | RTCSB_SQWE));
1243a1d41cf9SPatrick Mooney 	rtc->reg_c = 0;
1244a1d41cf9SPatrick Mooney 	ASSERT(!callout_active(&vrtc->callout));
12454c87aefeSPatrick Mooney 
12464c87aefeSPatrick Mooney 	VRTC_UNLOCK(vrtc);
12474c87aefeSPatrick Mooney }
12484c87aefeSPatrick Mooney 
12494c87aefeSPatrick Mooney struct vrtc *
vrtc_init(struct vm * vm)12504c87aefeSPatrick Mooney vrtc_init(struct vm *vm)
12514c87aefeSPatrick Mooney {
12524c87aefeSPatrick Mooney 	struct vrtc *vrtc;
12534c87aefeSPatrick Mooney 	struct rtcdev *rtc;
12544c87aefeSPatrick Mooney 
12558130f8e1SPatrick Mooney 	vrtc = kmem_zalloc(sizeof (struct vrtc), KM_SLEEP);
12564c87aefeSPatrick Mooney 	vrtc->vm = vm;
1257fb29dee0SPatrick Mooney 	mutex_init(&vrtc->lock, NULL, MUTEX_ADAPTIVE, NULL);
12584c87aefeSPatrick Mooney 	callout_init(&vrtc->callout, 1);
12594c87aefeSPatrick Mooney 
12604c87aefeSPatrick Mooney 	/* Allow dividers to keep time but disable everything else */
12614c87aefeSPatrick Mooney 	rtc = &vrtc->rtcdev;
1262a1d41cf9SPatrick Mooney 	rtc->reg_a = RTCSA_DIVIDER_32K;
12634c87aefeSPatrick Mooney 	rtc->reg_b = RTCSB_24HR;
12644c87aefeSPatrick Mooney 	rtc->reg_c = 0;
12654c87aefeSPatrick Mooney 	rtc->reg_d = RTCSD_PWR;
12664c87aefeSPatrick Mooney 
12674c87aefeSPatrick Mooney 	/* Reset the index register to a safe value. */
12684c87aefeSPatrick Mooney 	vrtc->addr = RTC_STATUSD;
12694c87aefeSPatrick Mooney 
12704c87aefeSPatrick Mooney 	VRTC_LOCK(vrtc);
1271a1d41cf9SPatrick Mooney 	/* Initialize RTC time to 00:00:00 1 January, 1970.  */
1272a1d41cf9SPatrick Mooney 	vrtc->base_rtctime = 0;
1273a1d41cf9SPatrick Mooney 	vrtc->base_clock = gethrtime();
1274a1d41cf9SPatrick Mooney 	vrtc->last_period = vrtc->base_clock;
1275a1d41cf9SPatrick Mooney 	vrtc_time_to_cmos(vrtc, false);
12764c87aefeSPatrick Mooney 	VRTC_UNLOCK(vrtc);
12774c87aefeSPatrick Mooney 
12784c87aefeSPatrick Mooney 	return (vrtc);
12794c87aefeSPatrick Mooney }
12804c87aefeSPatrick Mooney 
12814c87aefeSPatrick Mooney void
vrtc_cleanup(struct vrtc * vrtc)12824c87aefeSPatrick Mooney vrtc_cleanup(struct vrtc *vrtc)
12834c87aefeSPatrick Mooney {
12844c87aefeSPatrick Mooney 	callout_drain(&vrtc->callout);
1285fb29dee0SPatrick Mooney 	mutex_destroy(&vrtc->lock);
12868130f8e1SPatrick Mooney 	kmem_free(vrtc, sizeof (*vrtc));
12874c87aefeSPatrick Mooney }
12884c87aefeSPatrick Mooney 
12894c87aefeSPatrick Mooney void
vrtc_localize_resources(struct vrtc * vrtc)12904c87aefeSPatrick Mooney vrtc_localize_resources(struct vrtc *vrtc)
12914c87aefeSPatrick Mooney {
12924c87aefeSPatrick Mooney 	vmm_glue_callout_localize(&vrtc->callout);
12934c87aefeSPatrick Mooney }
1294d515dd77SPatrick Mooney 
12952cac0506SPatrick Mooney void
vrtc_pause(struct vrtc * vrtc)12962cac0506SPatrick Mooney vrtc_pause(struct vrtc *vrtc)
12972cac0506SPatrick Mooney {
12982cac0506SPatrick Mooney 	VRTC_LOCK(vrtc);
12992cac0506SPatrick Mooney 	callout_stop(&vrtc->callout);
13002cac0506SPatrick Mooney 	VRTC_UNLOCK(vrtc);
13012cac0506SPatrick Mooney }
13022cac0506SPatrick Mooney 
13032cac0506SPatrick Mooney void
vrtc_resume(struct vrtc * vrtc)13042cac0506SPatrick Mooney vrtc_resume(struct vrtc *vrtc)
13052cac0506SPatrick Mooney {
13062cac0506SPatrick Mooney 	VRTC_LOCK(vrtc);
13072cac0506SPatrick Mooney 	ASSERT(!callout_active(&vrtc->callout));
1308a1d41cf9SPatrick Mooney 	vrtc_callout_reschedule(vrtc);
13092cac0506SPatrick Mooney 	VRTC_UNLOCK(vrtc);
13102cac0506SPatrick Mooney }
13112cac0506SPatrick Mooney 
1312d515dd77SPatrick Mooney static int
vrtc_data_read(void * datap,const vmm_data_req_t * req)1313d515dd77SPatrick Mooney vrtc_data_read(void *datap, const vmm_data_req_t *req)
1314d515dd77SPatrick Mooney {
1315d515dd77SPatrick Mooney 	VERIFY3U(req->vdr_class, ==, VDC_RTC);
1316a1d41cf9SPatrick Mooney 	VERIFY3U(req->vdr_version, ==, 2);
1317a1d41cf9SPatrick Mooney 	VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_rtc_v2));
1318d515dd77SPatrick Mooney 
1319d515dd77SPatrick Mooney 	struct vrtc *vrtc = datap;
1320a1d41cf9SPatrick Mooney 	struct vdi_rtc_v2 *out = req->vdr_data;
1321d515dd77SPatrick Mooney 
1322d515dd77SPatrick Mooney 	VRTC_LOCK(vrtc);
1323d515dd77SPatrick Mooney 
1324d515dd77SPatrick Mooney 	out->vr_addr = vrtc->addr;
1325a1d41cf9SPatrick Mooney 	out->vr_base_clock = vm_normalize_hrtime(vrtc->vm, vrtc->base_clock);
1326a1d41cf9SPatrick Mooney 	out->vr_last_period = vm_normalize_hrtime(vrtc->vm, vrtc->last_period);
1327d515dd77SPatrick Mooney 	bcopy(&vrtc->rtcdev, out->vr_content, sizeof (out->vr_content));
1328d515dd77SPatrick Mooney 
1329d515dd77SPatrick Mooney 	VRTC_UNLOCK(vrtc);
1330d515dd77SPatrick Mooney 
1331d515dd77SPatrick Mooney 	return (0);
1332d515dd77SPatrick Mooney }
1333d515dd77SPatrick Mooney 
1334d515dd77SPatrick Mooney static int
vrtc_data_write(void * datap,const vmm_data_req_t * req)1335d515dd77SPatrick Mooney vrtc_data_write(void *datap, const vmm_data_req_t *req)
1336d515dd77SPatrick Mooney {
1337d515dd77SPatrick Mooney 	VERIFY3U(req->vdr_class, ==, VDC_RTC);
1338a1d41cf9SPatrick Mooney 	VERIFY3U(req->vdr_version, ==, 2);
1339a1d41cf9SPatrick Mooney 	VERIFY3U(req->vdr_len, >=, sizeof (struct vdi_rtc_v2));
1340d515dd77SPatrick Mooney 
1341d515dd77SPatrick Mooney 	struct vrtc *vrtc = datap;
1342a1d41cf9SPatrick Mooney 	const struct vdi_rtc_v2 *src = req->vdr_data;
1343a1d41cf9SPatrick Mooney 
1344a1d41cf9SPatrick Mooney 	const hrtime_t base_clock =
1345a1d41cf9SPatrick Mooney 	    vm_denormalize_hrtime(vrtc->vm, src->vr_base_clock);
1346a1d41cf9SPatrick Mooney 	const hrtime_t last_period =
1347a1d41cf9SPatrick Mooney 	    vm_denormalize_hrtime(vrtc->vm, src->vr_last_period);
1348a1d41cf9SPatrick Mooney 
1349a1d41cf9SPatrick Mooney 	const hrtime_t now = gethrtime();
1350a1d41cf9SPatrick Mooney 	if (base_clock > now || last_period > now) {
1351a1d41cf9SPatrick Mooney 		/*
1352a1d41cf9SPatrick Mooney 		 * Neither the base clock nor the last periodic event edge
1353a1d41cf9SPatrick Mooney 		 * should be in the future, since they should trail (or at most
1354a1d41cf9SPatrick Mooney 		 * equal) the current time.
1355a1d41cf9SPatrick Mooney 		 */
1356a1d41cf9SPatrick Mooney 		return (EINVAL);
1357a1d41cf9SPatrick Mooney 	}
1358a1d41cf9SPatrick Mooney 
1359a1d41cf9SPatrick Mooney 	/*
1360a1d41cf9SPatrick Mooney 	 * The phase of last_period could be checked against that of base_clock,
1361a1d41cf9SPatrick Mooney 	 * but for now, any shenanigans there will go unhandled.
1362a1d41cf9SPatrick Mooney 	 */
1363d515dd77SPatrick Mooney 
1364d515dd77SPatrick Mooney 	VRTC_LOCK(vrtc);
1365d515dd77SPatrick Mooney 
1366a1d41cf9SPatrick Mooney 	vrtc->base_clock = base_clock;
1367d515dd77SPatrick Mooney 	bcopy(src->vr_content, &vrtc->rtcdev, sizeof (vrtc->rtcdev));
1368a1d41cf9SPatrick Mooney 	vrtc->addr = src->vr_addr;
1369d515dd77SPatrick Mooney 
1370d515dd77SPatrick Mooney 	vrtc->rtcdev.reg_a &= ~RTCSA_TUP;
1371a1d41cf9SPatrick Mooney 	/* register B needs requires no masking */
1372d515dd77SPatrick Mooney 	vrtc->rtcdev.reg_c &= RTCSC_MASK;
1373d515dd77SPatrick Mooney 	vrtc->rtcdev.reg_d = RTCSD_PWR;
1374d515dd77SPatrick Mooney 
1375a1d41cf9SPatrick Mooney 	/* Set internal time based on what is stored in CMOS */
1376a1d41cf9SPatrick Mooney 	vrtc->base_rtctime = vrtc_cmos_to_secs(vrtc);
1377a1d41cf9SPatrick Mooney 	/* Using the specified divider edge timing */
1378a1d41cf9SPatrick Mooney 	vrtc->base_clock = base_clock;
1379a1d41cf9SPatrick Mooney 	vrtc->last_period = last_period;
1380d515dd77SPatrick Mooney 
13812cac0506SPatrick Mooney 	if (!vm_is_paused(vrtc->vm)) {
1382a1d41cf9SPatrick Mooney 		vrtc_callout_reschedule(vrtc);
13832cac0506SPatrick Mooney 	}
1384d515dd77SPatrick Mooney 
1385d515dd77SPatrick Mooney 	VRTC_UNLOCK(vrtc);
1386d515dd77SPatrick Mooney 	return (0);
1387d515dd77SPatrick Mooney }
1388d515dd77SPatrick Mooney 
1389a1d41cf9SPatrick Mooney static const vmm_data_version_entry_t rtc_v2 = {
1390d515dd77SPatrick Mooney 	.vdve_class = VDC_RTC,
1391a1d41cf9SPatrick Mooney 	.vdve_version = 2,
1392a1d41cf9SPatrick Mooney 	.vdve_len_expect = sizeof (struct vdi_rtc_v2),
1393d515dd77SPatrick Mooney 	.vdve_readf = vrtc_data_read,
1394d515dd77SPatrick Mooney 	.vdve_writef = vrtc_data_write,
1395d515dd77SPatrick Mooney };
1396a1d41cf9SPatrick Mooney VMM_DATA_VERSION(rtc_v2);
1397