2 * Copyright (c) 2011 NetApp, Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/types.h>
40 #include <machine/vmm.h>
48 #define RTC_SEC 0x00 /* seconds */
49 #define RTC_SEC_ALARM 0x01
51 #define RTC_MIN_ALARM 0x03
53 #define RTC_HRS_ALARM 0x05
56 #define RTC_MONTH 0x08
58 #define RTC_CENTURY 0x32 /* current century */
60 #define RTC_STATUSA 0xA
61 #define RTCSA_TUP 0x80 /* time update, don't look now */
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 */
70 #define RTC_INTR 0x0c /* status register C (R) interrupt source */
72 #define RTC_STATUSD 0x0d /* status register D (R) Lost Power */
73 #define RTCSD_PWR 0x80 /* clock power OK */
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)
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
89 #define m_64KB (64*1024)
90 #define m_16MB (16*1024*1024)
91 #define m_4GB (4ULL*1024*1024*1024)
95 static uint8_t rtc_nvram[RTC_NVRAM_SZ];
97 /* XXX initialize these to default values as they would be from BIOS */
98 static uint8_t status_a, status_b;
106 static 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
118 #define bin2bcd(bin) (bin2bcd_data[bin])
120 #define rtcout(val) ((status_b & RTCSB_BIN) ? (val) : bin2bcd((val)))
123 timevalfix(struct timeval *t1)
126 if (t1->tv_usec < 0) {
128 t1->tv_usec += 1000000;
130 if (t1->tv_usec >= 1000000) {
132 t1->tv_usec -= 1000000;
137 timevalsub(struct timeval *t1, const struct timeval *t2)
140 t1->tv_sec -= t2->tv_sec;
141 t1->tv_usec -= t2->tv_usec;
146 rtc_addr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
147 uint32_t *eax, void *arg)
153 /* straight read of this register will return 0xFF */
158 switch (*eax & 0x7f) {
173 case RTC_NVRAM_START ... RTC_NVRAM_END:
184 rtc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
185 uint32_t *eax, void *arg)
189 struct timeval cur, delta;
191 static struct timeval last;
197 gettimeofday(&cur, NULL);
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.
205 timevalsub(&delta, &last);
206 if (delta.tv_sec >= 1 && (status_b & RTCSB_HALT) == 0) {
208 localtime_r(&t, &tm);
215 *eax = rtc_alarm.secs;
218 *eax = rtc_alarm.mins;
221 *eax = rtc_alarm.hours;
224 *eax = rtcout(tm.tm_sec);
227 *eax = rtcout(tm.tm_min);
230 if (status_b & RTCSB_24HR)
233 hour = (tm.tm_hour % 12) + 1;
238 * If we are representing time in the 12-hour format
239 * then set the MSB to indicate PM.
241 if ((status_b & RTCSB_24HR) == 0 && tm.tm_hour >= 12)
246 *eax = rtcout(tm.tm_wday + 1);
249 *eax = rtcout(tm.tm_mday);
252 *eax = rtcout(tm.tm_mon + 1);
255 *eax = rtcout(tm.tm_year % 100);
269 case RTC_NVRAM_START ... RTC_NVRAM_END:
270 *eax = rtc_nvram[addr - RTC_NVRAM_START];
279 status_a = *eax & ~RTCSA_TUP;
282 /* XXX not implemented yet XXX */
283 if (*eax & RTCSB_PINTR)
291 rtc_alarm.secs = *eax;
294 rtc_alarm.mins = *eax;
297 rtc_alarm.hours = *eax;
307 * Ignore writes to the time of day registers
310 case RTC_NVRAM_START ... RTC_NVRAM_END:
311 rtc_nvram[addr - RTC_NVRAM_START] = *eax;
320 rtc_init(struct vmctx *ctx)
328 err = gettimeofday(&cur, NULL);
330 (void) localtime_r(&cur.tv_sec, &tm);
332 memset(rtc_nvram, 0, sizeof(rtc_nvram));
334 rtc_nvram[nvoff(RTC_CENTURY)] = bin2bcd((tm.tm_year + 1900) / 100);
336 /* XXX init diag/reset code/equipment/checksum ? */
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
344 err = vm_get_memory_seg(ctx, 0, &lomem, NULL);
347 lomem = (lomem - m_16MB) / m_64KB;
348 rtc_nvram[nvoff(RTC_LMEM_LSB)] = lomem;
349 rtc_nvram[nvoff(RTC_LMEM_MSB)] = lomem >> 8;
351 if (vm_get_memory_seg(ctx, m_4GB, &himem, NULL) == 0) {
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;
359 INOUT_PORT(rtc, IO_RTC, IOPORT_F_INOUT, rtc_addr_handler);
360 INOUT_PORT(rtc, IO_RTC + 1, IOPORT_F_INOUT, rtc_data_handler);