Skip to content

Commit 1b7032a

Browse files
committed
multiboot2: refactor to use common abstractions
This especially significantly changes the builder. The normal public API however is only slightly affected.
1 parent 79b8c8a commit 1b7032a

21 files changed

+917
-1278
lines changed

multiboot2/Cargo.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,24 @@ rust-version = "1.70"
3535

3636
[features]
3737
default = ["builder"]
38-
alloc = []
39-
builder = ["alloc"]
38+
alloc = ["multiboot2-common/alloc"]
39+
builder = ["alloc", "multiboot2-common/builder"]
4040
# Nightly-only features, which will eventually be stabilized.
41-
unstable = []
41+
unstable = ["multiboot2-common/unstable"]
4242

4343
[dependencies]
4444
bitflags.workspace = true
4545
derive_more.workspace = true
4646
log.workspace = true
47+
# TODO remove? move to common entirely?
48+
ptr_meta.workspace = true
49+
multiboot2-common = { version = "0.1.0", default-features = false }
4750

48-
ptr_meta = { version = "~0.2", default-features = false }
4951
# We only use a very basic type definition from this crate. To prevent MSRV
5052
# bumps from uefi-raw, I restrict this here. Upstream users are likely to have
5153
# two versions of this library in it, which is no problem, as we only use the
5254
# type definition.
5355
uefi-raw = { version = "~0.5", default-features = false }
5456

55-
[dev-dependencies]
56-
5757
[package.metadata.docs.rs]
5858
all-features = true

multiboot2/src/boot_information.rs

Lines changed: 76 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,26 @@
11
//! Module for [`BootInformation`].
22
3-
#[cfg(feature = "builder")]
4-
use crate::builder::AsBytes;
53
use crate::framebuffer::UnknownFramebufferType;
6-
use crate::tag::{TagHeader, TagIter};
4+
use crate::tag::TagHeader;
75
use crate::{
86
module, BasicMemoryInfoTag, BootLoaderNameTag, CommandLineTag, EFIBootServicesNotExitedTag,
97
EFIImageHandle32Tag, EFIImageHandle64Tag, EFIMemoryMapTag, EFISdt32Tag, EFISdt64Tag,
108
ElfSectionIter, ElfSectionsTag, EndTag, FramebufferTag, ImageLoadPhysAddrTag, MemoryMapTag,
11-
ModuleIter, RsdpV1Tag, RsdpV2Tag, SmbiosTag, TagTrait, VBEInfoTag,
9+
ModuleIter, RsdpV1Tag, RsdpV2Tag, SmbiosTag, TagIter, TagType, VBEInfoTag,
1210
};
1311
use core::fmt;
1412
use core::mem;
15-
use core::ptr;
13+
use core::ptr::NonNull;
1614
use derive_more::Display;
15+
use multiboot2_common::{DynSizedStructure, Header, MaybeDynSized, MemoryError, Tag};
1716

1817
/// Error type that describes errors while loading/parsing a multiboot2 information structure
1918
/// from a given address.
2019
#[derive(Display, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
2120
pub enum MbiLoadError {
22-
/// The address is invalid. Make sure that the address is 8-byte aligned,
23-
/// according to the spec.
24-
#[display(fmt = "The address is invalid")]
25-
IllegalAddress,
26-
/// The total size of the multiboot2 information structure must be not zero
27-
/// and a multiple of 8.
28-
#[display(fmt = "The size of the MBI is unexpected")]
29-
IllegalTotalSize(u32),
30-
/// Missing end tag. Each multiboot2 boot information requires to have an
31-
/// end tag.
32-
#[display(fmt = "There is no end tag")]
21+
/// See [`MemoryError`].
22+
Memory(MemoryError),
23+
/// Missing mandatory end tag.
3324
NoEndTag,
3425
}
3526

@@ -62,40 +53,19 @@ impl BootInformationHeader {
6253
}
6354
}
6455

65-
#[cfg(feature = "builder")]
66-
impl AsBytes for BootInformationHeader {}
67-
68-
/// This type holds the whole data of the MBI. This helps to better satisfy miri
69-
/// when it checks for memory issues.
70-
#[derive(ptr_meta::Pointee)]
71-
#[repr(C, align(8))]
72-
struct BootInformationInner {
73-
header: BootInformationHeader,
74-
tags: [u8],
75-
}
76-
77-
impl BootInformationInner {
78-
/// Checks if the MBI has a valid end tag by checking the end of the mbi's
79-
/// bytes.
80-
fn has_valid_end_tag(&self) -> bool {
81-
let self_ptr = ptr::addr_of!(*self);
82-
83-
let end_tag_ptr = unsafe {
84-
self_ptr
85-
.cast::<u8>()
86-
.add(self.header.total_size as usize)
87-
.sub(mem::size_of::<EndTag>())
88-
.cast::<TagHeader>()
89-
};
90-
let end_tag = unsafe { &*end_tag_ptr };
56+
impl Header for BootInformationHeader {
57+
fn payload_len(&self) -> usize {
58+
self.total_size as usize - mem::size_of::<Self>()
59+
}
9160

92-
end_tag.typ == EndTag::ID && end_tag.size as usize == mem::size_of::<EndTag>()
61+
fn set_size(&mut self, total_size: usize) {
62+
self.total_size = total_size as u32;
9363
}
9464
}
9565

9666
/// A Multiboot 2 Boot Information (MBI) accessor.
9767
#[repr(transparent)]
98-
pub struct BootInformation<'a>(&'a BootInformationInner);
68+
pub struct BootInformation<'a>(&'a DynSizedStructure<BootInformationHeader>);
9969

10070
impl<'a> BootInformation<'a> {
10171
/// Loads the [`BootInformation`] from a pointer. The pointer must be valid
@@ -115,36 +85,38 @@ impl<'a> BootInformation<'a> {
11585
/// ```
11686
///
11787
/// ## Safety
118-
/// * `ptr` must be valid for reading. Otherwise this function might cause
88+
/// * `ptr` must be valid for reading. Otherwise, this function might cause
11989
/// invalid machine state or crash your binary (kernel). This can be the
12090
/// case in environments with standard environment (segfault), but also in
12191
/// boot environments, such as UEFI.
12292
/// * The memory at `ptr` must not be modified after calling `load` or the
12393
/// program may observe unsynchronized mutation.
12494
pub unsafe fn load(ptr: *const BootInformationHeader) -> Result<Self, MbiLoadError> {
125-
// null or not aligned
126-
if ptr.is_null() || ptr.align_offset(8) != 0 {
127-
return Err(MbiLoadError::IllegalAddress);
128-
}
129-
130-
// mbi: reference to basic header
131-
let mbi = &*ptr;
95+
let ptr = NonNull::new(ptr.cast_mut()).ok_or(MbiLoadError::Memory(MemoryError::Null))?;
96+
let inner = DynSizedStructure::ref_from_ptr(ptr).map_err(MbiLoadError::Memory)?;
13297

133-
// Check if total size is not 0 and a multiple of 8.
134-
if mbi.total_size == 0 || mbi.total_size & 0b111 != 0 {
135-
return Err(MbiLoadError::IllegalTotalSize(mbi.total_size));
136-
}
137-
138-
let slice_size = mbi.total_size as usize - mem::size_of::<BootInformationHeader>();
139-
// mbi: reference to full mbi
140-
let mbi = ptr_meta::from_raw_parts::<BootInformationInner>(ptr.cast(), slice_size);
141-
let mbi = &*mbi;
142-
143-
if !mbi.has_valid_end_tag() {
98+
let this = Self(inner);
99+
if !this.has_valid_end_tag() {
144100
return Err(MbiLoadError::NoEndTag);
145101
}
102+
Ok(this)
103+
}
104+
105+
/// Checks if the MBI has a valid end tag by checking the end of the mbi's
106+
/// bytes.
107+
fn has_valid_end_tag(&self) -> bool {
108+
let header = self.0.header();
109+
let end_tag_ptr = unsafe {
110+
self.0
111+
.payload()
112+
.as_ptr()
113+
.add(header.payload_len())
114+
.sub(mem::size_of::<EndTag>())
115+
.cast::<TagHeader>()
116+
};
117+
let end_tag = unsafe { &*end_tag_ptr };
146118

147-
Ok(Self(mbi))
119+
end_tag.typ == EndTag::ID && end_tag.size as usize == mem::size_of::<EndTag>()
148120
}
149121

150122
/// Get the start address of the boot info.
@@ -177,7 +149,7 @@ impl<'a> BootInformation<'a> {
177149
/// Get the total size of the boot info struct.
178150
#[must_use]
179151
pub const fn total_size(&self) -> usize {
180-
self.0.header.total_size as usize
152+
self.0.header().total_size as usize
181153
}
182154

183155
// ######################################################
@@ -279,7 +251,7 @@ impl<'a> BootInformation<'a> {
279251
pub fn elf_sections(&self) -> Option<ElfSectionIter> {
280252
let tag = self.get_tag::<ElfSectionsTag>();
281253
tag.map(|t| {
282-
assert!((t.entry_size() * t.shndx()) <= t.size() as u32);
254+
assert!((t.entry_size() * t.shndx()) <= t.header().size);
283255
t.sections_iter()
284256
})
285257
}
@@ -289,10 +261,12 @@ impl<'a> BootInformation<'a> {
289261
#[must_use]
290262
pub fn framebuffer_tag(&self) -> Option<Result<&FramebufferTag, UnknownFramebufferType>> {
291263
self.get_tag::<FramebufferTag>()
292-
.map(|tag| match tag.buffer_type() {
293-
Ok(_) => Ok(tag),
294-
Err(e) => Err(e),
295-
})
264+
// TODO temporarily. Someone needs to fix the framebuffer thingy.
265+
.map(Ok)
266+
/*.map(|tag| match tag.buffer_type() {
267+
Ok(_) => Ok(tag),
268+
Err(e) => Err(e),
269+
})*/
296270
}
297271

298272
/// Search for the Image Load Base Physical Address tag.
@@ -361,34 +335,44 @@ impl<'a> BootInformation<'a> {
361335
/// special handling is required. This is reflected by code-comments.
362336
///
363337
/// ```no_run
364-
/// use multiboot2::{BootInformation, BootInformationHeader, parse_slice_as_string, StringError, TagHeader, TagTrait, TagType, TagTypeId};
338+
/// use std::mem;
339+
/// use multiboot2::{BootInformation, BootInformationHeader, parse_slice_as_string, StringError, TagHeader, TagType, TagTypeId}; ///
340+
/// use multiboot2_common::{MaybeDynSized, Tag};
365341
///
366342
/// #[repr(C)]
367343
/// #[derive(multiboot2::Pointee)] // Only needed for DSTs.
368344
/// struct CustomTag {
369-
/// tag: TagTypeId,
370-
/// size: u32,
371-
/// // begin of inline string
345+
/// header: TagHeader,
346+
/// some_other_prop: u32,
347+
/// // Begin of C string, for example.
372348
/// name: [u8],
373349
/// }
374350
///
375-
/// // This implementation is only necessary for tags that are DSTs.
376-
/// impl TagTrait for CustomTag {
377-
/// const ID: TagType = TagType::Custom(0x1337);
351+
/// impl CustomTag {
352+
/// fn name(&self) -> Result<&str, StringError> {
353+
/// parse_slice_as_string(&self.name)
354+
/// }
355+
/// }
356+
///
357+
/// // Give the library hints how big this tag is.
358+
/// impl MaybeDynSized for CustomTag {
359+
/// type Header = TagHeader;
360+
/// const BASE_SIZE: usize = mem::size_of::<TagHeader>() + mem::size_of::<u32>();
378361
///
362+
/// // This differs for DSTs and normal structs. See function
363+
/// // documentation.
379364
/// fn dst_len(header: &TagHeader) -> usize {
380-
/// // The size of the sized portion of the custom tag.
381-
/// let tag_base_size = 8; // id + size is 8 byte in size
382-
/// assert!(header.size >= 8);
383-
/// header.size as usize - tag_base_size
365+
/// assert!(header.size >= Self::BASE_SIZE as u32);
366+
/// header.size as usize - Self::BASE_SIZE
384367
/// }
385368
/// }
386369
///
387-
/// impl CustomTag {
388-
/// fn name(&self) -> Result<&str, StringError> {
389-
/// parse_slice_as_string(&self.name)
390-
/// }
370+
/// // Make the Tag identifiable.
371+
/// impl Tag for CustomTag {
372+
/// type IDType = TagType;
373+
/// const ID: TagType = TagType::Custom(0x1337);
391374
/// }
375+
///
392376
/// let mbi_ptr = 0xdeadbeef as *const BootInformationHeader;
393377
/// let mbi = unsafe { BootInformation::load(mbi_ptr).unwrap() };
394378
///
@@ -400,15 +384,17 @@ impl<'a> BootInformation<'a> {
400384
///
401385
/// [`TagType`]: crate::TagType
402386
#[must_use]
403-
pub fn get_tag<TagT: TagTrait + ?Sized + 'a>(&'a self) -> Option<&'a TagT> {
387+
pub fn get_tag<T: Tag<IDType = TagType, Header = TagHeader> + ?Sized + 'a>(
388+
&'a self,
389+
) -> Option<&'a T> {
404390
self.tags()
405-
.find(|tag| tag.header().typ == TagT::ID)
406-
.map(|tag| tag.cast::<TagT>())
391+
.find(|tag| tag.header().typ == T::ID)
392+
.map(|tag| tag.cast::<T>())
407393
}
408394

409395
/// Returns an iterator over all tags.
410-
fn tags(&self) -> TagIter {
411-
TagIter::new(&self.0.tags)
396+
pub(crate) fn tags(&self) -> TagIter {
397+
TagIter::new(self.0.payload())
412398
}
413399
}
414400

multiboot2/src/boot_loader_name.rs

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
//! Module for [`BootLoaderNameTag`].
22
33
use crate::tag::TagHeader;
4-
use crate::{parse_slice_as_string, StringError, TagTrait, TagType};
4+
use crate::{parse_slice_as_string, StringError, TagType};
55
use core::fmt::{Debug, Formatter};
66
use core::mem;
7+
use multiboot2_common::{MaybeDynSized, Tag};
78
#[cfg(feature = "builder")]
8-
use {crate::new_boxed, alloc::boxed::Box};
9-
10-
const METADATA_SIZE: usize = mem::size_of::<TagHeader>();
9+
use {alloc::boxed::Box, multiboot2_common::new_boxed};
1110

1211
/// The bootloader name tag.
1312
#[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -23,11 +22,12 @@ impl BootLoaderNameTag {
2322
#[cfg(feature = "builder")]
2423
#[must_use]
2524
pub fn new(name: &str) -> Box<Self> {
25+
let header = TagHeader::new(Self::ID, 0);
2626
let bytes = name.as_bytes();
2727
if bytes.ends_with(&[0]) {
28-
new_boxed(&[bytes])
28+
new_boxed(header, &[bytes])
2929
} else {
30-
new_boxed(&[bytes, &[0]])
30+
new_boxed(header, &[bytes, &[0]])
3131
}
3232
}
3333

@@ -75,21 +75,29 @@ impl Debug for BootLoaderNameTag {
7575
}
7676
}
7777

78-
impl TagTrait for BootLoaderNameTag {
79-
const ID: TagType = TagType::BootLoaderName;
78+
impl MaybeDynSized for BootLoaderNameTag {
79+
type Header = TagHeader;
80+
81+
const BASE_SIZE: usize = mem::size_of::<TagHeader>();
8082

8183
fn dst_len(header: &TagHeader) -> usize {
82-
assert!(header.size as usize >= METADATA_SIZE);
83-
header.size as usize - METADATA_SIZE
84+
assert!(header.size as usize >= Self::BASE_SIZE);
85+
header.size as usize - Self::BASE_SIZE
8486
}
8587
}
8688

89+
impl Tag for BootLoaderNameTag {
90+
type IDType = TagType;
91+
92+
const ID: TagType = TagType::BootLoaderName;
93+
}
94+
8795
#[cfg(test)]
8896
mod tests {
8997
use super::*;
90-
use crate::tag::{GenericTag, TagBytesRef};
91-
use crate::test_util::AlignedBytes;
98+
use crate::GenericInfoTag;
9299
use core::borrow::Borrow;
100+
use multiboot2_common::test_utils::AlignedBytes;
93101

94102
#[rustfmt::skip]
95103
fn get_bytes() -> AlignedBytes<16> {
@@ -106,8 +114,7 @@ mod tests {
106114
#[test]
107115
fn test_parse_str() {
108116
let bytes = get_bytes();
109-
let bytes = TagBytesRef::try_from(bytes.borrow()).unwrap();
110-
let tag = GenericTag::ref_from(bytes);
117+
let tag = GenericInfoTag::ref_from_slice(bytes.borrow()).unwrap();
111118
let tag = tag.cast::<BootLoaderNameTag>();
112119
assert_eq!(tag.header.typ, TagType::BootLoaderName);
113120
assert_eq!(tag.name(), Ok("hello"));
@@ -118,14 +125,16 @@ mod tests {
118125
#[cfg(feature = "builder")]
119126
fn test_build_str() {
120127
let tag = BootLoaderNameTag::new("hello");
121-
let bytes = tag.as_bytes();
122-
assert_eq!(bytes, &get_bytes()[..tag.size()]);
128+
let bytes = tag.as_bytes().as_ref();
129+
let bytes = &bytes[..tag.header.size as usize];
130+
assert_eq!(bytes, &get_bytes()[..tag.header.size as usize]);
123131
assert_eq!(tag.name(), Ok("hello"));
124132

125133
// With terminating null.
126134
let tag = BootLoaderNameTag::new("hello\0");
127-
let bytes = tag.as_bytes();
128-
assert_eq!(bytes, &get_bytes()[..tag.size()]);
135+
let bytes = tag.as_bytes().as_ref();
136+
let bytes = &bytes[..tag.header.size as usize];
137+
assert_eq!(bytes, &get_bytes()[..tag.header.size as usize]);
129138
assert_eq!(tag.name(), Ok("hello"));
130139

131140
// test also some bigger message

0 commit comments

Comments
 (0)