1 // Copyright 2023, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! The library implements Rust wrappers for a set of UEFI interfaces needed by GBL. It also
16 //! provides a global allocator and supports auto release of dynamic UEFI resource such as
17 //! protocols and UEFI allocated buffers.
18 //!
19 //! # Examples
20 //!
21 //! The following example covers the basic use pattern of the library. It scans all block devices
22 //! and prints out the device path, block size and io alignment info for each of them.
23 //!
24 //! ```
25 //! fn main(image: EfiHandle, systab_ptr: *mut EfiSystemTable) -> efi::EfiResult<()> {
26 //! let efi_entry = initialize(image, systab_ptr)?;
27 //! let mut con_out = efi_entry.system_table().con_out()?;
28 //! let boot_services = efi_entry.system_table().boot_services();
29 //! let path_to_text = boot_services.find_first_and_open::<DevicePathToTextProtocol>()?;
30 //!
31 //! write!(con_out, "Scanning block devices...\n")?;
32 //!
33 //! let block_handles = boot_services.locate_handle_buffer_by_protocol::<BlockIoProtocol>()?;
34 //!
35 //! for (i, handle) in block_handles.handles().iter().enumerate() {
36 //! let path = boot_services.open_protocol::<DevicePathProtocol>(*handle)?;
37 //! write!(con_out, "Block Device #{}: ", i)?;
38 //! path_to_text.convert_device_path_to_text(&path, false, false)?.print()?;
39 //! write!(con_out, "\n")?;
40 //!
41 //! let block_io_protocol = boot_services.open_protocol::<BlockIoProtocol>(*handle)?;
42 //! let media = block_io_protocol.media()?;
43 //! write!(con_out, " block size = {}\n", media.block_size)?;
44 //! write!(con_out, " io alignment = {}\n", media.io_align)?;
45 //! }
46 //!
47 //! Ok(())
48 //! }
49 //! ```
50
51 #![cfg_attr(not(test), no_std)]
52
53 extern crate alloc;
54 use alloc::vec::Vec;
55
56 use core::ptr::null_mut;
57 use core::slice::from_raw_parts;
58 #[cfg(not(test))]
59 use core::{fmt::Write, panic::PanicInfo};
60
61 use zerocopy::Ref;
62
63 #[rustfmt::skip]
64 pub mod defs;
65 use defs::*;
66
67 #[cfg(not(test))]
68 mod allocation;
69
70 #[cfg(not(test))]
71 pub use allocation::{efi_free, efi_malloc};
72
73 pub mod protocol;
74 use protocol::simple_text_output::SimpleTextOutputProtocol;
75 use protocol::{Protocol, ProtocolInfo};
76
77 mod error {
78 use super::defs::EFI_STATUS_SUCCESS;
79 use super::EfiStatus;
80
81 #[derive(Debug, Copy, Clone, PartialEq)]
82 pub enum ErrorTypes {
83 Unknown,
84 EfiStatusError(EfiStatus),
85 }
86
87 #[derive(Debug, PartialEq)]
88 pub struct EfiError(ErrorTypes);
89
90 impl EfiError {
err(&self) -> ErrorTypes91 pub fn err(&self) -> ErrorTypes {
92 self.0
93 }
94
95 /// Checks if the error is a particular EFI error.
is_efi_err(&self, code: EfiStatus) -> bool96 pub fn is_efi_err(&self, code: EfiStatus) -> bool {
97 *self == code.into()
98 }
99 }
100
101 impl From<EfiStatus> for EfiError {
from(efi_status: EfiStatus) -> EfiError102 fn from(efi_status: EfiStatus) -> EfiError {
103 EfiError(match efi_status {
104 EFI_STATUS_SUCCESS => ErrorTypes::Unknown,
105 _ => ErrorTypes::EfiStatusError(
106 // Remove the highest bit in the error code so that it's eaiser to interpret
107 // when printing.
108 efi_status & !(1 << (core::mem::size_of::<EfiStatus>() * 8 - 1)),
109 ),
110 })
111 }
112 }
113 }
114
115 pub use error::*;
116
117 /// Result type for this library.
118 pub type EfiResult<T> = core::result::Result<T, EfiError>;
119
120 /// Helper method to convert an EFI status code to EfiResult.
map_efi_err(code: EfiStatus) -> EfiResult<()>121 fn map_efi_err(code: EfiStatus) -> EfiResult<()> {
122 match code {
123 EFI_STATUS_SUCCESS => Ok(()),
124 _ => Err(code.into()),
125 }
126 }
127
128 /// `EfiEntry` stores the EFI system table pointer and image handle passed from the entry point.
129 /// It's the root data structure that derives all other wrapper APIs and structures.
130 pub struct EfiEntry {
131 image_handle: EfiHandle,
132 systab_ptr: *const EfiSystemTable,
133 }
134
135 impl EfiEntry {
136 /// Gets an instance of `SystemTable`.
system_table(&self) -> SystemTable137 pub fn system_table(&self) -> SystemTable {
138 // SAFETY: Pointers to UEFI data strucutres.
139 SystemTable { efi_entry: self, table: unsafe { self.systab_ptr.as_ref() }.unwrap() }
140 }
141
142 /// Gets the image handle.
image_handle(&self) -> DeviceHandle143 pub fn image_handle(&self) -> DeviceHandle {
144 DeviceHandle(self.image_handle)
145 }
146 }
147
148 /// Creates an `EfiEntry` and initialize EFI global allocator.
149 ///
150 /// # Safety
151 ///
152 /// The API modifies internal global state. It should only be called once upon EFI entry to obtain
153 /// an instance of `EfiEntry` for accessing other APIs. Calling it again when EFI APIs are already
154 /// being used can introduce a risk of race.
155 #[cfg(not(test))]
initialize( image_handle: EfiHandle, systab_ptr: *const EfiSystemTable, ) -> EfiResult<EfiEntry>156 pub unsafe fn initialize(
157 image_handle: EfiHandle,
158 systab_ptr: *const EfiSystemTable,
159 ) -> EfiResult<EfiEntry> {
160 let efi_entry = EfiEntry { image_handle, systab_ptr };
161 // SAFETY: By safety requirement of this function, `initialize` is only called once upon
162 // entering EFI application, where there should be no event notify function that can be
163 // triggered.
164 unsafe {
165 // Create another one for internal global allocator.
166 allocation::init_efi_global_alloc(EfiEntry { image_handle, systab_ptr })?;
167 }
168 Ok(efi_entry)
169 }
170
171 /// A helper for getting a subslice with an aligned address.
aligned_subslice(buffer: &mut [u8], alignment: usize) -> Option<&mut [u8]>172 pub fn aligned_subslice(buffer: &mut [u8], alignment: usize) -> Option<&mut [u8]> {
173 let addr = buffer.as_ptr() as usize;
174 let aligned_offset = addr
175 .checked_add(alignment - 1)?
176 .checked_div(alignment)?
177 .checked_mul(alignment)?
178 .checked_sub(addr)?;
179 buffer.get_mut(aligned_offset..)
180 }
181
182 /// Exits boot service and returns the memory map in the given buffer.
183 ///
184 /// The API takes ownership of the given `entry` and causes it to go out of scope.
185 /// This enforces strict compile time check that any reference/borrow in effect will cause compile
186 /// errors.
187 ///
188 /// Existing heap allocated memories will maintain their states. All system memory including them
189 /// will be under onwership of the subsequent OS or OS loader code.
exit_boot_services(entry: EfiEntry, mmap_buffer: &mut [u8]) -> EfiResult<EfiMemoryMap>190 pub fn exit_boot_services(entry: EfiEntry, mmap_buffer: &mut [u8]) -> EfiResult<EfiMemoryMap> {
191 let aligned = aligned_subslice(mmap_buffer, core::mem::align_of::<EfiMemoryDescriptor>())
192 .ok_or_else::<EfiError, _>(|| EFI_STATUS_BUFFER_TOO_SMALL.into())?;
193
194 let res = entry.system_table().boot_services().get_memory_map(aligned)?;
195 entry.system_table().boot_services().exit_boot_services(&res)?;
196 // SAFETY:
197 // At this point, UEFI has successfully exited boot services and no event/notification can be
198 // triggered.
199 #[cfg(not(test))]
200 unsafe {
201 allocation::exit_efi_global_alloc();
202 }
203 Ok(res)
204 }
205
206 /// `SystemTable` provides methods for accessing fields in `EFI_SYSTEM_TABLE`.
207 #[derive(Clone, Copy)]
208 pub struct SystemTable<'a> {
209 efi_entry: &'a EfiEntry,
210 table: &'a EfiSystemTable,
211 }
212
213 impl<'a> SystemTable<'a> {
214 /// Creates an instance of `BootServices`
boot_services(&self) -> BootServices<'a>215 pub fn boot_services(&self) -> BootServices<'a> {
216 BootServices {
217 efi_entry: self.efi_entry,
218 // SAFETY: Pointers to UEFI data strucutres.
219 boot_services: unsafe { self.table.boot_services.as_ref() }.unwrap(),
220 }
221 }
222
223 /// Creates an instance of `RuntimeServices`
runtime_services(&self) -> RuntimeServices<'a>224 pub fn runtime_services(&self) -> RuntimeServices<'a> {
225 RuntimeServices {
226 // SAFETY: Pointers to UEFI data strucutres.
227 runtime_services: unsafe { self.table.runtime_services.as_ref() }.unwrap(),
228 }
229 }
230
231 /// Gets the `EFI_SYSTEM_TABLE.ConOut` field.
con_out(&self) -> EfiResult<Protocol<'a, SimpleTextOutputProtocol>>232 pub fn con_out(&self) -> EfiResult<Protocol<'a, SimpleTextOutputProtocol>> {
233 // SAFETY: `EFI_SYSTEM_TABLE.ConOut` is a pointer to EfiSimpleTextOutputProtocol structure
234 // by definition. It lives until ExitBootService and thus as long as `self.efi_entry` or,
235 // 'a
236 Ok(unsafe {
237 Protocol::<SimpleTextOutputProtocol>::new(
238 // No device handle. This protocol is a permanent reference.
239 DeviceHandle(null_mut()),
240 self.table.con_out,
241 self.efi_entry,
242 )
243 })
244 }
245
246 /// Gets the `EFI_SYSTEM_TABLE.ConfigurationTable` array.
configuration_table(&self) -> Option<&[EfiConfigurationTable]>247 pub fn configuration_table(&self) -> Option<&[EfiConfigurationTable]> {
248 match self.table.configuration_table.is_null() {
249 true => None,
250 // SAFETY: Non-null pointer to EFI configuration table.
251 false => unsafe {
252 Some(from_raw_parts(
253 self.table.configuration_table,
254 self.table.number_of_table_entries,
255 ))
256 },
257 }
258 }
259 }
260
261 /// `BootServices` provides methods for accessing various EFI_BOOT_SERVICES interfaces.
262 #[derive(Clone, Copy)]
263 pub struct BootServices<'a> {
264 efi_entry: &'a EfiEntry,
265 boot_services: &'a EfiBootService,
266 }
267
268 impl<'a> BootServices<'a> {
269 /// Wrapper of `EFI_BOOT_SERVICES.AllocatePool()`.
270 #[allow(dead_code)]
allocate_pool( &self, pool_type: EfiMemoryType, size: usize, ) -> EfiResult<*mut core::ffi::c_void>271 fn allocate_pool(
272 &self,
273 pool_type: EfiMemoryType,
274 size: usize,
275 ) -> EfiResult<*mut core::ffi::c_void> {
276 let mut out: *mut core::ffi::c_void = null_mut();
277 // SAFETY: `EFI_BOOT_SERVICES` method call.
278 unsafe {
279 efi_call!(self.boot_services.allocate_pool, pool_type, size, &mut out)?;
280 }
281 Ok(out)
282 }
283
284 /// Wrapper of `EFI_BOOT_SERVICES.FreePool()`.
free_pool(&self, buf: *mut core::ffi::c_void) -> EfiResult<()>285 fn free_pool(&self, buf: *mut core::ffi::c_void) -> EfiResult<()> {
286 // SAFETY: `EFI_BOOT_SERVICES` method call.
287 unsafe { efi_call!(self.boot_services.free_pool, buf) }
288 }
289
290 /// Wrapper of `EFI_BOOT_SERVICES.OpenProtocol()`.
open_protocol<T: ProtocolInfo>( &self, handle: DeviceHandle, ) -> EfiResult<Protocol<'a, T>>291 pub fn open_protocol<T: ProtocolInfo>(
292 &self,
293 handle: DeviceHandle,
294 ) -> EfiResult<Protocol<'a, T>> {
295 let mut out_handle: EfiHandle = null_mut();
296 // SAFETY: EFI_BOOT_SERVICES method call.
297 unsafe {
298 efi_call!(
299 self.boot_services.open_protocol,
300 handle.0,
301 &T::GUID,
302 &mut out_handle as *mut _,
303 self.efi_entry.image_handle().0,
304 null_mut(),
305 EFI_OPEN_PROTOCOL_ATTRIBUTE_BY_HANDLE_PROTOCOL
306 )?;
307 }
308 // SAFETY: `EFI_SYSTEM_TABLE.OpenProtocol` returns a valid pointer to `T::InterfaceType`
309 // on success. The pointer remains valid until closed by
310 // `EFI_BOOT_SERVICES.CloseProtocol()` when Protocol goes out of scope.
311 Ok(unsafe { Protocol::<T>::new(handle, out_handle as *mut _, self.efi_entry) })
312 }
313
314 /// Wrapper of `EFI_BOOT_SERVICES.CloseProtocol()`.
close_protocol<T: ProtocolInfo>(&self, handle: DeviceHandle) -> EfiResult<()>315 fn close_protocol<T: ProtocolInfo>(&self, handle: DeviceHandle) -> EfiResult<()> {
316 // SAFETY: EFI_BOOT_SERVICES method call.
317 unsafe {
318 efi_call!(
319 self.boot_services.close_protocol,
320 handle.0,
321 &T::GUID,
322 self.efi_entry.image_handle().0,
323 null_mut()
324 )
325 }
326 }
327
328 /// Call `EFI_BOOT_SERVICES.LocateHandleBuffer()` with fixed
329 /// `EFI_LOCATE_HANDLE_SEARCH_TYPE_BY_PROTOCOL` and without search key.
locate_handle_buffer_by_protocol<T: ProtocolInfo>( &self, ) -> EfiResult<LocatedHandles<'a>>330 pub fn locate_handle_buffer_by_protocol<T: ProtocolInfo>(
331 &self,
332 ) -> EfiResult<LocatedHandles<'a>> {
333 let mut num_handles: usize = 0;
334 let mut handles: *mut EfiHandle = null_mut();
335 // SAFETY: EFI_BOOT_SERVICES method call.
336 unsafe {
337 efi_call!(
338 self.boot_services.locate_handle_buffer,
339 EFI_LOCATE_HANDLE_SEARCH_TYPE_BY_PROTOCOL,
340 &T::GUID,
341 null_mut(),
342 &mut num_handles as *mut usize as *mut _,
343 &mut handles as *mut *mut EfiHandle
344 )?
345 };
346 // `handles` should be a valid pointer if the above succeeds. But just double check
347 // to be safe. If assert fails, then there's a bug in the UEFI firmware.
348 assert!(!handles.is_null());
349 Ok(LocatedHandles::new(handles, num_handles, self.efi_entry))
350 }
351
352 /// Search and open the first found target EFI protocol.
find_first_and_open<T: ProtocolInfo>(&self) -> EfiResult<Protocol<'a, T>>353 pub fn find_first_and_open<T: ProtocolInfo>(&self) -> EfiResult<Protocol<'a, T>> {
354 // We don't use EFI_BOOT_SERVICES.LocateProtocol() because it doesn't give device handle
355 // which is required to close the protocol.
356 let handle = *self
357 .locate_handle_buffer_by_protocol::<T>()?
358 .handles()
359 .first()
360 .ok_or::<EfiError>(EFI_STATUS_NOT_FOUND.into())?;
361 self.open_protocol::<T>(handle)
362 }
363
364 /// Wrapper of `EFI_BOOT_SERVICE.GetMemoryMap()`.
get_memory_map<'b>(&self, mmap_buffer: &'b mut [u8]) -> EfiResult<EfiMemoryMap<'b>>365 pub fn get_memory_map<'b>(&self, mmap_buffer: &'b mut [u8]) -> EfiResult<EfiMemoryMap<'b>> {
366 let mut mmap_size = mmap_buffer.len();
367 let mut map_key: usize = 0;
368 let mut descriptor_size: usize = 0;
369 let mut descriptor_version: u32 = 0;
370 // SAFETY: EFI_BOOT_SERVICES method call.
371 unsafe {
372 efi_call!(
373 self.boot_services.get_memory_map,
374 &mut mmap_size,
375 mmap_buffer.as_mut_ptr() as *mut _,
376 &mut map_key,
377 &mut descriptor_size,
378 &mut descriptor_version
379 )
380 }?;
381 Ok(EfiMemoryMap::new(
382 &mut mmap_buffer[..mmap_size],
383 map_key,
384 descriptor_size,
385 descriptor_version,
386 ))
387 }
388
389 /// Wrapper of `EFI_BOOT_SERVICE.ExitBootServices()`.
exit_boot_services<'b>(&self, mmap: &'b EfiMemoryMap<'b>) -> EfiResult<()>390 fn exit_boot_services<'b>(&self, mmap: &'b EfiMemoryMap<'b>) -> EfiResult<()> {
391 // SAFETY: EFI_BOOT_SERVICES method call.
392 unsafe {
393 efi_call!(
394 self.boot_services.exit_boot_services,
395 self.efi_entry.image_handle().0,
396 mmap.map_key()
397 )
398 }
399 }
400
401 /// Wrapper of `EFI_BOOT_SERVICE.Stall()`.
stall(&self, micro: usize) -> EfiResult<()>402 pub fn stall(&self, micro: usize) -> EfiResult<()> {
403 // SAFETY: EFI_BOOT_SERVICES method call.
404 unsafe { efi_call!(self.boot_services.stall, micro) }
405 }
406
407 /// Wrapper of `EFI_BOOT_SERVICE.CreateEvent()`.
408 ///
409 /// Args:
410 ///
411 /// * `event_type`: The EFI event type.
412 /// * `cb`: An optional `&'e mut EventNotify`, which implements the event
413 /// notification function and provides the task level priority setting.
create_event<'n, 'e: 'n>( &self, event_type: EventType, mut cb: Option<&'n mut EventNotify<'e>>, ) -> EfiResult<Event<'a, 'n>>414 pub fn create_event<'n, 'e: 'n>(
415 &self,
416 event_type: EventType,
417 mut cb: Option<&'n mut EventNotify<'e>>,
418 ) -> EfiResult<Event<'a, 'n>> {
419 let mut efi_event: EfiEvent = null_mut();
420 let (tpl, c_callback, cookie): (EfiTpl, EfiEventNotify, *mut core::ffi::c_void) = match cb {
421 Some(ref mut event_notify) => {
422 (event_notify.tpl as _, Some(efi_event_cb), *event_notify as *mut _ as _)
423 }
424 None => (0, None, null_mut()),
425 };
426 // SAFETY:
427 // Pointers passed are output/callback context pointers which will not be retained by the
428 // callback (`fn efi_event_cb()`).
429 // The returned `Event` enforces a borrow to `cb` for 'e. It closes the event when it
430 // goes out of scope. This ensures that `cb` lives at least as long as the event is in
431 // effect and there can be no other borrows to `cb`.
432 unsafe {
433 efi_call!(
434 self.boot_services.create_event,
435 event_type as u32,
436 tpl as usize,
437 c_callback,
438 cookie,
439 &mut efi_event
440 )?;
441 }
442 Ok(Event::new(
443 Some(self.efi_entry),
444 efi_event,
445 cb.map::<&'n mut dyn FnMut(EfiEvent), _>(|v| v.cb),
446 ))
447 }
448
449 /// Wrapper of `EFI_BOOT_SERVICE.CloseEvent()`.
close_event(&self, event: &Event) -> EfiResult<()>450 fn close_event(&self, event: &Event) -> EfiResult<()> {
451 // SAFETY: EFI_BOOT_SERVICES method call.
452 unsafe { efi_call!(self.boot_services.close_event, event.efi_event) }
453 }
454
455 /// Wrapper of `EFI_BOOT_SERVICE.CheckEvent()`.
456 ///
457 /// On success, returns true if the event is signaled, false if not.
check_event(&self, event: &Event) -> EfiResult<bool>458 pub fn check_event(&self, event: &Event) -> EfiResult<bool> {
459 // SAFETY: EFI_BOOT_SERVICES method call.
460 match unsafe { efi_call!(self.boot_services.check_event, event.efi_event) } {
461 Err(e) if e != EFI_STATUS_NOT_READY.into() => Err(e),
462 Ok(()) => Ok(true),
463 _ => Ok(false),
464 }
465 }
466
467 /// Wrapper of `EFI_BOOT_SERVICE.SetTimer()`.
set_timer( &self, event: &Event, delay_type: EfiTimerDelay, trigger_time: u64, ) -> EfiResult<()>468 pub fn set_timer(
469 &self,
470 event: &Event,
471 delay_type: EfiTimerDelay,
472 trigger_time: u64,
473 ) -> EfiResult<()> {
474 // SAFETY: EFI_BOOT_SERVICES method call.
475 unsafe {
476 efi_call!(self.boot_services.set_timer, event.efi_event, delay_type, trigger_time)
477 }
478 }
479 }
480
481 /// `RuntimeServices` provides methods for accessing various EFI_RUNTIME_SERVICES interfaces.
482 #[derive(Clone, Copy)]
483 pub struct RuntimeServices<'a> {
484 runtime_services: &'a EfiRuntimeService,
485 }
486
487 impl<'a> RuntimeServices<'a> {
488 /// Wrapper of `EFI_RUNTIME_SERVICES.GetVariable()`.
get_variable(&self, guid: &EfiGuid, name: &str, out: &mut [u8]) -> EfiResult<usize>489 pub fn get_variable(&self, guid: &EfiGuid, name: &str, out: &mut [u8]) -> EfiResult<usize> {
490 let mut size = out.len();
491
492 let mut name_utf16: Vec<u16> = name.encode_utf16().collect();
493 name_utf16.push(0); // null-terminator
494
495 // SAFETY:
496 // * `&mut size` and `&mut out` are input/output params only and will not be retained
497 // * `&mut size` and `&mut out` are valid pointers and outlive the call
498 match unsafe {
499 efi_call!(
500 self.runtime_services.get_variable,
501 name_utf16.as_ptr(),
502 guid,
503 null_mut(),
504 &mut size,
505 out.as_mut_ptr() as *mut core::ffi::c_void
506 )
507 } {
508 Ok(()) => Ok(size),
509 Err(e) => Err(e),
510 }
511 }
512 }
513
514 /// EFI Event type to pass to BootServicess::create_event;
515 #[repr(u32)]
516 pub enum EventType {
517 Timer = EFI_EVENT_TYPE_TIMER,
518 RunTime = EFI_EVENT_TYPE_RUNTIME,
519 NotifyWait = EFI_EVENT_TYPE_NOTIFY_WAIT,
520 NotifySignal = EFI_EVENT_TYPE_NOTIFY_SIGNAL,
521 SignalExitBootServices = EFI_EVENT_TYPE_SIGNAL_EXIT_BOOT_SERVICES,
522 SignalVirtualAddressChange = EFI_EVENT_TYPE_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
523
524 // Valid combinations:
525 TimerNotifySignal = EFI_EVENT_TYPE_TIMER | EFI_EVENT_TYPE_NOTIFY_SIGNAL,
526 }
527
528 /// EFI task level priority setting for event notify function.
529 #[repr(usize)]
530 #[derive(Copy, Clone)]
531 pub enum Tpl {
532 Application = 4,
533 Callback = 8,
534 Notify = 16,
535 HighLevel = 31,
536 }
537
538 /// `EventNotify` contains the task level priority setting and a mutable reference to a
539 /// closure for the callback. It is passed as the context pointer to low level EFI event
540 /// notification function entry (`unsafe extern "C" fn efi_event_cb(...)`).
541 pub struct EventNotify<'e> {
542 tpl: Tpl,
543 cb: &'e mut dyn FnMut(EfiEvent),
544 }
545
546 impl<'e> EventNotify<'e> {
new(tpl: Tpl, cb: &'e mut dyn FnMut(EfiEvent)) -> Self547 pub fn new(tpl: Tpl, cb: &'e mut dyn FnMut(EfiEvent)) -> Self {
548 Self { tpl, cb }
549 }
550 }
551
552 /// `Event` wraps the raw `EfiEvent` handle and internally enforces a borrow of the registered
553 /// callback for the given life time `e. The event is automatically closed when going out of scope.
554 pub struct Event<'a, 'n> {
555 // If `efi_entry` is None, it represents an unowned Event and won't get closed on drop.
556 efi_entry: Option<&'a EfiEntry>,
557 efi_event: EfiEvent,
558 _cb: Option<&'n mut dyn FnMut(EfiEvent)>,
559 }
560
561 impl<'a, 'n> Event<'a, 'n> {
562 /// Creates an instance of owned `Event`. The `Event` is closed when going out of scope.
new( efi_entry: Option<&'a EfiEntry>, efi_event: EfiEvent, _cb: Option<&'n mut dyn FnMut(EfiEvent)>, ) -> Self563 fn new(
564 efi_entry: Option<&'a EfiEntry>,
565 efi_event: EfiEvent,
566 _cb: Option<&'n mut dyn FnMut(EfiEvent)>,
567 ) -> Self {
568 Self { efi_entry, efi_event, _cb }
569 }
570
571 /// Creates an unowned `Event`. The `Event` is not closed when going out of scope.
new_unowned(efi_event: EfiEvent) -> Self572 fn new_unowned(efi_event: EfiEvent) -> Self {
573 Self { efi_entry: None, efi_event: efi_event, _cb: None }
574 }
575 }
576
577 impl Drop for Event<'_, '_> {
drop(&mut self)578 fn drop(&mut self) {
579 if let Some(efi_entry) = self.efi_entry {
580 efi_entry.system_table().boot_services().close_event(self).unwrap();
581 }
582 }
583 }
584
585 /// Event notify function entry for EFI events.
586 ///
587 /// Safety:
588 ///
589 /// `ctx` must point to a `EventNotify` type object.
590 /// `ctx` must live longer than the event.
591 /// There should be no other references to `ctx`.
efi_event_cb(event: EfiEvent, ctx: *mut core::ffi::c_void)592 unsafe extern "C" fn efi_event_cb(event: EfiEvent, ctx: *mut core::ffi::c_void) {
593 // SAFETY: By safety requirement of this function, ctx points to a valid `EventNotify` object,
594 // outlives the event/the function call, and there is no other borrows.
595 let event_cb = unsafe { (ctx as *mut EventNotify).as_mut() }.unwrap();
596 (event_cb.cb)(event);
597 }
598
599 /// A type for accessing memory map.
600 pub struct EfiMemoryMap<'a> {
601 buffer: &'a mut [u8],
602 map_key: usize,
603 descriptor_size: usize,
604 descriptor_version: u32,
605 }
606
607 /// Iterator for traversing `EfiMemoryDescriptor` items in `EfiMemoryMap::buffer`.
608 pub struct EfiMemoryMapIter<'a: 'b, 'b> {
609 memory_map: &'b EfiMemoryMap<'a>,
610 offset: usize,
611 }
612
613 impl<'a, 'b> Iterator for EfiMemoryMapIter<'a, 'b> {
614 type Item = &'b EfiMemoryDescriptor;
615
next(&mut self) -> Option<Self::Item>616 fn next(&mut self) -> Option<Self::Item> {
617 if self.offset >= self.memory_map.buffer.len() {
618 return None;
619 }
620 let bytes = &self.memory_map.buffer[self.offset..][..self.memory_map.descriptor_size];
621 self.offset += self.memory_map.descriptor_size;
622 Some(Ref::<_, EfiMemoryDescriptor>::new_from_prefix(bytes).unwrap().0.into_ref())
623 }
624 }
625
626 impl<'a> EfiMemoryMap<'a> {
627 /// Creates a new instance with the given parameters obtained from `get_memory_map()`.
new( buffer: &'a mut [u8], map_key: usize, descriptor_size: usize, descriptor_version: u32, ) -> Self628 fn new(
629 buffer: &'a mut [u8],
630 map_key: usize,
631 descriptor_size: usize,
632 descriptor_version: u32,
633 ) -> Self {
634 Self { buffer, map_key, descriptor_size, descriptor_version }
635 }
636
637 /// Returns the buffer.
buffer(&self) -> &[u8]638 pub fn buffer(&self) -> &[u8] {
639 self.buffer
640 }
641
642 /// Returns the value of `map_key`.
map_key(&self) -> usize643 pub fn map_key(&self) -> usize {
644 self.map_key
645 }
646
647 /// Returns the value of `descriptor_version`.
descriptor_version(&self) -> u32648 pub fn descriptor_version(&self) -> u32 {
649 self.descriptor_version
650 }
651
652 /// Returns the number of descriptors.
len(&self) -> usize653 pub fn len(&self) -> usize {
654 self.buffer.len() / self.descriptor_size
655 }
656 }
657
658 impl<'a: 'b, 'b> IntoIterator for &'b EfiMemoryMap<'a> {
659 type Item = &'b EfiMemoryDescriptor;
660 type IntoIter = EfiMemoryMapIter<'a, 'b>;
661
into_iter(self) -> Self::IntoIter662 fn into_iter(self) -> Self::IntoIter {
663 EfiMemoryMapIter { memory_map: self, offset: 0 }
664 }
665 }
666
667 /// A type representing a UEFI handle to a UEFI device.
668 #[derive(Debug, Copy, Clone, PartialEq)]
669 pub struct DeviceHandle(EfiHandle);
670
671 /// `LocatedHandles` holds the array of handles return by
672 /// `BootServices::locate_handle_buffer_by_protocol()`.
673 pub struct LocatedHandles<'a> {
674 handles: &'a [DeviceHandle],
675 efi_entry: &'a EfiEntry,
676 }
677
678 impl<'a> LocatedHandles<'a> {
new(handles: *mut EfiHandle, len: usize, efi_entry: &'a EfiEntry) -> Self679 pub(crate) fn new(handles: *mut EfiHandle, len: usize, efi_entry: &'a EfiEntry) -> Self {
680 // Implementation is not suppose to call this with a NULL pointer.
681 debug_assert!(!handles.is_null());
682 Self {
683 // SAFETY: Given correct UEFI firmware, non-null pointer points to valid memory.
684 // The memory is owned by the objects.
685 handles: unsafe { from_raw_parts(handles as *mut DeviceHandle, len) },
686 efi_entry: efi_entry,
687 }
688 }
689 /// Get the list of handles as a slice.
handles(&self) -> &[DeviceHandle]690 pub fn handles(&self) -> &[DeviceHandle] {
691 self.handles
692 }
693 }
694
695 impl Drop for LocatedHandles<'_> {
drop(&mut self)696 fn drop(&mut self) {
697 self.efi_entry
698 .system_table()
699 .boot_services()
700 .free_pool(self.handles.as_ptr() as *mut _)
701 .unwrap();
702 }
703 }
704
705 /// Helper macro for printing message via `EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL` in
706 /// `EFI_SYSTEM_TABLE.ConOut`.
707 #[macro_export]
708 macro_rules! efi_print {
709 ( $efi_entry:expr, $( $x:expr ),* $(,)? ) => {
710 write!($efi_entry.system_table().con_out().unwrap(), $($x,)*).unwrap()
711 };
712 }
713
714 #[macro_export]
715 macro_rules! efi_println {
716 ( $efi_entry:expr, $( $x:expr ),* ) => {
717 efi_print!($efi_entry, $($x,)*);
718 efi_print!($efi_entry, "\r\n");
719 };
720 }
721
722 /// Provides a builtin panic handler.
723 /// In the long term, to improve flexibility, consider allowing application to install a custom
724 /// handler into `EfiEntry` to be called here.
725 #[cfg(not(test))]
726 #[panic_handler]
panic(panic: &PanicInfo) -> !727 fn panic(panic: &PanicInfo) -> ! {
728 // If there is a valid internal `efi_entry` from global allocator, print the panic info.
729 let entry = allocation::internal_efi_entry();
730 if let Some(e) = entry {
731 match e.system_table().con_out() {
732 Ok(mut con_out) => {
733 let _ = write!(con_out, "Panics! {}\r\n", panic);
734 }
735 _ => {}
736 }
737 }
738 loop {}
739 }
740
741 #[cfg(test)]
742 mod test {
743 use super::*;
744 use crate::protocol::block_io::BlockIoProtocol;
745 use std::cell::RefCell;
746 use std::collections::VecDeque;
747 use std::mem::size_of;
748 use std::slice::from_raw_parts_mut;
749
750 use zerocopy::AsBytes;
751
752 /// A structure to store the traces of arguments/outputs for EFI methods.
753 #[derive(Default)]
754 pub struct EfiCallTraces {
755 pub free_pool_trace: FreePoolTrace,
756 pub open_protocol_trace: OpenProtocolTrace,
757 pub close_protocol_trace: CloseProtocolTrace,
758 pub locate_handle_buffer_trace: LocateHandleBufferTrace,
759 pub get_memory_map_trace: GetMemoryMapTrace,
760 pub exit_boot_services_trace: ExitBootServicespTrace,
761 pub create_event_trace: CreateEventTrace,
762 pub close_event_trace: CloseEventTrace,
763 pub check_event_trace: CheckEventTrace,
764 }
765
766 // Declares a global instance of EfiCallTraces.
767 // Need to use thread local storage because rust unit test is multi-threaded.
768 thread_local! {
769 static EFI_CALL_TRACES: RefCell<EfiCallTraces> = RefCell::new(Default::default());
770 }
771
772 /// Exports for unit-test in submodules.
efi_call_traces() -> &'static std::thread::LocalKey<RefCell<EfiCallTraces>>773 pub fn efi_call_traces() -> &'static std::thread::LocalKey<RefCell<EfiCallTraces>> {
774 &EFI_CALL_TRACES
775 }
776
777 /// EFI_BOOT_SERVICE.FreePool() test implementation.
778 #[derive(Default)]
779 pub struct FreePoolTrace {
780 // Capture `buf`
781 pub inputs: VecDeque<*mut core::ffi::c_void>,
782 }
783
784 /// Mock of the `EFI_BOOT_SERVICE.FreePool` C API in test environment.
free_pool(buf: *mut core::ffi::c_void) -> EfiStatus785 extern "C" fn free_pool(buf: *mut core::ffi::c_void) -> EfiStatus {
786 EFI_CALL_TRACES.with(|traces| {
787 traces.borrow_mut().free_pool_trace.inputs.push_back(buf);
788 EFI_STATUS_SUCCESS
789 })
790 }
791
792 /// EFI_BOOT_SERVICE.OpenProtocol() test implementation.
793 #[derive(Default)]
794 pub struct OpenProtocolTrace {
795 // Capture `handle`, `protocol_guid`, `agent_handle`.
796 pub inputs: VecDeque<(DeviceHandle, EfiGuid, EfiHandle)>,
797 // Return `intf`, EfiStatus.
798 pub outputs: VecDeque<(EfiHandle, EfiStatus)>,
799 }
800
801 /// Mock of the `EFI_BOOT_SERVICE.OpenProtocol` C API in test environment.
802 ///
803 /// # Safety
804 ///
805 /// Caller should guarantee that `intf` and `protocol_guid` point to valid memory locations.
open_protocol( handle: EfiHandle, protocol_guid: *const EfiGuid, intf: *mut *mut core::ffi::c_void, agent_handle: EfiHandle, _: EfiHandle, attr: u32, ) -> EfiStatus806 unsafe extern "C" fn open_protocol(
807 handle: EfiHandle,
808 protocol_guid: *const EfiGuid,
809 intf: *mut *mut core::ffi::c_void,
810 agent_handle: EfiHandle,
811 _: EfiHandle,
812 attr: u32,
813 ) -> EfiStatus {
814 assert_eq!(attr, EFI_OPEN_PROTOCOL_ATTRIBUTE_BY_HANDLE_PROTOCOL);
815 EFI_CALL_TRACES.with(|traces| {
816 let trace = &mut traces.borrow_mut().open_protocol_trace;
817 trace.inputs.push_back((DeviceHandle(handle), *protocol_guid, agent_handle));
818
819 let (intf_handle, status) = trace.outputs.pop_front().unwrap();
820 *intf = intf_handle;
821
822 status
823 })
824 }
825
826 /// EFI_BOOT_SERVICE.CloseProtocol() test implementation.
827 #[derive(Default)]
828 pub struct CloseProtocolTrace {
829 // Capture `handle`, `protocol_guid`, `agent_handle`
830 pub inputs: VecDeque<(DeviceHandle, EfiGuid, EfiHandle)>,
831 }
832
833 /// Mock of the `EFI_BOOT_SERVICE.CloseProtocol` C API in test environment.
834 ///
835 /// # Safety
836 ///
837 /// Caller should guarantee that `protocol_guid` points to valid memory location.
close_protocol( handle: EfiHandle, protocol_guid: *const EfiGuid, agent_handle: EfiHandle, _: EfiHandle, ) -> EfiStatus838 unsafe extern "C" fn close_protocol(
839 handle: EfiHandle,
840 protocol_guid: *const EfiGuid,
841 agent_handle: EfiHandle,
842 _: EfiHandle,
843 ) -> EfiStatus {
844 EFI_CALL_TRACES.with(|traces| {
845 traces.borrow_mut().close_protocol_trace.inputs.push_back((
846 DeviceHandle(handle),
847 *protocol_guid,
848 agent_handle,
849 ));
850 EFI_STATUS_SUCCESS
851 })
852 }
853
854 /// EFI_BOOT_SERVICE.LocateHandleBuffer.
855 #[derive(Default)]
856 pub struct LocateHandleBufferTrace {
857 // Capture `protocol`.
858 pub inputs: VecDeque<EfiGuid>,
859 // For returning in `num_handles` and `buf`.
860 pub outputs: VecDeque<(usize, *mut DeviceHandle)>,
861 }
862
863 /// Mock of the `EFI_BOOT_SERVICE.LocateHandleBuffer` C API in test environment.
864 ///
865 /// # Safety
866 ///
867 /// Caller should guarantee that `num_handles` and `buf` point to valid memory locations.
locate_handle_buffer( search_type: EfiLocateHandleSearchType, protocol: *const EfiGuid, search_key: *mut core::ffi::c_void, num_handles: *mut usize, buf: *mut *mut EfiHandle, ) -> EfiStatus868 unsafe extern "C" fn locate_handle_buffer(
869 search_type: EfiLocateHandleSearchType,
870 protocol: *const EfiGuid,
871 search_key: *mut core::ffi::c_void,
872 num_handles: *mut usize,
873 buf: *mut *mut EfiHandle,
874 ) -> EfiStatus {
875 assert_eq!(search_type, EFI_LOCATE_HANDLE_SEARCH_TYPE_BY_PROTOCOL);
876 assert_eq!(search_key, null_mut());
877 EFI_CALL_TRACES.with(|traces| {
878 let trace = &mut traces.borrow_mut().locate_handle_buffer_trace;
879 trace.inputs.push_back(*protocol);
880
881 let (num, handles) = trace.outputs.pop_front().unwrap();
882 *num_handles = num as usize;
883 *buf = handles as *mut EfiHandle;
884
885 EFI_STATUS_SUCCESS
886 })
887 }
888
889 /// EFI_BOOT_SERVICE.GetMemoryMap.
890 #[derive(Default)]
891 pub struct GetMemoryMapTrace {
892 // Capture `memory_map_size` and `memory_map` argument.
893 pub inputs: VecDeque<(usize, *mut EfiMemoryDescriptor)>,
894 // Output value `map_key`, `memory_map_size`.
895 pub outputs: VecDeque<(usize, usize)>,
896 }
897
898 /// Mock of the `EFI_BOOT_SERVICE.GetMemoryMap` C API in test environment.
899 ///
900 /// # Safety
901 ///
902 /// Caller should guarantee that `memory_map_size`, `map_key` and `desc_size` point to valid
903 /// memory locations.
get_memory_map( memory_map_size: *mut usize, memory_map: *mut EfiMemoryDescriptor, map_key: *mut usize, desc_size: *mut usize, _: *mut u32, ) -> EfiStatus904 unsafe extern "C" fn get_memory_map(
905 memory_map_size: *mut usize,
906 memory_map: *mut EfiMemoryDescriptor,
907 map_key: *mut usize,
908 desc_size: *mut usize,
909 _: *mut u32,
910 ) -> EfiStatus {
911 EFI_CALL_TRACES.with(|traces| {
912 let trace = &mut traces.borrow_mut().get_memory_map_trace;
913 trace.inputs.push_back((unsafe { *memory_map_size }, memory_map));
914 (*map_key, *memory_map_size) = trace.outputs.pop_front().unwrap();
915 *desc_size = size_of::<EfiMemoryDescriptor>();
916 EFI_STATUS_SUCCESS
917 })
918 }
919
920 /// EFI_BOOT_SERVICE.ExitBootServices.
921 #[derive(Default)]
922 pub struct ExitBootServicespTrace {
923 // Capture `image_handle`, `map_key`
924 pub inputs: VecDeque<(EfiHandle, usize)>,
925 }
926
927 /// Mock of the `EFI_BOOT_SERVICE.ExitBootServices` C API in test environment.
exit_boot_services(image_handle: EfiHandle, map_key: usize) -> EfiStatus928 extern "C" fn exit_boot_services(image_handle: EfiHandle, map_key: usize) -> EfiStatus {
929 EFI_CALL_TRACES.with(|traces| {
930 let trace = &mut traces.borrow_mut().exit_boot_services_trace;
931 trace.inputs.push_back((image_handle, map_key));
932 EFI_STATUS_SUCCESS
933 })
934 }
935
936 /// EFI_BOOT_SERVICE.CreateEvent.
937 #[derive(Default)]
938 pub struct CreateEventTrace {
939 // Capture `type_`, `notify_tpl`, `notify_fn`, `notify_ctx`
940 pub inputs: VecDeque<(u32, EfiTpl, EfiEventNotify, *mut core::ffi::c_void)>,
941 // Output a EfiEvent.
942 pub outputs: VecDeque<EfiEvent>,
943 }
944
945 /// Mock of the `EFI_BOOT_SERVICE.CreateEvent` C API in test environment.
946 ///
947 /// # Safety
948 ///
949 /// Caller should guarantee that `event` points to valid memory location.
create_event( type_: u32, notify_tpl: EfiTpl, notify_fn: EfiEventNotify, notify_ctx: *mut core::ffi::c_void, event: *mut EfiEvent, ) -> EfiStatus950 unsafe extern "C" fn create_event(
951 type_: u32,
952 notify_tpl: EfiTpl,
953 notify_fn: EfiEventNotify,
954 notify_ctx: *mut core::ffi::c_void,
955 event: *mut EfiEvent,
956 ) -> EfiStatus {
957 EFI_CALL_TRACES.with(|traces| {
958 let trace = &mut traces.borrow_mut().create_event_trace;
959 trace.inputs.push_back((type_, notify_tpl, notify_fn, notify_ctx));
960 *event = trace.outputs.pop_front().unwrap();
961 EFI_STATUS_SUCCESS
962 })
963 }
964
965 /// EFI_BOOT_SERVICE.CloseEvent.
966 #[derive(Default)]
967 pub struct CloseEventTrace {
968 // Capture `event`
969 pub inputs: VecDeque<EfiEvent>,
970 }
971
972 /// Mock of the `EFI_BOOT_SERVICE.CloseEvent` C API in test environment.
close_event(event: EfiEvent) -> EfiStatus973 extern "C" fn close_event(event: EfiEvent) -> EfiStatus {
974 EFI_CALL_TRACES.with(|traces| {
975 let trace = &mut traces.borrow_mut().close_event_trace;
976 trace.inputs.push_back(event);
977 EFI_STATUS_SUCCESS
978 })
979 }
980
981 /// EFI_BOOT_SERVICE.CheckEvent.
982 #[derive(Default)]
983 pub struct CheckEventTrace {
984 // EfiStatus for return.
985 pub outputs: VecDeque<EfiStatus>,
986 }
987
988 /// Mock of the `EFI_BOOT_SERVICE.CheckEvent` C API in test environment.
check_event(_: EfiEvent) -> EfiStatus989 extern "C" fn check_event(_: EfiEvent) -> EfiStatus {
990 EFI_CALL_TRACES.with(|traces| {
991 let trace = &mut traces.borrow_mut().check_event_trace;
992 trace.outputs.pop_front().unwrap()
993 })
994 }
995
996 /// A test wrapper that sets up a system table, image handle and runs a test function like it
997 /// is an EFI application.
998 /// TODO(300168989): Investigate using procedural macro to generate test that auto calls this.
run_test(func: fn(EfiHandle, *mut EfiSystemTable) -> ())999 pub fn run_test(func: fn(EfiHandle, *mut EfiSystemTable) -> ()) {
1000 // Reset all traces
1001 EFI_CALL_TRACES.with(|trace| {
1002 *trace.borrow_mut() = Default::default();
1003 });
1004
1005 let mut systab: EfiSystemTable = Default::default();
1006 let mut boot_services: EfiBootService = Default::default();
1007
1008 boot_services.free_pool = Some(free_pool);
1009 boot_services.open_protocol = Some(open_protocol);
1010 boot_services.close_protocol = Some(close_protocol);
1011 boot_services.locate_handle_buffer = Some(locate_handle_buffer);
1012 boot_services.get_memory_map = Some(get_memory_map);
1013 boot_services.exit_boot_services = Some(exit_boot_services);
1014 boot_services.create_event = Some(create_event);
1015 boot_services.close_event = Some(close_event);
1016 boot_services.check_event = Some(check_event);
1017 systab.boot_services = &mut boot_services as *mut _;
1018 let image_handle: usize = 1234; // Don't care.
1019
1020 func(image_handle as EfiHandle, &mut systab as *mut _);
1021
1022 // Reset all traces
1023 EFI_CALL_TRACES.with(|trace| {
1024 *trace.borrow_mut() = Default::default();
1025 });
1026 }
1027
1028 /// Get the pointer to an object as an EfiHandle type.
as_efi_handle<T>(val: &mut T) -> EfiHandle1029 fn as_efi_handle<T>(val: &mut T) -> EfiHandle {
1030 val as *mut T as *mut _
1031 }
1032
1033 #[test]
test_open_close_protocol()1034 fn test_open_close_protocol() {
1035 run_test(|image_handle, systab_ptr| {
1036 let efi_entry = EfiEntry { image_handle, systab_ptr };
1037
1038 // Set up open_protocol trace
1039 let mut block_io: EfiBlockIoProtocol = Default::default();
1040 EFI_CALL_TRACES.with(|traces| {
1041 traces.borrow_mut().open_protocol_trace.outputs =
1042 VecDeque::from([(as_efi_handle(&mut block_io), EFI_STATUS_SUCCESS)]);
1043 });
1044
1045 let mut device_handle: usize = 0; // Don't care
1046 {
1047 // Open a protocol
1048 let protocol = efi_entry
1049 .system_table()
1050 .boot_services()
1051 .open_protocol::<BlockIoProtocol>(DeviceHandle(as_efi_handle(
1052 &mut device_handle,
1053 )))
1054 .unwrap();
1055
1056 // Validate call args
1057 EFI_CALL_TRACES.with(|trace| {
1058 assert_eq!(
1059 trace.borrow_mut().open_protocol_trace.inputs,
1060 [(
1061 DeviceHandle(as_efi_handle(&mut device_handle)),
1062 BlockIoProtocol::GUID,
1063 image_handle
1064 ),]
1065 );
1066
1067 // close_protocol not called yet.
1068 assert_eq!(trace.borrow_mut().close_protocol_trace.inputs, []);
1069 });
1070
1071 // The protocol gets the correct EfiBlockIoProtocol structure we pass in.
1072 assert_eq!(protocol.interface_ptr(), &mut block_io as *mut _);
1073 }
1074
1075 // Close protocol is called as `protocol` goes out of scope.
1076 EFI_CALL_TRACES.with(|trace| {
1077 assert_eq!(
1078 trace.borrow_mut().close_protocol_trace.inputs,
1079 [(
1080 DeviceHandle(as_efi_handle(&mut device_handle)),
1081 BlockIoProtocol::GUID,
1082 image_handle
1083 ),]
1084 )
1085 });
1086 })
1087 }
1088
1089 #[test]
test_null_efi_method()1090 fn test_null_efi_method() {
1091 // Test that wrapper call fails if efi method is None.
1092 run_test(|image_handle, systab_ptr| {
1093 let efi_entry = EfiEntry { image_handle, systab_ptr };
1094
1095 // Set up open_protocol trace
1096 let mut block_io: EfiBlockIoProtocol = Default::default();
1097 EFI_CALL_TRACES.with(|traces| {
1098 traces.borrow_mut().open_protocol_trace.outputs =
1099 VecDeque::from([(as_efi_handle(&mut block_io), EFI_STATUS_SUCCESS)]);
1100 });
1101
1102 // Set the method to None.
1103 // SAFETY:
1104 // run_test() guarantees `boot_services` pointer points to valid object.
1105 unsafe { (*(*systab_ptr).boot_services).open_protocol = None };
1106
1107 let mut device_handle: usize = 0; // Don't care
1108 assert!(efi_entry
1109 .system_table()
1110 .boot_services()
1111 .open_protocol::<BlockIoProtocol>(DeviceHandle(as_efi_handle(&mut device_handle)))
1112 .is_err());
1113 })
1114 }
1115
1116 #[test]
test_error_efi_method()1117 fn test_error_efi_method() {
1118 // Test that wrapper call fails if efi method returns error.
1119 run_test(|image_handle, systab_ptr| {
1120 let efi_entry = EfiEntry { image_handle, systab_ptr };
1121
1122 // Set up open_protocol trace.
1123 let mut block_io: EfiBlockIoProtocol = Default::default();
1124 EFI_CALL_TRACES.with(|traces| {
1125 traces.borrow_mut().open_protocol_trace.outputs =
1126 VecDeque::from([(as_efi_handle(&mut block_io), EFI_STATUS_NOT_FOUND)]);
1127 });
1128
1129 let mut device_handle: usize = 0; // Don't care
1130 assert!(efi_entry
1131 .system_table()
1132 .boot_services()
1133 .open_protocol::<BlockIoProtocol>(DeviceHandle(as_efi_handle(&mut device_handle)))
1134 .is_err());
1135 })
1136 }
1137
1138 #[test]
test_locate_handle_buffer_by_protocol()1139 fn test_locate_handle_buffer_by_protocol() {
1140 run_test(|image_handle, systab_ptr| {
1141 let efi_entry = EfiEntry { image_handle, systab_ptr };
1142
1143 // Set up locate_handle_buffer_trace trace.
1144 let mut located_handles: [DeviceHandle; 3] =
1145 [DeviceHandle(1 as *mut _), DeviceHandle(2 as *mut _), DeviceHandle(3 as *mut _)];
1146 EFI_CALL_TRACES.with(|traces| {
1147 traces.borrow_mut().locate_handle_buffer_trace.outputs =
1148 VecDeque::from([(located_handles.len(), located_handles.as_mut_ptr())]);
1149 });
1150
1151 {
1152 let handles = efi_entry
1153 .system_table()
1154 .boot_services()
1155 .locate_handle_buffer_by_protocol::<BlockIoProtocol>()
1156 .unwrap();
1157
1158 // Returned handles are expected.
1159 assert_eq!(handles.handles().to_vec(), located_handles);
1160 }
1161
1162 EFI_CALL_TRACES.with(|traces| {
1163 let traces = traces.borrow_mut();
1164 // Arguments are passed correctly.
1165 assert_eq!(traces.locate_handle_buffer_trace.inputs, [BlockIoProtocol::GUID]);
1166 // Free pool is called with the correct address.
1167 assert_eq!(traces.free_pool_trace.inputs, [located_handles.as_mut_ptr() as *mut _]);
1168 });
1169 })
1170 }
1171
1172 #[test]
test_find_first_and_open()1173 fn test_find_first_and_open() {
1174 run_test(|image_handle, systab_ptr| {
1175 let efi_entry = EfiEntry { image_handle, systab_ptr };
1176
1177 // Set up locate_handle_buffer_trace trace.
1178 let mut located_handles: [DeviceHandle; 3] =
1179 [DeviceHandle(1 as *mut _), DeviceHandle(2 as *mut _), DeviceHandle(3 as *mut _)];
1180 EFI_CALL_TRACES.with(|traces| {
1181 traces.borrow_mut().locate_handle_buffer_trace.outputs =
1182 VecDeque::from([(located_handles.len(), located_handles.as_mut_ptr())]);
1183 });
1184
1185 // Set up open_protocol trace.
1186 let mut block_io: EfiBlockIoProtocol = Default::default();
1187 EFI_CALL_TRACES.with(|traces| {
1188 traces.borrow_mut().open_protocol_trace.outputs =
1189 VecDeque::from([(as_efi_handle(&mut block_io), EFI_STATUS_SUCCESS)]);
1190 });
1191
1192 efi_entry
1193 .system_table()
1194 .boot_services()
1195 .find_first_and_open::<BlockIoProtocol>()
1196 .unwrap();
1197
1198 // Check open_protocol is called on the first handle.
1199 EFI_CALL_TRACES.with(|traces| {
1200 assert_eq!(
1201 traces.borrow_mut().open_protocol_trace.inputs,
1202 [(DeviceHandle(1 as *mut _), BlockIoProtocol::GUID, image_handle),]
1203 );
1204 });
1205 })
1206 }
1207
1208 #[test]
test_exit_boot_services()1209 fn test_exit_boot_services() {
1210 run_test(|image_handle, systab_ptr| {
1211 let efi_entry = EfiEntry { image_handle, systab_ptr };
1212 // Create a buffer large enough to hold two EfiMemoryDescriptor.
1213 let mut descriptors: [EfiMemoryDescriptor; 2] = [
1214 EfiMemoryDescriptor {
1215 memory_type: EFI_MEMORY_TYPE_LOADER_DATA,
1216 padding: 0,
1217 physical_start: 0,
1218 virtual_start: 0,
1219 number_of_pages: 0,
1220 attributes: 0,
1221 },
1222 EfiMemoryDescriptor {
1223 memory_type: EFI_MEMORY_TYPE_LOADER_CODE,
1224 padding: 0,
1225 physical_start: 0,
1226 virtual_start: 0,
1227 number_of_pages: 0,
1228 attributes: 0,
1229 },
1230 ];
1231 let map_key: usize = 12345;
1232 // Set up get_memory_map trace.
1233 EFI_CALL_TRACES.with(|traces| {
1234 // Output only the first EfiMemoryDescriptor.
1235 traces.borrow_mut().get_memory_map_trace.outputs =
1236 VecDeque::from([(map_key, 1 * size_of::<EfiMemoryDescriptor>())]);
1237 });
1238
1239 // SAFETY: Buffer is guaranteed valid.
1240 let buffer = unsafe {
1241 from_raw_parts_mut(
1242 descriptors.as_mut_ptr() as *mut u8,
1243 descriptors.len() * size_of::<EfiMemoryDescriptor>(),
1244 )
1245 };
1246
1247 // Test `exit_boot_services`
1248 let desc = super::exit_boot_services(efi_entry, buffer).unwrap();
1249
1250 // Validate that UEFI APIs are correctly called.
1251 EFI_CALL_TRACES.with(|traces| {
1252 assert_eq!(
1253 traces.borrow_mut().get_memory_map_trace.inputs,
1254 [(
1255 descriptors.len() * size_of::<EfiMemoryDescriptor>(),
1256 descriptors.as_mut_ptr()
1257 )]
1258 );
1259
1260 assert_eq!(
1261 traces.borrow_mut().exit_boot_services_trace.inputs,
1262 [(image_handle, map_key)],
1263 );
1264 });
1265
1266 // Validate that the returned `EfiMemoryMap` contains only 1 EfiMemoryDescriptor.
1267 assert_eq!(desc.into_iter().map(|v| *v).collect::<Vec<_>>(), descriptors[..1].to_vec());
1268 // Validate that the returned `EfiMemoryMap` has the correct map_key.
1269 assert_eq!(desc.map_key(), map_key);
1270 })
1271 }
1272
1273 #[test]
test_exit_boot_services_unaligned_buffer()1274 fn test_exit_boot_services_unaligned_buffer() {
1275 run_test(|image_handle, systab_ptr| {
1276 let efi_entry = EfiEntry { image_handle, systab_ptr };
1277 // Create a buffer for 2 EfiMemoryDescriptor.
1278 let descriptors: [EfiMemoryDescriptor; 2] = [
1279 EfiMemoryDescriptor {
1280 memory_type: EFI_MEMORY_TYPE_LOADER_DATA,
1281 padding: 0,
1282 physical_start: 0,
1283 virtual_start: 0,
1284 number_of_pages: 0,
1285 attributes: 0,
1286 },
1287 EfiMemoryDescriptor {
1288 memory_type: EFI_MEMORY_TYPE_LOADER_CODE,
1289 padding: 0,
1290 physical_start: 0,
1291 virtual_start: 0,
1292 number_of_pages: 0,
1293 attributes: 0,
1294 },
1295 ];
1296
1297 let map_key: usize = 12345;
1298 // Set up get_memory_map trace.
1299 EFI_CALL_TRACES.with(|traces| {
1300 traces.borrow_mut().get_memory_map_trace.outputs =
1301 VecDeque::from([(map_key, 2 * size_of::<EfiMemoryDescriptor>())]);
1302 });
1303
1304 // Construct the destination buffer.
1305 let mut buffer = [0u8; 256];
1306 let alignment = core::mem::align_of::<EfiMemoryDescriptor>();
1307 let size = core::mem::size_of::<EfiMemoryDescriptor>();
1308 let aligned = aligned_subslice(&mut buffer[..], alignment).unwrap();
1309 // Offset by 1 element so that we can make an unaligned buffer starting somewhere in
1310 // between.
1311 let start = aligned.get_mut(size..).unwrap();
1312 start[..size].clone_from_slice(descriptors[0].as_bytes());
1313 start[size..][..size].clone_from_slice(descriptors[1].as_bytes());
1314 // Pass an unaligned address.
1315 let desc = super::exit_boot_services(efi_entry, &mut aligned[size - 1..]).unwrap();
1316 // Validate that the returned `EfiMemoryMap` contains the correct EfiMemoryDescriptor.
1317 assert_eq!(desc.into_iter().map(|v| *v).collect::<Vec<_>>(), descriptors[..2].to_vec());
1318 // Validate that the returned `EfiMemoryMap` has the correct map_key.
1319 assert_eq!(desc.map_key(), map_key);
1320 });
1321 }
1322
1323 #[test]
test_create_event_with_notify_fn()1324 fn test_create_event_with_notify_fn() {
1325 run_test(|image_handle, systab_ptr| {
1326 let efi_entry = EfiEntry { image_handle, systab_ptr };
1327 let mut cb_impl = |_: EfiEvent| {};
1328 let mut cb = EventNotify::new(Tpl::Callback, &mut cb_impl);
1329 let event: EfiEvent = 1234usize as _;
1330 EFI_CALL_TRACES.with(|traces| {
1331 traces.borrow_mut().create_event_trace.outputs.push_back(event);
1332 });
1333 {
1334 let _ = efi_entry
1335 .system_table()
1336 .boot_services()
1337 .create_event(EventType::Timer, Some(&mut cb))
1338 .unwrap();
1339 }
1340 let efi_cb: EfiEventNotify = Some(efi_event_cb);
1341 EFI_CALL_TRACES.with(|traces| {
1342 assert_eq!(
1343 traces.borrow_mut().create_event_trace.inputs,
1344 [(
1345 EventType::Timer as _,
1346 Tpl::Callback as _,
1347 efi_cb,
1348 &mut cb as *mut _ as *mut _
1349 )]
1350 )
1351 });
1352 // Verify close_event is called.
1353 EFI_CALL_TRACES
1354 .with(|traces| assert_eq!(traces.borrow_mut().close_event_trace.inputs, [event]));
1355 });
1356 }
1357
1358 #[test]
test_create_event_wo_notify_fn()1359 fn test_create_event_wo_notify_fn() {
1360 run_test(|image_handle, systab_ptr| {
1361 let efi_entry = EfiEntry { image_handle, systab_ptr };
1362 let event: EfiEvent = 1234usize as _;
1363 EFI_CALL_TRACES.with(|traces| {
1364 traces.borrow_mut().create_event_trace.outputs.push_back(event);
1365 });
1366 {
1367 let _ = efi_entry
1368 .system_table()
1369 .boot_services()
1370 .create_event(EventType::Timer, None)
1371 .unwrap();
1372 }
1373 EFI_CALL_TRACES.with(|traces| {
1374 assert_eq!(
1375 traces.borrow_mut().create_event_trace.inputs,
1376 [(EventType::Timer as _, 0, None, null_mut())]
1377 )
1378 });
1379 });
1380 }
1381
1382 #[test]
test_check_event()1383 fn test_check_event() {
1384 run_test(|image_handle, systab_ptr| {
1385 let efi_entry = EfiEntry { image_handle, systab_ptr };
1386 let event: EfiEvent = 1234usize as _;
1387 EFI_CALL_TRACES.with(|traces| {
1388 traces.borrow_mut().create_event_trace.outputs.push_back(event);
1389 traces.borrow_mut().check_event_trace.outputs.push_back(EFI_STATUS_SUCCESS);
1390 traces.borrow_mut().check_event_trace.outputs.push_back(EFI_STATUS_NOT_READY);
1391 traces.borrow_mut().check_event_trace.outputs.push_back(EFI_STATUS_UNSUPPORTED);
1392 });
1393 let res = efi_entry
1394 .system_table()
1395 .boot_services()
1396 .create_event(EventType::Timer, None)
1397 .unwrap();
1398 assert_eq!(efi_entry.system_table().boot_services().check_event(&res), Ok(true));
1399 assert_eq!(efi_entry.system_table().boot_services().check_event(&res), Ok(false));
1400 assert!(efi_entry.system_table().boot_services().check_event(&res).is_err());
1401 });
1402 }
1403
1404 #[test]
test_efi_error()1405 fn test_efi_error() {
1406 let res: EfiResult<()> = Err(EFI_STATUS_NOT_FOUND.into());
1407 assert_eq!(res.unwrap_err().err(), ErrorTypes::EfiStatusError(14));
1408 }
1409 }
1410