x86_64/
multiboot_spec.rs

1// Copyright 2024 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Multiboot specification 0.6.96 definitions
6//!
7//! <https://www.gnu.org/software/grub/manual/multiboot/multiboot.html>
8
9use zerocopy::FromBytes;
10use zerocopy::Immutable;
11use zerocopy::IntoBytes;
12use zerocopy::KnownLayout;
13
14/// Magic value stored in EAX to indicate bootloader is Multiboot compliant.
15pub const MULTIBOOT_BOOTLOADER_MAGIC: u32 = 0x2BADB002;
16
17#[derive(Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
18#[repr(C, packed)]
19pub struct MultibootInfo {
20    pub flags: u32,
21
22    pub mem_lower: u32,
23    pub mem_upper: u32,
24
25    pub boot_device: u32,
26
27    pub cmdline: u32,
28
29    pub mods_count: u32,
30    pub mods_addr: u32,
31
32    // TODO: add union for ELF + a.out symbols if needed
33    pub syms: [u32; 4],
34
35    pub mmap_length: u32,
36    pub mmap_addr: u32,
37
38    pub drives_length: u32,
39    pub drives_addr: u32,
40
41    pub config_table: u32,
42
43    pub boot_loader_name: u32,
44
45    pub apm_table: u32,
46
47    pub vbe_control_info: u32,
48    pub vbe_mode_info: u32,
49    pub vbe_mode: u16,
50    pub vbe_interface_seg: u16,
51    pub vbe_interface_off: u16,
52    pub vbe_interface_len: u16,
53
54    pub framebuffer_addr: u64,
55    pub framebuffer_pitch: u32,
56    pub framebuffer_width: u32,
57    pub framebuffer_height: u32,
58    pub framebuffer_bpp: u8,
59    pub framebuffer_type: u8,
60
61    // TODO: add union for palette + RGB color info if needed
62    pub color_info: [u8; 6],
63}
64
65impl MultibootInfo {
66    pub const F_MEM: u32 = 1 << 0;
67    pub const F_BOOT_DEVICE: u32 = 1 << 1;
68    pub const F_CMDLINE: u32 = 1 << 2;
69    pub const F_MODS: u32 = 1 << 3;
70    pub const F_SYMS_AOUT: u32 = 1 << 4;
71    pub const F_SYMS_ELF: u32 = 1 << 5;
72    pub const F_MMAP: u32 = 1 << 6;
73    pub const F_DRIVES: u32 = 1 << 7;
74    pub const F_CONFIG_TABLE: u32 = 1 << 8;
75    pub const F_BOOT_LOADER_NAME: u32 = 1 << 9;
76    pub const F_APM_TABLE: u32 = 1 << 10;
77    pub const F_VBE: u32 = 1 << 11;
78    pub const F_FRAMEBUFFER: u32 = 1 << 12;
79}
80
81#[derive(Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
82#[repr(C, packed)]
83pub struct MultibootMmapEntry {
84    pub size: u32,
85    pub base_addr: u64,
86    pub length: u64,
87    pub type_: u32,
88}
89
90#[cfg(test)]
91mod tests {
92    use std::mem::offset_of;
93    use std::mem::size_of;
94
95    use super::*;
96
97    #[test]
98    fn test_multiboot_info_offsets() {
99        // Validate that multiboot_info field offsets match the spec.
100        assert_eq!(0, offset_of!(MultibootInfo, flags));
101        assert_eq!(4, offset_of!(MultibootInfo, mem_lower));
102        assert_eq!(8, offset_of!(MultibootInfo, mem_upper));
103        assert_eq!(8, offset_of!(MultibootInfo, mem_upper));
104        assert_eq!(12, offset_of!(MultibootInfo, boot_device));
105        assert_eq!(16, offset_of!(MultibootInfo, cmdline));
106        assert_eq!(20, offset_of!(MultibootInfo, mods_count));
107        assert_eq!(24, offset_of!(MultibootInfo, mods_addr));
108        assert_eq!(28, offset_of!(MultibootInfo, syms));
109        assert_eq!(44, offset_of!(MultibootInfo, mmap_length));
110        assert_eq!(48, offset_of!(MultibootInfo, mmap_addr));
111        assert_eq!(52, offset_of!(MultibootInfo, drives_length));
112        assert_eq!(56, offset_of!(MultibootInfo, drives_addr));
113        assert_eq!(60, offset_of!(MultibootInfo, config_table));
114        assert_eq!(64, offset_of!(MultibootInfo, boot_loader_name));
115        assert_eq!(68, offset_of!(MultibootInfo, apm_table));
116        assert_eq!(72, offset_of!(MultibootInfo, vbe_control_info));
117        assert_eq!(76, offset_of!(MultibootInfo, vbe_mode_info));
118        assert_eq!(80, offset_of!(MultibootInfo, vbe_mode));
119        assert_eq!(82, offset_of!(MultibootInfo, vbe_interface_seg));
120        assert_eq!(84, offset_of!(MultibootInfo, vbe_interface_off));
121        assert_eq!(86, offset_of!(MultibootInfo, vbe_interface_len));
122        assert_eq!(88, offset_of!(MultibootInfo, framebuffer_addr));
123        assert_eq!(96, offset_of!(MultibootInfo, framebuffer_pitch));
124        assert_eq!(100, offset_of!(MultibootInfo, framebuffer_width));
125        assert_eq!(104, offset_of!(MultibootInfo, framebuffer_height));
126        assert_eq!(108, offset_of!(MultibootInfo, framebuffer_bpp));
127        assert_eq!(109, offset_of!(MultibootInfo, framebuffer_type));
128        assert_eq!(110, offset_of!(MultibootInfo, color_info));
129
130        assert_eq!(size_of::<MultibootInfo>(), 116);
131    }
132
133    #[test]
134    fn test_multiboot_mmap_entry_offsets() {
135        // The spec defines the mmap entry structure in a confusing way (`size` at offset -4 and
136        // `base_addr` at offset 0), but this does not match how both bootloaders and kernels have
137        // implemented Multiboot (`size` at offset 0), so this does not exactly match the table in
138        // the spec.
139        assert_eq!(0, offset_of!(MultibootMmapEntry, size));
140        assert_eq!(4, offset_of!(MultibootMmapEntry, base_addr));
141        assert_eq!(12, offset_of!(MultibootMmapEntry, length));
142        assert_eq!(20, offset_of!(MultibootMmapEntry, type_));
143
144        assert_eq!(size_of::<MultibootMmapEntry>(), 24); // 20-byte e820 data + 4 bytes for size
145    }
146}