From b0c5b4e480a2a411632e5c88174d11f2a98c75b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Asensio=20Garc=C3=ADa?= Date: Thu, 5 Jan 2023 01:23:12 +0100 Subject: [PATCH 1/6] Separate the main logger functionality from the framebuffer section of the logger --- common/src/framebuffer.rs | 157 ++++++++++++++++++++++++++++++++++++ common/src/lib.rs | 4 +- common/src/logger.rs | 165 ++------------------------------------ 3 files changed, 166 insertions(+), 160 deletions(-) create mode 100644 common/src/framebuffer.rs diff --git a/common/src/framebuffer.rs b/common/src/framebuffer.rs new file mode 100644 index 00000000..1f5949cc --- /dev/null +++ b/common/src/framebuffer.rs @@ -0,0 +1,157 @@ +use bootloader_api::info::{FrameBufferInfo, PixelFormat}; +use core::{ + fmt::self, + ptr, +}; +use font_constants::BACKUP_CHAR; +use noto_sans_mono_bitmap::{ + get_raster, get_raster_width, FontWeight, RasterHeight, RasterizedChar, +}; + +/// Additional vertical space between lines +const LINE_SPACING: usize = 2; +/// Additional horizontal space between characters. +const LETTER_SPACING: usize = 0; + +/// Padding from the border. Prevent that font is too close to border. +const BORDER_PADDING: usize = 1; + +/// Constants for the usage of the [`noto_sans_mono_bitmap`] crate. +mod font_constants { + use super::*; + + /// Height of each char raster. The font size is ~0.84% of this. Thus, this is the line height that + /// enables multiple characters to be side-by-side and appear optically in one line in a natural way. + pub const CHAR_RASTER_HEIGHT: RasterHeight = RasterHeight::Size16; + + /// The width of each single symbol of the mono space font. + pub const CHAR_RASTER_WIDTH: usize = get_raster_width(FontWeight::Regular, CHAR_RASTER_HEIGHT); + + /// Backup character if a desired symbol is not available by the font. + /// The '�' character requires the feature "unicode-specials". + pub const BACKUP_CHAR: char = '�'; + + pub const FONT_WEIGHT: FontWeight = FontWeight::Regular; +} + +/// Returns the raster of the given char or the raster of [`font_constants::BACKUP_CHAR`]. +fn get_char_raster(c: char) -> RasterizedChar { + fn get(c: char) -> Option { + get_raster( + c, + font_constants::FONT_WEIGHT, + font_constants::CHAR_RASTER_HEIGHT, + ) + } + get(c).unwrap_or_else(|| get(BACKUP_CHAR).expect("Should get raster of backup char.")) +} + +/// Allows logging text to a pixel-based framebuffer. +pub struct FrameBufferWriter { + framebuffer: &'static mut [u8], + info: FrameBufferInfo, + x_pos: usize, + y_pos: usize, +} + +impl FrameBufferWriter { + /// Creates a new logger that uses the given framebuffer. + pub fn new(framebuffer: &'static mut [u8], info: FrameBufferInfo) -> Self { + let mut logger = Self { + framebuffer, + info, + x_pos: 0, + y_pos: 0, + }; + logger.clear(); + logger + } + + fn newline(&mut self) { + self.y_pos += font_constants::CHAR_RASTER_HEIGHT.val() + LINE_SPACING; + self.carriage_return() + } + + fn carriage_return(&mut self) { + self.x_pos = BORDER_PADDING; + } + + /// Erases all text on the screen. Resets `self.x_pos` and `self.y_pos`. + pub fn clear(&mut self) { + self.x_pos = BORDER_PADDING; + self.y_pos = BORDER_PADDING; + self.framebuffer.fill(0); + } + + fn width(&self) -> usize { + self.info.width + } + + fn height(&self) -> usize { + self.info.height + } + + /// Writes a single char to the framebuffer. Takes care of special control characters, such as + /// newlines and carriage returns. + fn write_char(&mut self, c: char) { + match c { + '\n' => self.newline(), + '\r' => self.carriage_return(), + c => { + let new_xpos = self.x_pos + font_constants::CHAR_RASTER_WIDTH; + if new_xpos >= self.width() { + self.newline(); + } + let new_ypos = + self.y_pos + font_constants::CHAR_RASTER_HEIGHT.val() + BORDER_PADDING; + if new_ypos >= self.height() { + self.clear(); + } + self.write_rendered_char(get_char_raster(c)); + } + } + } + + /// Prints a rendered char into the framebuffer. + /// Updates `self.x_pos`. + fn write_rendered_char(&mut self, rendered_char: RasterizedChar) { + for (y, row) in rendered_char.raster().iter().enumerate() { + for (x, byte) in row.iter().enumerate() { + self.write_pixel(self.x_pos + x, self.y_pos + y, *byte); + } + } + self.x_pos += rendered_char.width() + LETTER_SPACING; + } + + fn write_pixel(&mut self, x: usize, y: usize, intensity: u8) { + let pixel_offset = y * self.info.stride + x; + let color = match self.info.pixel_format { + PixelFormat::Rgb => [intensity, intensity, intensity / 2, 0], + PixelFormat::Bgr => [intensity / 2, intensity, intensity, 0], + PixelFormat::U8 => [if intensity > 200 { 0xf } else { 0 }, 0, 0, 0], + other => { + // set a supported (but invalid) pixel format before panicking to avoid a double + // panic; it might not be readable though + self.info.pixel_format = PixelFormat::Rgb; + panic!("pixel format {:?} not supported in logger", other) + } + }; + let bytes_per_pixel = self.info.bytes_per_pixel; + let byte_offset = pixel_offset * bytes_per_pixel; + self.framebuffer[byte_offset..(byte_offset + bytes_per_pixel)] + .copy_from_slice(&color[..bytes_per_pixel]); + let _ = unsafe { ptr::read_volatile(&self.framebuffer[byte_offset]) }; + } +} + +unsafe impl Send for FrameBufferWriter {} +unsafe impl Sync for FrameBufferWriter {} + +impl fmt::Write for FrameBufferWriter { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + self.write_char(c); + } + Ok(()) + } +} diff --git a/common/src/lib.rs b/common/src/lib.rs index fc1c7d59..064efccd 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -29,7 +29,9 @@ pub mod legacy_memory_region; pub mod level_4_entries; /// Implements a loader for the kernel ELF binary. pub mod load_kernel; -/// Provides a logger type that logs output as text to pixel-based framebuffers. +/// Provides a type that logs output as text to pixel-based framebuffers. +pub mod framebuffer; +/// Provides a logger that logs output as text in various formats. pub mod logger; const PAGE_SIZE: u64 = 4096; diff --git a/common/src/logger.rs b/common/src/logger.rs index 7aea0bf4..a7513c71 100644 --- a/common/src/logger.rs +++ b/common/src/logger.rs @@ -1,63 +1,19 @@ -use bootloader_api::info::{FrameBufferInfo, PixelFormat}; +use crate::framebuffer::FrameBufferWriter; +use bootloader_api::info::FrameBufferInfo; +use core::fmt::Write; use conquer_once::spin::OnceCell; -use core::{ - fmt::{self, Write}, - ptr, -}; -use font_constants::BACKUP_CHAR; -use noto_sans_mono_bitmap::{ - get_raster, get_raster_width, FontWeight, RasterHeight, RasterizedChar, -}; use spinning_top::Spinlock; /// The global logger instance used for the `log` crate. pub static LOGGER: OnceCell = OnceCell::uninit(); -/// A [`Logger`] instance protected by a spinlock. -pub struct LockedLogger(Spinlock); - -/// Additional vertical space between lines -const LINE_SPACING: usize = 2; -/// Additional horizontal space between characters. -const LETTER_SPACING: usize = 0; - -/// Padding from the border. Prevent that font is too close to border. -const BORDER_PADDING: usize = 1; - -/// Constants for the usage of the [`noto_sans_mono_bitmap`] crate. -mod font_constants { - use super::*; - - /// Height of each char raster. The font size is ~0.84% of this. Thus, this is the line height that - /// enables multiple characters to be side-by-side and appear optically in one line in a natural way. - pub const CHAR_RASTER_HEIGHT: RasterHeight = RasterHeight::Size16; - - /// The width of each single symbol of the mono space font. - pub const CHAR_RASTER_WIDTH: usize = get_raster_width(FontWeight::Regular, CHAR_RASTER_HEIGHT); - - /// Backup character if a desired symbol is not available by the font. - /// The '�' character requires the feature "unicode-specials". - pub const BACKUP_CHAR: char = '�'; - - pub const FONT_WEIGHT: FontWeight = FontWeight::Regular; -} - -/// Returns the raster of the given char or the raster of [`font_constants::BACKUP_CHAR`]. -fn get_char_raster(c: char) -> RasterizedChar { - fn get(c: char) -> Option { - get_raster( - c, - font_constants::FONT_WEIGHT, - font_constants::CHAR_RASTER_HEIGHT, - ) - } - get(c).unwrap_or_else(|| get(BACKUP_CHAR).expect("Should get raster of backup char.")) -} +/// A [`FrameBufferWriter`] instance protected by a spinlock. +pub struct LockedLogger(Spinlock); impl LockedLogger { /// Create a new instance that logs to the given framebuffer. pub fn new(framebuffer: &'static mut [u8], info: FrameBufferInfo) -> Self { - LockedLogger(Spinlock::new(Logger::new(framebuffer, info))) + LockedLogger(Spinlock::new(FrameBufferWriter::new(framebuffer, info))) } /// Force-unlocks the logger to prevent a deadlock. @@ -82,112 +38,3 @@ impl log::Log for LockedLogger { fn flush(&self) {} } -/// Allows logging text to a pixel-based framebuffer. -pub struct Logger { - framebuffer: &'static mut [u8], - info: FrameBufferInfo, - x_pos: usize, - y_pos: usize, -} - -impl Logger { - /// Creates a new logger that uses the given framebuffer. - pub fn new(framebuffer: &'static mut [u8], info: FrameBufferInfo) -> Self { - let mut logger = Self { - framebuffer, - info, - x_pos: 0, - y_pos: 0, - }; - logger.clear(); - logger - } - - fn newline(&mut self) { - self.y_pos += font_constants::CHAR_RASTER_HEIGHT.val() + LINE_SPACING; - self.carriage_return() - } - - fn carriage_return(&mut self) { - self.x_pos = BORDER_PADDING; - } - - /// Erases all text on the screen. Resets `self.x_pos` and `self.y_pos`. - pub fn clear(&mut self) { - self.x_pos = BORDER_PADDING; - self.y_pos = BORDER_PADDING; - self.framebuffer.fill(0); - } - - fn width(&self) -> usize { - self.info.width - } - - fn height(&self) -> usize { - self.info.height - } - - /// Writes a single char to the framebuffer. Takes care of special control characters, such as - /// newlines and carriage returns. - fn write_char(&mut self, c: char) { - match c { - '\n' => self.newline(), - '\r' => self.carriage_return(), - c => { - let new_xpos = self.x_pos + font_constants::CHAR_RASTER_WIDTH; - if new_xpos >= self.width() { - self.newline(); - } - let new_ypos = - self.y_pos + font_constants::CHAR_RASTER_HEIGHT.val() + BORDER_PADDING; - if new_ypos >= self.height() { - self.clear(); - } - self.write_rendered_char(get_char_raster(c)); - } - } - } - - /// Prints a rendered char into the framebuffer. - /// Updates `self.x_pos`. - fn write_rendered_char(&mut self, rendered_char: RasterizedChar) { - for (y, row) in rendered_char.raster().iter().enumerate() { - for (x, byte) in row.iter().enumerate() { - self.write_pixel(self.x_pos + x, self.y_pos + y, *byte); - } - } - self.x_pos += rendered_char.width() + LETTER_SPACING; - } - - fn write_pixel(&mut self, x: usize, y: usize, intensity: u8) { - let pixel_offset = y * self.info.stride + x; - let color = match self.info.pixel_format { - PixelFormat::Rgb => [intensity, intensity, intensity / 2, 0], - PixelFormat::Bgr => [intensity / 2, intensity, intensity, 0], - PixelFormat::U8 => [if intensity > 200 { 0xf } else { 0 }, 0, 0, 0], - other => { - // set a supported (but invalid) pixel format before panicking to avoid a double - // panic; it might not be readable though - self.info.pixel_format = PixelFormat::Rgb; - panic!("pixel format {:?} not supported in logger", other) - } - }; - let bytes_per_pixel = self.info.bytes_per_pixel; - let byte_offset = pixel_offset * bytes_per_pixel; - self.framebuffer[byte_offset..(byte_offset + bytes_per_pixel)] - .copy_from_slice(&color[..bytes_per_pixel]); - let _ = unsafe { ptr::read_volatile(&self.framebuffer[byte_offset]) }; - } -} - -unsafe impl Send for Logger {} -unsafe impl Sync for Logger {} - -impl fmt::Write for Logger { - fn write_str(&mut self, s: &str) -> fmt::Result { - for c in s.chars() { - self.write_char(c); - } - Ok(()) - } -} From c40c19f99f797337f36b80882a4a4e461f899dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Asensio=20Garc=C3=ADa?= Date: Thu, 5 Jan 2023 01:59:01 +0100 Subject: [PATCH 2/6] Check formatting --- common/src/framebuffer.rs | 5 +---- common/src/lib.rs | 4 ++-- common/src/logger.rs | 3 +-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/common/src/framebuffer.rs b/common/src/framebuffer.rs index 1f5949cc..bfcb52a2 100644 --- a/common/src/framebuffer.rs +++ b/common/src/framebuffer.rs @@ -1,8 +1,5 @@ use bootloader_api::info::{FrameBufferInfo, PixelFormat}; -use core::{ - fmt::self, - ptr, -}; +use core::{fmt, ptr}; use font_constants::BACKUP_CHAR; use noto_sans_mono_bitmap::{ get_raster, get_raster_width, FontWeight, RasterHeight, RasterizedChar, diff --git a/common/src/lib.rs b/common/src/lib.rs index 064efccd..0525e20b 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -22,6 +22,8 @@ use xmas_elf::ElfFile; /// Provides a function to gather entropy and build a RNG. mod entropy; +/// Provides a type that logs output as text to pixel-based framebuffers. +pub mod framebuffer; mod gdt; /// Provides a frame allocator based on a BIOS or UEFI memory map. pub mod legacy_memory_region; @@ -29,8 +31,6 @@ pub mod legacy_memory_region; pub mod level_4_entries; /// Implements a loader for the kernel ELF binary. pub mod load_kernel; -/// Provides a type that logs output as text to pixel-based framebuffers. -pub mod framebuffer; /// Provides a logger that logs output as text in various formats. pub mod logger; diff --git a/common/src/logger.rs b/common/src/logger.rs index a7513c71..b6da2a69 100644 --- a/common/src/logger.rs +++ b/common/src/logger.rs @@ -1,7 +1,7 @@ use crate::framebuffer::FrameBufferWriter; use bootloader_api::info::FrameBufferInfo; -use core::fmt::Write; use conquer_once::spin::OnceCell; +use core::fmt::Write; use spinning_top::Spinlock; /// The global logger instance used for the `log` crate. @@ -37,4 +37,3 @@ impl log::Log for LockedLogger { fn flush(&self) {} } - From ab93f1ba0ce7c913c52fcfbcc04f83618c53485d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Asensio=20Garc=C3=ADa?= Date: Thu, 5 Jan 2023 13:54:23 +0100 Subject: [PATCH 3/6] Support Serial Being logger --- Cargo.lock | 6 ++++-- common/Cargo.toml | 1 + common/src/lib.rs | 1 + common/src/logger.rs | 16 ++++++++++++---- common/src/serial.rs | 20 ++++++++++++++++++++ 5 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 common/src/serial.rs diff --git a/Cargo.lock b/Cargo.lock index 645da8cd..a9a70725 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,6 +120,7 @@ dependencies = [ "rand_hc", "raw-cpuid", "spinning_top", + "uart_16550", "usize_conversions", "x86_64", "xmas-elf", @@ -628,11 +629,12 @@ dependencies = [ [[package]] name = "uart_16550" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3616395dbb38a9c39a5865b5691c21d9f8369ba876355cfef8ce39d0d4cf3281" +checksum = "b074eb9300ad949edd74c529c0e8d451625af71bb948e6b65fe69f72dc1363d9" dependencies = [ "bitflags", + "rustversion", "x86_64", ] diff --git a/common/Cargo.toml b/common/Cargo.toml index 0d84c5f5..c830200a 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -19,6 +19,7 @@ xmas-elf = "0.8.0" raw-cpuid = "10.2.0" rand = { version = "0.8.4", default-features = false } rand_hc = "0.3.1" +uart_16550 = "0.2.18" [dependencies.noto-sans-mono-bitmap] version = "0.2.0" diff --git a/common/src/lib.rs b/common/src/lib.rs index 0525e20b..8051ee7c 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -33,6 +33,7 @@ pub mod level_4_entries; pub mod load_kernel; /// Provides a logger that logs output as text in various formats. pub mod logger; +pub mod serial; const PAGE_SIZE: u64 = 4096; diff --git a/common/src/logger.rs b/common/src/logger.rs index b6da2a69..7bb719e4 100644 --- a/common/src/logger.rs +++ b/common/src/logger.rs @@ -1,4 +1,4 @@ -use crate::framebuffer::FrameBufferWriter; +use crate::{framebuffer::FrameBufferWriter, serial::SerialPort}; use bootloader_api::info::FrameBufferInfo; use conquer_once::spin::OnceCell; use core::fmt::Write; @@ -8,12 +8,15 @@ use spinning_top::Spinlock; pub static LOGGER: OnceCell = OnceCell::uninit(); /// A [`FrameBufferWriter`] instance protected by a spinlock. -pub struct LockedLogger(Spinlock); +pub struct LockedLogger(Spinlock, Spinlock); impl LockedLogger { /// Create a new instance that logs to the given framebuffer. pub fn new(framebuffer: &'static mut [u8], info: FrameBufferInfo) -> Self { - LockedLogger(Spinlock::new(FrameBufferWriter::new(framebuffer, info))) + LockedLogger( + Spinlock::new(FrameBufferWriter::new(framebuffer, info)), + Spinlock::new(SerialPort::new()), + ) } /// Force-unlocks the logger to prevent a deadlock. @@ -21,7 +24,10 @@ impl LockedLogger { /// ## Safety /// This method is not memory safe and should be only used when absolutely necessary. pub unsafe fn force_unlock(&self) { - unsafe { self.0.force_unlock() }; + unsafe { + self.0.force_unlock(); + self.1.force_unlock(); + }; } } @@ -32,7 +38,9 @@ impl log::Log for LockedLogger { fn log(&self, record: &log::Record) { let mut logger = self.0.lock(); + let mut serial = self.1.lock(); writeln!(logger, "{:5}: {}", record.level(), record.args()).unwrap(); + writeln!(serial, "{:5}: {}", record.level(), record.args()).unwrap(); } fn flush(&self) {} diff --git a/common/src/serial.rs b/common/src/serial.rs new file mode 100644 index 00000000..1da346f2 --- /dev/null +++ b/common/src/serial.rs @@ -0,0 +1,20 @@ +use core::fmt; + +pub struct SerialPort { + port: uart_16550::SerialPort, +} + +impl SerialPort { + pub fn new() -> Self { + let mut port = unsafe { uart_16550::SerialPort::new(0x3F8) }; + port.init(); + Self { port } + } +} + +impl fmt::Write for SerialPort { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.port.write_str(s).unwrap(); + Ok(()) + } +} From 34e50cabfcaa2bf7a788736632081392e21f0bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Asensio=20Garc=C3=ADa?= Date: Thu, 5 Jan 2023 16:07:54 +0100 Subject: [PATCH 4/6] Add configuration options --- api/build.rs | 2 ++ api/src/config.rs | 62 ++++++++++++++++++++++++++++++++++++++-- bios/stage-4/src/main.rs | 24 +++++++++++++--- common/src/lib.rs | 20 +++++++++++-- common/src/logger.rs | 30 ++++++++++++++----- uefi/src/main.rs | 8 +++++- 6 files changed, 129 insertions(+), 17 deletions(-) diff --git a/api/build.rs b/api/build.rs index bacb5bf9..f2eb9548 100644 --- a/api/build.rs +++ b/api/build.rs @@ -23,6 +23,8 @@ fn main() { (97, 9), (106, 9), (115, 1), + (116, 1), + (117, 1), ]; let mut code = String::new(); diff --git a/api/src/config.rs b/api/src/config.rs index a7660746..6c6e99fb 100644 --- a/api/src/config.rs +++ b/api/src/config.rs @@ -31,6 +31,12 @@ pub struct BootloaderConfig { /// Configuration for changing the level of the filter of the messages that are shown in the /// screen when booting. The default is 'Trace'. pub log_level: LevelFilter, + + /// Configuration for enable or disable the framebuffer logger. + pub frame_buffer_logger_status: LoggerStatus, + + /// Configuration for enable or disable the serial being logger. + pub serial_logger_status: LoggerStatus, } impl BootloaderConfig { @@ -39,7 +45,7 @@ impl BootloaderConfig { 0x3D, ]; #[doc(hidden)] - pub const SERIALIZED_LEN: usize = 116; + pub const SERIALIZED_LEN: usize = 118; /// Creates a new default configuration with the following values: /// @@ -53,6 +59,8 @@ impl BootloaderConfig { mappings: Mappings::new_default(), frame_buffer: FrameBuffer::new_default(), log_level: LevelFilter::Trace, + frame_buffer_logger_status: LoggerStatus::Enable, + serial_logger_status: LoggerStatus::Enable, } } @@ -67,6 +75,8 @@ impl BootloaderConfig { kernel_stack_size, frame_buffer, log_level, + frame_buffer_logger_status, + serial_logger_status, } = self; let ApiVersion { version_major, @@ -147,7 +157,15 @@ impl BootloaderConfig { }, ); - concat_115_1(buf, (*log_level as u8).to_le_bytes()) + let log_level = concat_115_1(buf, (*log_level as u8).to_le_bytes()); + + let frame_buffer_logger_status = + concat_116_1(log_level, (*frame_buffer_logger_status as u8).to_le_bytes()); + + concat_117_1( + frame_buffer_logger_status, + (*serial_logger_status as u8).to_le_bytes(), + ) } /// Tries to deserialize a config byte array that was created using [`Self::serialize`]. @@ -267,6 +285,21 @@ impl BootloaderConfig { Option::None => return Err("log_level invalid"), }; + let (&frame_buffer_logger_status, s) = split_array_ref(s); + let frame_buffer_logger_status = + LoggerStatus::from_u8(u8::from_le_bytes(frame_buffer_logger_status)); + let frame_buffer_logger_status = match frame_buffer_logger_status { + Option::Some(status) => status, + Option::None => return Err("frame_buffer_logger_status invalid"), + }; + + let (&serial_logger_status, s) = split_array_ref(s); + let serial_logger_status = LoggerStatus::from_u8(u8::from_le_bytes(serial_logger_status)); + let serial_logger_status = match serial_logger_status { + Option::Some(status) => status, + Option::None => return Err("serial_logger_status invalid"), + }; + if !s.is_empty() { return Err("unexpected rest"); } @@ -277,6 +310,8 @@ impl BootloaderConfig { mappings, frame_buffer, log_level, + frame_buffer_logger_status, + serial_logger_status, }) } @@ -288,6 +323,8 @@ impl BootloaderConfig { kernel_stack_size: rand::random(), frame_buffer: FrameBuffer::random(), log_level: LevelFilter::Trace, + frame_buffer_logger_status: LoggerStatus::Enable, + serial_logger_status: LoggerStatus::Enable, } } } @@ -587,6 +624,27 @@ impl LevelFilter { } } +/// An enum for enabling or disabling the different methods for logging. +#[repr(u8)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum LoggerStatus { + /// This method of logging is disabled + Disable, + /// This method of logging is enabled + Enable, +} + +impl LoggerStatus { + /// Converts an u8 into a Option + pub fn from_u8(value: u8) -> Option { + match value { + 0 => Some(Self::Disable), + 1 => Some(Self::Enable), + _ => None, + } + } +} + /// Taken from https://github.com/rust-lang/rust/blob/e100ec5bc7cd768ec17d75448b29c9ab4a39272b/library/core/src/slice/mod.rs#L1673-L1677 /// /// TODO replace with `split_array` feature in stdlib as soon as it's stabilized, diff --git a/bios/stage-4/src/main.rs b/bios/stage-4/src/main.rs index d6a50b9c..d5685e9d 100644 --- a/bios/stage-4/src/main.rs +++ b/bios/stage-4/src/main.rs @@ -3,7 +3,7 @@ use crate::memory_descriptor::MemoryRegion; use bootloader_api::{ - config::LevelFilter, + config::{LevelFilter, LoggerStatus}, info::{FrameBufferInfo, PixelFormat}, }; use bootloader_x86_64_bios_common::{BiosFramebufferInfo, BiosInfo, E820MemoryRegion}; @@ -109,7 +109,12 @@ pub extern "C" fn _start(info: &mut BiosInfo) -> ! { }; let kernel = Kernel::parse(kernel_slice); - let framebuffer_info = init_logger(info.framebuffer, kernel.config.log_level); + let framebuffer_info = init_logger( + info.framebuffer, + kernel.config.log_level, + kernel.config.frame_buffer_logger_status, + kernel.config.serial_logger_status, + ); log::info!("4th Stage"); log::info!("{info:x?}"); @@ -126,7 +131,12 @@ pub extern "C" fn _start(info: &mut BiosInfo) -> ! { load_and_switch_to_kernel(kernel, frame_allocator, page_tables, system_info); } -fn init_logger(info: BiosFramebufferInfo, log_level: LevelFilter) -> FrameBufferInfo { +fn init_logger( + info: BiosFramebufferInfo, + log_level: LevelFilter, + frame_buffer_logger_status: LoggerStatus, + serial_logger_status: LoggerStatus, +) -> FrameBufferInfo { let framebuffer_info = FrameBufferInfo { byte_len: info.region.len.try_into().unwrap(), width: info.width.into(), @@ -155,7 +165,13 @@ fn init_logger(info: BiosFramebufferInfo, log_level: LevelFilter) -> FrameBuffer ) }; - bootloader_x86_64_common::init_logger(framebuffer, framebuffer_info, log_level); + bootloader_x86_64_common::init_logger( + framebuffer, + framebuffer_info, + log_level, + frame_buffer_logger_status, + serial_logger_status, + ); framebuffer_info } diff --git a/common/src/lib.rs b/common/src/lib.rs index 8051ee7c..b431215e 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -4,7 +4,7 @@ use crate::legacy_memory_region::{LegacyFrameAllocator, LegacyMemoryRegion}; use bootloader_api::{ - config::{LevelFilter, Mapping}, + config::{LevelFilter, LoggerStatus, Mapping}, info::{FrameBuffer, FrameBufferInfo, MemoryRegion, TlsTemplate}, BootInfo, BootloaderConfig, }; @@ -33,13 +33,27 @@ pub mod level_4_entries; pub mod load_kernel; /// Provides a logger that logs output as text in various formats. pub mod logger; +/// Provides a type that logs output as text to a Serial Being port. pub mod serial; const PAGE_SIZE: u64 = 4096; /// Initialize a text-based logger using the given pixel-based framebuffer as output. -pub fn init_logger(framebuffer: &'static mut [u8], info: FrameBufferInfo, log_level: LevelFilter) { - let logger = logger::LOGGER.get_or_init(move || logger::LockedLogger::new(framebuffer, info)); +pub fn init_logger( + framebuffer: &'static mut [u8], + info: FrameBufferInfo, + log_level: LevelFilter, + frame_buffer_logger_status: LoggerStatus, + serial_logger_status: LoggerStatus, +) { + let logger = logger::LOGGER.get_or_init(move || { + logger::LockedLogger::new( + framebuffer, + info, + frame_buffer_logger_status, + serial_logger_status, + ) + }); log::set_logger(logger).expect("logger already set"); log::set_max_level(convert_level(log_level)); log::info!("Framebuffer info: {:?}", info); diff --git a/common/src/logger.rs b/common/src/logger.rs index 7bb719e4..708585bb 100644 --- a/common/src/logger.rs +++ b/common/src/logger.rs @@ -1,5 +1,5 @@ use crate::{framebuffer::FrameBufferWriter, serial::SerialPort}; -use bootloader_api::info::FrameBufferInfo; +use bootloader_api::{config::LoggerStatus, info::FrameBufferInfo}; use conquer_once::spin::OnceCell; use core::fmt::Write; use spinning_top::Spinlock; @@ -8,14 +8,26 @@ use spinning_top::Spinlock; pub static LOGGER: OnceCell = OnceCell::uninit(); /// A [`FrameBufferWriter`] instance protected by a spinlock. -pub struct LockedLogger(Spinlock, Spinlock); +pub struct LockedLogger( + Spinlock, + Spinlock, + LoggerStatus, + LoggerStatus, +); impl LockedLogger { /// Create a new instance that logs to the given framebuffer. - pub fn new(framebuffer: &'static mut [u8], info: FrameBufferInfo) -> Self { + pub fn new( + framebuffer: &'static mut [u8], + info: FrameBufferInfo, + frame_buffer_logger_status: LoggerStatus, + serial_logger_status: LoggerStatus, + ) -> Self { LockedLogger( Spinlock::new(FrameBufferWriter::new(framebuffer, info)), Spinlock::new(SerialPort::new()), + frame_buffer_logger_status, + serial_logger_status, ) } @@ -37,10 +49,14 @@ impl log::Log for LockedLogger { } fn log(&self, record: &log::Record) { - let mut logger = self.0.lock(); - let mut serial = self.1.lock(); - writeln!(logger, "{:5}: {}", record.level(), record.args()).unwrap(); - writeln!(serial, "{:5}: {}", record.level(), record.args()).unwrap(); + if self.2 == LoggerStatus::Enable { + let mut logger = self.0.lock(); + writeln!(logger, "{:5}: {}", record.level(), record.args()).unwrap(); + } + if self.3 == LoggerStatus::Enable { + let mut serial = self.1.lock(); + writeln!(serial, "{:5}: {}", record.level(), record.args()).unwrap(); + } } fn flush(&self) {} diff --git a/uefi/src/main.rs b/uefi/src/main.rs index d96d60c1..0f7cd469 100644 --- a/uefi/src/main.rs +++ b/uefi/src/main.rs @@ -420,7 +420,13 @@ fn init_logger(st: &SystemTable, config: BootloaderConfig) -> Option Date: Thu, 5 Jan 2023 18:52:43 +0100 Subject: [PATCH 5/6] Apply suggestions --- api/src/config.rs | 8 +++++-- common/src/logger.rs | 52 +++++++++++++++++++++++++------------------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/api/src/config.rs b/api/src/config.rs index 6c6e99fb..9c789803 100644 --- a/api/src/config.rs +++ b/api/src/config.rs @@ -32,10 +32,14 @@ pub struct BootloaderConfig { /// screen when booting. The default is 'Trace'. pub log_level: LevelFilter, - /// Configuration for enable or disable the framebuffer logger. + /// Wheter the bootloader should print log messages to the framebuffer when booting. + /// + /// Enabled by default. pub frame_buffer_logger_status: LoggerStatus, - /// Configuration for enable or disable the serial being logger. + /// Wheter the bootloader should print log messages to the serial port when booting. + /// + /// Enabled by default. pub serial_logger_status: LoggerStatus, } diff --git a/common/src/logger.rs b/common/src/logger.rs index 708585bb..741c64c5 100644 --- a/common/src/logger.rs +++ b/common/src/logger.rs @@ -7,13 +7,11 @@ use spinning_top::Spinlock; /// The global logger instance used for the `log` crate. pub static LOGGER: OnceCell = OnceCell::uninit(); -/// A [`FrameBufferWriter`] instance protected by a spinlock. -pub struct LockedLogger( - Spinlock, - Spinlock, - LoggerStatus, - LoggerStatus, -); +/// A logger instance protected by a spinlock. +pub struct LockedLogger { + framebuffer: Option>, + serial: Option>, +} impl LockedLogger { /// Create a new instance that logs to the given framebuffer. @@ -23,12 +21,20 @@ impl LockedLogger { frame_buffer_logger_status: LoggerStatus, serial_logger_status: LoggerStatus, ) -> Self { - LockedLogger( - Spinlock::new(FrameBufferWriter::new(framebuffer, info)), - Spinlock::new(SerialPort::new()), - frame_buffer_logger_status, - serial_logger_status, - ) + let framebuffer = match frame_buffer_logger_status { + LoggerStatus::Enable => Some(Spinlock::new(FrameBufferWriter::new(framebuffer, info))), + LoggerStatus::Disable => None, + }; + + let serial = match serial_logger_status { + LoggerStatus::Enable => Some(Spinlock::new(SerialPort::new())), + LoggerStatus::Disable => None, + }; + + LockedLogger { + framebuffer, + serial, + } } /// Force-unlocks the logger to prevent a deadlock. @@ -36,10 +42,12 @@ impl LockedLogger { /// ## Safety /// This method is not memory safe and should be only used when absolutely necessary. pub unsafe fn force_unlock(&self) { - unsafe { - self.0.force_unlock(); - self.1.force_unlock(); - }; + if let Some(framebuffer) = &self.framebuffer { + unsafe { framebuffer.force_unlock() }; + } + if let Some(serial) = &self.serial { + unsafe { serial.force_unlock() }; + } } } @@ -49,12 +57,12 @@ impl log::Log for LockedLogger { } fn log(&self, record: &log::Record) { - if self.2 == LoggerStatus::Enable { - let mut logger = self.0.lock(); - writeln!(logger, "{:5}: {}", record.level(), record.args()).unwrap(); + if let Some(framebuffer) = &self.framebuffer { + let mut framebuffer = framebuffer.lock(); + writeln!(framebuffer, "{:5}: {}", record.level(), record.args()).unwrap(); } - if self.3 == LoggerStatus::Enable { - let mut serial = self.1.lock(); + if let Some(serial) = &self.serial { + let mut serial = serial.lock(); writeln!(serial, "{:5}: {}", record.level(), record.args()).unwrap(); } } From 74759606f03829542aaf1f61c4d7be028e4f6ac1 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 9 Jan 2023 16:38:21 +0100 Subject: [PATCH 6/6] Fix typo --- api/src/config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/config.rs b/api/src/config.rs index 9c789803..a94ebdaf 100644 --- a/api/src/config.rs +++ b/api/src/config.rs @@ -32,12 +32,12 @@ pub struct BootloaderConfig { /// screen when booting. The default is 'Trace'. pub log_level: LevelFilter, - /// Wheter the bootloader should print log messages to the framebuffer when booting. + /// Whether the bootloader should print log messages to the framebuffer when booting. /// /// Enabled by default. pub frame_buffer_logger_status: LoggerStatus, - /// Wheter the bootloader should print log messages to the serial port when booting. + /// Whether the bootloader should print log messages to the serial port when booting. /// /// Enabled by default. pub serial_logger_status: LoggerStatus,