Phase 34
Real Time Clock
The OS knows what time it is. A CMOS RTC driver reads the hardware clock at boot to establish the current wall-clock time. `clock_gettime(CLOCK_REALTIME)` returns actual Unix timestamps, distinct from `CLOCK_MONOTONIC` which tracks time since boot. The `date` command displays the current date and time.
Learning goal
Understand the difference between wall-clock time (RTC, NTP) and monotonic time (tick counter).
Category
foundations
Components touched
Builds on
Unlocked by this phase
Why this phase matters
The OS knows what time it is. A CMOS RTC driver reads the hardware clock at boot to establish the current wall-clock time. clock_gettime(CLOCK_REALTIME) returns actual Unix timestamps, distinct from CLOCK_MONOTONIC which tracks time since boot. The date command displays the current date and time.
How it works in m3OS
Phase 34 adds wall-clock time awareness to m3OS by reading the CMOS Real-Time Clock (RTC) at boot, computing the Unix epoch timestamp, and exposing it through the clock_gettime() and gettimeofday() syscalls. Userspace utilities date and uptime are provided.
How it builds on earlier phases
This phase builds on Kernel Memory Improvements and turns that foundation into Real Time Clock.
Implementation outline
- Write CMOS RTC read functions (with BCD conversion and atomic read loop).
- Implement
rtc_to_unix_timestamp()in kernel-core (host-testable). - Read RTC during kernel boot and store
BOOT_EPOCH_SECS. - Update
clock_gettime()to distinguishCLOCK_REALTIMEvsCLOCK_MONOTONIC. - Update
gettimeofday()to use wall-clock time. - Write
dateuserspace utility. - Write host-side tests for time conversion (known dates -> expected timestamps).
- Verify:
dateinside the OS matches host system time (within a few seconds).
Task progress snapshot
48 of 50 tracked tasks are complete.
Documentation targets
- Documentation deliverables have not been listed yet.
Deferred follow-on work
- NTP time synchronization
- High-resolution timers (HPET, TSC)
- vDSO for fast clock_gettime
- Timezone support
- RTC alarm / wakeup
- adjtimex() clock steering
- Hardware watchdog timer
Source materials
Important code
Code spotlight cards
Code spotlight
Implement `days_in_month()` and `is_leap_year()`
Implement `days_in_month()` and `is_leap_year()`. Why it matters: Leap-year-aware month math is the base layer for every RTC conversion added later in the phase. Focus on `days_in_month` and `is_leap_year` in `kernel-core/src/time.rs` while you trace the snippet.
/// Returns the number of days in the given month (1-indexed) for the given year.
///
/// Panics if `month` is 0 or greater than 12.
pub fn days_in_month(year: u32, month: u32) -> u32 {
match month {
1 => 31,
2 => {
if is_leap_year(year) {
29
} else {
28
}
}
3 => 31,
4 => 30,
5 => 31,
6 => 30,
7 => 31,
8 => 31,
9 => 30,
10 => 31,
11 => 30,
12 => 31,
_ => panic!("invalid month"), Code spotlight
Implement CMOS register read function
Implement CMOS register read function. Why it matters: A minimal raw CMOS accessor is the hardware boundary that the rest of the RTC driver can safely build on. Focus on `cmos_read` in `kernel/src/rtc.rs` while you trace the snippet.
/// Read a single CMOS register.
///
/// Port 0x70 is the address/NMI-disable port (bit 7 disables NMI).
/// Port 0x71 is the data port.
///
/// # Safety
///
/// Performs raw x86 port I/O. Must only be called in ring 0.
unsafe fn cmos_read(register: u8) -> u8 {
let mut addr_port = Port::<u8>::new(0x70);
let mut data_port = Port::<u8>::new(0x71);
unsafe {
addr_port.write(register | 0x80); // bit 7 = disable NMI during read
let value = data_port.read();
// Re-enable NMI by writing the register index with bit 7 clear.
addr_port.write(register & 0x7F);
value
}
} Code spotlight
Wire RTC module into kernel build
Wire RTC module into kernel build. Why it matters: Boot-time RTC initialization is what replaces the earlier monotonic-only startup state with a real wall-clock epoch. Focus on `rtc::init_rtc` in `kernel/src/main.rs` while you trace the snippet.
Key files
- kernel/src/rtc.rs — Reads the CMOS RTC via I/O ports 0x70/0x71. Implements the atomic read protocol
- kernel-core/src/time.rs — detection. Called once during kernel boot to establish the boot epoch.
- kernel-core/src/lib.rs — Added pub mod time
- kernel/src/main.rs — Added mod rtc + init_rtc() call
- kernel/src/arch/x86_64/syscall.rs — Updated clock_gettime/gettimeofday
- kernel/src/fs/ramdisk.rs — Added date/uptime to initrd
- userspace/syscall-lib/src/lib.rs — Added time wrappers + formatting
- userspace/coreutils-rs/src/date.rs — New: date utility
- userspace/coreutils-rs/src/uptime.rs — New: uptime utility
- userspace/coreutils-rs/Cargo.toml — Added date/uptime binaries
- xtask/src/main.rs — Added date/uptime to build list
- kernel/Cargo.toml — Version bump 0.33.0 → 0.34.0
How real OSes differ
- **NTP** (Network Time Protocol) to synchronize wall-clock time with atomic clocks.
- **HPET** or **TSC** for nanosecond-resolution timekeeping (vs our 10ms LAPIC ticks).
- **vDSO** to serve `clock_gettime()` from userspace without a syscall.
- **Timezone databases** (tzdata) for local time conversion.
- **RTC IRQ** (interrupt on alarm or periodic) for wakeup-from-sleep.
- **adjtimex()** syscall for fine-grained clock steering.
Success criteria
- `clock_gettime(CLOCK_REALTIME)` returns correct Unix timestamps (verified against host).
- `clock_gettime(CLOCK_MONOTONIC)` returns time since boot (unchanged from current behavior).
- `date` command displays the correct current date and time.
- RTC read handles BCD vs binary mode correctly.
- RTC read handles 12-hour vs 24-hour mode correctly.
- Time conversion passes test vectors (known dates -> Unix timestamps).
- Boot log shows the detected wall-clock time.
Related phases
Keep exploring the roadmap
Phase 33
CompleteKernel Memory Improvements
The kernel heap allocator is robust, efficient, and recoverable. Out-of-memory conditions no longer panic the kernel — the allocator grows on demand and retries failed allocations. Buddy allocation, working `munmap()`, userspace-heap coalescing, and slab-cache infrastructure all land. Broad migration of hot kernel object families onto slab caches remains later work.
Phase 35
CompleteTrue SMP Multitasking
All CPU cores dispatch and run user tasks. The BSP-only restriction is removed by making syscall infrastructure per-core. The scheduler introduces per-CPU run queues with load balancing and task priorities, substantially improving dispatch locality and multi-core throughput even though the global scheduler lock remains on the dispatch hot path for task-state coordination.