From d13a11a2c38f7d26fd8d98598ef53dbbebd297eb Mon Sep 17 00:00:00 2001 From: Martin Lucina Date: Tue, 30 Jun 2015 18:19:56 +0200 Subject: [PATCH] hw: Implement wall time using RTC offset at boot Addresses issue #31. --- platform/hw/Makefile | 2 +- platform/hw/arch/x86/clock.c | 57 +++++++++++++ platform/hw/clock.c | 2 +- platform/hw/clock_subr.c | 120 +++++++++++++++++++++++++++ platform/hw/include/arch/x86/reg.h | 13 +++ platform/hw/include/bmk/clock_subr.h | 112 +++++++++++++++++++++++++ platform/hw/include/bmk/kernel.h | 1 + 7 files changed, 305 insertions(+), 2 deletions(-) create mode 100644 platform/hw/clock_subr.c create mode 100644 platform/hw/include/bmk/clock_subr.h diff --git a/platform/hw/Makefile b/platform/hw/Makefile index d77915ad2..59674bffb 100644 --- a/platform/hw/Makefile +++ b/platform/hw/Makefile @@ -38,7 +38,7 @@ endif LDFLAGS_BAKE+= -L$(abspath rump/lib) -OBJS_BMK-y+= intr.o clock.o kernel.o multiboot.o undefs.o +OBJS_BMK-y+= intr.o clock.o clock_subr.o kernel.o multiboot.o undefs.o OBJS_BMK+= ${OBJS_BMK-y} diff --git a/platform/hw/arch/x86/clock.c b/platform/hw/arch/x86/clock.c index f61585987..66d7bd655 100644 --- a/platform/hw/arch/x86/clock.c +++ b/platform/hw/arch/x86/clock.c @@ -25,6 +25,7 @@ */ #include +#include #define NSEC_PER_SEC 1000000000ULL /* Minimum delta to sleep using PIT. Programming seems to have an overhead of @@ -51,6 +52,9 @@ static const uint32_t pit_mult = (1ULL << 63) / ((NSEC_PER_SEC << 31) / TIMER_HZ static bmk_time_t time_base; static uint64_t tsc_base; +/* RTC wall time offset at monotonic time base. */ +static bmk_time_t rtc_epochoffset; + /* Set to 1 when the i8254 interrupt is handled. */ static volatile int ticktock = 0; @@ -147,6 +151,38 @@ rdtsc(void) return val; } +/* + * Read a RTC register. Due to PC platform braindead-ness also disables NMI. + */ +static inline uint8_t rtc_read(uint8_t reg) +{ + + outb(RTC_COMMAND, reg | RTC_NMI_DISABLE); + return inb(RTC_DATA); +} + +/* + * Return current RTC time. Note that due to waiting for the update cycle to + * complete, this call may take some time. + */ +static bmk_time_t rtc_gettimeofday(void) +{ + struct bmk_clock_ymdhms dt; + + /* If time update in progress then spin until complete. */ + while(rtc_read(RTC_STATUS_A) & RTC_UIP) + continue; + + dt.dt_sec = bcdtobin(rtc_read(RTC_SEC)); + dt.dt_min = bcdtobin(rtc_read(RTC_MIN)); + dt.dt_hour = bcdtobin(rtc_read(RTC_HOUR)); + dt.dt_day = bcdtobin(rtc_read(RTC_DAY)); + dt.dt_mon = bcdtobin(rtc_read(RTC_MONTH)); + dt.dt_year = bcdtobin(rtc_read(RTC_YEAR)) + 2000; + + return bmk_clock_ymdhms_to_secs(&dt) * NSEC_PER_SEC; +} + void bmk_x86_initclocks(void) { @@ -157,6 +193,12 @@ bmk_x86_initclocks(void) outb(TIMER_CNTR, (TIMER_HZ / HZ) & 0xff); outb(TIMER_CNTR, (TIMER_HZ / HZ) >> 8); + /* + * Read RTC time to use as epoch offset. This must be done just before + * tsc_base is initialised in order to get a correct offset. + */ + rtc_epochoffset = rtc_gettimeofday(); + /* * Calculate TSC frequency by calibrating against an 0.1s delay * using the i8254 timer. @@ -176,6 +218,11 @@ bmk_x86_initclocks(void) * (0.32) tsc_mult = NSEC_PER_SEC (32.32) / tsc_freq (32.0) */ tsc_mult = (NSEC_PER_SEC << 32) / tsc_freq; + + /* + * Monotonic time begins at tsc_base (first read of TSC before + * calibration). + */ time_base = mul64_32(tsc_base, tsc_mult); /* @@ -216,6 +263,16 @@ bmk_cpu_clock_now(void) return time_base; } +/* + * Return epoch offset (wall time offset to monotonic clock start). + */ +bmk_time_t +bmk_cpu_clock_epochoffset(void) +{ + + return rtc_epochoffset; +} + /* * Block the CPU until monotonic time is *no later than* the specified time. * Returns early if any non-timer interrupts are serviced, or if the requested diff --git a/platform/hw/clock.c b/platform/hw/clock.c index d79529626..47eaa6525 100644 --- a/platform/hw/clock.c +++ b/platform/hw/clock.c @@ -38,5 +38,5 @@ bmk_time_t bmk_platform_clock_epochoffset(void) { - return 0; /* needs more bits */ + return bmk_cpu_clock_epochoffset(); } diff --git a/platform/hw/clock_subr.c b/platform/hw/clock_subr.c new file mode 100644 index 000000000..5455ce3bc --- /dev/null +++ b/platform/hw/clock_subr.c @@ -0,0 +1,120 @@ +/* $NetBSD: clock_subr.c,v 1.26 2014/12/22 18:09:20 christos Exp $ */ + +/* + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1982, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: Utah $Hdr: clock.c 1.18 91/01/21$ + * + * @(#)clock.c 8.2 (Berkeley) 1/12/94 + */ + +/* + * Generic routines to convert between a POSIX date + * (seconds since 1/1/1970) and yr/mo/day/hr/min/sec + * Derived from arch/hp300/hp300/clock.c + */ + +#include +#include + +#define FEBRUARY 2 + +/* for easier alignment: + * time from the epoch to 2000 (there were 7 leap years): */ +#define DAYSTO2000 (365*30+7) + +/* 4 year intervals include 1 leap year */ +#define DAYS4YEARS (365*4+1) + +/* 100 year intervals include 24 leap years */ +#define DAYS100YEARS (365*100+24) + +/* 400 year intervals include 97 leap years */ +#define DAYS400YEARS (365*400+97) + +bmk_time_t +bmk_clock_ymdhms_to_secs(struct bmk_clock_ymdhms *dt) +{ + uint64_t secs, i, year, days; + + year = dt->dt_year; + + /* + * Compute days since start of time + * First from years, then from months. + */ + if (year < POSIX_BASE_YEAR) + return 0; + days = 0; + if (is_leap_year(year) && dt->dt_mon > FEBRUARY) + days++; + + if (year < 2000) { + /* simple way for early years */ + for (i = POSIX_BASE_YEAR; i < year; i++) + days += days_per_year(i); + } else { + /* years are properly aligned */ + days += DAYSTO2000; + year -= 2000; + + i = year / 400; + days += i * DAYS400YEARS; + year -= i * 400; + + i = year / 100; + days += i * DAYS100YEARS; + year -= i * 100; + + i = year / 4; + days += i * DAYS4YEARS; + year -= i * 4; + + for (i = dt->dt_year-year; i < dt->dt_year; i++) + days += days_per_year(i); + } + + + /* Months */ + for (i = 1; i < dt->dt_mon; i++) + days += days_in_month(i); + days += (dt->dt_day - 1); + + /* Add hours, minutes, seconds. */ + secs = (((uint64_t)days + * 24 + dt->dt_hour) + * 60 + dt->dt_min) + * 60 + dt->dt_sec; + + return secs; +} diff --git a/platform/hw/include/arch/x86/reg.h b/platform/hw/include/arch/x86/reg.h index 5a27eff34..3e5ed8b65 100644 --- a/platform/hw/include/arch/x86/reg.h +++ b/platform/hw/include/arch/x86/reg.h @@ -14,3 +14,16 @@ #define TIMER_ONESHOT 0x08 #define TIMER_16BIT 0x30 #define TIMER_HZ 1193182 + +#define RTC_COMMAND 0x70 +#define RTC_DATA 0x71 +#define RTC_NMI_DISABLE (1<<8) +#define RTC_NMI_ENABLE 0 +#define RTC_SEC 0x00 +#define RTC_MIN 0x02 +#define RTC_HOUR 0x04 +#define RTC_DAY 0x07 +#define RTC_MONTH 0x08 +#define RTC_YEAR 0x09 +#define RTC_STATUS_A 0x0a +#define RTC_UIP (1<<7) diff --git a/platform/hw/include/bmk/clock_subr.h b/platform/hw/include/bmk/clock_subr.h new file mode 100644 index 000000000..fd5e87204 --- /dev/null +++ b/platform/hw/include/bmk/clock_subr.h @@ -0,0 +1,112 @@ +/*- + * Copyright (c) 1996 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Gordon W. Ross + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _BMK_CLOCK_SUBR_H_ +#define _BMK_CLOCK_SUBR_H_ + +#include + +/* Some handy constants. */ +#define SECS_PER_MINUTE 60 +#define SECS_PER_HOUR 3600 +#define SECS_PER_DAY 86400 +#define DAYS_PER_COMMON_YEAR 365 +#define DAYS_PER_LEAP_YEAR 366 +#define SECS_PER_COMMON_YEAR (SECS_PER_DAY * DAYS_PER_COMMON_YEAR) +#define SECS_PER_LEAP_YEAR (SECS_PER_DAY * DAYS_PER_LEAP_YEAR) + +/* Traditional POSIX base year */ +#define POSIX_BASE_YEAR 1970 + +/* Some handy functions */ +static inline int +days_in_month(int m) +{ + switch (m) { + case 2: + return 28; + case 4: case 6: case 9: case 11: + return 30; + case 1: case 3: case 5: case 7: case 8: case 10: case 12: + return 31; + default: + return -1; + } +} + +/* + * This inline avoids some unnecessary modulo operations + * as compared with the usual macro: + * ( ((year % 4) == 0 && + * (year % 100) != 0) || + * ((year % 400) == 0) ) + * It is otherwise equivalent. + */ +static inline int +is_leap_year(uint64_t year) +{ + if ((year & 3) != 0) + return 0; + + if ((year % 100) != 0) + return 1; + + return (year % 400) == 0; +} + +static inline int +days_per_year(uint64_t year) +{ + return is_leap_year(year) ? DAYS_PER_LEAP_YEAR : DAYS_PER_COMMON_YEAR; +} + +/* + * "POSIX time" to/from "YY/MM/DD/hh/mm/ss" + */ +struct bmk_clock_ymdhms { + uint64_t dt_year; + uint8_t dt_mon; + uint8_t dt_day; + uint8_t dt_hour; + uint8_t dt_min; + uint8_t dt_sec; +}; + +bmk_time_t bmk_clock_ymdhms_to_secs(struct bmk_clock_ymdhms *); + +/* + * BCD to binary. + */ +static inline unsigned int +bcdtobin(unsigned int bcd) +{ + return ((bcd >> 4) & 0x0f) * 10 + (bcd & 0x0f); +} + +#endif /* _BMK_CLOCK_SUBR_H_ */ diff --git a/platform/hw/include/bmk/kernel.h b/platform/hw/include/bmk/kernel.h index 072182317..6e9017ccf 100644 --- a/platform/hw/include/bmk/kernel.h +++ b/platform/hw/include/bmk/kernel.h @@ -22,6 +22,7 @@ int bmk_cpu_intr_init(int); void bmk_cpu_intr_ack(void); bmk_time_t bmk_cpu_clock_now(void); +bmk_time_t bmk_cpu_clock_epochoffset(void); void bmk_isr_clock(void); void bmk_isr(int);