Refactor boot modules to make heap allocation explicit

This commit is contained in:
Zhang Junyang
2024-12-31 13:44:35 +08:00
committed by Tate, Hongliang Tian
parent 51349a3da1
commit 397ce9652f
15 changed files with 267 additions and 301 deletions

View File

@ -15,7 +15,7 @@ use core::{
use component::{init_component, ComponentInitError};
use font8x8::UnicodeFonts;
use ostd::{
boot::{self, memory_region::MemoryRegionType, memory_regions},
boot::{boot_info, memory_region::MemoryRegionType},
io_mem::IoMem,
mm::{VmIo, PAGE_SIZE},
sync::SpinLock,
@ -36,9 +36,11 @@ pub(crate) static WRITER: Once<SpinLock<Writer>> = Once::new();
#[allow(clippy::diverging_sub_expression)]
pub(crate) fn init() {
let mut writer = {
let framebuffer = boot::framebuffer_arg();
let Some(framebuffer) = boot_info().framebuffer_arg else {
return;
};
let mut size = 0;
for region in memory_regions().iter() {
for region in boot_info().memory_regions.iter() {
if region.typ() == MemoryRegionType::Framebuffer {
size = region.len();
}
@ -56,9 +58,9 @@ pub(crate) fn init() {
io_mem,
x_pos: 0,
y_pos: 0,
bytes_per_pixel: (framebuffer.bpp / 8) as usize,
width: framebuffer.width as usize,
height: framebuffer.height as usize,
bytes_per_pixel: (framebuffer.bpp / 8),
width: framebuffer.width,
height: framebuffer.height,
buffer: buffer.leak(),
}
};

View File

@ -32,7 +32,7 @@
use ostd::{
arch::qemu::{exit_qemu, QemuExitCode},
boot,
boot::boot_info,
cpu::{CpuId, CpuSet, PinCurrentCpu},
};
use process::Process;
@ -96,7 +96,7 @@ pub fn init() {
#[cfg(target_arch = "x86_64")]
net::init();
sched::init();
fs::rootfs::init(boot::initramfs()).unwrap();
fs::rootfs::init(boot_info().initramfs.expect("No initramfs found!")).unwrap();
device::init().unwrap();
syscall::init();
vdso::init();
@ -141,7 +141,7 @@ fn init_thread() {
print_banner();
let karg = boot::kernel_cmdline();
let karg = &boot_info().kernel_cmdline;
let initproc = Process::spawn_user_process(
karg.get_initproc_path().unwrap(),

View File

@ -7,7 +7,7 @@ mod tests {
#[ktest]
fn it_works() {
let memory_regions = ostd::boot::memory_regions();
let memory_regions = &ostd::boot::boot_info().memory_regions;
assert!(!memory_regions.is_empty());
}
}

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
pub fn available_memory() -> usize {
let regions = ostd::boot::memory_regions();
let regions = &ostd::boot::boot_info().memory_regions;
regions.iter().map(|region| region.len()).sum()
}

View File

@ -4,7 +4,6 @@
pub mod smp;
use alloc::string::String;
use core::arch::global_asm;
use fdt::Fdt;
@ -12,11 +11,7 @@ use spin::Once;
use crate::{
boot::{
kcmdline::KCmdlineArg,
memory_region::{
non_overlapping_regions_from, MemoryRegion, MemoryRegionArray, MemoryRegionType,
MAX_REGIONS,
},
memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType},
BootloaderAcpiArg, BootloaderFramebufferArg,
},
early_println,
@ -28,32 +23,33 @@ global_asm!(include_str!("boot.S"));
/// The Flattened Device Tree of the platform.
pub static DEVICE_TREE: Once<Fdt> = Once::new();
fn init_bootloader_name(bootloader_name: &'static Once<String>) {
bootloader_name.call_once(|| "Unknown".into());
fn parse_bootloader_name() -> &'static str {
"Unknown"
}
fn init_kernel_commandline(kernel_cmdline: &'static Once<KCmdlineArg>) {
let bootargs = DEVICE_TREE.get().unwrap().chosen().bootargs().unwrap_or("");
kernel_cmdline.call_once(|| bootargs.into());
fn parse_kernel_commandline() -> &'static str {
DEVICE_TREE.get().unwrap().chosen().bootargs().unwrap_or("")
}
fn init_initramfs(initramfs: &'static Once<&'static [u8]>) {
fn parse_initramfs() -> Option<&'static [u8]> {
let Some((start, end)) = parse_initramfs_range() else {
return;
return None;
};
let base_va = paddr_to_vaddr(start);
let length = end - start;
initramfs.call_once(|| unsafe { core::slice::from_raw_parts(base_va as *const u8, length) });
Some(unsafe { core::slice::from_raw_parts(base_va as *const u8, length) })
}
fn init_acpi_arg(acpi: &'static Once<BootloaderAcpiArg>) {
acpi.call_once(|| BootloaderAcpiArg::NotProvided);
fn parse_acpi_arg() -> BootloaderAcpiArg {
BootloaderAcpiArg::NotProvided
}
fn init_framebuffer_info(_framebuffer_arg: &'static Once<BootloaderFramebufferArg>) {}
fn parse_framebuffer_info() -> Option<BootloaderFramebufferArg> {
None
}
fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
fn parse_memory_regions() -> MemoryRegionArray {
let mut regions = MemoryRegionArray::new();
for region in DEVICE_TREE.get().unwrap().memory().regions() {
@ -92,7 +88,7 @@ fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
));
}
memory_regions.call_once(|| regions.into_non_overlapping());
regions.into_non_overlapping()
}
fn parse_initramfs_range() -> Option<(usize, usize)> {
@ -111,14 +107,16 @@ pub extern "C" fn riscv_boot(_hart_id: usize, device_tree_paddr: usize) -> ! {
let fdt = unsafe { fdt::Fdt::from_ptr(device_tree_ptr).unwrap() };
DEVICE_TREE.call_once(|| fdt);
crate::boot::register_boot_init_callbacks(
init_bootloader_name,
init_kernel_commandline,
init_initramfs,
init_acpi_arg,
init_framebuffer_info,
init_memory_regions,
);
use crate::boot::{call_ostd_main, EarlyBootInfo, EARLY_INFO};
crate::boot::call_ostd_main();
EARLY_INFO.call_once(|| EarlyBootInfo {
bootloader_name: parse_bootloader_name(),
kernel_cmdline: parse_kernel_commandline(),
initramfs: parse_initramfs(),
acpi_arg: parse_acpi_arg(),
framebuffer_arg: parse_framebuffer_info(),
memory_regions: parse_memory_regions(),
});
call_ostd_main();
}

View File

@ -3,30 +3,24 @@
//! The Linux 64-bit Boot Protocol supporting module.
//!
use alloc::{borrow::ToOwned, format, string::String};
use core::ffi::CStr;
use linux_boot_params::{BootParams, E820Type, LINUX_BOOT_HEADER_MAGIC};
use spin::Once;
use crate::{
boot::{
kcmdline::KCmdlineArg,
memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType},
BootloaderAcpiArg, BootloaderFramebufferArg,
},
mm::kspace::{paddr_to_vaddr, LINEAR_MAPPING_BASE_VADDR},
};
static BOOT_PARAMS: Once<BootParams> = Once::new();
fn init_bootloader_name(bootloader_name: &'static Once<String>) {
let hdr = &BOOT_PARAMS.get().unwrap().hdr;
fn parse_bootloader_name(boot_params: &BootParams) -> &str {
let hdr = &boot_params.hdr;
// The bootloaders have assigned IDs in Linux, see
// https://www.kernel.org/doc/Documentation/x86/boot.txt
// for details.
let ext_str: String;
let name = match hdr.type_of_loader {
match hdr.type_of_loader {
0x0 => "LILO", // (0x00 reserved for pre-2.00 bootloader)
0x1 => "Loadlin",
0x2 => "bootsect-loader", // (0x20, all other values reserved)
@ -40,37 +34,27 @@ fn init_bootloader_name(bootloader_name: &'static Once<String>) {
0xB => "Qemu",
0xC => "Arcturus Networks uCbootloader",
0xD => "kexec-tools",
0xE => {
// Extended
ext_str = format!(
"Extended bootloader {}, version {}",
(hdr.ext_loader_type + 0x10),
(hdr.type_of_loader & 0x0f) + (hdr.ext_loader_ver << 4)
);
&ext_str
}
0xE => "Extended loader",
0xF => "Special", // (0xFF = undefined)
0x10 => "Reserved",
0x11 => "Minimal Linux Bootloader <http://sebastian-plotz.blogspot.de>",
0x12 => "OVMF UEFI virtualization stack",
_ => "Unknown bootloader type!",
}
.to_owned();
bootloader_name.call_once(|| name);
}
fn init_kernel_commandline(kernel_cmdline: &'static Once<KCmdlineArg>) {
let cmdline_c_str: &CStr =
unsafe { CStr::from_ptr(BOOT_PARAMS.get().unwrap().hdr.cmd_line_ptr as *const i8) };
fn parse_kernel_commandline(boot_params: &BootParams) -> &str {
// SAFETY: The pointer in the header points to a valid C string.
let cmdline_c_str: &CStr = unsafe { CStr::from_ptr(boot_params.hdr.cmd_line_ptr as *const i8) };
let cmdline_str = cmdline_c_str.to_str().unwrap();
kernel_cmdline.call_once(|| cmdline_str.into());
cmdline_str
}
fn init_initramfs(initramfs: &'static Once<&'static [u8]>) {
let hdr = &BOOT_PARAMS.get().unwrap().hdr;
fn parse_initramfs(boot_params: &BootParams) -> Option<&[u8]> {
let hdr = &boot_params.hdr;
let ptr = hdr.ramdisk_image as usize;
if ptr == 0 {
return;
return None;
}
// We must return a slice composed by VA since kernel should read everything in VA.
let base_va = if ptr < LINEAR_MAPPING_BASE_VADDR {
@ -80,30 +64,32 @@ fn init_initramfs(initramfs: &'static Once<&'static [u8]>) {
};
let length = hdr.ramdisk_size as usize;
if length == 0 {
return;
return None;
}
initramfs.call_once(|| unsafe { core::slice::from_raw_parts(base_va as *const u8, length) });
// SAFETY: The regions is reported as initramfs by the bootloader, so it should be valid.
Some(unsafe { core::slice::from_raw_parts(base_va as *const u8, length) })
}
fn init_acpi_arg(acpi: &'static Once<BootloaderAcpiArg>) {
let rsdp = BOOT_PARAMS.get().unwrap().acpi_rsdp_addr;
fn parse_acpi_arg(boot_params: &BootParams) -> BootloaderAcpiArg {
let rsdp = boot_params.acpi_rsdp_addr;
if rsdp == 0 {
acpi.call_once(|| BootloaderAcpiArg::NotProvided);
BootloaderAcpiArg::NotProvided
} else {
acpi.call_once(|| {
BootloaderAcpiArg::Rsdp(rsdp.try_into().expect("RSDP address overflowed!"))
});
}
}
fn init_framebuffer_info(framebuffer_arg: &'static Once<BootloaderFramebufferArg>) {
let screen_info = &BOOT_PARAMS.get().unwrap().screen_info;
framebuffer_arg.call_once(|| BootloaderFramebufferArg {
fn parse_framebuffer_info(boot_params: &BootParams) -> Option<BootloaderFramebufferArg> {
let screen_info = boot_params.screen_info;
if screen_info.lfb_base == 0 {
return None;
}
Some(BootloaderFramebufferArg {
address: screen_info.lfb_base as usize,
width: screen_info.lfb_width as usize,
height: screen_info.lfb_height as usize,
bpp: screen_info.lfb_depth as usize,
});
})
}
impl From<E820Type> for MemoryRegionType {
@ -118,11 +104,9 @@ impl From<E820Type> for MemoryRegionType {
}
}
fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
fn parse_memory_regions(boot_params: &BootParams) -> MemoryRegionArray {
let mut regions = MemoryRegionArray::new();
let boot_params = BOOT_PARAMS.get().unwrap();
// Add regions from E820.
let num_entries = boot_params.e820_entries as usize;
for e820_entry in &boot_params.e820_table[0..num_entries] {
@ -156,22 +140,25 @@ fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
))
.unwrap();
memory_regions.call_once(|| regions.into_non_overlapping());
regions.into_non_overlapping()
}
/// The entry point of the Rust code portion of Asterinas.
#[no_mangle]
unsafe extern "sysv64" fn __linux_boot(params_ptr: *const BootParams) -> ! {
let params = *params_ptr;
let params = unsafe { &*params_ptr };
assert_eq!({ params.hdr.header }, LINUX_BOOT_HEADER_MAGIC);
BOOT_PARAMS.call_once(|| params);
crate::boot::register_boot_init_callbacks(
init_bootloader_name,
init_kernel_commandline,
init_initramfs,
init_acpi_arg,
init_framebuffer_info,
init_memory_regions,
);
crate::boot::call_ostd_main();
use crate::boot::{call_ostd_main, EarlyBootInfo, EARLY_INFO};
EARLY_INFO.call_once(|| EarlyBootInfo {
bootloader_name: parse_bootloader_name(params),
kernel_cmdline: parse_kernel_commandline(params),
initramfs: parse_initramfs(params),
acpi_arg: parse_acpi_arg(params),
framebuffer_arg: parse_framebuffer_info(params),
memory_regions: parse_memory_regions(params),
});
call_ostd_main();
}

View File

@ -1,13 +1,9 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::string::String;
use core::arch::global_asm;
use spin::Once;
use crate::{
boot::{
kcmdline::KCmdlineArg,
memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType},
BootloaderAcpiArg, BootloaderFramebufferArg,
},
@ -21,14 +17,12 @@ global_asm!(include_str!("header.S"));
pub(super) const MULTIBOOT_ENTRY_MAGIC: u32 = 0x2BADB002;
fn init_bootloader_name(bootloader_name: &'static Once<String>) {
bootloader_name.call_once(|| {
let mut name = "";
let info = MB1_INFO.get().unwrap();
if info.boot_loader_name != 0 {
fn parse_bootloader_name(mb1_info: &MultibootLegacyInfo) -> &str {
let mut name = "Unknown Multiboot loader";
if mb1_info.boot_loader_name != 0 {
// SAFETY: the bootloader name is C-style zero-terminated string.
unsafe {
let cstr = paddr_to_vaddr(info.boot_loader_name as usize) as *const u8;
let cstr = paddr_to_vaddr(mb1_info.boot_loader_name as usize) as *const u8;
let mut len = 0;
while cstr.add(len).read() != 0 {
len += 1;
@ -38,18 +32,15 @@ fn init_bootloader_name(bootloader_name: &'static Once<String>) {
.expect("cmdline is not a utf-8 string");
}
}
name.into()
});
name
}
fn init_kernel_commandline(kernel_cmdline: &'static Once<KCmdlineArg>) {
kernel_cmdline.call_once(|| {
fn parse_kernel_commandline(mb1_info: &MultibootLegacyInfo) -> &str {
let mut cmdline = "";
let info = MB1_INFO.get().unwrap();
if info.cmdline != 0 {
if mb1_info.cmdline != 0 {
// SAFETY: the command line is C-style zero-terminated string.
unsafe {
let cstr = paddr_to_vaddr(info.cmdline as usize) as *const u8;
let cstr = paddr_to_vaddr(mb1_info.cmdline as usize) as *const u8;
let mut len = 0;
while cstr.add(len).read() != 0 {
len += 1;
@ -59,17 +50,15 @@ fn init_kernel_commandline(kernel_cmdline: &'static Once<KCmdlineArg>) {
.expect("cmdline is not a utf-8 string");
}
}
cmdline.into()
});
cmdline
}
fn init_initramfs(initramfs: &'static Once<&'static [u8]>) {
let info = MB1_INFO.get().unwrap();
fn parse_initramfs(mb1_info: &MultibootLegacyInfo) -> Option<&[u8]> {
// FIXME: We think all modules are initramfs, can this cause problems?
if info.mods_count == 0 {
return;
if mb1_info.mods_count == 0 {
return None;
}
let modules_addr = info.mods_addr as usize;
let modules_addr = mb1_info.mods_addr as usize;
// We only use one module
let (start, end) = unsafe {
(
@ -84,32 +73,33 @@ fn init_initramfs(initramfs: &'static Once<&'static [u8]>) {
start
};
let length = end - start;
initramfs.call_once(|| unsafe { core::slice::from_raw_parts(base_va as *const u8, length) });
Some(unsafe { core::slice::from_raw_parts(base_va as *const u8, length) })
}
fn init_acpi_arg(acpi: &'static Once<BootloaderAcpiArg>) {
fn parse_acpi_arg(_mb1_info: &MultibootLegacyInfo) -> BootloaderAcpiArg {
// The multiboot protocol does not contain RSDP address.
// TODO: What about UEFI?
acpi.call_once(|| BootloaderAcpiArg::NotProvided);
BootloaderAcpiArg::NotProvided
}
fn init_framebuffer_info(framebuffer_arg: &'static Once<BootloaderFramebufferArg>) {
let info = MB1_INFO.get().unwrap();
framebuffer_arg.call_once(|| BootloaderFramebufferArg {
address: info.framebuffer_table.addr as usize,
width: info.framebuffer_table.width as usize,
height: info.framebuffer_table.height as usize,
bpp: info.framebuffer_table.bpp as usize,
});
fn parse_framebuffer_info(mb1_info: &MultibootLegacyInfo) -> Option<BootloaderFramebufferArg> {
if mb1_info.framebuffer_table.addr == 0 {
return None;
}
Some(BootloaderFramebufferArg {
address: mb1_info.framebuffer_table.addr as usize,
width: mb1_info.framebuffer_table.width as usize,
height: mb1_info.framebuffer_table.height as usize,
bpp: mb1_info.framebuffer_table.bpp as usize,
})
}
fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
fn parse_memory_regions(mb1_info: &MultibootLegacyInfo) -> MemoryRegionArray {
let mut regions = MemoryRegionArray::new();
let info = MB1_INFO.get().unwrap();
// Add the regions in the multiboot protocol.
for entry in info.get_memory_map() {
for entry in mb1_info.get_memory_map() {
let start = entry.base_addr();
let region = MemoryRegion::new(
start.try_into().unwrap(),
@ -121,10 +111,10 @@ fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
// Add the framebuffer region.
let fb = BootloaderFramebufferArg {
address: info.framebuffer_table.addr as usize,
width: info.framebuffer_table.width as usize,
height: info.framebuffer_table.height as usize,
bpp: info.framebuffer_table.bpp as usize,
address: mb1_info.framebuffer_table.addr as usize,
width: mb1_info.framebuffer_table.width as usize,
height: mb1_info.framebuffer_table.height as usize,
bpp: mb1_info.framebuffer_table.bpp as usize,
};
regions
.push(MemoryRegion::new(
@ -138,8 +128,8 @@ fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
regions.push(MemoryRegion::kernel()).unwrap();
// Add the initramfs area.
if info.mods_count != 0 {
let modules_addr = info.mods_addr as usize;
if mb1_info.mods_count != 0 {
let modules_addr = mb1_info.mods_addr as usize;
// We only use one module
let (start, end) = unsafe {
(
@ -165,8 +155,7 @@ fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
))
.unwrap();
// Initialize with non-overlapping regions.
memory_regions.call_once(move || regions.into_non_overlapping());
regions.into_non_overlapping()
}
/// Representation of Multiboot Information according to specification.
@ -402,20 +391,23 @@ impl Iterator for MemoryEntryIter {
}
}
static MB1_INFO: Once<&'static MultibootLegacyInfo> = Once::new();
/// The entry point of Rust code called by inline asm.
#[no_mangle]
unsafe extern "sysv64" fn __multiboot_entry(boot_magic: u32, boot_params: u64) -> ! {
assert_eq!(boot_magic, MULTIBOOT_ENTRY_MAGIC);
MB1_INFO.call_once(|| &*(paddr_to_vaddr(boot_params as usize) as *const MultibootLegacyInfo));
crate::boot::register_boot_init_callbacks(
init_bootloader_name,
init_kernel_commandline,
init_initramfs,
init_acpi_arg,
init_framebuffer_info,
init_memory_regions,
);
crate::boot::call_ostd_main();
let mb1_info =
unsafe { &*(paddr_to_vaddr(boot_params as usize) as *const MultibootLegacyInfo) };
use crate::boot::{call_ostd_main, EarlyBootInfo, EARLY_INFO};
EARLY_INFO.call_once(|| EarlyBootInfo {
bootloader_name: parse_bootloader_name(mb1_info),
kernel_cmdline: parse_kernel_commandline(mb1_info),
initramfs: parse_initramfs(mb1_info),
acpi_arg: parse_acpi_arg(mb1_info),
framebuffer_arg: parse_framebuffer_info(mb1_info),
memory_regions: parse_memory_regions(mb1_info),
});
call_ostd_main();
}

View File

@ -1,6 +1,5 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::string::{String, ToString};
use core::arch::global_asm;
use multiboot2::{BootInformation, BootInformationHeader, MemoryAreaType};
@ -8,7 +7,6 @@ use spin::Once;
use crate::{
boot::{
kcmdline::KCmdlineArg,
memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType},
BootloaderAcpiArg, BootloaderFramebufferArg,
},
@ -21,8 +19,7 @@ pub(super) const MULTIBOOT2_ENTRY_MAGIC: u32 = 0x36d76289;
static MB2_INFO: Once<BootInformation> = Once::new();
fn init_bootloader_name(bootloader_name: &'static Once<String>) {
bootloader_name.call_once(|| {
fn parse_bootloader_name() -> &'static str {
MB2_INFO
.get()
.unwrap()
@ -30,12 +27,9 @@ fn init_bootloader_name(bootloader_name: &'static Once<String>) {
.expect("Bootloader name not found from the Multiboot2 header!")
.name()
.expect("UTF-8 error: failed to parse bootloader name!")
.to_string()
});
}
fn init_kernel_commandline(kernel_cmdline: &'static Once<KCmdlineArg>) {
kernel_cmdline.call_once(|| {
fn parse_kernel_commandline() -> &'static str {
MB2_INFO
.get()
.unwrap()
@ -43,23 +37,18 @@ fn init_kernel_commandline(kernel_cmdline: &'static Once<KCmdlineArg>) {
.expect("Kernel command-line not found from the Multiboot2 header!")
.cmdline()
.expect("UTF-8 error: failed to parse kernel command-line!")
.into()
});
}
fn init_initramfs(initramfs: &'static Once<&'static [u8]>) {
let Some(mb2_module_tag) = MB2_INFO.get().unwrap().module_tags().next() else {
return;
};
fn parse_initramfs() -> Option<&'static [u8]> {
let mb2_module_tag = MB2_INFO.get().unwrap().module_tags().next()?;
let base_addr = mb2_module_tag.start_address() as usize;
// We must return a slice composed by VA since kernel should read everything in VA.
let base_va = paddr_to_vaddr(base_addr);
let length = mb2_module_tag.module_size() as usize;
initramfs.call_once(|| unsafe { core::slice::from_raw_parts(base_va as *const u8, length) });
Some(unsafe { core::slice::from_raw_parts(base_va as *const u8, length) })
}
fn init_acpi_arg(acpi: &'static Once<BootloaderAcpiArg>) {
acpi.call_once(|| {
fn parse_acpi_arg() -> BootloaderAcpiArg {
if let Some(v2_tag) = MB2_INFO.get().unwrap().rsdp_v2_tag() {
// check for rsdp v2
BootloaderAcpiArg::Xsdt(v2_tag.xsdt_address())
@ -67,21 +56,20 @@ fn init_acpi_arg(acpi: &'static Once<BootloaderAcpiArg>) {
// fall back to rsdp v1
BootloaderAcpiArg::Rsdt(v1_tag.rsdt_address())
} else {
panic!("No ACPI RDSP information found!");
BootloaderAcpiArg::NotProvided
}
});
}
fn init_framebuffer_info(framebuffer_arg: &'static Once<BootloaderFramebufferArg>) {
fn parse_framebuffer_info() -> Option<BootloaderFramebufferArg> {
let Some(Ok(fb_tag)) = MB2_INFO.get().unwrap().framebuffer_tag() else {
return;
return None;
};
framebuffer_arg.call_once(|| BootloaderFramebufferArg {
Some(BootloaderFramebufferArg {
address: fb_tag.address() as usize,
width: fb_tag.width() as usize,
height: fb_tag.height() as usize,
bpp: fb_tag.bpp() as usize,
});
})
}
impl From<MemoryAreaType> for MemoryRegionType {
@ -96,7 +84,7 @@ impl From<MemoryAreaType> for MemoryRegionType {
}
}
fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
fn parse_memory_regions() -> MemoryRegionArray {
let mut regions = MemoryRegionArray::new();
let mb2_info = MB2_INFO.get().unwrap();
@ -158,8 +146,7 @@ fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
))
.unwrap();
// Initialize with non-overlapping regions.
memory_regions.call_once(move || regions.into_non_overlapping());
regions.into_non_overlapping()
}
/// The entry point of Rust code called by inline asm.
@ -169,13 +156,17 @@ unsafe extern "sysv64" fn __multiboot2_entry(boot_magic: u32, boot_params: u64)
MB2_INFO.call_once(|| unsafe {
BootInformation::load(boot_params as *const BootInformationHeader).unwrap()
});
crate::boot::register_boot_init_callbacks(
init_bootloader_name,
init_kernel_commandline,
init_initramfs,
init_acpi_arg,
init_framebuffer_info,
init_memory_regions,
);
crate::boot::call_ostd_main();
use crate::boot::{call_ostd_main, EarlyBootInfo, EARLY_INFO};
EARLY_INFO.call_once(|| EarlyBootInfo {
bootloader_name: parse_bootloader_name(),
kernel_cmdline: parse_kernel_commandline(),
initramfs: parse_initramfs(),
acpi_arg: parse_acpi_arg(),
framebuffer_arg: parse_framebuffer_info(),
memory_regions: parse_memory_regions(),
});
call_ostd_main();
}

View File

@ -5,7 +5,6 @@
pub mod dmar;
pub mod remapping;
use alloc::borrow::ToOwned;
use core::ptr::NonNull;
use acpi::{rsdp::Rsdp, AcpiHandler, AcpiTables};
@ -43,7 +42,7 @@ impl AcpiHandler for AcpiMemoryHandler {
}
pub fn init() {
let acpi_tables = match boot::acpi_arg().to_owned() {
let acpi_tables = match boot::EARLY_INFO.get().unwrap().acpi_arg {
BootloaderAcpiArg::Rsdp(addr) => unsafe {
AcpiTables::from_rsdp(AcpiMemoryHandler {}, addr).unwrap()
},

View File

@ -1,7 +1,5 @@
// SPDX-License-Identifier: MPL-2.0
#![allow(dead_code)]
//! The architecture-independent boot module, which provides
//! 1. a universal information getter interface from the bootloader to the
//! rest of OSTD;
@ -12,12 +10,35 @@ pub mod kcmdline;
pub mod memory_region;
pub mod smp;
use alloc::string::String;
use alloc::{
string::{String, ToString},
vec::Vec,
};
use kcmdline::KCmdlineArg;
use memory_region::{MemoryRegion, MemoryRegionArray};
use spin::Once;
use self::memory_region::MemoryRegionArray;
/// 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: KCmdlineArg,
/// 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.
pub fn boot_info() -> &'static BootInfo {
INFO.get().unwrap()
}
static INFO: Once<BootInfo> = Once::new();
/// ACPI information from the bootloader.
///
@ -49,66 +70,41 @@ pub struct BootloaderFramebufferArg {
pub bpp: usize,
}
macro_rules! define_global_static_boot_arguments {
( $( $lower:ident, $upper:ident, $typ:ty; )* ) => {
// Define statics and corresponding public getter APIs.
$(
static $upper: Once<$typ> = Once::new();
/// Macro generated public getter API.
pub fn $lower() -> &'static $typ {
$upper.get().unwrap()
}
)*
/*************************** Boot-time information ***************************/
struct BootInitCallBacks {
$( $lower: fn(&'static Once<$typ>) -> (), )*
}
static BOOT_INIT_CALLBACKS: Once<BootInitCallBacks> = Once::new();
/// The macro generated boot init callbacks registering interface.
///
/// For the introduction of a new boot protocol, the entry point could be a novel
/// one. The entry point function should register all the boot initialization
/// methods before `ostd::main` is called. A boot initialization method takes a
/// reference of the global static boot information variable and initialize it,
/// so that the boot information it represents could be accessed in the kernel
/// anywhere.
///
/// The reason why the entry point function is not designed to directly initialize
/// the boot information variables is simply that the heap is not initialized at
/// that moment.
pub fn register_boot_init_callbacks($( $lower: fn(&'static Once<$typ>) -> (), )* ) {
BOOT_INIT_CALLBACKS.call_once(|| {
BootInitCallBacks { $( $lower, )* }
});
}
fn call_all_boot_init_callbacks() {
let callbacks = &BOOT_INIT_CALLBACKS.get().unwrap();
$( (callbacks.$lower)(&$upper); )*
}
};
/// 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,
}
// Define a series of static variables and their getter APIs.
define_global_static_boot_arguments!(
// Getter Names | Static Variables | Variable Types
bootloader_name, BOOTLOADER_NAME, String;
kernel_cmdline, KERNEL_CMDLINE, KCmdlineArg;
initramfs, INITRAMFS, &'static [u8];
acpi_arg, ACPI_ARG, BootloaderAcpiArg;
framebuffer_arg, FRAMEBUFFER_ARG, BootloaderFramebufferArg;
memory_regions, MEMORY_REGIONS, MemoryRegionArray;
);
/// The boot-time information.
pub(crate) static EARLY_INFO: Once<EarlyBootInfo> = Once::new();
/// The initialization method of the boot module.
/// Initializes the runtime information.
///
/// After initializing the boot module, the get functions could be called.
/// The initialization must be done after the heap is set and before physical
/// mappings are cancelled.
pub fn init() {
call_all_boot_init_callbacks();
/// This function allows the run-time getters to work properly.
pub(crate) fn init() {
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.into(),
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.

View File

@ -31,6 +31,7 @@ struct PerApInfo {
// no longer be used, and the `Segment` can be deallocated (this problem also
// exists in the boot processor, but the memory it occupies should be returned
// to the frame allocator).
#[allow(dead_code)]
boot_stack_pages: Segment<KernelMeta>,
}

View File

@ -12,7 +12,7 @@ use core::str::FromStr;
use log::{LevelFilter, Metadata, Record};
use spin::Once;
use crate::boot::{kcmdline::ModuleArg, kernel_cmdline};
use crate::boot::{boot_info, kcmdline::ModuleArg};
static LOGGER: Logger = Logger::new();
@ -82,7 +82,7 @@ pub(crate) fn init() {
}
fn get_log_level() -> Option<LevelFilter> {
let module_args = kernel_cmdline().get_module_args("ostd")?;
let module_args = boot_info().kernel_cmdline.get_module_args("ostd")?;
let value = {
let value = module_args.iter().find_map(|arg| match arg {

View File

@ -184,7 +184,7 @@ impl CountingFrameAllocator {
pub(in crate::mm) static FRAME_ALLOCATOR: Once<SpinLock<CountingFrameAllocator>> = Once::new();
pub(crate) fn init() {
let regions = crate::boot::memory_regions();
let regions = &crate::boot::EARLY_INFO.get().unwrap().memory_regions;
let mut total: usize = 0;
let mut allocator = FrameAllocator::<32>::new();
for region in regions.iter() {

View File

@ -246,7 +246,7 @@ impl_frame_meta_for!(MetaPageMeta);
/// The function returns a list of `Frame`s containing the metadata.
pub(crate) fn init() -> Segment<MetaPageMeta> {
let max_paddr = {
let regions = crate::boot::memory_regions();
let regions = &crate::boot::EARLY_INFO.get().unwrap().memory_regions;
regions.iter().map(|r| r.base() + r.len()).max().unwrap()
};

View File

@ -134,7 +134,7 @@ pub static KERNEL_PAGE_TABLE: Once<PageTable<KernelMode, PageTableEntry, PagingC
pub fn init_kernel_page_table(meta_pages: Segment<MetaPageMeta>) {
info!("Initializing the kernel page table");
let regions = crate::boot::memory_regions();
let regions = &crate::boot::EARLY_INFO.get().unwrap().memory_regions;
let phys_mem_cap = regions.iter().map(|r| r.base() + r.len()).max().unwrap();
// Start to initialize the kernel page table.