Skip to content

Commit e5b3e46

Browse files
committed
uefi: introduce MemoryMapBackingMemory helper type
1 parent c2e6078 commit e5b3e46

File tree

1 file changed

+100
-1
lines changed

1 file changed

+100
-1
lines changed

uefi/src/table/boot.rs

Lines changed: 100 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;
@@ -1615,6 +1615,105 @@ impl Align for MemoryDescriptor {
16151615
#[repr(C)]
16161616
pub struct MemoryMapKey(usize);
16171617

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

0 commit comments

Comments
 (0)