@@ -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 , 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::build`]
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,31 @@ impl DevicePath {
377
424
let data = data. into_boxed_slice ( ) ;
378
425
unsafe { mem:: transmute ( data) }
379
426
}
427
+
428
+ /// Transforms the device path to its string representation using the
429
+ /// [`DevicePathToText`] protocol.
430
+ ///
431
+ /// The resulting string is only None, if there was not enough memory.
432
+ #[ cfg( feature = "alloc" ) ]
433
+ pub fn to_string (
434
+ & self ,
435
+ bs : & BootServices ,
436
+ display_only : DisplayOnly ,
437
+ allow_shortcuts : AllowShortcuts ,
438
+ ) -> Result < Option < CString16 > , DevicePathToTextError > {
439
+ let to_text_protocol = open_text_protocol ( bs) ?;
440
+
441
+ let cstring16 = to_text_protocol
442
+ . convert_device_path_to_text ( bs, self , display_only, allow_shortcuts)
443
+ . ok ( )
444
+ . map ( |pool_string| {
445
+ let cstr16 = & * pool_string;
446
+ // Another allocation; pool string is dropped. This overhead
447
+ // is negligible. CString16 is more convenient to use.
448
+ CString16 :: from ( cstr16)
449
+ } ) ;
450
+ Ok ( cstring16)
451
+ }
380
452
}
381
453
382
454
impl Debug for DevicePath {
@@ -700,6 +772,63 @@ impl Deref for LoadedImageDevicePath {
700
772
}
701
773
}
702
774
775
+ /// Errors that may happen when a device path is transformed to a string
776
+ /// representation using:
777
+ /// - [`DevicePath::to_string`]
778
+ /// - [`DevicePathNode::to_string`]
779
+ #[ derive( Debug ) ]
780
+ pub enum DevicePathToTextError {
781
+ /// Can't locate a handle buffer with handles associated with the
782
+ /// [`DevicePathToText`] protocol.
783
+ CantLocateHandleBuffer ( crate :: Error ) ,
784
+ /// There is no handle supporting the [`DevicePathToText`] protocol.
785
+ NoHandle ,
786
+ /// The handle supporting the [`DevicePathToText`] protocol exists but it
787
+ /// could not be opened.
788
+ CantOpenProtocol ( crate :: Error ) ,
789
+ }
790
+
791
+ impl Display for DevicePathToTextError {
792
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
793
+ write ! ( f, "{self:?}" )
794
+ }
795
+ }
796
+
797
+ #[ cfg( feature = "unstable" ) ]
798
+ impl core:: error:: Error for DevicePathToTextError {
799
+ fn source ( & self ) -> Option < & ( dyn core:: error:: Error + ' static ) > {
800
+ match self {
801
+ DevicePathToTextError :: CantLocateHandleBuffer ( e) => Some ( e) ,
802
+ DevicePathToTextError :: CantOpenProtocol ( e) => Some ( e) ,
803
+ _ => None ,
804
+ }
805
+ }
806
+ }
807
+
808
+ /// Helper function to open the [`DevicePathToText`] protocol using the boot
809
+ /// services.
810
+ fn open_text_protocol (
811
+ bs : & BootServices ,
812
+ ) -> Result < ScopedProtocol < DevicePathToText > , DevicePathToTextError > {
813
+ let & handle = bs
814
+ . locate_handle_buffer ( SearchType :: ByProtocol ( & DevicePathToText :: GUID ) )
815
+ . map_err ( DevicePathToTextError :: CantLocateHandleBuffer ) ?
816
+ . first ( )
817
+ . ok_or ( DevicePathToTextError :: NoHandle ) ?;
818
+
819
+ unsafe {
820
+ bs. open_protocol :: < DevicePathToText > (
821
+ OpenProtocolParams {
822
+ handle,
823
+ agent : bs. image_handle ( ) ,
824
+ controller : None ,
825
+ } ,
826
+ OpenProtocolAttributes :: GetProtocol ,
827
+ )
828
+ }
829
+ . map_err ( DevicePathToTextError :: CantOpenProtocol )
830
+ }
831
+
703
832
#[ cfg( test) ]
704
833
mod tests {
705
834
use super :: * ;
0 commit comments