Files
FerrOS/kernel/src/logger/logger.rs
2025-07-22 19:17:20 +02:00

96 lines
2.6 KiB
Rust

use bootloader_api::info::FrameBufferInfo;
use conquer_once::spin::OnceCell;
use core::fmt::Write;
use spinning_top::Spinlock;
use super::*;
/// The global logger instance used for the `log` crate.
static LOGGER: OnceCell<LockedLogger> = OnceCell::uninit();
/// A logger instance protected by a spinlock.
struct LockedLogger {
framebuffer: Option<Spinlock<framebuffer::FrameBufferWriter>>,
serial: Option<Spinlock<serial::SerialPort>>
}
impl LockedLogger {
/// Create a new instance that logs to the given framebuffer.
pub fn new(
framebuffer: &'static mut [u8],
info: FrameBufferInfo,
) -> Self {
let framebuffer = Some(Spinlock::new(framebuffer::FrameBufferWriter::new(framebuffer, info)));
let serial = Some(Spinlock::new(unsafe { serial::SerialPort::init() }));
LockedLogger {
framebuffer,
serial
}
}
/// Force-unlocks the logger to prevent a deadlock.
///
/// ## Safety
/// This method is not memory safe and should be only used when absolutely necessary.
pub unsafe fn force_unlock(&self) {
if let Some(framebuffer) = &self.framebuffer {
unsafe { framebuffer.force_unlock() };
}
if let Some(serial) = &self.serial {
unsafe { serial.force_unlock() };
}
}
}
impl log::Log for LockedLogger {
fn enabled(&self, _metadata: &log::Metadata) -> bool {
true
}
fn log(&self, record: &log::Record) {
if let Some(framebuffer) = &self.framebuffer {
let mut framebuffer = framebuffer.lock();
writeln!(framebuffer, "{:5}: {}", record.level(), record.args()).unwrap();
}
if let Some(serial) = &self.serial {
let mut serial = serial.lock();
writeln!(serial, "{:5}: {}", record.level(), record.args()).unwrap();
}
}
fn flush(&self) {}
}
/// Initialize a text-based logger using the given pixel-based framebuffer as output.
pub fn init_logger(
framebuffer: &'static mut [u8],
info: FrameBufferInfo,
) {
let logger = LOGGER.get_or_init(move || {
LockedLogger::new(
framebuffer,
info,
)
});
log::set_logger(logger).expect("logger already set");
#[cfg(debug_assertions)]
log::set_max_level(log::LevelFilter::Debug);
#[cfg(not(debug_assertions))]
log::set_max_level(log::LevelFilter::Info);
log::debug!("Framebuffer info: {:?}", info);
}
pub fn force_unlock() {
unsafe {
LOGGER
.get()
.map(|l| l.force_unlock())
};
}