Skip to content

Commit 8ea06c7

Browse files
committed
multiboot2-header: initial version of crate
1 parent 3e20693 commit 8ea06c7

20 files changed

+1745
-4
lines changed

README.md

Lines changed: 3 additions & 2 deletions
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

Lines changed: 3 additions & 1 deletion
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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,45 @@
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
19+
```rust
20+
use multiboot2_header::builder::Multiboot2HeaderBuilder;
21+
use multiboot2_header::{ConsoleHeaderTag, HeaderTagFlag, HeaderTagISA, InformationRequestHeaderTagBuilder, MbiTagType, Multiboot2Header, RelocatableHeaderTag, RelocatableHeaderTagPreference, load_mb2_header};
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 { load_mb2_header(mb2_hdr_bytes.as_ptr() as usize) };
43+
println!("{:#?}", mb2_hdr);
44+
}
45+
46+
```
47+
948
## License & Contribution
1049

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

multiboot2-header/examples/minimal.rs

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

multiboot2-header/src/address.rs

Lines changed: 68 additions & 0 deletions
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 fn typ(&self) -> HeaderTagType {
46+
self.typ
47+
}
48+
pub fn flags(&self) -> HeaderTagFlag {
49+
self.flags
50+
}
51+
pub fn size(&self) -> u32 {
52+
self.size
53+
}
54+
pub fn header_addr(&self) -> u32 {
55+
self.header_addr
56+
}
57+
pub fn load_addr(&self) -> u32 {
58+
self.load_addr
59+
}
60+
pub fn load_end_addr(&self) -> u32 {
61+
self.load_end_addr
62+
}
63+
pub fn bss_end_addr(&self) -> u32 {
64+
self.bss_end_addr
65+
}
66+
}
67+
68+
impl StructAsBytes for AddressHeaderTag {}

multiboot2-header/src/console.rs

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

multiboot2-header/src/end.rs

Lines changed: 36 additions & 0 deletions
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 fn typ(&self) -> HeaderTagType {
26+
self.typ
27+
}
28+
pub fn flags(&self) -> HeaderTagFlag {
29+
self.flags
30+
}
31+
pub fn size(&self) -> u32 {
32+
self.size
33+
}
34+
}
35+
36+
impl StructAsBytes for EndHeaderTag {}

multiboot2-header/src/entry_efi_32.rs

Lines changed: 53 additions & 0 deletions
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 fn typ(&self) -> HeaderTagType {
29+
self.typ
30+
}
31+
pub fn flags(&self) -> HeaderTagFlag {
32+
self.flags
33+
}
34+
pub fn size(&self) -> u32 {
35+
self.size
36+
}
37+
pub 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 {}

multiboot2-header/src/entry_efi_64.rs

Lines changed: 53 additions & 0 deletions
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 amd64 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 EntryEfi64HeaderTag {
12+
typ: HeaderTagType,
13+
flags: HeaderTagFlag,
14+
size: u32,
15+
entry_addr: u32,
16+
}
17+
18+
impl EntryEfi64HeaderTag {
19+
pub const fn new(flags: HeaderTagFlag, entry_addr: u32) -> Self {
20+
EntryEfi64HeaderTag {
21+
typ: HeaderTagType::EntryAddressEFI64,
22+
flags,
23+
size: size_of::<Self>() as u32,
24+
entry_addr,
25+
}
26+
}
27+
28+
pub fn typ(&self) -> HeaderTagType {
29+
self.typ
30+
}
31+
pub fn flags(&self) -> HeaderTagFlag {
32+
self.flags
33+
}
34+
pub fn size(&self) -> u32 {
35+
self.size
36+
}
37+
pub fn entry_addr(&self) -> u32 {
38+
self.entry_addr
39+
}
40+
}
41+
42+
impl Debug for EntryEfi64HeaderTag {
43+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
44+
f.debug_struct("EntryEfi64HeaderTag")
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 EntryEfi64HeaderTag {}

0 commit comments

Comments
 (0)