Added stdout/stderr logger that can work at any stage of booting.
parent
f65b87ad29
commit
b0501daeb9
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
52
src/main.rs
52
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<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);
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
pub fn halt() -> ! {
|
||||
use x86_64::instructions::{interrupts, hlt};
|
||||
interrupts::disable();
|
||||
loop { hlt(); }
|
||||
}
|
Loading…
Reference in New Issue