use crate::cap::cnode; use crate::cap::object::ObjectTag; use crate::cap::pool::POOL; use crate::cap::table::{CapRef, CapSlot, Rights}; use crate::error::KernelError; use crate::mem::phys::BitmapFrameAllocator; use crate::proc::PROCESSES; use crate::proc::context::CpuContext; use crate::syscall::{SyscallResult, try_syscall}; use lancer_core::header::KernelObjectHeader; use lancer_core::object_layout::{CNodeObject, KernelObject}; pub fn sys_cnode_create(ctx: &mut CpuContext) { let size_bits = try_syscall!(ctx, super::u8_from_reg(ctx.rdi)); let dest_addr = ctx.rsi; let pid = crate::arch::syscall::current_pid(); if !(cnode::MIN_CNODE_BITS..=cnode::MAX_CNODE_BITS).contains(&size_bits) { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } let allocator = &BitmapFrameAllocator; let cnode_data = match cnode::create_cnode(size_bits, allocator) { Ok(cd) => cd, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; let frame_count = cnode_data.frame_count; let obj_phys = match crate::cap::kernel_objects::alloc_slot() { Some(p) => p, None => { cnode::destroy_cnode(&cnode_data, allocator); ctx.rax = SyscallResult::error(KernelError::PoolExhausted).raw(); return; } }; let header = KernelObjectHeader::new(ObjectTag::CNode, 0, 64); let mut obj = CNodeObject::init_default(header); obj.slots_phys = cnode_data.slots_phys.as_u64(); obj.size_bits = cnode_data.size_bits; obj.frame_count = cnode_data.frame_count; crate::cap::kernel_objects::write_at(obj_phys, obj); let mut ptable = PROCESSES.lock(); let mut pool = POOL.lock_after(&ptable); let (cnode_id, cnode_gen) = match pool.register_object(obj_phys, ObjectTag::CNode) { Ok(pair) => pair, Err(e) => { crate::cap::kernel_objects::free_slot(obj_phys); cnode::destroy_cnode(&cnode_data, allocator); ctx.rax = SyscallResult::error(e).raw(); return; } }; let cap = CapRef::new(ObjectTag::CNode, cnode_id, Rights::ALL, cnode_gen); let (caller_cnode_id, caller_cnode_gen, depth, guard_value, guard_bits) = match cnode::cnode_coords(pid, &ptable) { Ok(c) => c, Err(e) => { let _ = pool.free_phys(cnode_id, cnode_gen); ctx.rax = SyscallResult::error(e).raw(); return; } }; if let Err(e) = cnode::resolve_and_insert( &pool, caller_cnode_id, caller_cnode_gen, dest_addr, depth, guard_value, guard_bits, cap, ) { let _ = pool.free_phys(cnode_id, cnode_gen); ctx.rax = SyscallResult::error(e).raw(); return; } match ptable.exec_mut(pid) { Some(exec) => { if let Err(e) = exec.charge_frames(frame_count as u16) { let _ = cnode::resolve_and_clear( &pool, caller_cnode_id, caller_cnode_gen, dest_addr, depth, guard_value, guard_bits, ); let _ = pool.free_phys(cnode_id, cnode_gen); ctx.rax = SyscallResult::error(e).raw(); return; } } None => { let _ = cnode::resolve_and_clear( &pool, caller_cnode_id, caller_cnode_gen, dest_addr, depth, guard_value, guard_bits, ); let _ = pool.free_phys(cnode_id, cnode_gen); ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); return; } } ctx.rax = SyscallResult::ok().raw(); } pub fn sys_cnode_copy(ctx: &mut CpuContext) { let src_addr = ctx.rdi; let dest_addr = ctx.rsi; let rights_raw = try_syscall!(ctx, super::u16_from_reg(ctx.rdx)); let rights_mask = Rights::from_bits(rights_raw); let pid = crate::arch::syscall::current_pid(); let ptable = PROCESSES.lock(); let (cnode_id, cnode_gen, depth, guard_value, guard_bits) = try_syscall!(ctx, cnode::cnode_coords(pid, &ptable)); let mut pool = POOL.lock_after(&ptable); let src_cap = match cnode::resolve_and_read( &pool, cnode_id, cnode_gen, src_addr, depth, guard_value, guard_bits, ) { Ok(cap) => cap, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; if let Err(e) = pool.inc_ref(src_cap.phys(), src_cap.generation()) { ctx.rax = SyscallResult::error(e).raw(); return; } let copied = src_cap.with_rights(src_cap.rights() & rights_mask); ctx.rax = match cnode::resolve_and_insert( &pool, cnode_id, cnode_gen, dest_addr, depth, guard_value, guard_bits, copied, ) { Ok(()) => SyscallResult::ok().raw(), Err(e) => { pool.dec_ref_phys(src_cap.phys(), src_cap.generation()); SyscallResult::error(e).raw() } }; } pub fn sys_cnode_delete(ctx: &mut CpuContext) { let address = ctx.rdi; let pid = crate::arch::syscall::current_pid(); let mut ptable = PROCESSES.lock(); let (cnode_id, cnode_gen, depth, guard_value, guard_bits) = try_syscall!(ctx, cnode::cnode_coords(pid, &ptable)); let mut pool = POOL.lock_after(&ptable); let cap = match cnode::resolve_and_clear( &pool, cnode_id, cnode_gen, address, depth, guard_value, guard_bits, ) { Ok(cap) => cap, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; let freed_phys = cap.phys(); match pool.dec_ref_phys(cap.phys(), cap.generation()) { Some((phys, lancer_core::object_tag::ObjectTag::CNode)) => { crate::cap::derivation::unlink_child(&mut pool, freed_phys); let obj = unsafe { &*(crate::mem::addr::phys_to_virt(x86_64::PhysAddr::new(phys)) .as_ptr::()) }; cnode::drain_cnode_phys( x86_64::PhysAddr::new(obj.slots_phys), obj.size_bits, obj.frame_count, &mut pool, &mut ptable, ); } Some((phys, tag)) => { crate::cap::derivation::unlink_child(&mut pool, freed_phys); crate::cap::ops::cleanup_by_tag_with_ptable(tag, phys, &mut ptable); } None => {} } ctx.rax = SyscallResult::ok().raw(); } #[allow(clippy::too_many_arguments)] pub fn sys_cnode_mint(ctx: &mut CpuContext) { let src_addr = ctx.rdi; let dest_addr = ctx.rsi; let rights_raw = try_syscall!(ctx, super::u16_from_reg(ctx.rdx)); let rights_mask = Rights::from_bits(rights_raw); let new_guard_value = ctx.r10; let new_guard_bits = try_syscall!(ctx, super::u8_from_reg(ctx.r8)); let pid = crate::arch::syscall::current_pid(); if new_guard_bits > 64 { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } match new_guard_bits { 64 => {} b => { if new_guard_value >= (1u64 << b) { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } } } let ptable = PROCESSES.lock(); let (cnode_id, cnode_gen, depth, gv, gb) = try_syscall!(ctx, cnode::cnode_coords(pid, &ptable)); let mut pool = POOL.lock_after(&ptable); let src_cap = match cnode::resolve_and_read(&pool, cnode_id, cnode_gen, src_addr, depth, gv, gb) { Ok(cap) => cap, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; if new_guard_bits > 0 && src_cap.tag() != ObjectTag::CNode { ctx.rax = SyscallResult::error(KernelError::InvalidType).raw(); return; } if src_cap.tag() == ObjectTag::CNode { let cnode_obj = match pool.read_as::(src_cap.phys(), src_cap.generation()) { Ok(c) => c, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; let total = (new_guard_bits as u16) + (cnode_obj.size_bits as u16); if total > 64 { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } } if let Err(e) = pool.inc_ref(src_cap.phys(), src_cap.generation()) { ctx.rax = SyscallResult::error(e).raw(); return; } let minted = src_cap .with_rights(src_cap.rights() & rights_mask) .with_guard(new_guard_value, new_guard_bits); ctx.rax = match cnode::resolve_and_insert( &pool, cnode_id, cnode_gen, dest_addr, depth, gv, gb, minted, ) { Ok(()) => SyscallResult::ok().raw(), Err(e) => { pool.dec_ref_phys(src_cap.phys(), src_cap.generation()); SyscallResult::error(e).raw() } }; } pub fn sys_cnode_copy_to(ctx: &mut CpuContext) { let src_addr = ctx.rdi; let dest_cnode_addr = ctx.rsi; let dest_index = ctx.rdx; let rights_raw = try_syscall!(ctx, super::u16_from_reg(ctx.r10)); let rights_mask = Rights::from_bits(rights_raw); let pid = crate::arch::syscall::current_pid(); let ptable = PROCESSES.lock(); let (cnode_id, cnode_gen, depth, gv, gb) = try_syscall!(ctx, cnode::cnode_coords(pid, &ptable)); let mut pool = POOL.lock_after(&ptable); let src_cap = match cnode::resolve_and_read(&pool, cnode_id, cnode_gen, src_addr, depth, gv, gb) { Ok(cap) => cap, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; let dest_cnode_cap = match cnode::resolve_and_validate( &pool, cnode_id, cnode_gen, dest_cnode_addr, depth, gv, gb, ObjectTag::CNode, Rights::ALL, ) { Ok(cap) => cap, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; let dest_cnode_obj = match pool .read_as::(dest_cnode_cap.phys(), dest_cnode_cap.generation()) { Ok(c) => c, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; let slot_count = 1u64 << dest_cnode_obj.size_bits; if dest_index >= slot_count { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } let slots_phys = x86_64::PhysAddr::new(dest_cnode_obj.slots_phys); let slot_ptr = unsafe { let base = crate::mem::addr::phys_to_virt(slots_phys).as_mut_ptr::(); &mut *base.add(dest_index as usize) }; match slot_ptr { CapSlot::Active(_) => { ctx.rax = SyscallResult::error(KernelError::SlotOccupied).raw(); return; } CapSlot::Empty => {} } if let Err(e) = pool.inc_ref(src_cap.phys(), src_cap.generation()) { ctx.rax = SyscallResult::error(e).raw(); return; } *slot_ptr = CapSlot::Active(src_cap.with_rights(src_cap.rights() & rights_mask)); ctx.rax = SyscallResult::ok().raw(); } pub fn sys_cspace_set_guard(ctx: &mut CpuContext) { let new_guard_bits = try_syscall!(ctx, super::u8_from_reg(ctx.rdi)); let pid = crate::arch::syscall::current_pid(); let mut ptable = PROCESSES.lock(); let pool = POOL.lock_after(&ptable); let exec = match ptable.exec(pid) { Some(e) => e, None => { ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); return; } }; let (cnode_id, cnode_gen) = match exec.root_cnode() { Some(pair) => pair, None => { ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); return; } }; let root_size_bits = match pool.read_as::(cnode_id, cnode_gen) { Ok(c) => c.size_bits, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; if (new_guard_bits as u16) + (root_size_bits as u16) > 64 { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } let exec_mut = match ptable.exec_mut(pid) { Some(e) => e, None => { ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); return; } }; exec_mut.root_guard_bits = new_guard_bits; ctx.rax = SyscallResult::ok().raw(); }