ostd/boot/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// SPDX-License-Identifier: MPL-2.0

//! The architecture-independent boot module, which provides
//!  1. a universal information getter interface from the bootloader to the
//!     rest of OSTD;
//!  2. the routine booting into the actual kernel;
//!  3. the routine booting the other processors in the SMP context.

pub mod memory_region;
pub mod smp;

use alloc::{
    string::{String, ToString},
    vec::Vec,
};

use memory_region::{MemoryRegion, MemoryRegionArray};
use spin::Once;

/// The boot information provided by the bootloader.
pub struct BootInfo {
    /// The name of the bootloader.
    pub bootloader_name: String,
    /// The kernel command line arguments.
    pub kernel_cmdline: String,
    /// The initial ramfs raw bytes.
    pub initramfs: Option<&'static [u8]>,
    /// The framebuffer arguments.
    pub framebuffer_arg: Option<BootloaderFramebufferArg>,
    /// The memory regions provided by the bootloader.
    pub memory_regions: Vec<MemoryRegion>,
}

/// Gets the boot information.
//
// This function is usable after initialization with `init_after_heap`.
pub fn boot_info() -> &'static BootInfo {
    INFO.get().unwrap()
}

static INFO: Once<BootInfo> = Once::new();

/// ACPI information from the bootloader.
///
/// The boot crate can choose either providing the raw RSDP physical address or
/// providing the RSDT/XSDT physical address after parsing RSDP.
/// This is because bootloaders differ in such behaviors.
#[derive(Copy, Clone, Debug)]
pub enum BootloaderAcpiArg {
    /// The bootloader does not provide one, a manual search is needed.
    NotProvided,
    /// Physical address of the RSDP.
    Rsdp(usize),
    /// Address of RSDT provided in RSDP v1.
    Rsdt(usize),
    /// Address of XSDT provided in RSDP v2+.
    Xsdt(usize),
}

/// The framebuffer arguments.
#[derive(Copy, Clone, Debug)]
pub struct BootloaderFramebufferArg {
    /// The address of the buffer.
    pub address: usize,
    /// The width of the buffer.
    pub width: usize,
    /// The height of the buffer.
    pub height: usize,
    /// Bits per pixel of the buffer.
    pub bpp: usize,
}

/*************************** Boot-time information ***************************/

/// The boot-time boot information.
///
/// When supporting multiple boot protocols with a single build, the entrypoint
/// and boot information getters are dynamically decided. The entry point
/// function should initializer all arguments at [`EARLY_INFO`].
///
/// All the references in this structure should be valid in the boot context.
/// After the kernel is booted, users should use [`BootInfo`] instead.
pub(crate) struct EarlyBootInfo {
    pub(crate) bootloader_name: &'static str,
    pub(crate) kernel_cmdline: &'static str,
    pub(crate) initramfs: Option<&'static [u8]>,
    pub(crate) acpi_arg: BootloaderAcpiArg,
    pub(crate) framebuffer_arg: Option<BootloaderFramebufferArg>,
    pub(crate) memory_regions: MemoryRegionArray,
}

/// The boot-time information.
pub(crate) static EARLY_INFO: Once<EarlyBootInfo> = Once::new();

/// Initializes the boot information.
///
/// This function copies the boot-time accessible information to the heap to
/// allow [`boot_info`] to work properly.
pub(crate) fn init_after_heap() {
    let boot_time_info = EARLY_INFO.get().unwrap();

    INFO.call_once(|| BootInfo {
        bootloader_name: boot_time_info.bootloader_name.to_string(),
        kernel_cmdline: boot_time_info.kernel_cmdline.to_string(),
        initramfs: boot_time_info.initramfs,
        framebuffer_arg: boot_time_info.framebuffer_arg,
        memory_regions: boot_time_info.memory_regions.to_vec(),
    });
}

/// Calls the OSTD-user defined entrypoint of the actual kernel.
///
/// Any kernel that uses the `ostd` crate should define a function marked with
/// `ostd::main` as the entrypoint.
///
/// This function should be only called from the bootloader-specific module.
pub(crate) fn call_ostd_main() -> ! {
    // The entry point of kernel code, which should be defined by the package that
    // uses OSTD.
    extern "Rust" {
        fn __ostd_main() -> !;
    }

    // SAFETY: The function is called only once on the BSP.
    unsafe { crate::init() };

    // SAFETY: This external function is defined by the package that uses OSTD,
    // which should be generated by the `ostd::main` macro. So it is safe.
    unsafe {
        __ostd_main();
    }
}