Skip to main content

ostd/boot/
mod.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! The architecture-independent boot module, which provides
4//!  1. a universal information getter interface from the bootloader to the
5//!     rest of OSTD;
6//!  2. the routine booting into the actual kernel;
7//!  3. the routine booting the other processors in the SMP context.
8
9#![cfg_attr(
10    any(target_arch = "riscv64", target_arch = "loongarch64"),
11    expect(dead_code)
12)]
13
14pub mod memory_region;
15pub mod smp;
16
17use alloc::{
18    string::{String, ToString},
19    vec::Vec,
20};
21
22use memory_region::{MemoryRegion, MemoryRegionArray};
23use spin::Once;
24
25use crate::log::LevelFilter;
26
27/// The boot information provided by the bootloader.
28pub struct BootInfo {
29    /// The name of the bootloader.
30    pub bootloader_name: String,
31    /// The kernel command line arguments.
32    pub kernel_cmdline: String,
33    /// The initial ramfs raw bytes.
34    pub initramfs: Option<&'static [u8]>,
35    /// The framebuffer arguments.
36    pub framebuffer_arg: Option<BootloaderFramebufferArg>,
37    /// The memory regions provided by the bootloader.
38    pub memory_regions: Vec<MemoryRegion>,
39}
40
41/// Gets the boot information.
42//
43// This function is usable after initialization with `init_after_heap`.
44pub fn boot_info() -> &'static BootInfo {
45    INFO.get().unwrap()
46}
47
48static INFO: Once<BootInfo> = Once::new();
49
50/// ACPI information from the bootloader.
51///
52/// The boot crate can choose either providing the raw RSDP physical address or
53/// providing the RSDT/XSDT physical address after parsing RSDP.
54/// This is because bootloaders differ in such behaviors.
55#[derive(Clone, Copy, Debug)]
56pub enum BootloaderAcpiArg {
57    /// The bootloader does not provide one.
58    NotProvided,
59    /// The boot path permits scanning legacy BIOS regions for the RSDP.
60    ScanBios,
61    /// Physical address of the RSDP.
62    Rsdp(usize),
63    /// Address of RSDT provided in RSDP v1.
64    Rsdt(usize),
65    /// Address of XSDT provided in RSDP v2+.
66    Xsdt(usize),
67}
68
69/// The framebuffer arguments.
70#[derive(Clone, Copy, Debug)]
71pub struct BootloaderFramebufferArg {
72    /// The address of the buffer.
73    pub address: usize,
74    /// The width of the buffer.
75    pub width: usize,
76    /// The height of the buffer.
77    pub height: usize,
78    /// Bits per pixel of the buffer.
79    pub bpp: usize,
80}
81
82/*************************** Boot-time information ***************************/
83
84/// The boot-time boot information.
85///
86/// When supporting multiple boot protocols with a single build, the entrypoint
87/// and boot information getters are dynamically decided. The entry point
88/// function should initializer all arguments at [`EARLY_INFO`].
89///
90/// All the references in this structure should be valid in the boot context.
91/// After the kernel is booted, users should use [`BootInfo`] instead.
92pub(crate) struct EarlyBootInfo {
93    pub(crate) bootloader_name: &'static str,
94    pub(crate) kernel_cmdline: &'static str,
95    pub(crate) initramfs: Option<&'static [u8]>,
96    pub(crate) acpi_arg: BootloaderAcpiArg,
97    pub(crate) framebuffer_arg: Option<BootloaderFramebufferArg>,
98    pub(crate) memory_regions: MemoryRegionArray,
99}
100
101/// The boot-time information.
102pub(crate) static EARLY_INFO: Once<EarlyBootInfo> = Once::new();
103
104/// Initializes the boot information.
105///
106/// This function copies the boot-time accessible information to the heap to
107/// allow [`boot_info`] to work properly.
108pub(crate) fn init_after_heap() {
109    let boot_time_info = EARLY_INFO.get().unwrap();
110
111    INFO.call_once(|| BootInfo {
112        bootloader_name: boot_time_info.bootloader_name.to_string(),
113        kernel_cmdline: boot_time_info.kernel_cmdline.to_string(),
114        initramfs: boot_time_info.initramfs,
115        framebuffer_arg: boot_time_info.framebuffer_arg,
116        memory_regions: boot_time_info.memory_regions.to_vec(),
117    });
118}
119
120/// The early command line arguments.
121///
122/// [`crate::early_cmdline_parser`] can be used to specify how this is parsed
123/// from the kernel command line. If it is not specified, we will use the
124/// default values (see the field documentation).
125pub struct EarlyCmdline {
126    /// The log level filter.
127    ///
128    /// The default value is [`LevelFilter::Debug`].
129    pub log_level: LevelFilter,
130    /// Whether to enable the early console.
131    ///
132    /// The default value is `true`.
133    ///
134    /// We choose `true` as the default value
135    /// in order to give a minimal OSTD-based kernel
136    /// (e.g., the one created with `osdk test`)
137    /// access to an early console and thus enable logging.
138    /// This is convenient for development purpose.
139    ///
140    /// On the other hand,
141    /// blindly assuming a deployment platform is attached
142    /// to a UART-based console is
143    /// unacceptable for a production-grade kernel,
144    /// which should instead register `crate::early_cmdline_parser`
145    /// to acquire this information from the kernel parameter.
146    pub has_early_console: bool,
147}
148
149#[linkage = "weak"]
150// SAFETY: The name does not collide with other symbols.
151#[unsafe(no_mangle)]
152fn __early_cmdline_parser(_cmdline: &str) -> EarlyCmdline {
153    EarlyCmdline {
154        log_level: LevelFilter::Debug,
155        has_early_console: true,
156    }
157}
158
159/// Parses the early command line arguments.
160pub(crate) fn parse_early_cmdline() -> EarlyCmdline {
161    let kernel_cmdline = EARLY_INFO.get().unwrap().kernel_cmdline;
162    __early_cmdline_parser(kernel_cmdline)
163}
164
165/// Starts the kernel.
166///
167/// The job of this function is to continue the early bootstrap (started in [`arch::boot`])
168/// and performs the initialization of OSTD.
169/// Eventually, it transfers control to the entrypoint function
170/// that the user of OSTD defines with `#[ostd::main]`,
171/// which completes the kernel initialization.
172///
173/// # Safety
174///
175/// This function must be called only once at a proper timing on the BSP by the
176/// [`arch::boot`] module.
177///
178/// [`arch::boot`]: crate::arch::boot
179pub(crate) unsafe fn start_kernel() -> ! {
180    // The entry point of kernel code, which should be defined by the package that
181    // uses OSTD.
182    unsafe extern "Rust" {
183        fn __ostd_main() -> !;
184    }
185
186    // SAFETY: The function is called only once on the BSP.
187    unsafe { crate::init() };
188
189    // SAFETY: This external function is defined by the package that uses OSTD,
190    // which should be generated by the `ostd::main` macro. So it is safe.
191    unsafe { __ostd_main() };
192}