From 56683111f495f16483a93e3887063945d1a8de18 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Thu, 13 Aug 2026 15:29:04 +0200 Subject: [PATCH 1/5] multiboot2-header: streamline main type names --- .../bins/multiboot2_chainloader/src/loader.rs | 2 +- multiboot2-header/CHANGELOG.md | 2 + multiboot2-header/README.md | 4 +- multiboot2-header/examples/minimal.rs | 6 +- multiboot2-header/src/builder.rs | 5 +- multiboot2-header/src/header.rs | 61 ++++++++++--------- multiboot2-header/src/lib.rs | 4 +- 7 files changed, 44 insertions(+), 40 deletions(-) diff --git a/integration-test/bins/multiboot2_chainloader/src/loader.rs b/integration-test/bins/multiboot2_chainloader/src/loader.rs index 8e5ef010..05ea1f4e 100644 --- a/integration-test/bins/multiboot2_chainloader/src/loader.rs +++ b/integration-test/bins/multiboot2_chainloader/src/loader.rs @@ -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:#?}"); } diff --git a/multiboot2-header/CHANGELOG.md b/multiboot2-header/CHANGELOG.md index f9fe3271..41c848a5 100644 --- a/multiboot2-header/CHANGELOG.md +++ b/multiboot2-header/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- **Breaking:** Renamed `multiboot2_header::Multiboot2Header` to + `multiboot2_header::Header`. - Standardized bootloader terminology. - Corrected the README example and API documentation. diff --git a/multiboot2-header/README.md b/multiboot2-header/README.md index 452f46ce..dde1d981 100644 --- a/multiboot2-header/README.md +++ b/multiboot2-header/README.md @@ -42,7 +42,7 @@ multiboot2-header = "" ```rust use multiboot2_header::{ Builder, HeaderTagFlag, HeaderTagISA, InformationRequestHeaderTag, - MaybeDynSized, MbiTagType, Multiboot2Header, RelocatableHeaderTag, + Header, MaybeDynSized, MbiTagType, RelocatableHeaderTag, RelocatableHeaderTagPreference, }; @@ -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:#?}"); } ``` diff --git a/multiboot2-header/examples/minimal.rs b/multiboot2-header/examples/minimal.rs index b5ffc241..b9c9032a 100644 --- a/multiboot2-header/examples/minimal.rs +++ b/multiboot2-header/examples/minimal.rs @@ -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. @@ -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:#?}"); } diff --git a/multiboot2-header/src/builder.rs b/multiboot2-header/src/builder.rs index d0fae381..405003ed 100644 --- a/multiboot2-header/src/builder.rs +++ b/multiboot2-header/src/builder.rs @@ -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() { @@ -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(())); diff --git a/multiboot2-header/src/header.rs b/multiboot2-header/src/header.rs index 64551320..28444daf 100644 --- a/multiboot2-header/src/header.rs +++ b/multiboot2-header/src/header.rs @@ -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); +pub struct Header<'a>(&'a DynSizedStructure); -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: @@ -292,9 +294,9 @@ 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()) @@ -306,7 +308,7 @@ impl Debug for Multiboot2Header<'_> { } /// 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. @@ -318,14 +320,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 { @@ -401,7 +406,7 @@ impl Multiboot2BasicHeader { } } -impl Header for Multiboot2BasicHeader { +impl DynSizedHeader for Multiboot2BasicHeader { fn total_size(&self) -> usize { self.length as usize } @@ -414,7 +419,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 }) @@ -426,9 +431,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; @@ -462,7 +465,7 @@ mod tests { let bytes = AlignedBytes::new([0; 16]); assert_eq!( - Multiboot2Header::find_header(bytes.borrow()), + Header::find_header(bytes.borrow()), Err(LoadError::MagicNotFound) ); } @@ -474,7 +477,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 ))) @@ -486,7 +489,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); } @@ -497,7 +500,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); } @@ -508,7 +511,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!(header.is_ok()); } @@ -523,7 +526,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))); } @@ -541,7 +544,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, diff --git a/multiboot2-header/src/lib.rs b/multiboot2-header/src/lib.rs index 0d396d20..2cb76cf3 100644 --- a/multiboot2-header/src/lib.rs +++ b/multiboot2-header/src/lib.rs @@ -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() { //! // //! } From d34697a8cd0063e51fb8f0b6d59727019cf67291 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Thu, 13 Aug 2026 15:33:08 +0200 Subject: [PATCH 2/5] multiboot2-header: clarify builder rename in changelog --- multiboot2-header/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/multiboot2-header/CHANGELOG.md b/multiboot2-header/CHANGELOG.md index 41c848a5..b9442fff 100644 --- a/multiboot2-header/CHANGELOG.md +++ b/multiboot2-header/CHANGELOG.md @@ -46,8 +46,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 From 21588d86736cf5a50ad7e34d300b13e06e4b57bd Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Thu, 13 Aug 2026 15:35:38 +0200 Subject: [PATCH 3/5] multiboot2: streamline name --- multiboot2/src/boot_information.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs index 1ab8fbf1..5e8a7deb 100644 --- a/multiboot2/src/boot_information.rs +++ b/multiboot2/src/boot_information.rs @@ -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()) From c2d225edea767509b3de649e3209282c07280e14 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Thu, 13 Aug 2026 15:56:49 +0200 Subject: [PATCH 4/5] treewide: expand Multiboot2 debug output --- multiboot2-header/CHANGELOG.md | 1 + multiboot2-header/src/header.rs | 30 ++++++++++++++++++++++++++---- multiboot2/CHANGELOG.md | 1 + multiboot2/src/boot_information.rs | 12 ++++++++++++ multiboot2/src/lib.rs | 5 +++++ 5 files changed, 45 insertions(+), 4 deletions(-) diff --git a/multiboot2-header/CHANGELOG.md b/multiboot2-header/CHANGELOG.md index b9442fff..4b715b91 100644 --- a/multiboot2-header/CHANGELOG.md +++ b/multiboot2-header/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Expanded `Header` debug output with parsed tags and tag headers. - **Breaking:** Renamed `multiboot2_header::Multiboot2Header` to `multiboot2_header::Header`. - Standardized bootloader terminology. diff --git a/multiboot2-header/src/header.rs b/multiboot2-header/src/header.rs index 28444daf..fee0f24b 100644 --- a/multiboot2-header/src/header.rs +++ b/multiboot2-header/src/header.rs @@ -301,8 +301,28 @@ impl Debug for Header<'_> { .field("arch", &self.arch()) .field("length", &self.length()) .field("checksum", &self.checksum()) - // TODO better debug impl - .field("tags", &"") + .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() } } @@ -511,9 +531,11 @@ mod tests { // SAFETY: The test buffer is aligned and contains a valid // header layout. - let header = unsafe { Header::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] diff --git a/multiboot2/CHANGELOG.md b/multiboot2/CHANGELOG.md index 538efba2..50a7dbae 100644 --- a/multiboot2/CHANGELOG.md +++ b/multiboot2/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Expanded `BootInformation` debug output with tag headers. - Standardized bootloader terminology. - Corrected README and API documentation. diff --git a/multiboot2/src/boot_information.rs b/multiboot2/src/boot_information.rs index 5e8a7deb..fb0f5450 100644 --- a/multiboot2/src/boot_information.rs +++ b/multiboot2/src/boot_information.rs @@ -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() } } diff --git a/multiboot2/src/lib.rs b/multiboot2/src/lib.rs index 86522d88..0c29060f 100644 --- a/multiboot2/src/lib.rs +++ b/multiboot2/src/lib.rs @@ -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] From 228d4334a75a3f45a3d710e75c643fbe9a236cb4 Mon Sep 17 00:00:00 2001 From: Philipp Schuster Date: Thu, 13 Aug 2026 16:49:17 +0200 Subject: [PATCH 5/5] integration-test: fix Failed since we have the new debug output where we walk the chain of tags. --- .../bins/multiboot2_payload/src/multiboot2_header.S | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/integration-test/bins/multiboot2_payload/src/multiboot2_header.S b/integration-test/bins/multiboot2_payload/src/multiboot2_header.S index 84ac5773..6c1ba784 100644 --- a/integration-test/bins/multiboot2_payload/src/multiboot2_header.S +++ b/integration-test/bins/multiboot2_payload/src/multiboot2_header.S @@ -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