Skip to content

Commit 8d1ed0d

Browse files
committed
mem: Add AlignedBuffer helper
AlignedBuffer is a helper class that manages the livetime of a memory region, allocated using a certain alignment. Like Box, it handles deallocation when the object isn't used anymore.
1 parent 8a858a7 commit 8d1ed0d

File tree

3 files changed

+129
-0
lines changed

3 files changed

+129
-0
lines changed

uefi/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- Added conversions between `proto::network::IpAddress` and `core::net` types.
66
- Added conversions between `proto::network::MacAddress` and the `[u8; 6]` type that's more commonly used to represent MAC addresses.
77
- Added `proto::media::disk_info::DiskInfo`.
8+
- Added `mem::AlignedBuffer`.
89

910
## Changed
1011
- **Breaking:** Removed `BootPolicyError` as `BootPolicy` construction is no

uefi/src/mem/aligned_buffer.rs

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
use alloc::alloc::{alloc, dealloc, Layout, LayoutError};
4+
use core::error::Error;
5+
use core::fmt;
6+
use core::ptr::NonNull;
7+
8+
/// Helper class to maintain the lifetime of a memory region allocated with a non-standard alignment.
9+
/// Facilitates RAII to properly deallocate when lifetime of the object ends.
10+
///
11+
/// Note: This uses the global Rust allocator under the hood.
12+
#[derive(Debug)]
13+
pub struct AlignedBuffer {
14+
ptr: NonNull<u8>,
15+
layout: Layout,
16+
}
17+
18+
impl AlignedBuffer {
19+
/// Allocate a new memory region with the requested len and alignment.
20+
///
21+
/// # Panics
22+
/// This method panics when the allocation fails (e.g. due to an out of memory situation).
23+
pub fn from_size_align(len: usize, alignment: usize) -> Result<Self, LayoutError> {
24+
let layout = Layout::from_size_align(len, alignment)?;
25+
Ok(Self::from_layout(layout))
26+
}
27+
28+
/// Allocate a new memory region with the requested layout.
29+
///
30+
/// # Panics
31+
/// This method panics when the allocation fails (e.g. due to an out of memory situation).
32+
#[must_use]
33+
pub fn from_layout(layout: Layout) -> Self {
34+
let ptr = unsafe { alloc(layout) };
35+
let ptr = NonNull::new(ptr).expect("Allocation failed");
36+
Self { ptr, layout }
37+
}
38+
39+
// TODO: Add non-panicking method variants as soon as alloc::AllocError was stabilized (#32838).
40+
// - try_from_layout(layout: Layout) -> Result<Self, AllocError>;
41+
42+
/// Get a pointer to the aligned memory region managed by this instance.
43+
#[must_use]
44+
pub const fn ptr(&self) -> *const u8 {
45+
self.ptr.as_ptr().cast_const()
46+
}
47+
48+
/// Get a mutable pointer to the aligned memory region managed by this instance.
49+
#[must_use]
50+
pub fn ptr_mut(&mut self) -> *mut u8 {
51+
self.ptr.as_ptr()
52+
}
53+
54+
/// Get the size of the aligned memory region managed by this instance.
55+
#[must_use]
56+
pub const fn size(&self) -> usize {
57+
self.layout.size()
58+
}
59+
60+
/// Fill the aligned memory region with data from the given buffer.
61+
///
62+
/// The length of `src` must be the same as `self`.
63+
pub fn copy_from_slice(&mut self, src: &[u8]) {
64+
assert_eq!(self.size(), src.len());
65+
unsafe {
66+
self.ptr_mut().copy_from(src.as_ptr(), src.len());
67+
}
68+
}
69+
70+
/// Check the buffer's alignment against the `required_alignment`.
71+
pub fn check_alignment(&self, required_alignment: usize) -> Result<(), AlignmentError> {
72+
//TODO: use bfr.addr() when it's available
73+
if (self.ptr() as usize) % required_alignment != 0 {
74+
return Err(AlignmentError); //TODO: use >is_aligned_to< when it's available
75+
}
76+
Ok(())
77+
}
78+
}
79+
80+
impl Drop for AlignedBuffer {
81+
fn drop(&mut self) {
82+
unsafe {
83+
dealloc(self.ptr_mut(), self.layout);
84+
}
85+
}
86+
}
87+
88+
/// The `AlignmentError` is returned if a user-provided buffer doesn't fulfill alignment requirements.
89+
#[derive(Clone, PartialEq, Eq, Debug)]
90+
pub struct AlignmentError;
91+
impl Error for AlignmentError {}
92+
impl fmt::Display for AlignmentError {
93+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94+
f.write_str("Buffer alignment does not fulfill requirements.")
95+
}
96+
}
97+
98+
#[cfg(test)]
99+
mod tests {
100+
use super::AlignedBuffer;
101+
102+
#[test]
103+
fn test_invalid_arguments() {
104+
// invalid alignments, valid len
105+
for request_alignment in [0, 3, 5, 7, 9] {
106+
for request_len in [1, 32, 64, 128, 1024] {
107+
assert!(AlignedBuffer::from_size_align(request_len, request_alignment).is_err());
108+
}
109+
}
110+
}
111+
112+
#[test]
113+
fn test_allocation_alignment() {
114+
for request_alignment in [1, 2, 4, 8, 16, 32, 64, 128] {
115+
for request_len in [1 as usize, 32, 64, 128, 1024] {
116+
let buffer =
117+
AlignedBuffer::from_size_align(request_len, request_alignment).unwrap();
118+
assert_eq!(buffer.ptr() as usize % request_alignment, 0);
119+
assert_eq!(buffer.size(), request_len);
120+
}
121+
}
122+
}
123+
}

uefi/src/mem/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ pub(crate) mod util;
1414
#[cfg(feature = "alloc")]
1515
pub(crate) use util::*;
1616

17+
#[cfg(feature = "alloc")]
18+
mod aligned_buffer;
19+
#[cfg(feature = "alloc")]
20+
pub use aligned_buffer::{AlignedBuffer, AlignmentError};
21+
1722
/// Wrapper for memory allocated with UEFI's pool allocator. The memory is freed
1823
/// on drop.
1924
#[derive(Debug)]

0 commit comments

Comments
 (0)