// Copyright 2024 The ChromiumOS Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //! Multiboot specification 0.6.96 definitions //! //! use zerocopy::FromBytes; use zerocopy::Immutable; use zerocopy::IntoBytes; use zerocopy::KnownLayout; /// Magic value stored in EAX to indicate bootloader is Multiboot compliant. pub const MULTIBOOT_BOOTLOADER_MAGIC: u32 = 0x2BADB002; #[derive(Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)] #[repr(C, packed)] pub struct MultibootInfo { pub flags: u32, pub mem_lower: u32, pub mem_upper: u32, pub boot_device: u32, pub cmdline: u32, pub mods_count: u32, pub mods_addr: u32, // TODO: add union for ELF + a.out symbols if needed pub syms: [u32; 4], pub mmap_length: u32, pub mmap_addr: u32, pub drives_length: u32, pub drives_addr: u32, pub config_table: u32, pub boot_loader_name: u32, pub apm_table: u32, pub vbe_control_info: u32, pub vbe_mode_info: u32, pub vbe_mode: u16, pub vbe_interface_seg: u16, pub vbe_interface_off: u16, pub vbe_interface_len: u16, pub framebuffer_addr: u64, pub framebuffer_pitch: u32, pub framebuffer_width: u32, pub framebuffer_height: u32, pub framebuffer_bpp: u8, pub framebuffer_type: u8, // TODO: add union for palette + RGB color info if needed pub color_info: [u8; 6], } impl MultibootInfo { pub const F_MEM: u32 = 1 << 0; pub const F_BOOT_DEVICE: u32 = 1 << 1; pub const F_CMDLINE: u32 = 1 << 2; pub const F_MODS: u32 = 1 << 3; pub const F_SYMS_AOUT: u32 = 1 << 4; pub const F_SYMS_ELF: u32 = 1 << 5; pub const F_MMAP: u32 = 1 << 6; pub const F_DRIVES: u32 = 1 << 7; pub const F_CONFIG_TABLE: u32 = 1 << 8; pub const F_BOOT_LOADER_NAME: u32 = 1 << 9; pub const F_APM_TABLE: u32 = 1 << 10; pub const F_VBE: u32 = 1 << 11; pub const F_FRAMEBUFFER: u32 = 1 << 12; } #[derive(Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)] #[repr(C, packed)] pub struct MultibootMmapEntry { pub size: u32, pub base_addr: u64, pub length: u64, pub type_: u32, } #[cfg(test)] mod tests { use std::mem::offset_of; use std::mem::size_of; use super::*; #[test] fn test_multiboot_info_offsets() { // Validate that multiboot_info field offsets match the spec. assert_eq!(0, offset_of!(MultibootInfo, flags)); assert_eq!(4, offset_of!(MultibootInfo, mem_lower)); assert_eq!(8, offset_of!(MultibootInfo, mem_upper)); assert_eq!(8, offset_of!(MultibootInfo, mem_upper)); assert_eq!(12, offset_of!(MultibootInfo, boot_device)); assert_eq!(16, offset_of!(MultibootInfo, cmdline)); assert_eq!(20, offset_of!(MultibootInfo, mods_count)); assert_eq!(24, offset_of!(MultibootInfo, mods_addr)); assert_eq!(28, offset_of!(MultibootInfo, syms)); assert_eq!(44, offset_of!(MultibootInfo, mmap_length)); assert_eq!(48, offset_of!(MultibootInfo, mmap_addr)); assert_eq!(52, offset_of!(MultibootInfo, drives_length)); assert_eq!(56, offset_of!(MultibootInfo, drives_addr)); assert_eq!(60, offset_of!(MultibootInfo, config_table)); assert_eq!(64, offset_of!(MultibootInfo, boot_loader_name)); assert_eq!(68, offset_of!(MultibootInfo, apm_table)); assert_eq!(72, offset_of!(MultibootInfo, vbe_control_info)); assert_eq!(76, offset_of!(MultibootInfo, vbe_mode_info)); assert_eq!(80, offset_of!(MultibootInfo, vbe_mode)); assert_eq!(82, offset_of!(MultibootInfo, vbe_interface_seg)); assert_eq!(84, offset_of!(MultibootInfo, vbe_interface_off)); assert_eq!(86, offset_of!(MultibootInfo, vbe_interface_len)); assert_eq!(88, offset_of!(MultibootInfo, framebuffer_addr)); assert_eq!(96, offset_of!(MultibootInfo, framebuffer_pitch)); assert_eq!(100, offset_of!(MultibootInfo, framebuffer_width)); assert_eq!(104, offset_of!(MultibootInfo, framebuffer_height)); assert_eq!(108, offset_of!(MultibootInfo, framebuffer_bpp)); assert_eq!(109, offset_of!(MultibootInfo, framebuffer_type)); assert_eq!(110, offset_of!(MultibootInfo, color_info)); assert_eq!(size_of::(), 116); } #[test] fn test_multiboot_mmap_entry_offsets() { // The spec defines the mmap entry structure in a confusing way (`size` at offset -4 and // `base_addr` at offset 0), but this does not match how both bootloaders and kernels have // implemented Multiboot (`size` at offset 0), so this does not exactly match the table in // the spec. assert_eq!(0, offset_of!(MultibootMmapEntry, size)); assert_eq!(4, offset_of!(MultibootMmapEntry, base_addr)); assert_eq!(12, offset_of!(MultibootMmapEntry, length)); assert_eq!(20, offset_of!(MultibootMmapEntry, type_)); assert_eq!(size_of::(), 24); // 20-byte e820 data + 4 bytes for size } }