Added stdout/stderr logger that can work at any stage of booting.

master
James T. Martin 2020-07-15 14:06:15 -07:00
parent f65b87ad29
commit b0501daeb9
Signed by: james
GPG Key ID: 4B7F3DA9351E577C
4 changed files with 117 additions and 43 deletions

View File

@ -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.

101
src/logger.rs Normal file
View File

@ -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<uefi::prelude::Boot>),
}
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();
}

View File

@ -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<Boot>),
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<Boot>, _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<uefi::table::Runtime>, mmap: uefi::table::boot::MemoryMapIter) -> ! {
@ -61,7 +29,7 @@ fn efi_main(handle: Handle, st_boot: SystemTable<Boot>) -> 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<Boot>) -> 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);

5
src/misc.rs Normal file
View File

@ -0,0 +1,5 @@
pub fn halt() -> ! {
use x86_64::instructions::{interrupts, hlt};
interrupts::disable();
loop { hlt(); }
}