1pub use uefi_raw::table::boot::MemoryAttribute as EFIMemoryAttribute;
5pub use uefi_raw::table::boot::MemoryDescriptor as EFIMemoryDesc;
6pub use uefi_raw::table::boot::MemoryType as EFIMemoryAreaType;
7
8use crate::tag::TagHeader;
9use crate::{TagType, TagTypeRaw};
10use core::fmt::{Debug, Formatter};
11use core::marker::PhantomData;
12use multiboot2_common::{MaybeDynSized, Tag};
13#[cfg(feature = "builder")]
14use {alloc::boxed::Box, core::slice, multiboot2_common::new_boxed};
15
16#[derive(ptr_meta::Pointee, Debug, PartialEq, Eq)]
27#[repr(C, align(8))]
28pub struct MemoryMapTag {
29 header: TagHeader,
30 entry_size: u32,
31 entry_version: u32,
32 areas: [MemoryArea],
33}
34
35impl MemoryMapTag {
36 #[cfg(feature = "builder")]
38 #[must_use]
39 pub fn new(areas: &[MemoryArea]) -> Box<Self> {
40 let header = TagHeader::new(Self::ID, 0);
41 let entry_size = (size_of::<MemoryArea>() as u32).to_ne_bytes();
42 let entry_version = 0_u32.to_ne_bytes();
43 let areas = {
44 let ptr = areas.as_ptr().cast::<u8>();
45 let len = size_of_val(areas);
46 unsafe { slice::from_raw_parts(ptr, len) }
49 };
50 new_boxed(header, &[&entry_size, &entry_version, areas])
51 }
52
53 #[must_use]
55 pub const fn entry_size(&self) -> u32 {
56 self.entry_size
57 }
58
59 #[must_use]
61 pub const fn entry_version(&self) -> u32 {
62 self.entry_version
63 }
64
65 #[must_use]
70 pub fn memory_areas(&self) -> &[MemoryArea] {
71 assert_eq!(self.entry_size as usize, size_of::<MemoryArea>());
73 &self.areas
74 }
75}
76
77unsafe impl MaybeDynSized for MemoryMapTag {
80 type Header = TagHeader;
81
82 const BASE_SIZE: usize = size_of::<TagHeader>() + 2 * size_of::<u32>();
83
84 fn dst_len(header: &TagHeader) -> usize {
85 assert!(header.size as usize >= Self::BASE_SIZE);
86 let size = header.size as usize - Self::BASE_SIZE;
87 assert_eq!(size % size_of::<MemoryArea>(), 0);
88 size / size_of::<MemoryArea>()
89 }
90}
91
92impl Tag for MemoryMapTag {
93 type IDType = TagType;
94
95 const ID: TagType = TagType::Mmap;
96}
97
98#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
100#[repr(C)]
101pub struct MemoryArea {
102 base_addr: u64,
103 length: u64,
104 typ: MemoryAreaTypeRaw,
105 _reserved: u32,
106}
107
108impl MemoryArea {
109 pub fn new(base_addr: u64, length: u64, typ: impl Into<MemoryAreaType>) -> Self {
111 Self {
112 base_addr,
113 length,
114 typ: MemoryAreaTypeRaw::new(typ.into().val()),
115 _reserved: 0,
116 }
117 }
118
119 #[must_use]
121 pub const fn start_address(&self) -> u64 {
122 self.base_addr
123 }
124
125 #[must_use]
127 pub const fn end_address(&self) -> u64 {
128 self.base_addr + self.length
129 }
130
131 #[must_use]
133 pub const fn size(&self) -> u64 {
134 self.length
135 }
136
137 #[must_use]
139 pub const fn typ(&self) -> MemoryAreaType {
140 MemoryAreaType::from_val(self.typ.get())
141 }
142}
143
144impl Debug for MemoryArea {
145 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
146 f.debug_struct("MemoryArea")
147 .field("base_addr", &self.base_addr)
148 .field("length", &self.length)
149 .field("typ", &self.typ)
150 .finish()
151 }
152}
153
154multiboot2_common::raw_type! {
155 pub struct MemoryAreaTypeRaw(u32);
159
160 pub enum MemoryAreaType {
169 Available = 1,
171
172 Reserved = 2,
174
175 AcpiAvailable = 3,
177
178 ReservedHibernate = 4,
182
183 Defective = 5,
185 }
186}
187
188#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
203#[repr(C)]
204pub struct BasicMemoryInfoTag {
205 header: TagHeader,
206 memory_lower: u32,
207 memory_upper: u32,
208}
209
210impl BasicMemoryInfoTag {
211 #[must_use]
213 pub fn new(memory_lower: u32, memory_upper: u32) -> Self {
214 Self {
215 header: TagHeader::new(Self::ID, size_of::<Self>().try_into().unwrap()),
216 memory_lower,
217 memory_upper,
218 }
219 }
220
221 #[must_use]
222 pub const fn memory_lower(&self) -> u32 {
224 self.memory_lower
225 }
226
227 #[must_use]
228 pub const fn memory_upper(&self) -> u32 {
230 self.memory_upper
231 }
232}
233
234unsafe impl MaybeDynSized for BasicMemoryInfoTag {
237 type Header = TagHeader;
238
239 const BASE_SIZE: usize = size_of::<Self>();
240}
241
242impl Tag for BasicMemoryInfoTag {
243 type IDType = TagType;
244
245 const ID: TagType = TagType::BasicMeminfo;
246}
247
248#[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)]
251#[repr(C)]
252pub struct EFIMemoryMapTag {
253 header: TagHeader,
254 desc_size: u32,
258 desc_version: u32,
262 memory_map: [u8],
271}
272
273impl EFIMemoryMapTag {
274 #[cfg(feature = "builder")]
276 #[must_use]
277 pub fn new_from_descs(descs: &[EFIMemoryDesc]) -> Box<Self> {
278 let efi_mmap = {
279 let ptr = descs.as_ptr().cast::<u8>();
280 let len = size_of_val(descs);
281 unsafe { slice::from_raw_parts(ptr, len) }
284 };
285
286 Self::new_from_map(
287 size_of::<EFIMemoryDesc>() as u32,
288 EFIMemoryDesc::VERSION,
289 efi_mmap,
290 )
291 }
292
293 #[cfg(feature = "builder")]
295 #[must_use]
296 pub fn new_from_map(desc_size: u32, desc_version: u32, efi_mmap: &[u8]) -> Box<Self> {
297 let header = TagHeader::new(Self::ID, 0);
298 assert_ne!(desc_size, 0);
299 let desc_size = desc_size.to_ne_bytes();
300 let desc_version = desc_version.to_ne_bytes();
301 new_boxed(header, &[&desc_size, &desc_version, efi_mmap])
302 }
303
304 #[must_use]
309 pub fn memory_areas(&self) -> EFIMemoryAreaIter<'_> {
310 assert_eq!(self.desc_version, EFIMemoryDesc::VERSION);
313 let desc_size = self.desc_size as usize;
314 assert_ne!(desc_size, 0, "EFI descriptor size must not be zero");
315 assert!(
316 desc_size >= size_of::<EFIMemoryDesc>(),
317 "EFI descriptor size must cover an EFI memory descriptor"
318 );
319 assert_eq!(
320 desc_size % align_of::<EFIMemoryDesc>(),
321 0,
322 "EFI descriptor size must preserve EFI memory descriptor alignment"
323 );
324 assert_eq!(
325 self.memory_map
326 .as_ptr()
327 .align_offset(align_of::<EFIMemoryDesc>()),
328 0
329 );
330
331 EFIMemoryAreaIter::new(self)
332 }
333}
334
335impl Debug for EFIMemoryMapTag {
336 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
337 f.debug_struct("EFIMemoryMapTag")
338 .field("typ", &self.header.typ)
339 .field("size", &self.header.size)
340 .field("desc_size", &self.desc_size)
341 .field("desc_version", &self.desc_version)
342 .field("buf", &self.memory_map.as_ptr())
343 .field("buf_len", &self.memory_map.len())
344 .field("entries", &self.memory_areas())
345 .finish()
346 }
347}
348
349unsafe impl MaybeDynSized for EFIMemoryMapTag {
352 type Header = TagHeader;
353
354 const BASE_SIZE: usize = size_of::<TagTypeRaw>() + 3 * size_of::<u32>();
355
356 fn dst_len(header: &TagHeader) -> usize {
357 assert!(header.size as usize >= Self::BASE_SIZE);
358 header.size as usize - Self::BASE_SIZE
359 }
360}
361
362impl Tag for EFIMemoryMapTag {
363 type IDType = TagType;
364
365 const ID: TagType = TagType::EfiMmap;
366}
367
368#[derive(Clone)]
370pub struct EFIMemoryAreaIter<'a> {
371 mmap_tag: &'a EFIMemoryMapTag,
372 i: usize,
373 entries: usize,
374 phantom: PhantomData<&'a EFIMemoryDesc>,
375}
376
377impl<'a> EFIMemoryAreaIter<'a> {
378 fn new(mmap_tag: &'a EFIMemoryMapTag) -> Self {
379 let desc_size = mmap_tag.desc_size as usize;
380 let mmap_len = mmap_tag.memory_map.len();
381 assert_eq!(
382 mmap_len % desc_size,
383 0,
384 "memory map length must be a multiple of `desc_size` by definition. The MBI seems to be corrupt."
385 );
386 Self {
387 mmap_tag,
388 i: 0,
389 entries: mmap_len / desc_size,
390 phantom: PhantomData,
391 }
392 }
393}
394
395impl<'a> Iterator for EFIMemoryAreaIter<'a> {
396 type Item = &'a EFIMemoryDesc;
397 fn next(&mut self) -> Option<&'a EFIMemoryDesc> {
398 if self.i >= self.entries {
399 return None;
400 }
401
402 let desc = {
403 let ptr = self
404 .mmap_tag
405 .memory_map
406 .as_ptr()
407 .wrapping_add(self.i * self.mmap_tag.desc_size as usize)
408 .cast::<EFIMemoryDesc>();
409 unsafe { ptr.as_ref().unwrap() }
412 };
413
414 self.i += 1;
415
416 Some(desc)
417 }
418}
419
420impl ExactSizeIterator for EFIMemoryAreaIter<'_> {
421 fn len(&self) -> usize {
422 self.entries
423 }
424}
425
426impl Debug for EFIMemoryAreaIter<'_> {
427 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
428 let mut debug = f.debug_list();
429 let iter = self.clone();
430 for elem in iter {
431 debug.entry(elem);
432 }
433 debug.finish()
434 }
435}
436
437#[cfg(all(test, feature = "builder"))]
438mod tests {
439 use super::*;
440
441 #[test]
442 fn test_create_old_mmap() {
443 let _mmap = MemoryMapTag::new(&[]);
444 let mmap = MemoryMapTag::new(&[
445 MemoryArea::new(0x1000, 0x2000, MemoryAreaType::Available),
446 MemoryArea::new(0x2000, 0x3000, MemoryAreaType::Available),
447 ]);
448 dbg!(mmap);
449 }
450
451 #[test]
452 fn efi_construct_and_parse() {
453 let descs = [
454 EFIMemoryDesc {
455 ty: EFIMemoryAreaType::CONVENTIONAL,
456 padding: 0,
457 phys_start: 0x1000,
458 virt_start: 0x1000,
459 page_count: 1,
460 att: Default::default(),
461 },
462 EFIMemoryDesc {
463 ty: EFIMemoryAreaType::LOADER_DATA,
464 padding: 0,
465 phys_start: 0x2000,
466 virt_start: 0x2000,
467 page_count: 3,
468 att: Default::default(),
469 },
470 ];
471 let efi_mmap_tag = EFIMemoryMapTag::new_from_descs(&descs);
472
473 let mut iter = efi_mmap_tag.memory_areas();
474
475 assert_eq!(iter.next(), Some(&descs[0]));
476 assert_eq!(iter.next(), Some(&descs[1]));
477
478 assert_eq!(iter.next(), None);
479 }
480
481 #[test]
482 #[should_panic(expected = "EFI descriptor size must cover an EFI memory descriptor")]
483 fn efi_rejects_too_small_desc_size() {
484 let map = [0; size_of::<EFIMemoryDesc>()];
485 let tag = EFIMemoryMapTag::new_from_map(1, EFIMemoryDesc::VERSION, &map);
486
487 let _ = tag.memory_areas();
488 }
489
490 #[test]
491 #[should_panic(expected = "EFI descriptor size must preserve EFI memory descriptor alignment")]
492 fn efi_rejects_misaligned_desc_size() {
493 let desc_size = size_of::<EFIMemoryDesc>() + 1;
494 let map = alloc::vec![0; desc_size];
495 let tag = EFIMemoryMapTag::new_from_map(desc_size as u32, EFIMemoryDesc::VERSION, &map);
496
497 let _ = tag.memory_areas();
498 }
499
500 #[test]
504 fn efi_test_real_data() {
505 const DESC_SIZE: u32 = 48;
506 const DESC_VERSION: u32 = 1;
507 const MMAP_RAW: [u64; 60] = [
510 3, 0, 0, 1, 15, 0, 7, 4096, 0, 134, 15, 0, 4, 552960, 0, 1, 15, 0, 7, 557056, 0, 24,
511 15, 0, 7, 1048576, 0, 1792, 15, 0, 10, 8388608, 0, 8, 15, 0, 7, 8421376, 0, 3, 15, 0,
512 10, 8433664, 0, 1, 15, 0, 7, 8437760, 0, 4, 15, 0, 10, 8454144, 0, 240, 15, 0,
513 ];
514 let buf = MMAP_RAW;
515 let buf = unsafe {
518 slice::from_raw_parts(buf.as_ptr().cast::<u8>(), buf.len() * size_of::<u64>())
519 };
520 let tag = EFIMemoryMapTag::new_from_map(DESC_SIZE, DESC_VERSION, buf);
521 let entries = tag.memory_areas().copied().collect::<alloc::vec::Vec<_>>();
522 let expected = [
523 EFIMemoryDesc {
524 ty: EFIMemoryAreaType::BOOT_SERVICES_CODE,
525 padding: 0,
526 phys_start: 0x0,
527 virt_start: 0x0,
528 page_count: 0x1,
529 att: EFIMemoryAttribute::UNCACHEABLE
530 | EFIMemoryAttribute::WRITE_COMBINE
531 | EFIMemoryAttribute::WRITE_THROUGH
532 | EFIMemoryAttribute::WRITE_BACK,
533 },
534 EFIMemoryDesc {
535 ty: EFIMemoryAreaType::CONVENTIONAL,
536 padding: 0,
537 phys_start: 0x1000,
538 virt_start: 0x0,
539 page_count: 0x86,
540 att: EFIMemoryAttribute::UNCACHEABLE
541 | EFIMemoryAttribute::WRITE_COMBINE
542 | EFIMemoryAttribute::WRITE_THROUGH
543 | EFIMemoryAttribute::WRITE_BACK,
544 },
545 EFIMemoryDesc {
546 ty: EFIMemoryAreaType::BOOT_SERVICES_DATA,
547 padding: 0,
548 phys_start: 0x87000,
549 virt_start: 0x0,
550 page_count: 0x1,
551 att: EFIMemoryAttribute::UNCACHEABLE
552 | EFIMemoryAttribute::WRITE_COMBINE
553 | EFIMemoryAttribute::WRITE_THROUGH
554 | EFIMemoryAttribute::WRITE_BACK,
555 },
556 EFIMemoryDesc {
557 ty: EFIMemoryAreaType::CONVENTIONAL,
558 padding: 0,
559 phys_start: 0x88000,
560 virt_start: 0x0,
561 page_count: 0x18,
562 att: EFIMemoryAttribute::UNCACHEABLE
563 | EFIMemoryAttribute::WRITE_COMBINE
564 | EFIMemoryAttribute::WRITE_THROUGH
565 | EFIMemoryAttribute::WRITE_BACK,
566 },
567 EFIMemoryDesc {
568 ty: EFIMemoryAreaType::CONVENTIONAL,
569 padding: 0,
570 phys_start: 0x100000,
571 virt_start: 0x0,
572 page_count: 0x700,
573 att: EFIMemoryAttribute::UNCACHEABLE
574 | EFIMemoryAttribute::WRITE_COMBINE
575 | EFIMemoryAttribute::WRITE_THROUGH
576 | EFIMemoryAttribute::WRITE_BACK,
577 },
578 EFIMemoryDesc {
579 ty: EFIMemoryAreaType::ACPI_NON_VOLATILE,
580 padding: 0,
581 phys_start: 0x800000,
582 virt_start: 0x0,
583 page_count: 0x8,
584 att: EFIMemoryAttribute::UNCACHEABLE
585 | EFIMemoryAttribute::WRITE_COMBINE
586 | EFIMemoryAttribute::WRITE_THROUGH
587 | EFIMemoryAttribute::WRITE_BACK,
588 },
589 EFIMemoryDesc {
590 ty: EFIMemoryAreaType::CONVENTIONAL,
591 padding: 0,
592 phys_start: 0x808000,
593 virt_start: 0x0,
594 page_count: 0x3,
595 att: EFIMemoryAttribute::UNCACHEABLE
596 | EFIMemoryAttribute::WRITE_COMBINE
597 | EFIMemoryAttribute::WRITE_THROUGH
598 | EFIMemoryAttribute::WRITE_BACK,
599 },
600 EFIMemoryDesc {
601 ty: EFIMemoryAreaType::ACPI_NON_VOLATILE,
602 padding: 0,
603 phys_start: 0x80b000,
604 virt_start: 0x0,
605 page_count: 0x1,
606 att: EFIMemoryAttribute::UNCACHEABLE
607 | EFIMemoryAttribute::WRITE_COMBINE
608 | EFIMemoryAttribute::WRITE_THROUGH
609 | EFIMemoryAttribute::WRITE_BACK,
610 },
611 EFIMemoryDesc {
612 ty: EFIMemoryAreaType::CONVENTIONAL,
613 padding: 0,
614 phys_start: 0x80c000,
615 virt_start: 0x0,
616 page_count: 0x4,
617 att: EFIMemoryAttribute::UNCACHEABLE
618 | EFIMemoryAttribute::WRITE_COMBINE
619 | EFIMemoryAttribute::WRITE_THROUGH
620 | EFIMemoryAttribute::WRITE_BACK,
621 },
622 EFIMemoryDesc {
623 ty: EFIMemoryAreaType::ACPI_NON_VOLATILE,
624 padding: 0,
625 phys_start: 0x810000,
626 virt_start: 0x0,
627 page_count: 0xf0,
628 att: EFIMemoryAttribute::UNCACHEABLE
629 | EFIMemoryAttribute::WRITE_COMBINE
630 | EFIMemoryAttribute::WRITE_THROUGH
631 | EFIMemoryAttribute::WRITE_BACK,
632 },
633 ];
634 assert_eq!(entries.as_slice(), &expected);
635 }
636}