@@ -80,15 +80,21 @@ mod device_path_gen;
80
80
pub use device_path_gen:: {
81
81
acpi, bios_boot_spec, end, hardware, media, messaging, DevicePathNodeEnum ,
82
82
} ;
83
- #[ cfg( feature = "alloc" ) ]
84
- use { alloc:: borrow:: ToOwned , alloc:: boxed:: Box } ;
85
83
86
- use crate :: proto:: { unsafe_protocol, ProtocolPointer } ;
87
84
use core:: ffi:: c_void;
88
- use core:: fmt:: { self , Debug , Formatter } ;
85
+ use core:: fmt:: { self , Debug , Display , Formatter } ;
89
86
use core:: mem;
90
87
use core:: ops:: Deref ;
91
88
use ptr_meta:: Pointee ;
89
+ use uefi:: table:: boot:: ScopedProtocol ;
90
+ #[ cfg( feature = "alloc" ) ]
91
+ use { alloc:: borrow:: ToOwned , alloc:: boxed:: Box , alloc:: vec:: Vec , uefi:: CString16 } ;
92
+
93
+ use crate :: prelude:: BootServices ;
94
+ use crate :: proto:: device_path:: text:: { AllowShortcuts , DevicePathToText , DisplayOnly } ;
95
+ use crate :: proto:: { unsafe_protocol, ProtocolPointer } ;
96
+ use crate :: table:: boot:: { OpenProtocolAttributes , OpenProtocolParams , SearchType } ;
97
+ use crate :: Identify ;
92
98
93
99
opaque_type ! {
94
100
/// Opaque type that should be used to represent a pointer to a
@@ -113,7 +119,23 @@ pub struct DevicePathHeader {
113
119
/// A single node within a [`DevicePath`].
114
120
///
115
121
/// Each node starts with a [`DevicePathHeader`]. The rest of the data
116
- /// in the node depends on the type of node.
122
+ /// in the node depends on the type of node. You can "cast" a node to a specific
123
+ /// one like this:
124
+ /// ```no_run
125
+ /// use uefi::proto::device_path::DevicePath;
126
+ /// use uefi::proto::device_path::media::FilePath;
127
+ ///
128
+ /// let image_device_path: &DevicePath = unsafe { DevicePath::from_ffi_ptr(0x1337 as *const _) };
129
+ /// let file_path = image_device_path
130
+ /// .node_iter()
131
+ /// .find_map(|node| {
132
+ /// let node: &FilePath = node.try_into().ok()?;
133
+ /// let path = node.path_name().to_cstring16().ok()?;
134
+ /// Some(path.to_string().to_uppercase())
135
+ /// });
136
+ /// ```
137
+ /// More types are available in [`uefi::proto::device_path`]. Builder types
138
+ /// can be found in [`uefi::proto::device_path::builder`]
117
139
///
118
140
/// See the [module-level documentation] for more details.
119
141
///
@@ -189,6 +211,31 @@ impl DevicePathNode {
189
211
pub fn as_enum ( & self ) -> Result < DevicePathNodeEnum , NodeConversionError > {
190
212
DevicePathNodeEnum :: try_from ( self )
191
213
}
214
+
215
+ /// Transforms the device path node to its string representation using the
216
+ /// [`DevicePathToText`] protocol.
217
+ ///
218
+ /// The resulting string is only None, if there was not enough memory.
219
+ #[ cfg( feature = "alloc" ) ]
220
+ pub fn to_string (
221
+ & self ,
222
+ bs : & BootServices ,
223
+ display_only : DisplayOnly ,
224
+ allow_shortcuts : AllowShortcuts ,
225
+ ) -> Result < Option < CString16 > , DevicePathToTextError > {
226
+ let to_text_protocol = open_text_protocol ( bs) ?;
227
+
228
+ let cstring16 = to_text_protocol
229
+ . convert_device_node_to_text ( bs, self , display_only, allow_shortcuts)
230
+ . ok ( )
231
+ . map ( |pool_string| {
232
+ let cstr16 = & * pool_string;
233
+ // Another allocation; pool string is dropped. This overhead
234
+ // is negligible. CString16 is more convenient to use.
235
+ CString16 :: from ( cstr16)
236
+ } ) ;
237
+ Ok ( cstring16)
238
+ }
192
239
}
193
240
194
241
impl Debug for DevicePathNode {
@@ -377,6 +424,51 @@ impl DevicePath {
377
424
let data = data. into_boxed_slice ( ) ;
378
425
unsafe { mem:: transmute ( data) }
379
426
}
427
+
428
+ /// Variant of [`Self::to_string`] that creates a list of tuples of each
429
+ /// [`DevicePathNode`] and the corresponding text representation using the
430
+ /// [`DevicePathToText`] protocol.
431
+ ///
432
+ /// The resulting string is only None, if there was not enough memory.
433
+ #[ cfg( feature = "alloc" ) ]
434
+ pub fn to_node_string_pairs (
435
+ & self ,
436
+ bs : & BootServices ,
437
+ display_only : DisplayOnly ,
438
+ allow_shortcuts : AllowShortcuts ,
439
+ ) -> Result < Vec < ( & DevicePathNode , Option < CString16 > ) > , DevicePathToTextError > {
440
+ let mut vec = Vec :: new ( ) ;
441
+ for node in self . node_iter ( ) {
442
+ let string = node. to_string ( bs, display_only, allow_shortcuts) ?;
443
+ vec. push ( ( node, string) )
444
+ }
445
+ Ok ( vec)
446
+ }
447
+
448
+ /// Transforms the device path to its string representation using the
449
+ /// [`DevicePathToText`] protocol.
450
+ ///
451
+ /// The resulting string is only None, if there was not enough memory.
452
+ #[ cfg( feature = "alloc" ) ]
453
+ pub fn to_string (
454
+ & self ,
455
+ bs : & BootServices ,
456
+ display_only : DisplayOnly ,
457
+ allow_shortcuts : AllowShortcuts ,
458
+ ) -> Result < Option < CString16 > , DevicePathToTextError > {
459
+ let to_text_protocol = open_text_protocol ( bs) ?;
460
+
461
+ let cstring16 = to_text_protocol
462
+ . convert_device_path_to_text ( bs, self , display_only, allow_shortcuts)
463
+ . ok ( )
464
+ . map ( |pool_string| {
465
+ let cstr16 = & * pool_string;
466
+ // Another allocation; pool string is dropped. This overhead
467
+ // is negligible. CString16 is more convenient to use.
468
+ CString16 :: from ( cstr16)
469
+ } ) ;
470
+ Ok ( cstring16)
471
+ }
380
472
}
381
473
382
474
impl Debug for DevicePath {
@@ -700,6 +792,64 @@ impl Deref for LoadedImageDevicePath {
700
792
}
701
793
}
702
794
795
+ /// Errors that may happen when a device path is transformed to a string
796
+ /// representation using:
797
+ /// - [`DevicePath::to_node_string_pairs`]
798
+ /// - [`DevicePath::to_string`]
799
+ /// - [`DevicePathNode::to_string`]
800
+ #[ derive( Debug ) ]
801
+ pub enum DevicePathToTextError {
802
+ /// Can't locate a handle buffer with handles associated with the
803
+ /// [`DevicePathToText`] protocol.
804
+ CantLocateHandleBuffer ( crate :: Error ) ,
805
+ /// There is no handle supporting the [`DevicePathToText`] protocol.
806
+ NoHandle ,
807
+ /// The handle supporting the [`DevicePathToText`] protocol exists but it
808
+ /// could not be opened.
809
+ CantOpenProtocol ( crate :: Error ) ,
810
+ }
811
+
812
+ impl Display for DevicePathToTextError {
813
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
814
+ write ! ( f, "{self:?}" )
815
+ }
816
+ }
817
+
818
+ #[ cfg( feature = "unstable" ) ]
819
+ impl core:: error:: Error for DevicePathToTextError {
820
+ fn source ( & self ) -> Option < & ( dyn core:: error:: Error + ' static ) > {
821
+ match self {
822
+ DevicePathToTextError :: CantLocateHandleBuffer ( e) => Some ( e) ,
823
+ DevicePathToTextError :: CantOpenProtocol ( e) => Some ( e) ,
824
+ _ => None ,
825
+ }
826
+ }
827
+ }
828
+
829
+ /// Helper function to open the [`DevicePathToText`] protocol using the boot
830
+ /// services.
831
+ fn open_text_protocol (
832
+ bs : & BootServices ,
833
+ ) -> Result < ScopedProtocol < DevicePathToText > , DevicePathToTextError > {
834
+ let & handle = bs
835
+ . locate_handle_buffer ( SearchType :: ByProtocol ( & DevicePathToText :: GUID ) )
836
+ . map_err ( DevicePathToTextError :: CantLocateHandleBuffer ) ?
837
+ . first ( )
838
+ . ok_or ( DevicePathToTextError :: NoHandle ) ?;
839
+
840
+ unsafe {
841
+ bs. open_protocol :: < DevicePathToText > (
842
+ OpenProtocolParams {
843
+ handle,
844
+ agent : bs. image_handle ( ) ,
845
+ controller : None ,
846
+ } ,
847
+ OpenProtocolAttributes :: GetProtocol ,
848
+ )
849
+ }
850
+ . map_err ( DevicePathToTextError :: CantOpenProtocol )
851
+ }
852
+
703
853
#[ cfg( test) ]
704
854
mod tests {
705
855
use super :: * ;
0 commit comments