Skip to content

Commit 098fb77

Browse files
committed
uefi: MemoryMap 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 98215a2 commit 098fb77

File tree

4 files changed

+110
-113
lines changed

4 files changed

+110
-113
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/CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,22 @@
1616
- Added `ByteConversionError`.
1717
- Re-exported `CapsuleFlags`.
1818
- One can now specify in `TimeError` what fields of `Time` are outside its valid
19-
range. `Time::is_valid` has been updated accordingly.
19+
range. `Time::is_valid` has been updated accordingly.
2020

2121
## Changed
2222
- `SystemTable::exit_boot_services` is now `unsafe`. See that method's
2323
documentation for details of obligations for callers.
2424
- `BootServices::allocate_pool` now returns `NonZero<u8>` instead of
2525
`*mut u8`.
2626
- `helpers::system_table` is deprecated, use `table::system_table_boot` instead.
27+
- `BootServices::memory_map` changed its signature from \
28+
`pub fn memory_map<'buf>(&self, buffer: &'buf mut [u8]) -> Result<MemoryMap<'buf>> {` \
29+
to \
30+
`pub fn memory_map(&self, mt: MemoryType) -> Result<MemoryMap>`
31+
- Allocations now happen automatically internally on the UEFI heap. Also, the
32+
returned type is automatically freed on the UEFI heap, as long as boot
33+
services are not excited. By removing the need for that explicit buffer and
34+
the lifetime, the API is simpler.
2735

2836
## Removed
2937
- Removed the `panic-on-logger-errors` feature of the `uefi` crate. Logger

uefi/src/table/boot.rs

Lines changed: 91 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -221,37 +221,46 @@ impl BootServices {
221221
mmm
222222
}
223223

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

256265
assert_eq!(
257266
(map_buffer as usize) % mem::align_of::<MemoryDescriptor>(),
@@ -265,18 +274,14 @@ impl BootServices {
265274
map_buffer,
266275
&mut map_key.0,
267276
&mut desc_size,
268-
&mut entry_version,
277+
&mut desc_version,
269278
)
270279
}
271-
.to_result_with_val(move || {
272-
let len = map_size / desc_size;
273-
274-
MemoryMap {
275-
key: map_key,
276-
buf: buffer,
277-
desc_size,
278-
len,
279-
}
280+
.to_result_with_val(|| MemoryMapMeta {
281+
map_size,
282+
desc_size,
283+
map_key,
284+
desc_version,
280285
})
281286
}
282287

@@ -1689,6 +1694,14 @@ impl MemoryMapBackingMemory {
16891694
Self(slice)
16901695
}
16911696

1697+
/// Creates an instance from the provided memory, which is not necessarily
1698+
/// on the UEFI heap.
1699+
#[cfg(test)]
1700+
fn from_slice(buffer: &mut [u8]) -> Self {
1701+
let len = buffer.len();
1702+
unsafe { Self::from_raw(buffer.as_mut_ptr(), len) }
1703+
}
1704+
16921705
/// Returns a "safe" best-effort size hint for the memory map size with
16931706
/// some additional bytes in buffer compared to the [`MemoryMapMeta`].
16941707
/// This helps
@@ -1703,8 +1716,15 @@ impl MemoryMapBackingMemory {
17031716
mmm.map_size + extra_size
17041717
}
17051718

1706-
/// Returns the raw pointer to the beginning of the allocation.
1707-
pub fn as_ptr_mut(&mut self) -> *mut u8 {
1719+
/// Returns a raw pointer to the beginning of the allocation.
1720+
#[must_use]
1721+
pub fn as_ptr(&self) -> *const u8 {
1722+
self.0.as_ptr().cast()
1723+
}
1724+
1725+
/// Returns a mutable raw pointer to the beginning of the allocation.
1726+
#[must_use]
1727+
pub fn as_mut_ptr(&mut self) -> *mut u8 {
17081728
self.0.as_ptr().cast()
17091729
}
17101730

@@ -1788,31 +1808,27 @@ impl MemoryMapMeta {
17881808
///
17891809
/// [0]: https://github.com/tianocore/edk2/blob/7142e648416ff5d3eac6c6d607874805f5de0ca8/MdeModulePkg/Core/PiSmmCore/Page.c#L1059
17901810
#[derive(Debug)]
1791-
pub struct MemoryMap<'buf> {
1811+
pub struct MemoryMap {
1812+
/// Backing memory, properly initialized at this point.
1813+
buf: MemoryMapBackingMemory,
17921814
key: MemoryMapKey,
1793-
buf: &'buf mut [u8],
17941815
/// Usually bound to the size of a [`MemoryDescriptor`] but can indicate if
17951816
/// this field is ever extended by a new UEFI standard.
17961817
desc_size: usize,
17971818
len: usize,
17981819
}
17991820

1800-
impl<'buf> MemoryMap<'buf> {
1801-
/// Creates a [`MemoryMap`] from the given buffer and entry size.
1802-
/// The entry size is usually bound to the size of a [`MemoryDescriptor`]
1803-
/// but can indicate if this field is ever extended by a new UEFI standard.
1804-
///
1805-
/// This allows parsing a memory map provided by a kernel after boot
1806-
/// services have already exited.
1807-
pub fn from_raw(buf: &'buf mut [u8], desc_size: usize) -> Self {
1808-
assert!(!buf.is_empty());
1809-
assert_eq!(
1810-
buf.len() % desc_size,
1811-
0,
1812-
"The buffer length must be a multiple of the desc_size"
1813-
);
1821+
impl MemoryMap {
1822+
/// Creates a [`MemoryMap`] from the give initialized memory map behind
1823+
/// the buffer and the reported `desc_size` from UEFI.
1824+
pub(crate) fn from_initialized_mem(buf: MemoryMapBackingMemory, meta: MemoryMapMeta) -> Self {
1825+
let MemoryMapMeta {
1826+
map_size,
1827+
desc_size,
1828+
..
1829+
} = meta;
18141830
assert!(desc_size >= mem::size_of::<MemoryDescriptor>());
1815-
let len = buf.len() / desc_size;
1831+
let len = map_size / desc_size;
18161832
MemoryMap {
18171833
key: MemoryMapKey(0),
18181834
buf,
@@ -1821,6 +1837,20 @@ impl<'buf> MemoryMap<'buf> {
18211837
}
18221838
}
18231839

1840+
#[cfg(test)]
1841+
fn from_raw(buf: &mut [u8], desc_size: usize) -> Self {
1842+
let mem = MemoryMapBackingMemory::from_slice(buf);
1843+
Self::from_initialized_mem(
1844+
mem,
1845+
MemoryMapMeta {
1846+
map_size: buf.len(),
1847+
desc_size,
1848+
map_key: MemoryMapKey(0),
1849+
desc_version: MemoryDescriptor::VERSION,
1850+
},
1851+
)
1852+
}
1853+
18241854
#[must_use]
18251855
/// Returns the unique [`MemoryMapKey`] associated with the memory map.
18261856
pub fn key(&self) -> MemoryMapKey {
@@ -1915,7 +1945,7 @@ impl<'buf> MemoryMap<'buf> {
19151945

19161946
/// Returns a reference to the [`MemoryDescriptor`] at `index` or `None` if out of bounds.
19171947
#[must_use]
1918-
pub fn get(&self, index: usize) -> Option<&'buf MemoryDescriptor> {
1948+
pub fn get(&self, index: usize) -> Option<&MemoryDescriptor> {
19191949
if index >= self.len {
19201950
return None;
19211951
}
@@ -1933,7 +1963,7 @@ impl<'buf> MemoryMap<'buf> {
19331963

19341964
/// Returns a mut reference to the [`MemoryDescriptor`] at `index` or `None` if out of bounds.
19351965
#[must_use]
1936-
pub fn get_mut(&mut self, index: usize) -> Option<&'buf mut MemoryDescriptor> {
1966+
pub fn get_mut(&mut self, index: usize) -> Option<&mut MemoryDescriptor> {
19371967
if index >= self.len {
19381968
return None;
19391969
}
@@ -1950,15 +1980,15 @@ impl<'buf> MemoryMap<'buf> {
19501980
}
19511981
}
19521982

1953-
impl core::ops::Index<usize> for MemoryMap<'_> {
1983+
impl core::ops::Index<usize> for MemoryMap {
19541984
type Output = MemoryDescriptor;
19551985

19561986
fn index(&self, index: usize) -> &Self::Output {
19571987
self.get(index).unwrap()
19581988
}
19591989
}
19601990

1961-
impl core::ops::IndexMut<usize> for MemoryMap<'_> {
1991+
impl core::ops::IndexMut<usize> for MemoryMap {
19621992
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
19631993
self.get_mut(index).unwrap()
19641994
}
@@ -1967,13 +1997,13 @@ impl core::ops::IndexMut<usize> for MemoryMap<'_> {
19671997
/// An iterator of [`MemoryDescriptor`]. The underlying memory map is always
19681998
/// associated with a unique [`MemoryMapKey`].
19691999
#[derive(Debug, Clone)]
1970-
pub struct MemoryMapIter<'buf> {
1971-
memory_map: &'buf MemoryMap<'buf>,
2000+
pub struct MemoryMapIter<'a> {
2001+
memory_map: &'a MemoryMap,
19722002
index: usize,
19732003
}
19742004

1975-
impl<'buf> Iterator for MemoryMapIter<'buf> {
1976-
type Item = &'buf MemoryDescriptor;
2005+
impl<'a> Iterator for MemoryMapIter<'a> {
2006+
type Item = &'a MemoryDescriptor;
19772007

19782008
fn size_hint(&self) -> (usize, Option<usize>) {
19792009
let sz = self.memory_map.len - self.index;
@@ -2224,7 +2254,7 @@ mod tests {
22242254
}
22252255

22262256
// Added for debug purposes on test failure
2227-
impl core::fmt::Display for MemoryMap<'_> {
2257+
impl core::fmt::Display for MemoryMap {
22282258
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
22292259
writeln!(f)?;
22302260
for desc in self.entries() {

0 commit comments

Comments
 (0)