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

View File

@@ -18,11 +18,10 @@ use domain::{
error::KernelError,
irq::IrqRoutingTable,
memory::{RegionTable, frame::FrameTable, quota::Quota},
message::Message,
notification::{MAX_NOTIFICATIONS, NotificationTable},
process::{MAX_PROCESSES, ProcessTable},
resource::Resource,
scheduler::{Scheduler, multi_core::MultiCoreScheduler},
syscall::WaitSet,
thread::{MAX_THREADS, ThreadState, ThreadTable},
};
use platform::Platform;
@@ -45,9 +44,9 @@ pub struct Kernel<P: Platform> {
quotas: [Quota; MAX_PROCESSES],
regions: RegionTable,
irqs: IrqRoutingTable,
pending_messages: [Option<Message>; MAX_THREADS],
notifications: NotificationTable,
ipc: [syscall::IpcState; MAX_THREADS],
pending_results: [Option<SyscallResult>; MAX_THREADS],
wait_sets: [Option<WaitSet>; MAX_THREADS],
}
impl<P: Platform> Kernel<P> {
@@ -63,9 +62,9 @@ impl<P: Platform> Kernel<P> {
quotas: [Quota::default(); MAX_PROCESSES],
regions: RegionTable::new(),
irqs: IrqRoutingTable::new(),
pending_messages: [const { None }; MAX_THREADS],
notifications: NotificationTable::new(),
ipc: [syscall::IpcState::default(); MAX_THREADS],
pending_results: [const { None }; MAX_THREADS],
wait_sets: [const { None }; MAX_THREADS],
}
}
@@ -95,9 +94,9 @@ impl<P: Platform> Kernel<P> {
addr_of_mut!((*slot).quotas).write([Quota::default(); MAX_PROCESSES]);
addr_of_mut!((*slot).regions).write(RegionTable::new());
addr_of_mut!((*slot).irqs).write(IrqRoutingTable::new());
addr_of_mut!((*slot).pending_messages).write([const { None }; MAX_THREADS]);
addr_of_mut!((*slot).notifications).write(NotificationTable::new());
addr_of_mut!((*slot).ipc).write([syscall::IpcState::default(); MAX_THREADS]);
addr_of_mut!((*slot).pending_results).write([const { None }; MAX_THREADS]);
addr_of_mut!((*slot).wait_sets).write([const { None }; MAX_THREADS]);
}
}
@@ -280,6 +279,7 @@ impl<P: Platform> Kernel<P> {
self.cleanup_threads(&thread_ids);
self.destroy_endpoints_owned_by(pid);
self.destroy_notifications_owned_by(pid);
self.teardown_memory(pid);
let _ = self.processes.destroy(pid);
@@ -289,10 +289,12 @@ impl<P: Platform> Kernel<P> {
use domain::scheduler::Scheduler;
for tid in thread_ids.iter().flatten() {
self.endpoints.remove_thread_from_all(*tid);
self.cleanup_wait_any(*tid);
self.irqs.unregister_thread(*tid);
self.notifications.remove_thread_from_all(*tid);
let ipc = core::mem::take(&mut self.ipc[tid.raw() as usize]);
if let Some(caller) = ipc.reply_to {
self.release_caller(caller);
}
self.scheduler.remove(*tid);
self.pending_messages[tid.raw() as usize] = None;
self.pending_results[tid.raw() as usize] = None;
let _ = self.threads.destroy(*tid);
}
@@ -315,28 +317,31 @@ impl<P: Platform> Kernel<P> {
}
}
/// Makes a blocked thread runnable with `error` as its syscall result.
fn wake_with_error(&mut self, tid: ThreadId, error: KernelError) {
self.cleanup_wait_any(tid);
self.pending_messages[tid.raw() as usize] = None;
let Ok(thread) = self.threads.get_mut(tid) else {
return;
};
if !matches!(thread.state(), ThreadState::Blocked(_)) {
return;
/// Destroys every notification `owner` created, waking its waiters
/// with an error and unbinding its bound thread.
pub(crate) fn destroy_notifications_owned_by(&mut self, owner: ProcessId) {
let mut owned = [None; MAX_NOTIFICATIONS];
for (slot, id) in owned.iter_mut().zip(self.notifications.ids_owned_by(owner)) {
*slot = Some(id);
}
let priority = thread.priority();
if thread.unblock().is_ok() {
self.pending_results[tid.raw() as usize] = Some(SyscallResult::Error(error));
let core = self.platform.core_id();
self.scheduler.add(tid, priority, core);
for id in owned.into_iter().flatten() {
let Some(mut n) = self.notifications.destroy(id) else {
continue;
};
if let Some(bound) = n.bound() {
self.ipc[bound.raw() as usize].bound = None;
}
while let Some(tid) = n.pop_waiter() {
self.wake_with_error(tid, KernelError::NoSuchObject);
}
}
}
pub(crate) fn cleanup_wait_any(&mut self, tid: ThreadId) {
if self.wait_sets[tid.raw() as usize].take().is_some() {
self.endpoints.remove_thread_from_all(tid);
}
/// Makes a blocked thread runnable with `error` as its syscall result,
/// dropping whatever it was about to send.
fn wake_with_error(&mut self, tid: ThreadId, error: KernelError) {
self.ipc[tid.raw() as usize].outgoing = None;
self.wake(tid, SyscallResult::Error(error));
}
fn check_cap(
@@ -362,6 +367,7 @@ impl<P: Platform> Kernel<P> {
match resource {
Resource::Endpoint(id) => self.endpoints.generation(*id),
Resource::Memory(id) => self.regions.generation(*id),
Resource::Notification(id) => self.notifications.generation(*id),
Resource::Process(pid) => self.processes.generation(*pid),
Resource::Interrupt(_) | Resource::ProcessCreate => Some(Generation::initial()),
}
@@ -371,6 +377,7 @@ impl<P: Platform> Kernel<P> {
match resource {
Resource::Endpoint(id) => self.endpoints.revoke(*id),
Resource::Memory(id) => self.regions.revoke(*id),
Resource::Notification(id) => self.notifications.revoke(*id),
_ => {}
}
}