Skip to content

Commit 122fc9c

Browse files
committed
multiboot2: Implement building the multiboot2 information
1 parent d198dce commit 122fc9c

File tree

10 files changed

+276
-17
lines changed

10 files changed

+276
-17
lines changed

multiboot2/src/boot_loader_name.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ use core::mem::size_of;
44
use core::str::Utf8Error;
55

66
#[cfg(feature = "builder")]
7-
use {crate::builder::boxed_dst_tag, alloc::boxed::Box, alloc::vec::Vec};
7+
use {
8+
crate::builder::boxed_dst_tag, crate::builder::traits::StructAsBytes, alloc::boxed::Box,
9+
alloc::vec::Vec,
10+
};
811

912
const METADATA_SIZE: usize = size_of::<TagTypeId>() + size_of::<u32>();
1013

@@ -63,6 +66,13 @@ impl TagTrait for BootLoaderNameTag {
6366
}
6467
}
6568

69+
#[cfg(feature = "builder")]
70+
impl StructAsBytes for BootLoaderNameTag {
71+
fn byte_size(&self) -> usize {
72+
self.size.try_into().unwrap()
73+
}
74+
}
75+
6676
#[cfg(test)]
6777
mod tests {
6878
use crate::{BootLoaderNameTag, Tag, TagType};
@@ -95,4 +105,15 @@ mod tests {
95105
assert_eq!({ tag.typ }, TagType::BootLoaderName);
96106
assert_eq!(tag.name().expect("must be valid UTF-8"), MSG);
97107
}
108+
109+
/// Test to generate a tag from a given string.
110+
#[test]
111+
#[cfg(feature = "builder")]
112+
fn test_build_str() {
113+
use crate::builder::traits::StructAsBytes;
114+
115+
let tag = BootLoaderNameTag::new(MSG);
116+
let bytes = tag.struct_as_bytes();
117+
assert_eq!(bytes, get_bytes());
118+
}
98119
}

multiboot2/src/builder/information.rs

Lines changed: 140 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
//! Exports item [`Multiboot2InformationBuilder`].
22
use crate::builder::traits::StructAsBytes;
33
use crate::{
4-
BasicMemoryInfoTag, BootLoaderNameTag, CommandLineTag, ElfSectionsTag, FramebufferTag,
5-
MemoryMapTag, ModuleTag,
4+
BasicMemoryInfoTag, BootInformationInner, BootLoaderNameTag, CommandLineTag, ElfSectionsTag,
5+
EndTag, FramebufferTag, MemoryMapTag, ModuleTag,
66
};
77

88
use alloc::boxed::Box;
99
use alloc::vec::Vec;
10+
use core::mem::size_of;
1011

1112
/// Builder to construct a valid Multiboot2 information dynamically at runtime.
1213
/// The tags will appear in the order of their corresponding enumeration,
@@ -35,6 +36,105 @@ impl Multiboot2InformationBuilder {
3536
}
3637
}
3738

39+
/// Returns the size, if the value is a multiple of 8 or returns
40+
/// the next number that is a multiple of 8. With this, one can
41+
/// easily calculate the size of a Multiboot2 header, where
42+
/// all the tags are 8-byte aligned.
43+
const fn size_or_up_aligned(size: usize) -> usize {
44+
let remainder = size % 8;
45+
if remainder == 0 {
46+
size
47+
} else {
48+
size + 8 - remainder
49+
}
50+
}
51+
52+
/// Returns the expected length of the Multiboot2 header,
53+
/// when the `build()`-method gets called.
54+
pub fn expected_len(&self) -> usize {
55+
let base_len = size_of::<BootInformationInner>();
56+
// size_or_up_aligned not required, because length is 16 and the
57+
// begin is 8 byte aligned => first tag automatically 8 byte aligned
58+
let mut len = Self::size_or_up_aligned(base_len);
59+
if let Some(tag) = &self.basic_memory_info_tag {
60+
// we use size_or_up_aligned, because each tag will start at an 8 byte aligned address
61+
len += Self::size_or_up_aligned(tag.byte_size())
62+
}
63+
if let Some(tag) = &self.boot_loader_name_tag {
64+
len += Self::size_or_up_aligned(tag.byte_size())
65+
}
66+
if let Some(tag) = &self.command_line_tag {
67+
len += Self::size_or_up_aligned(tag.byte_size())
68+
}
69+
if let Some(tag) = &self.elf_sections_tag {
70+
len += Self::size_or_up_aligned(tag.byte_size())
71+
}
72+
if let Some(tag) = &self.framebuffer_tag {
73+
len += Self::size_or_up_aligned(tag.byte_size())
74+
}
75+
if let Some(tag) = &self.memory_map_tag {
76+
len += Self::size_or_up_aligned(tag.byte_size())
77+
}
78+
for tag in &self.module_tags {
79+
len += Self::size_or_up_aligned(tag.byte_size())
80+
}
81+
// only here size_or_up_aligned is not important, because it is the last tag
82+
len += size_of::<EndTag>();
83+
len
84+
}
85+
86+
/// Adds the bytes of a tag to the final Multiboot2 information byte vector.
87+
/// Align should be true for all tags except the end tag.
88+
fn build_add_bytes(dest: &mut Vec<u8>, source: &[u8], is_end_tag: bool) {
89+
dest.extend(source);
90+
if !is_end_tag {
91+
let size = source.len();
92+
let size_to_8_align = Self::size_or_up_aligned(size);
93+
let size_to_8_align_diff = size_to_8_align - size;
94+
// fill zeroes so that next data block is 8-byte aligned
95+
dest.extend([0].repeat(size_to_8_align_diff));
96+
}
97+
}
98+
99+
/// Constructs the bytes for a valid Multiboot2 information with the given properties.
100+
/// The bytes can be casted to a Multiboot2 structure.
101+
pub fn build(self) -> Vec<u8> {
102+
let mut data = Vec::new();
103+
104+
Self::build_add_bytes(
105+
&mut data,
106+
// important that we write the correct expected length into the header!
107+
&BootInformationInner::new(self.expected_len() as u32).struct_as_bytes(),
108+
false,
109+
);
110+
111+
if let Some(tag) = self.basic_memory_info_tag.as_ref() {
112+
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
113+
}
114+
if let Some(tag) = self.boot_loader_name_tag.as_ref() {
115+
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
116+
}
117+
if let Some(tag) = self.command_line_tag.as_ref() {
118+
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
119+
}
120+
if let Some(tag) = self.elf_sections_tag.as_ref() {
121+
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
122+
}
123+
if let Some(tag) = self.framebuffer_tag.as_ref() {
124+
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
125+
}
126+
if let Some(tag) = self.memory_map_tag.as_ref() {
127+
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
128+
}
129+
for tag in self.module_tags {
130+
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
131+
}
132+
133+
Self::build_add_bytes(&mut data, &EndTag::default().struct_as_bytes(), true);
134+
135+
data
136+
}
137+
38138
pub fn basic_memory_info_tag(&mut self, basic_memory_info_tag: BasicMemoryInfoTag) {
39139
self.basic_memory_info_tag = Some(basic_memory_info_tag)
40140
}
@@ -63,3 +163,41 @@ impl Multiboot2InformationBuilder {
63163
self.module_tags.push(module_tag);
64164
}
65165
}
166+
167+
#[cfg(test)]
168+
mod tests {
169+
use crate::builder::information::Multiboot2InformationBuilder;
170+
use crate::{load, BasicMemoryInfoTag, CommandLineTag, ModuleTag};
171+
172+
#[test]
173+
fn test_size_or_up_aligned() {
174+
assert_eq!(0, Multiboot2InformationBuilder::size_or_up_aligned(0));
175+
assert_eq!(8, Multiboot2InformationBuilder::size_or_up_aligned(1));
176+
assert_eq!(8, Multiboot2InformationBuilder::size_or_up_aligned(8));
177+
assert_eq!(16, Multiboot2InformationBuilder::size_or_up_aligned(9));
178+
}
179+
180+
#[test]
181+
fn test_size_builder() {
182+
let mut builder = Multiboot2InformationBuilder::new();
183+
// Multiboot2 basic information + end tag
184+
let expected_len = 8 + 8;
185+
assert_eq!(builder.expected_len(), expected_len);
186+
187+
// the most simple tag
188+
builder.basic_memory_info_tag(BasicMemoryInfoTag::new(640, 7 * 1024));
189+
// a tag that has a dynamic size
190+
builder.command_line_tag(CommandLineTag::new("test"));
191+
// many modules
192+
builder.add_module_tag(ModuleTag::new(0, 1234, "module1"));
193+
builder.add_module_tag(ModuleTag::new(5678, 6789, "module2"));
194+
195+
println!("builder: {:#?}", builder);
196+
println!("expected_len: {} bytes", builder.expected_len());
197+
198+
let mb2i_data = builder.build();
199+
let mb2i_addr = mb2i_data.as_ptr() as usize;
200+
let mb2i = unsafe { load(mb2i_addr) };
201+
println!("{:#?}", mb2i);
202+
}
203+
}

multiboot2/src/builder/traits.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
//! Module for the helper trait [`StructAsBytes`].
22
3-
use core::mem::size_of;
4-
53
/// Trait for all tags that helps to create a byte array from the tag.
64
/// Useful in builders to construct a byte vector that
75
/// represents the Multiboot2 information with all its tags.
8-
pub(crate) trait StructAsBytes: Sized {
9-
/// Returns the size in bytes of the struct, as known during compile
10-
/// time. This doesn't use read the "size" field of tags.
11-
fn byte_size(&self) -> usize {
12-
size_of::<Self>()
13-
}
6+
pub(crate) trait StructAsBytes {
7+
/// Returns the size in bytes of the struct.
8+
/// This can be either the "size" field of tags or the compile-time size
9+
/// (if known).
10+
fn byte_size(&self) -> usize;
1411

1512
/// Returns a byte pointer to the begin of the struct.
1613
fn as_ptr(&self) -> *const u8 {

multiboot2/src/command_line.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
//! Module for [CommandLineTag].
22
33
use crate::{Tag, TagTrait, TagType, TagTypeId};
4+
5+
use core::convert::TryInto;
46
use core::fmt::{Debug, Formatter};
57
use core::mem;
68
use core::str;
79

810
#[cfg(feature = "builder")]
9-
use {crate::builder::boxed_dst_tag, alloc::boxed::Box, alloc::vec::Vec};
11+
use {
12+
crate::builder::boxed_dst_tag, crate::builder::traits::StructAsBytes, alloc::boxed::Box,
13+
alloc::vec::Vec,
14+
};
1015

1116
pub(crate) const METADATA_SIZE: usize = mem::size_of::<TagTypeId>() + mem::size_of::<u32>();
1217

@@ -71,6 +76,13 @@ impl TagTrait for CommandLineTag {
7176
}
7277
}
7378

79+
#[cfg(feature = "builder")]
80+
impl StructAsBytes for CommandLineTag {
81+
fn byte_size(&self) -> usize {
82+
self.size.try_into().unwrap()
83+
}
84+
}
85+
7486
#[cfg(test)]
7587
mod tests {
7688
use crate::{CommandLineTag, Tag, TagType};
@@ -103,4 +115,15 @@ mod tests {
103115
assert_eq!({ tag.typ }, TagType::Cmdline);
104116
assert_eq!(tag.command_line().expect("must be valid UTF-8"), MSG);
105117
}
118+
119+
/// Test to generate a tag from a given string.
120+
#[test]
121+
#[cfg(feature = "builder")]
122+
fn test_build_str() {
123+
use crate::builder::traits::StructAsBytes;
124+
125+
let tag = CommandLineTag::new(MSG);
126+
let bytes = tag.struct_as_bytes();
127+
assert_eq!(bytes, get_bytes());
128+
}
106129
}

multiboot2/src/elf_sections.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use core::mem::size_of;
55
use core::str::Utf8Error;
66

77
#[cfg(feature = "builder")]
8-
use {crate::builder::boxed_dst_tag, alloc::boxed::Box};
8+
use {crate::builder::boxed_dst_tag, crate::builder::traits::StructAsBytes, alloc::boxed::Box};
99

1010
const METADATA_SIZE: usize = size_of::<TagTypeId>() + 4 * size_of::<u32>();
1111

@@ -76,6 +76,13 @@ impl TagTrait for ElfSectionsTag {
7676
}
7777
}
7878

79+
#[cfg(feature = "builder")]
80+
impl StructAsBytes for ElfSectionsTag {
81+
fn byte_size(&self) -> usize {
82+
self.size.try_into().unwrap()
83+
}
84+
}
85+
7986
/// An iterator over some ELF sections.
8087
#[derive(Clone)]
8188
pub struct ElfSectionIter {

multiboot2/src/framebuffer.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,13 @@ impl TagTrait for FramebufferTag {
149149
}
150150
}
151151

152+
#[cfg(feature = "builder")]
153+
impl StructAsBytes for FramebufferTag {
154+
fn byte_size(&self) -> usize {
155+
self.size.try_into().unwrap()
156+
}
157+
}
158+
152159
/// Helper struct for [`FramebufferType`].
153160
#[derive(Debug, PartialEq, Eq)]
154161
#[repr(u8)]
@@ -226,7 +233,12 @@ pub struct FramebufferField {
226233
pub size: u8,
227234
}
228235

229-
impl StructAsBytes for FramebufferField {}
236+
#[cfg(feature = "builder")]
237+
impl StructAsBytes for FramebufferField {
238+
fn byte_size(&self) -> usize {
239+
size_of::<Self>()
240+
}
241+
}
230242

231243
/// A framebuffer color descriptor in the palette.
232244
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -250,4 +262,9 @@ pub struct UnknownFramebufferType(u8);
250262
#[cfg(feature = "unstable")]
251263
impl core::error::Error for UnknownFramebufferType {}
252264

253-
impl StructAsBytes for FramebufferColor {}
265+
#[cfg(feature = "builder")]
266+
impl StructAsBytes for FramebufferColor {
267+
fn byte_size(&self) -> usize {
268+
size_of::<Self>()
269+
}
270+
}

multiboot2/src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ pub use ptr_meta::Pointee;
4747

4848
use crate::framebuffer::UnknownFramebufferType;
4949
pub use boot_loader_name::BootLoaderNameTag;
50+
#[cfg(feature = "builder")]
51+
use builder::traits::StructAsBytes;
5052
pub use command_line::CommandLineTag;
5153
pub use efi::{EFIImageHandle32, EFIImageHandle64, EFISdt32, EFISdt64};
5254
pub use elf_sections::{
@@ -203,6 +205,22 @@ struct BootInformationInner {
203205
_reserved: u32,
204206
}
205207

208+
impl BootInformationInner {
209+
fn new(total_size: u32) -> Self {
210+
Self {
211+
total_size,
212+
_reserved: 0,
213+
}
214+
}
215+
}
216+
217+
#[cfg(feature = "builder")]
218+
impl StructAsBytes for BootInformationInner {
219+
fn byte_size(&self) -> usize {
220+
core::mem::size_of::<Self>()
221+
}
222+
}
223+
206224
impl BootInformation {
207225
/// Get the start address of the boot info.
208226
pub fn start_address(&self) -> usize {

0 commit comments

Comments
 (0)