123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- use crate::{Tag, TagTrait, TagType, TagTypeId};
- use core::convert::TryInto;
- use core::marker::PhantomData;
- use core::mem;
- #[cfg(feature = "builder")]
- use {crate::builder::boxed_dst_tag, crate::builder::traits::StructAsBytes, alloc::boxed::Box};
- const METADATA_SIZE: usize = mem::size_of::<TagTypeId>() + 3 * mem::size_of::<u32>();
- /// This tag provides an initial host memory map.
- ///
- /// The map provided is guaranteed to list all standard RAM that should be
- /// available for normal use. This type however includes the regions occupied
- /// by kernel, mbi, segments and modules. Kernel must take care not to
- /// overwrite these regions.
- ///
- /// This tag may not be provided by some boot loaders on EFI platforms if EFI
- /// boot services are enabled and available for the loaded image (The EFI boot
- /// services tag may exist in the Multiboot2 boot information structure).
- #[derive(Debug, ptr_meta::Pointee)]
- #[repr(C)]
- pub struct MemoryMapTag {
- typ: TagTypeId,
- size: u32,
- entry_size: u32,
- entry_version: u32,
- areas: [MemoryArea],
- }
- impl MemoryMapTag {
- #[cfg(feature = "builder")]
- pub fn new(areas: &[MemoryArea]) -> Box<Self> {
- let entry_size: u32 = mem::size_of::<MemoryArea>().try_into().unwrap();
- let entry_version: u32 = 0;
- let mut bytes = [entry_size.to_le_bytes(), entry_version.to_le_bytes()].concat();
- for area in areas {
- bytes.extend(area.struct_as_bytes());
- }
- boxed_dst_tag(TagType::Mmap, bytes.as_slice())
- }
- /// Return an iterator over all memory areas that have the type
- /// [`MemoryAreaType::Available`].
- pub fn available_memory_areas(&self) -> impl Iterator<Item = &MemoryArea> {
- self.memory_areas()
- .filter(|entry| matches!(entry.typ, MemoryAreaType::Available))
- }
- /// Return an iterator over all memory areas.
- pub fn memory_areas(&self) -> MemoryAreaIter {
- let self_ptr = self as *const MemoryMapTag;
- let start_area = (&self.areas[0]) as *const MemoryArea;
- MemoryAreaIter {
- current_area: start_area as u64,
- // NOTE: `last_area` is only a bound, it doesn't necessarily point exactly to the last element
- last_area: (self_ptr as *const () as u64 + (self.size - self.entry_size) as u64),
- entry_size: self.entry_size,
- phantom: PhantomData,
- }
- }
- }
- impl TagTrait for MemoryMapTag {
- fn dst_size(base_tag: &Tag) -> usize {
- assert!(base_tag.size as usize >= METADATA_SIZE);
- base_tag.size as usize - METADATA_SIZE
- }
- }
- #[cfg(feature = "builder")]
- impl StructAsBytes for MemoryMapTag {
- fn byte_size(&self) -> usize {
- self.size.try_into().unwrap()
- }
- }
- /// A memory area entry descriptor.
- #[derive(Debug, Clone)]
- #[repr(C)]
- pub struct MemoryArea {
- base_addr: u64,
- length: u64,
- typ: MemoryAreaType,
- _reserved: u32,
- }
- impl MemoryArea {
- /// Create a new MemoryArea.
- pub fn new(base_addr: u64, length: u64, typ: MemoryAreaType) -> Self {
- Self {
- base_addr,
- length,
- typ,
- _reserved: 0,
- }
- }
- /// The start address of the memory region.
- pub fn start_address(&self) -> u64 {
- self.base_addr
- }
- /// The end address of the memory region.
- pub fn end_address(&self) -> u64 {
- self.base_addr + self.length
- }
- /// The size, in bytes, of the memory region.
- pub fn size(&self) -> u64 {
- self.length
- }
- /// The type of the memory region.
- pub fn typ(&self) -> MemoryAreaType {
- self.typ
- }
- }
- #[cfg(feature = "builder")]
- impl StructAsBytes for MemoryArea {
- fn byte_size(&self) -> usize {
- mem::size_of::<Self>()
- }
- }
- /// An enum of possible reported region types.
- /// Inside the Multiboot2 spec this is kind of hidden
- /// inside the implementation of `struct multiboot_mmap_entry`.
- #[derive(Debug, PartialEq, Eq, Copy, Clone)]
- #[repr(u32)]
- pub enum MemoryAreaType {
- /// Available memory free to be used by the OS.
- Available = 1,
- /// A reserved area that must not be used.
- Reserved = 2,
- /// Usable memory holding ACPI information.
- AcpiAvailable = 3,
- /// Reserved memory which needs to be preserved on hibernation.
- /// Also called NVS in spec, which stands for "Non-Volatile Sleep/Storage",
- /// which is part of ACPI specification.
- ReservedHibernate = 4,
- /// Memory which is occupied by defective RAM modules.
- Defective = 5,
- }
- /// An iterator over all memory areas
- #[derive(Clone, Debug)]
- pub struct MemoryAreaIter<'a> {
- current_area: u64,
- last_area: u64,
- entry_size: u32,
- phantom: PhantomData<&'a MemoryArea>,
- }
- impl<'a> Iterator for MemoryAreaIter<'a> {
- type Item = &'a MemoryArea;
- fn next(&mut self) -> Option<&'a MemoryArea> {
- if self.current_area > self.last_area {
- None
- } else {
- let area = unsafe { &*(self.current_area as *const MemoryArea) };
- self.current_area += self.entry_size as u64;
- Some(area)
- }
- }
- }
- /// Basic memory info
- ///
- /// This tag includes "basic memory information".
- /// This means (legacy) lower and upper memory:
- /// In Real Mode (modeled after the 8086),
- /// only the first 1MB of memory is accessible.
- /// Typically, the region between 640KB and 1MB is not freely usable,
- /// because it is used for memory-mapped IO, for instance.
- /// The term “lower memory” refers to those first 640KB of memory that are
- /// freely usable for an application in Real Mode.
- /// “Upper memory” then refers to the next freely usable chunk of memory,
- /// starting at 1MB up to about 10MB, in practice.
- /// This is the memory an application running on a 286
- /// (which had a 24-bit address bus) could use, historically.
- /// Nowadays, much bigger chunks of continuous memory are available at higher
- /// addresses, but the Multiboot standard still references those two terms.
- #[derive(Debug)]
- #[repr(C, packed)]
- pub struct BasicMemoryInfoTag {
- typ: TagTypeId,
- size: u32,
- memory_lower: u32,
- memory_upper: u32,
- }
- impl BasicMemoryInfoTag {
- pub fn new(memory_lower: u32, memory_upper: u32) -> Self {
- Self {
- typ: TagType::BasicMeminfo.into(),
- size: mem::size_of::<BasicMemoryInfoTag>().try_into().unwrap(),
- memory_lower,
- memory_upper,
- }
- }
- pub fn memory_lower(&self) -> u32 {
- self.memory_lower
- }
- pub fn memory_upper(&self) -> u32 {
- self.memory_upper
- }
- }
- #[cfg(feature = "builder")]
- impl StructAsBytes for BasicMemoryInfoTag {
- fn byte_size(&self) -> usize {
- mem::size_of::<Self>()
- }
- }
- /// EFI memory map as per EFI specification.
- #[derive(Debug)]
- #[repr(C)]
- pub struct EFIMemoryMapTag {
- typ: TagTypeId,
- size: u32,
- desc_size: u32,
- desc_version: u32,
- first_desc: [EFIMemoryDesc; 0],
- }
- impl EFIMemoryMapTag {
- /// Return an iterator over ALL marked memory areas.
- ///
- /// This differs from `MemoryMapTag` as for UEFI, the OS needs some non-
- /// available memory areas for tables and such.
- pub fn memory_areas(&self) -> EFIMemoryAreaIter {
- let self_ptr = self as *const EFIMemoryMapTag;
- let start_area = self.first_desc.as_ptr();
- EFIMemoryAreaIter {
- current_area: start_area as u64,
- // NOTE: `last_area` is only a bound, it doesn't necessarily point exactly to the last element
- last_area: (self_ptr as u64
- + (self.size as u64 - core::mem::size_of::<EFIMemoryMapTag>() as u64)),
- entry_size: self.desc_size,
- phantom: PhantomData,
- }
- }
- }
- /// EFI Boot Memory Map Descriptor
- #[derive(Debug)]
- #[repr(C)]
- pub struct EFIMemoryDesc {
- typ: u32,
- _padding: u32,
- phys_addr: u64,
- virt_addr: u64,
- num_pages: u64,
- attr: u64,
- }
- /// An enum of possible reported region types.
- #[derive(Debug, PartialEq, Eq)]
- pub enum EFIMemoryAreaType {
- /// Unusable.
- EfiReservedMemoryType,
- /// Code area of a UEFI application.
- EfiLoaderCode,
- /// Data area of a UEFI application.
- EfiLoaderData,
- /// Code area of a UEFI Boot Service Driver.
- EfiBootServicesCode,
- /// Data area of a UEFI Boot Service Driver.
- EfiBootServicesData,
- /// Code area of a UEFI Runtime Driver.
- ///
- /// Must be preserved in working and ACPI S1-S3 states.
- EfiRuntimeServicesCode,
- /// Data area of a UEFI Runtime Driver.
- ///
- /// Must be preserved in working and ACPI S1-S3 states.
- EfiRuntimeServicesData,
- /// Available memory.
- EfiConventionalMemory,
- /// Memory with errors, treat as unusable.
- EfiUnusableMemory,
- /// Memory containing the ACPI tables.
- ///
- /// Must be preserved in working and ACPI S1-S3 states.
- EfiACPIReclaimMemory,
- /// Memory reserved by firmware.
- ///
- /// Must be preserved in working and ACPI S1-S3 states.
- EfiACPIMemoryNVS,
- /// Memory used by firmware for requesting memory mapping of IO.
- ///
- /// Should not be used by the OS. Use the ACPI tables for memory mapped IO
- /// information.
- EfiMemoryMappedIO,
- /// Memory used to translate memory cycles to IO cycles.
- ///
- /// Should not be used by the OS. Use the ACPI tables for memory mapped IO
- /// information.
- EfiMemoryMappedIOPortSpace,
- /// Memory used by the processor.
- ///
- /// Must be preserved in working and ACPI S1-S4 states. Processor defined
- /// otherwise.
- EfiPalCode,
- /// Available memory supporting byte-addressable non-volatility.
- EfiPersistentMemory,
- /// Unknown region type, treat as unusable.
- EfiUnknown,
- }
- impl EFIMemoryDesc {
- /// The physical address of the memory region.
- pub fn physical_address(&self) -> u64 {
- self.phys_addr
- }
- /// The virtual address of the memory region.
- pub fn virtual_address(&self) -> u64 {
- self.virt_addr
- }
- /// The size in bytes of the memory region.
- pub fn size(&self) -> u64 {
- // Spec says this is number of 4KiB pages.
- self.num_pages * 4096
- }
- /// The type of the memory region.
- pub fn typ(&self) -> EFIMemoryAreaType {
- match self.typ {
- 0 => EFIMemoryAreaType::EfiReservedMemoryType,
- 1 => EFIMemoryAreaType::EfiLoaderCode,
- 2 => EFIMemoryAreaType::EfiLoaderData,
- 3 => EFIMemoryAreaType::EfiBootServicesCode,
- 4 => EFIMemoryAreaType::EfiBootServicesData,
- 5 => EFIMemoryAreaType::EfiRuntimeServicesCode,
- 6 => EFIMemoryAreaType::EfiRuntimeServicesData,
- 7 => EFIMemoryAreaType::EfiConventionalMemory,
- 8 => EFIMemoryAreaType::EfiUnusableMemory,
- 9 => EFIMemoryAreaType::EfiACPIReclaimMemory,
- 10 => EFIMemoryAreaType::EfiACPIMemoryNVS,
- 11 => EFIMemoryAreaType::EfiMemoryMappedIO,
- 12 => EFIMemoryAreaType::EfiMemoryMappedIOPortSpace,
- 13 => EFIMemoryAreaType::EfiPalCode,
- 14 => EFIMemoryAreaType::EfiPersistentMemory,
- _ => EFIMemoryAreaType::EfiUnknown,
- }
- }
- }
- /// EFI ExitBootServices was not called
- #[derive(Debug)]
- #[repr(C)]
- pub struct EFIBootServicesNotExited {
- typ: u32,
- size: u32,
- }
- /// An iterator over ALL EFI memory areas.
- #[derive(Clone, Debug)]
- pub struct EFIMemoryAreaIter<'a> {
- current_area: u64,
- last_area: u64,
- entry_size: u32,
- phantom: PhantomData<&'a EFIMemoryDesc>,
- }
- impl<'a> Iterator for EFIMemoryAreaIter<'a> {
- type Item = &'a EFIMemoryDesc;
- fn next(&mut self) -> Option<&'a EFIMemoryDesc> {
- if self.current_area > self.last_area {
- None
- } else {
- let area = unsafe { &*(self.current_area as *const EFIMemoryDesc) };
- self.current_area += self.entry_size as u64;
- Some(area)
- }
- }
- }
|