multiboot2_header/
builder.rs

1//! Exports a builder [`Builder`].
2
3use crate::{
4    AddressHeaderTag, ConsoleHeaderTag, EfiBootServiceHeaderTag, EntryAddressHeaderTag,
5    EntryEfi32HeaderTag, EntryEfi64HeaderTag, FramebufferHeaderTag, HeaderTagISA,
6    InformationRequestHeaderTag, ModuleAlignHeaderTag, Multiboot2BasicHeader, RelocatableHeaderTag,
7};
8use alloc::boxed::Box;
9use alloc::vec::Vec;
10use multiboot2_common::{DynSizedStructure, MaybeDynSized, new_boxed};
11
12/// Builder for a Multiboot2 header information.
13#[derive(Debug)]
14pub struct Builder {
15    arch: HeaderTagISA,
16    information_request_tag: Option<Box<InformationRequestHeaderTag>>,
17    address_tag: Option<AddressHeaderTag>,
18    entry_tag: Option<EntryAddressHeaderTag>,
19    console_tag: Option<ConsoleHeaderTag>,
20    framebuffer_tag: Option<FramebufferHeaderTag>,
21    module_align_tag: Option<ModuleAlignHeaderTag>,
22    efi_bs_tag: Option<EfiBootServiceHeaderTag>,
23    efi_32_tag: Option<EntryEfi32HeaderTag>,
24    efi_64_tag: Option<EntryEfi64HeaderTag>,
25    relocatable_tag: Option<RelocatableHeaderTag>,
26    // TODO add support for custom tags once someone requests it.
27}
28
29impl Builder {
30    /// Set the [`RelocatableHeaderTag`] tag.
31    #[must_use]
32    pub const fn new(arch: HeaderTagISA) -> Self {
33        Self {
34            arch,
35            information_request_tag: None,
36            address_tag: None,
37            entry_tag: None,
38            console_tag: None,
39            framebuffer_tag: None,
40            module_align_tag: None,
41            efi_bs_tag: None,
42            efi_32_tag: None,
43            efi_64_tag: None,
44            relocatable_tag: None,
45        }
46    }
47
48    /// Set the [`InformationRequestHeaderTag`] tag.
49    #[must_use]
50    pub fn information_request_tag(
51        mut self,
52        information_request_tag: Box<InformationRequestHeaderTag>,
53    ) -> Self {
54        self.information_request_tag = Some(information_request_tag);
55        self
56    }
57
58    /// Set the [`AddressHeaderTag`] tag.
59    #[must_use]
60    pub const fn address_tag(mut self, address_tag: AddressHeaderTag) -> Self {
61        self.address_tag = Some(address_tag);
62        self
63    }
64
65    /// Set the [`EntryAddressHeaderTag`] tag.
66    #[must_use]
67    pub const fn entry_tag(mut self, entry_tag: EntryAddressHeaderTag) -> Self {
68        self.entry_tag = Some(entry_tag);
69        self
70    }
71
72    /// Set the [`ConsoleHeaderTag`] tag.
73    #[must_use]
74    pub const fn console_tag(mut self, console_tag: ConsoleHeaderTag) -> Self {
75        self.console_tag = Some(console_tag);
76        self
77    }
78
79    /// Set the [`FramebufferHeaderTag`] tag.
80    #[must_use]
81    pub const fn framebuffer_tag(mut self, framebuffer_tag: FramebufferHeaderTag) -> Self {
82        self.framebuffer_tag = Some(framebuffer_tag);
83        self
84    }
85
86    /// Set the [`ModuleAlignHeaderTag`] tag.
87    #[must_use]
88    pub const fn module_align_tag(mut self, module_align_tag: ModuleAlignHeaderTag) -> Self {
89        self.module_align_tag = Some(module_align_tag);
90        self
91    }
92
93    /// Set the [`EfiBootServiceHeaderTag`] tag.
94    #[must_use]
95    pub const fn efi_bs_tag(mut self, efi_bs_tag: EfiBootServiceHeaderTag) -> Self {
96        self.efi_bs_tag = Some(efi_bs_tag);
97        self
98    }
99
100    /// Set the [`EntryEfi32HeaderTag`] tag.
101    #[must_use]
102    pub const fn efi_32_tag(mut self, efi_32_tag: EntryEfi32HeaderTag) -> Self {
103        self.efi_32_tag = Some(efi_32_tag);
104        self
105    }
106
107    /// Set the [`EntryEfi64HeaderTag`] tag.
108    #[must_use]
109    pub const fn efi_64_tag(mut self, efi_64_tag: EntryEfi64HeaderTag) -> Self {
110        self.efi_64_tag = Some(efi_64_tag);
111        self
112    }
113
114    /// Set the [`RelocatableHeaderTag`] tag.
115    #[must_use]
116    pub const fn relocatable_tag(mut self, relocatable_tag: RelocatableHeaderTag) -> Self {
117        self.relocatable_tag = Some(relocatable_tag);
118        self
119    }
120
121    /// Returns properly aligned bytes on the heap representing a valid
122    /// Multiboot2 header structure.
123    #[must_use]
124    pub fn build(self) -> Box<DynSizedStructure<Multiboot2BasicHeader>> {
125        let header = Multiboot2BasicHeader::new(self.arch, 0);
126        let mut byte_refs = Vec::new();
127        if let Some(tag) = self.information_request_tag.as_ref() {
128            byte_refs.push(tag.as_bytes().as_ref());
129        }
130        if let Some(tag) = self.address_tag.as_ref() {
131            byte_refs.push(tag.as_bytes().as_ref());
132        }
133        if let Some(tag) = self.entry_tag.as_ref() {
134            byte_refs.push(tag.as_bytes().as_ref());
135        }
136        if let Some(tag) = self.console_tag.as_ref() {
137            byte_refs.push(tag.as_bytes().as_ref());
138        }
139        if let Some(tag) = self.framebuffer_tag.as_ref() {
140            byte_refs.push(tag.as_bytes().as_ref());
141        }
142        if let Some(tag) = self.module_align_tag.as_ref() {
143            byte_refs.push(tag.as_bytes().as_ref());
144        }
145        if let Some(tag) = self.efi_bs_tag.as_ref() {
146            byte_refs.push(tag.as_bytes().as_ref());
147        }
148        if let Some(tag) = self.efi_32_tag.as_ref() {
149            byte_refs.push(tag.as_bytes().as_ref());
150        }
151        if let Some(tag) = self.efi_64_tag.as_ref() {
152            byte_refs.push(tag.as_bytes().as_ref());
153        }
154        if let Some(tag) = self.relocatable_tag.as_ref() {
155            byte_refs.push(tag.as_bytes().as_ref());
156        }
157        // TODO add support for custom tags once someone requests it.
158        new_boxed(header, byte_refs.as_slice())
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165    use crate::ConsoleHeaderTagFlags::ConsoleRequired;
166    use crate::HeaderTagFlag::{Optional, Required};
167    use crate::RelocatableHeaderTagPreference::High;
168    use crate::{MbiTagType, Multiboot2Header};
169
170    #[test]
171    fn build_and_parse() {
172        let builder = Builder::new(HeaderTagISA::I386)
173            .information_request_tag(InformationRequestHeaderTag::new(
174                Optional,
175                &[
176                    MbiTagType::Cmdline.into(),
177                    MbiTagType::BootLoaderName.into(),
178                    MbiTagType::Module.into(),
179                    MbiTagType::BasicMeminfo.into(),
180                    MbiTagType::Bootdev.into(),
181                    MbiTagType::Mmap.into(),
182                    MbiTagType::Vbe.into(),
183                    MbiTagType::Framebuffer.into(),
184                    MbiTagType::ElfSections.into(),
185                    MbiTagType::Apm.into(),
186                    MbiTagType::Efi32.into(),
187                    MbiTagType::Efi64.into(),
188                    MbiTagType::Smbios.into(),
189                    MbiTagType::AcpiV1.into(),
190                    MbiTagType::AcpiV2.into(),
191                    MbiTagType::Network.into(),
192                    MbiTagType::EfiMmap.into(),
193                    MbiTagType::EfiBs.into(),
194                    MbiTagType::Efi32Ih.into(),
195                    MbiTagType::Efi64Ih.into(),
196                    MbiTagType::LoadBaseAddr.into(),
197                    MbiTagType::Custom(0x1337).into(),
198                ],
199            ))
200            .address_tag(AddressHeaderTag::new(
201                Required, 0x1000, 0x2000, 0x3000, 0x4000,
202            ))
203            .entry_tag(EntryAddressHeaderTag::new(Required, 0x5000))
204            .console_tag(ConsoleHeaderTag::new(Required, ConsoleRequired))
205            .framebuffer_tag(FramebufferHeaderTag::new(Optional, 720, 1024, 8))
206            .module_align_tag(ModuleAlignHeaderTag::new(Required))
207            .efi_bs_tag(EfiBootServiceHeaderTag::new(Optional))
208            .efi_32_tag(EntryEfi32HeaderTag::new(Required, 0x7000))
209            .efi_64_tag(EntryEfi64HeaderTag::new(Required, 0x8000))
210            .relocatable_tag(RelocatableHeaderTag::new(
211                Required, 0x9000, 0x10000, 4096, High,
212            ));
213
214        let structure = builder.build();
215        let header =
216            unsafe { Multiboot2Header::load(structure.as_bytes().as_ref().as_ptr().cast()) }
217                .unwrap();
218
219        assert!(header.verify_checksum());
220
221        for tag in header.iter() {
222            dbg!(tag);
223        }
224
225        dbg!(header.arch());
226        dbg!(header.checksum());
227        dbg!(header.information_request_tag());
228        dbg!(header.address_tag());
229        dbg!(header.entry_address_tag());
230        dbg!(header.console_flags_tag());
231        dbg!(header.framebuffer_tag());
232        dbg!(header.module_align_tag());
233        dbg!(header.efi_boot_services_tag());
234        dbg!(header.entry_address_efi32_tag());
235        dbg!(header.entry_address_efi64_tag());
236        dbg!(header.relocatable_tag());
237    }
238}