diff --git a/kernel/src/root.rs b/kernel/src/root.rs index 1d8d78a..0b9842d 100644 --- a/kernel/src/root.rs +++ b/kernel/src/root.rs @@ -1,18 +1,21 @@ use crate::arch::layout::{mmap_phys_to_virt, mmap_virt_to_phys, PAGE_BITS, PAGE_SIZE}; use crate::arch::vspace::{copy_kernel_pagetable, map_range, RAM_ALLOCATOR}; +use crate::arch::FDT; use crate::objects::*; use crate::plat::trap::TrapContextOps; use crate::scheduler::SCHEDULER; use crate::vspace::*; -use core::{cmp::min, ops::Range}; +use core::{cmp::min, ops::Range, sync::atomic::Ordering}; use elf::{abi::*, endian::AnyEndian, ElfBytes}; use fdt::Fdt; use log::{debug, trace}; use spin::Lazy; -use uapi::{cspace::CNodeSlot, error::SysError}; +use uapi::{consts::*, cspace::CNodeSlot, error::SysError}; use utils::{addr::*, bin::prev_power_of_two}; +const ROOT_ASID: usize = 1; const ROOT_CNODE_SIZE: usize = 128; + static mut ROOT_CNODE: Lazy<[CapEntry; ROOT_CNODE_SIZE]> = Lazy::new(|| core::array::from_fn(|_| CapEntry::new(NullCap::mint()))); @@ -47,7 +50,7 @@ pub fn setup_root_server(fdt: &Fdt) { load_root_server(cnode_obj, &mut free_idx, fdt); // 6. map other memory (fdt, initrd) - // TODO: map fdt and initrd + map_misc(cnode_obj, &mut free_idx, fdt); // 7. insert into scheduler SCHEDULER.add( @@ -217,45 +220,75 @@ fn create_objects(cnode_obj: &mut CNodeObject, free_idx: &mut usize, cnode_entry let tcb = tcb_cap.as_object_mut(); *tcb = TcbObject::new(); + assert!(tcb.tid() == ROOT_ASID, "init: root tcb tid is invalid"); + tcb.set_cspace(cnode_entry.clone()); tcb.set_vspace(vspace.clone()); tcb.set_state(tcb::ThreadState::Idle); } } -fn load_root_server(cnode_obj: &mut CNodeObject, free_idx: &mut usize, fdt: &Fdt) { - let mut table = { - let root = &cnode_obj[CNodeSlot::VSpaceCap as usize]; +macro_rules! get_root_table { + ($cnode_obj:expr) => {{ + let root = &$cnode_obj[CNodeSlot::VSpaceCap as usize]; let root_vaddr = TableCap::try_from(root).unwrap().as_object::().vaddr(); // use unsafe to workaround borrow checker unsafe { Table::new(root_vaddr) } + }}; +} + +// TODO: figure out a better way to create a `load_page` function for `map_range` +macro_rules! create_load_page { + ($cnode_obj:expr, $free_idx:expr) => { + |mapped_addr| { + trace!("[root] allocating pagetable at slot #{}", $free_idx); + + alloc_objects::($cnode_obj, *$free_idx, PAGE_BITS, *$free_idx..*$free_idx + 1); + *$free_idx += 1; + + let page = &$cnode_obj[*$free_idx - 1]; + let page = TableCap::try_from(page).unwrap(); + + page.set_mapped(true); + page.set_mapped_asid(ROOT_ASID); + page.set_mapped_vaddr(mapped_addr); + + let table: Table = page.as_object(); // Any level is fine + + table.paddr() + } }; +} - let mut rootfs = unsafe { - let chosen = fdt.find_node("/chosen").expect("/chosen is required"); +fn get_rootfs(fdt: &Fdt) -> cpio::Reader<'static> { + let chosen = fdt.find_node("/chosen").expect("/chosen is required"); - let initrd_start = chosen - .properties() - .find(|n| n.name == "linux,initrd-start") - .expect("linux,initrd-start is required") - .as_usize() - .unwrap(); + let initrd_start = chosen + .properties() + .find(|n| n.name == "linux,initrd-start") + .expect("linux,initrd-start is required") + .as_usize() + .unwrap(); - let initrd_end = chosen - .properties() - .find(|n| n.name == "linux,initrd-end") - .expect("linux,initrd-end is required") - .as_usize() - .unwrap(); + let initrd_end = chosen + .properties() + .find(|n| n.name == "linux,initrd-end") + .expect("linux,initrd-end is required") + .as_usize() + .unwrap(); - let vaddr = mmap_phys_to_virt(PhysAddr(initrd_start)); - let len = initrd_end - initrd_start; + let vaddr = unsafe { mmap_phys_to_virt(PhysAddr(initrd_start)) }; + let len = initrd_end - initrd_start; - let data = core::slice::from_raw_parts(vaddr.as_const_ptr(), len); - cpio::Reader::new(data) - }; + let data = unsafe { core::slice::from_raw_parts(vaddr.as_const_ptr(), len) }; + cpio::Reader::new(data) +} +fn load_root_server(cnode_obj: &mut CNodeObject, free_idx: &mut usize, fdt: &Fdt) { + let mut table = get_root_table!(cnode_obj); + + let mut rootfs = get_rootfs(fdt); let root_entry = rootfs .find_map(|entry| { if let Ok(entry) = entry { @@ -322,7 +355,7 @@ fn load_root_server(cnode_obj: &mut CNodeObject, free_idx: &mut usize, fdt: &Fdt let mem = frame.as_object_mut(); copy(cur, mem); - // map frame to vspace + // map attributes let vaddr = seg_addr + cur; let paddr = unsafe { mmap_virt_to_phys(VirtAddr::from(mem as *const _ as *const u8)) }; let attr = { @@ -341,19 +374,12 @@ fn load_root_server(cnode_obj: &mut CNodeObject, free_idx: &mut usize, fdt: &Fdt attr }; - let alloc_page = || { - trace!("[initrd] allocating pagetable at slot #{}", free_idx); - - alloc_objects::(cnode_obj, *free_idx, PAGE_BITS, *free_idx..*free_idx + 1); - *free_idx += 1; - - let page = &cnode_obj[*free_idx - 1]; - let page = TableCap::try_from(page).unwrap(); - let table: Table = page.as_object(); // Any level is fine - - table.paddr() - }; + // manually set frame object + frame.set_mapped_asid(ROOT_ASID); + frame.set_mapped_vaddr(vaddr); + // map to vspace + let alloc_page = create_load_page!(cnode_obj, free_idx); map_range(alloc_page, &mut table, vaddr, paddr, block_size, attr); // update pointer @@ -365,3 +391,36 @@ fn load_root_server(cnode_obj: &mut CNodeObject, free_idx: &mut usize, fdt: &Fdt let tcb = tcb_cap.as_object_mut(); tcb.trapframe.set_pc(root_elf.ehdr.e_entry as usize); } + +fn map_misc(cnode_obj: &mut CNodeObject, free_idx: &mut usize, fdt: &Fdt) { + // NOTE: there are no FrameObjects referencing these memory regions + + let mut table = get_root_table!(cnode_obj); + let attr = MapAttr::USER_ACCESSIBLE | MapAttr::READABLE; + + // map fdt + { + assert!(fdt.total_size() < ROOT_FDT_MAX_SIZE, "init: fdt is too large"); + + let fdt_vaddr = FDT.load(Ordering::Relaxed); + let fdt_vaddr = VirtAddr::from(fdt_vaddr); + let fdt_paddr = unsafe { mmap_virt_to_phys(fdt_vaddr) }; + let fdt_size = align_up(fdt.total_size(), PAGE_SIZE); + + let alloc_page = create_load_page!(cnode_obj, free_idx); + map_range(alloc_page, &mut table, ROOT_FDT_ADDR, fdt_paddr, fdt_size, attr); + } + + // map rootfs + { + let rootfs = get_rootfs(fdt); + assert!(rootfs.data().len() < ROOT_INITRD_MAX_SIZE, "init: initrd is too large"); + + let initrd_vaddr = VirtAddr::from(rootfs.data().as_ptr()); + let initrd_paddr = unsafe { mmap_virt_to_phys(initrd_vaddr) }; + let initrd_size = align_up(rootfs.data().len(), PAGE_SIZE); + + let alloc_page = create_load_page!(cnode_obj, free_idx); + map_range(alloc_page, &mut table, ROOT_INITRD_ADDR, initrd_paddr, initrd_size, attr); + } +} diff --git a/lib/cpio/src/lib.rs b/lib/cpio/src/lib.rs index 3cd6fe9..90c5953 100644 --- a/lib/cpio/src/lib.rs +++ b/lib/cpio/src/lib.rs @@ -166,6 +166,10 @@ impl<'a> Reader<'a> { pub fn new(data: &'a [u8]) -> Self { Reader { data } } + + pub fn data(&self) -> &'a [u8] { + self.data + } } impl<'a> Iterator for Reader<'a> { diff --git a/uapi/src/consts.rs b/uapi/src/consts.rs new file mode 100644 index 0000000..2539a46 --- /dev/null +++ b/uapi/src/consts.rs @@ -0,0 +1,9 @@ +use utils::{addr::VirtAddr, size::MIB}; + +pub const ROOT_FDT_ADDR: VirtAddr = VirtAddr(0xfbf0_0000); +pub const ROOT_FDT_MAX_SIZE: usize = 1 * MIB; +pub const ROOT_FDT_MAX_BITS: usize = ROOT_FDT_MAX_SIZE.ilog2() as usize; + +pub const ROOT_INITRD_ADDR: VirtAddr = VirtAddr(0xfc00_0000); +pub const ROOT_INITRD_MAX_SIZE: usize = 64 * MIB; +pub const ROOT_INITRD_MAX_BITS: usize = ROOT_INITRD_MAX_SIZE.ilog2() as usize; diff --git a/uapi/src/lib.rs b/uapi/src/lib.rs index c67c9ee..ac44aa4 100644 --- a/uapi/src/lib.rs +++ b/uapi/src/lib.rs @@ -5,6 +5,7 @@ extern crate num_derive; pub mod cap; +pub mod consts; pub mod cspace; pub mod error; pub mod fault;