11//! UEFI services available during boot.
22
3- use super :: Revision ;
3+ use super :: { system_table_boot , Revision } ;
44use crate :: data_types:: { Align , PhysicalAddress } ;
55use crate :: proto:: device_path:: DevicePath ;
66use crate :: proto:: loaded_image:: LoadedImage ;
@@ -186,7 +186,7 @@ impl BootServices {
186186 /// for the memory map, as the memory map itself also needs heap memory,
187187 /// and other allocations might occur before that call.
188188 #[ must_use]
189- pub fn memory_map_size ( & self ) -> MemoryMapMeta {
189+ fn memory_map_size ( & self ) -> MemoryMapMeta {
190190 let mut map_size = 0 ;
191191 let mut map_key = MemoryMapKey ( 0 ) ;
192192 let mut desc_size = 0 ;
@@ -209,12 +209,16 @@ impl BootServices {
209209 "Memory map must be a multiple of the reported descriptor size."
210210 ) ;
211211
212- MemoryMapMeta {
212+ let mmm = MemoryMapMeta {
213213 desc_size,
214214 map_size,
215215 map_key,
216216 desc_version,
217- }
217+ } ;
218+
219+ mmm. assert_sanity_checks ( ) ;
220+
221+ mmm
218222 }
219223
220224 /// Stores the current UEFI memory map in the provided buffer.
@@ -1621,6 +1625,111 @@ impl Align for MemoryDescriptor {
16211625#[ repr( C ) ]
16221626pub struct MemoryMapKey ( usize ) ;
16231627
1628+ /// The backing memory for the UEFI memory app on the UEFI heap, allocated using
1629+ /// the UEFI boot services allocator. This occupied memory will also be
1630+ /// reflected in the memory map itself.
1631+ ///
1632+ /// Although untyped, it is similar to the `Box` type in terms of heap
1633+ /// allocation and deallocation, as well as ownership of the corresponding
1634+ /// memory. Apart from that, this type only has the semantics of a buffer.
1635+ ///
1636+ /// The memory is untyped, which is necessary due to the nature of the UEFI
1637+ /// spec. It still ensures a correct alignment to hold [`MemoryDescriptor`]. The
1638+ /// size of the buffer is sufficient to hold the memory map at the point in time
1639+ /// where this is created. Note that due to (not obvious or asynchronous)
1640+ /// allocations/deallocations in your environment, this might be outdated at the
1641+ /// time you store the memory map in it.
1642+ ///
1643+ /// Note that due to the nature of the UEFI memory app, this buffer might
1644+ /// hold (a few) bytes more than necessary. The `map_size` reported by
1645+ /// `get_memory_map` tells the actual size.
1646+ ///
1647+ /// When this type is dropped and boot services are not exited yet, the memory
1648+ /// is freed.
1649+ ///
1650+ /// # Usage
1651+ /// The type is intended to be used like this:
1652+ /// 1. create it using [`MemoryMapBackingMemory::new`]
1653+ /// 2. pass it to [`BootServices::get_memory_map`]
1654+ /// 3. construct a [`MemoryMap`] from it
1655+ #[ derive( Debug ) ]
1656+ #[ allow( clippy:: len_without_is_empty) ] // this type is never empty
1657+ pub ( crate ) struct MemoryMapBackingMemory ( NonNull < [ u8 ] > ) ;
1658+
1659+ impl MemoryMapBackingMemory {
1660+ /// Constructs a new [`MemoryMapBackingMemory`].
1661+ ///
1662+ /// # Parameters
1663+ /// - `memory_type`: The memory type for the memory map allocation.
1664+ /// Typically, [`MemoryType::LOADER_DATA`] for regular UEFI applications.
1665+ pub ( crate ) fn new ( memory_type : MemoryType ) -> Result < Self > {
1666+ let st = system_table_boot ( ) . expect ( "Should have boot services activated" ) ;
1667+ let bs = st. boot_services ( ) ;
1668+
1669+ let memory_map_meta = bs. memory_map_size ( ) ;
1670+ let len = Self :: safe_allocation_size_hint ( memory_map_meta) ;
1671+ let ptr = bs. allocate_pool ( memory_type, len) ?. as_ptr ( ) ;
1672+
1673+ // Should be fine as UEFI always has allocations with a guaranteed
1674+ // alignment of 8 bytes.
1675+ assert_eq ! ( ptr. align_offset( mem:: align_of:: <MemoryDescriptor >( ) ) , 0 ) ;
1676+
1677+ // If this panics, the UEFI implementation is broken.
1678+ assert_eq ! ( memory_map_meta. map_size % memory_map_meta. desc_size, 0 ) ;
1679+
1680+ unsafe { Ok ( Self :: from_raw ( ptr, len) ) }
1681+ }
1682+
1683+ unsafe fn from_raw ( ptr : * mut u8 , len : usize ) -> Self {
1684+ assert_eq ! ( ptr. align_offset( mem:: align_of:: <MemoryDescriptor >( ) ) , 0 ) ;
1685+
1686+ let ptr = NonNull :: new ( ptr) . expect ( "UEFI should never return a null ptr. An error should have been reflected via an Err earlier." ) ;
1687+ let slice = NonNull :: slice_from_raw_parts ( ptr, len) ;
1688+
1689+ Self ( slice)
1690+ }
1691+
1692+ /// Returns a "safe" best-effort size hint for the memory map size with
1693+ /// some additional bytes in buffer compared to the [`MemoryMapMeta`].
1694+ /// This helps
1695+ #[ must_use]
1696+ fn safe_allocation_size_hint ( mmm : MemoryMapMeta ) -> usize {
1697+ // Allocate space for extra entries beyond the current size of the
1698+ // memory map. The value of 8 matches the value in the Linux kernel:
1699+ // https://github.com/torvalds/linux/blob/e544a07438/drivers/firmware/efi/libstub/efistub.h#L173
1700+ const EXTRA_ENTRIES : usize = 8 ;
1701+
1702+ let extra_size = mmm. desc_size * EXTRA_ENTRIES ;
1703+ mmm. map_size + extra_size
1704+ }
1705+
1706+ /// Returns the raw pointer to the beginning of the allocation.
1707+ pub fn as_ptr_mut ( & mut self ) -> * mut u8 {
1708+ self . 0 . as_ptr ( ) . cast ( )
1709+ }
1710+
1711+ /// Returns a mutable slice to the underlying memory.
1712+ #[ must_use]
1713+ pub fn as_mut_slice ( & mut self ) -> & mut [ u8 ] {
1714+ unsafe { self . 0 . as_mut ( ) }
1715+ }
1716+ }
1717+
1718+ // Don't drop when we use this in unit tests.
1719+ #[ cfg( not( test) ) ]
1720+ impl Drop for MemoryMapBackingMemory {
1721+ fn drop ( & mut self ) {
1722+ if let Some ( bs) = system_table_boot ( ) {
1723+ let res = unsafe { bs. boot_services ( ) . free_pool ( self . 0 . as_ptr ( ) . cast ( ) ) } ;
1724+ if let Err ( e) = res {
1725+ log:: error!( "Failed to deallocate memory map: {e:?}" ) ;
1726+ }
1727+ } else {
1728+ log:: debug!( "Boot services are excited. Memory map won't be freed using the UEFI boot services allocator." ) ;
1729+ }
1730+ }
1731+ }
1732+
16241733/// A structure containing the meta attributes associated with a call to
16251734/// `GetMemoryMap` of UEFI. Note that all values refer to the time this was
16261735/// called. All following invocations (hidden, subtle, and asynchronous ones)
@@ -1645,6 +1754,21 @@ impl MemoryMapMeta {
16451754 assert_eq ! ( self . map_size % self . desc_size, 0 ) ;
16461755 self . map_size / self . desc_size
16471756 }
1757+
1758+ /// Runs some sanity assertions.
1759+ pub fn assert_sanity_checks ( & self ) {
1760+ assert ! ( self . desc_size > 0 ) ;
1761+ // Although very unlikely, this might fail if the memory descriptor is
1762+ // extended by a future UEFI revision by a significant amount, we
1763+ // update the struct, but an old UEFI implementation reports a small
1764+ // size.
1765+ assert ! ( self . desc_size >= mem:: size_of:: <MemoryDescriptor >( ) ) ;
1766+ assert ! ( self . map_size > 0 ) ;
1767+
1768+ // Ensure the mmap size is (somehow) sane.
1769+ const ONE_GB : usize = 1024 * 1024 * 1024 ;
1770+ assert ! ( self . map_size <= ONE_GB ) ;
1771+ }
16481772}
16491773
16501774/// An accessory to the memory map that can be either iterated or
0 commit comments