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 | |
11 | use crate::{ |
12 | proto::device_path::{DevicePath, DevicePathNode, FfiDevicePath}, |
13 | proto::unsafe_protocol , |
14 | table::boot::BootServices, |
15 | CStr16, Char16, Result, Status, |
16 | }; |
17 | use 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)] |
29 | pub 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)] |
41 | pub struct AllowShortcuts(pub bool); |
42 | |
43 | /// Wrapper for a string internally allocated from |
44 | /// UEFI boot services memory. |
45 | pub struct PoolString<'a> { |
46 | boot_services: &'a BootServices, |
47 | text: *const Char16, |
48 | } |
49 | |
50 | impl<'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 | |
63 | impl<'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 | |
71 | impl 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" )] |
86 | pub 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 | |
99 | impl 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" )] |
153 | pub 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 | |
160 | impl 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 | |