فهرست منبع

Merge pull request #95 from rust-osdev/multiboot2-header-crate

multiboot2-header crate - initial release
Philipp Schuster 3 سال پیش
والد
کامیت
50ee5dbc0e

+ 3 - 2
README.md

@@ -6,8 +6,9 @@ Please check their individual README-files ([multiboot2](multiboot2/README.md),
 
 The `multiboot2` crate helps to parse the Multiboot2 information structure
 (MBI) and is relevant in kernels, that get booted by a bootloader such as
-GRUB, for example. `multiboot2-header` is relevant, if you want to write a bootloader that provides a MBI to a payload for
-example.
+GRUB, for example. `multiboot2-header` helps you to either build
+Multiboot2-headers yourself, or to parse Multiboot2 headers in custom bootloader
+or similar applications.
 
 ## License
 

+ 3 - 1
multiboot2-header/Cargo.toml

@@ -4,7 +4,7 @@ description = """
 Library with type definitions and parsing functions for Multiboot2 headers.
 This library is `no_std` and can be used in bootloaders.
 """
-version = "0.0.0"
+version = "0.1.0"
 authors = [
     "Philipp Schuster <phip1611@gmail.com>"
 ]
@@ -28,3 +28,5 @@ repository = "https://github.com/rust-osdev/multiboot2"
 documentation = "https://docs.rs/multiboot2-header"
 
 [dependencies]
+# used for MBI tags
+multiboot2 = "0.12.2"

+ 49 - 0
multiboot2-header/README.md

@@ -6,6 +6,55 @@
 Rust library with type definitions and parsing functions for Multiboot2 headers.
 This library is `no_std` and can be used in bootloaders.
 
+What this library is good for:
+- writing a small binary which writes you a valid Multiboot2 header
+  into a file (such as `header.bin`)
+- understanding Multiboot2 headers better
+- analyze Multiboot2 headers at runtime
+
+What this library is not optimal for:
+- compiling a Multiboot2 header statically into an object file using only Rust code
+
+## Example 1: Builder + Parse
+```rust
+use multiboot2_header::builder::Multiboot2HeaderBuilder;
+use multiboot2_header::{HeaderTagFlag, HeaderTagISA, InformationRequestHeaderTagBuilder, MbiTagType, RelocatableHeaderTag, RelocatableHeaderTagPreference, Multiboot2Header};
+
+/// 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 = Multiboot2HeaderBuilder::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::from_addr(mb2_hdr_bytes.as_ptr() as usize) };
+    println!("{:#?}", mb2_hdr);
+}
+```
+
+## Example 2: Multiboot2 header as static data in Rust file
+You can use the builder, construct a Multiboot2 header, write it to a file and include it like this:
+```
+#[used]
+#[no_mangle]
+#[link_section = ".text.multiboot2_header"]
+static MULTIBOOT2_HDR: &[u8; 64] = include_bytes!("mb2_hdr_dump.bin");
+```
+You may need a special linker script to place this in a LOAD segment with a file offset with less than 32768 bytes.
+See specification.
+
 ## License & Contribution
 
 See main [README](https://github.com/rust-osdev/multiboot2/blob/main/README.md) file.

+ 28 - 0
multiboot2-header/examples/minimal.rs

@@ -0,0 +1,28 @@
+use multiboot2_header::builder::Multiboot2HeaderBuilder;
+use multiboot2_header::{
+    HeaderTagFlag, HeaderTagISA, InformationRequestHeaderTagBuilder, 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 = Multiboot2HeaderBuilder::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::from_addr(mb2_hdr_bytes.as_ptr() as usize) };
+    println!("{:#?}", mb2_hdr);
+}

+ 68 - 0
multiboot2-header/src/address.rs

@@ -0,0 +1,68 @@
+use crate::{HeaderTagFlag, HeaderTagType, StructAsBytes};
+use core::mem::size_of;
+
+/// 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)]
+#[repr(C, packed(8))]
+pub struct AddressHeaderTag {
+    typ: HeaderTagType,
+    flags: HeaderTagFlag,
+    size: u32,
+    /// 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.
+    header_addr: u32,
+    /// Contains the physical address of the beginning of the text segment. The offset in the OS image file at which to start loading is defined by the offset at which the header was found, minus (header_addr - load_addr). load_addr must be less than or equal to header_addr.
+    ///
+    /// Special value -1 means that the file must be loaded from its beginning.
+    load_addr: u32,
+    /// Contains the physical address of the end of the data segment. (load_end_addr - load_addr) specifies how much data to load. This implies that the text and data segments must be consecutive in the OS image; this is true for existing a.out executable formats. If this field is zero, the boot loader assumes that the text and data segments occupy the whole OS image file.
+    load_end_addr: u32,
+    /// Contains the physical address of the end of the bss segment. The boot loader initializes this area to zero, and reserves the memory it occupies to avoid placing boot modules and other data relevant to the operating system in that area. If this field is zero, the boot loader assumes that no bss segment is present.
+    bss_end_addr: u32,
+}
+
+impl AddressHeaderTag {
+    pub const fn new(
+        flags: HeaderTagFlag,
+        header_addr: u32,
+        load_addr: u32,
+        load_end_addr: u32,
+        bss_end_addr: u32,
+    ) -> Self {
+        AddressHeaderTag {
+            typ: HeaderTagType::Address,
+            flags,
+            size: size_of::<Self>() as u32,
+            header_addr,
+            load_addr,
+            load_end_addr,
+            bss_end_addr,
+        }
+    }
+
+    pub const fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub const fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub const fn size(&self) -> u32 {
+        self.size
+    }
+    pub const fn header_addr(&self) -> u32 {
+        self.header_addr
+    }
+    pub const fn load_addr(&self) -> u32 {
+        self.load_addr
+    }
+    pub const fn load_end_addr(&self) -> u32 {
+        self.load_end_addr
+    }
+    pub const fn bss_end_addr(&self) -> u32 {
+        self.bss_end_addr
+    }
+}
+
+impl StructAsBytes for AddressHeaderTag {}

+ 81 - 0
multiboot2-header/src/console.rs

@@ -0,0 +1,81 @@
+use crate::{HeaderTagFlag, HeaderTagType, StructAsBytes};
+use core::mem::size_of;
+
+/// Possible flags for [`ConsoleHeaderTag`].
+#[repr(u32)]
+#[derive(Copy, Clone, Debug)]
+pub enum ConsoleHeaderTagFlags {
+    /// Console required.
+    ConsoleRequired = 0,
+    /// EGA text support.
+    EgaTextSupported = 1,
+}
+
+/// Tells that a console must be available in MBI.
+/// Only relevant for legacy BIOS.
+#[derive(Copy, Clone, Debug)]
+#[repr(C, packed(8))]
+pub struct ConsoleHeaderTag {
+    typ: HeaderTagType,
+    flags: HeaderTagFlag,
+    size: u32,
+    console_flags: ConsoleHeaderTagFlags,
+}
+
+impl ConsoleHeaderTag {
+    pub const fn new(flags: HeaderTagFlag, console_flags: ConsoleHeaderTagFlags) -> Self {
+        ConsoleHeaderTag {
+            typ: HeaderTagType::ConsoleFlags,
+            flags,
+            size: size_of::<Self>() as u32,
+            console_flags,
+        }
+    }
+
+    pub const fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub const fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub const fn size(&self) -> u32 {
+        self.size
+    }
+    pub const fn console_flags(&self) -> ConsoleHeaderTagFlags {
+        self.console_flags
+    }
+}
+
+impl StructAsBytes for ConsoleHeaderTag {}
+
+#[cfg(test)]
+mod tests {
+    use crate::{ConsoleHeaderTag, ConsoleHeaderTagFlags, HeaderTagFlag, HeaderTagType};
+    use std::mem::size_of_val;
+
+    /// Checks if rust aligns the type correctly and still "pack" all properties.
+    /// This test is necessary, because Rust doesn't support "packed" together with "align()" yet.
+    /// It seems like "packed(N)" does the right thing tho.
+    ///
+    /// This test is representative for all header tags, because all use the "packed(8)" attribute.
+    #[test]
+    fn test_alignment_and_size() {
+        let tag = ConsoleHeaderTag::new(
+            HeaderTagFlag::Required,
+            ConsoleHeaderTagFlags::ConsoleRequired,
+        );
+        let ptr = get_ptr!(tag, ConsoleHeaderTag);
+        let is_aligned = ptr % 8 == 0;
+        assert!(is_aligned);
+        // 2x u16, 2x u32
+        assert_eq!(2 + 2 + 4 + 4, size_of_val(&tag));
+
+        assert_eq!(ptr + 0, get_field_ptr!(tag, typ, HeaderTagType));
+        assert_eq!(ptr + 2, get_field_ptr!(tag, flags, HeaderTagFlag));
+        assert_eq!(ptr + 4, get_field_ptr!(tag, size, u32));
+        assert_eq!(
+            ptr + 8,
+            get_field_ptr!(tag, console_flags, ConsoleHeaderTagFlags)
+        );
+    }
+}

+ 36 - 0
multiboot2-header/src/end.rs

@@ -0,0 +1,36 @@
+use crate::{HeaderTagFlag, HeaderTagType, StructAsBytes};
+use core::mem::size_of;
+
+/// Terminates a list of optional tags
+/// in a Multiboot2 header.
+#[derive(Copy, Clone, Debug)]
+#[repr(C, packed(8))]
+pub struct EndHeaderTag {
+    // u16 value
+    typ: HeaderTagType,
+    // u16 value
+    flags: HeaderTagFlag,
+    size: u32,
+}
+
+impl EndHeaderTag {
+    pub const fn new() -> Self {
+        EndHeaderTag {
+            typ: HeaderTagType::End,
+            flags: HeaderTagFlag::Required,
+            size: size_of::<Self>() as u32,
+        }
+    }
+
+    pub const fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub const fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub const fn size(&self) -> u32 {
+        self.size
+    }
+}
+
+impl StructAsBytes for EndHeaderTag {}

+ 53 - 0
multiboot2-header/src/entry_efi_32.rs

@@ -0,0 +1,53 @@
+use crate::{HeaderTagFlag, HeaderTagType, StructAsBytes};
+use core::fmt;
+use core::fmt::{Debug, Formatter};
+use core::mem::size_of;
+
+/// 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
+/// tag of Multiboot2 header are ignored.
+#[derive(Copy, Clone)]
+#[repr(C, packed(8))]
+pub struct EntryEfi32HeaderTag {
+    typ: HeaderTagType,
+    flags: HeaderTagFlag,
+    size: u32,
+    entry_addr: u32,
+}
+
+impl EntryEfi32HeaderTag {
+    pub const fn new(flags: HeaderTagFlag, entry_addr: u32) -> Self {
+        EntryEfi32HeaderTag {
+            typ: HeaderTagType::EntryAddressEFI32,
+            flags,
+            size: size_of::<Self>() as u32,
+            entry_addr,
+        }
+    }
+
+    pub const fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub const fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub const fn size(&self) -> u32 {
+        self.size
+    }
+    pub const fn entry_addr(&self) -> u32 {
+        self.entry_addr
+    }
+}
+
+impl Debug for EntryEfi32HeaderTag {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        f.debug_struct("EntryEfi32HeaderTag")
+            .field("type", &{ self.typ })
+            .field("flags", &{ self.flags })
+            .field("size", &{ self.size })
+            .field("entry_addr", &(self.entry_addr as *const u32))
+            .finish()
+    }
+}
+
+impl StructAsBytes for EntryEfi32HeaderTag {}

+ 53 - 0
multiboot2-header/src/entry_efi_64.rs

@@ -0,0 +1,53 @@
+use crate::{HeaderTagFlag, HeaderTagType, StructAsBytes};
+use core::fmt;
+use core::fmt::{Debug, Formatter};
+use core::mem::size_of;
+
+/// 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
+/// tag of Multiboot2 header are ignored.
+#[derive(Copy, Clone)]
+#[repr(C, packed(8))]
+pub struct EntryEfi64HeaderTag {
+    typ: HeaderTagType,
+    flags: HeaderTagFlag,
+    size: u32,
+    entry_addr: u32,
+}
+
+impl EntryEfi64HeaderTag {
+    pub const fn new(flags: HeaderTagFlag, entry_addr: u32) -> Self {
+        EntryEfi64HeaderTag {
+            typ: HeaderTagType::EntryAddressEFI64,
+            flags,
+            size: size_of::<Self>() as u32,
+            entry_addr,
+        }
+    }
+
+    pub const fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub const fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub const fn size(&self) -> u32 {
+        self.size
+    }
+    pub const fn entry_addr(&self) -> u32 {
+        self.entry_addr
+    }
+}
+
+impl Debug for EntryEfi64HeaderTag {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        f.debug_struct("EntryEfi64HeaderTag")
+            .field("type", &{ self.typ })
+            .field("flags", &{ self.flags })
+            .field("size", &{ self.size })
+            .field("entry_addr", &(self.entry_addr as *const u32))
+            .finish()
+    }
+}
+
+impl StructAsBytes for EntryEfi64HeaderTag {}

+ 53 - 0
multiboot2-header/src/entry_header.rs

@@ -0,0 +1,53 @@
+use crate::{HeaderTagFlag, HeaderTagType, StructAsBytes};
+use core::fmt;
+use core::fmt::{Debug, Formatter};
+use core::mem::size_of;
+
+/// 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)]
+#[repr(C, packed(8))]
+pub struct EntryHeaderTag {
+    typ: HeaderTagType,
+    flags: HeaderTagFlag,
+    size: u32,
+    entry_addr: u32,
+}
+
+impl EntryHeaderTag {
+    pub const fn new(flags: HeaderTagFlag, entry_addr: u32) -> Self {
+        EntryHeaderTag {
+            typ: HeaderTagType::EntryAddress,
+            flags,
+            size: size_of::<Self>() as u32,
+            entry_addr,
+        }
+    }
+
+    pub const fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub const fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub const fn size(&self) -> u32 {
+        self.size
+    }
+    pub const fn entry_addr(&self) -> u32 {
+        self.entry_addr
+    }
+}
+
+impl Debug for EntryHeaderTag {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        f.debug_struct("EntryHeaderTag")
+            .field("type", &{ self.typ })
+            .field("flags", &{ self.flags })
+            .field("size", &{ self.size })
+            .field("entry_addr", &(self.entry_addr as *const u32))
+            .finish()
+    }
+}
+
+impl StructAsBytes for EntryHeaderTag {}

+ 51 - 0
multiboot2-header/src/framebuffer.rs

@@ -0,0 +1,51 @@
+use crate::{HeaderTagFlag, HeaderTagType, StructAsBytes};
+use core::mem::size_of;
+
+/// 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)]
+#[repr(C, packed(8))]
+pub struct FramebufferHeaderTag {
+    typ: HeaderTagType,
+    flags: HeaderTagFlag,
+    size: u32,
+    width: u32,
+    height: u32,
+    depth: u32,
+}
+
+impl FramebufferHeaderTag {
+    pub const fn new(flags: HeaderTagFlag, width: u32, height: u32, depth: u32) -> Self {
+        FramebufferHeaderTag {
+            typ: HeaderTagType::Framebuffer,
+            flags,
+            size: size_of::<Self>() as u32,
+            width,
+            height,
+            depth,
+        }
+    }
+
+    pub const fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub const fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub const fn size(&self) -> u32 {
+        self.size
+    }
+    pub const fn width(&self) -> u32 {
+        self.width
+    }
+    pub const fn height(&self) -> u32 {
+        self.height
+    }
+    pub const fn depth(&self) -> u32 {
+        self.depth
+    }
+}
+
+impl StructAsBytes for FramebufferHeaderTag {}

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

@@ -0,0 +1,288 @@
+//! Exports item [`Multiboot2HeaderBuilder`].
+
+use crate::HeaderTagISA;
+use crate::{
+    AddressHeaderTag, ConsoleHeaderTag, EfiBootServiceHeaderTag, EndHeaderTag, EntryEfi32HeaderTag,
+    EntryEfi64HeaderTag, EntryHeaderTag, FramebufferHeaderTag, InformationRequestHeaderTagBuilder,
+    ModuleAlignHeaderTag, Multiboot2BasicHeader, RelocatableHeaderTag, StructAsBytes,
+};
+use core::mem::size_of;
+
+/// 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(Debug)]
+pub struct Multiboot2HeaderBuilder {
+    arch: HeaderTagISA,
+    // first
+    information_request_tag: Option<InformationRequestHeaderTagBuilder>,
+    // second
+    address_tag: Option<AddressHeaderTag>,
+    // third
+    entry_tag: Option<EntryHeaderTag>,
+    // 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 Multiboot2HeaderBuilder {
+    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 {
+        let remainder = size % 8;
+        if remainder == 0 {
+            size
+        } else {
+            size + 8 - remainder
+        }
+    }
+
+    /// Returns the expected length of the Multiboot2 header,
+    /// when the `build()`-method gets called.
+    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::<EntryHeaderTag>())
+        }
+        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.
+    /// Align should be true for all tags except the end tag.
+    fn build_add_bytes(dest: &mut Vec<u8>, source: &[u8], is_end_tag: bool) {
+        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.
+    /// The bytes can be casted to a Multiboot2 structure.
+    pub fn build(mut self) -> Vec<u8> {
+        let mut data = Vec::new();
+
+        Self::build_add_bytes(
+            &mut data,
+            // 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 self.information_request_tag.is_some() {
+            Self::build_add_bytes(
+                &mut data,
+                &self.information_request_tag.take().unwrap().build(),
+                false,
+            )
+        }
+        if let Some(tag) = self.address_tag.as_ref() {
+            Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
+        }
+        if let Some(tag) = self.entry_tag.as_ref() {
+            Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
+        }
+        if let Some(tag) = self.console_tag.as_ref() {
+            Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
+        }
+        if let Some(tag) = self.framebuffer_tag.as_ref() {
+            Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
+        }
+        if let Some(tag) = self.module_align_tag.as_ref() {
+            Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
+        }
+        if let Some(tag) = self.efi_bs_tag.as_ref() {
+            Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
+        }
+        if let Some(tag) = self.efi_32_tag.as_ref() {
+            Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
+        }
+        if let Some(tag) = self.efi_64_tag.as_ref() {
+            Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
+        }
+        if let Some(tag) = self.relocatable_tag.as_ref() {
+            Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
+        }
+
+        Self::build_add_bytes(&mut data, &EndHeaderTag::new().struct_as_bytes(), true);
+
+        data
+    }
+
+    // clippy thinks this can be a const fn but the compiler denies it
+    #[allow(clippy::missing_const_for_fn)]
+    pub fn information_request_tag(
+        mut self,
+        information_request_tag: InformationRequestHeaderTagBuilder,
+    ) -> Self {
+        self.information_request_tag = Some(information_request_tag);
+        self
+    }
+    pub const fn address_tag(mut self, address_tag: AddressHeaderTag) -> Self {
+        self.address_tag = Some(address_tag);
+        self
+    }
+    pub const fn entry_tag(mut self, entry_tag: EntryHeaderTag) -> Self {
+        self.entry_tag = Some(entry_tag);
+        self
+    }
+    pub const fn console_tag(mut self, console_tag: ConsoleHeaderTag) -> Self {
+        self.console_tag = Some(console_tag);
+        self
+    }
+    pub const fn framebuffer_tag(mut self, framebuffer_tag: FramebufferHeaderTag) -> Self {
+        self.framebuffer_tag = Some(framebuffer_tag);
+        self
+    }
+    pub const fn module_align_tag(mut self, module_align_tag: ModuleAlignHeaderTag) -> Self {
+        self.module_align_tag = Some(module_align_tag);
+        self
+    }
+    pub const fn efi_bs_tag(mut self, efi_bs_tag: EfiBootServiceHeaderTag) -> Self {
+        self.efi_bs_tag = Some(efi_bs_tag);
+        self
+    }
+    pub const fn efi_32_tag(mut self, efi_32_tag: EntryEfi32HeaderTag) -> Self {
+        self.efi_32_tag = Some(efi_32_tag);
+        self
+    }
+    pub const fn efi_64_tag(mut self, efi_64_tag: EntryEfi64HeaderTag) -> Self {
+        self.efi_64_tag = Some(efi_64_tag);
+        self
+    }
+    pub const fn relocatable_tag(mut self, relocatable_tag: RelocatableHeaderTag) -> Self {
+        self.relocatable_tag = Some(relocatable_tag);
+        self
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::{
+        HeaderTagFlag, HeaderTagISA, InformationRequestHeaderTagBuilder, MbiTagType,
+        Multiboot2Header, Multiboot2HeaderBuilder, RelocatableHeaderTag,
+        RelocatableHeaderTagPreference,
+    };
+
+    #[test]
+    fn test_size_or_up_aligned() {
+        assert_eq!(0, Multiboot2HeaderBuilder::size_or_up_aligned(0));
+        assert_eq!(8, Multiboot2HeaderBuilder::size_or_up_aligned(1));
+        assert_eq!(8, Multiboot2HeaderBuilder::size_or_up_aligned(8));
+        assert_eq!(16, Multiboot2HeaderBuilder::size_or_up_aligned(9));
+    }
+
+    #[test]
+    fn test_size_builder() {
+        let builder = Multiboot2HeaderBuilder::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,
+        ));
+
+        println!("builder: {:#?}", builder);
+        println!("expected_len: {} bytes", builder.expected_len());
+
+        let mb2_hdr_data = builder.build();
+        let mb2_hdr = mb2_hdr_data.as_ptr() as usize;
+        let mb2_hdr = unsafe { Multiboot2Header::from_addr(mb2_hdr) };
+        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();*/
+    }
+}

+ 334 - 0
multiboot2-header/src/header/mod.rs

@@ -0,0 +1,334 @@
+//! Module for the main struct, which marks the begin of a Multiboot2 header.
+//! See [`Multiboot2Header`].
+
+pub mod builder;
+
+pub use self::builder::*;
+use crate::{AddressHeaderTag, InformationRequestHeaderTag, RelocatableHeaderTag, StructAsBytes};
+use crate::{ConsoleHeaderTag, EntryHeaderTag};
+use crate::{EfiBootServiceHeaderTag, FramebufferHeaderTag};
+use crate::{EndHeaderTag, HeaderTagType};
+use crate::{EntryEfi32HeaderTag, EntryEfi64HeaderTag};
+use crate::{HeaderTag, HeaderTagISA};
+use core::fmt::{Debug, Formatter};
+use core::mem::size_of;
+
+/// Magic value for a [`Multiboot2Header`], as defined in spec.
+pub const MULTIBOOT2_HEADER_MAGIC: u32 = 0xe85250d6;
+
+/// Wrapper type around a pointer to the Multiboot2 header.
+/// The Multiboot2 header is the [`Multiboot2BasicHeader`] followed
+/// by all tags (see [`crate::tags::HeaderTagType`]).
+/// Use this if you get a pointer to the header and just want
+/// to parse it. If you want to construct the type by yourself,
+/// please look at [`builder::Multiboot2HeaderBuilder`].
+#[repr(transparent)]
+pub struct Multiboot2Header<'a> {
+    inner: &'a Multiboot2BasicHeader,
+}
+
+impl<'a> Multiboot2Header<'a> {
+    /// Public constructor for this type with various validations. It panics if the address is invalid.
+    /// It panics rather than returning a result, because if this fails, it is
+    /// a fatal, unrecoverable error anyways and a bug in your code.
+    ///
+    /// # Panics
+    /// Panics if one of the following conditions is true:
+    /// - `addr` is a null-pointer
+    /// - `addr` isn't 8-byte aligned
+    /// - the magic value of the header is not present
+    /// - the checksum field is invalid
+    ///
+    /// # Safety
+    /// This function may produce undefined behaviour, if the provided `addr` is not a valid
+    /// Multiboot2 header pointer.
+    pub unsafe fn from_addr(addr: usize) -> Self {
+        assert_ne!(0, addr, "`addr` is null pointer");
+        assert_eq!(
+            addr % 8,
+            0,
+            "`addr` must be 8-byte aligned, see Multiboot2 spec"
+        );
+        let ptr = addr as *const Multiboot2BasicHeader;
+        let reference = &*ptr;
+        assert_eq!(
+            reference.header_magic(),
+            MULTIBOOT2_HEADER_MAGIC,
+            "The Multiboot2 header must contain the MULTIBOOT2_HEADER_MAGIC={:x}",
+            MULTIBOOT2_HEADER_MAGIC
+        );
+        assert!(
+            reference.verify_checksum(),
+            "checksum invalid! Is {:x}, expected {:x}",
+            reference.checksum(),
+            Self::calc_checksum(reference.header_magic, reference.arch, reference.length)
+        );
+        Self { inner: reference }
+    }
+
+    /// Wrapper around [`Multiboot2BasicHeader::verify_checksum`].
+    pub const fn verify_checksum(&self) -> bool {
+        self.inner.verify_checksum()
+    }
+    /// Wrapper around [`Multiboot2BasicHeader::header_magic`].
+    pub const fn header_magic(&self) -> u32 {
+        self.inner.header_magic()
+    }
+    /// Wrapper around [`Multiboot2BasicHeader::arch`].
+    pub const fn arch(&self) -> HeaderTagISA {
+        self.inner.arch()
+    }
+    /// Wrapper around [`Multiboot2BasicHeader::length`].
+    pub const fn length(&self) -> u32 {
+        self.inner.length()
+    }
+    /// Wrapper around [`Multiboot2BasicHeader::checksum`].
+    pub const fn checksum(&self) -> u32 {
+        self.inner.checksum()
+    }
+    /// Wrapper around [`Multiboot2BasicHeader::tag_iter`].
+    pub fn iter(&self) -> Multiboot2HeaderTagIter {
+        self.inner.tag_iter()
+    }
+    /// Wrapper around [`Multiboot2BasicHeader::calc_checksum`].
+    pub const fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 {
+        Multiboot2BasicHeader::calc_checksum(magic, arch, length)
+    }
+}
+
+impl<'a> Debug for Multiboot2Header<'a> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
+        // For debug fmt we only output the inner field
+        let reference = unsafe { &*(self.inner as *const Multiboot2BasicHeader) };
+        Debug::fmt(reference, f)
+    }
+}
+
+/// **Use this only if you know what you do. You probably want to use
+/// [`Multiboot2Header`] instead.**
+///
+/// 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)]
+#[repr(C, packed(8))]
+pub struct Multiboot2BasicHeader {
+    /// Must be the value of [`MULTIBOOT2_HEADER_MAGIC`].
+    header_magic: u32,
+    arch: HeaderTagISA,
+    length: u32,
+    checksum: u32,
+    // additional tags..
+    // at minimum the end tag
+}
+
+impl Multiboot2BasicHeader {
+    /// Constructor for the basic header.
+    pub(crate) const fn new(arch: HeaderTagISA, length: u32) -> Self {
+        let magic = MULTIBOOT2_HEADER_MAGIC;
+        let checksum = Self::calc_checksum(magic, arch, length);
+        Multiboot2BasicHeader {
+            header_magic: magic,
+            arch,
+            length,
+            checksum,
+        }
+    }
+
+    /// Verifies that a Multiboot2 header is valid.
+    pub const fn verify_checksum(&self) -> bool {
+        let check = Self::calc_checksum(self.header_magic, self.arch, self.length);
+        check == self.checksum
+    }
+
+    /// Calculates the checksum as described in the spec.
+    pub const fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 {
+        (0x100000000 - magic as u64 - arch as u64 - length as u64) as u32
+    }
+
+    /// Returns
+    pub const fn header_magic(&self) -> u32 {
+        self.header_magic
+    }
+    pub const fn arch(&self) -> HeaderTagISA {
+        self.arch
+    }
+    pub const fn length(&self) -> u32 {
+        self.length
+    }
+    pub const fn checksum(&self) -> u32 {
+        self.checksum
+    }
+
+    /// Returns a [`Multiboot2HeaderTagIter`].
+    ///
+    /// # Panics
+    /// See doc of [`Multiboot2HeaderTagIter`].
+    pub fn tag_iter(&self) -> Multiboot2HeaderTagIter {
+        let base_hdr_size = size_of::<Multiboot2BasicHeader>();
+        if base_hdr_size == self.length as usize {
+            panic!("No end tag!");
+        }
+        let tag_base_addr = self as *const Multiboot2BasicHeader;
+        // 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 HeaderTag;
+        let tags_len = self.length as usize - base_hdr_size;
+        Multiboot2HeaderTagIter::new(tag_base_addr, tags_len as u32)
+    }
+}
+
+impl Debug for Multiboot2BasicHeader {
+    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
+        f.debug_struct("Multiboot2Header")
+            .field("header_magic", &{ self.header_magic })
+            .field("arch", &{ self.arch })
+            .field("length", &{ self.length })
+            .field("checksum", &{ self.checksum })
+            .field("tags", &self.tag_iter())
+            .finish()
+    }
+}
+
+impl StructAsBytes for Multiboot2BasicHeader {}
+
+/// 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 HeaderTag,
+    /// 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 HeaderTag, 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 HeaderTag;
+        Self {
+            base,
+            n: 0,
+            size,
+            tag_count: 0,
+            end_tag_found: false,
+        }
+    }
+}
+
+impl Iterator for Multiboot2HeaderTagIter {
+    type Item = *const HeaderTag;
+
+    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 HeaderTag;
+        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 EntryHeaderTag;
+                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::Relocatable {
+                let entry = t as *const RelocatableHeaderTag;
+                let entry = &*(entry);
+                debug.entry(entry);
+            } else {
+                panic!("unknown tag ({:?})!", typ);
+            }
+        });
+        debug.finish()
+    }
+}

+ 244 - 0
multiboot2-header/src/information_request.rs

@@ -0,0 +1,244 @@
+use crate::{HeaderTagFlag, MbiTagType};
+use crate::{HeaderTagType, StructAsBytes};
+use core::fmt;
+use core::fmt::{Debug, Formatter};
+use core::marker::PhantomData;
+use core::mem::size_of;
+use std::collections::HashSet;
+
+/// Specifies what specific tag types the bootloader should provide
+/// inside the mbi.
+#[derive(Copy, Clone)]
+#[repr(C, packed(8))]
+pub struct InformationRequestHeaderTag<const N: usize> {
+    typ: HeaderTagType,
+    flags: HeaderTagFlag,
+    size: u32,
+    // Length is determined by size.
+    // Must be parsed during runtime with unsafe pointer magic and the size field.
+    requests: [MbiTagType; N],
+}
+
+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.
+    pub fn new(flags: HeaderTagFlag, requests: [MbiTagType; N], size: Option<u32>) -> Self {
+        InformationRequestHeaderTag {
+            typ: HeaderTagType::InformationRequest,
+            flags,
+            size: size.unwrap_or(size_of::<Self>() as u32),
+            requests,
+        }
+    }
+
+    pub const fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub const fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub const fn size(&self) -> u32 {
+        self.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()`.
+    pub const fn requests(&self) -> [MbiTagType; 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.
+    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`].
+    pub 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 InformationRequestHeaderTag<N>;
+        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 MbiTagType;
+        InformationRequestHeaderTagIter::new(count, base_ptr)
+    }
+}
+
+impl<const N: usize> Debug for InformationRequestHeaderTag<N> {
+    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() })
+            .finish()
+    }
+}
+
+impl<const N: usize> StructAsBytes for InformationRequestHeaderTag<N> {}
+
+/// Helper to build the dynamically sized [`InformationRequestHeaderTag`]
+/// at runtime.
+#[derive(Debug)]
+pub struct InformationRequestHeaderTagBuilder {
+    flag: HeaderTagFlag,
+    irs: HashSet<MbiTagType>,
+}
+
+impl InformationRequestHeaderTagBuilder {
+    /// New builder.
+    pub fn new(flag: HeaderTagFlag) -> Self {
+        Self {
+            irs: HashSet::new(),
+            flag,
+        }
+    }
+
+    /// Returns the expected length of the information request tag,
+    /// when the `build`-method gets called.
+    pub fn expected_len(&self) -> usize {
+        let basic_header_size = size_of::<InformationRequestHeaderTag<0>>();
+        let req_tags_size = self.irs.len() * size_of::<MbiTagType>();
+        basic_header_size + req_tags_size
+    }
+
+    /// Adds an [`MbiTagType`] to the information request.
+    pub fn add_ir(mut self, tag: MbiTagType) -> Self {
+        self.irs.insert(tag);
+        self
+    }
+
+    /// Adds multiple [`MbiTagType`] to the information request.
+    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 in &self.irs {
+            let bytes: [u8; 4] = (*tag as u32).to_ne_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
+    }
+}
+
+/// 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 MbiTagType,
+    i: u32,
+    count: u32,
+    _marker: PhantomData<&'a ()>,
+}
+
+impl<'a> InformationRequestHeaderTagIter<'a> {
+    fn new(count: u32, base_ptr: *const MbiTagType) -> Self {
+        Self {
+            i: 0,
+            count,
+            base_ptr,
+            _marker: PhantomData::default(),
+        }
+    }
+}
+
+impl<'a> Iterator for InformationRequestHeaderTagIter<'a> {
+    type Item = &'a 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;
+            Some(unsafe { &*ptr })
+        } else {
+            None
+        }
+    }
+}
+
+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()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::{
+        HeaderTagFlag, InformationRequestHeaderTag, InformationRequestHeaderTagBuilder, MbiTagType,
+    };
+
+    #[test]
+    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(&MbiTagType::EfiMmap));
+        assert!(tag.requests.contains(&MbiTagType::BootLoaderName));
+        assert!(tag.requests.contains(&MbiTagType::Cmdline));
+        assert_eq!(tag.requests.len(), 3);
+        assert!(!tag.requests.contains(&MbiTagType::AcpiV1));
+        println!("{:#?}", tag);
+    }
+}

+ 127 - 1
multiboot2-header/src/lib.rs

@@ -1 +1,127 @@
-//! TODO
+//! Library with type definitions and parsing functions for Multiboot2 headers.
+//! This library is `no_std` and can be used in bootloaders.
+//!
+//! # Example
+//! ```rust
+//! use multiboot2_header::builder::Multiboot2HeaderBuilder;
+//! use multiboot2_header::{HeaderTagFlag, HeaderTagISA, InformationRequestHeaderTagBuilder, 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 = Multiboot2HeaderBuilder::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::from_addr(mb2_hdr_bytes.as_ptr() as usize) };
+//! println!("{:#?}", mb2_hdr);
+//!
+//! ```
+
+#![deny(rustdoc::all)]
+#![allow(rustdoc::missing_doc_code_examples)]
+#![deny(clippy::all)]
+#![deny(clippy::missing_const_for_fn)]
+#![deny(missing_debug_implementations)]
+
+#[cfg_attr(test, macro_use)]
+#[cfg(test)]
+pub(crate) mod test_utils;
+
+mod address;
+mod console;
+mod end;
+mod entry_efi_32;
+mod entry_efi_64;
+mod entry_header;
+mod framebuffer;
+mod header;
+mod information_request;
+mod module_alignment;
+mod relocatable;
+mod tags;
+mod uefi_bs;
+
+pub use self::address::*;
+pub use self::console::*;
+pub use self::end::*;
+pub use self::entry_efi_32::*;
+pub use self::entry_efi_64::*;
+pub use self::entry_header::*;
+pub use self::framebuffer::*;
+pub use self::header::*;
+pub use self::information_request::*;
+pub use self::module_alignment::*;
+pub use self::relocatable::*;
+pub use self::tags::*;
+pub use self::uefi_bs::*;
+
+/// Re-export of [`multiboot2::TagType`] from `multiboot2`-crate as `MbiTagType`, i.e. tags that
+/// describe the entries in the Multiboot2 Information Structure (MBI).
+pub use multiboot2::TagType as MbiTagType;
+use std::mem::size_of;
+
+/// Trait for all tags that creates a byte array from the tag.
+/// Useful in builders to construct a byte vector that
+/// represents the Multiboot2 header with all its tags.
+pub(crate) 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 [`size`].
+    fn struct_as_bytes(&self) -> Vec<u8> {
+        let ptr = self.as_ptr();
+        let mut vec = Vec::with_capacity(self.byte_size());
+        for i in 0..self.byte_size() {
+            vec.push(unsafe { *ptr.add(i) })
+        }
+        vec
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_as_bytes() {
+        struct Foobar {
+            a: u32,
+            b: u8,
+            c: u128,
+        }
+        impl StructAsBytes for Foobar {}
+        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);
+    }
+}

+ 33 - 0
multiboot2-header/src/module_alignment.rs

@@ -0,0 +1,33 @@
+use crate::{HeaderTagFlag, HeaderTagType, StructAsBytes};
+use core::mem::size_of;
+
+/// If this tag is present, provided boot modules must be page aligned.
+#[derive(Copy, Clone, Debug)]
+#[repr(C, packed(8))]
+pub struct ModuleAlignHeaderTag {
+    typ: HeaderTagType,
+    flags: HeaderTagFlag,
+    size: u32,
+}
+
+impl ModuleAlignHeaderTag {
+    pub const fn new(flags: HeaderTagFlag) -> Self {
+        ModuleAlignHeaderTag {
+            typ: HeaderTagType::ModuleAlign,
+            flags,
+            size: size_of::<Self>() as u32,
+        }
+    }
+
+    pub const fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub const fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub const fn size(&self) -> u32 {
+        self.size
+    }
+}
+
+impl StructAsBytes for ModuleAlignHeaderTag {}

+ 94 - 0
multiboot2-header/src/relocatable.rs

@@ -0,0 +1,94 @@
+use crate::{HeaderTagFlag, HeaderTagType, StructAsBytes};
+use core::fmt;
+use core::fmt::{Debug, Formatter};
+use core::mem::size_of;
+
+/// 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
+/// but not lower than min addr and ‘2’ means load image at highest possible
+/// address but not higher than max addr.
+#[repr(u32)]
+#[derive(Copy, Clone, Debug)]
+pub enum RelocatableHeaderTagPreference {
+    /// Let boot loader decide.
+    None = 0,
+    /// Locate at lower end of possible address space.
+    Low = 1,
+    /// Locate at higher end of possible address space.
+    High = 2,
+}
+
+/// This tag indicates that the image is relocatable.
+#[derive(Copy, Clone)]
+#[repr(C, packed(8))]
+pub struct RelocatableHeaderTag {
+    typ: HeaderTagType,
+    flags: HeaderTagFlag,
+    size: u32,
+    /// Lowest possible physical address at which image should be loaded. The bootloader cannot load any part of image below this address
+    min_addr: u32,
+    /// Highest possible physical address at which loaded image should end. The bootloader cannot load any part of image above this address.
+    max_addr: u32,
+    /// Image alignment in memory, e.g. 4096.
+    align: u32,
+    preference: RelocatableHeaderTagPreference,
+}
+
+impl RelocatableHeaderTag {
+    pub const fn new(
+        flags: HeaderTagFlag,
+        min_addr: u32,
+        max_addr: u32,
+        align: u32,
+        preference: RelocatableHeaderTagPreference,
+    ) -> Self {
+        RelocatableHeaderTag {
+            typ: HeaderTagType::Relocatable,
+            flags,
+            size: size_of::<Self>() as u32,
+            min_addr,
+            max_addr,
+            align,
+            preference,
+        }
+    }
+
+    pub const fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub const fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub const fn size(&self) -> u32 {
+        self.size
+    }
+    pub const fn min_addr(&self) -> u32 {
+        self.min_addr
+    }
+    pub const fn max_addr(&self) -> u32 {
+        self.max_addr
+    }
+    pub const fn align(&self) -> u32 {
+        self.align
+    }
+    pub const fn preference(&self) -> RelocatableHeaderTagPreference {
+        self.preference
+    }
+}
+
+impl Debug for RelocatableHeaderTag {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        f.debug_struct("RelocatableHeaderTag")
+            .field("type", &{ self.typ })
+            .field("flags", &{ self.flags })
+            .field("size", &{ self.size })
+            // trick to print this as hexadecimal pointer
+            .field("min_addr", &(self.min_addr as *const u32))
+            .field("max_addr", &(self.max_addr as *const u32))
+            .field("align", &{ self.align })
+            .field("preference", &{ self.preference })
+            .finish()
+    }
+}
+
+impl StructAsBytes for RelocatableHeaderTag {}

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

@@ -0,0 +1,88 @@
+//! Definition for all types of "Multiboot2 header tags". The values are taken from the example C
+//! code at the end of the official Multiboot2 spec. These tags follow in memory right after
+//! [`crate::Multiboot2BasicHeader`].
+
+use core::fmt::Debug;
+
+/// ISA/ARCH in Multiboot2 header.
+#[repr(u32)]
+#[derive(Copy, Clone, Debug)]
+pub enum HeaderTagISA {
+    /// Spec: "means 32-bit (protected) mode of i386".
+    /// Caution: This is confusing. If you use the EFI64-tag
+    /// on an UEFI system, you get into `64-bit long mode`.
+    /// Therefore this tag should be understood as "arch=x86|x86_64".
+    I386 = 0,
+    /// 32-bit MIPS
+    MIPS32 = 4,
+}
+
+/// Possible types for header tags of a Multiboot2 header. The names and values are taken
+/// from the example C code at the bottom of the Multiboot2 specification. This value
+/// stands in the `typ` property of [`crate::tags::HeaderTag`].
+#[repr(u16)]
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum HeaderTagType {
+    /// Type for [`crate::EndHeaderTag`].
+    End = 0,
+    /// Type for [`crate::InformationRequestHeaderTag`].
+    InformationRequest = 1,
+    /// Type for [`crate::AddressHeaderTag`].
+    Address = 2,
+    /// Type for [`crate::EntryHeaderTag`].
+    EntryAddress = 3,
+    /// Type for [`crate::ConsoleHeaderTag`].
+    ConsoleFlags = 4,
+    /// Type for [`crate::FramebufferHeaderTag`].
+    Framebuffer = 5,
+    /// Type for [`crate::ModuleAlignHeaderTag`].
+    ModuleAlign = 6,
+    /// Type for [`crate::EfiBootServiceHeaderTag`].
+    EfiBS = 7,
+    /// Type for [`crate::EntryEfi32HeaderTag`].
+    EntryAddressEFI32 = 8,
+    /// Type for [`crate::EntryEfi64HeaderTag`].
+    EntryAddressEFI64 = 9,
+    /// Type for [`crate::RelocatableHeaderTag`].
+    Relocatable = 10,
+}
+
+impl HeaderTagType {
+    /// Returns the number of possible variants.
+    pub const fn count() -> u32 {
+        11
+    }
+}
+
+/// Flags for Multiboot2 header tags.
+#[repr(u16)]
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum HeaderTagFlag {
+    Required = 0,
+    Optional = 1,
+}
+
+/// Common properties for all header tags. Other tags may have additional fields
+/// that depend on the `typ` and the `size` field.
+#[derive(Copy, Clone, Debug)]
+#[repr(C, packed(8))]
+pub struct HeaderTag {
+    // u16 value
+    typ: HeaderTagType,
+    // u16 value
+    flags: HeaderTagFlag,
+    size: u32,
+    // maybe additional fields (tag specific)
+}
+
+impl HeaderTag {
+    pub const fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub const fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub const fn size(&self) -> u32 {
+        self.size
+    }
+}

+ 91 - 0
multiboot2-header/src/test_utils.rs

@@ -0,0 +1,91 @@
+//! Test utilities.
+
+use crate::{HeaderTagFlag, HeaderTagType};
+use std::mem::size_of;
+
+/// Returns the pointer to a variable value in memory.
+#[macro_export]
+macro_rules! get_ptr {
+    (&$tag_ref: ident, $type: ty) => {
+        (&$tag_ref) as *const $type as u64
+    };
+    ($tag: ident, $type: ty) => {
+        get_ptr!(&$tag, $type)
+    };
+}
+
+/// This macro helps to get the pointer to a specific field
+/// and is helpful to check what Rust produces from the structs,
+/// i.e. in scenarios with "packed" and alignments.
+#[macro_export]
+macro_rules! get_field_ptr {
+    ($tag: ident, $field: ident, $type: ty) => {
+        (&$tag.$field) as *const $type as u64
+    };
+}
+
+#[repr(C, packed(8))]
+struct DummyHeaderTag {
+    typ: HeaderTagType,
+    flags: HeaderTagFlag,
+    size: u32,
+    foo: u32,
+    bar: u32,
+}
+
+#[test]
+fn test_macro_get_ptr() {
+    let tag = DummyHeaderTag {
+        typ: HeaderTagType::End,
+        flags: HeaderTagFlag::Required,
+        size: 0,
+        foo: 0,
+        bar: 0,
+    };
+
+    let expected_base_ptr = (&tag) as *const DummyHeaderTag as u64;
+    let actual_base_ptr1 = get_ptr!(tag, DummyHeaderTag);
+    let actual_base_ptr2 = get_ptr!(&tag, DummyHeaderTag);
+    assert_eq!(expected_base_ptr, actual_base_ptr1);
+    assert_eq!(actual_base_ptr1, actual_base_ptr2);
+}
+
+#[test]
+fn test_macro_get_field_ptr() {
+    let tag = DummyHeaderTag {
+        typ: HeaderTagType::End,
+        flags: HeaderTagFlag::Required,
+        size: 0,
+        foo: 0,
+        bar: 0,
+    };
+
+    let base_ptr = get_ptr!(tag, DummyHeaderTag);
+
+    let prop0_ptr = get_field_ptr!(tag, typ, HeaderTagType);
+    let prop1_ptr = get_field_ptr!(tag, flags, HeaderTagFlag);
+    let prop2_ptr = get_field_ptr!(tag, size, u32);
+    let prop3_ptr = get_field_ptr!(tag, foo, u32);
+    let prop4_ptr = get_field_ptr!(tag, bar, u32);
+
+    assert_eq!(
+        prop0_ptr,
+        base_ptr + 0 * size_of::<u16>() as u64 + 0 * size_of::<u32>() as u64
+    );
+    assert_eq!(
+        prop1_ptr,
+        base_ptr + 1 * size_of::<u16>() as u64 + 0 * size_of::<u32>() as u64
+    );
+    assert_eq!(
+        prop2_ptr,
+        base_ptr + 2 * size_of::<u16>() as u64 + 0 * size_of::<u32>() as u64
+    );
+    assert_eq!(
+        prop3_ptr,
+        base_ptr + 2 * size_of::<u16>() as u64 + 1 * size_of::<u32>() as u64
+    );
+    assert_eq!(
+        prop4_ptr,
+        base_ptr + 2 * size_of::<u16>() as u64 + 2 * size_of::<u32>() as u64
+    );
+}

+ 34 - 0
multiboot2-header/src/uefi_bs.rs

@@ -0,0 +1,34 @@
+use crate::{HeaderTagFlag, HeaderTagType, StructAsBytes};
+use core::mem::size_of;
+
+/// 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)]
+#[repr(C, packed(8))]
+pub struct EfiBootServiceHeaderTag {
+    typ: HeaderTagType,
+    flags: HeaderTagFlag,
+    size: u32,
+}
+
+impl EfiBootServiceHeaderTag {
+    pub const fn new(flags: HeaderTagFlag) -> Self {
+        EfiBootServiceHeaderTag {
+            typ: HeaderTagType::EfiBS,
+            flags,
+            size: size_of::<Self>() as u32,
+        }
+    }
+
+    pub const fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub const fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub const fn size(&self) -> u32 {
+        self.size
+    }
+}
+
+impl StructAsBytes for EfiBootServiceHeaderTag {}

+ 1 - 1
multiboot2/src/lib.rs

@@ -389,7 +389,7 @@ impl Reader {
 
     pub(crate) fn read_u8(&mut self) -> u8 {
         self.off += 1;
-        unsafe { core::ptr::read(self.ptr.add(self.off - 1)) }
+        unsafe { *self.ptr.add(self.off - 1) }
     }
 
     pub(crate) fn read_u16(&mut self) -> u16 {