192 lines
7.4 KiB
Rust
192 lines
7.4 KiB
Rust
mod capability;
|
|
mod fault;
|
|
mod ipc;
|
|
mod lifecycle;
|
|
mod memory;
|
|
mod simple;
|
|
|
|
use domain::{error::KernelError, syscall::Syscall};
|
|
use platform::Platform;
|
|
use types::{bounded::CapSlot, id::ThreadId, syscall_abi::ErrorCode};
|
|
|
|
use crate::Kernel;
|
|
|
|
pub use ipc::{IpcState, ThreadRef};
|
|
pub use types::syscall_abi::{Delivery, DeliveryKind};
|
|
|
|
pub enum SyscallOutcome {
|
|
Complete(SyscallResult),
|
|
BlockCaller(domain::thread::BlockReason),
|
|
/// The caller blocked and `ThreadId` should run next.
|
|
SwitchTo(ThreadId),
|
|
TerminateCaller,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum SyscallResult {
|
|
Ok,
|
|
Value(usize),
|
|
Slot(CapSlot),
|
|
Received(Delivery),
|
|
Error(KernelError),
|
|
}
|
|
|
|
/// The status code userspace sees for a kernel error.
|
|
pub fn error_code(error: &KernelError) -> ErrorCode {
|
|
use KernelError::*;
|
|
match error {
|
|
InvalidSlot(_) | RevokedCapability(_) | NoSuchObject | InvalidEndpoint(_) => {
|
|
ErrorCode::InvalidCap
|
|
}
|
|
PermissionDenied { .. } | ProcessCreateDenied | WritableAndExecutable => {
|
|
ErrorCode::PermissionDenied
|
|
}
|
|
WrongObject => ErrorCode::WrongObject,
|
|
NoFreeSlot => ErrorCode::NoFreeSlot,
|
|
OutOfMemory => ErrorCode::OutOfMemory,
|
|
InvalidAddress => ErrorCode::InvalidAddress,
|
|
AddressInUse => ErrorCode::AddressInUse,
|
|
NoReply => ErrorCode::NoReply,
|
|
TooManyThreadsInProcess | TooManyProcesses | TooManyThreads | EndpointFull => {
|
|
ErrorCode::LimitReached
|
|
}
|
|
InvalidProcess(_)
|
|
| InvalidThread(_)
|
|
| InvalidStateTransition(_)
|
|
| InvalidIrq(_)
|
|
| InvalidRegion
|
|
| IrqAlreadyOwned(_)
|
|
| WouldBlock
|
|
| InvalidArgument => ErrorCode::InvalidArgument,
|
|
}
|
|
}
|
|
|
|
fn complete(result: Result<SyscallResult, KernelError>) -> SyscallOutcome {
|
|
SyscallOutcome::Complete(result.unwrap_or_else(SyscallResult::Error))
|
|
}
|
|
|
|
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, arg } => {
|
|
self.handle_spawn_thread(pid, entry, stack, arg)
|
|
}
|
|
Syscall::CreateProcess {
|
|
create_cap,
|
|
image,
|
|
limits,
|
|
} => self.handle_create_process(pid, create_cap, image, limits),
|
|
Syscall::Kill { process } => self.handle_kill(pid, process),
|
|
Syscall::Revoke { slot } => self.handle_revoke(pid, slot),
|
|
Syscall::DropCap { slot } => self.handle_drop_cap(pid, slot),
|
|
Syscall::CreateRegion { pages } => {
|
|
complete(self.create_region_cap(pid, pages).map(SyscallResult::Slot))
|
|
}
|
|
Syscall::MapRegion { region, va, perms } => complete(
|
|
self.map_region_cap(pid, region, va, perms)
|
|
.map(|pages| SyscallResult::Value(pages.value() as usize)),
|
|
),
|
|
Syscall::RegionAddress { region } => complete(
|
|
self.region_address_cap(pid, region)
|
|
.map(|pa| SyscallResult::Value(pa.raw())),
|
|
),
|
|
Syscall::WatchDeath {
|
|
process,
|
|
notification,
|
|
} => complete(
|
|
self.watch_death(pid, process, notification)
|
|
.map(|()| SyscallResult::Ok),
|
|
),
|
|
Syscall::SetTimer {
|
|
notification,
|
|
deadline,
|
|
} => complete(
|
|
self.set_timer(pid, notification, deadline)
|
|
.map(|()| SyscallResult::Ok),
|
|
),
|
|
Syscall::Delegate {
|
|
slot,
|
|
to_endpoint,
|
|
permissions,
|
|
} => self.handle_delegate(pid, slot, to_endpoint, permissions),
|
|
Syscall::Mint {
|
|
slot,
|
|
badge,
|
|
permissions,
|
|
} => complete(
|
|
self.mint(pid, slot, badge, permissions)
|
|
.map(SyscallResult::Slot),
|
|
),
|
|
Syscall::Send { endpoint, message } => self.send(tid, pid, endpoint, message, false),
|
|
Syscall::Call { endpoint, message } => self.send(tid, pid, endpoint, message, true),
|
|
Syscall::Receive { endpoint } => self.receive(tid, pid, endpoint, true),
|
|
Syscall::Reply { message } => {
|
|
complete(self.reply(tid, pid, message).map(|()| SyscallResult::Ok))
|
|
}
|
|
Syscall::ReplyRecv { endpoint, message } => {
|
|
self.reply_recv(tid, pid, endpoint, message)
|
|
}
|
|
Syscall::SaveReply => complete(
|
|
self.save_reply(tid)
|
|
.map(|r| SyscallResult::Value(usize::from(r.value()))),
|
|
),
|
|
Syscall::ReplySaved { reply, message } => complete(
|
|
self.reply_saved(tid, pid, reply, message)
|
|
.map(|()| SyscallResult::Ok),
|
|
),
|
|
Syscall::CreateNotification => {
|
|
complete(self.create_notification_cap(pid).map(SyscallResult::Slot))
|
|
}
|
|
Syscall::Signal { notification } => {
|
|
complete(self.signal(pid, notification).map(|()| SyscallResult::Ok))
|
|
}
|
|
Syscall::Wait { notification } => self.wait(tid, pid, notification),
|
|
Syscall::Poll { notification } => complete(
|
|
self.poll(pid, notification)
|
|
.map(|bits| SyscallResult::Received(Delivery::signal(bits))),
|
|
),
|
|
Syscall::BindNotification { notification } => complete(
|
|
self.bind_notification(tid, pid, notification)
|
|
.map(|()| SyscallResult::Ok),
|
|
),
|
|
Syscall::SetIrqNotification { irq, notification } => complete(
|
|
self.set_irq_notification(pid, irq, notification)
|
|
.map(|()| SyscallResult::Ok),
|
|
),
|
|
Syscall::AcknowledgeInterrupt { irq } => {
|
|
complete(self.ack_interrupt(pid, irq).map(|()| SyscallResult::Ok))
|
|
}
|
|
Syscall::Halt { control } => self.handle_halt(pid, control),
|
|
Syscall::CreateEndpoint => self.handle_create_endpoint(pid),
|
|
Syscall::DebugPutchar { console, byte } => complete(
|
|
self.debug_putchar(pid, console, byte)
|
|
.map(|()| SyscallResult::Ok),
|
|
),
|
|
Syscall::MapPage {
|
|
process,
|
|
src_va,
|
|
dst_va,
|
|
executable,
|
|
} => self.handle_map_page(pid, process, src_va, dst_va, executable),
|
|
Syscall::SpawnThreadIn {
|
|
process,
|
|
entry,
|
|
stack,
|
|
arg,
|
|
} => self.handle_spawn_thread_in(pid, process, entry, stack, arg),
|
|
}
|
|
}
|
|
}
|