//! The logger used to store logs from the kernel. This accepts logs from the `log` crate, and //! delivers them to console devices. use core::fmt::{self, Write}; /// Initializes the logger. This should be called exactly once, early in boot. pub fn init_early() { log::set_logger(&Logger).expect("failed to set logger"); log::set_max_level(log::LevelFilter::Trace); log::warn!("Using a bad unportable logger that only works on qemu-virt"); } struct BadRiscvWrite; impl Write for BadRiscvWrite { fn write_str(&mut self, s: &str) -> fmt::Result { let ptr = 0x10000000 as *mut u8; for b in s.bytes() { unsafe { ptr.write_volatile(b) }; } Ok(()) } } pub struct Logger; impl log::Log for Logger { fn enabled(&self, _metadata: &log::Metadata) -> bool { true } fn log(&self, record: &log::Record) { if !self.enabled(record.metadata()) { return; } let body = |line| { let level = match record.level() { log::Level::Error => "\x1b[1;31mERR\x1b[0m", log::Level::Warn => "\x1b[1;33mWRN\x1b[0m", log::Level::Info => "\x1b[1;36mINF\x1b[0m", log::Level::Debug => "\x1b[1;35mDBG\x1b[0m", log::Level::Trace => "TRC", }; let file = record.file().unwrap_or("???"); let args = record.args(); let result = if args.as_str() == Some("") { // A silly convenience, but don't write the extra space if we're // not going to write anything anyways. writeln!(BadRiscvWrite, "[{level}][{file}:{line}]") } else { writeln!(BadRiscvWrite, "[{level}][{file}:{line}] {args}") }; // UNWRAP: Since the fmt::Write impl for ConsoleInner has a // contract promising that it won't ever return an error, this // should be unreachable. result.unwrap(); }; // Some contortions to avoid running afoul of the lifetime requirements // of format_args... match record.line() { Some(line) => body(format_args!("{line}")), None => body(format_args!("???")), } } fn flush(&self) {} }