Skip to content

Commit afac12a

Browse files
committed
multiboot2: fix handling of efi memory map
1 parent 3077c3b commit afac12a

File tree

3 files changed

+82
-33
lines changed

3 files changed

+82
-33
lines changed

multiboot2/Changelog.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# CHANGELOG for crate `multiboot2`
22

3-
## Unreleased
3+
## 0.20.1
4+
5+
- fixed the handling of `EFIMemoryMapTag` and `EFIMemoryAreaIter`
46

57
## 0.20.0 (2024-05-01)
68

multiboot2/src/lib.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,10 @@ impl<'a> BootInformation<'a> {
302302
// If the EFIBootServicesNotExited is present, then we should not use
303303
// the memory map, as it could still be in use.
304304
match self.get_tag::<EFIBootServicesNotExitedTag>() {
305-
Some(_tag) => None,
305+
Some(_tag) => {
306+
log::debug!("The EFI memory map is present but the UEFI Boot Services Not Existed Tag is present. Returning None.");
307+
None
308+
}
306309
None => self.get_tag::<EFIMemoryMapTag>(),
307310
}
308311
}
@@ -1450,15 +1453,15 @@ mod tests {
14501453
#[cfg_attr(miri, ignore)]
14511454
fn efi_memory_map() {
14521455
#[repr(C, align(8))]
1453-
struct Bytes([u8; 72]);
1456+
struct Bytes([u8; 80]);
14541457
// test that the EFI memory map is detected.
14551458
let bytes: Bytes = Bytes([
1456-
72, 0, 0, 0, // size
1459+
80, 0, 0, 0, // size
14571460
0, 0, 0, 0, // reserved
14581461
17, 0, 0, 0, // EFI memory map type
1459-
56, 0, 0, 0, // EFI memory map size
1462+
64, 0, 0, 0, // EFI memory map size
14601463
48, 0, 0, 0, // EFI descriptor size
1461-
1, 0, 0, 0, // EFI descriptor version, don't think this matters.
1464+
1, 0, 0, 0, // EFI descriptor version
14621465
7, 0, 0, 0, // Type: EfiConventionalMemory
14631466
0, 0, 0, 0, // Padding
14641467
0, 0, 16, 0, // Physical Address: should be 0x100000
@@ -1469,6 +1472,8 @@ mod tests {
14691472
0, 0, 0, 0, // Extension of pages
14701473
0, 0, 0, 0, // Attributes of this memory range.
14711474
0, 0, 0, 0, // Extension of attributes
1475+
0, 0, 0, 0, // More padding to extend the efi mmap to `desc_size`.
1476+
0, 0, 0, 0, // padding/alignment for end tag
14721477
0, 0, 0, 0, // end tag type.
14731478
8, 0, 0, 0, // end tag size.
14741479
]);

multiboot2/src/memory_map.rs

Lines changed: 69 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ impl MemoryMapTag {
5555
self.entry_version
5656
}
5757

58-
/// Return the slice with all memory areas.
58+
/// Return the slice of the provided [`MemoryArea`]s.
59+
///
60+
/// Usually, this should already reflect the memory consumed by the
61+
/// code running this.
5962
pub fn memory_areas(&self) -> &[MemoryArea] {
6063
// If this ever fails, we need to model this differently in this crate.
6164
assert_eq!(self.entry_size as usize, mem::size_of::<MemoryArea>());
@@ -74,7 +77,7 @@ impl TagTrait for MemoryMapTag {
7477
}
7578
}
7679

77-
/// A memory area entry descriptor.
80+
/// A descriptor for an available or taken area of physical memory.
7881
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
7982
#[repr(C)]
8083
pub struct MemoryArea {
@@ -284,9 +287,20 @@ impl AsBytes for EFIMemoryDesc {}
284287
pub struct EFIMemoryMapTag {
285288
typ: TagTypeId,
286289
size: u32,
290+
/// Most likely a little more than the size of a [`EFIMemoryDesc`].
291+
/// This is always the reference, and `size_of` never.
292+
/// See <https://github.com/tianocore/edk2/blob/7142e648416ff5d3eac6c6d607874805f5de0ca8/MdeModulePkg/Core/PiSmmCore/Page.c#L1059>.
287293
desc_size: u32,
294+
/// Version of the tag. The spec leaves it open to extend the memory
295+
/// descriptor in the future. However, this never happened so far.
296+
/// At the moment, only version "1" is supported.
288297
desc_version: u32,
289-
descs: [EFIMemoryDesc],
298+
/// Contains the UEFI memory map.
299+
///
300+
/// To follow the UEFI spec and to allow extendability for future UEFI
301+
/// revisions, the length is a multiple of `desc_size` and not a multiple
302+
/// of `size_of::<EfiMemoryDescriptor>()`.
303+
memory_map: [u8],
290304
}
291305

292306
impl EFIMemoryMapTag {
@@ -308,20 +322,20 @@ impl EFIMemoryMapTag {
308322
BoxedDst::new(bytes.as_slice())
309323
}
310324

311-
/// Return an iterator over ALL marked memory areas.
325+
/// Returns an iterator over the provided memory areas.
312326
///
313-
/// This differs from `MemoryMapTag` as for UEFI, the OS needs some non-
314-
/// available memory areas for tables and such.
327+
/// Usually, this should already reflect the memory consumed by the
328+
/// code running this.
315329
pub fn memory_areas(&self) -> EFIMemoryAreaIter {
316-
let self_ptr = self as *const EFIMemoryMapTag;
317-
let start_area = (&self.descs[0]) as *const EFIMemoryDesc;
318-
EFIMemoryAreaIter {
319-
current_area: start_area as u64,
320-
// NOTE: `last_area` is only a bound, it doesn't necessarily point exactly to the last element
321-
last_area: (self_ptr as *const () as u64 + self.size as u64),
322-
entry_size: self.desc_size,
323-
phantom: PhantomData,
330+
// If this ever fails, this needs to be refactored in a joint-effort
331+
// with the uefi-rs project to have all corresponding typings.
332+
assert_eq!(self.desc_version, EFIMemoryDesc::VERSION);
333+
334+
if self.desc_size as usize > mem::size_of::<EFIMemoryDesc>() {
335+
log::debug!("desc_size larger than expected typing. We might miss a few fields.");
324336
}
337+
338+
EFIMemoryAreaIter::new(self)
325339
}
326340
}
327341

@@ -330,30 +344,58 @@ impl TagTrait for EFIMemoryMapTag {
330344

331345
fn dst_size(base_tag: &Tag) -> usize {
332346
assert!(base_tag.size as usize >= EFI_METADATA_SIZE);
333-
let size = base_tag.size as usize - EFI_METADATA_SIZE;
334-
assert_eq!(size % mem::size_of::<EFIMemoryDesc>(), 0);
335-
size / mem::size_of::<EFIMemoryDesc>()
347+
base_tag.size as usize - EFI_METADATA_SIZE
336348
}
337349
}
338350

339-
/// An iterator over ALL EFI memory areas.
351+
/// An iterator over the EFI memory areas emitting [`EFIMemoryDesc`] items.
340352
#[derive(Clone, Debug)]
341353
pub struct EFIMemoryAreaIter<'a> {
342-
current_area: u64,
343-
last_area: u64,
344-
entry_size: u32,
354+
mmap_tag: &'a EFIMemoryMapTag,
355+
i: usize,
356+
entries: usize,
345357
phantom: PhantomData<&'a EFIMemoryDesc>,
346358
}
347359

360+
impl<'a> EFIMemoryAreaIter<'a> {
361+
fn new(mmap_tag: &'a EFIMemoryMapTag) -> Self {
362+
let desc_size = mmap_tag.desc_size as usize;
363+
let mmap_len = mmap_tag.memory_map.len();
364+
assert_eq!(mmap_len % desc_size, 0, "memory map length must be a multiple of `desc_size` by definition. The MBI seems to be corrupt.");
365+
Self {
366+
mmap_tag,
367+
i: 0,
368+
entries: mmap_len / desc_size,
369+
phantom: PhantomData,
370+
}
371+
}
372+
}
373+
348374
impl<'a> Iterator for EFIMemoryAreaIter<'a> {
349375
type Item = &'a EFIMemoryDesc;
350376
fn next(&mut self) -> Option<&'a EFIMemoryDesc> {
351-
if self.current_area > self.last_area {
352-
None
353-
} else {
354-
let area = unsafe { &*(self.current_area as *const EFIMemoryDesc) };
355-
self.current_area += self.entry_size as u64;
356-
Some(area)
377+
if self.i >= self.entries {
378+
return None;
357379
}
380+
381+
let desc = unsafe {
382+
self.mmap_tag
383+
.memory_map
384+
.as_ptr()
385+
.add(self.i * self.mmap_tag.desc_size as usize)
386+
.cast::<EFIMemoryDesc>()
387+
.as_ref()
388+
.unwrap()
389+
};
390+
391+
self.i += 1;
392+
393+
Some(desc)
394+
}
395+
}
396+
397+
impl<'a> ExactSizeIterator for EFIMemoryAreaIter<'a> {
398+
fn len(&self) -> usize {
399+
self.entries
358400
}
359401
}

0 commit comments

Comments
 (0)