refactor(kernel): split syscall.rs into per-tier modules (simple, memory, lifecycle, capability, ipc)
This commit is contained in:
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
46
crates/kernel/src/syscall/capability.rs
Normal file
46
crates/kernel/src/syscall/capability.rs
Normal 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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
298
crates/kernel/src/syscall/ipc.rs
Normal file
298
crates/kernel/src/syscall/ipc.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
128
crates/kernel/src/syscall/lifecycle.rs
Normal file
128
crates/kernel/src/syscall/lifecycle.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
101
crates/kernel/src/syscall/memory.rs
Normal file
101
crates/kernel/src/syscall/memory.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
40
crates/kernel/src/syscall/mod.rs
Normal file
40
crates/kernel/src/syscall/mod.rs
Normal 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
94
crates/kernel/src/syscall/simple.rs
Normal file
94
crates/kernel/src/syscall/simple.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user