From d64b27a9020a617a1f3ab6e337077edc90a98717 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 13 Apr 2024 11:07:26 +0200 Subject: [PATCH 1/7] uefi-services: deprecation: move to uefi::helpers uefi_services::init temporarily stays API compatible to prevent breaking changes. But we've added deprecation warnings. The new uefi::helpers module consists mostly of an unchanged code import. Only minor adjustments were applied. Cleanup will happen in a follow-up. --- Cargo.lock | 5 +- uefi-services/CHANGELOG.md | 4 +- uefi-services/Cargo.toml | 9 +- uefi-services/README.md | 16 +- uefi-services/build.rs | 3 + uefi-services/src/lib.rs | 257 ++----------------------------- uefi/Cargo.toml | 15 +- uefi/src/allocator.rs | 4 +- uefi/src/{ => helpers}/logger.rs | 0 uefi/src/helpers/mod.rs | 242 +++++++++++++++++++++++++++++ uefi/src/lib.rs | 6 +- 11 files changed, 280 insertions(+), 281 deletions(-) create mode 100644 uefi-services/build.rs rename uefi/src/{ => helpers}/logger.rs (100%) create mode 100644 uefi/src/helpers/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 0fb6093c0..80b2f6352 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -853,8 +853,10 @@ name = "uefi" version = "0.27.0" dependencies = [ "bitflags 2.5.0", + "cfg-if", "log", "ptr_meta", + "qemu-exit", "ucs2", "uefi-macros", "uefi-raw", @@ -885,9 +887,6 @@ dependencies = [ name = "uefi-services" version = "0.24.0" dependencies = [ - "cfg-if", - "log", - "qemu-exit", "uefi", ] diff --git a/uefi-services/CHANGELOG.md b/uefi-services/CHANGELOG.md index 4d4201b07..6b9550b77 100644 --- a/uefi-services/CHANGELOG.md +++ b/uefi-services/CHANGELOG.md @@ -1,8 +1,8 @@ # uefi-services - [Unreleased] ## Changed -- The implicit `qemu-exit` crate feature has been removed. (Note that this is - different from the `qemu` crate feature, which is unchanged.) +- `uefi-services` is deprecated and should be removed. All functionality was + moved to `uefi::helpers::init()` # uefi-services - 0.23.0 (2023-11-12) diff --git a/uefi-services/Cargo.toml b/uefi-services/Cargo.toml index 176496ce7..9ceb954f1 100644 --- a/uefi-services/Cargo.toml +++ b/uefi-services/Cargo.toml @@ -2,7 +2,7 @@ name = "uefi-services" version = "0.24.0" readme = "README.md" -description = "Higher-level utilities for the `uefi` crate." +description = "Deprecated. Please migrate to `uefi::helpers`." authors.workspace = true categories.workspace = true @@ -14,13 +14,10 @@ rust-version.workspace = true [dependencies] uefi = { version = "0.27.0", features = ["global_allocator"] } -log.workspace = true -cfg-if = "1.0.0" -qemu-exit = { version = "3.0.1", optional = true } [features] default = ["panic_handler", "logger"] # Enable QEMU-specific functionality -qemu = ["dep:qemu-exit"] -panic_handler = [] +qemu = ["uefi/qemu"] +panic_handler = [ "uefi/panic_handler" ] logger = ["uefi/logger"] diff --git a/uefi-services/README.md b/uefi-services/README.md index 885916dd4..642763126 100644 --- a/uefi-services/README.md +++ b/uefi-services/README.md @@ -1,17 +1,3 @@ # uefi-services -[![Crates.io](https://img.shields.io/crates/v/uefi-services)](https://crates.io/crates/uefi-services) -[![Docs.rs](https://docs.rs/uefi-macros/badge.svg)](https://docs.rs/uefi-services) - -This crate enables you some convenience features on top of the -[`uefi`](https://crates.io/crates/uefi) crate. It includes a panic handler, a logger, and -a global allocator. - -`uefi-services` is part of the `uefi-rs` project. Please refer to - for comprehensive documentation. - -## Optional features - -This crate's features are described in [`src/lib.rs`]. - -[`src/lib.rs`]: src/lib.rs +WARNING: `uefi-services` is deprecated. Functionality was moved to `uefi::helpers::init`. diff --git a/uefi-services/build.rs b/uefi-services/build.rs new file mode 100644 index 000000000..96cea2f34 --- /dev/null +++ b/uefi-services/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:warning=`uefi-services` is deprecated. Functionality was moved to `uefi::helpers::init`."); +} diff --git a/uefi-services/src/lib.rs b/uefi-services/src/lib.rs index 5006b2838..a598b446a 100644 --- a/uefi-services/src/lib.rs +++ b/uefi-services/src/lib.rs @@ -1,253 +1,20 @@ -//! This crate simplifies the writing of higher-level code for UEFI. -//! -//! It initializes the memory allocation and logging crates, -//! allowing code to use Rust's data structures and to log errors. -//! -//! Logging and allocation are only allowed while boot services are -//! active. Once runtime services are activated by calling -//! [`exit_boot_services`], the logger will be disabled and the -//! allocator will always return null. -//! -//! It also stores a global reference to the UEFI system table, -//! in order to reduce the redundant passing of references to it. -//! -//! Library code can simply use global UEFI functions -//! through the reference provided by `system_table`. -//! -//! ## Optional crate features -//! -//! - `logger` (enabled by default): Initialize a global logger. -//! - `panic_handler` (enabled by default): Register a panic handler. A -//! panic handler must be provided for your program to compile, but -//! you can choose to provide your own if you don't want to use this -//! one. -//! - `qemu`: On x86_64, make qemu exit with code 3 if a panic -//! occurs. This feature assumes the program is running under QEMU. -//! -//! [`exit_boot_services`]: uefi::table::SystemTable::exit_boot_services - +//! WARNING: `uefi-services` is deprecated. Functionality was moved to `uefi::helpers::init`. #![no_std] -#![deny(clippy::must_use_candidate)] -#![deny(missing_debug_implementations)] - -extern crate log; -// Core types. -extern crate uefi; - -use core::ffi::c_void; -use core::fmt::Write; -use core::ptr::{self, NonNull}; -use core::sync::atomic::{AtomicPtr, Ordering}; - -#[cfg(feature = "panic_handler")] -use cfg_if::cfg_if; - -use uefi::table::boot::{EventType, Tpl}; -use uefi::table::{Boot, SystemTable}; -use uefi::{Event, Result, Status, StatusExt}; -/// 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 = AtomicPtr::new(ptr::null_mut()); +use uefi::prelude::*; +use uefi::Event; +use uefi::Result; -/// Global logger object -#[cfg(feature = "logger")] -static LOGGER: uefi::logger::Logger = uefi::logger::Logger::new(); +pub use uefi::{print, println}; -#[must_use] -fn system_table_opt() -> Option> { - 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] -pub fn system_table() -> SystemTable { - system_table_opt().expect("The system table handle is not available") -} - -/// Initialize the UEFI utility library. -/// -/// This must be called as early as possible, -/// before trying to use logging or memory allocation capabilities. +/// Deprecated. Use [`uefi::helpers::init`] instead. +#[deprecated = "WARNING: `uefi-services` is deprecated. Functionality was moved to `uefi::helpers::init`."] pub fn init(st: &mut SystemTable) -> Result> { - if system_table_opt().is_some() { - // Avoid double initialization. - return Status::SUCCESS.to_result_with_val(|| None); - } - - // Setup the system table singleton - SYSTEM_TABLE.store(st.as_ptr().cast_mut(), Ordering::Release); - - unsafe { - // Setup logging and memory allocation - - #[cfg(feature = "logger")] - init_logger(st); - - uefi::allocator::init(st); - - // Schedule these tools to be disabled on exit from UEFI boot services - let boot_services = st.boot_services(); - boot_services - .create_event( - EventType::SIGNAL_EXIT_BOOT_SERVICES, - Tpl::NOTIFY, - Some(exit_boot_services), - None, - ) - .map(Some) - } -} - -// Internal function for print macros. -#[doc(hidden)] -pub fn _print(args: core::fmt::Arguments) { - system_table() - .stdout() - .write_fmt(args) - .expect("Failed to write to stdout"); + uefi::helpers::init(st) } -/// Prints to the standard output. -/// -/// # Panics -/// Will panic if `SYSTEM_TABLE` is `None` (Before [init()] and after [uefi::prelude::SystemTable::exit_boot_services()]). -/// -/// # Examples -/// ``` -/// print!(""); -/// print!("Hello World\n"); -/// print!("Hello {}", "World"); -/// ``` -#[macro_export] -macro_rules! print { - ($($arg:tt)*) => ($crate::_print(core::format_args!($($arg)*))); -} - -/// Prints to the standard output, with a newline. -/// -/// # Panics -/// Will panic if `SYSTEM_TABLE` is `None` (Before [init()] and after [uefi::prelude::SystemTable::exit_boot_services()]). -/// -/// # Examples -/// ``` -/// println!(); -/// println!("Hello World"); -/// println!("Hello {}", "World"); -/// ``` -#[macro_export] -macro_rules! println { - () => ($crate::print!("\n")); - ($($arg:tt)*) => ($crate::_print(core::format_args!("{}{}", core::format_args!($($arg)*), "\n"))); -} - -/// Set up logging -/// -/// This is unsafe because you must arrange for the logger to be reset with -/// disable() on exit from UEFI boot services. -#[cfg(feature = "logger")] -unsafe fn init_logger(st: &mut SystemTable) { - // Connect the logger to stdout. - LOGGER.set_output(st.stdout()); - - // Set the logger. - log::set_logger(&LOGGER).unwrap(); // Can only fail if already initialized. - - // Set logger max level to level specified by log features - log::set_max_level(log::STATIC_MAX_LEVEL); -} - -/// Notify the utility library that boot services are not safe to call anymore -/// As this is a callback, it must be `extern "efiapi"`. -unsafe extern "efiapi" fn exit_boot_services(_e: Event, _ctx: Option>) { - // 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(); - - uefi::allocator::exit_boot_services(); -} - -#[cfg(feature = "panic_handler")] -#[panic_handler] -fn panic_handler(info: &core::panic::PanicInfo) -> ! { - println!("[PANIC]: {}", info); - - // Give the user some time to read the message - if let Some(st) = system_table_opt() { - st.boot_services().stall(10_000_000); - } else { - let mut dummy = 0u64; - // FIXME: May need different counter values in debug & release builds - 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"))] { - // If running in QEMU, use the f4 exit port to signal the error and exit - 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 the system table is available, use UEFI's standard shutdown mechanism - if let Some(st) = system_table_opt() { - use uefi::table::runtime::ResetType; - st.runtime_services() - .reset(ResetType::SHUTDOWN, uefi::Status::ABORTED, None); - } - - // If we don't have any shutdown mechanism handy, the best we can do is loop - log::error!("Could not shut down, please power off the system manually..."); - - cfg_if! { - if #[cfg(target_arch = "x86_64")] { - loop { - unsafe { - // Try to at least keep CPU from running at 100% - core::arch::asm!("hlt", options(nomem, nostack)); - } - } - } else if #[cfg(target_arch = "aarch64")] { - loop { - unsafe { - // Try to at least keep CPU from running at 100% - core::arch::asm!("hlt 420", options(nomem, nostack)); - } - } - } else { - loop { - // just run forever dammit how do you return never anyway - } - } - } - } - } +/// Deprecated. Use [`uefi::helpers::system_table`] instead. +#[deprecated = "WARNING: `uefi-services` is deprecated. Functionality was moved to `uefi::helpers::system_table`."] +pub fn system_table() -> SystemTable { + uefi::helpers::system_table() } diff --git a/uefi/Cargo.toml b/uefi/Cargo.toml index 7dd6fbc13..0511dc5aa 100644 --- a/uefi/Cargo.toml +++ b/uefi/Cargo.toml @@ -15,23 +15,30 @@ rust-version.workspace = true [features] default = ["panic-on-logger-errors"] alloc = [] -global_allocator = [] + +# Generic gate to code that uses unstable features of Rust. You usually need a nightly toolchain. +unstable = [] + +# Helper features: logger = [] +global_allocator = [] +panic_handler = [] # Ignore text output errors in logger as a workaround for firmware issues that # were observed on the VirtualBox UEFI implementation (see uefi-rs#121). # In those cases, this feature can be excluded by removing the default features. panic-on-logger-errors = [] -# Generic gate to code that uses unstable features of Rust. You usually need a nightly toolchain. -unstable = [] +qemu = ["dep:qemu-exit"] [dependencies] bitflags.workspace = true log.workspace = true ptr_meta.workspace = true +uguid.workspace = true +cfg-if = "1.0.0" ucs2 = "0.3.2" uefi-macros = "0.13.0" uefi-raw = "0.5.1" -uguid.workspace = true +qemu-exit = { version = "3.0.2", optional = true } [package.metadata.docs.rs] all-features = true diff --git a/uefi/src/allocator.rs b/uefi/src/allocator.rs index 364326e09..2080d79bd 100644 --- a/uefi/src/allocator.rs +++ b/uefi/src/allocator.rs @@ -131,6 +131,4 @@ unsafe impl GlobalAlloc for Allocator { } } -#[cfg(feature = "global_allocator")] -#[global_allocator] -static ALLOCATOR: Allocator = Allocator; + diff --git a/uefi/src/logger.rs b/uefi/src/helpers/logger.rs similarity index 100% rename from uefi/src/logger.rs rename to uefi/src/helpers/logger.rs diff --git a/uefi/src/helpers/mod.rs b/uefi/src/helpers/mod.rs new file mode 100644 index 000000000..9183569c2 --- /dev/null +++ b/uefi/src/helpers/mod.rs @@ -0,0 +1,242 @@ +//! 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 [`Allocator`] as global allocator (feature `global_allocator`) +//! - an implementation of [`Log`] (feature `logger`) +//! - [`print!`] and [`println!`] 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. + +#[cfg(feature = "logger")] +pub mod logger; + +#[cfg(feature = "panic_handler")] +use cfg_if::cfg_if; +use core::ffi::c_void; +use core::fmt::Write; +use core::ptr::{self, NonNull}; +use core::sync::atomic::{AtomicPtr, Ordering}; +use log::Log; +use uefi::allocator::Allocator; +use uefi::table::boot::{EventType, Tpl}; +use uefi::table::{Boot, SystemTable}; +use uefi::{Event, Result, Status, StatusExt}; + +#[cfg_attr(feature = "global_allocator", global_allocator)] +static ALLOCATOR: Allocator = Allocator; + +/// 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 = AtomicPtr::new(ptr::null_mut()); + +/// Global logger object +#[cfg(feature = "logger")] +static LOGGER: logger::Logger = logger::Logger::new(); + +#[must_use] +fn system_table_opt() -> Option> { + 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 { + 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) -> Result> { + if system_table_opt().is_some() { + // Avoid double initialization. + return Status::SUCCESS.to_result_with_val(|| None); + } + + // Setup the system table singleton + SYSTEM_TABLE.store(st.as_ptr().cast_mut(), Ordering::Release); + + unsafe { + // Setup logging and memory allocation + + #[cfg(feature = "logger")] + init_logger(st); + + uefi::allocator::init(st); + + // Schedule these tools to be disabled on exit from UEFI boot services + let boot_services = st.boot_services(); + boot_services + .create_event( + EventType::SIGNAL_EXIT_BOOT_SERVICES, + Tpl::NOTIFY, + Some(exit_boot_services), + None, + ) + .map(Some) + } +} + +/// INTERNAL API! Helper for print macros. +#[doc(hidden)] +pub fn _print(args: core::fmt::Arguments) { + system_table() + .stdout() + .write_fmt(args) + .expect("Failed to write to stdout"); +} + +/// Prints to the standard output. +/// +/// # Panics +/// Will panic if `SYSTEM_TABLE` is `None` (Before [init()] and after [uefi::prelude::SystemTable::exit_boot_services()]). +/// +/// # Examples +/// ``` +/// print!(""); +/// print!("Hello World\n"); +/// print!("Hello {}", "World"); +/// ``` +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::helpers::_print(core::format_args!($($arg)*))); +} + +/// Prints to the standard output, with a newline. +/// +/// # Panics +/// Will panic if `SYSTEM_TABLE` is `None` (Before [init()] and after [uefi::prelude::SystemTable::exit_boot_services()]). +/// +/// # Examples +/// ``` +/// println!(); +/// println!("Hello World"); +/// println!("Hello {}", "World"); +/// ``` +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ($crate::helpers::_print(core::format_args!("{}{}", core::format_args!($($arg)*), "\n"))); +} + +/// Set up logging +/// +/// This is unsafe because you must arrange for the logger to be reset with +/// disable() on exit from UEFI boot services. +#[cfg(feature = "logger")] +unsafe fn init_logger(st: &mut SystemTable) { + // Connect the logger to stdout. + LOGGER.set_output(st.stdout()); + + // Set the logger. + log::set_logger(&LOGGER).unwrap(); // Can only fail if already initialized. + + // Set logger max level to level specified by log features + log::set_max_level(log::STATIC_MAX_LEVEL); +} + +/// Notify the utility library that boot services are not safe to call anymore +/// As this is a callback, it must be `extern "efiapi"`. +unsafe extern "efiapi" fn exit_boot_services(_e: Event, _ctx: Option>) { + // 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(); + + uefi::allocator::exit_boot_services(); +} + +#[cfg(feature = "panic_handler")] +#[panic_handler] +fn panic_handler(info: &core::panic::PanicInfo) -> ! { + println!("[PANIC]: {}", info); + + // Give the user some time to read the message + if let Some(st) = system_table_opt() { + st.boot_services().stall(10_000_000); + } else { + let mut dummy = 0u64; + // FIXME: May need different counter values in debug & release builds + 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"))] { + // If running in QEMU, use the f4 exit port to signal the error and exit + 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 the system table is available, use UEFI's standard shutdown mechanism + if let Some(st) = system_table_opt() { + use uefi::table::runtime::ResetType; + st.runtime_services() + .reset(ResetType::SHUTDOWN, uefi::Status::ABORTED, None); + } + + // If we don't have any shutdown mechanism handy, the best we can do is loop + log::error!("Could not shut down, please power off the system manually..."); + + cfg_if! { + if #[cfg(target_arch = "x86_64")] { + loop { + unsafe { + // Try to at least keep CPU from running at 100% + core::arch::asm!("hlt", options(nomem, nostack)); + } + } + } else if #[cfg(target_arch = "aarch64")] { + loop { + unsafe { + // Try to at least keep CPU from running at 100% + core::arch::asm!("hlt 420", options(nomem, nostack)); + } + } + } else { + loop { + // just run forever dammit how do you return never anyway + } + } + } + } + } +} diff --git a/uefi/src/lib.rs b/uefi/src/lib.rs index f8fa31f62..78908dc7a 100644 --- a/uefi/src/lib.rs +++ b/uefi/src/lib.rs @@ -114,9 +114,6 @@ pub mod prelude; pub mod allocator; -#[cfg(feature = "logger")] -pub mod logger; - #[cfg(feature = "alloc")] pub mod fs; @@ -126,8 +123,11 @@ pub(crate) mod mem; pub(crate) mod polyfill; +pub mod helpers; + mod util; + #[cfg(test)] // Crates that create procedural macros can't unit test the macros they export. // Therefore, we do some tests here. From ed7fd4b205b979b3828690bf071287802ba00471 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 13 Apr 2024 11:10:38 +0200 Subject: [PATCH 2/7] uefi-test-runner: use new uefi::helpers module Note that even without these changes, everything works. This proves that we stay API compatible for the next uefi-services release. --- Cargo.lock | 1 - uefi-test-runner/Cargo.toml | 3 +-- uefi-test-runner/examples/hello_world.rs | 2 +- uefi-test-runner/examples/loaded_image.rs | 2 +- uefi-test-runner/examples/shell_params.rs | 4 ++-- uefi-test-runner/examples/sierpinski.rs | 2 +- uefi-test-runner/src/bin/shell_launcher.rs | 2 +- uefi-test-runner/src/main.rs | 4 ++-- 8 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 80b2f6352..c7c3b3eaa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -897,7 +897,6 @@ dependencies = [ "log", "qemu-exit", "uefi", - "uefi-services", ] [[package]] diff --git a/uefi-test-runner/Cargo.toml b/uefi-test-runner/Cargo.toml index b31a9f7ed..be314c930 100644 --- a/uefi-test-runner/Cargo.toml +++ b/uefi-test-runner/Cargo.toml @@ -6,8 +6,7 @@ publish = false edition = "2021" [dependencies] -uefi = { path = "../uefi", features = ["alloc"] } -uefi-services = { path = "../uefi-services" } +uefi = { path = "../uefi", features = ["alloc", "global_allocator", "panic_handler", "logger", "qemu"] } log.workspace = true diff --git a/uefi-test-runner/examples/hello_world.rs b/uefi-test-runner/examples/hello_world.rs index 2c3c3a52a..a2b46d826 100644 --- a/uefi-test-runner/examples/hello_world.rs +++ b/uefi-test-runner/examples/hello_world.rs @@ -14,7 +14,7 @@ use uefi::prelude::*; fn main(_image_handle: Handle, mut system_table: SystemTable) -> Status { // ANCHOR_END: entry // ANCHOR: services - uefi_services::init(&mut system_table).unwrap(); + uefi::helpers::init(&mut system_table).unwrap(); // ANCHOR_END: services // ANCHOR: log info!("Hello world!"); diff --git a/uefi-test-runner/examples/loaded_image.rs b/uefi-test-runner/examples/loaded_image.rs index d565115fa..01debc0cb 100644 --- a/uefi-test-runner/examples/loaded_image.rs +++ b/uefi-test-runner/examples/loaded_image.rs @@ -14,7 +14,7 @@ use uefi::{Identify, Result}; // ANCHOR: main #[entry] fn main(image_handle: Handle, mut system_table: SystemTable) -> Status { - uefi_services::init(&mut system_table).unwrap(); + uefi::helpers::init(&mut system_table).unwrap(); let boot_services = system_table.boot_services(); print_image_path(boot_services).unwrap(); diff --git a/uefi-test-runner/examples/shell_params.rs b/uefi-test-runner/examples/shell_params.rs index eb9beb092..ad95a89f4 100644 --- a/uefi-test-runner/examples/shell_params.rs +++ b/uefi-test-runner/examples/shell_params.rs @@ -7,8 +7,8 @@ use log::error; // ANCHOR: use use uefi::prelude::*; +use uefi::println; use uefi::proto::shell_params::ShellParameters; -use uefi_services::println; extern crate alloc; use alloc::string::{String, ToString}; @@ -20,7 +20,7 @@ use alloc::vec::Vec; fn main(image_handle: Handle, mut system_table: SystemTable) -> Status { // ANCHOR_END: entry // ANCHOR: services - uefi_services::init(&mut system_table).unwrap(); + uefi::helpers::init(&mut system_table).unwrap(); let boot_services = system_table.boot_services(); // ANCHOR_END: services diff --git a/uefi-test-runner/examples/sierpinski.rs b/uefi-test-runner/examples/sierpinski.rs index ad21904b5..bbd2de48c 100644 --- a/uefi-test-runner/examples/sierpinski.rs +++ b/uefi-test-runner/examples/sierpinski.rs @@ -125,7 +125,7 @@ fn draw_sierpinski(bt: &BootServices) -> Result { #[entry] fn main(_handle: Handle, mut system_table: SystemTable) -> Status { - uefi_services::init(&mut system_table).unwrap(); + uefi::helpers::init(&mut system_table).unwrap(); let bt = system_table.boot_services(); draw_sierpinski(bt).unwrap(); Status::SUCCESS diff --git a/uefi-test-runner/src/bin/shell_launcher.rs b/uefi-test-runner/src/bin/shell_launcher.rs index e322a2d2d..4accdca6f 100644 --- a/uefi-test-runner/src/bin/shell_launcher.rs +++ b/uefi-test-runner/src/bin/shell_launcher.rs @@ -46,7 +46,7 @@ fn get_shell_app_device_path<'a>( #[entry] fn efi_main(image: Handle, mut st: SystemTable) -> Status { - uefi_services::init(&mut st).unwrap(); + uefi::helpers::init(&mut st).unwrap(); let boot_services = st.boot_services(); let mut storage = Vec::new(); diff --git a/uefi-test-runner/src/main.rs b/uefi-test-runner/src/main.rs index 7b96d570c..b3a3e7ad4 100644 --- a/uefi-test-runner/src/main.rs +++ b/uefi-test-runner/src/main.rs @@ -14,7 +14,7 @@ use uefi::proto::device_path::build::{self, DevicePathBuilder}; use uefi::proto::device_path::messaging::Vendor; use uefi::table::boot::MemoryType; use uefi::Result; -use uefi_services::{print, println}; +use uefi::{print, println}; mod boot; mod fs; @@ -24,7 +24,7 @@ mod runtime; #[entry] fn efi_main(image: Handle, mut st: SystemTable) -> Status { // Initialize utilities (logging, memory allocation...) - uefi_services::init(&mut st).expect("Failed to initialize utilities"); + uefi::helpers::init(&mut st).expect("Failed to initialize utilities"); // unit tests here From e5c2a0ce33c23a28de0c8f17f18414c18426d761 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 13 Apr 2024 11:39:55 +0200 Subject: [PATCH 3/7] uefi::helper: clean up, multiple sub modules I didn't change anything about the implementations, just moved the existing code into the corresponding modules. --- uefi/src/allocator.rs | 2 - uefi/src/helpers/global_allocator.rs | 4 + uefi/src/helpers/logger.rs | 25 ++++ uefi/src/helpers/mod.rs | 172 +++++---------------------- uefi/src/helpers/panic_handler.rs | 63 ++++++++++ uefi/src/helpers/println.rs | 46 +++++++ uefi/src/lib.rs | 1 - 7 files changed, 166 insertions(+), 147 deletions(-) create mode 100644 uefi/src/helpers/global_allocator.rs create mode 100644 uefi/src/helpers/panic_handler.rs create mode 100644 uefi/src/helpers/println.rs diff --git a/uefi/src/allocator.rs b/uefi/src/allocator.rs index 2080d79bd..3be75c164 100644 --- a/uefi/src/allocator.rs +++ b/uefi/src/allocator.rs @@ -130,5 +130,3 @@ unsafe impl GlobalAlloc for Allocator { (*boot_services()).free_pool(ptr).unwrap(); } } - - diff --git a/uefi/src/helpers/global_allocator.rs b/uefi/src/helpers/global_allocator.rs new file mode 100644 index 000000000..4ca5bdd06 --- /dev/null +++ b/uefi/src/helpers/global_allocator.rs @@ -0,0 +1,4 @@ +use crate::allocator::Allocator; + +#[global_allocator] +static ALLOCATOR: Allocator = Allocator; diff --git a/uefi/src/helpers/logger.rs b/uefi/src/helpers/logger.rs index 64434cf0b..3fc386bb1 100644 --- a/uefi/src/helpers/logger.rs +++ b/uefi/src/helpers/logger.rs @@ -14,10 +14,35 @@ use crate::proto::console::text::Output; +use crate::prelude::{Boot, SystemTable}; use core::fmt::{self, Write}; use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; +/// Global logger object +#[cfg(feature = "logger")] +static LOGGER: Logger = Logger::new(); + +/// Set up logging +/// +/// This is unsafe because you must arrange for the logger to be reset with +/// disable() on exit from UEFI boot services. +#[cfg(feature = "logger")] +pub unsafe fn init(st: &mut SystemTable) { + // Connect the logger to stdout. + LOGGER.set_output(st.stdout()); + + // Set the logger. + log::set_logger(&LOGGER).unwrap(); // Can only fail if already initialized. + + // Set logger max level to level specified by log features + log::set_max_level(log::STATIC_MAX_LEVEL); +} + +pub fn disable() { + LOGGER.disable(); +} + /// Logging implementation which writes to a UEFI output stream. /// /// If this logger is used as a global logger, you must disable it using the diff --git a/uefi/src/helpers/mod.rs b/uefi/src/helpers/mod.rs index 9183569c2..d16b9c6fc 100644 --- a/uefi/src/helpers/mod.rs +++ b/uefi/src/helpers/mod.rs @@ -3,43 +3,45 @@ //! ecosystem. //! //! For now, this includes: -//! - using [`Allocator`] as global allocator (feature `global_allocator`) -//! - an implementation of [`Log`] (feature `logger`) -//! - [`print!`] and [`println!`] macros defaulting to the uefi boot service -//! stdout stream +//! - 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! -#[cfg(feature = "logger")] -pub mod logger; - -#[cfg(feature = "panic_handler")] -use cfg_if::cfg_if; +use crate::prelude::{Boot, SystemTable}; +use crate::Event; +use crate::Result; +use crate::StatusExt; use core::ffi::c_void; -use core::fmt::Write; -use core::ptr::{self, NonNull}; +use core::ptr; +use core::ptr::NonNull; use core::sync::atomic::{AtomicPtr, Ordering}; -use log::Log; -use uefi::allocator::Allocator; -use uefi::table::boot::{EventType, Tpl}; -use uefi::table::{Boot, SystemTable}; -use uefi::{Event, Result, Status, StatusExt}; +#[doc(hidden)] +pub use println::_print; +use uefi_raw::table::boot::{EventType, Tpl}; +use uefi_raw::Status; -#[cfg_attr(feature = "global_allocator", global_allocator)] -static ALLOCATOR: Allocator = Allocator; +#[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 = AtomicPtr::new(ptr::null_mut()); - -/// Global logger object -#[cfg(feature = "logger")] -static LOGGER: logger::Logger = logger::Logger::new(); +static SYSTEM_TABLE: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); #[must_use] fn system_table_opt() -> Option> { @@ -88,7 +90,7 @@ pub fn init(st: &mut SystemTable) -> Result> { // Setup logging and memory allocation #[cfg(feature = "logger")] - init_logger(st); + logger::init(st); uefi::allocator::init(st); @@ -105,64 +107,6 @@ pub fn init(st: &mut SystemTable) -> Result> { } } -/// INTERNAL API! Helper for print macros. -#[doc(hidden)] -pub fn _print(args: core::fmt::Arguments) { - system_table() - .stdout() - .write_fmt(args) - .expect("Failed to write to stdout"); -} - -/// Prints to the standard output. -/// -/// # Panics -/// Will panic if `SYSTEM_TABLE` is `None` (Before [init()] and after [uefi::prelude::SystemTable::exit_boot_services()]). -/// -/// # Examples -/// ``` -/// print!(""); -/// print!("Hello World\n"); -/// print!("Hello {}", "World"); -/// ``` -#[macro_export] -macro_rules! print { - ($($arg:tt)*) => ($crate::helpers::_print(core::format_args!($($arg)*))); -} - -/// Prints to the standard output, with a newline. -/// -/// # Panics -/// Will panic if `SYSTEM_TABLE` is `None` (Before [init()] and after [uefi::prelude::SystemTable::exit_boot_services()]). -/// -/// # Examples -/// ``` -/// println!(); -/// println!("Hello World"); -/// println!("Hello {}", "World"); -/// ``` -#[macro_export] -macro_rules! println { - () => ($crate::print!("\n")); - ($($arg:tt)*) => ($crate::helpers::_print(core::format_args!("{}{}", core::format_args!($($arg)*), "\n"))); -} - -/// Set up logging -/// -/// This is unsafe because you must arrange for the logger to be reset with -/// disable() on exit from UEFI boot services. -#[cfg(feature = "logger")] -unsafe fn init_logger(st: &mut SystemTable) { - // Connect the logger to stdout. - LOGGER.set_output(st.stdout()); - - // Set the logger. - log::set_logger(&LOGGER).unwrap(); // Can only fail if already initialized. - - // Set logger max level to level specified by log features - log::set_max_level(log::STATIC_MAX_LEVEL); -} - /// Notify the utility library that boot services are not safe to call anymore /// As this is a callback, it must be `extern "efiapi"`. unsafe extern "efiapi" fn exit_boot_services(_e: Event, _ctx: Option>) { @@ -175,68 +119,8 @@ unsafe extern "efiapi" fn exit_boot_services(_e: Event, _ctx: Option ! { - println!("[PANIC]: {}", info); - - // Give the user some time to read the message - if let Some(st) = system_table_opt() { - st.boot_services().stall(10_000_000); - } else { - let mut dummy = 0u64; - // FIXME: May need different counter values in debug & release builds - 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"))] { - // If running in QEMU, use the f4 exit port to signal the error and exit - 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 the system table is available, use UEFI's standard shutdown mechanism - if let Some(st) = system_table_opt() { - use uefi::table::runtime::ResetType; - st.runtime_services() - .reset(ResetType::SHUTDOWN, uefi::Status::ABORTED, None); - } - - // If we don't have any shutdown mechanism handy, the best we can do is loop - log::error!("Could not shut down, please power off the system manually..."); - - cfg_if! { - if #[cfg(target_arch = "x86_64")] { - loop { - unsafe { - // Try to at least keep CPU from running at 100% - core::arch::asm!("hlt", options(nomem, nostack)); - } - } - } else if #[cfg(target_arch = "aarch64")] { - loop { - unsafe { - // Try to at least keep CPU from running at 100% - core::arch::asm!("hlt 420", options(nomem, nostack)); - } - } - } else { - loop { - // just run forever dammit how do you return never anyway - } - } - } - } - } -} diff --git a/uefi/src/helpers/panic_handler.rs b/uefi/src/helpers/panic_handler.rs new file mode 100644 index 000000000..b70b33273 --- /dev/null +++ b/uefi/src/helpers/panic_handler.rs @@ -0,0 +1,63 @@ +use crate::helpers::system_table_opt; +use crate::println; +use cfg_if::cfg_if; + +#[panic_handler] +fn panic_handler(info: &core::panic::PanicInfo) -> ! { + println!("[PANIC]: {}", info); + + // Give the user some time to read the message + if let Some(st) = system_table_opt() { + st.boot_services().stall(10_000_000); + } else { + let mut dummy = 0u64; + // FIXME: May need different counter values in debug & release builds + 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"))] { + // If running in QEMU, use the f4 exit port to signal the error and exit + 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 the system table is available, use UEFI's standard shutdown mechanism + if let Some(st) = system_table_opt() { + use uefi::table::runtime::ResetType; + st.runtime_services() + .reset(ResetType::SHUTDOWN, uefi::Status::ABORTED, None); + } + + // If we don't have any shutdown mechanism handy, the best we can do is loop + log::error!("Could not shut down, please power off the system manually..."); + + cfg_if! { + if #[cfg(target_arch = "x86_64")] { + loop { + unsafe { + // Try to at least keep CPU from running at 100% + core::arch::asm!("hlt", options(nomem, nostack)); + } + } + } else if #[cfg(target_arch = "aarch64")] { + loop { + unsafe { + // Try to at least keep CPU from running at 100% + core::arch::asm!("hlt 420", options(nomem, nostack)); + } + } + } else { + loop { + // just run forever dammit how do you return never anyway + } + } + } + } + } +} diff --git a/uefi/src/helpers/println.rs b/uefi/src/helpers/println.rs new file mode 100644 index 000000000..265911da1 --- /dev/null +++ b/uefi/src/helpers/println.rs @@ -0,0 +1,46 @@ +use crate::helpers::system_table; +use core::fmt::Write; + +/// INTERNAL API! Helper for print macros. +#[doc(hidden)] +pub fn _print(args: core::fmt::Arguments) { + system_table() + .stdout() + .write_fmt(args) + .expect("Failed to write to stdout"); +} + +/// Prints to the standard output. +/// +/// # Panics +/// Will panic if `SYSTEM_TABLE` is `None` (Before [`uefi::helpers::init()`] and +/// after [`uefi::prelude::SystemTable::exit_boot_services()`]). +/// +/// # Examples +/// ``` +/// print!(""); +/// print!("Hello World\n"); +/// print!("Hello {}", "World"); +/// ``` +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ($crate::helpers::_print(core::format_args!($($arg)*))); +} + +/// Prints to the standard output, with a newline. +/// +/// # Panics +/// Will panic if `SYSTEM_TABLE` is `None` (Before [`uefi::helpers::init()`] and +/// after [`uefi::prelude::SystemTable::exit_boot_services()`]). +/// +/// # Examples +/// ``` +/// println!(); +/// println!("Hello World"); +/// println!("Hello {}", "World"); +/// ``` +#[macro_export] +macro_rules! println { + () => ($crate::print!("\n")); + ($($arg:tt)*) => ($crate::helpers::_print(core::format_args!("{}{}", core::format_args!($($arg)*), "\n"))); +} diff --git a/uefi/src/lib.rs b/uefi/src/lib.rs index 78908dc7a..4672ad43b 100644 --- a/uefi/src/lib.rs +++ b/uefi/src/lib.rs @@ -127,7 +127,6 @@ pub mod helpers; mod util; - #[cfg(test)] // Crates that create procedural macros can't unit test the macros they export. // Therefore, we do some tests here. From 8faad8b949e972b0eec5ad97fb2fa8488c106841 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 13 Apr 2024 11:40:16 +0200 Subject: [PATCH 4/7] clippy: code improvement --- uefi/src/table/boot.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/uefi/src/table/boot.rs b/uefi/src/table/boot.rs index abf4dca2b..ea6b1e63f 100644 --- a/uefi/src/table/boot.rs +++ b/uefi/src/table/boot.rs @@ -1942,7 +1942,7 @@ pub struct ProtocolSearchKey(NonNull); #[cfg(test)] mod tests { - use core::mem::size_of; + use core::mem::{size_of, size_of_val}; use crate::table::boot::{MemoryAttribute, MemoryMap, MemoryType}; @@ -1952,8 +1952,9 @@ mod tests { let desc_count = buffer.len(); let byte_buffer = { - let size = desc_count * size_of::(); - unsafe { core::slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, size) } + unsafe { + core::slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, size_of_val(buffer)) + } }; MemoryMap::from_raw(byte_buffer, size_of::()) From fb86b5a00fe9925d6e21e8de3247b956ab25810b Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 13 Apr 2024 11:59:11 +0200 Subject: [PATCH 5/7] doc: update READMEs and CHANGELOGs --- README.md | 10 ++++++---- uefi/CHANGELOG.md | 5 +++++ uefi/Cargo.toml | 2 +- uefi/README.md | 38 +++++++++++++------------------------- uefi/src/lib.rs | 18 +++++++++++++----- uefi/src/table/system.rs | 1 - 6 files changed, 38 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 5700e1a01..434172832 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,10 @@ This repository provides various crates: - `uefi-raw`: Raw Rust UEFI bindings for basic structures and functions. - `uefi`: High-level wrapper around various low-level UEFI APIs. \ - Most probably, you want to use this crate. -- `uefi-services`: Optional Rust convenience with a global allocator and a - `log`-based logger implementation. + Offers various optional features for typical Rust convenience, such as a + Logger and an Allocator. (_This is what you are usually looking for!_) +- `uefi-macros`: Helper macros. Used by `uefi`. + You can use the abstractions for example to: @@ -44,6 +45,8 @@ our Rust library. ## User Documentation + + For a quick start, please check out [the UEFI application template](template). The [uefi-rs book] contains a tutorial, how-tos, and overviews of some important @@ -53,7 +56,6 @@ UEFI concepts. Reference documentation for the various crates can be found on - [docs.rs/uefi](https://docs.rs/uefi) - [docs.rs/uefi-macros](https://docs.rs/uefi-macros) - [docs.rs/uefi-raw](https://docs.rs/uefi-raw) -- [docs.rs/uefi-services](https://docs.rs/uefi-services) For additional information, refer to the [UEFI specification][spec]. diff --git a/uefi/CHANGELOG.md b/uefi/CHANGELOG.md index 2e68cd714..6d221044c 100644 --- a/uefi/CHANGELOG.md +++ b/uefi/CHANGELOG.md @@ -4,6 +4,11 @@ - Added `Timestamp` protocol. - Added `UnalignedSlice::as_ptr`. - Added common derives for `Event` and `Handle`. +- `uefi::helpers::init` with the functionality that used to be in + `uefi::services`. With that, new features were added: + - `global_allocator` + - `panic_handler` + - `qemu` # uefi - 0.27.0 (2024-03-17) diff --git a/uefi/Cargo.toml b/uefi/Cargo.toml index 0511dc5aa..9e2fe0e6f 100644 --- a/uefi/Cargo.toml +++ b/uefi/Cargo.toml @@ -27,7 +27,7 @@ panic_handler = [] # were observed on the VirtualBox UEFI implementation (see uefi-rs#121). # In those cases, this feature can be excluded by removing the default features. panic-on-logger-errors = [] -qemu = ["dep:qemu-exit"] +qemu = ["dep:qemu-exit", "panic_handler"] # panic_handler: logical, not technical dependency [dependencies] bitflags.workspace = true diff --git a/uefi/README.md b/uefi/README.md index 51477f4fc..625a39363 100644 --- a/uefi/README.md +++ b/uefi/README.md @@ -2,47 +2,35 @@ [![Crates.io](https://img.shields.io/crates/v/uefi)](https://crates.io/crates/uefi) [![Docs.rs](https://docs.rs/uefi/badge.svg)](https://docs.rs/uefi) -![Stars](https://img.shields.io/github/stars/rust-osdev/uefi-rs) ![License](https://img.shields.io/github/license/rust-osdev/uefi-rs) ![Build status](https://github.com/rust-osdev/uefi-rs/workflows/Rust/badge.svg) +![Stars](https://img.shields.io/github/stars/rust-osdev/uefi-rs) -[UEFI] is the successor to the BIOS. It provides an early boot environment for -OS loaders, hypervisors and other low-level applications. - -The `uefi` crate makes it easy to: -- Write UEFI applications in Rust (for `i686`, `x86_64`, or `aarch64`) -- Call UEFI functions from an OS (usually built with a [custom target][rustc-custom]) - -The objective is to provide **safe** and **performant** wrappers for UEFI interfaces, -and allow developers to write idiomatic Rust code. -Check out the [UEFI application template] for a quick start. +For an introduction to the `uefi-rs` project and documentation, please refer to +our main [README]. -[UEFI]: https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface -[rustc-custom]: https://doc.rust-lang.org/rustc/targets/custom.html -[UEFI application template]: https://github.com/rust-osdev/uefi-rs/tree/HEAD/template +[README]: https://github.com/rust-osdev/uefi-rs/blob/main/README.md ## Optional features This crate's features are described in [`src/lib.rs`]. -See also the [`uefi-services`] crate, which provides a panic handler and -initializes the `global_allocator` and `logger` features. - [`src/lib.rs`]: src/lib.rs -[`uefi-services`]: https://crates.io/crates/uefi-services -## Documentation +## User Documentation + + -The [uefi-rs book] contains a tutorial, how-tos, and overviews of some -important UEFI concepts. +For a quick start, please check out [the UEFI application template](template). + +The [uefi-rs book] contains a tutorial, how-tos, and overviews of some important +UEFI concepts. Reference documentation for the various crates can be found on +[docs.rs]: -Reference documentation can be found on docs.rs: - [docs.rs/uefi](https://docs.rs/uefi) - [docs.rs/uefi-macros](https://docs.rs/uefi-macros) -- [docs.rs/uefi-services](https://docs.rs/uefi-services) - -For additional information, refer to the [UEFI specification][spec]. +- [docs.rs/uefi-raw](https://docs.rs/uefi-raw) [spec]: http://www.uefi.org/specifications [uefi-rs book]: https://rust-osdev.github.io/uefi-rs/HEAD diff --git a/uefi/src/lib.rs b/uefi/src/lib.rs index 4672ad43b..d715bfb55 100644 --- a/uefi/src/lib.rs +++ b/uefi/src/lib.rs @@ -7,6 +7,12 @@ //! Feel free to file bug reports and questions in our [issue tracker], and [PR //! contributions][contributing] are also welcome! //! +//! # Interaction with uefi services +//! +//! With this crate you can write code for the pre- and post-exit boot services +//! epochs. However, the `uefi` crate unfolds its true potential when +//! interacting with UEFI boot services. +//! //! # Crate organisation //! //! The top-level module contains some of the most used types and macros, @@ -51,24 +57,26 @@ //! - `logger`: Logging implementation for the standard [`log`] crate //! that prints output to the UEFI console. No buffering is done; this //! is not a high-performance logger. +//! - `panic_handler`: Add a default panic handler that logs to `stdout`. //! - `panic-on-logger-errors` (enabled by default): Panic if a text //! output error occurs in the logger. //! - `unstable`: Enable functionality that depends on [unstable //! features] in the nightly compiler. //! As example, in conjunction with the `alloc`-feature, this gate allows //! the `allocator_api` on certain functions. +//! - `qemu`: Enable some code paths to adapt their execution when executed +//! in QEMU, such as using the special `qemu-exit` device when the panic +//! handler is called. //! -//! The `global_allocator` and `logger` features require special -//! handling to perform initialization and tear-down. The -//! [`uefi-services`] crate provides an `init` method that takes care of -//! this. +//! Some of these features, such as the `logger` or `panic_handler` features, +//! only unfold their potential when you invoke `uefi::helpers::init` as soon +//! as possible in your application. //! //! [Rust UEFI Book]: https://rust-osdev.github.io/uefi-rs/HEAD/ //! [UEFI]: https://uefi.org/ //! [`BootServices`]: table::boot::BootServices //! [`GlobalAlloc`]: alloc::alloc::GlobalAlloc //! [`SystemTable`]: table::SystemTable -//! [`uefi-services`]: https://crates.io/crates/uefi-services //! [`unsafe_protocol`]: proto::unsafe_protocol //! [contributing]: https://github.com/rust-osdev/uefi-rs/blob/main/CONTRIBUTING.md //! [issue tracker]: https://github.com/rust-osdev/uefi-rs/issues diff --git a/uefi/src/table/system.rs b/uefi/src/table/system.rs index 88f9a5296..d79770490 100644 --- a/uefi/src/table/system.rs +++ b/uefi/src/table/system.rs @@ -225,7 +225,6 @@ impl SystemTable { /// /// [`allocator::exit_boot_services`]: crate::allocator::exit_boot_services /// [`Logger::disable`]: crate::logger::Logger::disable - /// [`uefi_services::init`]: https://docs.rs/uefi-services/latest/uefi_services/fn.init.html #[must_use] pub fn exit_boot_services( self, From 01b8f54fdaddf7f5130b3f236edfa0967248db3f Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sat, 13 Apr 2024 12:04:35 +0200 Subject: [PATCH 6/7] uefi::helpers: remove exit hook No need for the complicated and error-prone approach using an event. --- uefi-services/src/lib.rs | 2 +- uefi/src/helpers/mod.rs | 25 ++++++------------------- uefi/src/table/system.rs | 15 ++++++--------- 3 files changed, 13 insertions(+), 29 deletions(-) diff --git a/uefi-services/src/lib.rs b/uefi-services/src/lib.rs index a598b446a..3b73e4c16 100644 --- a/uefi-services/src/lib.rs +++ b/uefi-services/src/lib.rs @@ -10,7 +10,7 @@ pub use uefi::{print, println}; /// Deprecated. Use [`uefi::helpers::init`] instead. #[deprecated = "WARNING: `uefi-services` is deprecated. Functionality was moved to `uefi::helpers::init`."] pub fn init(st: &mut SystemTable) -> Result> { - uefi::helpers::init(st) + uefi::helpers::init(st).map(|_| None) } /// Deprecated. Use [`uefi::helpers::system_table`] instead. diff --git a/uefi/src/helpers/mod.rs b/uefi/src/helpers/mod.rs index d16b9c6fc..0f31f765a 100644 --- a/uefi/src/helpers/mod.rs +++ b/uefi/src/helpers/mod.rs @@ -16,16 +16,13 @@ //! [println_macro]: uefi::println! use crate::prelude::{Boot, SystemTable}; -use crate::Event; use crate::Result; use crate::StatusExt; use core::ffi::c_void; use core::ptr; -use core::ptr::NonNull; use core::sync::atomic::{AtomicPtr, Ordering}; #[doc(hidden)] pub use println::_print; -use uefi_raw::table::boot::{EventType, Tpl}; use uefi_raw::Status; #[cfg(feature = "global_allocator")] @@ -77,10 +74,10 @@ pub fn system_table() -> SystemTable { /// /// **PLEASE NOTE** that these helpers are meant for the pre exit boot service /// epoch. -pub fn init(st: &mut SystemTable) -> Result> { +pub fn init(st: &mut SystemTable) -> Result<()> { if system_table_opt().is_some() { // Avoid double initialization. - return Status::SUCCESS.to_result_with_val(|| None); + return Status::SUCCESS.to_result_with_val(|| ()); } // Setup the system table singleton @@ -92,24 +89,14 @@ pub fn init(st: &mut SystemTable) -> Result> { #[cfg(feature = "logger")] logger::init(st); + #[cfg(feature = "global_allocator")] uefi::allocator::init(st); - - // Schedule these tools to be disabled on exit from UEFI boot services - let boot_services = st.boot_services(); - boot_services - .create_event( - EventType::SIGNAL_EXIT_BOOT_SERVICES, - Tpl::NOTIFY, - Some(exit_boot_services), - None, - ) - .map(Some) } + + Ok(()) } -/// Notify the utility library that boot services are not safe to call anymore -/// As this is a callback, it must be `extern "efiapi"`. -unsafe extern "efiapi" fn exit_boot_services(_e: Event, _ctx: Option>) { +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 diff --git a/uefi/src/table/system.rs b/uefi/src/table/system.rs index d79770490..d9cdec170 100644 --- a/uefi/src/table/system.rs +++ b/uefi/src/table/system.rs @@ -206,12 +206,10 @@ impl SystemTable { /// live until the program ends. The lifetime of the memory map is therefore /// `'static`. /// - /// Once boot services are exited, the logger and allocator provided by - /// this crate can no longer be used. The logger should be disabled using - /// the [`Logger::disable`] method, and the allocator should be disabled by - /// calling [`allocator::exit_boot_services`]. Note that if the logger and - /// allocator were initialized with [`uefi_services::init`], they will be - /// disabled automatically when `exit_boot_services` is called. + /// Note that once the boot services are exited, associated loggers and + /// allocators can't use the boot services anymore. For the corresponding + /// abstractions provided by this crate, invoking this function will + /// automatically disable them. /// /// # Errors /// @@ -222,14 +220,13 @@ impl SystemTable { /// All errors are treated as unrecoverable because the system is /// now in an undefined state. Rather than returning control to the /// caller, the system will be reset. - /// - /// [`allocator::exit_boot_services`]: crate::allocator::exit_boot_services - /// [`Logger::disable`]: crate::logger::Logger::disable #[must_use] pub fn exit_boot_services( self, memory_type: MemoryType, ) -> (SystemTable, MemoryMap<'static>) { + crate::helpers::exit(); + let boot_services = self.boot_services(); // Reboot the device. From 9a257a059541ab58dc3c54db094e68b73dead527 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Sun, 14 Apr 2024 11:26:00 +0200 Subject: [PATCH 7/7] template: remove from uefi-services --- Cargo.lock | 1 - template/Cargo.toml | 1 - template/src/main.rs | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7c3b3eaa..919b39e48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -904,7 +904,6 @@ name = "uefi_app" version = "0.1.0" dependencies = [ "uefi", - "uefi-services", ] [[package]] diff --git a/template/Cargo.toml b/template/Cargo.toml index fd2d904a7..527713ab8 100644 --- a/template/Cargo.toml +++ b/template/Cargo.toml @@ -6,4 +6,3 @@ publish = false [dependencies] uefi = { version = "0.27.0", features = ["alloc"] } -uefi-services = "0.24.0" diff --git a/template/src/main.rs b/template/src/main.rs index e89e4bfe0..64ae41733 100644 --- a/template/src/main.rs +++ b/template/src/main.rs @@ -5,7 +5,7 @@ use uefi::prelude::*; #[entry] fn main(_handle: Handle, mut system_table: SystemTable) -> Status { - uefi_services::init(&mut system_table).unwrap(); + uefi::helpers::init(&mut system_table).unwrap(); Status::SUCCESS }