rtc.c revision 256072
156893Sfenner/*- 256893Sfenner * Copyright (c) 2011 NetApp, Inc. 356893Sfenner * All rights reserved. 4127668Sbms * 556893Sfenner * Redistribution and use in source and binary forms, with or without 656893Sfenner * modification, are permitted provided that the following conditions 756893Sfenner * are met: 856893Sfenner * 1. Redistributions of source code must retain the above copyright 956893Sfenner * notice, this list of conditions and the following disclaimer. 1056893Sfenner * 2. Redistributions in binary form must reproduce the above copyright 1156893Sfenner * notice, this list of conditions and the following disclaimer in the 1256893Sfenner * documentation and/or other materials provided with the distribution. 1356893Sfenner * 1456893Sfenner * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 1556893Sfenner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16127668Sbms * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1756893Sfenner * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 1856893Sfenner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1956893Sfenner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2056893Sfenner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2156893Sfenner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2256893Sfenner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2356893Sfenner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2456893Sfenner * SUCH DAMAGE. 2556893Sfenner * 2656893Sfenner * $FreeBSD: head/usr.sbin/bhyve/rtc.c 256072 2013-10-05 21:22:35Z neel $ 2756893Sfenner */ 2856893Sfenner 2956893Sfenner#include <sys/cdefs.h> 3056893Sfenner__FBSDID("$FreeBSD: head/usr.sbin/bhyve/rtc.c 256072 2013-10-05 21:22:35Z neel $"); 3156893Sfenner 32127668Sbms#include <sys/types.h> 33214478Srpaulo#include <sys/time.h> 3456893Sfenner 3556893Sfenner#include <stdio.h> 36190207Srpaulo#include <string.h> 3756893Sfenner#include <time.h> 3856893Sfenner#include <assert.h> 3956893Sfenner 4056893Sfenner#include <machine/vmm.h> 41127668Sbms#include <vmmapi.h> 42127668Sbms 4356893Sfenner#include "inout.h" 4456893Sfenner#include "rtc.h" 4556893Sfenner 4656893Sfenner#define IO_RTC 0x70 4756893Sfenner 4856893Sfenner#define RTC_SEC 0x00 /* seconds */ 4956893Sfenner#define RTC_SEC_ALARM 0x01 5056893Sfenner#define RTC_MIN 0x02 5156893Sfenner#define RTC_MIN_ALARM 0x03 5256893Sfenner#define RTC_HRS 0x04 5356893Sfenner#define RTC_HRS_ALARM 0x05 5475115Sfenner#define RTC_WDAY 0x06 5575115Sfenner#define RTC_DAY 0x07 5675115Sfenner#define RTC_MONTH 0x08 5775115Sfenner#define RTC_YEAR 0x09 5875115Sfenner#define RTC_CENTURY 0x32 /* current century */ 5956893Sfenner 6056893Sfenner#define RTC_STATUSA 0xA 6156893Sfenner#define RTCSA_TUP 0x80 /* time update, don't look now */ 6256893Sfenner 63190207Srpaulo#define RTC_STATUSB 0xB 64190207Srpaulo#define RTCSB_DST 0x01 65190207Srpaulo#define RTCSB_24HR 0x02 66190207Srpaulo#define RTCSB_BIN 0x04 /* 0 = BCD, 1 = Binary */ 67190207Srpaulo#define RTCSB_PINTR 0x40 /* 1 = enable periodic clock interrupt */ 68190207Srpaulo#define RTCSB_HALT 0x80 /* stop clock updates */ 69190207Srpaulo 70190207Srpaulo#define RTC_INTR 0x0c /* status register C (R) interrupt source */ 71190207Srpaulo 72190207Srpaulo#define RTC_STATUSD 0x0d /* status register D (R) Lost Power */ 73190207Srpaulo#define RTCSD_PWR 0x80 /* clock power OK */ 74190207Srpaulo 75190207Srpaulo#define RTC_NVRAM_START 0x0e 76190207Srpaulo#define RTC_NVRAM_END 0x7f 77190207Srpaulo#define RTC_NVRAM_SZ (128 - RTC_NVRAM_START) 78190207Srpaulo#define nvoff(x) ((x) - RTC_NVRAM_START) 79190207Srpaulo 80190207Srpaulo#define RTC_DIAG 0x0e 81190207Srpaulo#define RTC_RSTCODE 0x0f 82190207Srpaulo#define RTC_EQUIPMENT 0x14 83190207Srpaulo#define RTC_LMEM_LSB 0x34 84190207Srpaulo#define RTC_LMEM_MSB 0x35 85190207Srpaulo#define RTC_HMEM_LSB 0x5b 86190207Srpaulo#define RTC_HMEM_SB 0x5c 87190207Srpaulo#define RTC_HMEM_MSB 0x5d 88190207Srpaulo 89190207Srpaulo#define m_64KB (64*1024) 90190207Srpaulo#define m_16MB (16*1024*1024) 91190207Srpaulo#define m_4GB (4ULL*1024*1024*1024) 92190207Srpaulo 93190207Srpaulostatic int addr; 94190207Srpaulo 95190207Srpaulostatic uint8_t rtc_nvram[RTC_NVRAM_SZ]; 96190207Srpaulo 97190207Srpaulo/* XXX initialize these to default values as they would be from BIOS */ 98190207Srpaulostatic uint8_t status_a, status_b; 99190207Srpaulo 100214478Srpaulostatic struct { 101214478Srpaulo uint8_t hours; 102214478Srpaulo uint8_t mins; 103214478Srpaulo uint8_t secs; 104214478Srpaulo} rtc_alarm; 105214478Srpaulo 106214478Srpaulostatic u_char const bin2bcd_data[] = { 107214478Srpaulo 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 108214478Srpaulo 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 109214478Srpaulo 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 110214478Srpaulo 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 111190207Srpaulo 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 112127668Sbms 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 113190207Srpaulo 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 114127668Sbms 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 115190207Srpaulo 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 116190207Srpaulo 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99 117214478Srpaulo}; 118190207Srpaulo#define bin2bcd(bin) (bin2bcd_data[bin]) 119190207Srpaulo 120190207Srpaulo#define rtcout(val) ((status_b & RTCSB_BIN) ? (val) : bin2bcd((val))) 121190207Srpaulo 122190207Srpaulostatic void 123190207Srpaulotimevalfix(struct timeval *t1) 12475115Sfenner{ 125127668Sbms 12656893Sfenner if (t1->tv_usec < 0) { 127190207Srpaulo t1->tv_sec--; 128190207Srpaulo t1->tv_usec += 1000000; 129190207Srpaulo } 130190207Srpaulo if (t1->tv_usec >= 1000000) { 131190207Srpaulo t1->tv_sec++; 13256893Sfenner t1->tv_usec -= 1000000; 13356893Sfenner } 13456893Sfenner} 13556893Sfenner 13656893Sfennerstatic void 13756893Sfennertimevalsub(struct timeval *t1, const struct timeval *t2) 13856893Sfenner{ 13956893Sfenner 14056893Sfenner t1->tv_sec -= t2->tv_sec; 141127668Sbms t1->tv_usec -= t2->tv_usec; 14256893Sfenner timevalfix(t1); 14356893Sfenner} 14456893Sfenner 14556893Sfennerstatic int 146127668Sbmsrtc_addr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 147190207Srpaulo uint32_t *eax, void *arg) 148190207Srpaulo{ 149190207Srpaulo if (bytes != 1) 150190207Srpaulo return (-1); 151190207Srpaulo 152190207Srpaulo if (in) { 153190207Srpaulo /* straight read of this register will return 0xFF */ 154190207Srpaulo *eax = 0xff; 155190207Srpaulo return (0); 156190207Srpaulo } 157190207Srpaulo 15856893Sfenner switch (*eax & 0x7f) { 15956893Sfenner case RTC_SEC: 16056893Sfenner case RTC_SEC_ALARM: 161190207Srpaulo case RTC_MIN: 162190207Srpaulo case RTC_MIN_ALARM: 163190207Srpaulo case RTC_HRS: 164190207Srpaulo case RTC_HRS_ALARM: 165190207Srpaulo case RTC_WDAY: 166190207Srpaulo case RTC_DAY: 167190207Srpaulo case RTC_MONTH: 16856893Sfenner case RTC_YEAR: 169190207Srpaulo case RTC_STATUSA: 170190207Srpaulo case RTC_STATUSB: 171190207Srpaulo case RTC_INTR: 172190207Srpaulo case RTC_STATUSD: 173190207Srpaulo case RTC_NVRAM_START ... RTC_NVRAM_END: 174190207Srpaulo break; 175190207Srpaulo default: 176190207Srpaulo return (-1); 177190207Srpaulo } 178190207Srpaulo 179190207Srpaulo addr = *eax & 0x7f; 180190207Srpaulo return (0); 181190207Srpaulo} 182190207Srpaulo 183190207Srpaulostatic int 184190207Srpaulortc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 185190207Srpaulo uint32_t *eax, void *arg) 186190207Srpaulo{ 187190207Srpaulo int hour; 188190207Srpaulo time_t t; 189190207Srpaulo struct timeval cur, delta; 190190207Srpaulo 191190207Srpaulo static struct timeval last; 192190207Srpaulo static struct tm tm; 193190207Srpaulo 194190207Srpaulo if (bytes != 1) 195190207Srpaulo return (-1); 196190207Srpaulo 197190207Srpaulo gettimeofday(&cur, NULL); 198190207Srpaulo 199214478Srpaulo /* 200190207Srpaulo * Increment the cached time only once per second so we can guarantee 201190207Srpaulo * that the guest has at least one second to read the hour:min:sec 20256893Sfenner * separately and still get a coherent view of the time. 20356893Sfenner */ 20456893Sfenner delta = cur; 205127668Sbms timevalsub(&delta, &last); 206190207Srpaulo if (delta.tv_sec >= 1 && (status_b & RTCSB_HALT) == 0) { 207190207Srpaulo t = cur.tv_sec; 208190207Srpaulo localtime_r(&t, &tm); 209190207Srpaulo last = cur; 210190207Srpaulo } 211190207Srpaulo 212190207Srpaulo if (in) { 213190207Srpaulo switch (addr) { 21456893Sfenner case RTC_SEC_ALARM: 21556893Sfenner *eax = rtc_alarm.secs; 21656893Sfenner break; 21756893Sfenner case RTC_MIN_ALARM: 21856893Sfenner *eax = rtc_alarm.mins; 21956893Sfenner break; 22056893Sfenner case RTC_HRS_ALARM: 22156893Sfenner *eax = rtc_alarm.hours; 222190207Srpaulo break; 223190207Srpaulo case RTC_SEC: 224190207Srpaulo *eax = rtcout(tm.tm_sec); 225190207Srpaulo return (0); 226190207Srpaulo case RTC_MIN: 227190207Srpaulo *eax = rtcout(tm.tm_min); 228190207Srpaulo return (0); 22956893Sfenner case RTC_HRS: 23056893Sfenner if (status_b & RTCSB_24HR) 23156893Sfenner hour = tm.tm_hour; 23256893Sfenner else 23356893Sfenner hour = (tm.tm_hour % 12) + 1; 23456893Sfenner 23556893Sfenner *eax = rtcout(hour); 23656893Sfenner 23756893Sfenner /* 23856893Sfenner * If we are representing time in the 12-hour format 23956893Sfenner * then set the MSB to indicate PM. 24056893Sfenner */ 24156893Sfenner if ((status_b & RTCSB_24HR) == 0 && tm.tm_hour >= 12) 24256893Sfenner *eax |= 0x80; 24356893Sfenner 24456893Sfenner return (0); 24556893Sfenner case RTC_WDAY: 24656893Sfenner *eax = rtcout(tm.tm_wday + 1); 24756893Sfenner return (0); 24856893Sfenner case RTC_DAY: 24956893Sfenner *eax = rtcout(tm.tm_mday); 25056893Sfenner return (0); 25156893Sfenner case RTC_MONTH: 25256893Sfenner *eax = rtcout(tm.tm_mon + 1); 25356893Sfenner return (0); 25456893Sfenner case RTC_YEAR: 25556893Sfenner *eax = rtcout(tm.tm_year % 100); 25656893Sfenner return (0); 25756893Sfenner case RTC_STATUSA: 25856893Sfenner *eax = status_a; 25956893Sfenner return (0); 26056893Sfenner case RTC_STATUSB: 26156893Sfenner *eax = status_b; 26256893Sfenner return (0); 26356893Sfenner case RTC_INTR: 26456893Sfenner *eax = 0; 26556893Sfenner return (0); 26656893Sfenner case RTC_STATUSD: 26756893Sfenner *eax = RTCSD_PWR; 26856893Sfenner return (0); 26956893Sfenner case RTC_NVRAM_START ... RTC_NVRAM_END: 27056893Sfenner *eax = rtc_nvram[addr - RTC_NVRAM_START]; 27156893Sfenner return (0); 27256893Sfenner default: 27356893Sfenner return (-1); 27456893Sfenner } 27556893Sfenner } 27675115Sfenner 27756893Sfenner switch (addr) { 27856893Sfenner case RTC_STATUSA: 27956893Sfenner status_a = *eax & ~RTCSA_TUP; 28056893Sfenner break; 28156893Sfenner case RTC_STATUSB: 28256893Sfenner /* XXX not implemented yet XXX */ 28356893Sfenner if (*eax & RTCSB_PINTR) 28456893Sfenner return (-1); 28556893Sfenner status_b = *eax; 28656893Sfenner break; 28756893Sfenner case RTC_STATUSD: 28856893Sfenner /* ignore write */ 28956893Sfenner break; 29056893Sfenner case RTC_SEC_ALARM: 29156893Sfenner rtc_alarm.secs = *eax; 29256893Sfenner break; 29356893Sfenner case RTC_MIN_ALARM: 29456893Sfenner rtc_alarm.mins = *eax; 29556893Sfenner break; 29656893Sfenner case RTC_HRS_ALARM: 29756893Sfenner rtc_alarm.hours = *eax; 29856893Sfenner break; 29956893Sfenner case RTC_SEC: 30056893Sfenner case RTC_MIN: 30156893Sfenner case RTC_HRS: 30256893Sfenner case RTC_WDAY: 30356893Sfenner case RTC_DAY: 30456893Sfenner case RTC_MONTH: 30556893Sfenner case RTC_YEAR: 30656893Sfenner /* 30756893Sfenner * Ignore writes to the time of day registers 30856893Sfenner */ 30956893Sfenner break; 31056893Sfenner case RTC_NVRAM_START ... RTC_NVRAM_END: 31156893Sfenner rtc_nvram[addr - RTC_NVRAM_START] = *eax; 31256893Sfenner break; 31356893Sfenner default: 31456893Sfenner return (-1); 31556893Sfenner } 31656893Sfenner return (0); 31756893Sfenner} 31856893Sfenner 31956893Sfennervoid 32056893Sfennerrtc_init(struct vmctx *ctx) 32156893Sfenner{ 32256893Sfenner struct timeval cur; 32356893Sfenner struct tm tm; 32456893Sfenner size_t himem; 32556893Sfenner size_t lomem; 32656893Sfenner int err; 32756893Sfenner 32856893Sfenner err = gettimeofday(&cur, NULL); 32956893Sfenner assert(err == 0); 33056893Sfenner (void) localtime_r(&cur.tv_sec, &tm); 33156893Sfenner 33256893Sfenner memset(rtc_nvram, 0, sizeof(rtc_nvram)); 33356893Sfenner 33456893Sfenner rtc_nvram[nvoff(RTC_CENTURY)] = rtcout(tm.tm_year / 100); 33556893Sfenner 33656893Sfenner /* XXX init diag/reset code/equipment/checksum ? */ 33756893Sfenner 33856893Sfenner /* 33956893Sfenner * Report guest memory size in nvram cells as required by UEFI. 34056893Sfenner * Little-endian encoding. 34156893Sfenner * 0x34/0x35 - 64KB chunks above 16MB, below 4GB 34275115Sfenner * 0x5b/0x5c/0x5d - 64KB chunks above 4GB 34356893Sfenner */ 34456893Sfenner err = vm_get_memory_seg(ctx, 0, &lomem, NULL); 34556893Sfenner assert(err == 0); 34656893Sfenner 34756893Sfenner lomem = (lomem - m_16MB) / m_64KB; 34856893Sfenner rtc_nvram[nvoff(RTC_LMEM_LSB)] = lomem; 34956893Sfenner rtc_nvram[nvoff(RTC_LMEM_MSB)] = lomem >> 8; 35056893Sfenner 35156893Sfenner if (vm_get_memory_seg(ctx, m_4GB, &himem, NULL) == 0) { 35256893Sfenner himem /= m_64KB; 35356893Sfenner rtc_nvram[nvoff(RTC_HMEM_LSB)] = himem; 35456893Sfenner rtc_nvram[nvoff(RTC_HMEM_SB)] = himem >> 8; 35556893Sfenner rtc_nvram[nvoff(RTC_HMEM_MSB)] = himem >> 16; 35656893Sfenner } 35756893Sfenner} 35856893Sfenner 35956893SfennerINOUT_PORT(rtc, IO_RTC, IOPORT_F_INOUT, rtc_addr_handler); 36056893SfennerINOUT_PORT(rtc, IO_RTC + 1, IOPORT_F_INOUT, rtc_data_handler); 36156893Sfenner