uefi/proto/device_path/text.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Protocols for converting between UEFI strings and [`DevicePath`]/[`DevicePathNode`].
4
5// Note on return types: the specification of the conversion functions
6// is a little unusual in that they return a pointer rather than
7// `EFI_STATUS`. A NULL pointer is used to indicate an error, and the
8// spec says that will only happen if the input pointer is null (which
9// can't happen here since we use references as input, not pointers), or
10// if there is insufficient memory. So we treat any NULL output as an
11// `OUT_OF_RESOURCES` error.
12
13use crate::data_types::PoolString;
14use crate::mem::PoolAllocation;
15use crate::proto::device_path::{DevicePath, DevicePathNode};
16use crate::proto::unsafe_protocol;
17use crate::{CStr16, Result, Status};
18use core::ptr::NonNull;
19use uefi_raw::protocol::device_path::{DevicePathFromTextProtocol, DevicePathToTextProtocol};
20
21use super::{PoolDevicePath, PoolDevicePathNode};
22
23/// Parameter for [`DevicePathToText`] that alters the output format.
24///
25/// * `DisplayOnly(false)` produces parseable output.
26/// * `DisplayOnly(true)` produces output that _may_ be shorter and not
27/// parseable.
28///
29/// Example of how a node's text representation may be altered by this
30/// parameter:
31/// * `DisplayOnly(false)`: `Ata(Primary,Master,0x1)`
32/// * `DisplayOnly(true)`: `Ata(0x1)`
33#[derive(Clone, Copy, Debug)]
34pub struct DisplayOnly(pub bool);
35
36/// Parameter for [`DevicePathToText`] that alters the output format.
37///
38/// * `AllowShortcuts(false)`: node names are based only on the node's type and
39/// subtype.
40/// * `AllowShortcuts(true)` _may_ alter the node name based on other fields
41/// within the node.
42///
43/// Example of how a node's text representation may be altered by this
44/// parameter:
45/// * `AllowShortcuts(false)`: `VenMsg(E0C14753-F9BE-11D2-9A0C-0090273FC14D)`
46/// * `AllowShortcuts(true)`: `VenPcAnsi()`
47#[derive(Clone, Copy, Debug)]
48pub struct AllowShortcuts(pub bool);
49
50/// Device Path to Text [`Protocol`].
51///
52/// Protocol for converting a [`DevicePath`] or `DevicePathNode`] to a string.
53///
54/// [`Protocol`]: uefi::proto::Protocol
55#[derive(Debug)]
56#[repr(transparent)]
57#[unsafe_protocol(DevicePathToTextProtocol::GUID)]
58pub struct DevicePathToText(DevicePathToTextProtocol);
59
60impl DevicePathToText {
61 /// Convert a [`DevicePathNode`] to a [`PoolString`].
62 ///
63 /// Returns an [`OUT_OF_RESOURCES`] error if there is insufficient
64 /// memory for the conversion.
65 ///
66 /// [`OUT_OF_RESOURCES`]: Status::OUT_OF_RESOURCES
67 pub fn convert_device_node_to_text(
68 &self,
69 device_node: &DevicePathNode,
70 display_only: DisplayOnly,
71 allow_shortcuts: AllowShortcuts,
72 ) -> Result<PoolString> {
73 let text = unsafe {
74 (self.0.convert_device_node_to_text)(
75 device_node.as_ffi_ptr().cast(),
76 display_only.0.into(),
77 allow_shortcuts.0.into(),
78 )
79 };
80 unsafe { PoolString::new(text.cast()) }
81 }
82
83 /// Convert a [`DevicePath`] to a [`PoolString`].
84 ///
85 /// Returns an [`OUT_OF_RESOURCES`] error if there is insufficient
86 /// memory for the conversion.
87 ///
88 /// [`OUT_OF_RESOURCES`]: Status::OUT_OF_RESOURCES
89 pub fn convert_device_path_to_text(
90 &self,
91 device_path: &DevicePath,
92 display_only: DisplayOnly,
93 allow_shortcuts: AllowShortcuts,
94 ) -> Result<PoolString> {
95 let text = unsafe {
96 (self.0.convert_device_path_to_text)(
97 device_path.as_ffi_ptr().cast(),
98 display_only.0.into(),
99 allow_shortcuts.0.into(),
100 )
101 };
102 unsafe { PoolString::new(text.cast()) }
103 }
104}
105
106/// Device Path from Text [`Protocol`].
107///
108/// Protocol for converting a string to a [`DevicePath`] or `DevicePathNode`].
109///
110/// [`Protocol`]: uefi::proto::Protocol
111#[derive(Debug)]
112#[repr(transparent)]
113#[unsafe_protocol("05c99a21-c70f-4ad2-8a5f-35df3343f51e")]
114pub struct DevicePathFromText(DevicePathFromTextProtocol);
115
116impl DevicePathFromText {
117 /// Convert a [`CStr16`] to a [`DevicePathNode`].
118 ///
119 /// If a non-device-node character is encountered, the rest of the string is ignored.
120 ///
121 /// Returns an [`OUT_OF_RESOURCES`] error if there is insufficient
122 /// memory for the conversion.
123 ///
124 /// [`OUT_OF_RESOURCES`]: Status::OUT_OF_RESOURCES
125 pub fn convert_text_to_device_node(
126 &self,
127 text_device_node: &CStr16,
128 ) -> Result<PoolDevicePathNode> {
129 unsafe {
130 let ptr = (self.0.convert_text_to_device_node)(text_device_node.as_ptr().cast());
131 NonNull::new(ptr.cast_mut())
132 .map(|p| PoolDevicePathNode(PoolAllocation::new(p.cast())))
133 .ok_or_else(|| Status::OUT_OF_RESOURCES.into())
134 }
135 }
136
137 /// Convert a [`CStr16`] to a [`DevicePath`].
138 ///
139 /// If a non-device-node character is encountered, the rest of the string is ignored.
140 ///
141 /// Returns an [`OUT_OF_RESOURCES`] error if there is insufficient
142 /// memory for the conversion.
143 ///
144 /// [`OUT_OF_RESOURCES`]: Status::OUT_OF_RESOURCES
145 pub fn convert_text_to_device_path(&self, text_device_path: &CStr16) -> Result<PoolDevicePath> {
146 unsafe {
147 let ptr = (self.0.convert_text_to_device_path)(text_device_path.as_ptr().cast());
148 NonNull::new(ptr.cast_mut())
149 .map(|p| PoolDevicePath(PoolAllocation::new(p.cast())))
150 .ok_or_else(|| Status::OUT_OF_RESOURCES.into())
151 }
152 }
153}