Skip to content

Commit a97a62b

Browse files
committed
uefi: MemoryMap now owns the 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 size can be obtained by another 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 bac1911 commit a97a62b

File tree

3 files changed

+142
-123
lines changed

3 files changed

+142
-123
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/src/table/boot.rs

+132-72
Original file line numberDiff line numberDiff line change
@@ -215,37 +215,46 @@ impl BootServices {
215215
}
216216
}
217217

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

250259
assert_eq!(
251260
(map_buffer as usize) % mem::align_of::<MemoryDescriptor>(),
@@ -259,18 +268,14 @@ impl BootServices {
259268
map_buffer,
260269
&mut map_key.0,
261270
&mut desc_size,
262-
&mut entry_version,
271+
&mut desc_version,
263272
)
264273
}
265-
.to_result_with_val(move || {
266-
let len = map_size / desc_size;
267-
268-
MemoryMap {
269-
key: map_key,
270-
buf: buffer,
271-
desc_size,
272-
len,
273-
}
274+
.to_result_with_val(|| GetMemoryMapResult {
275+
map_size,
276+
desc_size,
277+
map_key,
278+
desc_version,
274279
})
275280
}
276281

@@ -1637,31 +1642,45 @@ pub struct MemoryMapKey(usize);
16371642
/// When this type is dropped and boot services are not exited yet, the memory
16381643
/// is freed.
16391644
#[derive(Debug)]
1640-
pub struct MemoryMapBackingMemory(NonNull<[u8]> /* buffer on UEFI heap */);
1645+
pub struct MemoryMapBackingMemory(NonNull<[u8]>);
16411646

16421647
impl MemoryMapBackingMemory {
16431648
/// Constructs a new [`MemoryMapBackingMemory`].
16441649
///
1645-
/// # Parameters
1650+
/// # Parameters
16461651
/// - `memory_type`: The memory type for the memory map allocation.
16471652
/// Typically, [`MemoryType::LOADER_DATA`] for regular UEFI applications.
16481653
pub(crate) fn new(memory_type: MemoryType) -> Result<Self> {
16491654
let st = system_table_boot().expect("Should have boot services activated");
16501655
let bs = st.boot_services();
16511656

16521657
let memory_map_size = bs.memory_map_size();
1653-
let alloc_size = Self::allocation_size_hint(memory_map_size);
1654-
let ptr = bs.allocate_pool(memory_type, alloc_size)?;
1658+
let len = Self::allocation_size_hint(memory_map_size);
1659+
let ptr = bs.allocate_pool(memory_type, len)?;
1660+
assert_eq!(ptr.align_offset(mem::align_of::<MemoryDescriptor>()), 0);
1661+
Ok(Self::from_raw(ptr, len))
1662+
}
1663+
1664+
fn from_raw(ptr: *mut u8, len: usize) -> Self {
16551665
assert_eq!(ptr.align_offset(mem::align_of::<MemoryDescriptor>()), 0);
16561666

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

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

16631681
/// Returns a best-effort size hint of the memory map size. This is
16641682
/// especially created with exiting boot services in mind.
1683+
#[must_use]
16651684
pub fn allocation_size_hint(mms: MemoryMapSize) -> usize {
16661685
let MemoryMapSize {
16671686
desc_size,
@@ -1689,16 +1708,34 @@ impl MemoryMapBackingMemory {
16891708
let allocation_size = map_size + extra_size;
16901709
allocation_size
16911710
}
1692-
1693-
/// Returns the raw pointer to the beginning of the allocation.
1694-
pub fn as_ptr_mut(&mut self) -> *mut u8 {
1711+
1712+
/// Returns a raw pointer to the beginning of the allocation.
1713+
#[must_use]
1714+
pub fn as_ptr(&self) -> *const u8 {
16951715
self.0.as_ptr().cast()
16961716
}
1697-
1717+
1718+
/// Returns a mutable raw pointer to the beginning of the allocation.
1719+
#[must_use]
1720+
pub fn as_mut_ptr(&mut self) -> *mut u8 {
1721+
self.0.as_ptr().cast()
1722+
}
1723+
16981724
/// Returns the length of the allocation.
1725+
#[must_use]
16991726
pub fn len(&self) -> usize {
17001727
self.0.len()
1701-
}
1728+
}
1729+
1730+
/*#[must_use]
1731+
pub fn as_slice(&self) -> &[u8] {
1732+
self.0.as_ref()
1733+
}*/
1734+
1735+
#[must_use]
1736+
pub fn as_mut_slice(&mut self) -> &mut [u8] {
1737+
unsafe { self.0.as_mut() }
1738+
}
17021739
}
17031740

17041741
impl Drop for MemoryMapBackingMemory {
@@ -1724,6 +1761,14 @@ pub struct MemoryMapSize {
17241761
pub map_size: usize,
17251762
}
17261763

1764+
#[derive(Copy, Clone, Debug)]
1765+
pub struct GetMemoryMapResult {
1766+
pub map_size: usize,
1767+
pub desc_size: usize,
1768+
pub map_key: MemoryMapKey,
1769+
pub desc_version: u32,
1770+
}
1771+
17271772
/// An accessory to the memory map that can be either iterated or
17281773
/// indexed like an array.
17291774
///
@@ -1739,35 +1784,36 @@ pub struct MemoryMapSize {
17391784
/// always use `entry_size` as step-size when interfacing with the memory map on
17401785
/// a low level.
17411786
///
1742-
///
1743-
///
17441787
/// [0]: https://github.com/tianocore/edk2/blob/7142e648416ff5d3eac6c6d607874805f5de0ca8/MdeModulePkg/Core/PiSmmCore/Page.c#L1059
17451788
#[derive(Debug)]
1746-
pub struct MemoryMap<'buf> {
1789+
pub struct MemoryMap {
1790+
/// Backing memory, properly initialized at this point.
1791+
buf: MemoryMapBackingMemory,
17471792
key: MemoryMapKey,
1748-
buf: &'buf mut [u8],
17491793
/// Usually bound to the size of a [`MemoryDescriptor`] but can indicate if
17501794
/// this field is ever extended by a new UEFI standard.
17511795
desc_size: usize,
17521796
len: usize,
17531797
}
17541798

1755-
impl<'buf> MemoryMap<'buf> {
1756-
/// Creates a [`MemoryMap`] from the given buffer and entry size.
1757-
/// The entry size is usually bound to the size of a [`MemoryDescriptor`]
1758-
/// but can indicate if this field is ever extended by a new UEFI standard.
1759-
///
1760-
/// This allows parsing a memory map provided by a kernel after boot
1761-
/// services have already exited.
1762-
pub fn from_raw(buf: &'buf mut [u8], desc_size: usize) -> Self {
1763-
assert!(!buf.is_empty());
1764-
assert_eq!(
1765-
buf.len() % desc_size,
1766-
0,
1767-
"The buffer length must be a multiple of the desc_size"
1768-
);
1799+
impl MemoryMap {
1800+
/*pub fn new() -> Self {
1801+
1802+
}*/
1803+
1804+
/// Creates a [`MemoryMap`] from the give initialized memory map behind
1805+
/// the buffer and the reported `desc_size` from UEFI.
1806+
pub(crate) fn from_initialized_mem(
1807+
buf: MemoryMapBackingMemory,
1808+
props: GetMemoryMapResult,
1809+
) -> Self {
1810+
let GetMemoryMapResult {
1811+
map_size,
1812+
desc_size,
1813+
..
1814+
} = props;
17691815
assert!(desc_size >= mem::size_of::<MemoryDescriptor>());
1770-
let len = buf.len() / desc_size;
1816+
let len = map_size / desc_size;
17711817
MemoryMap {
17721818
key: MemoryMapKey(0),
17731819
buf,
@@ -1776,6 +1822,20 @@ impl<'buf> MemoryMap<'buf> {
17761822
}
17771823
}
17781824

1825+
#[cfg(test)]
1826+
fn from_raw(buf: &mut [u8], desc_size: usize) -> Self {
1827+
let mem = MemoryMapBackingMemory::from_slice(buf);
1828+
Self::from_initialized_mem(
1829+
mem,
1830+
GetMemoryMapResult {
1831+
map_size: buf.len(),
1832+
desc_size,
1833+
map_key: MemoryMapKey(0),
1834+
desc_version: MemoryDescriptor::VERSION,
1835+
},
1836+
)
1837+
}
1838+
17791839
#[must_use]
17801840
/// Returns the unique [`MemoryMapKey`] associated with the memory map.
17811841
pub fn key(&self) -> MemoryMapKey {
@@ -1870,7 +1930,7 @@ impl<'buf> MemoryMap<'buf> {
18701930

18711931
/// Returns a reference to the [`MemoryDescriptor`] at `index` or `None` if out of bounds.
18721932
#[must_use]
1873-
pub fn get(&self, index: usize) -> Option<&'buf MemoryDescriptor> {
1933+
pub fn get(&self, index: usize) -> Option<&MemoryDescriptor> {
18741934
if index >= self.len {
18751935
return None;
18761936
}
@@ -1888,7 +1948,7 @@ impl<'buf> MemoryMap<'buf> {
18881948

18891949
/// Returns a mut reference to the [`MemoryDescriptor`] at `index` or `None` if out of bounds.
18901950
#[must_use]
1891-
pub fn get_mut(&mut self, index: usize) -> Option<&'buf mut MemoryDescriptor> {
1951+
pub fn get_mut(&mut self, index: usize) -> Option<&mut MemoryDescriptor> {
18921952
if index >= self.len {
18931953
return None;
18941954
}
@@ -1905,15 +1965,15 @@ impl<'buf> MemoryMap<'buf> {
19051965
}
19061966
}
19071967

1908-
impl core::ops::Index<usize> for MemoryMap<'_> {
1968+
impl core::ops::Index<usize> for MemoryMap {
19091969
type Output = MemoryDescriptor;
19101970

19111971
fn index(&self, index: usize) -> &Self::Output {
19121972
self.get(index).unwrap()
19131973
}
19141974
}
19151975

1916-
impl core::ops::IndexMut<usize> for MemoryMap<'_> {
1976+
impl core::ops::IndexMut<usize> for MemoryMap {
19171977
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
19181978
self.get_mut(index).unwrap()
19191979
}
@@ -1922,13 +1982,13 @@ impl core::ops::IndexMut<usize> for MemoryMap<'_> {
19221982
/// An iterator of [`MemoryDescriptor`]. The underlying memory map is always
19231983
/// associated with a unique [`MemoryMapKey`].
19241984
#[derive(Debug, Clone)]
1925-
pub struct MemoryMapIter<'buf> {
1926-
memory_map: &'buf MemoryMap<'buf>,
1985+
pub struct MemoryMapIter<'a> {
1986+
memory_map: &'a MemoryMap,
19271987
index: usize,
19281988
}
19291989

1930-
impl<'buf> Iterator for MemoryMapIter<'buf> {
1931-
type Item = &'buf MemoryDescriptor;
1990+
impl<'a> Iterator for MemoryMapIter<'a> {
1991+
type Item = &'a MemoryDescriptor;
19321992

19331993
fn size_hint(&self) -> (usize, Option<usize>) {
19341994
let sz = self.memory_map.len - self.index;
@@ -2179,7 +2239,7 @@ mod tests {
21792239
}
21802240

21812241
// Added for debug purposes on test failure
2182-
impl core::fmt::Display for MemoryMap<'_> {
2242+
impl core::fmt::Display for MemoryMap {
21832243
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
21842244
writeln!(f)?;
21852245
for desc in self.entries() {

0 commit comments

Comments
 (0)