Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion integration-test/bins/multiboot2_chainloader/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub fn load_module(mut modules: multiboot::information::ModuleIter) -> ! {

// Check if a header is present.
{
let (hdr, _) = multiboot2_header::Multiboot2Header::find_header(elf_bytes)
let (hdr, _) = multiboot2_header::Header::find_header(elf_bytes)
.expect("Should have Multiboot2 header");
log::info!("Multiboot2 header:\n{hdr:#?}");
}
Expand Down
11 changes: 9 additions & 2 deletions integration-test/bins/multiboot2_payload/src/multiboot2_header.S
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,18 @@

.align 8
.Lmb2_header_tag_module_alignment_start:
.word 7 # type (16bit)
.word 6 # type (16bit)
.word 0 # flags (16bit)
.long .Lmb2_header_tag_module_alignment_end - .Lmb2_header_tag_module_alignment_start # size (32bit)
.long start
.Lmb2_header_tag_module_alignment_end:

.align 8
.Lmb2_header_tag_entry_address_start:
.word 3 # type (16bit)
.word 0 # flags (16bit)
.long .Lmb2_header_tag_entry_address_end - .Lmb2_header_tag_entry_address_start # size (32bit)
.long start
.Lmb2_header_tag_entry_address_end:
# ------------------------------------------------------------------------------------

# REQUIRED END TAG
Expand Down
7 changes: 5 additions & 2 deletions multiboot2-header/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- Expanded `Header` debug output with parsed tags and tag headers.
- **Breaking:** Renamed `multiboot2_header::Multiboot2Header` to
`multiboot2_header::Header`.
- Standardized bootloader terminology.
- Corrected the README example and API documentation.

Expand Down Expand Up @@ -44,8 +47,8 @@ If you are interested in the internals of the major refactorings recently taken
place, please head to the documentation of `multiboot2-common`.

- **Breaking** All functions that returns something useful are now `#[must_use]`
- **Breaking** The builder type is now just called `Builder`. This needs the
`builder` feature.
- **Breaking** Renamed `multiboot2_header::builder::HeaderBuilder` to
`multiboot2_header::Builder`. This needs the `builder` feature.
- **Breaking:** The error type returned by `Multiboot2Header::load` has been
changed.
- Updated to latest `multiboot2` dependency
Expand Down
4 changes: 2 additions & 2 deletions multiboot2-header/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ multiboot2-header = "<latest>"
```rust
use multiboot2_header::{
Builder, HeaderTagFlag, HeaderTagISA, InformationRequestHeaderTag,
MaybeDynSized, MbiTagType, Multiboot2Header, RelocatableHeaderTag,
Header, MaybeDynSized, MbiTagType, RelocatableHeaderTag,
RelocatableHeaderTagPreference,
};

Expand All @@ -65,7 +65,7 @@ fn main() {
))
.build();

let header = unsafe { Multiboot2Header::load(header_bytes.as_ptr()) }.unwrap();
let header = unsafe { Header::load(header_bytes.as_ptr()) }.unwrap();
println!("{header:#?}");
}
```
Expand Down
6 changes: 3 additions & 3 deletions multiboot2-header/examples/minimal.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use multiboot2_header::{
Builder, HeaderTagFlag, HeaderTagISA, InformationRequestHeaderTag, MaybeDynSized, MbiTagType,
Multiboot2Header, RelocatableHeaderTag, RelocatableHeaderTagPreference,
Builder, Header, HeaderTagFlag, HeaderTagISA, InformationRequestHeaderTag, MaybeDynSized,
MbiTagType, RelocatableHeaderTag, RelocatableHeaderTagPreference,
};

/// Small example that creates a Multiboot2 header and parses it afterwards.
Expand All @@ -25,6 +25,6 @@ fn main() {
))
.build();

let header = unsafe { Multiboot2Header::load(header_bytes.as_ptr()) }.unwrap();
let header = unsafe { Header::load(header_bytes.as_ptr()) }.unwrap();
println!("{header:#?}");
}
5 changes: 2 additions & 3 deletions multiboot2-header/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ mod tests {
use crate::ConsoleHeaderTagFlags::ConsoleRequired;
use crate::HeaderTagFlag::{Optional, Required};
use crate::RelocatableHeaderTagPreference::High;
use crate::{MbiTagType, Multiboot2Header};
use crate::{Header, MbiTagType};

#[test]
fn build_and_parse() {
Expand Down Expand Up @@ -219,8 +219,7 @@ mod tests {
let header = {
// SAFETY: The builder emits a fully formed, aligned header
// buffer with a valid end tag.
unsafe { Multiboot2Header::load(structure.as_bytes().as_ref().as_ptr().cast()) }
.unwrap()
unsafe { Header::load(structure.as_bytes().as_ref().as_ptr().cast()) }.unwrap()
};

assert_eq!(header.verify_checksum(), Ok(()));
Expand Down
89 changes: 57 additions & 32 deletions multiboot2-header/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,31 @@ use crate::{
use core::fmt::{Debug, Formatter};
use core::ptr::NonNull;
use multiboot2_common::{
ALIGNMENT, DynSizedStructure, Header, MemoryError, Tag, validate_tag_sequence,
ALIGNMENT, DynSizedStructure, Header as DynSizedHeader, MemoryError, Tag, validate_tag_sequence,
};
use thiserror::Error;

/// Magic value for a [`Multiboot2Header`], as defined by the spec.
/// Magic value for a [`Header`], as defined by the spec.
pub const MAGIC: u32 = 0xe85250d6;
/// Range from the beginning of an image in which bootloaders will search for a
/// multiboot2 header.
pub const HEADER_SEARCH_LIMIT: usize = 32768;

/// Wrapper type around a pointer to the Multiboot2 header.
/// A parsed complete Multiboot2 header.
///
/// The Multiboot2 header is the [`Multiboot2BasicHeader`] followed
/// by all tags (see [`HeaderTagType`]).
/// Use this if you get a pointer to the header and just want
/// to parse it. If you want to construct the type by yourself,
/// use `Builder` (requires the `builder` feature).
/// It consists of the fixed [`Multiboot2BasicHeader`] prefix followed by all
/// header tags (see [`HeaderTagType`]). [`Multiboot2BasicHeader`] represents
/// only that prefix; use this type when working with the complete, dynamically
/// sized header.
///
/// Use this to parse a header from a pointer. To construct a header, use
/// `Builder` (requires the `builder` feature).
#[repr(transparent)]
#[derive(PartialEq, Eq)]
pub struct Multiboot2Header<'a>(&'a DynSizedStructure<Multiboot2BasicHeader>);
pub struct Header<'a>(&'a DynSizedStructure<Multiboot2BasicHeader>);

impl<'a> Multiboot2Header<'a> {
/// Loads the [`Multiboot2Header`] from a pointer.
impl<'a> Header<'a> {
/// Loads a complete [`Header`] from a pointer.
///
/// If the header is invalid, it returns a [`LoadError`].
/// This may be because:
Expand Down Expand Up @@ -292,21 +294,41 @@ impl<'a> Multiboot2Header<'a> {
}
}

impl Debug for Multiboot2Header<'_> {
impl Debug for Header<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Multiboot2Header")
f.debug_struct("Header")
.field("magic", &self.header_magic())
.field("arch", &self.arch())
.field("length", &self.length())
.field("checksum", &self.checksum())
// TODO better debug impl
.field("tags", &"<tags iter>")
.field("information_request", &self.information_request_tag())
.field("address", &self.address_tag())
.field("entry_address", &self.entry_address_tag())
.field("entry_address_efi32", &self.entry_address_efi32_tag())
.field("entry_address_efi64", &self.entry_address_efi64_tag())
.field("console_flags", &self.console_flags_tag())
.field("framebuffer", &self.framebuffer_tag())
.field("module_align", &self.module_align_tag())
.field("efi_boot_services", &self.efi_boot_services_tag())
.field("relocatable", &self.relocatable_tag())
.field("tag_headers", &DebugTagHeaders(self.iter()))
.finish()
}
}

/// Formats the on-wire header tag sequence without dumping tag payloads.
struct DebugTagHeaders<'a>(TagIter<'a>);

impl Debug for DebugTagHeaders<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_list()
.entries(self.0.clone().map(|tag| tag.header()))
.finish()
}
}

/// Errors that occur when a chunk of memory can't be parsed as
/// [`Multiboot2Header`].
/// [`Header`].
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Error)]
pub enum LoadError {
/// The provided checksum does not match the expected value.
Expand All @@ -318,14 +340,17 @@ pub enum LoadError {
/// Missing mandatory end tag.
#[error("missing mandatory end tag")]
NoEndTag,
/// The provided memory can't be parsed as [`Multiboot2Header`].
/// The provided memory can't be parsed as a complete [`Header`].
/// See [`MemoryError`].
#[error("memory can't be parsed as multiboot2 header")]
Memory(#[source] MemoryError),
}

/// The "basic" Multiboot2 header. This means only the properties, that are known during
/// compile time. All other information are derived during runtime from the size property.
/// The fixed prefix of a Multiboot2 header.
///
/// This contains only fields with a compile-time-known layout. It is followed
/// by a dynamically sized sequence of header tags, so it is not a complete
/// Multiboot2 header. Use [`Header`] to parse the complete header.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C, align(8))]
pub struct Multiboot2BasicHeader {
Expand Down Expand Up @@ -401,7 +426,7 @@ impl Multiboot2BasicHeader {
}
}

impl Header for Multiboot2BasicHeader {
impl DynSizedHeader for Multiboot2BasicHeader {
fn total_size(&self) -> usize {
self.length as usize
}
Expand All @@ -414,7 +439,7 @@ impl Header for Multiboot2BasicHeader {

impl Debug for Multiboot2BasicHeader {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Multiboot2Header")
f.debug_struct("Multiboot2BasicHeader")
.field("header_magic", &{ self.header_magic })
.field("arch", &{ self.arch })
.field("length", &{ self.length })
Expand All @@ -426,9 +451,7 @@ impl Debug for Multiboot2BasicHeader {

#[cfg(test)]
mod tests {
use crate::{
HeaderTagISA, HeaderTagType, LoadError, MAGIC, Multiboot2BasicHeader, Multiboot2Header,
};
use crate::{Header, HeaderTagISA, HeaderTagType, LoadError, MAGIC, Multiboot2BasicHeader};
use core::borrow::Borrow;
use multiboot2_common::MemoryError;
use multiboot2_common::test_utils::AlignedBytes;
Expand Down Expand Up @@ -462,7 +485,7 @@ mod tests {
let bytes = AlignedBytes::new([0; 16]);

assert_eq!(
Multiboot2Header::find_header(bytes.borrow()),
Header::find_header(bytes.borrow()),
Err(LoadError::MagicNotFound)
);
}
Expand All @@ -474,7 +497,7 @@ mod tests {
bytes.0[8..12].copy_from_slice(&32_u32.to_le_bytes());

assert_eq!(
Multiboot2Header::find_header(bytes.borrow()),
Header::find_header(bytes.borrow()),
Err(LoadError::Memory(MemoryError::InvalidReportedTotalSize(
32, 16
)))
Expand All @@ -486,7 +509,7 @@ mod tests {
let mut bytes = AlignedBytes::new([0; 9000]);
write_minimal_valid_header_tag(&mut bytes.0[8192..]);

let (_header, offset) = Multiboot2Header::find_header(bytes.borrow()).unwrap();
let (_header, offset) = Header::find_header(bytes.borrow()).unwrap();
assert_eq!(offset, 8192);
}

Expand All @@ -497,7 +520,7 @@ mod tests {
bytes.0[4..8].copy_from_slice(&MAGIC.to_le_bytes());
write_minimal_valid_header_tag(&mut bytes.0[8..]);

let (_header, offset) = Multiboot2Header::find_header(bytes.borrow()).unwrap();
let (_header, offset) = Header::find_header(bytes.borrow()).unwrap();
assert_eq!(offset, 8);
}

Expand All @@ -508,9 +531,11 @@ mod tests {

// SAFETY: The test buffer is aligned and contains a valid
// header layout.
let header = unsafe { Multiboot2Header::load(bytes.as_ptr().cast()) };
let header = unsafe { Header::load(bytes.as_ptr().cast()) }.unwrap();

assert!(header.is_ok());
let debug = format!("{header:?}");
assert!(debug.contains("tag_headers"));
assert!(debug.contains("End"));
}

#[test]
Expand All @@ -523,7 +548,7 @@ mod tests {

// SAFETY: The test buffer is aligned and contains a valid
// header layout.
let header = unsafe { Multiboot2Header::load(bytes.as_ptr().cast()) };
let header = unsafe { Header::load(bytes.as_ptr().cast()) };

assert!(matches!(header, Err(LoadError::NoEndTag)));
}
Expand All @@ -541,7 +566,7 @@ mod tests {

// SAFETY: The test buffer is aligned and contains a valid
// header layout.
let header = unsafe { Multiboot2Header::load(bytes.as_ptr().cast()) };
let header = unsafe { Header::load(bytes.as_ptr().cast()) };

assert_eq!(
header,
Expand Down
4 changes: 2 additions & 2 deletions multiboot2-header/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
//! ## Example: Parsing a Header
//!
//! ```no_run
//! use multiboot2_header::Multiboot2Header;
//! use multiboot2_header::Header;
//!
//! let ptr = 0x1337_0000 as *const u8 /* use real ptr here */;
//! let mb2_hdr = unsafe { Multiboot2Header::load(ptr.cast()) }.unwrap();
//! let mb2_hdr = unsafe { Header::load(ptr.cast()) }.unwrap();
//! for _tag in mb2_hdr.iter() {
//! //
//! }
Expand Down
1 change: 1 addition & 0 deletions multiboot2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- Expanded `BootInformation` debug output with tag headers.
- Standardized bootloader terminology.
- Corrected README and API documentation.

Expand Down
14 changes: 13 additions & 1 deletion multiboot2/src/boot_information.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ impl<'a> BootInformation<'a> {

impl fmt::Debug for BootInformation<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut debug = f.debug_struct("Multiboot2BootInformation");
let mut debug = f.debug_struct("BootInformation");
debug
.field("start_address", &self.start_address())
.field("end_address", &self.end_address())
Expand Down Expand Up @@ -462,6 +462,18 @@ impl fmt::Debug for BootInformation<'_> {
})
.count()
})
.field("tag_headers", &DebugTagHeaders(self.tags()))
.finish()
}
}

/// Formats the on-wire boot information tag sequence without dumping payloads.
struct DebugTagHeaders<'a>(TagIter<'a>);

impl fmt::Debug for DebugTagHeaders<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list()
.entries(self.0.clone().map(|tag| tag.header()))
.finish()
}
}
5 changes: 5 additions & 0 deletions multiboot2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,11 @@ mod tests {
.expect("must be valid utf8")
);
assert!(bi.command_line_tag().is_none());

let debug = format!("{bi:?}");
assert!(debug.contains("tag_headers"));
assert!(debug.contains("BootLoaderName"));
assert!(debug.contains("End"));
}

#[test]
Expand Down