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}