1
1
//! UEFI services available during boot.
2
2
3
- use super :: Revision ;
3
+ use super :: { system_table_boot , Revision } ;
4
4
use crate :: data_types:: { Align , PhysicalAddress } ;
5
5
use crate :: proto:: device_path:: DevicePath ;
6
6
use crate :: proto:: loaded_image:: LoadedImage ;
@@ -186,7 +186,7 @@ impl BootServices {
186
186
/// for the memory map, as the memory map itself also needs heap memory,
187
187
/// and other allocations might occur before that call.
188
188
#[ must_use]
189
- pub fn memory_map_size ( & self ) -> MemoryMapMeta {
189
+ fn memory_map_size ( & self ) -> MemoryMapMeta {
190
190
let mut map_size = 0 ;
191
191
let mut map_key = MemoryMapKey ( 0 ) ;
192
192
let mut desc_size = 0 ;
@@ -209,12 +209,16 @@ impl BootServices {
209
209
"Memory map must be a multiple of the reported descriptor size."
210
210
) ;
211
211
212
- MemoryMapMeta {
212
+ let mmm = MemoryMapMeta {
213
213
desc_size,
214
214
map_size,
215
215
map_key,
216
216
desc_version,
217
- }
217
+ } ;
218
+
219
+ mmm. assert_sanity_checks ( ) ;
220
+
221
+ mmm
218
222
}
219
223
220
224
/// Stores the current UEFI memory map in the provided buffer.
@@ -1621,6 +1625,111 @@ impl Align for MemoryDescriptor {
1621
1625
#[ repr( C ) ]
1622
1626
pub struct MemoryMapKey ( usize ) ;
1623
1627
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
+
1624
1733
/// A structure containing the meta attributes associated with a call to
1625
1734
/// `GetMemoryMap` of UEFI. Note that all values refer to the time this was
1626
1735
/// called. All following invocations (hidden, subtle, and asynchronous ones)
@@ -1645,6 +1754,21 @@ impl MemoryMapMeta {
1645
1754
assert_eq ! ( self . map_size % self . desc_size, 0 ) ;
1646
1755
self . map_size / self . desc_size
1647
1756
}
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
+ }
1648
1772
}
1649
1773
1650
1774
/// An accessory to the memory map that can be either iterated or
0 commit comments