//! 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>> { fn inner( guid: &mut Guid, boot_services: NonNull, 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::(), 0); let num_of_handles = buf_len / size_of::(); let mut buf: Vec = 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( handle: NonNull, mut protocol_guid: Guid, ) -> io::Result> { let boot_services: NonNull = 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, context: *mut crate::ffi::c_void, ) -> io::Result> { let boot_services: NonNull = 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) -> io::Result<()> { let boot_services: NonNull = 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(()) } } /// Get the Protocol for current system handle. /// Note: Some protocols need to be manually freed. It is the callers responsibility to do so. pub(crate) fn image_handle_protocol(protocol_guid: Guid) -> Option> { let system_handle = uefi::env::try_image_handle()?; open_protocol(system_handle, protocol_guid).ok() }