|
@@ -45,138 +45,50 @@ impl Deref for BootInformationBytes {
|
|
|
/// The tags will appear in the order of their corresponding enumeration,
|
|
|
/// except for the END tag.
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
|
-pub struct InformationBuilder {
|
|
|
- basic_memory_info_tag: Option<BasicMemoryInfoTag>,
|
|
|
- boot_loader_name_tag: Option<BoxedDst<BootLoaderNameTag>>,
|
|
|
- command_line_tag: Option<BoxedDst<CommandLineTag>>,
|
|
|
- efi_boot_services_not_exited_tag: Option<EFIBootServicesNotExitedTag>,
|
|
|
- efi_image_handle32: Option<EFIImageHandle32Tag>,
|
|
|
- efi_image_handle64: Option<EFIImageHandle64Tag>,
|
|
|
- efi_memory_map_tag: Option<BoxedDst<EFIMemoryMapTag>>,
|
|
|
- elf_sections_tag: Option<BoxedDst<ElfSectionsTag>>,
|
|
|
- framebuffer_tag: Option<BoxedDst<FramebufferTag>>,
|
|
|
- image_load_addr: Option<ImageLoadPhysAddrTag>,
|
|
|
- memory_map_tag: Option<BoxedDst<MemoryMapTag>>,
|
|
|
- module_tags: Vec<BoxedDst<ModuleTag>>,
|
|
|
- efisdt32_tag: Option<EFISdt32Tag>,
|
|
|
- efisdt64_tag: Option<EFISdt64Tag>,
|
|
|
- rsdp_v1_tag: Option<RsdpV1Tag>,
|
|
|
- rsdp_v2_tag: Option<RsdpV2Tag>,
|
|
|
- smbios_tags: Vec<BoxedDst<SmbiosTag>>,
|
|
|
-}
|
|
|
+pub struct InformationBuilder(Vec<(TagType, Vec<u8> /* Serialized tag */)>);
|
|
|
|
|
|
impl InformationBuilder {
|
|
|
pub const fn new() -> Self {
|
|
|
- Self {
|
|
|
- basic_memory_info_tag: None,
|
|
|
- boot_loader_name_tag: None,
|
|
|
- command_line_tag: None,
|
|
|
- efisdt32_tag: None,
|
|
|
- efisdt64_tag: None,
|
|
|
- efi_boot_services_not_exited_tag: None,
|
|
|
- efi_image_handle32: None,
|
|
|
- efi_image_handle64: None,
|
|
|
- efi_memory_map_tag: None,
|
|
|
- elf_sections_tag: None,
|
|
|
- framebuffer_tag: None,
|
|
|
- image_load_addr: None,
|
|
|
- memory_map_tag: None,
|
|
|
- module_tags: Vec::new(),
|
|
|
- rsdp_v1_tag: None,
|
|
|
- rsdp_v2_tag: None,
|
|
|
- smbios_tags: Vec::new(),
|
|
|
- }
|
|
|
+ Self(Vec::new())
|
|
|
}
|
|
|
|
|
|
- /// Returns the size, if the value is a multiple of 8 or returns
|
|
|
- /// the next number that is a multiple of 8. With this, one can
|
|
|
- /// easily calculate the size of a Multiboot2 header, where
|
|
|
- /// all the tags are 8-byte aligned.
|
|
|
+ /// Returns the provided number or the next multiple of 8. This is helpful
|
|
|
+ /// to ensure that the following tag starts at a 8-byte aligned boundary.
|
|
|
const fn size_or_up_aligned(size: usize) -> usize {
|
|
|
(size + 7) & !7
|
|
|
}
|
|
|
|
|
|
/// Returns the expected length of the boot information, when the
|
|
|
- /// [`Self::build`]-method gets called.
|
|
|
+ /// [`Self::build`]-method is called. This function assumes that the begin
|
|
|
+ /// of the boot information is 8-byte aligned and automatically adds padding
|
|
|
+ /// between tags to ensure that each tag is 8-byte aligned.
|
|
|
pub fn expected_len(&self) -> usize {
|
|
|
- let base_len = size_of::<BootInformationHeader>();
|
|
|
- // size_or_up_aligned not required, because length is 16 and the
|
|
|
- // begin is 8 byte aligned => first tag automatically 8 byte aligned
|
|
|
- let mut len = Self::size_or_up_aligned(base_len);
|
|
|
- if let Some(tag) = &self.basic_memory_info_tag {
|
|
|
- // we use size_or_up_aligned, because each tag will start at an 8 byte aligned address
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- if let Some(tag) = &self.boot_loader_name_tag {
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- if let Some(tag) = &self.command_line_tag {
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- if let Some(tag) = &self.efisdt32_tag {
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- if let Some(tag) = &self.efisdt64_tag {
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- if let Some(tag) = &self.efi_boot_services_not_exited_tag {
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- if let Some(tag) = &self.efi_image_handle32 {
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- if let Some(tag) = &self.efi_image_handle64 {
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- if let Some(tag) = &self.efi_memory_map_tag {
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- if let Some(tag) = &self.elf_sections_tag {
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- if let Some(tag) = &self.framebuffer_tag {
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- if let Some(tag) = &self.image_load_addr {
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- if let Some(tag) = &self.memory_map_tag {
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- for tag in &self.module_tags {
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- if let Some(tag) = &self.rsdp_v1_tag {
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- if let Some(tag) = &self.rsdp_v2_tag {
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- for tag in &self.smbios_tags {
|
|
|
- len += Self::size_or_up_aligned(tag.size())
|
|
|
- }
|
|
|
- // only here size_or_up_aligned is not important, because it is the last tag
|
|
|
- len += size_of::<EndTag>();
|
|
|
- len
|
|
|
+ let tag_size_iter = self.0.iter().map(|(_, bytes)| bytes.len());
|
|
|
+
|
|
|
+ let payload_tags_size = tag_size_iter.fold(0, |acc, tag_size| {
|
|
|
+ // size_or_up_aligned: make sure next tag is 8-byte aligned
|
|
|
+ acc + Self::size_or_up_aligned(tag_size)
|
|
|
+ });
|
|
|
+
|
|
|
+ size_of::<BootInformationHeader>() + payload_tags_size + size_of::<EndTag>()
|
|
|
}
|
|
|
|
|
|
/// Adds the bytes of a tag to the final Multiboot2 information byte vector.
|
|
|
- fn build_add_tag<T: TagTrait + ?Sized>(dest: &mut Vec<u8>, source: &T) {
|
|
|
- let vec_next_write_ptr = unsafe { dest.as_ptr().add(dest.len()) };
|
|
|
+ fn build_add_tag(dest_buf: &mut Vec<u8>, tag_serialized: &[u8], tag_type: TagType) {
|
|
|
+ let vec_next_write_ptr = unsafe { dest_buf.as_ptr().add(dest_buf.len()) };
|
|
|
+
|
|
|
// At this point, the alignment is guaranteed. If not, something is
|
|
|
// broken fundamentally.
|
|
|
assert_eq!(vec_next_write_ptr.align_offset(8), 0);
|
|
|
|
|
|
- dest.extend(source.as_bytes());
|
|
|
+ dest_buf.extend(tag_serialized);
|
|
|
|
|
|
- let is_end_tag = source.as_base_tag().typ == TagType::End;
|
|
|
-
|
|
|
- if !is_end_tag {
|
|
|
- let size = source.size();
|
|
|
+ if tag_type != TagType::End {
|
|
|
+ let size = tag_serialized.len();
|
|
|
let size_to_8_align = Self::size_or_up_aligned(size);
|
|
|
let size_to_8_align_diff = size_to_8_align - size;
|
|
|
// fill zeroes so that next data block is 8-byte aligned
|
|
|
- dest.extend([0].repeat(size_to_8_align_diff));
|
|
|
+ dest_buf.extend([0].repeat(size_to_8_align_diff));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -207,7 +119,11 @@ impl InformationBuilder {
|
|
|
// -----------------------------------------------
|
|
|
// PHASE 2/2: Add Tags
|
|
|
bytes.extend(BootInformationHeader::new(self.expected_len() as u32).as_bytes());
|
|
|
- self.build_add_tags(&mut bytes);
|
|
|
+
|
|
|
+ for (tag_type, tag_serialized) in self.0 {
|
|
|
+ Self::build_add_tag(&mut bytes, tag_serialized.as_slice(), tag_type)
|
|
|
+ }
|
|
|
+ Self::build_add_tag(&mut bytes, EndTag::default().as_bytes(), TagType::End);
|
|
|
|
|
|
assert_eq!(
|
|
|
alloc_ptr,
|
|
@@ -227,166 +143,125 @@ impl InformationBuilder {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /// Helper method that adds all the tags to the given vector.
|
|
|
- fn build_add_tags(&self, bytes: &mut Vec<u8>) {
|
|
|
- if let Some(tag) = self.basic_memory_info_tag.as_ref() {
|
|
|
- Self::build_add_tag(bytes, tag)
|
|
|
- }
|
|
|
- if let Some(tag) = self.boot_loader_name_tag.as_ref() {
|
|
|
- Self::build_add_tag(bytes, &**tag)
|
|
|
- }
|
|
|
- if let Some(tag) = self.command_line_tag.as_ref() {
|
|
|
- Self::build_add_tag(bytes, &**tag)
|
|
|
- }
|
|
|
- if let Some(tag) = self.efisdt32_tag.as_ref() {
|
|
|
- Self::build_add_tag(bytes, tag)
|
|
|
- }
|
|
|
- if let Some(tag) = self.efisdt64_tag.as_ref() {
|
|
|
- Self::build_add_tag(bytes, tag)
|
|
|
- }
|
|
|
- if let Some(tag) = self.efi_boot_services_not_exited_tag.as_ref() {
|
|
|
- Self::build_add_tag(bytes, tag)
|
|
|
- }
|
|
|
- if let Some(tag) = self.efi_image_handle32.as_ref() {
|
|
|
- Self::build_add_tag(bytes, tag)
|
|
|
- }
|
|
|
- if let Some(tag) = self.efi_image_handle64.as_ref() {
|
|
|
- Self::build_add_tag(bytes, tag)
|
|
|
- }
|
|
|
- if let Some(tag) = self.efi_memory_map_tag.as_ref() {
|
|
|
- Self::build_add_tag(bytes, &**tag)
|
|
|
- }
|
|
|
- if let Some(tag) = self.elf_sections_tag.as_ref() {
|
|
|
- Self::build_add_tag(bytes, &**tag)
|
|
|
- }
|
|
|
- if let Some(tag) = self.framebuffer_tag.as_ref() {
|
|
|
- Self::build_add_tag(bytes, &**tag)
|
|
|
- }
|
|
|
- if let Some(tag) = self.image_load_addr.as_ref() {
|
|
|
- Self::build_add_tag(bytes, tag)
|
|
|
- }
|
|
|
- if let Some(tag) = self.memory_map_tag.as_ref() {
|
|
|
- Self::build_add_tag(bytes, &**tag)
|
|
|
- }
|
|
|
- for tag in &self.module_tags {
|
|
|
- Self::build_add_tag(bytes, &**tag)
|
|
|
- }
|
|
|
- if let Some(tag) = self.rsdp_v1_tag.as_ref() {
|
|
|
- Self::build_add_tag(bytes, tag)
|
|
|
- }
|
|
|
- if let Some(tag) = self.rsdp_v2_tag.as_ref() {
|
|
|
- Self::build_add_tag(bytes, tag)
|
|
|
- }
|
|
|
- for tag in &self.smbios_tags {
|
|
|
- Self::build_add_tag(bytes, &**tag)
|
|
|
- }
|
|
|
- Self::build_add_tag(bytes, &EndTag::default());
|
|
|
+ /// Adds a arbitrary tag that implements [`TagTrait`] to the builder. Only
|
|
|
+ /// [`TagType::Module`] and [`TagType::Custom`] are allowed to appear
|
|
|
+ /// multiple times. For other tags, this function returns an error.
|
|
|
+ ///
|
|
|
+ /// The tags of the boot information will be ordered naturally by their
|
|
|
+ /// numeric ID.
|
|
|
+ ///
|
|
|
+ /// It is not required to manually add the [`TagType::End`] tag.
|
|
|
+ pub fn add_tag<T: TagTrait + ?Sized>(mut self, tag: &T) -> Self {
|
|
|
+ // not required to do this manually
|
|
|
+ if T::ID == TagType::End {
|
|
|
+ return self;
|
|
|
+ }
|
|
|
+
|
|
|
+ if self
|
|
|
+ .0
|
|
|
+ .iter()
|
|
|
+ .map(|(typ, _)| *typ)
|
|
|
+ .any(|typ| typ == T::ID && !Self::tag_is_allowed_multiple_times(typ))
|
|
|
+ {
|
|
|
+ // TODO return Result
|
|
|
+ panic!("Can't add tag of type {:?}. Only Module tags and Custom tags are allowed to appear multiple times.", T::ID);
|
|
|
+ }
|
|
|
+ self.0.push((T::ID, tag.as_bytes().to_vec()));
|
|
|
+ self.0.sort_by_key(|(typ, _)| *typ);
|
|
|
+ self
|
|
|
}
|
|
|
|
|
|
/// Adds a 'basic memory information' tag (represented by [`BasicMemoryInfoTag`]) to the builder.
|
|
|
- pub fn basic_memory_info_tag(mut self, basic_memory_info_tag: BasicMemoryInfoTag) -> Self {
|
|
|
- self.basic_memory_info_tag = Some(basic_memory_info_tag);
|
|
|
- self
|
|
|
+ pub fn basic_memory_info_tag(self, tag: BasicMemoryInfoTag) -> Self {
|
|
|
+ self.add_tag(&tag)
|
|
|
}
|
|
|
|
|
|
/// Adds a 'bootloader name' tag (represented by [`BootLoaderNameTag`]) to the builder.
|
|
|
- pub fn bootloader_name_tag(
|
|
|
- mut self,
|
|
|
- boot_loader_name_tag: BoxedDst<BootLoaderNameTag>,
|
|
|
- ) -> Self {
|
|
|
- self.boot_loader_name_tag = Some(boot_loader_name_tag);
|
|
|
- self
|
|
|
+ pub fn bootloader_name_tag(self, tag: BoxedDst<BootLoaderNameTag>) -> Self {
|
|
|
+ self.add_tag(&*tag)
|
|
|
}
|
|
|
|
|
|
/// Adds a 'command line' tag (represented by [`CommandLineTag`]) to the builder.
|
|
|
- pub fn command_line_tag(mut self, command_line_tag: BoxedDst<CommandLineTag>) -> Self {
|
|
|
- self.command_line_tag = Some(command_line_tag);
|
|
|
- self
|
|
|
+ pub fn command_line_tag(self, tag: BoxedDst<CommandLineTag>) -> Self {
|
|
|
+ self.add_tag(&*tag)
|
|
|
}
|
|
|
|
|
|
/// Adds a 'EFI 32-bit system table pointer' tag (represented by [`EFISdt32Tag`]) to the builder.
|
|
|
- pub fn efisdt32_tag(mut self, efisdt32: EFISdt32Tag) -> Self {
|
|
|
- self.efisdt32_tag = Some(efisdt32);
|
|
|
- self
|
|
|
+ pub fn efisdt32_tag(self, tag: EFISdt32Tag) -> Self {
|
|
|
+ self.add_tag(&tag)
|
|
|
}
|
|
|
|
|
|
/// Adds a 'EFI 64-bit system table pointer' tag (represented by [`EFISdt64Tag`]) to the builder.
|
|
|
- pub fn efisdt64_tag(mut self, efisdt64: EFISdt64Tag) -> Self {
|
|
|
- self.efisdt64_tag = Some(efisdt64);
|
|
|
- self
|
|
|
+ pub fn efisdt64_tag(self, tag: EFISdt64Tag) -> Self {
|
|
|
+ self.add_tag(&tag)
|
|
|
}
|
|
|
|
|
|
/// Adds a 'EFI boot services not terminated' tag (represented by [`EFIBootServicesNotExitedTag`]) to the builder.
|
|
|
- pub fn efi_boot_services_not_exited_tag(mut self) -> Self {
|
|
|
- self.efi_boot_services_not_exited_tag = Some(EFIBootServicesNotExitedTag::new());
|
|
|
- self
|
|
|
+ pub fn efi_boot_services_not_exited_tag(self) -> Self {
|
|
|
+ self.add_tag(&EFIBootServicesNotExitedTag::new())
|
|
|
}
|
|
|
|
|
|
/// Adds a 'EFI 32-bit image handle pointer' tag (represented by [`EFIImageHandle32Tag`]) to the builder.
|
|
|
- pub fn efi_image_handle32(mut self, efi_image_handle32: EFIImageHandle32Tag) -> Self {
|
|
|
- self.efi_image_handle32 = Some(efi_image_handle32);
|
|
|
- self
|
|
|
+ pub fn efi_image_handle32(self, tag: EFIImageHandle32Tag) -> Self {
|
|
|
+ self.add_tag(&tag)
|
|
|
}
|
|
|
|
|
|
/// Adds a 'EFI 64-bit image handle pointer' tag (represented by [`EFIImageHandle64Tag`]) to the builder.
|
|
|
- pub fn efi_image_handle64(mut self, efi_image_handle64: EFIImageHandle64Tag) -> Self {
|
|
|
- self.efi_image_handle64 = Some(efi_image_handle64);
|
|
|
- self
|
|
|
+ pub fn efi_image_handle64(self, tag: EFIImageHandle64Tag) -> Self {
|
|
|
+ self.add_tag(&tag)
|
|
|
}
|
|
|
|
|
|
/// Adds a 'EFI Memory map' tag (represented by [`EFIMemoryMapTag`]) to the builder.
|
|
|
- pub fn efi_memory_map_tag(mut self, efi_memory_map_tag: BoxedDst<EFIMemoryMapTag>) -> Self {
|
|
|
- self.efi_memory_map_tag = Some(efi_memory_map_tag);
|
|
|
- self
|
|
|
+ pub fn efi_memory_map_tag(self, tag: BoxedDst<EFIMemoryMapTag>) -> Self {
|
|
|
+ self.add_tag(&*tag)
|
|
|
}
|
|
|
|
|
|
/// Adds a 'ELF-Symbols' tag (represented by [`ElfSectionsTag`]) to the builder.
|
|
|
- pub fn elf_sections_tag(mut self, elf_sections_tag: BoxedDst<ElfSectionsTag>) -> Self {
|
|
|
- self.elf_sections_tag = Some(elf_sections_tag);
|
|
|
- self
|
|
|
+ pub fn elf_sections_tag(self, tag: BoxedDst<ElfSectionsTag>) -> Self {
|
|
|
+ self.add_tag(&*tag)
|
|
|
}
|
|
|
|
|
|
/// Adds a 'Framebuffer info' tag (represented by [`FramebufferTag`]) to the builder.
|
|
|
- pub fn framebuffer_tag(mut self, framebuffer_tag: BoxedDst<FramebufferTag>) -> Self {
|
|
|
- self.framebuffer_tag = Some(framebuffer_tag);
|
|
|
- self
|
|
|
+ pub fn framebuffer_tag(self, tag: BoxedDst<FramebufferTag>) -> Self {
|
|
|
+ self.add_tag(&*tag)
|
|
|
}
|
|
|
|
|
|
/// Adds a 'Image load base physical address' tag (represented by [`ImageLoadPhysAddrTag`]) to the builder.
|
|
|
- pub fn image_load_addr(mut self, image_load_addr: ImageLoadPhysAddrTag) -> Self {
|
|
|
- self.image_load_addr = Some(image_load_addr);
|
|
|
- self
|
|
|
+ pub fn image_load_addr(self, tag: ImageLoadPhysAddrTag) -> Self {
|
|
|
+ self.add_tag(&tag)
|
|
|
}
|
|
|
|
|
|
/// Adds a (*none EFI*) 'memory map' tag (represented by [`MemoryMapTag`]) to the builder.
|
|
|
- pub fn memory_map_tag(mut self, memory_map_tag: BoxedDst<MemoryMapTag>) -> Self {
|
|
|
- self.memory_map_tag = Some(memory_map_tag);
|
|
|
- self
|
|
|
+ pub fn memory_map_tag(self, tag: BoxedDst<MemoryMapTag>) -> Self {
|
|
|
+ self.add_tag(&*tag)
|
|
|
}
|
|
|
|
|
|
/// Adds a 'Modules' tag (represented by [`ModuleTag`]) to the builder.
|
|
|
/// This tag can occur multiple times in boot information.
|
|
|
- pub fn add_module_tag(mut self, module_tag: BoxedDst<ModuleTag>) -> Self {
|
|
|
- self.module_tags.push(module_tag);
|
|
|
- self
|
|
|
+ pub fn add_module_tag(self, tag: BoxedDst<ModuleTag>) -> Self {
|
|
|
+ self.add_tag(&*tag)
|
|
|
}
|
|
|
|
|
|
/// Adds a 'ACPI old RSDP' tag (represented by [`RsdpV1Tag`]) to the builder.
|
|
|
- pub fn rsdp_v1_tag(mut self, rsdp_v1_tag: RsdpV1Tag) -> Self {
|
|
|
- self.rsdp_v1_tag = Some(rsdp_v1_tag);
|
|
|
- self
|
|
|
+ pub fn rsdp_v1_tag(self, tag: RsdpV1Tag) -> Self {
|
|
|
+ self.add_tag(&tag)
|
|
|
}
|
|
|
|
|
|
/// Adds a 'ACPI new RSDP' tag (represented by [`RsdpV2Tag`]) to the builder.
|
|
|
- pub fn rsdp_v2_tag(mut self, rsdp_v2_tag: RsdpV2Tag) -> Self {
|
|
|
- self.rsdp_v2_tag = Some(rsdp_v2_tag);
|
|
|
- self
|
|
|
+ pub fn rsdp_v2_tag(self, tag: RsdpV2Tag) -> Self {
|
|
|
+ self.add_tag(&tag)
|
|
|
}
|
|
|
|
|
|
/// Adds a 'SMBIOS tables' tag (represented by [`SmbiosTag`]) to the builder.
|
|
|
- pub fn add_smbios_tag(mut self, smbios_tag: BoxedDst<SmbiosTag>) -> Self {
|
|
|
- self.smbios_tags.push(smbios_tag);
|
|
|
- self
|
|
|
+ pub fn add_smbios_tag(self, tag: BoxedDst<SmbiosTag>) -> Self {
|
|
|
+ self.add_tag(&*tag)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn tag_is_allowed_multiple_times(tag_type: TagType) -> bool {
|
|
|
+ matches!(
|
|
|
+ tag_type,
|
|
|
+ TagType::Module | TagType::Smbios | TagType::Custom(_)
|
|
|
+ )
|
|
|
}
|
|
|
}
|
|
|
|