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