Skip to main content

strat9_kernel/boot/
logger.rs

1use core::sync::atomic::{AtomicBool, Ordering};
2use log::{Level, LevelFilter, Metadata, Record};
3
4/// Gate for format_args-heavy logging paths.
5///
6/// During early boot, `format_args!` involving nested `fmt::Arguments`
7/// (e.g. `record.args()` inside another `format_args!`) can resolve to
8/// identity-mapped function pointers and #UD.  This flag is set to `true`
9/// after `init_cpu_extensions()` enables CR4.OSXSAVE + XCR0, at which
10/// point all formatting is safe.
11static EXTENSIONS_READY: AtomicBool = AtomicBool::new(false);
12
13/// Simple serial port logger
14struct SerialLogger;
15
16static LOGGER: SerialLogger = SerialLogger;
17
18impl log::Log for SerialLogger {
19    /// Performs the enabled operation.
20    fn enabled(&self, metadata: &Metadata) -> bool {
21        metadata.level() <= Level::Trace
22    }
23
24    /// Performs the log operation.
25    ///
26    /// Architecture (FreeBSD vt(4) / Linux nbcon pattern):
27    ///   1. Queue serial output at runtime (synchronous during early boot).
28    ///   2. Enqueue a plain-text copy into the lock-free `vgabuf` ring buffer.
29    ///      No lock is acquired here : safe from any context (IRQ, panic).
30    ///   3. The `console-render` task drains `vgabuf` to the framebuffer
31    ///      asynchronously via `vgabuf_flush_to_framebuffer()`.
32    ///
33    /// This decouples the hot logging path from framebuffer rendering,
34    /// making deadlocks structurally impossible.
35    fn log(&self, record: &Record) {
36        // Gate: skip format_args-heavy paths until SSE/XSAVE are initialized.
37        if !EXTENSIONS_READY.load(Ordering::Acquire) {
38            return;
39        }
40        if self.enabled(record.metadata()) {
41            // In quiet mode, skip all log output entirely.
42            if crate::debug_cfg::is_quiet() {
43                return;
44            }
45
46            let (level_str, msg_color) = match record.level() {
47                Level::Error => ("\x1b[31mERROR\x1b[0m", "\x1b[31m"),
48                Level::Warn => ("\x1b[33mWARN\x1b[0m", "\x1b[33m"),
49                Level::Info => ("\x1b[32mINFO\x1b[0m", "\x1b[37m"),
50                Level::Debug => ("\x1b[90mDEBUG\x1b[0m", "\x1b[90m"),
51                Level::Trace => ("\x1b[90mTRACE\x1b[0m", "\x1b[90m"),
52            };
53
54            // 1. Serial: bounded, nonblocking enqueue once its worker is running.
55            crate::arch::serial::_print(format_args!(
56                "[{}] {}{}\x1b[0m\n",
57                level_str,
58                msg_color,
59                record.args()
60            ));
61
62            // 1b. Live VGA debug (early boot): write directly to framebuffer
63            //     via vga_debug_write.  This bypasses VGA_WRITER and works
64            //     before the scheduler / status_line_task are running.
65            //     Controlled by debug_cfg::VGA_DEBUG_LIVE toggle.
66            if crate::debug_cfg::is_vga_debug_live() && crate::arch::vga::is_available() {
67                let mut line_buf = [0u8; 256];
68                let mut lpos = 0usize;
69                use core::fmt::Write;
70                struct LineWriter<'a> {
71                    buf: &'a mut [u8],
72                    pos: &'a mut usize,
73                }
74                impl Write for LineWriter<'_> {
75                    fn write_str(&mut self, s: &str) -> core::fmt::Result {
76                        let bytes = s.as_bytes();
77                        let remaining = self.buf.len().saturating_sub(*self.pos);
78                        let n = bytes.len().min(remaining);
79                        self.buf[*self.pos..*self.pos + n].copy_from_slice(&bytes[..n]);
80                        *self.pos += n;
81                        Ok(())
82                    }
83                }
84                let _ = write!(
85                    LineWriter {
86                        buf: &mut line_buf,
87                        pos: &mut lpos
88                    },
89                    "[{}] {}",
90                    record.level(),
91                    record.args(),
92                );
93                if lpos > 0 {
94                    if let Ok(s) = core::str::from_utf8(&line_buf[..lpos]) {
95                        crate::arch::vga::vga_debug_writeln(s);
96                    }
97                }
98            }
99
100            // 2. VGA ring buffer : lock-free enqueue.
101            //    Controlled by debug_cfg::VGA_DEBUG_BUFFER toggle.
102            if crate::debug_cfg::is_vga_debug_buffer() && crate::arch::vga::is_available() {
103                let mut vbuf = [0u8; crate::arch::vgabuf::VGABUF_LINE_LEN];
104                let mut vpos = 0usize;
105                use core::fmt::Write;
106                struct VgaBufWriter<'a> {
107                    buf: &'a mut [u8],
108                    pos: &'a mut usize,
109                }
110                impl Write for VgaBufWriter<'_> {
111                    fn write_str(&mut self, s: &str) -> core::fmt::Result {
112                        let bytes = s.as_bytes();
113                        let remaining = self.buf.len().saturating_sub(*self.pos);
114                        let n = bytes.len().min(remaining);
115                        self.buf[*self.pos..*self.pos + n].copy_from_slice(&bytes[..n]);
116                        *self.pos += n;
117                        Ok(())
118                    }
119                }
120                let _ = write!(
121                    VgaBufWriter {
122                        buf: &mut vbuf,
123                        pos: &mut vpos
124                    },
125                    "[{}] {}\n",
126                    record.level(),
127                    record.args(),
128                );
129                if vpos > 0 {
130                    crate::arch::vgabuf::vgabuf_write(&vbuf[..vpos]);
131                }
132            }
133        }
134    }
135
136    /// Performs the flush operation.
137    fn flush(&self) {}
138}
139
140/// Initialize the logger
141pub fn init() {
142    crate::e9_mark!(b'{');
143    log::set_logger(&LOGGER)
144        .map(|()| {
145            crate::e9_mark!(b'|');
146            log::set_max_level(LevelFilter::Trace);
147            crate::e9_mark!(b'}');
148        })
149        .expect("Failed to set logger");
150    crate::e9_mark!(b'~');
151}
152
153/// Mark SSE/XSAVE extensions as ready.
154///
155/// Called once from `kernel_main` after `init_cpu_extensions()` has set
156/// CR4.OSXSAVE and XCR0.  Until this is called, all `log::info!` / etc.
157/// fire the `e9_println!("LOG enter")` marker but skip the format_args-heavy
158/// formatting paths that can #UD on early boot.
159pub fn set_extensions_ready() {
160    crate::e9_mark!(b'Z');
161    EXTENSIONS_READY.store(true, Ordering::Release);
162    crate::e9_mark!(b'z');
163}