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 93static int addr; 94 95static uint8_t rtc_nvram[RTC_NVRAM_SZ]; 96 97/* XXX initialize these to default values as they would be from BIOS */ 98static uint8_t status_a, status_b; 99 100static struct { 101 uint8_t hours; 102 uint8_t mins; 103 uint8_t secs; 104} rtc_alarm; 105 106static 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 122static void 123timevalfix(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 136static void 137timevalsub(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 145static int 146rtc_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 183static int 184rtc_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 319void 320rtc_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 359INOUT_PORT(rtc, IO_RTC, IOPORT_F_INOUT, rtc_addr_handler); 360INOUT_PORT(rtc, IO_RTC + 1, IOPORT_F_INOUT, rtc_data_handler); 361