rtc.c revision 245920
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: head/usr.sbin/bhyve/rtc.c 245920 2013-01-25 22:43:20Z grehan $ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD: head/usr.sbin/bhyve/rtc.c 245920 2013-01-25 22:43:20Z grehan $"); 31221828Sgrehan 32221828Sgrehan#include <sys/types.h> 33221828Sgrehan#include <sys/time.h> 34221828Sgrehan 35221828Sgrehan#include <stdio.h> 36221828Sgrehan#include <time.h> 37221828Sgrehan#include <assert.h> 38221828Sgrehan 39221828Sgrehan#include "inout.h" 40221828Sgrehan 41221828Sgrehan#define IO_RTC 0x70 42221828Sgrehan 43221828Sgrehan#define RTC_SEC 0x00 /* seconds */ 44221828Sgrehan#define RTC_MIN 0x02 45221828Sgrehan#define RTC_HRS 0x04 46221828Sgrehan#define RTC_WDAY 0x06 47221828Sgrehan#define RTC_DAY 0x07 48221828Sgrehan#define RTC_MONTH 0x08 49221828Sgrehan#define RTC_YEAR 0x09 50221828Sgrehan#define RTC_CENTURY 0x32 /* current century */ 51221828Sgrehan 52221828Sgrehan#define RTC_STATUSA 0xA 53221828Sgrehan#define RTCSA_TUP 0x80 /* time update, don't look now */ 54221828Sgrehan 55221828Sgrehan#define RTC_STATUSB 0xB 56221828Sgrehan#define RTCSB_DST 0x01 57221828Sgrehan#define RTCSB_24HR 0x02 58221828Sgrehan#define RTCSB_BIN 0x04 /* 0 = BCD, 1 = Binary */ 59221828Sgrehan#define RTCSB_PINTR 0x40 /* 1 = enable periodic clock interrupt */ 60221828Sgrehan#define RTCSB_HALT 0x80 /* stop clock updates */ 61221828Sgrehan 62221828Sgrehan#define RTC_INTR 0x0c /* status register C (R) interrupt source */ 63221828Sgrehan 64221828Sgrehan#define RTC_STATUSD 0x0d /* status register D (R) Lost Power */ 65221828Sgrehan#define RTCSD_PWR 0x80 /* clock power OK */ 66221828Sgrehan 67221828Sgrehan#define RTC_DIAG 0x0e 68221828Sgrehan 69221828Sgrehan#define RTC_RSTCODE 0x0f 70221828Sgrehan 71222105Sgrehan#define RTC_EQUIPMENT 0x14 72222105Sgrehan 73221828Sgrehanstatic int addr; 74221828Sgrehan 75221828Sgrehan/* XXX initialize these to default values as they would be from BIOS */ 76221828Sgrehanstatic uint8_t status_a, status_b, rstcode; 77221828Sgrehan 78221828Sgrehanstatic u_char const bin2bcd_data[] = { 79221828Sgrehan 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 80221828Sgrehan 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 81221828Sgrehan 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 82221828Sgrehan 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 83221828Sgrehan 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 84221828Sgrehan 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 85221828Sgrehan 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 86221828Sgrehan 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 87221828Sgrehan 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 88221828Sgrehan 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99 89221828Sgrehan}; 90221828Sgrehan#define bin2bcd(bin) (bin2bcd_data[bin]) 91221828Sgrehan 92221828Sgrehan#define rtcout(val) ((status_b & RTCSB_BIN) ? (val) : bin2bcd((val))) 93221828Sgrehan 94221828Sgrehanstatic void 95221828Sgrehantimevalfix(struct timeval *t1) 96221828Sgrehan{ 97221828Sgrehan 98221828Sgrehan if (t1->tv_usec < 0) { 99221828Sgrehan t1->tv_sec--; 100221828Sgrehan t1->tv_usec += 1000000; 101221828Sgrehan } 102221828Sgrehan if (t1->tv_usec >= 1000000) { 103221828Sgrehan t1->tv_sec++; 104221828Sgrehan t1->tv_usec -= 1000000; 105221828Sgrehan } 106221828Sgrehan} 107221828Sgrehan 108221828Sgrehanstatic void 109221828Sgrehantimevalsub(struct timeval *t1, const struct timeval *t2) 110221828Sgrehan{ 111221828Sgrehan 112221828Sgrehan t1->tv_sec -= t2->tv_sec; 113221828Sgrehan t1->tv_usec -= t2->tv_usec; 114221828Sgrehan timevalfix(t1); 115221828Sgrehan} 116221828Sgrehan 117221828Sgrehanstatic int 118221828Sgrehanrtc_addr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 119221828Sgrehan uint32_t *eax, void *arg) 120221828Sgrehan{ 121221828Sgrehan if (bytes != 1) 122221828Sgrehan return (-1); 123221828Sgrehan 124245920Sgrehan if (in) { 125245920Sgrehan /* straight read of this register will return 0xFF */ 126245920Sgrehan *eax = 0xff; 127245920Sgrehan return (0); 128245920Sgrehan } 129245920Sgrehan 130245920Sgrehan switch (*eax & 0x7f) { 131221828Sgrehan case RTC_SEC: 132221828Sgrehan case RTC_MIN: 133221828Sgrehan case RTC_HRS: 134221828Sgrehan case RTC_WDAY: 135221828Sgrehan case RTC_DAY: 136221828Sgrehan case RTC_MONTH: 137221828Sgrehan case RTC_YEAR: 138221828Sgrehan case RTC_CENTURY: 139221828Sgrehan case RTC_STATUSA: 140221828Sgrehan case RTC_STATUSB: 141221828Sgrehan case RTC_INTR: 142221828Sgrehan case RTC_STATUSD: 143221828Sgrehan case RTC_DIAG: 144221828Sgrehan case RTC_RSTCODE: 145222105Sgrehan case RTC_EQUIPMENT: 146221828Sgrehan break; 147221828Sgrehan default: 148221828Sgrehan return (-1); 149221828Sgrehan } 150221828Sgrehan 151245920Sgrehan addr = *eax & 0x7f; 152221828Sgrehan return (0); 153221828Sgrehan} 154221828Sgrehan 155221828Sgrehanstatic int 156221828Sgrehanrtc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 157221828Sgrehan uint32_t *eax, void *arg) 158221828Sgrehan{ 159221828Sgrehan int hour; 160221828Sgrehan time_t t; 161221828Sgrehan struct timeval cur, delta; 162221828Sgrehan 163221828Sgrehan static struct timeval last; 164221828Sgrehan static struct tm tm; 165221828Sgrehan 166221828Sgrehan if (bytes != 1) 167221828Sgrehan return (-1); 168221828Sgrehan 169221828Sgrehan gettimeofday(&cur, NULL); 170221828Sgrehan 171221828Sgrehan /* 172221828Sgrehan * Increment the cached time only once per second so we can guarantee 173221828Sgrehan * that the guest has at least one second to read the hour:min:sec 174221828Sgrehan * separately and still get a coherent view of the time. 175221828Sgrehan */ 176221828Sgrehan delta = cur; 177221828Sgrehan timevalsub(&delta, &last); 178221828Sgrehan if (delta.tv_sec >= 1 && (status_b & RTCSB_HALT) == 0) { 179221828Sgrehan t = cur.tv_sec; 180221828Sgrehan localtime_r(&t, &tm); 181221828Sgrehan last = cur; 182221828Sgrehan } 183221828Sgrehan 184221828Sgrehan if (in) { 185221828Sgrehan switch (addr) { 186221828Sgrehan case RTC_SEC: 187221828Sgrehan *eax = rtcout(tm.tm_sec); 188221828Sgrehan return (0); 189221828Sgrehan case RTC_MIN: 190221828Sgrehan *eax = rtcout(tm.tm_min); 191221828Sgrehan return (0); 192221828Sgrehan case RTC_HRS: 193221828Sgrehan if (status_b & RTCSB_24HR) 194221828Sgrehan hour = tm.tm_hour; 195221828Sgrehan else 196221828Sgrehan hour = (tm.tm_hour % 12) + 1; 197221828Sgrehan 198221828Sgrehan *eax = rtcout(hour); 199221828Sgrehan 200221828Sgrehan /* 201221828Sgrehan * If we are representing time in the 12-hour format 202221828Sgrehan * then set the MSB to indicate PM. 203221828Sgrehan */ 204221828Sgrehan if ((status_b & RTCSB_24HR) == 0 && tm.tm_hour >= 12) 205221828Sgrehan *eax |= 0x80; 206221828Sgrehan 207221828Sgrehan return (0); 208221828Sgrehan case RTC_WDAY: 209221828Sgrehan *eax = rtcout(tm.tm_wday + 1); 210221828Sgrehan return (0); 211221828Sgrehan case RTC_DAY: 212221828Sgrehan *eax = rtcout(tm.tm_mday); 213221828Sgrehan return (0); 214221828Sgrehan case RTC_MONTH: 215221828Sgrehan *eax = rtcout(tm.tm_mon + 1); 216221828Sgrehan return (0); 217221828Sgrehan case RTC_YEAR: 218221828Sgrehan *eax = rtcout(tm.tm_year % 100); 219221828Sgrehan return (0); 220221828Sgrehan case RTC_CENTURY: 221221828Sgrehan *eax = rtcout(tm.tm_year / 100); 222221828Sgrehan break; 223221828Sgrehan case RTC_STATUSA: 224221828Sgrehan *eax = status_a; 225221828Sgrehan return (0); 226245920Sgrehan case RTC_STATUSB: 227245920Sgrehan *eax = status_b; 228245920Sgrehan return (0); 229221828Sgrehan case RTC_INTR: 230221828Sgrehan *eax = 0; 231221828Sgrehan return (0); 232221828Sgrehan case RTC_STATUSD: 233221828Sgrehan *eax = RTCSD_PWR; 234221828Sgrehan return (0); 235221828Sgrehan case RTC_DIAG: 236221828Sgrehan *eax = 0; 237221828Sgrehan return (0); 238221828Sgrehan case RTC_RSTCODE: 239221828Sgrehan *eax = rstcode; 240221828Sgrehan return (0); 241222105Sgrehan case RTC_EQUIPMENT: 242222105Sgrehan *eax = 0; 243222105Sgrehan return (0); 244221828Sgrehan default: 245221828Sgrehan return (-1); 246221828Sgrehan } 247221828Sgrehan } 248221828Sgrehan 249221828Sgrehan switch (addr) { 250221828Sgrehan case RTC_STATUSA: 251221828Sgrehan status_a = *eax & ~RTCSA_TUP; 252221828Sgrehan break; 253221828Sgrehan case RTC_STATUSB: 254221828Sgrehan /* XXX not implemented yet XXX */ 255221828Sgrehan if (*eax & RTCSB_PINTR) 256221828Sgrehan return (-1); 257221828Sgrehan status_b = *eax; 258221828Sgrehan break; 259245920Sgrehan case RTC_STATUSD: 260245920Sgrehan /* ignore write */ 261245920Sgrehan break; 262221828Sgrehan case RTC_RSTCODE: 263221828Sgrehan rstcode = *eax; 264221828Sgrehan break; 265221828Sgrehan case RTC_SEC: 266221828Sgrehan case RTC_MIN: 267221828Sgrehan case RTC_HRS: 268221828Sgrehan case RTC_WDAY: 269221828Sgrehan case RTC_DAY: 270221828Sgrehan case RTC_MONTH: 271221828Sgrehan case RTC_YEAR: 272221828Sgrehan case RTC_CENTURY: 273221828Sgrehan /* 274221828Sgrehan * Ignore writes to the time of day registers 275221828Sgrehan */ 276221828Sgrehan break; 277221828Sgrehan default: 278221828Sgrehan return (-1); 279221828Sgrehan } 280221828Sgrehan return (0); 281221828Sgrehan} 282221828Sgrehan 283245920SgrehanINOUT_PORT(rtc, IO_RTC, IOPORT_F_INOUT, rtc_addr_handler); 284221828SgrehanINOUT_PORT(rtc, IO_RTC + 1, IOPORT_F_INOUT, rtc_data_handler); 285