Skip to content

Commit 12b74ed

Browse files
committed
multiboot2: Get a mutable reference to the memory map
1 parent c2275f4 commit 12b74ed

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
@@ -99,7 +99,7 @@ use derive_more::Display;
9999
#[cfg(feature = "builder")]
100100
use crate::builder::AsBytes;
101101
use crate::framebuffer::UnknownFramebufferType;
102-
use tag::TagIter;
102+
use tag::{TagIter, TagIterMut};
103103

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

153153
/// This type holds the whole data of the MBI. This helps to better satisfy miri
154154
/// when it checks for memory issues.
155-
#[derive(ptr_meta::Pointee)]
155+
#[derive(ptr_meta::Pointee, Debug)]
156156
#[repr(C)]
157-
struct BootInformationInner {
157+
pub struct BootInformationInner {
158158
header: BootInformationHeader,
159159
tags: [u8],
160160
}
@@ -178,11 +178,22 @@ impl BootInformationInner {
178178
}
179179
}
180180

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

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

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

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

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

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

259305
/// Get the total size of the boot info struct.
260306
pub fn total_size(&self) -> usize {
261-
self.0.header.total_size as usize
307+
self.0.as_ref().header.total_size as usize
262308
}
263309

264310
// ######################################################
@@ -458,19 +504,44 @@ impl<'a> BootInformation<'a> {
458504
/// .unwrap();
459505
/// assert_eq!(tag.name(), Ok("name"));
460506
/// ```
461-
pub fn get_tag<TagT: TagTrait + ?Sized + 'a>(&'a self) -> Option<&'a TagT> {
507+
pub fn get_tag<TagT: TagTrait + ?Sized>(&self) -> Option<&TagT> {
462508
self.tags()
463509
.find(|tag| tag.typ == TagT::ID)
464510
.map(|tag| tag.cast_tag::<TagT>())
465511
}
466512

467513
/// Returns an iterator over all tags.
468514
fn tags(&self) -> TagIter {
469-
TagIter::new(&self.0.tags)
515+
TagIter::new(&self.0.as_ref().tags)
470516
}
471517
}
472518

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

12361307
/// Helper for [`grub2`].
12371308
fn test_grub2_boot_info(
1238-
bi: &BootInformation,
1309+
bi: &BootInformation<&BootInformationInner>,
12391310
addr: usize,
12401311
string_addr: u64,
12411312
bytes: &[u8],

multiboot2/src/memory_map.rs

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

6673
impl TagTrait for MemoryMapTag {

multiboot2/src/tag.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ impl Tag {
6464
unsafe { TagTrait::from_base_tag(self) }
6565
}
6666

67+
/// Casts the base tag to the specific tag type, but mutably.
68+
pub fn cast_tag_mut<'a, T: TagTrait + ?Sized + 'a>(&'a mut self) -> &'a mut T {
69+
assert_eq!(self.typ, T::ID);
70+
// Safety: At this point, we trust that "self.size" and the size hint
71+
// for DST tags are sane.
72+
unsafe { TagTrait::from_base_tag_mut(self) }
73+
}
74+
6775
/// Parses the provided byte sequence as Multiboot string, which maps to a
6876
/// [`str`].
6977
pub fn parse_slice_as_string(bytes: &[u8]) -> Result<&str, StringError> {
@@ -139,6 +147,55 @@ impl<'a> Iterator for TagIter<'a> {
139147
}
140148
}
141149

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