diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 2ad6c86..c6e918e 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -20,8 +20,9 @@ riscv = [] "riscv.board.virt" = ["riscv.riscv64"] [dependencies] -uapi = { path = "../uapi" } allocator = { path = "../lib/allocator" } +tracer = { path = "../lib/tracer" } +uapi = { path = "../uapi" } utils = { path = "../lib/utils", default-features = false } bitflags = "2.5" diff --git a/kernel/src/main.rs b/kernel/src/main.rs index a5506e9..37f0e0b 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -27,6 +27,7 @@ mod lang; mod logging; mod objects; mod plat; +mod root; mod scheduler; mod vspace; diff --git a/kernel/src/objects/untyped.rs b/kernel/src/objects/untyped.rs index 77b5394..8ff8709 100644 --- a/kernel/src/objects/untyped.rs +++ b/kernel/src/objects/untyped.rs @@ -1,7 +1,7 @@ use super::cap::RawCap; use super::cnode::{CNodeCap, CNodeObject}; use super::null::NullCap; -use super::{Cap, KernelObject}; +use super::{Cap, KernelObject, TableCap, TcbCap}; use core::fmt::Debug; use uapi::cap::ObjectType; use uapi::error::{SysError, SysResult}; @@ -54,7 +54,7 @@ impl UntypedCap<'_> { }); } - fn is_device(&self) -> bool { + pub fn is_device(&self) -> bool { (self.cte.cap.get().args[1] >> 6) & 1 == 1 } @@ -117,6 +117,8 @@ impl UntypedCap<'_> { let new_cap = match obj_type { ObjectType::Untyped => UntypedCap::mint(0, obj_type.bits(user_obj_bits), self.is_device(), addr), ObjectType::CNode => CNodeCap::mint(user_obj_bits, 0, 0, addr), + ObjectType::TCB => TcbCap::mint(addr), + ObjectType::Table => TableCap::mint(addr), // TODO: other object types _ => return Err(SysError::InvalidArgument), }; diff --git a/kernel/src/root.rs b/kernel/src/root.rs new file mode 100644 index 0000000..1a5795b --- /dev/null +++ b/kernel/src/root.rs @@ -0,0 +1,220 @@ +use crate::arch::layout::{mmap_phys_to_virt, mmap_virt_to_phys, PAGE_SIZE}; +use crate::arch::vspace::{KERNEL_PAGETABLE_SIZE, RAM_ALLOCATOR}; +use crate::objects::*; +use crate::vspace::*; +use core::ops::Range; +use elf::{abi::PT_LOAD, endian::AnyEndian, ElfBytes}; +use fdt::Fdt; +use log::{debug, trace}; +use spin::Lazy; +use uapi::cspace::CNodeSlot; +use uapi::error::SysError; +use utils::addr::*; + +const ROOT_CNODE_SIZE: usize = 128; +static mut ROOT_CNODE: Lazy<[CapEntry; ROOT_CNODE_SIZE]> = + Lazy::new(|| core::array::from_fn(|i| CapEntry::new(NullCap::mint()))); + +pub fn setup_root_server(fdt: &Fdt) { + // Root Server is using statically allocated ROOT_CNODE + + // 1. Setup CNodeCap -> self reference + let cnode_entry = { + let radix_bits = ROOT_CNODE_SIZE.ilog2() as usize; + let guard_bits = 0; + let cap_guard = 0; + let ptr = unsafe { mmap_virt_to_phys(VirtAddr::from(ROOT_CNODE.as_ptr())) }; + let cap = CNodeCap::mint(radix_bits, guard_bits, cap_guard, ptr); + CapEntry::new(cap) + }; + let mut cnode = CNodeCap::try_from(&cnode_entry).unwrap(); + let cnode_obj = cnode.as_object_mut(); + cnode_obj[CNodeSlot::CNodeCap as usize] = cnode_entry.clone(); + + // 2. insert current free memory [CNodeSlot::UntypedCap, memory_idx) + let memory_idx = create_untyped_memory(cnode_obj); + + // 3. insert device memory [memory_idx, device_idx) + let device_idx = create_untyped_device(cnode_obj, fdt, memory_idx); + + // 4. create objects + let free_idx = create_objects(cnode_obj, device_idx); + + // 5. load root server + + // 6. map other memory (fdt, initrd) + + // 7. setup threads +} + +fn create_untyped_memory(cnode_obj: &mut CNodeObject) -> usize { + let mut memory_idx = CNodeSlot::UntypedCap as usize; + let mut ram = RAM_ALLOCATOR.lock(); + ram.blocks_mut() + .iter_mut() + .filter(|b| b.is_some()) + .map(|b| b.take().unwrap()) + .for_each(|b| { + let start = { + let start = b.start_addr(); + let aligned = start.align_up(PAGE_SIZE); + + if aligned != start { + debug!("[root] wasting memory: {:#x?} -> {:#x?}", start, aligned); + } + + aligned + }; + + let bits = { + let size = b.size(); + let aligned = align_down(size, PAGE_SIZE); + + if aligned != size { + debug!("[root] wasting memory: {:#x?} -> {:#x?}", start + aligned, start + size); + } + + if aligned != 0 { + aligned.ilog2() as usize + } else { + 0 + } + }; + + if bits == 0 { + return; + } + + trace!( + "[root] creating untyped memory: {}: {:?} -> {:?}", + memory_idx, + start, + start + (1 << bits) + ); + + let cap = UntypedCap::mint(0, bits, false, start); + let cap_entry = CapEntry::new(cap); + cnode_obj[memory_idx] = cap_entry; + memory_idx += 1; + }); + + assert!(ram.blocks().iter().all(|b| b.is_none())); + + memory_idx +} + +fn create_untyped_device(cnode_obj: &mut CNodeObject, fdt: &Fdt, memory_idx: usize) -> usize { + let mut device_idx = memory_idx; + let memory = fdt.memory(); + + fdt.all_nodes().filter_map(|n| n.reg()).for_each(|r| { + r.filter(|r| r.size.is_some_and(|s| s > 0)) + .filter(|r| { + memory.regions().all(|m| { + let m_start = m.starting_address as usize; + let m_end = m_start + m.size.unwrap(); + let r_start = r.starting_address as usize; + let r_end = r_start + r.size.unwrap(); + r_end <= m_start || m_end <= r_start + }) + }) + .for_each(|region| { + let start = PhysAddr::from(region.starting_address); + let size = region.size.unwrap(); + let bits = size.next_power_of_two().ilog2() as usize; + + if (1 << bits) > size { + debug!( + "[root] allocating more device memory: {:#x} -> {:#x}", + start + size, + start + (1 << bits) + ); + } + + trace!( + "[root] creating untyped device: {}: {:?} -> {:?}", + device_idx, + start, + start + (1 << bits) + ); + + let cap = UntypedCap::mint(0, bits, true, start); + let cap_entry = CapEntry::new(cap); + cnode_obj[device_idx] = cap_entry; + device_idx += 1; + }) + }); + + device_idx +} + +fn create_objects(cnode_obj: &mut CNodeObject, end_idx: usize) -> usize { + fn alloc( + cnode_obj: &mut CNodeObject, + end_idx: usize, + user_obj_bits: usize, + range: Range, + ) { + let untyped_start = CNodeSlot::UntypedCap as usize; + + let (memory, slots) = { + let (prologue, remain) = cnode_obj.split_at_mut(untyped_start); + let (memory, epilogue) = remain.split_at_mut(end_idx - untyped_start); + + match range { + r if r.end < untyped_start => (memory, &mut prologue[r]), + r if r.start >= end_idx => (memory, &mut epilogue[(r.start - end_idx)..(r.end - end_idx)]), + _ => panic!("init: slot range overlaps with untyped memory"), + } + }; + + for untyped in memory { + let mut untyped = UntypedCap::try_from(&*untyped).expect("init: untyped cap is invalid"); + + if untyped.is_device() { + // all non-device memory are placed before device memory + break; + } + + match untyped.retype(T::OBJ_TYPE, user_obj_bits, slots) { + Ok(()) => return, + Err(SysError::RangeError) => continue, + _ => panic!("init: retype failed"), + } + } + + panic!("init: untyped memory is exhausted"); + } + + let mut free_idx = end_idx; + + // tcb + { + trace!("[root] allocating tcb object"); + let tcb_idx = CNodeSlot::TcbCap as usize; + alloc::(cnode_obj, end_idx, 0, tcb_idx..tcb_idx + 1); + } + + // vspace + { + trace!("[root] allocating vspace object"); + let pagetable_bits = PAGE_SIZE.ilog2() as usize; + + // root + let vspace_idx = CNodeSlot::VSpaceCap as usize; + alloc::(cnode_obj, end_idx, pagetable_bits, vspace_idx..vspace_idx + 1); + + if KERNEL_PAGETABLE_SIZE > 1 { + // remaining pagetables + let range = free_idx..(free_idx + KERNEL_PAGETABLE_SIZE - 1); + alloc::(cnode_obj, end_idx, pagetable_bits, range); + free_idx += KERNEL_PAGETABLE_SIZE - 1; + } + + todo!("copy pagetables"); + } + + // bootinfo? + + free_idx +}