Revise code under arch/x86/boot

This commit is contained in:
Ruihan Li 2025-03-23 17:32:21 +08:00 committed by Tate, Hongliang Tian
parent 78a9ec9e96
commit 58980d4c55
5 changed files with 243 additions and 283 deletions

View File

@ -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 <http://sebastian-plotz.blogspot.de>",
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<BootloaderFramebufferArg> {
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<E820Type> 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),

View File

@ -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<BootloaderFr
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,
@ -88,9 +80,8 @@ fn parse_memory_regions(mb1_info: &MultibootLegacyInfo) -> 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::<u32>().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::<u64>().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::<u64>().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::<u8>().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),

View File

@ -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<BootInformation> = 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<BootloaderFramebufferArg> {
let Some(Ok(fb_tag)) = MB2_INFO.get().unwrap().framebuffer_tag() else {
return None;
};
fn parse_framebuffer_info(mb2_info: &BootInformation) -> Option<BootloaderFramebufferArg> {
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<MemoryAreaType> 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();

View File

@ -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;

View File

@ -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