mirror of
https://github.com/edera-dev/sprout.git
synced 2025-12-19 01:10:17 +00:00
feat(sprout): implement custom logger which shortens output
This commit is contained in:
@@ -20,7 +20,7 @@ log.workspace = true
|
||||
|
||||
[dependencies.uefi]
|
||||
workspace = true
|
||||
features = ["alloc", "global_allocator", "logger", "panic_handler"]
|
||||
features = ["alloc", "global_allocator", "panic_handler"]
|
||||
|
||||
[dependencies.uefi-raw]
|
||||
workspace = true
|
||||
|
||||
94
crates/sprout/src/logger.rs
Normal file
94
crates/sprout/src/logger.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
//! Based on: https://github.com/rust-osdev/uefi-rs/blob/main/uefi/src/helpers/logger.rs
|
||||
|
||||
use alloc::format;
|
||||
use core::fmt::Write;
|
||||
use core::ptr;
|
||||
use core::sync::atomic::{AtomicPtr, Ordering};
|
||||
use log::{Log, Record};
|
||||
use uefi::proto::console::text::Output;
|
||||
|
||||
/// The global logger object.
|
||||
static LOGGER: Logger = Logger::new();
|
||||
|
||||
/// Logging mechanism for Sprout.
|
||||
/// Must be initialized to be used, as we use atomic pointers to store the output to write to.
|
||||
pub struct Logger {
|
||||
writer: AtomicPtr<Output>,
|
||||
}
|
||||
|
||||
impl Default for Logger {
|
||||
/// Creates a default logger, which is uninitialized with an output.
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Logger {
|
||||
/// Create a new logger with an output not specified.
|
||||
/// This will cause the logger to not print anything until it is configured.
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
writer: AtomicPtr::new(ptr::null_mut()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves the pointer to the output.
|
||||
/// SAFETY: This pointer might be null, it should be checked before use.
|
||||
#[must_use]
|
||||
fn output(&self) -> *mut Output {
|
||||
self.writer.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
/// Sets the output to write to.
|
||||
///
|
||||
/// # Safety
|
||||
/// This function is unsafe because the output is technically leaked and unmanaged.
|
||||
pub unsafe fn set_output(&self, output: *mut Output) {
|
||||
self.writer.store(output, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
impl Log for Logger {
|
||||
/// Enable the logger always.
|
||||
fn enabled(&self, _metadata: &log::Metadata<'_>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Log the specified `record` to the output if one is set.
|
||||
fn log(&self, record: &Record) {
|
||||
// Acquire the output. If one is not set, we do nothing.
|
||||
let Some(output) = (unsafe { self.output().as_mut() }) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Format the log message.
|
||||
let message = format!("{}", record.args());
|
||||
|
||||
// Iterate over every line, formatting the message and writing it to the output.
|
||||
for line in message.lines() {
|
||||
// The format writes the log level in front of every line of text.
|
||||
let _ = writeln!(output, "[{:>5}] {}", record.level(), line);
|
||||
}
|
||||
}
|
||||
|
||||
/// This log is not buffered, so flushing isn't required.
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
/// Initialize the logging environment, calling panic if something goes wrong.
|
||||
pub fn init() {
|
||||
// Retrieve the stdout handle and set it as the output for the global logger.
|
||||
uefi::system::with_stdout(|stdout| unsafe {
|
||||
// SAFETY: We are using the stdout handle to create a pointer to the output.
|
||||
// The handle is global and is guaranteed to be valid for the lifetime of the program.
|
||||
LOGGER.set_output(stdout);
|
||||
});
|
||||
|
||||
// Set the logger to the global logger.
|
||||
if let Err(error) = log::set_logger(&LOGGER) {
|
||||
panic!("unable to set logger: {}", error);
|
||||
}
|
||||
|
||||
// Set the max level to the level specified by the log features.
|
||||
log::set_max_level(log::STATIC_MAX_LEVEL);
|
||||
}
|
||||
@@ -51,18 +51,24 @@ pub mod extractors;
|
||||
/// generators: Runtime code that can generate entries with specific values.
|
||||
pub mod generators;
|
||||
|
||||
/// platform: Integration or support code for specific hardware platforms.
|
||||
pub mod platform;
|
||||
/// integrations: Code that interacts with other systems.
|
||||
pub mod integrations;
|
||||
|
||||
/// logger: Code for the logging mechanism of Sprout.
|
||||
pub mod logger;
|
||||
|
||||
/// menu: Display a boot menu to select an entry to boot.
|
||||
pub mod menu;
|
||||
|
||||
/// integrations: Code that interacts with other systems.
|
||||
pub mod integrations;
|
||||
/// options: Parse the options of the Sprout executable.
|
||||
pub mod options;
|
||||
|
||||
/// phases: Hooks into specific parts of the boot process.
|
||||
pub mod phases;
|
||||
|
||||
/// platform: Integration or support code for specific hardware platforms.
|
||||
pub mod platform;
|
||||
|
||||
/// sbat: Secure Boot Attestation section.
|
||||
pub mod sbat;
|
||||
|
||||
@@ -72,9 +78,6 @@ pub mod secure;
|
||||
/// setup: Code that initializes the UEFI environment for Sprout.
|
||||
pub mod setup;
|
||||
|
||||
/// options: Parse the options of the Sprout executable.
|
||||
pub mod options;
|
||||
|
||||
/// utils: Utility functions that are used by other parts of Sprout.
|
||||
pub mod utils;
|
||||
|
||||
@@ -385,6 +388,9 @@ fn run() -> Result<()> {
|
||||
fn efi_main() -> Status {
|
||||
// Initialize the basic UEFI environment.
|
||||
// If initialization fails, we will return ABORTED.
|
||||
// NOTE: This function will also initialize the logger.
|
||||
// The logger will panic if it is unable to initialize.
|
||||
// It is guaranteed that if this returns, the logger is initialized.
|
||||
if let Err(error) = setup::init() {
|
||||
error!("unable to initialize environment: {}", error);
|
||||
return Status::ABORTED;
|
||||
@@ -394,7 +400,7 @@ fn efi_main() -> Status {
|
||||
let result = run();
|
||||
if let Err(ref error) = result {
|
||||
// Print an error trace.
|
||||
error!("sprout encountered an error");
|
||||
error!("sprout encountered an error:");
|
||||
for (index, stack) in error.chain().enumerate() {
|
||||
error!("[{}]: {}", index, stack);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
use crate::logger;
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
/// Initializes the UEFI environment.
|
||||
pub fn init() -> Result<()> {
|
||||
// Initialize the uefi internals.
|
||||
uefi::helpers::init().context("unable to initialize uefi")?;
|
||||
// Initialize the logger for Sprout.
|
||||
// NOTE: This cannot use a result type as errors need to be printed
|
||||
// using the logger, which is not initialized until this returns.
|
||||
logger::init();
|
||||
|
||||
// Initialize further UEFI internals.
|
||||
uefi::helpers::init().context("unable to initialize uefi environment")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user