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::proto::device_path::{DevicePath, DevicePathNode};
12 use crate::proto::unsafe_protocol;
13 use crate::{boot, CStr16, Char16, Result, Status};
14 use core::ops::Deref;
15 use core::ptr::NonNull;
16 use uefi_raw::protocol::device_path::{DevicePathFromTextProtocol, DevicePathToTextProtocol};
17 
18 /// This struct is a wrapper of `display_only` parameter
19 /// used by Device Path to Text protocol.
20 ///
21 /// The `display_only` parameter controls whether the longer
22 /// (parseable)  or shorter (display-only) form of the conversion
23 /// is used. If `display_only` is TRUE, then the shorter text
24 /// representation of the display node is used, where applicable.
25 /// If `display_only` is FALSE, then the longer text representation
26 /// of the display node is used.
27 #[derive(Clone, Copy, Debug)]
28 pub struct DisplayOnly(pub bool);
29 
30 /// This struct is a wrapper of `allow_shortcuts` parameter
31 /// used by Device Path to Text protocol.
32 ///
33 /// The `allow_shortcuts` is FALSE, then the shortcut forms of
34 /// text representation for a device node cannot be used. A
35 /// shortcut form is one which uses information other than the
36 /// type or subtype. If `allow_shortcuts is TRUE, then the
37 /// shortcut forms of text representation for a device node
38 /// can be used, where applicable.
39 #[derive(Clone, Copy, Debug)]
40 pub struct AllowShortcuts(pub bool);
41 
42 /// Wrapper for a string internally allocated from
43 /// UEFI boot services memory.
44 #[derive(Debug)]
45 pub struct PoolString(NonNull<Char16>);
46 
47 impl PoolString {
new(text: *const Char16) -> Result<Self>48     fn new(text: *const Char16) -> Result<Self> {
49         NonNull::new(text.cast_mut())
50             .map(Self)
51             .ok_or(Status::OUT_OF_RESOURCES.into())
52     }
53 }
54 
55 impl Deref for PoolString {
56     type Target = CStr16;
57 
deref(&self) -> &Self::Target58     fn deref(&self) -> &Self::Target {
59         unsafe { CStr16::from_ptr(self.0.as_ptr()) }
60     }
61 }
62 
63 impl Drop for PoolString {
drop(&mut self)64     fn drop(&mut self) {
65         unsafe { boot::free_pool(self.0.cast()) }.expect("Failed to free pool [{addr:#?}]");
66     }
67 }
68 
69 /// Device Path to Text protocol.
70 ///
71 /// This protocol provides common utility functions for converting device
72 /// nodes and device paths to a text representation.
73 #[derive(Debug)]
74 #[repr(transparent)]
75 #[unsafe_protocol(DevicePathToTextProtocol::GUID)]
76 pub struct DevicePathToText(DevicePathToTextProtocol);
77 
78 impl DevicePathToText {
79     /// Convert a device node to its text representation.
80     ///
81     /// Returns an [`OUT_OF_RESOURCES`] error if there is insufficient
82     /// memory for the conversion.
83     ///
84     /// [`OUT_OF_RESOURCES`]: Status::OUT_OF_RESOURCES
convert_device_node_to_text( &self, device_node: &DevicePathNode, display_only: DisplayOnly, allow_shortcuts: AllowShortcuts, ) -> Result<PoolString>85     pub fn convert_device_node_to_text(
86         &self,
87         device_node: &DevicePathNode,
88         display_only: DisplayOnly,
89         allow_shortcuts: AllowShortcuts,
90     ) -> Result<PoolString> {
91         let text_device_node = unsafe {
92             (self.0.convert_device_node_to_text)(
93                 device_node.as_ffi_ptr().cast(),
94                 display_only.0,
95                 allow_shortcuts.0,
96             )
97         };
98         PoolString::new(text_device_node.cast())
99     }
100 
101     /// Convert a device path to its text representation.
102     ///
103     /// Returns an [`OUT_OF_RESOURCES`] error if there is insufficient
104     /// memory for the conversion.
105     ///
106     /// [`OUT_OF_RESOURCES`]: Status::OUT_OF_RESOURCES
convert_device_path_to_text( &self, device_path: &DevicePath, display_only: DisplayOnly, allow_shortcuts: AllowShortcuts, ) -> Result<PoolString>107     pub fn convert_device_path_to_text(
108         &self,
109         device_path: &DevicePath,
110         display_only: DisplayOnly,
111         allow_shortcuts: AllowShortcuts,
112     ) -> Result<PoolString> {
113         let text_device_path = unsafe {
114             (self.0.convert_device_path_to_text)(
115                 device_path.as_ffi_ptr().cast(),
116                 display_only.0,
117                 allow_shortcuts.0,
118             )
119         };
120         PoolString::new(text_device_path.cast())
121     }
122 }
123 
124 /// Device Path from Text protocol.
125 ///
126 /// This protocol provides common utilities for converting text to
127 /// device paths and device nodes.
128 #[derive(Debug)]
129 #[repr(transparent)]
130 #[unsafe_protocol("05c99a21-c70f-4ad2-8a5f-35df3343f51e")]
131 pub struct DevicePathFromText(DevicePathFromTextProtocol);
132 
133 impl DevicePathFromText {
134     /// Convert text to the binary representation of a device node.
135     ///
136     /// `text_device_node` is the text representation of a device node.
137     /// Conversion starts with the first character and continues until
138     /// the first non-device node character.
139     ///
140     /// Returns an [`OUT_OF_RESOURCES`] error if there is insufficient
141     /// memory for the conversion.
142     ///
143     /// [`OUT_OF_RESOURCES`]: Status::OUT_OF_RESOURCES
convert_text_to_device_node( &self, text_device_node: &CStr16, ) -> Result<&DevicePathNode>144     pub fn convert_text_to_device_node(
145         &self,
146         text_device_node: &CStr16,
147     ) -> Result<&DevicePathNode> {
148         unsafe {
149             let ptr = (self.0.convert_text_to_device_node)(text_device_node.as_ptr().cast());
150             if ptr.is_null() {
151                 Err(Status::OUT_OF_RESOURCES.into())
152             } else {
153                 Ok(DevicePathNode::from_ffi_ptr(ptr.cast()))
154             }
155         }
156     }
157 
158     /// Convert a text to its binary device path representation.
159     ///
160     /// `text_device_path` is the text representation of a device path.
161     /// Conversion starts with the first character and continues until
162     /// the first non-device path character.
163     ///
164     /// Returns an [`OUT_OF_RESOURCES`] error if there is insufficient
165     /// memory for the conversion.
166     ///
167     /// [`OUT_OF_RESOURCES`]: Status::OUT_OF_RESOURCES
convert_text_to_device_path(&self, text_device_path: &CStr16) -> Result<&DevicePath>168     pub fn convert_text_to_device_path(&self, text_device_path: &CStr16) -> Result<&DevicePath> {
169         unsafe {
170             let ptr = (self.0.convert_text_to_device_path)(text_device_path.as_ptr().cast());
171             if ptr.is_null() {
172                 Err(Status::OUT_OF_RESOURCES.into())
173             } else {
174                 Ok(DevicePath::from_ffi_ptr(ptr.cast()))
175             }
176         }
177     }
178 }
179