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