use crate::cap::kernel_objects; use crate::cap::pool::ObjectPool; use crate::error::KernelError; use crate::mem::phys::BitmapFrameAllocator; use crate::types::{Generation, ObjPhys, Pid, Priority}; use lancer_core::header::KernelObjectHeader; use lancer_core::object_layout::{ CNodeObject, EndpointObject, KernelObject, NotificationObject, SchedContextObject, UntypedObject, }; use lancer_core::object_tag::ObjectTag; pub fn alloc_typed( pool: &mut ObjectPool, tag: ObjectTag, obj: T, ) -> Result<(ObjPhys, Generation), KernelError> { let phys = kernel_objects::alloc_slot().ok_or(KernelError::PoolExhausted)?; kernel_objects::write_at(phys, obj); pool.register_object(phys, tag).map_err(|e| { kernel_objects::free_slot(phys); e }) } fn alloc_default( pool: &mut ObjectPool, ) -> Result<(ObjPhys, Generation), KernelError> { let header = KernelObjectHeader::new(T::TAG, 0, 64); alloc_typed(pool, T::TAG, T::init_default(header)) } pub fn alloc_endpoint(pool: &mut ObjectPool) -> Result<(ObjPhys, Generation), KernelError> { alloc_default::(pool) } pub fn alloc_notification(pool: &mut ObjectPool) -> Result<(ObjPhys, Generation), KernelError> { alloc_default::(pool) } pub fn alloc_endpoint_cap() -> (ObjPhys, Generation, crate::cap::table::CapRef) { let mut pool = crate::cap::pool::POOL.lock(); let (id, generation) = alloc_endpoint(&mut pool).expect("alloc endpoint"); let cap = crate::cap::table::CapRef::new(ObjectTag::Endpoint, id, crate::cap::table::Rights::ALL, generation); (id, generation, cap) } pub fn alloc_notification_cap() -> (ObjPhys, Generation, crate::cap::table::CapRef) { let mut pool = crate::cap::pool::POOL.lock(); let (id, generation) = alloc_notification(&mut pool).expect("alloc notification"); let cap = crate::cap::table::CapRef::new(ObjectTag::Notification, id, crate::cap::table::Rights::ALL, generation); (id, generation, cap) } pub fn alloc_cnode(pool: &mut ObjectPool) -> Result<(ObjPhys, Generation, u8), KernelError> { let size_bits = crate::proc::ROOT_CNODE_SIZE_BITS; let allocator = &crate::mem::phys::BitmapFrameAllocator; let cnode_data = crate::cap::cnode::create_cnode(size_bits, allocator)?; let frame_count = cnode_data.frame_count; let header = KernelObjectHeader::new(ObjectTag::CNode, 0, 64); let mut cnode_obj = CNodeObject::init_default(header); cnode_obj.slots_phys = cnode_data.slots_phys.as_u64(); cnode_obj.size_bits = cnode_data.size_bits; cnode_obj.frame_count = cnode_data.frame_count; let (id, generation) = alloc_typed(pool, ObjectTag::CNode, cnode_obj)?; Ok((id, generation, frame_count)) } pub fn bootstrap_test_cnode(pid: Pid, ptable: &mut crate::proc::ProcessManager) { let mut pool = crate::cap::pool::POOL.lock(); let (cnode_id, cnode_gen, frame_count) = alloc_cnode(&mut pool).expect("alloc cnode"); let exec = ptable.exec_mut(pid).expect("get exec"); exec.root_cnode = Some((cnode_id, cnode_gen)); exec.cnode_depth = 64; exec.root_guard_bits = 64 - crate::proc::ROOT_CNODE_SIZE_BITS; exec.root_guard_value = 0; exec.charge_frames(frame_count as u16) .expect("charge frames"); } pub fn allocate_small_untyped( ptable: &crate::sync::IrqMutexGuard<'_, crate::proc::ProcessManager, 0>, size_bits: u8, ) -> (ObjPhys, Generation, x86_64::PhysAddr) { allocate_untyped_inner(ptable, size_bits, false) } pub fn allocate_untyped( ptable: &crate::sync::IrqMutexGuard<'_, crate::proc::ProcessManager, 0>, is_device: bool, ) -> (ObjPhys, Generation, x86_64::PhysAddr) { allocate_untyped_inner(ptable, 16, is_device) } pub fn allocate_untyped_inner( ptable: &crate::sync::IrqMutexGuard<'_, crate::proc::ProcessManager, 0>, size_bits: u8, is_device: bool, ) -> (ObjPhys, Generation, x86_64::PhysAddr) { let frame_count = 1usize << size_bits.saturating_sub(12); let allocator = &crate::mem::phys::BitmapFrameAllocator; let phys_base = allocator .allocate_contiguous(frame_count) .expect("alloc untyped backing"); let base_virt = crate::mem::addr::phys_to_virt(phys_base); unsafe { core::ptr::write_bytes(base_virt.as_mut_ptr::(), 0, frame_count * 4096); } let header = KernelObjectHeader::new(ObjectTag::Untyped, 0, 64); let mut ut_obj = UntypedObject::init_default(header); ut_obj.phys_base = phys_base.as_u64(); ut_obj.size_bits = size_bits; ut_obj.is_device = is_device as u8; let phys = kernel_objects::alloc_slot().expect("alloc ut slot"); kernel_objects::write_at(phys, ut_obj); let (id, generation) = crate::cap::pool::POOL .lock_after(ptable) .register_object(phys, ObjectTag::Untyped) .expect("register untyped obj"); (id, generation, phys_base) } pub struct SchedBatch { pub pids: crate::static_vec::StaticVec, pub sc_ids: crate::static_vec::StaticVec<(ObjPhys, Generation), 2048>, } pub fn spawn_batch_with_sched( count: usize, budget_us: u64, period_us: u64, priority_fn: fn(usize) -> u8, ) -> SchedBatch { let mut allocator = BitmapFrameAllocator; let mut ptable = crate::proc::PROCESSES.lock(); let mut batch = SchedBatch { pids: crate::static_vec::StaticVec::new(), sc_ids: crate::static_vec::StaticVec::new(), }; (0..count).for_each(|i| { let created = ptable .allocate(&mut allocator) .unwrap_or_else(|| panic!("spawn failed at {}/{}", i, count)); let pid = created.pid(); let priority = Priority::new(priority_fn(i)); let header = KernelObjectHeader::new(ObjectTag::SchedContext, 0, 64); let mut sc = SchedContextObject::init_default(header); sc.budget_us = budget_us; sc.period_us = period_us; sc.remaining_us = budget_us; sc.priority = priority.raw(); sc.attached_pid = pid.raw(); let (sc_id, sc_gen) = alloc_typed( &mut crate::cap::pool::POOL.lock_after(&ptable), ObjectTag::SchedContext, sc, ) .unwrap_or_else(|e| panic!("sched context alloc failed at {}: {:?}", i, e)); ptable[pid].attach_sched_context(sc_id, sc_gen, priority); ptable.start(created).expect("start"); batch.pids.push(pid).expect("pid vec overflow"); batch.sc_ids.push((sc_id, sc_gen)).expect("sc vec overflow"); }); batch } pub fn destroy_batch_and_verify(batch: &SchedBatch, baseline_free_frames: usize) { let mut allocator = BitmapFrameAllocator; let mut ptable = crate::proc::PROCESSES.lock(); batch.pids.iter().for_each(|&pid| { ptable.destroy(pid, &mut allocator); }); let leaves_after = ptable.pid_table_leaf_count(); let mut pool = crate::cap::pool::POOL.lock_after(&ptable); batch.sc_ids.iter().for_each(|&(id, generation)| { if let Some((phys, _)) = pool.dec_ref_phys(id, generation) { kernel_objects::free_slot(phys); } }); drop(pool); drop(ptable); let after = BitmapFrameAllocator::free_frames(); let retained_leaves = leaves_after as usize; assert!( after >= baseline_free_frames.saturating_sub(retained_leaves + 2), "frame leak: had {} free before, {} after (delta={}, {} retained leaf frames)", baseline_free_frames, after, baseline_free_frames as i64 - after as i64, retained_leaves, ); } pub fn dequeue_ours( ptable: &mut crate::proc::ProcessManager, batch: &SchedBatch, limit: usize, ours: &mut crate::static_vec::StaticVec, ) { let mut all = crate::static_vec::StaticVec::::new(); core::iter::from_fn(|| ptable.dequeue_highest()) .take(limit + 64) .for_each(|pid| { all.push(pid).expect("dequeue overflow"); }); all.iter().for_each( |&pid| match batch.pids.iter().any(|&p| p == pid) { true if ours.len() < limit => ours.push(pid).expect("ours overflow"), _ => ptable.enqueue_ready(pid), }, ); }