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 "acpi.h"
44#include "inout.h"
45#include "pci_lpc.h"
46#include "rtc.h"
47
48#define	IO_RTC	0x70
49
50#define RTC_SEC		0x00	/* seconds */
51#define	RTC_SEC_ALARM	0x01
52#define	RTC_MIN		0x02
53#define	RTC_MIN_ALARM	0x03
54#define	RTC_HRS		0x04
55#define	RTC_HRS_ALARM	0x05
56#define	RTC_WDAY	0x06
57#define	RTC_DAY		0x07
58#define	RTC_MONTH	0x08
59#define	RTC_YEAR	0x09
60#define	RTC_CENTURY	0x32	/* current century */
61
62#define RTC_STATUSA	0xA
63#define  RTCSA_TUP	 0x80	/* time update, don't look now */
64
65#define	RTC_STATUSB	0xB
66#define	 RTCSB_DST	 0x01
67#define	 RTCSB_24HR	 0x02
68#define	 RTCSB_BIN	 0x04	/* 0 = BCD, 1 = Binary */
69#define	 RTCSB_PINTR	 0x40	/* 1 = enable periodic clock interrupt */
70#define	 RTCSB_HALT      0x80	/* stop clock updates */
71
72#define RTC_INTR	0x0c	/* status register C (R) interrupt source */
73
74#define RTC_STATUSD	0x0d	/* status register D (R) Lost Power */
75#define  RTCSD_PWR	 0x80	/* clock power OK */
76
77#define	RTC_NVRAM_START	0x0e
78#define	RTC_NVRAM_END	0x7f
79#define RTC_NVRAM_SZ	(128 - RTC_NVRAM_START)
80#define	nvoff(x)	((x) - RTC_NVRAM_START)
81
82#define	RTC_DIAG	0x0e
83#define RTC_RSTCODE	0x0f
84#define	RTC_EQUIPMENT	0x14
85#define	RTC_LMEM_LSB	0x34
86#define	RTC_LMEM_MSB	0x35
87#define	RTC_HMEM_LSB	0x5b
88#define	RTC_HMEM_SB	0x5c
89#define	RTC_HMEM_MSB	0x5d
90
91#define m_64KB		(64*1024)
92#define	m_16MB		(16*1024*1024)
93#define	m_4GB		(4ULL*1024*1024*1024)
94
95static int addr;
96
97static uint8_t rtc_nvram[RTC_NVRAM_SZ];
98
99/* XXX initialize these to default values as they would be from BIOS */
100static uint8_t status_a, status_b;
101
102static struct {
103	uint8_t  hours;
104	uint8_t  mins;
105	uint8_t  secs;
106} rtc_alarm;
107
108static u_char const bin2bcd_data[] = {
109	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
110	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
111	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
112	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
113	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
114	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
115	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
116	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
117	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
118	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99
119};
120#define	bin2bcd(bin)	(bin2bcd_data[bin])
121
122#define	rtcout(val)	((status_b & RTCSB_BIN) ? (val) : bin2bcd((val)))
123
124static void
125timevalfix(struct timeval *t1)
126{
127
128	if (t1->tv_usec < 0) {
129		t1->tv_sec--;
130		t1->tv_usec += 1000000;
131	}
132	if (t1->tv_usec >= 1000000) {
133		t1->tv_sec++;
134		t1->tv_usec -= 1000000;
135	}
136}
137
138static void
139timevalsub(struct timeval *t1, const struct timeval *t2)
140{
141
142	t1->tv_sec -= t2->tv_sec;
143	t1->tv_usec -= t2->tv_usec;
144	timevalfix(t1);
145}
146
147static int
148rtc_addr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
149		 uint32_t *eax, void *arg)
150{
151	if (bytes != 1)
152		return (-1);
153
154	if (in) {
155		/* straight read of this register will return 0xFF */
156		*eax = 0xff;
157		return (0);
158	}
159
160	switch (*eax & 0x7f) {
161	case RTC_SEC:
162	case RTC_SEC_ALARM:
163	case RTC_MIN:
164	case RTC_MIN_ALARM:
165	case RTC_HRS:
166	case RTC_HRS_ALARM:
167	case RTC_WDAY:
168	case RTC_DAY:
169	case RTC_MONTH:
170	case RTC_YEAR:
171	case RTC_STATUSA:
172	case RTC_STATUSB:
173	case RTC_INTR:
174	case RTC_STATUSD:
175	case RTC_NVRAM_START ... RTC_NVRAM_END:
176		break;
177	default:
178		return (-1);
179	}
180
181	addr = *eax & 0x7f;
182	return (0);
183}
184
185static int
186rtc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
187		 uint32_t *eax, void *arg)
188{
189	int hour;
190	time_t t;
191	struct timeval cur, delta;
192
193	static struct timeval last;
194	static struct tm tm;
195
196	if (bytes != 1)
197		return (-1);
198
199	gettimeofday(&cur, NULL);
200
201	/*
202	 * Increment the cached time only once per second so we can guarantee
203	 * that the guest has at least one second to read the hour:min:sec
204	 * separately and still get a coherent view of the time.
205	 */
206	delta = cur;
207	timevalsub(&delta, &last);
208	if (delta.tv_sec >= 1 && (status_b & RTCSB_HALT) == 0) {
209		t = cur.tv_sec;
210		localtime_r(&t, &tm);
211		last = cur;
212	}
213
214	if (in) {
215		switch (addr) {
216		case RTC_SEC_ALARM:
217			*eax = rtc_alarm.secs;
218			break;
219		case RTC_MIN_ALARM:
220			*eax = rtc_alarm.mins;
221			break;
222		case RTC_HRS_ALARM:
223			*eax = rtc_alarm.hours;
224			break;
225		case RTC_SEC:
226			*eax = rtcout(tm.tm_sec);
227			return (0);
228		case RTC_MIN:
229			*eax = rtcout(tm.tm_min);
230			return (0);
231		case RTC_HRS:
232			if (status_b & RTCSB_24HR)
233				hour = tm.tm_hour;
234			else
235				hour = (tm.tm_hour % 12) + 1;
236
237			*eax = rtcout(hour);
238
239			/*
240			 * If we are representing time in the 12-hour format
241			 * then set the MSB to indicate PM.
242			 */
243			if ((status_b & RTCSB_24HR) == 0 && tm.tm_hour >= 12)
244				*eax |= 0x80;
245
246			return (0);
247		case RTC_WDAY:
248			*eax = rtcout(tm.tm_wday + 1);
249			return (0);
250		case RTC_DAY:
251			*eax = rtcout(tm.tm_mday);
252			return (0);
253		case RTC_MONTH:
254			*eax = rtcout(tm.tm_mon + 1);
255			return (0);
256		case RTC_YEAR:
257			*eax = rtcout(tm.tm_year % 100);
258			return (0);
259		case RTC_STATUSA:
260			*eax = status_a;
261			return (0);
262		case RTC_STATUSB:
263			*eax = status_b;
264			return (0);
265		case RTC_INTR:
266			*eax = 0;
267			return (0);
268		case RTC_STATUSD:
269			*eax = RTCSD_PWR;
270			return (0);
271		case RTC_NVRAM_START ... RTC_NVRAM_END:
272			*eax = rtc_nvram[addr - RTC_NVRAM_START];
273			return (0);
274		default:
275			return (-1);
276		}
277	}
278
279	switch (addr) {
280	case RTC_STATUSA:
281		status_a = *eax & ~RTCSA_TUP;
282		break;
283	case RTC_STATUSB:
284		/* XXX not implemented yet XXX */
285		if (*eax & RTCSB_PINTR)
286			return (-1);
287		status_b = *eax;
288		break;
289	case RTC_STATUSD:
290		/* ignore write */
291		break;
292	case RTC_SEC_ALARM:
293		rtc_alarm.secs = *eax;
294		break;
295	case RTC_MIN_ALARM:
296		rtc_alarm.mins = *eax;
297		break;
298	case RTC_HRS_ALARM:
299		rtc_alarm.hours = *eax;
300		break;
301	case RTC_SEC:
302	case RTC_MIN:
303	case RTC_HRS:
304	case RTC_WDAY:
305	case RTC_DAY:
306	case RTC_MONTH:
307	case RTC_YEAR:
308		/*
309		 * Ignore writes to the time of day registers
310		 */
311		break;
312	case RTC_NVRAM_START ... RTC_NVRAM_END:
313		rtc_nvram[addr - RTC_NVRAM_START] = *eax;
314		break;
315	default:
316		return (-1);
317	}
318	return (0);
319}
320
321void
322rtc_init(struct vmctx *ctx)
323{
324	struct timeval cur;
325	struct tm tm;
326	size_t himem;
327	size_t lomem;
328	int err;
329
330	err = gettimeofday(&cur, NULL);
331	assert(err == 0);
332	(void) localtime_r(&cur.tv_sec, &tm);
333
334	memset(rtc_nvram, 0, sizeof(rtc_nvram));
335
336	rtc_nvram[nvoff(RTC_CENTURY)] = bin2bcd((tm.tm_year + 1900) / 100);
337
338	/* XXX init diag/reset code/equipment/checksum ? */
339
340	/*
341	 * Report guest memory size in nvram cells as required by UEFI.
342	 * Little-endian encoding.
343	 * 0x34/0x35 - 64KB chunks above 16MB, below 4GB
344	 * 0x5b/0x5c/0x5d - 64KB chunks above 4GB
345	 */
346	lomem = (vm_get_lowmem_size(ctx) - m_16MB) / m_64KB;
347	rtc_nvram[nvoff(RTC_LMEM_LSB)] = lomem;
348	rtc_nvram[nvoff(RTC_LMEM_MSB)] = lomem >> 8;
349
350	himem = vm_get_highmem_size(ctx) / m_64KB;
351	rtc_nvram[nvoff(RTC_HMEM_LSB)] = himem;
352	rtc_nvram[nvoff(RTC_HMEM_SB)]  = himem >> 8;
353	rtc_nvram[nvoff(RTC_HMEM_MSB)] = himem >> 16;
354}
355
356INOUT_PORT(rtc, IO_RTC, IOPORT_F_INOUT, rtc_addr_handler);
357INOUT_PORT(rtc, IO_RTC + 1, IOPORT_F_INOUT, rtc_data_handler);
358
359static void
360rtc_dsdt(void)
361{
362
363	dsdt_line("");
364	dsdt_line("Device (RTC)");
365	dsdt_line("{");
366	dsdt_line("  Name (_HID, EisaId (\"PNP0B00\"))");
367	dsdt_line("  Name (_CRS, ResourceTemplate ()");
368	dsdt_line("  {");
369	dsdt_indent(2);
370	dsdt_fixed_ioport(IO_RTC, 2);
371	dsdt_fixed_irq(8);
372	dsdt_unindent(2);
373	dsdt_line("  })");
374	dsdt_line("}");
375}
376LPC_DSDT(rtc_dsdt);
377
378SYSRES_IO(0x72, 6);
379