Impossible OS Docs

Time and FILETIME

What is it?

The time subsystem gives the kernel one family of clocks: a FILETIME wall clock (100-nanosecond ticks since 1601-01-01 UTC, the Windows convention), a monotonic nanosecond counter for uptime and scheduling, interrupt time with and without suspend bias, and performance-counter system calls for user mode. Without it, file timestamps, log lines and scheduler deadlines would be zero or invented.

It also owns the KUSER_SHARED_DATA time fields that let user code read the clock without a system call, per-process timer resolution, time zone bias, and the timestamps FAT32 and NTFS write. Most of the roadmap has shipped; per-CPU TSC synchronization and suspend-time bias tracking are parked on prerequisites outside this subsystem.

How does it work?

The monotonic counter comes first. mono_clock_init() in mono_clock.c picks the best source in order: invariant TSC, then the HPET, then the ACPI PM timer, then the LAPIC or PIT tick count, and logs the choice (for example Monotonic clock: TSC (N MHz, invariant)). A candidate with an implausible frequency is rejected at boot. When the TSC is active and a qualified HPET exists, a watchdog compares the two about every half second and demotes a drifting TSC, logging clocksource: demoting ...; without an HPET there is no automatic drift monitor. A floor guarantees mono_ns() never steps backward across a demotion.

The wall clock in wall_clock.c is a FILETIME anchor plus the monotonic reading at that instant, protected by a seqlock. wall_clock_init() seeds it from UEFI GetTime() with a plausibility check and falls back to the CMOS clock only through rtc_try_read(), which fails closed instead of returning a zeroed date. KeQuerySystemTime() adds the monotonic delta to the anchor; KeQuerySystemTimeCoarse() returns a value cached once per tick, for hot paths such as klog.

On every timer tick, kusd_time.c updates the KUSER_SHARED_DATA page mapped read-only at 0x7FFE0000: interrupt time, system time, time zone bias and tick count, at the offsets defined in kusd.h. Each 64-bit time is written with the three-step KSYSTEM_TIME protocol, so a reader never sees a torn value. KeQueryInterruptTime() includes a suspend bias that a future resume path will add to; KeQueryUnbiasedInterruptTime() excludes it.

Timer resolution (timer_resolution.c) keeps one request per process in a 16-slot table, and the shortest request wins, between 0.5 ms and the 15.625 ms default. A change reprograms the LAPIC timer and logs Timer resolution: N us.

NTP support is two layers. ke_ntp_adjtime() (ntp_adj.h) is the entry point a future network time client will call: an offset over one second steps the wall clock, a smaller one is slewed, and a frequency correction is applied continuously. A per-tick applier consumes the slew behind a floor, so a negative correction slows the clock instead of stepping it backward. The monotonic clock, performance counter and interrupt time are never adjusted.

flowchart LR
    SRC["TSC, HPET, PM timer, LAPIC"] --> MONO["mono_ns"]
    UEFI["UEFI GetTime or CMOS"] --> WALL["wall clock"]
    MONO --> WALL
    WALL --> KUSD["KUSER_SHARED_DATA"]
    WALL --> NT["NtQuerySystemTime, NtQueryPerformanceCounter"]
    WALL --> FS["FAT32 and NTFS timestamps"]
    NTP["ke_ntp_adjtime"] --> WALL

What are its interfaces?

Interface Purpose
filetime_from_unix_seconds(), filetime_to_dos_datetime(), filetime_from_efi_time(), filetime_to_string() FILETIME conversion and formatting (filetime.h)
mono_ns(), mono_ns_coarse(), mono_clock_source_name() Monotonic clock (mono_clock.h)
KeQuerySystemTime(), KeSetSystemTime(), KeQuerySystemTimePrecise(), KeQuerySystemTimeCoarse() Wall clock (wall_clock.h)
KeQueryInterruptTime(), KeQueryUnbiasedInterruptTime(), ke_suspend_bias_update() Interrupt time with and without suspend bias
KeQueryTickCount(), KeDelayExecutionThread() Tick count and thread delay
NtQuerySystemTime, NtSetSystemTime, NtQueryPerformanceCounter, NtQueryTimerResolution, NtSetTimerResolution System calls for user mode
KeSetTimerResolution(), KeQueryTimerResolution() Timer resolution arbitration (timer_resolution.h)
timezone_set(), filetime_to_local(), filetime_from_local() Time zone bias and daylight saving (timezone.h)
ke_ntp_adjtime(), ke_ntp_get_status() Hooks for a network time client (ntp_adj.h)
hpet_ns(), rtc_try_read() HPET counter and fail-closed CMOS read (hpet.c, rtc.c)

How do I use it?

The time service starts on every boot; there is no setting.

bash scripts/test.sh SUITE=sched    # clocks, interrupt time, timer resolution, NTP, time zone
bash scripts/test.sh SUITE=abi      # FILETIME conversion and the time system calls

Serial shows the chosen monotonic source and the time zone default (Timezone: UTC ...). From user mode, NtQueryPerformanceCounter reports a fixed 10 MHz frequency, and the tick count and system time can be read straight from KUSER_SHARED_DATA without a system call. The tests are in test_time.c.

What is not implemented yet?

How does it compare with Windows 11 and Linux?

Impossible OS matches both on the FILETIME wall clock, an invariant-TSC monotonic clock with HPET and PM timer fallback, a drift watchdog with demotion, NTP slew and frequency discipline, biased and unbiased interrupt time, per-process timer resolution, the shared time page updated from the timer interrupt, and FAT32 and NTFS timestamps. Leap seconds are not counted, as in Win32 and POSIX.

Two gaps remain: per-CPU TSC synchronization (Windows syncs at CPU start, Linux runs check_tsc_sync) and suspend-time bias (Windows InterruptTimeBias, Linux CLOCK_BOOTTIME). Two extras go beyond both: a fixed 10 MHz performance-counter frequency, so applications never need to query it, and named coarse time functions for hot paths.

See also

Impossible OS is free software under the GPL-3.0-only license. Last updated Edit this page on GitHub