Sixty-five headers sat side by side in include/, so `#include "irq.h"` said nothing about where irq.h was or what it belonged to, and errno.h, stat.h and fcntl.h at the top shadowed the system's for any host tool built with -I./include. They are in folders now -- arch/ core/ fs/ dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and every include names the folder, as Linux's do: "core/proc.h". Done mechanically and checked the hard way: the kernel built from this tree disassembles identically to the one built from the tree before it. The full image boots, and userland is the same set of programs. Paths in docs and comments follow; docs/guides/layout.md has a table of what is where, and tools/fix-includes.py rewrites old-style includes from the folders in the tree, for code written before this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
163 lines
6.8 KiB
C
163 lines
6.8 KiB
C
// Wall-clock time.
|
|
//
|
|
// The kernel has always known how long it had been running -- `ticks`, bumped
|
|
// 100 times a second by the timer interrupt -- and has never known what time it
|
|
// was. sys_gettimeofday() returned `ticks / 100` as though it were seconds
|
|
// since the epoch, so every SchOS machine believed it was the 1st of January
|
|
// 1970 and that the date advanced at the rate of its own uptime. `date` printed
|
|
// a number that barely moved, which is what it looks like when a clock is
|
|
// counting from the wrong place rather than not counting at all.
|
|
//
|
|
// cmostime() in kernel/arch/x86_64/lapic.c has been able to read the hardware
|
|
// clock the whole time. Nothing called it.
|
|
//
|
|
// The arrangement here is the usual one: read the RTC once at boot, convert it
|
|
// to a Unix timestamp, and add uptime to it thereafter. The RTC is slow to read
|
|
// (it is polled over two I/O ports and has to be sampled twice to catch it
|
|
// mid-update) so reading it on every gettimeofday would be absurd; and `ticks`
|
|
// is a perfectly good interval clock once it has something to count from.
|
|
|
|
#include "core/types.h"
|
|
#include "core/defs.h"
|
|
#include "arch/x86.h"
|
|
#include "core/date.h"
|
|
#include "core/param.h"
|
|
#include "core/spinlock.h"
|
|
|
|
extern uint ticks;
|
|
|
|
// Seconds between the Unix epoch and the moment this machine booted. Zero until
|
|
// time_init() runs, which makes the behaviour before that point exactly what it
|
|
// used to be -- uptime presented as absolute time -- rather than something new
|
|
// and differently wrong.
|
|
uint64 boot_epoch;
|
|
|
|
// Days from 1970-01-01 to the given civil date, proleptic Gregorian.
|
|
//
|
|
// Howard Hinnant's days_from_civil. It is worth using rather than a loop over
|
|
// years because it is branchless, exact for any year, and has no table to get
|
|
// wrong; the alternative -- accumulating 365 or 366 days per year since 1970 --
|
|
// gets leap centuries wrong unless you remember that 1900 was not a leap year
|
|
// and 2000 was, which is precisely the sort of thing that is discovered on a
|
|
// February morning years later.
|
|
//
|
|
// The trick is shifting the year so that March is month 1, which puts the leap
|
|
// day at the *end* of the year and makes the leap rule depend only on the year
|
|
// number rather than on where in the year you are.
|
|
static int64
|
|
days_from_civil(int64 y, unsigned m, unsigned d)
|
|
{
|
|
y -= m <= 2;
|
|
const int64 era = (y >= 0 ? y : y - 399) / 400;
|
|
const unsigned yoe = (unsigned)(y - era * 400); // [0, 399]
|
|
const unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
|
|
const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // [0, 146096]
|
|
return era * 146097 + (int64)doe - 719468;
|
|
}
|
|
|
|
// Read the hardware clock and work out when we booted.
|
|
//
|
|
// The RTC is read in UTC. That is not universally true on a PC -- a machine
|
|
// that has had Windows installed on it usually keeps the RTC in local time --
|
|
// but there is nowhere to store a timezone yet and no way to ask, and UTC is
|
|
// the honest default for a system with no configuration. When there is a
|
|
// settings store, this is where the offset goes.
|
|
void
|
|
time_init(void)
|
|
{
|
|
struct rtcdate r;
|
|
|
|
cmostime(&r);
|
|
|
|
// cmostime() returns a two-digit year on hardware that only stores one.
|
|
// 2000-2099 is the right guess now and stays right for a lifetime; a machine
|
|
// reporting 70 means the battery is flat, and the epoch is as good an answer
|
|
// as any for that.
|
|
int64 year = r.year;
|
|
if (year < 100)
|
|
year += 2000;
|
|
|
|
// A wildly implausible reading means the CMOS is not telling us anything.
|
|
// Leaving boot_epoch at zero keeps the old behaviour, which is wrong but
|
|
// predictable, rather than putting the machine somewhere in the Bronze Age.
|
|
if (r.month < 1 || r.month > 12 || r.day < 1 || r.day > 31 ||
|
|
r.hour > 23 || r.minute > 59 || r.second > 60 || year < 1970) {
|
|
cprintf("time: CMOS clock is not plausible, staying at the epoch\n");
|
|
return;
|
|
}
|
|
|
|
int64 days = days_from_civil(year, r.month, r.day);
|
|
uint64 now = (uint64)(days * 86400) + r.hour * 3600 + r.minute * 60 + r.second;
|
|
|
|
// Subtract the uptime so far, so boot_epoch is the moment of boot rather than
|
|
// the moment this function ran. It is only a fraction of a second, but it
|
|
// keeps boot_epoch meaning exactly what its name says.
|
|
uint64 up, dummy;
|
|
monotonic(&up, &dummy);
|
|
boot_epoch = now > up ? now - up : now;
|
|
|
|
kinfo("time: %d-%d-%d %d:%d:%d UTC from the hardware clock\n",
|
|
(int)year, r.month, r.day, r.hour, r.minute, r.second);
|
|
}
|
|
|
|
// How long the machine has been running, from the TSC.
|
|
//
|
|
// **Not from `ticks`.** This is the important line in this file.
|
|
//
|
|
// `ticks` counts timer interrupts, and a timer interrupt that arrives while
|
|
// interrupts are disabled is not delayed, it is *lost* -- the LAPIC in periodic
|
|
// mode holds one pending interrupt and drops the rest. So anything the kernel
|
|
// does with interrupts off for longer than 10 ms makes the clock run slow, and
|
|
// it never catches up. A long console write, a framebuffer scroll on a display
|
|
// that is expensive to draw to, a driver spinning on a register: all of them
|
|
// silently steal time from every program that asks what time it is.
|
|
//
|
|
// That has now happened twice. The first was an interrupt storm taking 83% of
|
|
// the CPU (docs/history/kernel.md §13) and the clock ran seven times slow; the
|
|
// second was heavy console output, and it presented the same way. Both times
|
|
// the tick was the messenger.
|
|
//
|
|
// The TSC cannot be starved. It is a counter in the silicon that advances at a
|
|
// fixed rate whether or not this kernel is paying attention, and it is already
|
|
// measured against the PIT at boot. Reading it is one instruction and no lock.
|
|
//
|
|
// `ticks` is still what the scheduler preempts on and what sleep() counts,
|
|
// which is the right job for it: losing a tick there means a slightly longer
|
|
// sleep, not a wrong answer about what time it is.
|
|
void
|
|
monotonic(uint64 *sec, uint64 *usec)
|
|
{
|
|
// Before calibration there is no TSC frequency to divide by. Fall back to
|
|
// the tick count, which is what this used to do always.
|
|
if (tsc_hz == 0) {
|
|
uint xticks;
|
|
acquire(&tickslock);
|
|
xticks = ticks;
|
|
release(&tickslock);
|
|
if (sec) *sec = xticks / HZ;
|
|
if (usec) *usec = (uint64)(xticks % HZ) * (1000000 / HZ);
|
|
return;
|
|
}
|
|
|
|
uint64 d = rdtsc() - boot_tsc;
|
|
uint64 s = d / tsc_hz;
|
|
// The remainder first, then scale. `d * 1000000` would overflow 64 bits
|
|
// after about 80 minutes at 3.8 GHz; the remainder is always below tsc_hz,
|
|
// so this cannot.
|
|
uint64 rem = d - s * tsc_hz;
|
|
|
|
if (sec) *sec = s;
|
|
if (usec) *usec = (rem * 1000000) / tsc_hz;
|
|
}
|
|
|
|
// Seconds and microseconds since the epoch. One place, so gettimeofday,
|
|
// clock_gettime and anything added later cannot disagree about what time it is.
|
|
void
|
|
wallclock(uint64 *sec, uint64 *usec)
|
|
{
|
|
uint64 s, us;
|
|
|
|
monotonic(&s, &us);
|
|
if (sec) *sec = boot_epoch + s;
|
|
if (usec) *usec = us;
|
|
}
|