Skip to main content

multiboot2_header/
builder.rs

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