use core::arch::global_asm;
use crate::{
boot::{
BootloaderAcpiArg, BootloaderFramebufferArg,
memory_region::{MemoryRegion, MemoryRegionArray, MemoryRegionType},
},
mm::{Paddr, kspace::paddr_to_vaddr},
};
global_asm!(include_str!("header.S"));
const MULTIBOOT_ENTRY_MAGIC: u32 = 0x2BADB002;
fn parse_bootloader_name(mb1_info: &MultibootLegacyInfo) -> Option<&str> {
unsafe { parse_as_cstr(mb1_info.boot_loader_name) }
}
fn parse_kernel_commandline(mb1_info: &MultibootLegacyInfo) -> Option<&str> {
unsafe { parse_as_cstr(mb1_info.cmdline) }
}
unsafe fn parse_as_cstr<'a>(ptr: u32) -> Option<&'a str> {
if ptr == 0 {
return None;
}
let name_ptr = paddr_to_vaddr(ptr as usize) as *const core::ffi::c_char;
let name_cstr = unsafe { core::ffi::CStr::from_ptr(name_ptr) };
name_cstr.to_str().ok()
}
fn parse_initramfs(mb1_info: &MultibootLegacyInfo) -> Option<&[u8]> {
if mb1_info.mods_count == 0 {
return None;
}
let mods_addr = paddr_to_vaddr(mb1_info.mods_addr as usize) as *const u32;
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;
Some(unsafe { core::slice::from_raw_parts(ptr, len) })
}
fn parse_acpi_arg(_mb1_info: &MultibootLegacyInfo) -> BootloaderAcpiArg {
BootloaderAcpiArg::NotProvided
}
fn parse_framebuffer_info(mb1_info: &MultibootLegacyInfo) -> Option<BootloaderFramebufferArg> {
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 parse_memory_regions(mb1_info: &MultibootLegacyInfo) -> MemoryRegionArray {
let mut regions = MemoryRegionArray::new();
for entry in mb1_info.get_memory_map() {
let region = MemoryRegion::new(
entry.base_addr().try_into().unwrap(),
entry.length().try_into().unwrap(),
entry.memory_type(),
);
regions.push(region).unwrap();
}
if let Some(fb) = parse_framebuffer_info(mb1_info) {
regions.push(MemoryRegion::framebuffer(&fb)).unwrap();
}
regions.push(MemoryRegion::kernel()).unwrap();
if let Some(initramfs) = parse_initramfs(mb1_info) {
regions.push(MemoryRegion::module(initramfs)).unwrap();
}
regions
.push(super::smp::reclaimable_memory_region())
.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()
}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
struct MultibootLegacyInfo {
flags: u32,
mem_lower: u32,
mem_upper: u32,
boot_device: u32,
cmdline: u32,
mods_count: u32,
mods_addr: u32,
symbols: [u8; 16],
memory_map_len: u32,
memory_map_addr: u32,
drives_length: u32,
drives_addr: u32,
config_table: u32,
boot_loader_name: u32,
apm_table: u32,
vbe_table: VbeTable,
framebuffer_table: FramebufferTable,
}
impl MultibootLegacyInfo {
fn get_memory_map(&self) -> MemoryEntryIter {
let cur_ptr = paddr_to_vaddr(self.memory_map_addr as Paddr) as *const u8;
let region_end = unsafe { cur_ptr.add(self.memory_map_len as usize) };
MemoryEntryIter {
cur_ptr,
region_end,
}
}
}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
struct VbeTable {
control_info: u32,
mode_info: u32,
mode: u16,
interface_seg: u16,
interface_off: u16,
interface_len: u16,
}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
struct FramebufferTable {
addr: u64,
pitch: u32,
width: u32,
height: u32,
bpp: u8,
typ: u8,
color_info: [u8; 6],
}
struct MemoryEntry {
ptr: *const u8,
}
impl MemoryEntry {
fn size(&self) -> u32 {
unsafe { self.ptr.cast::<u32>().read_unaligned() }
}
fn base_addr(&self) -> u64 {
unsafe { self.ptr.byte_add(4).cast::<u64>().read_unaligned() }
}
fn length(&self) -> u64 {
unsafe { self.ptr.byte_add(12).cast::<u64>().read_unaligned() }
}
fn memory_type(&self) -> MemoryRegionType {
let typ_val = unsafe { self.ptr.add(20).cast::<u8>().read_unaligned() };
match typ_val {
1 => MemoryRegionType::Usable,
2 => MemoryRegionType::Reserved,
3 => MemoryRegionType::Reclaimable,
4 => MemoryRegionType::NonVolatileSleep,
5 => MemoryRegionType::BadMemory,
_ => MemoryRegionType::Reserved,
}
}
}
#[derive(Clone, Copy, Debug)]
struct MemoryEntryIter {
cur_ptr: *const u8,
region_end: *const u8,
}
impl Iterator for MemoryEntryIter {
type Item = MemoryEntry;
fn next(&mut self) -> Option<Self::Item> {
if self.cur_ptr >= self.region_end {
return None;
}
let entry = MemoryEntry { ptr: self.cur_ptr };
let entry_size = entry.size() as usize + 4;
unsafe { self.cur_ptr = self.cur_ptr.add(entry_size) };
Some(entry)
}
}
#[unsafe(no_mangle)]
unsafe extern "sysv64" fn __multiboot_entry(boot_magic: u32, boot_params: u64) -> ! {
assert_eq!(boot_magic, MULTIBOOT_ENTRY_MAGIC);
let mb1_info =
unsafe { &*(paddr_to_vaddr(boot_params as usize) as *const MultibootLegacyInfo) };
use crate::boot::{EARLY_INFO, EarlyBootInfo, start_kernel};
EARLY_INFO.call_once(|| EarlyBootInfo {
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),
memory_regions: parse_memory_regions(mb1_info),
});
unsafe { start_kernel() };
}