use crate::arch::gdt; use crate::cap::cnode; use crate::cap::object::{ObjectData, ObjectTag, ProcessObjectData}; 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; const USER_RFLAGS_MASK: u64 = 0x0000_0000_0000_0CD5; const USER_RFLAGS_FORCE: u64 = 0x202; pub fn sys_proc_create(ctx: &mut CpuContext) { let dest_addr = ctx.rdi; let pid = crate::arch::syscall::current_pid(); let mut allocator = BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); let created = match ptable.allocate(&mut allocator) { Some(c) => c, None => { ctx.rax = SyscallResult::error(KernelError::PoolExhausted).raw(); return; } }; let child_pid = created.pid(); 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) => { ptable.destroy(child_pid, &mut allocator); ctx.rax = SyscallResult::error(e).raw(); return; } }; let child_frame_count = child_cnode.frame_count; let data = ObjectData::Process(ProcessObjectData { pid: child_pid }); let (cnode_id, cnode_gen, depth) = match cnode::cnode_coords(pid, &ptable) { Ok(c) => c, Err(_) => { cnode::destroy_cnode(&child_cnode, &allocator); ptable.destroy(child_pid, &mut allocator); ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); return; } }; let mut pool = POOL.lock_after(&ptable); let (child_cnode_id, child_cnode_gen) = match pool.allocate(ObjectData::CNode(child_cnode)) { Ok(pair) => pair, Err(e) => { drop(pool); ptable.destroy(child_pid, &mut allocator); ctx.rax = SyscallResult::error(e).raw(); return; } }; if let Some(child) = ptable.get_mut(child_pid) { child.root_cnode = Some((child_cnode_id, child_cnode_gen)); child.cnode_depth = child_cnode_size; if child.charge_frames(child_frame_count as u16).is_err() { let _ = pool.free(child_cnode_id, child_cnode_gen); drop(pool); ptable.destroy(child_pid, &mut allocator); ctx.rax = SyscallResult::error(KernelError::ResourceExhausted).raw(); return; } } let result = ops::insert_object_cap_via_cnode( &mut pool, cnode_id, cnode_gen, dest_addr, depth, data, Rights::ALL, ); ctx.rax = match result { Ok(_) => SyscallResult::success(child_pid.raw() as u64).raw(), Err(e) => { drop(pool); ptable.destroy(child_pid, &mut allocator); SyscallResult::error(e).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)) ); let child = match ptable.get_mut(child_pid) { Some(c) => c, None => { ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); return; } }; match child.state() { ProcessState::Created => {} _ => { ctx.rax = SyscallResult::error(KernelError::BadState).raw(); return; } } let sels = gdt::selectors(); child.saved_context.rip = rip; child.saved_context.rsp = rsp; child.saved_context.rflags = (rflags & USER_RFLAGS_MASK) | USER_RFLAGS_FORCE; child.saved_context.cs = sels.user_code.0 as u64; child.saved_context.ss = sels.user_data.0 as u64; child.seal_context(); 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.get_mut(child_pid) { Some(child) => { child.saved_context.rdi = bootstrap; child.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(proc_cap.object_id(), 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.object_id(); 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 dest_addr = ctx.rdi; let entry_point = try_syscall!(ctx, validate_user_vaddr(ctx.rsi)); let user_stack_ptr = try_syscall!(ctx, validate_user_vaddr(ctx.rdx)); let arg = ctx.r10; 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 allocator = BitmapFrameAllocator; let mut ptable = PROCESSES.lock(); match ptable.get(caller_pid) { Some(_) => {} None => { ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); return; } } let created = match ptable.allocate_thread(caller_pid, &mut allocator) { Some(c) => c, None => { ctx.rax = SyscallResult::error(KernelError::PoolExhausted).raw(); return; } }; let child_pid = created.pid(); let sels = gdt::selectors(); match ptable.get_mut(child_pid) { Some(child) => { child.saved_context.rip = entry_point.as_u64(); child.saved_context.rsp = user_stack_ptr.as_u64(); child.saved_context.rdi = arg; child.saved_context.rflags = USER_RFLAGS_FORCE; child.saved_context.cs = sels.user_code.0 as u64; child.saved_context.ss = sels.user_data.0 as u64; child.seal_context(); } None => { ptable.destroy(child_pid, &mut allocator); ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); return; } } let data = ObjectData::Process(ProcessObjectData { pid: child_pid }); let (cnode_id, cnode_gen, depth) = match cnode::cnode_coords(caller_pid, &ptable) { Ok(c) => c, Err(_) => { ptable.destroy(child_pid, &mut allocator); ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); return; } }; let mut pool = POOL.lock_after(&ptable); if pool.inc_ref(cnode_id, cnode_gen).is_err() { drop(pool); ptable.destroy(child_pid, &mut allocator); ctx.rax = SyscallResult::error(KernelError::InvalidObject).raw(); return; } let result = ops::insert_object_cap_via_cnode( &mut pool, cnode_id, cnode_gen, dest_addr, depth, data, Rights::ALL, ); ctx.rax = match result { Ok(_) => SyscallResult::success(child_pid.raw() as u64).raw(), Err(e) => { drop(pool); ptable.destroy(child_pid, &mut allocator); SyscallResult::error(e).raw() } }; } pub fn sys_set_fsbase(ctx: &mut CpuContext) { let fs_base = ctx.rdi; let sign_bit = (fs_base >> 47) & 1; let upper_bits = fs_base >> 48; let canonical = match sign_bit { 0 => upper_bits == 0, _ => upper_bits == 0xFFFF, }; if !canonical { ctx.rax = SyscallResult::error(KernelError::InvalidAddress).raw(); return; } let caller_pid = crate::arch::syscall::current_pid(); let mut ptable = PROCESSES.lock(); match ptable.get_mut(caller_pid) { Some(proc) => { proc.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_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(child) = ptable.get_mut(child_pid) { child.set_name(filename); } SyscallResult::ok().raw() } Err(e) => SyscallResult::error(e).raw(), }; }