diff --git a/README.md b/README.md index 0a4c390..4e2d385 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Make sure you have the `cargo-xbuild` crate installed and nightly Rust so you ca First, build with: ``` -cargo xbuild --release --target x86_64-unknown-uefi +cargo xbuild --target x86_64-unknown-uefi ``` And to run, `./run.sh` will launch bootproof in QEMU. diff --git a/src/logger.rs b/src/logger.rs new file mode 100644 index 0000000..bd4dfa9 --- /dev/null +++ b/src/logger.rs @@ -0,0 +1,101 @@ +use crate::misc::halt; + +use core::fmt::Write; + +pub enum LoggerBackend { + None, + // It is impossible to get ownership of an Output, + // so instead we must pass in the entire boot system table. + UefiStdio(uefi::prelude::SystemTable), +} + +enum LoggerOutput { + Stdout, + Stderr +} + +pub struct Logger<'a> { + backend: &'a LoggerBackend, + output: LoggerOutput +} + +impl LoggerBackend { + pub fn stdout<'a>(&'a self) -> Logger<'a> { + Logger { + backend: self, + output: LoggerOutput::Stdout + } + } + + pub fn stderr<'a>(&'a self) -> Logger<'a> { + Logger { + backend: self, + output: LoggerOutput::Stderr + } + } +} + +impl Write for Logger<'_> { + fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { + match self.backend { + LoggerBackend::UefiStdio(st) => { + let output = match &self.output { + LoggerOutput::Stdout => st.stdout(), + LoggerOutput::Stderr => st.stderr() + }; + output.write_str(s) + }, + _ => { + // There's pretty much no way to recover from a missing logger. + // What are we supposed to do-- log the error? + Ok(()) + }, + } + } +} + +pub static mut LOGGER_BACKEND: LoggerBackend = LoggerBackend::None; + +#[macro_export] +macro_rules! log { + ($( $arg:expr ),* ) => { + unsafe { + use core::fmt::Write; + core::writeln!(LOGGER_BACKEND.stderr(), $( $arg ),*).unwrap(); + } + } +} + +#[macro_export] +macro_rules! print { + ($( $arg:expr ),* ) => { + unsafe { + use core::fmt::Write; + core::write!(LOGGER_BACKEND.stdout(), $( $arg ),*).unwrap(); + } + } +} + +#[macro_export] +macro_rules! println { + ($( $arg:expr ),* ) => { + unsafe { + use core::fmt::Write; + core::writeln!(LOGGER_BACKEND.stdout(), $( $arg ),*).unwrap(); + } + } +} + +#[macro_export] +macro_rules! panic { + ($( $arg:expr ),* ) => { + log!($( $arg ),*); + halt(); + } +} + +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + log!("{}", info); + halt(); +} diff --git a/src/main.rs b/src/main.rs index 308be4e..1d990de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,53 +3,21 @@ #![feature(abi_efiapi)] #![feature(asm)] -use core::fmt::Write; -use core::panic::PanicInfo; +mod logger; +mod misc; + +use crate::logger::{LoggerBackend, LOGGER_BACKEND}; +use crate::misc::halt; + use core::mem; use core::slice; -use core::writeln; use uefi::prelude::*; use uefi::table::boot::{AllocateType, MemoryDescriptor, MemoryType}; -use x86_64::instructions; - -enum LoggerStdio { - // It is impossible to get ownership of an Output, - // so instead we must pass in the entire boot system table. - Boot(SystemTable), - None -} - -fn halt() -> ! { - instructions::interrupts::disable(); - loop { instructions::hlt(); } -} - -// Required for use by the panic handler. -// This should not be used anywhere else. -static mut LOGGER_STDIO: LoggerStdio = LoggerStdio::None; - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - match unsafe { &LOGGER_STDIO } { - LoggerStdio::Boot(st) => { - writeln!(st.stderr(), "{}", info).unwrap(); - }, - LoggerStdio::None => { - // There's pretty much nothing we can do in this case. - // What are we supposed to do-- panic? - } - } - - halt(); -} fn setup(st: &SystemTable, _handle: Handle) { - let stdout = st.stdout(); + st.stdout().reset(false).expect_success("Failed to reset UEFI stdout."); - stdout.reset(false).expect_success("Failed to reset UEFI stdout."); - writeln!(stdout, "Booting...").unwrap(); - - writeln!(stdout, "Exiting the UEFI boot services.").unwrap(); + println!("Booting..."); } fn main(st: SystemTable, mmap: uefi::table::boot::MemoryMapIter) -> ! { @@ -61,7 +29,7 @@ fn efi_main(handle: Handle, st_boot: SystemTable) -> Status { // Tasks that require the UEFI boot services. unsafe { - LOGGER_STDIO = LoggerStdio::Boot(st_boot.unsafe_clone()); + LOGGER_BACKEND = LoggerBackend::UefiStdio(st_boot.unsafe_clone()); } setup(&st_boot, handle); @@ -86,7 +54,7 @@ fn efi_main(handle: Handle, st_boot: SystemTable) -> Status { // I do not currently have an adequate stdout for post-UEFI, but the UEFI one is now invalid. unsafe { - LOGGER_STDIO = LoggerStdio::None; + LOGGER_BACKEND = LoggerBackend::None; } main(st_runtime, mmap); diff --git a/src/misc.rs b/src/misc.rs new file mode 100644 index 0000000..33783e0 --- /dev/null +++ b/src/misc.rs @@ -0,0 +1,5 @@ +pub fn halt() -> ! { + use x86_64::instructions::{interrupts, hlt}; + interrupts::disable(); + loop { hlt(); } +}