Skip to content

Commit 0b9edbd

Browse files
committed
multiboot2: Support setting the EFI memory map tag
1 parent 403087f commit 0b9edbd

File tree

3 files changed

+131
-16
lines changed

3 files changed

+131
-16
lines changed

multiboot2/src/builder/information.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
use crate::builder::traits::StructAsBytes;
33
use crate::{
44
BasicMemoryInfoTag, BootInformationInner, BootLoaderNameTag, CommandLineTag,
5-
EFISdt32, EFISdt64, ElfSectionsTag, EndTag, FramebufferTag, MemoryMapTag,
6-
ModuleTag, RsdpV1Tag, RsdpV2Tag, SmbiosTag,
5+
EFISdt32, EFISdt64, EFIMemoryMapTag, ElfSectionsTag, EndTag, FramebufferTag,
6+
MemoryMapTag, ModuleTag, RsdpV1Tag, RsdpV2Tag, SmbiosTag,
77
};
88

99
use alloc::boxed::Box;
@@ -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
}
@@ -151,6 +156,9 @@ impl Multiboot2InformationBuilder {
151156
if let Some(tag) = self.efisdt64.as_ref() {
152157
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
153158
}
159+
if let Some(tag) = self.efi_memory_map_tag.as_ref() {
160+
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
161+
}
154162
if let Some(tag) = self.elf_sections_tag.as_ref() {
155163
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
156164
}
@@ -198,6 +206,10 @@ impl Multiboot2InformationBuilder {
198206
self.efisdt64 = Some(efisdt64);
199207
}
200208

209+
pub fn efi_memory_map_tag(&mut self, efi_memory_map_tag: Box<EFIMemoryMapTag>) {
210+
self.efi_memory_map_tag = Some(efi_memory_map_tag);
211+
}
212+
201213
pub fn elf_sections_tag(&mut self, elf_sections_tag: Box<ElfSectionsTag>) {
202214
self.elf_sections_tag = Some(elf_sections_tag);
203215
}

multiboot2/src/lib.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,13 @@ impl BootInformation {
325325
}
326326
}
327327

328+
/// Search for the EFI Memory map tag, return a mutable reference.
329+
pub fn efi_memory_map_tag_mut(&self) -> Option<&mut EFIMemoryMapTag> {
330+
self
331+
.get_tag(TagType::EfiMmap)
332+
.map(|tag| unsafe { &mut *(tag as *const Tag as *mut EFIMemoryMapTag) })
333+
}
334+
328335
/// Search for the EFI 32-bit image handle pointer.
329336
pub fn efi_32_ih(&self) -> Option<&EFIImageHandle32> {
330337
self.get_tag(TagType::Efi32Ih)
@@ -1430,14 +1437,4 @@ mod tests {
14301437
let efi_mmap = bi.efi_memory_map_tag();
14311438
assert!(efi_mmap.is_none());
14321439
}
1433-
1434-
#[test]
1435-
/// Compile time test for `EFIMemoryMapTag`.
1436-
fn efi_memory_map_tag_size() {
1437-
use super::EFIMemoryMapTag;
1438-
unsafe {
1439-
// `EFIMemoryMapTag` is 16 bytes + `EFIMemoryDesc` is 40 bytes.
1440-
core::mem::transmute::<[u8; 56], EFIMemoryMapTag>([0u8; 56]);
1441-
}
1442-
}
14431440
}

multiboot2/src/memory_map.rs

Lines changed: 110 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,28 +266,69 @@ pub struct EFIMemoryMapTag {
266266
size: u32,
267267
desc_size: u32,
268268
desc_version: u32,
269-
first_desc: EFIMemoryDesc,
269+
descs: [EFIMemoryDesc],
270270
}
271271

272272
impl EFIMemoryMapTag {
273+
#[cfg(feature = "builder")]
274+
/// Create a new EFI memory map tag with the given memory descriptors.
275+
/// Version and size can't be set because you're passing a slice of
276+
/// EFIMemoryDescs, not the ones you might have gotten from the firmware.
277+
pub fn new(descs: &[EFIMemoryDesc]) -> Box<Self> {
278+
// update this when updating EFIMemoryDesc
279+
const MEMORY_DESCRIPTOR_VERSION: u32 = 1;
280+
let mut bytes = [
281+
(mem::size_of::<EFIMemoryDesc>() as u32).to_le_bytes(),
282+
MEMORY_DESCRIPTOR_VERSION.to_le_bytes(),
283+
].concat();
284+
for desc in descs {
285+
bytes.extend(desc.struct_as_bytes());
286+
}
287+
let tag = boxed_dst_tag(TagType::EfiMmap, bytes.as_slice());
288+
unsafe { Box::from_raw(Box::into_raw(tag) as *mut Self) }
289+
}
290+
273291
/// Return an iterator over ALL marked memory areas.
274292
///
275293
/// This differs from `MemoryMapTag` as for UEFI, the OS needs some non-
276294
/// available memory areas for tables and such.
277295
pub fn memory_areas(&self) -> EFIMemoryAreaIter {
278296
let self_ptr = self as *const EFIMemoryMapTag;
279-
let start_area = (&self.first_desc) as *const EFIMemoryDesc;
297+
let start_area = (&self.descs[0]) as *const EFIMemoryDesc;
280298
EFIMemoryAreaIter {
281299
current_area: start_area as u64,
282-
last_area: (self_ptr as u64 + self.size as u64),
300+
last_area: (self_ptr as *const () as u64 + self.size as u64),
301+
entry_size: self.desc_size,
302+
phantom: PhantomData,
303+
}
304+
}
305+
306+
#[cfg(feature = "builder")]
307+
/// Return an iterator over ALL marked memory areas, mutably.
308+
///
309+
/// This differs from `MemoryMapTag` as for UEFI, the OS needs some non-
310+
/// available memory areas for tables and such.
311+
pub fn memory_areas_mut(&mut self) -> EFIMemoryAreaIterMut {
312+
let self_ptr = self as *const EFIMemoryMapTag;
313+
let start_area = (&self.descs[0]) as *const EFIMemoryDesc;
314+
EFIMemoryAreaIterMut {
315+
current_area: start_area as u64,
316+
last_area: (self_ptr as *const () as u64 + self.size as u64),
283317
entry_size: self.desc_size,
284318
phantom: PhantomData,
285319
}
286320
}
287321
}
288322

323+
#[cfg(feature = "builder")]
324+
impl StructAsBytes for EFIMemoryMapTag {
325+
fn byte_size(&self) -> usize {
326+
self.size.try_into().unwrap()
327+
}
328+
}
329+
289330
/// EFI Boot Memory Map Descriptor
290-
#[derive(Debug)]
331+
#[derive(Debug, Clone)]
291332
#[repr(C)]
292333
pub struct EFIMemoryDesc {
293334
typ: u32,
@@ -298,6 +339,13 @@ pub struct EFIMemoryDesc {
298339
attr: u64,
299340
}
300341

342+
#[cfg(feature = "builder")]
343+
impl StructAsBytes for EFIMemoryDesc {
344+
fn byte_size(&self) -> usize {
345+
mem::size_of::<Self>()
346+
}
347+
}
348+
301349
/// An enum of possible reported region types.
302350
#[derive(Debug, PartialEq, Eq)]
303351
pub enum EFIMemoryAreaType {
@@ -352,6 +400,29 @@ pub enum EFIMemoryAreaType {
352400
EfiUnknown,
353401
}
354402

403+
impl Into<u32> for EFIMemoryAreaType {
404+
fn into(self) -> u32 {
405+
match self {
406+
EFIMemoryAreaType::EfiReservedMemoryType => 0,
407+
EFIMemoryAreaType::EfiLoaderCode => 1,
408+
EFIMemoryAreaType::EfiLoaderData => 2,
409+
EFIMemoryAreaType::EfiBootServicesCode => 3,
410+
EFIMemoryAreaType::EfiBootServicesData => 4,
411+
EFIMemoryAreaType::EfiRuntimeServicesCode => 5,
412+
EFIMemoryAreaType::EfiRuntimeServicesData => 6,
413+
EFIMemoryAreaType::EfiConventionalMemory => 7,
414+
EFIMemoryAreaType::EfiUnusableMemory => 8,
415+
EFIMemoryAreaType::EfiACPIReclaimMemory => 9,
416+
EFIMemoryAreaType::EfiACPIMemoryNVS => 10,
417+
EFIMemoryAreaType::EfiMemoryMappedIO => 11,
418+
EFIMemoryAreaType::EfiMemoryMappedIOPortSpace => 12,
419+
EFIMemoryAreaType::EfiPalCode => 13,
420+
EFIMemoryAreaType::EfiPersistentMemory => 14,
421+
EFIMemoryAreaType::EfiUnknown => panic!("unknown type"),
422+
}
423+
}
424+
}
425+
355426
impl EFIMemoryDesc {
356427
/// The physical address of the memory region.
357428
pub fn physical_address(&self) -> u64 {
@@ -392,6 +463,19 @@ impl EFIMemoryDesc {
392463
}
393464
}
394465

466+
impl Default for EFIMemoryDesc {
467+
fn default() -> Self {
468+
Self {
469+
typ: EFIMemoryAreaType::EfiReservedMemoryType.into(),
470+
_padding: 0,
471+
phys_addr: 0,
472+
virt_addr: 0,
473+
num_pages: 0,
474+
attr: 0,
475+
}
476+
}
477+
}
478+
395479
/// EFI ExitBootServices was not called
396480
#[derive(Debug)]
397481
#[repr(C)]
@@ -421,3 +505,25 @@ impl<'a> Iterator for EFIMemoryAreaIter<'a> {
421505
}
422506
}
423507
}
508+
509+
/// An iterator over ALL EFI memory areas, mutably.
510+
#[derive(Clone, Debug)]
511+
pub struct EFIMemoryAreaIterMut<'a> {
512+
current_area: u64,
513+
last_area: u64,
514+
entry_size: u32,
515+
phantom: PhantomData<&'a mut EFIMemoryDesc>,
516+
}
517+
518+
impl<'a> Iterator for EFIMemoryAreaIterMut<'a> {
519+
type Item = &'a mut EFIMemoryDesc;
520+
fn next(&mut self) -> Option<&'a mut EFIMemoryDesc> {
521+
if self.current_area > self.last_area {
522+
None
523+
} else {
524+
let area = unsafe { &mut*(self.current_area as *mut EFIMemoryDesc) };
525+
self.current_area += self.entry_size as u64;
526+
Some(area)
527+
}
528+
}
529+
}

0 commit comments

Comments
 (0)