@@ -37,17 +37,20 @@ use crate::proto::{BootPolicy, Protocol, ProtocolPointer};
3737use crate :: runtime:: { self , ResetType } ;
3838use crate :: table:: Revision ;
3939use crate :: util:: opt_nonnull_to_ptr;
40- use crate :: { Char16 , Error , Event , Guid , Handle , Result , Status , StatusExt , table} ;
40+ use crate :: {
41+ Char16 , Error , Event , Guid , Handle , Identify , Result , ResultExt , Status , StatusExt , table,
42+ } ;
43+ #[ cfg( feature = "alloc" ) ]
44+ use alloc:: vec:: Vec ;
4145use core:: ffi:: c_void;
4246use core:: mem:: MaybeUninit ;
4347use core:: ops:: { Deref , DerefMut } ;
4448use core:: ptr:: { self , NonNull } ;
4549use core:: sync:: atomic:: { AtomicPtr , Ordering } ;
4650use core:: time:: Duration ;
4751use core:: { mem, slice} ;
52+ use log:: error;
4853use uefi_raw:: table:: boot:: { AllocateType as RawAllocateType , InterfaceType , TimerDelay } ;
49- #[ cfg( feature = "alloc" ) ]
50- use { alloc:: vec:: Vec , uefi:: ResultExt } ;
5154
5255/// Global image handle. This is only set by [`set_image_handle`], and it is
5356/// only read by [`image_handle`].
@@ -713,7 +716,12 @@ pub fn disconnect_controller(
713716 . to_result_with_err ( |_| ( ) )
714717}
715718
716- /// Installs a protocol interface on a device handle.
719+ /// Installs a protocol interface on a device handle. If no handle is
720+ /// specified, a new handle will be allocated and returned.
721+ ///
722+ /// It is recommended to use [`install_multiple_protocol_interface`] when you
723+ /// plan to install multiple protocols, as it performs more error checking
724+ /// and cleanup under the hood.
717725///
718726/// When a protocol interface is installed, firmware will call all functions
719727/// that have registered to wait for that interface to be installed.
@@ -736,7 +744,7 @@ pub fn disconnect_controller(
736744pub unsafe fn install_protocol_interface < P : ProtocolPointer + ?Sized > (
737745 handle : Option < Handle > ,
738746 interface : * const c_void ,
739- ) -> Result < Handle > {
747+ ) -> Result < Handle /* new if input was None */ > {
740748 unsafe { install_protocol_interface_by_guid ( handle, & P :: GUID , interface) }
741749}
742750
@@ -750,7 +758,7 @@ pub unsafe fn install_protocol_interface_by_guid(
750758 handle : Option < Handle > ,
751759 protocol : & Guid ,
752760 interface : * const c_void ,
753- ) -> Result < Handle > {
761+ ) -> Result < Handle /* new if Input was None */ > {
754762 let bt = boot_services_raw_panicking ( ) ;
755763 let bt = unsafe { bt. as_ref ( ) } ;
756764
@@ -852,6 +860,168 @@ pub unsafe fn uninstall_protocol_interface_by_guid(
852860 unsafe { ( bt. uninstall_protocol_interface ) ( handle. as_ptr ( ) , protocol, interface) . to_result ( ) }
853861}
854862
863+ /// Installs multiple protocol interfaces for a given handle at once, and
864+ /// reverts all operations if a single operation fails. If no handle is
865+ /// specified, a new handle will be allocated and returned.
866+ ///
867+ /// When a protocol interface is installed, firmware will call all functions
868+ /// that have registered to wait for that interface to be installed.
869+ ///
870+ /// As Rust is not having proper C variadic support, this function emulates the
871+ /// behavior of the `CoreInstallMultipleProtocolInterfaces` function from
872+ /// `edk2`. Effectively, the behavior is the same, but it doesn't use the
873+ /// corresponding boot service under the hood.
874+ ///
875+ /// # Arguments
876+ ///
877+ /// - `handle`: Either `None` to allocate a new handle or an existing handle.
878+ /// - `pairs`: Pairs of the [`Guid`] of the [`Protocol`] to install and the
879+ /// protocol implementation. The memory backing the implementation
880+ /// **must live as long as the handle!**. Callers need to ensure a matching
881+ /// lifetime!
882+ ///
883+ /// # Safety
884+ ///
885+ /// The caller is responsible for ensuring that they pass a valid `Guid` for `protocol`.
886+ ///
887+ /// # Errors
888+ ///
889+ /// * [`Status::OUT_OF_RESOURCES`]: failed to allocate a new handle.
890+ /// * [`Status::INVALID_PARAMETER`]: this protocol is already installed on the handle.
891+ /// * [`Status::ALREADY_STARTED`]: A Device Path Protocol instance was passed in that is already present in the handle database.
892+ pub unsafe fn install_multiple_protocol_interface (
893+ mut handle : Option < Handle > ,
894+ pairs : & [ ( & Guid , * const c_void ) ] ,
895+ ) -> Result < Handle /* new if input was None */ > {
896+ // TODO once Rust has sensible variadic argument support, we should
897+ // fallback to the correct boot service.
898+
899+ // Taken from edk2 source.
900+ const TPL_NOTIFY : Tpl = Tpl ( 16 ) ;
901+ let tpl = TPL_NOTIFY ;
902+ // SAFETY: We do not want our loop to be interrupted.
903+ let _old_tpl = unsafe { raise_tpl ( tpl) } ;
904+
905+ // Variables that are updated in the loop.
906+ let mut installed_count = 0 ;
907+ let mut status = Status :: SUCCESS ;
908+
909+ // try to install all interfaces and update `handle` if it is `None`
910+ for ( guid, interface) in pairs {
911+ // prevent multiple installations of the device path protocol on the
912+ // same handle:
913+ if let Some ( handle) = handle {
914+ if * guid == & DevicePath :: GUID
915+ && test_protocol_by_guid (
916+ guid,
917+ OpenProtocolParams {
918+ handle,
919+ agent : image_handle ( ) ,
920+ controller : None ,
921+ } ,
922+ ) ?
923+ {
924+ status = Status :: ALREADY_STARTED ;
925+ break ;
926+ }
927+ }
928+
929+ let result = unsafe { install_protocol_interface_by_guid ( handle, guid, * interface) } ;
930+
931+ match ( result, handle, installed_count) {
932+ ( Ok ( new_handle) , None , 0 ) => {
933+ handle = Some ( new_handle) ;
934+ }
935+ ( Ok ( _handle) , _, _) => { }
936+ ( Err ( err) , _, _) => {
937+ error ! ( "Failed to install protocol interface: {err}" ) ;
938+ // next, we need to uninstall for all succeeded iterations
939+ status = err. status ( ) ;
940+ break ;
941+ }
942+ }
943+
944+ installed_count += 1 ;
945+ }
946+
947+ if !status. is_success ( ) {
948+ // try to uninstall all that were just successfully installed
949+ for ( guid, interface) in pairs. iter ( ) . take ( installed_count) {
950+ let res =
951+ unsafe { uninstall_protocol_interface_by_guid ( handle. unwrap ( ) , guid, * interface) } ;
952+ if let Err ( e) = res {
953+ let handle_addr = & raw const * handle. as_ref ( ) . unwrap ( ) ;
954+ // We don't fail here, as this would break the contract of the
955+ // function.
956+ error ! (
957+ "Failed to uninstall interface after failed multiple install attempt: handle={handle_addr:?}, guid={}, interface={:?}, error={e}" ,
958+ guid, interface
959+ ) ;
960+ }
961+ }
962+
963+ Err ( status. into ( ) )
964+ } else {
965+ Ok ( handle. unwrap ( ) )
966+ }
967+ }
968+
969+ /// Removes one or more protocol interfaces into the boot services environment.
970+ ///
971+ /// If any errors are generated while the protocol interfaces are being
972+ /// uninstalled, then the protocols uninstalled prior to the error will be
973+ /// reinstalled.
974+ ///
975+ /// # Safety
976+ ///
977+ /// The caller is responsible for ensuring that there are no references to a protocol interface
978+ /// that has been removed. Some protocols may not be able to be removed as there is no information
979+ /// available regarding the references. This includes Console I/O, Block I/O, Disk I/o, and handles
980+ /// to device protocols.
981+ ///
982+ /// # Errors
983+ ///
984+ /// * [`Status::NOT_FOUND`]: the interface was not found on the handle.
985+ /// * [`Status::ACCESS_DENIED`]: the interface is still in use and cannot be uninstalled.
986+ pub unsafe fn uninstall_multiple_protocol_interface (
987+ handle : Handle ,
988+ pairs : & [ ( & Guid , * const c_void ) ] ,
989+ ) -> Result < ( ) > {
990+ let mut uninstalled_count = 0 ;
991+ let mut status = Status :: SUCCESS ;
992+
993+ // try to install all interfaces and update `handle` if it is `None`
994+ for ( guid, interface) in pairs {
995+ let result = unsafe { uninstall_protocol_interface_by_guid ( handle, guid, * interface) } ;
996+
997+ if result. is_err ( ) {
998+ // next, we need to install for all succeeded iterations
999+ status = result. status ( ) ;
1000+ break ;
1001+ }
1002+
1003+ uninstalled_count += 1 ;
1004+ }
1005+
1006+ if !status. is_success ( ) {
1007+ // try to uninstall all failed ones
1008+ for ( guid, interface) in pairs. iter ( ) . take ( uninstalled_count) {
1009+ let res = unsafe { install_protocol_interface_by_guid ( Some ( handle) , guid, * interface) } ;
1010+ if let Err ( e) = res {
1011+ let handle_addr = & raw const handle;
1012+ // We don't fail here, as this would break the contract of the
1013+ // function.
1014+ error ! (
1015+ "Failed to install interface after failed multiple uninstall attempt: handle={handle_addr:?}, guid={}, interface={:?}, error={e}" ,
1016+ guid, interface
1017+ ) ;
1018+ }
1019+ }
1020+ }
1021+
1022+ Ok ( ( ) )
1023+ }
1024+
8551025/// Registers `event` to be signaled whenever a protocol interface is registered for
8561026/// `protocol` by [`install_protocol_interface`] or [`reinstall_protocol_interface`].
8571027///
0 commit comments