Skip to content

Commit

Permalink
Merge pull request #1156 from nicholasbishop/bishop-global-tables-simple
Browse files Browse the repository at this point in the history
Add basic API for a global system table
  • Loading branch information
phip1611 authored May 23, 2024
2 parents 65a215d + c04e423 commit 887cd56
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 16 deletions.
3 changes: 3 additions & 0 deletions uefi-macros/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# uefi-macros - [Unreleased]

## Changed
- The `entry` macro now sets the global system table pointer with `uefi::set_system_table`.

## Removed
- Removed the `cstr8` and `cstr16` macros. Use the declarative macros of the
same names exported by the `uefi` crate as a replacement.
Expand Down
1 change: 1 addition & 0 deletions uefi-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
parse_quote! {
unsafe {
#system_table_ident.boot_services().set_image_handle(#image_handle_ident);
::uefi::table::set_system_table(#system_table_ident.as_ptr().cast());
}
},
);
Expand Down
2 changes: 1 addition & 1 deletion uefi-test-runner/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ fn shutdown(mut st: SystemTable<Boot>) -> ! {
info!("Testing complete, exiting boot services...");

// Exit boot services as a proof that it works :)
let (st, mmap) = st.exit_boot_services(MemoryType::LOADER_DATA);
let (st, mmap) = unsafe { st.exit_boot_services(MemoryType::LOADER_DATA) };

info!("Memory Map:");
for desc in mmap.entries() {
Expand Down
7 changes: 7 additions & 0 deletions uefi/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
works on x86. It is activated by default (only on x86) and can be deactivated
by removing the `log-debugcon` cargo feature. The major benefit is that one
can get log messages even after one exited the boot services.
- Added `table::{set_system_table, system_table_boot, system_table_runtime}`.
This provides an initial API for global tables that do not require passing
around a reference.

## Changed
- `SystemTable::exit_boot_services` is now `unsafe`. See that method's
documentation for details of obligations for callers.

## Removed
- Removed the `panic-on-logger-errors` feature of the `uefi` crate. Logger
Expand Down
87 changes: 75 additions & 12 deletions uefi/src/table/mod.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,84 @@
//! Standard UEFI tables.

/// Common trait implemented by all standard UEFI tables.
pub trait Table {
/// A unique number assigned by the UEFI specification
/// to the standard tables.
const SIGNATURE: u64;
}
pub mod boot;
pub mod cfg;
pub mod runtime;

mod header;
pub use header::Header;

mod system;

pub use header::Header;
pub use system::{Boot, Runtime, SystemTable};
pub use uefi_raw::table::Revision;

pub mod boot;
pub mod runtime;
use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};

pub mod cfg;
/// Global system table pointer. This is only modified by [`set_system_table`].
static SYSTEM_TABLE: AtomicPtr<uefi_raw::table::system::SystemTable> =
AtomicPtr::new(ptr::null_mut());

pub use uefi_raw::table::Revision;
/// Update the global system table pointer.
///
/// This is called automatically in the `main` entry point as part of
/// [`uefi::entry`]. It should not be called at any other point in time, unless
/// the executable does not use [`uefi::entry`], in which case it should be
/// called once before calling any other API in this crate.
///
/// # Safety
///
/// This function should only be called as described above, and the
/// `ptr` must be a valid [`SystemTable`].
pub unsafe fn set_system_table(ptr: *const uefi_raw::table::system::SystemTable) {
SYSTEM_TABLE.store(ptr.cast_mut(), Ordering::Release);
}

/// Get the system table while boot services are active.
///
/// # Panics
///
/// Panics if the system table has not been set with `set_system_table`, or if
/// boot services are not available (e.g. if [`exit_boot_services`] has been
/// called).
///
/// [`exit_boot_services`]: SystemTable::exit_boot_services
pub fn system_table_boot() -> SystemTable<Boot> {
let st = SYSTEM_TABLE.load(Ordering::Acquire);
assert!(!st.is_null());

// SAFETY: the system table is valid per the requirements of `set_system_table`.
unsafe {
if (*st).boot_services.is_null() {
panic!("boot services are not active");
}

SystemTable::<Boot>::from_ptr(st.cast()).unwrap()
}
}

/// Get the system table while runtime services are active.
///
/// # Panics
///
/// Panics if the system table has not been set with `set_system_table`, or if
/// runtime services are not available.
pub fn system_table_runtime() -> SystemTable<Runtime> {
let st = SYSTEM_TABLE.load(Ordering::Acquire);
assert!(!st.is_null());

// SAFETY: the system table is valid per the requirements of `set_system_table`.
unsafe {
if (*st).runtime_services.is_null() {
panic!("runtime services are not active");
}

SystemTable::<Runtime>::from_ptr(st.cast()).unwrap()
}
}

/// Common trait implemented by all standard UEFI tables.
pub trait Table {
/// A unique number assigned by the UEFI specification
/// to the standard tables.
const SIGNATURE: u64;
}
29 changes: 26 additions & 3 deletions uefi/src/table/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,27 @@ impl SystemTable<Boot> {
///
/// 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.
/// abstractions provided by this crate (see the [`helpers`] module),
/// invoking this function will automatically disable them. If the
/// `global_allocator` feature is enabled, attempting to use the allocator
/// after exiting boot services will panic.
///
/// # Safety
///
/// The caller is responsible for ensuring that no references to
/// boot-services data remain. A non-exhaustive list of resources to check:
///
/// * All protocols will be invalid after exiting boot services. This
/// includes the [`Output`] protocols attached to stdout/stderr. The
/// caller must ensure that no protocol references remain.
/// * The pool allocator is not usable after exiting boot services. Types
/// such as [`PoolString`] which call [`BootServices::free_pool`] on drop
/// must be cleaned up before calling `exit_boot_services`, or leaked to
/// avoid drop ever being called.
/// * All data in the memory map marked as
/// [`MemoryType::BOOT_SERVICES_CODE`] and
/// [`MemoryType::BOOT_SERVICES_DATA`] will become free memory, the caller
/// must ensure that no references to such memory exist.
///
/// # Errors
///
Expand All @@ -220,8 +239,12 @@ impl SystemTable<Boot> {
/// 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.
///
/// [`helpers`]: crate::helpers
/// [`Output`]: crate::proto::console::text::Output
/// [`PoolString`]: crate::proto::device_path::text::PoolString
#[must_use]
pub fn exit_boot_services(
pub unsafe fn exit_boot_services(
self,
memory_type: MemoryType,
) -> (SystemTable<Runtime>, MemoryMap<'static>) {
Expand Down

0 comments on commit 887cd56

Please sign in to comment.