Skip to main content

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