feat(kernel): add SpawnThread, CreateProcess, and Kill syscalls
This commit is contained in:
@@ -29,6 +29,17 @@ impl WaitQueue {
|
|||||||
thread
|
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 {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.count == 0
|
self.count == 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ impl<P: Platform> Kernel<P> {
|
|||||||
Syscall::ExitThread => self.handle_exit_thread(tid, pid),
|
Syscall::ExitThread => self.handle_exit_thread(tid, pid),
|
||||||
Syscall::RequestPages { count } => self.handle_request_pages(pid, count),
|
Syscall::RequestPages { count } => self.handle_request_pages(pid, count),
|
||||||
Syscall::ReleasePages { addr, count } => self.handle_release_pages(pid, addr, 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(
|
_ => SyscallOutcome::Complete(SyscallResult::Error(
|
||||||
domain::error::KernelError::WouldBlock,
|
domain::error::KernelError::WouldBlock,
|
||||||
)),
|
)),
|
||||||
@@ -188,4 +191,134 @@ impl<P: Platform> Kernel<P> {
|
|||||||
|
|
||||||
SyscallOutcome::Complete(SyscallResult::Ok)
|
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<types::id::ThreadId>; 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
65
crates/kernel/src/tests/lifecycle.rs
Normal file
65
crates/kernel/src/tests/lifecycle.rs
Normal file
@@ -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)
|
||||||
|
));
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
mod exit;
|
mod exit;
|
||||||
|
mod lifecycle;
|
||||||
mod memory;
|
mod memory;
|
||||||
mod simple;
|
mod simple;
|
||||||
mod tick;
|
mod tick;
|
||||||
|
|||||||
Reference in New Issue
Block a user