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
25/// The boot information provided by the bootloader.
26pub struct BootInfo {
27    /// The name of the bootloader.
28    pub bootloader_name: String,
29    /// The kernel command line arguments.
30    pub kernel_cmdline: String,
31    /// The initial ramfs raw bytes.
32    pub initramfs: Option<&'static [u8]>,
33    /// The framebuffer arguments.
34    pub framebuffer_arg: Option<BootloaderFramebufferArg>,
35    /// The memory regions provided by the bootloader.
36    pub memory_regions: Vec<MemoryRegion>,
37}
38
39/// Gets the boot information.
40//
41// This function is usable after initialization with `init_after_heap`.
42pub fn boot_info() -> &'static BootInfo {
43    INFO.get().unwrap()
44}
45
46static INFO: Once<BootInfo> = Once::new();
47
48/// ACPI information from the bootloader.
49///
50/// The boot crate can choose either providing the raw RSDP physical address or
51/// providing the RSDT/XSDT physical address after parsing RSDP.
52/// This is because bootloaders differ in such behaviors.
53#[derive(Clone, Copy, Debug)]
54pub enum BootloaderAcpiArg {
55    /// The bootloader does not provide one.
56    NotProvided,
57    /// The boot path permits scanning legacy BIOS regions for the RSDP.
58    ScanBios,
59    /// Physical address of the RSDP.
60    Rsdp(usize),
61    /// Address of RSDT provided in RSDP v1.
62    Rsdt(usize),
63    /// Address of XSDT provided in RSDP v2+.
64    Xsdt(usize),
65}
66
67/// The framebuffer arguments.
68#[derive(Clone, Copy, Debug)]
69pub struct BootloaderFramebufferArg {
70    /// The address of the buffer.
71    pub address: usize,
72    /// The width of the buffer.
73    pub width: usize,
74    /// The height of the buffer.
75    pub height: usize,
76    /// Bits per pixel of the buffer.
77    pub bpp: usize,
78}
79
80/*************************** Boot-time information ***************************/
81
82/// The boot-time boot information.
83///
84/// When supporting multiple boot protocols with a single build, the entrypoint
85/// and boot information getters are dynamically decided. The entry point
86/// function should initializer all arguments at [`EARLY_INFO`].
87///
88/// All the references in this structure should be valid in the boot context.
89/// After the kernel is booted, users should use [`BootInfo`] instead.
90pub(crate) struct EarlyBootInfo {
91    pub(crate) bootloader_name: &'static str,
92    pub(crate) kernel_cmdline: &'static str,
93    pub(crate) initramfs: Option<&'static [u8]>,
94    pub(crate) acpi_arg: BootloaderAcpiArg,
95    pub(crate) framebuffer_arg: Option<BootloaderFramebufferArg>,
96    pub(crate) memory_regions: MemoryRegionArray,
97}
98
99/// The boot-time information.
100pub(crate) static EARLY_INFO: Once<EarlyBootInfo> = Once::new();
101
102/// Initializes the boot information.
103///
104/// This function copies the boot-time accessible information to the heap to
105/// allow [`boot_info`] to work properly.
106pub(crate) fn init_after_heap() {
107    let boot_time_info = EARLY_INFO.get().unwrap();
108
109    INFO.call_once(|| BootInfo {
110        bootloader_name: boot_time_info.bootloader_name.to_string(),
111        kernel_cmdline: boot_time_info.kernel_cmdline.to_string(),
112        initramfs: boot_time_info.initramfs,
113        framebuffer_arg: boot_time_info.framebuffer_arg,
114        memory_regions: boot_time_info.memory_regions.to_vec(),
115    });
116}
117
118/// Starts the kernel.
119///
120/// The job of this function is to continue the early bootstrap (started in [`arch::boot`])
121/// and performs the initialization of OSTD.
122/// Eventually, it transfers control to the entrypoint function
123/// that the user of OSTD defines with `#[ostd::main]`,
124/// which completes the kernel initialization.
125///
126/// # Safety
127///
128/// This function must be called only once at a proper timing on the BSP by the
129/// [`arch::boot`] module.
130///
131/// [`arch::boot`]: crate::arch::boot
132pub(crate) unsafe fn start_kernel() -> ! {
133    // The entry point of kernel code, which should be defined by the package that
134    // uses OSTD.
135    unsafe extern "Rust" {
136        fn __ostd_main() -> !;
137    }
138
139    // SAFETY: The function is called only once on the BSP.
140    unsafe { crate::init() };
141
142    // SAFETY: This external function is defined by the package that uses OSTD,
143    // which should be generated by the `ostd::main` macro. So it is safe.
144    unsafe { __ostd_main() };
145}