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 bace1c6

Browse files
committed
device_path: add to_string() and to_node_string_pairs()
1 parent a0085d0 commit bace1c6

File tree

3 files changed

+228
-54
lines changed

3 files changed

+228
-54
lines changed

‎CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- `DevicePath::to_boxed`, `DevicePath::to_owned`, and `DevicePath::as_bytes`
77
- `DevicePathInstance::to_boxed`, `DevicePathInstance::to_owned`, and `DevicePathInstance::as_bytes`
88
- `DevicePathNode::data`
9+
- `DevicePath::to_string`, `DevicePath::to_node_string_pairs`, and `DevicePathNode::to_string`,
910

1011
### Changed
1112
- Renamed `LoadImageSource::FromFilePath` to `LoadImageSource::FromDevicePath`

‎uefi-test-runner/src/proto/device_path.rs

Lines changed: 72 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,57 +7,80 @@ use uefi::table::boot::BootServices;
77
pub fn test(image: Handle, bt: &BootServices) {
88
info!("Running device path protocol test");
99

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-
);
39-
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}");
45-
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);
10+
// test 1/2: test low-level API by directly opening all protocols
11+
{
12+
let loaded_image = bt
13+
.open_protocol_exclusive::<LoadedImage>(image)
14+
.expect("Failed to open LoadedImage protocol");
15+
16+
let device_path = bt
17+
.open_protocol_exclusive::<DevicePath>(loaded_image.device())
18+
.expect("Failed to open DevicePath protocol");
19+
20+
let device_path_to_text = bt
21+
.open_protocol_exclusive::<DevicePathToText>(
22+
bt.get_handle_for_protocol::<DevicePathToText>()
23+
.expect("Failed to get DevicePathToText handle"),
24+
)
25+
.expect("Failed to open DevicePathToText protocol");
26+
27+
let device_path_from_text = bt
28+
.open_protocol_exclusive::<DevicePathFromText>(
29+
bt.get_handle_for_protocol::<DevicePathFromText>()
30+
.expect("Failed to get DevicePathFromText handle"),
31+
)
32+
.expect("Failed to open DevicePathFromText protocol");
33+
34+
for path in device_path.node_iter() {
35+
info!(
36+
"path: type={:?}, subtype={:?}, length={}",
37+
path.device_type(),
38+
path.sub_type(),
39+
path.length(),
40+
);
41+
42+
let text = device_path_to_text
43+
.convert_device_node_to_text(bt, path, DisplayOnly(true), AllowShortcuts(false))
44+
.expect("Failed to convert device path to text");
45+
let text = &*text;
46+
info!("path name: {text}");
47+
48+
let convert = device_path_from_text
49+
.convert_text_to_device_node(text)
50+
.expect("Failed to convert text to device path");
51+
assert_eq!(path, convert);
52+
}
53+
54+
// Get the `LoadedImageDevicePath`. Verify it start with the same nodes as
55+
// `device_path`.
56+
let loaded_image_device_path = bt
57+
.open_protocol_exclusive::<LoadedImageDevicePath>(image)
58+
.expect("Failed to open LoadedImageDevicePath protocol");
59+
60+
for (n1, n2) in device_path
61+
.node_iter()
62+
.zip(loaded_image_device_path.node_iter())
63+
{
64+
assert_eq!(n1, n2);
65+
}
5066
}
5167

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())
68+
// test 2/2: test high-level to-string api
6069
{
61-
assert_eq!(n1, n2);
70+
let loaded_image_device_path = bt
71+
.open_protocol_exclusive::<LoadedImageDevicePath>(image)
72+
.expect("Failed to open LoadedImageDevicePath protocol");
73+
let device_path: &DevicePath = &loaded_image_device_path;
74+
75+
// Now also test the higher level interface to the DevicePathToText protocol.
76+
// This invokes to_string() on each single node.
77+
let vec = device_path
78+
.to_node_string_pairs(bt, DisplayOnly(false), AllowShortcuts(false))
79+
.unwrap();
80+
assert_eq!(vec.len(), device_path.node_iter().count());
81+
82+
device_path
83+
.to_string(bt, DisplayOnly(false), AllowShortcuts(false))
84+
.unwrap();
6285
}
6386
}

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

Lines changed: 155 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, 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;
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,51 @@ impl DevicePath {
377424
let data = data.into_boxed_slice();
378425
unsafe { mem::transmute(data) }
379426
}
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+
}
380472
}
381473

382474
impl Debug for DevicePath {
@@ -700,6 +792,64 @@ impl Deref for LoadedImageDevicePath {
700792
}
701793
}
702794

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

0 commit comments

Comments
(0)

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