Skip to content

Commit 0e70808

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

File tree

2 files changed

+71
-28
lines changed

2 files changed

+71
-28
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/memory_map.rs

Lines changed: 68 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,22 @@ 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.
326+
///
327+
/// Usually, this should already reflect the memory consumed by the
328+
/// code running this.
312329
///
313-
/// This differs from `MemoryMapTag` as for UEFI, the OS needs some non-
314-
/// available memory areas for tables and such.
330+
/// See [`EFIMemoryAreaIter`] for more info.
315331
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,
332+
// If this ever fails, this needs to be refactored in a joint-effort
333+
// with the uefi-rs project to have all corresponding typings.
334+
assert_eq!(self.desc_version, EFIMemoryDesc::VERSION);
335+
336+
if self.desc_size as usize > mem::size_of::<EFIMemoryDesc>() {
337+
log::debug!("desc_size larger than expected typing. We might miss a few fields.");
324338
}
339+
340+
EFIMemoryAreaIter::new(self)
325341
}
326342
}
327343

@@ -330,30 +346,55 @@ impl TagTrait for EFIMemoryMapTag {
330346

331347
fn dst_size(base_tag: &Tag) -> usize {
332348
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>()
349+
base_tag.size as usize - EFI_METADATA_SIZE
336350
}
337351
}
338352

339-
/// An iterator over ALL EFI memory areas.
353+
/// An iterator over the EFI memory areas emitting [`EFIMemoryDesc`] items.
340354
#[derive(Clone, Debug)]
341355
pub struct EFIMemoryAreaIter<'a> {
342-
current_area: u64,
343-
last_area: u64,
344-
entry_size: u32,
356+
mmap_tag: &'a EFIMemoryMapTag,
357+
i: usize,
358+
entries: usize,
345359
phantom: PhantomData<&'a EFIMemoryDesc>,
346360
}
347361

362+
impl<'a> EFIMemoryAreaIter<'a> {
363+
fn new(mmap_tag: &'a EFIMemoryMapTag) -> Self {
364+
Self {
365+
mmap_tag,
366+
i: 0,
367+
entries: mmap_tag.memory_map.len() / mmap_tag.desc_size as usize,
368+
phantom: PhantomData,
369+
}
370+
}
371+
}
372+
348373
impl<'a> Iterator for EFIMemoryAreaIter<'a> {
349374
type Item = &'a EFIMemoryDesc;
350375
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)
376+
if self.i >= self.entries {
377+
return None;
357378
}
379+
380+
let desc = unsafe {
381+
self.mmap_tag
382+
.memory_map
383+
.as_ptr()
384+
.add(self.i * self.mmap_tag.desc_size as usize)
385+
.cast::<EFIMemoryDesc>()
386+
.as_ref()
387+
.unwrap()
388+
};
389+
390+
self.i += 1;
391+
392+
Some(desc)
393+
}
394+
}
395+
396+
impl<'a> ExactSizeIterator for EFIMemoryAreaIter<'a> {
397+
fn len(&self) -> usize {
398+
self.entries
358399
}
359400
}

0 commit comments

Comments
 (0)