multiboot2/
boot_information.rs

1//! Module for [`BootInformation`].
2
3use crate::framebuffer::UnknownFramebufferType;
4use crate::tag::TagHeader;
5use crate::{
6    ApmTag, BasicMemoryInfoTag, BootLoaderNameTag, BootdevTag, CommandLineTag,
7    EFIBootServicesNotExitedTag, EFIImageHandle32Tag, EFIImageHandle64Tag, EFIMemoryMapTag,
8    EFISdt32Tag, EFISdt64Tag, ElfSectionIter, ElfSectionsTag, EndTag, FramebufferTag,
9    ImageLoadPhysAddrTag, MemoryMapTag, ModuleIter, NetworkTag, RsdpV1Tag, RsdpV2Tag, SmbiosTag,
10    TagIter, TagType, VBEInfoTag, module,
11};
12use core::fmt;
13use core::mem;
14use core::ptr::NonNull;
15use multiboot2_common::{DynSizedStructure, Header, MaybeDynSized, MemoryError, Tag};
16use thiserror::Error;
17
18/// Errors that occur when a chunk of memory can't be parsed as
19/// [`BootInformation`].
20#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Error)]
21pub enum LoadError {
22    /// The provided memory can't be parsed as [`BootInformation`].
23    /// See [`MemoryError`].
24    #[error("memory can't be parsed as boot information")]
25    Memory(#[source] MemoryError),
26    /// Missing mandatory end tag.
27    #[error("missing mandatory end tag")]
28    NoEndTag,
29}
30
31/// The basic header of a [`BootInformation`] as sized Rust type.
32#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
33#[repr(C, align(8))]
34pub struct BootInformationHeader {
35    // size is multiple of 8
36    total_size: u32,
37    _reserved: u32,
38    // Followed by the boot information tags.
39}
40
41impl BootInformationHeader {
42    #[cfg(feature = "builder")]
43    pub(crate) const fn new(total_size: u32) -> Self {
44        Self {
45            total_size,
46            _reserved: 0,
47        }
48    }
49
50    /// Returns the total size of the structure.
51    #[must_use]
52    pub const fn total_size(&self) -> u32 {
53        self.total_size
54    }
55}
56
57impl Header for BootInformationHeader {
58    fn payload_len(&self) -> usize {
59        self.total_size as usize - mem::size_of::<Self>()
60    }
61
62    fn set_size(&mut self, total_size: usize) {
63        self.total_size = total_size as u32;
64    }
65}
66
67/// A Multiboot 2 Boot Information (MBI) accessor.
68#[repr(transparent)]
69pub struct BootInformation<'a>(&'a DynSizedStructure<BootInformationHeader>);
70
71impl<'a> BootInformation<'a> {
72    /// Loads the [`BootInformation`] from a pointer. The pointer must be valid
73    /// and aligned to an 8-byte boundary, as defined by the spec.
74    ///
75    /// ## Example
76    ///
77    /// ```rust
78    /// use multiboot2::{BootInformation, BootInformationHeader};
79    ///
80    /// fn kernel_entry(mb_magic: u32, mbi_ptr: u32) {
81    ///     if mb_magic == multiboot2::MAGIC {
82    ///         let boot_info = unsafe { BootInformation::load(mbi_ptr as *const BootInformationHeader).unwrap() };
83    ///         let _cmd = boot_info.command_line_tag();
84    ///     } else { /* Panic or use multiboot1 flow. */ }
85    /// }
86    /// ```
87    ///
88    /// ## Safety
89    /// * `ptr` must be valid for reading. Otherwise, this function might cause
90    ///   invalid machine state or crash your binary (kernel). This can be the
91    ///   case in environments with standard environment (segfault), but also in
92    ///   boot environments, such as UEFI.
93    /// * The memory at `ptr` must not be modified after calling `load` or the
94    ///   program may observe unsynchronized mutation.
95    pub unsafe fn load(ptr: *const BootInformationHeader) -> Result<Self, LoadError> {
96        let ptr = NonNull::new(ptr.cast_mut()).ok_or(LoadError::Memory(MemoryError::Null))?;
97        let inner = unsafe { DynSizedStructure::ref_from_ptr(ptr).map_err(LoadError::Memory)? };
98
99        let this = Self(inner);
100        if !this.has_valid_end_tag() {
101            return Err(LoadError::NoEndTag);
102        }
103        Ok(this)
104    }
105
106    /// Checks if the MBI has a valid end tag by checking the end of the mbi's
107    /// bytes.
108    fn has_valid_end_tag(&self) -> bool {
109        let header = self.0.header();
110        let end_tag_ptr = unsafe {
111            self.0
112                .payload()
113                .as_ptr()
114                .add(header.payload_len())
115                .sub(mem::size_of::<EndTag>())
116                .cast::<TagHeader>()
117        };
118        let end_tag = unsafe { &*end_tag_ptr };
119
120        end_tag.typ == EndTag::ID && end_tag.size as usize == mem::size_of::<EndTag>()
121    }
122
123    /// Get the start address of the boot info.
124    #[must_use]
125    // TODO deprecated and use pointers only (see provenance discussions)
126    pub fn start_address(&self) -> usize {
127        self.as_ptr() as usize
128    }
129
130    /// Get the start address of the boot info as pointer.
131    #[must_use]
132    pub const fn as_ptr(&self) -> *const () {
133        core::ptr::addr_of!(*self.0).cast()
134    }
135
136    /// Get the end address of the boot info.
137    ///
138    /// This is the same as doing:
139    ///
140    /// ```rust,no_run
141    /// # use multiboot2::{BootInformation, BootInformationHeader};
142    /// # let ptr = 0xdeadbeef as *const BootInformationHeader;
143    /// # let boot_info = unsafe { BootInformation::load(ptr).unwrap() };
144    /// let end_addr = boot_info.start_address() + boot_info.total_size();
145    /// ```
146    #[must_use]
147    // TODO deprecated and use pointers only (see provenance discussions)
148    pub fn end_address(&self) -> usize {
149        self.start_address() + self.total_size()
150    }
151
152    /// Get the total size of the boot info struct.
153    #[must_use]
154    pub const fn total_size(&self) -> usize {
155        self.0.header().total_size as usize
156    }
157
158    // ######################################################
159    // ### BEGIN OF TAG GETTERS (in alphabetical order)
160
161    /// Search for the [`ApmTag`].
162    #[must_use]
163    pub fn apm_tag(&self) -> Option<&ApmTag> {
164        self.get_tag::<ApmTag>()
165    }
166
167    /// Search for the [`BasicMemoryInfoTag`].
168    #[must_use]
169    pub fn basic_memory_info_tag(&self) -> Option<&BasicMemoryInfoTag> {
170        self.get_tag::<BasicMemoryInfoTag>()
171    }
172
173    /// Search for the [`BootLoaderNameTag`].
174    #[must_use]
175    pub fn boot_loader_name_tag(&self) -> Option<&BootLoaderNameTag> {
176        self.get_tag::<BootLoaderNameTag>()
177    }
178
179    /// Search for the [`BootdevTag`].
180    #[must_use]
181    pub fn bootdev_tag(&self) -> Option<&BootdevTag> {
182        self.get_tag::<BootdevTag>()
183    }
184
185    /// Search for the [`CommandLineTag`].
186    #[must_use]
187    pub fn command_line_tag(&self) -> Option<&CommandLineTag> {
188        self.get_tag::<CommandLineTag>()
189    }
190
191    /// Search for the [`EFIBootServicesNotExitedTag`].
192    #[must_use]
193    pub fn efi_bs_not_exited_tag(&self) -> Option<&EFIBootServicesNotExitedTag> {
194        self.get_tag::<EFIBootServicesNotExitedTag>()
195    }
196
197    /// Search for the [`EFIMemoryMapTag`], if the boot services were exited.
198    /// Otherwise, if the [`TagType::EfiBs`] tag is present, this returns `None`
199    /// as it is strictly recommended to get the memory map from `uefi`
200    /// instead.
201    ///
202    /// [`TagType::EfiBs`]: crate::TagType::EfiBs
203    #[must_use]
204    pub fn efi_memory_map_tag(&self) -> Option<&EFIMemoryMapTag> {
205        // If the EFIBootServicesNotExited is present, then we should not use
206        // the memory map, as it could still be in use.
207        self.get_tag::<EFIBootServicesNotExitedTag>().map_or_else(
208            || self.get_tag::<EFIMemoryMapTag>(), |_tag| {
209                            log::debug!("The EFI memory map is present but the UEFI Boot Services Not Existed Tag is present. Returning None.");
210                             None
211                        })
212    }
213
214    /// Search for the [`EFISdt32Tag`].
215    #[must_use]
216    pub fn efi_sdt32_tag(&self) -> Option<&EFISdt32Tag> {
217        self.get_tag::<EFISdt32Tag>()
218    }
219
220    /// Search for the [`EFISdt64Tag`].
221    #[must_use]
222    pub fn efi_sdt64_tag(&self) -> Option<&EFISdt64Tag> {
223        self.get_tag::<EFISdt64Tag>()
224    }
225
226    /// Search for the [`EFIImageHandle32Tag`].
227    #[must_use]
228    pub fn efi_ih32_tag(&self) -> Option<&EFIImageHandle32Tag> {
229        self.get_tag::<EFIImageHandle32Tag>()
230    }
231
232    /// Search for the [`EFIImageHandle64Tag`].
233    #[must_use]
234    pub fn efi_ih64_tag(&self) -> Option<&EFIImageHandle64Tag> {
235        self.get_tag::<EFIImageHandle64Tag>()
236    }
237
238    /// Returns an [`ElfSectionIter`] iterator over the ELF Sections, if the
239    /// [`ElfSectionsTag`] is present.
240    ///
241    /// # Examples
242    ///
243    /// ```rust,no_run
244    /// # use multiboot2::{BootInformation, BootInformationHeader};
245    /// # let ptr = 0xdeadbeef as *const BootInformationHeader;
246    /// # let boot_info = unsafe { BootInformation::load(ptr).unwrap() };
247    /// if let Some(sections) = boot_info.elf_sections_tag().map(|tag| tag.sections()) {
248    ///     let mut total = 0;
249    ///     for section in sections {
250    ///         println!("Section: {:?}", section);
251    ///         total += 1;
252    ///     }
253    /// }
254    /// ```
255    #[must_use]
256    #[deprecated = "Use elf_sections_tag() instead and corresponding getters"]
257    pub fn elf_sections(&self) -> Option<ElfSectionIter> {
258        let tag = self.get_tag::<ElfSectionsTag>();
259        tag.map(|t| {
260            assert!((t.entry_size() * t.shndx()) <= t.header().size);
261            t.sections()
262        })
263    }
264
265    /// Search for the [`ElfSectionsTag`].
266    #[must_use]
267    pub fn elf_sections_tag(&self) -> Option<&ElfSectionsTag> {
268        self.get_tag()
269    }
270
271    /// Search for the [`FramebufferTag`]. The result is `Some(Err(e))`, if the
272    /// framebuffer type is unknown, while the framebuffer tag is present.
273    #[must_use]
274    pub fn framebuffer_tag(&self) -> Option<Result<&FramebufferTag, UnknownFramebufferType>> {
275        self.get_tag::<FramebufferTag>()
276            .map(|tag| match tag.buffer_type() {
277                Ok(_) => Ok(tag),
278                Err(e) => Err(e),
279            })
280    }
281
282    /// Search for the [`ImageLoadPhysAddrTag`].
283    #[must_use]
284    pub fn load_base_addr_tag(&self) -> Option<&ImageLoadPhysAddrTag> {
285        self.get_tag::<ImageLoadPhysAddrTag>()
286    }
287
288    /// Search for the [`MemoryMapTag`].
289    #[must_use]
290    pub fn memory_map_tag(&self) -> Option<&MemoryMapTag> {
291        self.get_tag::<MemoryMapTag>()
292    }
293
294    /// Get an iterator of all [`ModuleTag`]s.
295    ///
296    /// [`ModuleTag`]: crate::ModuleTag
297    #[must_use]
298    pub fn module_tags(&self) -> ModuleIter {
299        module::module_iter(self.tags())
300    }
301
302    /// Search for the [`NetworkTag`].
303    #[must_use]
304    pub fn network_tag(&self) -> Option<&NetworkTag> {
305        self.get_tag::<NetworkTag>()
306    }
307
308    /// Search for the [`RsdpV1Tag`].
309    #[must_use]
310    pub fn rsdp_v1_tag(&self) -> Option<&RsdpV1Tag> {
311        self.get_tag::<RsdpV1Tag>()
312    }
313
314    /// Search for the [`RsdpV2Tag`].
315    #[must_use]
316    pub fn rsdp_v2_tag(&self) -> Option<&RsdpV2Tag> {
317        self.get_tag::<RsdpV2Tag>()
318    }
319
320    /// Search for the [`SmbiosTag`].
321    #[must_use]
322    pub fn smbios_tag(&self) -> Option<&SmbiosTag> {
323        self.get_tag::<SmbiosTag>()
324    }
325
326    /// Search for the [`VBEInfoTag`].
327    #[must_use]
328    pub fn vbe_info_tag(&self) -> Option<&VBEInfoTag> {
329        self.get_tag::<VBEInfoTag>()
330    }
331
332    // ### END OF TAG GETTERS
333    // ######################################################
334
335    /// Public getter to find any Multiboot tag by its type, including
336    /// specified and custom ones.
337    ///
338    /// # Specified or Custom Tags
339    /// The Multiboot2 specification specifies a list of tags, see [`TagType`].
340    /// However, it doesn't forbid to use custom tags. Because of this, there
341    /// exists the [`TagType`] abstraction. It is recommended to use this
342    /// getter only for custom tags. For specified tags, use getters, such as
343    /// [`Self::efi_ih64_tag`].
344    ///
345    /// ## Use Custom Tags
346    /// The following example shows how you may use this interface to parse
347    /// custom tags from the MBI. If they are dynamically sized (DST), a few more
348    /// special handling is required. This is reflected by code-comments.
349    ///
350    /// ```no_run
351    /// use std::mem;
352    /// use multiboot2::{BootInformation, BootInformationHeader, parse_slice_as_string, StringError, TagHeader, TagType, TagTypeId};    ///
353    /// use multiboot2_common::{MaybeDynSized, Tag};
354    ///
355    /// #[repr(C)]
356    /// #[derive(multiboot2::Pointee)] // Only needed for DSTs.
357    /// struct CustomTag {
358    ///     header: TagHeader,
359    ///     some_other_prop: u32,
360    ///     // Begin of C string, for example.
361    ///     name: [u8],
362    /// }
363    ///
364    /// impl CustomTag {
365    ///     fn name(&self) -> Result<&str, StringError> {
366    ///         parse_slice_as_string(&self.name)
367    ///     }
368    /// }
369    ///
370    /// // Give the library hints how big this tag is.
371    /// impl MaybeDynSized for CustomTag {
372    ///     type Header = TagHeader;
373    ///     const BASE_SIZE: usize = mem::size_of::<TagHeader>() + mem::size_of::<u32>();
374    ///
375    ///     // This differs for DSTs and normal structs. See function
376    ///     // documentation.
377    ///     fn dst_len(header: &TagHeader) -> usize {
378    ///         assert!(header.size >= Self::BASE_SIZE as u32);
379    ///         header.size as usize - Self::BASE_SIZE
380    ///     }
381    /// }
382    ///
383    /// // Make the Tag identifiable.
384    /// impl Tag for CustomTag {
385    ///     type IDType = TagType;
386    ///     const ID: TagType = TagType::Custom(0x1337);
387    /// }
388    ///
389    /// let mbi_ptr = 0xdeadbeef as *const BootInformationHeader;
390    /// let mbi = unsafe { BootInformation::load(mbi_ptr).unwrap() };
391    ///
392    /// let tag = mbi
393    ///     .get_tag::<CustomTag>()
394    ///     .unwrap();
395    /// assert_eq!(tag.name(), Ok("name"));
396    /// ```
397    ///
398    /// [`TagType`]: crate::TagType
399    #[must_use]
400    pub fn get_tag<T: Tag<IDType = TagType, Header = TagHeader> + ?Sized + 'a>(
401        &'a self,
402    ) -> Option<&'a T> {
403        self.tags()
404            .find(|tag| tag.header().typ == T::ID)
405            .map(|tag| tag.cast::<T>())
406    }
407
408    /// Returns an iterator over all tags.
409    ///
410    /// This is public to enable users to iterate over tags that appear multiple
411    /// times, even tho this is unusual. However, it is recommended to use the
412    /// tag getters as normal bootloaders provide most tags only once.
413    #[must_use]
414    pub fn tags(&self) -> TagIter {
415        TagIter::new(self.0.payload())
416    }
417}
418
419impl fmt::Debug for BootInformation<'_> {
420    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
421        let mut debug = f.debug_struct("Multiboot2BootInformation");
422        debug
423            .field("start_address", &self.start_address())
424            .field("end_address", &self.end_address())
425            .field("total_size", &self.total_size())
426            // now tags in alphabetical order
427            .field("apm", &self.apm_tag())
428            .field("basic_memory_info", &(self.basic_memory_info_tag()))
429            .field("boot_loader_name", &self.boot_loader_name_tag())
430            .field("bootdev", &self.bootdev_tag())
431            .field("command_line", &self.command_line_tag())
432            .field("efi_bs_not_exited", &self.efi_bs_not_exited_tag())
433            .field("efi_ih32", &self.efi_ih32_tag())
434            .field("efi_ih64", &self.efi_ih64_tag())
435            .field("efi_memory_map", &self.efi_memory_map_tag())
436            .field("efi_sdt32", &self.efi_sdt32_tag())
437            .field("efi_sdt64", &self.efi_sdt64_tag())
438            .field("elf_sections", &self.elf_sections_tag())
439            .field("framebuffer", &self.framebuffer_tag())
440            .field("load_base_addr", &self.load_base_addr_tag())
441            .field("memory_map", &self.memory_map_tag())
442            .field("modules", &self.module_tags())
443            .field("network", &self.network_tag())
444            .field("rsdp_v1", &self.rsdp_v1_tag())
445            .field("rsdp_v2", &self.rsdp_v2_tag())
446            .field("smbios", &self.smbios_tag())
447            .field("vbe_info", &self.vbe_info_tag())
448            // computed fields
449            .field("custom_tags_count", &{
450                self.tags()
451                    .filter(|tag| {
452                        let id: TagType = tag.header().typ.into();
453                        matches!(id, TagType::Custom(_))
454                    })
455                    .count()
456            })
457            .finish()
458    }
459}