strat9_abi/boot.rs
1//! Bootloader-to-kernel handoff ABI.
2//!
3//! This module defines the data structures passed from the bootloader
4//! (Limine) to the kernel at entry point. The kernel reads these structures
5//! to discover memory layout, ACPI tables, framebuffer configuration, and
6//! kernel modules.
7//!
8//! # Boot flow
9//!
10//! ```text
11//! BIOS/UEFI → Limine bootloader → kernel_main(KernelArgs)
12//! ```
13//!
14//! The bootloader populates `KernelArgs` on the initial stack or in a
15//! reserved memory region, then jumps to the kernel entry point with a
16//! pointer to this structure in a register (typically RDI on x86_64).
17//!
18//! # ABI stability
19//!
20//! The `KernelArgs` layout is frozen per ABI version. Changing the layout
21//! requires bumping [`STRAT9_BOOT_ABI_VERSION`] and updating both
22//! bootloader and kernel simultaneously.
23//!
24//! # Example (kernel side)
25//!
26//! ```ignore
27//! // In kernel_main():
28//! unsafe fn kernel_main(args: *const KernelArgs) -> ! {
29//! let args = &*args;
30//!
31//! // Validate magic number
32//! assert_eq!(args.magic, STRAT9_BOOT_MAGIC);
33//!
34//! // Validate ABI version
35//! assert_eq!(args.abi_version, STRAT9_BOOT_ABI_VERSION);
36//!
37//! // Use memory map to initialize buddy allocator
38//! let mmap = core::slice::from_raw_parts(
39//! args.memory_map_base as *const MemoryRegion,
40//! args.memory_map_size as usize / core::mem::size_of::<MemoryRegion>(),
41//! );
42//!
43//! // Use ACPI RSDP to find ACPI tables
44//! let rsdp = args.acpi_rsdp_base;
45//!
46//! // Use framebuffer for early console
47//! let fb_addr = args.framebuffer_addr;
48//! let fb_w = args.framebuffer_width;
49//! let fb_h = args.framebuffer_height;
50//! }
51//! ```
52
53use zerocopy::{FromBytes, IntoBytes};
54
55/// ABI version for the boot handoff structure.
56///
57/// Increment this when `KernelArgs` layout changes.
58pub const STRAT9_BOOT_ABI_VERSION: u32 = 1;
59
60/// Magic number validating the boot handoff (`"ST9B"` in ASCII).
61///
62/// The kernel must check this value before trusting any `KernelArgs` data.
63pub const STRAT9_BOOT_MAGIC: u32 = 0x5354_3942; // "ST9B"
64
65/// Bootloader-to-kernel handoff structure.
66///
67/// Passed by the bootloader to the kernel at entry point.
68/// Layout is `#[repr(C)]` for natural alignment across all fields.
69/// Total size: 160 bytes, alignment: 8 bytes.
70///
71/// # Field groups
72///
73/// ## Identity (8 bytes)
74/// - `magic`: must equal [`STRAT9_BOOT_MAGIC`] (`0x5354_3942`)
75/// - `abi_version`: must equal [`STRAT9_BOOT_ABI_VERSION`] (currently `1`)
76///
77/// ## Kernel memory (16 bytes)
78/// - `kernel_base`: physical address of the kernel ELF image
79/// - `kernel_size`: size of the kernel image in bytes
80///
81/// ## Boot stack (16 bytes)
82/// - `stack_base`: physical address of the initial kernel stack
83/// - `stack_size`: size of the initial stack in bytes
84///
85/// ## Environment (16 bytes)
86/// - `env_base`: physical address of the environment block
87/// - `env_size`: size of the environment block in bytes
88///
89/// ## ACPI (16 bytes)
90/// - `acpi_rsdp_base`: physical address of the RSDP (Root System Description Pointer)
91/// - `acpi_rsdp_size`: size of the RSDP in bytes (typically 36 or 276)
92///
93/// ## Memory map (16 bytes)
94/// - `memory_map_base`: physical address of the memory map array
95/// - `memory_map_size`: total size of the memory map in bytes
96///
97/// ## Init filesystem (16 bytes)
98/// - `initfs_base`: physical address of the init filesystem (cpio/tar archive)
99/// - `initfs_size`: size of the init filesystem in bytes
100///
101/// ## Framebuffer (32 bytes)
102/// - `framebuffer_addr`: physical address of the linear framebuffer
103/// - `framebuffer_width`: width in pixels
104/// - `framebuffer_height`: height in pixels
105/// - `framebuffer_stride`: bytes per row (may include padding)
106/// - `framebuffer_bpp`: bits per pixel (15, 16, 24, or 32)
107/// - `framebuffer_*_mask_size/shift`: RGB channel mask layout
108///
109/// ## HHDM (8 bytes)
110/// - `hhdm_offset`: Higher Half Direct Map offset (physical-to-virtual)
111///
112/// ## Command line (16 bytes)
113/// - `cmdline_ptr`: pointer to null-terminated kernel command line string
114/// - `cmdline_len`: length of the command line (including null terminator)
115#[derive(Debug, FromBytes, IntoBytes)]
116#[repr(C)]
117pub struct KernelArgs {
118 pub magic: u32,
119 pub abi_version: u32,
120 pub kernel_base: u64,
121 pub kernel_size: u64,
122 pub stack_base: u64,
123 pub stack_size: u64,
124 pub env_base: u64,
125 pub env_size: u64,
126 pub acpi_rsdp_base: u64,
127 pub acpi_rsdp_size: u64,
128 pub memory_map_base: u64,
129 pub memory_map_size: u64,
130 pub initfs_base: u64,
131 pub initfs_size: u64,
132 pub framebuffer_addr: u64,
133 pub framebuffer_width: u32,
134 pub framebuffer_height: u32,
135 pub framebuffer_stride: u32,
136 pub framebuffer_bpp: u16,
137 pub framebuffer_red_mask_size: u8,
138 pub framebuffer_red_mask_shift: u8,
139 pub framebuffer_green_mask_size: u8,
140 pub framebuffer_green_mask_shift: u8,
141 pub framebuffer_blue_mask_size: u8,
142 pub framebuffer_blue_mask_shift: u8,
143 pub _padding1: [u8; 4], // Align hhdm_offset to 8-byte boundary
144 pub hhdm_offset: u64,
145 /// Pointer to the kernel command line string (null-terminated C string).
146 pub cmdline_ptr: u64,
147 /// Length of the kernel command line string (including null terminator).
148 pub cmdline_len: u64,
149}
150
151/// Memory region descriptor for the bootloader memory map.
152///
153/// The memory map is an array of these descriptors, passed via
154/// `KernelArgs::memory_map_base` and `KernelArgs::memory_map_size`.
155/// Each descriptor is 24 bytes (aligned to 8).
156///
157/// # Example
158///
159/// ```text
160/// Region 1: base=0x0000_0000, size=0x0009_F000, kind=Free (conventional memory)
161/// Region 2: base=0x0010_0000, size=0x7EF0_0000, kind=Free (extended memory)
162/// Region 3: base=0xFD00_0000, size=0x0200_0000, kind=Reserved (MMIO, framebuffer)
163/// Region 4: base=0x7FE0_0000, size=0x0020_0000, kind=Reserved (ACPI NVS)
164/// ```
165#[derive(Debug, Clone, Copy, FromBytes, IntoBytes)]
166#[repr(C)]
167pub struct MemoryRegion {
168 /// Physical base address of the region.
169 pub base: u64,
170 /// Size of the region in bytes.
171 pub size: u64,
172 /// Region type (see [`MemoryKind`]).
173 pub kind: MemoryKind,
174}
175
176/// Memory region type identifier.
177///
178/// Used by the bootloader to communicate the memory map to the kernel.
179/// The kernel uses this to decide which regions can be used for the
180/// buddy allocator, which are reserved for hardware, etc.
181///
182/// # Example
183///
184/// ```ignore
185/// let regions = /* from KernelArgs */;
186/// for region in regions {
187/// match region.kind {
188/// MemoryKind::Free => {
189/// // Add to buddy allocator
190/// buddy_init_region(region.base, region.size);
191/// }
192/// MemoryKind::Reserved => {
193/// // Skip — hardware MMIO (framebuffer, APIC, etc.)
194/// }
195/// MemoryKind::Reclaim => {
196/// // Bootloader code — can be freed after init
197/// reclaim_region(region.base, region.size);
198/// }
199/// _ => {}
200/// }
201/// }
202/// ```
203#[derive(Clone, Copy, Debug, PartialEq, Eq, FromBytes, IntoBytes)]
204#[repr(transparent)]
205pub struct MemoryKind(pub u64);
206
207#[allow(non_upper_case_globals)]
208impl MemoryKind {
209 /// Null/invalid region (should not appear in the memory map).
210 pub const Null: Self = Self(0);
211
212 /// Free usable memory — available for kernel allocation.
213 pub const Free: Self = Self(1);
214
215 /// Bootloader-reclaimable memory — free after boot, before first use.
216 pub const Reclaim: Self = Self(2);
217
218 /// Reserved memory — hardware MMIO, firmware, ACPI NVS, etc.
219 /// Kernel must not allocate from these regions.
220 pub const Reserved: Self = Self(3);
221}
222
223// ABI size assertions for bootloader structures
224static_assertions::assert_eq_size!(KernelArgs, [u8; 160]);
225static_assertions::const_assert_eq!(core::mem::align_of::<KernelArgs>(), 8);
226static_assertions::assert_eq_size!(MemoryRegion, [u8; 24]);
227static_assertions::const_assert_eq!(core::mem::align_of::<MemoryRegion>(), 8);
228static_assertions::assert_eq_size!(MemoryKind, [u8; 8]);