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 735094d

Browse files
committed
add memory util mem::make_boxed
1 parent 53ee64a commit 735094d

File tree

4 files changed

+150
-103
lines changed

4 files changed

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

‎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: 5 additions & 51 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};
@@ -166,53 +162,11 @@ pub trait File: Sized {
166162
}
167163

168164
#[cfg(feature = "alloc")]
169-
/// Get the dynamically allocated info for a file
165+
/// Read 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 によって変換されたページ (->オリジナル) /