1 | use super::{Error, Result}; |
2 | use core::fmt::Debug; |
3 | |
4 | /// Bit indicating that an UEFI status code is an error |
5 | const ERROR_BIT: usize = 1 << (core::mem::size_of::<usize>() * 8 - 1); |
6 | |
7 | newtype_enum! { |
8 | /// UEFI uses status codes in order to report successes, errors, and warnings. |
9 | /// |
10 | /// Unfortunately, the spec allows and encourages implementation-specific |
11 | /// non-portable status codes. Therefore, these cannot be modeled as a Rust |
12 | /// enum, as injecting an unknown value in a Rust enum is undefined behaviour. |
13 | /// |
14 | /// For lack of a better option, we therefore model them as a newtype of usize. |
15 | /// |
16 | /// For a convenient integration into the Rust ecosystem, there are multiple |
17 | /// factory methods to convert a Status into a [`uefi::Result`]: |
18 | /// - [`Status::into_with`] |
19 | /// - [`Status::into_with_val`] |
20 | /// - [`Status::into_with_err`] |
21 | #[must_use ] |
22 | pub enum Status: usize => { |
23 | /// The operation completed successfully. |
24 | SUCCESS = 0, |
25 | |
26 | /// The string contained characters that could not be rendered and were skipped. |
27 | WARN_UNKNOWN_GLYPH = 1, |
28 | /// The handle was closed, but the file was not deleted. |
29 | WARN_DELETE_FAILURE = 2, |
30 | /// The handle was closed, but the data to the file was not flushed properly. |
31 | WARN_WRITE_FAILURE = 3, |
32 | /// The resulting buffer was too small, and the data was truncated. |
33 | WARN_BUFFER_TOO_SMALL = 4, |
34 | /// The data has not been updated within the timeframe set by local policy. |
35 | WARN_STALE_DATA = 5, |
36 | /// The resulting buffer contains UEFI-compliant file system. |
37 | WARN_FILE_SYSTEM = 6, |
38 | /// The operation will be processed across a system reset. |
39 | WARN_RESET_REQUIRED = 7, |
40 | |
41 | /// The image failed to load. |
42 | LOAD_ERROR = ERROR_BIT | 1, |
43 | /// A parameter was incorrect. |
44 | INVALID_PARAMETER = ERROR_BIT | 2, |
45 | /// The operation is not supported. |
46 | UNSUPPORTED = ERROR_BIT | 3, |
47 | /// The buffer was not the proper size for the request. |
48 | BAD_BUFFER_SIZE = ERROR_BIT | 4, |
49 | /// The buffer is not large enough to hold the requested data. |
50 | /// The required buffer size is returned in the appropriate parameter. |
51 | BUFFER_TOO_SMALL = ERROR_BIT | 5, |
52 | /// There is no data pending upon return. |
53 | NOT_READY = ERROR_BIT | 6, |
54 | /// The physical device reported an error while attempting the operation. |
55 | DEVICE_ERROR = ERROR_BIT | 7, |
56 | /// The device cannot be written to. |
57 | WRITE_PROTECTED = ERROR_BIT | 8, |
58 | /// A resource has run out. |
59 | OUT_OF_RESOURCES = ERROR_BIT | 9, |
60 | /// An inconstency was detected on the file system. |
61 | VOLUME_CORRUPTED = ERROR_BIT | 10, |
62 | /// There is no more space on the file system. |
63 | VOLUME_FULL = ERROR_BIT | 11, |
64 | /// The device does not contain any medium to perform the operation. |
65 | NO_MEDIA = ERROR_BIT | 12, |
66 | /// The medium in the device has changed since the last access. |
67 | MEDIA_CHANGED = ERROR_BIT | 13, |
68 | /// The item was not found. |
69 | NOT_FOUND = ERROR_BIT | 14, |
70 | /// Access was denied. |
71 | ACCESS_DENIED = ERROR_BIT | 15, |
72 | /// The server was not found or did not respond to the request. |
73 | NO_RESPONSE = ERROR_BIT | 16, |
74 | /// A mapping to a device does not exist. |
75 | NO_MAPPING = ERROR_BIT | 17, |
76 | /// The timeout time expired. |
77 | TIMEOUT = ERROR_BIT | 18, |
78 | /// The protocol has not been started. |
79 | NOT_STARTED = ERROR_BIT | 19, |
80 | /// The protocol has already been started. |
81 | ALREADY_STARTED = ERROR_BIT | 20, |
82 | /// The operation was aborted. |
83 | ABORTED = ERROR_BIT | 21, |
84 | /// An ICMP error occurred during the network operation. |
85 | ICMP_ERROR = ERROR_BIT | 22, |
86 | /// A TFTP error occurred during the network operation. |
87 | TFTP_ERROR = ERROR_BIT | 23, |
88 | /// A protocol error occurred during the network operation. |
89 | PROTOCOL_ERROR = ERROR_BIT | 24, |
90 | /// The function encountered an internal version that was |
91 | /// incompatible with a version requested by the caller. |
92 | INCOMPATIBLE_VERSION = ERROR_BIT | 25, |
93 | /// The function was not performed due to a security violation. |
94 | SECURITY_VIOLATION = ERROR_BIT | 26, |
95 | /// A CRC error was detected. |
96 | CRC_ERROR = ERROR_BIT | 27, |
97 | /// Beginning or end of media was reached |
98 | END_OF_MEDIA = ERROR_BIT | 28, |
99 | /// The end of the file was reached. |
100 | END_OF_FILE = ERROR_BIT | 31, |
101 | /// The language specified was invalid. |
102 | INVALID_LANGUAGE = ERROR_BIT | 32, |
103 | /// The security status of the data is unknown or compromised and |
104 | /// the data must be updated or replaced to restore a valid security status. |
105 | COMPROMISED_DATA = ERROR_BIT | 33, |
106 | /// There is an address conflict address allocation |
107 | IP_ADDRESS_CONFLICT = ERROR_BIT | 34, |
108 | /// A HTTP error occurred during the network operation. |
109 | HTTP_ERROR = ERROR_BIT | 35, |
110 | }} |
111 | |
112 | impl Status { |
113 | /// Returns true if status code indicates success. |
114 | #[inline ] |
115 | #[must_use ] |
116 | pub fn is_success(self) -> bool { |
117 | self == Status::SUCCESS |
118 | } |
119 | |
120 | /// Returns true if status code indicates a warning. |
121 | #[inline ] |
122 | #[must_use ] |
123 | pub fn is_warning(self) -> bool { |
124 | (self != Status::SUCCESS) && (self.0 & ERROR_BIT == 0) |
125 | } |
126 | |
127 | /// Returns true if the status code indicates an error. |
128 | #[inline ] |
129 | #[must_use ] |
130 | pub const fn is_error(self) -> bool { |
131 | self.0 & ERROR_BIT != 0 |
132 | } |
133 | |
134 | /// Converts this status code into a [`uefi::Result`] with a given `Ok` value. |
135 | /// |
136 | /// If the status does not indicate success, the status representing the specific error |
137 | /// code is embedded into the `Err` variant of type [`uefi::Error`]. |
138 | #[inline ] |
139 | pub fn into_with_val<T>(self, val: impl FnOnce() -> T) -> Result<T, ()> { |
140 | if self.is_success() { |
141 | Ok(val()) |
142 | } else { |
143 | Err(self.into()) |
144 | } |
145 | } |
146 | |
147 | /// Converts this status code into a [`uefi::Result`] with a given `Err` payload. |
148 | /// |
149 | /// If the status does not indicate success, the status representing the specific error |
150 | /// code is embedded into the `Err` variant of type [`uefi::Error`]. |
151 | #[inline ] |
152 | pub fn into_with_err<ErrData: Debug>( |
153 | self, |
154 | err: impl FnOnce(Status) -> ErrData, |
155 | ) -> Result<(), ErrData> { |
156 | if self.is_success() { |
157 | Ok(()) |
158 | } else { |
159 | Err(Error::new(self, err(self))) |
160 | } |
161 | } |
162 | |
163 | /// Convert this status code into a result with a given `Ok` value and `Err` payload. |
164 | /// |
165 | /// If the status does not indicate success, the status representing the specific error |
166 | /// code is embedded into the `Err` variant of type [`uefi::Error`]. |
167 | #[inline ] |
168 | pub fn into_with<T, ErrData: Debug>( |
169 | self, |
170 | val: impl FnOnce() -> T, |
171 | err: impl FnOnce(Status) -> ErrData, |
172 | ) -> Result<T, ErrData> { |
173 | if self.is_success() { |
174 | Ok(val()) |
175 | } else { |
176 | Err(Error::new(self, err(self))) |
177 | } |
178 | } |
179 | } |
180 | |
181 | // An UEFI status is equivalent to a Result with no data or error payload |
182 | impl From<Status> for Result<(), ()> { |
183 | #[inline ] |
184 | fn from(status: Status) -> Result<(), ()> { |
185 | status.into_with(|| (), |_| ()) |
186 | } |
187 | } |
188 | |
189 | impl core::fmt::Display for Status { |
190 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
191 | Debug::fmt(self, f) |
192 | } |
193 | } |
194 | |
195 | #[cfg (test)] |
196 | mod tests { |
197 | use super::*; |
198 | |
199 | #[test ] |
200 | fn test_status_to_result() { |
201 | assert!(Result::from(Status::SUCCESS).is_ok()); |
202 | assert!(Result::from(Status::WARN_DELETE_FAILURE).is_err()); |
203 | assert!(Result::from(Status::BUFFER_TOO_SMALL).is_err()); |
204 | |
205 | assert_eq!(Status::SUCCESS.into_with_val(|| 123).unwrap(), 123); |
206 | assert!(Status::WARN_DELETE_FAILURE.into_with_val(|| 123).is_err()); |
207 | assert!(Status::BUFFER_TOO_SMALL.into_with_val(|| 123).is_err()); |
208 | |
209 | assert!(Status::SUCCESS.into_with_err(|_| 123).is_ok()); |
210 | assert_eq!( |
211 | *Status::WARN_DELETE_FAILURE |
212 | .into_with_err(|_| 123) |
213 | .unwrap_err() |
214 | .data(), |
215 | 123 |
216 | ); |
217 | assert_eq!( |
218 | *Status::BUFFER_TOO_SMALL |
219 | .into_with_err(|_| 123) |
220 | .unwrap_err() |
221 | .data(), |
222 | 123 |
223 | ); |
224 | |
225 | assert_eq!(Status::SUCCESS.into_with(|| 123, |_| 456).unwrap(), 123); |
226 | assert_eq!( |
227 | *Status::WARN_DELETE_FAILURE |
228 | .into_with(|| 123, |_| 456) |
229 | .unwrap_err() |
230 | .data(), |
231 | 456 |
232 | ); |
233 | assert_eq!( |
234 | *Status::BUFFER_TOO_SMALL |
235 | .into_with(|| 123, |_| 456) |
236 | .unwrap_err() |
237 | .data(), |
238 | 456 |
239 | ); |
240 | } |
241 | } |
242 | |