Skip to content

Commit ac8be5a

Browse files
committed
multiboot2: model MBI as DST
This is an improvement that enables better memory checks by miri. Furthermore, it is technically the right to model this in Rust.
1 parent 267e0ee commit ac8be5a

File tree

2 files changed

+60
-31
lines changed

2 files changed

+60
-31
lines changed

multiboot2/src/builder/information.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Exports item [`InformationBuilder`].
22
use crate::builder::traits::StructAsBytes;
33
use crate::{
4-
BasicMemoryInfoTag, BootInformationInner, BootLoaderNameTag, CommandLineTag,
4+
BasicMemoryInfoTag, BootInformationHeader, BootLoaderNameTag, CommandLineTag,
55
EFIBootServicesNotExitedTag, EFIImageHandle32Tag, EFIImageHandle64Tag, EFIMemoryMapTag,
66
EFISdt32Tag, EFISdt64Tag, ElfSectionsTag, EndTag, FramebufferTag, ImageLoadPhysAddrTag,
77
MemoryMapTag, ModuleTag, RsdpV1Tag, RsdpV2Tag, SmbiosTag,
@@ -74,7 +74,7 @@ impl InformationBuilder {
7474
/// Returns the expected length of the Multiboot2 header,
7575
/// when the `build()`-method gets called.
7676
pub fn expected_len(&self) -> usize {
77-
let base_len = size_of::<BootInformationInner>();
77+
let base_len = size_of::<BootInformationHeader>();
7878
// size_or_up_aligned not required, because length is 16 and the
7979
// begin is 8 byte aligned => first tag automatically 8 byte aligned
8080
let mut len = Self::size_or_up_aligned(base_len);
@@ -156,7 +156,7 @@ impl InformationBuilder {
156156
Self::build_add_bytes(
157157
&mut data,
158158
// important that we write the correct expected length into the header!
159-
&BootInformationInner::new(self.expected_len() as u32).struct_as_bytes(),
159+
&BootInformationHeader::new(self.expected_len() as u32).struct_as_bytes(),
160160
false,
161161
);
162162

@@ -327,7 +327,7 @@ mod tests {
327327
assert_eq!(builder.expected_len(), expected_len);
328328

329329
let mb2i_data = builder.build();
330-
let mb2i = unsafe { BootInformation::load(mb2i_data.as_ptr()) }
330+
let mb2i = unsafe { BootInformation::load(mb2i_data.as_ptr().cast()) }
331331
.expect("the generated information to be readable");
332332
println!("{:#?}", mb2i);
333333
assert_eq!(mb2i.basic_memory_info_tag().unwrap().memory_lower(), 640);

multiboot2/src/lib.rs

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@
2222
//! ## Example
2323
//!
2424
//! ```rust
25-
//! use multiboot2::BootInformation;
26-
//! fn kmain(multiboot_info_ptr: u32) {
27-
//! let boot_info = unsafe { BootInformation::load(multiboot_info_ptr as *const u8).unwrap() };
28-
//! println!("{:?}", boot_info);
25+
//! use multiboot2::{BootInformation, BootInformationHeader};
26+
//!
27+
//! fn kernel_entry(mb_magic: u32, mbi_ptr: u32) {
28+
//! if mb_magic == multiboot2::MAGIC {
29+
//! let boot_info = unsafe { BootInformation::load(mbi_ptr as *const BootInformationHeader).unwrap() };
30+
//! let _cmd = boot_info.command_line_tag();
31+
//! } else { /* Panic or use multiboot1 flow. */ }
2932
//! }
3033
//! ```
3134
//!
@@ -41,6 +44,7 @@ extern crate alloc;
4144
extern crate std;
4245

4346
use core::fmt;
47+
use core::mem::size_of;
4448
use derive_more::Display;
4549
// Must be public so that custom tags can be DSTs.
4650
pub use ptr_meta::Pointee;
@@ -102,7 +106,7 @@ pub const MAGIC: u32 = 0x36d76289;
102106
/// Deprecated. Please use BootInformation::load() instead.
103107
#[deprecated = "Please use BootInformation::load() instead."]
104108
pub unsafe fn load<'a>(address: usize) -> Result<BootInformation<'a>, MbiLoadError> {
105-
let ptr = address as *const u8;
109+
let ptr = address as *const BootInformationHeader;
106110
BootInformation::load(ptr)
107111
}
108112

@@ -139,39 +143,58 @@ pub enum MbiLoadError {
139143
#[cfg(feature = "unstable")]
140144
impl core::error::Error for MbiLoadError {}
141145

146+
/// The basic header of a boot information.
147+
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
142148
#[repr(C, align(8))]
143-
struct BootInformationInner {
149+
pub struct BootInformationHeader {
150+
// size is multiple of 8
144151
total_size: u32,
145152
_reserved: u32,
146-
// followed by various, dynamically sized multiboot2 tags
147-
tags: [Tag; 0],
153+
// Followed by the boot information tags.
148154
}
149155

150-
impl BootInformationInner {
156+
impl BootInformationHeader {
151157
#[cfg(feature = "builder")]
152158
fn new(total_size: u32) -> Self {
153159
Self {
154160
total_size,
155161
_reserved: 0,
156-
tags: [],
157162
}
158163
}
164+
}
165+
166+
#[cfg(feature = "builder")]
167+
impl StructAsBytes for BootInformationHeader {
168+
fn byte_size(&self) -> usize {
169+
core::mem::size_of::<Self>()
170+
}
171+
}
159172

173+
/// This type holds the whole data of the MBI. This helps to better satisfy miri
174+
/// when it checks for memory issues.
175+
#[derive(ptr_meta::Pointee)]
176+
#[repr(C)]
177+
struct BootInformationInner {
178+
header: BootInformationHeader,
179+
tags: [u8],
180+
}
181+
182+
impl BootInformationInner {
183+
/// Checks if the MBI has a valid end tag by checking the end of the mbi's
184+
/// bytes.
160185
fn has_valid_end_tag(&self) -> bool {
161186
let end_tag_prototype = EndTag::default();
162187

163-
let self_ptr = self as *const _;
164-
let end_tag_addr = self_ptr as usize + (self.total_size - end_tag_prototype.size) as usize;
165-
let end_tag = unsafe { &*(end_tag_addr as *const Tag) };
188+
let self_ptr = unsafe { self.tags.as_ptr().sub(size_of::<BootInformationHeader>()) };
166189

167-
end_tag.typ == end_tag_prototype.typ && end_tag.size == end_tag_prototype.size
168-
}
169-
}
190+
let end_tag_ptr = unsafe {
191+
self_ptr
192+
.add(self.header.total_size as usize)
193+
.sub(size_of::<EndTag>())
194+
};
195+
let end_tag = unsafe { &*(end_tag_ptr as *const EndTag) };
170196

171-
#[cfg(feature = "builder")]
172-
impl StructAsBytes for BootInformationInner {
173-
fn byte_size(&self) -> usize {
174-
core::mem::size_of::<Self>()
197+
end_tag.typ == end_tag_prototype.typ && end_tag.size == end_tag_prototype.size
175198
}
176199
}
177200

@@ -186,11 +209,11 @@ impl BootInformation<'_> {
186209
/// ## Example
187210
///
188211
/// ```rust
189-
/// use multiboot2::BootInformation;
212+
/// use multiboot2::{BootInformation, BootInformationHeader};
190213
///
191214
/// fn kernel_entry(mb_magic: u32, mbi_ptr: u32) {
192215
/// if mb_magic == multiboot2::MAGIC {
193-
/// let boot_info = unsafe { BootInformation::load(mbi_ptr as *const u8).unwrap() };
216+
/// let boot_info = unsafe { BootInformation::load(mbi_ptr as *const BootInformationHeader).unwrap() };
194217
/// let _cmd = boot_info.command_line_tag();
195218
/// } else { /* Panic or use multiboot1 flow. */ }
196219
/// }
@@ -203,20 +226,26 @@ impl BootInformation<'_> {
203226
/// boot environments, such as UEFI.
204227
/// * The memory at `ptr` must not be modified after calling `load` or the
205228
/// program may observe unsynchronized mutation.
206-
pub unsafe fn load(ptr: *const u8) -> Result<Self, MbiLoadError> {
229+
pub unsafe fn load(ptr: *const BootInformationHeader) -> Result<Self, MbiLoadError> {
207230
// null or not aligned
208231
if ptr.is_null() || ptr.align_offset(8) != 0 {
209232
return Err(MbiLoadError::IllegalAddress);
210233
}
211234

212-
let mbi = &*ptr.cast::<BootInformationInner>();
235+
// mbi: reference to basic header
236+
let mbi = &*ptr;
213237

214238
// Check if total size is a multiple of 8.
215239
// See MbiLoadError::IllegalTotalSize for comments
216240
if mbi.total_size & 0b111 != 0 {
217241
return Err(MbiLoadError::IllegalTotalSize(mbi.total_size));
218242
}
219243

244+
let slice_size = mbi.total_size as usize - size_of::<BootInformationHeader>();
245+
// mbi: reference to full mbi
246+
let mbi = ptr_meta::from_raw_parts::<BootInformationInner>(ptr.cast(), slice_size);
247+
let mbi = &*mbi;
248+
220249
if !mbi.has_valid_end_tag() {
221250
return Err(MbiLoadError::NoEndTag);
222251
}
@@ -226,7 +255,7 @@ impl BootInformation<'_> {
226255

227256
/// Get the start address of the boot info.
228257
pub fn start_address(&self) -> usize {
229-
core::ptr::addr_of!(*self.0) as usize
258+
self.as_ptr() as usize
230259
}
231260

232261
/// Get the start address of the boot info as pointer.
@@ -248,7 +277,7 @@ impl BootInformation<'_> {
248277

249278
/// Get the total size of the boot info struct.
250279
pub fn total_size(&self) -> usize {
251-
self.0.total_size as usize
280+
self.0.header.total_size as usize
252281
}
253282

254283
/// Search for the basic memory info tag.
@@ -1520,7 +1549,7 @@ mod tests {
15201549
0, 0, 0, 0, // end tag type.
15211550
8, 0, 0, 0, // end tag size.
15221551
]);
1523-
let bi = unsafe { BootInformation::load(bytes2.0.as_ptr()) };
1552+
let bi = unsafe { BootInformation::load(bytes2.0.as_ptr().cast()) };
15241553
let bi = bi.unwrap();
15251554
let efi_mmap = bi.efi_memory_map_tag();
15261555
assert!(efi_mmap.is_none());

0 commit comments

Comments
 (0)