feat(kernel): add WaitForInterrupt, AcknowledgeInterrupt, and WaitAny syscalls

This commit is contained in:
2026-10-18 00:26:11 +02:00
parent e7835c0435
commit 8912a944b8
4 changed files with 260 additions and 5 deletions

View File

@@ -11,6 +11,28 @@ pub struct WaitSet {
interrupts: [Option<CapSlot>; 4],
}
impl WaitSet {
pub fn new(
endpoints: [Option<CapSlot>; 4],
interrupts: [Option<CapSlot>; 4],
) -> Self {
Self { endpoints, interrupts }
}
pub fn endpoints(&self) -> &[Option<CapSlot>; 4] {
&self.endpoints
}
pub fn interrupts(&self) -> &[Option<CapSlot>; 4] {
&self.interrupts
}
pub fn is_empty(&self) -> bool {
self.endpoints.iter().all(|s| s.is_none())
&& self.interrupts.iter().all(|s| s.is_none())
}
}
pub enum WakeReason {
MessageReady(CapSlot),
InterruptFired(CapSlot),

View File

@@ -21,6 +21,7 @@ use domain::{
process::{ProcessTable, MAX_PROCESSES},
resource::Resource,
scheduler::{multi_core::MultiCoreScheduler, Scheduler},
syscall::WaitSet,
thread::{ThreadTable, ThreadState, MAX_THREADS},
};
use platform::Platform;
@@ -60,6 +61,7 @@ pub struct Kernel<P: Platform> {
irqs: IrqRoutingTable,
pending_messages: [Option<Message>; MAX_THREADS],
pending_results: [Option<SyscallResult>; MAX_THREADS],
wait_sets: [Option<WaitSet>; MAX_THREADS],
}
impl<P: Platform> Kernel<P> {
@@ -77,6 +79,7 @@ impl<P: Platform> Kernel<P> {
irqs: IrqRoutingTable::new(),
pending_messages: [const { None }; MAX_THREADS],
pending_results: [const { None }; MAX_THREADS],
wait_sets: [const { None }; MAX_THREADS],
}
}

View File

@@ -26,9 +26,9 @@ impl<P: Platform> Kernel<P> {
}
Syscall::Send { endpoint, message } => self.handle_send(tid, pid, endpoint, message),
Syscall::Receive { endpoint } => self.handle_receive(tid, pid, endpoint),
_ => SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::WouldBlock,
)),
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),
}
}
@@ -524,4 +524,141 @@ impl<P: Platform> Kernel<P> {
SyscallOutcome::BlockCaller(domain::thread::BlockReason::Receiving(endpoint_slot))
}
}
fn handle_wait_for_interrupt(
&mut self,
caller_tid: types::id::ThreadId,
caller_pid: types::id::ProcessId,
irq_slot: types::bounded::CapSlot,
) -> SyscallOutcome {
use domain::scheduler::Scheduler;
let irq_id = match self.check_cap(
caller_pid,
irq_slot,
types::permission::Permission::Read,
) {
Ok(cap) => match cap.resource() {
domain::resource::Resource::Interrupt(id) => *id,
_ => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidIrq(types::id::IrqId::from_raw(0)),
)),
},
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let _ = self.irqs.register(irq_id, caller_tid);
let caller = match self.threads.get_mut(caller_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let _ = caller.block(domain::thread::BlockReason::WaitingForInterrupt(irq_slot));
self.scheduler.remove(caller_tid);
SyscallOutcome::BlockCaller(domain::thread::BlockReason::WaitingForInterrupt(irq_slot))
}
fn handle_ack_interrupt(
&mut self,
caller_pid: types::id::ProcessId,
irq_slot: types::bounded::CapSlot,
) -> SyscallOutcome {
let irq_id = match self.check_cap(
caller_pid,
irq_slot,
types::permission::Permission::Write,
) {
Ok(cap) => match cap.resource() {
domain::resource::Resource::Interrupt(id) => *id,
_ => return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::InvalidIrq(types::id::IrqId::from_raw(0)),
)),
},
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
self.platform.unmask_irq(irq_id);
SyscallOutcome::Complete(SyscallResult::Ok)
}
fn handle_wait_any(
&mut self,
caller_tid: types::id::ThreadId,
caller_pid: types::id::ProcessId,
sources: domain::syscall::WaitSet,
) -> SyscallOutcome {
use domain::scheduler::Scheduler;
if sources.is_empty() {
return SyscallOutcome::Complete(SyscallResult::Error(
domain::error::KernelError::WouldBlock,
));
}
for ep_slot in sources.endpoints() {
if let Some(slot) = ep_slot {
let ep_id = match self.check_cap(
caller_pid,
*slot,
types::permission::Permission::Read,
) {
Ok(cap) => match cap.resource() {
domain::resource::Resource::Endpoint(id) => *id,
_ => continue,
},
Err(_) => continue,
};
if let Some(ep) = self.endpoints.get_mut(ep_id) {
if ep.has_waiting_sender() {
return SyscallOutcome::Complete(SyscallResult::WakeReason(
domain::syscall::WakeReason::MessageReady(*slot),
));
}
}
}
}
for irq_slot in sources.interrupts() {
if let Some(slot) = irq_slot {
if let Ok(cap) = self.check_cap(
caller_pid,
*slot,
types::permission::Permission::Read,
) {
if let domain::resource::Resource::Interrupt(irq_id) = cap.resource() {
if self.irqs.owner(*irq_id).is_some() {
return SyscallOutcome::Complete(SyscallResult::WakeReason(
domain::syscall::WakeReason::InterruptFired(*slot),
));
}
}
}
}
}
for ep_slot in sources.endpoints() {
if let Some(slot) = ep_slot {
if let Ok(cap) = self.check_cap(caller_pid, *slot, types::permission::Permission::Read) {
if let domain::resource::Resource::Endpoint(id) = cap.resource() {
if let Some(ep) = self.endpoints.get_mut(*id) {
let _ = ep.enqueue_receiver(caller_tid);
}
}
}
}
}
self.wait_sets[caller_tid.raw() as usize] = Some(sources);
let caller = match self.threads.get_mut(caller_tid) {
Ok(t) => t,
Err(e) => return SyscallOutcome::Complete(SyscallResult::Error(e)),
};
let _ = caller.block(domain::thread::BlockReason::WaitingAny);
self.scheduler.remove(caller_tid);
SyscallOutcome::BlockCaller(domain::thread::BlockReason::WaitingAny)
}
}

View File

@@ -1,10 +1,10 @@
use super::*;
use crate::{SyscallOutcome, SyscallResult};
use domain::message::Message;
use domain::syscall::Syscall;
use domain::syscall::{Syscall, WaitSet, WakeReason};
use domain::thread::BlockReason;
use types::bounded::CapSlot;
use types::id::ThreadId;
use types::id::{IrqId, ThreadId};
use types::permission::{Permission, PermissionSet};
fn setup_ipc_kernel() -> (
@@ -138,3 +138,96 @@ fn send_checks_endpoint_capability() {
SyscallOutcome::Complete(SyscallResult::Error(_))
));
}
// --- WaitForInterrupt / AcknowledgeInterrupt ---
#[test]
fn wait_for_interrupt_blocks_caller() {
let (mut k, pid_a, _, _, _, _) = setup_ipc_kernel();
let irq = IrqId::from_raw(7);
let irq_slot = k.grant_cap_for_test(
pid_a,
domain::resource::Resource::Interrupt(irq),
PermissionSet::empty()
.with(Permission::Read)
.with(Permission::Write),
);
let outcome = k.handle_syscall(Syscall::WaitForInterrupt { irq: irq_slot });
assert!(matches!(
outcome,
SyscallOutcome::BlockCaller(BlockReason::WaitingForInterrupt(_))
));
}
#[test]
fn ack_interrupt_returns_ok() {
let (mut k, pid_a, _, _, _, _) = setup_ipc_kernel();
let irq = IrqId::from_raw(7);
let irq_slot = k.grant_cap_for_test(
pid_a,
domain::resource::Resource::Interrupt(irq),
PermissionSet::empty().with(Permission::Write),
);
let outcome = k.handle_syscall(Syscall::AcknowledgeInterrupt { irq: irq_slot });
assert!(matches!(
outcome,
SyscallOutcome::Complete(SyscallResult::Ok)
));
}
// --- WaitAny ---
#[test]
fn wait_any_with_ready_source_returns_immediately() {
let (mut k, pid_a, _, cap_a, cap_b, tid_b) = setup_ipc_kernel();
let tid_a = ThreadId::from_raw(0);
// Thread B sends a message (blocks since no receiver)
k.switch_to_thread_for_test(tid_b);
k.handle_syscall(Syscall::Send {
endpoint: cap_b,
message: Message::from_words([77, 0, 0, 0]),
});
// Thread A does WaitAny on the endpoint
k.switch_to_thread_for_test(tid_a);
let wait_set = WaitSet::new(
[Some(cap_a), None, None, None],
[None, None, None, None],
);
let outcome = k.handle_syscall(Syscall::WaitAny { sources: wait_set });
assert!(matches!(
outcome,
SyscallOutcome::Complete(SyscallResult::WakeReason(WakeReason::MessageReady(_)))
));
}
#[test]
fn wait_any_with_no_ready_source_blocks() {
let (mut k, pid_a, _, cap_a, _, _) = setup_ipc_kernel();
let wait_set = WaitSet::new(
[Some(cap_a), None, None, None],
[None, None, None, None],
);
let outcome = k.handle_syscall(Syscall::WaitAny { sources: wait_set });
assert!(matches!(
outcome,
SyscallOutcome::BlockCaller(BlockReason::WaitingAny)
));
}
#[test]
fn wait_any_empty_sources_returns_error() {
let (mut k, _, _, _, _, _) = setup_ipc_kernel();
let wait_set = WaitSet::new(
[None, None, None, None],
[None, None, None, None],
);
let outcome = k.handle_syscall(Syscall::WaitAny { sources: wait_set });
assert!(matches!(
outcome,
SyscallOutcome::Complete(SyscallResult::Error(_))
));
}