Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4038e0d

Browse files
committed
device_path: add to_string() for more convenience
1 parent 38f2c3a commit 4038e0d

File tree

3 files changed

+221
-52
lines changed

3 files changed

+221
-52
lines changed

‎CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- Added `Event::from_ptr`, `Event::as_ptr`, and `Handle::as_ptr`.
1010
- Added `ScopedProtocol::get` and `ScopedProtocol::get_mut` to access
1111
potentially-null interfaces without panicking.
12+
- `DevicePath::to_string` and `DevicePathNode::to_string`,
1213

1314
### Changed
1415
- Renamed `LoadImageSource::FromFilePath` to `LoadImageSource::FromDevicePath`
Lines changed: 86 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use alloc::string::ToString;
2+
use alloc::vec::Vec;
13
use uefi::prelude::*;
24
use uefi::proto::device_path::text::*;
35
use uefi::proto::device_path::{DevicePath, LoadedImageDevicePath};
@@ -7,57 +9,94 @@ use uefi::table::boot::BootServices;
79
pub fn test(image: Handle, bt: &BootServices) {
810
info!("Running device path protocol test");
911

10-
let loaded_image = bt
11-
.open_protocol_exclusive::<LoadedImage>(image)
12-
.expect("Failed to open LoadedImage protocol");
13-
14-
let device_path = bt
15-
.open_protocol_exclusive::<DevicePath>(loaded_image.device())
16-
.expect("Failed to open DevicePath protocol");
17-
18-
let device_path_to_text = bt
19-
.open_protocol_exclusive::<DevicePathToText>(
20-
bt.get_handle_for_protocol::<DevicePathToText>()
21-
.expect("Failed to get DevicePathToText handle"),
22-
)
23-
.expect("Failed to open DevicePathToText protocol");
24-
25-
let device_path_from_text = bt
26-
.open_protocol_exclusive::<DevicePathFromText>(
27-
bt.get_handle_for_protocol::<DevicePathFromText>()
28-
.expect("Failed to get DevicePathFromText handle"),
29-
)
30-
.expect("Failed to open DevicePathFromText protocol");
31-
32-
for path in device_path.node_iter() {
33-
info!(
34-
"path: type={:?}, subtype={:?}, length={}",
35-
path.device_type(),
36-
path.sub_type(),
37-
path.length(),
38-
);
12+
// test 1/2: test low-level API by directly opening all protocols
13+
{
14+
let loaded_image = bt
15+
.open_protocol_exclusive::<LoadedImage>(image)
16+
.expect("Failed to open LoadedImage protocol");
17+
18+
let device_path = bt
19+
.open_protocol_exclusive::<DevicePath>(loaded_image.device())
20+
.expect("Failed to open DevicePath protocol");
21+
22+
let device_path_to_text = bt
23+
.open_protocol_exclusive::<DevicePathToText>(
24+
bt.get_handle_for_protocol::<DevicePathToText>()
25+
.expect("Failed to get DevicePathToText handle"),
26+
)
27+
.expect("Failed to open DevicePathToText protocol");
28+
29+
let device_path_from_text = bt
30+
.open_protocol_exclusive::<DevicePathFromText>(
31+
bt.get_handle_for_protocol::<DevicePathFromText>()
32+
.expect("Failed to get DevicePathFromText handle"),
33+
)
34+
.expect("Failed to open DevicePathFromText protocol");
35+
36+
for path in device_path.node_iter() {
37+
info!(
38+
"path: type={:?}, subtype={:?}, length={}",
39+
path.device_type(),
40+
path.sub_type(),
41+
path.length(),
42+
);
3943

40-
let text = device_path_to_text
41-
.convert_device_node_to_text(bt, path, DisplayOnly(true), AllowShortcuts(false))
42-
.expect("Failed to convert device path to text");
43-
let text = &*text;
44-
info!("path name: {text}");
44+
let text = device_path_to_text
45+
.convert_device_node_to_text(bt, path, DisplayOnly(true), AllowShortcuts(false))
46+
.expect("Failed to convert device path to text");
47+
let text = &*text;
48+
info!("path name: {text}");
4549

46-
let convert = device_path_from_text
47-
.convert_text_to_device_node(text)
48-
.expect("Failed to convert text to device path");
49-
assert_eq!(path, convert);
50+
let convert = device_path_from_text
51+
.convert_text_to_device_node(text)
52+
.expect("Failed to convert text to device path");
53+
assert_eq!(path, convert);
54+
}
55+
56+
// Get the `LoadedImageDevicePath`. Verify it start with the same nodes as
57+
// `device_path`.
58+
let loaded_image_device_path = bt
59+
.open_protocol_exclusive::<LoadedImageDevicePath>(image)
60+
.expect("Failed to open LoadedImageDevicePath protocol");
61+
62+
for (n1, n2) in device_path
63+
.node_iter()
64+
.zip(loaded_image_device_path.node_iter())
65+
{
66+
assert_eq!(n1, n2);
67+
}
5068
}
5169

52-
// Get the `LoadedImageDevicePath`. Verify it start with the same nodes as
53-
// `device_path`.
54-
let loaded_image_device_path = bt
55-
.open_protocol_exclusive::<LoadedImageDevicePath>(image)
56-
.expect("Failed to open LoadedImageDevicePath protocol");
57-
for (n1, n2) in device_path
58-
.node_iter()
59-
.zip(loaded_image_device_path.node_iter())
70+
// test 2/2: test high-level to-string api
6071
{
61-
assert_eq!(n1, n2);
72+
let loaded_image_device_path = bt
73+
.open_protocol_exclusive::<LoadedImageDevicePath>(image)
74+
.expect("Failed to open LoadedImageDevicePath protocol");
75+
let device_path: &DevicePath = &loaded_image_device_path;
76+
77+
let path = device_path
78+
.node_iter()
79+
.map(|node| node.to_string(bt, DisplayOnly(false), AllowShortcuts(false)))
80+
.map(|str| str.unwrap().unwrap().to_string())
81+
.collect::<Vec<_>>();
82+
83+
assert_eq!(
84+
path.as_slice(),
85+
&[
86+
"PciRoot(0x0)",
87+
"Pci(0x1F,0x2)",
88+
"Sata(0x0,0xFFFF,0x0)",
89+
"HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)",
90+
"\\efi\\boot\\test_runner.efi"
91+
]
92+
);
93+
94+
// Test that to_string works for device_paths
95+
let path = device_path
96+
.to_string(bt, DisplayOnly(false), AllowShortcuts(false))
97+
.unwrap()
98+
.unwrap()
99+
.to_string();
100+
assert_eq!(path, "PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x0,0xFFFF,0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)/\\efi\\boot\\test_runner.efi");
62101
}
63102
}

‎uefi/src/proto/device_path/mod.rs

Lines changed: 134 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,21 @@ mod device_path_gen;
8080
pub use device_path_gen::{
8181
acpi, bios_boot_spec, end, hardware, media, messaging, DevicePathNodeEnum,
8282
};
83-
#[cfg(feature = "alloc")]
84-
use {alloc::borrow::ToOwned, alloc::boxed::Box};
8583

86-
use crate::proto::{unsafe_protocol, ProtocolPointer};
8784
use core::ffi::c_void;
88-
use core::fmt::{self, Debug, Formatter};
85+
use core::fmt::{self, Debug, Display,Formatter};
8986
use core::mem;
9087
use core::ops::Deref;
9188
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;
9298

9399
opaque_type! {
94100
/// Opaque type that should be used to represent a pointer to a
@@ -113,7 +119,23 @@ pub struct DevicePathHeader {
113119
/// A single node within a [`DevicePath`].
114120
///
115121
/// 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`]
117139
///
118140
/// See the [module-level documentation] for more details.
119141
///
@@ -189,6 +211,31 @@ impl DevicePathNode {
189211
pub fn as_enum(&self) -> Result<DevicePathNodeEnum, NodeConversionError> {
190212
DevicePathNodeEnum::try_from(self)
191213
}
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+
}
192239
}
193240

194241
impl Debug for DevicePathNode {
@@ -377,6 +424,31 @@ impl DevicePath {
377424
let data = data.into_boxed_slice();
378425
unsafe { mem::transmute(data) }
379426
}
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+
}
380452
}
381453

382454
impl Debug for DevicePath {
@@ -700,6 +772,63 @@ impl Deref for LoadedImageDevicePath {
700772
}
701773
}
702774

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+
703832
#[cfg(test)]
704833
mod tests {
705834
use super::*;

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /