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:
|
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.
|
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(abi_efiapi)]
|
||||||
#![feature(asm)]
|
#![feature(asm)]
|
||||||
|
|
||||||
use core::fmt::Write;
|
mod logger;
|
||||||
use core::panic::PanicInfo;
|
mod misc;
|
||||||
|
|
||||||
|
use crate::logger::{LoggerBackend, LOGGER_BACKEND};
|
||||||
|
use crate::misc::halt;
|
||||||
|
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::slice;
|
use core::slice;
|
||||||
use core::writeln;
|
|
||||||
use uefi::prelude::*;
|
use uefi::prelude::*;
|
||||||
use uefi::table::boot::{AllocateType, MemoryDescriptor, MemoryType};
|
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) {
|
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.");
|
println!("Booting...");
|
||||||
writeln!(stdout, "Booting...").unwrap();
|
|
||||||
|
|
||||||
writeln!(stdout, "Exiting the UEFI boot services.").unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main(st: SystemTable<uefi::table::Runtime>, mmap: uefi::table::boot::MemoryMapIter) -> ! {
|
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.
|
// Tasks that require the UEFI boot services.
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
LOGGER_STDIO = LoggerStdio::Boot(st_boot.unsafe_clone());
|
LOGGER_BACKEND = LoggerBackend::UefiStdio(st_boot.unsafe_clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
setup(&st_boot, handle);
|
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.
|
// I do not currently have an adequate stdout for post-UEFI, but the UEFI one is now invalid.
|
||||||
unsafe {
|
unsafe {
|
||||||
LOGGER_STDIO = LoggerStdio::None;
|
LOGGER_BACKEND = LoggerBackend::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
main(st_runtime, mmap);
|
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