1 | use core::ffi::c_void; |
2 | use core::fmt::{Debug, Formatter}; |
3 | use core::marker::PhantomData; |
4 | use core::ptr::NonNull; |
5 | use core::{ptr, slice}; |
6 | |
7 | use crate::proto::console::text; |
8 | use crate::{CStr16, Char16, Handle, Result, Status}; |
9 | |
10 | use super::boot::{BootServices, MemoryDescriptor, MemoryMap, MemoryType}; |
11 | use super::runtime::{ResetType, RuntimeServices}; |
12 | use super::{cfg, Header, Revision}; |
13 | |
14 | /// Marker trait used to provide different views of the UEFI System Table |
15 | pub trait SystemTableView {} |
16 | |
17 | /// Marker struct associated with the boot view of the UEFI System Table |
18 | pub struct Boot; |
19 | impl SystemTableView for Boot {} |
20 | |
21 | /// Marker struct associated with the run-time view of the UEFI System Table |
22 | pub struct Runtime; |
23 | impl SystemTableView for Runtime {} |
24 | |
25 | /// UEFI System Table interface |
26 | /// |
27 | /// The UEFI System Table is the gateway to all UEFI services which an UEFI |
28 | /// application is provided access to on startup. However, not all UEFI services |
29 | /// will remain accessible forever. |
30 | /// |
31 | /// Some services, called "boot services", may only be called during a bootstrap |
32 | /// stage where the UEFI firmware still has control of the hardware, and will |
33 | /// become unavailable once the firmware hands over control of the hardware to |
34 | /// an operating system loader. Others, called "runtime services", may still be |
35 | /// used after that point, but require a rather specific CPU configuration which |
36 | /// an operating system loader is unlikely to preserve. |
37 | /// |
38 | /// We handle this state transition by providing two different views of the UEFI |
39 | /// system table, the "Boot" view and the "Runtime" view. An UEFI application |
40 | /// is initially provided with access to the "Boot" view, and may transition |
41 | /// to the "Runtime" view through the ExitBootServices mechanism that is |
42 | /// documented in the UEFI spec. At that point, the boot view of the system |
43 | /// table will be destroyed (which conveniently invalidates all references to |
44 | /// UEFI boot services in the eye of the Rust borrow checker) and a runtime view |
45 | /// will be provided to replace it. |
46 | #[repr (transparent)] |
47 | #[derive (Debug)] |
48 | pub struct SystemTable<View: SystemTableView> { |
49 | table: &'static SystemTableImpl, |
50 | _marker: PhantomData<View>, |
51 | } |
52 | |
53 | // These parts of the UEFI System Table interface will always be available |
54 | impl<View: SystemTableView> SystemTable<View> { |
55 | /// Return the firmware vendor string |
56 | #[must_use ] |
57 | pub fn firmware_vendor(&self) -> &CStr16 { |
58 | unsafe { CStr16::from_ptr(self.table.fw_vendor) } |
59 | } |
60 | |
61 | /// Return the firmware revision |
62 | #[must_use ] |
63 | pub const fn firmware_revision(&self) -> u32 { |
64 | self.table.fw_revision |
65 | } |
66 | |
67 | /// Returns the revision of this table, which is defined to be |
68 | /// the revision of the UEFI specification implemented by the firmware. |
69 | #[must_use ] |
70 | pub const fn uefi_revision(&self) -> Revision { |
71 | self.table.header.revision |
72 | } |
73 | |
74 | /// Returns the config table entries, a linear array of structures |
75 | /// pointing to other system-specific tables. |
76 | #[allow (clippy::missing_const_for_fn)] // Required until we bump the MSRV. |
77 | #[must_use ] |
78 | pub fn config_table(&self) -> &[cfg::ConfigTableEntry] { |
79 | unsafe { slice::from_raw_parts(self.table.cfg_table, self.table.nr_cfg) } |
80 | } |
81 | |
82 | /// Creates a new `SystemTable<View>` from a raw address. The address might |
83 | /// come from the Multiboot2 information structure or something similar. |
84 | /// |
85 | /// # Example |
86 | /// ```no_run |
87 | /// use core::ffi::c_void; |
88 | /// use uefi::prelude::{Boot, SystemTable}; |
89 | /// |
90 | /// let system_table_addr = 0xdeadbeef as *mut c_void; |
91 | /// |
92 | /// let mut uefi_system_table = unsafe { |
93 | /// SystemTable::<Boot>::from_ptr(system_table_addr).expect("Pointer must not be null!" ) |
94 | /// }; |
95 | /// ``` |
96 | /// |
97 | /// # Safety |
98 | /// This function is unsafe because the caller must be sure that the pointer |
99 | /// is valid. Otherwise, further operations on the object might result in |
100 | /// undefined behaviour, even if the methods aren't marked as unsafe. |
101 | pub unsafe fn from_ptr(ptr: *mut c_void) -> Option<Self> { |
102 | NonNull::new(ptr.cast()).map(|ptr| Self { |
103 | table: ptr.as_ref(), |
104 | _marker: PhantomData, |
105 | }) |
106 | } |
107 | } |
108 | |
109 | // These parts of the UEFI System Table interface may only be used until boot |
110 | // services are exited and hardware control is handed over to the OS loader |
111 | impl SystemTable<Boot> { |
112 | /// Returns the standard input protocol. |
113 | pub fn stdin(&mut self) -> &mut text::Input { |
114 | unsafe { &mut *self.table.stdin } |
115 | } |
116 | |
117 | /// Returns the standard output protocol. |
118 | pub fn stdout(&mut self) -> &mut text::Output { |
119 | unsafe { &mut *self.table.stdout.cast() } |
120 | } |
121 | |
122 | /// Returns the standard error protocol. |
123 | pub fn stderr(&mut self) -> &mut text::Output { |
124 | unsafe { &mut *self.table.stderr.cast() } |
125 | } |
126 | |
127 | /// Access runtime services |
128 | #[must_use ] |
129 | pub const fn runtime_services(&self) -> &RuntimeServices { |
130 | self.table.runtime |
131 | } |
132 | |
133 | /// Access boot services |
134 | #[must_use ] |
135 | pub const fn boot_services(&self) -> &BootServices { |
136 | unsafe { &*self.table.boot } |
137 | } |
138 | |
139 | /// Get the size in bytes of the buffer to allocate for storing the memory |
140 | /// map in `exit_boot_services`. |
141 | /// |
142 | /// This map contains some extra room to avoid needing to allocate more than |
143 | /// once. |
144 | /// |
145 | /// Returns `None` on overflow. |
146 | fn memory_map_size_for_exit_boot_services(&self) -> Option<usize> { |
147 | // Allocate space for extra entries beyond the current size of the |
148 | // memory map. The value of 8 matches the value in the Linux kernel: |
149 | // https://github.com/torvalds/linux/blob/e544a07438/drivers/firmware/efi/libstub/efistub.h#L173 |
150 | let extra_entries = 8; |
151 | |
152 | let memory_map_size = self.boot_services().memory_map_size(); |
153 | let extra_size = memory_map_size.entry_size.checked_mul(extra_entries)?; |
154 | memory_map_size.map_size.checked_add(extra_size) |
155 | } |
156 | |
157 | /// Get the current memory map and exit boot services. |
158 | unsafe fn get_memory_map_and_exit_boot_services( |
159 | &self, |
160 | buf: &'static mut [u8], |
161 | ) -> Result<MemoryMap<'static>> { |
162 | let boot_services = self.boot_services(); |
163 | |
164 | // Get the memory map. |
165 | let memory_map = boot_services.memory_map(buf)?; |
166 | |
167 | // Try to exit boot services using the memory map key. Note that after |
168 | // the first call to `exit_boot_services`, there are restrictions on |
169 | // what boot services functions can be called. In UEFI 2.8 and earlier, |
170 | // only `get_memory_map` and `exit_boot_services` are allowed. Starting |
171 | // in UEFI 2.9 other memory allocation functions may also be called. |
172 | boot_services |
173 | .exit_boot_services(boot_services.image_handle(), memory_map.key()) |
174 | .map(move |()| memory_map) |
175 | } |
176 | |
177 | /// Exit the UEFI boot services. |
178 | /// |
179 | /// After this function completes, UEFI hands over control of the hardware |
180 | /// to the executing OS loader, which implies that the UEFI boot services |
181 | /// are shut down and cannot be used anymore. Only UEFI configuration tables |
182 | /// and run-time services can be used, and the latter requires special care |
183 | /// from the OS loader. We model this situation by consuming the |
184 | /// `SystemTable<Boot>` view of the System Table and returning a more |
185 | /// restricted `SystemTable<Runtime>` view as an output. |
186 | /// |
187 | /// The memory map at the time of exiting boot services is also |
188 | /// returned. The map is backed by a [`MemoryType::LOADER_DATA`] |
189 | /// allocation. Since the boot services function to free that memory is no |
190 | /// longer available after calling `exit_boot_services`, the allocation is |
191 | /// live until the program ends. The lifetime of the memory map is therefore |
192 | /// `'static`. |
193 | /// |
194 | /// Once boot services are exited, the logger and allocator provided by |
195 | /// this crate can no longer be used. The logger should be disabled using |
196 | /// the [`Logger::disable`] method, and the allocator should be disabled by |
197 | /// calling [`global_allocator::exit_boot_services`]. Note that if the logger and |
198 | /// allocator were initialized with [`uefi_services::init`], they will be |
199 | /// disabled automatically when `exit_boot_services` is called. |
200 | /// |
201 | /// # Errors |
202 | /// |
203 | /// This function will fail if it is unable to allocate memory for |
204 | /// the memory map, if it fails to retrieve the memory map, or if |
205 | /// exiting boot services fails (with up to one retry). |
206 | /// |
207 | /// All errors are treated as unrecoverable because the system is |
208 | /// now in an undefined state. Rather than returning control to the |
209 | /// caller, the system will be reset. |
210 | /// |
211 | /// [`global_allocator::exit_boot_services`]: crate::global_allocator::exit_boot_services |
212 | /// [`Logger::disable`]: crate::logger::Logger::disable |
213 | /// [`uefi_services::init`]: https://docs.rs/uefi-services/latest/uefi_services/fn.init.html |
214 | #[must_use ] |
215 | pub fn exit_boot_services(self) -> (SystemTable<Runtime>, MemoryMap<'static>) { |
216 | let boot_services = self.boot_services(); |
217 | |
218 | // Reboot the device. |
219 | let reset = |status| -> ! { self.runtime_services().reset(ResetType::Cold, status, None) }; |
220 | |
221 | // Get the size of the buffer to allocate. If that calculation |
222 | // overflows treat it as an unrecoverable error. |
223 | let buf_size = match self.memory_map_size_for_exit_boot_services() { |
224 | Some(buf_size) => buf_size, |
225 | None => reset(Status::ABORTED), |
226 | }; |
227 | |
228 | // Allocate a byte slice to hold the memory map. If the |
229 | // allocation fails treat it as an unrecoverable error. |
230 | let buf: *mut u8 = match boot_services.allocate_pool(MemoryType::LOADER_DATA, buf_size) { |
231 | Ok(buf) => buf, |
232 | Err(err) => reset(err.status()), |
233 | }; |
234 | |
235 | // Calling `exit_boot_services` can fail if the memory map key is not |
236 | // current. Retry a second time if that occurs. This matches the |
237 | // behavior of the Linux kernel: |
238 | // https://github.com/torvalds/linux/blob/e544a0743/drivers/firmware/efi/libstub/efi-stub-helper.c#L375 |
239 | let mut status = Status::ABORTED; |
240 | for _ in 0..2 { |
241 | let buf: &mut [u8] = unsafe { slice::from_raw_parts_mut(buf, buf_size) }; |
242 | match unsafe { self.get_memory_map_and_exit_boot_services(buf) } { |
243 | Ok(memory_map) => { |
244 | let st = SystemTable { |
245 | table: self.table, |
246 | _marker: PhantomData, |
247 | }; |
248 | return (st, memory_map); |
249 | } |
250 | Err(err) => status = err.status(), |
251 | } |
252 | } |
253 | |
254 | // Failed to exit boot services. |
255 | reset(status) |
256 | } |
257 | |
258 | /// Clone this boot-time UEFI system table interface |
259 | /// |
260 | /// # Safety |
261 | /// |
262 | /// This is unsafe because you must guarantee that the clone will not be |
263 | /// used after boot services are exited. However, the singleton-based |
264 | /// designs that Rust uses for memory allocation, logging, and panic |
265 | /// handling require taking this risk. |
266 | #[must_use ] |
267 | pub const unsafe fn unsafe_clone(&self) -> Self { |
268 | SystemTable { |
269 | table: self.table, |
270 | _marker: PhantomData, |
271 | } |
272 | } |
273 | } |
274 | |
275 | impl Debug for SystemTable<Boot> { |
276 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { |
277 | self.table.fmt(f) |
278 | } |
279 | } |
280 | |
281 | // These parts of the SystemTable struct are only visible after exit from UEFI |
282 | // boot services. They provide unsafe access to the UEFI runtime services, which |
283 | // which were already available before but in safe form. |
284 | impl SystemTable<Runtime> { |
285 | /// Access runtime services |
286 | /// |
287 | /// # Safety |
288 | /// |
289 | /// This is unsafe because UEFI runtime services require an elaborate |
290 | /// CPU configuration which may not be preserved by OS loaders. See the |
291 | /// "Calling Conventions" chapter of the UEFI specification for details. |
292 | #[must_use ] |
293 | pub const unsafe fn runtime_services(&self) -> &RuntimeServices { |
294 | self.table.runtime |
295 | } |
296 | |
297 | /// Changes the runtime addressing mode of EFI firmware from physical to virtual. |
298 | /// It is up to the caller to translate the old SystemTable address to a new virtual |
299 | /// address and provide it for this function. |
300 | /// See [`get_current_system_table_addr`] |
301 | /// |
302 | /// # Safety |
303 | /// |
304 | /// Setting new virtual memory map is unsafe and may cause undefined behaviors. |
305 | /// |
306 | /// [`get_current_system_table_addr`]: SystemTable::get_current_system_table_addr |
307 | pub unsafe fn set_virtual_address_map( |
308 | self, |
309 | map: &mut [MemoryDescriptor], |
310 | new_system_table_virtual_addr: u64, |
311 | ) -> Result<Self> { |
312 | // Unsafe Code Guidelines guarantees that there is no padding in an array or a slice |
313 | // between its elements if the element type is `repr(C)`, which is our case. |
314 | // |
315 | // See https://rust-lang.github.io/unsafe-code-guidelines/layout/arrays-and-slices.html |
316 | let map_size = core::mem::size_of_val(map); |
317 | let entry_size = core::mem::size_of::<MemoryDescriptor>(); |
318 | let entry_version = crate::table::boot::MEMORY_DESCRIPTOR_VERSION; |
319 | let map_ptr = map.as_mut_ptr(); |
320 | (self.table.runtime.set_virtual_address_map)(map_size, entry_size, entry_version, map_ptr) |
321 | .into_with_val(|| { |
322 | let new_table_ref = |
323 | &mut *(new_system_table_virtual_addr as usize as *mut SystemTableImpl); |
324 | Self { |
325 | table: new_table_ref, |
326 | _marker: PhantomData, |
327 | } |
328 | }) |
329 | } |
330 | |
331 | /// Return the address of the SystemTable that resides in a UEFI runtime services |
332 | /// memory region. |
333 | #[must_use ] |
334 | pub fn get_current_system_table_addr(&self) -> u64 { |
335 | self.table as *const _ as usize as u64 |
336 | } |
337 | } |
338 | |
339 | /// The actual UEFI system table |
340 | #[repr (C)] |
341 | struct SystemTableImpl { |
342 | header: Header, |
343 | /// Null-terminated string representing the firmware's vendor. |
344 | fw_vendor: *const Char16, |
345 | fw_revision: u32, |
346 | stdin_handle: Handle, |
347 | stdin: *mut text::Input, |
348 | stdout_handle: Handle, |
349 | stdout: *mut text::Output, |
350 | stderr_handle: Handle, |
351 | stderr: *mut text::Output, |
352 | /// Runtime services table. |
353 | runtime: &'static RuntimeServices, |
354 | /// Boot services table. |
355 | boot: *const BootServices, |
356 | /// Number of entries in the configuration table. |
357 | nr_cfg: usize, |
358 | /// Pointer to beginning of the array. |
359 | cfg_table: *const cfg::ConfigTableEntry, |
360 | } |
361 | |
362 | impl<View: SystemTableView> super::Table for SystemTable<View> { |
363 | const SIGNATURE: u64 = 0x5453_5953_2049_4249; |
364 | } |
365 | |
366 | impl Debug for SystemTableImpl { |
367 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { |
368 | f&mut DebugStruct<'_, '_>.debug_struct("UefiSystemTable" ) |
369 | .field("header" , &self.header) |
370 | .field("fw_vendor" , &(unsafe { CStr16::from_ptr(self.fw_vendor) })) |
371 | .field("fw_revision" , &self.fw_revision) |
372 | .field("stdin_handle" , &self.stdin_handle) |
373 | .field("stdin" , &self.stdin) |
374 | .field("stdout_handle" , &self.stdout_handle) |
375 | .field("stdout" , &self.stdout) |
376 | .field("stderr_handle" , &self.stderr_handle) |
377 | .field("stderr" , &self.stderr) |
378 | .field("runtime" , &self.runtime) |
379 | // a little bit of extra work needed to call debug-fmt on the BootServices |
380 | // instead of printing the raw pointer |
381 | .field("boot" , &(unsafe { ptr::read(self.boot) })) |
382 | .field("nf_cfg" , &self.nr_cfg) |
383 | .field(name:"cfg_table" , &self.cfg_table) |
384 | .finish() |
385 | } |
386 | } |
387 | |