From b5419212189e216ea85e41bbf59b638a141d6d4d Mon Sep 17 00:00:00 2001 From: Daniel Samson <12231216+daniel-samson@users.noreply.github.com> Date: Tue, 21 Jul 2026 19:15:14 +0100 Subject: [PATCH] =?UTF-8?q?build:=20-Ddiagnose=20=E2=80=94=20boot=20withou?= =?UTF-8?q?t=20the=20display=20so=20the=20transcript=20stays=20on=20screen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The display service claiming the framebuffer suppresses the on-screen boot transcript — correctly in normal operation, but on a serial-less machine being debugged, the timeline vanishes just when it matters. A diagnose image (zig build -Ddiagnose=true) has init skip the display service and demo: the timestamped transcript stays on screen indefinitely, and the power button still runs the orderly shutdown (so the logger's files land when storage works). First use immediately caught a real bug IN QEMU: an intermittent (~1 in 3) usb-storage READ CAPACITY failure after a ~21 s stall — after which usb-storage and fat both exit cleanly and nothing retries: one transient early-boot USB failure leaves the system permanently without storage (and therefore without logs). That no-retry policy is a prime suspect for real hardware never mounting /var, and is Track B's first work item. --- build.zig | 5 +++++ system/services/init/init.zig | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 5f7ceed..3021d44 100644 --- a/build.zig +++ b/build.zig @@ -468,6 +468,10 @@ pub fn build(b: *std.Build) void { // QEMU test harness (test/qemu_test.py, which asserts on serial markers) turn // it on; a flashable `zig build` image leaves it out. See serial.zig. const serial = b.option(bool, "serial", "Compile the serial-console log sink into the kernel (default: off; run-x86-64 and the test harness enable it)") orelse false; + // The diagnose boot: init skips the display service (and demo), so the + // on-screen boot transcript is never suppressed — the full timestamped + // timeline stays on the screen for real-hardware debugging by eye. + const diagnose = b.option(bool, "diagnose", "Boot without the display service so the timestamped boot transcript stays on screen (real-hardware debugging)") orelse false; // --- Kernel: freestanding x86_64 ELF, jumped to by the bootloader --- // SSE2 is part of the x86_64 baseline and UEFI leaves it enabled at handoff, @@ -511,6 +515,7 @@ pub fn build(b: *std.Build) void { // the heartbeat stays present under test. const init_options = b.addOptions(); init_options.addOption(bool, "serial", serial); + init_options.addOption(bool, "diagnose", diagnose); programModule(init_exe).addImport("build_options", init_options.createModule()); // --- the rest of the /system tree: services, drivers, test fixtures --- diff --git a/system/services/init/init.zig b/system/services/init/init.zig index 1a06839..137b1fe 100644 --- a/system/services/init/init.zig +++ b/system/services/init/init.zig @@ -27,7 +27,15 @@ const build_options = @import("build_options"); /// kernel. Drivers are absent on purpose: the device manager owns those. (A /// future init reads this from a manifest under /system/services instead of a /// hardcoded list.) -const boot_services = [_][]const u8{ +const boot_services = if (build_options.diagnose) [_][]const u8{ + // The diagnose boot: no display service, so the kernel's on-screen boot + // transcript is never suppressed — the timestamped timeline (USB bring-up, + // storage, logger) stays readable on real hardware with no serial. + "/system/services/input", + "/system/services/device-manager", + "/system/services/fat", + "/system/services/logger", +} else [_][]const u8{ "/system/services/input", "/system/services/device-manager", "/system/services/fat",