Files
k-os/docs/ipc.md
Gabriel Kaszewski 154599e3af feat(kernel): derivation-tree revocation, region caps
- caps record parent (mint/delegate/ipc), CapRef w/ pid+slot gens
- Revoke = delete subtree, keep self; drop/death re-parents children
- CreateRegion/MapRegion; mapping lives only as long as its cap
- drop Permission::Revoke + object-wide revoke
- mint any cap (badge only ep/ntfn), process caps delegable
- kani: descendants == subtree; model-based fuzz vs spec
2026-10-26 20:41:03 +01:00

5.6 KiB

IPC

Synchronous message passing modelled on classic (non-MCS) seL4: endpoints for rendezvous messages, notifications for asynchronous signals, an implicit per-thread reply slot for Call. This document is the contract the kernel tests and proofs check.

Objects

Endpoint. A rendezvous point. Holds a FIFO of blocked senders and a FIFO of blocked receivers; never both non-empty. Owned by its creator and destroyed with it.

Notification. A word of signal bits plus a FIFO of blocked waiters and at most one bound thread. Owned by its creator and destroyed with it.

Both are named by capabilities whose generation must match (see P2).

Capabilities

A capability to an endpoint or notification carries a badge (a word; 0 means unbadged). Mint copies a capability with narrower permissions and, for endpoints and notifications, a badge. A badge cannot be changed once set: a badged capability cannot be minted again.

Derivation and revocation

Every capability made from another (by Mint, Delegate or an IPC transfer) records it as its parent; capabilities the kernel creates directly have none. Revoke(cap) deletes every capability derived from cap, transitively and across processes, and keeps cap itself. Any holder may revoke, but only its own subtree.

Deleting a capability (DropCap, or its process dying) makes its parent adopt its children, so stepping out of the chain never shields anyone from a revocation further up.

A region mapping made with MapRegion(cap, va, perms) lasts only as long as cap: deleting or revoking cap unmaps it. perms needs Read, plus Write for read-write or Execute for read-execute.

Object Permission Allows
Endpoint Execute Send, Call
Endpoint Read Receive, ReplyRecv
Endpoint Delegate transfer a capability through it; Mint
Notification Write Signal
Notification Read Wait, Poll, BindNotification
Notification Delegate Mint
Interrupt Execute SetIrqNotification, AckInterrupt

Messages

A message is 4 words and at most one capability. The capability is transferred only if the sender's endpoint capability has Delegate and the transferred capability itself has Delegate; it is copied (the sender keeps its own) with its permissions and badge unchanged. If the receiver's table is full, or the capability is no longer valid at delivery time, the message is delivered without it.

The receiver learns the badge of the capability the sender used. The kernel never reveals the sender's process or thread id.

Operations

  • Send(ep, msg): if a receiver waits, deliver and continue; otherwise block until one arrives.
  • Receive(ep): if the caller has a bound notification with bits set, return those bits (and clear them). Else if a sender waits, take its message. Else block until a sender arrives or the bound notification is signalled.
  • Call(ep, msg): like Send, but the caller then blocks awaiting a reply. The receiving thread's reply slot now names the caller.
  • Reply(msg): deliver msg to the thread in the caller's reply slot and clear the slot. Error if the slot is empty. Never blocks.
  • ReplyRecv(ep, msg): Reply if the slot is full, then Receive(ep).
  • Signal(ntfn): let b be the capability's badge, or 1 if unbadged. If a waiter is blocked, wake the first with b. Else if the bound thread is blocked in Receive, wake it with b. Else bits |= b.
  • Wait(ntfn): if bits != 0 return and clear them; else block.
  • Poll(ntfn): return and clear bits (possibly 0); never blocks.
  • BindNotification(ntfn): bind to the calling thread. One binding per thread and per notification.
  • SetIrqNotification(irq, ntfn): deliver interrupt irq as Signal through the given (badged) notification capability.
  • AckInterrupt(irq): unmask the line after handling.

An interrupt masks its line and signals its notification. If no route is set it stays masked.

Reply slot rules

  • A server thread has at most one pending reply. Receive (not ReplyRecv) with a pending reply drops it: the caller wakes with NoReply instead of blocking forever.
  • A reply slot names the caller by thread id and generation, so a reply can never reach a different thread that reused the id.
  • If the server thread dies, its pending caller wakes with NoReply.
  • If the caller dies, the reply slot is simply stale and Reply is a successful no-op.

Death

Destroying an endpoint or notification wakes every thread blocked on it with InvalidCap. Destroying a thread removes it from every queue, binding, reply slot and IRQ route.

Register ABI (RISC-V)

Inputs: a7 = syscall number, arguments in a0..a5. A capability argument of NONE (usize::MAX) means "no capability".

Outputs: a0 = status (0 = ok, else an ErrorCode), a1.. = results. Receiving operations (Receive, Call, ReplyRecv, Wait) return:

Register Value
a0 status
a1 badge (message) or signal bits (notification)
a2..a5 message words 0..3
a6 received capability slot, or NONE
a7 kind: 0 = message, 1 = call (reply expected), 2 = signal