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