1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
//! This module provides miscellaneous opinionated but optional helpers to
//! better integrate your application with the Rust runtime and the Rust
//! ecosystem.
//!
//! For now, this includes:
//! - using [`uefi::allocator::Allocator`] as global allocator (feature `global_allocator`)
//! - an implementation of [`log::Log`] (feature `logger`)
//! - [`print!`][print_macro] and [`println!`][println_macro] macros defaulting
//! to the uefi boot service stdout stream
//! - default panic handler (feature `panic_handler`)
//!
//! **PLEASE NOTE** that these helpers are meant for the pre exit boot service
//! epoch.
//!
//! [print_macro]: uefi::print!
//! [println_macro]: uefi::println!
use crate::prelude::{Boot, SystemTable};
use crate::Result;
use crate::StatusExt;
use core::ffi::c_void;
use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};
#[doc(hidden)]
pub use println::_print;
use uefi_raw::Status;
#[cfg(feature = "global_allocator")]
mod global_allocator;
#[cfg(feature = "logger")]
mod logger;
#[cfg(feature = "panic_handler")]
mod panic_handler;
mod println;
/// Reference to the system table.
///
/// This table is only fully safe to use until UEFI boot services have been exited.
/// After that, some fields and methods are unsafe to use, see the documentation of
/// UEFI's ExitBootServices entry point for more details.
static SYSTEM_TABLE: AtomicPtr<c_void> = AtomicPtr::new(core::ptr::null_mut());
#[must_use]
fn system_table_opt() -> Option<SystemTable<Boot>> {
let ptr = SYSTEM_TABLE.load(Ordering::Acquire);
// Safety: the `SYSTEM_TABLE` pointer either be null or a valid system
// table.
//
// Null is the initial value, as well as the value set when exiting boot
// services. Otherwise, the value is set by the call to `init`, which
// requires a valid system table reference as input.
unsafe { SystemTable::from_ptr(ptr) }
}
/// Obtains a pointer to the system table.
///
/// This is meant to be used by higher-level libraries,
/// which want a convenient way to access the system table singleton.
///
/// `init` must have been called first by the UEFI app.
///
/// The returned pointer is only valid until boot services are exited.
#[must_use]
// TODO do we want to keep this public?
pub fn system_table() -> SystemTable<Boot> {
system_table_opt().expect("The system table handle is not available")
}
/// Initialize all helpers defined in [`uefi::helpers`] whose Cargo features
/// are activated.
///
/// This must be called as early as possible, before trying to use logging or
/// memory allocation capabilities.
///
/// **PLEASE NOTE** that these helpers are meant for the pre exit boot service
/// epoch.
pub fn init(st: &mut SystemTable<Boot>) -> Result<()> {
if system_table_opt().is_some() {
// Avoid double initialization.
return Status::SUCCESS.to_result_with_val(|| ());
}
// Setup the system table singleton
SYSTEM_TABLE.store(st.as_ptr().cast_mut(), Ordering::Release);
// Setup logging and memory allocation
#[cfg(feature = "logger")]
unsafe {
logger::init(st);
}
#[cfg(feature = "global_allocator")]
unsafe {
uefi::allocator::init(st);
}
Ok(())
}
pub(crate) fn exit() {
// DEBUG: The UEFI spec does not guarantee that this printout will work, as
// the services used by logging might already have been shut down.
// But it works on current OVMF, and can be used as a handy way to
// check that the callback does get called.
//
// info!("Shutting down the UEFI utility library");
SYSTEM_TABLE.store(ptr::null_mut(), Ordering::Release);
#[cfg(feature = "logger")]
logger::disable();
#[cfg(feature = "global_allocator")]
uefi::allocator::exit_boot_services();
}