[PATCH] Implement timekeeping for rumprun/hw (x86)

  • From: Martin Lucina <martin@xxxxxxxxxx>
  • To: rumpkernel-users@xxxxxxxxxxxxx
  • Date: Tue, 30 Jun 2015 18:45:21 +0200

On Monday, 22.06.2015 at 11:38, Martin Lucina wrote:

Anyhow, I need to try some approaches and measure the results, we can then
iterate on that once I have something that works.

Turns out timekeeping is much more complicated than I expected. :-(

I have working code now, which I'm including as a full patch against
current master here for review.

I have also pushed this to the 'wip-timekeeping' branch where you can see
the full commit history.

The code addresses issues #30 and #31 and I have tested it under QEMU, KVM
and on actual bare metal (~10yo laptop with Pentium M CPU).

It implements a full "dynamic tick" using the PIT timer to program
one-shot clock interrupts when there is no work to do.

The code does not yet support a common "out-of-band" timecounter that is
not reliant on clock "interrupts" in the rump kernel. It also does not
include any support for the KVM pvclock. Both of these will be addressed
later as time permits, and should improve sleep overhead for userspace, and
also address the 0.1s boot delay.

Comments? Given that no one spoke up on my initial questions about
fixed-point math I've just assumed no one here has experience with that. If
you do, please review my (heavily commented) code that performs the TSC to
nsecs / nsecs to PIT ticks conversions.

I'm not entirely happy about the MD/MI split of the code, perhaps that
could be improved. Antti?

Patch follows.

-mato

diff --git a/platform/hw/Makefile b/platform/hw/Makefile
index d77915a..59674bf 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/amd64/Makefile.inc
b/platform/hw/arch/amd64/Makefile.inc
index dc71ec8..87d85ce 100644
--- a/platform/hw/arch/amd64/Makefile.inc
+++ b/platform/hw/arch/amd64/Makefile.inc
@@ -4,6 +4,7 @@ OBJS_BMK+= arch/amd64/machdep.o arch/amd64/boot.o
OBJS_BMK+= arch/x86/vgacons.o
OBJS_BMK+= arch/x86/cpu_subr.o
OBJS_BMK+= arch/x86/x86_subr.o
+OBJS_BMK+= arch/x86/clock.o

LDSCRIPT= arch/amd64/kern.ldscript

diff --git a/platform/hw/arch/amd64/machdep.c b/platform/hw/arch/amd64/machdep.c
index 8255c56..6ee44e6 100644
--- a/platform/hw/arch/amd64/machdep.c
+++ b/platform/hw/arch/amd64/machdep.c
@@ -83,7 +83,6 @@ struct tss {
static struct gate_descriptor idt[256];

/* actual interrupt service routines */
-void bmk_cpu_isr_clock(void);
void bmk_cpu_isr_9(void);
void bmk_cpu_isr_10(void);
void bmk_cpu_isr_11(void);
@@ -132,9 +131,6 @@ bmk_cpu_init(void)

bmk_x86_initpic();

- /* fill clock interrupt */
- bmk_x86_fillgate(32, bmk_cpu_isr_clock, 0);
-
/*
* fill TSS
*/
@@ -154,7 +150,7 @@ bmk_cpu_init(void)
td->td_zero = 0;
bmk_amd64_ltr(4*8);

- bmk_x86_inittimer();
+ bmk_x86_initclocks();
}

void bmk_cpu_pagefault(void *, void *);
diff --git a/platform/hw/arch/i386/Makefile.inc
b/platform/hw/arch/i386/Makefile.inc
index a6d8ffd..52adf6a 100644
--- a/platform/hw/arch/i386/Makefile.inc
+++ b/platform/hw/arch/i386/Makefile.inc
@@ -3,6 +3,7 @@ OBJS_BMK+= arch/i386/locore32.o arch/i386/machdep.o
arch/i386/boot.o
OBJS_BMK+= arch/x86/vgacons.o
OBJS_BMK+= arch/x86/cpu_subr.o
OBJS_BMK+= arch/x86/x86_subr.o
+OBJS_BMK+= arch/x86/clock.o

LDSCRIPT= arch/i386/kern.ldscript

diff --git a/platform/hw/arch/i386/machdep.c b/platform/hw/arch/i386/machdep.c
index 7eec268..1df3292 100644
--- a/platform/hw/arch/i386/machdep.c
+++ b/platform/hw/arch/i386/machdep.c
@@ -57,9 +57,6 @@ static struct gate_descriptor idt[256];
/* interrupt-not-service-routine */
void bmk_cpu_insr(void);

-/* actual interrupt service routines */
-void bmk_cpu_isr_clock(void);
-
void
bmk_x86_fillgate(int num, void *fun, int unused)
{
@@ -157,14 +154,7 @@ bmk_cpu_init(void)

bmk_x86_initpic();

- /*
- * map clock interrupt.
- * note, it's still disabled in the PIC, we only enable it
- * during nanohlt
- */
- bmk_x86_fillgate(32, bmk_cpu_isr_clock, 0);
-
- bmk_x86_inittimer();
+ bmk_x86_initclocks();
}

void
diff --git a/platform/hw/arch/x86/clock.c b/platform/hw/arch/x86/clock.c
new file mode 100644
index 0000000..66d7bd6
--- /dev/null
+++ b/platform/hw/arch/x86/clock.c
@@ -0,0 +1,331 @@
+/*-
+ * Copyright (c) 2014, 2015 Antti Kantee. All Rights Reserved.
+ * Copyright (c) 2015 Martin Lucina. All Rights Reserved.
+ *
+ * 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 AUTHOR ``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 AUTHOR 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.
+ */
+
+#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
+ * 3-4us, but play it safe here. */
+#define PIT_MIN_DELTA 16
+
+/* clock isr trampoline (in locore.S) */
+void bmk_cpu_isr_clock(void);
+
+/* Multiplier for converting TSC ticks to nsecs. (0.32) fixed point. */
+static uint32_t tsc_mult;
+
+/*
+ * Multiplier for converting nsecs to PIT ticks. (1.32) fixed point.
+ *
+ * Calculated as:
+ *
+ * f = NSEC_PER_SEC / TIMER_HZ (0.31) fixed point.
+ * pit_mult = 1 / f (1.32) fixed point.
+ */
+static const uint32_t pit_mult = (1ULL << 63) / ((NSEC_PER_SEC << 31) /
TIMER_HZ);
+
+/* Base time values at the last call to bmk_cpu_clock_now(). */
+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;
+
+/*
+ * Calculate prod = (a * b) where a is (64.0) fixed point and b is (0.32) fixed
+ * point. The intermediate product is (64.32) fixed point, discarding the
+ * fractional bits leaves us with a (64.0) fixed point result.
+ *
+ * XXX Document what range of (a, b) is safe from overflow in this calculation.
+ */
+static inline uint64_t mul64_32(uint64_t a, uint32_t b)
+{
+ uint64_t prod;
+#if defined(__x86_64__)
+ /* For x86_64 the computation can be done using 64-bit multiply and
+ * shift. */
+ __asm__ (
+ "mul %%rdx ; "
+ "shrd $32, %%rdx, %%rax"
+ : "=a" (prod)
+ : "0" (a), "d" ((uint64_t)b)
+ );
+#elif defined(__i386__)
+ /* For i386 we compute the partial products and add them up, discarding
+ * the lower 32 bits of the product in the process. */
+ uint32_t h = (uint32_t)(a >> 32);
+ uint32_t l = (uint32_t)a;
+ uint32_t t1, t2;
+ __asm__ (
+ "mul %5 ; " /* %edx:%eax = (l * b) */
+ "mov %4,%%eax ; " /* %eax = h */
+ "mov %%edx,%4 ; " /* t1 = ((l * b) >> 32) */
+ "mul %5 ; " /* %edx:%eax = (h * b) */
+ "add %4,%%eax ; "
+ "xor %5,%5 ; "
+ "adc %5,%%edx ; " /* %edx:%eax = (h * b) + ((l * b) >> 32) */
+ : "=A" (prod), "=r" (t1), "=r" (t2)
+ : "a" (l), "1" (h), "2" (b)
+ );
+
+ return prod;
+#else
+#error
+#endif
+}
+
+/*
+ * Read the current i8254 channel 0 tick count.
+ */
+static unsigned int
+i8254_gettick(void)
+{
+ uint16_t rdval;
+
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
+ rdval = inb(TIMER_CNTR);
+ rdval |= (inb(TIMER_CNTR) << 8);
+ return rdval;
+}
+
+/*
+ * Delay for approximately n microseconds using the i8254 channel 0 counter.
+ * Timer must be programmed appropriately before calling this function.
+ */
+static void
+i8254_delay(unsigned int n)
+{
+ unsigned int cur_tick, initial_tick;
+ int remaining;
+ const unsigned long timer_rval = TIMER_HZ / HZ;
+
+ initial_tick = i8254_gettick();
+
+ remaining = (unsigned long long) n * TIMER_HZ / 1000000;
+
+ while (remaining > 1) {
+ cur_tick = i8254_gettick();
+ if (cur_tick > initial_tick)
+ remaining -= timer_rval - (cur_tick - initial_tick);
+ else
+ remaining -= initial_tick - cur_tick;
+ initial_tick = cur_tick;
+ }
+}
+
+static uint64_t
+rdtsc(void)
+{
+ uint64_t val;
+ unsigned long eax, edx;
+
+ __asm__ __volatile__("rdtsc" : "=a"(eax), "=d"(edx));
+ val = ((uint64_t)edx<<32)|(eax);
+ 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)
+{
+ uint64_t tsc_freq;
+
+ /* Initialise i8254 timer channel 0 to mode 2 at 100 Hz */
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
+ 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.
+ */
+ tsc_base = rdtsc();
+ i8254_delay(100000);
+ tsc_freq = (rdtsc() - tsc_base) * 10;
+
+ /*
+ * Reinitialise i8254 timer channel 0 to mode 4 (one shot).
+ */
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_ONESHOT | TIMER_16BIT);
+
+ /*
+ * Calculate TSC scaling multiplier.
+ *
+ * (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);
+
+ /*
+ * Map i8254 interrupt vector and enable it in the PIC.
+ * XXX: We don't really want to enable IRQ2 here, but ...
+ */
+ bmk_x86_fillgate(32, bmk_cpu_isr_clock, 0);
+ outb(PIC1_DATA, 0xff & ~(1<<2|1<<0));
+}
+
+void
+bmk_isr_clock(void)
+{
+
+ /*
+ * Nothing to do here except signal completion, the clock interrupt
+ * serves only as a way to wake the CPU from halt state.
+ */
+ ticktock = 1;
+}
+
+/*
+ * Return monotonic time since system boot in nanoseconds.
+ */
+bmk_time_t
+bmk_cpu_clock_now(void)
+{
+ uint64_t tsc_now, tsc_delta;
+
+ /*
+ * Update time_base (monotonic time) and tsc_base (TSC time).
+ */
+ tsc_now = rdtsc();
+ tsc_delta = tsc_now - tsc_base;
+ time_base += mul64_32(tsc_delta, tsc_mult);
+ tsc_base = tsc_now;
+
+ 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
+ * delay is too short.
+ */
+void
+bmk_cpu_block(bmk_time_t until)
+{
+ bmk_time_t now, delta_ns;
+ int64_t delta_ticks;
+ unsigned int ticks;
+
+ now = bmk_cpu_clock_now();
+ if (until < now)
+ return;
+
+ delta_ns = until - now;
+ /*
+ * Compute delta in PIT ticks.
+ */
+ delta_ticks = mul64_32(delta_ns, pit_mult);
+
+ /*
+ * While the delay is more than a minimum safe amount of ticks,
+ * loop and program the timer to interrupt the CPU after the
+ * delay has expired.
+ */
+ while (delta_ticks >= PIT_MIN_DELTA) {
+ /*
+ * Maximum timer delay is 65535 ticks.
+ */
+ if (delta_ticks > 65535)
+ ticks = 65535;
+ else
+ ticks = delta_ticks;
+
+ /*
+ * Note that according to the Intel 82C54 datasheet, p12 the
+ * interrupt is actually delivered in N + 1 ticks.
+ */
+ outb(TIMER_CNTR, (ticks - 1) & 0xff);
+ outb(TIMER_CNTR, (ticks - 1) >> 8);
+
+ /*
+ * Wait for any interrupt. If we got a non-timer interrupt then
+ * just return into the scheduler which will check if there is
+ * work to do.
+ */
+ hlt();
+ if(!ticktock)
+ break;
+ ticktock = 0;
+
+ delta_ticks -= ticks;
+ }
+}
diff --git a/platform/hw/arch/x86/cpu_subr.c b/platform/hw/arch/x86/cpu_subr.c
index b98877c..e0a939c 100644
--- a/platform/hw/arch/x86/cpu_subr.c
+++ b/platform/hw/arch/x86/cpu_subr.c
@@ -73,29 +73,3 @@ bmk_cpu_intr_ack(void)
"outb %%al, $0x20\n"
::: "al");
}
-
-bmk_time_t
-bmk_cpu_clock_now(void)
-{
- uint64_t val;
- unsigned long eax, edx;
-
- /* um um um */
- __asm__ __volatile__("rdtsc" : "=a"(eax), "=d"(edx));
- val = ((uint64_t)edx<<32)|(eax);
-
- /* just approximate that 1 cycle = 1ns. "good enuf" for now */
- return val;
-}
-
-void
-bmk_cpu_nanohlt(void)
-{
-
- /*
- * Enable clock interrupt and wait for the next whichever interrupt
- */
- outb(PIC1_DATA, 0xff & ~(1<<2|1<<0));
- hlt();
- outb(PIC1_DATA, 0xff & ~(1<<2));
-}
diff --git a/platform/hw/arch/x86/x86_subr.c b/platform/hw/arch/x86/x86_subr.c
index 043aa50..f9b1051 100644
--- a/platform/hw/arch/x86/x86_subr.c
+++ b/platform/hw/arch/x86/x86_subr.c
@@ -46,16 +46,6 @@ bmk_x86_initpic(void)
outb(PIC2_DATA, 0xff); /* all masked */
}

-void
-bmk_x86_inittimer(void)
-{
-
- /* initialize the timer to 100Hz */
- outb(TIMER_MODE, TIMER_RATEGEN | TIMER_16BIT);
- outb(TIMER_CNTR, (TIMER_HZ/HZ) & 0xff);
- outb(TIMER_CNTR, (TIMER_HZ/HZ) >> 8);
-}
-
/* interrupt-not-service-routine */
void bmk_cpu_insr(void);

@@ -64,7 +54,7 @@ bmk_x86_initidt(void)
{
int i;

- for (i = 0; i < 32; i++) {
+ for (i = 0; i < 48; i++) {
bmk_x86_fillgate(i, bmk_cpu_insr, 0);
}

diff --git a/platform/hw/clock.c b/platform/hw/clock.c
index d795296..47eaa65 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 0000000..5455ce3
--- /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 <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;
+}
diff --git a/platform/hw/include/arch/x86/reg.h
b/platform/hw/include/arch/x86/reg.h
index 973695b..3e5ed8b 100644
--- a/platform/hw/include/arch/x86/reg.h
+++ b/platform/hw/include/arch/x86/reg.h
@@ -8,6 +8,22 @@

#define TIMER_CNTR 0x40
#define TIMER_MODE 0x43
+#define TIMER_SEL0 0x00
+#define TIMER_LATCH 0x00
#define TIMER_RATEGEN 0x04
+#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/arch/x86/var.h
b/platform/hw/include/arch/x86/var.h
index 8f9532f..0bf190c 100644
--- a/platform/hw/include/arch/x86/var.h
+++ b/platform/hw/include/arch/x86/var.h
@@ -4,7 +4,7 @@
#ifndef _LOCORE
void bmk_x86_initpic(void);
void bmk_x86_initidt(void);
-void bmk_x86_inittimer(void);
+void bmk_x86_initclocks(void);
void bmk_x86_fillgate(int, void *, int);

/* trap "handlers" */
diff --git a/platform/hw/include/bmk/clock_subr.h
b/platform/hw/include/bmk/clock_subr.h
new file mode 100644
index 0000000..fd5e872
--- /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 <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_ */
diff --git a/platform/hw/include/bmk/kernel.h b/platform/hw/include/bmk/kernel.h
index 232ce52..6e9017c 100644
--- a/platform/hw/include/bmk/kernel.h
+++ b/platform/hw/include/bmk/kernel.h
@@ -16,11 +16,13 @@ void bmk_cons_putc(int);
void bmk_cons_puts(const char *);

void bmk_cpu_init(void);
+void bmk_cpu_block(bmk_time_t);
void bmk_cpu_nanohlt(void);
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);
diff --git a/platform/hw/intr.c b/platform/hw/intr.c
index 1726d1b..b3eef2e 100644
--- a/platform/hw/intr.c
+++ b/platform/hw/intr.c
@@ -53,13 +53,6 @@ static unsigned int isr_lowest = sizeof(isr_todo)*8;

static struct bmk_thread *isr_thread;

-void
-bmk_isr_clock(void)
-{
-
- /* nada */
-}
-
/* thread context we use to deliver interrupts to the rump kernel */
static void
isr(void *arg)
diff --git a/platform/hw/kernel.c b/platform/hw/kernel.c
index 20ad79b..b607285 100644
--- a/platform/hw/kernel.c
+++ b/platform/hw/kernel.c
@@ -46,7 +46,7 @@ bmk_platform_block(bmk_time_t until)
bmk_spldepth = 1;
spl0();
}
- bmk_cpu_nanohlt();
+ bmk_cpu_block(until);
if (s) {
splhigh();
bmk_spldepth = s;



Other related posts:

  • » [PATCH] Implement timekeeping for rumprun/hw (x86) - Martin Lucina