Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

An experimental Multiboot 2 crate for ELF-64 kernels. It's still incomplete, so please open an issue if you're missing some functionality. Contributions welcome!

It uses the Multiboot 1.6 specification at http://nongnu.askapache.com/grub/phcoder/multiboot.pdf and the ELF 64 specification at http://www.uclibc.org/docs/elf-64-gen.pdf.
It uses the Multiboot 2.0 specification at https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html and the ELF 64 specification at http://www.uclibc.org/docs/elf-64-gen.pdf.

Below is the draft for a blog post about this. I don't plan to finish it but maybe it's helpful as documentation.

Expand Down
120 changes: 118 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,15 @@ pub use elf_sections::{
};
pub use framebuffer::{FramebufferColor, FramebufferField, FramebufferTag, FramebufferType};
use header::{Tag, TagIter};
pub use memory_map::{MemoryArea, MemoryAreaIter, MemoryAreaType, MemoryMapTag};
pub use memory_map::{
EFIMemoryAreaType, EFIMemoryMapTag, EFIMemoryDesc, MemoryArea, MemoryAreaIter,
MemoryAreaType, MemoryMapTag,
};
pub use module::{ModuleIter, ModuleTag};
pub use rsdp::{RsdpV1Tag, RsdpV2Tag};
pub use rsdp::{
EFIImageHandle32, EFIImageHandle64, EFISdt32, EFISdt64, ImageLoadPhysAddr,
RsdpV1Tag, RsdpV2Tag,
};
pub use vbe_info::{
VBECapabilities, VBEControlInfo, VBEDirectColorAttributes, VBEField, VBEInfoTag,
VBEMemoryModel, VBEModeAttributes, VBEModeInfo, VBEWindowAttributes,
Expand Down Expand Up @@ -170,6 +176,18 @@ impl BootInformation {
self.get_tag(8).map(|tag| framebuffer::framebuffer_tag(tag))
}

/// Search for the EFI 32-bit SDT tag.
pub fn efi_sdt_32_tag<'a>(&self) -> Option<&'a EFISdt32> {
self.get_tag(11)
.map(|tag| unsafe { &*(tag as *const Tag as *const EFISdt32) })
}

/// Search for the EFI 64-bit SDT tag.
pub fn efi_sdt_64_tag<'a>(&self) -> Option<&'a EFISdt64> {
self.get_tag(12)
.map(|tag| unsafe { &*(tag as *const Tag as *const EFISdt64) })
}

/// Search for the (ACPI 1.0) RSDP tag.
pub fn rsdp_v1_tag<'a>(&self) -> Option<&'a RsdpV1Tag> {
self.get_tag(14)
Expand All @@ -182,6 +200,37 @@ impl BootInformation {
.map(|tag| unsafe { &*(tag as *const Tag as *const RsdpV2Tag) })
}

/// Search for the EFI Memory map tag.
pub fn efi_memory_map_tag<'a>(&'a self) -> Option<&'a EFIMemoryMapTag> {
// If the EFIBootServicesNotExited is present, then we should not use
// the memory map, as it could still be in use.
match self.get_tag(18) {
Some(_tag) => None,
None => {
self.get_tag(17)
.map(|tag| unsafe { &*(tag as *const Tag as *const EFIMemoryMapTag) })
},
}
}

/// Search for the EFI 32-bit image handle pointer.
pub fn efi_32_ih<'a>(&'a self) -> Option<&'a EFIImageHandle32> {
self.get_tag(19)
.map(|tag| unsafe { &*(tag as *const Tag as *const EFIImageHandle32) })
}

/// Search for the EFI 64-bit image handle pointer.
pub fn efi_64_ih<'a>(&'a self) -> Option<&'a EFIImageHandle64> {
self.get_tag(20)
.map(|tag| unsafe { &*(tag as *const Tag as *const EFIImageHandle64) })
}

/// Search for the Image Load Base Physical Address.
pub fn load_base_addr<'a>(&'a self) -> Option<&'a ImageLoadPhysAddr> {
self.get_tag(21)
.map(|tag| unsafe { &*(tag as *const Tag as *const ImageLoadPhysAddr) })
}

/// Search for the VBE information tag.
pub fn vbe_info_tag(&self) -> Option<&'static VBEInfoTag> {
self.get_tag(7)
Expand Down Expand Up @@ -1174,4 +1223,71 @@ mod tests {
assert_eq!(ElfSectionType::StringTable, s1.section_type());
assert!(s.next().is_none());
}

#[test]
fn efi_memory_map() {
use memory_map::EFIMemoryAreaType;
#[repr(C, align(8))]
struct Bytes([u8; 72]);
// test that the EFI memory map is detected.
let bytes: Bytes = Bytes([
72, 0, 0, 0, // size
0, 0, 0, 0, // reserved
17, 0, 0, 0, // EFI memory map type
56, 0, 0, 0, // EFI memory map size
48, 0, 0, 0, // EFI descriptor size
1, 0, 0, 0, // EFI descriptor version, don't think this matters.
7, 0, 0, 0, // Type: EfiConventionalMemory
0, 0, 0, 0, // Padding
0, 0, 16, 0,// Physical Address: should be 0x100000
0, 0, 0, 0, // Extension of physical address.
0, 0, 16, 0,// Virtual Address: should be 0x100000
0, 0, 0, 0, // Extension of virtual address.
4, 0, 0, 0, // 4 KiB Pages: 16 KiB
0, 0, 0, 0, // Extension of pages
0, 0, 0, 0, // Attributes of this memory range.
0, 0, 0, 0, // Extension of attributes
0, 0, 0, 0, // end tag type.
8, 0, 0, 0, // end tag size.
]);
let addr = bytes.0.as_ptr() as usize;
let boot_info = unsafe { load(addr) };
assert_eq!(addr, boot_info.start_address());
assert_eq!(addr + bytes.0.len(), boot_info.end_address());
assert_eq!(bytes.0.len(), boot_info.total_size() as usize);
let efi_memory_map = boot_info.efi_memory_map_tag().unwrap();
let mut efi_mmap_iter = efi_memory_map.memory_areas();
let desc = efi_mmap_iter.next().unwrap();
assert_eq!(desc.physical_address(), 0x100000);
assert_eq!(desc.size(), 16384);
assert_eq!(desc.typ(), EFIMemoryAreaType::EfiConventionalMemory);
// test that the EFI memory map is not detected if the boot services
// are not exited.
struct Bytes2([u8; 80]);
let bytes2: Bytes2 = Bytes2([
80, 0, 0, 0, // size
0, 0, 0, 0, // reserved
17, 0, 0, 0, // EFI memory map type
56, 0, 0, 0, // EFI memory map size
48, 0, 0, 0, // EFI descriptor size
1, 0, 0, 0, // EFI descriptor version, don't think this matters.
7, 0, 0, 0, // Type: EfiConventionalMemory
0, 0, 0, 0, // Padding
0, 0, 16, 0,// Physical Address: should be 0x100000
0, 0, 0, 0, // Extension of physical address.
0, 0, 16, 0,// Virtual Address: should be 0x100000
0, 0, 0, 0, // Extension of virtual address.
4, 0, 0, 0, // 4 KiB Pages: 16 KiB
0, 0, 0, 0, // Extension of pages
0, 0, 0, 0, // Attributes of this memory range.
0, 0, 0, 0, // Extension of attributes
18, 0, 0, 0, // Tag ExitBootServices not terminated.
8, 0, 0, 0, // Tag ExitBootServices size.
0, 0, 0, 0, // end tag type.
8, 0, 0, 0, // end tag size.
]);
let boot_info = unsafe { load(bytes2.0.as_ptr() as usize) };
let efi_mmap = boot_info.efi_memory_map_tag();
assert!(efi_mmap.is_none());
}
}
168 changes: 167 additions & 1 deletion src/memory_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl MemoryArea {

/// The end address of the memory region.
pub fn end_address(&self) -> u64 {
(self.base_addr + self.length)
self.base_addr + self.length
}

/// The size, in bytes, of the memory region.
Expand Down Expand Up @@ -114,3 +114,169 @@ impl<'a> Iterator for MemoryAreaIter<'a> {
}
}
}

/// EFI memory map as per EFI specification.
#[derive(Debug)]
#[repr(C)]
pub struct EFIMemoryMapTag {
typ: u32,
size: u32,
desc_size: u32,
desc_version: u32,
first_desc: EFIMemoryDesc,
}

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 *const EFIMemoryDesc;
EFIMemoryAreaIter {
current_area: start_area as u64,
last_area: (self_ptr as u64 + self.size 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.current_area + (self.entry_size as u64);
Some(area)
}
}
}

Loading