From e499f500c36d6eb4505136a87b2212cc8f18b01d Mon Sep 17 00:00:00 2001 From: Daniel Samson <12231216+daniel-samson@users.noreply.github.com> Date: Sat, 11 Jul 2026 03:43:33 +0100 Subject: [PATCH] adding clock system call --- library/runtime/system.zig | 10 ++++++++++ system/abi.zig | 1 + system/kernel/tests.zig | 26 ++++++++++++++++++++++++++ test/qemu_test.py | 4 ++++ 4 files changed, 41 insertions(+) diff --git a/library/runtime/system.zig b/library/runtime/system.zig index 39bb4c4..40eb40d 100644 --- a/library/runtime/system.zig +++ b/library/runtime/system.zig @@ -27,6 +27,16 @@ pub fn sleep(ms: usize) void { _ = sc.systemCall1(.sleep, ms); } +/// Monotonic nanoseconds since boot — a time source for timeouts and short delays. It +/// only ever moves forward. This is *not* wall-clock time (no date, no timezone — that +/// is a user-space service layered on top). Deadline pattern for a bounded poll loop: +/// +/// const deadline = clock() + timeout_ns; +/// while (clock() < deadline) { ... } +pub fn clock() u64 { + return @intCast(sc.systemCall0(.clock)); +} + /// End the process. Never returns. pub fn exit(code: usize) noreturn { _ = sc.systemCall1(.exit, code); diff --git a/system/abi.zig b/system/abi.zig index ac12c7d..acb7627 100644 --- a/system/abi.zig +++ b/system/abi.zig @@ -49,6 +49,7 @@ pub const SystemCall = enum(u64) { msi_bind = 20, // msi_bind(device_id, endpoint) -> address (rax), data (rdx): a per-device MSI vector for a claimed device io_read = 21, // io_read(device_id, resource_index, offset, width) -> value: read a port in a claimed device's io_port resource io_write = 22, // io_write(device_id, resource_index, offset, width, value) -> 0: write a port in a claimed device's io_port resource + clock = 23, // clock() -> nanoseconds since boot: a monotonic time source (for timeouts/delays) _, }; diff --git a/system/kernel/tests.zig b/system/kernel/tests.zig index 1d7f909..cceeb8f 100644 --- a/system/kernel/tests.zig +++ b/system/kernel/tests.zig @@ -92,6 +92,8 @@ pub fn run(case: []const u8, boot_information: *const BootInformation) void { iommuTest(); } else if (eql(case, "ioport")) { ioPortTest(); + } else if (eql(case, "clock")) { + clockTest(); } else if (eql(case, "smp")) { smpTest(); } else if (eql(case, "affinity")) { @@ -1100,6 +1102,30 @@ fn ioPortTest() void { result(); } +/// The monotonic clock (the source `clock()` surfaces to user space). It must be +/// calibrated, move forward over a spin, and never run backwards — the guarantees a +/// driver's deadline timeout depends on. The syscall is a thin wrapper over this same +/// `architecture.nanos()`. +fn clockTest() void { + log("DANOS-TEST-BEGIN: clock\n", .{}); + const t0 = architecture.nanos(); + check("monotonic clock is calibrated (nonzero)", t0 != 0); + + var last = t0; + var monotonic = true; + var advanced = false; + var i: u32 = 0; + while (i < 1_000_000) : (i += 1) { + const t = architecture.nanos(); + if (t < last) monotonic = false; + if (t > t0) advanced = true; + last = t; + } + check("clock advanced over the spin", advanced); + check("clock never ran backwards (monotonic)", monotonic); + result(); +} + var proc_worker_run: bool = true; var proc_worker_ran: bool = false; diff --git a/test/qemu_test.py b/test/qemu_test.py index 2587fb8..21d5f9c 100644 --- a/test/qemu_test.py +++ b/test/qemu_test.py @@ -151,6 +151,10 @@ CASES = [ {"name": "ioport", "expect": r"DANOS-TEST-RESULT: PASS", "fail": r"DANOS-TEST-RESULT: FAIL"}, + # Monotonic clock (clock() syscall source): calibrated, advancing, never backwards. + {"name": "clock", + "expect": r"DANOS-TEST-RESULT: PASS", + "fail": r"DANOS-TEST-RESULT: FAIL"}, # Parallelism: needs more than one core, so this case boots with -smp 4. {"name": "smp", "smp": 4,