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 685c937

Browse files
committed
add memory util mem::make_boxed
1 parent 6b269e9 commit 685c937

File tree

4 files changed

+148
-102
lines changed

4 files changed

+148
-102
lines changed

‎uefi/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,6 @@ pub mod global_allocator;
7979

8080
#[cfg(feature = "logger")]
8181
pub mod logger;
82+
83+
#[cfg(feature = "alloc")]
84+
pub(crate) mod mem;

‎uefi/src/mem.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
//! This is a utility module with helper methods for allocations/memory.
2+
3+
use crate::ResultExt;
4+
use crate::{Result, Status};
5+
use ::alloc::{alloc, boxed::Box};
6+
use core::alloc::Layout;
7+
use core::fmt::Debug;
8+
use core::ptr::NonNull;
9+
use core::slice;
10+
use uefi::data_types::Align;
11+
use uefi::Error;
12+
13+
/// Helper to return owned versions of certain UEFI data structures on the heap, hence, in a
14+
/// [`Box`]. It is intended to wrap low-level UEFI functions off this crate that
15+
/// - consume a mutable reference to a buffer that needs to be filled with some data of type,
16+
/// - return a mutable typed reference (which points to the same memory as the input buffer) on
17+
/// success, and
18+
/// - return the required buffer size on error.
19+
pub fn make_boxed<'a, Data: Align + ?Sized + Debug + 'a>(
20+
mut fetch_data_fn: impl FnMut(&'a mut [u8]) -> Result<&'a mut Data, Option<usize>>,
21+
) -> Result<Box<Data>> {
22+
let required_size = match fetch_data_fn(&mut []).map_err(Error::split) {
23+
// This is the expected case: the empty buffer passed in is too
24+
// small, so we get the required size.
25+
Err((Status::BUFFER_TOO_SMALL, Some(required_size))) => Ok(required_size),
26+
// Propagate any other error.
27+
Err((status, _)) => Err(Error::from(status)),
28+
// Success is unexpected, return an error.
29+
Ok(_) => Err(Error::from(Status::UNSUPPORTED)),
30+
}?;
31+
32+
// We add trailing padding because the size of a rust structure must
33+
// always be a multiple of alignment.
34+
let layout = Layout::from_size_align(required_size, Data::alignment())
35+
.unwrap()
36+
.pad_to_align();
37+
38+
// Allocate the buffer.
39+
let heap_buf: *mut u8 = unsafe {
40+
let ptr = alloc::alloc(layout);
41+
if ptr.is_null() {
42+
return Err(Status::OUT_OF_RESOURCES.into());
43+
}
44+
ptr
45+
};
46+
47+
// Read the data into the provided buffer.
48+
let data: Result<&mut Data> = {
49+
let buffer = unsafe { slice::from_raw_parts_mut(heap_buf, required_size) };
50+
fetch_data_fn(buffer).discard_errdata()
51+
};
52+
53+
// If an error occurred, deallocate the memory before returning.
54+
let data: &mut Data = match data {
55+
Ok(data) => data,
56+
Err(err) => {
57+
unsafe { alloc::dealloc(heap_buf.as_ptr(), layout) };
58+
return Err(err);
59+
}
60+
};
61+
62+
let data = unsafe { Box::from_raw(data) };
63+
64+
Ok(data)
65+
}
66+
67+
#[cfg(test)]
68+
mod tests {
69+
use super::*;
70+
use crate::ResultExt;
71+
use core::mem::{align_of, size_of};
72+
73+
#[derive(Debug)]
74+
#[repr(C)]
75+
struct SomeData([u8; 4]);
76+
77+
impl Align for SomeData {
78+
fn alignment() -> usize {
79+
align_of::<Self>()
80+
}
81+
}
82+
83+
/// Function that behaves like the other UEFI functions. It takes a
84+
/// mutable reference to a buffer memory that represents a [`SomeData`]
85+
/// instance.
86+
fn uefi_function_stub_read(buf: &mut [u8]) -> Result<&mut SomeData, Option<usize>> {
87+
if buf.len() < 4 {
88+
return Status::BUFFER_TOO_SMALL.into_with(|| panic!(), |_| Some(4));
89+
};
90+
91+
buf[0] = 1;
92+
buf[1] = 2;
93+
buf[2] = 3;
94+
buf[3] = 4;
95+
96+
let data = unsafe { &mut *buf.as_mut_ptr().cast::<SomeData>() };
97+
98+
Ok(data)
99+
}
100+
101+
// Some basic checks so that miri reports everything is fine.
102+
#[test]
103+
fn some_data_type_size_constraints() {
104+
assert_eq!(size_of::<SomeData>(), 4);
105+
assert_eq!(align_of::<SomeData>(), 1);
106+
}
107+
108+
#[test]
109+
fn basic_stub_read() {
110+
assert_eq!(
111+
uefi_function_stub_read(&mut []).status(),
112+
Status::BUFFER_TOO_SMALL
113+
);
114+
assert_eq!(
115+
*uefi_function_stub_read(&mut []).unwrap_err().data(),
116+
Some(4)
117+
);
118+
119+
let mut buf: [u8; 4] = [0; 4];
120+
let data = uefi_function_stub_read(&mut buf).unwrap();
121+
122+
assert_eq!(&data.0, &[1, 2, 3, 4])
123+
}
124+
125+
#[test]
126+
fn make_boxed_utility() {
127+
let fetch_data_fn = |buf| uefi_function_stub_read(buf);
128+
let data: Box<SomeData> = make_boxed(fetch_data_fn).unwrap();
129+
130+
assert_eq!(&data.0, &[1, 2, 3, 4])
131+
}
132+
}

‎uefi/src/proto/media/file/dir.rs

Lines changed: 9 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,7 @@ use crate::Result;
44
use core::ffi::c_void;
55

66
#[cfg(feature = "alloc")]
7-
use {
8-
crate::{ResultExt, Status},
9-
::alloc::boxed::Box,
10-
alloc::alloc,
11-
core::alloc::Layout,
12-
core::ptr::NonNull,
13-
core::slice,
14-
};
7+
use {crate::mem::make_boxed, alloc::boxed::Box};
158

169
/// A `FileHandle` that is also a directory.
1710
///
@@ -77,51 +70,15 @@ impl Directory {
7770
return Ok(None);
7871
}
7972

80-
let required_size = match read_entry_res
81-
.expect_err("zero sized read unexpectedly succeeded")
82-
.split()
83-
{
84-
// Early return if something has failed.
85-
(s, None) => return Err(s.into()),
86-
(_, Some(required_size)) => required_size,
73+
let fetch_data_fn = |buf| {
74+
self.read_entry(buf)
75+
// this is safe, as above, we checked that there are more entries
76+
.map(|maybe_info: Option<&mut FileInfo>| {
77+
maybe_info.expect("Should have more entries")
78+
})
8779
};
88-
89-
// We add trailing padding because the size of a rust structure must
90-
// always be a multiple of alignment.
91-
let layout = Layout::from_size_align(required_size, FileInfo::alignment())
92-
.unwrap()
93-
.pad_to_align();
94-
95-
// Allocate the buffer.
96-
let heap_buf: NonNull<u8> = unsafe {
97-
let ptr = alloc::alloc(layout);
98-
match NonNull::new(ptr) {
99-
None => return Err(Status::OUT_OF_RESOURCES.into()),
100-
Some(ptr) => ptr,
101-
}
102-
};
103-
104-
// Get the file info using the allocated buffer for storage.
105-
let info = {
106-
let buffer = unsafe { slice::from_raw_parts_mut(heap_buf.as_ptr(), layout.size()) };
107-
self.read_entry(buffer).discard_errdata()
108-
};
109-
110-
// If an error occurred, deallocate the memory before returning.
111-
let info = match info {
112-
Ok(info) => info,
113-
Err(err) => {
114-
unsafe { alloc::dealloc(heap_buf.as_ptr(), layout) };
115-
return Err(err);
116-
}
117-
};
118-
119-
// Wrap the file info in a box so that it will be deallocated on
120-
// drop. This is valid because the memory was allocated with the
121-
// global allocator.
122-
let info = info.map(|info| unsafe { Box::from_raw(info) });
123-
124-
Ok(info)
80+
let file_info = make_boxed::<FileInfo>(fetch_data_fn)?;
81+
Ok(Some(file_info))
12582
}
12683

12784
/// Start over the process of enumerating directory entries

‎uefi/src/proto/media/file/mod.rs

Lines changed: 4 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,7 @@ use core::fmt::Debug;
1717
use core::mem;
1818
use core::ptr;
1919
#[cfg(feature = "alloc")]
20-
use {
21-
crate::ResultExt,
22-
::alloc::{alloc, alloc::Layout, boxed::Box},
23-
core::slice,
24-
};
20+
use {alloc::boxed::Box, uefi::mem::make_boxed};
2521

2622
pub use self::info::{FileInfo, FileProtocolInfo, FileSystemInfo, FileSystemVolumeLabel, FromUefi};
2723
pub use self::{dir::Directory, regular::RegularFile};
@@ -168,51 +164,9 @@ pub trait File: Sized {
168164
#[cfg(feature = "alloc")]
169165
/// Get the dynamically allocated info for a file
170166
fn get_boxed_info<Info: FileProtocolInfo + ?Sized + Debug>(&mut self) -> Result<Box<Info>> {
171-
// Initially try get_info with an empty array, this should always fail
172-
// as all Info types at least need room for a null-terminator.
173-
let size = match self
174-
.get_info::<Info>(&mut [])
175-
.expect_err("zero sized get_info unexpectedly succeeded")
176-
.split()
177-
{
178-
(s, None) => return Err(s.into()),
179-
(_, Some(size)) => size,
180-
};
181-
182-
// We add trailing padding because the size of a rust structure must
183-
// always be a multiple of alignment.
184-
let layout = Layout::from_size_align(size, Info::alignment())
185-
.unwrap()
186-
.pad_to_align();
187-
188-
// Allocate the buffer.
189-
let data: *mut u8 = unsafe {
190-
let data = alloc::alloc(layout);
191-
if data.is_null() {
192-
return Err(Status::OUT_OF_RESOURCES.into());
193-
}
194-
data
195-
};
196-
197-
// Get the file info using the allocated buffer for storage.
198-
let info = {
199-
let buffer = unsafe { slice::from_raw_parts_mut(data, layout.size()) };
200-
self.get_info::<Info>(buffer).discard_errdata()
201-
};
202-
203-
// If an error occurred, deallocate the memory before returning.
204-
let info = match info {
205-
Ok(info) => info,
206-
Err(err) => {
207-
unsafe { alloc::dealloc(data, layout) };
208-
return Err(err);
209-
}
210-
};
211-
212-
// Wrap the file info in a box so that it will be deallocated on
213-
// drop. This is valid because the memory was allocated with the
214-
// global allocator.
215-
unsafe { Ok(Box::from_raw(info)) }
167+
let fetch_data_fn = |buf| self.get_info::<Info>(buf);
168+
let file_info = make_boxed::<Info>(fetch_data_fn)?;
169+
Ok(file_info)
216170
}
217171

218172
/// Returns if the underlying file is a regular file.

0 commit comments

Comments
(0)

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