Skip to main content

strat9_kernel/boot/
logger.rs

1use log::{Level, LevelFilter, Metadata, Record};
2
3/// Simple serial port logger
4struct SerialLogger;
5
6static LOGGER: SerialLogger = SerialLogger;
7
8impl log::Log for SerialLogger {
9    /// Performs the enabled operation.
10    fn enabled(&self, metadata: &Metadata) -> bool {
11        metadata.level() <= Level::Trace
12    }
13
14    /// Performs the log operation.
15    ///
16    /// Architecture (FreeBSD vt(4) / Linux nbcon pattern):
17    ///   1. Always write to serial (fast, always works).
18    ///   2. Enqueue a plain-text copy into the lock-free `vgabuf` ring buffer.
19    ///      No lock is acquired here : safe from any context (IRQ, panic).
20    ///   3. The `status_line_task` drains `vgabuf` to the framebuffer
21    ///      asynchronously via `vgabuf_flush_to_framebuffer()`.
22    ///
23    /// This decouples the hot logging path from framebuffer rendering,
24    /// making deadlocks structurally impossible.
25    fn log(&self, record: &Record) {
26        if self.enabled(record.metadata()) {
27            let (level_str, msg_color) = match record.level() {
28                Level::Error => ("\x1b[31mERROR\x1b[0m", "\x1b[31m"),
29                Level::Warn => ("\x1b[33mWARN\x1b[0m", "\x1b[33m"),
30                Level::Info => ("\x1b[32mINFO\x1b[0m", "\x1b[37m"),
31                Level::Debug => ("\x1b[90mDEBUG\x1b[0m", "\x1b[90m"),
32                Level::Trace => ("\x1b[90mTRACE\x1b[0m", "\x1b[90m"),
33            };
34
35            // 1. Serial : always works, no locks needed.
36            crate::arch::x86_64::serial::_print(format_args!(
37                "[{}] {}{}\x1b[0m\n",
38                level_str,
39                msg_color,
40                record.args()
41            ));
42
43            // 1b. Live VGA debug (early boot): write directly to framebuffer
44            //     via vga_debug_write.  This bypasses VGA_WRITER and works
45            //     before the scheduler / status_line_task are running.
46            //     Controlled by debug_cfg::VGA_DEBUG_LIVE toggle.
47            if crate::debug_cfg::is_vga_debug_live() && crate::arch::x86_64::vga::is_available() {
48                let mut line_buf = [0u8; 256];
49                let mut lpos = 0usize;
50                use core::fmt::Write;
51                struct LineWriter<'a> {
52                    buf: &'a mut [u8],
53                    pos: &'a mut usize,
54                }
55                impl Write for LineWriter<'_> {
56                    fn write_str(&mut self, s: &str) -> core::fmt::Result {
57                        let bytes = s.as_bytes();
58                        let remaining = self.buf.len().saturating_sub(*self.pos);
59                        let n = bytes.len().min(remaining);
60                        self.buf[*self.pos..*self.pos + n].copy_from_slice(&bytes[..n]);
61                        *self.pos += n;
62                        Ok(())
63                    }
64                }
65                let _ = write!(
66                    LineWriter {
67                        buf: &mut line_buf,
68                        pos: &mut lpos
69                    },
70                    "[{}] {}",
71                    record.level(),
72                    record.args(),
73                );
74                if lpos > 0 {
75                    if let Ok(s) = core::str::from_utf8(&line_buf[..lpos]) {
76                        crate::arch::x86_64::vga::vga_debug_writeln(s);
77                    }
78                }
79            }
80
81            // 2. VGA ring buffer : lock-free enqueue.
82            //    Controlled by debug_cfg::VGA_DEBUG_BUFFER toggle.
83            if crate::debug_cfg::is_vga_debug_buffer() && crate::arch::x86_64::vga::is_available() {
84                let mut vbuf = [0u8; crate::arch::x86_64::vgabuf::VGABUF_LINE_LEN];
85                let mut vpos = 0usize;
86                use core::fmt::Write;
87                struct VgaBufWriter<'a> {
88                    buf: &'a mut [u8],
89                    pos: &'a mut usize,
90                }
91                impl Write for VgaBufWriter<'_> {
92                    fn write_str(&mut self, s: &str) -> core::fmt::Result {
93                        let bytes = s.as_bytes();
94                        let remaining = self.buf.len().saturating_sub(*self.pos);
95                        let n = bytes.len().min(remaining);
96                        self.buf[*self.pos..*self.pos + n].copy_from_slice(&bytes[..n]);
97                        *self.pos += n;
98                        Ok(())
99                    }
100                }
101                let _ = write!(
102                    VgaBufWriter {
103                        buf: &mut vbuf,
104                        pos: &mut vpos
105                    },
106                    "[{}] {}\n",
107                    record.level(),
108                    record.args(),
109                );
110                if vpos > 0 {
111                    crate::arch::x86_64::vgabuf::vgabuf_write(&vbuf[..vpos]);
112                }
113            }
114        }
115    }
116
117    /// Performs the flush operation.
118    fn flush(&self) {}
119}
120
121/// Initialize the logger
122pub fn init() {
123    log::set_logger(&LOGGER)
124        .map(|()| log::set_max_level(LevelFilter::Trace))
125        .expect("Failed to set logger");
126}