refactor(kernel): split syscall.rs into per-tier modules (simple, memory, lifecycle, capability, ipc)

This commit is contained in:
2026-10-18 00:36:34 +02:00
parent b8c5090fe5
commit 38ebb6002b
7 changed files with 707 additions and 673 deletions

View File

@@ -1,673 +0,0 @@
use domain::syscall::Syscall;
use platform::Platform;
use crate::{Kernel, SyscallOutcome, SyscallResult};
impl<P: Platform> Kernel<P> {
pub fn handle_syscall(&mut self, syscall: Syscall) -> SyscallOutcome {
let (tid, pid) = match self.caller() {
Ok(caller) => caller,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
match syscall {
Syscall::GetTime => self.handle_get_time(),
Syscall::GetProcessId => self.handle_get_process_id(pid),
Syscall::Yield => self.handle_yield(tid),
Syscall::Exit => self.handle_exit(tid, pid),
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),
Syscall::Delegate { slot, to_endpoint, permissions } => {
self.handle_delegate(pid, slot, to_endpoint, permissions)
}
Syscall::Send { endpoint, message } => self.handle_send(tid, pid, endpoint, message),
Syscall::Receive { endpoint } => self.handle_receive(tid, pid, endpoint),
Syscall::WaitForInterrupt { irq } => self.handle_wait_for_interrupt(tid, pid, irq),
Syscall::AcknowledgeInterrupt { irq } => self.handle_ack_interrupt(pid, irq),
Syscall::WaitAny { sources } => self.handle_wait_any(tid, pid, sources),
}
}
fn handle_get_time(&self) -> SyscallOutcome {
let ticks = self.platform.current_ticks();
SyscallOutcome::Complete(SyscallResult::Value(ticks.raw() as usize))
}
fn handle_get_process_id(&self, pid: types::id::ProcessId) -> SyscallOutcome {
SyscallOutcome::Complete(SyscallResult::Value(pid.raw() as usize))
}
fn handle_exit(&mut self, caller_tid: types::id::ThreadId, pid: types::id::ProcessId) -> SyscallOutcome {
use domain::scheduler::Scheduler;
// Kill all threads of this process
let thread_ids: [Option<types::id::ThreadId>; 4] = {
match self.processes.get(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(_) => return SyscallOutcome::TerminateCaller,
}
};
self.cleanup_threads(&thread_ids);
let _ = self.processes.destroy(pid);
self.process_memory[pid.raw() as usize] = None;
SyscallOutcome::TerminateCaller
}
fn handle_exit_thread(&mut self, caller_tid: types::id::ThreadId, pid: types::id::ProcessId) -> SyscallOutcome {
use domain::scheduler::Scheduler;
self.scheduler.remove(caller_tid);
let _ = self.threads.destroy(caller_tid);
if let Ok(process) = self.processes.get_mut(pid) {
process.remove_thread(caller_tid);
if process.thread_ids().count() == 0 {
let _ = self.processes.destroy(pid);
self.process_memory[pid.raw() as usize] = None;
}
}
SyscallOutcome::TerminateCaller
}
fn handle_yield(&mut self, caller_tid: types::id::ThreadId) -> SyscallOutcome {
use domain::scheduler::Scheduler;
let core = self.platform.core_id();
let thread = match self.threads.get_mut(caller_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let priority = thread.priority();
if thread.preempt().is_err() {
return SyscallOutcome::Complete(SyscallResult::Ok);
}
self.scheduler.add(caller_tid, priority, core);
match self.scheduler.next(core) {
Some(next) if next != caller_tid => {
if self.threads.get_mut(next).and_then(|t| t.set_running(core)).is_ok() {
SyscallOutcome::SwitchTo(next)
} else {
let _ = self.threads.get_mut(caller_tid).map(|t| t.set_running(core));
SyscallOutcome::Complete(SyscallResult::Ok)
}
}
_ => {
let _ = self.threads.get_mut(caller_tid).map(|t| t.set_running(core));
SyscallOutcome::Complete(SyscallResult::Ok)
}
}
}
fn handle_request_pages(
&mut self,
pid: types::id::ProcessId,
count: types::bounded::PageCount,
) -> SyscallOutcome {
let pages = count.value() as usize;
if let Some(pm) = &mut self.process_memory[pid.raw() as usize] {
if pm.grant(pages).is_err() {
return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::OutOfMemory,
));
}
} else {
return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidProcess(pid),
));
}
let addr = match self.page_pool.allocate(pages) {
Some(a) => a,
None => {
if let Some(pm) = &mut self.process_memory[pid.raw() as usize] {
let _ = pm.release(pages);
}
return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::OutOfMemory,
));
}
};
let region = domain::memory::MemoryRegion::new(
types::id::RegionId::from_raw(0),
pid,
addr,
count,
types::generation::Generation::initial(),
);
let region_id = match self.regions.insert(region) {
Some(id) => id,
None => {
self.page_pool.free(addr, pages);
if let Some(pm) = &mut self.process_memory[pid.raw() as usize] {
let _ = pm.release(pages);
}
return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::OutOfMemory,
));
}
};
let slot = match self.grant_cap(
pid,
domain::resource::Resource::Memory(region_id),
types::permission::PermissionSet::empty()
.with(types::permission::Permission::Read)
.with(types::permission::Permission::Write),
) {
Ok(s) => s,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
SyscallOutcome::Complete(SyscallResult::Slot(slot))
}
fn handle_release_pages(
&mut self,
pid: types::id::ProcessId,
addr: types::address::VirtualAddress,
count: types::bounded::PageCount,
) -> SyscallOutcome {
let pages = count.value() as usize;
let phys = types::address::PhysicalAddress::from_raw(addr.raw());
let region_id = match self.regions.find_by_base(pid, phys) {
Some((rid, _)) => rid,
None => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::OutOfMemory,
)),
};
if !self.page_pool.free(phys, pages) {
return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::OutOfMemory,
));
}
let _ = self.regions.remove(region_id);
if let Some(pm) = &mut self.process_memory[pid.raw() as usize] {
let _ = pm.release(pages);
}
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)),
}
};
self.cleanup_threads(&thread_ids);
let _ = self.processes.destroy(target_pid);
self.process_memory[target_pid.raw() as usize] = None;
SyscallOutcome::Complete(SyscallResult::Ok)
}
fn handle_delegate(
&mut self,
caller_pid: types::id::ProcessId,
slot: types::bounded::CapSlot,
to_endpoint: types::bounded::CapSlot,
restrict: types::permission::PermissionSet,
) -> SyscallOutcome {
let (resource, permissions) = match self.check_cap(
caller_pid,
slot,
types::permission::Permission::Delegate,
) {
Ok(cap) => (cap.resource().clone(), *cap.permissions()),
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let restricted = permissions.restrict_to(&restrict);
let target_pid = match self.check_cap(
caller_pid,
to_endpoint,
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: to_endpoint,
needed: types::permission::Permission::Execute,
},
)),
},
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
match self.grant_cap(target_pid, resource, restricted) {
Ok(_) => SyscallOutcome::Complete(SyscallResult::Ok),
Err(e) => SyscallOutcome::Complete(SyscallResult::Error(e)),
}
}
fn handle_send(
&mut self,
caller_tid: types::id::ThreadId,
caller_pid: types::id::ProcessId,
endpoint_slot: types::bounded::CapSlot,
message: domain::message::Message,
) -> SyscallOutcome {
use domain::scheduler::Scheduler;
let ep_id = match self.check_cap(
caller_pid,
endpoint_slot,
types::permission::Permission::Execute,
) {
Ok(cap) => match cap.resource() {
domain::resource::Resource::Endpoint(id) => *id,
_ => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidEndpoint(types::id::EndpointId::from_raw(0)),
)),
},
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let endpoint = match self.endpoints.get_mut(ep_id) {
Some(ep) => ep,
None => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidEndpoint(ep_id),
)),
};
if endpoint.has_waiting_receiver() {
let receiver_tid = match endpoint.dequeue_receiver() {
Some(t) => t,
None => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::WouldBlock,
)),
};
self.pending_results[receiver_tid.raw() as usize] =
Some(SyscallResult::Message(message));
let core = self.platform.core_id();
let receiver = match self.threads.get_mut(receiver_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let _ = receiver.unblock();
let _ = receiver.set_running(core);
let caller = match self.threads.get_mut(caller_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let caller_priority = caller.priority();
let _ = caller.preempt();
self.scheduler.add(caller_tid, caller_priority, core);
self.scheduler.set_current(core, receiver_tid);
SyscallOutcome::SwitchTo(receiver_tid)
} else {
self.pending_messages[caller_tid.raw() as usize] = Some(message);
let endpoint = match self.endpoints.get_mut(ep_id) {
Some(ep) => ep,
None => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidEndpoint(ep_id),
)),
};
let _ = endpoint.enqueue_sender(caller_tid);
let caller = match self.threads.get_mut(caller_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let _ = caller.block(domain::thread::BlockReason::Sending(endpoint_slot));
self.scheduler.remove(caller_tid);
SyscallOutcome::BlockCaller(domain::thread::BlockReason::Sending(endpoint_slot))
}
}
fn handle_receive(
&mut self,
caller_tid: types::id::ThreadId,
caller_pid: types::id::ProcessId,
endpoint_slot: types::bounded::CapSlot,
) -> SyscallOutcome {
use domain::scheduler::Scheduler;
let ep_id = match self.check_cap(
caller_pid,
endpoint_slot,
types::permission::Permission::Read,
) {
Ok(cap) => match cap.resource() {
domain::resource::Resource::Endpoint(id) => *id,
_ => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidEndpoint(types::id::EndpointId::from_raw(0)),
)),
},
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let endpoint = match self.endpoints.get_mut(ep_id) {
Some(ep) => ep,
None => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidEndpoint(ep_id),
)),
};
if endpoint.has_waiting_sender() {
let sender_tid = match endpoint.dequeue_sender() {
Some(t) => t,
None => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::WouldBlock,
)),
};
let message = self.pending_messages[sender_tid.raw() as usize]
.take()
.unwrap_or_else(|| domain::message::Message::empty());
self.pending_results[sender_tid.raw() as usize] = Some(SyscallResult::Ok);
let sender = match self.threads.get_mut(sender_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let priority = sender.priority();
let _ = sender.unblock();
let core = self.platform.core_id();
self.scheduler.add(sender_tid, priority, core);
SyscallOutcome::Complete(SyscallResult::Message(message))
} else {
let endpoint = match self.endpoints.get_mut(ep_id) {
Some(ep) => ep,
None => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidEndpoint(ep_id),
)),
};
let _ = endpoint.enqueue_receiver(caller_tid);
let caller = match self.threads.get_mut(caller_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let _ = caller.block(domain::thread::BlockReason::Receiving(endpoint_slot));
self.scheduler.remove(caller_tid);
SyscallOutcome::BlockCaller(domain::thread::BlockReason::Receiving(endpoint_slot))
}
}
fn handle_wait_for_interrupt(
&mut self,
caller_tid: types::id::ThreadId,
caller_pid: types::id::ProcessId,
irq_slot: types::bounded::CapSlot,
) -> SyscallOutcome {
use domain::scheduler::Scheduler;
let irq_id = match self.check_cap(
caller_pid,
irq_slot,
types::permission::Permission::Read,
) {
Ok(cap) => match cap.resource() {
domain::resource::Resource::Interrupt(id) => *id,
_ => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidIrq(types::id::IrqId::from_raw(0)),
)),
},
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let _ = self.irqs.register(irq_id, caller_tid);
let caller = match self.threads.get_mut(caller_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let _ = caller.block(domain::thread::BlockReason::WaitingForInterrupt(irq_slot));
self.scheduler.remove(caller_tid);
SyscallOutcome::BlockCaller(domain::thread::BlockReason::WaitingForInterrupt(irq_slot))
}
fn handle_ack_interrupt(
&mut self,
caller_pid: types::id::ProcessId,
irq_slot: types::bounded::CapSlot,
) -> SyscallOutcome {
let irq_id = match self.check_cap(
caller_pid,
irq_slot,
types::permission::Permission::Write,
) {
Ok(cap) => match cap.resource() {
domain::resource::Resource::Interrupt(id) => *id,
_ => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidIrq(types::id::IrqId::from_raw(0)),
)),
},
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
self.platform.unmask_irq(irq_id);
SyscallOutcome::Complete(SyscallResult::Ok)
}
fn handle_wait_any(
&mut self,
caller_tid: types::id::ThreadId,
caller_pid: types::id::ProcessId,
sources: domain::syscall::WaitSet,
) -> SyscallOutcome {
use domain::scheduler::Scheduler;
if sources.is_empty() {
return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::WouldBlock,
));
}
for ep_slot in sources.endpoints() {
if let Some(slot) = ep_slot {
let ep_id = match self.check_cap(
caller_pid,
*slot,
types::permission::Permission::Read,
) {
Ok(cap) => match cap.resource() {
domain::resource::Resource::Endpoint(id) => *id,
_ => continue,
},
Err(_) => continue,
};
if let Some(ep) = self.endpoints.get_mut(ep_id) {
if ep.has_waiting_sender() {
return SyscallOutcome::Complete(SyscallResult::WakeReason(
domain::syscall::WakeReason::MessageReady(*slot),
));
}
}
}
}
for irq_slot in sources.interrupts() {
if let Some(slot) = irq_slot {
if let Ok(cap) = self.check_cap(
caller_pid,
*slot,
types::permission::Permission::Read,
) {
if let domain::resource::Resource::Interrupt(irq_id) = cap.resource() {
if self.irqs.owner(*irq_id).is_some() {
return SyscallOutcome::Complete(SyscallResult::WakeReason(
domain::syscall::WakeReason::InterruptFired(*slot),
));
}
}
}
}
}
for ep_slot in sources.endpoints() {
if let Some(slot) = ep_slot {
if let Ok(cap) = self.check_cap(caller_pid, *slot, types::permission::Permission::Read) {
if let domain::resource::Resource::Endpoint(id) = cap.resource() {
if let Some(ep) = self.endpoints.get_mut(*id) {
let _ = ep.enqueue_receiver(caller_tid);
}
}
}
}
}
self.wait_sets[caller_tid.raw() as usize] = Some(sources);
let caller = match self.threads.get_mut(caller_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let _ = caller.block(domain::thread::BlockReason::WaitingAny);
self.scheduler.remove(caller_tid);
SyscallOutcome::BlockCaller(domain::thread::BlockReason::WaitingAny)
}
}

View File

@@ -0,0 +1,46 @@
use platform::Platform;
use crate::{Kernel, SyscallOutcome, SyscallResult};
impl<P: Platform> Kernel<P> {
pub(crate) fn handle_delegate(
&mut self,
caller_pid: types::id::ProcessId,
slot: types::bounded::CapSlot,
to_endpoint: types::bounded::CapSlot,
restrict: types::permission::PermissionSet,
) -> SyscallOutcome {
let (resource, permissions) = match self.check_cap(
caller_pid,
slot,
types::permission::Permission::Delegate,
) {
Ok(cap) => (cap.resource().clone(), *cap.permissions()),
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let restricted = permissions.restrict_to(&restrict);
let target_pid = match self.check_cap(
caller_pid,
to_endpoint,
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: to_endpoint,
needed: types::permission::Permission::Execute,
},
)),
},
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
match self.grant_cap(target_pid, resource, restricted) {
Ok(_) => SyscallOutcome::Complete(SyscallResult::Ok),
Err(e) => SyscallOutcome::Complete(SyscallResult::Error(e)),
}
}
}

View File

@@ -0,0 +1,298 @@
use platform::Platform;
use crate::{Kernel, SyscallOutcome, SyscallResult};
impl<P: Platform> Kernel<P> {
pub(crate) fn handle_send(
&mut self,
caller_tid: types::id::ThreadId,
caller_pid: types::id::ProcessId,
endpoint_slot: types::bounded::CapSlot,
message: domain::message::Message,
) -> SyscallOutcome {
use domain::scheduler::Scheduler;
let ep_id = match self.check_cap(
caller_pid,
endpoint_slot,
types::permission::Permission::Execute,
) {
Ok(cap) => match cap.resource() {
domain::resource::Resource::Endpoint(id) => *id,
_ => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidEndpoint(types::id::EndpointId::from_raw(0)),
)),
},
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let endpoint = match self.endpoints.get_mut(ep_id) {
Some(ep) => ep,
None => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidEndpoint(ep_id),
)),
};
if endpoint.has_waiting_receiver() {
let receiver_tid = match endpoint.dequeue_receiver() {
Some(t) => t,
None => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::WouldBlock,
)),
};
self.pending_results[receiver_tid.raw() as usize] =
Some(SyscallResult::Message(message));
let core = self.platform.core_id();
let receiver = match self.threads.get_mut(receiver_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let _ = receiver.unblock();
let _ = receiver.set_running(core);
let caller = match self.threads.get_mut(caller_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let caller_priority = caller.priority();
let _ = caller.preempt();
self.scheduler.add(caller_tid, caller_priority, core);
self.scheduler.set_current(core, receiver_tid);
SyscallOutcome::SwitchTo(receiver_tid)
} else {
self.pending_messages[caller_tid.raw() as usize] = Some(message);
let endpoint = match self.endpoints.get_mut(ep_id) {
Some(ep) => ep,
None => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidEndpoint(ep_id),
)),
};
let _ = endpoint.enqueue_sender(caller_tid);
let caller = match self.threads.get_mut(caller_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let _ = caller.block(domain::thread::BlockReason::Sending(endpoint_slot));
self.scheduler.remove(caller_tid);
SyscallOutcome::BlockCaller(domain::thread::BlockReason::Sending(endpoint_slot))
}
}
pub(crate) fn handle_receive(
&mut self,
caller_tid: types::id::ThreadId,
caller_pid: types::id::ProcessId,
endpoint_slot: types::bounded::CapSlot,
) -> SyscallOutcome {
use domain::scheduler::Scheduler;
let ep_id = match self.check_cap(
caller_pid,
endpoint_slot,
types::permission::Permission::Read,
) {
Ok(cap) => match cap.resource() {
domain::resource::Resource::Endpoint(id) => *id,
_ => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidEndpoint(types::id::EndpointId::from_raw(0)),
)),
},
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let endpoint = match self.endpoints.get_mut(ep_id) {
Some(ep) => ep,
None => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidEndpoint(ep_id),
)),
};
if endpoint.has_waiting_sender() {
let sender_tid = match endpoint.dequeue_sender() {
Some(t) => t,
None => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::WouldBlock,
)),
};
let message = self.pending_messages[sender_tid.raw() as usize]
.take()
.unwrap_or_else(|| domain::message::Message::empty());
self.pending_results[sender_tid.raw() as usize] = Some(SyscallResult::Ok);
let sender = match self.threads.get_mut(sender_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let priority = sender.priority();
let _ = sender.unblock();
let core = self.platform.core_id();
self.scheduler.add(sender_tid, priority, core);
SyscallOutcome::Complete(SyscallResult::Message(message))
} else {
let endpoint = match self.endpoints.get_mut(ep_id) {
Some(ep) => ep,
None => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidEndpoint(ep_id),
)),
};
let _ = endpoint.enqueue_receiver(caller_tid);
let caller = match self.threads.get_mut(caller_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let _ = caller.block(domain::thread::BlockReason::Receiving(endpoint_slot));
self.scheduler.remove(caller_tid);
SyscallOutcome::BlockCaller(domain::thread::BlockReason::Receiving(endpoint_slot))
}
}
pub(crate) fn handle_wait_for_interrupt(
&mut self,
caller_tid: types::id::ThreadId,
caller_pid: types::id::ProcessId,
irq_slot: types::bounded::CapSlot,
) -> SyscallOutcome {
use domain::scheduler::Scheduler;
let irq_id = match self.check_cap(
caller_pid,
irq_slot,
types::permission::Permission::Read,
) {
Ok(cap) => match cap.resource() {
domain::resource::Resource::Interrupt(id) => *id,
_ => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidIrq(types::id::IrqId::from_raw(0)),
)),
},
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let _ = self.irqs.register(irq_id, caller_tid);
let caller = match self.threads.get_mut(caller_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let _ = caller.block(domain::thread::BlockReason::WaitingForInterrupt(irq_slot));
self.scheduler.remove(caller_tid);
SyscallOutcome::BlockCaller(domain::thread::BlockReason::WaitingForInterrupt(irq_slot))
}
pub(crate) fn handle_ack_interrupt(
&mut self,
caller_pid: types::id::ProcessId,
irq_slot: types::bounded::CapSlot,
) -> SyscallOutcome {
let irq_id = match self.check_cap(
caller_pid,
irq_slot,
types::permission::Permission::Write,
) {
Ok(cap) => match cap.resource() {
domain::resource::Resource::Interrupt(id) => *id,
_ => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidIrq(types::id::IrqId::from_raw(0)),
)),
},
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
self.platform.unmask_irq(irq_id);
SyscallOutcome::Complete(SyscallResult::Ok)
}
pub(crate) fn handle_wait_any(
&mut self,
caller_tid: types::id::ThreadId,
caller_pid: types::id::ProcessId,
sources: domain::syscall::WaitSet,
) -> SyscallOutcome {
use domain::scheduler::Scheduler;
if sources.is_empty() {
return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::WouldBlock,
));
}
for ep_slot in sources.endpoints() {
if let Some(slot) = ep_slot {
let ep_id = match self.check_cap(
caller_pid,
*slot,
types::permission::Permission::Read,
) {
Ok(cap) => match cap.resource() {
domain::resource::Resource::Endpoint(id) => *id,
_ => continue,
},
Err(_) => continue,
};
if let Some(ep) = self.endpoints.get_mut(ep_id) {
if ep.has_waiting_sender() {
return SyscallOutcome::Complete(SyscallResult::WakeReason(
domain::syscall::WakeReason::MessageReady(*slot),
));
}
}
}
}
for irq_slot in sources.interrupts() {
if let Some(slot) = irq_slot {
if let Ok(cap) = self.check_cap(
caller_pid,
*slot,
types::permission::Permission::Read,
) {
if let domain::resource::Resource::Interrupt(irq_id) = cap.resource() {
if self.irqs.owner(*irq_id).is_some() {
return SyscallOutcome::Complete(SyscallResult::WakeReason(
domain::syscall::WakeReason::InterruptFired(*slot),
));
}
}
}
}
}
for ep_slot in sources.endpoints() {
if let Some(slot) = ep_slot {
if let Ok(cap) = self.check_cap(caller_pid, *slot, types::permission::Permission::Read) {
if let domain::resource::Resource::Endpoint(id) = cap.resource() {
if let Some(ep) = self.endpoints.get_mut(*id) {
let _ = ep.enqueue_receiver(caller_tid);
}
}
}
}
}
self.wait_sets[caller_tid.raw() as usize] = Some(sources);
let caller = match self.threads.get_mut(caller_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let _ = caller.block(domain::thread::BlockReason::WaitingAny);
self.scheduler.remove(caller_tid);
SyscallOutcome::BlockCaller(domain::thread::BlockReason::WaitingAny)
}
}

View File

@@ -0,0 +1,128 @@
use platform::Platform;
use crate::{Kernel, SyscallOutcome, SyscallResult};
impl<P: Platform> Kernel<P> {
pub(crate) 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))
}
pub(crate) 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))
}
pub(crate) fn handle_kill(
&mut self,
caller_pid: types::id::ProcessId,
process_cap: types::bounded::CapSlot,
) -> SyscallOutcome {
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)),
}
};
self.cleanup_threads(&thread_ids);
let _ = self.processes.destroy(target_pid);
self.process_memory[target_pid.raw() as usize] = None;
SyscallOutcome::Complete(SyscallResult::Ok)
}
}

View File

@@ -0,0 +1,101 @@
use platform::Platform;
use crate::{Kernel, SyscallOutcome, SyscallResult};
impl<P: Platform> Kernel<P> {
pub(crate) fn handle_request_pages(
&mut self,
pid: types::id::ProcessId,
count: types::bounded::PageCount,
) -> SyscallOutcome {
let pages = count.value() as usize;
if let Some(pm) = &mut self.process_memory[pid.raw() as usize] {
if pm.grant(pages).is_err() {
return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::OutOfMemory,
));
}
} else {
return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidProcess(pid),
));
}
let addr = match self.page_pool.allocate(pages) {
Some(a) => a,
None => {
if let Some(pm) = &mut self.process_memory[pid.raw() as usize] {
let _ = pm.release(pages);
}
return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::OutOfMemory,
));
}
};
let region = domain::memory::MemoryRegion::new(
types::id::RegionId::from_raw(0),
pid,
addr,
count,
types::generation::Generation::initial(),
);
let region_id = match self.regions.insert(region) {
Some(id) => id,
None => {
self.page_pool.free(addr, pages);
if let Some(pm) = &mut self.process_memory[pid.raw() as usize] {
let _ = pm.release(pages);
}
return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::OutOfMemory,
));
}
};
let slot = match self.grant_cap(
pid,
domain::resource::Resource::Memory(region_id),
types::permission::PermissionSet::empty()
.with(types::permission::Permission::Read)
.with(types::permission::Permission::Write),
) {
Ok(s) => s,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
SyscallOutcome::Complete(SyscallResult::Slot(slot))
}
pub(crate) fn handle_release_pages(
&mut self,
pid: types::id::ProcessId,
addr: types::address::VirtualAddress,
count: types::bounded::PageCount,
) -> SyscallOutcome {
let pages = count.value() as usize;
let phys = types::address::PhysicalAddress::from_raw(addr.raw());
let region_id = match self.regions.find_by_base(pid, phys) {
Some((rid, _)) => rid,
None => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::OutOfMemory,
)),
};
if !self.page_pool.free(phys, pages) {
return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::OutOfMemory,
));
}
let _ = self.regions.remove(region_id);
if let Some(pm) = &mut self.process_memory[pid.raw() as usize] {
let _ = pm.release(pages);
}
SyscallOutcome::Complete(SyscallResult::Ok)
}
}

View File

@@ -0,0 +1,40 @@
mod capability;
mod ipc;
mod lifecycle;
mod memory;
mod simple;
use domain::syscall::Syscall;
use platform::Platform;
use crate::{Kernel, SyscallOutcome, SyscallResult};
impl<P: Platform> Kernel<P> {
pub fn handle_syscall(&mut self, syscall: Syscall) -> SyscallOutcome {
let (tid, pid) = match self.caller() {
Ok(caller) => caller,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
match syscall {
Syscall::GetTime => self.handle_get_time(),
Syscall::GetProcessId => self.handle_get_process_id(pid),
Syscall::Yield => self.handle_yield(tid),
Syscall::Exit => self.handle_exit(tid, pid),
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),
Syscall::Delegate { slot, to_endpoint, permissions } => {
self.handle_delegate(pid, slot, to_endpoint, permissions)
}
Syscall::Send { endpoint, message } => self.handle_send(tid, pid, endpoint, message),
Syscall::Receive { endpoint } => self.handle_receive(tid, pid, endpoint),
Syscall::WaitForInterrupt { irq } => self.handle_wait_for_interrupt(tid, pid, irq),
Syscall::AcknowledgeInterrupt { irq } => self.handle_ack_interrupt(pid, irq),
Syscall::WaitAny { sources } => self.handle_wait_any(tid, pid, sources),
}
}
}

View File

@@ -0,0 +1,94 @@
use platform::Platform;
use crate::{Kernel, SyscallOutcome, SyscallResult};
impl<P: Platform> Kernel<P> {
pub(crate) fn handle_get_time(&self) -> SyscallOutcome {
let ticks = self.platform.current_ticks();
SyscallOutcome::Complete(SyscallResult::Value(ticks.raw() as usize))
}
pub(crate) fn handle_get_process_id(&self, pid: types::id::ProcessId) -> SyscallOutcome {
SyscallOutcome::Complete(SyscallResult::Value(pid.raw() as usize))
}
pub(crate) fn handle_yield(&mut self, caller_tid: types::id::ThreadId) -> SyscallOutcome {
use domain::scheduler::Scheduler;
let core = self.platform.core_id();
let thread = match self.threads.get_mut(caller_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let priority = thread.priority();
if thread.preempt().is_err() {
return SyscallOutcome::Complete(SyscallResult::Ok);
}
self.scheduler.add(caller_tid, priority, core);
match self.scheduler.next(core) {
Some(next) if next != caller_tid => {
if self.threads.get_mut(next).and_then(|t| t.set_running(core)).is_ok() {
SyscallOutcome::SwitchTo(next)
} else {
let _ = self.threads.get_mut(caller_tid).map(|t| t.set_running(core));
SyscallOutcome::Complete(SyscallResult::Ok)
}
}
_ => {
let _ = self.threads.get_mut(caller_tid).map(|t| t.set_running(core));
SyscallOutcome::Complete(SyscallResult::Ok)
}
}
}
pub(crate) fn handle_exit(
&mut self,
_caller_tid: types::id::ThreadId,
pid: types::id::ProcessId,
) -> SyscallOutcome {
let thread_ids: [Option<types::id::ThreadId>; 4] = {
match self.processes.get(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(_) => return SyscallOutcome::TerminateCaller,
}
};
self.cleanup_threads(&thread_ids);
let _ = self.processes.destroy(pid);
self.process_memory[pid.raw() as usize] = None;
SyscallOutcome::TerminateCaller
}
pub(crate) fn handle_exit_thread(
&mut self,
caller_tid: types::id::ThreadId,
pid: types::id::ProcessId,
) -> SyscallOutcome {
use domain::scheduler::Scheduler;
self.scheduler.remove(caller_tid);
let _ = self.threads.destroy(caller_tid);
if let Ok(process) = self.processes.get_mut(pid) {
process.remove_thread(caller_tid);
if process.thread_ids().count() == 0 {
let _ = self.processes.destroy(pid);
self.process_memory[pid.raw() as usize] = None;
}
}
SyscallOutcome::TerminateCaller
}
}