Skip to content

Commit d634190

Browse files
committed
multiboot2: Support setting the EFI memory map tag
1 parent 25b9e5b commit d634190

File tree

3 files changed

+132
-17
lines changed

3 files changed

+132
-17
lines changed

multiboot2/src/builder/information.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//! Exports item [`Multiboot2InformationBuilder`].
22
use crate::builder::traits::StructAsBytes;
33
use crate::{
4-
BasicMemoryInfoTag, BootInformationInner, BootLoaderNameTag, CommandLineTag, EFISdt32,
5-
EFISdt64, ElfSectionsTag, EndTag, FramebufferTag, MemoryMapTag, ModuleTag, RsdpV1Tag,
4+
BasicMemoryInfoTag, BootInformationInner, BootLoaderNameTag, CommandLineTag, EFIMemoryMapTag,
5+
EFISdt32, EFISdt64, ElfSectionsTag, EndTag, FramebufferTag, MemoryMapTag, ModuleTag, RsdpV1Tag,
66
RsdpV2Tag, SmbiosTag,
77
};
88

@@ -18,6 +18,7 @@ pub struct Multiboot2InformationBuilder {
1818
basic_memory_info_tag: Option<BasicMemoryInfoTag>,
1919
boot_loader_name_tag: Option<Box<BootLoaderNameTag>>,
2020
command_line_tag: Option<Box<CommandLineTag>>,
21+
efi_memory_map_tag: Option<Box<EFIMemoryMapTag>>,
2122
elf_sections_tag: Option<Box<ElfSectionsTag>>,
2223
framebuffer_tag: Option<Box<FramebufferTag>>,
2324
memory_map_tag: Option<Box<MemoryMapTag>>,
@@ -37,6 +38,7 @@ impl Multiboot2InformationBuilder {
3738
command_line_tag: None,
3839
efisdt32: None,
3940
efisdt64: None,
41+
efi_memory_map_tag: None,
4042
elf_sections_tag: None,
4143
framebuffer_tag: None,
4244
memory_map_tag: None,
@@ -83,6 +85,9 @@ impl Multiboot2InformationBuilder {
8385
if let Some(tag) = &self.efisdt64 {
8486
len += Self::size_or_up_aligned(tag.byte_size())
8587
}
88+
if let Some(tag) = &self.efi_memory_map_tag {
89+
len += Self::size_or_up_aligned(tag.byte_size())
90+
}
8691
if let Some(tag) = &self.elf_sections_tag {
8792
len += Self::size_or_up_aligned(tag.byte_size())
8893
}
@@ -149,6 +154,9 @@ impl Multiboot2InformationBuilder {
149154
if let Some(tag) = self.efisdt64.as_ref() {
150155
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
151156
}
157+
if let Some(tag) = self.efi_memory_map_tag.as_ref() {
158+
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
159+
}
152160
if let Some(tag) = self.elf_sections_tag.as_ref() {
153161
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
154162
}
@@ -196,6 +204,10 @@ impl Multiboot2InformationBuilder {
196204
self.efisdt64 = Some(efisdt64);
197205
}
198206

207+
pub fn efi_memory_map_tag(&mut self, efi_memory_map_tag: Box<EFIMemoryMapTag>) {
208+
self.efi_memory_map_tag = Some(efi_memory_map_tag);
209+
}
210+
199211
pub fn elf_sections_tag(&mut self, elf_sections_tag: Box<ElfSectionsTag>) {
200212
self.elf_sections_tag = Some(elf_sections_tag);
201213
}

multiboot2/src/lib.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,12 @@ impl BootInformation {
334334
}
335335
}
336336

337+
/// Search for the EFI Memory map tag, return a mutable reference.
338+
pub fn efi_memory_map_tag_mut(&mut self) -> Option<&mut EFIMemoryMapTag> {
339+
self.get_tag_mut(TagType::EfiMmap)
340+
.map(|tag| unsafe { &mut *(tag as *mut Tag as *mut EFIMemoryMapTag) })
341+
}
342+
337343
/// Search for the EFI 32-bit image handle pointer.
338344
pub fn efi_32_ih(&self) -> Option<&EFIImageHandle32> {
339345
self.get_tag::<EFIImageHandle32, _>(TagType::Efi32Ih)
@@ -1509,16 +1515,6 @@ mod tests {
15091515
assert!(efi_mmap.is_none());
15101516
}
15111517

1512-
#[test]
1513-
/// Compile time test for `EFIMemoryMapTag`.
1514-
fn efi_memory_map_tag_size() {
1515-
use super::EFIMemoryMapTag;
1516-
unsafe {
1517-
// `EFIMemoryMapTag` is 16 bytes without the 1st entry
1518-
core::mem::transmute::<[u8; 16], EFIMemoryMapTag>([0u8; 16]);
1519-
}
1520-
}
1521-
15221518
#[test]
15231519
#[cfg(feature = "unstable")]
15241520
/// This test succeeds if it compiles.

multiboot2/src/memory_map.rs

Lines changed: 112 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -271,30 +271,72 @@ pub struct EFIMemoryMapTag {
271271
size: u32,
272272
desc_size: u32,
273273
desc_version: u32,
274-
first_desc: [EFIMemoryDesc; 0],
274+
descs: [EFIMemoryDesc],
275275
}
276276

277277
impl EFIMemoryMapTag {
278+
#[cfg(feature = "builder")]
279+
/// Create a new EFI memory map tag with the given memory descriptors.
280+
/// Version and size can't be set because you're passing a slice of
281+
/// EFIMemoryDescs, not the ones you might have gotten from the firmware.
282+
pub fn new(descs: &[EFIMemoryDesc]) -> Box<Self> {
283+
// update this when updating EFIMemoryDesc
284+
const MEMORY_DESCRIPTOR_VERSION: u32 = 1;
285+
let mut bytes = [
286+
(mem::size_of::<EFIMemoryDesc>() as u32).to_le_bytes(),
287+
MEMORY_DESCRIPTOR_VERSION.to_le_bytes(),
288+
]
289+
.concat();
290+
for desc in descs {
291+
bytes.extend(desc.struct_as_bytes());
292+
}
293+
let tag = boxed_dst_tag(TagType::EfiMmap.into(), bytes.as_slice());
294+
unsafe { Box::from_raw(Box::into_raw(tag) as *mut Self) }
295+
}
296+
278297
/// Return an iterator over ALL marked memory areas.
279298
///
280299
/// This differs from `MemoryMapTag` as for UEFI, the OS needs some non-
281300
/// available memory areas for tables and such.
282301
pub fn memory_areas(&self) -> EFIMemoryAreaIter {
283302
let self_ptr = self as *const EFIMemoryMapTag;
284-
let start_area = self.first_desc.as_ptr();
303+
let start_area = (&self.descs[0]) as *const EFIMemoryDesc;
285304
EFIMemoryAreaIter {
286305
current_area: start_area as u64,
287306
// NOTE: `last_area` is only a bound, it doesn't necessarily point exactly to the last element
288-
last_area: (self_ptr as u64
289-
+ (self.size as u64 - core::mem::size_of::<EFIMemoryMapTag>() as u64)),
307+
last_area: (self_ptr as *const () as u64 + self.size as u64),
308+
entry_size: self.desc_size,
309+
phantom: PhantomData,
310+
}
311+
}
312+
313+
#[cfg(feature = "builder")]
314+
/// Return an iterator over ALL marked memory areas, mutably.
315+
///
316+
/// This differs from `MemoryMapTag` as for UEFI, the OS needs some non-
317+
/// available memory areas for tables and such.
318+
pub fn memory_areas_mut(&mut self) -> EFIMemoryAreaIterMut {
319+
let self_ptr = self as *const EFIMemoryMapTag;
320+
let start_area = (&self.descs[0]) as *const EFIMemoryDesc;
321+
EFIMemoryAreaIterMut {
322+
current_area: start_area as u64,
323+
// NOTE: `last_area` is only a bound, it doesn't necessarily point exactly to the last element
324+
last_area: (self_ptr as *const () as u64 + self.size as u64),
290325
entry_size: self.desc_size,
291326
phantom: PhantomData,
292327
}
293328
}
294329
}
295330

331+
#[cfg(feature = "builder")]
332+
impl StructAsBytes for EFIMemoryMapTag {
333+
fn byte_size(&self) -> usize {
334+
self.size.try_into().unwrap()
335+
}
336+
}
337+
296338
/// EFI Boot Memory Map Descriptor
297-
#[derive(Debug)]
339+
#[derive(Debug, Clone)]
298340
#[repr(C)]
299341
pub struct EFIMemoryDesc {
300342
typ: u32,
@@ -305,6 +347,13 @@ pub struct EFIMemoryDesc {
305347
attr: u64,
306348
}
307349

350+
#[cfg(feature = "builder")]
351+
impl StructAsBytes for EFIMemoryDesc {
352+
fn byte_size(&self) -> usize {
353+
mem::size_of::<Self>()
354+
}
355+
}
356+
308357
/// An enum of possible reported region types.
309358
#[derive(Debug, PartialEq, Eq)]
310359
pub enum EFIMemoryAreaType {
@@ -359,6 +408,29 @@ pub enum EFIMemoryAreaType {
359408
EfiUnknown,
360409
}
361410

411+
impl From<EFIMemoryAreaType> for u32 {
412+
fn from(area: EFIMemoryAreaType) -> Self {
413+
match area {
414+
EFIMemoryAreaType::EfiReservedMemoryType => 0,
415+
EFIMemoryAreaType::EfiLoaderCode => 1,
416+
EFIMemoryAreaType::EfiLoaderData => 2,
417+
EFIMemoryAreaType::EfiBootServicesCode => 3,
418+
EFIMemoryAreaType::EfiBootServicesData => 4,
419+
EFIMemoryAreaType::EfiRuntimeServicesCode => 5,
420+
EFIMemoryAreaType::EfiRuntimeServicesData => 6,
421+
EFIMemoryAreaType::EfiConventionalMemory => 7,
422+
EFIMemoryAreaType::EfiUnusableMemory => 8,
423+
EFIMemoryAreaType::EfiACPIReclaimMemory => 9,
424+
EFIMemoryAreaType::EfiACPIMemoryNVS => 10,
425+
EFIMemoryAreaType::EfiMemoryMappedIO => 11,
426+
EFIMemoryAreaType::EfiMemoryMappedIOPortSpace => 12,
427+
EFIMemoryAreaType::EfiPalCode => 13,
428+
EFIMemoryAreaType::EfiPersistentMemory => 14,
429+
EFIMemoryAreaType::EfiUnknown => panic!("unknown type"),
430+
}
431+
}
432+
}
433+
362434
impl EFIMemoryDesc {
363435
/// The physical address of the memory region.
364436
pub fn physical_address(&self) -> u64 {
@@ -399,6 +471,19 @@ impl EFIMemoryDesc {
399471
}
400472
}
401473

474+
impl Default for EFIMemoryDesc {
475+
fn default() -> Self {
476+
Self {
477+
typ: EFIMemoryAreaType::EfiReservedMemoryType.into(),
478+
_padding: 0,
479+
phys_addr: 0,
480+
virt_addr: 0,
481+
num_pages: 0,
482+
attr: 0,
483+
}
484+
}
485+
}
486+
402487
/// EFI ExitBootServices was not called
403488
#[derive(Debug)]
404489
#[repr(C)]
@@ -428,3 +513,25 @@ impl<'a> Iterator for EFIMemoryAreaIter<'a> {
428513
}
429514
}
430515
}
516+
517+
/// An iterator over ALL EFI memory areas, mutably.
518+
#[derive(Clone, Debug)]
519+
pub struct EFIMemoryAreaIterMut<'a> {
520+
current_area: u64,
521+
last_area: u64,
522+
entry_size: u32,
523+
phantom: PhantomData<&'a mut EFIMemoryDesc>,
524+
}
525+
526+
impl<'a> Iterator for EFIMemoryAreaIterMut<'a> {
527+
type Item = &'a mut EFIMemoryDesc;
528+
fn next(&mut self) -> Option<&'a mut EFIMemoryDesc> {
529+
if self.current_area > self.last_area {
530+
None
531+
} else {
532+
let area = unsafe { &mut *(self.current_area as *mut EFIMemoryDesc) };
533+
self.current_area += self.entry_size as u64;
534+
Some(area)
535+
}
536+
}
537+
}

0 commit comments

Comments
 (0)