Skip to main content

ostd/arch/x86/boot/
mod.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! The x86 boot module defines the entrypoints of Asterinas and
4//! the corresponding headers for different x86 boot protocols.
5//!
6//! We directly support
7//!
8//!  - Multiboot
9//!  - Multiboot2
10//!  - Linux x86 Boot Protocol
11//!  - PVH
12//!
13//! without any additional configurations.
14//!
15//! Asterinas differentiates the boot protocol by the entry point
16//! chosen by the boot loader. In each entry point function,
17//! the universal callback registration method from
18//! `crate::boot` will be called. Thus the initialization of
19//! boot information is transparent for the upper level kernel.
20//!
21
22mod linux_boot;
23mod multiboot;
24mod multiboot2;
25mod pvh;
26
27pub(crate) mod smp;
28
29use core::{arch::global_asm, num::NonZeroUsize};
30
31use acpi::rsdp::Rsdp;
32
33use crate::{
34    arch::kernel::acpi::AcpiMemoryHandler,
35    boot::{
36        BootloaderAcpiArg, BootloaderFramebufferArg, EarlyBootInfo,
37        memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType},
38    },
39};
40
41global_asm!(
42    include_str!("bsp_boot.S"),
43    KCODE64 = const super::trap::gdt::KCODE64,
44    KDATA = const super::trap::gdt::KDATA,
45    KCODE32 = const super::trap::gdt::KCODE32,
46);
47global_asm!(include_str!("ap_boot.S"));
48
49/// A bootloader-provided structure that can be converted into [`EarlyBootInfo`].
50trait ToEarlyBootInfo {
51    /// Returns the name of the bootloader.
52    fn bootloader_name(&self) -> &'static str;
53
54    /// Returns the kernel command line, if provided.
55    fn kernel_commandline(&self) -> Option<&'static str>;
56
57    /// Returns the initramfs bytes, if provided.
58    fn initramfs(&self) -> Option<&'static [u8]>;
59
60    /// Returns the ACPI information provided by the bootloader.
61    fn acpi_arg(&self) -> BootloaderAcpiArg;
62
63    /// Returns the framebuffer information, if provided.
64    fn framebuffer_arg(&self) -> Option<BootloaderFramebufferArg>;
65
66    /// Builds the bootloader-specific memory regions, appends the common
67    /// regions, and resolves the overlaps.
68    fn memory_regions(
69        &self,
70        initramfs: Option<&'static [u8]>,
71        kernel_cmdline: Option<&'static str>,
72        framebuffer_arg: Option<BootloaderFramebufferArg>,
73    ) -> MemoryRegionArray;
74
75    /// Converts the bootloader-provided information into [`EarlyBootInfo`].
76    fn to_early_boot_info(&self) -> EarlyBootInfo {
77        let kernel_cmdline = self.kernel_commandline();
78        let initramfs = self.initramfs();
79        let framebuffer_arg = self.framebuffer_arg();
80
81        EarlyBootInfo {
82            bootloader_name: self.bootloader_name(),
83            kernel_cmdline: kernel_cmdline.unwrap_or(""),
84            initramfs,
85            acpi_arg: self.acpi_arg(),
86            framebuffer_arg,
87            memory_regions: self.memory_regions(initramfs, kernel_cmdline, framebuffer_arg),
88        }
89    }
90}
91
92/// Appends the memory regions common to all boot protocols and resolves the
93/// overlapping regions.
94fn finish_memory_regions(
95    mut regions: MemoryRegionArray,
96    framebuffer_arg: Option<BootloaderFramebufferArg>,
97    initramfs: Option<&'static [u8]>,
98    kernel_cmdline: Option<&'static str>,
99) -> MemoryRegionArray {
100    // Add the framebuffer region since some loaders do not specify it.
101    if let Some(fb) = framebuffer_arg {
102        regions.push(MemoryRegion::framebuffer(&fb)).unwrap();
103    }
104
105    // Add the kernel region since some loaders do not specify it.
106    regions.push(MemoryRegion::kernel()).unwrap();
107
108    // Add the initramfs region.
109    if let Some(initramfs) = initramfs {
110        regions.push(MemoryRegion::module(initramfs)).unwrap();
111    }
112
113    // Add the AP boot code region that will be copied into by the BSP.
114    regions.push(smp::reclaimable_memory_region()).unwrap();
115
116    // Add the kernel cmdline region since some loaders do not specify it.
117    if let Some(kcmdline) = kernel_cmdline {
118        regions
119            .push(MemoryRegion::module(kcmdline.as_bytes()))
120            .unwrap();
121    }
122
123    regions.into_non_overlapping()
124}
125
126/// Finds the physical address of the ACPI root table.
127fn find_acpi_root_table_address() -> Option<NonZeroUsize> {
128    // Some boot paths do not provide the RSDP directly and are
129    // BIOS-compatible: Multiboot v1 has no standard EFI System Table field,
130    // and the PVH entry path under QEMU goes through SeaBIOS (the pvh.bin
131    // option ROM may leave `rsdp_paddr` zero). So we use the BIOS RSDP scan
132    // as the legacy fallback.
133    //
134    // SAFETY: These entry paths are treated as BIOS-compatible.
135    let Ok(rsdp) = (unsafe { Rsdp::search_for_on_bios(AcpiMemoryHandler {}) }) else {
136        return None;
137    };
138
139    if rsdp.revision() == 0 {
140        NonZeroUsize::new(rsdp.rsdt_address() as usize)
141    } else {
142        NonZeroUsize::new(rsdp.xsdt_address() as usize)
143    }
144}
145
146/// Promotes a reserved region containing the ACPI root table to `Reclaimable`.
147fn effective_region_type(
148    typ: MemoryRegionType,
149    base: usize,
150    len: usize,
151    acpi_root_table_address: Option<NonZeroUsize>,
152) -> MemoryRegionType {
153    if typ == MemoryRegionType::Reserved
154        && acpi_root_table_address.is_some_and(|addr| (base..(base + len)).contains(&addr.get()))
155    {
156        MemoryRegionType::Reclaimable
157    } else {
158        typ
159    }
160}