|
1 | 1 | //! Exports item [`Multiboot2InformationBuilder`]. |
2 | 2 | use crate::builder::traits::StructAsBytes; |
3 | 3 | use crate::{ |
4 | | - BasicMemoryInfoTag, BootLoaderNameTag, CommandLineTag, ElfSectionsTag, FramebufferTag, |
5 | | - MemoryMapTag, ModuleTag, |
| 4 | + BasicMemoryInfoTag, BootInformationInner, BootLoaderNameTag, CommandLineTag, ElfSectionsTag, |
| 5 | + EndTag, FramebufferTag, MemoryMapTag, ModuleTag, |
6 | 6 | }; |
7 | 7 |
|
8 | 8 | use alloc::boxed::Box; |
9 | 9 | use alloc::vec::Vec; |
| 10 | +use core::mem::size_of; |
10 | 11 |
|
11 | 12 | /// Builder to construct a valid Multiboot2 information dynamically at runtime. |
12 | 13 | /// The tags will appear in the order of their corresponding enumeration, |
@@ -35,6 +36,105 @@ impl Multiboot2InformationBuilder { |
35 | 36 | } |
36 | 37 | } |
37 | 38 |
|
| 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 | + |
38 | 138 | pub fn basic_memory_info_tag(&mut self, basic_memory_info_tag: BasicMemoryInfoTag) { |
39 | 139 | self.basic_memory_info_tag = Some(basic_memory_info_tag) |
40 | 140 | } |
@@ -63,3 +163,41 @@ impl Multiboot2InformationBuilder { |
63 | 163 | self.module_tags.push(module_tag); |
64 | 164 | } |
65 | 165 | } |
| 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 | +} |
0 commit comments