1#![no_std]
2#![deny(
4 clippy::all,
5 clippy::cargo,
6 clippy::nursery,
7 clippy::must_use_candidate,
8 )]
11#![allow(clippy::multiple_crate_versions)]
14#![deny(missing_docs)]
15#![deny(missing_debug_implementations)]
16#![deny(rustdoc::all)]
17#[cfg_attr(feature = "builder", macro_use)]
47#[cfg(feature = "builder")]
48extern crate alloc;
49
50#[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
61pub type TagIter<'a> = multiboot2_common::TagIter<'a, TagHeader>;
63
64#[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
123pub 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 #[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, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, ]);
147 let ptr = bytes.0.as_ptr();
148 let bi = unsafe { BootInformation::load(ptr.cast()) };
149 let bi = bi.unwrap();
150
151 accept(bi);
153 }
154
155 #[test]
156 fn no_tags() {
157 let bytes = AlignedBytes([
158 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, ]);
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, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, ]);
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, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, ]);
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, 0, 0, 0, 0, 2, 0, 0, 0, 13, 0, 0, 0, 110, 97, 109, 101, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, ]);
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 let bytes = AlignedBytes([
261 56, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 253, 0, 0, 0, 0, 0, 20, 0, 0, 0, 5, 0, 0, 208, 2, 0, 0, 32, 1, 0, 0, 16, 8, 8, 8, 0, 8, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, ]);
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 #[rustfmt::skip]
318 let bytes = AlignedBytes([
319 64, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 253, 0, 0, 0, 0, 0, 20, 0, 0, 0, 5, 0, 0, 208, 2, 0, 0, 32, 0, 0, 0, 4, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 3, 7, 73, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, ]);
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 let bytes = AlignedBytes([
392 32, 3, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 16, 3, 0, 0, 122, 65, 255, 255, 0, 96, 79, 0, 86, 69, 83, 65, 0, 3, 220, 87, 0, 192, 1, 0, 0, 0, 34, 128, 0, 96, 0, 1, 0, 0, 240, 87, 0, 192, 3,
405 88, 0, 192, 23,
407 88, 0, 192, 0, 1, 1, 1, 2, 1, 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, 187, 0, 7, 0, 64, 0, 64, 0, 0, 160, 0, 0, 186, 84, 0, 192, 0, 20, 0, 5, 32, 3, 8, 16, 1, 32, 1, 6, 0, 3, 1, 8, 16, 8, 8,
437 8, 0, 8, 24,
439 2, 0, 0, 0, 253, 0, 0, 0, 0, 0, 0, 0, 20, 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, 0, 0, 0, 0, 8, 0, 0, 0, ]);
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 fn vbe_info_tag_size() {
548 unsafe {
549 core::mem::transmute::<[u8; 784], VBEInfoTag>([0u8; 784]);
551 }
552 }
553
554 #[test]
557 fn grub2() {
558 let mut bytes = AlignedBytes([
559 192, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 26, 0, 0, 0, 71, 82, 85, 66, 32, 50, 46, 48, 50, 126, 98, 101, 116, 97, 51, 45, 53, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 28, 0, 0, 0, 2, 1, 0, 240, 207, 212, 0, 0, 0, 240, 0, 240, 3, 0, 240, 255, 240, 255, 240, 255, 0, 0, 0, 0, 6, 0, 0, 0, 160, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 252, 9, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 238, 7, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 7, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 255, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 84, 2, 0, 0, 9, 0, 0, 0, 64, 0, 0, 0, 8, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 128, 255, 255, 0, 16, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 1, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 48, 16, 0, 0, 128, 255, 255, 0, 64, 0, 0, 0, 0, 0, 0, 0, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 192, 16, 0, 0, 128, 255, 255, 0, 208, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 224, 16, 0, 0, 128, 255, 255, 0, 240, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 48, 17, 0, 0, 128, 255, 255, 0, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 17, 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 224, 43, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 102, 1, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 91, 17, 0, 0, 0, 0, 0, 224, 27, 1, 0, 0, 0, 0, 0, 145, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113, 147, 17, 0, 0, 0, 0, 0, 113, 83, 1, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 16, 0, 0, 0, 127, 2, 0, 0, 128, 251, 1, 0, 5, 0, 0, 0, 20, 0, 0, 0, 224, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 8, 0, 0, 0, 32, 0, 0, 0, 0, 128, 11, 0, 0, 0, 0, 0, 160, 0, 0, 0, 80, 0, 0, 0, 25, 0, 0, 0, 16, 2, 0, 0, 14, 0, 0, 0, 28, 0, 0, 0, 82, 83, 68, 32, 80, 84, 82, 32, 89, 66, 79, 67, 72, 83, 32, 0, 220, 24, 254, 7, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, ]);
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 println!("{bi:#?}");
822 }
823
824 #[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 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 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, 0, 0, 0, 0, 9, 0, 0, 0, 148, 0, 0, 0, 2, 0, 0, 0, 64, 0, 0, 0, 1, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 113, 83, 1, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, ]);
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 let bytes = AlignedBytes([
1041 80, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 64, 0, 0, 0, 48, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 4, 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, 8, 0, 0, 0, ]);
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, 0, 0, 0, 0, 17, 0, 0, 0, 56, 0, 0, 0, 48, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, ]);
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 fn mbi_load_error_implements_error() {
1106 fn consumer<E: core::error::Error>(_e: E) {}
1107 consumer(LoadError::NoEndTag)
1108 }
1109
1110 #[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 let bytes = AlignedBytes([
1136 32,
1137 0,
1138 0,
1139 0, 0,
1141 0,
1142 0,
1143 0, 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], 12,
1149 0,
1150 0,
1151 0, 42,
1153 0,
1154 0,
1155 0,
1156 0,
1157 0,
1158 0,
1159 0, 0,
1161 0,
1162 0,
1163 0, 8,
1165 0,
1166 0,
1167 0, ]);
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 #[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 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 let bytes = AlignedBytes([
1219 32,
1220 0,
1221 0,
1222 0, 0,
1224 0,
1225 0,
1226 0, 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], 14,
1232 0,
1233 0,
1234 0, b'h',
1236 b'e',
1237 b'l',
1238 b'l',
1239 b'o',
1240 b'\0',
1241 0,
1242 0, 0,
1244 0,
1245 0,
1246 0, 8,
1248 0,
1249 0,
1250 0, ]);
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 #[test]
1266 fn get_tag_into_variants() {
1267 let bytes = AlignedBytes([
1268 32,
1269 0,
1270 0,
1271 0, 0,
1273 0,
1274 0,
1275 0, 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, 110,
1285 97,
1286 109,
1287 101, 0,
1289 0,
1290 0,
1291 0, 0,
1293 0,
1294 0,
1295 0, 8,
1297 0,
1298 0,
1299 0, ]);
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}