Skip to content

Commit cfb2e11

Browse files
committed
uefi: Rewrite error handling
Enums in Rust are not extensible. `EFI_STATUS` must be able to be constructed from and represent any unsigned value. Remove `enum Error`, which only represents values defined in UEFI Appendix D, and use `Status`, which can represent any `EFI_STATUS`, for errors in Result. One trade off is it limits the use of `?` when converting `Status` from a call to a UEFI API to a `Result<T>`, such as when used in a Rust function that wraps the UEFI call. (Converting from `Result<T>` to `Status` is always possible though.) Other crates may now define `Status` to use with `Result<T>`, such as any defined in UEFI PI specification (if it even does) or custom OEM values defined in e.g. UEFI applications using the crate. The use of nightly features `try_trait_v2` and `control_flow_enum` are removed, allowing the base crate to be compiled using a stable toolchain. Ref: UEFI Specification, Version 2.9: Appendix D - Status Codes Signed-off-by: Tim Crawford <[email protected]>
1 parent 8e25274 commit cfb2e11

File tree

7 files changed

+302
-158
lines changed

7 files changed

+302
-158
lines changed

crates/uefi/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
#![allow(dead_code)]
22
#![allow(non_snake_case)]
33
#![no_std]
4-
#![feature(try_trait_v2)]
5-
#![feature(control_flow_enum)]
64

75
#[macro_use]
86
pub mod macros;

crates/uefi/src/status.rs

Lines changed: 214 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,132 +1,238 @@
1-
use core::ops::{ControlFlow, FromResidual, Try};
1+
// SPDX-License-Identifier: MIT
22

3-
pub const ERROR_BIT: usize = 1 << (usize::BITS - 1);
3+
//! # Status codes
4+
//!
5+
//! Status codes for UEFI interfaces.
6+
//!
7+
//! Most `usize` values can represent a valid status, but only a few are
8+
//! defined by the UEFI specification. Ranges are reserved for use by the UEFI
9+
//! specification itself, the [Platform Initialization specification][PI Spec],
10+
//! and OEM usage. On a 64-bit platform, these ranges are:
11+
//!
12+
//! | Start | End | Usage |
13+
//! | ----------------------- | ----------------------- | ------------- |
14+
//! | `0x0000_0000_0000_0000` | `0x1FFF_FFFF_FFFF_FFFF` | UEFI warnings |
15+
//! | `0x2000_0000_0000_0000` | `0x3FFF_FFFF_FFFF_FFFF` | PI warnings |
16+
//! | `0x4000_0000_0000_0000` | `0x7FFF_FFFF_FFFF_FFFF` | OEM warnings |
17+
//! | `0x8000_0000_0000_0000` | `0x9FFF_FFFF_FFFF_FFFF` | UEFI errors |
18+
//! | `0xA000_0000_0000_0000` | `0xBFFF_FFFF_FFFF_FFFF` | PI errors |
19+
//! | `0xC000_0000_0000_0000` | `0xCFFF_FFFF_FFFF_FFFF` | OEM errors |
20+
//!
21+
//! Error codes always have the highest bit set, while success and warnings
22+
//! always have the highest bit cleared.
23+
//!
24+
//! ## References
25+
//!
26+
//! - [UEFI Specification, Version 2.9][UEFI Spec]: Appendix D - Status Codes
27+
//!
28+
//! [PI Spec]: https://uefi.org/sites/default/files/resources/PI_Spec_1_7_A_final_May1.pdf
29+
//! [UEFI Spec]: https://uefi.org/sites/default/files/resources/UEFI_Spec_2_9_2021_03_18.pdf
430
31+
use core::fmt;
32+
33+
/// A value that represents success, a warning, or an error.
34+
#[must_use]
35+
#[repr(transparent)]
536
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
6-
#[repr(usize)]
7-
pub enum Error {
8-
Success,
9-
LoadError,
10-
InvalidParameter,
11-
Unsupported,
12-
BadBufferSize,
13-
BufferTooSmall,
14-
NotReady,
15-
DeviceError,
16-
WriteProtected,
17-
OutOfResources,
18-
VolumeCorrupted,
19-
VolumeFull,
20-
NoMedia,
21-
MediaChanged,
22-
NotFound,
23-
AccessDenied,
24-
NoResponse,
25-
NoMapping,
26-
Timeout,
27-
NotStarted,
28-
AlreadyStarted,
29-
Aborted,
30-
IcmpError,
31-
TftpError,
32-
ProtocolError,
33-
IncompatibleVersion,
34-
SecurityViolation,
35-
CrcError,
36-
EndOfMedia,
37-
Error29,
38-
Error30,
39-
EndOfFile,
40-
InvalidLanguage,
41-
CompromisedData,
42-
Error34,
43-
HttpError,
44-
Unknown,
37+
pub struct Status(pub usize);
38+
39+
impl Status {
40+
pub const ERROR_BIT: usize = 1 << (usize::BITS - 1);
41+
42+
// Success codes
43+
/// The operation completed successfully.
44+
pub const SUCCESS: Self = Self(0);
45+
46+
// Warning codes
47+
/// The string contained one or more characters that the device could not
48+
/// render and were skipped.
49+
pub const WARN_UNKNOWN_GLYPH: Self = Self(1);
50+
/// The handle was closed, but the file was not deleted.
51+
pub const WARN_DELETE_FAILURE: Self = Self(2);
52+
/// The handle was closed, but the data to the file was not flushed.
53+
pub const WARN_WRITE_FAILURE: Self = Self(3);
54+
/// The resulting buffer was too small, and the data was truncated to fit.
55+
pub const WARN_BUFFER_TOO_SMALL: Self = Self(4);
56+
/// The data has not been updated without the timeframe set by local policy
57+
/// for this type of data.
58+
pub const WARN_STALE_DATA: Self = Self(5);
59+
/// The resulting buffer contains a UEFI-compliant file system.
60+
pub const WARN_FILE_SYSTEM: Self = Self(6);
61+
/// The operation will be processed across a system reset.
62+
pub const WARN_RESET_REQUIRED: Self = Self(7);
63+
64+
// Error codes
65+
/// The image failed to load.
66+
pub const LOAD_ERROR: Self = Self(Self::ERROR_BIT | 1);
67+
/// A parameter was incorrect.
68+
pub const INVALID_PARAMETER: Self = Self(Self::ERROR_BIT | 2);
69+
/// The operation is not supported.
70+
pub const UNSUPPORTED: Self = Self(Self::ERROR_BIT | 3);
71+
/// The buffer was not the proper size for the request.
72+
pub const BAD_BUFFER_SIZE: Self = Self(Self::ERROR_BIT | 4);
73+
/// The buffer is not large enough to hold the requested data.
74+
pub const BUFFER_TOO_SMALL: Self = Self(Self::ERROR_BIT | 5);
75+
/// There is no data pending upon return.
76+
pub const NOT_READY: Self = Self(Self::ERROR_BIT | 6);
77+
/// The physical device reported an error while attempting the operation.
78+
pub const DEVICE_ERROR: Self = Self(Self::ERROR_BIT | 7);
79+
/// The device cannot be written to.
80+
pub const WRITE_PROTECTED: Self = Self(Self::ERROR_BIT | 8);
81+
/// A resource has run out.
82+
pub const OUT_OF_RESOURCES: Self = Self(Self::ERROR_BIT | 9);
83+
/// An inconsistency was detected on the file system.
84+
pub const VOLUME_CORRUPTED: Self = Self(Self::ERROR_BIT | 10);
85+
/// There is no more space on the file system.
86+
pub const VOLUME_FULL: Self = Self(Self::ERROR_BIT | 11);
87+
/// The device does not contain any medium to perform the operation.
88+
pub const NO_MEDIA: Self = Self(Self::ERROR_BIT | 12);
89+
/// The medium in the device has changed since the last access.
90+
pub const MEDIA_CHANGED: Self = Self(Self::ERROR_BIT | 13);
91+
/// The item was not found.
92+
pub const NOT_FOUND: Self = Self(Self::ERROR_BIT | 14);
93+
/// Access was denied.
94+
pub const ACCESS_DENIED: Self = Self(Self::ERROR_BIT | 15);
95+
/// The server was not found or did not respond to the request.
96+
pub const NO_RESPONSE: Self = Self(Self::ERROR_BIT | 16);
97+
/// A mapping to a device does not exist.
98+
pub const NO_MAPPING: Self = Self(Self::ERROR_BIT | 17);
99+
/// The timeout time expired.
100+
pub const TIMEOUT: Self = Self(Self::ERROR_BIT | 18);
101+
/// The protocol has not been started.
102+
pub const NOT_STARTED: Self = Self(Self::ERROR_BIT | 19);
103+
/// The protocol has already been started.
104+
pub const ALREADY_STARTED: Self = Self(Self::ERROR_BIT | 20);
105+
/// The operation was aborted.
106+
pub const ABORTED: Self = Self(Self::ERROR_BIT | 21);
107+
/// An ICMP error occurred during the network operation.
108+
pub const ICMP_ERROR: Self = Self(Self::ERROR_BIT | 22);
109+
/// A TFTP error occurred during the network operation.
110+
pub const TFTP_ERROR: Self = Self(Self::ERROR_BIT | 23);
111+
/// A protocol error occurred during the network operation.
112+
pub const PROTOCOL_ERROR: Self = Self(Self::ERROR_BIT | 24);
113+
/// The function encountered an internal version that was incompatible with
114+
/// a version requested by the caller.
115+
pub const INCOMPATIBLE_VERSION: Self = Self(Self::ERROR_BIT | 25);
116+
/// The function was not performed due to a security violation.
117+
pub const SECURITY_VIOLATION: Self = Self(Self::ERROR_BIT | 26);
118+
/// A CRC error was detected.
119+
pub const CRC_ERROR: Self = Self(Self::ERROR_BIT | 27);
120+
/// Beginning or end of media was reached.
121+
pub const END_OF_MEDIA: Self = Self(Self::ERROR_BIT | 28);
122+
// No meaning defined for 29-30.
123+
/// The end of the file was reached.
124+
pub const END_OF_FILE: Self = Self(Self::ERROR_BIT | 31);
125+
/// The language specified was invalid.
126+
pub const INVALID_LANGUAGE: Self = Self(Self::ERROR_BIT | 32);
127+
/// The security status of the data is unknown or compromised and the data
128+
/// must be updated or replaced to restore a valid security status.
129+
pub const COMPROMISED_DATA: Self = Self(Self::ERROR_BIT | 33);
130+
/// There is an address conflict allocation.
131+
pub const IP_ADDRESS_CONFLICT: Self = Self(Self::ERROR_BIT | 34);
132+
/// An HTTP error occurred during the network operation.
133+
pub const HTTP_ERROR: Self = Self(Self::ERROR_BIT | 35);
134+
135+
/// Returns true if the value represents a success.
136+
pub fn is_success(&self) -> bool {
137+
self == &Self::SUCCESS
138+
}
139+
140+
/// Returns true if the value represents an error.
141+
pub fn is_error(&self) -> bool {
142+
(self.0 & Self::ERROR_BIT) == Self::ERROR_BIT
143+
}
144+
145+
/// Returns true if the value represents a warning.
146+
pub fn is_warning(&self) -> bool {
147+
!self.is_error() && !self.is_success()
148+
}
45149
}
46150

47-
impl From<usize> for Error {
151+
impl From<usize> for Status {
48152
fn from(value: usize) -> Self {
49-
use self::Error::*;
50-
match value {
51-
0 => Success,
52-
1 => LoadError,
53-
2 => InvalidParameter,
54-
3 => Unsupported,
55-
4 => BadBufferSize,
56-
5 => BufferTooSmall,
57-
6 => NotReady,
58-
7 => DeviceError,
59-
8 => WriteProtected,
60-
9 => OutOfResources,
61-
10 => VolumeCorrupted,
62-
11 => VolumeFull,
63-
12 => NoMedia,
64-
13 => MediaChanged,
65-
14 => NotFound,
66-
15 => AccessDenied,
67-
16 => NoResponse,
68-
17 => NoMapping,
69-
18 => Timeout,
70-
19 => NotStarted,
71-
20 => AlreadyStarted,
72-
21 => Aborted,
73-
22 => IcmpError,
74-
23 => TftpError,
75-
24 => ProtocolError,
76-
25 => IncompatibleVersion,
77-
26 => SecurityViolation,
78-
27 => CrcError,
79-
28 => EndOfMedia,
80-
29 => Error29,
81-
30 => Error30,
82-
31 => EndOfFile,
83-
32 => InvalidLanguage,
84-
33 => CompromisedData,
85-
34 => Error34,
86-
35 => HttpError,
87-
_ => Unknown,
88-
}
153+
Self(value)
89154
}
90155
}
91156

92-
pub type Result<T> = ::core::result::Result<T, Error>;
157+
impl fmt::Display for Status {
158+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159+
match *self {
160+
Self::SUCCESS => write!(f, "success"),
93161

94-
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
95-
#[must_use]
96-
#[repr(transparent)]
97-
pub struct Status(pub usize);
162+
Self::WARN_UNKNOWN_GLYPH => write!(f, "unknown glyph"),
163+
Self::WARN_DELETE_FAILURE => write!(f, "delete failure"),
164+
Self::WARN_WRITE_FAILURE => write!(f, "write failure"),
165+
Self::WARN_STALE_DATA => write!(f, "stale data"),
166+
Self::WARN_FILE_SYSTEM => write!(f, "file system"),
167+
Self::WARN_RESET_REQUIRED => write!(f, "reset required"),
98168

99-
impl Status {
100-
pub fn new(value: usize) -> Self {
101-
Status(value)
169+
Self::LOAD_ERROR => write!(f, "load error"),
170+
Self::INVALID_PARAMETER => write!(f, "invalid paramter"),
171+
Self::UNSUPPORTED => write!(f, "unsupported"),
172+
Self::BAD_BUFFER_SIZE => write!(f, "bad buffer size"),
173+
Self::WARN_BUFFER_TOO_SMALL | Self::BUFFER_TOO_SMALL => write!(f, "buffer too small"),
174+
Self::NOT_READY => write!(f, "not ready"),
175+
Self::DEVICE_ERROR => write!(f, "device error"),
176+
Self::WRITE_PROTECTED => write!(f, "write protected"),
177+
Self::OUT_OF_RESOURCES => write!(f, "out of resources"),
178+
Self::VOLUME_CORRUPTED => write!(f, "volume corrupted"),
179+
Self::VOLUME_FULL => write!(f, "volume full"),
180+
Self::NO_MEDIA => write!(f, "no media"),
181+
Self::MEDIA_CHANGED => write!(f, "media changed"),
182+
Self::NOT_FOUND => write!(f, "not found"),
183+
Self::ACCESS_DENIED => write!(f, "acccess denied"),
184+
Self::NO_RESPONSE => write!(f, "no response"),
185+
Self::NO_MAPPING => write!(f, "no mapping"),
186+
Self::TIMEOUT => write!(f, "timeout"),
187+
Self::NOT_STARTED => write!(f, "not started"),
188+
Self::ALREADY_STARTED => write!(f, "already started"),
189+
Self::ABORTED => write!(f, "aborted"),
190+
Self::ICMP_ERROR => write!(f, "ICMP error"),
191+
Self::TFTP_ERROR => write!(f, "TFTP error"),
192+
Self::PROTOCOL_ERROR => write!(f, "protocol error"),
193+
Self::INCOMPATIBLE_VERSION => write!(f, "incompatible version"),
194+
Self::SECURITY_VIOLATION => write!(f, "security violation"),
195+
Self::CRC_ERROR => write!(f, "CRC error"),
196+
Self::END_OF_MEDIA => write!(f, "end of media"),
197+
Self::END_OF_FILE => write!(f, "end of file"),
198+
Self::INVALID_LANGUAGE => write!(f, "invalid language"),
199+
Self::COMPROMISED_DATA => write!(f, "compromised data"),
200+
Self::IP_ADDRESS_CONFLICT => write!(f, "IP address conflict"),
201+
Self::HTTP_ERROR => write!(f, "HTTP error"),
202+
203+
_ => write!(f, "{:#X}", self.0),
204+
}
102205
}
103206
}
104207

105-
impl Try for Status {
106-
type Output = usize;
107-
type Residual = Error;
108-
109-
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
110-
if self.0 & ERROR_BIT == 0 {
111-
ControlFlow::Continue(self.0)
112-
} else {
113-
ControlFlow::Break(Error::from(self.0 & !ERROR_BIT))
114-
}
208+
impl fmt::LowerHex for Status {
209+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210+
fmt::LowerHex::fmt(&self.0, f)
115211
}
212+
}
116213

117-
fn from_output(v: Self::Output) -> Self {
118-
Self(v & !ERROR_BIT)
214+
impl fmt::UpperHex for Status {
215+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
216+
fmt::UpperHex::fmt(&self.0, f)
119217
}
120218
}
121219

122-
impl FromResidual for Status {
123-
fn from_residual(v: Error) -> Self {
124-
Self(v as usize | ERROR_BIT)
220+
pub type Result<T> = core::result::Result<T, Status>;
221+
222+
impl From<Status> for Result<()> {
223+
fn from(status: Status) -> Self {
224+
match status {
225+
Status::SUCCESS => Ok(()),
226+
e => Err(e),
227+
}
125228
}
126229
}
127230

128-
impl<T> FromResidual<Error> for Result<T> {
129-
fn from_residual(err: Error) -> Self {
130-
Err(err)
231+
impl<T> From<Result<T>> for Status {
232+
fn from(res: Result<T>) -> Self {
233+
match res {
234+
Ok(_) => Status::SUCCESS,
235+
Err(e) => e,
236+
}
131237
}
132238
}

crates/uefi_alloc/src/lib.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
#![feature(allocator_api)]
2-
#![feature(try_trait_v2)]
3-
#![feature(control_flow_enum)]
42
#![no_std]
53

64
use core::alloc::{GlobalAlloc, Layout};
7-
use core::ops::{ControlFlow, Try};
85
use core::ptr::{self, NonNull};
96
use uefi::memory::MemoryType;
7+
use uefi::status::Status;
108
use uefi::system::SystemTable;
119

1210
static mut UEFI: Option<NonNull<SystemTable>> = None;
@@ -25,12 +23,11 @@ unsafe impl GlobalAlloc for Allocator {
2523
MemoryType::EfiLoaderData,
2624
layout.size(),
2725
&mut ptr,
28-
)
29-
.branch();
26+
);
3027

3128
match res {
32-
ControlFlow::Continue(_) => ptr as *mut u8,
33-
ControlFlow::Break(_) => ptr::null_mut(),
29+
Status::SUCCESS => ptr as *mut u8,
30+
_ => ptr::null_mut(),
3431
}
3532
}
3633

0 commit comments

Comments
 (0)