ソースを参照

multiboot2-header: refactor to use common abstractions

This especially significantly changes the builder. The normal public API however
is only slightly affected.
Philipp Schuster 6 ヶ月 前
コミット
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;