1221828Sgrehan/*- 2221828Sgrehan * Copyright (c) 2011 NetApp, Inc. 3221828Sgrehan * All rights reserved. 4221828Sgrehan * 5221828Sgrehan * Redistribution and use in source and binary forms, with or without 6221828Sgrehan * modification, are permitted provided that the following conditions 7221828Sgrehan * are met: 8221828Sgrehan * 1. Redistributions of source code must retain the above copyright 9221828Sgrehan * notice, this list of conditions and the following disclaimer. 10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11221828Sgrehan * notice, this list of conditions and the following disclaimer in the 12221828Sgrehan * documentation and/or other materials provided with the distribution. 13221828Sgrehan * 14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17221828Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24221828Sgrehan * SUCH DAMAGE. 25221828Sgrehan * 26221828Sgrehan * $FreeBSD$ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD$"); 31221828Sgrehan 32221828Sgrehan#include <sys/types.h> 33221828Sgrehan#include <sys/time.h> 34221828Sgrehan 35221828Sgrehan#include <stdio.h> 36253181Sgrehan#include <string.h> 37221828Sgrehan#include <time.h> 38221828Sgrehan#include <assert.h> 39221828Sgrehan 40253181Sgrehan#include <machine/vmm.h> 41253181Sgrehan#include <vmmapi.h> 42253181Sgrehan 43221828Sgrehan#include "inout.h" 44253181Sgrehan#include "rtc.h" 45221828Sgrehan 46221828Sgrehan#define IO_RTC 0x70 47221828Sgrehan 48221828Sgrehan#define RTC_SEC 0x00 /* seconds */ 49255689Sgrehan#define RTC_SEC_ALARM 0x01 50221828Sgrehan#define RTC_MIN 0x02 51255689Sgrehan#define RTC_MIN_ALARM 0x03 52221828Sgrehan#define RTC_HRS 0x04 53255689Sgrehan#define RTC_HRS_ALARM 0x05 54221828Sgrehan#define RTC_WDAY 0x06 55221828Sgrehan#define RTC_DAY 0x07 56221828Sgrehan#define RTC_MONTH 0x08 57221828Sgrehan#define RTC_YEAR 0x09 58221828Sgrehan#define RTC_CENTURY 0x32 /* current century */ 59221828Sgrehan 60221828Sgrehan#define RTC_STATUSA 0xA 61221828Sgrehan#define RTCSA_TUP 0x80 /* time update, don't look now */ 62221828Sgrehan 63221828Sgrehan#define RTC_STATUSB 0xB 64221828Sgrehan#define RTCSB_DST 0x01 65221828Sgrehan#define RTCSB_24HR 0x02 66221828Sgrehan#define RTCSB_BIN 0x04 /* 0 = BCD, 1 = Binary */ 67221828Sgrehan#define RTCSB_PINTR 0x40 /* 1 = enable periodic clock interrupt */ 68221828Sgrehan#define RTCSB_HALT 0x80 /* stop clock updates */ 69221828Sgrehan 70221828Sgrehan#define RTC_INTR 0x0c /* status register C (R) interrupt source */ 71221828Sgrehan 72221828Sgrehan#define RTC_STATUSD 0x0d /* status register D (R) Lost Power */ 73221828Sgrehan#define RTCSD_PWR 0x80 /* clock power OK */ 74221828Sgrehan 75253181Sgrehan#define RTC_NVRAM_START 0x0e 76253181Sgrehan#define RTC_NVRAM_END 0x7f 77253181Sgrehan#define RTC_NVRAM_SZ (128 - RTC_NVRAM_START) 78253181Sgrehan#define nvoff(x) ((x) - RTC_NVRAM_START) 79253181Sgrehan 80221828Sgrehan#define RTC_DIAG 0x0e 81221828Sgrehan#define RTC_RSTCODE 0x0f 82222105Sgrehan#define RTC_EQUIPMENT 0x14 83253181Sgrehan#define RTC_LMEM_LSB 0x34 84253181Sgrehan#define RTC_LMEM_MSB 0x35 85253181Sgrehan#define RTC_HMEM_LSB 0x5b 86253181Sgrehan#define RTC_HMEM_SB 0x5c 87253181Sgrehan#define RTC_HMEM_MSB 0x5d 88222105Sgrehan 89253181Sgrehan#define m_64KB (64*1024) 90253181Sgrehan#define m_16MB (16*1024*1024) 91253181Sgrehan#define m_4GB (4ULL*1024*1024*1024) 92253181Sgrehan 93221828Sgrehanstatic int addr; 94221828Sgrehan 95253181Sgrehanstatic uint8_t rtc_nvram[RTC_NVRAM_SZ]; 96253181Sgrehan 97221828Sgrehan/* XXX initialize these to default values as they would be from BIOS */ 98253181Sgrehanstatic uint8_t status_a, status_b; 99221828Sgrehan 100255689Sgrehanstatic struct { 101255689Sgrehan uint8_t hours; 102255689Sgrehan uint8_t mins; 103255689Sgrehan uint8_t secs; 104255689Sgrehan} rtc_alarm; 105255689Sgrehan 106221828Sgrehanstatic u_char const bin2bcd_data[] = { 107221828Sgrehan 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 108221828Sgrehan 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 109221828Sgrehan 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 110221828Sgrehan 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 111221828Sgrehan 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 112221828Sgrehan 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 113221828Sgrehan 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 114221828Sgrehan 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 115221828Sgrehan 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 116221828Sgrehan 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99 117221828Sgrehan}; 118221828Sgrehan#define bin2bcd(bin) (bin2bcd_data[bin]) 119221828Sgrehan 120221828Sgrehan#define rtcout(val) ((status_b & RTCSB_BIN) ? (val) : bin2bcd((val))) 121221828Sgrehan 122221828Sgrehanstatic void 123221828Sgrehantimevalfix(struct timeval *t1) 124221828Sgrehan{ 125221828Sgrehan 126221828Sgrehan if (t1->tv_usec < 0) { 127221828Sgrehan t1->tv_sec--; 128221828Sgrehan t1->tv_usec += 1000000; 129221828Sgrehan } 130221828Sgrehan if (t1->tv_usec >= 1000000) { 131221828Sgrehan t1->tv_sec++; 132221828Sgrehan t1->tv_usec -= 1000000; 133221828Sgrehan } 134221828Sgrehan} 135221828Sgrehan 136221828Sgrehanstatic void 137221828Sgrehantimevalsub(struct timeval *t1, const struct timeval *t2) 138221828Sgrehan{ 139221828Sgrehan 140221828Sgrehan t1->tv_sec -= t2->tv_sec; 141221828Sgrehan t1->tv_usec -= t2->tv_usec; 142221828Sgrehan timevalfix(t1); 143221828Sgrehan} 144221828Sgrehan 145221828Sgrehanstatic int 146221828Sgrehanrtc_addr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 147221828Sgrehan uint32_t *eax, void *arg) 148221828Sgrehan{ 149221828Sgrehan if (bytes != 1) 150221828Sgrehan return (-1); 151221828Sgrehan 152245920Sgrehan if (in) { 153245920Sgrehan /* straight read of this register will return 0xFF */ 154245920Sgrehan *eax = 0xff; 155245920Sgrehan return (0); 156245920Sgrehan } 157245920Sgrehan 158245920Sgrehan switch (*eax & 0x7f) { 159221828Sgrehan case RTC_SEC: 160255689Sgrehan case RTC_SEC_ALARM: 161221828Sgrehan case RTC_MIN: 162255689Sgrehan case RTC_MIN_ALARM: 163221828Sgrehan case RTC_HRS: 164255689Sgrehan case RTC_HRS_ALARM: 165221828Sgrehan case RTC_WDAY: 166221828Sgrehan case RTC_DAY: 167221828Sgrehan case RTC_MONTH: 168221828Sgrehan case RTC_YEAR: 169221828Sgrehan case RTC_STATUSA: 170221828Sgrehan case RTC_STATUSB: 171221828Sgrehan case RTC_INTR: 172221828Sgrehan case RTC_STATUSD: 173253181Sgrehan case RTC_NVRAM_START ... RTC_NVRAM_END: 174221828Sgrehan break; 175221828Sgrehan default: 176221828Sgrehan return (-1); 177221828Sgrehan } 178221828Sgrehan 179245920Sgrehan addr = *eax & 0x7f; 180221828Sgrehan return (0); 181221828Sgrehan} 182221828Sgrehan 183221828Sgrehanstatic int 184221828Sgrehanrtc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 185221828Sgrehan uint32_t *eax, void *arg) 186221828Sgrehan{ 187221828Sgrehan int hour; 188221828Sgrehan time_t t; 189221828Sgrehan struct timeval cur, delta; 190221828Sgrehan 191221828Sgrehan static struct timeval last; 192221828Sgrehan static struct tm tm; 193221828Sgrehan 194221828Sgrehan if (bytes != 1) 195221828Sgrehan return (-1); 196221828Sgrehan 197221828Sgrehan gettimeofday(&cur, NULL); 198221828Sgrehan 199221828Sgrehan /* 200221828Sgrehan * Increment the cached time only once per second so we can guarantee 201221828Sgrehan * that the guest has at least one second to read the hour:min:sec 202221828Sgrehan * separately and still get a coherent view of the time. 203221828Sgrehan */ 204221828Sgrehan delta = cur; 205221828Sgrehan timevalsub(&delta, &last); 206221828Sgrehan if (delta.tv_sec >= 1 && (status_b & RTCSB_HALT) == 0) { 207221828Sgrehan t = cur.tv_sec; 208221828Sgrehan localtime_r(&t, &tm); 209221828Sgrehan last = cur; 210221828Sgrehan } 211221828Sgrehan 212221828Sgrehan if (in) { 213221828Sgrehan switch (addr) { 214255689Sgrehan case RTC_SEC_ALARM: 215255689Sgrehan *eax = rtc_alarm.secs; 216255689Sgrehan break; 217255689Sgrehan case RTC_MIN_ALARM: 218255689Sgrehan *eax = rtc_alarm.mins; 219255689Sgrehan break; 220255689Sgrehan case RTC_HRS_ALARM: 221255689Sgrehan *eax = rtc_alarm.hours; 222255689Sgrehan break; 223221828Sgrehan case RTC_SEC: 224221828Sgrehan *eax = rtcout(tm.tm_sec); 225221828Sgrehan return (0); 226221828Sgrehan case RTC_MIN: 227221828Sgrehan *eax = rtcout(tm.tm_min); 228221828Sgrehan return (0); 229221828Sgrehan case RTC_HRS: 230221828Sgrehan if (status_b & RTCSB_24HR) 231221828Sgrehan hour = tm.tm_hour; 232221828Sgrehan else 233221828Sgrehan hour = (tm.tm_hour % 12) + 1; 234221828Sgrehan 235221828Sgrehan *eax = rtcout(hour); 236221828Sgrehan 237221828Sgrehan /* 238221828Sgrehan * If we are representing time in the 12-hour format 239221828Sgrehan * then set the MSB to indicate PM. 240221828Sgrehan */ 241221828Sgrehan if ((status_b & RTCSB_24HR) == 0 && tm.tm_hour >= 12) 242221828Sgrehan *eax |= 0x80; 243221828Sgrehan 244221828Sgrehan return (0); 245221828Sgrehan case RTC_WDAY: 246221828Sgrehan *eax = rtcout(tm.tm_wday + 1); 247221828Sgrehan return (0); 248221828Sgrehan case RTC_DAY: 249221828Sgrehan *eax = rtcout(tm.tm_mday); 250221828Sgrehan return (0); 251221828Sgrehan case RTC_MONTH: 252221828Sgrehan *eax = rtcout(tm.tm_mon + 1); 253221828Sgrehan return (0); 254221828Sgrehan case RTC_YEAR: 255221828Sgrehan *eax = rtcout(tm.tm_year % 100); 256221828Sgrehan return (0); 257221828Sgrehan case RTC_STATUSA: 258221828Sgrehan *eax = status_a; 259221828Sgrehan return (0); 260245920Sgrehan case RTC_STATUSB: 261245920Sgrehan *eax = status_b; 262245920Sgrehan return (0); 263221828Sgrehan case RTC_INTR: 264221828Sgrehan *eax = 0; 265221828Sgrehan return (0); 266221828Sgrehan case RTC_STATUSD: 267221828Sgrehan *eax = RTCSD_PWR; 268221828Sgrehan return (0); 269253181Sgrehan case RTC_NVRAM_START ... RTC_NVRAM_END: 270253181Sgrehan *eax = rtc_nvram[addr - RTC_NVRAM_START]; 271221828Sgrehan return (0); 272221828Sgrehan default: 273221828Sgrehan return (-1); 274221828Sgrehan } 275221828Sgrehan } 276221828Sgrehan 277221828Sgrehan switch (addr) { 278221828Sgrehan case RTC_STATUSA: 279221828Sgrehan status_a = *eax & ~RTCSA_TUP; 280221828Sgrehan break; 281221828Sgrehan case RTC_STATUSB: 282221828Sgrehan /* XXX not implemented yet XXX */ 283221828Sgrehan if (*eax & RTCSB_PINTR) 284221828Sgrehan return (-1); 285221828Sgrehan status_b = *eax; 286221828Sgrehan break; 287245920Sgrehan case RTC_STATUSD: 288245920Sgrehan /* ignore write */ 289245920Sgrehan break; 290255689Sgrehan case RTC_SEC_ALARM: 291255689Sgrehan rtc_alarm.secs = *eax; 292255689Sgrehan break; 293255689Sgrehan case RTC_MIN_ALARM: 294255689Sgrehan rtc_alarm.mins = *eax; 295255689Sgrehan break; 296255689Sgrehan case RTC_HRS_ALARM: 297255689Sgrehan rtc_alarm.hours = *eax; 298255689Sgrehan break; 299221828Sgrehan case RTC_SEC: 300221828Sgrehan case RTC_MIN: 301221828Sgrehan case RTC_HRS: 302221828Sgrehan case RTC_WDAY: 303221828Sgrehan case RTC_DAY: 304221828Sgrehan case RTC_MONTH: 305221828Sgrehan case RTC_YEAR: 306221828Sgrehan /* 307221828Sgrehan * Ignore writes to the time of day registers 308221828Sgrehan */ 309221828Sgrehan break; 310253181Sgrehan case RTC_NVRAM_START ... RTC_NVRAM_END: 311253181Sgrehan rtc_nvram[addr - RTC_NVRAM_START] = *eax; 312253181Sgrehan break; 313221828Sgrehan default: 314221828Sgrehan return (-1); 315221828Sgrehan } 316221828Sgrehan return (0); 317221828Sgrehan} 318221828Sgrehan 319253181Sgrehanvoid 320253181Sgrehanrtc_init(struct vmctx *ctx) 321253181Sgrehan{ 322253181Sgrehan struct timeval cur; 323253181Sgrehan struct tm tm; 324253181Sgrehan size_t himem; 325253181Sgrehan size_t lomem; 326253181Sgrehan int err; 327253181Sgrehan 328253181Sgrehan err = gettimeofday(&cur, NULL); 329253181Sgrehan assert(err == 0); 330253181Sgrehan (void) localtime_r(&cur.tv_sec, &tm); 331253181Sgrehan 332253181Sgrehan memset(rtc_nvram, 0, sizeof(rtc_nvram)); 333253181Sgrehan 334256755Sgrehan rtc_nvram[nvoff(RTC_CENTURY)] = bin2bcd((tm.tm_year + 1900) / 100); 335253181Sgrehan 336253181Sgrehan /* XXX init diag/reset code/equipment/checksum ? */ 337253181Sgrehan 338253181Sgrehan /* 339253181Sgrehan * Report guest memory size in nvram cells as required by UEFI. 340253181Sgrehan * Little-endian encoding. 341253181Sgrehan * 0x34/0x35 - 64KB chunks above 16MB, below 4GB 342253181Sgrehan * 0x5b/0x5c/0x5d - 64KB chunks above 4GB 343253181Sgrehan */ 344256072Sneel err = vm_get_memory_seg(ctx, 0, &lomem, NULL); 345253181Sgrehan assert(err == 0); 346253181Sgrehan 347253181Sgrehan lomem = (lomem - m_16MB) / m_64KB; 348253181Sgrehan rtc_nvram[nvoff(RTC_LMEM_LSB)] = lomem; 349253181Sgrehan rtc_nvram[nvoff(RTC_LMEM_MSB)] = lomem >> 8; 350253181Sgrehan 351256072Sneel if (vm_get_memory_seg(ctx, m_4GB, &himem, NULL) == 0) { 352253181Sgrehan himem /= m_64KB; 353253181Sgrehan rtc_nvram[nvoff(RTC_HMEM_LSB)] = himem; 354253181Sgrehan rtc_nvram[nvoff(RTC_HMEM_SB)] = himem >> 8; 355255688Sgrehan rtc_nvram[nvoff(RTC_HMEM_MSB)] = himem >> 16; 356253181Sgrehan } 357253181Sgrehan} 358253181Sgrehan 359245920SgrehanINOUT_PORT(rtc, IO_RTC, IOPORT_F_INOUT, rtc_addr_handler); 360221828SgrehanINOUT_PORT(rtc, IO_RTC + 1, IOPORT_F_INOUT, rtc_data_handler); 361