From 07534e52d903c346db940d1f0ab10f6a3090c495 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Sun, 18 Oct 2026 00:21:01 +0200 Subject: [PATCH] feat(kernel): add SpawnThread, CreateProcess, and Kill syscalls --- crates/domain/src/endpoint/wait_queue.rs | 11 ++ crates/kernel/src/syscall.rs | 133 +++++++++++++++++++++++ crates/kernel/src/tests/lifecycle.rs | 65 +++++++++++ crates/kernel/src/tests/mod.rs | 1 + 4 files changed, 210 insertions(+) create mode 100644 crates/kernel/src/tests/lifecycle.rs diff --git a/crates/domain/src/endpoint/wait_queue.rs b/crates/domain/src/endpoint/wait_queue.rs index 9caf380..80d5ead 100644 --- a/crates/domain/src/endpoint/wait_queue.rs +++ b/crates/domain/src/endpoint/wait_queue.rs @@ -29,6 +29,17 @@ impl WaitQueue { thread } + pub fn remove_thread(&mut self, thread: ThreadId) -> bool { + for slot in &mut self.entries { + if *slot == Some(thread) { + *slot = None; + self.count -= 1; + return true; + } + } + false + } + pub fn is_empty(&self) -> bool { self.count == 0 } diff --git a/crates/kernel/src/syscall.rs b/crates/kernel/src/syscall.rs index 237d8fd..678bf6d 100644 --- a/crates/kernel/src/syscall.rs +++ b/crates/kernel/src/syscall.rs @@ -18,6 +18,9 @@ impl Kernel

{ Syscall::ExitThread => self.handle_exit_thread(tid, pid), Syscall::RequestPages { count } => self.handle_request_pages(pid, count), Syscall::ReleasePages { addr, count } => self.handle_release_pages(pid, addr, count), + Syscall::SpawnThread { entry, stack } => self.handle_spawn_thread(pid, entry, stack), + Syscall::CreateProcess { create_cap, image } => self.handle_create_process(pid, create_cap, image), + Syscall::Kill { process } => self.handle_kill(pid, process), _ => SyscallOutcome::Complete(SyscallResult::Error( domain::error::KernelError::WouldBlock, )), @@ -188,4 +191,134 @@ impl Kernel

{ SyscallOutcome::Complete(SyscallResult::Ok) } + + fn handle_spawn_thread( + &mut self, + pid: types::id::ProcessId, + _entry: types::address::EntryPoint, + _stack: types::address::StackPointer, + ) -> SyscallOutcome { + use domain::scheduler::Scheduler; + + let priority = types::bounded::Priority::new(16) + .unwrap_or_else(|| unreachable!()); + let time_slice = types::time::TimeSlice::new(100); + + let tid = match self.threads.spawn(pid, priority, time_slice, None) { + Ok(t) => t, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + if let Err(e) = self.processes.get_mut(pid).and_then(|p| p.add_thread(tid)) { + let _ = self.threads.destroy(tid); + return SyscallOutcome::Complete(SyscallResult::Error(e)); + } + + let core = self.platform.core_id(); + self.scheduler.add(tid, priority, core); + SyscallOutcome::Complete(SyscallResult::Value(tid.raw() as usize)) + } + + fn handle_create_process( + &mut self, + caller_pid: types::id::ProcessId, + create_cap: types::bounded::CapSlot, + _image: types::address::DataRef, + ) -> SyscallOutcome { + use domain::scheduler::Scheduler; + + if let Err(e) = self.check_cap(caller_pid, create_cap, types::permission::Permission::Execute) { + return SyscallOutcome::Complete(SyscallResult::Error(e)); + } + + let new_pid = match self.processes.create( + domain::capability::CapabilityTable::new(), + None, + ) { + Ok(p) => p, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + self.process_memory[new_pid.raw() as usize] = + Some(domain::memory::process_memory::ProcessMemory::new(new_pid, 1024)); + + let priority = types::bounded::Priority::new(16) + .unwrap_or_else(|| unreachable!()); + let time_slice = types::time::TimeSlice::new(100); + + let tid = match self.threads.spawn(new_pid, priority, time_slice, None) { + Ok(t) => t, + Err(e) => { + let _ = self.processes.destroy(new_pid); + return SyscallOutcome::Complete(SyscallResult::Error(e)); + } + }; + + if let Err(e) = self.processes.get_mut(new_pid).and_then(|p| p.add_thread(tid)) { + let _ = self.threads.destroy(tid); + let _ = self.processes.destroy(new_pid); + return SyscallOutcome::Complete(SyscallResult::Error(e)); + } + + let core = self.platform.core_id(); + self.scheduler.add(tid, priority, core); + + let slot = match self.grant_cap( + caller_pid, + domain::resource::Resource::Process(new_pid), + types::permission::PermissionSet::empty() + .with(types::permission::Permission::Execute) + .with(types::permission::Permission::Read), + ) { + Ok(s) => s, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + SyscallOutcome::Complete(SyscallResult::Slot(slot)) + } + + fn handle_kill( + &mut self, + caller_pid: types::id::ProcessId, + process_cap: types::bounded::CapSlot, + ) -> SyscallOutcome { + use domain::scheduler::Scheduler; + + let target_pid = match self.check_cap(caller_pid, process_cap, types::permission::Permission::Execute) { + Ok(cap) => match cap.resource() { + domain::resource::Resource::Process(pid) => *pid, + _ => return SyscallOutcome::Complete(SyscallResult::Error( + domain::error::KernelError::PermissionDenied { + slot: process_cap, + needed: types::permission::Permission::Execute, + }, + )), + }, + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + }; + + let thread_ids: [Option; 4] = { + match self.processes.get(target_pid) { + Ok(p) => { + let mut ids = [None; 4]; + for (i, tid) in p.thread_ids().enumerate() { + if i < 4 { ids[i] = Some(tid); } + } + ids + } + Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)), + } + }; + + for slot in &thread_ids { + if let Some(tid) = slot { + self.scheduler.remove(*tid); + let _ = self.threads.destroy(*tid); + } + } + + let _ = self.processes.destroy(target_pid); + self.process_memory[target_pid.raw() as usize] = None; + SyscallOutcome::Complete(SyscallResult::Ok) + } } diff --git a/crates/kernel/src/tests/lifecycle.rs b/crates/kernel/src/tests/lifecycle.rs new file mode 100644 index 0000000..ad83368 --- /dev/null +++ b/crates/kernel/src/tests/lifecycle.rs @@ -0,0 +1,65 @@ +use super::*; +use crate::{SyscallOutcome, SyscallResult}; +use domain::syscall::Syscall; +use types::address::{DataRef, EntryPoint, StackPointer}; +use types::bounded::CapSlot; + +#[test] +fn spawn_thread_creates_ready_thread() { + let (mut k, _) = kernel_with_process(); + let outcome = k.handle_syscall(Syscall::SpawnThread { + entry: EntryPoint::from_raw(0x1000), + stack: StackPointer::from_raw(0x2000), + }); + assert!(matches!( + outcome, + SyscallOutcome::Complete(SyscallResult::Value(_)) + )); +} + +#[test] +fn create_process_with_cap_succeeds() { + let (mut k, _) = kernel_with_process(); + // boot() grants ProcessCreate cap at slot 0 + let outcome = k.handle_syscall(Syscall::CreateProcess { + create_cap: CapSlot::new(0).unwrap(), + image: DataRef::empty(), + }); + assert!(matches!( + outcome, + SyscallOutcome::Complete(SyscallResult::Slot(_)) + )); +} + +#[test] +fn create_process_without_cap_fails() { + let (mut k, _) = kernel_with_process(); + // slot 5 doesn't exist + let outcome = k.handle_syscall(Syscall::CreateProcess { + create_cap: CapSlot::new(5).unwrap(), + image: DataRef::empty(), + }); + assert!(matches!( + outcome, + SyscallOutcome::Complete(SyscallResult::Error(_)) + )); +} + +#[test] +fn kill_removes_process() { + let (mut k, _pid) = kernel_with_process(); + // Create a second process, get cap for it + let slot = match k.handle_syscall(Syscall::CreateProcess { + create_cap: CapSlot::new(0).unwrap(), + image: DataRef::empty(), + }) { + SyscallOutcome::Complete(SyscallResult::Slot(s)) => s, + _ => panic!("expected slot"), + }; + + let outcome = k.handle_syscall(Syscall::Kill { process: slot }); + assert!(matches!( + outcome, + SyscallOutcome::Complete(SyscallResult::Ok) + )); +} diff --git a/crates/kernel/src/tests/mod.rs b/crates/kernel/src/tests/mod.rs index 34841ec..e0a1f1a 100644 --- a/crates/kernel/src/tests/mod.rs +++ b/crates/kernel/src/tests/mod.rs @@ -1,4 +1,5 @@ mod exit; +mod lifecycle; mod memory; mod simple; mod tick;