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 = OnceCell::uninit(); /// A logger instance protected by a spinlock. struct LockedLogger { framebuffer: Option>, serial: Option> } 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()) }; }