use alloc::{string::String, vec::Vec};
use core::arch::global_asm;
use spin::Once;
use crate::{
boot::{
kcmdline::KCmdlineArg,
memory_region::{non_overlapping_regions_from, MemoryRegion, MemoryRegionType},
BootloaderAcpiArg, BootloaderFramebufferArg,
},
mm::{
kspace::{paddr_to_vaddr, LINEAR_MAPPING_BASE_VADDR},
Paddr, Vaddr,
},
};
global_asm!(include_str!("header.S"));
pub(super) const MULTIBOOT_ENTRY_MAGIC: u32 = 0x2BADB002;
fn init_bootloader_name(bootloader_name: &'static Once<String>) {
bootloader_name.call_once(|| {
let mut name = "";
let info = MB1_INFO.get().unwrap();
if info.boot_loader_name != 0 {
unsafe {
let cstr = paddr_to_vaddr(info.boot_loader_name as usize) as *const u8;
let mut len = 0;
while cstr.add(len).read() != 0 {
len += 1;
}
name = core::str::from_utf8(core::slice::from_raw_parts(cstr, len))
.expect("cmdline is not a utf-8 string");
}
}
name.into()
});
}
fn init_kernel_commandline(kernel_cmdline: &'static Once<KCmdlineArg>) {
kernel_cmdline.call_once(|| {
let mut cmdline = "";
let info = MB1_INFO.get().unwrap();
if info.cmdline != 0 {
unsafe {
let cstr = paddr_to_vaddr(info.cmdline as usize) as *const u8;
let mut len = 0;
while cstr.add(len).read() != 0 {
len += 1;
}
cmdline = core::str::from_utf8(core::slice::from_raw_parts(cstr, len))
.expect("cmdline is not a utf-8 string");
}
}
cmdline.into()
});
}
fn init_initramfs(initramfs: &'static Once<&'static [u8]>) {
let info = MB1_INFO.get().unwrap();
if info.mods_count == 0 {
return;
}
let modules_addr = info.mods_addr as usize;
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,
)
};
let base_va = if start < LINEAR_MAPPING_BASE_VADDR {
paddr_to_vaddr(start)
} else {
start
};
let length = end - start;
initramfs.call_once(|| unsafe { core::slice::from_raw_parts(base_va as *const u8, length) });
}
fn init_acpi_arg(acpi: &'static Once<BootloaderAcpiArg>) {
acpi.call_once(|| BootloaderAcpiArg::NotProvided);
}
fn init_framebuffer_info(framebuffer_arg: &'static Once<BootloaderFramebufferArg>) {
let info = MB1_INFO.get().unwrap();
framebuffer_arg.call_once(|| BootloaderFramebufferArg {
address: info.framebuffer_table.addr as usize,
width: info.framebuffer_table.width as usize,
height: info.framebuffer_table.height as usize,
bpp: info.framebuffer_table.bpp as usize,
});
}
fn init_memory_regions(memory_regions: &'static Once<Vec<MemoryRegion>>) {
let mut regions = Vec::<MemoryRegion>::new();
let info = MB1_INFO.get().unwrap();
for entry in info.get_memory_map() {
let start = entry.base_addr();
let region = MemoryRegion::new(
start.try_into().unwrap(),
entry.length().try_into().unwrap(),
entry.memory_type(),
);
regions.push(region);
}
let fb = BootloaderFramebufferArg {
address: info.framebuffer_table.addr as usize,
width: info.framebuffer_table.width as usize,
height: info.framebuffer_table.height as usize,
bpp: info.framebuffer_table.bpp as usize,
};
regions.push(MemoryRegion::new(
fb.address,
(fb.width * fb.height * fb.bpp + 7) / 8, MemoryRegionType::Framebuffer,
));
regions.push(MemoryRegion::kernel());
if info.mods_count != 0 {
let modules_addr = info.mods_addr as usize;
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,
));
}
regions.push(MemoryRegion::new(
super::smp::AP_BOOT_START_PA,
super::smp::ap_boot_code_size(),
MemoryRegionType::Reclaimable,
));
memory_regions.call_once(move || non_overlapping_regions_from(regions.as_ref()));
}
#[derive(Debug, Copy, Clone)]
#[repr(C, packed)]
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 ptr = self.memory_map_addr as Paddr;
let end = ptr + self.memory_map_len as usize;
MemoryEntryIter {
cur_ptr: paddr_to_vaddr(ptr),
region_end: paddr_to_vaddr(end),
}
}
}
#[derive(Debug, Copy, Clone)]
#[repr(C, packed)]
struct VbeTable {
control_info: u32,
mode_info: u32,
mode: u16,
interface_seg: u16,
interface_off: u16,
interface_len: u16,
}
#[derive(Debug, Copy, Clone)]
#[repr(C, packed)]
struct FramebufferTable {
addr: u64,
pitch: u32,
width: u32,
height: u32,
bpp: u8,
typ: u8,
color_info: [u8; 6],
}
struct MemoryEntry {
ptr: Vaddr,
}
impl MemoryEntry {
fn size(&self) -> u32 {
unsafe { (self.ptr as *const u32).read_unaligned() }
}
fn base_addr(&self) -> u64 {
unsafe { ((self.ptr + 4) as *const u64).read_unaligned() }
}
fn length(&self) -> u64 {
unsafe { ((self.ptr + 12) as *const u64).read_unaligned() }
}
fn memory_type(&self) -> MemoryRegionType {
let typ_val = unsafe { ((self.ptr + 20) as *const u8).read_unaligned() };
match typ_val {
1 => MemoryRegionType::Usable,
2 => MemoryRegionType::Reserved,
3 => MemoryRegionType::Reclaimable,
4 => MemoryRegionType::NonVolatileSleep,
5 => MemoryRegionType::BadMemory,
_ => MemoryRegionType::Reserved,
}
}
}
#[derive(Debug, Copy, Clone)]
struct MemoryEntryIter {
cur_ptr: Vaddr,
region_end: Vaddr,
}
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 };
self.cur_ptr += entry.size() as usize + 4;
Some(entry)
}
}
static MB1_INFO: Once<&'static MultibootLegacyInfo> = Once::new();
#[no_mangle]
unsafe extern "sysv64" fn __multiboot_entry(boot_magic: u32, boot_params: u64) -> ! {
assert_eq!(boot_magic, MULTIBOOT_ENTRY_MAGIC);
MB1_INFO.call_once(|| &*(paddr_to_vaddr(boot_params as usize) as *const MultibootLegacyInfo));
crate::boot::register_boot_init_callbacks(
init_bootloader_name,
init_kernel_commandline,
init_initramfs,
init_acpi_arg,
init_framebuffer_info,
init_memory_regions,
);
crate::boot::call_ostd_main();
}