use crate::arch::gdt; use crate::cap::cnode; use crate::cap::object::ObjectTag; use crate::cap::ops; use crate::cap::pool::POOL; use crate::cap::table::Rights; use crate::error::KernelError; use crate::mem::phys::BitmapFrameAllocator; use crate::proc::context::CpuContext; use crate::proc::{PROCESSES, ProcessState}; use crate::syscall::{SyscallResult, try_syscall, validate_user_vaddr}; use crate::types::Pid; use lancer_core::header::KernelObjectHeader; use lancer_core::object_layout::{CNodeObject, KernelObject}; const USER_RFLAGS_MASK: u64 = 0x0000_0000_0000_0CD5; const USER_RFLAGS_FORCE: u64 = 0x202; pub fn sys_proc_create(ctx: &mut CpuContext) { let untyped_cap_addr = ctx.rdi; let dest_addr = ctx.rsi; let caller_pid = crate::arch::syscall::current_pid(); let mut ptable = PROCESSES.lock(); let (cnode_id, cnode_gen, depth, guard_value, guard_bits) = match cnode::cnode_coords(caller_pid, &ptable) { Ok(c) => c, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; let mut pool = POOL.lock_after(&ptable); let untyped_cap = match cnode::resolve_and_validate( &pool, cnode_id, cnode_gen, untyped_cap_addr, depth, guard_value, guard_bits, ObjectTag::Untyped, Rights::REVOKE, ) { Ok(cap) => cap, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; let untyped_oid = untyped_cap.phys(); let untyped_gen = untyped_cap.generation(); match crate::cap::retype::kernel_retype( &mut pool, Some(&mut *ptable), untyped_oid, untyped_gen, ObjectTag::Process, 0, cnode_id, cnode_gen, dest_addr, depth, guard_value, guard_bits, 1, ) { Ok(()) => {} Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } } let proc_cap = match cnode::resolve_and_validate( &pool, cnode_id, cnode_gen, dest_addr, depth, guard_value, guard_bits, ObjectTag::Process, Rights::ALL, ) { Ok(cap) => cap, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; let child_pid = match ops::resolve_process_cap(&proc_cap, &pool) { Ok(pid) => pid, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; let allocator = BitmapFrameAllocator; let child_cnode_size = crate::proc::ROOT_CNODE_SIZE_BITS; let child_cnode = match cnode::create_cnode(child_cnode_size, &allocator) { Ok(cd) => cd, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; let child_frame_count = child_cnode.frame_count; let obj_phys = match crate::cap::kernel_objects::alloc_slot() { Some(p) => p, None => { 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 = child_cnode.slots_phys.as_u64(); obj.size_bits = child_cnode.size_bits; obj.frame_count = child_cnode.frame_count; crate::cap::kernel_objects::write_at(obj_phys, obj); let (child_cnode_id, child_cnode_gen) = match pool.register_object(obj_phys, ObjectTag::CNode) { Ok(pair) => pair, Err(e) => { crate::cap::kernel_objects::free_slot(obj_phys); ctx.rax = SyscallResult::error(e).raw(); return; } }; match ptable.exec_mut(child_pid) { Some(exec) => { exec.root_cnode = Some((child_cnode_id, child_cnode_gen)); exec.cnode_depth = 64; exec.root_guard_bits = 64 - child_cnode_size; exec.root_guard_value = 0; if exec.charge_frames(child_frame_count as u16).is_err() { let _ = pool.free_phys(child_cnode_id, child_cnode_gen); crate::cap::kernel_objects::free_slot(obj_phys); ctx.rax = SyscallResult::error(KernelError::ResourceExhausted).raw(); return; } } None => { let _ = pool.free_phys(child_cnode_id, child_cnode_gen); crate::cap::kernel_objects::free_slot(obj_phys); ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); return; } } ctx.rax = SyscallResult::success(child_pid.raw() as u64).raw(); } pub fn sys_proc_set_regs(ctx: &mut CpuContext) { let proc_cap_addr = ctx.rdi; let rip = ctx.rsi; let rsp = ctx.rdx; let rflags = ctx.r10; let pid = crate::arch::syscall::current_pid(); try_syscall!(ctx, validate_user_vaddr(rip)); try_syscall!(ctx, validate_user_vaddr(rsp)); if !rsp.is_multiple_of(16) { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } let mut ptable = PROCESSES.lock(); let proc_cap = { let pool = POOL.lock_after(&ptable); try_syscall!( ctx, cnode::resolve_caller_validate( pid, proc_cap_addr, ObjectTag::Process, Rights::WRITE, &ptable, &pool, ) ) }; let child_pid = try_syscall!( ctx, ops::resolve_process_cap(&proc_cap, &POOL.lock_after(&ptable)) ); match ptable.get(child_pid) { Some(sched) => match sched.state() { ProcessState::Created => {} _ => { ctx.rax = SyscallResult::error(KernelError::BadState).raw(); return; } }, None => { ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); return; } } let sels = gdt::selectors(); match ptable.exec_mut(child_pid) { Some(exec) => { exec.saved_context.rip = rip; exec.saved_context.rsp = rsp; exec.saved_context.rflags = (rflags & USER_RFLAGS_MASK) | USER_RFLAGS_FORCE; exec.saved_context.cs = sels.user_code.0 as u64; exec.saved_context.ss = sels.user_data.0 as u64; exec.seal_context(); } None => { ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); return; } } ctx.rax = SyscallResult::ok().raw(); } pub fn sys_proc_start(ctx: &mut CpuContext) { let proc_cap_addr = ctx.rdi; let bootstrap = ctx.rsi; let pid = crate::arch::syscall::current_pid(); let mut ptable = PROCESSES.lock(); let proc_cap = { let pool = POOL.lock_after(&ptable); try_syscall!( ctx, cnode::resolve_caller_validate( pid, proc_cap_addr, ObjectTag::Process, Rights::WRITE, &ptable, &pool, ) ) }; let child_pid = try_syscall!( ctx, ops::resolve_process_cap(&proc_cap, &POOL.lock_after(&ptable)) ); match ptable.exec_mut(child_pid) { Some(exec) => { exec.saved_context.rdi = bootstrap; exec.seal_context(); } None => { ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); return; } } ctx.rax = match ptable.as_created(child_pid) { Some(created) => match ptable.start(created) { Ok(()) => SyscallResult::ok().raw(), Err(e) => SyscallResult::error(e).raw(), }, None => SyscallResult::error(KernelError::BadState).raw(), }; } pub fn sys_proc_destroy(ctx: &mut CpuContext) { let proc_cap_addr = ctx.rdi; let pid = crate::arch::syscall::current_pid(); let mut ptable = PROCESSES.lock(); let proc_cap = { let pool = POOL.lock_after(&ptable); try_syscall!( ctx, cnode::resolve_caller_validate( pid, proc_cap_addr, ObjectTag::Process, Rights::REVOKE, &ptable, &pool, ) ) }; let child_pid = match ops::resolve_process_cap(&proc_cap, &POOL.lock_after(&ptable)) { Ok(cp) => cp, Err(KernelError::StaleGeneration) => { let pool = POOL.lock_after(&ptable); let _ = cnode::resolve_caller_clear(pid, proc_cap_addr, &ptable, &pool); ctx.rax = SyscallResult::ok().raw(); return; } Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; if child_pid == pid || child_pid.raw() == 0 { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } let mut allocator = BitmapFrameAllocator; if ptable.destroy(child_pid, &mut allocator) { POOL.lock_after(&ptable) .dec_ref_phys(proc_cap.phys(), proc_cap.generation()); { let pool = POOL.lock_after(&ptable); let _ = cnode::resolve_caller_clear(pid, proc_cap_addr, &ptable, &pool); } ctx.rax = SyscallResult::ok().raw(); } else { ctx.rax = SyscallResult::error(KernelError::BadState).raw(); } } pub fn sys_proc_bind_death_notif(ctx: &mut CpuContext) { let proc_cap_addr = ctx.rdi; let notif_cap_addr = ctx.rsi; let bits = ctx.rdx; let pid = crate::arch::syscall::current_pid(); let mut ptable = PROCESSES.lock(); let (proc_cap, notif_cap) = { let pool = POOL.lock_after(&ptable); let pc = try_syscall!( ctx, cnode::resolve_caller_validate( pid, proc_cap_addr, ObjectTag::Process, Rights::WRITE, &ptable, &pool, ) ); let nc = try_syscall!( ctx, cnode::resolve_caller_validate( pid, notif_cap_addr, ObjectTag::Notification, Rights::WRITE, &ptable, &pool, ) ); (pc, nc) }; let child_pid = try_syscall!( ctx, ops::resolve_process_cap(&proc_cap, &POOL.lock_after(&ptable)) ); let notif_id = notif_cap.phys(); let notif_gen = notif_cap.generation(); match ptable.get_mut(child_pid) { Some(child) => { child.set_death_notification(notif_id, notif_gen, bits); ctx.rax = SyscallResult::ok().raw(); } None => { ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); } } } pub fn sys_thread_create(ctx: &mut CpuContext) { let untyped_cap_addr = ctx.rdi; let dest_addr = ctx.rsi; let entry_point = try_syscall!(ctx, validate_user_vaddr(ctx.rdx)); let user_stack_ptr = try_syscall!(ctx, validate_user_vaddr(ctx.r10)); let arg = ctx.r8; if !user_stack_ptr.as_u64().is_multiple_of(16) { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } let caller_pid = crate::arch::syscall::current_pid(); let mut ptable = PROCESSES.lock(); match ptable.get(caller_pid) { Some(_) => {} None => { ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); return; } } let (cnode_id, cnode_gen, depth, guard_value, guard_bits) = match cnode::cnode_coords(caller_pid, &ptable) { Ok(c) => c, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; let mut pool = POOL.lock_after(&ptable); let untyped_cap = match cnode::resolve_and_validate( &pool, cnode_id, cnode_gen, untyped_cap_addr, depth, guard_value, guard_bits, ObjectTag::Untyped, Rights::REVOKE, ) { Ok(cap) => cap, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; let ut_id = untyped_cap.phys(); let ut_gen = untyped_cap.generation(); let child_pid = match crate::cap::retype::retype_thread_from_untyped( &mut pool, &mut ptable, ut_id, ut_gen, caller_pid, cnode_id, cnode_gen, dest_addr, depth, guard_value, guard_bits, ) { Ok(pid) => pid, Err(e) => { ctx.rax = SyscallResult::error(e).raw(); return; } }; let sels = gdt::selectors(); let exec = ptable.exec_mut(child_pid).unwrap(); exec.saved_context.rip = entry_point.as_u64(); exec.saved_context.rsp = user_stack_ptr.as_u64(); exec.saved_context.rdi = arg; exec.saved_context.rflags = USER_RFLAGS_FORCE; exec.saved_context.cs = sels.user_code.0 as u64; exec.saved_context.ss = sels.user_data.0 as u64; exec.seal_context(); ctx.rax = SyscallResult::success(child_pid.raw() as u64).raw(); } pub fn sys_set_fsbase(ctx: &mut CpuContext) { let fs_base = ctx.rdi; let user_canonical = fs_base < (1u64 << 47); if !user_canonical { ctx.rax = SyscallResult::error(KernelError::InvalidAddress).raw(); return; } let caller_pid = crate::arch::syscall::current_pid(); let mut ptable = PROCESSES.lock(); match ptable.exec_mut(caller_pid) { Some(exec) => { exec.fs_base = fs_base; crate::sched::switch::write_fs_base(fs_base); ctx.rax = SyscallResult::ok().raw(); } None => { ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); } } } pub fn sys_get_fsbase(ctx: &mut CpuContext) { ctx.rax = SyscallResult::success(crate::sched::switch::read_fs_base()).raw(); } pub fn sys_proc_load_module(ctx: &mut CpuContext) { let proc_cap_addr = ctx.rdi; let module_index = ctx.rsi; let pid = crate::arch::syscall::current_pid(); let modules = match crate::arch::boot::modules() { Some(m) => m, None => { ctx.rax = SyscallResult::error(KernelError::NotFound).raw(); return; } }; if module_index >= modules.len() as u64 { ctx.rax = SyscallResult::error(KernelError::InvalidParameter).raw(); return; } let idx = module_index as usize; let child_pid: Pid = { let ptable = PROCESSES.lock(); let pool = POOL.lock_after(&ptable); let proc_cap = try_syscall!( ctx, cnode::resolve_caller_validate( pid, proc_cap_addr, ObjectTag::Process, Rights::WRITE, &ptable, &pool, ) ); let child = try_syscall!(ctx, ops::resolve_process_cap(&proc_cap, &pool)); match ptable.get(child) { Some(c) => match c.state() { ProcessState::Created => {} _ => { ctx.rax = SyscallResult::error(KernelError::BadState).raw(); return; } }, None => { ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); return; } } child }; let file = modules[idx]; let data = unsafe { core::slice::from_raw_parts(file.addr(), file.size() as usize) }; let mut allocator = BitmapFrameAllocator; ctx.rax = match crate::proc::loader::spawn_module(child_pid, data, &mut allocator) { Ok(()) => { let path_bytes = file.path().to_bytes(); let filename = { let last_slash = (0..path_bytes.len()).rev().find(|&i| path_bytes[i] == b'/'); match last_slash { Some(pos) => &path_bytes[pos + 1..], None => path_bytes, } }; let mut ptable = PROCESSES.lock(); if let Some(exec) = ptable.exec_mut(child_pid) { exec.set_name(filename); } SyscallResult::ok().raw() } Err(e) => SyscallResult::error(e).raw(), }; }