From 58980d4c558f2bc62c91440552860675b82f2406 Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Sun, 23 Mar 2025 17:32:21 +0800 Subject: [PATCH] Revise code under `arch/x86/boot` --- ostd/src/arch/x86/boot/linux_boot/mod.rs | 115 +++++++------- ostd/src/arch/x86/boot/multiboot/mod.rs | 187 +++++++++-------------- ostd/src/arch/x86/boot/multiboot2/mod.rs | 184 ++++++++-------------- ostd/src/arch/x86/boot/smp.rs | 13 +- ostd/src/boot/memory_region.rs | 27 +++- 5 files changed, 243 insertions(+), 283 deletions(-) diff --git a/ostd/src/arch/x86/boot/linux_boot/mod.rs b/ostd/src/arch/x86/boot/linux_boot/mod.rs index e80fd430d..d6a80e194 100644 --- a/ostd/src/arch/x86/boot/linux_boot/mod.rs +++ b/ostd/src/arch/x86/boot/linux_boot/mod.rs @@ -3,8 +3,6 @@ //! The Linux 64-bit Boot Protocol supporting module. //! -use core::ffi::CStr; - use linux_boot_params::{BootParams, E820Type, LINUX_BOOT_HEADER_MAGIC}; use crate::{ @@ -14,18 +12,14 @@ use crate::{ BootloaderAcpiArg, BootloaderFramebufferArg, }, if_tdx_enabled, - mm::{ - kspace::{paddr_to_vaddr, LINEAR_MAPPING_BASE_VADDR}, - Paddr, - }, + mm::kspace::paddr_to_vaddr, }; 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. - match hdr.type_of_loader { + match boot_params.hdr.type_of_loader { 0x0 => "LILO", // (0x00 reserved for pre-2.00 bootloader) 0x1 => "Loadlin", 0x2 => "bootsect-loader", // (0x20, all other values reserved) @@ -44,40 +38,56 @@ fn parse_bootloader_name(boot_params: &BootParams) -> &str { 0x10 => "Reserved", 0x11 => "Minimal Linux Bootloader ", 0x12 => "OVMF UEFI virtualization stack", - _ => "Unknown bootloader type!", + _ => "Unknown Linux Loader", } } -fn parse_kernel_commandline(boot_params: &BootParams) -> &str { - let ptr = paddr_to_vaddr(boot_params.hdr.cmd_line_ptr as usize) as *const i8; - // SAFETY: The pointer in the header points to a valid C string. - let cmdline_c_str: &CStr = unsafe { CStr::from_ptr(ptr) }; - let cmdline_str = cmdline_c_str.to_str().unwrap(); - cmdline_str +fn parse_kernel_commandline(boot_params: &BootParams) -> Option<&str> { + if boot_params.ext_cmd_line_ptr != 0 { + // TODO: We can support the above 4GiB command line after setting up + // linear mappings. By far, we cannot log the error because the serial is + // not up. Proceed as if there was no command line. + return None; + } + + if boot_params.hdr.cmd_line_ptr == 0 || boot_params.hdr.cmdline_size == 0 { + return None; + } + + let cmdline_ptr = paddr_to_vaddr(boot_params.hdr.cmd_line_ptr as usize); + let cmdline_len = boot_params.hdr.cmdline_size as usize; + // SAFETY: The command line is safe to read because of the contract with the loader. + let cmdline = unsafe { core::slice::from_raw_parts(cmdline_ptr as *const u8, cmdline_len) }; + + // Now, unfortunately, there are silent errors because the serial is not up. + core::ffi::CStr::from_bytes_until_nul(cmdline) + .ok()? + .to_str() + .ok() } fn parse_initramfs(boot_params: &BootParams) -> Option<&[u8]> { - let hdr = &boot_params.hdr; - let ptr = hdr.ramdisk_image as usize; - if ptr == 0 { + if boot_params.ext_ramdisk_image != 0 || boot_params.ext_ramdisk_size != 0 { + // See the explanation in `parse_kernel_commandline`. 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 { - paddr_to_vaddr(ptr) - } else { - ptr - }; - let length = hdr.ramdisk_size as usize; - if length == 0 { + + if boot_params.hdr.ramdisk_image == 0 || boot_params.hdr.ramdisk_size == 0 { return None; } - // 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) }) + + let initramfs_ptr = paddr_to_vaddr(boot_params.hdr.ramdisk_image as usize); + let initramfs_len = boot_params.hdr.ramdisk_size as usize; + // SAFETY: The initramfs is safe to read because of the contract with the loader. + let initramfs = + unsafe { core::slice::from_raw_parts(initramfs_ptr as *const u8, initramfs_len) }; + + Some(initramfs) } fn parse_acpi_arg(boot_params: &BootParams) -> BootloaderAcpiArg { let rsdp = boot_params.acpi_rsdp_addr; + if rsdp == 0 { BootloaderAcpiArg::NotProvided } else { @@ -87,11 +97,14 @@ fn parse_acpi_arg(boot_params: &BootParams) -> BootloaderAcpiArg { fn parse_framebuffer_info(boot_params: &BootParams) -> Option { let screen_info = boot_params.screen_info; - if screen_info.lfb_base == 0 { + + let address = screen_info.lfb_base as usize | ((screen_info.ext_lfb_base as usize) << 32); + if address == 0 { return None; } + Some(BootloaderFramebufferArg { - address: screen_info.lfb_base as usize, + address, width: screen_info.lfb_width as usize, height: screen_info.lfb_height as usize, bpp: screen_info.lfb_depth as usize, @@ -105,7 +118,11 @@ impl From for MemoryRegionType { E820Type::Reserved => Self::Reserved, E820Type::Acpi => Self::Reclaimable, E820Type::Nvs => Self::NonVolatileSleep, - _ => Self::BadMemory, + E820Type::Unusable => Self::BadMemory, + // All other memory regions are reserved. + // FIXME: Using Rust enum in this way can be unsound if the bootloader passes an + // unknown memory type to the kernel (e.g., due to a newer protocol version). + _ => Self::Reserved, } } } @@ -137,36 +154,30 @@ fn parse_memory_regions(boot_params: &BootParams) -> MemoryRegionArray { .unwrap(); } + // Add the framebuffer region. + if let Some(fb) = parse_framebuffer_info(boot_params) { + regions.push(MemoryRegion::framebuffer(&fb)).unwrap(); + } + // Add the kernel region. regions.push(MemoryRegion::kernel()).unwrap(); // Add the initramfs region. - regions - .push(MemoryRegion::new( - boot_params.hdr.ramdisk_image as usize, - boot_params.hdr.ramdisk_size as usize, - MemoryRegionType::Module, - )) - .unwrap(); + if let Some(initramfs) = parse_initramfs(boot_params) { + regions.push(MemoryRegion::module(initramfs)).unwrap(); + } // Add the AP boot code region that will be copied into by the BSP. regions - .push(MemoryRegion::new( - super::smp::AP_BOOT_START_PA, - super::smp::ap_boot_code_size(), - MemoryRegionType::Reclaimable, - )) + .push(super::smp::reclaimable_memory_region()) .unwrap(); // Add the region of the kernel cmdline since some bootloaders do not provide it. - let kcmdline_str = parse_kernel_commandline(boot_params); - regions - .push(MemoryRegion::new( - kcmdline_str.as_ptr() as Paddr - LINEAR_MAPPING_BASE_VADDR, - kcmdline_str.len(), - MemoryRegionType::Reclaimable, - )) - .unwrap(); + if let Some(kcmdline) = parse_kernel_commandline(boot_params) { + regions + .push(MemoryRegion::module(kcmdline.as_bytes())) + .unwrap(); + } regions.into_non_overlapping() } @@ -184,7 +195,7 @@ unsafe extern "sysv64" fn __linux_boot(params_ptr: *const BootParams) -> ! { EARLY_INFO.call_once(|| EarlyBootInfo { bootloader_name: parse_bootloader_name(params), - kernel_cmdline: parse_kernel_commandline(params), + kernel_cmdline: parse_kernel_commandline(params).unwrap_or(""), initramfs: parse_initramfs(params), acpi_arg: parse_acpi_arg(params), framebuffer_arg: parse_framebuffer_info(params), diff --git a/ostd/src/arch/x86/boot/multiboot/mod.rs b/ostd/src/arch/x86/boot/multiboot/mod.rs index 33d8b679e..28e6fd2ec 100644 --- a/ostd/src/arch/x86/boot/multiboot/mod.rs +++ b/ostd/src/arch/x86/boot/multiboot/mod.rs @@ -7,38 +7,35 @@ use crate::{ memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType}, BootloaderAcpiArg, BootloaderFramebufferArg, }, - mm::{ - kspace::{paddr_to_vaddr, LINEAR_MAPPING_BASE_VADDR}, - Paddr, Vaddr, - }, + mm::{kspace::paddr_to_vaddr, Paddr}, }; global_asm!(include_str!("header.S")); -pub(super) const MULTIBOOT_ENTRY_MAGIC: u32 = 0x2BADB002; +const MULTIBOOT_ENTRY_MAGIC: u32 = 0x2BADB002; -fn parse_bootloader_name(mb1_info: &MultibootLegacyInfo) -> &str { - let mut name = "Unknown Multiboot loader"; - if mb1_info.boot_loader_name != 0 { - let ptr = paddr_to_vaddr(mb1_info.boot_loader_name as usize) as *const i8; - // SAFETY: the bootloader name is C-style zero-terminated string. - let cstr = unsafe { core::ffi::CStr::from_ptr(ptr) }; - if let Ok(s) = cstr.to_str() { - name = s; - } - } - name +fn parse_bootloader_name(mb1_info: &MultibootLegacyInfo) -> Option<&str> { + // SAFETY: The bootloader name is a C-style NUL-terminated string because of the contract with + // the Multiboot loader. + unsafe { parse_as_cstr(mb1_info.boot_loader_name) } } -fn parse_kernel_commandline(mb1_info: &MultibootLegacyInfo) -> &str { - let mut cmdline = ""; - if mb1_info.cmdline != 0 { - let ptr = paddr_to_vaddr(mb1_info.cmdline as usize) as *const i8; - // SAFETY: the command line is C-style zero-terminated string. - let cstr = unsafe { core::ffi::CStr::from_ptr(ptr) }; - cmdline = cstr.to_str().unwrap(); +fn parse_kernel_commandline(mb1_info: &MultibootLegacyInfo) -> Option<&str> { + // SAFETY: The bootloader name is a C-style NUL-terminated string because of the contract with + // the Multiboot loader. + unsafe { parse_as_cstr(mb1_info.cmdline) } +} + +unsafe fn parse_as_cstr<'a>(ptr: u32) -> Option<&'a str> { + if ptr == 0 { + return None; } - cmdline + + let name_ptr = paddr_to_vaddr(ptr as usize) as *const core::ffi::c_char; + // SAFETY: The safety is upheld by the caller. + let name_cstr = unsafe { core::ffi::CStr::from_ptr(name_ptr) }; + + name_cstr.to_str().ok() } fn parse_initramfs(mb1_info: &MultibootLegacyInfo) -> Option<&[u8]> { @@ -46,23 +43,17 @@ fn parse_initramfs(mb1_info: &MultibootLegacyInfo) -> Option<&[u8]> { 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 { - ( - (*(paddr_to_vaddr(modules_addr) as *const u32)) as usize, - (*(paddr_to_vaddr(modules_addr + 4) as *const u32)) as usize, - ) - }; - // We must return a slice composed by VA since kernel should read every in VA. - let base_va = if start < LINEAR_MAPPING_BASE_VADDR { - paddr_to_vaddr(start) - } else { - start - }; - let length = end - start; - Some(unsafe { core::slice::from_raw_parts(base_va as *const u8, length) }) + let mods_addr = paddr_to_vaddr(mb1_info.mods_addr as usize) as *const u32; + // SAFETY: We have checked `mods_count` above. By the contract with the Multiboot loader, the + // module addresses are available. + let (start, end) = unsafe { (*mods_addr, *mods_addr.add(1)) }; + + let ptr = paddr_to_vaddr(start as usize) as *const u8; + let len = (end - start) as usize; + + // SAFETY: The initramfs is safe to read because of the contract with the loader. + Some(unsafe { core::slice::from_raw_parts(ptr, len) }) } fn parse_acpi_arg(_mb1_info: &MultibootLegacyInfo) -> BootloaderAcpiArg { @@ -75,6 +66,7 @@ fn parse_framebuffer_info(mb1_info: &MultibootLegacyInfo) -> Option MemoryRegionArray { // Add the regions in the multiboot protocol. for entry in mb1_info.get_memory_map() { - let start = entry.base_addr(); let region = MemoryRegion::new( - start.try_into().unwrap(), + entry.base_addr().try_into().unwrap(), entry.length().try_into().unwrap(), entry.memory_type(), ); @@ -98,68 +89,34 @@ fn parse_memory_regions(mb1_info: &MultibootLegacyInfo) -> MemoryRegionArray { } // Add the framebuffer region. - let fb = 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, - }; - regions - .push(MemoryRegion::new( - fb.address, - (fb.width * fb.height * fb.bpp).div_ceil(8), // round up when divide with 8 (bits/Byte) - MemoryRegionType::Framebuffer, - )) - .unwrap(); + if let Some(fb) = parse_framebuffer_info(mb1_info) { + regions.push(MemoryRegion::framebuffer(&fb)).unwrap(); + } // Add the kernel region. regions.push(MemoryRegion::kernel()).unwrap(); - // Add the initramfs area. - if mb1_info.mods_count != 0 { - let modules_addr = mb1_info.mods_addr as usize; - // We only use one module - let (start, end) = unsafe { - ( - (*(paddr_to_vaddr(modules_addr) as *const u32)) as usize, - (*(paddr_to_vaddr(modules_addr + 4) as *const u32)) as usize, - ) - }; - regions - .push(MemoryRegion::new( - start, - end - start, - MemoryRegionType::Module, - )) - .unwrap(); + // Add the initramfs region. + if let Some(initramfs) = parse_initramfs(mb1_info) { + regions.push(MemoryRegion::module(initramfs)).unwrap(); } // Add the AP boot code region that will be copied into by the BSP. regions - .push(MemoryRegion::new( - super::smp::AP_BOOT_START_PA, - super::smp::ap_boot_code_size(), - MemoryRegionType::Reclaimable, - )) + .push(super::smp::reclaimable_memory_region()) .unwrap(); // Add the kernel cmdline and boot loader name region since Grub does not specify it. - let kcmdline = parse_kernel_commandline(mb1_info); - regions - .push(MemoryRegion::new( - kcmdline.as_ptr() as Paddr - LINEAR_MAPPING_BASE_VADDR, - kcmdline.len(), - MemoryRegionType::Reclaimable, - )) - .unwrap(); - let bootloader_name = parse_bootloader_name(mb1_info); - regions - .push(MemoryRegion::new( - bootloader_name.as_ptr() as Paddr - LINEAR_MAPPING_BASE_VADDR, - bootloader_name.len(), - MemoryRegionType::Reclaimable, - )) - .unwrap(); + if let Some(kcmdline) = parse_kernel_commandline(mb1_info) { + regions + .push(MemoryRegion::module(kcmdline.as_bytes())) + .unwrap(); + } + if let Some(bootloader_name) = parse_bootloader_name(mb1_info) { + regions + .push(MemoryRegion::module(bootloader_name.as_bytes())) + .unwrap(); + } regions.into_non_overlapping() } @@ -289,11 +246,13 @@ struct MultibootLegacyInfo { impl MultibootLegacyInfo { fn get_memory_map(&self) -> MemoryEntryIter { - let ptr = self.memory_map_addr as Paddr; - let end = ptr + self.memory_map_len as usize; + let cur_ptr = paddr_to_vaddr(self.memory_map_addr as Paddr) as *const u8; + // SAFETY: The range is in bounds by the contract with the Multiboot loader. + let region_end = unsafe { cur_ptr.add(self.memory_map_len as usize) }; + MemoryEntryIter { - cur_ptr: paddr_to_vaddr(ptr), - region_end: paddr_to_vaddr(end), + cur_ptr, + region_end, } } } @@ -341,30 +300,31 @@ struct FramebufferTable { /// 4, it is not guaranteed. So we need to use pointer arithmetic to /// access the fields. struct MemoryEntry { - ptr: Vaddr, + ptr: *const u8, } impl MemoryEntry { fn size(&self) -> u32 { - // SAFETY: the entry can only be constructed from a valid address. - unsafe { (self.ptr as *const u32).read_unaligned() } + // SAFETY: The entry can only be constructed from a valid address. The offset is in bounds. + unsafe { self.ptr.cast::().read_unaligned() } } fn base_addr(&self) -> u64 { - // SAFETY: the entry can only be constructed from a valid address. - unsafe { ((self.ptr + 4) as *const u64).read_unaligned() } + // SAFETY: The entry can only be constructed from a valid address. The offset is in bounds. + unsafe { self.ptr.byte_add(4).cast::().read_unaligned() } } fn length(&self) -> u64 { - // SAFETY: the entry can only be constructed from a valid address. - unsafe { ((self.ptr + 12) as *const u64).read_unaligned() } + // SAFETY: The entry can only be constructed from a valid address. The offset is in bounds. + unsafe { self.ptr.byte_add(12).cast::().read_unaligned() } } fn memory_type(&self) -> MemoryRegionType { // The multiboot (v1) manual doesn't specify the length of the type field. // Experimental result shows that "u8" works. So be it. - // SAFETY: the entry can only be constructed from a valid address. - let typ_val = unsafe { ((self.ptr + 20) as *const u8).read_unaligned() }; + // SAFETY: The entry can only be constructed from a valid address. The offset is in bounds. + let typ_val = unsafe { self.ptr.add(20).cast::().read_unaligned() }; + // The meaning of the values are however documented clearly by the manual. match typ_val { 1 => MemoryRegionType::Usable, @@ -380,8 +340,8 @@ impl MemoryEntry { /// A memory entry iterator in the memory map header info region. #[derive(Debug, Copy, Clone)] struct MemoryEntryIter { - cur_ptr: Vaddr, - region_end: Vaddr, + cur_ptr: *const u8, + region_end: *const u8, } impl Iterator for MemoryEntryIter { @@ -391,8 +351,13 @@ impl Iterator for MemoryEntryIter { if self.cur_ptr >= self.region_end { return None; } + let entry = MemoryEntry { ptr: self.cur_ptr }; - self.cur_ptr += entry.size() as usize + 4; + + let entry_size = entry.size() as usize + 4; + // SAFETY: The offset is in bounds according to the Multiboot specification. + unsafe { self.cur_ptr = self.cur_ptr.add(entry_size) }; + Some(entry) } } @@ -407,8 +372,8 @@ unsafe extern "sysv64" fn __multiboot_entry(boot_magic: u32, boot_params: u64) - 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), + bootloader_name: parse_bootloader_name(mb1_info).unwrap_or("Unknown Multiboot Loader"), + kernel_cmdline: parse_kernel_commandline(mb1_info).unwrap_or(""), initramfs: parse_initramfs(mb1_info), acpi_arg: parse_acpi_arg(mb1_info), framebuffer_arg: parse_framebuffer_info(mb1_info), diff --git a/ostd/src/arch/x86/boot/multiboot2/mod.rs b/ostd/src/arch/x86/boot/multiboot2/mod.rs index 62d58f6f8..a6ab118f6 100644 --- a/ostd/src/arch/x86/boot/multiboot2/mod.rs +++ b/ostd/src/arch/x86/boot/multiboot2/mod.rs @@ -3,88 +3,69 @@ use core::arch::global_asm; use multiboot2::{BootInformation, BootInformationHeader, MemoryAreaType}; -use spin::Once; use crate::{ boot::{ memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType}, BootloaderAcpiArg, BootloaderFramebufferArg, }, - mm::{ - kspace::{paddr_to_vaddr, LINEAR_MAPPING_BASE_VADDR}, - Paddr, - }, + mm::{kspace::paddr_to_vaddr, Paddr}, }; global_asm!(include_str!("header.S")); -pub(super) const MULTIBOOT2_ENTRY_MAGIC: u32 = 0x36d76289; +const MULTIBOOT2_ENTRY_MAGIC: u32 = 0x36d76289; -static MB2_INFO: Once = Once::new(); +fn parse_bootloader_name(mb2_info: &BootInformation) -> Option<&'static str> { + let name = mb2_info.boot_loader_name_tag()?.name().ok()?; -fn parse_bootloader_name() -> &'static str { - let s = 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!"); - let pa = s.as_ptr() as Paddr; - // SAFETY: We just convert this to virtual address. - unsafe { - core::str::from_utf8(core::slice::from_raw_parts( - paddr_to_vaddr(pa) as *const u8, - s.len(), - )) - .unwrap() - } + // SAFETY: The address of `name` is physical and the bootloader name will live for `'static`. + Some(unsafe { make_str_vaddr_static(name) }) } -fn parse_kernel_commandline() -> &'static str { - let s = 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!"); - let pa = s.as_ptr() as Paddr; - // SAFETY: We just convert this to virtual address. - unsafe { - core::str::from_utf8(core::slice::from_raw_parts( - paddr_to_vaddr(pa) as *const u8, - s.len(), - )) - .unwrap() - } +fn parse_kernel_commandline(mb2_info: &BootInformation) -> Option<&'static str> { + let cmdline = mb2_info.command_line_tag()?.cmdline().ok()?; + + // SAFETY: The address of `cmdline` is physical and the command line will live for `'static`. + Some(unsafe { make_str_vaddr_static(cmdline) }) } -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; - Some(unsafe { core::slice::from_raw_parts(base_va as *const u8, length) }) +unsafe fn make_str_vaddr_static(str: &str) -> &'static str { + let vaddr = paddr_to_vaddr(str.as_ptr() as Paddr); + + // SAFETY: The safety is upheld by the caller. + let bytes = unsafe { core::slice::from_raw_parts(vaddr as *const u8, str.len()) }; + + core::str::from_utf8(bytes).unwrap() } -fn parse_acpi_arg() -> BootloaderAcpiArg { - if let Some(v2_tag) = MB2_INFO.get().unwrap().rsdp_v2_tag() { - // check for rsdp v2 +fn parse_initramfs(mb2_info: &BootInformation) -> Option<&'static [u8]> { + let module_tag = mb2_info.module_tags().next()?; + + let initramfs_ptr = paddr_to_vaddr(module_tag.start_address() as usize); + let initramfs_len = module_tag.module_size() as usize; + // SAFETY: The initramfs is safe to read because of the contract with the loader. + let initramfs = + unsafe { core::slice::from_raw_parts(initramfs_ptr as *const u8, initramfs_len) }; + + Some(initramfs) +} + +fn parse_acpi_arg(mb2_info: &BootInformation) -> BootloaderAcpiArg { + if let Some(v2_tag) = mb2_info.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 + } else if let Some(v1_tag) = mb2_info.rsdp_v1_tag() { + // Fall back to RSDP v1 BootloaderAcpiArg::Rsdt(v1_tag.rsdt_address()) } else { BootloaderAcpiArg::NotProvided } } -fn parse_framebuffer_info() -> Option { - let Some(Ok(fb_tag)) = MB2_INFO.get().unwrap().framebuffer_tag() else { - return None; - }; +fn parse_framebuffer_info(mb2_info: &BootInformation) -> Option { + let fb_tag = mb2_info.framebuffer_tag()?.ok()?; + Some(BootloaderFramebufferArg { address: fb_tag.address() as usize, width: fb_tag.width() as usize, @@ -100,20 +81,19 @@ impl From for MemoryRegionType { MemoryAreaType::Reserved => Self::Reserved, MemoryAreaType::AcpiAvailable => Self::Reclaimable, MemoryAreaType::ReservedHibernate => Self::NonVolatileSleep, - _ => Self::BadMemory, + MemoryAreaType::Defective => Self::BadMemory, + MemoryAreaType::Custom(_) => Self::Reserved, } } } -fn parse_memory_regions() -> MemoryRegionArray { +fn parse_memory_regions(mb2_info: &BootInformation) -> MemoryRegionArray { let mut regions = MemoryRegionArray::new(); - let mb2_info = MB2_INFO.get().unwrap(); - // Add the regions returned by Grub. let memory_regions_tag = mb2_info .memory_map_tag() - .expect("Memory region not found from the Multiboot2 header!"); + .expect("No memory regions are found in the Multiboot2 header!"); for region in memory_regions_tag.memory_areas() { let start = region.start_address(); let end = region.end_address(); @@ -126,64 +106,35 @@ fn parse_memory_regions() -> MemoryRegionArray { regions.push(region).unwrap(); } - if let Some(Ok(fb_tag)) = mb2_info.framebuffer_tag() { - // Add the framebuffer region since Grub does not specify it. - let fb = 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, - }; - regions - .push(MemoryRegion::new( - fb.address, - (fb.width * fb.height * fb.bpp).div_ceil(8), // round up when divide with 8 (bits/Byte) - MemoryRegionType::Framebuffer, - )) - .unwrap(); + // Add the framebuffer region since Grub does not specify it. + if let Some(fb) = parse_framebuffer_info(mb2_info) { + regions.push(MemoryRegion::framebuffer(&fb)).unwrap(); } // Add the kernel region since Grub does not specify it. regions.push(MemoryRegion::kernel()).unwrap(); - // Add the boot module region since Grub does not specify it. - let mb2_module_tag = mb2_info.module_tags(); - for module in mb2_module_tag { - regions - .push(MemoryRegion::new( - module.start_address() as usize, - module.module_size() as usize, - MemoryRegionType::Module, - )) - .unwrap(); + // Add the initramfs region. + if let Some(initramfs) = parse_initramfs(mb2_info) { + regions.push(MemoryRegion::module(initramfs)).unwrap(); } // Add the AP boot code region that will be copied into by the BSP. regions - .push(MemoryRegion::new( - super::smp::AP_BOOT_START_PA, - super::smp::ap_boot_code_size(), - MemoryRegionType::Reclaimable, - )) + .push(super::smp::reclaimable_memory_region()) .unwrap(); // Add the kernel cmdline and boot loader name region since Grub does not specify it. - let kcmdline = parse_kernel_commandline(); - regions - .push(MemoryRegion::new( - kcmdline.as_ptr() as Paddr - LINEAR_MAPPING_BASE_VADDR, - kcmdline.len(), - MemoryRegionType::Reclaimable, - )) - .unwrap(); - let bootloader_name = parse_bootloader_name(); - regions - .push(MemoryRegion::new( - bootloader_name.as_ptr() as Paddr - LINEAR_MAPPING_BASE_VADDR, - bootloader_name.len(), - MemoryRegionType::Reclaimable, - )) - .unwrap(); + if let Some(kcmdline) = parse_kernel_commandline(mb2_info) { + regions + .push(MemoryRegion::module(kcmdline.as_bytes())) + .unwrap(); + } + if let Some(bootloader_name) = parse_bootloader_name(mb2_info) { + regions + .push(MemoryRegion::module(bootloader_name.as_bytes())) + .unwrap(); + } regions.into_non_overlapping() } @@ -192,19 +143,18 @@ fn parse_memory_regions() -> MemoryRegionArray { #[no_mangle] unsafe extern "sysv64" fn __multiboot2_entry(boot_magic: u32, boot_params: u64) -> ! { assert_eq!(boot_magic, MULTIBOOT2_ENTRY_MAGIC); - MB2_INFO.call_once(|| unsafe { - BootInformation::load(boot_params as *const BootInformationHeader).unwrap() - }); + let mb2_info = + unsafe { BootInformation::load(boot_params as *const BootInformationHeader).unwrap() }; 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(), + bootloader_name: parse_bootloader_name(&mb2_info).unwrap_or("Unknown Multiboot2 Loader"), + kernel_cmdline: parse_kernel_commandline(&mb2_info).unwrap_or(""), + initramfs: parse_initramfs(&mb2_info), + acpi_arg: parse_acpi_arg(&mb2_info), + framebuffer_arg: parse_framebuffer_info(&mb2_info), + memory_regions: parse_memory_regions(&mb2_info), }); call_ostd_main(); diff --git a/ostd/src/arch/x86/boot/smp.rs b/ostd/src/arch/x86/boot/smp.rs index c36c9d6d6..32975aada 100644 --- a/ostd/src/arch/x86/boot/smp.rs +++ b/ostd/src/arch/x86/boot/smp.rs @@ -35,6 +35,7 @@ use crate::{ Level, TriggerMode, }, }, + boot::memory_region::{MemoryRegion, MemoryRegionType}, if_tdx_enabled, mm::{paddr_to_vaddr, PAGE_SIZE}, }; @@ -86,13 +87,21 @@ pub(crate) fn bringup_all_aps(num_cpus: u32) { /// This is where the linker load the symbols in the `.ap_boot` section. /// The BSP would copy the AP boot code to this address. -pub(super) const AP_BOOT_START_PA: usize = 0x8000; +const AP_BOOT_START_PA: usize = 0x8000; /// The size of the AP boot code (the `.ap_boot` section). -pub(super) fn ap_boot_code_size() -> usize { +fn ap_boot_code_size() -> usize { __ap_boot_end as usize - __ap_boot_start as usize } +pub(super) fn reclaimable_memory_region() -> MemoryRegion { + MemoryRegion::new( + AP_BOOT_START_PA, + ap_boot_code_size(), + MemoryRegionType::Reclaimable, + ) +} + fn copy_ap_boot_code() { let ap_boot_start = __ap_boot_start as usize as *const u8; let len = __ap_boot_end as usize - __ap_boot_start as usize; diff --git a/ostd/src/boot/memory_region.rs b/ostd/src/boot/memory_region.rs index 974e966a5..d771a6a46 100644 --- a/ostd/src/boot/memory_region.rs +++ b/ostd/src/boot/memory_region.rs @@ -6,7 +6,7 @@ use core::ops::Deref; use align_ext::AlignExt; -use crate::mm::{kspace::kernel_loaded_offset, Paddr, PAGE_SIZE}; +use crate::mm::{kspace::kernel_loaded_offset, Paddr, Vaddr, PAGE_SIZE}; /// The type of initial memory regions that are needed for the kernel. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] @@ -73,6 +73,31 @@ impl MemoryRegion { } } + /// Constructs a framebuffer memory region. + pub fn framebuffer(fb: &crate::boot::BootloaderFramebufferArg) -> Self { + Self { + base: fb.address, + len: (fb.width * fb.height * fb.bpp).div_ceil(8), // round up when divide with 8 (bits/Byte) + typ: MemoryRegionType::Framebuffer, + } + } + + /// Constructs a module memory region from a byte slice that lives in the linear mapping. + /// + /// # Panics + /// + /// This method will panic if the byte slice does not live in the linear mapping. + pub fn module(bytes: &[u8]) -> Self { + let vaddr = bytes.as_ptr() as Vaddr; + assert!(crate::mm::kspace::LINEAR_MAPPING_VADDR_RANGE.contains(&vaddr)); + + Self { + base: vaddr - crate::mm::kspace::LINEAR_MAPPING_BASE_VADDR, + len: bytes.len(), + typ: MemoryRegionType::Reclaimable, + } + } + /// The physical address of the base of the region. pub fn base(&self) -> Paddr { self.base