Browse Source

multiboot2-header: refactor to use common abstractions

This especially significantly changes the builder. The normal public API however
is only slightly affected.
Philipp Schuster 10 tháng trước cách đây
mục cha
commit
bbb28ebf7c

+ 3 - 0
multiboot2-header/Cargo.toml

@@ -41,7 +41,10 @@ unstable = []
 
 [dependencies]
 derive_more.workspace = true
+log.workspace = true
+ptr_meta.workspace = true
 multiboot2 = { version = "0.21.0", default-features = false }
+multiboot2-common = "0.1.0"
 
 [package.metadata.docs.rs]
 all-features = true

+ 18 - 11
multiboot2-header/examples/minimal.rs

@@ -1,14 +1,16 @@
-use multiboot2_header::builder::{HeaderBuilder, InformationRequestHeaderTagBuilder};
+use multiboot2_common::MaybeDynSized;
+use multiboot2_header::Builder;
 use multiboot2_header::{
-    HeaderTagFlag, HeaderTagISA, MbiTagType, Multiboot2Header, RelocatableHeaderTag,
-    RelocatableHeaderTagPreference,
+    HeaderTagFlag, HeaderTagISA, InformationRequestHeaderTag, MbiTagType, Multiboot2Header,
+    RelocatableHeaderTag, RelocatableHeaderTagPreference,
 };
 
 /// Small example that creates a Multiboot2 header and parses it afterwards.
 fn main() {
-    // We create a Multiboot2 header during runtime here. A practical example is that your
-    // program gets the header from a file and parses it afterwards.
-    let mb2_hdr_bytes = HeaderBuilder::new(HeaderTagISA::I386)
+    // We create a Multiboot2 header during runtime here. A more practical
+    // example, however, would be that you parse the header from kernel binary
+    // at runtime.
+    let mb2_hdr_bytes = Builder::new(HeaderTagISA::I386)
         .relocatable_tag(RelocatableHeaderTag::new(
             HeaderTagFlag::Required,
             0x1337,
@@ -16,13 +18,18 @@ fn main() {
             4096,
             RelocatableHeaderTagPreference::None,
         ))
-        .information_request_tag(
-            InformationRequestHeaderTagBuilder::new(HeaderTagFlag::Required)
-                .add_irs(&[MbiTagType::Cmdline, MbiTagType::BootLoaderName]),
-        )
+        .information_request_tag(InformationRequestHeaderTag::new(
+            HeaderTagFlag::Required,
+            &[
+                MbiTagType::Cmdline.into(),
+                MbiTagType::BootLoaderName.into(),
+            ],
+        ))
         .build();
 
     // Cast bytes in vector to Multiboot2 information structure
-    let mb2_hdr = unsafe { Multiboot2Header::load(mb2_hdr_bytes.as_ptr().cast()) };
+    let ptr = mb2_hdr_bytes.as_bytes().as_ptr();
+    let mb2_hdr = unsafe { Multiboot2Header::load(ptr.cast()) };
+    let mb2_hdr = mb2_hdr.unwrap();
     println!("{:#?}", mb2_hdr);
 }

+ 15 - 1
multiboot2-header/src/address.rs

@@ -1,12 +1,13 @@
 use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
 use core::mem::size_of;
+use multiboot2_common::{MaybeDynSized, Tag};
 
 /// This information does not need to be provided if the kernel image is in ELF
 /// format, but it must be provided if the image is in a.out format or in some
 /// other format. Required for legacy boot (BIOS).
 /// Determines load addresses.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[repr(C)]
+#[repr(C, align(8))]
 pub struct AddressHeaderTag {
     header: HeaderTagHeader,
     /// Contains the address corresponding to the beginning of the Multiboot2 header — the physical memory location at which the magic value is supposed to be loaded. This field serves to synchronize the mapping between OS image offsets and physical memory addresses.
@@ -84,6 +85,19 @@ impl AddressHeaderTag {
     }
 }
 
+impl MaybeDynSized for AddressHeaderTag {
+    type Header = HeaderTagHeader;
+
+    const BASE_SIZE: usize = size_of::<Self>();
+
+    fn dst_len(_header: &Self::Header) -> Self::Metadata {}
+}
+
+impl Tag for AddressHeaderTag {
+    type IDType = HeaderTagType;
+    const ID: HeaderTagType = HeaderTagType::Address;
+}
+
 #[cfg(test)]
 mod tests {
     use crate::AddressHeaderTag;

+ 238 - 0
multiboot2-header/src/builder.rs

@@ -0,0 +1,238 @@
+//! Exports a builder [`Builder`].
+
+use crate::{
+    AddressHeaderTag, ConsoleHeaderTag, EfiBootServiceHeaderTag, EntryAddressHeaderTag,
+    EntryEfi32HeaderTag, EntryEfi64HeaderTag, FramebufferHeaderTag, HeaderTagISA,
+    InformationRequestHeaderTag, ModuleAlignHeaderTag, Multiboot2BasicHeader, RelocatableHeaderTag,
+};
+use alloc::boxed::Box;
+use alloc::vec::Vec;
+use multiboot2_common::{new_boxed, DynSizedStructure, MaybeDynSized};
+
+/// Builder for a Multiboot2 header information.
+#[derive(Debug)]
+pub struct Builder {
+    arch: HeaderTagISA,
+    information_request_tag: Option<Box<InformationRequestHeaderTag>>,
+    address_tag: Option<AddressHeaderTag>,
+    entry_tag: Option<EntryAddressHeaderTag>,
+    console_tag: Option<ConsoleHeaderTag>,
+    framebuffer_tag: Option<FramebufferHeaderTag>,
+    module_align_tag: Option<ModuleAlignHeaderTag>,
+    efi_bs_tag: Option<EfiBootServiceHeaderTag>,
+    efi_32_tag: Option<EntryEfi32HeaderTag>,
+    efi_64_tag: Option<EntryEfi64HeaderTag>,
+    relocatable_tag: Option<RelocatableHeaderTag>,
+    // TODO add support for custom tags once someone requests it.
+}
+
+impl Builder {
+    /// Set the [`RelocatableHeaderTag`] tag.
+    #[must_use]
+    pub const fn new(arch: HeaderTagISA) -> Self {
+        Self {
+            arch,
+            information_request_tag: None,
+            address_tag: None,
+            entry_tag: None,
+            console_tag: None,
+            framebuffer_tag: None,
+            module_align_tag: None,
+            efi_bs_tag: None,
+            efi_32_tag: None,
+            efi_64_tag: None,
+            relocatable_tag: None,
+        }
+    }
+
+    /// Set the [`InformationRequestHeaderTag`] tag.
+    #[must_use]
+    pub fn information_request_tag(
+        mut self,
+        information_request_tag: Box<InformationRequestHeaderTag>,
+    ) -> Self {
+        self.information_request_tag = Some(information_request_tag);
+        self
+    }
+
+    /// Set the [`AddressHeaderTag`] tag.
+    #[must_use]
+    pub const fn address_tag(mut self, address_tag: AddressHeaderTag) -> Self {
+        self.address_tag = Some(address_tag);
+        self
+    }
+
+    /// Set the [`EntryAddressHeaderTag`] tag.
+    #[must_use]
+    pub const fn entry_tag(mut self, entry_tag: EntryAddressHeaderTag) -> Self {
+        self.entry_tag = Some(entry_tag);
+        self
+    }
+
+    /// Set the [`ConsoleHeaderTag`] tag.
+    #[must_use]
+    pub const fn console_tag(mut self, console_tag: ConsoleHeaderTag) -> Self {
+        self.console_tag = Some(console_tag);
+        self
+    }
+
+    /// Set the [`FramebufferHeaderTag`] tag.
+    #[must_use]
+    pub const fn framebuffer_tag(mut self, framebuffer_tag: FramebufferHeaderTag) -> Self {
+        self.framebuffer_tag = Some(framebuffer_tag);
+        self
+    }
+
+    /// Set the [`ModuleAlignHeaderTag`] tag.
+    #[must_use]
+    pub const fn module_align_tag(mut self, module_align_tag: ModuleAlignHeaderTag) -> Self {
+        self.module_align_tag = Some(module_align_tag);
+        self
+    }
+
+    /// Set the [`EfiBootServiceHeaderTag`] tag.
+    #[must_use]
+    pub const fn efi_bs_tag(mut self, efi_bs_tag: EfiBootServiceHeaderTag) -> Self {
+        self.efi_bs_tag = Some(efi_bs_tag);
+        self
+    }
+
+    /// Set the [`EntryEfi32HeaderTag`] tag.
+    #[must_use]
+    pub const fn efi_32_tag(mut self, efi_32_tag: EntryEfi32HeaderTag) -> Self {
+        self.efi_32_tag = Some(efi_32_tag);
+        self
+    }
+
+    /// Set the [`EntryEfi64HeaderTag`] tag.
+    #[must_use]
+    pub const fn efi_64_tag(mut self, efi_64_tag: EntryEfi64HeaderTag) -> Self {
+        self.efi_64_tag = Some(efi_64_tag);
+        self
+    }
+
+    /// Set the [`RelocatableHeaderTag`] tag.
+    #[must_use]
+    pub const fn relocatable_tag(mut self, relocatable_tag: RelocatableHeaderTag) -> Self {
+        self.relocatable_tag = Some(relocatable_tag);
+        self
+    }
+
+    /// Returns properly aligned bytes on the heap representing a valid
+    /// Multiboot2 header structure.
+    #[must_use]
+    pub fn build(self) -> Box<DynSizedStructure<Multiboot2BasicHeader>> {
+        let header = Multiboot2BasicHeader::new(self.arch, 0);
+        let mut byte_refs = Vec::new();
+        if let Some(tag) = self.information_request_tag.as_ref() {
+            byte_refs.push(tag.as_bytes().as_ref());
+        }
+        if let Some(tag) = self.address_tag.as_ref() {
+            byte_refs.push(tag.as_bytes().as_ref());
+        }
+        if let Some(tag) = self.entry_tag.as_ref() {
+            byte_refs.push(tag.as_bytes().as_ref());
+        }
+        if let Some(tag) = self.console_tag.as_ref() {
+            byte_refs.push(tag.as_bytes().as_ref());
+        }
+        if let Some(tag) = self.framebuffer_tag.as_ref() {
+            byte_refs.push(tag.as_bytes().as_ref());
+        }
+        if let Some(tag) = self.module_align_tag.as_ref() {
+            byte_refs.push(tag.as_bytes().as_ref());
+        }
+        if let Some(tag) = self.efi_bs_tag.as_ref() {
+            byte_refs.push(tag.as_bytes().as_ref());
+        }
+        if let Some(tag) = self.efi_32_tag.as_ref() {
+            byte_refs.push(tag.as_bytes().as_ref());
+        }
+        if let Some(tag) = self.efi_64_tag.as_ref() {
+            byte_refs.push(tag.as_bytes().as_ref());
+        }
+        if let Some(tag) = self.relocatable_tag.as_ref() {
+            byte_refs.push(tag.as_bytes().as_ref());
+        }
+        // TODO add support for custom tags once someone requests it.
+        new_boxed(header, byte_refs.as_slice())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::ConsoleHeaderTagFlags::ConsoleRequired;
+    use crate::HeaderTagFlag::{Optional, Required};
+    use crate::RelocatableHeaderTagPreference::High;
+    use crate::{MbiTagType, Multiboot2Header};
+
+    #[test]
+    fn build_and_parse() {
+        let builder = Builder::new(HeaderTagISA::I386)
+            .information_request_tag(InformationRequestHeaderTag::new(
+                Optional,
+                &[
+                    MbiTagType::Cmdline.into(),
+                    MbiTagType::BootLoaderName.into(),
+                    MbiTagType::Module.into(),
+                    MbiTagType::BasicMeminfo.into(),
+                    MbiTagType::Bootdev.into(),
+                    MbiTagType::Mmap.into(),
+                    MbiTagType::Vbe.into(),
+                    MbiTagType::Framebuffer.into(),
+                    MbiTagType::ElfSections.into(),
+                    MbiTagType::Apm.into(),
+                    MbiTagType::Efi32.into(),
+                    MbiTagType::Efi64.into(),
+                    MbiTagType::Smbios.into(),
+                    MbiTagType::AcpiV1.into(),
+                    MbiTagType::AcpiV2.into(),
+                    MbiTagType::Network.into(),
+                    MbiTagType::EfiMmap.into(),
+                    MbiTagType::EfiBs.into(),
+                    MbiTagType::Efi32Ih.into(),
+                    MbiTagType::Efi64Ih.into(),
+                    MbiTagType::LoadBaseAddr.into(),
+                    MbiTagType::Custom(0x1337).into(),
+                ],
+            ))
+            .address_tag(AddressHeaderTag::new(
+                Required, 0x1000, 0x2000, 0x3000, 0x4000,
+            ))
+            .entry_tag(EntryAddressHeaderTag::new(Required, 0x5000))
+            .console_tag(ConsoleHeaderTag::new(Required, ConsoleRequired))
+            .framebuffer_tag(FramebufferHeaderTag::new(Optional, 720, 1024, 8))
+            .module_align_tag(ModuleAlignHeaderTag::new(Required))
+            .efi_bs_tag(EfiBootServiceHeaderTag::new(Optional))
+            .efi_32_tag(EntryEfi32HeaderTag::new(Required, 0x7000))
+            .efi_64_tag(EntryEfi64HeaderTag::new(Required, 0x8000))
+            .relocatable_tag(RelocatableHeaderTag::new(
+                Required, 0x9000, 0x10000, 4096, High,
+            ));
+
+        let structure = builder.build();
+        let header =
+            unsafe { Multiboot2Header::load(structure.as_bytes().as_ref().as_ptr().cast()) }
+                .unwrap();
+
+        assert!(header.verify_checksum());
+
+        for tag in header.iter() {
+            dbg!(tag);
+        }
+
+        dbg!(header.arch());
+        dbg!(header.checksum());
+        dbg!(header.information_request_tag());
+        dbg!(header.address_tag());
+        dbg!(header.entry_address_tag());
+        dbg!(header.console_flags_tag());
+        dbg!(header.framebuffer_tag());
+        dbg!(header.module_align_tag());
+        dbg!(header.efi_boot_services_tag());
+        dbg!(header.entry_address_efi32_tag());
+        dbg!(header.entry_address_efi64_tag());
+        dbg!(header.relocatable_tag());
+    }
+}

+ 0 - 425
multiboot2-header/src/builder/header.rs

@@ -1,425 +0,0 @@
-//! Exports item [`HeaderBuilder`].
-
-use crate::builder::information_request::InformationRequestHeaderTagBuilder;
-use crate::builder::traits::StructAsBytes;
-use crate::HeaderTagISA;
-use crate::{
-    AddressHeaderTag, ConsoleHeaderTag, EfiBootServiceHeaderTag, EndHeaderTag,
-    EntryAddressHeaderTag, EntryEfi32HeaderTag, EntryEfi64HeaderTag, FramebufferHeaderTag,
-    ModuleAlignHeaderTag, Multiboot2BasicHeader, RelocatableHeaderTag,
-};
-use alloc::vec::Vec;
-use core::mem::size_of;
-use core::ops::Deref;
-
-/// Holds the raw bytes of a boot information built with [`HeaderBuilder`]
-/// on the heap. The bytes returned by [`HeaderBytes::as_bytes`] are
-/// guaranteed to be properly aligned.
-#[derive(Clone, Debug)]
-pub struct HeaderBytes {
-    // Offset into the bytes where the header starts. This is necessary to
-    // guarantee alignment at the moment.
-    offset: usize,
-    structure_len: usize,
-    bytes: Vec<u8>,
-}
-
-impl HeaderBytes {
-    /// Returns the bytes. They are guaranteed to be correctly aligned.
-    pub fn as_bytes(&self) -> &[u8] {
-        let slice = &self.bytes[self.offset..self.offset + self.structure_len];
-        // At this point, the alignment is guaranteed. If not, something is
-        // broken fundamentally.
-        assert_eq!(slice.as_ptr().align_offset(8), 0);
-        slice
-    }
-}
-
-impl Deref for HeaderBytes {
-    type Target = [u8];
-
-    fn deref(&self) -> &Self::Target {
-        self.as_bytes()
-    }
-}
-
-/// Builder to construct a valid Multiboot2 header dynamically at runtime.
-/// The tags will appear in the order of their corresponding enumeration,
-/// except for the END tag.
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct HeaderBuilder {
-    arch: HeaderTagISA,
-    // first
-    information_request_tag: Option<InformationRequestHeaderTagBuilder>,
-    // second
-    address_tag: Option<AddressHeaderTag>,
-    // third
-    entry_tag: Option<EntryAddressHeaderTag>,
-    // fourth
-    console_tag: Option<ConsoleHeaderTag>,
-    // fifth
-    framebuffer_tag: Option<FramebufferHeaderTag>,
-    // sixth
-    module_align_tag: Option<ModuleAlignHeaderTag>,
-    // seventh
-    efi_bs_tag: Option<EfiBootServiceHeaderTag>,
-    // eighth
-    efi_32_tag: Option<EntryEfi32HeaderTag>,
-    // ninth
-    efi_64_tag: Option<EntryEfi64HeaderTag>,
-    // tenth (last)
-    relocatable_tag: Option<RelocatableHeaderTag>,
-}
-
-impl HeaderBuilder {
-    /// Creates a new builder.
-    #[must_use]
-    pub const fn new(arch: HeaderTagISA) -> Self {
-        Self {
-            arch,
-            information_request_tag: None,
-            address_tag: None,
-            entry_tag: None,
-            console_tag: None,
-            framebuffer_tag: None,
-            module_align_tag: None,
-            efi_bs_tag: None,
-            efi_32_tag: None,
-            efi_64_tag: None,
-            relocatable_tag: None,
-        }
-    }
-
-    /// Returns the size, if the value is a multiple of 8 or returns
-    /// the next number that is a multiple of 8. With this, one can
-    /// easily calculate the size of a Multiboot2 header, where
-    /// all the tags are 8-byte aligned.
-    const fn size_or_up_aligned(size: usize) -> usize {
-        (size + 7) & !7
-    }
-
-    /// Returns the expected length of the Multiboot2 header, when the
-    /// [`Self::build`]-method gets called.
-    #[must_use]
-    pub fn expected_len(&self) -> usize {
-        let base_len = size_of::<Multiboot2BasicHeader>();
-        // size_or_up_aligned not required, because basic header length is 16 and the
-        // begin is 8 byte aligned => first tag automatically 8 byte aligned
-        let mut len = Self::size_or_up_aligned(base_len);
-        if let Some(tag_builder) = self.information_request_tag.as_ref() {
-            // we use size_or_up_aligned, because each tag will start at an 8 byte aligned address.
-            // Attention: expected len from builder, not the size of the builder itself!
-            len += Self::size_or_up_aligned(tag_builder.expected_len())
-        }
-        if self.address_tag.is_some() {
-            // we use size_or_up_aligned, because each tag will start at an 8 byte aligned address
-            len += Self::size_or_up_aligned(size_of::<AddressHeaderTag>())
-        }
-        if self.entry_tag.is_some() {
-            len += Self::size_or_up_aligned(size_of::<EntryAddressHeaderTag>())
-        }
-        if self.console_tag.is_some() {
-            len += Self::size_or_up_aligned(size_of::<ConsoleHeaderTag>())
-        }
-        if self.framebuffer_tag.is_some() {
-            len += Self::size_or_up_aligned(size_of::<FramebufferHeaderTag>())
-        }
-        if self.module_align_tag.is_some() {
-            len += Self::size_or_up_aligned(size_of::<ModuleAlignHeaderTag>())
-        }
-        if self.efi_bs_tag.is_some() {
-            len += Self::size_or_up_aligned(size_of::<EfiBootServiceHeaderTag>())
-        }
-        if self.efi_32_tag.is_some() {
-            len += Self::size_or_up_aligned(size_of::<EntryEfi32HeaderTag>())
-        }
-        if self.efi_64_tag.is_some() {
-            len += Self::size_or_up_aligned(size_of::<EntryEfi64HeaderTag>())
-        }
-        if self.relocatable_tag.is_some() {
-            len += Self::size_or_up_aligned(size_of::<RelocatableHeaderTag>())
-        }
-        // only here size_or_up_aligned is not important, because it is the last tag
-        len += size_of::<EndHeaderTag>();
-        len
-    }
-
-    /// Adds the bytes of a tag to the final Multiboot2 header byte vector.
-    fn build_add_bytes(dest: &mut Vec<u8>, source: &[u8], is_end_tag: bool) {
-        let vec_next_write_ptr = unsafe { dest.as_ptr().add(dest.len()) };
-        // At this point, the alignment is guaranteed. If not, something is
-        // broken fundamentally.
-        assert_eq!(vec_next_write_ptr.align_offset(8), 0);
-
-        dest.extend(source);
-        if !is_end_tag {
-            let size = source.len();
-            let size_to_8_align = Self::size_or_up_aligned(size);
-            let size_to_8_align_diff = size_to_8_align - size;
-            // fill zeroes so that next data block is 8-byte aligned
-            dest.extend([0].repeat(size_to_8_align_diff));
-        }
-    }
-
-    /// Constructs the bytes for a valid Multiboot2 header with the given properties.
-    #[must_use]
-    pub fn build(self) -> HeaderBytes {
-        const ALIGN: usize = 8;
-
-        // PHASE 1/2: Prepare Vector
-
-        // We allocate more than necessary so that we can ensure an correct
-        // alignment within this data.
-        let expected_len = self.expected_len();
-        let alloc_len = expected_len + 7;
-        let mut bytes = Vec::<u8>::with_capacity(alloc_len);
-        // Pointer to check that no relocation happened.
-        let alloc_ptr = bytes.as_ptr();
-
-        // As long as there is no nice way in stable Rust to guarantee the
-        // alignment of a vector, I add zero bytes at the beginning and the
-        // header might not start at the start of the allocation.
-        //
-        // Unfortunately, it is not possible to reliably test this in a unit
-        // test as long as the allocator_api feature is not stable.
-        // Due to my manual testing, however, it works.
-        let offset = bytes.as_ptr().align_offset(ALIGN);
-        bytes.extend([0].repeat(offset));
-
-        // -----------------------------------------------
-        // PHASE 2/2: Add Tags
-        self.build_add_tags(&mut bytes);
-
-        assert_eq!(
-            alloc_ptr,
-            bytes.as_ptr(),
-            "Vector was reallocated. Alignment of header probably broken!"
-        );
-        assert_eq!(
-            bytes[0..offset].iter().sum::<u8>(),
-            0,
-            "The offset to alignment area should be zero."
-        );
-
-        HeaderBytes {
-            offset,
-            bytes,
-            structure_len: expected_len,
-        }
-    }
-
-    /// Helper method that adds all the tags to the given vector.
-    fn build_add_tags(&self, bytes: &mut Vec<u8>) {
-        Self::build_add_bytes(
-            bytes,
-            // important that we write the correct expected length into the header!
-            &Multiboot2BasicHeader::new(self.arch, self.expected_len() as u32).struct_as_bytes(),
-            false,
-        );
-
-        if let Some(irs) = self.information_request_tag.clone() {
-            Self::build_add_bytes(bytes, &irs.build(), false)
-        }
-        if let Some(tag) = self.address_tag.as_ref() {
-            Self::build_add_bytes(bytes, &tag.struct_as_bytes(), false)
-        }
-        if let Some(tag) = self.entry_tag.as_ref() {
-            Self::build_add_bytes(bytes, &tag.struct_as_bytes(), false)
-        }
-        if let Some(tag) = self.console_tag.as_ref() {
-            Self::build_add_bytes(bytes, &tag.struct_as_bytes(), false)
-        }
-        if let Some(tag) = self.framebuffer_tag.as_ref() {
-            Self::build_add_bytes(bytes, &tag.struct_as_bytes(), false)
-        }
-        if let Some(tag) = self.module_align_tag.as_ref() {
-            Self::build_add_bytes(bytes, &tag.struct_as_bytes(), false)
-        }
-        if let Some(tag) = self.efi_bs_tag.as_ref() {
-            Self::build_add_bytes(bytes, &tag.struct_as_bytes(), false)
-        }
-        if let Some(tag) = self.efi_32_tag.as_ref() {
-            Self::build_add_bytes(bytes, &tag.struct_as_bytes(), false)
-        }
-        if let Some(tag) = self.efi_64_tag.as_ref() {
-            Self::build_add_bytes(bytes, &tag.struct_as_bytes(), false)
-        }
-        if let Some(tag) = self.relocatable_tag.as_ref() {
-            Self::build_add_bytes(bytes, &tag.struct_as_bytes(), false)
-        }
-        Self::build_add_bytes(bytes, &EndHeaderTag::new().struct_as_bytes(), true);
-    }
-
-    // clippy thinks this can be a const fn but the compiler denies it
-    // #[allow(clippy::missing_const_for_fn)]
-    /// Adds information requests from the
-    /// [`InformationRequestHeaderTagBuilder`] to the builder.
-    #[must_use]
-    #[allow(clippy::missing_const_for_fn)] // only in Rust 1.70 necessary
-    pub fn information_request_tag(
-        mut self,
-        information_request_tag: InformationRequestHeaderTagBuilder,
-    ) -> Self {
-        self.information_request_tag = Some(information_request_tag);
-        self
-    }
-
-    /// Adds a [`AddressHeaderTag`] to the builder.
-    #[must_use]
-    pub const fn address_tag(mut self, address_tag: AddressHeaderTag) -> Self {
-        self.address_tag = Some(address_tag);
-        self
-    }
-
-    /// Adds a [`EntryAddressHeaderTag`] to the builder.
-    #[must_use]
-    pub const fn entry_tag(mut self, entry_tag: EntryAddressHeaderTag) -> Self {
-        self.entry_tag = Some(entry_tag);
-        self
-    }
-
-    /// Adds a [`ConsoleHeaderTag`] to the builder.
-    #[must_use]
-    pub const fn console_tag(mut self, console_tag: ConsoleHeaderTag) -> Self {
-        self.console_tag = Some(console_tag);
-        self
-    }
-
-    /// Adds a [`FramebufferHeaderTag`] to the builder.
-    #[must_use]
-    pub const fn framebuffer_tag(mut self, framebuffer_tag: FramebufferHeaderTag) -> Self {
-        self.framebuffer_tag = Some(framebuffer_tag);
-        self
-    }
-
-    /// Adds a [`ModuleAlignHeaderTag`] to the builder.
-    #[must_use]
-    pub const fn module_align_tag(mut self, module_align_tag: ModuleAlignHeaderTag) -> Self {
-        self.module_align_tag = Some(module_align_tag);
-        self
-    }
-
-    /// Adds a [`EfiBootServiceHeaderTag`] to the builder.
-    #[must_use]
-    pub const fn efi_bs_tag(mut self, efi_bs_tag: EfiBootServiceHeaderTag) -> Self {
-        self.efi_bs_tag = Some(efi_bs_tag);
-        self
-    }
-
-    /// Adds a [`EntryEfi32HeaderTag`] to the builder.
-    #[must_use]
-    pub const fn efi_32_tag(mut self, efi_32_tag: EntryEfi32HeaderTag) -> Self {
-        self.efi_32_tag = Some(efi_32_tag);
-        self
-    }
-
-    /// Adds a [`EntryEfi64HeaderTag`] to the builder.
-    #[must_use]
-    pub const fn efi_64_tag(mut self, efi_64_tag: EntryEfi64HeaderTag) -> Self {
-        self.efi_64_tag = Some(efi_64_tag);
-        self
-    }
-
-    /// Adds a [`RelocatableHeaderTag`] to the builder.
-    #[must_use]
-    pub const fn relocatable_tag(mut self, relocatable_tag: RelocatableHeaderTag) -> Self {
-        self.relocatable_tag = Some(relocatable_tag);
-        self
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::builder::header::HeaderBuilder;
-    use crate::builder::information_request::InformationRequestHeaderTagBuilder;
-    use crate::{
-        HeaderTagFlag, HeaderTagISA, MbiTagType, Multiboot2Header, RelocatableHeaderTag,
-        RelocatableHeaderTagPreference,
-    };
-
-    fn create_builder() -> HeaderBuilder {
-        let builder = HeaderBuilder::new(HeaderTagISA::I386);
-        // Multiboot2 basic header + end tag
-        let mut expected_len = 16 + 8;
-        assert_eq!(builder.expected_len(), expected_len);
-
-        // add information request tag
-        let ifr_builder =
-            InformationRequestHeaderTagBuilder::new(HeaderTagFlag::Required).add_irs(&[
-                MbiTagType::EfiMmap,
-                MbiTagType::Cmdline,
-                MbiTagType::ElfSections,
-            ]);
-        let ifr_tag_size_with_padding = ifr_builder.expected_len() + 4;
-        assert_eq!(
-            ifr_tag_size_with_padding % 8,
-            0,
-            "the length of the IFR tag with padding must be a multiple of 8"
-        );
-        expected_len += ifr_tag_size_with_padding;
-        let builder = builder.information_request_tag(ifr_builder);
-        assert_eq!(builder.expected_len(), expected_len);
-
-        let builder = builder.relocatable_tag(RelocatableHeaderTag::new(
-            HeaderTagFlag::Required,
-            0x1337,
-            0xdeadbeef,
-            4096,
-            RelocatableHeaderTagPreference::None,
-        ));
-        expected_len += 0x18;
-        assert_eq!(builder.expected_len(), expected_len);
-
-        builder
-    }
-
-    #[test]
-    fn test_size_or_up_aligned() {
-        assert_eq!(0, HeaderBuilder::size_or_up_aligned(0));
-        assert_eq!(8, HeaderBuilder::size_or_up_aligned(1));
-        assert_eq!(8, HeaderBuilder::size_or_up_aligned(8));
-        assert_eq!(16, HeaderBuilder::size_or_up_aligned(9));
-    }
-
-    /// Test of the `build` method in isolation specifically for miri to check
-    /// for memory issues.
-    #[test]
-    fn test_builder_miri() {
-        let builder = create_builder();
-        let expected_len = builder.expected_len();
-        assert_eq!(builder.build().as_bytes().len(), expected_len);
-    }
-
-    #[test]
-    #[cfg_attr(miri, ignore)]
-    fn test_builder() {
-        // Step 1/2: Build Header
-        let mb2_hdr_data = create_builder().build();
-
-        // Step 2/2: Test the built Header
-        let mb2_hdr = mb2_hdr_data.as_ptr().cast();
-        let mb2_hdr = unsafe { Multiboot2Header::load(mb2_hdr) }
-            .expect("the generated header to be loadable");
-        println!("{:#?}", mb2_hdr);
-        assert_eq!(
-            mb2_hdr.relocatable_tag().unwrap().flags(),
-            HeaderTagFlag::Required
-        );
-        assert_eq!(mb2_hdr.relocatable_tag().unwrap().min_addr(), 0x1337);
-        assert_eq!(mb2_hdr.relocatable_tag().unwrap().max_addr(), 0xdeadbeef);
-        assert_eq!(mb2_hdr.relocatable_tag().unwrap().align(), 4096);
-        assert_eq!(
-            mb2_hdr.relocatable_tag().unwrap().preference(),
-            RelocatableHeaderTagPreference::None
-        );
-
-        // Printing the header transitively ensures that a lot of stuff works.
-        println!("{:#?}", mb2_hdr);
-
-        /* you can write the binary to a file and a tool such as crate "bootinfo"
-           will be able to fully parse the MB2 header
-        let mut file = std::file::File::create("mb2_hdr.bin").unwrap();
-        use std::io::Write;
-        file.write_all(mb2_hdr_data.as_slice()).unwrap();*/
-    }
-}

+ 0 - 138
multiboot2-header/src/builder/information_request.rs

@@ -1,138 +0,0 @@
-use super::traits::StructAsBytes;
-use crate::{HeaderTagFlag, MbiTagType};
-use crate::{InformationRequestHeaderTag, MbiTagTypeId};
-use alloc::collections::BTreeSet;
-use alloc::vec::Vec;
-use core::fmt::Debug;
-use core::mem::size_of;
-use multiboot2::TagTypeId;
-
-/// Helper to build the dynamically sized [`InformationRequestHeaderTag`]
-/// at runtime. The information request tag has a dedicated builder because this way one
-/// can dynamically attach several requests to it. Otherwise, the number of requested tags
-/// must be known at compile time.
-#[derive(Clone, Debug, PartialEq, Eq)]
-#[cfg(feature = "builder")]
-pub struct InformationRequestHeaderTagBuilder {
-    flag: HeaderTagFlag,
-    // information requests (irs)
-    irs: BTreeSet<MbiTagType>,
-}
-
-#[cfg(feature = "builder")]
-impl InformationRequestHeaderTagBuilder {
-    /// New builder.
-    #[must_use]
-    pub const fn new(flag: HeaderTagFlag) -> Self {
-        Self {
-            irs: BTreeSet::new(),
-            flag,
-        }
-    }
-
-    /// Returns the expected length of the information request tag,
-    /// when the `build`-method gets called.
-    #[must_use]
-    pub fn expected_len(&self) -> usize {
-        let basic_header_size = size_of::<InformationRequestHeaderTag<0>>();
-        let req_tags_size = self.irs.len() * size_of::<MbiTagTypeId>();
-        basic_header_size + req_tags_size
-    }
-
-    /// Adds an [`MbiTagType`] to the information request.
-    #[must_use]
-    pub fn add_ir(mut self, tag: MbiTagType) -> Self {
-        self.irs.insert(tag);
-        self
-    }
-
-    /// Adds multiple [`MbiTagType`] to the information request.
-    #[must_use]
-    pub fn add_irs(mut self, tags: &[MbiTagType]) -> Self {
-        self.irs.extend(tags);
-        self
-    }
-
-    /// Builds the bytes of the dynamically sized information request header.
-    pub fn build(self) -> Vec<u8> {
-        let expected_len = self.expected_len();
-        let mut data = Vec::with_capacity(expected_len);
-
-        let basic_tag = InformationRequestHeaderTag::<0>::new(
-            self.flag,
-            [],
-            // we put the expected length here already, because in the next step we write
-            // all the tags into the byte array. We can't know this during compile time,
-            // therefore N is 0.
-            Some(expected_len as u32),
-        );
-        data.extend(basic_tag.struct_as_bytes());
-        #[cfg(debug_assertions)]
-        {
-            let basic_tag_size = size_of::<InformationRequestHeaderTag<0>>();
-            assert_eq!(
-                data.len(),
-                basic_tag_size,
-                "the vector must be as long as the basic tag!"
-            );
-        }
-
-        for tag_type in self
-            .irs
-            .into_iter()
-            // Transform to the ABI-compatible type
-            .map(TagTypeId::from)
-        {
-            let bytes: [u8; 4] = (u32::from(tag_type)).to_le_bytes();
-            data.extend(&bytes);
-        }
-
-        debug_assert_eq!(
-            data.len(),
-            expected_len,
-            "the byte vector must be as long as the expected size of the struct"
-        );
-
-        data
-    }
-}
-#[cfg(test)]
-mod tests {
-    use crate::builder::information_request::InformationRequestHeaderTagBuilder;
-    use crate::{HeaderTagFlag, InformationRequestHeaderTag, MbiTagType, MbiTagTypeId};
-
-    #[test]
-    #[cfg_attr(miri, ignore)]
-    fn test_builder() {
-        let builder = InformationRequestHeaderTagBuilder::new(HeaderTagFlag::Required)
-            .add_ir(MbiTagType::EfiMmap)
-            .add_ir(MbiTagType::BootLoaderName)
-            .add_ir(MbiTagType::Cmdline);
-        // type(u16) + flags(u16) + size(u32) + 3 tags (u32)
-        assert_eq!(builder.expected_len(), 2 + 2 + 4 + 3 * 4);
-        let tag = builder.build();
-        let tag = unsafe {
-            (tag.as_ptr() as *const InformationRequestHeaderTag<3>)
-                .as_ref()
-                .unwrap()
-        };
-        assert_eq!(tag.flags(), HeaderTagFlag::Required);
-        // type(u16) + flags(u16) + size(u32) + 3 tags (u32)
-        assert_eq!(tag.size(), 2 + 2 + 4 + 3 * 4);
-        assert_eq!(tag.dynamic_requests_size(), 3);
-        assert!(tag
-            .requests()
-            .contains(&MbiTagTypeId::from(MbiTagType::EfiMmap)));
-        assert!(tag
-            .requests()
-            .contains(&MbiTagTypeId::from(MbiTagType::BootLoaderName)));
-        assert!(tag
-            .requests()
-            .contains(&MbiTagTypeId::from(MbiTagType::Cmdline)));
-        assert_eq!(tag.requests().len(), 3);
-        assert!(!tag
-            .requests()
-            .contains(&MbiTagTypeId::from(MbiTagType::AcpiV1)));
-        println!("{:#?}", tag);
-    }
-}

+ 0 - 8
multiboot2-header/src/builder/mod.rs

@@ -1,8 +0,0 @@
-//! Module for the builder-feature.
-
-mod header;
-mod information_request;
-pub(crate) mod traits;
-
-pub use header::HeaderBuilder;
-pub use information_request::InformationRequestHeaderTagBuilder;

+ 0 - 75
multiboot2-header/src/builder/traits.rs

@@ -1,75 +0,0 @@
-//! Module for the helper trait [`StructAsBytes`].
-
-use crate::{
-    AddressHeaderTag, ConsoleHeaderTag, EfiBootServiceHeaderTag, EndHeaderTag,
-    EntryAddressHeaderTag, EntryEfi32HeaderTag, EntryEfi64HeaderTag, FramebufferHeaderTag,
-    InformationRequestHeaderTag, ModuleAlignHeaderTag, Multiboot2BasicHeader, RelocatableHeaderTag,
-};
-use alloc::vec::Vec;
-use core::mem::size_of;
-
-/// Trait for all tags that helps to create a byte array from the tag.
-/// Useful in builders to construct a byte vector that
-/// represents the Multiboot2 header with all its tags.
-pub trait StructAsBytes: Sized {
-    /// Returns the size in bytes of the struct, as known during compile
-    /// time. This doesn't use read the "size" field of tags.
-    fn byte_size(&self) -> usize {
-        size_of::<Self>()
-    }
-
-    /// Returns a byte pointer to the begin of the struct.
-    fn as_ptr(&self) -> *const u8 {
-        self as *const Self as *const u8
-    }
-
-    /// Returns the structure as a vector of its bytes.
-    /// The length is determined by [`Self::byte_size`].
-    fn struct_as_bytes(&self) -> Vec<u8> {
-        let ptr = self.as_ptr();
-        let bytes = unsafe { core::slice::from_raw_parts(ptr, self.byte_size()) };
-        Vec::from(bytes)
-    }
-}
-
-impl StructAsBytes for AddressHeaderTag {}
-impl StructAsBytes for ConsoleHeaderTag {}
-impl StructAsBytes for EndHeaderTag {}
-impl StructAsBytes for EntryEfi32HeaderTag {}
-impl StructAsBytes for EntryEfi64HeaderTag {}
-impl StructAsBytes for EntryAddressHeaderTag {}
-impl StructAsBytes for FramebufferHeaderTag {}
-impl StructAsBytes for InformationRequestHeaderTag<0> {}
-impl StructAsBytes for ModuleAlignHeaderTag {}
-impl StructAsBytes for RelocatableHeaderTag {}
-impl StructAsBytes for EfiBootServiceHeaderTag {}
-
-impl StructAsBytes for Multiboot2BasicHeader {}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    #[cfg_attr(miri, ignore)]
-    fn test_as_bytes() {
-        struct Foobar {
-            a: u32,
-            b: u8,
-            c: u128,
-        }
-        impl StructAsBytes for Foobar {}
-        #[allow(clippy::disallowed_names)]
-        let foo = Foobar {
-            a: 11,
-            b: 22,
-            c: 33,
-        };
-        let bytes = foo.struct_as_bytes();
-        let foo_from_bytes = unsafe { (bytes.as_ptr() as *const Foobar).as_ref().unwrap() };
-        assert_eq!(bytes.len(), size_of::<Foobar>());
-        assert_eq!(foo.a, foo_from_bytes.a);
-        assert_eq!(foo.b, foo_from_bytes.b);
-        assert_eq!(foo.c, foo_from_bytes.c);
-    }
-}

+ 14 - 10
multiboot2-header/src/console.rs

@@ -1,5 +1,6 @@
 use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
-use core::mem::size_of;
+use core::mem;
+use multiboot2_common::{MaybeDynSized, Tag};
 
 /// Possible flags for [`ConsoleHeaderTag`].
 #[repr(u32)]
@@ -14,7 +15,7 @@ pub enum ConsoleHeaderTagFlags {
 /// Tells that a console must be available in MBI.
 /// Only relevant for legacy BIOS.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[repr(C)]
+#[repr(C, align(8))]
 pub struct ConsoleHeaderTag {
     header: HeaderTagHeader,
     console_flags: ConsoleHeaderTagFlags,
@@ -25,7 +26,7 @@ impl ConsoleHeaderTag {
     #[must_use]
     pub const fn new(flags: HeaderTagFlag, console_flags: ConsoleHeaderTagFlags) -> Self {
         let header =
-            HeaderTagHeader::new(HeaderTagType::ConsoleFlags, flags, size_of::<Self>() as u32);
+            HeaderTagHeader::new(HeaderTagType::ConsoleFlags, flags, Self::BASE_SIZE as u32);
         Self {
             header,
             console_flags,
@@ -57,12 +58,15 @@ impl ConsoleHeaderTag {
     }
 }
 
-#[cfg(test)]
-mod tests {
-    use crate::ConsoleHeaderTag;
+impl MaybeDynSized for ConsoleHeaderTag {
+    type Header = HeaderTagHeader;
 
-    #[test]
-    fn test_assert_size() {
-        assert_eq!(core::mem::size_of::<ConsoleHeaderTag>(), 2 + 2 + 4 + 4);
-    }
+    const BASE_SIZE: usize = mem::size_of::<HeaderTagHeader>() + mem::size_of::<u32>();
+
+    fn dst_len(_header: &Self::Header) -> Self::Metadata {}
+}
+
+impl Tag for ConsoleHeaderTag {
+    type IDType = HeaderTagType;
+    const ID: HeaderTagType = HeaderTagType::ConsoleFlags;
 }

+ 16 - 2
multiboot2-header/src/end.rs

@@ -1,5 +1,6 @@
 use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
-use core::mem::size_of;
+use core::mem;
+use multiboot2_common::{MaybeDynSized, Tag};
 
 /// Terminates a list of optional tags in a Multiboot2 header.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -21,7 +22,7 @@ impl EndHeaderTag {
         let header = HeaderTagHeader::new(
             HeaderTagType::EntryAddress,
             HeaderTagFlag::Required,
-            size_of::<Self>() as u32,
+            mem::size_of::<Self>() as u32,
         );
         Self { header }
     }
@@ -45,6 +46,19 @@ impl EndHeaderTag {
     }
 }
 
+impl MaybeDynSized for EndHeaderTag {
+    type Header = HeaderTagHeader;
+
+    const BASE_SIZE: usize = mem::size_of::<Self>();
+
+    fn dst_len(_header: &Self::Header) -> Self::Metadata {}
+}
+
+impl Tag for EndHeaderTag {
+    type IDType = HeaderTagType;
+    const ID: HeaderTagType = HeaderTagType::End;
+}
+
 #[cfg(test)]
 mod tests {
     use crate::EndHeaderTag;

+ 14 - 10
multiboot2-header/src/entry_address.rs

@@ -1,12 +1,13 @@
 use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
 use core::fmt;
 use core::fmt::{Debug, Formatter};
-use core::mem::size_of;
+use core::mem;
+use multiboot2_common::{MaybeDynSized, Tag};
 
 /// Specifies the physical address to which the boot loader should jump in
 /// order to start running the operating system. Not needed for ELF files.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[repr(C)]
+#[repr(C, align(8))]
 pub struct EntryAddressHeaderTag {
     header: HeaderTagHeader,
     entry_addr: u32,
@@ -17,7 +18,7 @@ impl EntryAddressHeaderTag {
     #[must_use]
     pub const fn new(flags: HeaderTagFlag, entry_addr: u32) -> Self {
         let header =
-            HeaderTagHeader::new(HeaderTagType::EntryAddress, flags, size_of::<Self>() as u32);
+            HeaderTagHeader::new(HeaderTagType::EntryAddress, flags, Self::BASE_SIZE as u32);
         Self { header, entry_addr }
     }
 
@@ -57,12 +58,15 @@ impl Debug for EntryAddressHeaderTag {
     }
 }
 
-#[cfg(test)]
-mod tests {
-    use crate::EntryAddressHeaderTag;
+impl MaybeDynSized for EntryAddressHeaderTag {
+    type Header = HeaderTagHeader;
 
-    #[test]
-    fn test_assert_size() {
-        assert_eq!(core::mem::size_of::<EntryAddressHeaderTag>(), 2 + 2 + 4 + 4);
-    }
+    const BASE_SIZE: usize = mem::size_of::<HeaderTagHeader>() + mem::size_of::<u32>();
+
+    fn dst_len(_header: &Self::Header) -> Self::Metadata {}
+}
+
+impl Tag for EntryAddressHeaderTag {
+    type IDType = HeaderTagType;
+    const ID: HeaderTagType = HeaderTagType::EntryAddress;
 }

+ 14 - 10
multiboot2-header/src/entry_efi_32.rs

@@ -1,7 +1,8 @@
 use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
 use core::fmt;
 use core::fmt::{Debug, Formatter};
-use core::mem::size_of;
+use core::mem;
+use multiboot2_common::{MaybeDynSized, Tag};
 
 /// This tag is taken into account only on EFI i386 platforms when Multiboot2 image header
 /// contains EFI boot services tag. Then entry point specified in ELF header and the entry address
@@ -10,7 +11,7 @@ use core::mem::size_of;
 /// Technically, this is equivalent to the [`crate::EntryAddressHeaderTag`] but with a different
 /// [`crate::HeaderTagType`].
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[repr(C)]
+#[repr(C, align(8))]
 pub struct EntryEfi32HeaderTag {
     header: HeaderTagHeader,
     entry_addr: u32,
@@ -23,7 +24,7 @@ impl EntryEfi32HeaderTag {
         let header = HeaderTagHeader::new(
             HeaderTagType::EntryAddressEFI32,
             flags,
-            size_of::<Self>() as u32,
+            Self::BASE_SIZE as u32,
         );
         Self { header, entry_addr }
     }
@@ -64,12 +65,15 @@ impl Debug for EntryEfi32HeaderTag {
     }
 }
 
-#[cfg(test)]
-mod tests {
-    use crate::EntryEfi32HeaderTag;
+impl MaybeDynSized for EntryEfi32HeaderTag {
+    type Header = HeaderTagHeader;
 
-    #[test]
-    fn test_assert_size() {
-        assert_eq!(core::mem::size_of::<EntryEfi32HeaderTag>(), 2 + 2 + 4 + 4);
-    }
+    const BASE_SIZE: usize = mem::size_of::<HeaderTagHeader>() + mem::size_of::<u32>();
+
+    fn dst_len(_header: &Self::Header) -> Self::Metadata {}
+}
+
+impl Tag for EntryEfi32HeaderTag {
+    type IDType = HeaderTagType;
+    const ID: HeaderTagType = HeaderTagType::EntryAddressEFI32;
 }

+ 14 - 10
multiboot2-header/src/entry_efi_64.rs

@@ -1,7 +1,8 @@
 use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
 use core::fmt;
 use core::fmt::{Debug, Formatter};
-use core::mem::size_of;
+use core::mem;
+use multiboot2_common::{MaybeDynSized, Tag};
 
 /// This tag is taken into account only on EFI amd64 platforms when Multiboot2 image header
 /// contains EFI boot services tag. Then entry point specified in ELF header and the entry address
@@ -10,7 +11,7 @@ use core::mem::size_of;
 /// Technically, this is equivalent to the [`crate::EntryAddressHeaderTag`] but with a different
 /// [`crate::HeaderTagType`].
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[repr(C)]
+#[repr(C, align(8))]
 pub struct EntryEfi64HeaderTag {
     header: HeaderTagHeader,
     entry_addr: u32,
@@ -23,7 +24,7 @@ impl EntryEfi64HeaderTag {
         let header = HeaderTagHeader::new(
             HeaderTagType::EntryAddressEFI64,
             flags,
-            size_of::<Self>() as u32,
+            Self::BASE_SIZE as u32,
         );
         Self { header, entry_addr }
     }
@@ -64,12 +65,15 @@ impl Debug for EntryEfi64HeaderTag {
     }
 }
 
-#[cfg(test)]
-mod tests {
-    use crate::EntryEfi64HeaderTag;
+impl MaybeDynSized for EntryEfi64HeaderTag {
+    type Header = HeaderTagHeader;
 
-    #[test]
-    fn test_assert_size() {
-        assert_eq!(core::mem::size_of::<EntryEfi64HeaderTag>(), 2 + 2 + 4 + 4);
-    }
+    const BASE_SIZE: usize = mem::size_of::<HeaderTagHeader>() + mem::size_of::<u32>();
+
+    fn dst_len(_header: &Self::Header) -> Self::Metadata {}
+}
+
+impl Tag for EntryEfi64HeaderTag {
+    type IDType = HeaderTagType;
+    const ID: HeaderTagType = HeaderTagType::EntryAddressEFI64;
 }

+ 14 - 16
multiboot2-header/src/framebuffer.rs

@@ -1,12 +1,13 @@
 use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
 use core::mem;
+use multiboot2_common::{MaybeDynSized, Tag};
 
 /// Specifies the preferred graphics mode. If this tag
 /// is present the bootloader assumes that the payload
 /// has framebuffer support. Note: This is only a
 /// recommended mode. Only relevant on legacy BIOS.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[repr(C)]
+#[repr(C, align(8))]
 pub struct FramebufferHeaderTag {
     header: HeaderTagHeader,
     width: u32,
@@ -18,11 +19,8 @@ impl FramebufferHeaderTag {
     /// Constructs a new tag.
     #[must_use]
     pub const fn new(flags: HeaderTagFlag, width: u32, height: u32, depth: u32) -> Self {
-        let header = HeaderTagHeader::new(
-            HeaderTagType::Framebuffer,
-            flags,
-            mem::size_of::<Self>() as u32,
-        );
+        let header =
+            HeaderTagHeader::new(HeaderTagType::Framebuffer, flags, Self::BASE_SIZE as u32);
         Self {
             header,
             width,
@@ -68,15 +66,15 @@ impl FramebufferHeaderTag {
     }
 }
 
-#[cfg(test)]
-mod tests {
-    use super::*;
+impl MaybeDynSized for FramebufferHeaderTag {
+    type Header = HeaderTagHeader;
 
-    #[test]
-    fn test_assert_size() {
-        assert_eq!(
-            mem::size_of::<FramebufferHeaderTag>(),
-            2 + 2 + 4 + 4 + 4 + 4
-        );
-    }
+    const BASE_SIZE: usize = mem::size_of::<HeaderTagHeader>() + 3 * mem::size_of::<u32>();
+
+    fn dst_len(_header: &Self::Header) -> Self::Metadata {}
+}
+
+impl Tag for FramebufferHeaderTag {
+    type IDType = HeaderTagType;
+    const ID: HeaderTagType = HeaderTagType::Framebuffer;
 }

+ 82 - 223
multiboot2-header/src/header.rs

@@ -1,11 +1,13 @@
 use crate::{
-    AddressHeaderTag, ConsoleHeaderTag, EfiBootServiceHeaderTag, EndHeaderTag,
-    EntryAddressHeaderTag, EntryEfi32HeaderTag, EntryEfi64HeaderTag, FramebufferHeaderTag,
-    HeaderTagHeader, HeaderTagISA, HeaderTagType, InformationRequestHeaderTag,
-    ModuleAlignHeaderTag, RelocatableHeaderTag,
+    AddressHeaderTag, ConsoleHeaderTag, EfiBootServiceHeaderTag, EntryAddressHeaderTag,
+    EntryEfi32HeaderTag, EntryEfi64HeaderTag, FramebufferHeaderTag, HeaderTagHeader, HeaderTagISA,
+    HeaderTagType, InformationRequestHeaderTag, ModuleAlignHeaderTag, RelocatableHeaderTag,
+    TagIter,
 };
 use core::fmt::{Debug, Formatter};
 use core::mem::size_of;
+use core::ptr::NonNull;
+use multiboot2_common::{DynSizedStructure, Header, Tag, ALIGNMENT};
 
 /// Magic value for a [`Multiboot2Header`], as defined by the spec.
 pub const MAGIC: u32 = 0xe85250d6;
@@ -16,9 +18,8 @@ pub const MAGIC: u32 = 0xe85250d6;
 /// 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,
 /// please look at `HeaderBuilder` (requires the `builder` feature).
-#[derive(Debug)]
 #[repr(transparent)]
-pub struct Multiboot2Header<'a>(&'a Multiboot2BasicHeader);
+pub struct Multiboot2Header<'a>(&'a DynSizedStructure<Multiboot2BasicHeader>);
 
 impl<'a> Multiboot2Header<'a> {
     /// Public constructor for this type with various validations.
@@ -34,22 +35,18 @@ impl<'a> Multiboot2Header<'a> {
     /// This function may produce undefined behaviour, if the provided `addr` is not a valid
     /// Multiboot2 header pointer.
     pub unsafe fn load(ptr: *const Multiboot2BasicHeader) -> Result<Self, LoadError> {
-        // null or not aligned
-        if ptr.is_null() || ptr.align_offset(8) != 0 {
-            return Err(LoadError::InvalidAddress);
-        }
-
-        let reference = &*ptr;
+        let ptr = NonNull::new(ptr.cast_mut()).ok_or(LoadError::InvalidAddress)?;
+        let inner = DynSizedStructure::ref_from_ptr(ptr).map_err(|_e| LoadError::TooSmall)?;
+        let this = Self(inner);
 
-        if reference.header_magic() != MAGIC {
+        let header = this.0.header();
+        if header.header_magic != MAGIC {
             return Err(LoadError::MagicNotFound);
         }
-
-        if !reference.verify_checksum() {
+        if !header.verify_checksum() {
             return Err(LoadError::ChecksumMismatch);
         }
-
-        Ok(Self(reference))
+        Ok(this)
     }
 
     /// Find the header in a given slice.
@@ -60,7 +57,7 @@ impl<'a> Multiboot2Header<'a> {
     /// or because it is truncated), it returns a [`LoadError`].
     /// If there is no header, it returns `None`.
     pub fn find_header(buffer: &[u8]) -> Result<Option<(&[u8], u32)>, LoadError> {
-        if buffer.as_ptr().align_offset(4) != 0 {
+        if buffer.as_ptr().align_offset(ALIGNMENT) != 0 {
             return Err(LoadError::InvalidAddress);
         }
 
@@ -102,35 +99,36 @@ impl<'a> Multiboot2Header<'a> {
         )))
     }
 
+    /// Returns a [`TagIter`].
+    #[must_use]
+    pub fn iter(&self) -> TagIter {
+        TagIter::new(self.0.payload())
+    }
+
     /// Wrapper around [`Multiboot2BasicHeader::verify_checksum`].
     #[must_use]
     pub const fn verify_checksum(&self) -> bool {
-        self.0.verify_checksum()
+        self.0.header().verify_checksum()
     }
     /// Wrapper around [`Multiboot2BasicHeader::header_magic`].
     #[must_use]
     pub const fn header_magic(&self) -> u32 {
-        self.0.header_magic()
+        self.0.header().header_magic()
     }
     /// Wrapper around [`Multiboot2BasicHeader::arch`].
     #[must_use]
     pub const fn arch(&self) -> HeaderTagISA {
-        self.0.arch()
+        self.0.header().arch()
     }
     /// Wrapper around [`Multiboot2BasicHeader::length`].
     #[must_use]
     pub const fn length(&self) -> u32 {
-        self.0.length()
+        self.0.header().length()
     }
     /// Wrapper around [`Multiboot2BasicHeader::checksum`].
     #[must_use]
     pub const fn checksum(&self) -> u32 {
-        self.0.checksum()
-    }
-    /// Wrapper around [`Multiboot2BasicHeader::tag_iter`].
-    #[must_use]
-    pub fn iter(&self) -> Multiboot2HeaderTagIter {
-        self.0.tag_iter()
+        self.0.header().checksum()
     }
     /// Wrapper around [`Multiboot2BasicHeader::calc_checksum`].
     #[must_use]
@@ -138,74 +136,88 @@ impl<'a> Multiboot2Header<'a> {
         Multiboot2BasicHeader::calc_checksum(magic, arch, length)
     }
 
-    /// Search for the address header tag.
+    /// Search for the [`InformationRequestHeaderTag`] header tag.
+    #[must_use]
+    pub fn information_request_tag(&self) -> Option<&InformationRequestHeaderTag> {
+        self.get_tag()
+    }
+
+    /// Search for the [`AddressHeaderTag`] header tag.
     #[must_use]
     pub fn address_tag(&self) -> Option<&AddressHeaderTag> {
-        self.get_tag(HeaderTagType::Address)
-            .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const AddressHeaderTag) })
+        self.get_tag()
     }
 
-    /// Search for the entry address header tag.
+    /// Search for the [`EntryAddressHeaderTag`] header tag.
     #[must_use]
     pub fn entry_address_tag(&self) -> Option<&EntryAddressHeaderTag> {
-        self.get_tag(HeaderTagType::EntryAddress)
-            .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const EntryAddressHeaderTag) })
+        self.get_tag()
     }
 
-    /// Search for the EFI32 entry address header tag.
+    /// Search for the [`EntryEfi32HeaderTag`] header tag.
     #[must_use]
     pub fn entry_address_efi32_tag(&self) -> Option<&EntryEfi32HeaderTag> {
-        self.get_tag(HeaderTagType::EntryAddressEFI32)
-            .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const EntryEfi32HeaderTag) })
+        self.get_tag()
     }
 
-    /// Search for the EFI64 entry address header tag.
+    /// Search for the [`EntryEfi64HeaderTag`] header tag.
     #[must_use]
     pub fn entry_address_efi64_tag(&self) -> Option<&EntryEfi64HeaderTag> {
-        self.get_tag(HeaderTagType::EntryAddressEFI64)
-            .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const EntryEfi64HeaderTag) })
+        self.get_tag()
     }
 
-    /// Search for the console flags header tag.
+    /// Search for the [`ConsoleHeaderTag`] header tag.
     #[must_use]
     pub fn console_flags_tag(&self) -> Option<&ConsoleHeaderTag> {
-        self.get_tag(HeaderTagType::ConsoleFlags)
-            .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const ConsoleHeaderTag) })
+        self.get_tag()
     }
 
-    /// Search for the framebuffer header tag.
+    /// Search for the [`FramebufferHeaderTag`] header tag.
     #[must_use]
     pub fn framebuffer_tag(&self) -> Option<&FramebufferHeaderTag> {
-        self.get_tag(HeaderTagType::Framebuffer)
-            .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const FramebufferHeaderTag) })
+        self.get_tag()
     }
 
-    /// Search for the module align header tag.
+    /// Search for the [`ModuleAlignHeaderTag`] header tag.
     #[must_use]
     pub fn module_align_tag(&self) -> Option<&ModuleAlignHeaderTag> {
-        self.get_tag(HeaderTagType::ModuleAlign)
-            .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const ModuleAlignHeaderTag) })
+        self.get_tag()
     }
 
-    /// Search for the EFI Boot Services header tag.
+    /// Search for the [`EfiBootServiceHeaderTag`] header tag.
     #[must_use]
     pub fn efi_boot_services_tag(&self) -> Option<&EfiBootServiceHeaderTag> {
-        self.get_tag(HeaderTagType::EfiBS).map(|tag| unsafe {
-            &*(tag as *const HeaderTagHeader as *const EfiBootServiceHeaderTag)
-        })
+        self.get_tag()
     }
 
-    /// Search for the EFI32 entry address header tag.
+    /// Search for the [`RelocatableHeaderTag`] header tag.
     #[must_use]
     pub fn relocatable_tag(&self) -> Option<&RelocatableHeaderTag> {
-        self.get_tag(HeaderTagType::Relocatable)
-            .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const RelocatableHeaderTag) })
+        self.get_tag()
     }
 
-    fn get_tag(&self, typ: HeaderTagType) -> Option<&HeaderTagHeader> {
+    /// Searches for the specified tag by iterating the structure and returns
+    /// the first occurrence, if present.
+    #[must_use]
+    fn get_tag<T: Tag<IDType = HeaderTagType, Header = HeaderTagHeader> + ?Sized + 'a>(
+        &'a self,
+    ) -> Option<&'a T> {
         self.iter()
-            .map(|tag| unsafe { tag.as_ref() }.unwrap())
-            .find(|tag| tag.typ() == typ)
+            .find(|tag| tag.header().typ() == T::ID)
+            .map(|tag| tag.cast::<T>())
+    }
+}
+
+impl Debug for Multiboot2Header<'_> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
+        f.debug_struct("Multiboot2Header")
+            .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>")
+            .finish()
     }
 }
 
@@ -229,7 +241,7 @@ impl core::error::Error for LoadError {}
 /// 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.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[repr(C)]
+#[repr(C, align(8))]
 pub struct Multiboot2BasicHeader {
     /// Must be the value of [`MAGIC`].
     header_magic: u32,
@@ -290,28 +302,16 @@ impl Multiboot2BasicHeader {
     pub const fn checksum(&self) -> u32 {
         self.checksum
     }
+}
 
-    /// Returns a [`Multiboot2HeaderTagIter`].
-    ///
-    /// # Panics
-    /// See doc of [`Multiboot2HeaderTagIter`].
-    #[must_use]
-    pub fn tag_iter(&self) -> Multiboot2HeaderTagIter {
-        let base_hdr_size = size_of::<Self>();
-        if base_hdr_size == self.length as usize {
-            panic!("No end tag!");
-        }
-        let tag_base_addr = self as *const Self;
-        // cast to u8 so that the offset in bytes works correctly
-        let tag_base_addr = tag_base_addr as *const u8;
-        // tag_base_addr should now point behind the "static" members
-        let tag_base_addr = unsafe { tag_base_addr.add(base_hdr_size) };
-        // align pointer to 8 byte according to spec
-        let tag_base_addr = unsafe { tag_base_addr.add(tag_base_addr.align_offset(8)) };
-        // cast back
-        let tag_base_addr = tag_base_addr as *const HeaderTagHeader;
-        let tags_len = self.length as usize - base_hdr_size;
-        Multiboot2HeaderTagIter::new(tag_base_addr, tags_len as u32)
+impl Header for Multiboot2BasicHeader {
+    fn payload_len(&self) -> usize {
+        self.length as usize - size_of::<Self>()
+    }
+
+    fn set_size(&mut self, total_size: usize) {
+        self.length = total_size as u32;
+        self.checksum = Self::calc_checksum(self.header_magic, self.arch, total_size as u32);
     }
 }
 
@@ -322,152 +322,11 @@ impl Debug for Multiboot2BasicHeader {
             .field("arch", &{ self.arch })
             .field("length", &{ self.length })
             .field("checksum", &{ self.checksum })
-            .field("tags", &self.tag_iter())
+            //.field("tags", &self.iter())
             .finish()
     }
 }
 
-/// Iterator over all tags of a Multiboot2 header. The number of items is derived
-/// by the size/length of the header.
-///
-/// # Panics
-/// Panics if the `length`-attribute doesn't match the number of found tags, there are
-/// more tags found than technically possible, or if there is more than one end tag.
-/// All of these errors come from bigger, underlying problems. Therefore, they are
-/// considered as "abort/panic" and not as recoverable errors.
-#[derive(Clone)]
-pub struct Multiboot2HeaderTagIter {
-    /// 8-byte aligned base address
-    base: *const HeaderTagHeader,
-    /// Offset in bytes from the base address.
-    /// Always <= than size.
-    n: u32,
-    /// Size / final value of [`Self::n`].
-    size: u32,
-    /// Counts the number of found tags. If more tags are found
-    /// than technically possible, for example because the length property
-    /// was invalid and there are hundreds of "End"-tags, we can use
-    /// this and enforce a hard iteration limit.
-    tag_count: u32,
-    /// Marks if the end-tag was found. Together with `tag_count`, this
-    /// further helps to improve safety when invalid length properties are given.
-    end_tag_found: bool,
-}
-
-impl Multiboot2HeaderTagIter {
-    fn new(base: *const HeaderTagHeader, size: u32) -> Self {
-        // transform to byte pointer => offset works properly
-        let base = base as *const u8;
-        let base = unsafe { base.add(base.align_offset(8)) };
-        let base = base as *const HeaderTagHeader;
-        Self {
-            base,
-            n: 0,
-            size,
-            tag_count: 0,
-            end_tag_found: false,
-        }
-    }
-}
-
-impl Iterator for Multiboot2HeaderTagIter {
-    type Item = *const HeaderTagHeader;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        // no more bytes left to check; length reached
-        if self.n >= self.size {
-            return None;
-        }
-
-        // transform to byte ptr => offset works correctly
-        let ptr = self.base as *const u8;
-        let ptr = unsafe { ptr.add(self.n as usize) };
-        let ptr = ptr as *const HeaderTagHeader;
-        assert_eq!(ptr as usize % 8, 0, "must be 8-byte aligned");
-        let tag = unsafe { &*ptr };
-        assert!(
-            tag.size() <= 500,
-            "no real mb2 header should be bigger than 500bytes - probably wrong memory?! is: {}",
-            { tag.size() }
-        );
-        assert!(
-            tag.size() >= 8,
-            "no real mb2 header tag is smaller than 8 bytes - probably wrong memory?! is: {}",
-            { tag.size() }
-        );
-        assert!(
-            !self.end_tag_found,
-            "There is more than one end tag! Maybe the `length` property is invalid?"
-        );
-        self.n += tag.size();
-        // 8-byte alignment of pointer address
-        self.n += self.n % 8;
-        self.tag_count += 1;
-        if tag.typ() == HeaderTagType::End {
-            self.end_tag_found = true;
-        }
-        assert!(self.tag_count < HeaderTagType::count(), "Invalid Multiboot2 header tags! There are more tags than technically possible! Maybe the `length` property is invalid?");
-        Some(ptr)
-    }
-}
-
-impl Debug for Multiboot2HeaderTagIter {
-    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
-        let mut debug = f.debug_list();
-        self.clone().for_each(|t| unsafe {
-            let typ = (*t).typ();
-            if typ == HeaderTagType::End {
-                let entry = t as *const EndHeaderTag;
-                let entry = &*(entry);
-                debug.entry(entry);
-            } else if typ == HeaderTagType::InformationRequest {
-                let entry = t as *const InformationRequestHeaderTag<0>;
-                let entry = &*(entry);
-                debug.entry(entry);
-            } else if typ == HeaderTagType::Address {
-                let entry = t as *const AddressHeaderTag;
-                let entry = &*(entry);
-                debug.entry(entry);
-            } else if typ == HeaderTagType::EntryAddress {
-                let entry = t as *const EntryAddressHeaderTag;
-                let entry = &*(entry);
-                debug.entry(entry);
-            } else if typ == HeaderTagType::ConsoleFlags {
-                let entry = t as *const ConsoleHeaderTag;
-                let entry = &*(entry);
-                debug.entry(entry);
-            } else if typ == HeaderTagType::Framebuffer {
-                let entry = t as *const FramebufferHeaderTag;
-                let entry = &*(entry);
-                debug.entry(entry);
-            } else if typ == HeaderTagType::EfiBS {
-                let entry = t as *const EfiBootServiceHeaderTag;
-                let entry = &*(entry);
-                debug.entry(entry);
-            } else if typ == HeaderTagType::EntryAddressEFI32 {
-                let entry = t as *const EntryEfi32HeaderTag;
-                let entry = &*(entry);
-                debug.entry(entry);
-            } else if typ == HeaderTagType::EntryAddressEFI64 {
-                let entry = t as *const EntryEfi64HeaderTag;
-                let entry = &*(entry);
-                debug.entry(entry);
-            } else if typ == HeaderTagType::ModuleAlign {
-                let entry = t as *const ModuleAlignHeaderTag;
-                let entry = &*(entry);
-                debug.entry(entry);
-            } else if typ == HeaderTagType::Relocatable {
-                let entry = t as *const RelocatableHeaderTag;
-                let entry = &*(entry);
-                debug.entry(entry);
-            } else {
-                panic!("unknown tag ({:?})!", typ);
-            }
-        });
-        debug.finish()
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use crate::Multiboot2BasicHeader;

+ 70 - 110
multiboot2-header/src/information_request.rs

@@ -1,34 +1,37 @@
-use crate::{HeaderTagFlag, HeaderTagHeader, MbiTagType};
+use crate::{HeaderTagFlag, HeaderTagHeader};
 use crate::{HeaderTagType, MbiTagTypeId};
 use core::fmt;
 use core::fmt::{Debug, Formatter};
-use core::marker::PhantomData;
-use core::mem::size_of;
-use multiboot2::TagType;
+use core::mem;
+#[cfg(feature = "builder")]
+use multiboot2_common::new_boxed;
+use multiboot2_common::{MaybeDynSized, Tag};
+#[cfg(feature = "builder")]
+use {
+    alloc::boxed::Box,
+    core::{ptr, slice},
+};
 
 /// Specifies what specific tag types the bootloader should provide
 /// inside the mbi.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[repr(C)]
-pub struct InformationRequestHeaderTag<const N: usize> {
+#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, ptr_meta::Pointee)]
+#[repr(C, align(8))]
+pub struct InformationRequestHeaderTag {
     header: HeaderTagHeader,
-    // Length is determined by size.
-    // Must be parsed during runtime with unsafe pointer magic and the size field.
-    requests: [MbiTagTypeId; N],
+    requests: [MbiTagTypeId],
 }
 
-impl<const N: usize> InformationRequestHeaderTag<N> {
-    /// Creates a new object. The size parameter is the value of the size property.
-    /// It doesn't have to match with `N` necessarily, because during compile time we
-    /// can't know the size of the tag in all runtime situations.
+impl InformationRequestHeaderTag {
+    /// Creates a new object.
+    #[cfg(feature = "builder")]
     #[must_use]
-    pub fn new(flags: HeaderTagFlag, requests: [MbiTagTypeId; N], size: Option<u32>) -> Self {
-        let header = HeaderTagHeader::new(
-            HeaderTagType::InformationRequest,
-            flags,
-            size.unwrap_or(size_of::<Self>() as u32),
-        );
-        Self { header, requests }
+    pub fn new(flags: HeaderTagFlag, requests: &[MbiTagTypeId]) -> Box<Self> {
+        let header = HeaderTagHeader::new(HeaderTagType::InformationRequest, flags, 0);
+        let requests = unsafe {
+            let ptr = ptr::addr_of!(*requests);
+            slice::from_raw_parts(ptr.cast::<u8>(), mem::size_of_val(requests))
+        };
+        new_boxed(header, &[requests])
     }
 
     /// Returns the [`HeaderTagType`].
@@ -49,119 +52,76 @@ impl<const N: usize> InformationRequestHeaderTag<N> {
         self.header.size()
     }
 
-    /// Returns the requests as array. Only works if the number of requests
-    /// is known at compile time. For safety and correctness during runtime,
-    /// you should use `req_iter()`.
-    #[must_use]
-    pub const fn requests(&self) -> [MbiTagTypeId; N] {
-        // cheap to copy, otherwise difficult with lifetime
-        self.requests
-    }
-
-    /// Returns the number of [`MbiTagType`]-requests derived
-    /// from the `size`-property. This method is useful
-    /// because this struct uses a const generic, but during runtime
-    /// we don't know the value in almost any case.
+    /// Returns the requests as array
     #[must_use]
-    pub const fn dynamic_requests_size(&self) -> u32 {
-        let base_struct_size = size_of::<InformationRequestHeaderTag<0>>();
-        let size_diff = self.size() - base_struct_size as u32;
-        if size_diff > 0 {
-            size_diff / size_of::<u32>() as u32
-        } else {
-            0
-        }
-    }
-
-    /// Returns an [`InformationRequestHeaderTagIter`].
-    #[must_use]
-    pub const fn req_iter(&self) -> InformationRequestHeaderTagIter {
-        let base_struct_size = size_of::<InformationRequestHeaderTag<0>>();
-        let count = self.dynamic_requests_size();
-        let base_ptr = self as *const Self;
-        let base_ptr = base_ptr as *const u8;
-        let base_ptr = unsafe { base_ptr.add(base_struct_size) };
-        let base_ptr = base_ptr as *const MbiTagTypeId;
-        InformationRequestHeaderTagIter::new(count, base_ptr)
+    pub const fn requests(&self) -> &[MbiTagTypeId] {
+        &self.requests
     }
 }
 
-impl<const N: usize> Debug for InformationRequestHeaderTag<N> {
+impl Debug for InformationRequestHeaderTag {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
         f.debug_struct("InformationRequestHeaderTag")
             .field("type", &self.typ())
             .field("flags", &self.flags())
             .field("size", &self.size())
-            .field("requests", &self.req_iter())
+            .field("requests", &self.requests())
             .finish()
     }
 }
 
-/// Iterates the dynamically sized information request structure and finds all MBI tags
-/// that are requested.
-#[derive(Copy, Clone)]
-pub struct InformationRequestHeaderTagIter<'a> {
-    base_ptr: *const MbiTagTypeId,
-    i: u32,
-    count: u32,
-    _marker: PhantomData<&'a ()>,
-}
+impl MaybeDynSized for InformationRequestHeaderTag {
+    type Header = HeaderTagHeader;
 
-impl<'a> InformationRequestHeaderTagIter<'a> {
-    const fn new(count: u32, base_ptr: *const MbiTagTypeId) -> Self {
-        Self {
-            i: 0,
-            count,
-            base_ptr,
-            _marker: PhantomData,
-        }
-    }
-}
+    const BASE_SIZE: usize = mem::size_of::<HeaderTagHeader>();
 
-impl<'a> Iterator for InformationRequestHeaderTagIter<'a> {
-    type Item = MbiTagType;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.i < self.count {
-            let ptr = unsafe { self.base_ptr.offset(self.i as isize) };
-            self.i += 1;
-            let tag_type_id = unsafe { *ptr };
-            Some(TagType::from(tag_type_id))
-        } else {
-            None
-        }
+    fn dst_len(header: &Self::Header) -> Self::Metadata {
+        let dst_size = header.size() as usize - Self::BASE_SIZE;
+        assert_eq!(dst_size % mem::size_of::<MbiTagTypeId>(), 0);
+        dst_size / mem::size_of::<MbiTagTypeId>()
     }
 }
 
-impl<'a> Debug for InformationRequestHeaderTagIter<'a> {
-    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        let mut debug = f.debug_list();
-        self.for_each(|e| {
-            debug.entry(&e);
-        });
-        debug.finish()
-    }
+impl Tag for InformationRequestHeaderTag {
+    type IDType = HeaderTagType;
+    const ID: HeaderTagType = HeaderTagType::InformationRequest;
 }
 
 #[cfg(test)]
+#[cfg(feature = "builder")]
 mod tests {
-    use crate::InformationRequestHeaderTag;
+    use super::*;
+    use crate::MbiTagType;
 
     #[test]
-    #[allow(clippy::erasing_op)]
-    #[allow(clippy::identity_op)]
-    fn test_assert_size() {
-        assert_eq!(
-            core::mem::size_of::<InformationRequestHeaderTag<0>>(),
-            2 + 2 + 4 + 0 * 4
-        );
-        assert_eq!(
-            core::mem::size_of::<InformationRequestHeaderTag<1>>(),
-            2 + 2 + 4 + 1 * 4
-        );
-        assert_eq!(
-            core::mem::size_of::<InformationRequestHeaderTag<2>>(),
-            2 + 2 + 4 + 2 * 4
+    fn creation() {
+        // Main objective here is to satisfy Miri.
+        let _ir = InformationRequestHeaderTag::new(
+            HeaderTagFlag::Optional,
+            &[
+                MbiTagType::Cmdline.into(),
+                MbiTagType::BootLoaderName.into(),
+                MbiTagType::Module.into(),
+                MbiTagType::BasicMeminfo.into(),
+                MbiTagType::Bootdev.into(),
+                MbiTagType::Mmap.into(),
+                MbiTagType::Vbe.into(),
+                MbiTagType::Framebuffer.into(),
+                MbiTagType::ElfSections.into(),
+                MbiTagType::Apm.into(),
+                MbiTagType::Efi32.into(),
+                MbiTagType::Efi64.into(),
+                MbiTagType::Smbios.into(),
+                MbiTagType::AcpiV1.into(),
+                MbiTagType::AcpiV2.into(),
+                MbiTagType::Network.into(),
+                MbiTagType::EfiMmap.into(),
+                MbiTagType::EfiBs.into(),
+                MbiTagType::Efi32Ih.into(),
+                MbiTagType::Efi64Ih.into(),
+                MbiTagType::LoadBaseAddr.into(),
+                MbiTagType::Custom(0x1337).into(),
+            ],
         );
     }
 }

+ 20 - 27
multiboot2-header/src/lib.rs

@@ -2,34 +2,16 @@
 //! headers, as well as a builder to build them at runtime. This library is
 //! `no_std` and can be used in bootloaders.
 //!
-//! # Example
+//! # Example: Parsing a Header
 //!
-//! ```rust
-//! use multiboot2_header::builder::{InformationRequestHeaderTagBuilder, HeaderBuilder};
-//! use multiboot2_header::{HeaderTagFlag, HeaderTagISA, MbiTagType, RelocatableHeaderTag, RelocatableHeaderTagPreference, Multiboot2Header};
-//!
-//! // Small example that creates a Multiboot2 header and parses it afterwards.
-//!
-//! // We create a Multiboot2 header during runtime here. A practical example is that your
-//! // program gets the header from a file and parses it afterwards.
-//! let mb2_hdr_bytes = HeaderBuilder::new(HeaderTagISA::I386)
-//!     .relocatable_tag(RelocatableHeaderTag::new(
-//!         HeaderTagFlag::Required,
-//!         0x1337,
-//!         0xdeadbeef,
-//!         4096,
-//!         RelocatableHeaderTagPreference::None,
-//!     ))
-//!     .information_request_tag(
-//!         InformationRequestHeaderTagBuilder::new(HeaderTagFlag::Required)
-//!             .add_irs(&[MbiTagType::Cmdline, MbiTagType::BootLoaderName]),
-//!     )
-//!     .build();
-//!
-//! // Cast bytes in vector to Multiboot2 information structure
-//! let mb2_hdr = unsafe { Multiboot2Header::load(mb2_hdr_bytes.as_ptr().cast()) };
-//! println!("{:#?}", mb2_hdr);
+//! ```no_run
+//! use multiboot2_header::Multiboot2Header;
 //!
+//! let ptr = 0x1337_0000 as *const u8 /* use real ptr here */;
+//! let mb2_hdr = unsafe { Multiboot2Header::load(ptr.cast()) }.unwrap();
+//! for _tag in mb2_hdr.iter() {
+//!     //
+//! }
 //! ```
 //!
 //! ## MSRV
@@ -62,6 +44,13 @@ extern crate alloc;
 #[cfg(test)]
 extern crate std;
 
+/// Iterator over the tags of a Multiboot2 boot information.
+pub type TagIter<'a> = multiboot2_common::TagIter<'a, HeaderTagHeader>;
+
+/// A generic version of all boot information tags.
+#[cfg(test)]
+pub type GenericHeaderTag = multiboot2_common::DynSizedStructure<HeaderTagHeader>;
+
 mod address;
 mod console;
 mod end;
@@ -77,7 +66,9 @@ mod tags;
 mod uefi_bs;
 
 #[cfg(feature = "builder")]
-pub mod builder;
+mod builder;
+
+pub use multiboot2_common::{DynSizedStructure, MaybeDynSized, Tag};
 
 pub use self::address::*;
 pub use self::console::*;
@@ -92,6 +83,8 @@ pub use self::module_align::*;
 pub use self::relocatable::*;
 pub use self::tags::*;
 pub use self::uefi_bs::*;
+#[cfg(feature = "builder")]
+pub use builder::Builder;
 
 /// Re-export of [`multiboot2::TagType`] from `multiboot2`-crate.
 pub use multiboot2::{TagType as MbiTagType, TagTypeId as MbiTagTypeId};

+ 21 - 4
multiboot2-header/src/module_align.rs

@@ -1,9 +1,10 @@
 use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
-use core::mem::size_of;
+use core::mem;
+use multiboot2_common::{MaybeDynSized, Tag};
 
 /// If this tag is present, provided boot modules must be page aligned.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[repr(C)]
+#[repr(C, align(8))]
 pub struct ModuleAlignHeaderTag {
     header: HeaderTagHeader,
 }
@@ -12,8 +13,11 @@ impl ModuleAlignHeaderTag {
     /// Constructs a new tag.
     #[must_use]
     pub const fn new(flags: HeaderTagFlag) -> Self {
-        let header =
-            HeaderTagHeader::new(HeaderTagType::ModuleAlign, flags, size_of::<Self>() as u32);
+        let header = HeaderTagHeader::new(
+            HeaderTagType::ModuleAlign,
+            flags,
+            mem::size_of::<Self>() as u32,
+        );
         Self { header }
     }
 
@@ -36,6 +40,19 @@ impl ModuleAlignHeaderTag {
     }
 }
 
+impl MaybeDynSized for ModuleAlignHeaderTag {
+    type Header = HeaderTagHeader;
+
+    const BASE_SIZE: usize = mem::size_of::<Self>();
+
+    fn dst_len(_header: &Self::Header) -> Self::Metadata {}
+}
+
+impl Tag for ModuleAlignHeaderTag {
+    type IDType = HeaderTagType;
+    const ID: HeaderTagType = HeaderTagType::ModuleAlign;
+}
+
 #[cfg(test)]
 mod tests {
     use crate::ModuleAlignHeaderTag;

+ 21 - 4
multiboot2-header/src/relocatable.rs

@@ -1,7 +1,8 @@
 use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
 use core::fmt;
 use core::fmt::{Debug, Formatter};
-use core::mem::size_of;
+use core::mem;
+use multiboot2_common::{MaybeDynSized, Tag};
 
 /// It contains load address placement suggestion for boot loader. Boot loader
 /// should follow it. ‘0’ means none, ‘1’ means load image at lowest possible address
@@ -20,7 +21,7 @@ pub enum RelocatableHeaderTagPreference {
 
 /// This tag indicates that the image is relocatable.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[repr(C)]
+#[repr(C, align(8))]
 pub struct RelocatableHeaderTag {
     header: HeaderTagHeader,
     /// Lowest possible physical address at which image should be loaded. The bootloader cannot load any part of image below this address
@@ -42,8 +43,11 @@ impl RelocatableHeaderTag {
         align: u32,
         preference: RelocatableHeaderTagPreference,
     ) -> Self {
-        let header =
-            HeaderTagHeader::new(HeaderTagType::Relocatable, flags, size_of::<Self>() as u32);
+        let header = HeaderTagHeader::new(
+            HeaderTagType::Relocatable,
+            flags,
+            mem::size_of::<Self>() as u32,
+        );
         Self {
             header,
             min_addr,
@@ -111,6 +115,19 @@ impl Debug for RelocatableHeaderTag {
     }
 }
 
+impl MaybeDynSized for RelocatableHeaderTag {
+    type Header = HeaderTagHeader;
+
+    const BASE_SIZE: usize = mem::size_of::<Self>();
+
+    fn dst_len(_header: &Self::Header) -> Self::Metadata {}
+}
+
+impl Tag for RelocatableHeaderTag {
+    type IDType = HeaderTagType;
+    const ID: HeaderTagType = HeaderTagType::Relocatable;
+}
+
 #[cfg(test)]
 mod tests {
     use crate::RelocatableHeaderTag;

+ 13 - 0
multiboot2-header/src/tags.rs

@@ -2,6 +2,9 @@
 //! code at the end of the official Multiboot2 spec. These tags follow in memory right after
 //! [`crate::Multiboot2BasicHeader`].
 
+use core::mem;
+use multiboot2_common::Header;
+
 /// ISA/ARCH in Multiboot2 header.
 #[repr(u32)]
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -102,6 +105,16 @@ impl HeaderTagHeader {
     }
 }
 
+impl Header for HeaderTagHeader {
+    fn payload_len(&self) -> usize {
+        self.size as usize - mem::size_of::<Self>()
+    }
+
+    fn set_size(&mut self, total_size: usize) {
+        self.size = total_size as u32;
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use crate::HeaderTagHeader;

+ 18 - 3
multiboot2-header/src/uefi_bs.rs

@@ -1,10 +1,11 @@
 use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
-use core::mem::size_of;
+use core::mem;
+use multiboot2_common::{MaybeDynSized, Tag};
 
 /// This tag indicates that payload supports starting without terminating UEFI boot services.
 /// Or in other words: The payload wants to use UEFI boot services.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[repr(C)]
+#[repr(C, align(8))]
 pub struct EfiBootServiceHeaderTag {
     header: HeaderTagHeader,
 }
@@ -13,7 +14,8 @@ impl EfiBootServiceHeaderTag {
     /// Constructs a new tag.
     #[must_use]
     pub const fn new(flags: HeaderTagFlag) -> Self {
-        let header = HeaderTagHeader::new(HeaderTagType::EfiBS, flags, size_of::<Self>() as u32);
+        let header =
+            HeaderTagHeader::new(HeaderTagType::EfiBS, flags, mem::size_of::<Self>() as u32);
         Self { header }
     }
 
@@ -36,6 +38,19 @@ impl EfiBootServiceHeaderTag {
     }
 }
 
+impl MaybeDynSized for EfiBootServiceHeaderTag {
+    type Header = HeaderTagHeader;
+
+    const BASE_SIZE: usize = mem::size_of::<Self>();
+
+    fn dst_len(_header: &Self::Header) -> Self::Metadata {}
+}
+
+impl Tag for EfiBootServiceHeaderTag {
+    type IDType = HeaderTagType;
+    const ID: HeaderTagType = HeaderTagType::EfiBS;
+}
+
 #[cfg(test)]
 mod tests {
     use crate::EfiBootServiceHeaderTag;