Skip to content

Commit cfcbaa2

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 1e041d6 commit cfcbaa2

File tree

4 files changed

+119
-114
lines changed

4 files changed

+119
-114
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: 8 additions & 0 deletions
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

Lines changed: 101 additions & 63 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

@@ -1676,13 +1681,22 @@ 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);
16801685

1681-
Ok(Self(slice))
1686+
Self(slice)
1687+
}
1688+
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
16851698
/// especially created with exiting boot services in mind.
1699+
#[must_use]
16861700
pub fn allocation_size_hint(mms: GetMemoryMapMeta) -> usize {
16871701
let GetMemoryMapMeta {
16881702
desc_size,
@@ -1711,8 +1725,15 @@ impl MemoryMapBackingMemory {
17111725
map_size + extra_size
17121726
}
17131727

1714-
/// Returns the raw pointer to the beginning of the allocation.
1715-
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 {
17161737
self.0.as_ptr().cast()
17171738
}
17181739

@@ -1770,31 +1791,34 @@ pub struct GetMemoryMapMeta {
17701791
///
17711792
/// [0]: https://github.com/tianocore/edk2/blob/7142e648416ff5d3eac6c6d607874805f5de0ca8/MdeModulePkg/Core/PiSmmCore/Page.c#L1059
17721793
#[derive(Debug)]
1773-
pub struct MemoryMap<'buf> {
1794+
pub struct MemoryMap {
1795+
/// Backing memory, properly initialized at this point.
1796+
buf: MemoryMapBackingMemory,
17741797
key: MemoryMapKey,
1775-
buf: &'buf mut [u8],
17761798
/// Usually bound to the size of a [`MemoryDescriptor`] but can indicate if
17771799
/// this field is ever extended by a new UEFI standard.
17781800
desc_size: usize,
17791801
len: usize,
17801802
}
17811803

1782-
impl<'buf> MemoryMap<'buf> {
1783-
/// Creates a [`MemoryMap`] from the given buffer and entry size.
1784-
/// The entry size is usually bound to the size of a [`MemoryDescriptor`]
1785-
/// but can indicate if this field is ever extended by a new UEFI standard.
1786-
///
1787-
/// This allows parsing a memory map provided by a kernel after boot
1788-
/// services have already exited.
1789-
pub fn from_raw(buf: &'buf mut [u8], desc_size: usize) -> Self {
1790-
assert!(!buf.is_empty());
1791-
assert_eq!(
1792-
buf.len() % desc_size,
1793-
0,
1794-
"The buffer length must be a multiple of the desc_size"
1795-
);
1804+
impl MemoryMap {
1805+
/*pub fn new() -> Self {
1806+
1807+
}*/
1808+
1809+
/// Creates a [`MemoryMap`] from the give initialized memory map behind
1810+
/// the buffer and the reported `desc_size` from UEFI.
1811+
pub(crate) fn from_initialized_mem(
1812+
buf: MemoryMapBackingMemory,
1813+
props: GetMemoryMapMeta,
1814+
) -> Self {
1815+
let GetMemoryMapMeta {
1816+
map_size,
1817+
desc_size,
1818+
..
1819+
} = props;
17961820
assert!(desc_size >= mem::size_of::<MemoryDescriptor>());
1797-
let len = buf.len() / desc_size;
1821+
let len = map_size / desc_size;
17981822
MemoryMap {
17991823
key: MemoryMapKey(0),
18001824
buf,
@@ -1803,6 +1827,20 @@ impl<'buf> MemoryMap<'buf> {
18031827
}
18041828
}
18051829

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

18981936
/// Returns a reference to the [`MemoryDescriptor`] at `index` or `None` if out of bounds.
18991937
#[must_use]
1900-
pub fn get(&self, index: usize) -> Option<&'buf MemoryDescriptor> {
1938+
pub fn get(&self, index: usize) -> Option<&MemoryDescriptor> {
19011939
if index >= self.len {
19021940
return None;
19031941
}
@@ -1915,7 +1953,7 @@ impl<'buf> MemoryMap<'buf> {
19151953

19161954
/// Returns a mut reference to the [`MemoryDescriptor`] at `index` or `None` if out of bounds.
19171955
#[must_use]
1918-
pub fn get_mut(&mut self, index: usize) -> Option<&'buf mut MemoryDescriptor> {
1956+
pub fn get_mut(&mut self, index: usize) -> Option<&mut MemoryDescriptor> {
19191957
if index >= self.len {
19201958
return None;
19211959
}
@@ -1932,15 +1970,15 @@ impl<'buf> MemoryMap<'buf> {
19321970
}
19331971
}
19341972

1935-
impl core::ops::Index<usize> for MemoryMap<'_> {
1973+
impl core::ops::Index<usize> for MemoryMap {
19361974
type Output = MemoryDescriptor;
19371975

19381976
fn index(&self, index: usize) -> &Self::Output {
19391977
self.get(index).unwrap()
19401978
}
19411979
}
19421980

1943-
impl core::ops::IndexMut<usize> for MemoryMap<'_> {
1981+
impl core::ops::IndexMut<usize> for MemoryMap {
19441982
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
19451983
self.get_mut(index).unwrap()
19461984
}
@@ -1949,13 +1987,13 @@ impl core::ops::IndexMut<usize> for MemoryMap<'_> {
19491987
/// An iterator of [`MemoryDescriptor`]. The underlying memory map is always
19501988
/// associated with a unique [`MemoryMapKey`].
19511989
#[derive(Debug, Clone)]
1952-
pub struct MemoryMapIter<'buf> {
1953-
memory_map: &'buf MemoryMap<'buf>,
1990+
pub struct MemoryMapIter<'a> {
1991+
memory_map: &'a MemoryMap,
19541992
index: usize,
19551993
}
19561994

1957-
impl<'buf> Iterator for MemoryMapIter<'buf> {
1958-
type Item = &'buf MemoryDescriptor;
1995+
impl<'a> Iterator for MemoryMapIter<'a> {
1996+
type Item = &'a MemoryDescriptor;
19591997

19601998
fn size_hint(&self) -> (usize, Option<usize>) {
19611999
let sz = self.memory_map.len - self.index;
@@ -2206,7 +2244,7 @@ mod tests {
22062244
}
22072245

22082246
// Added for debug purposes on test failure
2209-
impl core::fmt::Display for MemoryMap<'_> {
2247+
impl core::fmt::Display for MemoryMap {
22102248
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
22112249
writeln!(f)?;
22122250
for desc in self.entries() {

0 commit comments

Comments
 (0)