Skip to content

Commit aab512f

Browse files
committed
uefi: introduce MemoryMapBackingMemory helper type
1 parent 3957106 commit aab512f

File tree

1 file changed

+101
-1
lines changed

1 file changed

+101
-1
lines changed

uefi/src/table/boot.rs

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! UEFI services available during boot.
22
3-
use super::Revision;
3+
use super::{system_table_boot, Revision};
44
use crate::data_types::{Align, PhysicalAddress};
55
use crate::proto::device_path::DevicePath;
66
use crate::proto::loaded_image::LoadedImage;
@@ -1617,6 +1617,106 @@ impl Align for MemoryDescriptor {
16171617
#[repr(C)]
16181618
pub struct MemoryMapKey(usize);
16191619

1620+
/// The backing memory for the UEFI memory app on the UEFI heap, allocated using
1621+
/// the UEFI boot services allocator. This occupied memory will also be
1622+
/// reflected in the memory map itself.
1623+
///
1624+
/// Although untyped, it is similar to the `Box` type in terms of heap
1625+
/// allocation and deallocation, as well as ownership of the corresponding
1626+
/// memory. Apart from that, this type only has the semantics of a buffer.
1627+
///
1628+
/// The memory is untyped, which is necessary due to the nature of the UEFI
1629+
/// spec. It still ensures a correct alignment to hold [`MemoryDescriptor`]. The
1630+
/// size of the buffer is sufficient to hold the memory map at the point in time
1631+
/// where this is created. Note that due to (not obvious or asynchronous)
1632+
/// allocations/allocations in your environment, this might be outdated at the
1633+
/// time you store the memory map in it.
1634+
///
1635+
/// Note that due to the nature of the UEFI memory app, this buffer might
1636+
/// hold (a few) bytes more than necessary. The `map_size` reported by
1637+
/// `get_memory_map` tells the actual size.
1638+
///
1639+
/// When this type is dropped and boot services are not exited yet, the memory
1640+
/// is freed.
1641+
#[derive(Debug)]
1642+
pub struct MemoryMapBackingMemory(NonNull<[u8]> /* buffer on UEFI heap */);
1643+
1644+
impl MemoryMapBackingMemory {
1645+
/// Constructs a new [`MemoryMapBackingMemory`].
1646+
///
1647+
/// # Parameters
1648+
/// - `memory_type`: The memory type for the memory map allocation.
1649+
/// Typically, [`MemoryType::LOADER_DATA`] for regular UEFI applications.
1650+
pub(crate) fn new(memory_type: MemoryType) -> Result<Self> {
1651+
let st = system_table_boot().expect("Should have boot services activated");
1652+
let bs = st.boot_services();
1653+
1654+
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)?;
1657+
assert_eq!(ptr.align_offset(mem::align_of::<MemoryDescriptor>()), 0);
1658+
1659+
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);
1661+
1662+
Ok(Self(slice))
1663+
}
1664+
1665+
/// Returns a best-effort size hint of the memory map size. This is
1666+
/// especially created with exiting boot services in mind.
1667+
pub fn allocation_size_hint(mms: GetMemoryMapMeta) -> usize {
1668+
let GetMemoryMapMeta {
1669+
desc_size,
1670+
map_size,
1671+
..
1672+
} = mms;
1673+
1674+
// Allocate space for extra entries beyond the current size of the
1675+
// memory map. The value of 8 matches the value in the Linux kernel:
1676+
// https://github.com/torvalds/linux/blob/e544a07438/drivers/firmware/efi/libstub/efistub.h#L173
1677+
let extra_entries = 8;
1678+
1679+
assert!(desc_size > 0);
1680+
// Although very unlikely, this might fail if the memory descriptor is
1681+
// extended by a future UEFI revision by a significant amount, we
1682+
// update the struct, but an old UEFI implementation reports a small
1683+
// size.
1684+
assert!(desc_size >= mem::size_of::<MemoryDescriptor>());
1685+
assert!(map_size > 0);
1686+
1687+
// Ensure the mmap size is (somehow) sane.
1688+
const ONE_GB: usize = 1024 * 1024 * 1024;
1689+
assert!(map_size <= ONE_GB);
1690+
1691+
let extra_size = desc_size * extra_entries;
1692+
let allocation_size = map_size + extra_size;
1693+
allocation_size
1694+
}
1695+
1696+
/// Returns the raw pointer to the beginning of the allocation.
1697+
pub fn as_ptr_mut(&mut self) -> *mut u8 {
1698+
self.0.as_ptr().cast()
1699+
}
1700+
1701+
/// Returns the length of the allocation.
1702+
pub fn len(&self) -> usize {
1703+
self.0.len()
1704+
}
1705+
}
1706+
1707+
impl Drop for MemoryMapBackingMemory {
1708+
fn drop(&mut self) {
1709+
if let Some(bs) = system_table_boot() {
1710+
let res = unsafe { bs.boot_services().free_pool(self.0.as_ptr().cast()) };
1711+
if let Err(e) = res {
1712+
log::error!("Failed to deallocate memory map: {e:?}");
1713+
}
1714+
} else {
1715+
log::debug!("Boot services are excited. Memory map won't be freed using the UEFI boot services allocator.");
1716+
}
1717+
}
1718+
}
1719+
16201720
/// A structure containing the meta attributes associated with a call to
16211721
/// `GetMemoryMap` of UEFI. Note that all values refer to the time this was
16221722
/// called. All following invocations (hidden, subtle, and asynchronous ones)

0 commit comments

Comments
 (0)