Skip to content

Commit 812a105

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 812a105

File tree

3 files changed

+123
-0
lines changed

3 files changed

+123
-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

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

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)