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