Skip to content

Commit 90a0318

Browse files
committed
uefi: MemoryMapType now owns its memory
This is an attempt to simplify the overall complex handling of obtaining the UEFI memory map. We have the following pre-requisites and use-cases all to keep in mind when designing the functions and associated helper types: - the memory map itself needs memory; typically on the UEFI heap - acquiring that memory and storing the memory map inside it are two distinct steps - the memory map is not just a slice of [MemoryDescriptor] (desc_size is bigger than size_of) - the required map size can be obtained by a call to a boot service function - the needed memory might change due to hidden or asynchronous allocations between the allocation of a buffer and storing the memory map inside it - when boot services are excited, best practise has shown (looking at linux code) that one should use the same buffer (with some extra capacity) and call exit_boot_services with that buffer at most two times in a loop This makes it hard to come up with an ergonomic solution such as using a Box or any other high-level Rust type. The main simplification of my design is that the MemoryMap type now doesn't has a reference to memory anymore but actually owns it. This also models the real world use case where one typically obtains the memory map once when boot services are exited. A &'static [u8] on the MemoryMap just creates more confusion that it brings any benefit. The MemoryMap now knows whether boot services are still active and frees that memory, or it doesn't if the boot services are exited. This means less fiddling with life-times and less cognitive overhead when - reading the code - calling BootService::memory_map independently of exit_boot_services
1 parent aab512f commit 90a0318

File tree

3 files changed

+131
-117
lines changed

3 files changed

+131
-117
lines changed

uefi-test-runner/src/boot/memory.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,8 @@ fn alloc_alignment() {
6363
fn memory_map(bt: &BootServices) {
6464
info!("Testing memory map functions");
6565

66-
// Get the memory descriptor size and an estimate of the memory map size
67-
let sizes = bt.memory_map_size();
68-
69-
// 2 extra descriptors should be enough.
70-
let buf_sz = sizes.map_size + 2 * sizes.desc_size;
71-
72-
// We will use vectors for convenience.
73-
let mut buffer = vec![0_u8; buf_sz];
74-
7566
let mut memory_map = bt
76-
.memory_map(&mut buffer)
67+
.memory_map(MemoryType::LOADER_DATA)
7768
.expect("Failed to retrieve UEFI memory map");
7869

7970
memory_map.sort();

uefi/src/table/boot.rs

Lines changed: 121 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -217,37 +217,46 @@ impl BootServices {
217217
}
218218
}
219219

220-
/// Stores the current UEFI memory map in the provided buffer.
221-
///
222-
/// The allocated buffer must be at least aligned to a [`MemoryDescriptor`]
223-
/// and should be big enough to store the whole map. To estimating how big
224-
/// the map will be, you can call [`Self::memory_map_size`].
225-
///
226-
/// The memory map contains entries of type [`MemoryDescriptor`]. However,
227-
/// the relevant step size is always the reported `desc_size` but never
228-
/// `size_of::<MemoryDescriptor>()`.
229-
///
230-
/// The returned key is a unique identifier of the current configuration of
231-
/// memory. Any allocations or such will change the memory map's key.
232-
///
233-
/// If you want to store the resulting memory map without having to keep
234-
/// the buffer around, you can use `.copied().collect()` on the iterator.
235-
/// Note that this will change the current memory map again, if the UEFI
236-
/// allocator is used under the hood.
220+
/// Stores the current UEFI memory map in an UEFI-heap allocated buffer
221+
/// and returns a [`MemoryMap`].
237222
///
238223
/// # Errors
239224
///
240-
/// See section `EFI_BOOT_SERVICES.GetMemoryMap()` in the UEFI Specification for more details.
225+
/// See section `EFI_BOOT_SERVICES.GetMemoryMap()` in the UEFI Specification
226+
/// for more details.
241227
///
242228
/// * [`uefi::Status::BUFFER_TOO_SMALL`]
243229
/// * [`uefi::Status::INVALID_PARAMETER`]
244-
pub fn memory_map<'buf>(&self, buffer: &'buf mut [u8]) -> Result<MemoryMap<'buf>> {
245-
let mut map_size = buffer.len();
246-
MemoryDescriptor::assert_aligned(buffer);
247-
let map_buffer = buffer.as_mut_ptr().cast::<MemoryDescriptor>();
230+
pub fn memory_map(&self, mt: MemoryType) -> Result<MemoryMap> {
231+
let mut buffer = MemoryMapBackingMemory::new(mt)?;
232+
233+
let GetMemoryMapMeta {
234+
map_size,
235+
map_key,
236+
desc_size,
237+
desc_version,
238+
} = self.get_memory_map(buffer.as_mut_slice())?;
239+
240+
let len = map_size / desc_size;
241+
assert_eq!(map_size % desc_size, 0);
242+
assert_eq!(desc_version, MemoryDescriptor::VERSION);
243+
Ok(MemoryMap {
244+
key: map_key,
245+
buf: buffer,
246+
desc_size,
247+
len,
248+
})
249+
}
250+
251+
/// Calls the underlying `GetMemoryMap` function of UEFI. On success,
252+
/// the buffer is mutated and contains the map. The map might be shorter
253+
/// than the buffer, which is reflected by the return value.
254+
pub(crate) fn get_memory_map(&self, buf: &mut [u8]) -> Result<GetMemoryMapMeta> {
255+
let mut map_size = buf.len();
256+
let map_buffer = buf.as_mut_ptr().cast::<MemoryDescriptor>();
248257
let mut map_key = MemoryMapKey(0);
249258
let mut desc_size = 0;
250-
let mut entry_version = 0;
259+
let mut desc_version = 0;
251260

252261
assert_eq!(
253262
(map_buffer as usize) % mem::align_of::<MemoryDescriptor>(),
@@ -261,18 +270,14 @@ impl BootServices {
261270
map_buffer,
262271
&mut map_key.0,
263272
&mut desc_size,
264-
&mut entry_version,
273+
&mut desc_version,
265274
)
266275
}
267-
.to_result_with_val(move || {
268-
let len = map_size / desc_size;
269-
270-
MemoryMap {
271-
key: map_key,
272-
buf: buffer,
273-
desc_size,
274-
len,
275-
}
276+
.to_result_with_val(|| GetMemoryMapMeta {
277+
map_size,
278+
desc_size,
279+
map_key,
280+
desc_version,
276281
})
277282
}
278283

@@ -1639,7 +1644,7 @@ pub struct MemoryMapKey(usize);
16391644
/// When this type is dropped and boot services are not exited yet, the memory
16401645
/// is freed.
16411646
#[derive(Debug)]
1642-
pub struct MemoryMapBackingMemory(NonNull<[u8]> /* buffer on UEFI heap */);
1647+
pub struct MemoryMapBackingMemory(NonNull<[u8]>);
16431648

16441649
impl MemoryMapBackingMemory {
16451650
/// Constructs a new [`MemoryMapBackingMemory`].
@@ -1652,18 +1657,32 @@ impl MemoryMapBackingMemory {
16521657
let bs = st.boot_services();
16531658

16541659
let memory_map_size = bs.memory_map_size();
1655-
let alloc_size = Self::allocation_size_hint(memory_map_size);
1656-
let ptr = bs.allocate_pool(memory_type, alloc_size)?;
1660+
let len = Self::allocation_size_hint(memory_map_size);
1661+
let ptr = bs.allocate_pool(memory_type, len)?;
1662+
assert_eq!(ptr.align_offset(mem::align_of::<MemoryDescriptor>()), 0);
1663+
Ok(Self::from_raw(ptr, len))
1664+
}
1665+
1666+
fn from_raw(ptr: *mut u8, len: usize) -> Self {
16571667
assert_eq!(ptr.align_offset(mem::align_of::<MemoryDescriptor>()), 0);
16581668

16591669
let ptr = NonNull::new(ptr).expect("UEFI should never return a null ptr. An error should have been reflected via an Err earlier.");
1660-
let slice = NonNull::slice_from_raw_parts(ptr, alloc_size);
1670+
let slice = NonNull::slice_from_raw_parts(ptr, len);
16611671

1662-
Ok(Self(slice))
1672+
Self(slice)
1673+
}
1674+
1675+
/// Creates an instance from the provided memory, which is not necessarily
1676+
/// on the UEFI heap.
1677+
#[cfg(test)]
1678+
fn from_slice(buffer: &mut [u8]) -> Self {
1679+
let len = buffer.len();
1680+
Self::from_raw(buffer.as_mut_ptr(), len)
16631681
}
16641682

16651683
/// Returns a best-effort size hint of the memory map size. This is
16661684
/// especially created with exiting boot services in mind.
1685+
#[must_use]
16671686
pub fn allocation_size_hint(mms: GetMemoryMapMeta) -> usize {
16681687
let GetMemoryMapMeta {
16691688
desc_size,
@@ -1693,15 +1712,34 @@ impl MemoryMapBackingMemory {
16931712
allocation_size
16941713
}
16951714

1696-
/// Returns the raw pointer to the beginning of the allocation.
1697-
pub fn as_ptr_mut(&mut self) -> *mut u8 {
1715+
/// Returns a raw pointer to the beginning of the allocation.
1716+
#[must_use]
1717+
pub fn as_ptr(&self) -> *const u8 {
1718+
self.0.as_ptr().cast()
1719+
}
1720+
1721+
/// Returns a mutable raw pointer to the beginning of the allocation.
1722+
#[must_use]
1723+
pub fn as_mut_ptr(&mut self) -> *mut u8 {
16981724
self.0.as_ptr().cast()
16991725
}
17001726

17011727
/// Returns the length of the allocation.
1728+
#[must_use]
17021729
pub fn len(&self) -> usize {
17031730
self.0.len()
17041731
}
1732+
1733+
/*#[must_use]
1734+
pub fn as_slice(&self) -> &[u8] {
1735+
self.0.as_ref()
1736+
}*/
1737+
1738+
/// Returns a mutable slice to the underlying memory.
1739+
#[must_use]
1740+
pub fn as_mut_slice(&mut self) -> &mut [u8] {
1741+
unsafe { self.0.as_mut() }
1742+
}
17051743
}
17061744

17071745
impl Drop for MemoryMapBackingMemory {
@@ -1751,31 +1789,34 @@ pub struct GetMemoryMapMeta {
17511789
///
17521790
/// [0]: https://github.com/tianocore/edk2/blob/7142e648416ff5d3eac6c6d607874805f5de0ca8/MdeModulePkg/Core/PiSmmCore/Page.c#L1059
17531791
#[derive(Debug)]
1754-
pub struct MemoryMap<'buf> {
1792+
pub struct MemoryMap {
1793+
/// Backing memory, properly initialized at this point.
1794+
buf: MemoryMapBackingMemory,
17551795
key: MemoryMapKey,
1756-
buf: &'buf mut [u8],
17571796
/// Usually bound to the size of a [`MemoryDescriptor`] but can indicate if
17581797
/// this field is ever extended by a new UEFI standard.
17591798
desc_size: usize,
17601799
len: usize,
17611800
}
17621801

1763-
impl<'buf> MemoryMap<'buf> {
1764-
/// Creates a [`MemoryMap`] from the given buffer and entry size.
1765-
/// The entry size is usually bound to the size of a [`MemoryDescriptor`]
1766-
/// but can indicate if this field is ever extended by a new UEFI standard.
1767-
///
1768-
/// This allows parsing a memory map provided by a kernel after boot
1769-
/// services have already exited.
1770-
pub fn from_raw(buf: &'buf mut [u8], desc_size: usize) -> Self {
1771-
assert!(!buf.is_empty());
1772-
assert_eq!(
1773-
buf.len() % desc_size,
1774-
0,
1775-
"The buffer length must be a multiple of the desc_size"
1776-
);
1802+
impl MemoryMap {
1803+
/*pub fn new() -> Self {
1804+
1805+
}*/
1806+
1807+
/// Creates a [`MemoryMap`] from the give initialized memory map behind
1808+
/// the buffer and the reported `desc_size` from UEFI.
1809+
pub(crate) fn from_initialized_mem(
1810+
buf: MemoryMapBackingMemory,
1811+
props: GetMemoryMapMeta,
1812+
) -> Self {
1813+
let GetMemoryMapMeta {
1814+
map_size,
1815+
desc_size,
1816+
..
1817+
} = props;
17771818
assert!(desc_size >= mem::size_of::<MemoryDescriptor>());
1778-
let len = buf.len() / desc_size;
1819+
let len = map_size / desc_size;
17791820
MemoryMap {
17801821
key: MemoryMapKey(0),
17811822
buf,
@@ -1784,6 +1825,20 @@ impl<'buf> MemoryMap<'buf> {
17841825
}
17851826
}
17861827

1828+
#[cfg(test)]
1829+
fn from_raw(buf: &mut [u8], desc_size: usize) -> Self {
1830+
let mem = MemoryMapBackingMemory::from_slice(buf);
1831+
Self::from_initialized_mem(
1832+
mem,
1833+
GetMemoryMapMeta {
1834+
map_size: buf.len(),
1835+
desc_size,
1836+
map_key: MemoryMapKey(0),
1837+
desc_version: MemoryDescriptor::VERSION,
1838+
},
1839+
)
1840+
}
1841+
17871842
#[must_use]
17881843
/// Returns the unique [`MemoryMapKey`] associated with the memory map.
17891844
pub fn key(&self) -> MemoryMapKey {
@@ -1878,7 +1933,7 @@ impl<'buf> MemoryMap<'buf> {
18781933

18791934
/// Returns a reference to the [`MemoryDescriptor`] at `index` or `None` if out of bounds.
18801935
#[must_use]
1881-
pub fn get(&self, index: usize) -> Option<&'buf MemoryDescriptor> {
1936+
pub fn get(&self, index: usize) -> Option<&MemoryDescriptor> {
18821937
if index >= self.len {
18831938
return None;
18841939
}
@@ -1896,7 +1951,7 @@ impl<'buf> MemoryMap<'buf> {
18961951

18971952
/// Returns a mut reference to the [`MemoryDescriptor`] at `index` or `None` if out of bounds.
18981953
#[must_use]
1899-
pub fn get_mut(&mut self, index: usize) -> Option<&'buf mut MemoryDescriptor> {
1954+
pub fn get_mut(&mut self, index: usize) -> Option<&mut MemoryDescriptor> {
19001955
if index >= self.len {
19011956
return None;
19021957
}
@@ -1913,15 +1968,15 @@ impl<'buf> MemoryMap<'buf> {
19131968
}
19141969
}
19151970

1916-
impl core::ops::Index<usize> for MemoryMap<'_> {
1971+
impl core::ops::Index<usize> for MemoryMap {
19171972
type Output = MemoryDescriptor;
19181973

19191974
fn index(&self, index: usize) -> &Self::Output {
19201975
self.get(index).unwrap()
19211976
}
19221977
}
19231978

1924-
impl core::ops::IndexMut<usize> for MemoryMap<'_> {
1979+
impl core::ops::IndexMut<usize> for MemoryMap {
19251980
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
19261981
self.get_mut(index).unwrap()
19271982
}
@@ -1930,13 +1985,13 @@ impl core::ops::IndexMut<usize> for MemoryMap<'_> {
19301985
/// An iterator of [`MemoryDescriptor`]. The underlying memory map is always
19311986
/// associated with a unique [`MemoryMapKey`].
19321987
#[derive(Debug, Clone)]
1933-
pub struct MemoryMapIter<'buf> {
1934-
memory_map: &'buf MemoryMap<'buf>,
1988+
pub struct MemoryMapIter<'a> {
1989+
memory_map: &'a MemoryMap,
19351990
index: usize,
19361991
}
19371992

1938-
impl<'buf> Iterator for MemoryMapIter<'buf> {
1939-
type Item = &'buf MemoryDescriptor;
1993+
impl<'a> Iterator for MemoryMapIter<'a> {
1994+
type Item = &'a MemoryDescriptor;
19401995

19411996
fn size_hint(&self) -> (usize, Option<usize>) {
19421997
let sz = self.memory_map.len - self.index;
@@ -2187,7 +2242,7 @@ mod tests {
21872242
}
21882243

21892244
// Added for debug purposes on test failure
2190-
impl core::fmt::Display for MemoryMap<'_> {
2245+
impl core::fmt::Display for MemoryMap {
21912246
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
21922247
writeln!(f)?;
21932248
for desc in self.entries() {

0 commit comments

Comments
 (0)