Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get mutable references to the memory information #140

Closed
wants to merge 4 commits into from
Closed
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
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use crate::verify::{print_memory_map, print_module_info};
use multiboot2::BootInformation;
use multiboot2::{BootInformation, BootInformationInner};

pub fn run(mbi: &BootInformation) -> anyhow::Result<()> {
pub fn run<T: AsRef<BootInformationInner>>(mbi: &BootInformation<T>) -> anyhow::Result<()> {
basic_sanity_checks(mbi)?;
print_memory_map(mbi)?;
print_module_info(mbi)?;
// print_elf_info(mbi)?;
Ok(())
}

fn basic_sanity_checks(mbi: &BootInformation) -> anyhow::Result<()> {
fn basic_sanity_checks<T: AsRef<BootInformationInner>>(mbi: &BootInformation<T>) -> anyhow::Result<()> {
// Some basic sanity checks
let bootloader_name = mbi
.boot_loader_name_tag()
Expand Down
6 changes: 3 additions & 3 deletions integration-test/bins/multiboot2_payload/src/verify/grub.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use crate::verify::{print_elf_info, print_memory_map, print_module_info};
use multiboot2::BootInformation;
use multiboot2::{BootInformation, BootInformationInner};

pub fn run(mbi: &BootInformation) -> anyhow::Result<()> {
pub fn run<T: AsRef<BootInformationInner>>(mbi: &BootInformation<T>) -> anyhow::Result<()> {
basic_sanity_checks(mbi)?;
print_memory_map(mbi)?;
print_module_info(mbi)?;
print_elf_info(mbi)?;
Ok(())
}

fn basic_sanity_checks(mbi: &BootInformation) -> anyhow::Result<()> {
fn basic_sanity_checks<T: AsRef<BootInformationInner>>(mbi: &BootInformation<T>) -> anyhow::Result<()> {
// Some basic sanity checks
let bootloader_name = mbi
.boot_loader_name_tag()
Expand Down
10 changes: 5 additions & 5 deletions integration-test/bins/multiboot2_payload/src/verify/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ mod grub;

use alloc::format;
use alloc::vec::Vec;
use multiboot2::BootInformation;
use multiboot2::{BootInformation, BootInformationInner};

pub fn run(mbi: &BootInformation) -> anyhow::Result<()> {
pub fn run<T: AsRef<BootInformationInner>>(mbi: &BootInformation<T>) -> anyhow::Result<()> {
println!("{mbi:#x?}");
println!();

Expand All @@ -27,7 +27,7 @@ pub fn run(mbi: &BootInformation) -> anyhow::Result<()> {
Ok(())
}

pub(self) fn print_memory_map(mbi: &BootInformation) -> anyhow::Result<()> {
pub(self) fn print_memory_map<T: AsRef<BootInformationInner>>(mbi: &BootInformation<T>) -> anyhow::Result<()> {
let memmap = mbi
.memory_map_tag()
.ok_or("Should have memory map")
Expand All @@ -46,7 +46,7 @@ pub(self) fn print_memory_map(mbi: &BootInformation) -> anyhow::Result<()> {
Ok(())
}

pub(self) fn print_elf_info(mbi: &BootInformation) -> anyhow::Result<()> {
pub(self) fn print_elf_info<T: AsRef<BootInformationInner>>(mbi: &BootInformation<T>) -> anyhow::Result<()> {
let sections_iter = mbi
.elf_sections()
.ok_or("Should have elf sections")
Expand All @@ -71,7 +71,7 @@ pub(self) fn print_elf_info(mbi: &BootInformation) -> anyhow::Result<()> {
Ok(())
}

pub(self) fn print_module_info(mbi: &BootInformation) -> anyhow::Result<()> {
pub(self) fn print_module_info<T: AsRef<BootInformationInner>>(mbi: &BootInformation<T>) -> anyhow::Result<()> {
let modules = mbi.module_tags().collect::<Vec<_>>();
if modules.len() != 1 {
Err(anyhow::Error::msg("Should have exactly one boot module"))?
Expand Down
103 changes: 92 additions & 11 deletions multiboot2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ use derive_more::Display;
#[cfg(feature = "builder")]
use crate::builder::AsBytes;
use crate::framebuffer::UnknownFramebufferType;
use tag::TagIter;
use tag::{TagIter, TagIterMut};

/// Magic number that a Multiboot2-compliant boot loader will use to identify
/// the handoff. The location depends on the architecture and the targeted
Expand Down Expand Up @@ -152,9 +152,9 @@ impl AsBytes for BootInformationHeader {}

/// This type holds the whole data of the MBI. This helps to better satisfy miri
/// when it checks for memory issues.
#[derive(ptr_meta::Pointee)]
#[derive(ptr_meta::Pointee, Debug)]
#[repr(C)]
struct BootInformationInner {
pub struct BootInformationInner {
header: BootInformationHeader,
tags: [u8],
}
Expand All @@ -178,11 +178,22 @@ impl BootInformationInner {
}
}

impl AsRef<BootInformationInner> for BootInformationInner {
fn as_ref(&self) -> &BootInformationInner {
self
}
}

impl AsMut<BootInformationInner> for BootInformationInner {
fn as_mut(&mut self) -> &mut BootInformationInner {
self
}
}
/// A Multiboot 2 Boot Information (MBI) accessor.
#[repr(transparent)]
pub struct BootInformation<'a>(&'a BootInformationInner);
pub struct BootInformation<T: AsRef<BootInformationInner>>(T);

impl<'a> BootInformation<'a> {
impl BootInformation<&BootInformationInner> {
/// Loads the [`BootInformation`] from a pointer. The pointer must be valid
/// and aligned to an 8-byte boundary, as defined by the spec.
///
Expand Down Expand Up @@ -231,15 +242,50 @@ impl<'a> BootInformation<'a> {

Ok(Self(mbi))
}
}

impl BootInformation<&mut BootInformationInner> {
/// `BootInformation::load`, but mutably.
///
/// # Safety
/// The same considerations that apply to `load` also apply here, but the
/// memory can be modified (through the `_mut` methods).
pub unsafe fn load_mut(ptr: *mut BootInformationHeader) -> Result<Self, MbiLoadError> {
// null or not aligned
if ptr.is_null() || ptr.align_offset(8) != 0 {
return Err(MbiLoadError::IllegalAddress);
}

// mbi: reference to basic header
let mbi = &*ptr;

// Check if total size is not 0 and a multiple of 8.
if mbi.total_size == 0 || mbi.total_size & 0b111 != 0 {
return Err(MbiLoadError::IllegalTotalSize(mbi.total_size));
}

let slice_size = mbi.total_size as usize - size_of::<BootInformationHeader>();
// mbi: reference to full mbi
let mbi = ptr_meta::from_raw_parts_mut::<BootInformationInner>(ptr.cast(), slice_size);
let mbi = &mut *mbi;

if !mbi.has_valid_end_tag() {
return Err(MbiLoadError::NoEndTag);
}

Ok(Self(mbi))
}
}

impl<T: AsRef<BootInformationInner>> BootInformation<T> {
/// Get the start address of the boot info.
pub fn start_address(&self) -> usize {
self.as_ptr() as usize
}

/// Get the start address of the boot info as pointer.
pub fn as_ptr(&self) -> *const () {
core::ptr::addr_of!(*self.0).cast()
core::ptr::addr_of!(*self.0.as_ref()).cast()
}

/// Get the end address of the boot info.
Expand All @@ -258,7 +304,7 @@ impl<'a> BootInformation<'a> {

/// Get the total size of the boot info struct.
pub fn total_size(&self) -> usize {
self.0.header.total_size as usize
self.0.as_ref().header.total_size as usize
}

// ######################################################
Expand Down Expand Up @@ -458,19 +504,54 @@ impl<'a> BootInformation<'a> {
/// .unwrap();
/// assert_eq!(tag.name(), Ok("name"));
/// ```
pub fn get_tag<TagT: TagTrait + ?Sized + 'a>(&'a self) -> Option<&'a TagT> {
pub fn get_tag<TagT: TagTrait + ?Sized>(&self) -> Option<&TagT> {
self.tags()
.find(|tag| tag.typ == TagT::ID)
.map(|tag| tag.cast_tag::<TagT>())
}

/// Returns an iterator over all tags.
fn tags(&self) -> TagIter {
TagIter::new(&self.0.tags)
TagIter::new(&self.0.as_ref().tags)
}
}

impl fmt::Debug for BootInformation<'_> {
impl<T: AsRef<BootInformationInner> + AsMut<BootInformationInner>> BootInformation<T> {
/// Search for the Memory map tag, return a mutable reference.
pub fn memory_map_tag_mut(&mut self) -> Option<&mut MemoryMapTag> {
self.get_tag_mut::<MemoryMapTag, _>(TagType::Mmap)
}

/// Search for the basic memory info tag, return a mutable reference.
pub fn basic_memory_info_tag_mut(&mut self) -> Option<&mut BasicMemoryInfoTag> {
self.get_tag_mut::<BasicMemoryInfoTag, _>(TagType::BasicMeminfo)
}

/// Search for the EFI Memory map tag, return a mutable reference.
pub fn efi_memory_map_tag_mut(&mut self) -> Option<&mut EFIMemoryMapTag> {
self.get_tag_mut::<EFIMemoryMapTag, _>(TagType::EfiMmap)
}

fn get_tag_mut<TagT: TagTrait + ?Sized, TagType: Into<TagTypeId>>(
&mut self,
typ: TagType,
) -> Option<&mut TagT> {
let typ = typ.into();
self.tags_mut()
.find(|tag| tag.typ == typ)
.map(|tag| tag.cast_tag_mut::<TagT>())
}

fn tags_mut(&mut self) -> TagIterMut {
TagIterMut::new(&mut self.0.as_mut().tags)
}
}

// SAFETY: BootInformation contains a const ptr to memory that is never mutated.
// Sending this pointer to other threads is sound.
unsafe impl<T: AsRef<BootInformationInner>> Send for BootInformation<T> {}

impl<T: AsRef<BootInformationInner>> fmt::Debug for BootInformation<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
/// Limit how many Elf-Sections should be debug-formatted.
/// Can be thousands of sections for a Rust binary => this is useless output.
Expand Down Expand Up @@ -1235,7 +1316,7 @@ mod tests {

/// Helper for [`grub2`].
fn test_grub2_boot_info(
bi: &BootInformation,
bi: &BootInformation<&BootInformationInner>,
addr: usize,
string_addr: u64,
bytes: &[u8],
Expand Down
44 changes: 40 additions & 4 deletions multiboot2/src/memory_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ impl MemoryMapTag {
assert_eq!(self.entry_size as usize, mem::size_of::<MemoryArea>());
&self.areas
}

/// Return a mutable slice with all memory areas.
pub fn all_memory_areas_mut(&mut self) -> &mut [MemoryArea] {
// If this ever fails, we need to model this differently in this crate.
assert_eq!(self.entry_size as usize, mem::size_of::<MemoryArea>());
&mut self.areas
}
}

impl TagTrait for MemoryMapTag {
Expand Down Expand Up @@ -312,7 +319,7 @@ impl EFIMemoryMapTag {
///
/// 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 {
pub fn memory_areas(&self) -> EFIMemoryAreaIter<&EFIMemoryDesc> {
let self_ptr = self as *const EFIMemoryMapTag;
let start_area = (&self.descs[0]) as *const EFIMemoryDesc;
EFIMemoryAreaIter {
Expand All @@ -323,6 +330,22 @@ impl EFIMemoryMapTag {
phantom: PhantomData,
}
}

/// Return an iterator over ALL marked memory areas, mutably.
///
/// This differs from `MemoryMapTag` as for UEFI, the OS needs some non-
/// available memory areas for tables and such.
pub fn memory_areas_mut(&mut self) -> EFIMemoryAreaIter<&mut EFIMemoryDesc> {
let self_ptr = self as *mut EFIMemoryMapTag;
let start_area = (&mut self.descs[0]) as *mut EFIMemoryDesc;
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 *mut () as u64 + self.size as u64),
entry_size: self.desc_size,
phantom: PhantomData,
}
}
}

impl TagTrait for EFIMemoryMapTag {
Expand All @@ -338,14 +361,14 @@ impl TagTrait for EFIMemoryMapTag {

/// An iterator over ALL EFI memory areas.
#[derive(Clone, Debug)]
pub struct EFIMemoryAreaIter<'a> {
pub struct EFIMemoryAreaIter<T> {
current_area: u64,
last_area: u64,
entry_size: u32,
phantom: PhantomData<&'a EFIMemoryDesc>,
phantom: PhantomData<T>,
}

impl<'a> Iterator for EFIMemoryAreaIter<'a> {
impl<'a> Iterator for EFIMemoryAreaIter<&'a EFIMemoryDesc> {
type Item = &'a EFIMemoryDesc;
fn next(&mut self) -> Option<&'a EFIMemoryDesc> {
if self.current_area > self.last_area {
Expand All @@ -357,3 +380,16 @@ impl<'a> Iterator for EFIMemoryAreaIter<'a> {
}
}
}

impl<'a> Iterator for EFIMemoryAreaIter<&'a mut EFIMemoryDesc> {
type Item = &'a mut EFIMemoryDesc;
fn next(&mut self) -> Option<&'a mut EFIMemoryDesc> {
if self.current_area > self.last_area {
None
} else {
let area = unsafe { &mut *(self.current_area as *mut EFIMemoryDesc) };
self.current_area += self.entry_size as u64;
Some(area)
}
}
}
Loading