Skip to content

Commit 68b204e

Browse files
Merge pull request #402 from supdrewin/text
`DevicePathToText` and `DevicePathFromText` Protocol
2 parents 3e10cba + a35737c commit 68b204e

File tree

3 files changed

+190
-2
lines changed

3 files changed

+190
-2
lines changed

src/proto/device_path.rs renamed to src/proto/device_path/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
//! the rest of the structure, and the `length` field indicates the
1717
//! total size of the Node including the header.
1818
19+
pub mod text;
20+
1921
use crate::{proto::Protocol, unsafe_guid};
2022
use core::slice;
2123

@@ -36,7 +38,7 @@ pub struct DevicePathHeader {
3638
/// This can be opened on a `LoadedImage.device()` handle using the `HandleProtocol` boot service.
3739
#[repr(C, packed)]
3840
#[unsafe_guid("09576e91-6d3f-11d2-8e39-00a0c969723b")]
39-
#[derive(Eq, Protocol)]
41+
#[derive(Debug, Eq, Protocol)]
4042
pub struct DevicePath {
4143
header: DevicePathHeader,
4244
}

src/proto/device_path/text.rs

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
//! `DevicePathToText` and `DevicePathFromText` Protocol
2+
3+
use crate::{
4+
proto::{device_path::DevicePath, Protocol},
5+
table::boot::BootServices,
6+
unsafe_guid, CStr16, Char16,
7+
};
8+
use core::ops::Deref;
9+
10+
/// This struct is a wrapper of `display_only` parameter
11+
/// used by Device Path to Text protocol.
12+
///
13+
/// The `display_only` parameter controls whether the longer
14+
/// (parseable) or shorter (display-only) form of the conversion
15+
/// is used. If `display_only` is TRUE, then the shorter text
16+
/// representation of the display node is used, where applicable.
17+
/// If `display_only` is FALSE, then the longer text representation
18+
/// of the display node is used.
19+
#[derive(Clone, Copy)]
20+
pub struct DisplayOnly(pub bool);
21+
22+
/// This struct is a wrapper of `allow_shortcuts` parameter
23+
/// used by Device Path to Text protocol.
24+
///
25+
/// The `allow_shortcuts` is FALSE, then the shortcut forms of
26+
/// text representation for a device node cannot be used. A
27+
/// shortcut form is one which uses information other than the
28+
/// type or subtype. If `allow_shortcuts is TRUE, then the
29+
/// shortcut forms of text representation for a device node
30+
/// can be used, where applicable.
31+
#[derive(Clone, Copy)]
32+
pub struct AllowShortcuts(pub bool);
33+
34+
/// Wrapper for a string internally allocated from
35+
/// UEFI boot services memory.
36+
pub struct PoolString<'a> {
37+
boot_services: &'a BootServices,
38+
text: *const Char16,
39+
}
40+
41+
impl<'a> PoolString<'a> {
42+
fn new(boot_services: &'a BootServices, text: *const Char16) -> Option<Self> {
43+
if text.is_null() {
44+
None
45+
} else {
46+
Some(Self {
47+
boot_services,
48+
text,
49+
})
50+
}
51+
}
52+
}
53+
54+
impl<'a> Deref for PoolString<'a> {
55+
type Target = CStr16;
56+
57+
fn deref(&self) -> &Self::Target {
58+
unsafe { CStr16::from_ptr(self.text) }
59+
}
60+
}
61+
62+
impl Drop for PoolString<'_> {
63+
fn drop(&mut self) {
64+
let addr = self.text as *mut u8;
65+
self.boot_services
66+
.free_pool(addr)
67+
.expect("Failed to free pool [{addr:#?}]");
68+
}
69+
}
70+
71+
/// Device Path to Text protocol.
72+
///
73+
/// This protocol provides common utility functions for converting device
74+
/// nodes and device paths to a text representation.
75+
#[repr(C)]
76+
#[unsafe_guid("8b843e20-8132-4852-90cc-551a4e4a7f1c")]
77+
#[derive(Protocol)]
78+
pub struct DevicePathToText {
79+
convert_device_node_to_text: unsafe extern "efiapi" fn(
80+
device_node: *const DevicePath,
81+
display_only: bool,
82+
allow_shortcuts: bool,
83+
) -> *const Char16,
84+
convert_device_path_to_text: unsafe extern "efiapi" fn(
85+
device_path: *const DevicePath,
86+
display_only: bool,
87+
allow_shortcuts: bool,
88+
) -> *const Char16,
89+
}
90+
91+
impl DevicePathToText {
92+
/// Convert a device node to its text representation.
93+
///
94+
/// Returns `None` if `device_node` was NULL or there was
95+
/// insufficient memory.
96+
pub fn convert_device_node_to_text<'boot>(
97+
&self,
98+
boot_services: &'boot BootServices,
99+
device_node: &DevicePath,
100+
display_only: DisplayOnly,
101+
allow_shortcuts: AllowShortcuts,
102+
) -> Option<PoolString<'boot>> {
103+
let text_device_node = unsafe {
104+
(self.convert_device_node_to_text)(device_node, display_only.0, allow_shortcuts.0)
105+
};
106+
PoolString::new(boot_services, text_device_node)
107+
}
108+
109+
/// Convert a device path to its text representation.
110+
///
111+
/// Returns `None` if `device_path` was NULL or there was
112+
/// insufficient memory.
113+
pub fn convert_device_path_to_text<'boot>(
114+
&self,
115+
boot_services: &'boot BootServices,
116+
device_path: &DevicePath,
117+
display_only: DisplayOnly,
118+
allow_shortcuts: AllowShortcuts,
119+
) -> Option<PoolString<'boot>> {
120+
let text_device_path = unsafe {
121+
(self.convert_device_path_to_text)(device_path, display_only.0, allow_shortcuts.0)
122+
};
123+
PoolString::new(boot_services, text_device_path)
124+
}
125+
}
126+
127+
/// Device Path from Text protocol.
128+
///
129+
/// This protocol provides common utilities for converting text to
130+
/// device paths and device nodes.
131+
#[repr(C)]
132+
#[unsafe_guid("05c99a21-c70f-4ad2-8a5f-35df3343f51e")]
133+
#[derive(Protocol)]
134+
pub struct DevicePathFromText {
135+
convert_text_to_device_node:
136+
unsafe extern "efiapi" fn(text_device_node: *const Char16) -> *const DevicePath,
137+
convert_text_to_device_path:
138+
unsafe extern "efiapi" fn(text_device_path: *const Char16) -> *const DevicePath,
139+
}
140+
141+
impl DevicePathFromText {
142+
/// Convert text to the binary representation of a device node.
143+
///
144+
/// `text_device_node` is the text representation of a device node.
145+
/// Conversion starts with the first character and continues until
146+
/// the first non-device node character.
147+
///
148+
/// Returns `None` if `text_device_node` was NULL or there was
149+
/// insufficient memory.
150+
pub fn convert_text_to_device_node(&self, text_device_node: &CStr16) -> Option<&DevicePath> {
151+
unsafe { (self.convert_text_to_device_node)(text_device_node.as_ptr()).as_ref() }
152+
}
153+
154+
/// Convert a text to its binary device path representation.
155+
///
156+
/// `text_device_path` is the text representation of a device path.
157+
/// Conversion starts with the first character and continues until
158+
/// the first non-device path character.
159+
///
160+
/// Returns `None` if `text_device_path` was NULL or there was
161+
/// insufficient memory.
162+
pub fn convert_text_to_device_path(&self, text_device_path: &CStr16) -> Option<&DevicePath> {
163+
unsafe { (self.convert_text_to_device_path)(text_device_path.as_ptr()).as_ref() }
164+
}
165+
}

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

+22-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use uefi::prelude::*;
2-
use uefi::proto::device_path::DevicePath;
2+
use uefi::proto::device_path::{text::*, DevicePath};
33
use uefi::proto::loaded_image::LoadedImage;
44
use uefi::table::boot::{BootServices, OpenProtocolAttributes, OpenProtocolParams};
55

@@ -30,12 +30,33 @@ pub fn test(image: Handle, bt: &BootServices) {
3030
.expect("Failed to open DevicePath protocol");
3131
let device_path = unsafe { &*device_path.interface.get() };
3232

33+
let device_path_to_text = bt
34+
.locate_protocol::<DevicePathToText>()
35+
.expect("Failed to open DevicePathToText protocol");
36+
let device_path_to_text = unsafe { &*device_path_to_text.get() };
37+
38+
let device_path_from_text = bt
39+
.locate_protocol::<DevicePathFromText>()
40+
.expect("Failed to open DevicePathFromText protocol");
41+
let device_path_from_text = unsafe { &*device_path_from_text.get() };
42+
3343
for path in device_path.iter() {
3444
info!(
3545
"path: type={:?}, subtype={:?}, length={}",
3646
path.device_type(),
3747
path.sub_type(),
3848
path.length(),
3949
);
50+
51+
let text = device_path_to_text
52+
.convert_device_path_to_text(bt, path, DisplayOnly(true), AllowShortcuts(false))
53+
.expect("Failed to convert device path to text");
54+
let text = &*text;
55+
info!("path name: {text}");
56+
57+
let convert = device_path_from_text
58+
.convert_text_to_device_path(text)
59+
.expect("Failed to convert text to device path");
60+
assert_eq!(path, convert);
4061
}
4162
}

0 commit comments

Comments
 (0)