Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 0037af7

Browse files
Handle UEFI warnings more cleanly (#60)
UEFI warnings represent non-fatal failures. We used to handle them as total failures, now we let the user choose how he wants to handle them.
1 parent e85ca03 commit 0037af7

File tree

24 files changed

+299
-117
lines changed

24 files changed

+299
-117
lines changed

‎Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ edition = "2018"
88

99
[dependencies]
1010
bitflags = "1"
11+
log = { version = "0.4", default-features = false }
1112
ucs2 = "0.1"
1213

1314
[workspace]

‎src/error/completion.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
use super::Status;
2+
use log::warn;
3+
4+
/// This type is used when an UEFI operation has completed, but some non-fatal
5+
/// problems may have been encountered along the way
6+
#[must_use]
7+
#[derive(Clone, Copy, Debug, PartialEq)]
8+
pub enum Completion<T> {
9+
/// The operation completed without problems
10+
Success(T),
11+
12+
/// The operation completed, but some non-fatal issues were encountered
13+
Warning(T, Status),
14+
}
15+
16+
impl<T> Completion<T> {
17+
/// Split the completion into a (status, value) pair
18+
pub fn split(self) -> (T, Status) {
19+
match self {
20+
Completion::Success(res) => (res, Status::SUCCESS),
21+
Completion::Warning(res, stat) => (res, stat),
22+
}
23+
}
24+
25+
/// Access the inner value, logging the warning if there is any
26+
pub fn log(self) -> T {
27+
match self {
28+
Completion::Success(res) => res,
29+
Completion::Warning(res, stat) => {
30+
log_warning(stat);
31+
res
32+
}
33+
}
34+
}
35+
36+
/// Assume that no warning occured, panic if not
37+
pub fn unwrap(self) -> T {
38+
match self {
39+
Completion::Success(res) => res,
40+
Completion::Warning(_, w) => {
41+
unwrap_failed("Called `Completion::unwrap()` on a `Warning` value", w)
42+
}
43+
}
44+
}
45+
46+
/// Assume that no warning occured, panic with provided message if not
47+
pub fn expect(self, msg: &str) -> T {
48+
match self {
49+
Completion::Success(res) => res,
50+
Completion::Warning(_, w) => unwrap_failed(msg, w),
51+
}
52+
}
53+
54+
/// Transform the inner value without unwrapping the Completion
55+
pub fn map<U>(self, f: impl Fn(T) -> U) -> Completion<U> {
56+
match self {
57+
Completion::Success(res) => Completion::Success(f(res)),
58+
Completion::Warning(res, stat) => Completion::Warning(f(res), stat),
59+
}
60+
}
61+
62+
/// Merge this completion with a success or warning status
63+
///
64+
/// Since this type only has storage for one warning, if two warnings must
65+
/// be stored, one of them will be spilled into the logs.
66+
///
67+
pub fn with_status(self, extra_status: Status) -> Self {
68+
match self {
69+
Completion::Success(res) => {
70+
if extra_status.is_success() {
71+
Completion::Success(res)
72+
} else {
73+
Completion::Warning(res, extra_status)
74+
}
75+
}
76+
Completion::Warning(res, stat) => {
77+
if extra_status.is_success() {
78+
Completion::Warning(res, stat)
79+
} else {
80+
log_warning(stat);
81+
Completion::Warning(res, extra_status)
82+
}
83+
}
84+
}
85+
}
86+
}
87+
88+
impl<T> From<T> for Completion<T> {
89+
fn from(res: T) -> Self {
90+
Completion::Success(res)
91+
}
92+
}
93+
94+
// These are separate functions to reduce the code size of the methods
95+
#[inline(never)]
96+
#[cold]
97+
fn unwrap_failed(msg: &str, warning: Status) -> ! {
98+
panic!("{}: {:?}", msg, warning)
99+
}
100+
101+
#[inline(never)]
102+
#[cold]
103+
fn log_warning(warning: Status) {
104+
warn!("Encountered UEFI warning: {:?}", warning)
105+
}

‎src/error/mod.rs

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,55 @@
1-
use core::result;
2-
31
/// Definition of UEFI's standard status codes
42
mod status;
53
pub use self::status::Status;
64

5+
/// Completions are used to model operations which have completed, but may have
6+
/// encountered non-fatal errors ("warnings") along the way
7+
mod completion;
8+
pub use self::completion::Completion;
9+
710
/// Return type of many UEFI functions.
8-
pub type Result<T> = result::Result<T, Status>;
11+
pub type Result<T> = core::result::Result<Completion<T>, Status>;
12+
13+
/// Extension trait for Result which helps dealing with UEFI's warnings
14+
pub trait ResultExt<T> {
15+
/// Treat warnings as errors
16+
fn warning_as_error(self) -> core::result::Result<T, Status>;
17+
18+
/// Ignore warnings, keeping a trace of them in the logs
19+
fn log_warning(self) -> core::result::Result<T, Status>;
20+
21+
/// Expect success without warnings, panic otherwise
22+
fn unwrap_success(self) -> T;
23+
24+
/// Expect success without warnings, panic with provided message otherwise
25+
fn expect_success(self, msg: &str) -> T;
26+
27+
/// Transform the inner output, if any
28+
fn map_inner<U>(self, f: impl Fn(T) -> U) -> Result<U>;
29+
}
30+
31+
impl<T> ResultExt<T> for Result<T> {
32+
fn warning_as_error(self) -> core::result::Result<T, Status> {
33+
match self {
34+
Ok(Completion::Success(v)) => Ok(v),
35+
Ok(Completion::Warning(_, s)) => Err(s),
36+
Err(s) => Err(s),
37+
}
38+
}
39+
40+
fn log_warning(self) -> core::result::Result<T, Status> {
41+
self.map(|completion| completion.log())
42+
}
43+
44+
fn unwrap_success(self) -> T {
45+
self.unwrap().unwrap()
46+
}
47+
48+
fn expect_success(self, msg: &str) -> T {
49+
self.expect(msg).expect(msg)
50+
}
51+
52+
fn map_inner<U>(self, f: impl Fn(T) -> U) -> Result<U> {
53+
self.map(|completion| completion.map(f))
54+
}
55+
}

‎src/error/status.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::Result;
1+
use super::{Completion,Result};
22
use core::ops;
33
use ucs2;
44

@@ -129,9 +129,10 @@ impl Status {
129129
where
130130
F: FnOnce() -> T,
131131
{
132-
// FIXME: Is that the best way to handle warnings?
133132
if self.is_success() {
134-
Ok(f())
133+
Ok(Completion::Success(f()))
134+
} else if self.is_warning() {
135+
Ok(Completion::Warning(f(), self))
135136
} else {
136137
Err(self)
137138
}
@@ -146,7 +147,7 @@ impl Into<Result<()>> for Status {
146147
}
147148

148149
impl ops::Try for Status {
149-
type Ok = ();
150+
type Ok = Completion<()>;
150151
type Error = Status;
151152

152153
fn into_result(self) -> Result<()> {

‎src/lib.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
//! therefore all the network protocols will be unavailable.
2525
2626
#![feature(optin_builtin_traits)]
27-
#![feature(min_const_fn)]
28-
#![feature(tool_lints)]
2927
#![feature(try_trait)]
3028
#![no_std]
3129
// Enable some additional warnings and lints.
@@ -37,7 +35,7 @@ mod data_types;
3735
pub use self::data_types::{Event, Guid, Handle};
3836

3937
mod error;
40-
pub use self::error::{Result, Status};
38+
pub use self::error::{Completion,Result,ResultExt, Status};
4139

4240
pub mod table;
4341

‎src/prelude.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//!
33
//! This includes the system table types, `Status` codes, etc.
44
5-
pub use crate::Status;
5+
pub use crate::{ResultExt,Status};
66

77
// Import the basic table types.
88
pub use crate::table::{boot::BootServices, runtime::RuntimeServices, SystemTable};

‎src/proto/console/gop.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
//! but in practice there might be some extra padding used for efficiency.
2525
2626
use core::{ptr, slice};
27-
use crate::{Result, Status};
27+
use crate::{Completion,Result, Status};
2828

2929
/// Provides access to the video hardware's frame buffer.
3030
///
@@ -71,7 +71,7 @@ impl GraphicsOutput {
7171
}
7272

7373
/// Returns information about all available graphics modes.
74-
pub fn modes<'a>(&'a self) -> impl Iterator<Item = Mode> + 'a {
74+
pub fn modes<'a>(&'a self) -> impl Iterator<Item = Completion<Mode>> + 'a {
7575
ModeIter {
7676
gop: self,
7777
current: 0,
@@ -397,7 +397,7 @@ struct ModeIter<'a> {
397397
}
398398

399399
impl<'a> Iterator for ModeIter<'a> {
400-
type Item = Mode;
400+
type Item = Completion<Mode>;
401401

402402
fn next(&mut self) -> Option<Self::Item> {
403403
let index = self.current;

‎src/proto/console/pointer/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,8 @@ impl Pointer {
3737
let mut pointer_state = unsafe { mem::uninitialized() };
3838

3939
match (self.get_state)(self, &mut pointer_state) {
40-
Status::SUCCESS => Ok(Some(pointer_state)),
41-
Status::NOT_READY => Ok(None),
42-
error => Err(error),
40+
Status::NOT_READY => Ok(None.into()),
41+
other => other.into_with(|| Some(pointer_state)),
4342
}
4443
}
4544

‎src/proto/console/serial.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Abstraction over byte stream devices, also known as serial I/O devices.
22
33
use bitflags::bitflags;
4-
use crate::{Result, Status};
4+
use crate::{Completion,Result, Status};
55

66
/// Provides access to a serial I/O device.
77
///
@@ -83,14 +83,14 @@ impl Serial {
8383
/// Returns the number of bytes actually written to the device.
8484
/// In the case of a timeout, this number will be smaller than
8585
/// the buffer's size.
86+
///
87+
/// Unlike UEFI, we handle timeouts as warnings, not errors
8688
pub fn write(&mut self, data: &[u8]) -> Result<usize> {
8789
let mut buffer_size = data.len();
8890

89-
let status = (self.write)(self, &mut buffer_size, data.as_ptr());
90-
91-
match status {
92-
Status::SUCCESS | Status::TIMEOUT => Ok(buffer_size),
93-
err => Err(err),
91+
match (self.write)(self, &mut buffer_size, data.as_ptr()) {
92+
s @ Status::TIMEOUT => Ok(Completion::Warning(buffer_size, s)),
93+
other => other.into_with(|| buffer_size),
9494
}
9595
}
9696

@@ -99,14 +99,14 @@ impl Serial {
9999
/// Returns the number of bytes actually read from the device.
100100
/// In the case of a timeout or buffer overrun, this number will be smaller
101101
/// than the buffer's size.
102+
///
103+
/// Unlike UEFI, we handle timeouts as warnings, not errors
102104
pub fn read(&mut self, data: &mut [u8]) -> Result<usize> {
103105
let mut buffer_size = data.len();
104106

105-
let status = (self.read)(self, &mut buffer_size, data.as_mut_ptr());
106-
107-
match status {
108-
Status::SUCCESS | Status::TIMEOUT => Ok(buffer_size),
109-
err => Err(err),
107+
match (self.read)(self, &mut buffer_size, data.as_mut_ptr()) {
108+
s @ Status::TIMEOUT => Ok(Completion::Warning(buffer_size, s)),
109+
other => other.into_with(|| buffer_size),
110110
}
111111
}
112112

‎src/proto/console/text/input.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,8 @@ impl Input {
3434
let mut key = unsafe { mem::uninitialized() };
3535

3636
match (self.read_key_stroke)(self, &mut key) {
37-
Status::SUCCESS => Ok(Some(key)),
38-
Status::NOT_READY => Ok(None),
39-
error => Err(error),
37+
Status::NOT_READY => Ok(None.into()),
38+
other => other.into_with(|| Some(key)),
4039
}
4140
}
4241

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /