Bladeren bron

multiboot2-header: initial version of crate

Philipp Schuster 3 jaren geleden
bovenliggende
commit
8ea06c7012

+ 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"

+ 39 - 0
multiboot2-header/README.md

@@ -6,6 +6,45 @@
 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
+```rust
+use multiboot2_header::builder::Multiboot2HeaderBuilder;
+use multiboot2_header::{ConsoleHeaderTag, HeaderTagFlag, HeaderTagISA, InformationRequestHeaderTagBuilder, MbiTagType, Multiboot2Header, RelocatableHeaderTag, RelocatableHeaderTagPreference, load_mb2_header};
+
+/// 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 { load_mb2_header(mb2_hdr_bytes.as_ptr() as usize) };
+    println!("{:#?}", mb2_hdr);
+}
+
+```
+
 ## License & Contribution
 
 See main [README](https://github.com/rust-osdev/multiboot2/blob/main/README.md) file.

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

@@ -0,0 +1,25 @@
+use multiboot2_header::builder::Multiboot2HeaderBuilder;
+use multiboot2_header::{ConsoleHeaderTag, HeaderTagFlag, HeaderTagISA, InformationRequestHeaderTagBuilder, MbiTagType, Multiboot2Header, RelocatableHeaderTag, RelocatableHeaderTagPreference, load_mb2_header};
+
+/// 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 { load_mb2_header(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 fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub fn size(&self) -> u32 {
+        self.size
+    }
+    pub fn header_addr(&self) -> u32 {
+        self.header_addr
+    }
+    pub fn load_addr(&self) -> u32 {
+        self.load_addr
+    }
+    pub fn load_end_addr(&self) -> u32 {
+        self.load_end_addr
+    }
+    pub fn bss_end_addr(&self) -> u32 {
+        self.bss_end_addr
+    }
+}
+
+impl StructAsBytes for AddressHeaderTag {}

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

@@ -0,0 +1,80 @@
+use crate::{HeaderTagFlag, HeaderTagType, StructAsBytes};
+use core::mem::size_of;
+
+#[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 fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub fn size(&self) -> u32 {
+        self.size
+    }
+    pub 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 fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub 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 fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub fn size(&self) -> u32 {
+        self.size
+    }
+    pub 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 fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub fn size(&self) -> u32 {
+        self.size
+    }
+    pub 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 fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub fn size(&self) -> u32 {
+        self.size
+    }
+    pub 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 fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub fn size(&self) -> u32 {
+        self.size
+    }
+    pub fn width(&self) -> u32 {
+        self.width
+    }
+    pub fn height(&self) -> u32 {
+        self.height
+    }
+    pub fn depth(&self) -> u32 {
+        self.depth
+    }
+}
+
+impl StructAsBytes for FramebufferHeaderTag {}

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

@@ -0,0 +1,284 @@
+use crate::HeaderTagISA;
+use crate::{
+    AddressHeaderTag, ConsoleHeaderTag, EfiBootServiceHeaderTag, EndHeaderTag, EntryEfi32HeaderTag,
+    EntryEfi64HeaderTag, EntryHeaderTag, FramebufferHeaderTag, InformationRequestHeaderTagBuilder,
+    ModuleAlignHeaderTag, Multiboot2HeaderInner, 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::<Multiboot2HeaderInner>();
+        // 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: &Vec<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!
+            &Multiboot2HeaderInner::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
+    }
+
+    pub fn information_request_tag(
+        mut self,
+        information_request_tag: InformationRequestHeaderTagBuilder,
+    ) -> Self {
+        self.information_request_tag = Some(information_request_tag);
+        self
+    }
+    pub fn address_tag(mut self, address_tag: AddressHeaderTag) -> Self {
+        self.address_tag = Some(address_tag);
+        self
+    }
+    pub fn entry_tag(mut self, entry_tag: EntryHeaderTag) -> Self {
+        self.entry_tag = Some(entry_tag);
+        self
+    }
+    pub fn console_tag(mut self, console_tag: ConsoleHeaderTag) -> Self {
+        self.console_tag = Some(console_tag);
+        self
+    }
+    pub fn framebuffer_tag(mut self, framebuffer_tag: FramebufferHeaderTag) -> Self {
+        self.framebuffer_tag = Some(framebuffer_tag);
+        self
+    }
+    pub fn module_align_tag(mut self, module_align_tag: ModuleAlignHeaderTag) -> Self {
+        self.module_align_tag = Some(module_align_tag);
+        self
+    }
+    pub fn efi_bs_tag(mut self, efi_bs_tag: EfiBootServiceHeaderTag) -> Self {
+        self.efi_bs_tag = Some(efi_bs_tag);
+        self
+    }
+    pub fn efi_32_tag(mut self, efi_32_tag: EntryEfi32HeaderTag) -> Self {
+        self.efi_32_tag = Some(efi_32_tag);
+        self
+    }
+    pub fn efi_64_tag(mut self, efi_64_tag: EntryEfi64HeaderTag) -> Self {
+        self.efi_64_tag = Some(efi_64_tag);
+        self
+    }
+    pub fn relocatable_tag(mut self, relocatable_tag: RelocatableHeaderTag) -> Self {
+        self.relocatable_tag = Some(relocatable_tag);
+        self
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::{
+        load_mb2_header, HeaderTagFlag, HeaderTagISA, InformationRequestHeaderTagBuilder,
+        MbiTagType, 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 { load_mb2_header(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();*/
+    }
+}

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

@@ -0,0 +1,271 @@
+pub mod builder;
+
+pub use self::builder::*;
+use crate::{
+    AddressHeaderTag, InformationRequestHeaderTag, RelocatableHeaderTag, StructAsBytes,
+    MULTIBOOT2_HEADER_MAGIC,
+};
+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;
+
+/// Wrapper type around a pointer to the Multiboot2 header.
+/// 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 [`Multiboot2HeaderInner`].
+#[repr(transparent)]
+pub struct Multiboot2Header<'a> {
+    inner: &'a Multiboot2HeaderInner,
+}
+
+impl<'a> Multiboot2Header<'a> {
+    /// Creates a new [`Multiboot2Header`]. Verifies the checksum.
+    pub fn new(inner: &'a Multiboot2HeaderInner) -> Self {
+        assert!(inner.verify_checksum(), "checksum invalid!");
+        Self { inner }
+    }
+
+    /*
+      Nope, this just results in really ugly code :/
+      I didn't find a nice way to solve this.
+
+    /// * `additional_tag_length`: additional tag length including end tag
+    pub fn new(arch: ISA,
+               additional_tag_length: u32) -> (Multiboot2HeaderInner, Self) {
+        let inner = Multiboot2HeaderInner::new(
+            arch,
+            (size_of::<Multiboot2HeaderInner>() + additional_tag_length as usize) as u32
+        );
+        let inner_ref = &inner;
+        (inner, Self{inner: inner_ref,})
+    }*/
+
+    /// Returns the size a static multiboot2 header without tags has.
+    pub fn static_size() -> u32 {
+        size_of::<Multiboot2HeaderInner>() as u32
+    }
+
+    pub fn verify_checksum(&self) -> bool {
+        self.inner.verify_checksum()
+    }
+
+    pub fn header_magic(&self) -> u32 {
+        self.inner.header_magic()
+    }
+    pub fn arch(&self) -> HeaderTagISA {
+        self.inner.arch()
+    }
+    pub fn length(&self) -> u32 {
+        self.inner.length()
+    }
+    pub fn checksum(&self) -> u32 {
+        self.inner.checksum()
+    }
+    pub fn iter(&self) -> Multiboot2HeaderTagIter {
+        self.inner.tag_iter()
+    }
+
+    pub fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 {
+        (0x100000000 - magic as u64 - arch as u64 - length as u64) as u32
+    }
+}
+
+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 Multiboot2HeaderInner) };
+        Debug::fmt(reference, f)
+    }
+}
+
+/// The actual multiboot2 header. The size is not known at
+/// compile time and can only be derived during runtime from
+/// the iteration of the tags until the end tag.
+///
+/// Use this only if you know what you do. You probably want to use
+/// [`Multiboot2Header`]. If you want to construct a Multiboot2 header at
+/// runtime, see [`builder::Multiboot2HeaderBuilder`].
+#[derive(Copy, Clone)]
+#[repr(C, packed(8))]
+pub struct Multiboot2HeaderInner {
+    /// 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 Multiboot2HeaderInner {
+    /// Constructs a new
+    pub const fn new(arch: HeaderTagISA, length: u32) -> Self {
+        let magic = MULTIBOOT2_HEADER_MAGIC;
+        let checksum = Self::calc_checksum(magic, arch, length);
+        Multiboot2HeaderInner {
+            header_magic: magic,
+            arch,
+            length,
+            checksum,
+        }
+    }
+
+    /// Verifies that a multiboot2 header is valid
+    pub(super) fn verify_checksum(&self) -> bool {
+        let check = Self::calc_checksum(self.header_magic, self.arch, self.length);
+        check == self.checksum
+    }
+
+    const fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 {
+        (0x100000000 - magic as u64 - arch as u64 - length as u64) as u32
+    }
+
+    fn header_magic(&self) -> u32 {
+        self.header_magic
+    }
+    fn arch(&self) -> HeaderTagISA {
+        self.arch
+    }
+    fn length(&self) -> u32 {
+        self.length
+    }
+    fn checksum(&self) -> u32 {
+        self.checksum
+    }
+
+    fn tag_iter(&self) -> Multiboot2HeaderTagIter {
+        let base_hdr_size = size_of::<Multiboot2HeaderInner>();
+        if base_hdr_size == self.length as usize {
+            panic!("No end tag!");
+        }
+        let tag_base_addr = self as *const Multiboot2HeaderInner;
+        // 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.offset(base_hdr_size as isize) };
+        // align pointer to 8 byte according to spec
+        let tag_base_addr = unsafe { tag_base_addr.offset(tag_base_addr.align_offset(8) as isize) };
+        // 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 Multiboot2HeaderInner {
+    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 Multiboot2HeaderInner {}
+
+#[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,
+}
+
+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.offset(base.align_offset(8) as isize) };
+        let base = base as *const HeaderTag;
+        Self { base, n: 0, size }
+    }
+}
+
+impl Iterator for Multiboot2HeaderTagIter {
+    type Item = *const HeaderTag;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.n < self.size {
+            // transform to byte ptr => offset works correctly
+            let ptr = self.base as *const u8;
+            let ptr = unsafe { ptr.offset(self.n as isize) };
+            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()}
+            );
+            self.n += tag.size();
+            // 8-byte alignment of pointer address
+            self.n += self.n % 8;
+            Some(ptr)
+        } else {
+            None
+        }
+    }
+}
+
+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()
+    }
+}

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

@@ -0,0 +1,241 @@
+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 fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub 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 fn requests(&self) -> [MbiTagType; N] {
+        // cheap to clone, otherwise difficult with lifetime
+        { self.requests }.clone()
+    }
+
+    /// 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 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.offset(base_struct_size as isize) };
+        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.clone().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 = tag.as_ptr() as *const InformationRequestHeaderTag<3>;
+        let tag = unsafe { core::ptr::read(tag) };
+        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);
+    }
+}

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

@@ -1 +1,139 @@
-//! 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::{ConsoleHeaderTag, HeaderTagFlag, HeaderTagISA, InformationRequestHeaderTagBuilder, MbiTagType, Multiboot2Header, RelocatableHeaderTag, RelocatableHeaderTagPreference, load_mb2_header};
+//!
+//! /// 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 { load_mb2_header(mb2_hdr_bytes.as_ptr() as usize) };
+//!     println!("{:#?}", mb2_hdr);
+//! }
+//! ```
+
+#![deny(rustdoc::all)]
+#![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;
+
+/// Value must be present in multiboot2 header.
+pub const MULTIBOOT2_HEADER_MAGIC: u32 = 0xe85250d6;
+
+/// Loads the data on the given address as Multiboot2 header.
+/// The address must be 8-byte aligned (see specification).
+pub unsafe fn load_mb2_header<'a>(addr: usize) -> Multiboot2Header<'a> {
+    assert_ne!(0, addr, "null pointer");
+    assert_eq!(addr % 8, 0, "must be 8-byte aligned, see multiboot spec");
+    let ptr = addr as *const Multiboot2HeaderInner;
+    let reference = &*ptr;
+    Multiboot2Header::new(reference)
+}
+
+/// Trait for all tags that creates a byte array from a tag.
+/// Useful in Builder-classes to construct a byte vector that
+/// represents the Multiboot2 header.
+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 { core::ptr::read(bytes.as_ptr() as *const Foobar) };
+        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 fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub 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 fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub fn size(&self) -> u32 {
+        self.size
+    }
+    pub fn min_addr(&self) -> u32 {
+        self.min_addr
+    }
+    pub fn max_addr(&self) -> u32 {
+        self.max_addr
+    }
+    pub fn align(&self) -> u32 {
+        self.align
+    }
+    pub 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 {}

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

@@ -0,0 +1,94 @@
+//! Type definitions for "multiboot2 header tags". These tags occur in a binary if it
+//! is multiboot2-compliant, for example a kernel.
+//!
+//! The values are taken from the example C code at the end of the official multiboot2 spec.
+//!
+//! This type definitions are only beneficial to parse such a structure. They can't be used
+//! to construct a multiboot2 header as a static global variable. To write a multiboot2
+//! header, it is highly recommended to do this directly in assembly, because of the
+//! unknown size of structs or some addresses are not known yet (keyword: relocations).
+
+use crate::StructAsBytes;
+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".
+    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.
+#[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,
+}
+
+/// Flags for multiboot2 header tags.
+#[repr(u16)]
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum HeaderTagFlag {
+    Required = 0,
+    Optional = 1,
+}
+
+/// Common structure for all header tags.
+#[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 {
+    // never needed to construct this publicly
+    /*
+    pub fn new(typ: HeaderTagType, flags: HeaderTagFlag, size: u32) -> Self {
+        HeaderTag { typ, flags, size }
+    }*/
+
+    pub fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub fn size(&self) -> u32 {
+        self.size
+    }
+}
+
+impl StructAsBytes for HeaderTag {}

+ 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 fn typ(&self) -> HeaderTagType {
+        self.typ
+    }
+    pub fn flags(&self) -> HeaderTagFlag {
+        self.flags
+    }
+    pub fn size(&self) -> u32 {
+        self.size
+    }
+}
+
+impl StructAsBytes for EfiBootServiceHeaderTag {}