multiboot2/
builder.rs

1//! Module for [`Builder`].
2
3use crate::apm::ApmTag;
4use crate::bootdev::BootdevTag;
5use crate::network::NetworkTag;
6use crate::{
7    BasicMemoryInfoTag, BootInformationHeader, BootLoaderNameTag, CommandLineTag,
8    EFIBootServicesNotExitedTag, EFIImageHandle32Tag, EFIImageHandle64Tag, EFIMemoryMapTag,
9    EFISdt32Tag, EFISdt64Tag, ElfSectionsTag, EndTag, FramebufferTag, ImageLoadPhysAddrTag,
10    MemoryMapTag, ModuleTag, RsdpV1Tag, RsdpV2Tag, SmbiosTag, TagHeader, TagType, VBEInfoTag,
11};
12use alloc::boxed::Box;
13use alloc::vec::Vec;
14use multiboot2_common::{DynSizedStructure, MaybeDynSized, new_boxed};
15
16/// Builder for a Multiboot2 header information.
17// #[derive(Debug)]
18#[derive(Debug)]
19pub struct Builder {
20    cmdline: Option<Box<CommandLineTag>>,
21    bootloader: Option<Box<BootLoaderNameTag>>,
22    modules: Vec<Box<ModuleTag>>,
23    meminfo: Option<BasicMemoryInfoTag>,
24    bootdev: Option<BootdevTag>,
25    mmap: Option<Box<MemoryMapTag>>,
26    vbe: Option<VBEInfoTag>,
27    framebuffer: Option<Box<FramebufferTag>>,
28    elf_sections: Option<Box<ElfSectionsTag>>,
29    apm: Option<ApmTag>,
30    efi32: Option<EFISdt32Tag>,
31    efi64: Option<EFISdt64Tag>,
32    smbios: Vec<Box<SmbiosTag>>,
33    rsdpv1: Option<RsdpV1Tag>,
34    rsdpv2: Option<RsdpV2Tag>,
35    network: Option<Box<NetworkTag>>,
36    efi_mmap: Option<Box<EFIMemoryMapTag>>,
37    efi_bs: Option<EFIBootServicesNotExitedTag>,
38    efi32_ih: Option<EFIImageHandle32Tag>,
39    efi64_ih: Option<EFIImageHandle64Tag>,
40    image_load_addr: Option<ImageLoadPhysAddrTag>,
41    custom_tags: Vec<Box<DynSizedStructure<TagHeader>>>,
42}
43
44impl Default for Builder {
45    fn default() -> Self {
46        Self::new()
47    }
48}
49
50impl Builder {
51    /// Creates a new builder.
52    #[must_use]
53    pub const fn new() -> Self {
54        Self {
55            cmdline: None,
56            bootloader: None,
57            modules: vec![],
58            meminfo: None,
59            bootdev: None,
60            mmap: None,
61            vbe: None,
62            framebuffer: None,
63            elf_sections: None,
64            apm: None,
65            efi32: None,
66            efi64: None,
67            smbios: vec![],
68            rsdpv1: None,
69            rsdpv2: None,
70            efi_mmap: None,
71            network: None,
72            efi_bs: None,
73            efi32_ih: None,
74            efi64_ih: None,
75            image_load_addr: None,
76            custom_tags: vec![],
77        }
78    }
79
80    /// Sets the [`CommandLineTag`] tag.
81    #[must_use]
82    pub fn cmdline(mut self, cmdline: Box<CommandLineTag>) -> Self {
83        self.cmdline = Some(cmdline);
84        self
85    }
86
87    /// Sets the [`BootLoaderNameTag`] tag.
88    #[must_use]
89    pub fn bootloader(mut self, bootloader: Box<BootLoaderNameTag>) -> Self {
90        self.bootloader = Some(bootloader);
91        self
92    }
93
94    /// Adds a [`ModuleTag`] tag.
95    #[must_use]
96    pub fn add_module(mut self, module: Box<ModuleTag>) -> Self {
97        self.modules.push(module);
98        self
99    }
100
101    /// Sets the [`BasicMemoryInfoTag`] tag.
102    #[must_use]
103    pub const fn meminfo(mut self, meminfo: BasicMemoryInfoTag) -> Self {
104        self.meminfo = Some(meminfo);
105        self
106    }
107
108    /// Sets the [`BootdevTag`] tag.
109    #[must_use]
110    pub const fn bootdev(mut self, bootdev: BootdevTag) -> Self {
111        self.bootdev = Some(bootdev);
112        self
113    }
114
115    /// Sets the [`MemoryMapTag`] tag.
116    #[must_use]
117    pub fn mmap(mut self, mmap: Box<MemoryMapTag>) -> Self {
118        self.mmap = Some(mmap);
119        self
120    }
121
122    /// Sets the [`VBEInfoTag`] tag.
123    #[must_use]
124    pub const fn vbe(mut self, vbe: VBEInfoTag) -> Self {
125        self.vbe = Some(vbe);
126        self
127    }
128
129    /// Sets the [`FramebufferTag`] tag.
130    #[must_use]
131    pub fn framebuffer(mut self, framebuffer: Box<FramebufferTag>) -> Self {
132        self.framebuffer = Some(framebuffer);
133        self
134    }
135
136    /// Sets the [`ElfSectionsTag`] tag.
137    #[must_use]
138    pub fn elf_sections(mut self, elf_sections: Box<ElfSectionsTag>) -> Self {
139        self.elf_sections = Some(elf_sections);
140        self
141    }
142
143    /// Sets the [`ApmTag`] tag.
144    #[must_use]
145    pub const fn apm(mut self, apm: ApmTag) -> Self {
146        self.apm = Some(apm);
147        self
148    }
149
150    /// Sets the [`EFISdt32Tag`] tag.
151    #[must_use]
152    pub const fn efi32(mut self, efi32: EFISdt32Tag) -> Self {
153        self.efi32 = Some(efi32);
154        self
155    }
156
157    /// Sets the [`EFISdt64Tag`] tag.
158    #[must_use]
159    pub const fn efi64(mut self, efi64: EFISdt64Tag) -> Self {
160        self.efi64 = Some(efi64);
161        self
162    }
163
164    /// Adds a [`SmbiosTag`] tag.
165    #[must_use]
166    pub fn add_smbios(mut self, smbios: Box<SmbiosTag>) -> Self {
167        self.smbios.push(smbios);
168        self
169    }
170
171    /// Sets the [`RsdpV1Tag`] tag.
172    #[must_use]
173    pub const fn rsdpv1(mut self, rsdpv1: RsdpV1Tag) -> Self {
174        self.rsdpv1 = Some(rsdpv1);
175        self
176    }
177
178    /// Sets the [`RsdpV2Tag`] tag.
179    #[must_use]
180    pub const fn rsdpv2(mut self, rsdpv2: RsdpV2Tag) -> Self {
181        self.rsdpv2 = Some(rsdpv2);
182        self
183    }
184
185    /// Sets the [`EFIMemoryMapTag`] tag.
186    #[must_use]
187    pub fn efi_mmap(mut self, efi_mmap: Box<EFIMemoryMapTag>) -> Self {
188        self.efi_mmap = Some(efi_mmap);
189        self
190    }
191
192    /// Sets the [`NetworkTag`] tag.
193    #[must_use]
194    pub fn network(mut self, network: Box<NetworkTag>) -> Self {
195        self.network = Some(network);
196        self
197    }
198
199    /// Sets the [`EFIBootServicesNotExitedTag`] tag.
200    #[must_use]
201    pub const fn efi_bs(mut self, efi_bs: EFIBootServicesNotExitedTag) -> Self {
202        self.efi_bs = Some(efi_bs);
203        self
204    }
205
206    /// Sets the [`EFIImageHandle32Tag`] tag.
207    #[must_use]
208    pub const fn efi32_ih(mut self, efi32_ih: EFIImageHandle32Tag) -> Self {
209        self.efi32_ih = Some(efi32_ih);
210        self
211    }
212
213    /// Sets the [`EFIImageHandle64Tag`] tag.
214    #[must_use]
215    pub const fn efi64_ih(mut self, efi64_ih: EFIImageHandle64Tag) -> Self {
216        self.efi64_ih = Some(efi64_ih);
217        self
218    }
219
220    /// Sets the [`ImageLoadPhysAddrTag`] tag.
221    #[must_use]
222    pub const fn image_load_addr(mut self, image_load_addr: ImageLoadPhysAddrTag) -> Self {
223        self.image_load_addr = Some(image_load_addr);
224        self
225    }
226
227    /// Adds a custom tag.
228    #[must_use]
229    pub fn add_custom_tag(mut self, custom_tag: Box<DynSizedStructure<TagHeader>>) -> Self {
230        if let TagType::Custom(_c) = custom_tag.header().typ.into() {
231            self.custom_tags.push(custom_tag);
232        } else {
233            panic!("Only for custom types!");
234        }
235        self
236    }
237
238    /// Returns properly aligned bytes on the heap representing a valid
239    /// Multiboot2 header structure.
240    #[must_use]
241    pub fn build(self) -> Box<DynSizedStructure<BootInformationHeader>> {
242        let header = BootInformationHeader::new(0);
243        let mut byte_refs = Vec::new();
244        if let Some(tag) = self.cmdline.as_ref() {
245            byte_refs.push(tag.as_bytes().as_ref());
246        }
247        if let Some(tag) = self.bootloader.as_ref() {
248            byte_refs.push(tag.as_bytes().as_ref());
249        }
250        for i in &self.modules {
251            byte_refs.push(i.as_bytes().as_ref());
252        }
253        if let Some(tag) = self.meminfo.as_ref() {
254            byte_refs.push(tag.as_bytes().as_ref());
255        }
256        if let Some(tag) = self.bootdev.as_ref() {
257            byte_refs.push(tag.as_bytes().as_ref());
258        }
259        if let Some(tag) = self.mmap.as_ref() {
260            byte_refs.push(tag.as_bytes().as_ref());
261        }
262        if let Some(tag) = self.vbe.as_ref() {
263            byte_refs.push(tag.as_bytes().as_ref());
264        }
265        if let Some(tag) = self.framebuffer.as_ref() {
266            byte_refs.push(tag.as_bytes().as_ref());
267        }
268        if let Some(tag) = self.elf_sections.as_ref() {
269            byte_refs.push(tag.as_bytes().as_ref());
270        }
271        if let Some(tag) = self.apm.as_ref() {
272            byte_refs.push(tag.as_bytes().as_ref());
273        }
274        if let Some(tag) = self.efi32.as_ref() {
275            byte_refs.push(tag.as_bytes().as_ref());
276        }
277        if let Some(tag) = self.efi64.as_ref() {
278            byte_refs.push(tag.as_bytes().as_ref());
279        }
280        for i in &self.smbios {
281            byte_refs.push(i.as_bytes().as_ref());
282        }
283        if let Some(tag) = self.rsdpv1.as_ref() {
284            byte_refs.push(tag.as_bytes().as_ref());
285        }
286        if let Some(tag) = self.rsdpv2.as_ref() {
287            byte_refs.push(tag.as_bytes().as_ref());
288        }
289        if let Some(tag) = self.efi_mmap.as_ref() {
290            byte_refs.push(tag.as_bytes().as_ref());
291        }
292        if let Some(tag) = self.efi_bs.as_ref() {
293            byte_refs.push(tag.as_bytes().as_ref());
294        }
295        if let Some(tag) = self.efi32_ih.as_ref() {
296            byte_refs.push(tag.as_bytes().as_ref());
297        }
298        if let Some(tag) = self.efi64_ih.as_ref() {
299            byte_refs.push(tag.as_bytes().as_ref());
300        }
301        if let Some(tag) = self.image_load_addr.as_ref() {
302            byte_refs.push(tag.as_bytes().as_ref());
303        }
304        for i in &self.custom_tags {
305            byte_refs.push(i.as_bytes().as_ref());
306        }
307        let end_tag = EndTag::default();
308        byte_refs.push(end_tag.as_bytes().as_ref());
309        new_boxed(header, byte_refs.as_slice())
310    }
311}
312
313#[cfg(test)]
314mod tests {
315    use super::*;
316    use crate::{
317        BootInformation, FramebufferType, MemoryArea, MemoryAreaType, VBEControlInfo, VBEModeInfo,
318    };
319    use uefi_raw::table::boot::MemoryDescriptor;
320
321    #[test]
322    fn build_and_parse() {
323        let builder = Builder::new()
324            .cmdline(CommandLineTag::new("this is a command line"))
325            .bootloader(BootLoaderNameTag::new("this is the bootloader"))
326            .add_module(ModuleTag::new(0x1000, 0x2000, "module 1"))
327            .add_module(ModuleTag::new(0x3000, 0x4000, "module 2"))
328            .meminfo(BasicMemoryInfoTag::new(0x4000, 0x5000))
329            .bootdev(BootdevTag::new(0x00, 0x00, 0x00))
330            .mmap(MemoryMapTag::new(&[MemoryArea::new(
331                0x1000000,
332                0x1000,
333                MemoryAreaType::Available,
334            )]))
335            .vbe(VBEInfoTag::new(
336                42,
337                2,
338                4,
339                9,
340                VBEControlInfo::default(),
341                VBEModeInfo::default(),
342            ))
343            // Currently causes UB.
344            .framebuffer(FramebufferTag::new(
345                0x1000,
346                1,
347                756,
348                1024,
349                8,
350                FramebufferType::Text,
351            ))
352            .elf_sections(ElfSectionsTag::new(0, 32, 0, &[]))
353            .apm(ApmTag::new(0, 0, 0, 0, 0, 0, 0, 0, 0))
354            .efi32(EFISdt32Tag::new(0x1000))
355            .efi64(EFISdt64Tag::new(0x1000))
356            .add_smbios(SmbiosTag::new(0, 0, &[1, 2, 3]))
357            .add_smbios(SmbiosTag::new(1, 1, &[4, 5, 6]))
358            .rsdpv1(RsdpV1Tag::new(0, *b"abcdef", 5, 6))
359            .rsdpv2(RsdpV2Tag::new(0, *b"abcdef", 5, 6, 5, 4, 7))
360            .efi_mmap(EFIMemoryMapTag::new_from_descs(&[
361                MemoryDescriptor::default(),
362                MemoryDescriptor::default(),
363            ]))
364            .network(NetworkTag::new(&[0; 1500]))
365            .efi_bs(EFIBootServicesNotExitedTag::new())
366            .efi32_ih(EFIImageHandle32Tag::new(0x1000))
367            .efi64_ih(EFIImageHandle64Tag::new(0x1000))
368            .image_load_addr(ImageLoadPhysAddrTag::new(0x1000))
369            .add_custom_tag(new_boxed::<DynSizedStructure<TagHeader>>(
370                TagHeader::new(TagType::Custom(0x1337), 0),
371                &[],
372            ));
373
374        let structure = builder.build();
375
376        let info = unsafe { BootInformation::load(structure.as_bytes().as_ptr().cast()) }.unwrap();
377        for tag in info.tags() {
378            // Mainly a test for Miri.
379            dbg!(tag.header(), tag.payload().len());
380        }
381        eprintln!("{info:#x?}")
382    }
383}