mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-10 13:56:48 +00:00
Revise code under arch/x86/boot
This commit is contained in:
parent
78a9ec9e96
commit
58980d4c55
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user