7777
7878pub mod build;
7979pub mod text;
80+ pub mod util;
8081
8182mod device_path_gen;
8283pub use device_path_gen:: {
8384 acpi, bios_boot_spec, end, hardware, media, messaging, DevicePathNodeEnum ,
8485} ;
8586pub use uefi_raw:: protocol:: device_path:: { DeviceSubType , DeviceType } ;
8687
88+ use crate :: mem:: PoolAllocation ;
8789use crate :: proto:: { unsafe_protocol, ProtocolPointer } ;
8890use core:: ffi:: c_void;
8991use core:: fmt:: { self , Debug , Display , Formatter } ;
@@ -94,6 +96,7 @@ use ptr_meta::Pointee;
9496use {
9597 crate :: boot:: { self , OpenProtocolAttributes , OpenProtocolParams , ScopedProtocol , SearchType } ,
9698 crate :: proto:: device_path:: text:: { AllowShortcuts , DevicePathToText , DisplayOnly } ,
99+ crate :: proto:: device_path:: util:: DevicePathUtilities ,
97100 crate :: { CString16 , Identify } ,
98101 alloc:: borrow:: ToOwned ,
99102 alloc:: boxed:: Box ,
@@ -108,6 +111,30 @@ opaque_type! {
108111 pub struct FfiDevicePath ;
109112}
110113
114+ /// Device path allocated from UEFI pool memory.
115+ #[ derive( Debug ) ]
116+ pub struct PoolDevicePath ( pub ( crate ) PoolAllocation ) ;
117+
118+ impl Deref for PoolDevicePath {
119+ type Target = DevicePath ;
120+
121+ fn deref ( & self ) -> & Self :: Target {
122+ unsafe { DevicePath :: from_ffi_ptr ( self . 0 . as_ptr ( ) . as_ptr ( ) . cast ( ) ) }
123+ }
124+ }
125+
126+ /// Device path node allocated from UEFI pool memory.
127+ #[ derive( Debug ) ]
128+ pub struct PoolDevicePathNode ( pub ( crate ) PoolAllocation ) ;
129+
130+ impl Deref for PoolDevicePathNode {
131+ type Target = DevicePathNode ;
132+
133+ fn deref ( & self ) -> & Self :: Target {
134+ unsafe { DevicePathNode :: from_ffi_ptr ( self . 0 . as_ptr ( ) . as_ptr ( ) . cast ( ) ) }
135+ }
136+ }
137+
111138/// Header that appears at the start of every [`DevicePathNode`].
112139#[ derive( Clone , Copy , Debug , Eq , PartialEq ) ]
113140#[ repr( C , packed) ]
@@ -499,6 +526,25 @@ impl DevicePath {
499526 } )
500527 . map_err ( |_| DevicePathToTextError :: OutOfMemory )
501528 }
529+
530+ /// Allocates and returns a new [`DevicePath`] by copying this one and appending the given `right` path.
531+ #[ cfg( feature = "alloc" ) ]
532+ pub fn append_path ( & self , right : & Self ) -> Result < PoolDevicePath , DevicePathUtilitiesError > {
533+ open_utility_protocol ( ) ?
534+ . append_path ( self , right)
535+ . map_err ( |_| DevicePathUtilitiesError :: OutOfMemory )
536+ }
537+
538+ /// Allocates and returns a new [`DevicePath`] by copying this one and appending the given `right` node.
539+ #[ cfg( feature = "alloc" ) ]
540+ pub fn append_node (
541+ & self ,
542+ right : & DevicePathNode ,
543+ ) -> Result < PoolDevicePath , DevicePathUtilitiesError > {
544+ open_utility_protocol ( ) ?
545+ . append_node ( self , right)
546+ . map_err ( |_| DevicePathUtilitiesError :: OutOfMemory )
547+ }
502548}
503549
504550impl Debug for DevicePath {
@@ -745,6 +791,63 @@ fn open_text_protocol() -> Result<ScopedProtocol<DevicePathToText>, DevicePathTo
745791 . map_err ( DevicePathToTextError :: CantOpenProtocol )
746792}
747793
794+ /// Errors that may occur when working with the [`DevicePathUtilities`] protocol.
795+ ///
796+ /// These errors are typically encountered during operations involving device
797+ /// paths, such as appending or manipulating path segments.
798+ #[ derive( Debug ) ]
799+ pub enum DevicePathUtilitiesError {
800+ /// Can't locate a handle buffer with handles associated with the
801+ /// [`DevicePathUtilities`] protocol.
802+ CantLocateHandleBuffer ( crate :: Error ) ,
803+ /// No handle supporting the [`DevicePathUtilities`] protocol was found.
804+ NoHandle ,
805+ /// The handle supporting the [`DevicePathUtilities`] protocol exists but
806+ /// it could not be opened.
807+ CantOpenProtocol ( crate :: Error ) ,
808+ /// Memory allocation failed during device path operations.
809+ OutOfMemory ,
810+ }
811+
812+ impl Display for DevicePathUtilitiesError {
813+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
814+ write ! ( f, "{self:?}" )
815+ }
816+ }
817+
818+ impl core:: error:: Error for DevicePathUtilitiesError {
819+ fn source ( & self ) -> Option < & ( dyn core:: error:: Error + ' static ) > {
820+ match self {
821+ Self :: CantLocateHandleBuffer ( e) => Some ( e) ,
822+ Self :: CantOpenProtocol ( e) => Some ( e) ,
823+ _ => None ,
824+ }
825+ }
826+ }
827+
828+ /// Helper function to open the [`DevicePathUtilities`] protocol using the boot
829+ /// services.
830+ #[ cfg( feature = "alloc" ) ]
831+ fn open_utility_protocol ( ) -> Result < ScopedProtocol < DevicePathUtilities > , DevicePathUtilitiesError >
832+ {
833+ let & handle = boot:: locate_handle_buffer ( SearchType :: ByProtocol ( & DevicePathToText :: GUID ) )
834+ . map_err ( DevicePathUtilitiesError :: CantLocateHandleBuffer ) ?
835+ . first ( )
836+ . ok_or ( DevicePathUtilitiesError :: NoHandle ) ?;
837+
838+ unsafe {
839+ boot:: open_protocol :: < DevicePathUtilities > (
840+ OpenProtocolParams {
841+ handle,
842+ agent : boot:: image_handle ( ) ,
843+ controller : None ,
844+ } ,
845+ OpenProtocolAttributes :: GetProtocol ,
846+ )
847+ }
848+ . map_err ( DevicePathUtilitiesError :: CantOpenProtocol )
849+ }
850+
748851#[ cfg( test) ]
749852mod tests {
750853 use super :: * ;
0 commit comments