Skip to content

Commit efacc77

Browse files
authored
Type-safety cleanup and safe exit from boot services (#65)
- Improve type correctness in various places (no more usize as a hidden pointer or fake 'static lifetimes). - Tie protocols to the lifetime of the UEFI boot services so that the borrow checker can invalidate them once boot services are exited - Disable global services (logging, memory allocation...) on exit from boot services. - Implement a safe way to exit the UEFI boot services (and hide the unsafe one).
1 parent 5e95f52 commit efacc77

File tree

27 files changed

+597
-219
lines changed

27 files changed

+597
-219
lines changed

BUILDING.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ The following steps allow you to build a simple UEFI app.
1919

2020
```rust
2121
#[no_mangle]
22-
pub extern "win64" fn uefi_start(handle: Handle, system_table: &'static table::SystemTable) -> Status;
22+
pub extern "win64" fn uefi_start(handle: Handle, system_table: SystemTable<Boot>) -> Status;
2323
```
2424

2525
- Copy the `uefi-test-runner/x86_64-uefi.json` target file to your project's root.

src/error/completion.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ impl<T> Completion<T> {
5252
}
5353

5454
/// Transform the inner value without unwrapping the Completion
55-
pub fn map<U>(self, f: impl Fn(T) -> U) -> Completion<U> {
55+
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Completion<U> {
5656
match self {
5757
Completion::Success(res) => Completion::Success(f(res)),
5858
Completion::Warning(res, stat) => Completion::Warning(f(res), stat),

src/error/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub trait ResultExt<T> {
2525
fn expect_success(self, msg: &str) -> T;
2626

2727
/// Transform the inner output, if any
28-
fn map_inner<U>(self, f: impl Fn(T) -> U) -> Result<U>;
28+
fn map_inner<U>(self, f: impl FnOnce(T) -> U) -> Result<U>;
2929
}
3030

3131
impl<T> ResultExt<T> for Result<T> {
@@ -49,7 +49,7 @@ impl<T> ResultExt<T> for Result<T> {
4949
self.expect(msg).expect(msg)
5050
}
5151

52-
fn map_inner<U>(self, f: impl Fn(T) -> U) -> Result<U> {
52+
fn map_inner<U>(self, f: impl FnOnce(T) -> U) -> Result<U> {
5353
self.map(|completion| completion.map(f))
5454
}
5555
}

src/prelude.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@
55
pub use crate::{ResultExt, Status};
66

77
// Import the basic table types.
8-
pub use crate::table::{boot::BootServices, runtime::RuntimeServices, SystemTable};
8+
pub use crate::table::boot::BootServices;
9+
pub use crate::table::runtime::RuntimeServices;
10+
pub use crate::table::{Boot, SystemTable};

src/proto/console/gop.rs

+26-17
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use crate::{Completion, Result, Status};
3333
/// The GOP can be used to set the properties of the frame buffer,
3434
/// and also allows the app to access the in-memory buffer.
3535
#[repr(C)]
36-
pub struct GraphicsOutput {
36+
pub struct GraphicsOutput<'boot> {
3737
query_mode:
3838
extern "win64" fn(&GraphicsOutput, mode: u32, info_sz: &mut usize, &mut *const ModeInfo)
3939
-> Status,
@@ -42,7 +42,7 @@ pub struct GraphicsOutput {
4242
#[allow(clippy::type_complexity)]
4343
blt: extern "win64" fn(
4444
this: &mut GraphicsOutput,
45-
buffer: usize,
45+
buffer: *mut BltPixel,
4646
op: u32,
4747
source_x: usize,
4848
source_y: usize,
@@ -52,18 +52,18 @@ pub struct GraphicsOutput {
5252
height: usize,
5353
stride: usize,
5454
) -> Status,
55-
mode: &'static ModeData,
55+
mode: &'boot ModeData<'boot>,
5656
}
5757

58-
impl GraphicsOutput {
58+
impl<'boot> GraphicsOutput<'boot> {
5959
/// Returns information for an available graphics mode that the graphics
6060
/// device and the set of active video output devices supports.
6161
fn query_mode(&self, index: u32) -> Result<Mode> {
6262
let mut info_sz = 0;
6363
let mut info = ptr::null();
6464

6565
(self.query_mode)(self, index, &mut info_sz, &mut info).into_with(|| {
66-
let info = unsafe { &*info };
66+
let info = unsafe { *info };
6767
Mode {
6868
index,
6969
info_sz,
@@ -103,7 +103,7 @@ impl GraphicsOutput {
103103
self.check_framebuffer_region((dest_x, dest_y), (width, height));
104104
(self.blt)(
105105
self,
106-
&color as *const _ as usize,
106+
&color as *const _ as *mut _,
107107
0,
108108
0,
109109
0,
@@ -126,7 +126,7 @@ impl GraphicsOutput {
126126
match dest_region {
127127
BltRegion::Full => (self.blt)(
128128
self,
129-
buffer.as_mut_ptr() as usize,
129+
buffer.as_mut_ptr(),
130130
1,
131131
src_x,
132132
src_y,
@@ -142,7 +142,7 @@ impl GraphicsOutput {
142142
px_stride,
143143
} => (self.blt)(
144144
self,
145-
buffer.as_mut_ptr() as usize,
145+
buffer.as_mut_ptr(),
146146
1,
147147
src_x,
148148
src_y,
@@ -166,7 +166,7 @@ impl GraphicsOutput {
166166
match src_region {
167167
BltRegion::Full => (self.blt)(
168168
self,
169-
buffer.as_ptr() as usize,
169+
buffer.as_ptr() as *mut _,
170170
2,
171171
0,
172172
0,
@@ -182,7 +182,7 @@ impl GraphicsOutput {
182182
px_stride,
183183
} => (self.blt)(
184184
self,
185-
buffer.as_ptr() as usize,
185+
buffer.as_ptr() as *mut _,
186186
2,
187187
src_x,
188188
src_y,
@@ -203,7 +203,16 @@ impl GraphicsOutput {
203203
self.check_framebuffer_region((src_x, src_y), (width, height));
204204
self.check_framebuffer_region((dest_x, dest_y), (width, height));
205205
(self.blt)(
206-
self, 0usize, 3, src_x, src_y, dest_x, dest_y, width, height, 0,
206+
self,
207+
ptr::null_mut(),
208+
3,
209+
src_x,
210+
src_y,
211+
dest_x,
212+
dest_y,
213+
width,
214+
height,
215+
0,
207216
)
208217
.into()
209218
}
@@ -269,19 +278,19 @@ impl GraphicsOutput {
269278
}
270279

271280
impl_proto! {
272-
protocol GraphicsOutput {
281+
protocol GraphicsOutput<'boot> {
273282
GUID = 0x9042a9de, 0x23dc, 0x4a38, [0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a];
274283
}
275284
}
276285

277286
#[repr(C)]
278-
struct ModeData {
287+
struct ModeData<'a> {
279288
// Number of modes which the GOP supports.
280289
max_mode: u32,
281290
// Current mode.
282291
mode: u32,
283292
// Information about the current mode.
284-
info: &'static ModeInfo,
293+
info: &'a ModeInfo,
285294
// Size of the above structure.
286295
info_sz: usize,
287296
// Physical address of the frame buffer.
@@ -329,7 +338,7 @@ pub struct PixelBitmask {
329338
pub struct Mode {
330339
index: u32,
331340
info_sz: usize,
332-
info: &'static ModeInfo,
341+
info: ModeInfo,
333342
}
334343

335344
impl Mode {
@@ -342,7 +351,7 @@ impl Mode {
342351

343352
/// Returns a reference to the mode info structure.
344353
pub fn info(&self) -> &ModeInfo {
345-
self.info
354+
&self.info
346355
}
347356
}
348357

@@ -391,7 +400,7 @@ impl ModeInfo {
391400

392401
/// Iterator for graphics modes.
393402
struct ModeIter<'a> {
394-
gop: &'a GraphicsOutput,
403+
gop: &'a GraphicsOutput<'a>,
395404
current: u32,
396405
max: u32,
397406
}

src/proto/console/pointer/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ use crate::{Event, Result, Status};
55

66
/// Provides information about a pointer device.
77
#[repr(C)]
8-
pub struct Pointer {
8+
pub struct Pointer<'boot> {
99
reset: extern "win64" fn(this: &mut Pointer, ext_verif: bool) -> Status,
1010
get_state: extern "win64" fn(this: &Pointer, state: &mut PointerState) -> Status,
1111
wait_for_input: Event,
12-
mode: &'static PointerMode,
12+
mode: &'boot PointerMode,
1313
}
1414

15-
impl Pointer {
15+
impl<'boot> Pointer<'boot> {
1616
/// Resets the pointer device hardware.
1717
///
1818
/// The `extended_verification` parameter is used to request that UEFI
@@ -55,7 +55,7 @@ impl Pointer {
5555
}
5656

5757
impl_proto! {
58-
protocol Pointer {
58+
protocol Pointer<'boot> {
5959
GUID = 0x31878c87, 0xb75, 0x11d5, [0x9a, 0x4f, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d];
6060
}
6161
}

src/proto/console/serial.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::{Completion, Result, Status};
1111
/// Since UEFI drivers are implemented through polling, if you fail to regularly
1212
/// check for input/output, some data might be lost.
1313
#[repr(C)]
14-
pub struct Serial {
14+
pub struct Serial<'boot> {
1515
// Revision of this protocol, only 1.0 is currently defined.
1616
// Future versions will be backwards compatible.
1717
revision: u32,
@@ -29,10 +29,10 @@ pub struct Serial {
2929
get_control_bits: extern "win64" fn(&Serial, &mut ControlBits) -> Status,
3030
write: extern "win64" fn(&mut Serial, &mut usize, *const u8) -> Status,
3131
read: extern "win64" fn(&mut Serial, &mut usize, *mut u8) -> Status,
32-
io_mode: &'static IoMode,
32+
io_mode: &'boot IoMode,
3333
}
3434

35-
impl Serial {
35+
impl<'boot> Serial<'boot> {
3636
/// Reset the device.
3737
pub fn reset(&mut self) -> Result<()> {
3838
(self.reset)(self).into()
@@ -117,7 +117,7 @@ impl Serial {
117117
}
118118

119119
impl_proto! {
120-
protocol Serial {
120+
protocol Serial<'boot> {
121121
GUID = 0xBB25CF6F, 0xF1D4, 0x11D2, [0x9A, 0x0C, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0xFD];
122122
}
123123
}

src/proto/console/text/output.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{CStr16, Char16, Completion, Result, Status};
77
/// It implements the fmt::Write trait, so you can use it to print text with
88
/// standard Rust constructs like the write!() and writeln!() macros.
99
#[repr(C)]
10-
pub struct Output {
10+
pub struct Output<'boot> {
1111
reset: extern "win64" fn(this: &Output, extended: bool) -> Status,
1212
output_string: extern "win64" fn(this: &Output, string: *const Char16) -> Status,
1313
test_string: extern "win64" fn(this: &Output, string: *const Char16) -> Status,
@@ -18,10 +18,10 @@ pub struct Output {
1818
clear_screen: extern "win64" fn(this: &mut Output) -> Status,
1919
set_cursor_position: extern "win64" fn(this: &mut Output, column: usize, row: usize) -> Status,
2020
enable_cursor: extern "win64" fn(this: &mut Output, visible: bool) -> Status,
21-
data: &'static OutputData,
21+
data: &'boot OutputData,
2222
}
2323

24-
impl Output {
24+
impl<'boot> Output<'boot> {
2525
/// Resets and clears the text output device hardware.
2626
pub fn reset(&mut self, extended: bool) -> Result<()> {
2727
(self.reset)(self, extended).into()
@@ -53,8 +53,8 @@ impl Output {
5353
}
5454

5555
/// Returns an iterator of all supported text modes.
56-
// TODO: fix the ugly lifetime parameter.
57-
pub fn modes<'a>(&'a mut self) -> impl Iterator<Item = Completion<OutputMode>> + 'a {
56+
// TODO: Bring back impl Trait once the story around bounds improves
57+
pub fn modes<'a>(&'a mut self) -> OutputModeIter<'a, 'boot> {
5858
let max = self.data.max_mode;
5959
OutputModeIter {
6060
output: self,
@@ -134,7 +134,7 @@ impl Output {
134134
}
135135
}
136136

137-
impl fmt::Write for Output {
137+
impl<'boot> fmt::Write for Output<'boot> {
138138
fn write_str(&mut self, s: &str) -> fmt::Result {
139139
// Allocate a small buffer on the stack.
140140
const BUF_SIZE: usize = 128;
@@ -215,13 +215,13 @@ impl OutputMode {
215215
}
216216

217217
/// An iterator of the text modes (possibly) supported by a device.
218-
struct OutputModeIter<'a> {
219-
output: &'a mut Output,
218+
pub struct OutputModeIter<'a, 'b: 'a> {
219+
output: &'a mut Output<'b>,
220220
current: i32,
221221
max: i32,
222222
}
223223

224-
impl<'a> Iterator for OutputModeIter<'a> {
224+
impl<'a, 'b> Iterator for OutputModeIter<'a, 'b> {
225225
type Item = Completion<OutputMode>;
226226

227227
fn next(&mut self) -> Option<Self::Item> {
@@ -259,7 +259,7 @@ struct OutputData {
259259
}
260260

261261
impl_proto! {
262-
protocol Output {
262+
protocol Output<'boot> {
263263
GUID = 0x387477c2, 0x69c7, 0x11d2, [0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b];
264264
}
265265
}

src/proto/macros.rs

+17
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,21 @@ macro_rules! impl_proto {
3333
// Most UEFI functions do not support multithreaded access.
3434
impl !Sync for $p {}
3535
};
36+
(
37+
protocol $p:ident<'boot> {
38+
GUID = $a:expr, $b:expr, $c:expr, $d:expr;
39+
}
40+
) => {
41+
impl<'boot> $crate::proto::Protocol for $p<'boot> {
42+
#[doc(hidden)]
43+
// These literals aren't meant to be human-readable.
44+
#[allow(clippy::unreadable_literal)]
45+
const GUID: $crate::Guid = $crate::Guid::from_values($a, $b, $c, $d);
46+
}
47+
48+
// Most UEFI functions expect to be called on the bootstrap processor.
49+
impl<'boot> !Send for $p<'boot> {}
50+
// Most UEFI functions do not support multithreaded access.
51+
impl<'boot> !Sync for $p<'boot> {}
52+
};
3653
}

src/proto/media/file.rs

+7-10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
88
use bitflags::bitflags;
99
use core::mem;
10+
use core::ptr;
1011
use crate::prelude::*;
1112
use crate::{CStr16, Char16, Result, Status};
1213
use ucs2;
@@ -22,10 +23,8 @@ pub struct File<'a> {
2223
}
2324

2425
impl<'a> File<'a> {
25-
pub(super) unsafe fn new(ptr: usize) -> Self {
26-
let ptr = ptr as *mut FileImpl;
27-
let inner = &mut *ptr;
28-
File { inner }
26+
pub(super) unsafe fn new(ptr: *mut FileImpl) -> Self {
27+
File { inner: &mut *ptr }
2928
}
3029

3130
/// Try to open a file relative to this file/directory.
@@ -58,7 +57,7 @@ impl<'a> File<'a> {
5857
Err(Status::INVALID_PARAMETER)
5958
} else {
6059
let mut buf = [0u16; BUF_SIZE + 1];
61-
let mut ptr = 0usize;
60+
let mut ptr = ptr::null_mut();
6261

6362
let len = ucs2::encode(filename, &mut buf)?;
6463
let filename = unsafe { CStr16::from_u16_with_nul_unchecked(&buf[..=len]) };
@@ -70,9 +69,7 @@ impl<'a> File<'a> {
7069
open_mode,
7170
attributes,
7271
)
73-
.into_with(|| File {
74-
inner: unsafe { &mut *(ptr as *mut FileImpl) },
75-
})
72+
.into_with(|| unsafe { File::new(ptr) })
7673
}
7774
}
7875

@@ -175,11 +172,11 @@ impl<'a> Drop for File<'a> {
175172

176173
/// The function pointer table for the File protocol.
177174
#[repr(C)]
178-
struct FileImpl {
175+
pub(super) struct FileImpl {
179176
revision: u64,
180177
open: extern "win64" fn(
181178
this: &mut FileImpl,
182-
new_handle: &mut usize,
179+
new_handle: &mut *mut FileImpl,
183180
filename: *const Char16,
184181
open_mode: FileMode,
185182
attributes: FileAttribute,

0 commit comments

Comments
 (0)