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(Copy, Clone, Debug)]
54pub enum BootloaderAcpiArg {
55    /// The bootloader does not provide one, a manual search is needed.
56    NotProvided,
57    /// Physical address of the RSDP.
58    Rsdp(usize),
59    /// Address of RSDT provided in RSDP v1.
60    Rsdt(usize),
61    /// Address of XSDT provided in RSDP v2+.
62    Xsdt(usize),
63}
64
65/// The framebuffer arguments.
66#[derive(Copy, Clone, Debug)]
67pub struct BootloaderFramebufferArg {
68    /// The address of the buffer.
69    pub address: usize,
70    /// The width of the buffer.
71    pub width: usize,
72    /// The height of the buffer.
73    pub height: usize,
74    /// Bits per pixel of the buffer.
75    pub bpp: usize,
76}
77
78/*************************** Boot-time information ***************************/
79
80/// The boot-time boot information.
81///
82/// When supporting multiple boot protocols with a single build, the entrypoint
83/// and boot information getters are dynamically decided. The entry point
84/// function should initializer all arguments at [`EARLY_INFO`].
85///
86/// All the references in this structure should be valid in the boot context.
87/// After the kernel is booted, users should use [`BootInfo`] instead.
88pub(crate) struct EarlyBootInfo {
89    pub(crate) bootloader_name: &'static str,
90    pub(crate) kernel_cmdline: &'static str,
91    pub(crate) initramfs: Option<&'static [u8]>,
92    pub(crate) acpi_arg: BootloaderAcpiArg,
93    pub(crate) framebuffer_arg: Option<BootloaderFramebufferArg>,
94    pub(crate) memory_regions: MemoryRegionArray,
95}
96
97/// The boot-time information.
98pub(crate) static EARLY_INFO: Once<EarlyBootInfo> = Once::new();
99
100/// Initializes the boot information.
101///
102/// This function copies the boot-time accessible information to the heap to
103/// allow [`boot_info`] to work properly.
104pub(crate) fn init_after_heap() {
105    let boot_time_info = EARLY_INFO.get().unwrap();
106
107    INFO.call_once(|| BootInfo {
108        bootloader_name: boot_time_info.bootloader_name.to_string(),
109        kernel_cmdline: boot_time_info.kernel_cmdline.to_string(),
110        initramfs: boot_time_info.initramfs,
111        framebuffer_arg: boot_time_info.framebuffer_arg,
112        memory_regions: boot_time_info.memory_regions.to_vec(),
113    });
114}
115
116/// Calls the OSTD-user defined entrypoint of the actual kernel.
117///
118/// Any kernel that uses the `ostd` crate should define a function marked with
119/// `ostd::main` as the entrypoint.
120///
121/// This function should be only called from the bootloader-specific module.
122pub(crate) fn call_ostd_main() -> ! {
123    // The entry point of kernel code, which should be defined by the package that
124    // uses OSTD.
125    unsafe extern "Rust" {
126        fn __ostd_main() -> !;
127    }
128
129    // SAFETY: The function is called only once on the BSP.
130    unsafe { crate::init() };
131
132    // SAFETY: This external function is defined by the package that uses OSTD,
133    // which should be generated by the `ostd::main` macro. So it is safe.
134    unsafe {
135        __ostd_main();
136    }
137}