Skip to content

Commit cb4a10b

Browse files
authored
Merge pull request #155 from rust-osdev/dev7
multiboot2: model MBI as DST
2 parents 267e0ee + 564933d commit cb4a10b

File tree

3 files changed

+95
-50
lines changed

3 files changed

+95
-50
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: 64 additions & 36 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,58 +143,77 @@ 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

178201
/// A Multiboot 2 Boot Information (MBI) accessor.
179202
#[repr(transparent)]
180203
pub struct BootInformation<'a>(&'a BootInformationInner);
181204

182-
impl BootInformation<'_> {
205+
impl<'a> BootInformation<'a> {
183206
/// Loads the [`BootInformation`] from a pointer. The pointer must be valid
184207
/// and aligned to an 8-byte boundary, as defined by the spec.
185208
///
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.
@@ -425,20 +454,19 @@ impl BootInformation<'_> {
425454
/// .unwrap();
426455
/// assert_eq!(tag.name(), Ok("name"));
427456
/// ```
428-
pub fn get_tag<TagT: TagTrait + ?Sized, TagType: Into<TagTypeId>>(
429-
&self,
457+
pub fn get_tag<TagT: TagTrait + ?Sized + 'a, TagType: Into<TagTypeId>>(
458+
&'a self,
430459
typ: TagType,
431-
) -> Option<&TagT> {
460+
) -> Option<&'a TagT> {
432461
let typ = typ.into();
433462
self.tags()
434463
.find(|tag| tag.typ == typ)
435464
.map(|tag| tag.cast_tag::<TagT>())
436465
}
437466

467+
/// Returns an iterator over all tags.
438468
fn tags(&self) -> TagIter {
439-
// The first tag starts 8 bytes after the begin of the boot info header
440-
let ptr = core::ptr::addr_of!(self.0.tags).cast();
441-
TagIter::new(ptr)
469+
TagIter::new(&self.0.tags)
442470
}
443471
}
444472

@@ -525,8 +553,8 @@ pub trait TagTrait: Pointee {
525553
/// sane and the underlying memory valid. The implementation of this trait
526554
/// **must have** a correct [`Self::dst_size`] implementation.
527555
unsafe fn from_base_tag<'a>(tag: &Tag) -> &'a Self {
528-
let ptr = tag as *const _ as *const ();
529-
let ptr = ptr_meta::from_raw_parts(ptr, Self::dst_size(tag));
556+
let ptr = core::ptr::addr_of!(*tag);
557+
let ptr = ptr_meta::from_raw_parts(ptr.cast(), Self::dst_size(tag));
530558
&*ptr
531559
}
532560
}
@@ -1520,7 +1548,7 @@ mod tests {
15201548
0, 0, 0, 0, // end tag type.
15211549
8, 0, 0, 0, // end tag size.
15221550
]);
1523-
let bi = unsafe { BootInformation::load(bytes2.0.as_ptr()) };
1551+
let bi = unsafe { BootInformation::load(bytes2.0.as_ptr().cast()) };
15241552
let bi = bi.unwrap();
15251553
let efi_mmap = bi.efi_memory_map_tag();
15261554
assert!(efi_mmap.is_none());

multiboot2/src/tag_type.rs

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -297,14 +297,15 @@ mod partial_eq_impls {
297297
#[derive(Clone, Copy)]
298298
#[repr(C)]
299299
pub struct Tag {
300-
pub typ: TagTypeId, // u32
300+
// u32
301+
pub typ: TagTypeId,
301302
pub size: u32,
302303
// additional, tag specific fields
303304
}
304305

305306
impl Tag {
306307
/// Casts the base tag to the specific tag type.
307-
pub fn cast_tag<'a, T: TagTrait + ?Sized>(&self) -> &'a T {
308+
pub fn cast_tag<'a, T: TagTrait + ?Sized + 'a>(&'a self) -> &'a T {
308309
// Safety: At this point, we trust that "self.size" and the size hint
309310
// for DST tags are sane.
310311
unsafe { TagTrait::from_base_tag(self) }
@@ -377,17 +378,25 @@ impl StructAsBytes for EndTag {
377378
}
378379
}
379380

381+
/// Iterates the MBI's tags from the first tag to the end tag.
380382
#[derive(Clone, Debug)]
381383
pub struct TagIter<'a> {
384+
/// Pointer to the next tag. Updated in each iteration.
382385
pub current: *const Tag,
383-
phantom: PhantomData<&'a Tag>,
386+
/// The pointer right after the MBI. Used for additional bounds checking.
387+
end_ptr_exclusive: *const u8,
388+
/// Lifetime capture of the MBI's memory.
389+
_mem: PhantomData<&'a ()>,
384390
}
385391

386392
impl<'a> TagIter<'a> {
387-
pub fn new(first: *const Tag) -> Self {
393+
/// Creates a new iterator
394+
pub fn new(mem: &'a [u8]) -> Self {
395+
assert_eq!(mem.as_ptr().align_offset(8), 0);
388396
TagIter {
389-
current: first,
390-
phantom: PhantomData,
397+
current: mem.as_ptr().cast(),
398+
end_ptr_exclusive: unsafe { mem.as_ptr().add(mem.len()) },
399+
_mem: PhantomData,
391400
}
392401
}
393402
}
@@ -396,17 +405,25 @@ impl<'a> Iterator for TagIter<'a> {
396405
type Item = &'a Tag;
397406

398407
fn next(&mut self) -> Option<&'a Tag> {
399-
match unsafe { &*self.current } {
408+
// This never failed so far. But better be safe.
409+
assert!(self.current.cast::<u8>() < self.end_ptr_exclusive);
410+
411+
let tag = unsafe { &*self.current };
412+
match tag {
400413
&Tag {
401414
// END-Tag
402415
typ: TagTypeId(0),
403416
size: 8,
404417
} => None, // end tag
405418
tag => {
419+
// We return the tag and update self.current already to the next
420+
// tag.
421+
422+
// next pointer (rounded up to 8-byte alignment)
423+
let ptr_offset = (tag.size as usize + 7) & !7;
424+
406425
// go to next tag
407-
let mut tag_addr = self.current as usize;
408-
tag_addr += ((tag.size + 7) & !7) as usize; //align at 8 byte
409-
self.current = tag_addr as *const _;
426+
self.current = unsafe { self.current.cast::<u8>().add(ptr_offset).cast::<Tag>() };
410427

411428
Some(tag)
412429
}

0 commit comments

Comments
 (0)