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

@@ -5,26 +5,26 @@ use types::bounded::CapSlot;
mod tests;
pub const MAX_MESSAGE_WORDS: usize = 4;
pub const MAX_CAP_TRANSFERS: usize = 4;
/// Four words and at most one capability (named by the sender's slot).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Message {
words: [usize; MAX_MESSAGE_WORDS],
caps: [Option<CapSlot>; MAX_CAP_TRANSFERS],
cap: Option<CapSlot>,
}
impl Message {
pub fn empty() -> Self {
Self {
words: [0; MAX_MESSAGE_WORDS],
caps: [None; MAX_CAP_TRANSFERS],
}
Self::from_words([0; MAX_MESSAGE_WORDS])
}
pub fn from_words(words: [usize; MAX_MESSAGE_WORDS]) -> Self {
Self {
words,
caps: [None; MAX_CAP_TRANSFERS],
}
Self { words, cap: None }
}
pub fn with_cap(mut self, slot: CapSlot) -> Self {
self.cap = Some(slot);
self
}
pub fn words(&self) -> &[usize; MAX_MESSAGE_WORDS] {
@@ -35,42 +35,7 @@ impl Message {
self.words[index]
}
pub fn caps(&self) -> &[Option<CapSlot>; MAX_CAP_TRANSFERS] {
&self.caps
}
pub fn cap(&self, index: usize) -> Option<CapSlot> {
self.caps[index]
}
pub fn builder() -> MessageBuilder {
MessageBuilder {
message: Message::empty(),
}
}
}
pub struct MessageBuilder {
message: Message,
}
impl MessageBuilder {
pub fn word(mut self, index: usize, value: usize) -> Self {
self.message.words[index] = value;
self
}
pub fn words(mut self, words: [usize; MAX_MESSAGE_WORDS]) -> Self {
self.message.words = words;
self
}
pub fn cap(mut self, index: usize, slot: CapSlot) -> Self {
self.message.caps[index] = Some(slot);
self
}
pub fn build(self) -> Message {
self.message
pub fn cap(&self) -> Option<CapSlot> {
self.cap
}
}

View File

@@ -1,78 +1,23 @@
use super::*;
use types::bounded::CapSlot;
fn slot(n: u8) -> CapSlot {
CapSlot::new(n).unwrap()
}
// --- Construction ---
#[test]
fn empty_message_has_zeroed_words_and_no_caps() {
fn empty_message_has_zeroed_words_and_no_cap() {
let msg = Message::empty();
assert_eq!(msg.words(), &[0; MAX_MESSAGE_WORDS]);
for i in 0..MAX_CAP_TRANSFERS {
assert!(msg.cap(i).is_none());
}
assert!(msg.cap().is_none());
}
#[test]
fn from_words_sets_words_and_no_caps() {
fn from_words_keeps_every_word() {
let msg = Message::from_words([10, 20, 30, 40]);
assert_eq!(msg.word(0), 10);
assert_eq!(msg.word(1), 20);
assert_eq!(msg.word(2), 30);
assert_eq!(msg.word(3), 40);
assert!(msg.cap(0).is_none());
}
// --- Accessors ---
#[test]
fn words_returns_full_array() {
let msg = Message::from_words([1, 2, 3, 4]);
assert_eq!(msg.words(), &[1, 2, 3, 4]);
assert_eq!(msg.words(), &[10, 20, 30, 40]);
assert!(msg.cap().is_none());
}
#[test]
fn caps_returns_full_array() {
let msg = Message::empty();
assert_eq!(msg.caps(), &[None; MAX_CAP_TRANSFERS]);
}
// --- Builder ---
#[test]
fn builder_sets_individual_words() {
let msg = Message::builder().word(0, 42).word(2, 99).build();
assert_eq!(msg.word(0), 42);
assert_eq!(msg.word(1), 0);
assert_eq!(msg.word(2), 99);
assert_eq!(msg.word(3), 0);
}
#[test]
fn builder_sets_caps() {
let msg = Message::builder().cap(0, slot(5)).cap(1, slot(10)).build();
assert_eq!(msg.cap(0), Some(slot(5)));
assert_eq!(msg.cap(1), Some(slot(10)));
assert!(msg.cap(2).is_none());
}
#[test]
fn builder_sets_words_and_caps_together() {
let msg = Message::builder()
.word(0, 100)
.word(1, 200)
.cap(0, slot(3))
.build();
assert_eq!(msg.word(0), 100);
assert_eq!(msg.word(1), 200);
assert_eq!(msg.cap(0), Some(slot(3)));
}
#[test]
fn builder_with_all_words() {
let msg = Message::builder().words([11, 22, 33, 44]).build();
assert_eq!(msg.words(), &[11, 22, 33, 44]);
fn with_cap_attaches_one_capability() {
let msg = Message::from_words([1, 2, 3, 4]).with_cap(CapSlot::new(5).unwrap());
assert_eq!(msg.cap(), CapSlot::new(5));
assert_eq!(msg.word(3), 4);
}