Skip to content

Commit ca33794

Browse files
committed
multiboot2: Get a mutable reference to the memory map
1 parent 4078c90 commit ca33794

File tree

7 files changed

+170
-22
lines changed

7 files changed

+170
-22
lines changed

integration-test/bins/multiboot2_payload/src/verify/chainloader.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
use crate::verify::{print_memory_map, print_module_info};
2-
use multiboot2::BootInformation;
2+
use multiboot2::{BootInformation, BootInformationInner};
33

4-
pub fn run(mbi: &BootInformation) -> anyhow::Result<()> {
4+
pub fn run<T: AsRef<BootInformationInner>>(mbi: &BootInformation<T>) -> anyhow::Result<()> {
55
basic_sanity_checks(mbi)?;
66
print_memory_map(mbi)?;
77
print_module_info(mbi)?;
88
// print_elf_info(mbi)?;
99
Ok(())
1010
}
1111

12-
fn basic_sanity_checks(mbi: &BootInformation) -> anyhow::Result<()> {
12+
fn basic_sanity_checks<T: AsRef<BootInformationInner>>(mbi: &BootInformation<T>) -> anyhow::Result<()> {
1313
// Some basic sanity checks
1414
let bootloader_name = mbi
1515
.boot_loader_name_tag()

integration-test/bins/multiboot2_payload/src/verify/grub.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
use crate::verify::{print_elf_info, print_memory_map, print_module_info};
2-
use multiboot2::BootInformation;
2+
use multiboot2::{BootInformation, BootInformationInner};
33

4-
pub fn run(mbi: &BootInformation) -> anyhow::Result<()> {
4+
pub fn run<T: AsRef<BootInformationInner>>(mbi: &BootInformation<T>) -> anyhow::Result<()> {
55
basic_sanity_checks(mbi)?;
66
print_memory_map(mbi)?;
77
print_module_info(mbi)?;
88
print_elf_info(mbi)?;
99
Ok(())
1010
}
1111

12-
fn basic_sanity_checks(mbi: &BootInformation) -> anyhow::Result<()> {
12+
fn basic_sanity_checks<T: AsRef<BootInformationInner>>(mbi: &BootInformation<T>) -> anyhow::Result<()> {
1313
// Some basic sanity checks
1414
let bootloader_name = mbi
1515
.boot_loader_name_tag()

integration-test/bins/multiboot2_payload/src/verify/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ mod grub;
33

44
use alloc::format;
55
use alloc::vec::Vec;
6-
use multiboot2::BootInformation;
6+
use multiboot2::{BootInformation, BootInformationInner};
77

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

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

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

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

74-
pub(self) fn print_module_info(mbi: &BootInformation) -> anyhow::Result<()> {
74+
pub(self) fn print_module_info<T: AsRef<BootInformationInner>>(mbi: &BootInformation<T>) -> anyhow::Result<()> {
7575
let modules = mbi.module_tags().collect::<Vec<_>>();
7676
if modules.len() != 1 {
7777
Err(anyhow::Error::msg("Should have exactly one boot module"))?

multiboot2/src/lib.rs

Lines changed: 82 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ use derive_more::Display;
9797
#[cfg(feature = "builder")]
9898
use crate::builder::AsBytes;
9999
use crate::framebuffer::UnknownFramebufferType;
100-
use tag::TagIter;
100+
use tag::{TagIter, TagIterMut};
101101

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

151151
/// This type holds the whole data of the MBI. This helps to better satisfy miri
152152
/// when it checks for memory issues.
153-
#[derive(ptr_meta::Pointee)]
153+
#[derive(ptr_meta::Pointee, Debug)]
154154
#[repr(C)]
155-
struct BootInformationInner {
155+
pub struct BootInformationInner {
156156
header: BootInformationHeader,
157157
tags: [u8],
158158
}
@@ -176,11 +176,22 @@ impl BootInformationInner {
176176
}
177177
}
178178

179+
impl AsRef<BootInformationInner> for BootInformationInner {
180+
fn as_ref(&self) -> &BootInformationInner {
181+
self
182+
}
183+
}
184+
185+
impl AsMut<BootInformationInner> for BootInformationInner {
186+
fn as_mut(&mut self) -> &mut BootInformationInner {
187+
self
188+
}
189+
}
179190
/// A Multiboot 2 Boot Information (MBI) accessor.
180191
#[repr(transparent)]
181-
pub struct BootInformation<'a>(&'a BootInformationInner);
192+
pub struct BootInformation<T: AsRef<BootInformationInner>>(T);
182193

183-
impl<'a> BootInformation<'a> {
194+
impl BootInformation<&BootInformationInner> {
184195
/// Loads the [`BootInformation`] from a pointer. The pointer must be valid
185196
/// and aligned to an 8-byte boundary, as defined by the spec.
186197
///
@@ -229,15 +240,50 @@ impl<'a> BootInformation<'a> {
229240

230241
Ok(Self(mbi))
231242
}
243+
}
244+
245+
impl BootInformation<&mut BootInformationInner> {
246+
/// `BootInformation::load`, but mutably.
247+
///
248+
/// # Safety
249+
/// The same considerations that apply to `load` also apply here, but the
250+
/// memory can be modified (through the `_mut` methods).
251+
pub unsafe fn load_mut(ptr: *mut BootInformationHeader) -> Result<Self, MbiLoadError> {
252+
// null or not aligned
253+
if ptr.is_null() || ptr.align_offset(8) != 0 {
254+
return Err(MbiLoadError::IllegalAddress);
255+
}
256+
257+
// mbi: reference to basic header
258+
let mbi = &*ptr;
259+
260+
// Check if total size is not 0 and a multiple of 8.
261+
if mbi.total_size == 0 || mbi.total_size & 0b111 != 0 {
262+
return Err(MbiLoadError::IllegalTotalSize(mbi.total_size));
263+
}
232264

265+
let slice_size = mbi.total_size as usize - size_of::<BootInformationHeader>();
266+
// mbi: reference to full mbi
267+
let mbi = ptr_meta::from_raw_parts_mut::<BootInformationInner>(ptr.cast(), slice_size);
268+
let mbi = &mut *mbi;
269+
270+
if !mbi.has_valid_end_tag() {
271+
return Err(MbiLoadError::NoEndTag);
272+
}
273+
274+
Ok(Self(mbi))
275+
}
276+
}
277+
278+
impl<T: AsRef<BootInformationInner>> BootInformation<T> {
233279
/// Get the start address of the boot info.
234280
pub fn start_address(&self) -> usize {
235281
self.as_ptr() as usize
236282
}
237283

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

243289
/// Get the end address of the boot info.
@@ -256,7 +302,7 @@ impl<'a> BootInformation<'a> {
256302

257303
/// Get the total size of the boot info struct.
258304
pub fn total_size(&self) -> usize {
259-
self.0.header.total_size as usize
305+
self.0.as_ref().header.total_size as usize
260306
}
261307

262308
// ######################################################
@@ -456,19 +502,44 @@ impl<'a> BootInformation<'a> {
456502
/// .unwrap();
457503
/// assert_eq!(tag.name(), Ok("name"));
458504
/// ```
459-
pub fn get_tag<TagT: TagTrait + ?Sized + 'a>(&'a self) -> Option<&'a TagT> {
505+
pub fn get_tag<TagT: TagTrait + ?Sized>(&self) -> Option<&TagT> {
460506
self.tags()
461507
.find(|tag| tag.typ == TagT::ID)
462508
.map(|tag| tag.cast_tag::<TagT>())
463509
}
464510

465511
/// Returns an iterator over all tags.
466512
fn tags(&self) -> TagIter {
467-
TagIter::new(&self.0.tags)
513+
TagIter::new(&self.0.as_ref().tags)
468514
}
469515
}
470516

471-
impl fmt::Debug for BootInformation<'_> {
517+
impl<T: AsRef<BootInformationInner> + AsMut<BootInformationInner>> BootInformation<T> {
518+
/// Search for the Memory map tag, return a mutable reference.
519+
pub fn memory_map_tag_mut(&mut self) -> Option<&mut MemoryMapTag> {
520+
self.get_tag_mut::<MemoryMapTag, _>(TagType::Mmap)
521+
}
522+
523+
fn get_tag_mut<TagT: TagTrait + ?Sized, TagType: Into<TagTypeId>>(
524+
&mut self,
525+
typ: TagType,
526+
) -> Option<&mut TagT> {
527+
let typ = typ.into();
528+
self.tags_mut()
529+
.find(|tag| tag.typ == typ)
530+
.map(|tag| tag.cast_tag_mut::<TagT>())
531+
}
532+
533+
fn tags_mut(&mut self) -> TagIterMut {
534+
TagIterMut::new(&mut self.0.as_mut().tags)
535+
}
536+
}
537+
538+
// SAFETY: BootInformation contains a const ptr to memory that is never mutated.
539+
// Sending this pointer to other threads is sound.
540+
unsafe impl<T: AsRef<BootInformationInner>> Send for BootInformation<T> {}
541+
542+
impl<T: AsRef<BootInformationInner>> fmt::Debug for BootInformation<T> {
472543
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
473544
/// Limit how many Elf-Sections should be debug-formatted.
474545
/// Can be thousands of sections for a Rust binary => this is useless output.
@@ -1234,7 +1305,7 @@ mod tests {
12341305

12351306
/// Helper for [`grub2`].
12361307
fn test_grub2_boot_info(
1237-
bi: &BootInformation,
1308+
bi: &BootInformation<&BootInformationInner>,
12381309
addr: usize,
12391310
string_addr: u64,
12401311
bytes: &[u8],

multiboot2/src/memory_map.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ impl MemoryMapTag {
5959
assert_eq!(self.entry_size as usize, mem::size_of::<MemoryArea>());
6060
&self.areas
6161
}
62+
63+
/// Return a mutable slice with all memory areas.
64+
pub fn all_memory_areas_mut(&mut self) -> &mut [MemoryArea] {
65+
// If this ever fails, we need to model this differently in this crate.
66+
assert_eq!(self.entry_size as usize, mem::size_of::<MemoryArea>());
67+
&mut self.areas
68+
}
6269
}
6370

6471
impl TagTrait for MemoryMapTag {

multiboot2/src/tag.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ impl Tag {
3838
unsafe { TagTrait::from_base_tag(self) }
3939
}
4040

41+
/// Casts the base tag to the specific tag type, but mutably.
42+
pub fn cast_tag_mut<'a, T: TagTrait + ?Sized + 'a>(&'a mut self) -> &'a mut T {
43+
assert_eq!(self.typ, T::ID);
44+
// Safety: At this point, we trust that "self.size" and the size hint
45+
// for DST tags are sane.
46+
unsafe { TagTrait::from_base_tag_mut(self) }
47+
}
48+
4149
/// Some multiboot2 tags are a DST as they end with a dynamically sized byte
4250
/// slice. This function parses this slice as [`str`] so that either a valid
4351
/// UTF-8 Rust string slice without a terminating null byte or an error is
@@ -130,6 +138,55 @@ impl<'a> Iterator for TagIter<'a> {
130138
}
131139
}
132140

141+
/// Iterates the MBI's tags from the first tag to the end tag.
142+
#[derive(Clone, Debug)]
143+
pub struct TagIterMut<'a> {
144+
/// Pointer to the next tag. Updated in each iteration.
145+
pub current: *mut Tag,
146+
/// The pointer right after the MBI. Used for additional bounds checking.
147+
end_ptr_exclusive: *mut u8,
148+
/// Lifetime capture of the MBI's memory.
149+
_mem: PhantomData<&'a mut ()>,
150+
}
151+
152+
impl<'a> TagIterMut<'a> {
153+
/// Creates a new iterator
154+
pub fn new(mem: &'a mut [u8]) -> Self {
155+
assert_eq!(mem.as_ptr().align_offset(8), 0);
156+
TagIterMut {
157+
current: mem.as_mut_ptr().cast(),
158+
end_ptr_exclusive: unsafe { mem.as_mut_ptr().add(mem.len()) },
159+
_mem: PhantomData,
160+
}
161+
}
162+
}
163+
164+
impl<'a> Iterator for TagIterMut<'a> {
165+
type Item = &'a mut Tag;
166+
167+
fn next(&mut self) -> Option<&'a mut Tag> {
168+
// This never failed so far. But better be safe.
169+
assert!(self.current.cast::<u8>() < self.end_ptr_exclusive);
170+
171+
let tag = unsafe { &mut *self.current };
172+
match tag.typ() {
173+
TagType::End => None, // end tag
174+
_ => {
175+
// We return the tag and update self.current already to the next
176+
// tag.
177+
178+
// next pointer (rounded up to 8-byte alignment)
179+
let ptr_offset = (tag.size as usize + 7) & !7;
180+
181+
// go to next tag
182+
self.current = unsafe { self.current.cast::<u8>().add(ptr_offset).cast::<Tag>() };
183+
184+
Some(tag)
185+
}
186+
}
187+
}
188+
}
189+
133190
#[cfg(test)]
134191
mod tests {
135192
use super::*;

multiboot2/src/tag_trait.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,17 @@ pub trait TagTrait: Pointee {
5656
let ptr = ptr_meta::from_raw_parts(ptr.cast(), Self::dst_size(tag));
5757
&*ptr
5858
}
59+
60+
/// Creates a reference to a (dynamically sized) tag type in a safe way.
61+
/// DST tags need to implement a proper [`Self::dst_size`] implementation.
62+
///
63+
/// # Safety
64+
/// Callers must be sure that the "size" field of the provided [`Tag`] is
65+
/// sane and the underlying memory valid. The implementation of this trait
66+
/// **must have** a correct [`Self::dst_size`] implementation.
67+
unsafe fn from_base_tag_mut<'a>(tag: &mut Tag) -> &'a mut Self {
68+
let ptr = core::ptr::addr_of_mut!(*tag);
69+
let ptr = ptr_meta::from_raw_parts_mut(ptr.cast(), Self::dst_size(tag));
70+
&mut *ptr
71+
}
5972
}

0 commit comments

Comments
 (0)