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:
2026-10-26 20:27:38 +01:00
parent d43421e33e
commit 76f10ff1f1
46 changed files with 3339 additions and 1296 deletions

21
crates/types/src/badge.rs Normal file
View File

@@ -0,0 +1,21 @@
/// A word stamped on an endpoint or notification capability by `Mint`.
/// Receivers see the badge of the capability a sender used, which is how a
/// server tells its clients apart. Zero means "unbadged".
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Badge(usize);
impl Badge {
pub const NONE: Self = Self(0);
pub const fn new(value: usize) -> Self {
Self(value)
}
pub const fn raw(&self) -> usize {
self.0
}
pub const fn is_none(&self) -> bool {
self.0 == 0
}
}

View File

@@ -4,5 +4,6 @@ kernel_id!(ProcessId, u16);
kernel_id!(ThreadId, u16);
kernel_id!(EndpointId, u16);
kernel_id!(RegionId, u16);
kernel_id!(NotificationId, u16);
kernel_id!(IrqId, u16);
kernel_id!(CoreId, u8);

View File

@@ -1,6 +1,7 @@
#![no_std]
pub mod address;
pub mod badge;
pub mod bounded;
pub mod display;
pub mod generation;

View File

@@ -1,9 +1,12 @@
//! Register-level syscall contract shared by the kernel and libkos.
//! See docs/ipc.md for the meaning of each call.
pub const SYS_SEND: usize = 0;
pub const SYS_RECEIVE: usize = 1;
pub const SYS_DELEGATE: usize = 2;
pub const SYS_WAIT_FOR_INTERRUPT: usize = 3;
// 3 was WaitForInterrupt; interrupts are delivered through notifications.
pub const SYS_ACKNOWLEDGE_INTERRUPT: usize = 4;
pub const SYS_WAIT_ANY: usize = 5;
// 5 was WaitAny; superseded by notifications.
pub const SYS_CREATE_PROCESS: usize = 6;
pub const SYS_KILL: usize = 7;
pub const SYS_REVOKE: usize = 8;
@@ -21,3 +24,63 @@ pub const SYS_DEBUG_PUTCHAR: usize = 19;
pub const SYS_MAP_PAGE: usize = 20;
pub const SYS_SPAWN_THREAD_IN: usize = 21;
pub const SYS_DROP_CAP: usize = 22;
pub const SYS_CALL: usize = 23;
pub const SYS_REPLY: usize = 24;
pub const SYS_REPLY_RECV: usize = 25;
pub const SYS_MINT: usize = 26;
pub const SYS_CREATE_NOTIFICATION: usize = 27;
pub const SYS_SIGNAL: usize = 28;
pub const SYS_WAIT: usize = 29;
pub const SYS_POLL: usize = 30;
pub const SYS_BIND_NOTIFICATION: usize = 31;
pub const SYS_SET_IRQ_NOTIFICATION: usize = 32;
/// "No capability" in a capability argument or result register.
pub const NONE: usize = usize::MAX;
/// What a receiving syscall got, returned in `a7`.
pub const KIND_MESSAGE: usize = 0;
pub const KIND_CALL: usize = 1;
pub const KIND_SIGNAL: usize = 2;
/// Status returned in `a0`; `0` is success.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(usize)]
pub enum ErrorCode {
/// No capability in that slot, or it names a dead or revoked object.
InvalidCap = 1,
/// The capability lacks the permission the operation needs.
PermissionDenied = 2,
/// The capability names the wrong kind of object.
WrongObject = 3,
NoFreeSlot = 4,
OutOfMemory = 5,
InvalidAddress = 6,
AddressInUse = 7,
InvalidArgument = 8,
/// `Reply` with nothing to reply to, or a call whose reply was dropped.
NoReply = 9,
/// A table (threads, processes, queue) is full.
LimitReached = 10,
UnknownSyscall = 11,
}
impl ErrorCode {
pub fn from_raw(code: usize) -> Option<Self> {
use ErrorCode::*;
Some(match code {
1 => InvalidCap,
2 => PermissionDenied,
3 => WrongObject,
4 => NoFreeSlot,
5 => OutOfMemory,
6 => InvalidAddress,
7 => AddressInUse,
8 => InvalidArgument,
9 => NoReply,
10 => LimitReached,
11 => UnknownSyscall,
_ => return None,
})
}
}