1//! `DevicePathToText` and `DevicePathFromText` Protocol
2
3// Note on return types: the specification of the conversion functions
4// is a little unusual in that they return a pointer rather than
5// `EFI_STATUS`. A NULL pointer is used to indicate an error, and the
6// spec says that will only happen if the input pointer is null (which
7// can't happen here since we use references as input, not pointers), or
8// if there is insufficient memory. So we treat any NULL output as an
9// `OUT_OF_RESOURCES` error.
10
11use crate::{
12 proto::device_path::{DevicePath, DevicePathNode, FfiDevicePath},
13 proto::unsafe_protocol,
14 table::boot::BootServices,
15 CStr16, Char16, Result, Status,
16};
17use core::ops::Deref;
18
19/// This struct is a wrapper of `display_only` parameter
20/// used by Device Path to Text protocol.
21///
22/// The `display_only` parameter controls whether the longer
23/// (parseable) or shorter (display-only) form of the conversion
24/// is used. If `display_only` is TRUE, then the shorter text
25/// representation of the display node is used, where applicable.
26/// If `display_only` is FALSE, then the longer text representation
27/// of the display node is used.
28#[derive(Clone, Copy)]
29pub struct DisplayOnly(pub bool);
30
31/// This struct is a wrapper of `allow_shortcuts` parameter
32/// used by Device Path to Text protocol.
33///
34/// The `allow_shortcuts` is FALSE, then the shortcut forms of
35/// text representation for a device node cannot be used. A
36/// shortcut form is one which uses information other than the
37/// type or subtype. If `allow_shortcuts is TRUE, then the
38/// shortcut forms of text representation for a device node
39/// can be used, where applicable.
40#[derive(Clone, Copy)]
41pub struct AllowShortcuts(pub bool);
42
43/// Wrapper for a string internally allocated from
44/// UEFI boot services memory.
45pub struct PoolString<'a> {
46 boot_services: &'a BootServices,
47 text: *const Char16,
48}
49
50impl<'a> PoolString<'a> {
51 fn new(boot_services: &'a BootServices, text: *const Char16) -> Result<Self> {
52 if text.is_null() {
53 Err(Status::OUT_OF_RESOURCES.into())
54 } else {
55 Ok(Self {
56 boot_services,
57 text,
58 })
59 }
60 }
61}
62
63impl<'a> Deref for PoolString<'a> {
64 type Target = CStr16;
65
66 fn deref(&self) -> &Self::Target {
67 unsafe { CStr16::from_ptr(self.text) }
68 }
69}
70
71impl Drop for PoolString<'_> {
72 fn drop(&mut self) {
73 let addr: *mut u8 = self.text as *mut u8;
74 self.boot_services
75 .free_pool(addr)
76 .expect(msg:"Failed to free pool [{addr:#?}]");
77 }
78}
79
80/// Device Path to Text protocol.
81///
82/// This protocol provides common utility functions for converting device
83/// nodes and device paths to a text representation.
84#[repr(C)]
85#[unsafe_protocol("8b843e20-8132-4852-90cc-551a4e4a7f1c")]
86pub struct DevicePathToText {
87 convert_device_node_to_text: unsafe extern "efiapi" fn(
88 device_node: *const FfiDevicePath,
89 display_only: bool,
90 allow_shortcuts: bool,
91 ) -> *const Char16,
92 convert_device_path_to_text: unsafe extern "efiapi" fn(
93 device_path: *const FfiDevicePath,
94 display_only: bool,
95 allow_shortcuts: bool,
96 ) -> *const Char16,
97}
98
99impl DevicePathToText {
100 /// Convert a device node to its text representation.
101 ///
102 /// Returns an [`OUT_OF_RESOURCES`] error if there is insufficient
103 /// memory for the conversion.
104 ///
105 /// [`OUT_OF_RESOURCES`]: Status::OUT_OF_RESOURCES
106 pub fn convert_device_node_to_text<'boot>(
107 &self,
108 boot_services: &'boot BootServices,
109 device_node: &DevicePathNode,
110 display_only: DisplayOnly,
111 allow_shortcuts: AllowShortcuts,
112 ) -> Result<PoolString<'boot>> {
113 let text_device_node = unsafe {
114 (self.convert_device_node_to_text)(
115 device_node.as_ffi_ptr(),
116 display_only.0,
117 allow_shortcuts.0,
118 )
119 };
120 PoolString::new(boot_services, text_device_node)
121 }
122
123 /// Convert a device path to its text representation.
124 ///
125 /// Returns an [`OUT_OF_RESOURCES`] error if there is insufficient
126 /// memory for the conversion.
127 ///
128 /// [`OUT_OF_RESOURCES`]: Status::OUT_OF_RESOURCES
129 pub fn convert_device_path_to_text<'boot>(
130 &self,
131 boot_services: &'boot BootServices,
132 device_path: &DevicePath,
133 display_only: DisplayOnly,
134 allow_shortcuts: AllowShortcuts,
135 ) -> Result<PoolString<'boot>> {
136 let text_device_path = unsafe {
137 (self.convert_device_path_to_text)(
138 device_path.as_ffi_ptr(),
139 display_only.0,
140 allow_shortcuts.0,
141 )
142 };
143 PoolString::new(boot_services, text_device_path)
144 }
145}
146
147/// Device Path from Text protocol.
148///
149/// This protocol provides common utilities for converting text to
150/// device paths and device nodes.
151#[repr(C)]
152#[unsafe_protocol("05c99a21-c70f-4ad2-8a5f-35df3343f51e")]
153pub struct DevicePathFromText {
154 convert_text_to_device_node:
155 unsafe extern "efiapi" fn(text_device_node: *const Char16) -> *const FfiDevicePath,
156 convert_text_to_device_path:
157 unsafe extern "efiapi" fn(text_device_path: *const Char16) -> *const FfiDevicePath,
158}
159
160impl DevicePathFromText {
161 /// Convert text to the binary representation of a device node.
162 ///
163 /// `text_device_node` is the text representation of a device node.
164 /// Conversion starts with the first character and continues until
165 /// the first non-device node character.
166 ///
167 /// Returns an [`OUT_OF_RESOURCES`] error if there is insufficient
168 /// memory for the conversion.
169 ///
170 /// [`OUT_OF_RESOURCES`]: Status::OUT_OF_RESOURCES
171 pub fn convert_text_to_device_node(
172 &self,
173 text_device_node: &CStr16,
174 ) -> Result<&DevicePathNode> {
175 unsafe {
176 let ptr = (self.convert_text_to_device_node)(text_device_node.as_ptr());
177 if ptr.is_null() {
178 Err(Status::OUT_OF_RESOURCES.into())
179 } else {
180 Ok(DevicePathNode::from_ffi_ptr(ptr))
181 }
182 }
183 }
184
185 /// Convert a text to its binary device path representation.
186 ///
187 /// `text_device_path` is the text representation of a device path.
188 /// Conversion starts with the first character and continues until
189 /// the first non-device path character.
190 ///
191 /// Returns an [`OUT_OF_RESOURCES`] error if there is insufficient
192 /// memory for the conversion.
193 ///
194 /// [`OUT_OF_RESOURCES`]: Status::OUT_OF_RESOURCES
195 pub fn convert_text_to_device_path(&self, text_device_path: &CStr16) -> Result<&DevicePath> {
196 unsafe {
197 let ptr = (self.convert_text_to_device_path)(text_device_path.as_ptr());
198 if ptr.is_null() {
199 Err(Status::OUT_OF_RESOURCES.into())
200 } else {
201 Ok(DevicePath::from_ffi_ptr(ptr))
202 }
203 }
204 }
205}
206