diff --git a/kernel/comps/framebuffer/src/lib.rs b/kernel/comps/framebuffer/src/lib.rs index 25035d032..90d146a4c 100644 --- a/kernel/comps/framebuffer/src/lib.rs +++ b/kernel/comps/framebuffer/src/lib.rs @@ -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> = 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(), } }; diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 2e2a9761a..c7f70df40 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -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(), diff --git a/osdk/src/commands/new/lib.template b/osdk/src/commands/new/lib.template index 744917421..63ccc9ef8 100644 --- a/osdk/src/commands/new/lib.template +++ b/osdk/src/commands/new/lib.template @@ -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()); } } diff --git a/osdk/tests/examples_in_book/work_in_workspace_templates/mylib/src/lib.rs b/osdk/tests/examples_in_book/work_in_workspace_templates/mylib/src/lib.rs index 5ab331370..bdbc2e7b7 100644 --- a/osdk/tests/examples_in_book/work_in_workspace_templates/mylib/src/lib.rs +++ b/osdk/tests/examples_in_book/work_in_workspace_templates/mylib/src/lib.rs @@ -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() } diff --git a/ostd/src/arch/riscv/boot/mod.rs b/ostd/src/arch/riscv/boot/mod.rs index aba50c2d7..d88524526 100644 --- a/ostd/src/arch/riscv/boot/mod.rs +++ b/ostd/src/arch/riscv/boot/mod.rs @@ -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 = Once::new(); -fn init_bootloader_name(bootloader_name: &'static Once) { - bootloader_name.call_once(|| "Unknown".into()); +fn parse_bootloader_name() -> &'static str { + "Unknown" } -fn init_kernel_commandline(kernel_cmdline: &'static Once) { - 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) { - acpi.call_once(|| BootloaderAcpiArg::NotProvided); +fn parse_acpi_arg() -> BootloaderAcpiArg { + BootloaderAcpiArg::NotProvided } -fn init_framebuffer_info(_framebuffer_arg: &'static Once) {} +fn parse_framebuffer_info() -> Option { + None +} -fn init_memory_regions(memory_regions: &'static Once) { +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) { )); } - 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(); } diff --git a/ostd/src/arch/x86/boot/linux_boot/mod.rs b/ostd/src/arch/x86/boot/linux_boot/mod.rs index 4886c9133..805e02b6f 100644 --- a/ostd/src/arch/x86/boot/linux_boot/mod.rs +++ b/ostd/src/arch/x86/boot/linux_boot/mod.rs @@ -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 = Once::new(); - -fn init_bootloader_name(bootloader_name: &'static Once) { - 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) { 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 ", 0x12 => "OVMF UEFI virtualization stack", _ => "Unknown bootloader type!", } - .to_owned(); - bootloader_name.call_once(|| name); } -fn init_kernel_commandline(kernel_cmdline: &'static Once) { - 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) { - 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!")) - }); + BootloaderAcpiArg::Rsdp(rsdp.try_into().expect("RSDP address overflowed!")) } } -fn init_framebuffer_info(framebuffer_arg: &'static Once) { - let screen_info = &BOOT_PARAMS.get().unwrap().screen_info; - framebuffer_arg.call_once(|| BootloaderFramebufferArg { +fn parse_framebuffer_info(boot_params: &BootParams) -> Option { + 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 for MemoryRegionType { @@ -118,11 +104,9 @@ impl From for MemoryRegionType { } } -fn init_memory_regions(memory_regions: &'static Once) { +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) { )) .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(); } diff --git a/ostd/src/arch/x86/boot/multiboot/mod.rs b/ostd/src/arch/x86/boot/multiboot/mod.rs index 3cf1aff2a..1d43b25ef 100644 --- a/ostd/src/arch/x86/boot/multiboot/mod.rs +++ b/ostd/src/arch/x86/boot/multiboot/mod.rs @@ -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,55 +17,48 @@ global_asm!(include_str!("header.S")); pub(super) const MULTIBOOT_ENTRY_MAGIC: u32 = 0x2BADB002; -fn init_bootloader_name(bootloader_name: &'static Once) { - bootloader_name.call_once(|| { - let mut name = ""; - let info = MB1_INFO.get().unwrap(); - if 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 mut len = 0; - 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"); +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(mb1_info.boot_loader_name as usize) as *const u8; + let mut len = 0; + 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.into() - }); -} - -fn init_kernel_commandline(kernel_cmdline: &'static Once) { - 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 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) { +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) { - 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 { + 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) { +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) { // 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) { 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) { )) .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(); } diff --git a/ostd/src/arch/x86/boot/multiboot2/mod.rs b/ostd/src/arch/x86/boot/multiboot2/mod.rs index 5c075e6f2..ebbefc61d 100644 --- a/ostd/src/arch/x86/boot/multiboot2/mod.rs +++ b/ostd/src/arch/x86/boot/multiboot2/mod.rs @@ -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,67 +19,57 @@ pub(super) const MULTIBOOT2_ENTRY_MAGIC: u32 = 0x36d76289; static MB2_INFO: Once = Once::new(); -fn init_bootloader_name(bootloader_name: &'static Once) { - bootloader_name.call_once(|| { - MB2_INFO - .get() - .unwrap() - .boot_loader_name_tag() - .expect("Bootloader name not found from the Multiboot2 header!") - .name() - .expect("UTF-8 error: failed to parse bootloader name!") - .to_string() - }); +fn parse_bootloader_name() -> &'static str { + MB2_INFO + .get() + .unwrap() + .boot_loader_name_tag() + .expect("Bootloader name not found from the Multiboot2 header!") + .name() + .expect("UTF-8 error: failed to parse bootloader name!") } -fn init_kernel_commandline(kernel_cmdline: &'static Once) { - kernel_cmdline.call_once(|| { - MB2_INFO - .get() - .unwrap() - .command_line_tag() - .expect("Kernel command-line not found from the Multiboot2 header!") - .cmdline() - .expect("UTF-8 error: failed to parse kernel command-line!") - .into() - }); +fn parse_kernel_commandline() -> &'static str { + MB2_INFO + .get() + .unwrap() + .command_line_tag() + .expect("Kernel command-line not found from the Multiboot2 header!") + .cmdline() + .expect("UTF-8 error: failed to parse kernel command-line!") } -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) { - acpi.call_once(|| { - if let Some(v2_tag) = MB2_INFO.get().unwrap().rsdp_v2_tag() { - // check for rsdp v2 - BootloaderAcpiArg::Xsdt(v2_tag.xsdt_address()) - } else if let Some(v1_tag) = MB2_INFO.get().unwrap().rsdp_v1_tag() { - // fall back to rsdp v1 - BootloaderAcpiArg::Rsdt(v1_tag.rsdt_address()) - } else { - panic!("No ACPI RDSP information found!"); - } - }); +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()) + } else if let Some(v1_tag) = MB2_INFO.get().unwrap().rsdp_v1_tag() { + // fall back to rsdp v1 + BootloaderAcpiArg::Rsdt(v1_tag.rsdt_address()) + } else { + BootloaderAcpiArg::NotProvided + } } -fn init_framebuffer_info(framebuffer_arg: &'static Once) { +fn parse_framebuffer_info() -> Option { 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 for MemoryRegionType { @@ -96,7 +84,7 @@ impl From for MemoryRegionType { } } -fn init_memory_regions(memory_regions: &'static Once) { +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) { )) .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(); } diff --git a/ostd/src/arch/x86/kernel/acpi/mod.rs b/ostd/src/arch/x86/kernel/acpi/mod.rs index f1f3e2833..1e763f74f 100644 --- a/ostd/src/arch/x86/kernel/acpi/mod.rs +++ b/ostd/src/arch/x86/kernel/acpi/mod.rs @@ -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() }, diff --git a/ostd/src/boot/mod.rs b/ostd/src/boot/mod.rs index 80b586ae0..d48ff12ce 100644 --- a/ostd/src/boot/mod.rs +++ b/ostd/src/boot/mod.rs @@ -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, + /// The memory regions provided by the bootloader. + pub memory_regions: Vec, +} + +/// Gets the boot information. +pub fn boot_info() -> &'static BootInfo { + INFO.get().unwrap() +} + +static INFO: Once = 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 = 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, + 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 = 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. diff --git a/ostd/src/boot/smp.rs b/ostd/src/boot/smp.rs index 512816b53..f02bcf517 100644 --- a/ostd/src/boot/smp.rs +++ b/ostd/src/boot/smp.rs @@ -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, } diff --git a/ostd/src/logger.rs b/ostd/src/logger.rs index 8ab512a14..066b3d341 100644 --- a/ostd/src/logger.rs +++ b/ostd/src/logger.rs @@ -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 { - 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 { diff --git a/ostd/src/mm/frame/allocator.rs b/ostd/src/mm/frame/allocator.rs index a7e94cb75..e0a4bc030 100644 --- a/ostd/src/mm/frame/allocator.rs +++ b/ostd/src/mm/frame/allocator.rs @@ -184,7 +184,7 @@ impl CountingFrameAllocator { pub(in crate::mm) static FRAME_ALLOCATOR: Once> = 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() { diff --git a/ostd/src/mm/frame/meta.rs b/ostd/src/mm/frame/meta.rs index 636b72508..aa702066d 100644 --- a/ostd/src/mm/frame/meta.rs +++ b/ostd/src/mm/frame/meta.rs @@ -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 { 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() }; diff --git a/ostd/src/mm/kspace/mod.rs b/ostd/src/mm/kspace/mod.rs index ce38ef45d..3e8168e53 100644 --- a/ostd/src/mm/kspace/mod.rs +++ b/ostd/src/mm/kspace/mod.rs @@ -134,7 +134,7 @@ pub static KERNEL_PAGE_TABLE: Once) { 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.