Skip to content

Commit 2b62224

Browse files
committed
multiboot2: Get a mutable reference to the memory map
1 parent ed76205 commit 2b62224

File tree

6 files changed

+173
-29
lines changed

6 files changed

+173
-29
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:#?}");
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: 95 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ pub use memory_map::{
6767
pub use module::{ModuleIter, ModuleTag};
6868
pub use rsdp::{RsdpV1Tag, RsdpV2Tag};
6969
pub use smbios::SmbiosTag;
70-
use tag_type::TagIter;
7170
pub use tag_type::{EndTag, Tag, TagType, TagTypeId};
71+
use tag_type::{TagIter, TagIterMut};
7272
pub use vbe_info::{
7373
VBECapabilities, VBEControlInfo, VBEDirectColorAttributes, VBEField, VBEInfoTag,
7474
VBEMemoryModel, VBEModeAttributes, VBEModeInfo, VBEWindowAttributes,
@@ -105,8 +105,10 @@ pub const MAGIC: u32 = 0x36d76289;
105105
/// # Safety
106106
/// Deprecated. Please use BootInformation::load() instead.
107107
#[deprecated = "Please use BootInformation::load() instead."]
108-
pub unsafe fn load<'a>(address: usize) -> Result<BootInformation<'a>, MbiLoadError> {
109-
let ptr = address as *const BootInformationHeader;
108+
pub unsafe fn load<'a>(
109+
address: usize,
110+
) -> Result<BootInformation<&'a BootInformationInner>, MbiLoadError> {
111+
let ptr = address as *mut BootInformationHeader;
110112
BootInformation::load(ptr)
111113
}
112114

@@ -116,8 +118,8 @@ pub unsafe fn load<'a>(address: usize) -> Result<BootInformation<'a>, MbiLoadErr
116118
pub unsafe fn load_with_offset<'a>(
117119
address: usize,
118120
offset: usize,
119-
) -> Result<BootInformation<'a>, MbiLoadError> {
120-
let ptr = address as *const u8;
121+
) -> Result<BootInformation<&'a BootInformationInner>, MbiLoadError> {
122+
let ptr = address as *mut u8;
121123
let ptr = ptr.add(offset);
122124
BootInformation::load(ptr.cast())
123125
}
@@ -172,9 +174,9 @@ impl StructAsBytes for BootInformationHeader {
172174

173175
/// This type holds the whole data of the MBI. This helps to better satisfy miri
174176
/// when it checks for memory issues.
175-
#[derive(ptr_meta::Pointee)]
177+
#[derive(ptr_meta::Pointee, Debug)]
176178
#[repr(C)]
177-
struct BootInformationInner {
179+
pub struct BootInformationInner {
178180
header: BootInformationHeader,
179181
tags: [u8],
180182
}
@@ -198,11 +200,17 @@ impl BootInformationInner {
198200
}
199201
}
200202

203+
impl AsRef<BootInformationInner> for BootInformationInner {
204+
fn as_ref(&self) -> &BootInformationInner {
205+
self
206+
}
207+
}
208+
201209
/// A Multiboot 2 Boot Information (MBI) accessor.
202210
#[repr(transparent)]
203-
pub struct BootInformation<'a>(&'a BootInformationInner);
211+
pub struct BootInformation<T: AsRef<BootInformationInner>>(T);
204212

205-
impl<'a> BootInformation<'a> {
213+
impl BootInformation<&BootInformationInner> {
206214
/// Loads the [`BootInformation`] from a pointer. The pointer must be valid
207215
/// and aligned to an 8-byte boundary, as defined by the spec.
208216
///
@@ -251,15 +259,50 @@ impl<'a> BootInformation<'a> {
251259

252260
Ok(Self(mbi))
253261
}
262+
}
254263

264+
impl BootInformation<&mut BootInformationInner> {
265+
/// [`load`], but mutably.
266+
///
267+
/// # Safety
268+
/// The same considerations that apply to `load` also apply here, but the
269+
/// memory can be modified (through the `_mut` methods).
270+
pub unsafe fn load_mut(ptr: *mut BootInformationHeader) -> Result<Self, MbiLoadError> {
271+
// null or not aligned
272+
if ptr.is_null() || ptr.align_offset(8) != 0 {
273+
return Err(MbiLoadError::IllegalAddress);
274+
}
275+
276+
// mbi: reference to basic header
277+
let mbi = &*ptr;
278+
279+
// Check if total size is not 0 and a multiple of 8.
280+
if mbi.total_size == 0 || mbi.total_size & 0b111 != 0 {
281+
return Err(MbiLoadError::IllegalTotalSize(mbi.total_size));
282+
}
283+
284+
let slice_size = mbi.total_size as usize - size_of::<BootInformationHeader>();
285+
// mbi: reference to full mbi
286+
let mbi = ptr_meta::from_raw_parts_mut::<BootInformationInner>(ptr.cast(), slice_size);
287+
let mbi = &mut *mbi;
288+
289+
if !mbi.has_valid_end_tag() {
290+
return Err(MbiLoadError::NoEndTag);
291+
}
292+
293+
Ok(Self(mbi))
294+
}
295+
}
296+
297+
impl<T: AsRef<BootInformationInner>> BootInformation<T> {
255298
/// Get the start address of the boot info.
256299
pub fn start_address(&self) -> usize {
257300
self.as_ptr() as usize
258301
}
259302

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

265308
/// Get the end address of the boot info.
@@ -276,7 +319,7 @@ impl<'a> BootInformation<'a> {
276319

277320
/// Get the total size of the boot info struct.
278321
pub fn total_size(&self) -> usize {
279-
self.0.header.total_size as usize
322+
self.0.as_ref().header.total_size as usize
280323
}
281324

282325
/// Search for the basic memory info tag.
@@ -454,10 +497,10 @@ impl<'a> BootInformation<'a> {
454497
/// .unwrap();
455498
/// assert_eq!(tag.name(), Ok("name"));
456499
/// ```
457-
pub fn get_tag<TagT: TagTrait + ?Sized + 'a, TagType: Into<TagTypeId>>(
458-
&'a self,
500+
pub fn get_tag<TagT: TagTrait + ?Sized, TagType: Into<TagTypeId>>(
501+
&self,
459502
typ: TagType,
460-
) -> Option<&'a TagT> {
503+
) -> Option<&TagT> {
461504
let typ = typ.into();
462505
self.tags()
463506
.find(|tag| tag.typ == typ)
@@ -466,15 +509,36 @@ impl<'a> BootInformation<'a> {
466509

467510
/// Returns an iterator over all tags.
468511
fn tags(&self) -> TagIter {
469-
TagIter::new(&self.0.tags)
512+
TagIter::new(&self.0.as_ref().tags)
513+
}
514+
}
515+
516+
impl<T: AsRef<BootInformationInner> + AsMut<BootInformationInner>> BootInformation<T> {
517+
/// Search for the Memory map tag, return a mutable reference.
518+
pub fn memory_map_tag_mut(&mut self) -> Option<&mut MemoryMapTag> {
519+
self.get_tag_mut::<MemoryMapTag, _>(TagType::Mmap)
520+
}
521+
522+
fn get_tag_mut<TagT: TagTrait + ?Sized, TagType: Into<TagTypeId>>(
523+
&mut self,
524+
typ: TagType,
525+
) -> Option<&mut TagT> {
526+
let typ = typ.into();
527+
self.tags_mut()
528+
.find(|tag| tag.typ == typ)
529+
.map(|tag| tag.cast_tag_mut::<TagT>())
530+
}
531+
532+
fn tags_mut(&mut self) -> TagIterMut {
533+
TagIterMut::new(&mut self.0.as_mut().tags)
470534
}
471535
}
472536

473537
// SAFETY: BootInformation contains a const ptr to memory that is never mutated.
474538
// Sending this pointer to other threads is sound.
475-
unsafe impl Send for BootInformation<'_> {}
539+
unsafe impl<T: AsRef<BootInformationInner>> Send for BootInformation<T> {}
476540

477-
impl fmt::Debug for BootInformation<'_> {
541+
impl<T: AsRef<BootInformationInner>> fmt::Debug for BootInformation<T> {
478542
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
479543
/// Limit how many Elf-Sections should be debug-formatted.
480544
/// Can be thousands of sections for a Rust binary => this is useless output.
@@ -557,6 +621,19 @@ pub trait TagTrait: Pointee {
557621
let ptr = ptr_meta::from_raw_parts(ptr.cast(), Self::dst_size(tag));
558622
&*ptr
559623
}
624+
625+
/// Creates a mutable reference to a (dynamically sized) tag type in a safe way.
626+
/// DST tags need to implement a proper [`Self::dst_size`] implementation.
627+
///
628+
/// # Safety
629+
/// Callers must be sure that the "size" field of the provided [`Tag`] is
630+
/// sane and the underlying memory valid. The implementation of this trait
631+
/// **must have** a correct [`Self::dst_size`] implementation.
632+
unsafe fn from_base_tag_mut<'a>(tag: &mut Tag) -> &'a mut Self {
633+
let ptr = core::ptr::addr_of_mut!(*tag);
634+
let ptr = ptr_meta::from_raw_parts_mut(ptr.cast(), Self::dst_size(tag));
635+
&mut *ptr
636+
}
560637
}
561638

562639
// All sized tags automatically have a Pointee implementation where
@@ -1280,7 +1357,7 @@ mod tests {
12801357

12811358
/// Helper for [`grub2`].
12821359
fn test_grub2_boot_info(
1283-
bi: &BootInformation,
1360+
bi: &BootInformation<&BootInformationInner>,
12841361
addr: usize,
12851362
string_addr: u64,
12861363
bytes: &[u8],

multiboot2/src/memory_map.rs

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

6572
impl TagTrait for MemoryMapTag {

multiboot2/src/tag_type.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,13 @@ impl Tag {
311311
unsafe { TagTrait::from_base_tag(self) }
312312
}
313313

314+
/// Casts the base tag to the specific tag type, but mutably.
315+
pub(crate) fn cast_tag_mut<'a, T: TagTrait + ?Sized>(&mut self) -> &'a mut T {
316+
// Safety: At this point, we trust that "self.size" and the size hint
317+
// for DST tags are sane.
318+
unsafe { TagTrait::from_base_tag_mut(self) }
319+
}
320+
314321
/// Some multiboot2 tags are a DST as they end with a dynamically sized byte
315322
/// slice. This function parses this slice as [`str`] so that either a valid
316323
/// UTF-8 Rust string slice without a terminating null byte or an error is
@@ -431,6 +438,59 @@ impl<'a> Iterator for TagIter<'a> {
431438
}
432439
}
433440

441+
/// Iterates the MBI's tags from the first tag to the end tag, mutably.
442+
#[derive(Clone, Debug)]
443+
pub struct TagIterMut<'a> {
444+
/// Pointer to the next tag. Updated in each iteration.
445+
pub current: *mut Tag,
446+
/// The pointer right after the MBI. Used for additional bounds checking.
447+
end_ptr_exclusive: *mut u8,
448+
/// Lifetime capture of the MBI's memory.
449+
_mem: PhantomData<&'a ()>,
450+
}
451+
452+
impl<'a> TagIterMut<'a> {
453+
/// Creates a new iterator
454+
pub fn new(mem: &'a mut [u8]) -> Self {
455+
assert_eq!(mem.as_mut_ptr().align_offset(8), 0);
456+
TagIterMut {
457+
current: mem.as_mut_ptr().cast(),
458+
end_ptr_exclusive: unsafe { mem.as_mut_ptr().add(mem.len()) },
459+
_mem: PhantomData,
460+
}
461+
}
462+
}
463+
464+
impl<'a> Iterator for TagIterMut<'a> {
465+
type Item = &'a mut Tag;
466+
467+
fn next(&mut self) -> Option<&'a mut Tag> {
468+
// This never failed so far. But better be safe.
469+
assert!(self.current.cast::<u8>() < self.end_ptr_exclusive);
470+
471+
let tag = unsafe { &mut *self.current };
472+
match tag {
473+
&mut Tag {
474+
// END-Tag
475+
typ: TagTypeId(0),
476+
size: 8,
477+
} => None, // end tag
478+
tag => {
479+
// We return the tag and update self.current already to the next
480+
// tag.
481+
482+
// next pointer (rounded up to 8-byte alignment)
483+
let ptr_offset = (tag.size as usize + 7) & !7;
484+
485+
// go to next tag
486+
self.current = unsafe { self.current.cast::<u8>().add(ptr_offset).cast::<Tag>() };
487+
488+
Some(tag)
489+
}
490+
}
491+
}
492+
}
493+
434494
#[cfg(test)]
435495
mod tests {
436496
use super::*;

0 commit comments

Comments
 (0)