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