rtc.c revision 245920
1221828Sgrehan/*-
2221828Sgrehan * Copyright (c) 2011 NetApp, Inc.
3221828Sgrehan * All rights reserved.
4221828Sgrehan *
5221828Sgrehan * Redistribution and use in source and binary forms, with or without
6221828Sgrehan * modification, are permitted provided that the following conditions
7221828Sgrehan * are met:
8221828Sgrehan * 1. Redistributions of source code must retain the above copyright
9221828Sgrehan *    notice, this list of conditions and the following disclaimer.
10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
11221828Sgrehan *    notice, this list of conditions and the following disclaimer in the
12221828Sgrehan *    documentation and/or other materials provided with the distribution.
13221828Sgrehan *
14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17221828Sgrehan * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24221828Sgrehan * SUCH DAMAGE.
25221828Sgrehan *
26221828Sgrehan * $FreeBSD: head/usr.sbin/bhyve/rtc.c 245920 2013-01-25 22:43:20Z grehan $
27221828Sgrehan */
28221828Sgrehan
29221828Sgrehan#include <sys/cdefs.h>
30221828Sgrehan__FBSDID("$FreeBSD: head/usr.sbin/bhyve/rtc.c 245920 2013-01-25 22:43:20Z grehan $");
31221828Sgrehan
32221828Sgrehan#include <sys/types.h>
33221828Sgrehan#include <sys/time.h>
34221828Sgrehan
35221828Sgrehan#include <stdio.h>
36221828Sgrehan#include <time.h>
37221828Sgrehan#include <assert.h>
38221828Sgrehan
39221828Sgrehan#include "inout.h"
40221828Sgrehan
41221828Sgrehan#define	IO_RTC	0x70
42221828Sgrehan
43221828Sgrehan#define RTC_SEC		0x00	/* seconds */
44221828Sgrehan#define	RTC_MIN		0x02
45221828Sgrehan#define	RTC_HRS		0x04
46221828Sgrehan#define	RTC_WDAY	0x06
47221828Sgrehan#define	RTC_DAY		0x07
48221828Sgrehan#define	RTC_MONTH	0x08
49221828Sgrehan#define	RTC_YEAR	0x09
50221828Sgrehan#define	RTC_CENTURY	0x32	/* current century */
51221828Sgrehan
52221828Sgrehan#define RTC_STATUSA	0xA
53221828Sgrehan#define  RTCSA_TUP	 0x80	/* time update, don't look now */
54221828Sgrehan
55221828Sgrehan#define	RTC_STATUSB	0xB
56221828Sgrehan#define	 RTCSB_DST	 0x01
57221828Sgrehan#define	 RTCSB_24HR	 0x02
58221828Sgrehan#define	 RTCSB_BIN	 0x04	/* 0 = BCD, 1 = Binary */
59221828Sgrehan#define	 RTCSB_PINTR	 0x40	/* 1 = enable periodic clock interrupt */
60221828Sgrehan#define	 RTCSB_HALT      0x80	/* stop clock updates */
61221828Sgrehan
62221828Sgrehan#define RTC_INTR	0x0c	/* status register C (R) interrupt source */
63221828Sgrehan
64221828Sgrehan#define RTC_STATUSD	0x0d	/* status register D (R) Lost Power */
65221828Sgrehan#define  RTCSD_PWR	 0x80	/* clock power OK */
66221828Sgrehan
67221828Sgrehan#define	RTC_DIAG	0x0e
68221828Sgrehan
69221828Sgrehan#define RTC_RSTCODE	0x0f
70221828Sgrehan
71222105Sgrehan#define	RTC_EQUIPMENT	0x14
72222105Sgrehan
73221828Sgrehanstatic int addr;
74221828Sgrehan
75221828Sgrehan/* XXX initialize these to default values as they would be from BIOS */
76221828Sgrehanstatic uint8_t status_a, status_b, rstcode;
77221828Sgrehan
78221828Sgrehanstatic u_char const bin2bcd_data[] = {
79221828Sgrehan	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
80221828Sgrehan	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
81221828Sgrehan	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
82221828Sgrehan	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
83221828Sgrehan	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
84221828Sgrehan	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
85221828Sgrehan	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
86221828Sgrehan	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
87221828Sgrehan	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
88221828Sgrehan	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99
89221828Sgrehan};
90221828Sgrehan#define	bin2bcd(bin)	(bin2bcd_data[bin])
91221828Sgrehan
92221828Sgrehan#define	rtcout(val)	((status_b & RTCSB_BIN) ? (val) : bin2bcd((val)))
93221828Sgrehan
94221828Sgrehanstatic void
95221828Sgrehantimevalfix(struct timeval *t1)
96221828Sgrehan{
97221828Sgrehan
98221828Sgrehan	if (t1->tv_usec < 0) {
99221828Sgrehan		t1->tv_sec--;
100221828Sgrehan		t1->tv_usec += 1000000;
101221828Sgrehan	}
102221828Sgrehan	if (t1->tv_usec >= 1000000) {
103221828Sgrehan		t1->tv_sec++;
104221828Sgrehan		t1->tv_usec -= 1000000;
105221828Sgrehan	}
106221828Sgrehan}
107221828Sgrehan
108221828Sgrehanstatic void
109221828Sgrehantimevalsub(struct timeval *t1, const struct timeval *t2)
110221828Sgrehan{
111221828Sgrehan
112221828Sgrehan	t1->tv_sec -= t2->tv_sec;
113221828Sgrehan	t1->tv_usec -= t2->tv_usec;
114221828Sgrehan	timevalfix(t1);
115221828Sgrehan}
116221828Sgrehan
117221828Sgrehanstatic int
118221828Sgrehanrtc_addr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
119221828Sgrehan		 uint32_t *eax, void *arg)
120221828Sgrehan{
121221828Sgrehan	if (bytes != 1)
122221828Sgrehan		return (-1);
123221828Sgrehan
124245920Sgrehan	if (in) {
125245920Sgrehan		/* straight read of this register will return 0xFF */
126245920Sgrehan		*eax = 0xff;
127245920Sgrehan		return (0);
128245920Sgrehan	}
129245920Sgrehan
130245920Sgrehan	switch (*eax & 0x7f) {
131221828Sgrehan	case RTC_SEC:
132221828Sgrehan	case RTC_MIN:
133221828Sgrehan	case RTC_HRS:
134221828Sgrehan	case RTC_WDAY:
135221828Sgrehan	case RTC_DAY:
136221828Sgrehan	case RTC_MONTH:
137221828Sgrehan	case RTC_YEAR:
138221828Sgrehan	case RTC_CENTURY:
139221828Sgrehan	case RTC_STATUSA:
140221828Sgrehan	case RTC_STATUSB:
141221828Sgrehan	case RTC_INTR:
142221828Sgrehan	case RTC_STATUSD:
143221828Sgrehan	case RTC_DIAG:
144221828Sgrehan	case RTC_RSTCODE:
145222105Sgrehan	case RTC_EQUIPMENT:
146221828Sgrehan		break;
147221828Sgrehan	default:
148221828Sgrehan		return (-1);
149221828Sgrehan	}
150221828Sgrehan
151245920Sgrehan	addr = *eax & 0x7f;
152221828Sgrehan	return (0);
153221828Sgrehan}
154221828Sgrehan
155221828Sgrehanstatic int
156221828Sgrehanrtc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
157221828Sgrehan		 uint32_t *eax, void *arg)
158221828Sgrehan{
159221828Sgrehan	int hour;
160221828Sgrehan	time_t t;
161221828Sgrehan	struct timeval cur, delta;
162221828Sgrehan
163221828Sgrehan	static struct timeval last;
164221828Sgrehan	static struct tm tm;
165221828Sgrehan
166221828Sgrehan	if (bytes != 1)
167221828Sgrehan		return (-1);
168221828Sgrehan
169221828Sgrehan	gettimeofday(&cur, NULL);
170221828Sgrehan
171221828Sgrehan	/*
172221828Sgrehan	 * Increment the cached time only once per second so we can guarantee
173221828Sgrehan	 * that the guest has at least one second to read the hour:min:sec
174221828Sgrehan	 * separately and still get a coherent view of the time.
175221828Sgrehan	 */
176221828Sgrehan	delta = cur;
177221828Sgrehan	timevalsub(&delta, &last);
178221828Sgrehan	if (delta.tv_sec >= 1 && (status_b & RTCSB_HALT) == 0) {
179221828Sgrehan		t = cur.tv_sec;
180221828Sgrehan		localtime_r(&t, &tm);
181221828Sgrehan		last = cur;
182221828Sgrehan	}
183221828Sgrehan
184221828Sgrehan	if (in) {
185221828Sgrehan		switch (addr) {
186221828Sgrehan		case RTC_SEC:
187221828Sgrehan			*eax = rtcout(tm.tm_sec);
188221828Sgrehan			return (0);
189221828Sgrehan		case RTC_MIN:
190221828Sgrehan			*eax = rtcout(tm.tm_min);
191221828Sgrehan			return (0);
192221828Sgrehan		case RTC_HRS:
193221828Sgrehan			if (status_b & RTCSB_24HR)
194221828Sgrehan				hour = tm.tm_hour;
195221828Sgrehan			else
196221828Sgrehan				hour = (tm.tm_hour % 12) + 1;
197221828Sgrehan
198221828Sgrehan			*eax = rtcout(hour);
199221828Sgrehan
200221828Sgrehan			/*
201221828Sgrehan			 * If we are representing time in the 12-hour format
202221828Sgrehan			 * then set the MSB to indicate PM.
203221828Sgrehan			 */
204221828Sgrehan			if ((status_b & RTCSB_24HR) == 0 && tm.tm_hour >= 12)
205221828Sgrehan				*eax |= 0x80;
206221828Sgrehan
207221828Sgrehan			return (0);
208221828Sgrehan		case RTC_WDAY:
209221828Sgrehan			*eax = rtcout(tm.tm_wday + 1);
210221828Sgrehan			return (0);
211221828Sgrehan		case RTC_DAY:
212221828Sgrehan			*eax = rtcout(tm.tm_mday);
213221828Sgrehan			return (0);
214221828Sgrehan		case RTC_MONTH:
215221828Sgrehan			*eax = rtcout(tm.tm_mon + 1);
216221828Sgrehan			return (0);
217221828Sgrehan		case RTC_YEAR:
218221828Sgrehan			*eax = rtcout(tm.tm_year % 100);
219221828Sgrehan			return (0);
220221828Sgrehan		case RTC_CENTURY:
221221828Sgrehan			*eax = rtcout(tm.tm_year / 100);
222221828Sgrehan			break;
223221828Sgrehan		case RTC_STATUSA:
224221828Sgrehan			*eax = status_a;
225221828Sgrehan			return (0);
226245920Sgrehan		case RTC_STATUSB:
227245920Sgrehan			*eax = status_b;
228245920Sgrehan			return (0);
229221828Sgrehan		case RTC_INTR:
230221828Sgrehan			*eax = 0;
231221828Sgrehan			return (0);
232221828Sgrehan		case RTC_STATUSD:
233221828Sgrehan			*eax = RTCSD_PWR;
234221828Sgrehan			return (0);
235221828Sgrehan		case RTC_DIAG:
236221828Sgrehan			*eax = 0;
237221828Sgrehan			return (0);
238221828Sgrehan		case RTC_RSTCODE:
239221828Sgrehan			*eax = rstcode;
240221828Sgrehan			return (0);
241222105Sgrehan		case RTC_EQUIPMENT:
242222105Sgrehan			*eax = 0;
243222105Sgrehan			return (0);
244221828Sgrehan		default:
245221828Sgrehan			return (-1);
246221828Sgrehan		}
247221828Sgrehan	}
248221828Sgrehan
249221828Sgrehan	switch (addr) {
250221828Sgrehan	case RTC_STATUSA:
251221828Sgrehan		status_a = *eax & ~RTCSA_TUP;
252221828Sgrehan		break;
253221828Sgrehan	case RTC_STATUSB:
254221828Sgrehan		/* XXX not implemented yet XXX */
255221828Sgrehan		if (*eax & RTCSB_PINTR)
256221828Sgrehan			return (-1);
257221828Sgrehan		status_b = *eax;
258221828Sgrehan		break;
259245920Sgrehan	case RTC_STATUSD:
260245920Sgrehan		/* ignore write */
261245920Sgrehan		break;
262221828Sgrehan	case RTC_RSTCODE:
263221828Sgrehan		rstcode = *eax;
264221828Sgrehan		break;
265221828Sgrehan	case RTC_SEC:
266221828Sgrehan	case RTC_MIN:
267221828Sgrehan	case RTC_HRS:
268221828Sgrehan	case RTC_WDAY:
269221828Sgrehan	case RTC_DAY:
270221828Sgrehan	case RTC_MONTH:
271221828Sgrehan	case RTC_YEAR:
272221828Sgrehan	case RTC_CENTURY:
273221828Sgrehan		/*
274221828Sgrehan		 * Ignore writes to the time of day registers
275221828Sgrehan		 */
276221828Sgrehan		break;
277221828Sgrehan	default:
278221828Sgrehan		return (-1);
279221828Sgrehan	}
280221828Sgrehan	return (0);
281221828Sgrehan}
282221828Sgrehan
283245920SgrehanINOUT_PORT(rtc, IO_RTC, IOPORT_F_INOUT, rtc_addr_handler);
284221828SgrehanINOUT_PORT(rtc, IO_RTC + 1, IOPORT_F_INOUT, rtc_data_handler);
285