Skip to content

Commit e9ed160

Browse files
committed
multiboot2: Implement building the multiboot2 information
1 parent 9fc4996 commit e9ed160

File tree

10 files changed

+250
-13
lines changed

10 files changed

+250
-13
lines changed

multiboot2/src/boot_loader_name.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use crate::TagType;
22
#[cfg(feature = "builder")]
33
use crate::builder::boxed_dst_tag;
4+
#[cfg(feature = "builder")]
5+
use crate::builder::traits::StructAsBytes;
46

57
use core::convert::TryInto;
68
use core::mem;
@@ -63,6 +65,13 @@ impl BootLoaderNameTag {
6365
}
6466
}
6567

68+
#[cfg(feature = "builder")]
69+
impl StructAsBytes for BootLoaderNameTag {
70+
fn byte_size(&self) -> usize {
71+
self.size.try_into().unwrap()
72+
}
73+
}
74+
6675
#[cfg(test)]
6776
mod tests {
6877
use crate::{TagType, boot_loader_name::METADATA_SIZE};

multiboot2/src/builder/information.rs

Lines changed: 145 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,
5-
FramebufferTag, MemoryMapTag, ModuleTag,
4+
BasicMemoryInfoTag, BootInformationInner, BootLoaderNameTag, CommandLineTag,
5+
ElfSectionsTag, 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,107 @@ 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(
108+
self.expected_len() as u32
109+
).struct_as_bytes(),
110+
false,
111+
);
112+
113+
if let Some(tag) = self.basic_memory_info_tag.as_ref() {
114+
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
115+
}
116+
if let Some(tag) = self.boot_loader_name_tag.as_ref() {
117+
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
118+
}
119+
if let Some(tag) = self.command_line_tag.as_ref() {
120+
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
121+
}
122+
if let Some(tag) = self.elf_sections_tag.as_ref() {
123+
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
124+
}
125+
if let Some(tag) = self.framebuffer_tag.as_ref() {
126+
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
127+
}
128+
if let Some(tag) = self.memory_map_tag.as_ref() {
129+
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
130+
}
131+
for tag in self.module_tags {
132+
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
133+
}
134+
135+
Self::build_add_bytes(&mut data, &EndTag::new().struct_as_bytes(), true);
136+
137+
data
138+
}
139+
38140
pub fn basic_memory_info_tag(&mut self, basic_memory_info_tag: BasicMemoryInfoTag) {
39141
self.basic_memory_info_tag = Some(basic_memory_info_tag)
40142
}
@@ -63,3 +165,44 @@ impl Multiboot2InformationBuilder {
63165
self.module_tags.push(module_tag);
64166
}
65167
}
168+
169+
#[cfg(test)]
170+
mod tests {
171+
use crate::builder::information::Multiboot2InformationBuilder;
172+
use crate::{
173+
BasicMemoryInfoTag, CommandLineTag, ModuleTag, load,
174+
};
175+
176+
#[test]
177+
fn test_size_or_up_aligned() {
178+
assert_eq!(0, Multiboot2InformationBuilder::size_or_up_aligned(0));
179+
assert_eq!(8, Multiboot2InformationBuilder::size_or_up_aligned(1));
180+
assert_eq!(8, Multiboot2InformationBuilder::size_or_up_aligned(8));
181+
assert_eq!(16, Multiboot2InformationBuilder::size_or_up_aligned(9));
182+
}
183+
184+
#[test]
185+
fn test_size_builder() {
186+
let mut builder = Multiboot2InformationBuilder::new();
187+
// Multiboot2 basic information + end tag
188+
let expected_len = 8 + 8;
189+
assert_eq!(builder.expected_len(), expected_len);
190+
191+
// the most simple tag
192+
builder.basic_memory_info_tag(BasicMemoryInfoTag::new(640, 7 * 1024));
193+
// a tag that has a dynamic size
194+
builder.command_line_tag(CommandLineTag::new("test"));
195+
// many modules
196+
builder.add_module_tag(ModuleTag::new(0, 1234, "module1"));
197+
builder.add_module_tag(ModuleTag::new(5678, 6789, "module2"));
198+
199+
println!("builder: {:#?}", builder);
200+
println!("expected_len: {} bytes", builder.expected_len());
201+
202+
let mb2i_data = builder.build();
203+
let mb2i_addr = mb2i_data.as_ptr() as usize;
204+
let mb2i = unsafe { load(mb2i_addr) };
205+
println!("{:#?}", mb2i);
206+
}
207+
}
208+

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: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
use crate::TagType;
44
#[cfg(feature = "builder")]
55
use crate::builder::boxed_dst_tag;
6+
#[cfg(feature = "builder")]
7+
use crate::builder::traits::StructAsBytes;
68
use core::convert::TryInto;
79
use core::mem;
810
use core::slice;
@@ -61,6 +63,13 @@ impl CommandLineTag {
6163
}
6264
}
6365

66+
#[cfg(feature = "builder")]
67+
impl StructAsBytes for CommandLineTag {
68+
fn byte_size(&self) -> usize {
69+
self.size.try_into().unwrap()
70+
}
71+
}
72+
6473
#[cfg(test)]
6574
mod tests {
6675
use crate::{TagType, command_line::METADATA_SIZE};

multiboot2/src/elf_sections.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use crate::tag_type::{Tag, TagType};
22
#[cfg(feature = "builder")]
33
use crate::builder::boxed_dst_tag;
4+
#[cfg(feature = "builder")]
5+
use crate::builder::traits::StructAsBytes;
6+
47
use core::convert::TryInto;
58
use core::{fmt::{Debug, Formatter}, mem};
69
#[cfg(feature = "builder")]
@@ -68,6 +71,13 @@ impl ElfSectionsTag {
6871
}
6972
}
7073

74+
#[cfg(feature = "builder")]
75+
impl StructAsBytes for ElfSectionsTag {
76+
fn byte_size(&self) -> usize {
77+
self.size.try_into().unwrap()
78+
}
79+
}
80+
7181
/// An iterator over some ELF sections.
7282
#[derive(Clone)]
7383
pub struct ElfSectionIter {

multiboot2/src/framebuffer.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,13 @@ impl FramebufferTag {
141141
}
142142
}
143143

144+
#[cfg(feature = "builder")]
145+
impl StructAsBytes for FramebufferTag {
146+
fn byte_size(&self) -> usize {
147+
self.size.try_into().unwrap()
148+
}
149+
}
150+
144151
/// The type of framebuffer.
145152
#[derive(Debug, PartialEq, Eq)]
146153
pub enum FramebufferType<'a> {
@@ -206,7 +213,12 @@ pub struct FramebufferField {
206213
pub size: u8,
207214
}
208215

209-
impl StructAsBytes for FramebufferField {}
216+
#[cfg(feature = "builder")]
217+
impl StructAsBytes for FramebufferField {
218+
fn byte_size(&self) -> usize {
219+
mem::size_of::<Self>()
220+
}
221+
}
210222

211223
/// A framebuffer color descriptor in the palette.
212224
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -222,4 +234,9 @@ pub struct FramebufferColor {
222234
pub blue: u8,
223235
}
224236

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

multiboot2/src/lib.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ extern crate std;
4747
use core::{fmt, convert::TryInto};
4848

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::{
@@ -195,6 +197,19 @@ struct BootInformationInner {
195197
_reserved: u32,
196198
}
197199

200+
impl BootInformationInner {
201+
fn new(total_size: u32) -> Self {
202+
Self { total_size, _reserved: 0 }
203+
}
204+
}
205+
206+
#[cfg(feature = "builder")]
207+
impl StructAsBytes for BootInformationInner {
208+
fn byte_size(&self) -> usize {
209+
core::mem::size_of::<Self>()
210+
}
211+
}
212+
198213
impl BootInformation {
199214
/// Get the start address of the boot info.
200215
pub fn start_address(&self) -> usize {

multiboot2/src/memory_map.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ impl MemoryMapTag {
6868
}
6969
}
7070

71+
#[cfg(feature = "builder")]
72+
impl StructAsBytes for MemoryMapTag {
73+
fn byte_size(&self) -> usize {
74+
self.size.try_into().unwrap()
75+
}
76+
}
77+
7178
/// A memory area entry descriptor.
7279
#[derive(Debug, Clone)]
7380
#[repr(C)]
@@ -105,7 +112,12 @@ impl MemoryArea {
105112
}
106113
}
107114

108-
impl StructAsBytes for MemoryArea {}
115+
#[cfg(feature = "builder")]
116+
impl StructAsBytes for MemoryArea {
117+
fn byte_size(&self) -> usize {
118+
mem::size_of::<Self>()
119+
}
120+
}
109121

110122
/// An enum of possible reported region types.
111123
/// Inside the Multiboot2 spec this is kind of hidden
@@ -197,6 +209,13 @@ impl BasicMemoryInfoTag {
197209
}
198210
}
199211

212+
#[cfg(feature = "builder")]
213+
impl StructAsBytes for BasicMemoryInfoTag {
214+
fn byte_size(&self) -> usize {
215+
mem::size_of::<Self>()
216+
}
217+
}
218+
200219
/// EFI memory map as per EFI specification.
201220
#[derive(Debug)]
202221
#[repr(C)]

multiboot2/src/module.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use crate::tag_type::{Tag, TagIter, TagType};
22
#[cfg(feature = "builder")]
33
use crate::builder::boxed_dst_tag;
4+
#[cfg(feature = "builder")]
5+
use crate::builder::traits::StructAsBytes;
46

57
use core::convert::TryInto;
68
use core::fmt::{Debug, Formatter};
@@ -76,6 +78,13 @@ impl ModuleTag {
7678
}
7779
}
7880

81+
#[cfg(feature = "builder")]
82+
impl StructAsBytes for ModuleTag {
83+
fn byte_size(&self) -> usize {
84+
self.size.try_into().unwrap()
85+
}
86+
}
87+
7988
impl Debug for ModuleTag {
8089
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
8190
f.debug_struct("ModuleTag")

0 commit comments

Comments
 (0)