|
1 |
| -//! This crate simplifies the writing of higher-level code for UEFI. |
2 |
| -//! |
3 |
| -//! It initializes the memory allocation and logging crates, |
4 |
| -//! allowing code to use Rust's data structures and to log errors. |
5 |
| -//! |
6 |
| -//! Logging and allocation are only allowed while boot services are |
7 |
| -//! active. Once runtime services are activated by calling |
8 |
| -//! [`exit_boot_services`], the logger will be disabled and the |
9 |
| -//! allocator will always return null. |
10 |
| -//! |
11 |
| -//! It also stores a global reference to the UEFI system table, |
12 |
| -//! in order to reduce the redundant passing of references to it. |
13 |
| -//! |
14 |
| -//! Library code can simply use global UEFI functions |
15 |
| -//! through the reference provided by `system_table`. |
16 |
| -//! |
17 |
| -//! ## Optional crate features |
18 |
| -//! |
19 |
| -//! - `logger` (enabled by default): Initialize a global logger. |
20 |
| -//! - `panic_handler` (enabled by default): Register a panic handler. A |
21 |
| -//! panic handler must be provided for your program to compile, but |
22 |
| -//! you can choose to provide your own if you don't want to use this |
23 |
| -//! one. |
24 |
| -//! - `qemu`: On x86_64, make qemu exit with code 3 if a panic |
25 |
| -//! occurs. This feature assumes the program is running under QEMU. |
26 |
| -//! |
27 |
| -//! [`exit_boot_services`]: uefi::table::SystemTable::exit_boot_services |
28 |
| -
|
| 1 | +//! WARNING: `uefi-services` is deprecated. Functionality was moved to `uefi::helpers::init`. |
29 | 2 | #![no_std]
|
30 |
| -#![deny(clippy::must_use_candidate)] |
31 |
| -#![deny(missing_debug_implementations)] |
32 |
| - |
33 |
| -extern crate log; |
34 |
| -// Core types. |
35 |
| -extern crate uefi; |
36 |
| - |
37 |
| -use core::ffi::c_void; |
38 |
| -use core::fmt::Write; |
39 |
| -use core::ptr::{self, NonNull}; |
40 |
| -use core::sync::atomic::{AtomicPtr, Ordering}; |
41 |
| - |
42 |
| -#[cfg(feature = "panic_handler")] |
43 |
| -use cfg_if::cfg_if; |
44 |
| - |
45 |
| -use uefi::table::boot::{EventType, Tpl}; |
46 |
| -use uefi::table::{Boot, SystemTable}; |
47 |
| -use uefi::{Event, Result, Status, StatusExt}; |
48 | 3 |
|
49 |
| -/// Reference to the system table. |
50 |
| -/// |
51 |
| -/// This table is only fully safe to use until UEFI boot services have been exited. |
52 |
| -/// After that, some fields and methods are unsafe to use, see the documentation of |
53 |
| -/// UEFI's ExitBootServices entry point for more details. |
54 |
| -static SYSTEM_TABLE: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut()); |
| 4 | +use uefi::prelude::*; |
| 5 | +use uefi::Event; |
| 6 | +use uefi::Result; |
55 | 7 |
|
56 |
| -/// Global logger object |
57 |
| -#[cfg(feature = "logger")] |
58 |
| -static LOGGER: uefi::logger::Logger = uefi::logger::Logger::new(); |
59 |
| - |
60 |
| -#[must_use] |
61 |
| -fn system_table_opt() -> Option<SystemTable<Boot>> { |
62 |
| - let ptr = SYSTEM_TABLE.load(Ordering::Acquire); |
63 |
| - // Safety: the `SYSTEM_TABLE` pointer either be null or a valid system |
64 |
| - // table. |
65 |
| - // |
66 |
| - // Null is the initial value, as well as the value set when exiting boot |
67 |
| - // services. Otherwise, the value is set by the call to `init`, which |
68 |
| - // requires a valid system table reference as input. |
69 |
| - unsafe { SystemTable::from_ptr(ptr) } |
70 |
| -} |
71 |
| - |
72 |
| -/// Obtains a pointer to the system table. |
73 |
| -/// |
74 |
| -/// This is meant to be used by higher-level libraries, |
75 |
| -/// which want a convenient way to access the system table singleton. |
76 |
| -/// |
77 |
| -/// `init` must have been called first by the UEFI app. |
78 |
| -/// |
79 |
| -/// The returned pointer is only valid until boot services are exited. |
80 |
| -#[must_use] |
81 |
| -pub fn system_table() -> SystemTable<Boot> { |
82 |
| - system_table_opt().expect("The system table handle is not available") |
83 |
| -} |
| 8 | +pub use uefi::{print, println}; |
84 | 9 |
|
85 |
| -/// Initialize the UEFI utility library. |
86 |
| -/// |
87 |
| -/// This must be called as early as possible, |
88 |
| -/// before trying to use logging or memory allocation capabilities. |
| 10 | +#[deprecated = "WARNING: `uefi-services` is deprecated. Functionality was moved to `uefi::helpers::init`."] |
89 | 11 | pub fn init(st: &mut SystemTable<Boot>) -> Result<Option<Event>> {
|
90 |
| - if system_table_opt().is_some() { |
91 |
| - // Avoid double initialization. |
92 |
| - return Status::SUCCESS.to_result_with_val(|| None); |
93 |
| - } |
94 |
| - |
95 |
| - // Setup the system table singleton |
96 |
| - SYSTEM_TABLE.store(st.as_ptr().cast_mut(), Ordering::Release); |
97 |
| - |
98 |
| - unsafe { |
99 |
| - // Setup logging and memory allocation |
100 |
| - |
101 |
| - #[cfg(feature = "logger")] |
102 |
| - init_logger(st); |
103 |
| - |
104 |
| - uefi::allocator::init(st); |
105 |
| - |
106 |
| - // Schedule these tools to be disabled on exit from UEFI boot services |
107 |
| - let boot_services = st.boot_services(); |
108 |
| - boot_services |
109 |
| - .create_event( |
110 |
| - EventType::SIGNAL_EXIT_BOOT_SERVICES, |
111 |
| - Tpl::NOTIFY, |
112 |
| - Some(exit_boot_services), |
113 |
| - None, |
114 |
| - ) |
115 |
| - .map(Some) |
116 |
| - } |
117 |
| -} |
118 |
| - |
119 |
| -// Internal function for print macros. |
120 |
| -#[doc(hidden)] |
121 |
| -pub fn _print(args: core::fmt::Arguments) { |
122 |
| - system_table() |
123 |
| - .stdout() |
124 |
| - .write_fmt(args) |
125 |
| - .expect("Failed to write to stdout"); |
126 |
| -} |
127 |
| - |
128 |
| -/// Prints to the standard output. |
129 |
| -/// |
130 |
| -/// # Panics |
131 |
| -/// Will panic if `SYSTEM_TABLE` is `None` (Before [init()] and after [uefi::prelude::SystemTable::exit_boot_services()]). |
132 |
| -/// |
133 |
| -/// # Examples |
134 |
| -/// ``` |
135 |
| -/// print!(""); |
136 |
| -/// print!("Hello World\n"); |
137 |
| -/// print!("Hello {}", "World"); |
138 |
| -/// ``` |
139 |
| -#[macro_export] |
140 |
| -macro_rules! print { |
141 |
| - ($($arg:tt)*) => ($crate::_print(core::format_args!($($arg)*))); |
142 |
| -} |
143 |
| - |
144 |
| -/// Prints to the standard output, with a newline. |
145 |
| -/// |
146 |
| -/// # Panics |
147 |
| -/// Will panic if `SYSTEM_TABLE` is `None` (Before [init()] and after [uefi::prelude::SystemTable::exit_boot_services()]). |
148 |
| -/// |
149 |
| -/// # Examples |
150 |
| -/// ``` |
151 |
| -/// println!(); |
152 |
| -/// println!("Hello World"); |
153 |
| -/// println!("Hello {}", "World"); |
154 |
| -/// ``` |
155 |
| -#[macro_export] |
156 |
| -macro_rules! println { |
157 |
| - () => ($crate::print!("\n")); |
158 |
| - ($($arg:tt)*) => ($crate::_print(core::format_args!("{}{}", core::format_args!($($arg)*), "\n"))); |
159 |
| -} |
160 |
| - |
161 |
| -/// Set up logging |
162 |
| -/// |
163 |
| -/// This is unsafe because you must arrange for the logger to be reset with |
164 |
| -/// disable() on exit from UEFI boot services. |
165 |
| -#[cfg(feature = "logger")] |
166 |
| -unsafe fn init_logger(st: &mut SystemTable<Boot>) { |
167 |
| - // Connect the logger to stdout. |
168 |
| - LOGGER.set_output(st.stdout()); |
169 |
| - |
170 |
| - // Set the logger. |
171 |
| - log::set_logger(&LOGGER).unwrap(); // Can only fail if already initialized. |
172 |
| - |
173 |
| - // Set logger max level to level specified by log features |
174 |
| - log::set_max_level(log::STATIC_MAX_LEVEL); |
175 |
| -} |
176 |
| - |
177 |
| -/// Notify the utility library that boot services are not safe to call anymore |
178 |
| -/// As this is a callback, it must be `extern "efiapi"`. |
179 |
| -unsafe extern "efiapi" fn exit_boot_services(_e: Event, _ctx: Option<NonNull<c_void>>) { |
180 |
| - // DEBUG: The UEFI spec does not guarantee that this printout will work, as |
181 |
| - // the services used by logging might already have been shut down. |
182 |
| - // But it works on current OVMF, and can be used as a handy way to |
183 |
| - // check that the callback does get called. |
184 |
| - // |
185 |
| - // info!("Shutting down the UEFI utility library"); |
186 |
| - SYSTEM_TABLE.store(ptr::null_mut(), Ordering::Release); |
187 |
| - |
188 |
| - #[cfg(feature = "logger")] |
189 |
| - LOGGER.disable(); |
190 |
| - |
191 |
| - uefi::allocator::exit_boot_services(); |
192 |
| -} |
193 |
| - |
194 |
| -#[cfg(feature = "panic_handler")] |
195 |
| -#[panic_handler] |
196 |
| -fn panic_handler(info: &core::panic::PanicInfo) -> ! { |
197 |
| - println!("[PANIC]: {}", info); |
198 |
| - |
199 |
| - // Give the user some time to read the message |
200 |
| - if let Some(st) = system_table_opt() { |
201 |
| - st.boot_services().stall(10_000_000); |
202 |
| - } else { |
203 |
| - let mut dummy = 0u64; |
204 |
| - // FIXME: May need different counter values in debug & release builds |
205 |
| - for i in 0..300_000_000 { |
206 |
| - unsafe { |
207 |
| - core::ptr::write_volatile(&mut dummy, i); |
208 |
| - } |
209 |
| - } |
210 |
| - } |
211 |
| - |
212 |
| - cfg_if! { |
213 |
| - if #[cfg(all(target_arch = "x86_64", feature = "qemu"))] { |
214 |
| - // If running in QEMU, use the f4 exit port to signal the error and exit |
215 |
| - use qemu_exit::QEMUExit; |
216 |
| - let custom_exit_success = 3; |
217 |
| - let qemu_exit_handle = qemu_exit::X86::new(0xF4, custom_exit_success); |
218 |
| - qemu_exit_handle.exit_failure(); |
219 |
| - } else { |
220 |
| - // If the system table is available, use UEFI's standard shutdown mechanism |
221 |
| - if let Some(st) = system_table_opt() { |
222 |
| - use uefi::table::runtime::ResetType; |
223 |
| - st.runtime_services() |
224 |
| - .reset(ResetType::SHUTDOWN, uefi::Status::ABORTED, None); |
225 |
| - } |
226 |
| - |
227 |
| - // If we don't have any shutdown mechanism handy, the best we can do is loop |
228 |
| - log::error!("Could not shut down, please power off the system manually..."); |
229 |
| - |
230 |
| - cfg_if! { |
231 |
| - if #[cfg(target_arch = "x86_64")] { |
232 |
| - loop { |
233 |
| - unsafe { |
234 |
| - // Try to at least keep CPU from running at 100% |
235 |
| - core::arch::asm!("hlt", options(nomem, nostack)); |
236 |
| - } |
237 |
| - } |
238 |
| - } else if #[cfg(target_arch = "aarch64")] { |
239 |
| - loop { |
240 |
| - unsafe { |
241 |
| - // Try to at least keep CPU from running at 100% |
242 |
| - core::arch::asm!("hlt 420", options(nomem, nostack)); |
243 |
| - } |
244 |
| - } |
245 |
| - } else { |
246 |
| - loop { |
247 |
| - // just run forever dammit how do you return never anyway |
248 |
| - } |
249 |
| - } |
250 |
| - } |
251 |
| - } |
252 |
| - } |
| 12 | + uefi::helpers::init(st) |
253 | 13 | }
|
0 commit comments