mirror of
https://github.com/edera-dev/sprout.git
synced 2025-12-19 15:20: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]
|
[dependencies.uefi]
|
||||||
workspace = true
|
workspace = true
|
||||||
features = ["alloc", "global_allocator", "logger", "panic_handler"]
|
features = ["alloc", "global_allocator", "panic_handler"]
|
||||||
|
|
||||||
[dependencies.uefi-raw]
|
[dependencies.uefi-raw]
|
||||||
workspace = true
|
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.
|
/// generators: Runtime code that can generate entries with specific values.
|
||||||
pub mod generators;
|
pub mod generators;
|
||||||
|
|
||||||
/// platform: Integration or support code for specific hardware platforms.
|
/// integrations: Code that interacts with other systems.
|
||||||
pub mod platform;
|
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.
|
/// menu: Display a boot menu to select an entry to boot.
|
||||||
pub mod menu;
|
pub mod menu;
|
||||||
|
|
||||||
/// integrations: Code that interacts with other systems.
|
/// options: Parse the options of the Sprout executable.
|
||||||
pub mod integrations;
|
pub mod options;
|
||||||
|
|
||||||
/// phases: Hooks into specific parts of the boot process.
|
/// phases: Hooks into specific parts of the boot process.
|
||||||
pub mod phases;
|
pub mod phases;
|
||||||
|
|
||||||
|
/// platform: Integration or support code for specific hardware platforms.
|
||||||
|
pub mod platform;
|
||||||
|
|
||||||
/// sbat: Secure Boot Attestation section.
|
/// sbat: Secure Boot Attestation section.
|
||||||
pub mod sbat;
|
pub mod sbat;
|
||||||
|
|
||||||
@@ -72,9 +78,6 @@ pub mod secure;
|
|||||||
/// setup: Code that initializes the UEFI environment for Sprout.
|
/// setup: Code that initializes the UEFI environment for Sprout.
|
||||||
pub mod setup;
|
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.
|
/// utils: Utility functions that are used by other parts of Sprout.
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
@@ -385,6 +388,9 @@ fn run() -> Result<()> {
|
|||||||
fn efi_main() -> Status {
|
fn efi_main() -> Status {
|
||||||
// Initialize the basic UEFI environment.
|
// Initialize the basic UEFI environment.
|
||||||
// If initialization fails, we will return ABORTED.
|
// 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() {
|
if let Err(error) = setup::init() {
|
||||||
error!("unable to initialize environment: {}", error);
|
error!("unable to initialize environment: {}", error);
|
||||||
return Status::ABORTED;
|
return Status::ABORTED;
|
||||||
@@ -394,7 +400,7 @@ fn efi_main() -> Status {
|
|||||||
let result = run();
|
let result = run();
|
||||||
if let Err(ref error) = result {
|
if let Err(ref error) = result {
|
||||||
// Print an error trace.
|
// Print an error trace.
|
||||||
error!("sprout encountered an error");
|
error!("sprout encountered an error:");
|
||||||
for (index, stack) in error.chain().enumerate() {
|
for (index, stack) in error.chain().enumerate() {
|
||||||
error!("[{}]: {}", index, stack);
|
error!("[{}]: {}", index, stack);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
|
use crate::logger;
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
/// Initializes the UEFI environment.
|
/// Initializes the UEFI environment.
|
||||||
pub fn init() -> Result<()> {
|
pub fn init() -> Result<()> {
|
||||||
// Initialize the uefi internals.
|
// Initialize the logger for Sprout.
|
||||||
uefi::helpers::init().context("unable to initialize uefi")?;
|
// 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user