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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
pub fn available_memory() -> usize { 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() regions.iter().map(|region| region.len()).sum()
} }

View File

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

View File

@ -1,13 +1,9 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use alloc::string::String;
use core::arch::global_asm; use core::arch::global_asm;
use spin::Once;
use crate::{ use crate::{
boot::{ boot::{
kcmdline::KCmdlineArg,
memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType}, memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType},
BootloaderAcpiArg, BootloaderFramebufferArg, BootloaderAcpiArg, BootloaderFramebufferArg,
}, },
@ -21,55 +17,48 @@ global_asm!(include_str!("header.S"));
pub(super) const MULTIBOOT_ENTRY_MAGIC: u32 = 0x2BADB002; pub(super) const MULTIBOOT_ENTRY_MAGIC: u32 = 0x2BADB002;
fn init_bootloader_name(bootloader_name: &'static Once<String>) { fn parse_bootloader_name(mb1_info: &MultibootLegacyInfo) -> &str {
bootloader_name.call_once(|| { let mut name = "Unknown Multiboot loader";
let mut name = ""; if mb1_info.boot_loader_name != 0 {
let info = MB1_INFO.get().unwrap(); // SAFETY: the bootloader name is C-style zero-terminated string.
if info.boot_loader_name != 0 { unsafe {
// SAFETY: the bootloader name is C-style zero-terminated string. let cstr = paddr_to_vaddr(mb1_info.boot_loader_name as usize) as *const u8;
unsafe { let mut len = 0;
let cstr = paddr_to_vaddr(info.boot_loader_name as usize) as *const u8; while cstr.add(len).read() != 0 {
let mut len = 0; len += 1;
while cstr.add(len).read() != 0 {
len += 1;
}
name = core::str::from_utf8(core::slice::from_raw_parts(cstr, len))
.expect("cmdline is not a utf-8 string");
} }
name = core::str::from_utf8(core::slice::from_raw_parts(cstr, len))
.expect("cmdline is not a utf-8 string");
} }
name.into()
});
}
fn init_kernel_commandline(kernel_cmdline: &'static Once<KCmdlineArg>) {
kernel_cmdline.call_once(|| {
let mut cmdline = "";
let info = MB1_INFO.get().unwrap();
if 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 mut len = 0;
while cstr.add(len).read() != 0 {
len += 1;
}
cmdline = core::str::from_utf8(core::slice::from_raw_parts(cstr, len))
.expect("cmdline is not a utf-8 string");
}
}
cmdline.into()
});
}
fn init_initramfs(initramfs: &'static Once<&'static [u8]>) {
let info = MB1_INFO.get().unwrap();
// FIXME: We think all modules are initramfs, can this cause problems?
if info.mods_count == 0 {
return;
} }
let modules_addr = info.mods_addr as usize; name
}
fn parse_kernel_commandline(mb1_info: &MultibootLegacyInfo) -> &str {
let mut cmdline = "";
if mb1_info.cmdline != 0 {
// SAFETY: the command line is C-style zero-terminated string.
unsafe {
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;
}
cmdline = core::str::from_utf8(core::slice::from_raw_parts(cstr, len))
.expect("cmdline is not a utf-8 string");
}
}
cmdline
}
fn parse_initramfs(mb1_info: &MultibootLegacyInfo) -> Option<&[u8]> {
// FIXME: We think all modules are initramfs, can this cause problems?
if mb1_info.mods_count == 0 {
return None;
}
let modules_addr = mb1_info.mods_addr as usize;
// We only use one module // We only use one module
let (start, end) = unsafe { let (start, end) = unsafe {
( (
@ -84,32 +73,33 @@ fn init_initramfs(initramfs: &'static Once<&'static [u8]>) {
start start
}; };
let length = end - 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. // The multiboot protocol does not contain RSDP address.
// TODO: What about UEFI? // TODO: What about UEFI?
acpi.call_once(|| BootloaderAcpiArg::NotProvided); BootloaderAcpiArg::NotProvided
} }
fn init_framebuffer_info(framebuffer_arg: &'static Once<BootloaderFramebufferArg>) { fn parse_framebuffer_info(mb1_info: &MultibootLegacyInfo) -> Option<BootloaderFramebufferArg> {
let info = MB1_INFO.get().unwrap(); if mb1_info.framebuffer_table.addr == 0 {
framebuffer_arg.call_once(|| BootloaderFramebufferArg { return None;
address: info.framebuffer_table.addr as usize, }
width: info.framebuffer_table.width as usize, Some(BootloaderFramebufferArg {
height: info.framebuffer_table.height as usize, address: mb1_info.framebuffer_table.addr as usize,
bpp: info.framebuffer_table.bpp 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 mut regions = MemoryRegionArray::new();
let info = MB1_INFO.get().unwrap();
// Add the regions in the multiboot protocol. // 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 start = entry.base_addr();
let region = MemoryRegion::new( let region = MemoryRegion::new(
start.try_into().unwrap(), start.try_into().unwrap(),
@ -121,10 +111,10 @@ fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
// Add the framebuffer region. // Add the framebuffer region.
let fb = BootloaderFramebufferArg { let fb = BootloaderFramebufferArg {
address: info.framebuffer_table.addr as usize, address: mb1_info.framebuffer_table.addr as usize,
width: info.framebuffer_table.width as usize, width: mb1_info.framebuffer_table.width as usize,
height: info.framebuffer_table.height as usize, height: mb1_info.framebuffer_table.height as usize,
bpp: info.framebuffer_table.bpp as usize, bpp: mb1_info.framebuffer_table.bpp as usize,
}; };
regions regions
.push(MemoryRegion::new( .push(MemoryRegion::new(
@ -138,8 +128,8 @@ fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
regions.push(MemoryRegion::kernel()).unwrap(); regions.push(MemoryRegion::kernel()).unwrap();
// Add the initramfs area. // Add the initramfs area.
if info.mods_count != 0 { if mb1_info.mods_count != 0 {
let modules_addr = info.mods_addr as usize; let modules_addr = mb1_info.mods_addr as usize;
// We only use one module // We only use one module
let (start, end) = unsafe { let (start, end) = unsafe {
( (
@ -165,8 +155,7 @@ fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
)) ))
.unwrap(); .unwrap();
// Initialize with non-overlapping regions. regions.into_non_overlapping()
memory_regions.call_once(move || regions.into_non_overlapping());
} }
/// Representation of Multiboot Information according to specification. /// 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. /// The entry point of Rust code called by inline asm.
#[no_mangle] #[no_mangle]
unsafe extern "sysv64" fn __multiboot_entry(boot_magic: u32, boot_params: u64) -> ! { unsafe extern "sysv64" fn __multiboot_entry(boot_magic: u32, boot_params: u64) -> ! {
assert_eq!(boot_magic, MULTIBOOT_ENTRY_MAGIC); assert_eq!(boot_magic, MULTIBOOT_ENTRY_MAGIC);
MB1_INFO.call_once(|| &*(paddr_to_vaddr(boot_params as usize) as *const MultibootLegacyInfo)); let mb1_info =
crate::boot::register_boot_init_callbacks( unsafe { &*(paddr_to_vaddr(boot_params as usize) as *const MultibootLegacyInfo) };
init_bootloader_name,
init_kernel_commandline, use crate::boot::{call_ostd_main, EarlyBootInfo, EARLY_INFO};
init_initramfs,
init_acpi_arg, EARLY_INFO.call_once(|| EarlyBootInfo {
init_framebuffer_info, bootloader_name: parse_bootloader_name(mb1_info),
init_memory_regions, kernel_cmdline: parse_kernel_commandline(mb1_info),
); initramfs: parse_initramfs(mb1_info),
crate::boot::call_ostd_main(); 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 // SPDX-License-Identifier: MPL-2.0
use alloc::string::{String, ToString};
use core::arch::global_asm; use core::arch::global_asm;
use multiboot2::{BootInformation, BootInformationHeader, MemoryAreaType}; use multiboot2::{BootInformation, BootInformationHeader, MemoryAreaType};
@ -8,7 +7,6 @@ use spin::Once;
use crate::{ use crate::{
boot::{ boot::{
kcmdline::KCmdlineArg,
memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType}, memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType},
BootloaderAcpiArg, BootloaderFramebufferArg, BootloaderAcpiArg, BootloaderFramebufferArg,
}, },
@ -21,67 +19,57 @@ pub(super) const MULTIBOOT2_ENTRY_MAGIC: u32 = 0x36d76289;
static MB2_INFO: Once<BootInformation> = Once::new(); static MB2_INFO: Once<BootInformation> = Once::new();
fn init_bootloader_name(bootloader_name: &'static Once<String>) { fn parse_bootloader_name() -> &'static str {
bootloader_name.call_once(|| { MB2_INFO
MB2_INFO .get()
.get() .unwrap()
.unwrap() .boot_loader_name_tag()
.boot_loader_name_tag() .expect("Bootloader name not found from the Multiboot2 header!")
.expect("Bootloader name not found from the Multiboot2 header!") .name()
.name() .expect("UTF-8 error: failed to parse bootloader name!")
.expect("UTF-8 error: failed to parse bootloader name!")
.to_string()
});
} }
fn init_kernel_commandline(kernel_cmdline: &'static Once<KCmdlineArg>) { fn parse_kernel_commandline() -> &'static str {
kernel_cmdline.call_once(|| { MB2_INFO
MB2_INFO .get()
.get() .unwrap()
.unwrap() .command_line_tag()
.command_line_tag() .expect("Kernel command-line not found from the Multiboot2 header!")
.expect("Kernel command-line not found from the Multiboot2 header!") .cmdline()
.cmdline() .expect("UTF-8 error: failed to parse kernel command-line!")
.expect("UTF-8 error: failed to parse kernel command-line!")
.into()
});
} }
fn init_initramfs(initramfs: &'static Once<&'static [u8]>) { fn parse_initramfs() -> Option<&'static [u8]> {
let Some(mb2_module_tag) = MB2_INFO.get().unwrap().module_tags().next() else { let mb2_module_tag = MB2_INFO.get().unwrap().module_tags().next()?;
return;
};
let base_addr = mb2_module_tag.start_address() as usize; 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. // We must return a slice composed by VA since kernel should read everything in VA.
let base_va = paddr_to_vaddr(base_addr); let base_va = paddr_to_vaddr(base_addr);
let length = mb2_module_tag.module_size() as usize; 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>) { fn parse_acpi_arg() -> BootloaderAcpiArg {
acpi.call_once(|| { if let Some(v2_tag) = MB2_INFO.get().unwrap().rsdp_v2_tag() {
if let Some(v2_tag) = MB2_INFO.get().unwrap().rsdp_v2_tag() { // check for rsdp v2
// check for rsdp v2 BootloaderAcpiArg::Xsdt(v2_tag.xsdt_address())
BootloaderAcpiArg::Xsdt(v2_tag.xsdt_address()) } else if let Some(v1_tag) = MB2_INFO.get().unwrap().rsdp_v1_tag() {
} else if let Some(v1_tag) = MB2_INFO.get().unwrap().rsdp_v1_tag() { // fall back to rsdp v1
// fall back to rsdp v1 BootloaderAcpiArg::Rsdt(v1_tag.rsdt_address())
BootloaderAcpiArg::Rsdt(v1_tag.rsdt_address()) } else {
} else { BootloaderAcpiArg::NotProvided
panic!("No ACPI RDSP information found!"); }
}
});
} }
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 { 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, address: fb_tag.address() as usize,
width: fb_tag.width() as usize, width: fb_tag.width() as usize,
height: fb_tag.height() as usize, height: fb_tag.height() as usize,
bpp: fb_tag.bpp() as usize, bpp: fb_tag.bpp() as usize,
}); })
} }
impl From<MemoryAreaType> for MemoryRegionType { 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 mut regions = MemoryRegionArray::new();
let mb2_info = MB2_INFO.get().unwrap(); let mb2_info = MB2_INFO.get().unwrap();
@ -158,8 +146,7 @@ fn init_memory_regions(memory_regions: &'static Once<MemoryRegionArray>) {
)) ))
.unwrap(); .unwrap();
// Initialize with non-overlapping regions. regions.into_non_overlapping()
memory_regions.call_once(move || regions.into_non_overlapping());
} }
/// The entry point of Rust code called by inline asm. /// 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 { MB2_INFO.call_once(|| unsafe {
BootInformation::load(boot_params as *const BootInformationHeader).unwrap() BootInformation::load(boot_params as *const BootInformationHeader).unwrap()
}); });
crate::boot::register_boot_init_callbacks(
init_bootloader_name, use crate::boot::{call_ostd_main, EarlyBootInfo, EARLY_INFO};
init_kernel_commandline,
init_initramfs, EARLY_INFO.call_once(|| EarlyBootInfo {
init_acpi_arg, bootloader_name: parse_bootloader_name(),
init_framebuffer_info, kernel_cmdline: parse_kernel_commandline(),
init_memory_regions, initramfs: parse_initramfs(),
); acpi_arg: parse_acpi_arg(),
crate::boot::call_ostd_main(); framebuffer_arg: parse_framebuffer_info(),
memory_regions: parse_memory_regions(),
});
call_ostd_main();
} }

View File

@ -5,7 +5,6 @@
pub mod dmar; pub mod dmar;
pub mod remapping; pub mod remapping;
use alloc::borrow::ToOwned;
use core::ptr::NonNull; use core::ptr::NonNull;
use acpi::{rsdp::Rsdp, AcpiHandler, AcpiTables}; use acpi::{rsdp::Rsdp, AcpiHandler, AcpiTables};
@ -43,7 +42,7 @@ impl AcpiHandler for AcpiMemoryHandler {
} }
pub fn init() { 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 { BootloaderAcpiArg::Rsdp(addr) => unsafe {
AcpiTables::from_rsdp(AcpiMemoryHandler {}, addr).unwrap() AcpiTables::from_rsdp(AcpiMemoryHandler {}, addr).unwrap()
}, },

View File

@ -1,7 +1,5 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
#![allow(dead_code)]
//! The architecture-independent boot module, which provides //! The architecture-independent boot module, which provides
//! 1. a universal information getter interface from the bootloader to the //! 1. a universal information getter interface from the bootloader to the
//! rest of OSTD; //! rest of OSTD;
@ -12,12 +10,35 @@ pub mod kcmdline;
pub mod memory_region; pub mod memory_region;
pub mod smp; pub mod smp;
use alloc::string::String; use alloc::{
string::{String, ToString},
vec::Vec,
};
use kcmdline::KCmdlineArg; use kcmdline::KCmdlineArg;
use memory_region::{MemoryRegion, MemoryRegionArray};
use spin::Once; 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. /// ACPI information from the bootloader.
/// ///
@ -49,66 +70,41 @@ pub struct BootloaderFramebufferArg {
pub bpp: usize, pub bpp: usize,
} }
macro_rules! define_global_static_boot_arguments { /*************************** Boot-time information ***************************/
( $( $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()
}
)*
struct BootInitCallBacks { /// The boot-time boot information.
$( $lower: fn(&'static Once<$typ>) -> (), )* ///
} /// When supporting multiple boot protocols with a single build, the entrypoint
/// and boot information getters are dynamically decided. The entry point
static BOOT_INIT_CALLBACKS: Once<BootInitCallBacks> = Once::new(); /// function should initializer all arguments at [`EARLY_INFO`].
///
/// The macro generated boot init callbacks registering interface. /// All the references in this structure should be valid in the boot context.
/// /// After the kernel is booted, users should use [`BootInfo`] instead.
/// For the introduction of a new boot protocol, the entry point could be a novel pub(crate) struct EarlyBootInfo {
/// one. The entry point function should register all the boot initialization pub(crate) bootloader_name: &'static str,
/// methods before `ostd::main` is called. A boot initialization method takes a pub(crate) kernel_cmdline: &'static str,
/// reference of the global static boot information variable and initialize it, pub(crate) initramfs: Option<&'static [u8]>,
/// so that the boot information it represents could be accessed in the kernel pub(crate) acpi_arg: BootloaderAcpiArg,
/// anywhere. pub(crate) framebuffer_arg: Option<BootloaderFramebufferArg>,
/// pub(crate) memory_regions: MemoryRegionArray,
/// 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); )*
}
};
} }
// Define a series of static variables and their getter APIs. /// The boot-time information.
define_global_static_boot_arguments!( pub(crate) static EARLY_INFO: Once<EarlyBootInfo> = Once::new();
// 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 initialization method of the boot module. /// Initializes the runtime information.
/// ///
/// After initializing the boot module, the get functions could be called. /// This function allows the run-time getters to work properly.
/// The initialization must be done after the heap is set and before physical pub(crate) fn init() {
/// mappings are cancelled. let boot_time_info = EARLY_INFO.get().unwrap();
pub fn init() {
call_all_boot_init_callbacks(); 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. /// 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 // 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 // exists in the boot processor, but the memory it occupies should be returned
// to the frame allocator). // to the frame allocator).
#[allow(dead_code)]
boot_stack_pages: Segment<KernelMeta>, boot_stack_pages: Segment<KernelMeta>,
} }

View File

@ -12,7 +12,7 @@ use core::str::FromStr;
use log::{LevelFilter, Metadata, Record}; use log::{LevelFilter, Metadata, Record};
use spin::Once; use spin::Once;
use crate::boot::{kcmdline::ModuleArg, kernel_cmdline}; use crate::boot::{boot_info, kcmdline::ModuleArg};
static LOGGER: Logger = Logger::new(); static LOGGER: Logger = Logger::new();
@ -82,7 +82,7 @@ pub(crate) fn init() {
} }
fn get_log_level() -> Option<LevelFilter> { 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 = {
let value = module_args.iter().find_map(|arg| match arg { 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(in crate::mm) static FRAME_ALLOCATOR: Once<SpinLock<CountingFrameAllocator>> = Once::new();
pub(crate) fn init() { 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 total: usize = 0;
let mut allocator = FrameAllocator::<32>::new(); let mut allocator = FrameAllocator::<32>::new();
for region in regions.iter() { 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. /// The function returns a list of `Frame`s containing the metadata.
pub(crate) fn init() -> Segment<MetaPageMeta> { pub(crate) fn init() -> Segment<MetaPageMeta> {
let max_paddr = { 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() 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>) { pub fn init_kernel_page_table(meta_pages: Segment<MetaPageMeta>) {
info!("Initializing the kernel page table"); 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(); let phys_mem_cap = regions.iter().map(|r| r.base() + r.len()).max().unwrap();
// Start to initialize the kernel page table. // Start to initialize the kernel page table.