1/*-
2 * Copyright (c) 2011 NetApp, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <sys/types.h>
33#include <sys/time.h>
34
35#include <stdio.h>
36#include <string.h>
37#include <time.h>
38#include <assert.h>
39
40#include <machine/vmm.h>
41#include <vmmapi.h>
42
43#include "inout.h"
44#include "rtc.h"
45
46#define	IO_RTC	0x70
47
48#define RTC_SEC		0x00	/* seconds */
49#define	RTC_SEC_ALARM	0x01
50#define	RTC_MIN		0x02
51#define	RTC_MIN_ALARM	0x03
52#define	RTC_HRS		0x04
53#define	RTC_HRS_ALARM	0x05
54#define	RTC_WDAY	0x06
55#define	RTC_DAY		0x07
56#define	RTC_MONTH	0x08
57#define	RTC_YEAR	0x09
58#define	RTC_CENTURY	0x32	/* current century */
59
60#define RTC_STATUSA	0xA
61#define  RTCSA_TUP	 0x80	/* time update, don't look now */
62
63#define	RTC_STATUSB	0xB
64#define	 RTCSB_DST	 0x01
65#define	 RTCSB_24HR	 0x02
66#define	 RTCSB_BIN	 0x04	/* 0 = BCD, 1 = Binary */
67#define	 RTCSB_PINTR	 0x40	/* 1 = enable periodic clock interrupt */
68#define	 RTCSB_HALT      0x80	/* stop clock updates */
69
70#define RTC_INTR	0x0c	/* status register C (R) interrupt source */
71
72#define RTC_STATUSD	0x0d	/* status register D (R) Lost Power */
73#define  RTCSD_PWR	 0x80	/* clock power OK */
74
75#define	RTC_NVRAM_START	0x0e
76#define	RTC_NVRAM_END	0x7f
77#define RTC_NVRAM_SZ	(128 - RTC_NVRAM_START)
78#define	nvoff(x)	((x) - RTC_NVRAM_START)
79
80#define	RTC_DIAG	0x0e
81#define RTC_RSTCODE	0x0f
82#define	RTC_EQUIPMENT	0x14
83#define	RTC_LMEM_LSB	0x34
84#define	RTC_LMEM_MSB	0x35
85#define	RTC_HMEM_LSB	0x5b
86#define	RTC_HMEM_SB	0x5c
87#define	RTC_HMEM_MSB	0x5d
88
89#define m_64KB		(64*1024)
90#define	m_16MB		(16*1024*1024)
91#define	m_4GB		(4ULL*1024*1024*1024)
92
93static int addr;
94
95static uint8_t rtc_nvram[RTC_NVRAM_SZ];
96
97/* XXX initialize these to default values as they would be from BIOS */
98static uint8_t status_a, status_b;
99
100static struct {
101	uint8_t  hours;
102	uint8_t  mins;
103	uint8_t  secs;
104} rtc_alarm;
105
106static u_char const bin2bcd_data[] = {
107	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
108	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
109	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
110	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
111	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
112	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
113	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
114	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
115	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
116	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99
117};
118#define	bin2bcd(bin)	(bin2bcd_data[bin])
119
120#define	rtcout(val)	((status_b & RTCSB_BIN) ? (val) : bin2bcd((val)))
121
122static void
123timevalfix(struct timeval *t1)
124{
125
126	if (t1->tv_usec < 0) {
127		t1->tv_sec--;
128		t1->tv_usec += 1000000;
129	}
130	if (t1->tv_usec >= 1000000) {
131		t1->tv_sec++;
132		t1->tv_usec -= 1000000;
133	}
134}
135
136static void
137timevalsub(struct timeval *t1, const struct timeval *t2)
138{
139
140	t1->tv_sec -= t2->tv_sec;
141	t1->tv_usec -= t2->tv_usec;
142	timevalfix(t1);
143}
144
145static int
146rtc_addr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
147		 uint32_t *eax, void *arg)
148{
149	if (bytes != 1)
150		return (-1);
151
152	if (in) {
153		/* straight read of this register will return 0xFF */
154		*eax = 0xff;
155		return (0);
156	}
157
158	switch (*eax & 0x7f) {
159	case RTC_SEC:
160	case RTC_SEC_ALARM:
161	case RTC_MIN:
162	case RTC_MIN_ALARM:
163	case RTC_HRS:
164	case RTC_HRS_ALARM:
165	case RTC_WDAY:
166	case RTC_DAY:
167	case RTC_MONTH:
168	case RTC_YEAR:
169	case RTC_STATUSA:
170	case RTC_STATUSB:
171	case RTC_INTR:
172	case RTC_STATUSD:
173	case RTC_NVRAM_START ... RTC_NVRAM_END:
174		break;
175	default:
176		return (-1);
177	}
178
179	addr = *eax & 0x7f;
180	return (0);
181}
182
183static int
184rtc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
185		 uint32_t *eax, void *arg)
186{
187	int hour;
188	time_t t;
189	struct timeval cur, delta;
190
191	static struct timeval last;
192	static struct tm tm;
193
194	if (bytes != 1)
195		return (-1);
196
197	gettimeofday(&cur, NULL);
198
199	/*
200	 * Increment the cached time only once per second so we can guarantee
201	 * that the guest has at least one second to read the hour:min:sec
202	 * separately and still get a coherent view of the time.
203	 */
204	delta = cur;
205	timevalsub(&delta, &last);
206	if (delta.tv_sec >= 1 && (status_b & RTCSB_HALT) == 0) {
207		t = cur.tv_sec;
208		localtime_r(&t, &tm);
209		last = cur;
210	}
211
212	if (in) {
213		switch (addr) {
214		case RTC_SEC_ALARM:
215			*eax = rtc_alarm.secs;
216			break;
217		case RTC_MIN_ALARM:
218			*eax = rtc_alarm.mins;
219			break;
220		case RTC_HRS_ALARM:
221			*eax = rtc_alarm.hours;
222			break;
223		case RTC_SEC:
224			*eax = rtcout(tm.tm_sec);
225			return (0);
226		case RTC_MIN:
227			*eax = rtcout(tm.tm_min);
228			return (0);
229		case RTC_HRS:
230			if (status_b & RTCSB_24HR)
231				hour = tm.tm_hour;
232			else
233				hour = (tm.tm_hour % 12) + 1;
234
235			*eax = rtcout(hour);
236
237			/*
238			 * If we are representing time in the 12-hour format
239			 * then set the MSB to indicate PM.
240			 */
241			if ((status_b & RTCSB_24HR) == 0 && tm.tm_hour >= 12)
242				*eax |= 0x80;
243
244			return (0);
245		case RTC_WDAY:
246			*eax = rtcout(tm.tm_wday + 1);
247			return (0);
248		case RTC_DAY:
249			*eax = rtcout(tm.tm_mday);
250			return (0);
251		case RTC_MONTH:
252			*eax = rtcout(tm.tm_mon + 1);
253			return (0);
254		case RTC_YEAR:
255			*eax = rtcout(tm.tm_year % 100);
256			return (0);
257		case RTC_STATUSA:
258			*eax = status_a;
259			return (0);
260		case RTC_STATUSB:
261			*eax = status_b;
262			return (0);
263		case RTC_INTR:
264			*eax = 0;
265			return (0);
266		case RTC_STATUSD:
267			*eax = RTCSD_PWR;
268			return (0);
269		case RTC_NVRAM_START ... RTC_NVRAM_END:
270			*eax = rtc_nvram[addr - RTC_NVRAM_START];
271			return (0);
272		default:
273			return (-1);
274		}
275	}
276
277	switch (addr) {
278	case RTC_STATUSA:
279		status_a = *eax & ~RTCSA_TUP;
280		break;
281	case RTC_STATUSB:
282		/* XXX not implemented yet XXX */
283		if (*eax & RTCSB_PINTR)
284			return (-1);
285		status_b = *eax;
286		break;
287	case RTC_STATUSD:
288		/* ignore write */
289		break;
290	case RTC_SEC_ALARM:
291		rtc_alarm.secs = *eax;
292		break;
293	case RTC_MIN_ALARM:
294		rtc_alarm.mins = *eax;
295		break;
296	case RTC_HRS_ALARM:
297		rtc_alarm.hours = *eax;
298		break;
299	case RTC_SEC:
300	case RTC_MIN:
301	case RTC_HRS:
302	case RTC_WDAY:
303	case RTC_DAY:
304	case RTC_MONTH:
305	case RTC_YEAR:
306		/*
307		 * Ignore writes to the time of day registers
308		 */
309		break;
310	case RTC_NVRAM_START ... RTC_NVRAM_END:
311		rtc_nvram[addr - RTC_NVRAM_START] = *eax;
312		break;
313	default:
314		return (-1);
315	}
316	return (0);
317}
318
319void
320rtc_init(struct vmctx *ctx)
321{
322	struct timeval cur;
323	struct tm tm;
324	size_t himem;
325	size_t lomem;
326	int err;
327
328	err = gettimeofday(&cur, NULL);
329	assert(err == 0);
330	(void) localtime_r(&cur.tv_sec, &tm);
331
332	memset(rtc_nvram, 0, sizeof(rtc_nvram));
333
334	rtc_nvram[nvoff(RTC_CENTURY)] = bin2bcd((tm.tm_year + 1900) / 100);
335
336	/* XXX init diag/reset code/equipment/checksum ? */
337
338	/*
339	 * Report guest memory size in nvram cells as required by UEFI.
340	 * Little-endian encoding.
341	 * 0x34/0x35 - 64KB chunks above 16MB, below 4GB
342	 * 0x5b/0x5c/0x5d - 64KB chunks above 4GB
343	 */
344	err = vm_get_memory_seg(ctx, 0, &lomem, NULL);
345	assert(err == 0);
346
347	lomem = (lomem - m_16MB) / m_64KB;
348	rtc_nvram[nvoff(RTC_LMEM_LSB)] = lomem;
349	rtc_nvram[nvoff(RTC_LMEM_MSB)] = lomem >> 8;
350
351	if (vm_get_memory_seg(ctx, m_4GB, &himem, NULL) == 0) {
352		himem /= m_64KB;
353		rtc_nvram[nvoff(RTC_HMEM_LSB)] = himem;
354		rtc_nvram[nvoff(RTC_HMEM_SB)]  = himem >> 8;
355		rtc_nvram[nvoff(RTC_HMEM_MSB)] = himem >> 16;
356	}
357}
358
359INOUT_PORT(rtc, IO_RTC, IOPORT_F_INOUT, rtc_addr_handler);
360INOUT_PORT(rtc, IO_RTC + 1, IOPORT_F_INOUT, rtc_data_handler);
361