]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/bhyve/rtc.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / bhyve / rtc.c
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
93 static int addr;
94
95 static uint8_t rtc_nvram[RTC_NVRAM_SZ];
96
97 /* XXX initialize these to default values as they would be from BIOS */
98 static uint8_t status_a, status_b;
99
100 static struct {
101         uint8_t  hours;
102         uint8_t  mins;
103         uint8_t  secs;
104 } rtc_alarm;
105
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
117 };
118 #define bin2bcd(bin)    (bin2bcd_data[bin])
119
120 #define rtcout(val)     ((status_b & RTCSB_BIN) ? (val) : bin2bcd((val)))
121
122 static void
123 timevalfix(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
136 static void
137 timevalsub(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
145 static int
146 rtc_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
183 static int
184 rtc_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
319 void
320 rtc_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
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);