Skip to content

Commit c3d3ed3

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 9774d89 commit c3d3ed3

File tree

4 files changed

+111
-114
lines changed

4 files changed

+111
-114
lines changed

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

+1-10
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

+8
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@
1515
## Changed
1616
- `SystemTable::exit_boot_services` is now `unsafe`. See that method's
1717
documentation for details of obligations for callers.
18+
- `BootServices::memory_map` changed its signature from \
19+
`pub fn memory_map<'buf>(&self, buffer: &'buf mut [u8]) -> Result<MemoryMap<'buf>> {` \
20+
to \
21+
`pub fn memory_map(&self, mt: MemoryType) -> Result<MemoryMap>`
22+
- Allocations now happen automatically internally on the UEFI heap. Also, the
23+
returned type is automatically freed on the UEFI heap, as long as boot
24+
services are not excited. By removing the need for that explicit buffer and
25+
the lifetime, the API is simpler.
1826

1927
## Removed
2028
- Removed the `panic-on-logger-errors` feature of the `uefi` crate. Logger

uefi/src/table/boot.rs

+93-63
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 MemoryMapMeta {
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<MemoryMapMeta> {
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(|| MemoryMapMeta {
277+
map_size,
278+
desc_size,
279+
map_key,
280+
desc_version,
276281
})
277282
}
278283

@@ -1676,9 +1681,17 @@ impl MemoryMapBackingMemory {
16761681
assert_eq!(ptr.align_offset(mem::align_of::<MemoryDescriptor>()), 0);
16771682

16781683
let ptr = NonNull::new(ptr).expect("UEFI should never return a null ptr. An error should have been reflected via an Err earlier.");
1679-
let slice = NonNull::slice_from_raw_parts(ptr, alloc_size);
1684+
let slice = NonNull::slice_from_raw_parts(ptr, len);
1685+
1686+
Self(slice)
1687+
}
16801688

1681-
Ok(Self(slice))
1689+
/// Creates an instance from the provided memory, which is not necessarily
1690+
/// on the UEFI heap.
1691+
#[cfg(test)]
1692+
fn from_slice(buffer: &mut [u8]) -> Self {
1693+
let len = buffer.len();
1694+
Self::from_raw(buffer.as_mut_ptr(), len)
16821695
}
16831696

16841697
/// Returns a best-effort size hint of the memory map size. This is
@@ -1712,8 +1725,15 @@ impl MemoryMapBackingMemory {
17121725
map_size + extra_size
17131726
}
17141727

1715-
/// Returns the raw pointer to the beginning of the allocation.
1716-
pub fn as_ptr_mut(&mut self) -> *mut u8 {
1728+
/// Returns a raw pointer to the beginning of the allocation.
1729+
#[must_use]
1730+
pub fn as_ptr(&self) -> *const u8 {
1731+
self.0.as_ptr().cast()
1732+
}
1733+
1734+
/// Returns a mutable raw pointer to the beginning of the allocation.
1735+
#[must_use]
1736+
pub fn as_mut_ptr(&mut self) -> *mut u8 {
17171737
self.0.as_ptr().cast()
17181738
}
17191739

@@ -1780,31 +1800,27 @@ impl MemoryMapMeta {
17801800
///
17811801
/// [0]: https://github.com/tianocore/edk2/blob/7142e648416ff5d3eac6c6d607874805f5de0ca8/MdeModulePkg/Core/PiSmmCore/Page.c#L1059
17821802
#[derive(Debug)]
1783-
pub struct MemoryMap<'buf> {
1803+
pub struct MemoryMap {
1804+
/// Backing memory, properly initialized at this point.
1805+
buf: MemoryMapBackingMemory,
17841806
key: MemoryMapKey,
1785-
buf: &'buf mut [u8],
17861807
/// Usually bound to the size of a [`MemoryDescriptor`] but can indicate if
17871808
/// this field is ever extended by a new UEFI standard.
17881809
desc_size: usize,
17891810
len: usize,
17901811
}
17911812

1792-
impl<'buf> MemoryMap<'buf> {
1793-
/// Creates a [`MemoryMap`] from the given buffer and entry size.
1794-
/// The entry size is usually bound to the size of a [`MemoryDescriptor`]
1795-
/// but can indicate if this field is ever extended by a new UEFI standard.
1796-
///
1797-
/// This allows parsing a memory map provided by a kernel after boot
1798-
/// services have already exited.
1799-
pub fn from_raw(buf: &'buf mut [u8], desc_size: usize) -> Self {
1800-
assert!(!buf.is_empty());
1801-
assert_eq!(
1802-
buf.len() % desc_size,
1803-
0,
1804-
"The buffer length must be a multiple of the desc_size"
1805-
);
1813+
impl MemoryMap {
1814+
/// Creates a [`MemoryMap`] from the give initialized memory map behind
1815+
/// the buffer and the reported `desc_size` from UEFI.
1816+
pub(crate) fn from_initialized_mem(buf: MemoryMapBackingMemory, meta: MemoryMapMeta) -> Self {
1817+
let MemoryMapMeta {
1818+
map_size,
1819+
desc_size,
1820+
..
1821+
} = meta;
18061822
assert!(desc_size >= mem::size_of::<MemoryDescriptor>());
1807-
let len = buf.len() / desc_size;
1823+
let len = map_size / desc_size;
18081824
MemoryMap {
18091825
key: MemoryMapKey(0),
18101826
buf,
@@ -1813,6 +1829,20 @@ impl<'buf> MemoryMap<'buf> {
18131829
}
18141830
}
18151831

1832+
#[cfg(test)]
1833+
fn from_raw(buf: &mut [u8], desc_size: usize) -> Self {
1834+
let mem = MemoryMapBackingMemory::from_slice(buf);
1835+
Self::from_initialized_mem(
1836+
mem,
1837+
MemoryMapMeta {
1838+
map_size: buf.len(),
1839+
desc_size,
1840+
map_key: MemoryMapKey(0),
1841+
desc_version: MemoryDescriptor::VERSION,
1842+
},
1843+
)
1844+
}
1845+
18161846
#[must_use]
18171847
/// Returns the unique [`MemoryMapKey`] associated with the memory map.
18181848
pub fn key(&self) -> MemoryMapKey {
@@ -1907,7 +1937,7 @@ impl<'buf> MemoryMap<'buf> {
19071937

19081938
/// Returns a reference to the [`MemoryDescriptor`] at `index` or `None` if out of bounds.
19091939
#[must_use]
1910-
pub fn get(&self, index: usize) -> Option<&'buf MemoryDescriptor> {
1940+
pub fn get(&self, index: usize) -> Option<&MemoryDescriptor> {
19111941
if index >= self.len {
19121942
return None;
19131943
}
@@ -1925,7 +1955,7 @@ impl<'buf> MemoryMap<'buf> {
19251955

19261956
/// Returns a mut reference to the [`MemoryDescriptor`] at `index` or `None` if out of bounds.
19271957
#[must_use]
1928-
pub fn get_mut(&mut self, index: usize) -> Option<&'buf mut MemoryDescriptor> {
1958+
pub fn get_mut(&mut self, index: usize) -> Option<&mut MemoryDescriptor> {
19291959
if index >= self.len {
19301960
return None;
19311961
}
@@ -1942,15 +1972,15 @@ impl<'buf> MemoryMap<'buf> {
19421972
}
19431973
}
19441974

1945-
impl core::ops::Index<usize> for MemoryMap<'_> {
1975+
impl core::ops::Index<usize> for MemoryMap {
19461976
type Output = MemoryDescriptor;
19471977

19481978
fn index(&self, index: usize) -> &Self::Output {
19491979
self.get(index).unwrap()
19501980
}
19511981
}
19521982

1953-
impl core::ops::IndexMut<usize> for MemoryMap<'_> {
1983+
impl core::ops::IndexMut<usize> for MemoryMap {
19541984
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
19551985
self.get_mut(index).unwrap()
19561986
}
@@ -1959,13 +1989,13 @@ impl core::ops::IndexMut<usize> for MemoryMap<'_> {
19591989
/// An iterator of [`MemoryDescriptor`]. The underlying memory map is always
19601990
/// associated with a unique [`MemoryMapKey`].
19611991
#[derive(Debug, Clone)]
1962-
pub struct MemoryMapIter<'buf> {
1963-
memory_map: &'buf MemoryMap<'buf>,
1992+
pub struct MemoryMapIter<'a> {
1993+
memory_map: &'a MemoryMap,
19641994
index: usize,
19651995
}
19661996

1967-
impl<'buf> Iterator for MemoryMapIter<'buf> {
1968-
type Item = &'buf MemoryDescriptor;
1997+
impl<'a> Iterator for MemoryMapIter<'a> {
1998+
type Item = &'a MemoryDescriptor;
19691999

19702000
fn size_hint(&self) -> (usize, Option<usize>) {
19712001
let sz = self.memory_map.len - self.index;
@@ -2216,7 +2246,7 @@ mod tests {
22162246
}
22172247

22182248
// Added for debug purposes on test failure
2219-
impl core::fmt::Display for MemoryMap<'_> {
2249+
impl core::fmt::Display for MemoryMap {
22202250
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
22212251
writeln!(f)?;
22222252
for desc in self.entries() {

0 commit comments

Comments
 (0)