rtc.c revision 222105
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 <time.h> 37#include <assert.h> 38 39#include "inout.h" 40 41#define IO_RTC 0x70 42 43#define RTC_SEC 0x00 /* seconds */ 44#define RTC_MIN 0x02 45#define RTC_HRS 0x04 46#define RTC_WDAY 0x06 47#define RTC_DAY 0x07 48#define RTC_MONTH 0x08 49#define RTC_YEAR 0x09 50#define RTC_CENTURY 0x32 /* current century */ 51 52#define RTC_STATUSA 0xA 53#define RTCSA_TUP 0x80 /* time update, don't look now */ 54 55#define RTC_STATUSB 0xB 56#define RTCSB_DST 0x01 57#define RTCSB_24HR 0x02 58#define RTCSB_BIN 0x04 /* 0 = BCD, 1 = Binary */ 59#define RTCSB_PINTR 0x40 /* 1 = enable periodic clock interrupt */ 60#define RTCSB_HALT 0x80 /* stop clock updates */ 61 62#define RTC_INTR 0x0c /* status register C (R) interrupt source */ 63 64#define RTC_STATUSD 0x0d /* status register D (R) Lost Power */ 65#define RTCSD_PWR 0x80 /* clock power OK */ 66 67#define RTC_DIAG 0x0e 68 69#define RTC_RSTCODE 0x0f 70 71#define RTC_EQUIPMENT 0x14 72 73static int addr; 74 75/* XXX initialize these to default values as they would be from BIOS */ 76static uint8_t status_a, status_b, rstcode; 77 78static u_char const bin2bcd_data[] = { 79 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 80 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 81 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 82 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 83 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 84 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 85 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 86 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 87 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 88 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99 89}; 90#define bin2bcd(bin) (bin2bcd_data[bin]) 91 92#define rtcout(val) ((status_b & RTCSB_BIN) ? (val) : bin2bcd((val))) 93 94static void 95timevalfix(struct timeval *t1) 96{ 97 98 if (t1->tv_usec < 0) { 99 t1->tv_sec--; 100 t1->tv_usec += 1000000; 101 } 102 if (t1->tv_usec >= 1000000) { 103 t1->tv_sec++; 104 t1->tv_usec -= 1000000; 105 } 106} 107 108static void 109timevalsub(struct timeval *t1, const struct timeval *t2) 110{ 111 112 t1->tv_sec -= t2->tv_sec; 113 t1->tv_usec -= t2->tv_usec; 114 timevalfix(t1); 115} 116 117static int 118rtc_addr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 119 uint32_t *eax, void *arg) 120{ 121 assert(in == 0); 122 123 if (bytes != 1) 124 return (-1); 125 126 switch (*eax) { 127 case RTC_SEC: 128 case RTC_MIN: 129 case RTC_HRS: 130 case RTC_WDAY: 131 case RTC_DAY: 132 case RTC_MONTH: 133 case RTC_YEAR: 134 case RTC_CENTURY: 135 case RTC_STATUSA: 136 case RTC_STATUSB: 137 case RTC_INTR: 138 case RTC_STATUSD: 139 case RTC_DIAG: 140 case RTC_RSTCODE: 141 case RTC_EQUIPMENT: 142 break; 143 default: 144 return (-1); 145 } 146 147 addr = *eax; 148 return (0); 149} 150 151static int 152rtc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 153 uint32_t *eax, void *arg) 154{ 155 int hour; 156 time_t t; 157 struct timeval cur, delta; 158 159 static struct timeval last; 160 static struct tm tm; 161 162 if (bytes != 1) 163 return (-1); 164 165 gettimeofday(&cur, NULL); 166 167 /* 168 * Increment the cached time only once per second so we can guarantee 169 * that the guest has at least one second to read the hour:min:sec 170 * separately and still get a coherent view of the time. 171 */ 172 delta = cur; 173 timevalsub(&delta, &last); 174 if (delta.tv_sec >= 1 && (status_b & RTCSB_HALT) == 0) { 175 t = cur.tv_sec; 176 localtime_r(&t, &tm); 177 last = cur; 178 } 179 180 if (in) { 181 switch (addr) { 182 case RTC_SEC: 183 *eax = rtcout(tm.tm_sec); 184 return (0); 185 case RTC_MIN: 186 *eax = rtcout(tm.tm_min); 187 return (0); 188 case RTC_HRS: 189 if (status_b & RTCSB_24HR) 190 hour = tm.tm_hour; 191 else 192 hour = (tm.tm_hour % 12) + 1; 193 194 *eax = rtcout(hour); 195 196 /* 197 * If we are representing time in the 12-hour format 198 * then set the MSB to indicate PM. 199 */ 200 if ((status_b & RTCSB_24HR) == 0 && tm.tm_hour >= 12) 201 *eax |= 0x80; 202 203 return (0); 204 case RTC_WDAY: 205 *eax = rtcout(tm.tm_wday + 1); 206 return (0); 207 case RTC_DAY: 208 *eax = rtcout(tm.tm_mday); 209 return (0); 210 case RTC_MONTH: 211 *eax = rtcout(tm.tm_mon + 1); 212 return (0); 213 case RTC_YEAR: 214 *eax = rtcout(tm.tm_year % 100); 215 return (0); 216 case RTC_CENTURY: 217 *eax = rtcout(tm.tm_year / 100); 218 break; 219 case RTC_STATUSA: 220 *eax = status_a; 221 return (0); 222 case RTC_INTR: 223 *eax = 0; 224 return (0); 225 case RTC_STATUSD: 226 *eax = RTCSD_PWR; 227 return (0); 228 case RTC_DIAG: 229 *eax = 0; 230 return (0); 231 case RTC_RSTCODE: 232 *eax = rstcode; 233 return (0); 234 case RTC_EQUIPMENT: 235 *eax = 0; 236 return (0); 237 default: 238 return (-1); 239 } 240 } 241 242 switch (addr) { 243 case RTC_STATUSA: 244 status_a = *eax & ~RTCSA_TUP; 245 break; 246 case RTC_STATUSB: 247 /* XXX not implemented yet XXX */ 248 if (*eax & RTCSB_PINTR) 249 return (-1); 250 status_b = *eax; 251 break; 252 case RTC_RSTCODE: 253 rstcode = *eax; 254 break; 255 case RTC_SEC: 256 case RTC_MIN: 257 case RTC_HRS: 258 case RTC_WDAY: 259 case RTC_DAY: 260 case RTC_MONTH: 261 case RTC_YEAR: 262 case RTC_CENTURY: 263 /* 264 * Ignore writes to the time of day registers 265 */ 266 break; 267 default: 268 return (-1); 269 } 270 return (0); 271} 272 273INOUT_PORT(rtc, IO_RTC, IOPORT_F_OUT, rtc_addr_handler); 274INOUT_PORT(rtc, IO_RTC + 1, IOPORT_F_INOUT, rtc_data_handler); 275