Skip to main content

strat9_kernel/ostd/
boot.rs

1//! Boot information abstraction
2//!
3//! Provides safe abstractions for bootloader-provided information including:
4//! - Memory map
5//! - ACPI tables
6//! - Module information
7//!
8//! Inspired by Asterinas and Theseus boot info abstractions.
9
10#![deny(unsafe_code)]
11
12extern crate alloc;
13
14use alloc::vec::Vec;
15use core::fmt;
16
17/// Memory region types
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19#[repr(u32)]
20pub enum MemoryRegionType {
21    /// Usable RAM
22    Usable = 0,
23    /// Reserved (in use or unusable)
24    Reserved = 1,
25    /// ACPI reclaimable memory
26    AcpiReclaimable = 2,
27    /// ACPI NVS memory
28    AcpiNvs = 3,
29    /// Bad memory (do not use)
30    Bad = 4,
31    /// Unknown type
32    Unknown = 0xFF,
33}
34
35impl From<u32> for MemoryRegionType {
36    /// Performs the from operation.
37    fn from(value: u32) -> Self {
38        match value {
39            0 => Self::Usable,
40            1 => Self::Reserved,
41            2 => Self::AcpiReclaimable,
42            3 => Self::AcpiNvs,
43            4 => Self::Bad,
44            _ => Self::Unknown,
45        }
46    }
47}
48
49/// A memory region descriptor
50#[derive(Debug, Clone, Copy)]
51#[repr(C)]
52pub struct MemoryRegion {
53    /// Base physical address
54    pub base: u64,
55    /// Size in bytes
56    pub size: u64,
57    /// Region type
58    pub region_type: MemoryRegionType,
59}
60
61impl MemoryRegion {
62    /// Creates a new memory region
63    pub const fn new(base: u64, size: u64, region_type: MemoryRegionType) -> Self {
64        Self {
65            base,
66            size,
67            region_type,
68        }
69    }
70
71    /// Returns the end address (exclusive)
72    pub const fn end(&self) -> u64 {
73        self.base.wrapping_add(self.size)
74    }
75
76    /// Returns true if this region is usable RAM
77    pub const fn is_usable(&self) -> bool {
78        matches!(self.region_type, MemoryRegionType::Usable)
79    }
80
81    /// Returns true if this region is ACPI reclaimable
82    pub const fn is_acpi_reclaimable(&self) -> bool {
83        matches!(self.region_type, MemoryRegionType::AcpiReclaimable)
84    }
85
86    /// Returns true if this region is ACPI NVS
87    pub const fn is_acpi_nvs(&self) -> bool {
88        matches!(self.region_type, MemoryRegionType::AcpiNvs)
89    }
90}
91
92/// Bootloader module information
93#[derive(Debug, Clone)]
94pub struct BootModule {
95    /// Base physical address
96    pub base: u64,
97    /// Size in bytes
98    pub size: u64,
99    /// Module name/identifier
100    pub name: alloc::string::String,
101}
102
103impl BootModule {
104    /// Creates a new boot module descriptor
105    pub fn new(base: u64, size: u64, name: alloc::string::String) -> Self {
106        Self { base, size, name }
107    }
108
109    /// Returns the end address (exclusive)
110    pub fn end(&self) -> u64 {
111        self.base.wrapping_add(self.size)
112    }
113}
114
115/// Boot information provided by the bootloader
116#[derive(Debug, Clone)]
117pub struct BootInfo {
118    /// Memory map from bootloader
119    pub memory_map: Vec<MemoryRegion>,
120    /// Higher Half Direct Map offset (if applicable)
121    pub hhdm_offset: u64,
122    /// ACPI RSDP physical address
123    pub acpi_rsdp: u64,
124    /// Bootloader modules
125    pub modules: Vec<BootModule>,
126    /// Kernel command line
127    pub cmdline: alloc::string::String,
128}
129
130impl BootInfo {
131    /// Creates a new BootInfo
132    pub fn new(
133        memory_map: Vec<MemoryRegion>,
134        hhdm_offset: u64,
135        acpi_rsdp: u64,
136        modules: Vec<BootModule>,
137        cmdline: alloc::string::String,
138    ) -> Self {
139        Self {
140            memory_map,
141            hhdm_offset,
142            acpi_rsdp,
143            modules,
144            cmdline,
145        }
146    }
147
148    /// Returns an iterator over usable memory regions
149    pub fn usable_regions(&self) -> impl Iterator<Item = &MemoryRegion> {
150        self.memory_map.iter().filter(|r| r.is_usable())
151    }
152
153    /// Returns the total amount of usable RAM in bytes
154    pub fn total_usable_ram(&self) -> u64 {
155        self.usable_regions().map(|r| r.size).sum()
156    }
157
158    /// Returns the total amount of physical memory (including reserved)
159    pub fn total_physical_memory(&self) -> u64 {
160        self.memory_map.iter().map(|r| r.size).sum()
161    }
162
163    /// Finds a module by name
164    pub fn find_module(&self, name: &str) -> Option<&BootModule> {
165        self.modules.iter().find(|m| m.name == name)
166    }
167}
168
169impl fmt::Display for BootInfo {
170    /// Performs the fmt operation.
171    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172        writeln!(f, "Boot Information:")?;
173        writeln!(f, "  HHDM Offset: 0x{:x}", self.hhdm_offset)?;
174        writeln!(f, "  ACPI RSDP: 0x{:x}", self.acpi_rsdp)?;
175        writeln!(f, "  Memory Regions:")?;
176        for region in &self.memory_map {
177            writeln!(
178                f,
179                "    0x{:016x}-0x{:016x}: {:?} ({} KB)",
180                region.base,
181                region.end(),
182                region.region_type,
183                region.size / 1024
184            )?;
185        }
186        writeln!(
187            f,
188            "  Total Usable RAM: {} MB",
189            self.total_usable_ram() / (1024 * 1024)
190        )?;
191        writeln!(f, "  Modules: {}", self.modules.len())?;
192        for module in &self.modules {
193            writeln!(
194                f,
195                "    {}: 0x{:x} ({} bytes)",
196                module.name, module.base, module.size
197            )?;
198        }
199        if !self.cmdline.is_empty() {
200            writeln!(f, "  Command Line: {}", self.cmdline)?;
201        }
202        Ok(())
203    }
204}