#![no_std]
#![deny(clippy::must_use_candidate)]
#![deny(missing_debug_implementations)]
extern crate log;
extern crate uefi;
use core::ffi::c_void;
use core::fmt::Write;
use core::ptr::{self, NonNull};
use core::sync::atomic::{AtomicPtr, Ordering};
use cfg_if::cfg_if;
use uefi::prelude::*;
use uefi::table::boot::{EventType, Tpl};
use uefi::table::{Boot, SystemTable};
use uefi::{Event, Result};
static SYSTEM_TABLE: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
#[cfg(feature = "logger")]
static LOGGER: uefi::logger::Logger = uefi::logger::Logger::new();
#[must_use]
fn system_table_opt() -> Option<SystemTable<Boot>> {
let ptr = SYSTEM_TABLE.load(Ordering::Acquire);
unsafe { SystemTable::from_ptr(ptr) }
}
#[must_use]
pub fn system_table() -> SystemTable<Boot> {
system_table_opt().expect("The system table handle is not available")
}
pub fn init(st: &mut SystemTable<Boot>) -> Result<Option<Event>> {
if system_table_opt().is_some() {
return Status::SUCCESS.to_result_with_val(|| None);
}
SYSTEM_TABLE.store(st.as_ptr().cast_mut(), Ordering::Release);
unsafe {
#[cfg(feature = "logger")]
init_logger(st);
uefi::allocator::init(st);
let boot_services = st.boot_services();
boot_services
.create_event(
EventType::SIGNAL_EXIT_BOOT_SERVICES,
Tpl::NOTIFY,
Some(exit_boot_services),
None,
)
.map(Some)
}
}
#[doc(hidden)]
pub fn _print(args: core::fmt::Arguments) {
system_table()
.stdout()
.write_fmt(args)
.expect("Failed to write to stdout");
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ($crate::_print(core::format_args!($($arg)*)));
}
#[macro_export]
macro_rules! println {
() => ($crate::print!("\n"));
($($arg:tt)*) => ($crate::_print(core::format_args!("{}{}", core::format_args!($($arg)*), "\n")));
}
#[cfg(feature = "logger")]
unsafe fn init_logger(st: &mut SystemTable<Boot>) {
LOGGER.set_output(st.stdout());
log::set_logger(&LOGGER).unwrap();
log::set_max_level(log::STATIC_MAX_LEVEL);
}
unsafe extern "efiapi" fn exit_boot_services(_e: Event, _ctx: Option<NonNull<c_void>>) {
SYSTEM_TABLE.store(ptr::null_mut(), Ordering::Release);
#[cfg(feature = "logger")]
LOGGER.disable();
uefi::allocator::exit_boot_services();
}
#[cfg(feature = "panic_handler")]
#[panic_handler]
fn panic_handler(info: &core::panic::PanicInfo) -> ! {
println!("[PANIC]: {}", info);
if let Some(st) = system_table_opt() {
st.boot_services().stall(10_000_000);
} else {
let mut dummy = 0u64;
for i in 0..300_000_000 {
unsafe {
core::ptr::write_volatile(&mut dummy, i);
}
}
}
cfg_if! {
if #[cfg(all(target_arch = "x86_64", feature = "qemu"))] {
use qemu_exit::QEMUExit;
let custom_exit_success = 3;
let qemu_exit_handle = qemu_exit::X86::new(0xF4, custom_exit_success);
qemu_exit_handle.exit_failure();
} else {
if let Some(st) = system_table_opt() {
use uefi::table::runtime::ResetType;
st.runtime_services()
.reset(ResetType::SHUTDOWN, uefi::Status::ABORTED, None);
}
log::error!("Could not shut down, please power off the system manually...");
cfg_if! {
if #[cfg(target_arch = "x86_64")] {
loop {
unsafe {
core::arch::asm!("hlt", options(nomem, nostack));
}
}
} else if #[cfg(target_arch = "aarch64")] {
loop {
unsafe {
core::arch::asm!("hlt 420", options(nomem, nostack));
}
}
} else {
loop {
}
}
}
}
}
}