From c7540c31d45dac68fa1852f5d25645d569b2da9d Mon Sep 17 00:00:00 2001 From: salmonik Date: Sat, 1 Aug 2026 14:09:14 +0200 Subject: klog: kernel log ring + /dev/klog + klog shell command - rsnix/src/klog.rs: RAM ring of the last 128 serial log lines (128 chars each), fed by serial_println! so every boot/low-level message is captured for free. Sequence numbers per line, wrap/drop accounting; the ring never allocates (const-constructible static). - /dev/klog char device; shell 'klog' is a thin alias for cat /dev/klog with the same filters/redirection, so 'klog -t 5', 'klog -g fat' and 'klog WRITING /disk/.../boot.log' all work. klog rejects positional args (usage) and input keywords. - Shell user-facing output (banner, prompt, term_print/term_println, pipeline sink, halt/poweroff) now writes to serial directly instead of through serial_println!, keeping echo/cat output out of the boot log (a streaming cat /dev/mem would otherwise wrap the ring). - serial_println! macro hooks klog::append_fmt via format_args! (no alloc at call sites); klog::dump formats the log for the device. Verified in QEMU: 28 boot lines captured (seq 0..27), shell output absent from the ring, klog WRITING persists a boot log (mtype-checked), wrap verified with a temporary 140-line boot loop (128 kept / 40 dropped, seq 40..167, oldest-live correct; loop reverted); full test-disk round trip still fsck-clean. --- shell/src/lib.rs | 64 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 15 deletions(-) (limited to 'shell/src') diff --git a/shell/src/lib.rs b/shell/src/lib.rs index cf46cb0..ddd8688 100644 --- a/shell/src/lib.rs +++ b/shell/src/lib.rs @@ -28,7 +28,7 @@ use alloc::collections::VecDeque; use alloc::string::{String, ToString}; use alloc::vec::Vec; use rsnix::tty::{self, LineInput}; -use rsnix::{gop_print, gop_println, serial_print, serial_println}; +use rsnix::{gop_print, gop_println}; /// Run the shell forever. pub fn run() -> ! { @@ -36,18 +36,17 @@ pub fn run() -> ! { "rs-dos v{} — type 'help' for commands", env!("CARGO_PKG_VERSION") ); - serial_println!( + term_println(&alloc::format!( "========== rs-dos v{} ==========", env!("CARGO_PKG_VERSION") - ); + )); loop { // Push any deferred scrolls so the prompt renders on a current // framebuffer (e.g. after a streaming command's batch of output). rsnix::vt::sync_scroll(); // Print prompt - gop_print!("> "); - serial_print!("> "); + term_print("> "); // Read one line from keyboard (tty canonical mode) match tty::read_line() { @@ -65,13 +64,26 @@ pub fn run() -> ! { } // --------------------------------------------------------------------------- -// Diagnostics go straight to terminal + serial, never through the filter -// pipeline and never into a file sink. +// Diagnostics and command output go straight to terminal + serial, never +// through the filter pipeline and never into a file sink. These helpers +// write to the serial port directly — NOT via `serial_println!`, which +// feeds the klog ring — so user-facing shell output (prompts, echoes, +// command results) never lands in the boot log. // --------------------------------------------------------------------------- +fn term_print(s: &str) { + gop_print!("{}", s); + rsnix::serial::write_str(s); +} + +fn term_println(s: &str) { + term_print(s); + gop_println!(); + rsnix::serial::write_str("\r\n"); +} + fn term_err(msg: &str) { - gop_println!("{}", msg); - serial_println!("{}", msg); + term_println(msg); } // --------------------------------------------------------------------------- @@ -541,8 +553,7 @@ impl Pipeline { fn emit(&mut self, l: &str) { match &self.sink { OutSink::Term => { - gop_println!("{}", l); - serial_println!("{}", l); + term_println(l); } OutSink::File { .. } => {} } @@ -672,6 +683,7 @@ write `{} TIL `", "pci" => cmd_pci(&parsed), "ls" => cmd_ls(&parsed), "cat" => cmd_cat(&parsed), + "klog" => cmd_klog(&parsed), "hexdump" => cmd_hexdump(&parsed), "mkdir" => cmd_mkdir(&parsed), "touch" => cmd_touch(&parsed), @@ -700,6 +712,7 @@ fn cmd_help(_p: &Parsed) { pci — list PCI devices ls — list a directory cat — print files (cat USE a TO b copies a→b; cat TIL END is a heredoc) + klog — kernel boot log (cat /dev/klog; WRITING persists it) hexdump — hex-dump a file or input stream mkdir — create a directory touch — create an empty file @@ -788,6 +801,29 @@ fn cmd_ls(p: &Parsed) { pipe.end(); } +// --------------------------------------------------------------------------- +// klog — kernel log (thin alias for `cat /dev/klog`) +// --------------------------------------------------------------------------- + +fn cmd_klog(p: &Parsed) { + if !p.args.is_empty() { + term_err("klog: usage: klog [-h N] [-t N] [-g PAT] [-p] [WRITING | TO ]"); + return; + } + // Alias for `cat /dev/klog` — same filters and redirection, so + // `klog -t 5` shows the last 5 log lines and `klog WRITING + // /disk/.../boot.log` persists the boot log. Input keywords were + // already rejected (klog does not accept input). + let q = Parsed { + cmd: alloc::string::String::from("klog"), + args: alloc::vec!["/dev/klog".into()], + input: Input::None, + filters: p.filters.clone(), + output: p.output.clone(), + }; + cmd_cat(&q); +} + // --------------------------------------------------------------------------- // cat — file and stream consumers // --------------------------------------------------------------------------- @@ -1471,8 +1507,7 @@ fn cmd_stress(p: &Parsed) { } fn cmd_halt() -> ! { - gop_println!("halting..."); - serial_println!("halting..."); + term_println("halting..."); rsnix::power::halt() } @@ -1515,7 +1550,6 @@ fn cmd_pci(p: &Parsed) { } fn cmd_poweroff() -> ! { - gop_println!("shutting down..."); - serial_println!("shutting down..."); + term_println("shutting down..."); rsnix::power::poweroff() } -- cgit v1.2.3