rtc.c revision 256072
156893Sfenner/*-
256893Sfenner * Copyright (c) 2011 NetApp, Inc.
356893Sfenner * All rights reserved.
4127668Sbms *
556893Sfenner * Redistribution and use in source and binary forms, with or without
656893Sfenner * modification, are permitted provided that the following conditions
756893Sfenner * are met:
856893Sfenner * 1. Redistributions of source code must retain the above copyright
956893Sfenner *    notice, this list of conditions and the following disclaimer.
1056893Sfenner * 2. Redistributions in binary form must reproduce the above copyright
1156893Sfenner *    notice, this list of conditions and the following disclaimer in the
1256893Sfenner *    documentation and/or other materials provided with the distribution.
1356893Sfenner *
1456893Sfenner * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
1556893Sfenner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16127668Sbms * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1756893Sfenner * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
1856893Sfenner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1956893Sfenner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2056893Sfenner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2156893Sfenner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2256893Sfenner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2356893Sfenner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2456893Sfenner * SUCH DAMAGE.
2556893Sfenner *
2656893Sfenner * $FreeBSD: head/usr.sbin/bhyve/rtc.c 256072 2013-10-05 21:22:35Z neel $
2756893Sfenner */
2856893Sfenner
2956893Sfenner#include <sys/cdefs.h>
3056893Sfenner__FBSDID("$FreeBSD: head/usr.sbin/bhyve/rtc.c 256072 2013-10-05 21:22:35Z neel $");
3156893Sfenner
32127668Sbms#include <sys/types.h>
33214478Srpaulo#include <sys/time.h>
3456893Sfenner
3556893Sfenner#include <stdio.h>
36190207Srpaulo#include <string.h>
3756893Sfenner#include <time.h>
3856893Sfenner#include <assert.h>
3956893Sfenner
4056893Sfenner#include <machine/vmm.h>
41127668Sbms#include <vmmapi.h>
42127668Sbms
4356893Sfenner#include "inout.h"
4456893Sfenner#include "rtc.h"
4556893Sfenner
4656893Sfenner#define	IO_RTC	0x70
4756893Sfenner
4856893Sfenner#define RTC_SEC		0x00	/* seconds */
4956893Sfenner#define	RTC_SEC_ALARM	0x01
5056893Sfenner#define	RTC_MIN		0x02
5156893Sfenner#define	RTC_MIN_ALARM	0x03
5256893Sfenner#define	RTC_HRS		0x04
5356893Sfenner#define	RTC_HRS_ALARM	0x05
5475115Sfenner#define	RTC_WDAY	0x06
5575115Sfenner#define	RTC_DAY		0x07
5675115Sfenner#define	RTC_MONTH	0x08
5775115Sfenner#define	RTC_YEAR	0x09
5875115Sfenner#define	RTC_CENTURY	0x32	/* current century */
5956893Sfenner
6056893Sfenner#define RTC_STATUSA	0xA
6156893Sfenner#define  RTCSA_TUP	 0x80	/* time update, don't look now */
6256893Sfenner
63190207Srpaulo#define	RTC_STATUSB	0xB
64190207Srpaulo#define	 RTCSB_DST	 0x01
65190207Srpaulo#define	 RTCSB_24HR	 0x02
66190207Srpaulo#define	 RTCSB_BIN	 0x04	/* 0 = BCD, 1 = Binary */
67190207Srpaulo#define	 RTCSB_PINTR	 0x40	/* 1 = enable periodic clock interrupt */
68190207Srpaulo#define	 RTCSB_HALT      0x80	/* stop clock updates */
69190207Srpaulo
70190207Srpaulo#define RTC_INTR	0x0c	/* status register C (R) interrupt source */
71190207Srpaulo
72190207Srpaulo#define RTC_STATUSD	0x0d	/* status register D (R) Lost Power */
73190207Srpaulo#define  RTCSD_PWR	 0x80	/* clock power OK */
74190207Srpaulo
75190207Srpaulo#define	RTC_NVRAM_START	0x0e
76190207Srpaulo#define	RTC_NVRAM_END	0x7f
77190207Srpaulo#define RTC_NVRAM_SZ	(128 - RTC_NVRAM_START)
78190207Srpaulo#define	nvoff(x)	((x) - RTC_NVRAM_START)
79190207Srpaulo
80190207Srpaulo#define	RTC_DIAG	0x0e
81190207Srpaulo#define RTC_RSTCODE	0x0f
82190207Srpaulo#define	RTC_EQUIPMENT	0x14
83190207Srpaulo#define	RTC_LMEM_LSB	0x34
84190207Srpaulo#define	RTC_LMEM_MSB	0x35
85190207Srpaulo#define	RTC_HMEM_LSB	0x5b
86190207Srpaulo#define	RTC_HMEM_SB	0x5c
87190207Srpaulo#define	RTC_HMEM_MSB	0x5d
88190207Srpaulo
89190207Srpaulo#define m_64KB		(64*1024)
90190207Srpaulo#define	m_16MB		(16*1024*1024)
91190207Srpaulo#define	m_4GB		(4ULL*1024*1024*1024)
92190207Srpaulo
93190207Srpaulostatic int addr;
94190207Srpaulo
95190207Srpaulostatic uint8_t rtc_nvram[RTC_NVRAM_SZ];
96190207Srpaulo
97190207Srpaulo/* XXX initialize these to default values as they would be from BIOS */
98190207Srpaulostatic uint8_t status_a, status_b;
99190207Srpaulo
100214478Srpaulostatic struct {
101214478Srpaulo	uint8_t  hours;
102214478Srpaulo	uint8_t  mins;
103214478Srpaulo	uint8_t  secs;
104214478Srpaulo} rtc_alarm;
105214478Srpaulo
106214478Srpaulostatic u_char const bin2bcd_data[] = {
107214478Srpaulo	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
108214478Srpaulo	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
109214478Srpaulo	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
110214478Srpaulo	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
111190207Srpaulo	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
112127668Sbms	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
113190207Srpaulo	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
114127668Sbms	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
115190207Srpaulo	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
116190207Srpaulo	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99
117214478Srpaulo};
118190207Srpaulo#define	bin2bcd(bin)	(bin2bcd_data[bin])
119190207Srpaulo
120190207Srpaulo#define	rtcout(val)	((status_b & RTCSB_BIN) ? (val) : bin2bcd((val)))
121190207Srpaulo
122190207Srpaulostatic void
123190207Srpaulotimevalfix(struct timeval *t1)
12475115Sfenner{
125127668Sbms
12656893Sfenner	if (t1->tv_usec < 0) {
127190207Srpaulo		t1->tv_sec--;
128190207Srpaulo		t1->tv_usec += 1000000;
129190207Srpaulo	}
130190207Srpaulo	if (t1->tv_usec >= 1000000) {
131190207Srpaulo		t1->tv_sec++;
13256893Sfenner		t1->tv_usec -= 1000000;
13356893Sfenner	}
13456893Sfenner}
13556893Sfenner
13656893Sfennerstatic void
13756893Sfennertimevalsub(struct timeval *t1, const struct timeval *t2)
13856893Sfenner{
13956893Sfenner
14056893Sfenner	t1->tv_sec -= t2->tv_sec;
141127668Sbms	t1->tv_usec -= t2->tv_usec;
14256893Sfenner	timevalfix(t1);
14356893Sfenner}
14456893Sfenner
14556893Sfennerstatic int
146127668Sbmsrtc_addr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
147190207Srpaulo		 uint32_t *eax, void *arg)
148190207Srpaulo{
149190207Srpaulo	if (bytes != 1)
150190207Srpaulo		return (-1);
151190207Srpaulo
152190207Srpaulo	if (in) {
153190207Srpaulo		/* straight read of this register will return 0xFF */
154190207Srpaulo		*eax = 0xff;
155190207Srpaulo		return (0);
156190207Srpaulo	}
157190207Srpaulo
15856893Sfenner	switch (*eax & 0x7f) {
15956893Sfenner	case RTC_SEC:
16056893Sfenner	case RTC_SEC_ALARM:
161190207Srpaulo	case RTC_MIN:
162190207Srpaulo	case RTC_MIN_ALARM:
163190207Srpaulo	case RTC_HRS:
164190207Srpaulo	case RTC_HRS_ALARM:
165190207Srpaulo	case RTC_WDAY:
166190207Srpaulo	case RTC_DAY:
167190207Srpaulo	case RTC_MONTH:
16856893Sfenner	case RTC_YEAR:
169190207Srpaulo	case RTC_STATUSA:
170190207Srpaulo	case RTC_STATUSB:
171190207Srpaulo	case RTC_INTR:
172190207Srpaulo	case RTC_STATUSD:
173190207Srpaulo	case RTC_NVRAM_START ... RTC_NVRAM_END:
174190207Srpaulo		break;
175190207Srpaulo	default:
176190207Srpaulo		return (-1);
177190207Srpaulo	}
178190207Srpaulo
179190207Srpaulo	addr = *eax & 0x7f;
180190207Srpaulo	return (0);
181190207Srpaulo}
182190207Srpaulo
183190207Srpaulostatic int
184190207Srpaulortc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
185190207Srpaulo		 uint32_t *eax, void *arg)
186190207Srpaulo{
187190207Srpaulo	int hour;
188190207Srpaulo	time_t t;
189190207Srpaulo	struct timeval cur, delta;
190190207Srpaulo
191190207Srpaulo	static struct timeval last;
192190207Srpaulo	static struct tm tm;
193190207Srpaulo
194190207Srpaulo	if (bytes != 1)
195190207Srpaulo		return (-1);
196190207Srpaulo
197190207Srpaulo	gettimeofday(&cur, NULL);
198190207Srpaulo
199214478Srpaulo	/*
200190207Srpaulo	 * Increment the cached time only once per second so we can guarantee
201190207Srpaulo	 * that the guest has at least one second to read the hour:min:sec
20256893Sfenner	 * separately and still get a coherent view of the time.
20356893Sfenner	 */
20456893Sfenner	delta = cur;
205127668Sbms	timevalsub(&delta, &last);
206190207Srpaulo	if (delta.tv_sec >= 1 && (status_b & RTCSB_HALT) == 0) {
207190207Srpaulo		t = cur.tv_sec;
208190207Srpaulo		localtime_r(&t, &tm);
209190207Srpaulo		last = cur;
210190207Srpaulo	}
211190207Srpaulo
212190207Srpaulo	if (in) {
213190207Srpaulo		switch (addr) {
21456893Sfenner		case RTC_SEC_ALARM:
21556893Sfenner			*eax = rtc_alarm.secs;
21656893Sfenner			break;
21756893Sfenner		case RTC_MIN_ALARM:
21856893Sfenner			*eax = rtc_alarm.mins;
21956893Sfenner			break;
22056893Sfenner		case RTC_HRS_ALARM:
22156893Sfenner			*eax = rtc_alarm.hours;
222190207Srpaulo			break;
223190207Srpaulo		case RTC_SEC:
224190207Srpaulo			*eax = rtcout(tm.tm_sec);
225190207Srpaulo			return (0);
226190207Srpaulo		case RTC_MIN:
227190207Srpaulo			*eax = rtcout(tm.tm_min);
228190207Srpaulo			return (0);
22956893Sfenner		case RTC_HRS:
23056893Sfenner			if (status_b & RTCSB_24HR)
23156893Sfenner				hour = tm.tm_hour;
23256893Sfenner			else
23356893Sfenner				hour = (tm.tm_hour % 12) + 1;
23456893Sfenner
23556893Sfenner			*eax = rtcout(hour);
23656893Sfenner
23756893Sfenner			/*
23856893Sfenner			 * If we are representing time in the 12-hour format
23956893Sfenner			 * then set the MSB to indicate PM.
24056893Sfenner			 */
24156893Sfenner			if ((status_b & RTCSB_24HR) == 0 && tm.tm_hour >= 12)
24256893Sfenner				*eax |= 0x80;
24356893Sfenner
24456893Sfenner			return (0);
24556893Sfenner		case RTC_WDAY:
24656893Sfenner			*eax = rtcout(tm.tm_wday + 1);
24756893Sfenner			return (0);
24856893Sfenner		case RTC_DAY:
24956893Sfenner			*eax = rtcout(tm.tm_mday);
25056893Sfenner			return (0);
25156893Sfenner		case RTC_MONTH:
25256893Sfenner			*eax = rtcout(tm.tm_mon + 1);
25356893Sfenner			return (0);
25456893Sfenner		case RTC_YEAR:
25556893Sfenner			*eax = rtcout(tm.tm_year % 100);
25656893Sfenner			return (0);
25756893Sfenner		case RTC_STATUSA:
25856893Sfenner			*eax = status_a;
25956893Sfenner			return (0);
26056893Sfenner		case RTC_STATUSB:
26156893Sfenner			*eax = status_b;
26256893Sfenner			return (0);
26356893Sfenner		case RTC_INTR:
26456893Sfenner			*eax = 0;
26556893Sfenner			return (0);
26656893Sfenner		case RTC_STATUSD:
26756893Sfenner			*eax = RTCSD_PWR;
26856893Sfenner			return (0);
26956893Sfenner		case RTC_NVRAM_START ... RTC_NVRAM_END:
27056893Sfenner			*eax = rtc_nvram[addr - RTC_NVRAM_START];
27156893Sfenner			return (0);
27256893Sfenner		default:
27356893Sfenner			return (-1);
27456893Sfenner		}
27556893Sfenner	}
27675115Sfenner
27756893Sfenner	switch (addr) {
27856893Sfenner	case RTC_STATUSA:
27956893Sfenner		status_a = *eax & ~RTCSA_TUP;
28056893Sfenner		break;
28156893Sfenner	case RTC_STATUSB:
28256893Sfenner		/* XXX not implemented yet XXX */
28356893Sfenner		if (*eax & RTCSB_PINTR)
28456893Sfenner			return (-1);
28556893Sfenner		status_b = *eax;
28656893Sfenner		break;
28756893Sfenner	case RTC_STATUSD:
28856893Sfenner		/* ignore write */
28956893Sfenner		break;
29056893Sfenner	case RTC_SEC_ALARM:
29156893Sfenner		rtc_alarm.secs = *eax;
29256893Sfenner		break;
29356893Sfenner	case RTC_MIN_ALARM:
29456893Sfenner		rtc_alarm.mins = *eax;
29556893Sfenner		break;
29656893Sfenner	case RTC_HRS_ALARM:
29756893Sfenner		rtc_alarm.hours = *eax;
29856893Sfenner		break;
29956893Sfenner	case RTC_SEC:
30056893Sfenner	case RTC_MIN:
30156893Sfenner	case RTC_HRS:
30256893Sfenner	case RTC_WDAY:
30356893Sfenner	case RTC_DAY:
30456893Sfenner	case RTC_MONTH:
30556893Sfenner	case RTC_YEAR:
30656893Sfenner		/*
30756893Sfenner		 * Ignore writes to the time of day registers
30856893Sfenner		 */
30956893Sfenner		break;
31056893Sfenner	case RTC_NVRAM_START ... RTC_NVRAM_END:
31156893Sfenner		rtc_nvram[addr - RTC_NVRAM_START] = *eax;
31256893Sfenner		break;
31356893Sfenner	default:
31456893Sfenner		return (-1);
31556893Sfenner	}
31656893Sfenner	return (0);
31756893Sfenner}
31856893Sfenner
31956893Sfennervoid
32056893Sfennerrtc_init(struct vmctx *ctx)
32156893Sfenner{
32256893Sfenner	struct timeval cur;
32356893Sfenner	struct tm tm;
32456893Sfenner	size_t himem;
32556893Sfenner	size_t lomem;
32656893Sfenner	int err;
32756893Sfenner
32856893Sfenner	err = gettimeofday(&cur, NULL);
32956893Sfenner	assert(err == 0);
33056893Sfenner	(void) localtime_r(&cur.tv_sec, &tm);
33156893Sfenner
33256893Sfenner	memset(rtc_nvram, 0, sizeof(rtc_nvram));
33356893Sfenner
33456893Sfenner	rtc_nvram[nvoff(RTC_CENTURY)] = rtcout(tm.tm_year / 100);
33556893Sfenner
33656893Sfenner	/* XXX init diag/reset code/equipment/checksum ? */
33756893Sfenner
33856893Sfenner	/*
33956893Sfenner	 * Report guest memory size in nvram cells as required by UEFI.
34056893Sfenner	 * Little-endian encoding.
34156893Sfenner	 * 0x34/0x35 - 64KB chunks above 16MB, below 4GB
34275115Sfenner	 * 0x5b/0x5c/0x5d - 64KB chunks above 4GB
34356893Sfenner	 */
34456893Sfenner	err = vm_get_memory_seg(ctx, 0, &lomem, NULL);
34556893Sfenner	assert(err == 0);
34656893Sfenner
34756893Sfenner	lomem = (lomem - m_16MB) / m_64KB;
34856893Sfenner	rtc_nvram[nvoff(RTC_LMEM_LSB)] = lomem;
34956893Sfenner	rtc_nvram[nvoff(RTC_LMEM_MSB)] = lomem >> 8;
35056893Sfenner
35156893Sfenner	if (vm_get_memory_seg(ctx, m_4GB, &himem, NULL) == 0) {
35256893Sfenner		himem /= m_64KB;
35356893Sfenner		rtc_nvram[nvoff(RTC_HMEM_LSB)] = himem;
35456893Sfenner		rtc_nvram[nvoff(RTC_HMEM_SB)]  = himem >> 8;
35556893Sfenner		rtc_nvram[nvoff(RTC_HMEM_MSB)] = himem >> 16;
35656893Sfenner	}
35756893Sfenner}
35856893Sfenner
35956893SfennerINOUT_PORT(rtc, IO_RTC, IOPORT_F_INOUT, rtc_addr_handler);
36056893SfennerINOUT_PORT(rtc, IO_RTC + 1, IOPORT_F_INOUT, rtc_data_handler);
36156893Sfenner