Quellcode durchsuchen

Merge pull request #224 from rust-osdev/refactoring

Refactoring
Philipp Schuster vor 8 Monaten
Ursprung
Commit
c8030ae48d
40 geänderte Dateien mit 1218 neuen und 817 gelöschten Zeilen
  1. 2 13
      .github/workflows/_build-rust.yml
  2. 4 0
      .github/workflows/rust.yml
  3. 2 1
      multiboot2-header/Changelog.md
  4. 30 11
      multiboot2-header/src/address.rs
  5. 37 3
      multiboot2-header/src/builder/header.rs
  6. 4 0
      multiboot2-header/src/builder/information_request.rs
  7. 1 1
      multiboot2-header/src/builder/traits.rs
  8. 22 11
      multiboot2-header/src/console.rs
  9. 21 14
      multiboot2-header/src/end.rs
  10. 24 16
      multiboot2-header/src/entry_address.rs
  11. 26 15
      multiboot2-header/src/entry_efi_32.rs
  12. 26 15
      multiboot2-header/src/entry_efi_64.rs
  13. 33 13
      multiboot2-header/src/framebuffer.rs
  14. 53 22
      multiboot2-header/src/header.rs
  15. 28 18
      multiboot2-header/src/information_request.rs
  16. 15 3
      multiboot2-header/src/lib.rs
  17. 18 12
      multiboot2-header/src/module_align.rs
  18. 34 14
      multiboot2-header/src/relocatable.rs
  19. 28 12
      multiboot2-header/src/tags.rs
  20. 17 12
      multiboot2-header/src/uefi_bs.rs
  21. 6 0
      multiboot2/Changelog.md
  22. 459 0
      multiboot2/src/boot_information.rs
  23. 21 8
      multiboot2/src/boot_loader_name.rs
  24. 1 1
      multiboot2/src/builder/boxed_dst.rs
  25. 21 1
      multiboot2/src/builder/information.rs
  26. 4 1
      multiboot2/src/builder/mod.rs
  27. 7 8
      multiboot2/src/command_line.rs
  28. 27 25
      multiboot2/src/efi.rs
  29. 13 4
      multiboot2/src/elf_sections.rs
  30. 2 2
      multiboot2/src/end.rs
  31. 37 31
      multiboot2/src/framebuffer.rs
  32. 8 6
      multiboot2/src/image_load_addr.rs
  33. 17 431
      multiboot2/src/lib.rs
  34. 36 21
      multiboot2/src/memory_map.rs
  35. 17 13
      multiboot2/src/module.rs
  36. 26 18
      multiboot2/src/rsdp.rs
  37. 33 13
      multiboot2/src/smbios.rs
  38. 29 2
      multiboot2/src/tag.rs
  39. 1 0
      multiboot2/src/tag_trait.rs
  40. 28 26
      multiboot2/src/tag_type.rs

+ 2 - 13
.github/workflows/_build-rust.yml

@@ -94,19 +94,8 @@ jobs:
         run: |
           cargo doc --no-deps --document-private-items --features ${{ inputs.features }} --no-default-features
           cargo clippy --all-targets --features ${{ inputs.features }} --no-default-features
-      - name: Unit Test (UNIX)
-        if: inputs.do-test && runner.os != 'Windows'
-        run: |
-          curl -LsSf https://get.nexte.st/latest/linux | tar zxf -
-          chmod u+x cargo-nextest
-          ./cargo-nextest nextest run --features ${{ inputs.features }} --no-default-features
-      - name: Unit Test (Windows)
-        if: inputs.do-test && runner.os == 'Windows'
-        run: |
-          Invoke-WebRequest https://get.nexte.st/latest/windows -OutFile cargo-nextest.zip
-          Expand-Archive .\cargo-nextest.zip
-          cp .\cargo-nextest/cargo-nextest.exe .
-          .\cargo-nextest.exe nextest run --features ${{ inputs.features }} --no-default-features
+      - name: Unit Test
+        run: cargo test --verbose
       - name: Unit Test with Miri
         if: inputs.do-miri
         # "--tests" so that the doctests are skipped. Currently, the doctest

+ 4 - 0
.github/workflows/rust.yml

@@ -11,6 +11,10 @@ name: "Cargo workspace"
 # Run on every push (tag, branch) and pull_request
 on: [ pull_request, push, workflow_dispatch, merge_group ]
 
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+  cancel-in-progress: true
+
 env:
   CARGO_TERM_COLOR: always
 

+ 2 - 1
multiboot2-header/Changelog.md

@@ -2,8 +2,9 @@
 
 ## Unreleased
 
+- **Breaking** All functions that returns something useful are now `#[must_use]`
 - updated dependencies
-- MSRV is 1.75
+- documentation enhancements
 
 ## 0.4.0 (2024-05-01)
 

+ 30 - 11
multiboot2-header/src/address.rs

@@ -1,4 +1,4 @@
-use crate::{HeaderTagFlag, HeaderTagType};
+use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
 use core::mem::size_of;
 
 /// This information does not need to be provided if the kernel image is in ELF
@@ -8,9 +8,7 @@ use core::mem::size_of;
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct AddressHeaderTag {
-    typ: HeaderTagType,
-    flags: HeaderTagFlag,
-    size: u32,
+    header: HeaderTagHeader,
     /// Contains the address corresponding to the beginning of the Multiboot2 header — the physical memory location at which the magic value is supposed to be loaded. This field serves to synchronize the mapping between OS image offsets and physical memory addresses.
     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.
@@ -24,6 +22,8 @@ pub struct AddressHeaderTag {
 }
 
 impl AddressHeaderTag {
+    /// Constructs a new tag.
+    #[must_use]
     pub const fn new(
         flags: HeaderTagFlag,
         header_addr: u32,
@@ -31,10 +31,9 @@ impl AddressHeaderTag {
         load_end_addr: u32,
         bss_end_addr: u32,
     ) -> Self {
-        AddressHeaderTag {
-            typ: HeaderTagType::Address,
-            flags,
-            size: size_of::<Self>() as u32,
+        let header = HeaderTagHeader::new(HeaderTagType::Address, flags, size_of::<Self>() as u32);
+        Self {
+            header,
             header_addr,
             load_addr,
             load_end_addr,
@@ -42,24 +41,44 @@ impl AddressHeaderTag {
         }
     }
 
+    /// Returns the [`HeaderTagType`].
+    #[must_use]
     pub const fn typ(&self) -> HeaderTagType {
-        self.typ
+        self.header.typ()
     }
+
+    /// Returns the [`HeaderTagFlag`]s.
+    #[must_use]
     pub const fn flags(&self) -> HeaderTagFlag {
-        self.flags
+        self.header.flags()
     }
+
+    /// Returns the size.
+    #[must_use]
     pub const fn size(&self) -> u32 {
-        self.size
+        self.header.size()
     }
+
+    /// Returns the header address.
+    #[must_use]
     pub const fn header_addr(&self) -> u32 {
         self.header_addr
     }
+
+    /// Returns the load begin address.
+    #[must_use]
     pub const fn load_addr(&self) -> u32 {
         self.load_addr
     }
+
+    /// Returns the load end address.
+    #[must_use]
     pub const fn load_end_addr(&self) -> u32 {
         self.load_end_addr
     }
+
+    /// Returns the bss end address.
+    #[must_use]
     pub const fn bss_end_addr(&self) -> u32 {
         self.bss_end_addr
     }

+ 37 - 3
multiboot2-header/src/builder/header.rs

@@ -72,6 +72,8 @@ pub struct HeaderBuilder {
 }
 
 impl HeaderBuilder {
+    /// Creates a new builder.
+    #[must_use]
     pub const fn new(arch: HeaderTagISA) -> Self {
         Self {
             arch,
@@ -98,6 +100,7 @@ impl HeaderBuilder {
 
     /// Returns the expected length of the Multiboot2 header, when the
     /// [`Self::build`]-method gets called.
+    #[must_use]
     pub fn expected_len(&self) -> usize {
         let base_len = size_of::<Multiboot2BasicHeader>();
         // size_or_up_aligned not required, because basic header length is 16 and the
@@ -159,7 +162,8 @@ impl HeaderBuilder {
     }
 
     /// Constructs the bytes for a valid Multiboot2 header with the given properties.
-    pub fn build(mut self) -> HeaderBytes {
+    #[must_use]
+    pub fn build(self) -> HeaderBytes {
         const ALIGN: usize = 8;
 
         // PHASE 1/2: Prepare Vector
@@ -205,7 +209,7 @@ impl HeaderBuilder {
     }
 
     /// Helper method that adds all the tags to the given vector.
-    fn build_add_tags(&mut self, bytes: &mut Vec<u8>) {
+    fn build_add_tags(&self, bytes: &mut Vec<u8>) {
         Self::build_add_bytes(
             bytes,
             // important that we write the correct expected length into the header!
@@ -247,7 +251,10 @@ impl HeaderBuilder {
     }
 
     // clippy thinks this can be a const fn but the compiler denies it
-    #[allow(clippy::missing_const_for_fn)]
+    // #[allow(clippy::missing_const_for_fn)]
+    /// Adds information requests from the
+    /// [`InformationRequestHeaderTagBuilder`] to the builder.
+    #[must_use]
     pub fn information_request_tag(
         mut self,
         information_request_tag: InformationRequestHeaderTagBuilder,
@@ -255,38 +262,65 @@ impl HeaderBuilder {
         self.information_request_tag = Some(information_request_tag);
         self
     }
+
+    /// Adds a [`AddressHeaderTag`] to the builder.
+    #[must_use]
     pub const fn address_tag(mut self, address_tag: AddressHeaderTag) -> Self {
         self.address_tag = Some(address_tag);
         self
     }
+
+    /// Adds a [`EntryAddressHeaderTag`] to the builder.
+    #[must_use]
     pub const fn entry_tag(mut self, entry_tag: EntryAddressHeaderTag) -> Self {
         self.entry_tag = Some(entry_tag);
         self
     }
+
+    /// Adds a [`ConsoleHeaderTag`] to the builder.
+    #[must_use]
     pub const fn console_tag(mut self, console_tag: ConsoleHeaderTag) -> Self {
         self.console_tag = Some(console_tag);
         self
     }
+
+    /// Adds a [`FramebufferHeaderTag`] to the builder.
+    #[must_use]
     pub const fn framebuffer_tag(mut self, framebuffer_tag: FramebufferHeaderTag) -> Self {
         self.framebuffer_tag = Some(framebuffer_tag);
         self
     }
+
+    /// Adds a [`ModuleAlignHeaderTag`] to the builder.
+    #[must_use]
     pub const fn module_align_tag(mut self, module_align_tag: ModuleAlignHeaderTag) -> Self {
         self.module_align_tag = Some(module_align_tag);
         self
     }
+
+    /// Adds a [`EfiBootServiceHeaderTag`] to the builder.
+    #[must_use]
     pub const fn efi_bs_tag(mut self, efi_bs_tag: EfiBootServiceHeaderTag) -> Self {
         self.efi_bs_tag = Some(efi_bs_tag);
         self
     }
+
+    /// Adds a [`EntryEfi32HeaderTag`] to the builder.
+    #[must_use]
     pub const fn efi_32_tag(mut self, efi_32_tag: EntryEfi32HeaderTag) -> Self {
         self.efi_32_tag = Some(efi_32_tag);
         self
     }
+
+    /// Adds a [`EntryEfi64HeaderTag`] to the builder.
+    #[must_use]
     pub const fn efi_64_tag(mut self, efi_64_tag: EntryEfi64HeaderTag) -> Self {
         self.efi_64_tag = Some(efi_64_tag);
         self
     }
+
+    /// Adds a [`RelocatableHeaderTag`] to the builder.
+    #[must_use]
     pub const fn relocatable_tag(mut self, relocatable_tag: RelocatableHeaderTag) -> Self {
         self.relocatable_tag = Some(relocatable_tag);
         self

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

@@ -22,6 +22,7 @@ pub struct InformationRequestHeaderTagBuilder {
 #[cfg(feature = "builder")]
 impl InformationRequestHeaderTagBuilder {
     /// New builder.
+    #[must_use]
     pub const fn new(flag: HeaderTagFlag) -> Self {
         Self {
             irs: BTreeSet::new(),
@@ -31,6 +32,7 @@ impl InformationRequestHeaderTagBuilder {
 
     /// Returns the expected length of the information request tag,
     /// when the `build`-method gets called.
+    #[must_use]
     pub fn expected_len(&self) -> usize {
         let basic_header_size = size_of::<InformationRequestHeaderTag<0>>();
         let req_tags_size = self.irs.len() * size_of::<MbiTagTypeId>();
@@ -38,12 +40,14 @@ impl InformationRequestHeaderTagBuilder {
     }
 
     /// Adds an [`MbiTagType`] to the information request.
+    #[must_use]
     pub fn add_ir(mut self, tag: MbiTagType) -> Self {
         self.irs.insert(tag);
         self
     }
 
     /// Adds multiple [`MbiTagType`] to the information request.
+    #[must_use]
     pub fn add_irs(mut self, tags: &[MbiTagType]) -> Self {
         self.irs.extend(tags);
         self

+ 1 - 1
multiboot2-header/src/builder/traits.rs

@@ -11,7 +11,7 @@ use core::mem::size_of;
 /// Trait for all tags that helps to create a byte array from the tag.
 /// Useful in builders to construct a byte vector that
 /// represents the Multiboot2 header with all its tags.
-pub(crate) trait StructAsBytes: Sized {
+pub trait StructAsBytes: Sized {
     /// Returns the size in bytes of the struct, as known during compile
     /// time. This doesn't use read the "size" field of tags.
     fn byte_size(&self) -> usize {

+ 22 - 11
multiboot2-header/src/console.rs

@@ -1,4 +1,4 @@
-use crate::{HeaderTagFlag, HeaderTagType};
+use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
 use core::mem::size_of;
 
 /// Possible flags for [`ConsoleHeaderTag`].
@@ -16,31 +16,42 @@ pub enum ConsoleHeaderTagFlags {
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct ConsoleHeaderTag {
-    typ: HeaderTagType,
-    flags: HeaderTagFlag,
-    size: u32,
+    header: HeaderTagHeader,
     console_flags: ConsoleHeaderTagFlags,
 }
 
 impl ConsoleHeaderTag {
+    /// Constructs a new tag.
+    #[must_use]
     pub const fn new(flags: HeaderTagFlag, console_flags: ConsoleHeaderTagFlags) -> Self {
-        ConsoleHeaderTag {
-            typ: HeaderTagType::ConsoleFlags,
-            flags,
-            size: size_of::<Self>() as u32,
+        let header =
+            HeaderTagHeader::new(HeaderTagType::ConsoleFlags, flags, size_of::<Self>() as u32);
+        Self {
+            header,
             console_flags,
         }
     }
 
+    /// Returns the [`HeaderTagType`].
+    #[must_use]
     pub const fn typ(&self) -> HeaderTagType {
-        self.typ
+        self.header.typ()
     }
+
+    /// Returns the [`HeaderTagFlag`]s.
+    #[must_use]
     pub const fn flags(&self) -> HeaderTagFlag {
-        self.flags
+        self.header.flags()
     }
+
+    /// Returns the size.
+    #[must_use]
     pub const fn size(&self) -> u32 {
-        self.size
+        self.header.size()
     }
+
+    /// Returns the [`ConsoleHeaderTagFlags`].
+    #[must_use]
     pub const fn console_flags(&self) -> ConsoleHeaderTagFlags {
         self.console_flags
     }

+ 21 - 14
multiboot2-header/src/end.rs

@@ -1,15 +1,11 @@
-use crate::{HeaderTagFlag, HeaderTagType};
+use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
 use core::mem::size_of;
 
 /// Terminates a list of optional tags in a Multiboot2 header.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct EndHeaderTag {
-    // u16 value
-    typ: HeaderTagType,
-    // u16 value
-    flags: HeaderTagFlag,
-    size: u32,
+    header: HeaderTagHeader,
 }
 
 impl Default for EndHeaderTag {
@@ -19,22 +15,33 @@ impl Default for EndHeaderTag {
 }
 
 impl EndHeaderTag {
+    /// Constructs a new tag.
+    #[must_use]
     pub const fn new() -> Self {
-        EndHeaderTag {
-            typ: HeaderTagType::End,
-            flags: HeaderTagFlag::Required,
-            size: size_of::<Self>() as u32,
-        }
+        let header = HeaderTagHeader::new(
+            HeaderTagType::EntryAddress,
+            HeaderTagFlag::Required,
+            size_of::<Self>() as u32,
+        );
+        Self { header }
     }
 
+    /// Returns the [`HeaderTagType`].
+    #[must_use]
     pub const fn typ(&self) -> HeaderTagType {
-        self.typ
+        self.header.typ()
     }
+
+    /// Returns the [`HeaderTagFlag`]s.
+    #[must_use]
     pub const fn flags(&self) -> HeaderTagFlag {
-        self.flags
+        self.header.flags()
     }
+
+    /// Returns the size.
+    #[must_use]
     pub const fn size(&self) -> u32 {
-        self.size
+        self.header.size()
     }
 }
 

+ 24 - 16
multiboot2-header/src/entry_address.rs

@@ -1,4 +1,4 @@
-use crate::{HeaderTagFlag, HeaderTagType};
+use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
 use core::fmt;
 use core::fmt::{Debug, Formatter};
 use core::mem::size_of;
@@ -8,31 +8,39 @@ use core::mem::size_of;
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct EntryAddressHeaderTag {
-    typ: HeaderTagType,
-    flags: HeaderTagFlag,
-    size: u32,
+    header: HeaderTagHeader,
     entry_addr: u32,
 }
 
 impl EntryAddressHeaderTag {
+    /// Constructs a new tag.
+    #[must_use]
     pub const fn new(flags: HeaderTagFlag, entry_addr: u32) -> Self {
-        EntryAddressHeaderTag {
-            typ: HeaderTagType::EntryAddress,
-            flags,
-            size: size_of::<Self>() as u32,
-            entry_addr,
-        }
+        let header =
+            HeaderTagHeader::new(HeaderTagType::EntryAddress, flags, size_of::<Self>() as u32);
+        Self { header, entry_addr }
     }
 
+    /// Returns the [`HeaderTagType`].
+    #[must_use]
     pub const fn typ(&self) -> HeaderTagType {
-        self.typ
+        self.header.typ()
     }
+
+    /// Returns the [`HeaderTagFlag`]s.
+    #[must_use]
     pub const fn flags(&self) -> HeaderTagFlag {
-        self.flags
+        self.header.flags()
     }
+
+    /// Returns the size.
+    #[must_use]
     pub const fn size(&self) -> u32 {
-        self.size
+        self.header.size()
     }
+
+    /// Returns the entry address.
+    #[must_use]
     pub const fn entry_addr(&self) -> u32 {
         self.entry_addr
     }
@@ -41,9 +49,9 @@ impl EntryAddressHeaderTag {
 impl Debug for EntryAddressHeaderTag {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
         f.debug_struct("EntryAddressHeaderTag")
-            .field("type", &{ self.typ })
-            .field("flags", &{ self.flags })
-            .field("size", &{ self.size })
+            .field("type", &self.typ())
+            .field("flags", &self.flags())
+            .field("size", &self.size())
             .field("entry_addr", &(self.entry_addr as *const u32))
             .finish()
     }

+ 26 - 15
multiboot2-header/src/entry_efi_32.rs

@@ -1,4 +1,4 @@
-use crate::{HeaderTagFlag, HeaderTagType};
+use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
 use core::fmt;
 use core::fmt::{Debug, Formatter};
 use core::mem::size_of;
@@ -12,31 +12,42 @@ use core::mem::size_of;
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct EntryEfi32HeaderTag {
-    typ: HeaderTagType,
-    flags: HeaderTagFlag,
-    size: u32,
+    header: HeaderTagHeader,
     entry_addr: u32,
 }
 
 impl EntryEfi32HeaderTag {
+    /// Constructs a new tag.
+    #[must_use]
     pub const fn new(flags: HeaderTagFlag, entry_addr: u32) -> Self {
-        EntryEfi32HeaderTag {
-            typ: HeaderTagType::EntryAddressEFI32,
+        let header = HeaderTagHeader::new(
+            HeaderTagType::EntryAddressEFI32,
             flags,
-            size: size_of::<Self>() as u32,
-            entry_addr,
-        }
+            size_of::<Self>() as u32,
+        );
+        Self { header, entry_addr }
     }
 
+    /// Returns the [`HeaderTagType`].
+    #[must_use]
     pub const fn typ(&self) -> HeaderTagType {
-        self.typ
+        self.header.typ()
     }
+
+    /// Returns the [`HeaderTagFlag`]s.
+    #[must_use]
     pub const fn flags(&self) -> HeaderTagFlag {
-        self.flags
+        self.header.flags()
     }
+
+    /// Returns the size.
+    #[must_use]
     pub const fn size(&self) -> u32 {
-        self.size
+        self.header.size()
     }
+
+    /// Returns the entry address.
+    #[must_use]
     pub const fn entry_addr(&self) -> u32 {
         self.entry_addr
     }
@@ -45,9 +56,9 @@ impl EntryEfi32HeaderTag {
 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("type", &self.typ())
+            .field("flags", &self.flags())
+            .field("size", &self.size())
             .field("entry_addr", &(self.entry_addr as *const u32))
             .finish()
     }

+ 26 - 15
multiboot2-header/src/entry_efi_64.rs

@@ -1,4 +1,4 @@
-use crate::{HeaderTagFlag, HeaderTagType};
+use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
 use core::fmt;
 use core::fmt::{Debug, Formatter};
 use core::mem::size_of;
@@ -12,31 +12,42 @@ use core::mem::size_of;
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct EntryEfi64HeaderTag {
-    typ: HeaderTagType,
-    flags: HeaderTagFlag,
-    size: u32,
+    header: HeaderTagHeader,
     entry_addr: u32,
 }
 
 impl EntryEfi64HeaderTag {
+    /// Constructs a new tag.
+    #[must_use]
     pub const fn new(flags: HeaderTagFlag, entry_addr: u32) -> Self {
-        EntryEfi64HeaderTag {
-            typ: HeaderTagType::EntryAddressEFI64,
+        let header = HeaderTagHeader::new(
+            HeaderTagType::EntryAddressEFI64,
             flags,
-            size: size_of::<Self>() as u32,
-            entry_addr,
-        }
+            size_of::<Self>() as u32,
+        );
+        Self { header, entry_addr }
     }
 
+    /// Returns the [`HeaderTagType`].
+    #[must_use]
     pub const fn typ(&self) -> HeaderTagType {
-        self.typ
+        self.header.typ()
     }
+
+    /// Returns the [`HeaderTagFlag`]s.
+    #[must_use]
     pub const fn flags(&self) -> HeaderTagFlag {
-        self.flags
+        self.header.flags()
     }
+
+    /// Returns the size.
+    #[must_use]
     pub const fn size(&self) -> u32 {
-        self.size
+        self.header.size()
     }
+
+    /// Returns the entry address.
+    #[must_use]
     pub const fn entry_addr(&self) -> u32 {
         self.entry_addr
     }
@@ -45,9 +56,9 @@ impl EntryEfi64HeaderTag {
 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("type", &self.typ())
+            .field("flags", &self.flags())
+            .field("size", &self.size())
             .field("entry_addr", &(self.entry_addr as *const u32))
             .finish()
     }

+ 33 - 13
multiboot2-header/src/framebuffer.rs

@@ -1,5 +1,5 @@
-use crate::{HeaderTagFlag, HeaderTagType};
-use core::mem::size_of;
+use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
+use core::mem;
 
 /// Specifies the preferred graphics mode. If this tag
 /// is present the bootloader assumes that the payload
@@ -8,41 +8,61 @@ use core::mem::size_of;
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct FramebufferHeaderTag {
-    typ: HeaderTagType,
-    flags: HeaderTagFlag,
-    size: u32,
+    header: HeaderTagHeader,
     width: u32,
     height: u32,
     depth: u32,
 }
 
 impl FramebufferHeaderTag {
+    /// Constructs a new tag.
+    #[must_use]
     pub const fn new(flags: HeaderTagFlag, width: u32, height: u32, depth: u32) -> Self {
-        FramebufferHeaderTag {
-            typ: HeaderTagType::Framebuffer,
+        let header = HeaderTagHeader::new(
+            HeaderTagType::Framebuffer,
             flags,
-            size: size_of::<Self>() as u32,
+            mem::size_of::<Self>() as u32,
+        );
+        Self {
+            header,
             width,
             height,
             depth,
         }
     }
 
+    /// Returns the [`HeaderTagType`].
+    #[must_use]
     pub const fn typ(&self) -> HeaderTagType {
-        self.typ
+        self.header.typ()
     }
+
+    /// Returns the [`HeaderTagFlag`]s.
+    #[must_use]
     pub const fn flags(&self) -> HeaderTagFlag {
-        self.flags
+        self.header.flags()
     }
+
+    /// Returns the size.
+    #[must_use]
     pub const fn size(&self) -> u32 {
-        self.size
+        self.header.size()
     }
+
+    /// Returns the width.
+    #[must_use]
     pub const fn width(&self) -> u32 {
         self.width
     }
+
+    /// Returns the height.
+    #[must_use]
     pub const fn height(&self) -> u32 {
         self.height
     }
+
+    /// Returns the depth.
+    #[must_use]
     pub const fn depth(&self) -> u32 {
         self.depth
     }
@@ -50,12 +70,12 @@ impl FramebufferHeaderTag {
 
 #[cfg(test)]
 mod tests {
-    use crate::FramebufferHeaderTag;
+    use super::*;
 
     #[test]
     fn test_assert_size() {
         assert_eq!(
-            core::mem::size_of::<FramebufferHeaderTag>(),
+            mem::size_of::<FramebufferHeaderTag>(),
             2 + 2 + 4 + 4 + 4 + 4
         );
     }

+ 53 - 22
multiboot2-header/src/header.rs

@@ -1,8 +1,8 @@
 use crate::{
     AddressHeaderTag, ConsoleHeaderTag, EfiBootServiceHeaderTag, EndHeaderTag,
     EntryAddressHeaderTag, EntryEfi32HeaderTag, EntryEfi64HeaderTag, FramebufferHeaderTag,
-    HeaderTag, HeaderTagISA, HeaderTagType, InformationRequestHeaderTag, ModuleAlignHeaderTag,
-    RelocatableHeaderTag,
+    HeaderTagHeader, HeaderTagISA, HeaderTagType, InformationRequestHeaderTag,
+    ModuleAlignHeaderTag, RelocatableHeaderTag,
 };
 use core::fmt::{Debug, Formatter};
 use core::mem::size_of;
@@ -103,89 +103,106 @@ impl<'a> Multiboot2Header<'a> {
     }
 
     /// Wrapper around [`Multiboot2BasicHeader::verify_checksum`].
+    #[must_use]
     pub const fn verify_checksum(&self) -> bool {
         self.0.verify_checksum()
     }
     /// Wrapper around [`Multiboot2BasicHeader::header_magic`].
+    #[must_use]
     pub const fn header_magic(&self) -> u32 {
         self.0.header_magic()
     }
     /// Wrapper around [`Multiboot2BasicHeader::arch`].
+    #[must_use]
     pub const fn arch(&self) -> HeaderTagISA {
         self.0.arch()
     }
     /// Wrapper around [`Multiboot2BasicHeader::length`].
+    #[must_use]
     pub const fn length(&self) -> u32 {
         self.0.length()
     }
     /// Wrapper around [`Multiboot2BasicHeader::checksum`].
+    #[must_use]
     pub const fn checksum(&self) -> u32 {
         self.0.checksum()
     }
     /// Wrapper around [`Multiboot2BasicHeader::tag_iter`].
+    #[must_use]
     pub fn iter(&self) -> Multiboot2HeaderTagIter {
         self.0.tag_iter()
     }
     /// Wrapper around [`Multiboot2BasicHeader::calc_checksum`].
+    #[must_use]
     pub const fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 {
         Multiboot2BasicHeader::calc_checksum(magic, arch, length)
     }
 
     /// Search for the address header tag.
+    #[must_use]
     pub fn address_tag(&self) -> Option<&AddressHeaderTag> {
         self.get_tag(HeaderTagType::Address)
-            .map(|tag| unsafe { &*(tag as *const HeaderTag as *const AddressHeaderTag) })
+            .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const AddressHeaderTag) })
     }
 
     /// Search for the entry address header tag.
+    #[must_use]
     pub fn entry_address_tag(&self) -> Option<&EntryAddressHeaderTag> {
         self.get_tag(HeaderTagType::EntryAddress)
-            .map(|tag| unsafe { &*(tag as *const HeaderTag as *const EntryAddressHeaderTag) })
+            .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const EntryAddressHeaderTag) })
     }
 
     /// Search for the EFI32 entry address header tag.
+    #[must_use]
     pub fn entry_address_efi32_tag(&self) -> Option<&EntryEfi32HeaderTag> {
         self.get_tag(HeaderTagType::EntryAddressEFI32)
-            .map(|tag| unsafe { &*(tag as *const HeaderTag as *const EntryEfi32HeaderTag) })
+            .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const EntryEfi32HeaderTag) })
     }
 
     /// Search for the EFI64 entry address header tag.
+    #[must_use]
     pub fn entry_address_efi64_tag(&self) -> Option<&EntryEfi64HeaderTag> {
         self.get_tag(HeaderTagType::EntryAddressEFI64)
-            .map(|tag| unsafe { &*(tag as *const HeaderTag as *const EntryEfi64HeaderTag) })
+            .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const EntryEfi64HeaderTag) })
     }
 
     /// Search for the console flags header tag.
+    #[must_use]
     pub fn console_flags_tag(&self) -> Option<&ConsoleHeaderTag> {
         self.get_tag(HeaderTagType::ConsoleFlags)
-            .map(|tag| unsafe { &*(tag as *const HeaderTag as *const ConsoleHeaderTag) })
+            .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const ConsoleHeaderTag) })
     }
 
     /// Search for the framebuffer header tag.
+    #[must_use]
     pub fn framebuffer_tag(&self) -> Option<&FramebufferHeaderTag> {
         self.get_tag(HeaderTagType::Framebuffer)
-            .map(|tag| unsafe { &*(tag as *const HeaderTag as *const FramebufferHeaderTag) })
+            .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const FramebufferHeaderTag) })
     }
 
     /// Search for the module align header tag.
+    #[must_use]
     pub fn module_align_tag(&self) -> Option<&ModuleAlignHeaderTag> {
         self.get_tag(HeaderTagType::ModuleAlign)
-            .map(|tag| unsafe { &*(tag as *const HeaderTag as *const ModuleAlignHeaderTag) })
+            .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const ModuleAlignHeaderTag) })
     }
 
     /// Search for the EFI Boot Services header tag.
+    #[must_use]
     pub fn efi_boot_services_tag(&self) -> Option<&EfiBootServiceHeaderTag> {
-        self.get_tag(HeaderTagType::EfiBS)
-            .map(|tag| unsafe { &*(tag as *const HeaderTag as *const EfiBootServiceHeaderTag) })
+        self.get_tag(HeaderTagType::EfiBS).map(|tag| unsafe {
+            &*(tag as *const HeaderTagHeader as *const EfiBootServiceHeaderTag)
+        })
     }
 
     /// Search for the EFI32 entry address header tag.
+    #[must_use]
     pub fn relocatable_tag(&self) -> Option<&RelocatableHeaderTag> {
         self.get_tag(HeaderTagType::Relocatable)
-            .map(|tag| unsafe { &*(tag as *const HeaderTag as *const RelocatableHeaderTag) })
+            .map(|tag| unsafe { &*(tag as *const HeaderTagHeader as *const RelocatableHeaderTag) })
     }
 
-    fn get_tag(&self, typ: HeaderTagType) -> Option<&HeaderTag> {
+    fn get_tag(&self, typ: HeaderTagType) -> Option<&HeaderTagHeader> {
         self.iter()
             .map(|tag| unsafe { tag.as_ref() }.unwrap())
             .find(|tag| tag.typ() == typ)
@@ -229,7 +246,7 @@ impl Multiboot2BasicHeader {
     pub(crate) const fn new(arch: HeaderTagISA, length: u32) -> Self {
         let magic = MAGIC;
         let checksum = Self::calc_checksum(magic, arch, length);
-        Multiboot2BasicHeader {
+        Self {
             header_magic: magic,
             arch,
             length,
@@ -238,25 +255,38 @@ impl Multiboot2BasicHeader {
     }
 
     /// Verifies that a Multiboot2 header is valid.
+    #[must_use]
     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.
+    #[must_use]
     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 the header magic.
+    #[must_use]
     pub const fn header_magic(&self) -> u32 {
         self.header_magic
     }
+
+    /// Returns the [`HeaderTagISA`].
+    #[must_use]
     pub const fn arch(&self) -> HeaderTagISA {
         self.arch
     }
+
+    /// Returns the length.
+    #[must_use]
     pub const fn length(&self) -> u32 {
         self.length
     }
+
+    /// Returns the checksum.
+    #[must_use]
     pub const fn checksum(&self) -> u32 {
         self.checksum
     }
@@ -265,12 +295,13 @@ impl Multiboot2BasicHeader {
     ///
     /// # Panics
     /// See doc of [`Multiboot2HeaderTagIter`].
+    #[must_use]
     pub fn tag_iter(&self) -> Multiboot2HeaderTagIter {
-        let base_hdr_size = size_of::<Multiboot2BasicHeader>();
+        let base_hdr_size = size_of::<Self>();
         if base_hdr_size == self.length as usize {
             panic!("No end tag!");
         }
-        let tag_base_addr = self as *const Multiboot2BasicHeader;
+        let tag_base_addr = self as *const Self;
         // cast to u8 so that the offset in bytes works correctly
         let tag_base_addr = tag_base_addr as *const u8;
         // tag_base_addr should now point behind the "static" members
@@ -278,7 +309,7 @@ impl Multiboot2BasicHeader {
         // 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 tag_base_addr = tag_base_addr as *const HeaderTagHeader;
         let tags_len = self.length as usize - base_hdr_size;
         Multiboot2HeaderTagIter::new(tag_base_addr, tags_len as u32)
     }
@@ -307,7 +338,7 @@ impl Debug for Multiboot2BasicHeader {
 #[derive(Clone)]
 pub struct Multiboot2HeaderTagIter {
     /// 8-byte aligned base address
-    base: *const HeaderTag,
+    base: *const HeaderTagHeader,
     /// Offset in bytes from the base address.
     /// Always <= than size.
     n: u32,
@@ -324,11 +355,11 @@ pub struct Multiboot2HeaderTagIter {
 }
 
 impl Multiboot2HeaderTagIter {
-    fn new(base: *const HeaderTag, size: u32) -> Self {
+    fn new(base: *const HeaderTagHeader, size: u32) -> Self {
         // transform to byte pointer => offset works properly
         let base = base as *const u8;
         let base = unsafe { base.add(base.align_offset(8)) };
-        let base = base as *const HeaderTag;
+        let base = base as *const HeaderTagHeader;
         Self {
             base,
             n: 0,
@@ -340,7 +371,7 @@ impl Multiboot2HeaderTagIter {
 }
 
 impl Iterator for Multiboot2HeaderTagIter {
-    type Item = *const HeaderTag;
+    type Item = *const HeaderTagHeader;
 
     fn next(&mut self) -> Option<Self::Item> {
         // no more bytes left to check; length reached
@@ -351,7 +382,7 @@ impl Iterator for Multiboot2HeaderTagIter {
         // 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;
+        let ptr = ptr as *const HeaderTagHeader;
         assert_eq!(ptr as usize % 8, 0, "must be 8-byte aligned");
         let tag = unsafe { &*ptr };
         assert!(

+ 28 - 18
multiboot2-header/src/information_request.rs

@@ -1,4 +1,4 @@
-use crate::{HeaderTagFlag, MbiTagType};
+use crate::{HeaderTagFlag, HeaderTagHeader, MbiTagType};
 use crate::{HeaderTagType, MbiTagTypeId};
 use core::fmt;
 use core::fmt::{Debug, Formatter};
@@ -11,9 +11,7 @@ use multiboot2::TagType;
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct InformationRequestHeaderTag<const N: usize> {
-    typ: HeaderTagType,
-    flags: HeaderTagFlag,
-    size: u32,
+    header: HeaderTagHeader,
     // Length is determined by size.
     // Must be parsed during runtime with unsafe pointer magic and the size field.
     requests: [MbiTagTypeId; N],
@@ -23,28 +21,38 @@ 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.
+    #[must_use]
     pub fn new(flags: HeaderTagFlag, requests: [MbiTagTypeId; N], size: Option<u32>) -> Self {
-        InformationRequestHeaderTag {
-            typ: HeaderTagType::InformationRequest,
+        let header = HeaderTagHeader::new(
+            HeaderTagType::InformationRequest,
             flags,
-            size: size.unwrap_or(size_of::<Self>() as u32),
-            requests,
-        }
+            size.unwrap_or(size_of::<Self>() as u32),
+        );
+        Self { header, requests }
     }
 
+    /// Returns the [`HeaderTagType`].
+    #[must_use]
     pub const fn typ(&self) -> HeaderTagType {
-        self.typ
+        self.header.typ()
     }
+
+    /// Returns the [`HeaderTagFlag`]s.
+    #[must_use]
     pub const fn flags(&self) -> HeaderTagFlag {
-        self.flags
+        self.header.flags()
     }
+
+    /// Returns the size.
+    #[must_use]
     pub const fn size(&self) -> u32 {
-        self.size
+        self.header.size()
     }
 
     /// Returns the requests as array. Only works if the number of requests
     /// is known at compile time. For safety and correctness during runtime,
     /// you should use `req_iter()`.
+    #[must_use]
     pub const fn requests(&self) -> [MbiTagTypeId; N] {
         // cheap to copy, otherwise difficult with lifetime
         self.requests
@@ -54,9 +62,10 @@ impl<const N: usize> InformationRequestHeaderTag<N> {
     /// 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.
+    #[must_use]
     pub const fn dynamic_requests_size(&self) -> u32 {
         let base_struct_size = size_of::<InformationRequestHeaderTag<0>>();
-        let size_diff = self.size - base_struct_size as u32;
+        let size_diff = self.size() - base_struct_size as u32;
         if size_diff > 0 {
             size_diff / size_of::<u32>() as u32
         } else {
@@ -65,10 +74,11 @@ impl<const N: usize> InformationRequestHeaderTag<N> {
     }
 
     /// Returns an [`InformationRequestHeaderTagIter`].
+    #[must_use]
     pub const fn req_iter(&self) -> InformationRequestHeaderTagIter {
         let base_struct_size = size_of::<InformationRequestHeaderTag<0>>();
         let count = self.dynamic_requests_size();
-        let base_ptr = self as *const InformationRequestHeaderTag<N>;
+        let base_ptr = self as *const Self;
         let base_ptr = base_ptr as *const u8;
         let base_ptr = unsafe { base_ptr.add(base_struct_size) };
         let base_ptr = base_ptr as *const MbiTagTypeId;
@@ -79,10 +89,10 @@ impl<const N: usize> InformationRequestHeaderTag<N> {
 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() })
+            .field("type", &self.typ())
+            .field("flags", &self.flags())
+            .field("size", &self.size())
+            .field("requests", &self.req_iter())
             .finish()
     }
 }

+ 15 - 3
multiboot2-header/src/lib.rs

@@ -38,10 +38,22 @@
 
 #![no_std]
 #![cfg_attr(feature = "unstable", feature(error_in_core))]
-#![deny(rustdoc::all)]
-#![deny(clippy::all)]
-#![deny(clippy::missing_const_for_fn)]
+// --- BEGIN STYLE CHECKS ---
+#![deny(
+    clippy::all,
+    clippy::cargo,
+    clippy::nursery,
+    clippy::must_use_candidate,
+    // clippy::restriction,
+    // clippy::pedantic
+)]
+// now allow a few rules which are denied by the above statement
+// --> They are either ridiculous, not necessary, or we can't fix them.
+#![allow(clippy::multiple_crate_versions)]
+#![deny(missing_docs)]
 #![deny(missing_debug_implementations)]
+#![deny(rustdoc::all)]
+// --- END STYLE CHECKS ---
 
 #[cfg(feature = "builder")]
 extern crate alloc;

+ 18 - 12
multiboot2-header/src/module_align.rs

@@ -1,32 +1,38 @@
-use crate::{HeaderTagFlag, HeaderTagType};
+use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
 use core::mem::size_of;
 
 /// If this tag is present, provided boot modules must be page aligned.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct ModuleAlignHeaderTag {
-    typ: HeaderTagType,
-    flags: HeaderTagFlag,
-    size: u32,
+    header: HeaderTagHeader,
 }
 
 impl ModuleAlignHeaderTag {
+    /// Constructs a new tag.
+    #[must_use]
     pub const fn new(flags: HeaderTagFlag) -> Self {
-        ModuleAlignHeaderTag {
-            typ: HeaderTagType::ModuleAlign,
-            flags,
-            size: size_of::<Self>() as u32,
-        }
+        let header =
+            HeaderTagHeader::new(HeaderTagType::ModuleAlign, flags, size_of::<Self>() as u32);
+        Self { header }
     }
 
+    /// Returns the [`HeaderTagType`].
+    #[must_use]
     pub const fn typ(&self) -> HeaderTagType {
-        self.typ
+        self.header.typ()
     }
+
+    /// Returns the [`HeaderTagFlag`]s.
+    #[must_use]
     pub const fn flags(&self) -> HeaderTagFlag {
-        self.flags
+        self.header.flags()
     }
+
+    /// Returns the size.
+    #[must_use]
     pub const fn size(&self) -> u32 {
-        self.size
+        self.header.size()
     }
 }
 

+ 34 - 14
multiboot2-header/src/relocatable.rs

@@ -1,4 +1,4 @@
-use crate::{HeaderTagFlag, HeaderTagType};
+use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
 use core::fmt;
 use core::fmt::{Debug, Formatter};
 use core::mem::size_of;
@@ -22,9 +22,7 @@ pub enum RelocatableHeaderTagPreference {
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct RelocatableHeaderTag {
-    typ: HeaderTagType,
-    flags: HeaderTagFlag,
-    size: u32,
+    header: HeaderTagHeader,
     /// 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.
@@ -35,6 +33,8 @@ pub struct RelocatableHeaderTag {
 }
 
 impl RelocatableHeaderTag {
+    /// Constructs a new tag.
+    #[must_use]
     pub const fn new(
         flags: HeaderTagFlag,
         min_addr: u32,
@@ -42,10 +42,10 @@ impl RelocatableHeaderTag {
         align: u32,
         preference: RelocatableHeaderTagPreference,
     ) -> Self {
-        RelocatableHeaderTag {
-            typ: HeaderTagType::Relocatable,
-            flags,
-            size: size_of::<Self>() as u32,
+        let header =
+            HeaderTagHeader::new(HeaderTagType::Relocatable, flags, size_of::<Self>() as u32);
+        Self {
+            header,
             min_addr,
             max_addr,
             align,
@@ -53,24 +53,44 @@ impl RelocatableHeaderTag {
         }
     }
 
+    /// Returns the [`HeaderTagType`].
+    #[must_use]
     pub const fn typ(&self) -> HeaderTagType {
-        self.typ
+        self.header.typ()
     }
+
+    /// Returns the [`HeaderTagFlag`]s.
+    #[must_use]
     pub const fn flags(&self) -> HeaderTagFlag {
-        self.flags
+        self.header.flags()
     }
+
+    /// Returns the size.
+    #[must_use]
     pub const fn size(&self) -> u32 {
-        self.size
+        self.header.size()
     }
+
+    /// Return the minimum address.
+    #[must_use]
     pub const fn min_addr(&self) -> u32 {
         self.min_addr
     }
+
+    /// Return the maximum address.
+    #[must_use]
     pub const fn max_addr(&self) -> u32 {
         self.max_addr
     }
+
+    /// Return the alignment.
+    #[must_use]
     pub const fn align(&self) -> u32 {
         self.align
     }
+
+    /// Return the preference.
+    #[must_use]
     pub const fn preference(&self) -> RelocatableHeaderTagPreference {
         self.preference
     }
@@ -79,9 +99,9 @@ impl RelocatableHeaderTag {
 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 })
+            .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))

+ 28 - 12
multiboot2-header/src/tags.rs

@@ -17,7 +17,7 @@ pub enum HeaderTagISA {
 
 /// 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`].
+/// stands in the `typ` property of [`HeaderTagHeader`].
 #[repr(u16)]
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub enum HeaderTagType {
@@ -47,6 +47,7 @@ pub enum HeaderTagType {
 
 impl HeaderTagType {
     /// Returns the number of possible variants.
+    #[must_use]
     pub const fn count() -> u32 {
         11
     }
@@ -56,31 +57,46 @@ impl HeaderTagType {
 #[repr(u16)]
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub enum HeaderTagFlag {
+    /// Bootloader must provide this tag. If this is not possible, the
+    /// bootloader will fail loading the kernel.
     Required = 0,
+    /// Bootloader should provide the tag if possible.
     Optional = 1,
 }
 
-/// Common properties for all header tags. Other tags may have additional fields
-/// that depend on the `typ` and the `size` field. All tags share the same beginning of the
-/// struct.
+/// The common header that all header tags share. Specific tags may have
+/// additional fields that depend on the `typ` and the `size` field.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
-pub struct HeaderTag {
+pub struct HeaderTagHeader {
+    typ: HeaderTagType, /* u16 */
     // u16 value
-    typ: HeaderTagType,
-    // u16 value
-    flags: HeaderTagFlag,
+    flags: HeaderTagFlag, /* u16 */
     size: u32,
-    // maybe additional fields (tag specific)
+    // Followed by optional additional tag specific fields.
 }
 
-impl HeaderTag {
+impl HeaderTagHeader {
+    /// Creates a new header.
+    #[must_use]
+    pub const fn new(typ: HeaderTagType, flags: HeaderTagFlag, size: u32) -> Self {
+        Self { typ, flags, size }
+    }
+
+    /// Returns the [`HeaderTagType`].
+    #[must_use]
     pub const fn typ(&self) -> HeaderTagType {
         self.typ
     }
+
+    /// Returns the [`HeaderTagFlag`]s.
+    #[must_use]
     pub const fn flags(&self) -> HeaderTagFlag {
         self.flags
     }
+
+    /// Returns the size.
+    #[must_use]
     pub const fn size(&self) -> u32 {
         self.size
     }
@@ -88,10 +104,10 @@ impl HeaderTag {
 
 #[cfg(test)]
 mod tests {
-    use crate::HeaderTag;
+    use crate::HeaderTagHeader;
 
     #[test]
     fn test_assert_size() {
-        assert_eq!(core::mem::size_of::<HeaderTag>(), 2 + 2 + 4);
+        assert_eq!(core::mem::size_of::<HeaderTagHeader>(), 2 + 2 + 4);
     }
 }

+ 17 - 12
multiboot2-header/src/uefi_bs.rs

@@ -1,4 +1,4 @@
-use crate::{HeaderTagFlag, HeaderTagType};
+use crate::{HeaderTagFlag, HeaderTagHeader, HeaderTagType};
 use core::mem::size_of;
 
 /// This tag indicates that payload supports starting without terminating UEFI boot services.
@@ -6,28 +6,33 @@ use core::mem::size_of;
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct EfiBootServiceHeaderTag {
-    typ: HeaderTagType,
-    flags: HeaderTagFlag,
-    size: u32,
+    header: HeaderTagHeader,
 }
 
 impl EfiBootServiceHeaderTag {
+    /// Constructs a new tag.
+    #[must_use]
     pub const fn new(flags: HeaderTagFlag) -> Self {
-        EfiBootServiceHeaderTag {
-            typ: HeaderTagType::EfiBS,
-            flags,
-            size: size_of::<Self>() as u32,
-        }
+        let header = HeaderTagHeader::new(HeaderTagType::EfiBS, flags, size_of::<Self>() as u32);
+        Self { header }
     }
 
+    /// Returns the [`HeaderTagType`].
+    #[must_use]
     pub const fn typ(&self) -> HeaderTagType {
-        self.typ
+        self.header.typ()
     }
+
+    /// Returns the [`HeaderTagFlag`]s.
+    #[must_use]
     pub const fn flags(&self) -> HeaderTagFlag {
-        self.flags
+        self.header.flags()
     }
+
+    /// Returns the size.
+    #[must_use]
     pub const fn size(&self) -> u32 {
-        self.size
+        self.header.size()
     }
 }
 

+ 6 - 0
multiboot2/Changelog.md

@@ -2,8 +2,14 @@
 
 ## Unreleased
 
+- **Breaking** All functions that returns something useful are now `#[must_use]`
+- **Breaking** More public fields in tags were replaced by public getters, such
+  as `SmbiosTag::major()`
 - updated dependencies
 - MSRV is 1.75
+- documentation enhancements
+- Introduced new `TagHeader` type as replacement for the `Tag` type that will
+  be changed in the next step.
 
 ## 0.20.2 (2024-05-26)
 

+ 459 - 0
multiboot2/src/boot_information.rs

@@ -0,0 +1,459 @@
+//! Module for [`BootInformation`].
+
+#[cfg(feature = "builder")]
+use crate::builder::AsBytes;
+use crate::framebuffer::UnknownFramebufferType;
+use crate::tag::{TagHeader, TagIter};
+use crate::{
+    module, BasicMemoryInfoTag, BootLoaderNameTag, CommandLineTag, EFIBootServicesNotExitedTag,
+    EFIImageHandle32Tag, EFIImageHandle64Tag, EFIMemoryMapTag, EFISdt32Tag, EFISdt64Tag,
+    ElfSectionIter, ElfSectionsTag, EndTag, FramebufferTag, ImageLoadPhysAddrTag, MemoryMapTag,
+    ModuleIter, RsdpV1Tag, RsdpV2Tag, SmbiosTag, TagTrait, VBEInfoTag,
+};
+use core::fmt;
+use core::mem;
+use core::ptr;
+use derive_more::Display;
+
+/// Error type that describes errors while loading/parsing a multiboot2 information structure
+/// from a given address.
+#[derive(Display, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum MbiLoadError {
+    /// The address is invalid. Make sure that the address is 8-byte aligned,
+    /// according to the spec.
+    #[display("The address is invalid")]
+    IllegalAddress,
+    /// The total size of the multiboot2 information structure must be not zero
+    /// and a multiple of 8.
+    #[display("The size of the MBI is unexpected")]
+    IllegalTotalSize(u32),
+    /// Missing end tag. Each multiboot2 boot information requires to have an
+    /// end tag.
+    #[display("There is no end tag")]
+    NoEndTag,
+}
+
+#[cfg(feature = "unstable")]
+impl core::error::Error for MbiLoadError {}
+
+/// The basic header of a [`BootInformation`] as sized Rust type.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[repr(C)]
+pub struct BootInformationHeader {
+    // size is multiple of 8
+    total_size: u32,
+    _reserved: u32,
+    // Followed by the boot information tags.
+}
+
+impl BootInformationHeader {
+    #[cfg(feature = "builder")]
+    pub(crate) const fn new(total_size: u32) -> Self {
+        Self {
+            total_size,
+            _reserved: 0,
+        }
+    }
+
+    /// Returns the total size of the structure.
+    #[must_use]
+    pub const fn total_size(&self) -> u32 {
+        self.total_size
+    }
+}
+
+#[cfg(feature = "builder")]
+impl AsBytes for BootInformationHeader {}
+
+/// This type holds the whole data of the MBI. This helps to better satisfy miri
+/// when it checks for memory issues.
+#[derive(ptr_meta::Pointee)]
+#[repr(C)]
+struct BootInformationInner {
+    header: BootInformationHeader,
+    tags: [u8],
+}
+
+impl BootInformationInner {
+    /// Checks if the MBI has a valid end tag by checking the end of the mbi's
+    /// bytes.
+    fn has_valid_end_tag(&self) -> bool {
+        let self_ptr = ptr::addr_of!(*self);
+
+        let end_tag_ptr = unsafe {
+            self_ptr
+                .cast::<u8>()
+                .add(self.header.total_size as usize)
+                .sub(mem::size_of::<EndTag>())
+                .cast::<TagHeader>()
+        };
+        let end_tag = unsafe { &*end_tag_ptr };
+
+        end_tag.typ == EndTag::ID && end_tag.size as usize == mem::size_of::<EndTag>()
+    }
+}
+
+/// A Multiboot 2 Boot Information (MBI) accessor.
+#[repr(transparent)]
+pub struct BootInformation<'a>(&'a BootInformationInner);
+
+impl<'a> BootInformation<'a> {
+    /// Loads the [`BootInformation`] from a pointer. The pointer must be valid
+    /// and aligned to an 8-byte boundary, as defined by the spec.
+    ///
+    /// ## Example
+    ///
+    /// ```rust
+    /// use multiboot2::{BootInformation, BootInformationHeader};
+    ///
+    /// fn kernel_entry(mb_magic: u32, mbi_ptr: u32) {
+    ///     if mb_magic == multiboot2::MAGIC {
+    ///         let boot_info = unsafe { BootInformation::load(mbi_ptr as *const BootInformationHeader).unwrap() };
+    ///         let _cmd = boot_info.command_line_tag();
+    ///     } else { /* Panic or use multiboot1 flow. */ }
+    /// }
+    /// ```
+    ///
+    /// ## Safety
+    /// * `ptr` must be valid for reading. Otherwise this function might cause
+    ///   invalid machine state or crash your binary (kernel). This can be the
+    ///   case in environments with standard environment (segfault), but also in
+    ///   boot environments, such as UEFI.
+    /// * The memory at `ptr` must not be modified after calling `load` or the
+    ///   program may observe unsynchronized mutation.
+    pub unsafe fn load(ptr: *const BootInformationHeader) -> Result<Self, MbiLoadError> {
+        // null or not aligned
+        if ptr.is_null() || ptr.align_offset(8) != 0 {
+            return Err(MbiLoadError::IllegalAddress);
+        }
+
+        // mbi: reference to basic header
+        let mbi = &*ptr;
+
+        // Check if total size is not 0 and a multiple of 8.
+        if mbi.total_size == 0 || mbi.total_size & 0b111 != 0 {
+            return Err(MbiLoadError::IllegalTotalSize(mbi.total_size));
+        }
+
+        let slice_size = mbi.total_size as usize - mem::size_of::<BootInformationHeader>();
+        // mbi: reference to full mbi
+        let mbi = ptr_meta::from_raw_parts::<BootInformationInner>(ptr.cast(), slice_size);
+        let mbi = &*mbi;
+
+        if !mbi.has_valid_end_tag() {
+            return Err(MbiLoadError::NoEndTag);
+        }
+
+        Ok(Self(mbi))
+    }
+
+    /// Get the start address of the boot info.
+    #[must_use]
+    pub fn start_address(&self) -> usize {
+        self.as_ptr() as usize
+    }
+
+    /// Get the start address of the boot info as pointer.
+    #[must_use]
+    pub const fn as_ptr(&self) -> *const () {
+        core::ptr::addr_of!(*self.0).cast()
+    }
+
+    /// Get the end address of the boot info.
+    ///
+    /// This is the same as doing:
+    ///
+    /// ```rust,no_run
+    /// # use multiboot2::{BootInformation, BootInformationHeader};
+    /// # let ptr = 0xdeadbeef as *const BootInformationHeader;
+    /// # let boot_info = unsafe { BootInformation::load(ptr).unwrap() };
+    /// let end_addr = boot_info.start_address() + boot_info.total_size();
+    /// ```
+    #[must_use]
+    pub fn end_address(&self) -> usize {
+        self.start_address() + self.total_size()
+    }
+
+    /// Get the total size of the boot info struct.
+    #[must_use]
+    pub const fn total_size(&self) -> usize {
+        self.0.header.total_size as usize
+    }
+
+    // ######################################################
+    // ### BEGIN OF TAG GETTERS (in alphabetical order)
+
+    /*fn apm(&self) {
+        // also add to debug output
+        todo!()
+    }*/
+
+    /// Search for the basic memory info tag.
+    #[must_use]
+    pub fn basic_memory_info_tag(&self) -> Option<&BasicMemoryInfoTag> {
+        self.get_tag::<BasicMemoryInfoTag>()
+    }
+
+    /// Search for the BootLoader name tag.
+    #[must_use]
+    pub fn boot_loader_name_tag(&self) -> Option<&BootLoaderNameTag> {
+        self.get_tag::<BootLoaderNameTag>()
+    }
+
+    /*fn bootdev(&self) {
+        // also add to debug output
+        todo!()
+    }*/
+
+    /// Search for the Command line tag.
+    #[must_use]
+    pub fn command_line_tag(&self) -> Option<&CommandLineTag> {
+        self.get_tag::<CommandLineTag>()
+    }
+
+    /// Search for the EFI boot services not exited tag.
+    #[must_use]
+    pub fn efi_bs_not_exited_tag(&self) -> Option<&EFIBootServicesNotExitedTag> {
+        self.get_tag::<EFIBootServicesNotExitedTag>()
+    }
+
+    /// Search for the EFI Memory map tag, if the boot services were exited.
+    /// Otherwise, if the [`TagType::EfiBs`] tag is present, this returns `None`
+    /// as it is strictly recommended to get the memory map from the `uefi`
+    /// services.
+    #[must_use]
+    pub fn efi_memory_map_tag(&self) -> Option<&EFIMemoryMapTag> {
+        // If the EFIBootServicesNotExited is present, then we should not use
+        // the memory map, as it could still be in use.
+        self.get_tag::<EFIBootServicesNotExitedTag>().map_or_else(
+            || self.get_tag::<EFIMemoryMapTag>(), |_tag| {
+                            log::debug!("The EFI memory map is present but the UEFI Boot Services Not Existed Tag is present. Returning None.");
+                             None
+                        })
+    }
+
+    /// Search for the EFI 32-bit SDT tag.
+    #[must_use]
+    pub fn efi_sdt32_tag(&self) -> Option<&EFISdt32Tag> {
+        self.get_tag::<EFISdt32Tag>()
+    }
+
+    /// Search for the EFI 64-bit SDT tag.
+    #[must_use]
+    pub fn efi_sdt64_tag(&self) -> Option<&EFISdt64Tag> {
+        self.get_tag::<EFISdt64Tag>()
+    }
+
+    /// Search for the EFI 32-bit image handle pointer tag.
+    #[must_use]
+    pub fn efi_ih32_tag(&self) -> Option<&EFIImageHandle32Tag> {
+        self.get_tag::<EFIImageHandle32Tag>()
+    }
+
+    /// Search for the EFI 64-bit image handle pointer tag.
+    #[must_use]
+    pub fn efi_ih64_tag(&self) -> Option<&EFIImageHandle64Tag> {
+        self.get_tag::<EFIImageHandle64Tag>()
+    }
+
+    /// Returns an [`ElfSectionIter`] iterator over the ELF Sections, if the
+    /// [`ElfSectionsTag`] is present.
+    ///
+    /// # Examples
+    ///
+    /// ```rust,no_run
+    /// # use multiboot2::{BootInformation, BootInformationHeader};
+    /// # let ptr = 0xdeadbeef as *const BootInformationHeader;
+    /// # let boot_info = unsafe { BootInformation::load(ptr).unwrap() };
+    /// if let Some(sections) = boot_info.elf_sections() {
+    ///     let mut total = 0;
+    ///     for section in sections {
+    ///         println!("Section: {:?}", section);
+    ///         total += 1;
+    ///     }
+    /// }
+    /// ```
+    #[must_use]
+    pub fn elf_sections(&self) -> Option<ElfSectionIter> {
+        let tag = self.get_tag::<ElfSectionsTag>();
+        tag.map(|t| {
+            assert!((t.entry_size * t.shndx) <= t.size);
+            t.sections()
+        })
+    }
+
+    /// Search for the VBE framebuffer tag. The result is `Some(Err(e))`, if the
+    /// framebuffer type is unknown, while the framebuffer tag is present.
+    #[must_use]
+    pub fn framebuffer_tag(&self) -> Option<Result<&FramebufferTag, UnknownFramebufferType>> {
+        self.get_tag::<FramebufferTag>()
+            .map(|tag| match tag.buffer_type() {
+                Ok(_) => Ok(tag),
+                Err(e) => Err(e),
+            })
+    }
+
+    /// Search for the Image Load Base Physical Address tag.
+    #[must_use]
+    pub fn load_base_addr_tag(&self) -> Option<&ImageLoadPhysAddrTag> {
+        self.get_tag::<ImageLoadPhysAddrTag>()
+    }
+
+    /// Search for the Memory map tag.
+    #[must_use]
+    pub fn memory_map_tag(&self) -> Option<&MemoryMapTag> {
+        self.get_tag::<MemoryMapTag>()
+    }
+
+    /// Get an iterator of all module tags.
+    #[must_use]
+    pub fn module_tags(&self) -> ModuleIter {
+        module::module_iter(self.tags())
+    }
+
+    /*fn network_tag(&self) {
+        // also add to debug output
+        todo!()
+    }*/
+
+    /// Search for the (ACPI 1.0) RSDP tag.
+    #[must_use]
+    pub fn rsdp_v1_tag(&self) -> Option<&RsdpV1Tag> {
+        self.get_tag::<RsdpV1Tag>()
+    }
+
+    /// Search for the (ACPI 2.0 or later) RSDP tag.
+    #[must_use]
+    pub fn rsdp_v2_tag(&self) -> Option<&RsdpV2Tag> {
+        self.get_tag::<RsdpV2Tag>()
+    }
+
+    /// Search for the SMBIOS tag.
+    #[must_use]
+    pub fn smbios_tag(&self) -> Option<&SmbiosTag> {
+        self.get_tag::<SmbiosTag>()
+    }
+
+    /// Search for the VBE information tag.
+    #[must_use]
+    pub fn vbe_info_tag(&self) -> Option<&VBEInfoTag> {
+        self.get_tag::<VBEInfoTag>()
+    }
+
+    // ### END OF TAG GETTERS
+    // ######################################################
+
+    /// Public getter to find any Multiboot tag by its type, including
+    /// specified and custom ones.
+    ///
+    /// # Specified or Custom Tags
+    /// The Multiboot2 specification specifies a list of tags, see [`TagType`].
+    /// However, it doesn't forbid to use custom tags. Because of this, there
+    /// exists the [`TagType`] abstraction. It is recommended to use this
+    /// getter only for custom tags. For specified tags, use getters, such as
+    /// [`Self::efi_ih64_tag`].
+    ///
+    /// ## Use Custom Tags
+    /// The following example shows how you may use this interface to parse
+    /// custom tags from the MBI. If they are dynamically sized (DST), a few more
+    /// special handling is required. This is reflected by code-comments.
+    ///
+    /// ```no_run
+    /// use multiboot2::{BootInformation, BootInformationHeader, StringError, Tag, TagTrait, TagType, TagTypeId};
+    ///
+    /// #[repr(C)]
+    /// #[derive(multiboot2::Pointee)] // Only needed for DSTs.
+    /// struct CustomTag {
+    ///     tag: TagTypeId,
+    ///     size: u32,
+    ///     // begin of inline string
+    ///     name: [u8],
+    /// }
+    ///
+    /// // This implementation is only necessary for tags that are DSTs.
+    /// impl TagTrait for CustomTag {
+    ///     const ID: TagType = TagType::Custom(0x1337);
+    ///
+    ///     fn dst_size(base_tag: &Tag) -> usize {
+    ///         // The size of the sized portion of the custom tag.
+    ///         let tag_base_size = 8; // id + size is 8 byte in size
+    ///         assert!(base_tag.size >= 8);
+    ///         base_tag.size as usize - tag_base_size
+    ///     }
+    /// }
+    ///
+    /// impl CustomTag {
+    ///     fn name(&self) -> Result<&str, StringError> {
+    ///         Tag::parse_slice_as_string(&self.name)
+    ///     }
+    /// }
+    /// let mbi_ptr = 0xdeadbeef as *const BootInformationHeader;
+    /// let mbi = unsafe { BootInformation::load(mbi_ptr).unwrap() };
+    ///
+    /// let tag = mbi
+    ///     .get_tag::<CustomTag>()
+    ///     .unwrap();
+    /// assert_eq!(tag.name(), Ok("name"));
+    /// ```
+    #[must_use]
+    pub fn get_tag<TagT: TagTrait + ?Sized + 'a>(&'a self) -> Option<&'a TagT> {
+        self.tags()
+            .find(|tag| tag.typ == TagT::ID)
+            .map(|tag| tag.cast_tag::<TagT>())
+    }
+
+    /// Returns an iterator over all tags.
+    fn tags(&self) -> TagIter {
+        TagIter::new(&self.0.tags)
+    }
+}
+
+impl fmt::Debug for BootInformation<'_> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        /// Limit how many Elf-Sections should be debug-formatted.
+        /// Can be thousands of sections for a Rust binary => this is useless output.
+        /// If the user really wants this, they should debug-format the field directly.
+        const ELF_SECTIONS_LIMIT: usize = 7;
+
+        let mut debug = f.debug_struct("Multiboot2BootInformation");
+        debug
+            .field("start_address", &self.start_address())
+            .field("end_address", &self.end_address())
+            .field("total_size", &self.total_size())
+            // now tags in alphabetical order
+            .field("basic_memory_info", &(self.basic_memory_info_tag()))
+            .field("boot_loader_name", &self.boot_loader_name_tag())
+            // .field("bootdev", &self.bootdev_tag())
+            .field("command_line", &self.command_line_tag())
+            .field("efi_bs_not_exited", &self.efi_bs_not_exited_tag())
+            .field("efi_memory_map", &self.efi_memory_map_tag())
+            .field("efi_sdt32", &self.efi_sdt32_tag())
+            .field("efi_sdt64", &self.efi_sdt64_tag())
+            .field("efi_ih32", &self.efi_ih32_tag())
+            .field("efi_ih64", &self.efi_ih64_tag());
+
+        // usually this is REALLY big (thousands of tags) => skip it here
+        {
+            let elf_sections_tag_entries_count =
+                self.elf_sections().map(|x| x.count()).unwrap_or(0);
+
+            if elf_sections_tag_entries_count > ELF_SECTIONS_LIMIT {
+                debug.field("elf_sections (count)", &elf_sections_tag_entries_count);
+            } else {
+                debug.field("elf_sections", &self.elf_sections().unwrap_or_default());
+            }
+        }
+
+        debug
+            .field("framebuffer", &self.framebuffer_tag())
+            .field("load_base_addr", &self.load_base_addr_tag())
+            .field("memory_map", &self.memory_map_tag())
+            .field("modules", &self.module_tags())
+            // .field("network", &self.network_tag())
+            .field("rsdp_v1", &self.rsdp_v1_tag())
+            .field("rsdp_v2", &self.rsdp_v2_tag())
+            .field("smbios_tag", &self.smbios_tag())
+            .field("vbe_info_tag", &self.vbe_info_tag())
+            .finish()
+    }
+}

+ 21 - 8
multiboot2/src/boot_loader_name.rs

@@ -1,26 +1,27 @@
 //! Module for [`BootLoaderNameTag`].
 
-use crate::tag::StringError;
+use crate::tag::{StringError, TagHeader};
 use crate::{Tag, TagTrait, TagType, TagTypeId};
 use core::fmt::{Debug, Formatter};
-use core::mem::size_of;
+use core::mem;
 #[cfg(feature = "builder")]
 use {crate::builder::BoxedDst, alloc::vec::Vec};
 
-const METADATA_SIZE: usize = size_of::<TagTypeId>() + size_of::<u32>();
+const METADATA_SIZE: usize = mem::size_of::<TagTypeId>() + mem::size_of::<u32>();
 
 /// The bootloader name tag.
 #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct BootLoaderNameTag {
-    typ: TagTypeId,
-    size: u32,
+    header: TagHeader,
     /// Null-terminated UTF-8 string
     name: [u8],
 }
 
 impl BootLoaderNameTag {
+    /// Constructs a new tag.
     #[cfg(feature = "builder")]
+    #[must_use]
     pub fn new(name: &str) -> BoxedDst<Self> {
         let mut bytes: Vec<_> = name.bytes().collect();
         if !bytes.ends_with(&[0]) {
@@ -30,6 +31,18 @@ impl BootLoaderNameTag {
         BoxedDst::new(&bytes)
     }
 
+    /// Returns the underlying [`TagType`].
+    #[must_use]
+    pub fn typ(&self) -> TagType {
+        self.header.typ.into()
+    }
+
+    /// Returns the underlying tag size.
+    #[must_use]
+    pub const fn size(&self) -> usize {
+        self.header.size as usize
+    }
+
     /// Reads the name of the bootloader that is booting the kernel as Rust
     /// string slice without the null-byte.
     ///
@@ -55,8 +68,8 @@ impl BootLoaderNameTag {
 impl Debug for BootLoaderNameTag {
     fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
         f.debug_struct("BootLoaderNameTag")
-            .field("typ", &{ self.typ })
-            .field("size", &{ self.size })
+            .field("typ", &self.header.typ)
+            .field("size", &self.header.size)
             .field("name", &self.name())
             .finish()
     }
@@ -101,7 +114,7 @@ mod tests {
         let tag = get_bytes();
         let tag = unsafe { &*tag.as_ptr().cast::<Tag>() };
         let tag = tag.cast_tag::<BootLoaderNameTag>();
-        assert_eq!({ tag.typ }, TagType::BootLoaderName);
+        assert_eq!(tag.header.typ, TagType::BootLoaderName);
         assert_eq!(tag.name().expect("must be valid UTF-8"), MSG);
     }
 

+ 1 - 1
multiboot2/src/builder/boxed_dst.rs

@@ -144,7 +144,7 @@ mod tests {
 
     #[test]
     fn can_hold_tag_trait() {
-        fn consume<T: TagTrait + ?Sized>(_: &T) {}
+        const fn consume<T: TagTrait + ?Sized>(_: &T) {}
         let content = b"hallo\0";
 
         let tag = BoxedDst::<CustomTag>::new(content);

+ 21 - 1
multiboot2/src/builder/information.rs

@@ -73,6 +73,7 @@ impl Default for InformationBuilder {
 
 impl InformationBuilder {
     /// Creates a new builder.
+    #[must_use]
     pub const fn new() -> Self {
         Self(Vec::new())
     }
@@ -87,6 +88,7 @@ impl InformationBuilder {
     /// [`Self::build`]-method is called. This function assumes that the begin
     /// of the boot information is 8-byte aligned and automatically adds padding
     /// between tags to ensure that each tag is 8-byte aligned.
+    #[must_use]
     pub fn expected_len(&self) -> usize {
         let tag_size_iter = self.0.iter().map(|(_, bytes)| bytes.len());
 
@@ -118,6 +120,7 @@ impl InformationBuilder {
     }
 
     /// Constructs the bytes for a valid Multiboot2 information with the given properties.
+    #[must_use]
     pub fn build(self) -> BootInformationBytes {
         const ALIGN: usize = 8;
 
@@ -202,92 +205,109 @@ impl InformationBuilder {
     }
 
     /// Adds a 'basic memory information' tag (represented by [`BasicMemoryInfoTag`]) to the builder.
+    #[must_use]
     pub fn basic_memory_info_tag(self, tag: BasicMemoryInfoTag) -> Self {
         self.add_tag(&tag).unwrap()
     }
 
     /// Adds a 'bootloader name' tag (represented by [`BootLoaderNameTag`]) to the builder.
+    #[must_use]
     pub fn bootloader_name_tag(self, tag: BoxedDst<BootLoaderNameTag>) -> Self {
         self.add_tag(&*tag).unwrap()
     }
 
     /// Adds a 'command line' tag (represented by [`CommandLineTag`]) to the builder.
+    #[must_use]
     pub fn command_line_tag(self, tag: BoxedDst<CommandLineTag>) -> Self {
         self.add_tag(&*tag).unwrap()
     }
 
     /// Adds a 'EFI 32-bit system table pointer' tag (represented by [`EFISdt32Tag`]) to the builder.
+    #[must_use]
     pub fn efisdt32_tag(self, tag: EFISdt32Tag) -> Self {
         self.add_tag(&tag).unwrap()
     }
 
     /// Adds a 'EFI 64-bit system table pointer' tag (represented by [`EFISdt64Tag`]) to the builder.
+    #[must_use]
     pub fn efisdt64_tag(self, tag: EFISdt64Tag) -> Self {
         self.add_tag(&tag).unwrap()
     }
 
     /// Adds a 'EFI boot services not terminated' tag (represented by [`EFIBootServicesNotExitedTag`]) to the builder.
+    #[must_use]
     pub fn efi_boot_services_not_exited_tag(self) -> Self {
         self.add_tag(&EFIBootServicesNotExitedTag::new()).unwrap()
     }
 
     /// Adds a 'EFI 32-bit image handle pointer' tag (represented by [`EFIImageHandle32Tag`]) to the builder.
+    #[must_use]
     pub fn efi_image_handle32(self, tag: EFIImageHandle32Tag) -> Self {
         self.add_tag(&tag).unwrap()
     }
 
     /// Adds a 'EFI 64-bit image handle pointer' tag (represented by [`EFIImageHandle64Tag`]) to the builder.
+    #[must_use]
     pub fn efi_image_handle64(self, tag: EFIImageHandle64Tag) -> Self {
         self.add_tag(&tag).unwrap()
     }
 
     /// Adds a 'EFI Memory map' tag (represented by [`EFIMemoryMapTag`]) to the builder.
+    #[must_use]
     pub fn efi_memory_map_tag(self, tag: BoxedDst<EFIMemoryMapTag>) -> Self {
         self.add_tag(&*tag).unwrap()
     }
 
     /// Adds a 'ELF-Symbols' tag (represented by [`ElfSectionsTag`]) to the builder.
+    #[must_use]
     pub fn elf_sections_tag(self, tag: BoxedDst<ElfSectionsTag>) -> Self {
         self.add_tag(&*tag).unwrap()
     }
 
     /// Adds a 'Framebuffer info' tag (represented by [`FramebufferTag`]) to the builder.
+    #[must_use]
     pub fn framebuffer_tag(self, tag: BoxedDst<FramebufferTag>) -> Self {
         self.add_tag(&*tag).unwrap()
     }
 
     /// Adds a 'Image load base physical address' tag (represented by [`ImageLoadPhysAddrTag`]) to the builder.
+    #[must_use]
     pub fn image_load_addr(self, tag: ImageLoadPhysAddrTag) -> Self {
         self.add_tag(&tag).unwrap()
     }
 
     /// Adds a (*none EFI*) 'memory map' tag (represented by [`MemoryMapTag`]) to the builder.
+    #[must_use]
     pub fn memory_map_tag(self, tag: BoxedDst<MemoryMapTag>) -> Self {
         self.add_tag(&*tag).unwrap()
     }
 
     /// Adds a 'Modules' tag (represented by [`ModuleTag`]) to the builder.
     /// This tag can occur multiple times in boot information.
+    #[must_use]
     pub fn add_module_tag(self, tag: BoxedDst<ModuleTag>) -> Self {
         self.add_tag(&*tag).unwrap()
     }
 
     /// Adds a 'ACPI old RSDP' tag (represented by [`RsdpV1Tag`]) to the builder.
+    #[must_use]
     pub fn rsdp_v1_tag(self, tag: RsdpV1Tag) -> Self {
         self.add_tag(&tag).unwrap()
     }
 
     /// Adds a 'ACPI new RSDP' tag (represented by [`RsdpV2Tag`]) to the builder.
+    #[must_use]
     pub fn rsdp_v2_tag(self, tag: RsdpV2Tag) -> Self {
         self.add_tag(&tag).unwrap()
     }
 
     /// Adds a 'SMBIOS tables' tag (represented by [`SmbiosTag`]) to the builder.
+    #[must_use]
     pub fn smbios_tag(self, tag: BoxedDst<SmbiosTag>) -> Self {
         self.add_tag(&*tag).unwrap()
     }
 
-    fn tag_is_allowed_multiple_times(tag_type: TagType) -> bool {
+    const fn tag_is_allowed_multiple_times(tag_type: TagType) -> bool {
         matches!(
             tag_type,
             TagType::Module | TagType::Smbios | TagType::Custom(_)

+ 4 - 1
multiboot2/src/builder/mod.rs

@@ -8,8 +8,11 @@ pub use boxed_dst::BoxedDst;
 pub use information::InformationBuilder;
 
 /// Helper trait for all structs that need to be serialized that do not
-/// implement `TagTrait`.
+/// implement [`TagTrait`].
+///
+/// [`TagTrait`]: crate::TagTrait
 pub trait AsBytes: Sized {
+    /// Returns the raw bytes of the type.
     fn as_bytes(&self) -> &[u8] {
         let ptr = core::ptr::addr_of!(*self);
         let size = core::mem::size_of::<Self>();

+ 7 - 8
multiboot2/src/command_line.rs

@@ -1,15 +1,14 @@
 //! Module for [`CommandLineTag`].
 
+use crate::tag::{StringError, TagHeader};
 use crate::{Tag, TagTrait, TagType, TagTypeId};
-
-use crate::tag::StringError;
 use core::fmt::{Debug, Formatter};
 use core::mem;
 use core::str;
 #[cfg(feature = "builder")]
 use {crate::builder::BoxedDst, alloc::vec::Vec};
 
-pub(crate) const METADATA_SIZE: usize = mem::size_of::<TagTypeId>() + mem::size_of::<u32>();
+const METADATA_SIZE: usize = mem::size_of::<TagTypeId>() + mem::size_of::<u32>();
 
 /// This tag contains the command line string.
 ///
@@ -18,8 +17,7 @@ pub(crate) const METADATA_SIZE: usize = mem::size_of::<TagTypeId>() + mem::size_
 #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct CommandLineTag {
-    typ: TagTypeId,
-    size: u32,
+    header: TagHeader,
     /// Null-terminated UTF-8 string
     cmdline: [u8],
 }
@@ -27,6 +25,7 @@ pub struct CommandLineTag {
 impl CommandLineTag {
     /// Create a new command line tag from the given string.
     #[cfg(feature = "builder")]
+    #[must_use]
     pub fn new(command_line: &str) -> BoxedDst<Self> {
         let mut bytes: Vec<_> = command_line.bytes().collect();
         if !bytes.ends_with(&[0]) {
@@ -63,8 +62,8 @@ impl CommandLineTag {
 impl Debug for CommandLineTag {
     fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
         f.debug_struct("CommandLineTag")
-            .field("typ", &{ self.typ })
-            .field("size", &{ self.size })
+            .field("typ", &self.header.typ)
+            .field("size", &self.header.size)
             .field("cmdline", &self.cmdline())
             .finish()
     }
@@ -109,7 +108,7 @@ mod tests {
         let tag = get_bytes();
         let tag = unsafe { &*tag.as_ptr().cast::<Tag>() };
         let tag = tag.cast_tag::<CommandLineTag>();
-        assert_eq!({ tag.typ }, TagType::Cmdline);
+        assert_eq!(tag.header.typ, TagType::Cmdline);
         assert_eq!(tag.cmdline().expect("must be valid UTF-8"), MSG);
     }
 

+ 27 - 25
multiboot2/src/efi.rs

@@ -6,7 +6,7 @@
 //! - [`EFIImageHandle64Tag`]
 //! - [`EFIBootServicesNotExitedTag`]
 
-use crate::TagTypeId;
+use crate::tag::TagHeader;
 use crate::{Tag, TagTrait, TagType};
 use core::mem::size_of;
 
@@ -14,23 +14,23 @@ use core::mem::size_of;
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct EFISdt32Tag {
-    typ: TagTypeId,
-    size: u32,
+    header: TagHeader,
     pointer: u32,
 }
 
 impl EFISdt32Tag {
     /// Create a new tag to pass the EFI32 System Table pointer.
+    #[must_use]
     pub fn new(pointer: u32) -> Self {
         Self {
-            typ: Self::ID.into(),
-            size: size_of::<Self>().try_into().unwrap(),
+            header: TagHeader::new(Self::ID, size_of::<Self>().try_into().unwrap()),
             pointer,
         }
     }
 
     /// The physical address of a i386 EFI system table.
-    pub fn sdt_address(&self) -> usize {
+    #[must_use]
+    pub const fn sdt_address(&self) -> usize {
         self.pointer as usize
     }
 }
@@ -45,23 +45,23 @@ impl TagTrait for EFISdt32Tag {
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct EFISdt64Tag {
-    typ: TagTypeId,
-    size: u32,
+    header: TagHeader,
     pointer: u64,
 }
 
 impl EFISdt64Tag {
     /// Create a new tag to pass the EFI64 System Table pointer.
+    #[must_use]
     pub fn new(pointer: u64) -> Self {
         Self {
-            typ: Self::ID.into(),
-            size: size_of::<Self>().try_into().unwrap(),
+            header: TagHeader::new(Self::ID, size_of::<Self>().try_into().unwrap()),
             pointer,
         }
     }
 
     /// The physical address of a x86_64 EFI system table.
-    pub fn sdt_address(&self) -> usize {
+    #[must_use]
+    pub const fn sdt_address(&self) -> usize {
         self.pointer as usize
     }
 }
@@ -77,23 +77,24 @@ impl TagTrait for EFISdt64Tag {
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct EFIImageHandle32Tag {
-    typ: TagTypeId,
-    size: u32,
+    header: TagHeader,
     pointer: u32,
 }
 
 impl EFIImageHandle32Tag {
+    /// Constructs a new tag.
     #[cfg(feature = "builder")]
+    #[must_use]
     pub fn new(pointer: u32) -> Self {
         Self {
-            typ: Self::ID.into(),
-            size: size_of::<Self>().try_into().unwrap(),
+            header: TagHeader::new(Self::ID, size_of::<Self>().try_into().unwrap()),
             pointer,
         }
     }
 
     /// Returns the physical address of the EFI image handle.
-    pub fn image_handle(&self) -> usize {
+    #[must_use]
+    pub const fn image_handle(&self) -> usize {
         self.pointer as usize
     }
 }
@@ -109,23 +110,24 @@ impl TagTrait for EFIImageHandle32Tag {
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct EFIImageHandle64Tag {
-    typ: TagTypeId,
-    size: u32,
+    header: TagHeader,
     pointer: u64,
 }
 
 impl EFIImageHandle64Tag {
+    /// Constructs a new tag.
     #[cfg(feature = "builder")]
+    #[must_use]
     pub fn new(pointer: u64) -> Self {
         Self {
-            typ: Self::ID.into(),
-            size: size_of::<Self>().try_into().unwrap(),
+            header: TagHeader::new(Self::ID, size_of::<Self>().try_into().unwrap()),
             pointer,
         }
     }
 
     /// Returns the physical address of the EFI image handle.
-    pub fn image_handle(&self) -> usize {
+    #[must_use]
+    pub const fn image_handle(&self) -> usize {
         self.pointer as usize
     }
 }
@@ -140,12 +142,13 @@ impl TagTrait for EFIImageHandle64Tag {
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct EFIBootServicesNotExitedTag {
-    typ: TagTypeId,
-    size: u32,
+    header: TagHeader,
 }
 
 impl EFIBootServicesNotExitedTag {
+    /// Constructs a new tag.
     #[cfg(feature = "builder")]
+    #[must_use]
     pub fn new() -> Self {
         Self::default()
     }
@@ -155,8 +158,7 @@ impl EFIBootServicesNotExitedTag {
 impl Default for EFIBootServicesNotExitedTag {
     fn default() -> Self {
         Self {
-            typ: TagType::EfiBs.into(),
-            size: core::mem::size_of::<Self>().try_into().unwrap(),
+            header: TagHeader::new(Self::ID, size_of::<Self>().try_into().unwrap()),
         }
     }
 }

+ 13 - 4
multiboot2/src/elf_sections.rs

@@ -4,10 +4,10 @@
 use crate::builder::BoxedDst;
 use crate::{Tag, TagTrait, TagType, TagTypeId};
 use core::fmt::{Debug, Formatter};
-use core::mem::size_of;
+use core::mem;
 use core::str::Utf8Error;
 
-const METADATA_SIZE: usize = size_of::<TagTypeId>() + 4 * size_of::<u32>();
+const METADATA_SIZE: usize = mem::size_of::<TagTypeId>() + 4 * mem::size_of::<u32>();
 
 /// This tag contains the section header table from an ELF binary.
 // The sections iterator is provided via the [`ElfSectionsTag::sections`]
@@ -26,6 +26,7 @@ pub struct ElfSectionsTag {
 impl ElfSectionsTag {
     /// Create a new ElfSectionsTag with the given data.
     #[cfg(feature = "builder")]
+    #[must_use]
     pub fn new(
         number_of_sections: u32,
         entry_size: u32,
@@ -43,7 +44,7 @@ impl ElfSectionsTag {
     }
 
     /// Get an iterator of loaded ELF sections.
-    pub(crate) fn sections(&self) -> ElfSectionIter {
+    pub(crate) const fn sections(&self) -> ElfSectionIter {
         let string_section_offset = (self.shndx * self.entry_size) as isize;
         let string_section_ptr =
             unsafe { self.first_section().offset(string_section_offset) as *const _ };
@@ -55,7 +56,7 @@ impl ElfSectionsTag {
         }
     }
 
-    fn first_section(&self) -> *const u8 {
+    const fn first_section(&self) -> *const u8 {
         &(self.sections[0]) as *const _
     }
 }
@@ -174,6 +175,7 @@ struct ElfSectionInner64 {
 
 impl ElfSection {
     /// Get the section type as a `ElfSectionType` enum variant.
+    #[must_use]
     pub fn section_type(&self) -> ElfSectionType {
         match self.get().typ() {
             0 => ElfSectionType::Unused,
@@ -201,6 +203,7 @@ impl ElfSection {
     }
 
     /// Get the "raw" section type as a `u32`
+    #[must_use]
     pub fn section_type_raw(&self) -> u32 {
         self.get().typ()
     }
@@ -224,6 +227,7 @@ impl ElfSection {
     }
 
     /// Get the physical start address of the section.
+    #[must_use]
     pub fn start_address(&self) -> u64 {
         self.get().addr()
     }
@@ -231,11 +235,13 @@ impl ElfSection {
     /// Get the physical end address of the section.
     ///
     /// This is the same as doing `section.start_address() + section.size()`
+    #[must_use]
     pub fn end_address(&self) -> u64 {
         self.get().addr() + self.get().size()
     }
 
     /// Get the section's size in bytes.
+    #[must_use]
     pub fn size(&self) -> u64 {
         self.get().size()
     }
@@ -246,16 +252,19 @@ impl ElfSection {
     /// modulo the value of `addrlign`. Currently, only 0 and positive
     /// integral powers of two are allowed. Values 0 and 1 mean the section has no
     /// alignment constraints.
+    #[must_use]
     pub fn addralign(&self) -> u64 {
         self.get().addralign()
     }
 
     /// Get the section's flags.
+    #[must_use]
     pub fn flags(&self) -> ElfSectionFlags {
         ElfSectionFlags::from_bits_truncate(self.get().flags())
     }
 
     /// Check if the `ALLOCATED` flag is set in the section flags.
+    #[must_use]
     pub fn is_allocated(&self) -> bool {
         self.flags().contains(ElfSectionFlags::ALLOCATED)
     }

+ 2 - 2
multiboot2/src/end.rs

@@ -6,8 +6,8 @@ use crate::{Tag, TagTrait, TagType, TagTypeId};
 #[repr(C)]
 #[derive(Debug)]
 pub struct EndTag {
-    pub typ: TagTypeId,
-    pub size: u32,
+    typ: TagTypeId,
+    size: u32,
 }
 
 impl Default for EndTag {

+ 37 - 31
multiboot2/src/framebuffer.rs

@@ -1,8 +1,9 @@
 //! Module for [`FramebufferTag`].
 
+use crate::tag::TagHeader;
 use crate::{Tag, TagTrait, TagType, TagTypeId};
 use core::fmt::Debug;
-use core::mem::size_of;
+use core::mem;
 use core::slice;
 use derive_more::Display;
 #[cfg(feature = "builder")]
@@ -16,8 +17,8 @@ struct Reader {
 }
 
 impl Reader {
-    fn new<T>(ptr: *const T) -> Reader {
-        Reader {
+    const fn new<T>(ptr: *const T) -> Self {
+        Self {
             ptr: ptr as *const u8,
             off: 0,
         }
@@ -41,18 +42,17 @@ impl Reader {
     }
 }
 
-const METADATA_SIZE: usize = size_of::<TagTypeId>()
-    + 4 * size_of::<u32>()
-    + size_of::<u64>()
-    + size_of::<u16>()
-    + 2 * size_of::<u8>();
+const METADATA_SIZE: usize = mem::size_of::<TagTypeId>()
+    + 4 * mem::size_of::<u32>()
+    + mem::size_of::<u64>()
+    + mem::size_of::<u16>()
+    + 2 * mem::size_of::<u8>();
 
 /// The VBE Framebuffer information tag.
 #[derive(ptr_meta::Pointee, Eq)]
 #[repr(C)]
 pub struct FramebufferTag {
-    typ: TagTypeId,
-    size: u32,
+    header: TagHeader,
 
     /// Contains framebuffer physical address.
     ///
@@ -84,7 +84,9 @@ pub struct FramebufferTag {
 }
 
 impl FramebufferTag {
+    /// Constructs a new tag.
     #[cfg(feature = "builder")]
+    #[must_use]
     pub fn new(
         address: u64,
         pitch: u32,
@@ -107,27 +109,32 @@ impl FramebufferTag {
     /// This field is 64-bit wide but bootloader should set it under 4GiB if
     /// possible for compatibility with payloads which aren’t aware of PAE or
     /// amd64.
-    pub fn address(&self) -> u64 {
+    #[must_use]
+    pub const fn address(&self) -> u64 {
         self.address
     }
 
     /// Contains the pitch in bytes.
-    pub fn pitch(&self) -> u32 {
+    #[must_use]
+    pub const fn pitch(&self) -> u32 {
         self.pitch
     }
 
     /// Contains framebuffer width in pixels.
-    pub fn width(&self) -> u32 {
+    #[must_use]
+    pub const fn width(&self) -> u32 {
         self.width
     }
 
     /// Contains framebuffer height in pixels.
-    pub fn height(&self) -> u32 {
+    #[must_use]
+    pub const fn height(&self) -> u32 {
         self.height
     }
 
     /// Contains number of bits per pixel.
-    pub fn bpp(&self) -> u8 {
+    #[must_use]
+    pub const fn bpp(&self) -> u8 {
         self.bpp
     }
 
@@ -185,13 +192,13 @@ impl TagTrait for FramebufferTag {
 impl Debug for FramebufferTag {
     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
         f.debug_struct("FramebufferTag")
-            .field("typ", &{ self.typ })
-            .field("size", &{ self.size })
+            .field("typ", &self.header.typ)
+            .field("size", &self.header.size)
             .field("buffer_type", &self.buffer_type())
-            .field("address", &{ self.address })
-            .field("pitch", &{ self.pitch })
-            .field("width", &{ self.width })
-            .field("height", &{ self.height })
+            .field("address", &self.address)
+            .field("pitch", &self.pitch)
+            .field("width", &self.width)
+            .field("height", &self.height)
             .field("bpp", &self.bpp)
             .finish()
     }
@@ -199,15 +206,14 @@ impl Debug for FramebufferTag {
 
 impl PartialEq for FramebufferTag {
     fn eq(&self, other: &Self) -> bool {
-        ({ self.typ } == { other.typ }
-            && { self.size } == { other.size }
-            && { self.address } == { other.address }
-            && { self.pitch } == { other.pitch }
-            && { self.width } == { other.width }
-            && { self.height } == { other.height }
-            && { self.bpp } == { other.bpp }
-            && { self.type_no } == { other.type_no }
-            && self.buffer == other.buffer)
+        self.header == other.header
+            && self.address == { other.address }
+            && self.pitch == { other.pitch }
+            && self.width == { other.width }
+            && self.height == { other.height }
+            && self.bpp == { other.bpp }
+            && self.type_no == { other.type_no }
+            && self.buffer == other.buffer
     }
 }
 
@@ -337,6 +343,6 @@ mod tests {
     // Compile time test
     #[test]
     fn test_size() {
-        assert_eq!(size_of::<FramebufferColor>(), 3)
+        assert_eq!(mem::size_of::<FramebufferColor>(), 3)
     }
 }

+ 8 - 6
multiboot2/src/image_load_addr.rs

@@ -1,6 +1,7 @@
 //! Module for [`ImageLoadPhysAddrTag`].
 
-use crate::{Tag, TagTrait, TagType, TagTypeId};
+use crate::tag::TagHeader;
+use crate::{Tag, TagTrait, TagType};
 #[cfg(feature = "builder")]
 use core::mem::size_of;
 
@@ -10,23 +11,24 @@ use core::mem::size_of;
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct ImageLoadPhysAddrTag {
-    typ: TagTypeId,
-    size: u32,
+    header: TagHeader,
     load_base_addr: u32,
 }
 
 impl ImageLoadPhysAddrTag {
+    /// Constructs a new tag.
     #[cfg(feature = "builder")]
+    #[must_use]
     pub fn new(load_base_addr: u32) -> Self {
         Self {
-            typ: Self::ID.into(),
-            size: size_of::<Self>().try_into().unwrap(),
+            header: TagHeader::new(Self::ID, size_of::<Self>().try_into().unwrap()),
             load_base_addr,
         }
     }
 
     /// Returns the load base address.
-    pub fn load_base_addr(&self) -> u32 {
+    #[must_use]
+    pub const fn load_base_addr(&self) -> u32 {
         self.load_base_addr
     }
 }

+ 17 - 431
multiboot2/src/lib.rs

@@ -1,12 +1,20 @@
 #![no_std]
 #![cfg_attr(feature = "unstable", feature(error_in_core))]
-#![deny(missing_debug_implementations)]
 // --- BEGIN STYLE CHECKS ---
-// These checks are optional in CI for PRs, as discussed in
-// https://github.com/rust-osdev/multiboot2/pull/92
-#![deny(clippy::all)]
+#![deny(
+    clippy::all,
+    clippy::cargo,
+    clippy::nursery,
+    clippy::must_use_candidate,
+    // clippy::restriction,
+    // clippy::pedantic
+)]
+// now allow a few rules which are denied by the above statement
+// --> They are either ridiculous, not necessary, or we can't fix them.
+#![allow(clippy::multiple_crate_versions)]
+#![deny(missing_docs)]
+#![deny(missing_debug_implementations)]
 #![deny(rustdoc::all)]
-#![allow(rustdoc::private_doc_tests)]
 // --- END STYLE CHECKS ---
 
 //! Library that assists parsing the Multiboot2 Information Structure (MBI) from
@@ -49,6 +57,7 @@ extern crate bitflags;
 #[cfg(feature = "builder")]
 pub mod builder;
 
+mod boot_information;
 mod boot_loader_name;
 mod command_line;
 mod efi;
@@ -65,6 +74,7 @@ mod tag_trait;
 mod tag_type;
 mod vbe_info;
 
+pub use boot_information::{BootInformation, BootInformationHeader, MbiLoadError};
 pub use boot_loader_name::BootLoaderNameTag;
 pub use command_line::CommandLineTag;
 pub use efi::{
@@ -92,437 +102,11 @@ pub use vbe_info::{
     VBEMemoryModel, VBEModeAttributes, VBEModeInfo, VBEWindowAttributes,
 };
 
-use core::fmt;
-use core::mem::size_of;
-use derive_more::Display;
-// Must be public so that custom tags can be DSTs.
-#[cfg(feature = "builder")]
-use crate::builder::AsBytes;
-use crate::framebuffer::UnknownFramebufferType;
-use tag::TagIter;
-
 /// Magic number that a Multiboot2-compliant boot loader will use to identify
 /// the handoff. The location depends on the architecture and the targeted
 /// machine state.
 pub const MAGIC: u32 = 0x36d76289;
 
-/// Error type that describes errors while loading/parsing a multiboot2 information structure
-/// from a given address.
-#[derive(Display, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum MbiLoadError {
-    /// The address is invalid. Make sure that the address is 8-byte aligned,
-    /// according to the spec.
-    #[display("The address is invalid")]
-    IllegalAddress,
-    /// The total size of the multiboot2 information structure must be not zero
-    /// and a multiple of 8.
-    #[display("The size of the MBI is unexpected")]
-    IllegalTotalSize(u32),
-    /// Missing end tag. Each multiboot2 boot information requires to have an
-    /// end tag.
-    #[display("There is no end tag")]
-    NoEndTag,
-}
-
-#[cfg(feature = "unstable")]
-impl core::error::Error for MbiLoadError {}
-
-/// The basic header of a boot information.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[repr(C)]
-pub struct BootInformationHeader {
-    // size is multiple of 8
-    pub total_size: u32,
-    _reserved: u32,
-    // Followed by the boot information tags.
-}
-
-#[cfg(feature = "builder")]
-impl BootInformationHeader {
-    fn new(total_size: u32) -> Self {
-        Self {
-            total_size,
-            _reserved: 0,
-        }
-    }
-}
-
-#[cfg(feature = "builder")]
-impl AsBytes for BootInformationHeader {}
-
-/// This type holds the whole data of the MBI. This helps to better satisfy miri
-/// when it checks for memory issues.
-#[derive(ptr_meta::Pointee)]
-#[repr(C)]
-struct BootInformationInner {
-    header: BootInformationHeader,
-    tags: [u8],
-}
-
-impl BootInformationInner {
-    /// Checks if the MBI has a valid end tag by checking the end of the mbi's
-    /// bytes.
-    fn has_valid_end_tag(&self) -> bool {
-        let end_tag_prototype = EndTag::default();
-
-        let self_ptr = unsafe { self.tags.as_ptr().sub(size_of::<BootInformationHeader>()) };
-
-        let end_tag_ptr = unsafe {
-            self_ptr
-                .add(self.header.total_size as usize)
-                .sub(size_of::<EndTag>())
-        };
-        let end_tag = unsafe { &*(end_tag_ptr as *const EndTag) };
-
-        end_tag.typ == end_tag_prototype.typ && end_tag.size == end_tag_prototype.size
-    }
-}
-
-/// A Multiboot 2 Boot Information (MBI) accessor.
-#[repr(transparent)]
-pub struct BootInformation<'a>(&'a BootInformationInner);
-
-impl<'a> BootInformation<'a> {
-    /// Loads the [`BootInformation`] from a pointer. The pointer must be valid
-    /// and aligned to an 8-byte boundary, as defined by the spec.
-    ///
-    /// ## Example
-    ///
-    /// ```rust
-    /// use multiboot2::{BootInformation, BootInformationHeader};
-    ///
-    /// fn kernel_entry(mb_magic: u32, mbi_ptr: u32) {
-    ///     if mb_magic == multiboot2::MAGIC {
-    ///         let boot_info = unsafe { BootInformation::load(mbi_ptr as *const BootInformationHeader).unwrap() };
-    ///         let _cmd = boot_info.command_line_tag();
-    ///     } else { /* Panic or use multiboot1 flow. */ }
-    /// }
-    /// ```
-    ///
-    /// ## Safety
-    /// * `ptr` must be valid for reading. Otherwise this function might cause
-    ///   invalid machine state or crash your binary (kernel). This can be the
-    ///   case in environments with standard environment (segfault), but also in
-    ///   boot environments, such as UEFI.
-    /// * The memory at `ptr` must not be modified after calling `load` or the
-    ///   program may observe unsynchronized mutation.
-    pub unsafe fn load(ptr: *const BootInformationHeader) -> Result<Self, MbiLoadError> {
-        // null or not aligned
-        if ptr.is_null() || ptr.align_offset(8) != 0 {
-            return Err(MbiLoadError::IllegalAddress);
-        }
-
-        // mbi: reference to basic header
-        let mbi = &*ptr;
-
-        // Check if total size is not 0 and a multiple of 8.
-        if mbi.total_size == 0 || mbi.total_size & 0b111 != 0 {
-            return Err(MbiLoadError::IllegalTotalSize(mbi.total_size));
-        }
-
-        let slice_size = mbi.total_size as usize - size_of::<BootInformationHeader>();
-        // mbi: reference to full mbi
-        let mbi = ptr_meta::from_raw_parts::<BootInformationInner>(ptr.cast(), slice_size);
-        let mbi = &*mbi;
-
-        if !mbi.has_valid_end_tag() {
-            return Err(MbiLoadError::NoEndTag);
-        }
-
-        Ok(Self(mbi))
-    }
-
-    /// Get the start address of the boot info.
-    pub fn start_address(&self) -> usize {
-        self.as_ptr() as usize
-    }
-
-    /// Get the start address of the boot info as pointer.
-    pub fn as_ptr(&self) -> *const () {
-        core::ptr::addr_of!(*self.0).cast()
-    }
-
-    /// Get the end address of the boot info.
-    ///
-    /// This is the same as doing:
-    ///
-    /// ```rust,no_run
-    /// # use multiboot2::{BootInformation, BootInformationHeader};
-    /// # let ptr = 0xdeadbeef as *const BootInformationHeader;
-    /// # let boot_info = unsafe { BootInformation::load(ptr).unwrap() };
-    /// let end_addr = boot_info.start_address() + boot_info.total_size();
-    /// ```
-    pub fn end_address(&self) -> usize {
-        self.start_address() + self.total_size()
-    }
-
-    /// Get the total size of the boot info struct.
-    pub fn total_size(&self) -> usize {
-        self.0.header.total_size as usize
-    }
-
-    // ######################################################
-    // ### BEGIN OF TAG GETTERS (in alphabetical order)
-
-    /*fn apm(&self) {
-        // also add to debug output
-        todo!()
-    }*/
-
-    /// Search for the basic memory info tag.
-    pub fn basic_memory_info_tag(&self) -> Option<&BasicMemoryInfoTag> {
-        self.get_tag::<BasicMemoryInfoTag>()
-    }
-
-    /// Search for the BootLoader name tag.
-    pub fn boot_loader_name_tag(&self) -> Option<&BootLoaderNameTag> {
-        self.get_tag::<BootLoaderNameTag>()
-    }
-
-    /*fn bootdev(&self) {
-        // also add to debug output
-        todo!()
-    }*/
-
-    /// Search for the Command line tag.
-    pub fn command_line_tag(&self) -> Option<&CommandLineTag> {
-        self.get_tag::<CommandLineTag>()
-    }
-
-    /// Search for the EFI boot services not exited tag.
-    pub fn efi_bs_not_exited_tag(&self) -> Option<&EFIBootServicesNotExitedTag> {
-        self.get_tag::<EFIBootServicesNotExitedTag>()
-    }
-
-    /// Search for the EFI Memory map tag, if the boot services were exited.
-    /// Otherwise, if the [`TagType::EfiBs`] tag is present, this returns `None`
-    /// as it is strictly recommended to get the memory map from the `uefi`
-    /// services.
-    pub fn efi_memory_map_tag(&self) -> Option<&EFIMemoryMapTag> {
-        // If the EFIBootServicesNotExited is present, then we should not use
-        // the memory map, as it could still be in use.
-        match self.get_tag::<EFIBootServicesNotExitedTag>() {
-            Some(_tag) => {
-                log::debug!("The EFI memory map is present but the UEFI Boot Services Not Existed Tag is present. Returning None.");
-                None
-            }
-            None => self.get_tag::<EFIMemoryMapTag>(),
-        }
-    }
-
-    /// Search for the EFI 32-bit SDT tag.
-    pub fn efi_sdt32_tag(&self) -> Option<&EFISdt32Tag> {
-        self.get_tag::<EFISdt32Tag>()
-    }
-
-    /// Search for the EFI 64-bit SDT tag.
-    pub fn efi_sdt64_tag(&self) -> Option<&EFISdt64Tag> {
-        self.get_tag::<EFISdt64Tag>()
-    }
-
-    /// Search for the EFI 32-bit image handle pointer tag.
-    pub fn efi_ih32_tag(&self) -> Option<&EFIImageHandle32Tag> {
-        self.get_tag::<EFIImageHandle32Tag>()
-    }
-
-    /// Search for the EFI 64-bit image handle pointer tag.
-    pub fn efi_ih64_tag(&self) -> Option<&EFIImageHandle64Tag> {
-        self.get_tag::<EFIImageHandle64Tag>()
-    }
-
-    /// Returns an [`ElfSectionIter`] iterator over the ELF Sections, if the
-    /// [`ElfSectionsTag`] is present.
-    ///
-    /// # Examples
-    ///
-    /// ```rust,no_run
-    /// # use multiboot2::{BootInformation, BootInformationHeader};
-    /// # let ptr = 0xdeadbeef as *const BootInformationHeader;
-    /// # let boot_info = unsafe { BootInformation::load(ptr).unwrap() };
-    /// if let Some(sections) = boot_info.elf_sections() {
-    ///     let mut total = 0;
-    ///     for section in sections {
-    ///         println!("Section: {:?}", section);
-    ///         total += 1;
-    ///     }
-    /// }
-    /// ```
-    pub fn elf_sections(&self) -> Option<ElfSectionIter> {
-        let tag = self.get_tag::<ElfSectionsTag>();
-        tag.map(|t| {
-            assert!((t.entry_size * t.shndx) <= t.size);
-            t.sections()
-        })
-    }
-
-    /// Search for the VBE framebuffer tag. The result is `Some(Err(e))`, if the
-    /// framebuffer type is unknown, while the framebuffer tag is present.
-    pub fn framebuffer_tag(&self) -> Option<Result<&FramebufferTag, UnknownFramebufferType>> {
-        self.get_tag::<FramebufferTag>()
-            .map(|tag| match tag.buffer_type() {
-                Ok(_) => Ok(tag),
-                Err(e) => Err(e),
-            })
-    }
-
-    /// Search for the Image Load Base Physical Address tag.
-    pub fn load_base_addr_tag(&self) -> Option<&ImageLoadPhysAddrTag> {
-        self.get_tag::<ImageLoadPhysAddrTag>()
-    }
-
-    /// Search for the Memory map tag.
-    pub fn memory_map_tag(&self) -> Option<&MemoryMapTag> {
-        self.get_tag::<MemoryMapTag>()
-    }
-
-    /// Get an iterator of all module tags.
-    pub fn module_tags(&self) -> ModuleIter {
-        module::module_iter(self.tags())
-    }
-
-    /*fn network_tag(&self) {
-        // also add to debug output
-        todo!()
-    }*/
-
-    /// Search for the (ACPI 1.0) RSDP tag.
-    pub fn rsdp_v1_tag(&self) -> Option<&RsdpV1Tag> {
-        self.get_tag::<RsdpV1Tag>()
-    }
-
-    /// Search for the (ACPI 2.0 or later) RSDP tag.
-    pub fn rsdp_v2_tag(&self) -> Option<&RsdpV2Tag> {
-        self.get_tag::<RsdpV2Tag>()
-    }
-
-    /// Search for the SMBIOS tag.
-    pub fn smbios_tag(&self) -> Option<&SmbiosTag> {
-        self.get_tag::<SmbiosTag>()
-    }
-
-    /// Search for the VBE information tag.
-    pub fn vbe_info_tag(&self) -> Option<&VBEInfoTag> {
-        self.get_tag::<VBEInfoTag>()
-    }
-
-    // ### END OF TAG GETTERS
-    // ######################################################
-
-    /// Public getter to find any Multiboot tag by its type, including
-    /// specified and custom ones.
-    ///
-    /// # Specified or Custom Tags
-    /// The Multiboot2 specification specifies a list of tags, see [`TagType`].
-    /// However, it doesn't forbid to use custom tags. Because of this, there
-    /// exists the [`TagType`] abstraction. It is recommended to use this
-    /// getter only for custom tags. For specified tags, use getters, such as
-    /// [`Self::efi_ih64_tag`].
-    ///
-    /// ## Use Custom Tags
-    /// The following example shows how you may use this interface to parse
-    /// custom tags from the MBI. If they are dynamically sized (DST), a few more
-    /// special handling is required. This is reflected by code-comments.
-    ///
-    /// ```no_run
-    /// use std::str::Utf8Error;
-    /// use multiboot2::{BootInformation, BootInformationHeader, Tag, TagTrait, TagType, TagTypeId};
-    ///
-    /// #[repr(C)]
-    /// #[derive(multiboot2::Pointee)] // Only needed for DSTs.
-    /// struct CustomTag {
-    ///     tag: TagTypeId,
-    ///     size: u32,
-    ///     // begin of inline string
-    ///     name: [u8],
-    /// }
-    ///
-    /// // This implementation is only necessary for tags that are DSTs.
-    /// impl TagTrait for CustomTag {
-    ///     const ID: TagType = TagType::Custom(0x1337);
-    ///
-    ///     fn dst_size(base_tag: &Tag) -> usize {
-    ///         // The size of the sized portion of the custom tag.
-    ///         let tag_base_size = 8; // id + size is 8 byte in size
-    ///         assert!(base_tag.size >= 8);
-    ///         base_tag.size as usize - tag_base_size
-    ///     }
-    /// }
-    ///
-    /// impl CustomTag {
-    ///     fn name(&self) -> Result<&str, Utf8Error> {
-    ///         Tag::parse_slice_as_string(&self.name)
-    ///     }
-    /// }
-    /// let mbi_ptr = 0xdeadbeef as *const BootInformationHeader;
-    /// let mbi = unsafe { BootInformation::load(mbi_ptr).unwrap() };
-    ///
-    /// let tag = mbi
-    ///     .get_tag::<CustomTag>()
-    ///     .unwrap();
-    /// assert_eq!(tag.name(), Ok("name"));
-    /// ```
-    pub fn get_tag<TagT: TagTrait + ?Sized + 'a>(&'a self) -> Option<&'a TagT> {
-        self.tags()
-            .find(|tag| tag.typ == TagT::ID)
-            .map(|tag| tag.cast_tag::<TagT>())
-    }
-
-    /// Returns an iterator over all tags.
-    fn tags(&self) -> TagIter {
-        TagIter::new(&self.0.tags)
-    }
-}
-
-impl fmt::Debug for BootInformation<'_> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        /// Limit how many Elf-Sections should be debug-formatted.
-        /// Can be thousands of sections for a Rust binary => this is useless output.
-        /// If the user really wants this, they should debug-format the field directly.
-        const ELF_SECTIONS_LIMIT: usize = 7;
-
-        let mut debug = f.debug_struct("Multiboot2BootInformation");
-        debug
-            .field("start_address", &self.start_address())
-            .field("end_address", &self.end_address())
-            .field("total_size", &self.total_size())
-            // now tags in alphabetical order
-            .field("basic_memory_info", &(self.basic_memory_info_tag()))
-            .field("boot_loader_name", &self.boot_loader_name_tag())
-            // .field("bootdev", &self.bootdev_tag())
-            .field("command_line", &self.command_line_tag())
-            .field("efi_bs_not_exited", &self.efi_bs_not_exited_tag())
-            .field("efi_memory_map", &self.efi_memory_map_tag())
-            .field("efi_sdt32", &self.efi_sdt32_tag())
-            .field("efi_sdt64", &self.efi_sdt64_tag())
-            .field("efi_ih32", &self.efi_ih32_tag())
-            .field("efi_ih64", &self.efi_ih64_tag());
-
-        // usually this is REALLY big (thousands of tags) => skip it here
-        {
-            let elf_sections_tag_entries_count =
-                self.elf_sections().map(|x| x.count()).unwrap_or(0);
-
-            if elf_sections_tag_entries_count > ELF_SECTIONS_LIMIT {
-                debug.field("elf_sections (count)", &elf_sections_tag_entries_count);
-            } else {
-                debug.field("elf_sections", &self.elf_sections().unwrap_or_default());
-            }
-        }
-
-        debug
-            .field("framebuffer", &self.framebuffer_tag())
-            .field("load_base_addr", &self.load_base_addr_tag())
-            .field("memory_map", &self.memory_map_tag())
-            .field("modules", &self.module_tags())
-            // .field("network", &self.network_tag())
-            .field("rsdp_v1", &self.rsdp_v1_tag())
-            .field("rsdp_v2", &self.rsdp_v2_tag())
-            .field("smbios_tag", &self.smbios_tag())
-            .field("vbe_info_tag", &self.vbe_info_tag())
-            .finish()
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -796,6 +380,7 @@ mod tests {
 
     #[test]
     #[cfg_attr(miri, ignore)]
+    #[allow(clippy::cognitive_complexity)]
     fn vbe_info_tag() {
         //Taken from GRUB2 running in QEMU.
         #[repr(C, align(8))]
@@ -1237,6 +822,7 @@ mod tests {
     }
 
     /// Helper for [`grub2`].
+    #[allow(clippy::cognitive_complexity)]
     fn test_grub2_boot_info(
         bi: &BootInformation,
         addr: usize,

+ 36 - 21
multiboot2/src/memory_map.rs

@@ -5,6 +5,7 @@ pub use uefi_raw::table::boot::MemoryAttribute as EFIMemoryAttribute;
 pub use uefi_raw::table::boot::MemoryDescriptor as EFIMemoryDesc;
 pub use uefi_raw::table::boot::MemoryType as EFIMemoryAreaType;
 
+use crate::tag::TagHeader;
 use crate::{Tag, TagTrait, TagType, TagTypeId};
 use core::fmt::{Debug, Formatter};
 use core::marker::PhantomData;
@@ -27,15 +28,16 @@ const METADATA_SIZE: usize = mem::size_of::<TagTypeId>() + 3 * mem::size_of::<u3
 #[derive(ptr_meta::Pointee, Debug, PartialEq, Eq)]
 #[repr(C)]
 pub struct MemoryMapTag {
-    typ: TagTypeId,
-    size: u32,
+    header: TagHeader,
     entry_size: u32,
     entry_version: u32,
     areas: [MemoryArea],
 }
 
 impl MemoryMapTag {
+    /// Constructs a new tag.
     #[cfg(feature = "builder")]
+    #[must_use]
     pub fn new(areas: &[MemoryArea]) -> BoxedDst<Self> {
         let entry_size: u32 = mem::size_of::<MemoryArea>().try_into().unwrap();
         let entry_version: u32 = 0;
@@ -47,12 +49,14 @@ impl MemoryMapTag {
     }
 
     /// Returns the entry size.
-    pub fn entry_size(&self) -> u32 {
+    #[must_use]
+    pub const fn entry_size(&self) -> u32 {
         self.entry_size
     }
 
     /// Returns the entry version.
-    pub fn entry_version(&self) -> u32 {
+    #[must_use]
+    pub const fn entry_version(&self) -> u32 {
         self.entry_version
     }
 
@@ -60,6 +64,7 @@ impl MemoryMapTag {
     ///
     /// Usually, this should already reflect the memory consumed by the
     /// code running this.
+    #[must_use]
     pub fn memory_areas(&self) -> &[MemoryArea] {
         // If this ever fails, we need to model this differently in this crate.
         assert_eq!(self.entry_size as usize, mem::size_of::<MemoryArea>());
@@ -100,22 +105,26 @@ impl MemoryArea {
     }
 
     /// The start address of the memory region.
-    pub fn start_address(&self) -> u64 {
+    #[must_use]
+    pub const fn start_address(&self) -> u64 {
         self.base_addr
     }
 
     /// The end address of the memory region.
-    pub fn end_address(&self) -> u64 {
+    #[must_use]
+    pub const fn end_address(&self) -> u64 {
         self.base_addr + self.length
     }
 
     /// The size, in bytes, of the memory region.
-    pub fn size(&self) -> u64 {
+    #[must_use]
+    pub const fn size(&self) -> u64 {
         self.length
     }
 
     /// The type of the memory region.
-    pub fn typ(&self) -> MemoryAreaTypeId {
+    #[must_use]
+    pub const fn typ(&self) -> MemoryAreaTypeId {
         self.typ
     }
 }
@@ -215,7 +224,7 @@ impl From<MemoryAreaType> for MemoryAreaTypeId {
 
 impl PartialEq<MemoryAreaType> for MemoryAreaTypeId {
     fn eq(&self, other: &MemoryAreaType) -> bool {
-        let val: MemoryAreaTypeId = (*other).into();
+        let val: Self = (*other).into();
         let val: u32 = val.0;
         self.0.eq(&val)
     }
@@ -246,27 +255,31 @@ impl PartialEq<MemoryAreaTypeId> for MemoryAreaType {
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct BasicMemoryInfoTag {
-    typ: TagTypeId,
-    size: u32,
+    header: TagHeader,
     memory_lower: u32,
     memory_upper: u32,
 }
 
 impl BasicMemoryInfoTag {
+    /// Constructs a new tag.
+    #[must_use]
     pub fn new(memory_lower: u32, memory_upper: u32) -> Self {
         Self {
-            typ: Self::ID.into(),
-            size: mem::size_of::<BasicMemoryInfoTag>().try_into().unwrap(),
+            header: TagHeader::new(Self::ID, mem::size_of::<Self>().try_into().unwrap()),
             memory_lower,
             memory_upper,
         }
     }
 
-    pub fn memory_lower(&self) -> u32 {
+    #[must_use]
+    /// Returns the lower memory bound.
+    pub const fn memory_lower(&self) -> u32 {
         self.memory_lower
     }
 
-    pub fn memory_upper(&self) -> u32 {
+    #[must_use]
+    /// Returns the upper memory bound.
+    pub const fn memory_upper(&self) -> u32 {
         self.memory_upper
     }
 }
@@ -287,8 +300,7 @@ impl AsBytes for EFIMemoryDesc {}
 #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct EFIMemoryMapTag {
-    typ: TagTypeId,
-    size: u32,
+    header: TagHeader,
     /// Most likely a little more than the size of a [`EFIMemoryDesc`].
     /// This is always the reference, and `size_of` never.
     /// See <https://github.com/tianocore/edk2/blob/7142e648416ff5d3eac6c6d607874805f5de0ca8/MdeModulePkg/Core/PiSmmCore/Page.c#L1059>.
@@ -309,10 +321,11 @@ pub struct EFIMemoryMapTag {
 }
 
 impl EFIMemoryMapTag {
-    #[cfg(feature = "builder")]
     /// Create a new EFI memory map tag with the given memory descriptors.
     /// Version and size can't be set because you're passing a slice of
     /// EFIMemoryDescs, not the ones you might have gotten from the firmware.
+    #[cfg(feature = "builder")]
+    #[must_use]
     pub fn new_from_descs(descs: &[EFIMemoryDesc]) -> BoxedDst<Self> {
         // TODO replace this EfiMemorydesc::uefi_desc_size() in the next uefi_raw
         // release.
@@ -338,8 +351,9 @@ impl EFIMemoryMapTag {
         )
     }
 
-    #[cfg(feature = "builder")]
     /// Create a new EFI memory map tag from the given EFI memory map.
+    #[cfg(feature = "builder")]
+    #[must_use]
     pub fn new_from_map(desc_size: u32, desc_version: u32, efi_mmap: &[u8]) -> BoxedDst<Self> {
         assert!(desc_size > 0);
         assert_eq!(efi_mmap.len() % desc_size as usize, 0);
@@ -362,6 +376,7 @@ impl EFIMemoryMapTag {
     ///
     /// Usually, this should already reflect the memory consumed by the
     /// code running this.
+    #[must_use]
     pub fn memory_areas(&self) -> EFIMemoryAreaIter {
         // If this ever fails, this needs to be refactored in a joint-effort
         // with the uefi-rs project to have all corresponding typings.
@@ -380,8 +395,8 @@ impl EFIMemoryMapTag {
 impl Debug for EFIMemoryMapTag {
     fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
         f.debug_struct("EFIMemoryMapTag")
-            .field("typ", &self.typ)
-            .field("size", &self.size)
+            .field("typ", &self.header.typ)
+            .field("size", &self.header.size)
             .field("desc_size", &self.desc_size)
             .field("buf", &self.memory_map.as_ptr())
             .field("buf_len", &self.memory_map.len())

+ 17 - 13
multiboot2/src/module.rs

@@ -1,13 +1,13 @@
 //! Module for [`ModuleTag`].
 
-use crate::tag::StringError;
-use crate::{Tag, TagIter, TagTrait, TagType, TagTypeId};
+use crate::tag::{StringError, TagHeader, TagIter};
+use crate::{Tag, TagTrait, TagType, TagTypeId};
 use core::fmt::{Debug, Formatter};
-use core::mem::size_of;
+use core::mem;
 #[cfg(feature = "builder")]
 use {crate::builder::BoxedDst, alloc::vec::Vec};
 
-const METADATA_SIZE: usize = size_of::<TagTypeId>() + 3 * size_of::<u32>();
+const METADATA_SIZE: usize = mem::size_of::<TagTypeId>() + 3 * mem::size_of::<u32>();
 
 /// The module tag can occur multiple times and specifies passed boot modules
 /// (blobs in memory). The tag itself doesn't include the blog, but references
@@ -15,8 +15,7 @@ const METADATA_SIZE: usize = size_of::<TagTypeId>() + 3 * size_of::<u32>();
 #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct ModuleTag {
-    typ: TagTypeId,
-    size: u32,
+    header: TagHeader,
     mod_start: u32,
     mod_end: u32,
     /// Null-terminated UTF-8 string
@@ -24,7 +23,9 @@ pub struct ModuleTag {
 }
 
 impl ModuleTag {
+    /// Constructs a new tag.
     #[cfg(feature = "builder")]
+    #[must_use]
     pub fn new(start: u32, end: u32, cmdline: &str) -> BoxedDst<Self> {
         assert!(end > start, "must have a size");
 
@@ -54,17 +55,20 @@ impl ModuleTag {
     }
 
     /// Start address of the module.
-    pub fn start_address(&self) -> u32 {
+    #[must_use]
+    pub const fn start_address(&self) -> u32 {
         self.mod_start
     }
 
     /// End address of the module
-    pub fn end_address(&self) -> u32 {
+    #[must_use]
+    pub const fn end_address(&self) -> u32 {
         self.mod_end
     }
 
     /// The size of the module/the BLOB in memory.
-    pub fn module_size(&self) -> u32 {
+    #[must_use]
+    pub const fn module_size(&self) -> u32 {
         self.mod_end - self.mod_start
     }
 }
@@ -81,8 +85,8 @@ impl TagTrait for ModuleTag {
 impl Debug for ModuleTag {
     fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
         f.debug_struct("ModuleTag")
-            .field("type", &{ self.typ })
-            .field("size", &{ self.size })
+            .field("type", &self.header.typ)
+            .field("size", &self.header.size)
             // Trick to print as hex.
             .field("mod_start", &self.mod_start)
             .field("mod_end", &self.mod_end)
@@ -92,7 +96,7 @@ impl Debug for ModuleTag {
     }
 }
 
-pub fn module_iter(iter: TagIter) -> ModuleIter {
+pub const fn module_iter(iter: TagIter) -> ModuleIter {
     ModuleIter { iter }
 }
 
@@ -155,7 +159,7 @@ mod tests {
         let tag = get_bytes();
         let tag = unsafe { &*tag.as_ptr().cast::<Tag>() };
         let tag = tag.cast_tag::<ModuleTag>();
-        assert_eq!({ tag.typ }, TagType::Module);
+        assert_eq!(tag.header.typ, TagType::Module);
         assert_eq!(tag.cmdline().expect("must be valid UTF-8"), MSG);
     }
 

+ 26 - 18
multiboot2/src/rsdp.rs

@@ -12,7 +12,8 @@
 //! signature should be manually verified.
 //!
 
-use crate::{Tag, TagTrait, TagType, TagTypeId};
+use crate::tag::TagHeader;
+use crate::{Tag, TagTrait, TagType};
 #[cfg(feature = "builder")]
 use core::mem::size_of;
 use core::slice;
@@ -25,8 +26,7 @@ const RSDPV1_LENGTH: usize = 20;
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct RsdpV1Tag {
-    typ: TagTypeId,
-    size: u32,
+    header: TagHeader,
     signature: [u8; 8],
     checksum: u8,
     oem_id: [u8; 6],
@@ -35,7 +35,9 @@ pub struct RsdpV1Tag {
 }
 
 impl RsdpV1Tag {
+    /// Constructs a new tag.
     #[cfg(feature = "builder")]
+    #[must_use]
     pub fn new(
         signature: [u8; 8],
         checksum: u8,
@@ -44,8 +46,7 @@ impl RsdpV1Tag {
         rsdt_address: u32,
     ) -> Self {
         Self {
-            typ: Self::ID.into(),
-            size: size_of::<Self>().try_into().unwrap(),
+            header: TagHeader::new(Self::ID, size_of::<Self>().try_into().unwrap()),
             signature,
             checksum,
             oem_id,
@@ -57,11 +58,12 @@ impl RsdpV1Tag {
     /// The "RSD PTR " marker signature.
     ///
     /// This is originally a 8-byte C string (not null terminated!) that must contain "RSD PTR "
-    pub fn signature(&self) -> Result<&str, Utf8Error> {
+    pub const fn signature(&self) -> Result<&str, Utf8Error> {
         str::from_utf8(&self.signature)
     }
 
     /// Validation of the RSDPv1 checksum
+    #[must_use]
     pub fn checksum_is_valid(&self) -> bool {
         let bytes =
             unsafe { slice::from_raw_parts(self as *const _ as *const u8, RSDPV1_LENGTH + 8) };
@@ -72,17 +74,19 @@ impl RsdpV1Tag {
     }
 
     /// An OEM-supplied string that identifies the OEM.
-    pub fn oem_id(&self) -> Result<&str, Utf8Error> {
+    pub const fn oem_id(&self) -> Result<&str, Utf8Error> {
         str::from_utf8(&self.oem_id)
     }
 
     /// The revision of the ACPI.
-    pub fn revision(&self) -> u8 {
+    #[must_use]
+    pub const fn revision(&self) -> u8 {
         self.revision
     }
 
     /// The physical (I repeat: physical) address of the RSDT table.
-    pub fn rsdt_address(&self) -> usize {
+    #[must_use]
+    pub const fn rsdt_address(&self) -> usize {
         self.rsdt_address as usize
     }
 }
@@ -97,8 +101,7 @@ impl TagTrait for RsdpV1Tag {
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct RsdpV2Tag {
-    typ: TagTypeId,
-    size: u32,
+    header: TagHeader,
     signature: [u8; 8],
     checksum: u8,
     oem_id: [u8; 6],
@@ -112,8 +115,10 @@ pub struct RsdpV2Tag {
 }
 
 impl RsdpV2Tag {
+    /// Constructs a new tag.
     #[cfg(feature = "builder")]
     #[allow(clippy::too_many_arguments)]
+    #[must_use]
     pub fn new(
         signature: [u8; 8],
         checksum: u8,
@@ -125,8 +130,7 @@ impl RsdpV2Tag {
         ext_checksum: u8,
     ) -> Self {
         Self {
-            typ: Self::ID.into(),
-            size: size_of::<Self>().try_into().unwrap(),
+            header: TagHeader::new(Self::ID, size_of::<Self>().try_into().unwrap()),
             signature,
             checksum,
             oem_id,
@@ -142,11 +146,12 @@ impl RsdpV2Tag {
     /// The "RSD PTR " marker signature.
     ///
     /// This is originally a 8-byte C string (not null terminated!) that must contain "RSD PTR ".
-    pub fn signature(&self) -> Result<&str, Utf8Error> {
+    pub const fn signature(&self) -> Result<&str, Utf8Error> {
         str::from_utf8(&self.signature)
     }
 
     /// Validation of the RSDPv2 extended checksum
+    #[must_use]
     pub fn checksum_is_valid(&self) -> bool {
         let bytes = unsafe {
             slice::from_raw_parts(self as *const _ as *const u8, self.length as usize + 8)
@@ -158,24 +163,27 @@ impl RsdpV2Tag {
     }
 
     /// An OEM-supplied string that identifies the OEM.
-    pub fn oem_id(&self) -> Result<&str, Utf8Error> {
+    pub const fn oem_id(&self) -> Result<&str, Utf8Error> {
         str::from_utf8(&self.oem_id)
     }
 
     /// The revision of the ACPI.
-    pub fn revision(&self) -> u8 {
+    #[must_use]
+    pub const fn revision(&self) -> u8 {
         self.revision
     }
 
     /// Physical address of the XSDT table.
     ///
     /// On x86, this is truncated from 64-bit to 32-bit.
-    pub fn xsdt_address(&self) -> usize {
+    #[must_use]
+    pub const fn xsdt_address(&self) -> usize {
         self.xsdt_address as usize
     }
 
     /// This field is used to calculate the checksum of the entire table, including both checksum fields.
-    pub fn ext_checksum(&self) -> u8 {
+    #[must_use]
+    pub const fn ext_checksum(&self) -> u8 {
         self.ext_checksum
     }
 }

+ 33 - 13
multiboot2/src/smbios.rs

@@ -2,32 +2,52 @@
 
 #[cfg(feature = "builder")]
 use crate::builder::BoxedDst;
+use crate::tag::TagHeader;
 use crate::{Tag, TagTrait, TagType, TagTypeId};
 use core::fmt::Debug;
+use core::mem;
 
-const METADATA_SIZE: usize = core::mem::size_of::<TagTypeId>()
-    + core::mem::size_of::<u32>()
-    + core::mem::size_of::<u8>() * 8;
+const METADATA_SIZE: usize =
+    mem::size_of::<TagTypeId>() + mem::size_of::<u32>() + mem::size_of::<u8>() * 8;
 
 /// This tag contains a copy of SMBIOS tables as well as their version.
 #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[repr(C)]
 pub struct SmbiosTag {
-    typ: TagTypeId,
-    size: u32,
-    pub major: u8,
-    pub minor: u8,
+    header: TagHeader,
+    major: u8,
+    minor: u8,
     _reserved: [u8; 6],
-    pub tables: [u8],
+    tables: [u8],
 }
 
 impl SmbiosTag {
+    /// Constructs a new tag.
     #[cfg(feature = "builder")]
+    #[must_use]
     pub fn new(major: u8, minor: u8, tables: &[u8]) -> BoxedDst<Self> {
         let mut bytes = [major, minor, 0, 0, 0, 0, 0, 0].to_vec();
         bytes.extend(tables);
         BoxedDst::new(&bytes)
     }
+
+    /// Returns the major number.
+    #[must_use]
+    pub const fn major(&self) -> u8 {
+        self.major
+    }
+
+    /// Returns the major number.
+    #[must_use]
+    pub const fn minor(&self) -> u8 {
+        self.minor
+    }
+
+    /// Returns the raw tables.
+    #[must_use]
+    pub const fn tables(&self) -> &[u8] {
+        &self.tables
+    }
 }
 
 impl TagTrait for SmbiosTag {
@@ -42,10 +62,10 @@ impl TagTrait for SmbiosTag {
 impl Debug for SmbiosTag {
     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
         f.debug_struct("BootLoaderNameTag")
-            .field("typ", &{ self.typ })
-            .field("size", &{ self.size })
-            .field("major", &{ self.major })
-            .field("minor", &{ self.minor })
+            .field("typ", &self.header.typ)
+            .field("size", &self.header.size)
+            .field("major", &self.major)
+            .field("minor", &self.minor)
             .finish()
     }
 }
@@ -76,7 +96,7 @@ mod tests {
         let tag = get_bytes();
         let tag = unsafe { &*tag.as_ptr().cast::<Tag>() };
         let tag = tag.cast_tag::<SmbiosTag>();
-        assert_eq!({ tag.typ }, TagType::Smbios);
+        assert_eq!(tag.header.typ, TagType::Smbios);
         assert_eq!(tag.major, 3);
         assert_eq!(tag.minor, 0);
         assert_eq!(tag.tables, [0xabu8; 24]);

+ 29 - 2
multiboot2/src/tag.rs

@@ -28,8 +28,32 @@ impl Display for StringError {
 impl core::error::Error for StringError {
     fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
         match self {
-            StringError::MissingNul(e) => Some(e),
-            StringError::Utf8(e) => Some(e),
+            Self::MissingNul(e) => Some(e),
+            Self::Utf8(e) => Some(e),
+        }
+    }
+}
+
+/// The common header that all tags have in common. This type is ABI compatible.
+///
+/// Not to be confused with Multiboot header tags, which are something
+/// different.
+#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+#[repr(C)]
+pub struct TagHeader {
+    /// The ABI-compatible [`TagType`].
+    pub typ: TagTypeId, /* u32 */
+    /// The total size of the tag including the header.
+    pub size: u32,
+    // Followed by optional additional tag specific fields.
+}
+
+impl TagHeader {
+    /// Creates a new header.
+    pub fn new(typ: impl Into<TagTypeId>, size: u32) -> Self {
+        Self {
+            typ: typ.into(),
+            size,
         }
     }
 }
@@ -44,6 +68,7 @@ impl core::error::Error for StringError {
 /// different.
 #[derive(Clone, Copy)]
 #[repr(C)]
+#[allow(missing_docs)] // type will be removed soon anyway in its form
 pub struct Tag {
     pub typ: TagTypeId, // u32
     pub size: u32,
@@ -52,11 +77,13 @@ pub struct Tag {
 
 impl Tag {
     /// Returns the underlying type of the tag.
+    #[must_use]
     pub fn typ(&self) -> TagType {
         self.typ.into()
     }
 
     /// Casts the base tag to the specific tag type.
+    #[must_use]
     pub fn cast_tag<'a, T: TagTrait + ?Sized + 'a>(&'a self) -> &'a T {
         assert_eq!(self.typ, T::ID);
         // Safety: At this point, we trust that "self.size" and the size hint

+ 1 - 0
multiboot2/src/tag_trait.rs

@@ -51,6 +51,7 @@ pub trait TagTrait: Pointee {
     /// Callers must be sure that the "size" field of the provided [`Tag`] is
     /// sane and the underlying memory valid. The implementation of this trait
     /// **must have** a correct [`Self::dst_size`] implementation.
+    #[must_use]
     unsafe fn from_base_tag(tag: &Tag) -> &Self {
         let ptr = core::ptr::addr_of!(*tag);
         let ptr = ptr_meta::from_raw_parts(ptr.cast(), Self::dst_size(tag));

+ 28 - 26
multiboot2/src/tag_type.rs

@@ -17,7 +17,8 @@ pub struct TagTypeId(u32);
 
 impl TagTypeId {
     /// Constructor.
-    pub fn new(val: u32) -> Self {
+    #[must_use]
+    pub const fn new(val: u32) -> Self {
         Self(val)
     }
 }
@@ -135,6 +136,7 @@ pub enum TagType {
 
 impl TagType {
     /// Convenient wrapper to get the underlying `u32` representation of the tag.
+    #[must_use]
     pub fn val(&self) -> u32 {
         u32::from(*self)
     }
@@ -161,29 +163,29 @@ mod primitive_conversion_impls {
     impl From<u32> for TagType {
         fn from(value: u32) -> Self {
             match value {
-                0 => TagType::End,
-                1 => TagType::Cmdline,
-                2 => TagType::BootLoaderName,
-                3 => TagType::Module,
-                4 => TagType::BasicMeminfo,
-                5 => TagType::Bootdev,
-                6 => TagType::Mmap,
-                7 => TagType::Vbe,
-                8 => TagType::Framebuffer,
-                9 => TagType::ElfSections,
-                10 => TagType::Apm,
-                11 => TagType::Efi32,
-                12 => TagType::Efi64,
-                13 => TagType::Smbios,
-                14 => TagType::AcpiV1,
-                15 => TagType::AcpiV2,
-                16 => TagType::Network,
-                17 => TagType::EfiMmap,
-                18 => TagType::EfiBs,
-                19 => TagType::Efi32Ih,
-                20 => TagType::Efi64Ih,
-                21 => TagType::LoadBaseAddr,
-                c => TagType::Custom(c),
+                0 => Self::End,
+                1 => Self::Cmdline,
+                2 => Self::BootLoaderName,
+                3 => Self::Module,
+                4 => Self::BasicMeminfo,
+                5 => Self::Bootdev,
+                6 => Self::Mmap,
+                7 => Self::Vbe,
+                8 => Self::Framebuffer,
+                9 => Self::ElfSections,
+                10 => Self::Apm,
+                11 => Self::Efi32,
+                12 => Self::Efi64,
+                13 => Self::Smbios,
+                14 => Self::AcpiV1,
+                15 => Self::AcpiV2,
+                16 => Self::Network,
+                17 => Self::EfiMmap,
+                18 => Self::EfiBs,
+                19 => Self::Efi32Ih,
+                20 => Self::Efi64Ih,
+                21 => Self::LoadBaseAddr,
+                c => Self::Custom(c),
             }
         }
     }
@@ -226,14 +228,14 @@ mod intermediate_conversion_impls {
     impl From<TagTypeId> for TagType {
         fn from(value: TagTypeId) -> Self {
             let value = u32::from(value);
-            TagType::from(value)
+            Self::from(value)
         }
     }
 
     impl From<TagType> for TagTypeId {
         fn from(value: TagType) -> Self {
             let value = u32::from(value);
-            TagTypeId::from(value)
+            Self::from(value)
         }
     }
 }