Skip to content

Commit d6f5de8

Browse files
committed
add memory util mem::make_boxed
1 parent 5e64a6e commit d6f5de8

File tree

4 files changed

+146
-102
lines changed

4 files changed

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

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)