Crate multiboot2_common

source ·
Expand description

Common helpers for the multiboot2 and multiboot2-header crates.

§Value-add

The main value-add of this crate is to abstract away the parsing and construction of Multiboot2 structures. This is more complex as it may sound at first due to the difficulties listed below.

The abstractions provided by this crate serve as the base to work with the following structures:

  • multiboot2:
    • boot information structure (whole)
    • boot information tags
  • multiboot2-header:
    • header structure (whole)
    • header tags

§The Problem / Difficulties

§Multiboot2 Structures

Multiboot2 structures are a consecutive chunk of bytes in memory. They use the “header pattern”, which means a fixed size and known Header type indicates the total size of the structure. This is roughly translated to the following rusty base type:

#[repr(C, align(8))]
struct DynStructure {
    header: MyHeader,
    payload: [u8]
}

Note that these structures can also be nested. So for example, the Multiboot2 boot information contains Multiboot2 tags, and the Multiboot2 header contains Multiboot2 header tags - both are itself dynamic structures.

A final [u8] field in the structs is the most rusty way to model this. However, this makes the type a Dynamically Sized Type (DST). To create references to these types from a byte slice, one needs fat pointers. They are a language feature currently not constructable with stable Rust. Luckily, we can utilize ptr_meta.

§Dynamic and Sized Structs

Note that we also have structures (tags) in Multiboot2 that looks like this:

#[repr(C, align(8))]
struct DynStructure {
    header: MyHeader,
    // Not just [`u8`]
    payload: [SomeType]
}

or

#[repr(C, align(8))]
struct CommandLineTag {
    header: TagHeader,
    start: u32,
    end: u32,
    // More than just the base header before the dynamic portion
    data: [u8]
}

§Fat Pointer Requirements

To create fat pointers with ptr_meta, each tag needs a Metadata type which is either usize (for DSTs) or (). A trait is needed to abstract above sized or unsized types.

§Multiboot2 Requirements

All tags must be 8-byte aligned. Space between multiple tags may be filled with zeroes if necessary. These zeroes are not reflected in the previous tag’s size.

§Rustc Requirements

The allocation space that Rust requires for types is a multiple of the alignment. This means that if we cast between byte slices and specific types, Rust doesn’t just see the size reported by the header but also any necessary padding bytes. If this is not the case, for example we cast to a structure from a &[u8; 15], Miri will complain as it expects &[u8; 16]

See https://doc.rust-lang.org/reference/type-layout.html for information.

§Provided Abstractions

§Parsing and Casting

First, we need byte slices which are guaranteed to be aligned and are a multiple of the alignment. We have BytesRef for that. With that, we can create a DynSizedStructure. This is a rusty type that owns all the bytes it owns, according to the size reported by its header. Using this type and with the help of MaybeDynSized, we can call DynSizedStructure::cast to cast this to arbitrary sized or unsized struct types fulfilling the corresponding requirements.

This way, one can create nice rusty structs modeling the structure of the tags, and we only need a single “complicated” type, namely DynSizedStructure.

§Iterating Tags

To iterate over the tags of a structure, use TagIter.

§Memory Guarantees and Safety Promises

For the parsing and construction of Multiboot2 structures, the alignment and necessary padding bytes as discussed above are guaranteed. When types are constructed, they return Results with appropriate error types. If during runtime something goes wrong, for example due to malformed tags, panics guarantee that no UB will happen.

§No Public API

Not meant as stable public API for others outside Multiboot2.

Modules§

Structs§

  • Wraps a byte slice representing a Multiboot2 structure including an optional terminating padding, if necessary. It guarantees that the memory requirements promised in the crates description are respected.
  • An C ABI-compatible dynamically sized type with a common sized Header and a dynamic amount of bytes. This structures owns all its bytes, unlike Header. Instances guarantees that the memory requirements promised in the crates description are respected.
  • Iterates over the tags (modelled by DynSizedStructure) of the underlying byte slice. Each tag is expected to have the same common Header.

Enums§

  • Errors that may occur when working with memory.

Constants§

  • The alignment of all Multiboot2 data structures.

Traits§

Functions§

  • Clones a MaybeDynSized by calling new_boxed.
  • Increases the given size to the next alignment boundary, if it is not a multiple of the alignment yet. This is relevant as in Rust’s type layout, the allocated size of a type is always a multiple of the alignment, even if the type is smaller.
  • Creates a new tag implementing MaybeDynSized on the heap. This works for sized and unsized tags. However, it only makes sense to use this for tags that are DSTs (unsized). For regular sized structs, you can just create a typical constructor and box the result.