Next Generation WASM Microkernel Operating System
1// Copyright 2025 Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use crate::state::cpu_local;
9use alloc::sync::Arc;
10use core::num::NonZero;
11use hashbrown::HashMap;
12use kasync::sync::wait_queue::WaitQueue;
13use spin::{LazyLock, RwLock};
14
15pub trait InterruptController {
16 fn irq_claim(&mut self) -> Option<IrqClaim>;
17 fn irq_complete(&mut self, claim: IrqClaim);
18 fn irq_mask(&mut self, irq_num: u32);
19 fn irq_unmask(&mut self, irq_num: u32);
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub struct IrqClaim(NonZero<u32>);
24
25impl IrqClaim {
26 pub unsafe fn from_raw(raw: NonZero<u32>) -> Self {
27 Self(raw)
28 }
29 pub fn as_u32(self) -> u32 {
30 self.0.get()
31 }
32}
33
34// hashbrown doesn't have a good const constructor, therefore the `LazyLock`
35static QUEUES: LazyLock<RwLock<HashMap<u32, Arc<WaitQueue>>>> =
36 LazyLock::new(|| RwLock::new(HashMap::new()));
37
38pub fn trigger_irq(irq_ctl: &mut dyn InterruptController) {
39 let Some(claim) = irq_ctl.irq_claim() else {
40 // Spurious interrupt
41 return;
42 };
43
44 // acknowledge the interrupt as fast as possible
45 irq_ctl.irq_complete(claim);
46
47 let queues = QUEUES.read();
48 if let Some(queue) = queues.get(&claim.as_u32()) {
49 tracing::trace!("waking wakers for irq-{}", claim.as_u32());
50 let woken = queue.wake_all();
51 tracing::trace!("woke {woken} wakers for irq-{}", claim.as_u32());
52 }
53}
54
55pub async fn next_event(irq_num: u32) -> Result<(), kasync::Closed> {
56 cpu_local()
57 .arch
58 .cpu
59 .interrupt_controller()
60 .irq_unmask(irq_num);
61
62 let wait = {
63 let mut queues = QUEUES.write();
64 let wait = queues
65 .entry(irq_num)
66 .or_insert_with(|| Arc::new(WaitQueue::new()))
67 .wait_owned();
68 // don't hold the RwLock guard across the await point
69 drop(queues);
70 wait
71 };
72
73 let res = wait.await;
74
75 cpu_local()
76 .arch
77 .cpu
78 .interrupt_controller()
79 .irq_mask(irq_num);
80
81 res
82}