multiboot2/
lib.rs

1#![no_std]
2// --- BEGIN STYLE CHECKS ---
3#![deny(
4    clippy::all,
5    clippy::cargo,
6    clippy::nursery,
7    clippy::must_use_candidate,
8    // clippy::restriction,
9    // clippy::pedantic
10)]
11// now allow a few rules which are denied by the above statement
12// --> They are either ridiculous, not necessary, or we can't fix them.
13#![allow(clippy::multiple_crate_versions)]
14#![deny(missing_docs)]
15#![deny(missing_debug_implementations)]
16#![deny(rustdoc::all)]
17// --- END STYLE CHECKS ---
18
19//! Convenient and safe parsing of Multiboot2 Boot Information (MBI) structures
20//! and the contained information tags. Usable in `no_std` environments, such as
21//! a kernel. An optional builder feature also allows the construction of
22//! the corresponding structures.
23//!
24//! ## Design
25//!
26//! For every Multiboot2 structure, there is an ABI-compatible rusty type. This
27//! enables a zero-copying parsing design while also enabling the creation of
28//! these structures via convenient constructors on the corresponding types.
29//!
30//! ## Example
31//!
32//! ```rust
33//! use multiboot2::{BootInformation, BootInformationHeader};
34//!
35//! fn kernel_entry(mb_magic: u32, mbi_ptr: u32) {
36//!     if mb_magic == multiboot2::MAGIC {
37//!         let boot_info = unsafe { BootInformation::load(mbi_ptr as *const BootInformationHeader).unwrap() };
38//!         let _cmd = boot_info.command_line_tag();
39//!     } else { /* Panic or use multiboot1 flow. */ }
40//! }
41//! ```
42//!
43//! ## MSRV
44//! The MSRV is 1.85.0 stable.
45
46#[cfg_attr(feature = "builder", macro_use)]
47#[cfg(feature = "builder")]
48extern crate alloc;
49
50// this crate can use std in tests only
51#[cfg_attr(test, macro_use)]
52#[cfg(test)]
53extern crate std;
54
55#[macro_use]
56extern crate bitflags;
57
58#[cfg(feature = "builder")]
59mod builder;
60
61/// Iterator over the tags of a Multiboot2 boot information.
62pub type TagIter<'a> = multiboot2_common::TagIter<'a, TagHeader>;
63
64/// A generic version of all boot information tags.
65#[cfg(test)]
66pub type GenericInfoTag = multiboot2_common::DynSizedStructure<TagHeader>;
67
68mod apm;
69mod boot_information;
70mod boot_loader_name;
71mod bootdev;
72mod command_line;
73mod efi;
74mod elf_sections;
75mod end;
76mod framebuffer;
77mod image_load_addr;
78mod memory_map;
79mod module;
80mod network;
81mod rsdp;
82mod smbios;
83mod tag;
84mod tag_type;
85pub(crate) mod util;
86mod vbe_info;
87
88pub use multiboot2_common::{DynSizedStructure, MaybeDynSized, Tag};
89
90pub use apm::ApmTag;
91pub use boot_information::{BootInformation, BootInformationHeader, LoadError};
92pub use boot_loader_name::BootLoaderNameTag;
93pub use bootdev::BootdevTag;
94#[cfg(feature = "builder")]
95pub use builder::Builder;
96pub use command_line::CommandLineTag;
97pub use efi::{
98    EFIBootServicesNotExitedTag, EFIImageHandle32Tag, EFIImageHandle64Tag, EFISdt32Tag, EFISdt64Tag,
99};
100pub use elf_sections::{
101    ElfSection, ElfSectionFlags, ElfSectionIter, ElfSectionType, ElfSectionsTag,
102};
103pub use end::EndTag;
104pub use framebuffer::{FramebufferColor, FramebufferField, FramebufferTag, FramebufferType};
105pub use image_load_addr::ImageLoadPhysAddrTag;
106pub use memory_map::{
107    BasicMemoryInfoTag, EFIMemoryAreaType, EFIMemoryAttribute, EFIMemoryDesc, EFIMemoryMapTag,
108    MemoryArea, MemoryAreaType, MemoryAreaTypeId, MemoryMapTag,
109};
110pub use module::{ModuleIter, ModuleTag};
111pub use network::NetworkTag;
112pub use ptr_meta::Pointee;
113pub use rsdp::{RsdpV1Tag, RsdpV2Tag};
114pub use smbios::SmbiosTag;
115pub use tag::TagHeader;
116pub use tag_type::{TagType, TagTypeId};
117pub use util::{StringError, parse_slice_as_string};
118pub use vbe_info::{
119    VBECapabilities, VBEControlInfo, VBEDirectColorAttributes, VBEField, VBEInfoTag,
120    VBEMemoryModel, VBEModeAttributes, VBEModeInfo, VBEWindowAttributes,
121};
122
123/// Magic number that a Multiboot2-compliant boot loader will use to identify
124/// the handoff. The location depends on the architecture and the targeted
125/// machine state.
126pub const MAGIC: u32 = 0x36d76289;
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131    use multiboot2_common::test_utils::AlignedBytes;
132    use multiboot2_common::{MaybeDynSized, Tag};
133    use std::mem;
134
135    /// Compile time test to check if the boot information is Send and Sync.
136    /// This test is relevant to give library users flexibility in passing the
137    /// struct around.
138    #[test]
139    fn boot_information_is_send_and_sync() {
140        fn accept<T: Send + Sync>(_: T) {}
141        let bytes = AlignedBytes([
142            16, 0, 0, 0, // total_size
143            0, 0, 0, 0, // reserved
144            0, 0, 0, 0, // end tag type
145            8, 0, 0, 0, // end tag size
146        ]);
147        let ptr = bytes.0.as_ptr();
148        let bi = unsafe { BootInformation::load(ptr.cast()) };
149        let bi = bi.unwrap();
150
151        // compile time test
152        accept(bi);
153    }
154
155    #[test]
156    fn no_tags() {
157        let bytes = AlignedBytes([
158            16, 0, 0, 0, // total_size
159            0, 0, 0, 0, // reserved
160            0, 0, 0, 0, // end tag type
161            8, 0, 0, 0, // end tag size
162        ]);
163        let ptr = bytes.0.as_ptr();
164        let addr = ptr as usize;
165        let bi = unsafe { BootInformation::load(ptr.cast()) };
166        let bi = bi.unwrap();
167        assert_eq!(addr, bi.start_address());
168        assert_eq!(addr + bytes.0.len(), bi.end_address());
169        assert_eq!(bytes.0.len(), bi.total_size());
170        assert!(bi.elf_sections_tag().is_none());
171        assert!(bi.memory_map_tag().is_none());
172        assert!(bi.module_tags().next().is_none());
173        assert!(bi.boot_loader_name_tag().is_none());
174        assert!(bi.command_line_tag().is_none());
175    }
176
177    #[test]
178    #[should_panic]
179    fn invalid_total_size() {
180        let bytes = AlignedBytes([
181            15, 0, 0, 0, // total_size
182            0, 0, 0, 0, // reserved
183            0, 0, 0, 0, // end tag type
184            8, 0, 0, // end tag size
185        ]);
186        let ptr = bytes.0.as_ptr();
187        let addr = ptr as usize;
188        let bi = unsafe { BootInformation::load(ptr.cast()) };
189        let bi = bi.unwrap();
190        assert_eq!(addr, bi.start_address());
191        assert_eq!(addr + bytes.0.len(), bi.end_address());
192        assert_eq!(bytes.0.len(), bi.total_size());
193        assert!(bi.elf_sections_tag().is_none());
194        assert!(bi.memory_map_tag().is_none());
195        assert!(bi.module_tags().next().is_none());
196        assert!(bi.boot_loader_name_tag().is_none());
197        assert!(bi.command_line_tag().is_none());
198    }
199
200    #[test]
201    #[should_panic]
202    fn invalid_end_tag() {
203        let bytes = AlignedBytes([
204            16, 0, 0, 0, // total_size
205            0, 0, 0, 0, // reserved
206            0, 0, 0, 0, // end tag type
207            9, 0, 0, 0, // end tag size
208        ]);
209        let ptr = bytes.0.as_ptr();
210        let addr = ptr as usize;
211        let bi = unsafe { BootInformation::load(ptr.cast()) };
212        let bi = bi.unwrap();
213        assert_eq!(addr, bi.start_address());
214        assert_eq!(addr + bytes.0.len(), bi.end_address());
215        assert_eq!(bytes.0.len(), bi.total_size());
216        assert!(bi.elf_sections_tag().is_none());
217        assert!(bi.memory_map_tag().is_none());
218        assert!(bi.module_tags().next().is_none());
219        assert!(bi.boot_loader_name_tag().is_none());
220        assert!(bi.command_line_tag().is_none());
221    }
222
223    #[test]
224    fn name_tag() {
225        let bytes = AlignedBytes([
226            32, 0, 0, 0, // total_size
227            0, 0, 0, 0, // reserved
228            2, 0, 0, 0, // boot loader name tag type
229            13, 0, 0, 0, // boot loader name tag size
230            110, 97, 109, 101, // boot loader name 'name'
231            0, 0, 0, 0, // boot loader name null + padding
232            0, 0, 0, 0, // end tag type
233            8, 0, 0, 0, // end tag size
234        ]);
235        let ptr = bytes.0.as_ptr();
236        let addr = ptr as usize;
237        let bi = unsafe { BootInformation::load(ptr.cast()) };
238        let bi = bi.unwrap();
239        assert_eq!(addr, bi.start_address());
240        assert_eq!(addr + bytes.0.len(), bi.end_address());
241        assert_eq!(bytes.0.len(), bi.total_size());
242        assert!(bi.elf_sections_tag().is_none());
243        assert!(bi.memory_map_tag().is_none());
244        assert!(bi.module_tags().next().is_none());
245        assert_eq!(
246            "name",
247            bi.boot_loader_name_tag()
248                .expect("tag must be present")
249                .name()
250                .expect("must be valid utf8")
251        );
252        assert!(bi.command_line_tag().is_none());
253    }
254
255    #[test]
256    fn framebuffer_tag_rgb() {
257        // direct RGB mode test:
258        // taken from GRUB2 running in QEMU at
259        // 1280x720 with 32bpp in BGRA format.
260        let bytes = AlignedBytes([
261            56, 0, 0, 0, // total size
262            0, 0, 0, 0, // reserved
263            8, 0, 0, 0, // framebuffer tag type
264            40, 0, 0, 0, // framebuffer tag size
265            0, 0, 0, 253, // framebuffer low dword of address
266            0, 0, 0, 0, // framebuffer high dword of address
267            0, 20, 0, 0, // framebuffer pitch
268            0, 5, 0, 0, // framebuffer width
269            208, 2, 0, 0, // framebuffer height
270            32, 1, 0, 0, // framebuffer bpp, type, reserved word
271            16, 8, 8, 8, // framebuffer red pos/size, green pos/size
272            0, 8, 0, 0, // framebuffer blue pos/size, padding word
273            0, 0, 0, 0, // end tag type
274            8, 0, 0, 0, // end tag size
275        ]);
276        let ptr = bytes.0.as_ptr();
277        let addr = ptr as usize;
278        let bi = unsafe { BootInformation::load(ptr.cast()) };
279        let bi = bi.unwrap();
280        assert_eq!(addr, bi.start_address());
281        assert_eq!(addr + bytes.0.len(), bi.end_address());
282        assert_eq!(bytes.0.len(), bi.total_size());
283        assert!(bi.framebuffer_tag().is_some());
284        let fbi = bi
285            .framebuffer_tag()
286            .expect("Framebuffer info should be available")
287            .expect("Framebuffer info type should be valid");
288        assert_eq!(fbi.address(), 4244635648);
289        assert_eq!(fbi.pitch(), 5120);
290        assert_eq!(fbi.width(), 1280);
291        assert_eq!(fbi.height(), 720);
292        assert_eq!(fbi.bpp(), 32);
293        assert_eq!(
294            fbi.buffer_type().unwrap(),
295            FramebufferType::RGB {
296                red: FramebufferField {
297                    position: 16,
298                    size: 8,
299                },
300                green: FramebufferField {
301                    position: 8,
302                    size: 8,
303                },
304                blue: FramebufferField {
305                    position: 0,
306                    size: 8,
307                },
308            }
309        );
310    }
311
312    #[test]
313    fn framebuffer_tag_indexed() {
314        // indexed mode test:
315        // this is synthetic, as I can't get QEMU
316        // to run in indexed color mode.
317        #[rustfmt::skip]
318        let bytes = AlignedBytes([
319            64, 0, 0, 0, // total size
320            0, 0, 0, 0, // reserved
321            8, 0, 0, 0, // framebuffer tag type
322            48, 0, 0, 0, // framebuffer tag size
323            0, 0, 0, 253, // framebuffer low dword of address
324            0, 0, 0, 0, // framebuffer high dword of address
325            0, 20, 0, 0, // framebuffer pitch
326            0, 5, 0, 0, // framebuffer width
327            208, 2, 0, 0, // framebuffer height
328            32, // framebuffer bpp
329            0, // framebuffer type
330            0, 0, // reserved word
331            4, 0, // framebuffer palette length
332            255, 0, 0, // framebuffer palette: 1/3
333            0, 255, 0, // framebuffer palette: 2/3
334            0, 0, 255, // framebuffer palette: 3/3
335            3, 7, 73, // framebuffer palette: 4/4
336            0, 0, // padding  for 8-byte alignment
337            0, 0, 0, 0, // end tag type
338            8, 0, 0, 0, // end tag size
339        ]);
340        let ptr = bytes.0.as_ptr();
341        let addr = ptr as usize;
342        let bi = unsafe { BootInformation::load(ptr.cast()) };
343        let bi = bi.unwrap();
344        assert_eq!(addr, bi.start_address());
345        assert_eq!(addr + bytes.0.len(), bi.end_address());
346        assert_eq!(bytes.0.len(), bi.total_size());
347        assert!(bi.framebuffer_tag().is_some());
348        let fbi = bi
349            .framebuffer_tag()
350            .expect("Framebuffer info should be available")
351            .expect("Framebuffer info type should be valid");
352        assert_eq!(fbi.address(), 4244635648);
353        assert_eq!(fbi.pitch(), 5120);
354        assert_eq!(fbi.width(), 1280);
355        assert_eq!(fbi.height(), 720);
356        assert_eq!(fbi.bpp(), 32);
357        match fbi.buffer_type().unwrap() {
358            FramebufferType::Indexed { palette } => assert_eq!(
359                palette,
360                [
361                    FramebufferColor {
362                        red: 255,
363                        green: 0,
364                        blue: 0,
365                    },
366                    FramebufferColor {
367                        red: 0,
368                        green: 255,
369                        blue: 0,
370                    },
371                    FramebufferColor {
372                        red: 0,
373                        green: 0,
374                        blue: 255,
375                    },
376                    FramebufferColor {
377                        red: 3,
378                        green: 7,
379                        blue: 73,
380                    }
381                ]
382            ),
383            _ => panic!("Expected indexed framebuffer type."),
384        }
385    }
386
387    #[test]
388    #[allow(clippy::cognitive_complexity)]
389    fn vbe_info_tag() {
390        //Taken from GRUB2 running in QEMU.
391        let bytes = AlignedBytes([
392            32, 3, 0, 0, // Total size.
393            0, 0, 0, 0, // Reserved
394            7, 0, 0, 0, // Tag type.
395            16, 3, 0, 0, // Tag size.
396            122, 65, 255, 255, // VBE mode, protected mode interface segment,
397            0, 96, 79, 0, // protected mode interface offset, and length.
398            86, 69, 83, 65, // "VESA" signature.
399            0, 3, 220, 87, // VBE version, lower half of OEM string ptr,
400            0, 192, 1, 0, // upper half of OEM string ptr, lower half of capabilities
401            0, 0, 34, 128, // upper half of capabilities, lower half of vide mode ptr,
402            0, 96, 0, 1, // upper half of video mode ptr, number of 64kb memory blocks
403            0, 0, 240, 87, // OEM software revision, lower half of OEM vendor string ptr,
404            0, 192, 3,
405            88, // upper half of OEM vendor string ptr, lower half of OEM product string ptr,
406            0, 192, 23,
407            88, // upper half of OEM product string ptr, lower half of OEM revision string ptr,
408            0, 192, 0, 1, // upper half of OEM revision string ptr.
409            1, 1, 2, 1, // Reserved data....
410            3, 1, 4, 1, 5, 1, 6, 1, 7, 1, 13, 1, 14, 1, 15, 1, 16, 1, 17, 1, 18, 1, 19, 1, 20, 1,
411            21, 1, 22, 1, 23, 1, 24, 1, 25, 1, 26, 1, 27, 1, 28, 1, 29, 1, 30, 1, 31, 1, 64, 1, 65,
412            1, 66, 1, 67, 1, 68, 1, 69, 1, 70, 1, 71, 1, 72, 1, 73, 1, 74, 1, 75, 1, 76, 1, 117, 1,
413            118, 1, 119, 1, 120, 1, 121, 1, 122, 1, 123, 1, 124, 1, 125, 1, 126, 1, 127, 1, 128, 1,
414            129, 1, 130, 1, 131, 1, 132, 1, 133, 1, 134, 1, 135, 1, 136, 1, 137, 1, 138, 1, 139, 1,
415            140, 1, 141, 1, 142, 1, 143, 1, 144, 1, 145, 1, 146, 1, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0,
416            5, 0, 6, 0, 7, 0, 13, 0, 14, 0, 15, 0, 16, 0, 17, 0, 18, 0, 19, 0, 106, 0, 255, 255, 0,
417            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
418            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
419            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
420            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
421            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
422            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
423            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
424            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
425            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
426            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
427            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Until Here
428            187, 0, 7, 0, // Mode attributes, window A and B attributes
429            64, 0, 64, 0, // Window granularity and size.
430            0, 160, 0, 0, // Window A and B segments.
431            186, 84, 0, 192, // Window relocation function pointer.
432            0, 20, 0, 5, // Pitch, X resolution.
433            32, 3, 8, 16, // Y resolution, X char size, Y char size.
434            1, 32, 1, 6, // Number of planes, BPP, number of banks, memory model.
435            0, 3, 1, 8, // Bank size, number of images, reserved, red mask size.
436            16, 8, 8,
437            8, // Red mask position, green mask size, green mask position, blue mask size,
438            0, 8, 24,
439            2, // blue mask position, reserved mask size, reserved mask position, color attributes.
440            0, 0, 0, 253, // Frame buffer base address.
441            0, 0, 0, 0, // Off screen memory offset.
442            0, 0, 0, 20, // Off screen memory size, reserved data...
443            0, 0, 8, 16, 8, 8, 8, 0, 8, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
444            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
445            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
446            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
447            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
448            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
449            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
450            0, 0, // Until here.
451            0, 0, 0, 0, // End tag type.
452            8, 0, 0, 0, // End tag size.
453        ]);
454
455        let ptr = bytes.0.as_ptr();
456        let addr = ptr as usize;
457        let bi = unsafe { BootInformation::load(ptr.cast()) };
458        let bi = bi.unwrap();
459        assert_eq!(addr, bi.start_address());
460        assert_eq!(addr + bytes.0.len(), bi.end_address());
461        assert_eq!(bytes.0.len(), bi.total_size());
462        assert!(bi.vbe_info_tag().is_some());
463        let vbe = bi.vbe_info_tag().unwrap();
464        use vbe_info::*;
465
466        assert_eq!({ vbe.mode() }, 16762);
467        assert_eq!({ vbe.interface_segment() }, 65535);
468        assert_eq!({ vbe.interface_offset() }, 24576);
469        assert_eq!({ vbe.interface_length() }, 79);
470        assert_eq!({ vbe.control_info().signature }, [86, 69, 83, 65]);
471        assert_eq!({ vbe.control_info().version }, 768);
472        assert_eq!({ vbe.control_info().oem_string_ptr }, 3221247964);
473        assert_eq!(
474            { vbe.control_info().capabilities },
475            VBECapabilities::SWITCHABLE_DAC
476        );
477        assert_eq!({ vbe.control_info().mode_list_ptr }, 1610645538);
478        assert_eq!({ vbe.control_info().total_memory }, 256);
479        assert_eq!({ vbe.control_info().oem_software_revision }, 0);
480        assert_eq!({ vbe.control_info().oem_vendor_name_ptr }, 3221247984);
481        assert_eq!({ vbe.control_info().oem_product_name_ptr }, 3221248003);
482        assert_eq!({ vbe.control_info().oem_product_revision_ptr }, 3221248023);
483        assert!({ vbe.mode_info().mode_attributes }.contains(
484            VBEModeAttributes::SUPPORTED
485                | VBEModeAttributes::COLOR
486                | VBEModeAttributes::GRAPHICS
487                | VBEModeAttributes::NOT_VGA_COMPATIBLE
488                | VBEModeAttributes::LINEAR_FRAMEBUFFER
489        ));
490        assert!(vbe.mode_info().window_a_attributes.contains(
491            VBEWindowAttributes::RELOCATABLE
492                | VBEWindowAttributes::READABLE
493                | VBEWindowAttributes::WRITEABLE
494        ));
495        assert_eq!({ vbe.mode_info().window_granularity }, 64);
496        assert_eq!({ vbe.mode_info().window_size }, 64);
497        assert_eq!({ vbe.mode_info().window_a_segment }, 40960);
498        assert_eq!({ vbe.mode_info().window_function_ptr }, 3221247162);
499        assert_eq!({ vbe.mode_info().pitch }, 5120);
500        assert_eq!({ vbe.mode_info().resolution }, (1280, 800));
501        assert_eq!(vbe.mode_info().character_size, (8, 16));
502        assert_eq!(vbe.mode_info().number_of_planes, 1);
503        assert_eq!(vbe.mode_info().bpp, 32);
504        assert_eq!(vbe.mode_info().number_of_banks, 1);
505        assert_eq!(vbe.mode_info().memory_model, VBEMemoryModel::DirectColor);
506        assert_eq!(vbe.mode_info().bank_size, 0);
507        assert_eq!(vbe.mode_info().number_of_image_pages, 3);
508        assert_eq!(
509            vbe.mode_info().red_field,
510            VBEField {
511                position: 16,
512                size: 8,
513            }
514        );
515        assert_eq!(
516            vbe.mode_info().green_field,
517            VBEField {
518                position: 8,
519                size: 8,
520            }
521        );
522        assert_eq!(
523            vbe.mode_info().blue_field,
524            VBEField {
525                position: 0,
526                size: 8,
527            }
528        );
529        assert_eq!(
530            vbe.mode_info().reserved_field,
531            VBEField {
532                position: 24,
533                size: 8,
534            }
535        );
536        assert_eq!(
537            vbe.mode_info().direct_color_attributes,
538            VBEDirectColorAttributes::RESERVED_USABLE
539        );
540        assert_eq!({ vbe.mode_info().framebuffer_base_ptr }, 4244635648);
541        assert_eq!({ vbe.mode_info().offscreen_memory_offset }, 0);
542        assert_eq!({ vbe.mode_info().offscreen_memory_size }, 0);
543    }
544
545    #[test]
546    /// Compile time test for [`VBEInfoTag`].
547    fn vbe_info_tag_size() {
548        unsafe {
549            // 16 for the start + 512 from `VBEControlInfo` + 256 from `VBEModeInfo`.
550            core::mem::transmute::<[u8; 784], VBEInfoTag>([0u8; 784]);
551        }
552    }
553
554    /// Tests to parse a MBI that was statically extracted from a test run with
555    /// GRUB as bootloader.
556    #[test]
557    fn grub2() {
558        let mut bytes = AlignedBytes([
559            192, 3, 0, 0, // total_size
560            0, 0, 0, 0, // reserved
561            1, 0, 0, 0, // boot command tag type
562            9, 0, 0, 0, // boot command tag size
563            0, 0, 0, 0, // boot command null + padding
564            0, 0, 0, 0, // boot command padding
565            2, 0, 0, 0, // boot loader name tag type
566            26, 0, 0, 0, // boot loader name tag size
567            71, 82, 85, 66, // boot loader name
568            32, 50, 46, 48, // boot loader name
569            50, 126, 98, 101, // boot loader name
570            116, 97, 51, 45, // boot loader name
571            53, 0, 0, 0, // boot loader name null + padding
572            0, 0, 0, 0, // boot loader name padding
573            10, 0, 0, 0, // APM tag type
574            28, 0, 0, 0, // APM tag size
575            2, 1, 0, 240, // APM version, cseg
576            207, 212, 0, 0, // APM offset
577            0, 240, 0, 240, // APM cseg_16, dseg
578            3, 0, 240, 255, // APM flags, cseg_len
579            240, 255, 240, 255, // APM cseg_16_len, dseg_len
580            0, 0, 0, 0, // APM padding
581            6, 0, 0, 0, // memory map tag type
582            160, 0, 0, 0, // memory map tag size
583            24, 0, 0, 0, // memory map entry_size
584            0, 0, 0, 0, // memory map entry_version
585            0, 0, 0, 0, // memory map entry 0 base_addr
586            0, 0, 0, 0, // memory map entry 0 base_addr
587            0, 252, 9, 0, // memory map entry 0 length
588            0, 0, 0, 0, // memory map entry 0 length
589            1, 0, 0, 0, // memory map entry 0 type
590            0, 0, 0, 0, // memory map entry 0 reserved
591            0, 252, 9, 0, // memory map entry 1 base_addr
592            0, 0, 0, 0, // memory map entry 1 base_addr
593            0, 4, 0, 0, // memory map entry 1 length
594            0, 0, 0, 0, // memory map entry 1 length
595            2, 0, 0, 0, // memory map entry 1 type
596            0, 0, 0, 0, // memory map entry 1 reserved
597            0, 0, 15, 0, // memory map entry 2 base_addr
598            0, 0, 0, 0, // memory map entry 2 base_addr
599            0, 0, 1, 0, // memory map entry 2 length
600            0, 0, 0, 0, // memory map entry 2 length
601            2, 0, 0, 0, // memory map entry 2 type
602            0, 0, 0, 0, // memory map entry 2 reserved
603            0, 0, 16, 0, // memory map entry 3 base_addr
604            0, 0, 0, 0, // memory map entry 3 base_addr
605            0, 0, 238, 7, // memory map entry 3 length
606            0, 0, 0, 0, // memory map entry 3 length
607            1, 0, 0, 0, // memory map entry 3 type
608            0, 0, 0, 0, // memory map entry 3 reserved
609            0, 0, 254, 7, // memory map entry 4 base_addr
610            0, 0, 0, 0, // memory map entry 4 base_addr
611            0, 0, 2, 0, // memory map entry 4 length
612            0, 0, 0, 0, // memory map entry 4 length
613            2, 0, 0, 0, // memory map entry 4 type
614            0, 0, 0, 0, // memory map entry 4 reserved
615            0, 0, 252, 255, // memory map entry 5 base_addr
616            0, 0, 0, 0, // memory map entry 5 base_addr
617            0, 0, 4, 0, // memory map entry 5 length
618            0, 0, 0, 0, // memory map entry 5 length
619            2, 0, 0, 0, // memory map entry 5 type
620            0, 0, 0, 0, // memory map entry 5 reserved
621            9, 0, 0, 0, // elf symbols tag type
622            84, 2, 0, 0, // elf symbols tag size
623            9, 0, 0, 0, // elf symbols num
624            64, 0, 0, 0, // elf symbols entsize
625            8, 0, 0, 0, // elf symbols shndx
626            0, 0, 0, 0, // elf symbols entry 0 name
627            0, 0, 0, 0, // elf symbols entry 0 type
628            0, 0, 0, 0, // elf symbols entry 0 flags
629            0, 0, 0, 0, // elf symbols entry 0 flags
630            0, 0, 0, 0, // elf symbols entry 0 addr
631            0, 0, 0, 0, // elf symbols entry 0 addr
632            0, 0, 0, 0, // elf symbols entry 0 offset
633            0, 0, 0, 0, // elf symbols entry 0 offset
634            0, 0, 0, 0, // elf symbols entry 0 size
635            0, 0, 0, 0, // elf symbols entry 0 size
636            0, 0, 0, 0, // elf symbols entry 0 link
637            0, 0, 0, 0, // elf symbols entry 0 info
638            0, 0, 0, 0, // elf symbols entry 0 addralign
639            0, 0, 0, 0, // elf symbols entry 0 addralign
640            0, 0, 0, 0, // elf symbols entry 0 entsize
641            0, 0, 0, 0, // elf symbols entry 0 entsize
642            27, 0, 0, 0, // elf symbols entry 1 name
643            1, 0, 0, 0, // elf symbols entry 1 type
644            2, 0, 0, 0, // elf symbols entry 1 flags
645            0, 0, 0, 0, // elf symbols entry 1 flags
646            0, 0, 16, 0, // elf symbols entry 1 addr
647            0, 128, 255, 255, // elf symbols entry 1 addr
648            0, 16, 0, 0, // elf symbols entry 1 offset
649            0, 0, 0, 0, // elf symbols entry 1 offset
650            0, 48, 0, 0, // elf symbols entry 1 size
651            0, 0, 0, 0, // elf symbols entry 1 size
652            0, 0, 0, 0, // elf symbols entry 1 link
653            0, 0, 0, 0, // elf symbols entry 1 info
654            16, 0, 0, 0, // elf symbols entry 1 addralign
655            0, 0, 0, 0, // elf symbols entry 1 addralign
656            0, 0, 0, 0, // elf symbols entry 1 entsize
657            0, 0, 0, 0, // elf symbols entry 1 entsize
658            35, 0, 0, 0, // elf symbols entry 2 name
659            1, 0, 0, 0, // elf symbols entry 2 type
660            6, 0, 0, 0, // elf symbols entry 2 flags
661            0, 0, 0, 0, // elf symbols entry 2 flags
662            0, 48, 16, 0, // elf symbols entry 2 addr
663            0, 128, 255, 255, // elf symbols entry 2 addr
664            0, 64, 0, 0, // elf symbols entry 2 offset
665            0, 0, 0, 0, // elf symbols entry 2 offset
666            0, 144, 0, 0, // elf symbols entry 2 size
667            0, 0, 0, 0, // elf symbols entry 2 size
668            0, 0, 0, 0, // elf symbols entry 2 link
669            0, 0, 0, 0, // elf symbols entry 2 info
670            16, 0, 0, 0, // elf symbols entry 2 addralign
671            0, 0, 0, 0, // elf symbols entry 2 addralign
672            0, 0, 0, 0, // elf symbols entry 2 entsize
673            0, 0, 0, 0, // elf symbols entry 2 entsize
674            41, 0, 0, 0, // elf symbols entry 3 name
675            1, 0, 0, 0, // elf symbols entry 3 type
676            3, 0, 0, 0, // elf symbols entry 3 flags
677            0, 0, 0, 0, // elf symbols entry 3 flags
678            0, 192, 16, 0, // elf symbols entry 3 addr
679            0, 128, 255, 255, // elf symbols entry 3 addr
680            0, 208, 0, 0, // elf symbols entry 3 offset
681            0, 0, 0, 0, // elf symbols entry 3 offset
682            0, 32, 0, 0, // elf symbols entry 3 size
683            0, 0, 0, 0, // elf symbols entry 3 size
684            0, 0, 0, 0, // elf symbols entry 3 link
685            0, 0, 0, 0, // elf symbols entry 3 info
686            8, 0, 0, 0, // elf symbols entry 3 addralign
687            0, 0, 0, 0, // elf symbols entry 3 addralign
688            0, 0, 0, 0, // elf symbols entry 3 entsize
689            0, 0, 0, 0, // elf symbols entry 3 entsize
690            47, 0, 0, 0, // elf symbols entry 4 name
691            8, 0, 0, 0, // elf symbols entry 4 type
692            3, 0, 0, 0, // elf symbols entry 4 flags
693            0, 0, 0, 0, // elf symbols entry 4 flags
694            0, 224, 16, 0, // elf symbols entry 4 addr
695            0, 128, 255, 255, // elf symbols entry 4 addr
696            0, 240, 0, 0, // elf symbols entry 4 offset
697            0, 0, 0, 0, // elf symbols entry 4 offset
698            0, 80, 0, 0, // elf symbols entry 4 size
699            0, 0, 0, 0, // elf symbols entry 4 size
700            0, 0, 0, 0, // elf symbols entry 4 link
701            0, 0, 0, 0, // elf symbols entry 4 info
702            0, 16, 0, 0, // elf symbols entry 4 addralign
703            0, 0, 0, 0, // elf symbols entry 4 addralign
704            0, 0, 0, 0, // elf symbols entry 4 entsize
705            0, 0, 0, 0, // elf symbols entry 4 entsize
706            52, 0, 0, 0, // elf symbols entry 5 name
707            1, 0, 0, 0, // elf symbols entry 5 type
708            3, 0, 0, 0, // elf symbols entry 5 flags
709            0, 0, 0, 0, // elf symbols entry 5 flags
710            0, 48, 17, 0, // elf symbols entry 5 addr
711            0, 128, 255, 255, // elf symbols entry 5 addr
712            0, 240, 0, 0, // elf symbols entry 5 offset
713            0, 0, 0, 0, // elf symbols entry 5 offset
714            0, 0, 0, 0, // elf symbols entry 5 size
715            0, 0, 0, 0, // elf symbols entry 5 size
716            0, 0, 0, 0, // elf symbols entry 5 link
717            0, 0, 0, 0, // elf symbols entry 5 info
718            1, 0, 0, 0, // elf symbols entry 5 addralign
719            0, 0, 0, 0, // elf symbols entry 5 addralign
720            0, 0, 0, 0, // elf symbols entry 5 entsize
721            0, 0, 0, 0, // elf symbols entry 5 entsize
722            1, 0, 0, 0, // elf symbols entry 6 name
723            2, 0, 0, 0, // elf symbols entry 6 type
724            0, 0, 0, 0, // elf symbols entry 6 flags
725            0, 0, 0, 0, // elf symbols entry 6 flags
726            0, 48, 17, 0, // elf symbols entry 6 addr
727            0, 0, 0, 0, // elf symbols entry 6 addr
728            0, 240, 0, 0, // elf symbols entry 6 offset
729            0, 0, 0, 0, // elf symbols entry 6 offset
730            224, 43, 0, 0, // elf symbols entry 6 size
731            0, 0, 0, 0, // elf symbols entry 6 size
732            7, 0, 0, 0, // elf symbols entry 6 link
733            102, 1, 0, 0, // elf symbols entry 6 info
734            8, 0, 0, 0, // elf symbols entry 6 addralign
735            0, 0, 0, 0, // elf symbols entry 6 addralign
736            24, 0, 0, 0, // elf symbols entry 6 entsize
737            0, 0, 0, 0, // elf symbols entry 6 entsize
738            9, 0, 0, 0, // elf symbols entry 7 name
739            3, 0, 0, 0, // elf symbols entry 7 type
740            0, 0, 0, 0, // elf symbols entry 7 flags
741            0, 0, 0, 0, // elf symbols entry 7 flags
742            224, 91, 17, 0, // elf symbols entry 7 addr
743            0, 0, 0, 0, // elf symbols entry 7 addr
744            224, 27, 1, 0, // elf symbols entry 7 offset
745            0, 0, 0, 0, // elf symbols entry 7 offset
746            145, 55, 0, 0, // elf symbols entry 7 size
747            0, 0, 0, 0, // elf symbols entry 7 size
748            0, 0, 0, 0, // elf symbols entry 7 link
749            0, 0, 0, 0, // elf symbols entry 7 info
750            1, 0, 0, 0, // elf symbols entry 7 addralign
751            0, 0, 0, 0, // elf symbols entry 7 addralign
752            0, 0, 0, 0, // elf symbols entry 7 entsize
753            0, 0, 0, 0, // elf symbols entry 7 entsize
754            17, 0, 0, 0, // elf symbols entry 8 name
755            3, 0, 0, 0, // elf symbols entry 8 type
756            0, 0, 0, 0, // elf symbols entry 8 flags
757            0, 0, 0, 0, // elf symbols entry 8 flags
758            113, 147, 17, 0, // elf symbols entry 8 addr
759            0, 0, 0, 0, // elf symbols entry 8 addr
760            113, 83, 1, 0, // elf symbols entry 8 offset
761            0, 0, 0, 0, // elf symbols entry 8 offset
762            65, 0, 0, 0, // elf symbols entry 8 size
763            0, 0, 0, 0, // elf symbols entry 8 size
764            0, 0, 0, 0, // elf symbols entry 8 link
765            0, 0, 0, 0, // elf symbols entry 8 info
766            1, 0, 0, 0, // elf symbols entry 8 addralign
767            0, 0, 0, 0, // elf symbols entry 8 addralign
768            0, 0, 0, 0, // elf symbols entry 8 entsize
769            0, 0, 0, 0, // elf symbols entry 8 entsize
770            0, 0, 0, 0, // elf symbols padding
771            4, 0, 0, 0, // basic memory tag type
772            16, 0, 0, 0, // basic memory tag size
773            127, 2, 0, 0, // basic memory mem_lower
774            128, 251, 1, 0, // basic memory mem_upper
775            5, 0, 0, 0, // BIOS boot device tag type
776            20, 0, 0, 0, // BIOS boot device tag size
777            224, 0, 0, 0, // BIOS boot device biosdev
778            255, 255, 255, 255, // BIOS boot device partition
779            255, 255, 255, 255, // BIOS boot device subpartition
780            0, 0, 0, 0, // BIOS boot device padding
781            8, 0, 0, 0, // framebuffer info tag type
782            32, 0, 0, 0, // framebuffer info tag size
783            0, 128, 11, 0, // framebuffer info framebuffer_addr
784            0, 0, 0, 0, // framebuffer info framebuffer_addr
785            160, 0, 0, 0, // framebuffer info framebuffer_pitch
786            80, 0, 0, 0, // framebuffer info framebuffer_width
787            25, 0, 0, 0, // framebuffer info framebuffer_height
788            16, 2, 0, 0, // framebuffer info framebuffer_[bpp,type], reserved, color_info
789            14, 0, 0, 0, // ACPI old tag type
790            28, 0, 0, 0, // ACPI old tag size
791            82, 83, 68, 32, // ACPI old
792            80, 84, 82, 32, // ACPI old
793            89, 66, 79, 67, // ACPI old
794            72, 83, 32, 0, // ACPI old
795            220, 24, 254, 7, // ACPI old
796            0, 0, 0, 0, // ACPI old padding
797            0, 0, 0, 0, // end tag type
798            8, 0, 0, 0, // end tag size
799        ]);
800        #[repr(C, align(8))]
801        struct StringBytes([u8; 65]);
802        let string_bytes: StringBytes = StringBytes([
803            0, 46, 115, 121, 109, 116, 97, 98, 0, 46, 115, 116, 114, 116, 97, 98, 0, 46, 115, 104,
804            115, 116, 114, 116, 97, 98, 0, 46, 114, 111, 100, 97, 116, 97, 0, 46, 116, 101, 120,
805            116, 0, 46, 100, 97, 116, 97, 0, 46, 98, 115, 115, 0, 46, 100, 97, 116, 97, 46, 114,
806            101, 108, 46, 114, 111, 0,
807        ]);
808        let string_addr = string_bytes.0.as_ptr() as u64;
809        for i in 0..8 {
810            bytes.0[796 + i] = (string_addr >> (i * 8)) as u8;
811        }
812        let ptr = bytes.0.as_ptr();
813        let addr = ptr as usize;
814        let bi = unsafe { BootInformation::load(ptr.cast()) };
815        let bi = bi.unwrap();
816        test_grub2_boot_info(&bi, addr, string_addr, &bytes.0, &string_bytes.0);
817
818        // Check that the MBI's debug output can be printed without SEGFAULT.
819        // If this works, it is a good indicator than transitively a lot of
820        // stuff works.
821        println!("{bi:#?}");
822    }
823
824    /// Helper for [`grub2`].
825    #[allow(clippy::cognitive_complexity)]
826    fn test_grub2_boot_info(
827        bi: &BootInformation,
828        addr: usize,
829        string_addr: u64,
830        bytes: &[u8],
831        string_bytes: &[u8],
832    ) {
833        assert_eq!(addr, bi.start_address());
834        assert_eq!(addr + bytes.len(), bi.end_address());
835        assert_eq!(bytes.len(), bi.total_size());
836        let mut es = bi.elf_sections_tag().unwrap().sections();
837        let s1 = es.next().expect("Should have one more section");
838        assert_eq!(".rodata", s1.name().expect("Should be valid utf-8"));
839        assert_eq!(0xFFFF_8000_0010_0000, s1.start_address());
840        assert_eq!(0xFFFF_8000_0010_3000, s1.end_address());
841        assert_eq!(0x0000_0000_0000_3000, s1.size());
842        assert_eq!(ElfSectionFlags::ALLOCATED, s1.flags());
843        assert_eq!(ElfSectionType::ProgramSection, s1.section_type());
844        let s2 = es.next().expect("Should have one more section");
845        assert_eq!(".text", s2.name().expect("Should be valid utf-8"));
846        assert_eq!(0xFFFF_8000_0010_3000, s2.start_address());
847        assert_eq!(0xFFFF_8000_0010_C000, s2.end_address());
848        assert_eq!(0x0000_0000_0000_9000, s2.size());
849        assert_eq!(
850            ElfSectionFlags::EXECUTABLE | ElfSectionFlags::ALLOCATED,
851            s2.flags()
852        );
853        assert_eq!(ElfSectionType::ProgramSection, s2.section_type());
854        let s3 = es.next().expect("Should have one more section");
855        assert_eq!(".data", s3.name().expect("Should be valid utf-8"));
856        assert_eq!(0xFFFF_8000_0010_C000, s3.start_address());
857        assert_eq!(0xFFFF_8000_0010_E000, s3.end_address());
858        assert_eq!(0x0000_0000_0000_2000, s3.size());
859        assert_eq!(
860            ElfSectionFlags::ALLOCATED | ElfSectionFlags::WRITABLE,
861            s3.flags()
862        );
863        assert_eq!(ElfSectionType::ProgramSection, s3.section_type());
864        let s4 = es.next().expect("Should have one more section");
865        assert_eq!(".bss", s4.name().expect("Should be valid utf-8"));
866        assert_eq!(0xFFFF_8000_0010_E000, s4.start_address());
867        assert_eq!(0xFFFF_8000_0011_3000, s4.end_address());
868        assert_eq!(0x0000_0000_0000_5000, s4.size());
869        assert_eq!(
870            ElfSectionFlags::ALLOCATED | ElfSectionFlags::WRITABLE,
871            s4.flags()
872        );
873        assert_eq!(ElfSectionType::Uninitialized, s4.section_type());
874        let s5 = es.next().expect("Should have one more section");
875        assert_eq!(".data.rel.ro", s5.name().expect("Should be valid utf-8"));
876        assert_eq!(0xFFFF_8000_0011_3000, s5.start_address());
877        assert_eq!(0xFFFF_8000_0011_3000, s5.end_address());
878        assert_eq!(0x0000_0000_0000_0000, s5.size());
879        assert_eq!(
880            ElfSectionFlags::ALLOCATED | ElfSectionFlags::WRITABLE,
881            s5.flags()
882        );
883        assert_eq!(ElfSectionType::ProgramSection, s5.section_type());
884        let s6 = es.next().expect("Should have one more section");
885        assert_eq!(".symtab", s6.name().expect("Should be valid utf-8"));
886        assert_eq!(0x0000_0000_0011_3000, s6.start_address());
887        assert_eq!(0x0000_0000_0011_5BE0, s6.end_address());
888        assert_eq!(0x0000_0000_0000_2BE0, s6.size());
889        assert_eq!(ElfSectionFlags::empty(), s6.flags());
890        assert_eq!(ElfSectionType::LinkerSymbolTable, s6.section_type());
891        let s7 = es.next().expect("Should have one more section");
892        assert_eq!(".strtab", s7.name().expect("Should be valid utf-8"));
893        assert_eq!(0x0000_0000_0011_5BE0, s7.start_address());
894        assert_eq!(0x0000_0000_0011_9371, s7.end_address());
895        assert_eq!(0x0000_0000_0000_3791, s7.size());
896        assert_eq!(ElfSectionFlags::empty(), s7.flags());
897        assert_eq!(ElfSectionType::StringTable, s7.section_type());
898        let s8 = es.next().expect("Should have one more section");
899        assert_eq!(".shstrtab", s8.name().expect("Should be valid utf-8"));
900        assert_eq!(string_addr, s8.start_address());
901        assert_eq!(string_addr + string_bytes.len() as u64, s8.end_address());
902        assert_eq!(string_bytes.len() as u64, s8.size());
903        assert_eq!(ElfSectionFlags::empty(), s8.flags());
904        assert_eq!(ElfSectionType::StringTable, s8.section_type());
905        assert!(es.next().is_none());
906        let mut mm = bi
907            .memory_map_tag()
908            .unwrap()
909            .memory_areas()
910            .iter()
911            .filter(|area| area.typ() == MemoryAreaType::Available);
912        let mm1 = mm.next().unwrap();
913        assert_eq!(0x00000000, mm1.start_address());
914        assert_eq!(0x009_FC00, mm1.end_address());
915        assert_eq!(0x009_FC00, mm1.size());
916        assert_eq!(MemoryAreaType::Available, mm1.typ());
917        let mm2 = mm.next().unwrap();
918        assert_eq!(0x010_0000, mm2.start_address());
919        assert_eq!(0x7FE_0000, mm2.end_address());
920        assert_eq!(0x7EE_0000, mm2.size());
921        assert_eq!(MemoryAreaType::Available, mm2.typ());
922        assert!(mm.next().is_none());
923
924        // Test the RSDP tag
925        let rsdp_old = bi.rsdp_v1_tag().unwrap();
926        assert_eq!("RSD PTR ", rsdp_old.signature().unwrap());
927        assert!(rsdp_old.checksum_is_valid());
928        assert_eq!("BOCHS ", rsdp_old.oem_id().unwrap());
929        assert_eq!(0, rsdp_old.revision());
930        assert_eq!(0x7FE18DC, rsdp_old.rsdt_address());
931
932        assert!(bi.module_tags().next().is_none());
933        assert_eq!(
934            "GRUB 2.02~beta3-5",
935            bi.boot_loader_name_tag()
936                .expect("tag must be present")
937                .name()
938                .expect("must be valid utf-8")
939        );
940        assert_eq!(
941            "",
942            bi.command_line_tag()
943                .expect("tag must present")
944                .cmdline()
945                .expect("must be valid utf-8")
946        );
947
948        // Test the Framebuffer tag
949        let fbi = bi
950            .framebuffer_tag()
951            .expect("Framebuffer info should be available")
952            .expect("Framebuffer info type should be valid");
953        assert_eq!(fbi.address(), 753664);
954        assert_eq!(fbi.pitch(), 160);
955        assert_eq!(fbi.width(), 80);
956        assert_eq!(fbi.height(), 25);
957        assert_eq!(fbi.bpp(), 16);
958        assert_eq!(fbi.buffer_type(), Ok(FramebufferType::Text));
959    }
960
961    #[test]
962    fn elf_sections() {
963        let mut bytes = AlignedBytes([
964            168, 0, 0, 0, // total_size
965            0, 0, 0, 0, // reserved
966            9, 0, 0, 0, // elf symbols tag type
967            148, 0, 0, 0, // elf symbols tag size
968            2, 0, 0, 0, // elf symbols num
969            64, 0, 0, 0, // elf symbols entsize
970            1, 0, 0, 0, // elf symbols shndx
971            0, 0, 0, 0, // elf symbols entry 0 name
972            0, 0, 0, 0, // elf symbols entry 0 type
973            0, 0, 0, 0, // elf symbols entry 0 flags
974            0, 0, 0, 0, // elf symbols entry 0 flags
975            0, 0, 0, 0, // elf symbols entry 0 addr
976            0, 0, 0, 0, // elf symbols entry 0 addr
977            0, 0, 0, 0, // elf symbols entry 0 offset
978            0, 0, 0, 0, // elf symbols entry 0 offset
979            0, 0, 0, 0, // elf symbols entry 0 size
980            0, 0, 0, 0, // elf symbols entry 0 size
981            0, 0, 0, 0, // elf symbols entry 0 link
982            0, 0, 0, 0, // elf symbols entry 0 info
983            0, 0, 0, 0, // elf symbols entry 0 addralign
984            0, 0, 0, 0, // elf symbols entry 0 addralign
985            0, 0, 0, 0, // elf symbols entry 0 entsize
986            0, 0, 0, 0, // elf symbols entry 0 entsize
987            1, 0, 0, 0, // elf symbols entry 1 name
988            3, 0, 0, 0, // elf symbols entry 1 type
989            0, 0, 0, 0, // elf symbols entry 1 flags
990            0, 0, 0, 0, // elf symbols entry 1 flags
991            255, 255, 255, 255, // elf symbols entry 1 addr
992            255, 255, 255, 255, // elf symbols entry 1 addr
993            113, 83, 1, 0, // elf symbols entry 1 offset
994            0, 0, 0, 0, // elf symbols entry 1 offset
995            11, 0, 0, 0, // elf symbols entry 1 size
996            0, 0, 0, 0, // elf symbols entry 1 size
997            0, 0, 0, 0, // elf symbols entry 1 link
998            0, 0, 0, 0, // elf symbols entry 1 info
999            1, 0, 0, 0, // elf symbols entry 1 addralign
1000            0, 0, 0, 0, // elf symbols entry 1 addralign
1001            0, 0, 0, 0, // elf symbols entry 1 entsize
1002            0, 0, 0, 0, // elf symbols entry 1 entsize
1003            0, 0, 0, 0, // elf symbols padding
1004            0, 0, 0, 0, // end tag type
1005            8, 0, 0, 0, // end tag size
1006        ]);
1007        #[repr(C, align(8))]
1008        struct StringBytes([u8; 11]);
1009        let string_bytes: StringBytes = StringBytes(*b"\0.shstrtab\0");
1010        let string_addr = string_bytes.0.as_ptr() as u64;
1011        for i in 0..8 {
1012            let offset = 108;
1013            assert_eq!(255, bytes.0[offset + i]);
1014            bytes.0[offset + i] = (string_addr >> (i * 8)) as u8;
1015        }
1016        let ptr = bytes.0.as_ptr();
1017        let addr = ptr as usize;
1018        let bi = unsafe { BootInformation::load(ptr.cast()) };
1019        let bi = bi.unwrap();
1020
1021        eprintln!("boot information with elf sections: {bi:#x?}");
1022
1023        assert_eq!(addr, bi.start_address());
1024        assert_eq!(addr + bytes.0.len(), bi.end_address());
1025        assert_eq!(bytes.0.len(), bi.total_size());
1026        let mut es = bi.elf_sections_tag().unwrap().sections();
1027        let s1 = es.next().expect("Should have one more section");
1028        assert_eq!(".shstrtab", s1.name().expect("Should be valid utf-8"));
1029        assert_eq!(string_addr, s1.start_address());
1030        assert_eq!(string_addr + string_bytes.0.len() as u64, s1.end_address());
1031        assert_eq!(string_bytes.0.len() as u64, s1.size());
1032        assert_eq!(ElfSectionFlags::empty(), s1.flags());
1033        assert_eq!(ElfSectionType::StringTable, s1.section_type());
1034        assert!(es.next().is_none());
1035    }
1036
1037    #[test]
1038    fn efi_memory_map() {
1039        // test that the EFI memory map is detected.
1040        let bytes = AlignedBytes([
1041            80, 0, 0, 0, // size
1042            0, 0, 0, 0, // reserved
1043            17, 0, 0, 0, // EFI memory map type
1044            64, 0, 0, 0, // EFI memory map size
1045            48, 0, 0, 0, // EFI descriptor size
1046            1, 0, 0, 0, // EFI descriptor version
1047            7, 0, 0, 0, // Type: EfiConventionalMemory
1048            0, 0, 0, 0, // Padding
1049            0, 0, 16, 0, // Physical Address: should be 0x100000
1050            0, 0, 0, 0, // Extension of physical address.
1051            0, 0, 16, 0, // Virtual Address: should be 0x100000
1052            0, 0, 0, 0, // Extension of virtual address.
1053            4, 0, 0, 0, // 4 KiB Pages: 16 KiB
1054            0, 0, 0, 0, // Extension of pages
1055            0, 0, 0, 0, // Attributes of this memory range.
1056            0, 0, 0, 0, // Extension of attributes
1057            0, 0, 0, 0, // More padding to extend the efi mmap to `desc_size`.
1058            0, 0, 0, 0, // padding/alignment for end tag
1059            0, 0, 0, 0, // end tag type.
1060            8, 0, 0, 0, // end tag size.
1061        ]);
1062        let ptr = bytes.0.as_ptr();
1063        let addr = ptr as usize;
1064        let bi = unsafe { BootInformation::load(ptr.cast()) };
1065        let bi = bi.unwrap();
1066        assert_eq!(addr, bi.start_address());
1067        assert_eq!(addr + bytes.0.len(), bi.end_address());
1068        assert_eq!(bytes.0.len(), bi.total_size());
1069        let efi_memory_map = bi.efi_memory_map_tag().unwrap();
1070        let mut efi_mmap_iter = efi_memory_map.memory_areas();
1071        let desc = efi_mmap_iter.next().unwrap();
1072        assert_eq!(desc.phys_start, 0x100000);
1073        assert_eq!(desc.page_count, 4);
1074        assert_eq!(desc.ty, EFIMemoryAreaType::CONVENTIONAL);
1075        let bytes2 = AlignedBytes([
1076            80, 0, 0, 0, // size
1077            0, 0, 0, 0, // reserved
1078            17, 0, 0, 0, // EFI memory map type
1079            56, 0, 0, 0, // EFI memory map size
1080            48, 0, 0, 0, // EFI descriptor size
1081            1, 0, 0, 0, // EFI descriptor version, don't think this matters.
1082            7, 0, 0, 0, // Type: EfiConventionalMemory
1083            0, 0, 0, 0, // Padding
1084            0, 0, 16, 0, // Physical Address: should be 0x100000
1085            0, 0, 0, 0, // Extension of physical address.
1086            0, 0, 16, 0, // Virtual Address: should be 0x100000
1087            0, 0, 0, 0, // Extension of virtual address.
1088            4, 0, 0, 0, // 4 KiB Pages: 16 KiB
1089            0, 0, 0, 0, // Extension of pages
1090            0, 0, 0, 0, // Attributes of this memory range.
1091            0, 0, 0, 0, // Extension of attributes
1092            18, 0, 0, 0, // Tag ExitBootServices not terminated.
1093            8, 0, 0, 0, // Tag ExitBootServices size.
1094            0, 0, 0, 0, // end tag type.
1095            8, 0, 0, 0, // end tag size.
1096        ]);
1097        let bi = unsafe { BootInformation::load(bytes2.as_ptr().cast()) };
1098        let bi = bi.unwrap();
1099        let efi_mmap = bi.efi_memory_map_tag();
1100        assert!(efi_mmap.is_none());
1101    }
1102
1103    #[test]
1104    /// This test succeeds if it compiles.
1105    fn mbi_load_error_implements_error() {
1106        fn consumer<E: core::error::Error>(_e: E) {}
1107        consumer(LoadError::NoEndTag)
1108    }
1109
1110    /// Example for a custom tag.
1111    #[test]
1112    fn get_custom_tag_from_mbi() {
1113        #[repr(C, align(8))]
1114        struct CustomTag {
1115            tag: TagTypeId,
1116            size: u32,
1117            foo: u32,
1118        }
1119
1120        impl MaybeDynSized for CustomTag {
1121            type Header = TagHeader;
1122
1123            const BASE_SIZE: usize = mem::size_of::<Self>();
1124
1125            fn dst_len(_: &TagHeader) -> Self::Metadata {}
1126        }
1127
1128        impl Tag for CustomTag {
1129            type IDType = TagType;
1130
1131            const ID: TagType = TagType::Custom(0x1337);
1132        }
1133
1134        // Raw bytes of a MBI that only contains the custom tag.
1135        let bytes = AlignedBytes([
1136            32,
1137            0,
1138            0,
1139            0, // end: total size
1140            0,
1141            0,
1142            0,
1143            0, // end: padding; end of multiboot2 boot information begin
1144            CustomTag::ID.val().to_le_bytes()[0],
1145            CustomTag::ID.val().to_le_bytes()[1],
1146            CustomTag::ID.val().to_le_bytes()[2],
1147            CustomTag::ID.val().to_le_bytes()[3], // end: my custom tag id
1148            12,
1149            0,
1150            0,
1151            0, // end: tag size
1152            42,
1153            0,
1154            0,
1155            0,
1156            0,
1157            0,
1158            0,
1159            0, // 8 byte padding
1160            0,
1161            0,
1162            0,
1163            0, // end: end tag type
1164            8,
1165            0,
1166            0,
1167            0, // end: end tag size
1168        ]);
1169        let ptr = bytes.0.as_ptr();
1170        let addr = ptr as usize;
1171        let bi = unsafe { BootInformation::load(ptr.cast()) };
1172        let bi = bi.unwrap();
1173        assert_eq!(addr, bi.start_address());
1174        assert_eq!(addr + bytes.0.len(), bi.end_address());
1175        assert_eq!(bytes.0.len(), bi.total_size());
1176
1177        let tag = bi.get_tag::<CustomTag>().unwrap();
1178        assert_eq!(tag.foo, 42);
1179    }
1180
1181    /// Example for a custom DST tag.
1182    #[test]
1183    fn get_custom_dst_tag_from_mbi() {
1184        #[repr(C)]
1185        #[derive(crate::Pointee)]
1186        struct CustomTag {
1187            tag: TagTypeId,
1188            size: u32,
1189            name: [u8],
1190        }
1191
1192        impl CustomTag {
1193            fn name(&self) -> Result<&str, StringError> {
1194                parse_slice_as_string(&self.name)
1195            }
1196        }
1197
1198        impl MaybeDynSized for CustomTag {
1199            type Header = TagHeader;
1200
1201            const BASE_SIZE: usize = mem::size_of::<TagHeader>() + mem::size_of::<u32>();
1202
1203            fn dst_len(header: &TagHeader) -> usize {
1204                // The size of the sized portion of the command line tag.
1205                let tag_base_size = 8;
1206                assert!(header.size >= 8);
1207                header.size as usize - tag_base_size
1208            }
1209        }
1210
1211        impl Tag for CustomTag {
1212            type IDType = TagType;
1213
1214            const ID: TagType = TagType::Custom(0x1337);
1215        }
1216
1217        // Raw bytes of a MBI that only contains the custom tag.
1218        let bytes = AlignedBytes([
1219            32,
1220            0,
1221            0,
1222            0, // end: total size
1223            0,
1224            0,
1225            0,
1226            0, // end: padding; end of multiboot2 boot information begin
1227            CustomTag::ID.val().to_le_bytes()[0],
1228            CustomTag::ID.val().to_le_bytes()[1],
1229            CustomTag::ID.val().to_le_bytes()[2],
1230            CustomTag::ID.val().to_le_bytes()[3], // end: my custom tag id
1231            14,
1232            0,
1233            0,
1234            0, // end: tag size
1235            b'h',
1236            b'e',
1237            b'l',
1238            b'l',
1239            b'o',
1240            b'\0',
1241            0,
1242            0, // 2 byte padding
1243            0,
1244            0,
1245            0,
1246            0, // end: end tag type
1247            8,
1248            0,
1249            0,
1250            0, // end: end tag size
1251        ]);
1252        let ptr = bytes.0.as_ptr();
1253        let addr = ptr as usize;
1254        let bi = unsafe { BootInformation::load(ptr.cast()) };
1255        let bi = bi.unwrap();
1256        assert_eq!(addr, bi.start_address());
1257        assert_eq!(addr + bytes.0.len(), bi.end_address());
1258        assert_eq!(bytes.0.len(), bi.total_size());
1259
1260        let tag = bi.get_tag::<CustomTag>().unwrap();
1261        assert_eq!(tag.name(), Ok("hello"));
1262    }
1263
1264    /// Tests that `get_tag` can consume multiple types that implement `Into<TagTypeId>`
1265    #[test]
1266    fn get_tag_into_variants() {
1267        let bytes = AlignedBytes([
1268            32,
1269            0,
1270            0,
1271            0, // total_size
1272            0,
1273            0,
1274            0,
1275            0, // reserved
1276            TagType::Cmdline.val().to_le_bytes()[0],
1277            TagType::Cmdline.val().to_le_bytes()[1],
1278            TagType::Cmdline.val().to_le_bytes()[2],
1279            TagType::Cmdline.val().to_le_bytes()[3],
1280            13,
1281            0,
1282            0,
1283            0, // tag size
1284            110,
1285            97,
1286            109,
1287            101, // ASCII string 'name'
1288            0,
1289            0,
1290            0,
1291            0, // null byte + padding
1292            0,
1293            0,
1294            0,
1295            0, // end tag type
1296            8,
1297            0,
1298            0,
1299            0, // end tag size
1300        ]);
1301
1302        let ptr = bytes.0.as_ptr();
1303        let bi = unsafe { BootInformation::load(ptr.cast()) };
1304        let bi = bi.unwrap();
1305
1306        let _tag = bi.get_tag::<CommandLineTag>().unwrap();
1307    }
1308}