Skip to content

Commit

Permalink
hw: Implement wall time using RTC offset at boot
Browse files Browse the repository at this point in the history
Addresses issue #31.
  • Loading branch information
mato committed Jun 30, 2015
1 parent 11c87b2 commit d13a11a
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 2 deletions.
2 changes: 1 addition & 1 deletion platform/hw/Makefile
Expand Up @@ -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}

Expand Down
57 changes: 57 additions & 0 deletions platform/hw/arch/x86/clock.c
Expand Up @@ -25,6 +25,7 @@
*/

#include <bmk/kernel.h>
#include <bmk/clock_subr.h>

#define NSEC_PER_SEC 1000000000ULL
/* Minimum delta to sleep using PIT. Programming seems to have an overhead of
Expand All @@ -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;

Expand Down Expand Up @@ -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)
{
Expand All @@ -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.
Expand All @@ -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);

/*
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion platform/hw/clock.c
Expand Up @@ -38,5 +38,5 @@ bmk_time_t
bmk_platform_clock_epochoffset(void)
{

return 0; /* needs more bits */
return bmk_cpu_clock_epochoffset();
}
120 changes: 120 additions & 0 deletions 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 <bmk/kernel.h>
#include <bmk/clock_subr.h>

#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;
}
13 changes: 13 additions & 0 deletions platform/hw/include/arch/x86/reg.h
Expand Up @@ -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)
112 changes: 112 additions & 0 deletions 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 <bmk/kernel.h>

/* 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_ */
1 change: 1 addition & 0 deletions platform/hw/include/bmk/kernel.h
Expand Up @@ -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);
Expand Down

0 comments on commit d13a11a

Please sign in to comment.