Skip to content

Commit 50ee5db

Browse files
authored
Merge pull request #95 from rust-osdev/multiboot2-header-crate
multiboot2-header crate - initial release
2 parents 3e20693 + 78de7b3 commit 50ee5db

21 files changed

+1812
-5
lines changed

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ Please check their individual README-files ([multiboot2](multiboot2/README.md),
66

77
The `multiboot2` crate helps to parse the Multiboot2 information structure
88
(MBI) and is relevant in kernels, that get booted by a bootloader such as
9-
GRUB, for example. `multiboot2-header` is relevant, if you want to write a bootloader that provides a MBI to a payload for
10-
example.
9+
GRUB, for example. `multiboot2-header` helps you to either build
10+
Multiboot2-headers yourself, or to parse Multiboot2 headers in custom bootloader
11+
or similar applications.
1112

1213
## License
1314

multiboot2-header/Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description = """
44
Library with type definitions and parsing functions for Multiboot2 headers.
55
This library is `no_std` and can be used in bootloaders.
66
"""
7-
version = "0.0.0"
7+
version = "0.1.0"
88
authors = [
99
"Philipp Schuster <[email protected]>"
1010
]
@@ -28,3 +28,5 @@ repository = "https://github.com/rust-osdev/multiboot2"
2828
documentation = "https://docs.rs/multiboot2-header"
2929

3030
[dependencies]
31+
# used for MBI tags
32+
multiboot2 = "0.12.2"

multiboot2-header/README.md

+49
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,55 @@
66
Rust library with type definitions and parsing functions for Multiboot2 headers.
77
This library is `no_std` and can be used in bootloaders.
88

9+
What this library is good for:
10+
- writing a small binary which writes you a valid Multiboot2 header
11+
into a file (such as `header.bin`)
12+
- understanding Multiboot2 headers better
13+
- analyze Multiboot2 headers at runtime
14+
15+
What this library is not optimal for:
16+
- compiling a Multiboot2 header statically into an object file using only Rust code
17+
18+
## Example 1: Builder + Parse
19+
```rust
20+
use multiboot2_header::builder::Multiboot2HeaderBuilder;
21+
use multiboot2_header::{HeaderTagFlag, HeaderTagISA, InformationRequestHeaderTagBuilder, MbiTagType, RelocatableHeaderTag, RelocatableHeaderTagPreference, Multiboot2Header};
22+
23+
/// Small example that creates a Multiboot2 header and parses it afterwards.
24+
fn main() {
25+
// We create a Multiboot2 header during runtime here. A practical example is that your
26+
// program gets the header from a file and parses it afterwards.
27+
let mb2_hdr_bytes = Multiboot2HeaderBuilder::new(HeaderTagISA::I386)
28+
.relocatable_tag(RelocatableHeaderTag::new(
29+
HeaderTagFlag::Required,
30+
0x1337,
31+
0xdeadbeef,
32+
4096,
33+
RelocatableHeaderTagPreference::None,
34+
))
35+
.information_request_tag(
36+
InformationRequestHeaderTagBuilder::new(HeaderTagFlag::Required)
37+
.add_irs(&[MbiTagType::Cmdline, MbiTagType::BootLoaderName]),
38+
)
39+
.build();
40+
41+
// Cast bytes in vector to Multiboot2 information structure
42+
let mb2_hdr = unsafe { Multiboot2Header::from_addr(mb2_hdr_bytes.as_ptr() as usize) };
43+
println!("{:#?}", mb2_hdr);
44+
}
45+
```
46+
47+
## Example 2: Multiboot2 header as static data in Rust file
48+
You can use the builder, construct a Multiboot2 header, write it to a file and include it like this:
49+
```
50+
#[used]
51+
#[no_mangle]
52+
#[link_section = ".text.multiboot2_header"]
53+
static MULTIBOOT2_HDR: &[u8; 64] = include_bytes!("mb2_hdr_dump.bin");
54+
```
55+
You may need a special linker script to place this in a LOAD segment with a file offset with less than 32768 bytes.
56+
See specification.
57+
958
## License & Contribution
1059

1160
See main [README](https://github.com/rust-osdev/multiboot2/blob/main/README.md) file.

multiboot2-header/examples/minimal.rs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use multiboot2_header::builder::Multiboot2HeaderBuilder;
2+
use multiboot2_header::{
3+
HeaderTagFlag, HeaderTagISA, InformationRequestHeaderTagBuilder, MbiTagType, Multiboot2Header,
4+
RelocatableHeaderTag, RelocatableHeaderTagPreference,
5+
};
6+
7+
/// Small example that creates a Multiboot2 header and parses it afterwards.
8+
fn main() {
9+
// We create a Multiboot2 header during runtime here. A practical example is that your
10+
// program gets the header from a file and parses it afterwards.
11+
let mb2_hdr_bytes = Multiboot2HeaderBuilder::new(HeaderTagISA::I386)
12+
.relocatable_tag(RelocatableHeaderTag::new(
13+
HeaderTagFlag::Required,
14+
0x1337,
15+
0xdeadbeef,
16+
4096,
17+
RelocatableHeaderTagPreference::None,
18+
))
19+
.information_request_tag(
20+
InformationRequestHeaderTagBuilder::new(HeaderTagFlag::Required)
21+
.add_irs(&[MbiTagType::Cmdline, MbiTagType::BootLoaderName]),
22+
)
23+
.build();
24+
25+
// Cast bytes in vector to Multiboot2 information structure
26+
let mb2_hdr = unsafe { Multiboot2Header::from_addr(mb2_hdr_bytes.as_ptr() as usize) };
27+
println!("{:#?}", mb2_hdr);
28+
}

multiboot2-header/src/address.rs

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use crate::{HeaderTagFlag, HeaderTagType, StructAsBytes};
2+
use core::mem::size_of;
3+
4+
/// This information does not need to be provided if the kernel image is in ELF
5+
/// format, but it must be provided if the image is in a.out format or in some
6+
/// other format. Required for legacy boot (BIOS).
7+
/// Determines load addresses.
8+
#[derive(Copy, Clone, Debug)]
9+
#[repr(C, packed(8))]
10+
pub struct AddressHeaderTag {
11+
typ: HeaderTagType,
12+
flags: HeaderTagFlag,
13+
size: u32,
14+
/// 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.
15+
header_addr: u32,
16+
/// 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.
17+
///
18+
/// Special value -1 means that the file must be loaded from its beginning.
19+
load_addr: u32,
20+
/// Contains the physical address of the end of the data segment. (load_end_addr - load_addr) specifies how much data to load. This implies that the text and data segments must be consecutive in the OS image; this is true for existing a.out executable formats. If this field is zero, the boot loader assumes that the text and data segments occupy the whole OS image file.
21+
load_end_addr: u32,
22+
/// Contains the physical address of the end of the bss segment. The boot loader initializes this area to zero, and reserves the memory it occupies to avoid placing boot modules and other data relevant to the operating system in that area. If this field is zero, the boot loader assumes that no bss segment is present.
23+
bss_end_addr: u32,
24+
}
25+
26+
impl AddressHeaderTag {
27+
pub const fn new(
28+
flags: HeaderTagFlag,
29+
header_addr: u32,
30+
load_addr: u32,
31+
load_end_addr: u32,
32+
bss_end_addr: u32,
33+
) -> Self {
34+
AddressHeaderTag {
35+
typ: HeaderTagType::Address,
36+
flags,
37+
size: size_of::<Self>() as u32,
38+
header_addr,
39+
load_addr,
40+
load_end_addr,
41+
bss_end_addr,
42+
}
43+
}
44+
45+
pub const fn typ(&self) -> HeaderTagType {
46+
self.typ
47+
}
48+
pub const fn flags(&self) -> HeaderTagFlag {
49+
self.flags
50+
}
51+
pub const fn size(&self) -> u32 {
52+
self.size
53+
}
54+
pub const fn header_addr(&self) -> u32 {
55+
self.header_addr
56+
}
57+
pub const fn load_addr(&self) -> u32 {
58+
self.load_addr
59+
}
60+
pub const fn load_end_addr(&self) -> u32 {
61+
self.load_end_addr
62+
}
63+
pub const fn bss_end_addr(&self) -> u32 {
64+
self.bss_end_addr
65+
}
66+
}
67+
68+
impl StructAsBytes for AddressHeaderTag {}

multiboot2-header/src/console.rs

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use crate::{HeaderTagFlag, HeaderTagType, StructAsBytes};
2+
use core::mem::size_of;
3+
4+
/// Possible flags for [`ConsoleHeaderTag`].
5+
#[repr(u32)]
6+
#[derive(Copy, Clone, Debug)]
7+
pub enum ConsoleHeaderTagFlags {
8+
/// Console required.
9+
ConsoleRequired = 0,
10+
/// EGA text support.
11+
EgaTextSupported = 1,
12+
}
13+
14+
/// Tells that a console must be available in MBI.
15+
/// Only relevant for legacy BIOS.
16+
#[derive(Copy, Clone, Debug)]
17+
#[repr(C, packed(8))]
18+
pub struct ConsoleHeaderTag {
19+
typ: HeaderTagType,
20+
flags: HeaderTagFlag,
21+
size: u32,
22+
console_flags: ConsoleHeaderTagFlags,
23+
}
24+
25+
impl ConsoleHeaderTag {
26+
pub const fn new(flags: HeaderTagFlag, console_flags: ConsoleHeaderTagFlags) -> Self {
27+
ConsoleHeaderTag {
28+
typ: HeaderTagType::ConsoleFlags,
29+
flags,
30+
size: size_of::<Self>() as u32,
31+
console_flags,
32+
}
33+
}
34+
35+
pub const fn typ(&self) -> HeaderTagType {
36+
self.typ
37+
}
38+
pub const fn flags(&self) -> HeaderTagFlag {
39+
self.flags
40+
}
41+
pub const fn size(&self) -> u32 {
42+
self.size
43+
}
44+
pub const fn console_flags(&self) -> ConsoleHeaderTagFlags {
45+
self.console_flags
46+
}
47+
}
48+
49+
impl StructAsBytes for ConsoleHeaderTag {}
50+
51+
#[cfg(test)]
52+
mod tests {
53+
use crate::{ConsoleHeaderTag, ConsoleHeaderTagFlags, HeaderTagFlag, HeaderTagType};
54+
use std::mem::size_of_val;
55+
56+
/// Checks if rust aligns the type correctly and still "pack" all properties.
57+
/// This test is necessary, because Rust doesn't support "packed" together with "align()" yet.
58+
/// It seems like "packed(N)" does the right thing tho.
59+
///
60+
/// This test is representative for all header tags, because all use the "packed(8)" attribute.
61+
#[test]
62+
fn test_alignment_and_size() {
63+
let tag = ConsoleHeaderTag::new(
64+
HeaderTagFlag::Required,
65+
ConsoleHeaderTagFlags::ConsoleRequired,
66+
);
67+
let ptr = get_ptr!(tag, ConsoleHeaderTag);
68+
let is_aligned = ptr % 8 == 0;
69+
assert!(is_aligned);
70+
// 2x u16, 2x u32
71+
assert_eq!(2 + 2 + 4 + 4, size_of_val(&tag));
72+
73+
assert_eq!(ptr + 0, get_field_ptr!(tag, typ, HeaderTagType));
74+
assert_eq!(ptr + 2, get_field_ptr!(tag, flags, HeaderTagFlag));
75+
assert_eq!(ptr + 4, get_field_ptr!(tag, size, u32));
76+
assert_eq!(
77+
ptr + 8,
78+
get_field_ptr!(tag, console_flags, ConsoleHeaderTagFlags)
79+
);
80+
}
81+
}

multiboot2-header/src/end.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use crate::{HeaderTagFlag, HeaderTagType, StructAsBytes};
2+
use core::mem::size_of;
3+
4+
/// Terminates a list of optional tags
5+
/// in a Multiboot2 header.
6+
#[derive(Copy, Clone, Debug)]
7+
#[repr(C, packed(8))]
8+
pub struct EndHeaderTag {
9+
// u16 value
10+
typ: HeaderTagType,
11+
// u16 value
12+
flags: HeaderTagFlag,
13+
size: u32,
14+
}
15+
16+
impl EndHeaderTag {
17+
pub const fn new() -> Self {
18+
EndHeaderTag {
19+
typ: HeaderTagType::End,
20+
flags: HeaderTagFlag::Required,
21+
size: size_of::<Self>() as u32,
22+
}
23+
}
24+
25+
pub const fn typ(&self) -> HeaderTagType {
26+
self.typ
27+
}
28+
pub const fn flags(&self) -> HeaderTagFlag {
29+
self.flags
30+
}
31+
pub const fn size(&self) -> u32 {
32+
self.size
33+
}
34+
}
35+
36+
impl StructAsBytes for EndHeaderTag {}

multiboot2-header/src/entry_efi_32.rs

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use crate::{HeaderTagFlag, HeaderTagType, StructAsBytes};
2+
use core::fmt;
3+
use core::fmt::{Debug, Formatter};
4+
use core::mem::size_of;
5+
6+
/// This tag is taken into account only on EFI i386 platforms when Multiboot2 image header
7+
/// contains EFI boot services tag. Then entry point specified in ELF header and the entry address
8+
/// tag of Multiboot2 header are ignored.
9+
#[derive(Copy, Clone)]
10+
#[repr(C, packed(8))]
11+
pub struct EntryEfi32HeaderTag {
12+
typ: HeaderTagType,
13+
flags: HeaderTagFlag,
14+
size: u32,
15+
entry_addr: u32,
16+
}
17+
18+
impl EntryEfi32HeaderTag {
19+
pub const fn new(flags: HeaderTagFlag, entry_addr: u32) -> Self {
20+
EntryEfi32HeaderTag {
21+
typ: HeaderTagType::EntryAddressEFI32,
22+
flags,
23+
size: size_of::<Self>() as u32,
24+
entry_addr,
25+
}
26+
}
27+
28+
pub const fn typ(&self) -> HeaderTagType {
29+
self.typ
30+
}
31+
pub const fn flags(&self) -> HeaderTagFlag {
32+
self.flags
33+
}
34+
pub const fn size(&self) -> u32 {
35+
self.size
36+
}
37+
pub const fn entry_addr(&self) -> u32 {
38+
self.entry_addr
39+
}
40+
}
41+
42+
impl Debug for EntryEfi32HeaderTag {
43+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
44+
f.debug_struct("EntryEfi32HeaderTag")
45+
.field("type", &{ self.typ })
46+
.field("flags", &{ self.flags })
47+
.field("size", &{ self.size })
48+
.field("entry_addr", &(self.entry_addr as *const u32))
49+
.finish()
50+
}
51+
}
52+
53+
impl StructAsBytes for EntryEfi32HeaderTag {}

0 commit comments

Comments
 (0)