feat(kernel): seL4-style IPC, notifications, status ABI
- badges + Mint, Call/Reply/ReplyRecv w/ gen-checked reply slot - cap transfer (1/msg, copy, needs Delegate, rechecked at delivery) - notifications: signal/wait/poll/bind, irq -> notification routing - WaitAny/WaitForInterrupt removed; a0=status, ErrorCode - libkos: single ecall primitive, compat wrappers + typed new api - fix: trap return clobbered user t0 on every trap - fix: wait queues FIFO, delegate keeps badge, fault cap checked - docs/ipc.md spec, ipc tests, ipc fuzz, kani ntfn + waitqueue
This commit is contained in:
@@ -5,28 +5,95 @@ mod lifecycle;
|
||||
mod memory;
|
||||
mod simple;
|
||||
|
||||
use domain::{error::KernelError, message::Message, syscall::Syscall};
|
||||
use domain::{error::KernelError, syscall::Syscall};
|
||||
use platform::Platform;
|
||||
use types::{bounded::CapSlot, id::ThreadId};
|
||||
use types::{bounded::CapSlot, id::ThreadId, syscall_abi::ErrorCode};
|
||||
|
||||
use crate::Kernel;
|
||||
|
||||
pub use ipc::{IpcState, ThreadRef};
|
||||
|
||||
pub enum SyscallOutcome {
|
||||
Complete(SyscallResult),
|
||||
BlockCaller(domain::thread::BlockReason),
|
||||
/// The caller blocked and `ThreadId` should run next.
|
||||
SwitchTo(ThreadId),
|
||||
TerminateCaller,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DeliveryKind {
|
||||
/// A `Send`; no reply expected.
|
||||
Message,
|
||||
/// A `Call`; the receiver's reply slot now names the caller.
|
||||
Call,
|
||||
/// Notification bits.
|
||||
Signal,
|
||||
}
|
||||
|
||||
/// What a receiving syscall hands back (docs/ipc.md, register ABI).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Delivery {
|
||||
pub kind: DeliveryKind,
|
||||
/// The sender's badge, or the signal bits.
|
||||
pub badge: usize,
|
||||
pub words: [usize; 4],
|
||||
/// Where a transferred capability landed in the receiver's table.
|
||||
pub cap: Option<CapSlot>,
|
||||
}
|
||||
|
||||
impl Delivery {
|
||||
pub fn signal(bits: usize) -> Self {
|
||||
Self {
|
||||
kind: DeliveryKind::Signal,
|
||||
badge: bits,
|
||||
words: [0; 4],
|
||||
cap: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SyscallResult {
|
||||
Ok,
|
||||
Value(usize),
|
||||
Slot(CapSlot),
|
||||
Message(Message),
|
||||
WakeReason(domain::syscall::WakeReason),
|
||||
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 => 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() {
|
||||
@@ -54,11 +121,45 @@ impl<P: Platform> Kernel<P> {
|
||||
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),
|
||||
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::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 => self.handle_halt(pid),
|
||||
Syscall::CreateEndpoint => self.handle_create_endpoint(pid),
|
||||
Syscall::DebugPutchar { byte } => self.handle_debug_putchar(byte),
|
||||
|
||||
Reference in New Issue
Block a user