2022-12-18 09:54:54 +05:30
//! 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
2023-01-28 23:37:54 +05:30
//!
//! 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)
2022-12-18 09:54:54 +05:30
2023-03-25 14:20:49 +05:30
use r_efi ::efi ::{ self , Guid } ;
2022-12-18 09:54:54 +05:30
2023-01-28 23:37:54 +05:30
use crate ::mem ::{ size_of , MaybeUninit } ;
2022-12-18 09:54:54 +05:30
use crate ::os ::uefi ;
use crate ::ptr ::NonNull ;
2023-03-25 14:20:49 +05:30
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 " ) ;
2022-12-18 09:54:54 +05:30
2023-01-28 23:37:54 +05:30
/// Locate Handles with a particular Protocol GUID
2022-12-18 09:54:54 +05:30
/// Implemented using `EFI_BOOT_SERVICES.LocateHandles()`
2023-01-28 23:37:54 +05:30
///
/// Returns an array of [Handles](r_efi::efi::Handle) that support a specified protocol.
2022-12-18 09:54:54 +05:30
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 ,
)
} ;
2023-05-11 11:35:15 +05:30
if r . is_error ( ) { Err ( crate ::io ::Error ::from_raw_os_error ( r . as_usize ( ) ) ) } else { Ok ( ( ) ) }
2022-12-18 09:54:54 +05:30
}
2023-03-27 19:53:53 +05:30
let boot_services = boot_services ( ) . ok_or ( BOOT_SERVICES_UNAVAILABLE ) ? . cast ( ) ;
2022-12-18 09:54:54 +05:30
let mut buf_len = 0 usize ;
2023-01-28 23:37:54 +05:30
// This should always fail since the size of buffer is 0. This call should update the buf_len
// variable with the required buffer length
2022-12-18 09:54:54 +05:30
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
2023-05-21 14:26:59 +05:30
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 ) ;
2022-12-18 09:54:54 +05:30
match inner ( & mut guid , boot_services , & mut buf_len , buf . as_mut_ptr ( ) ) {
Ok ( ( ) ) = > {
2023-01-28 23:37:54 +05:30
// 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
2023-05-21 14:26:59 +05:30
unsafe { buf . set_len ( num_of_handles ) } ;
2023-01-28 23:37:54 +05:30
Ok ( buf . into_iter ( ) . filter_map ( | x | NonNull ::new ( x ) ) . collect ( ) )
2022-12-18 09:54:54 +05:30
}
Err ( e ) = > Err ( e ) ,
}
}
2023-01-28 23:37:54 +05:30
/// 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.
2022-12-18 09:54:54 +05:30
pub ( crate ) fn open_protocol < T > (
handle : NonNull < crate ::ffi ::c_void > ,
mut protocol_guid : Guid ,
) -> io ::Result < NonNull < T > > {
2023-03-27 19:53:53 +05:30
let boot_services : NonNull < efi ::BootServices > =
boot_services ( ) . ok_or ( BOOT_SERVICES_UNAVAILABLE ) ? . cast ( ) ;
2022-12-18 09:54:54 +05:30
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 ( ) {
2023-05-11 11:35:15 +05:30
Err ( crate ::io ::Error ::from_raw_os_error ( r . as_usize ( ) ) )
2022-12-18 09:54:54 +05:30
} else {
NonNull ::new ( unsafe { protocol . assume_init ( ) } )
. ok_or ( const_io_error! ( io ::ErrorKind ::Other , " null protocol " ) )
}
}
2023-03-25 14:20:49 +05:30
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 > > {
2023-03-27 19:53:53 +05:30
let boot_services : NonNull < efi ::BootServices > =
boot_services ( ) . ok_or ( BOOT_SERVICES_UNAVAILABLE ) ? . cast ( ) ;
2023-05-21 14:26:59 +05:30
let mut event : r_efi ::efi ::Event = crate ::ptr ::null_mut ( ) ;
2023-03-25 14:20:49 +05:30
let r = unsafe {
let create_event = ( * boot_services . as_ptr ( ) ) . create_event ;
2023-05-21 14:26:59 +05:30
( create_event ) ( signal , tpl , handler , context , & mut event )
2023-03-25 14:20:49 +05:30
} ;
if r . is_error ( ) {
2023-05-11 11:35:15 +05:30
Err ( crate ::io ::Error ::from_raw_os_error ( r . as_usize ( ) ) )
2023-03-25 14:20:49 +05:30
} else {
2023-05-21 14:26:59 +05:30
NonNull ::new ( event ) . ok_or ( const_io_error! ( io ::ErrorKind ::Other , " null protocol " ) )
2023-03-25 14:20:49 +05:30
}
2022-12-18 09:54:54 +05:30
}
2023-03-25 14:20:49 +05:30
2023-03-28 20:14:33 +05:30
/// # SAFETY
/// - The supplied event must be valid
pub ( crate ) unsafe fn close_event ( evt : NonNull < crate ::ffi ::c_void > ) -> io ::Result < ( ) > {
2023-03-27 19:53:53 +05:30
let boot_services : NonNull < efi ::BootServices > =
boot_services ( ) . ok_or ( BOOT_SERVICES_UNAVAILABLE ) ? . cast ( ) ;
2023-03-25 14:20:49 +05:30
let r = unsafe {
let close_event = ( * boot_services . as_ptr ( ) ) . close_event ;
( close_event ) ( evt . as_ptr ( ) )
} ;
2023-05-11 11:35:15 +05:30
if r . is_error ( ) { Err ( crate ::io ::Error ::from_raw_os_error ( r . as_usize ( ) ) ) } else { Ok ( ( ) ) }
2022-12-18 09:54:54 +05:30
}
2023-10-02 17:00:09 +05:30
/// 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 < T > ( protocol_guid : Guid ) -> Option < NonNull < T > > {
let system_handle = uefi ::env ::try_image_handle ( ) ? ;
open_protocol ( system_handle , protocol_guid ) . ok ( )
}