Skip to content

Commit 7a0a8ee

Browse files
authored
Merge pull request #1237 from nicholasbishop/bishop-system-freestanding
Add uefi::system module
2 parents 611202b + 46e47c7 commit 7a0a8ee

File tree

6 files changed

+191
-3
lines changed

6 files changed

+191
-3
lines changed

uefi-test-runner/src/main.rs

+25-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use uefi::proto::console::serial::Serial;
1313
use uefi::proto::device_path::build::{self, DevicePathBuilder};
1414
use uefi::proto::device_path::messaging::Vendor;
1515
use uefi::table::boot::{MemoryMap, MemoryType};
16-
use uefi::{print, println, Result};
16+
use uefi::{print, println, system, Result};
1717

1818
mod boot;
1919
mod fs;
@@ -45,6 +45,9 @@ fn efi_main(image: Handle, mut st: SystemTable<Boot>) -> Status {
4545
// Ensure the tests are run on a version of UEFI we support.
4646
check_revision(st.uefi_revision());
4747

48+
// Check the `uefi::system` module.
49+
check_system(&st);
50+
4851
// Test all the boot services.
4952
let bt = st.boot_services();
5053

@@ -67,6 +70,8 @@ fn efi_main(image: Handle, mut st: SystemTable<Boot>) -> Status {
6770
}
6871

6972
fn check_revision(rev: uefi::table::Revision) {
73+
assert_eq!(system::uefi_revision(), rev);
74+
7075
let (major, minor) = (rev.major(), rev.minor());
7176

7277
info!("UEFI {}.{}", major, minor / 10);
@@ -78,6 +83,25 @@ fn check_revision(rev: uefi::table::Revision) {
7883
);
7984
}
8085

86+
fn check_system(st: &SystemTable<Boot>) {
87+
assert_eq!(system::firmware_vendor(), cstr16!("EDK II"));
88+
check_revision(system::uefi_revision());
89+
90+
assert_eq!(system::firmware_revision(), st.firmware_revision());
91+
system::with_config_table(|t| assert_eq!(t, st.config_table()));
92+
93+
system::with_stdout(|stdout| {
94+
stdout
95+
.output_string(cstr16!("test system::with_stdout\n"))
96+
.unwrap()
97+
});
98+
system::with_stderr(|stdout| {
99+
stdout
100+
.output_string(cstr16!("test system::with_stderr\n"))
101+
.unwrap()
102+
});
103+
}
104+
81105
#[derive(Clone, Copy, Debug)]
82106
enum HostRequest {
83107
/// Tell the host to take a screenshot and compare against the

uefi/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# uefi - [Unreleased]
22

3+
## Added
4+
- `uefi::system` is a new module that provides freestanding functions for
5+
accessing fields of the global system table.
6+
- Add standard derives for `ConfigTableEntry`.
7+
38
## Changed
49
- **Breaking:** `uefi::helpers::init` no longer takes an argument.
510
- The lifetime of the `SearchType` returned from

uefi/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ pub use uguid::guid;
119119
mod result;
120120
pub use result::{Error, Result, ResultExt, Status, StatusExt};
121121

122+
pub mod system;
122123
pub mod table;
123124

124125
pub mod proto;

uefi/src/system.rs

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
//! Functions for accessing fields of the system table.
2+
//!
3+
//! Some of these functions use a callback argument rather than returning a
4+
//! reference to the field directly. This pattern is used because some fields
5+
//! are allowed to change, and so a static lifetime cannot be used.
6+
//!
7+
//! Some functions can only be called while boot services are active, and will
8+
//! panic otherwise. See each function's documentation for details.
9+
10+
use crate::proto::console::text::{Input, Output};
11+
use crate::table::cfg::ConfigTableEntry;
12+
use crate::table::{self, Revision};
13+
use crate::{CStr16, Char16};
14+
use core::slice;
15+
16+
/// Get the firmware vendor string.
17+
#[must_use]
18+
pub fn firmware_vendor() -> &'static CStr16 {
19+
let st = table::system_table_raw_panicking();
20+
// SAFETY: valid per requirements of `set_system_table`.
21+
let st = unsafe { st.as_ref() };
22+
23+
let vendor: *const Char16 = st.firmware_vendor.cast();
24+
25+
// SAFETY: this assumes that the firmware vendor string is never mutated or freed.
26+
unsafe { CStr16::from_ptr(vendor) }
27+
}
28+
29+
/// Get the firmware revision.
30+
#[must_use]
31+
pub fn firmware_revision() -> u32 {
32+
let st = table::system_table_raw_panicking();
33+
// SAFETY: valid per requirements of `set_system_table`.
34+
let st = unsafe { st.as_ref() };
35+
36+
st.firmware_revision
37+
}
38+
39+
/// Get the revision of the system table, which is defined to be the revision of
40+
/// the UEFI specification implemented by the firmware.
41+
#[must_use]
42+
pub fn uefi_revision() -> Revision {
43+
let st = table::system_table_raw_panicking();
44+
// SAFETY: valid per requirements of `set_system_table`.
45+
let st = unsafe { st.as_ref() };
46+
47+
st.header.revision
48+
}
49+
50+
/// Call `f` with a slice of [`ConfigTableEntry`]. Each entry provides access to
51+
/// a vendor-specific table.
52+
pub fn with_config_table<F, R>(f: F) -> R
53+
where
54+
F: Fn(&[ConfigTableEntry]) -> R,
55+
{
56+
let st = table::system_table_raw_panicking();
57+
// SAFETY: valid per requirements of `set_system_table`.
58+
let st = unsafe { st.as_ref() };
59+
60+
let ptr: *const ConfigTableEntry = st.configuration_table.cast();
61+
let len = st.number_of_configuration_table_entries;
62+
let slice = if ptr.is_null() {
63+
&[]
64+
} else {
65+
unsafe { slice::from_raw_parts(ptr, len) }
66+
};
67+
f(slice)
68+
}
69+
70+
/// Call `f` with the [`Input`] protocol attached to stdin.
71+
///
72+
/// # Panics
73+
///
74+
/// This function will panic if called after exiting boot services, or if stdin
75+
/// is not available.
76+
pub fn with_stdin<F, R>(f: F) -> R
77+
where
78+
F: Fn(&mut Input) -> R,
79+
{
80+
let st = table::system_table_raw_panicking();
81+
// SAFETY: valid per requirements of `set_system_table`.
82+
let st = unsafe { st.as_ref() };
83+
// The I/O protocols cannot be used after exiting boot services.
84+
assert!(!st.boot_services.is_null(), "boot services are not active");
85+
assert!(!st.stdin.is_null(), "stdin is not available");
86+
87+
let stdin: *mut Input = st.stdin.cast();
88+
89+
// SAFETY: `Input` is a `repr(transparent)` wrapper around the raw input
90+
// type. The underlying pointer in the system table is assumed to be valid.
91+
let stdin = unsafe { &mut *stdin };
92+
93+
f(stdin)
94+
}
95+
96+
/// Call `f` with the [`Output`] protocol attached to stdout.
97+
///
98+
/// # Panics
99+
///
100+
/// This function will panic if called after exiting boot services, or if stdout
101+
/// is not available.
102+
pub fn with_stdout<F, R>(f: F) -> R
103+
where
104+
F: Fn(&mut Output) -> R,
105+
{
106+
let st = table::system_table_raw_panicking();
107+
// SAFETY: valid per requirements of `set_system_table`.
108+
let st = unsafe { st.as_ref() };
109+
// The I/O protocols cannot be used after exiting boot services.
110+
assert!(!st.boot_services.is_null(), "boot services are not active");
111+
assert!(!st.stdout.is_null(), "stdout is not available");
112+
113+
let stdout: *mut Output = st.stdout.cast();
114+
115+
// SAFETY: `Output` is a `repr(transparent)` wrapper around the raw output
116+
// type. The underlying pointer in the system table is assumed to be valid.
117+
let stdout = unsafe { &mut *stdout };
118+
119+
f(stdout)
120+
}
121+
122+
/// Call `f` with the [`Output`] protocol attached to stderr.
123+
///
124+
/// # Panics
125+
///
126+
/// This function will panic if called after exiting boot services, or if stderr
127+
/// is not available.
128+
pub fn with_stderr<F, R>(f: F) -> R
129+
where
130+
F: Fn(&mut Output) -> R,
131+
{
132+
let st = table::system_table_raw_panicking();
133+
// SAFETY: valid per requirements of `set_system_table`.
134+
let st = unsafe { st.as_ref() };
135+
// The I/O protocols cannot be used after exiting boot services.
136+
assert!(!st.boot_services.is_null(), "boot services are not active");
137+
assert!(!st.stderr.is_null(), "stderr is not available");
138+
139+
let stderr: *mut Output = st.stderr.cast();
140+
141+
// SAFETY: `Output` is a `repr(transparent)` wrapper around the raw output
142+
// type. The underlying pointer in the system table is assumed to be valid.
143+
let stderr = unsafe { &mut *stderr };
144+
145+
f(stderr)
146+
}

uefi/src/table/cfg.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use core::ffi::c_void;
1414
/// Contains a set of GUID / pointer for a vendor-specific table.
1515
///
1616
/// The UEFI standard guarantees each entry is unique.
17-
#[derive(Debug)]
17+
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
1818
#[repr(C)]
1919
pub struct ConfigTableEntry {
2020
/// The GUID identifying this table.

uefi/src/table/mod.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,25 @@ pub use header::Header;
1111
pub use system::{Boot, Runtime, SystemTable};
1212
pub use uefi_raw::table::Revision;
1313

14-
use core::ptr;
14+
use core::ptr::{self, NonNull};
1515
use core::sync::atomic::{AtomicPtr, Ordering};
1616

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

21+
/// Get the raw system table pointer. This may only be called after
22+
/// `set_system_table` has been used to set the global pointer.
23+
///
24+
/// # Panics
25+
///
26+
/// Panics if the global system table pointer is null.
27+
#[track_caller]
28+
pub(crate) fn system_table_raw_panicking() -> NonNull<uefi_raw::table::system::SystemTable> {
29+
let ptr = SYSTEM_TABLE.load(Ordering::Acquire);
30+
NonNull::new(ptr).expect("global system table pointer is not set")
31+
}
32+
2133
/// Update the global system table pointer.
2234
///
2335
/// This is called automatically in the `main` entry point as part of

0 commit comments

Comments
 (0)