use crate::builder::information_request::InformationRequestHeaderTagBuilder;
use crate::builder::traits::StructAsBytes;
use crate::HeaderTagISA;
use crate::{
AddressHeaderTag, ConsoleHeaderTag, EfiBootServiceHeaderTag, EndHeaderTag,
EntryAddressHeaderTag, EntryEfi32HeaderTag, EntryEfi64HeaderTag, FramebufferHeaderTag,
ModuleAlignHeaderTag, Multiboot2BasicHeader, RelocatableHeaderTag,
};
use alloc::vec::Vec;
use core::mem::size_of;
#[derive(Debug)]
pub struct Multiboot2HeaderBuilder {
arch: HeaderTagISA,
information_request_tag: Option<InformationRequestHeaderTagBuilder>,
address_tag: Option<AddressHeaderTag>,
entry_tag: Option<EntryAddressHeaderTag>,
console_tag: Option<ConsoleHeaderTag>,
framebuffer_tag: Option<FramebufferHeaderTag>,
module_align_tag: Option<ModuleAlignHeaderTag>,
efi_bs_tag: Option<EfiBootServiceHeaderTag>,
efi_32_tag: Option<EntryEfi32HeaderTag>,
efi_64_tag: Option<EntryEfi64HeaderTag>,
relocatable_tag: Option<RelocatableHeaderTag>,
}
impl Multiboot2HeaderBuilder {
pub const fn new(arch: HeaderTagISA) -> Self {
Self {
arch,
information_request_tag: None,
address_tag: None,
entry_tag: None,
console_tag: None,
framebuffer_tag: None,
module_align_tag: None,
efi_bs_tag: None,
efi_32_tag: None,
efi_64_tag: None,
relocatable_tag: None,
}
}
const fn size_or_up_aligned(size: usize) -> usize {
let remainder = size % 8;
if remainder == 0 {
size
} else {
size + 8 - remainder
}
}
pub fn expected_len(&self) -> usize {
let base_len = size_of::<Multiboot2BasicHeader>();
let mut len = Self::size_or_up_aligned(base_len);
if let Some(tag_builder) = self.information_request_tag.as_ref() {
len += Self::size_or_up_aligned(tag_builder.expected_len())
}
if self.address_tag.is_some() {
len += Self::size_or_up_aligned(size_of::<AddressHeaderTag>())
}
if self.entry_tag.is_some() {
len += Self::size_or_up_aligned(size_of::<EntryAddressHeaderTag>())
}
if self.console_tag.is_some() {
len += Self::size_or_up_aligned(size_of::<ConsoleHeaderTag>())
}
if self.framebuffer_tag.is_some() {
len += Self::size_or_up_aligned(size_of::<FramebufferHeaderTag>())
}
if self.module_align_tag.is_some() {
len += Self::size_or_up_aligned(size_of::<ModuleAlignHeaderTag>())
}
if self.efi_bs_tag.is_some() {
len += Self::size_or_up_aligned(size_of::<EfiBootServiceHeaderTag>())
}
if self.efi_32_tag.is_some() {
len += Self::size_or_up_aligned(size_of::<EntryEfi32HeaderTag>())
}
if self.efi_64_tag.is_some() {
len += Self::size_or_up_aligned(size_of::<EntryEfi64HeaderTag>())
}
if self.relocatable_tag.is_some() {
len += Self::size_or_up_aligned(size_of::<RelocatableHeaderTag>())
}
len += size_of::<EndHeaderTag>();
len
}
fn build_add_bytes(dest: &mut Vec<u8>, source: &[u8], is_end_tag: bool) {
dest.extend(source);
if !is_end_tag {
let size = source.len();
let size_to_8_align = Self::size_or_up_aligned(size);
let size_to_8_align_diff = size_to_8_align - size;
dest.extend([0].repeat(size_to_8_align_diff));
}
}
pub fn build(mut self) -> Vec<u8> {
let mut data = Vec::new();
Self::build_add_bytes(
&mut data,
&Multiboot2BasicHeader::new(self.arch, self.expected_len() as u32).struct_as_bytes(),
false,
);
if self.information_request_tag.is_some() {
Self::build_add_bytes(
&mut data,
&self.information_request_tag.take().unwrap().build(),
false,
)
}
if let Some(tag) = self.address_tag.as_ref() {
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
}
if let Some(tag) = self.entry_tag.as_ref() {
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
}
if let Some(tag) = self.console_tag.as_ref() {
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
}
if let Some(tag) = self.framebuffer_tag.as_ref() {
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
}
if let Some(tag) = self.module_align_tag.as_ref() {
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
}
if let Some(tag) = self.efi_bs_tag.as_ref() {
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
}
if let Some(tag) = self.efi_32_tag.as_ref() {
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
}
if let Some(tag) = self.efi_64_tag.as_ref() {
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
}
if let Some(tag) = self.relocatable_tag.as_ref() {
Self::build_add_bytes(&mut data, &tag.struct_as_bytes(), false)
}
Self::build_add_bytes(&mut data, &EndHeaderTag::new().struct_as_bytes(), true);
data
}
#[allow(clippy::missing_const_for_fn)]
pub fn information_request_tag(
mut self,
information_request_tag: InformationRequestHeaderTagBuilder,
) -> Self {
self.information_request_tag = Some(information_request_tag);
self
}
pub const fn address_tag(mut self, address_tag: AddressHeaderTag) -> Self {
self.address_tag = Some(address_tag);
self
}
pub const fn entry_tag(mut self, entry_tag: EntryAddressHeaderTag) -> Self {
self.entry_tag = Some(entry_tag);
self
}
pub const fn console_tag(mut self, console_tag: ConsoleHeaderTag) -> Self {
self.console_tag = Some(console_tag);
self
}
pub const fn framebuffer_tag(mut self, framebuffer_tag: FramebufferHeaderTag) -> Self {
self.framebuffer_tag = Some(framebuffer_tag);
self
}
pub const fn module_align_tag(mut self, module_align_tag: ModuleAlignHeaderTag) -> Self {
self.module_align_tag = Some(module_align_tag);
self
}
pub const fn efi_bs_tag(mut self, efi_bs_tag: EfiBootServiceHeaderTag) -> Self {
self.efi_bs_tag = Some(efi_bs_tag);
self
}
pub const fn efi_32_tag(mut self, efi_32_tag: EntryEfi32HeaderTag) -> Self {
self.efi_32_tag = Some(efi_32_tag);
self
}
pub const fn efi_64_tag(mut self, efi_64_tag: EntryEfi64HeaderTag) -> Self {
self.efi_64_tag = Some(efi_64_tag);
self
}
pub const fn relocatable_tag(mut self, relocatable_tag: RelocatableHeaderTag) -> Self {
self.relocatable_tag = Some(relocatable_tag);
self
}
}
#[cfg(test)]
mod tests {
use crate::builder::header::Multiboot2HeaderBuilder;
use crate::builder::information_request::InformationRequestHeaderTagBuilder;
use crate::{
HeaderTagFlag, HeaderTagISA, MbiTagType, Multiboot2Header, RelocatableHeaderTag,
RelocatableHeaderTagPreference,
};
#[test]
fn test_size_or_up_aligned() {
assert_eq!(0, Multiboot2HeaderBuilder::size_or_up_aligned(0));
assert_eq!(8, Multiboot2HeaderBuilder::size_or_up_aligned(1));
assert_eq!(8, Multiboot2HeaderBuilder::size_or_up_aligned(8));
assert_eq!(16, Multiboot2HeaderBuilder::size_or_up_aligned(9));
}
#[test]
fn test_size_builder() {
let builder = Multiboot2HeaderBuilder::new(HeaderTagISA::I386);
let mut expected_len = 16 + 8;
assert_eq!(builder.expected_len(), expected_len);
let ifr_builder =
InformationRequestHeaderTagBuilder::new(HeaderTagFlag::Required).add_irs(&[
MbiTagType::EfiMmap,
MbiTagType::Cmdline,
MbiTagType::ElfSections,
]);
let ifr_tag_size_with_padding = ifr_builder.expected_len() + 4;
assert_eq!(
ifr_tag_size_with_padding % 8,
0,
"the length of the IFR tag with padding must be a multiple of 8"
);
expected_len += ifr_tag_size_with_padding;
let builder = builder.information_request_tag(ifr_builder);
assert_eq!(builder.expected_len(), expected_len);
let builder = builder.relocatable_tag(RelocatableHeaderTag::new(
HeaderTagFlag::Required,
0x1337,
0xdeadbeef,
4096,
RelocatableHeaderTagPreference::None,
));
println!("builder: {:#?}", builder);
println!("expected_len: {} bytes", builder.expected_len());
let mb2_hdr_data = builder.build();
let mb2_hdr = mb2_hdr_data.as_ptr() as usize;
let mb2_hdr = unsafe { Multiboot2Header::from_addr(mb2_hdr) };
println!("{:#?}", mb2_hdr);
}
}