diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:44 +0000 |
commit | c23a457e72abe608715ac76f076f47dc42af07a5 (patch) | |
tree | 2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /library/std/src/sys/uefi/helpers.rs | |
parent | Releasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-c23a457e72abe608715ac76f076f47dc42af07a5.tar.xz rustc-c23a457e72abe608715ac76f076f47dc42af07a5.zip |
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/std/src/sys/uefi/helpers.rs')
-rw-r--r-- | library/std/src/sys/uefi/helpers.rs | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/library/std/src/sys/uefi/helpers.rs b/library/std/src/sys/uefi/helpers.rs new file mode 100644 index 000000000..126661bfc --- /dev/null +++ b/library/std/src/sys/uefi/helpers.rs @@ -0,0 +1,141 @@ +//! Contains most of the shared UEFI specific stuff. Some of this might be moved to `std::os::uefi` +//! if needed but no point in adding extra public API when there is not Std support for UEFI in the +//! first place +//! +//! Some Nomenclature +//! * Protocol: +//! - Protocols serve to enable communication between separately built modules, including drivers. +//! - Every protocol has a GUID associated with it. The GUID serves as the name for the protocol. +//! - Protocols are produced and consumed. +//! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) + +use r_efi::efi::{self, Guid}; + +use crate::mem::{size_of, MaybeUninit}; +use crate::os::uefi; +use crate::ptr::NonNull; +use crate::{ + io::{self, const_io_error}, + os::uefi::env::boot_services, +}; + +const BOOT_SERVICES_UNAVAILABLE: io::Error = + const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available"); + +/// Locate Handles with a particular Protocol GUID +/// Implemented using `EFI_BOOT_SERVICES.LocateHandles()` +/// +/// Returns an array of [Handles](r_efi::efi::Handle) that support a specified protocol. +pub(crate) fn locate_handles(mut guid: Guid) -> io::Result<Vec<NonNull<crate::ffi::c_void>>> { + fn inner( + guid: &mut Guid, + boot_services: NonNull<r_efi::efi::BootServices>, + buf_size: &mut usize, + buf: *mut r_efi::efi::Handle, + ) -> io::Result<()> { + let r = unsafe { + ((*boot_services.as_ptr()).locate_handle)( + r_efi::efi::BY_PROTOCOL, + guid, + crate::ptr::null_mut(), + buf_size, + buf, + ) + }; + + if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } + + let boot_services = boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); + let mut buf_len = 0usize; + + // This should always fail since the size of buffer is 0. This call should update the buf_len + // variable with the required buffer length + match inner(&mut guid, boot_services, &mut buf_len, crate::ptr::null_mut()) { + Ok(()) => unreachable!(), + Err(e) => match e.kind() { + io::ErrorKind::FileTooLarge => {} + _ => return Err(e), + }, + } + + // The returned buf_len is in bytes + assert_eq!(buf_len % size_of::<r_efi::efi::Handle>(), 0); + let num_of_handles = buf_len / size_of::<r_efi::efi::Handle>(); + let mut buf: Vec<r_efi::efi::Handle> = Vec::with_capacity(num_of_handles); + match inner(&mut guid, boot_services, &mut buf_len, buf.as_mut_ptr()) { + Ok(()) => { + // This is safe because the call will succeed only if buf_len >= required length. + // Also, on success, the `buf_len` is updated with the size of bufferv (in bytes) written + unsafe { buf.set_len(num_of_handles) }; + Ok(buf.into_iter().filter_map(|x| NonNull::new(x)).collect()) + } + Err(e) => Err(e), + } +} + +/// Open Protocol on a handle. +/// Internally just a call to `EFI_BOOT_SERVICES.OpenProtocol()`. +/// +/// Queries a handle to determine if it supports a specified protocol. If the protocol is +/// supported by the handle, it opens the protocol on behalf of the calling agent. +pub(crate) fn open_protocol<T>( + handle: NonNull<crate::ffi::c_void>, + mut protocol_guid: Guid, +) -> io::Result<NonNull<T>> { + let boot_services: NonNull<efi::BootServices> = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); + let system_handle = uefi::env::image_handle(); + let mut protocol: MaybeUninit<*mut T> = MaybeUninit::uninit(); + + let r = unsafe { + ((*boot_services.as_ptr()).open_protocol)( + handle.as_ptr(), + &mut protocol_guid, + protocol.as_mut_ptr().cast(), + system_handle.as_ptr(), + crate::ptr::null_mut(), + r_efi::system::OPEN_PROTOCOL_GET_PROTOCOL, + ) + }; + + if r.is_error() { + Err(crate::io::Error::from_raw_os_error(r.as_usize())) + } else { + NonNull::new(unsafe { protocol.assume_init() }) + .ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) + } +} + +pub(crate) fn create_event( + signal: u32, + tpl: efi::Tpl, + handler: Option<efi::EventNotify>, + context: *mut crate::ffi::c_void, +) -> io::Result<NonNull<crate::ffi::c_void>> { + let boot_services: NonNull<efi::BootServices> = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); + let mut event: r_efi::efi::Event = crate::ptr::null_mut(); + let r = unsafe { + let create_event = (*boot_services.as_ptr()).create_event; + (create_event)(signal, tpl, handler, context, &mut event) + }; + if r.is_error() { + Err(crate::io::Error::from_raw_os_error(r.as_usize())) + } else { + NonNull::new(event).ok_or(const_io_error!(io::ErrorKind::Other, "null protocol")) + } +} + +/// # SAFETY +/// - The supplied event must be valid +pub(crate) unsafe fn close_event(evt: NonNull<crate::ffi::c_void>) -> io::Result<()> { + let boot_services: NonNull<efi::BootServices> = + boot_services().ok_or(BOOT_SERVICES_UNAVAILABLE)?.cast(); + let r = unsafe { + let close_event = (*boot_services.as_ptr()).close_event; + (close_event)(evt.as_ptr()) + }; + + if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } +} |