Nothing to see here, move along
1use crate::cap::pool::{ObjectPool, POOL};
2use crate::cap::table::CapRef;
3use crate::error::KernelError;
4use crate::ipc::{IpcOutcome, IpcResult};
5use crate::proc::{BlockedReason, ProcessManager};
6use crate::types::{Generation, MAX_PIDS, ObjPhys, Pid};
7use lancer_core::header::NONE_SENTINEL;
8use lancer_core::object_layout::{EndpointObject, NotificationObject};
9
10pub const BOUND_NOTIFICATION_BADGE: u64 = (-128i64) as u64;
11
12pub type SignalResult = Result<bool, KernelError>;
13pub type PollResult = Result<u64, KernelError>;
14
15const MAX_NOTIFICATION_WAITERS: usize = 4;
16
17fn has_waiters(notif: &NotificationObject) -> bool {
18 notif.waiter_count > 0
19}
20
21pub fn signal_inner(notif: &mut NotificationObject, bits: u64) -> bool {
22 debug_assert!(
23 !x86_64::instructions::interrupts::are_enabled(),
24 "signal_inner requires interrupts disabled"
25 );
26 notif.word |= bits;
27 has_waiters(notif)
28}
29
30pub fn drain_and_wake(notif: &mut NotificationObject, ptable: &mut ProcessManager) {
31 let woken_word = notif.word;
32 let mut failed_count = 0u8;
33 let mut failed_pids = [NONE_SENTINEL; MAX_NOTIFICATION_WAITERS];
34
35 (0..notif.waiter_count as usize).for_each(|i| {
36 let raw = notif.waiters[i];
37 match Pid::try_new(raw) {
38 Some(pid) => {
39 let proof = ptable[pid].blocked_proof();
40 match ptable.unblock_and_enqueue(pid, proof) {
41 Ok(()) => {
42 let exec = ptable.exec_mut(pid).unwrap();
43 exec.saved_context.rax = 0;
44 exec.saved_context.rdx = woken_word;
45 exec.seal_context();
46 }
47 Err(e) => {
48 crate::kprintln!(
49 "[notify] BUG: failed to unblock waiter pid {}: {:?}",
50 pid.raw(),
51 e
52 );
53 failed_pids[failed_count as usize] = raw;
54 failed_count += 1;
55 }
56 }
57 }
58 None => {}
59 }
60 });
61
62 notif.waiter_count = failed_count;
63 (0..MAX_NOTIFICATION_WAITERS).for_each(|i| {
64 notif.waiters[i] = match i < failed_count as usize {
65 true => failed_pids[i],
66 false => NONE_SENTINEL,
67 };
68 });
69 if failed_count == 0 {
70 notif.word = 0;
71 }
72}
73
74pub fn do_signal(cap: &CapRef, bits: u64, ptable: &mut ProcessManager) -> SignalResult {
75 let notif_id = cap.phys();
76 let notif_gen = cap.generation();
77
78 let mut woke_any = false;
79
80 {
81 let mut pool = POOL.lock();
82 let notif = pool.write_as::<NotificationObject>(notif_id, notif_gen)?;
83
84 if signal_inner(notif, bits) {
85 woke_any = true;
86 drain_and_wake(notif, ptable);
87 }
88 }
89
90 let mut pool = POOL.lock();
91 if wake_bound_receivers_with_pool(notif_id, notif_gen, bits, ptable, &mut pool) {
92 woke_any = true;
93 }
94
95 Ok(woke_any)
96}
97
98pub fn do_wait(cap: &CapRef, pid: Pid, ptable: &mut ProcessManager) -> IpcResult<u64> {
99 let mut pool = POOL.lock();
100 let notif = pool.write_as::<NotificationObject>(cap.phys(), cap.generation())?;
101
102 if notif.word != 0 {
103 let val = notif.word;
104 notif.word = 0;
105 Ok(IpcOutcome::Done(val))
106 } else {
107 let phys = cap.phys();
108 let proof = ptable[pid].block_on(BlockedReason::WaitingNotification(
109 phys,
110 cap.generation(),
111 ))?;
112 match (notif.waiter_count as usize) < MAX_NOTIFICATION_WAITERS {
113 true => {
114 notif.waiters[notif.waiter_count as usize] = pid.raw();
115 notif.waiter_count += 1;
116 Ok(IpcOutcome::Blocked)
117 }
118 false => {
119 ptable.unblock_and_enqueue(pid, proof)?;
120 Err(KernelError::ResourceExhausted)
121 }
122 }
123 }
124}
125
126pub fn do_poll(cap: &CapRef) -> PollResult {
127 let mut pool = POOL.lock();
128 let notif = pool.write_as::<NotificationObject>(cap.phys(), cap.generation())?;
129
130 let val = notif.word;
131 notif.word = 0;
132 Ok(val)
133}
134
135pub fn wake_bound_receivers_with_pool(
136 notif_id: ObjPhys,
137 notif_gen: Generation,
138 woken_word: u64,
139 ptable: &mut ProcessManager,
140 pool: &mut ObjectPool,
141) -> bool {
142 (0..MAX_PIDS as u32).fold(false, |woke, idx| {
143 let pid = Pid::new(idx);
144 let Some(sched) = ptable.get(pid) else {
145 return woke;
146 };
147 let matches_bound = sched
148 .bound_notification()
149 .is_some_and(|(id, g)| id == notif_id && g == notif_gen);
150 if !matches_bound {
151 return woke;
152 }
153 let is_receiving = matches!(sched.blocked_reason(), Some(BlockedReason::Receiving(_, _)));
154 if !is_receiving {
155 return woke;
156 }
157
158 let (ep_id, ep_gen) = match sched.blocked_reason() {
159 Some(BlockedReason::Receiving(id, generation)) => (id, generation),
160 _ => return woke,
161 };
162
163 let proof = ptable[pid].blocked_proof();
164 match ptable.unblock_and_enqueue(pid, proof) {
165 Ok(()) => {
166 let exec = ptable.exec_mut(pid).unwrap();
167 exec.saved_context.rax = BOUND_NOTIFICATION_BADGE;
168 exec.saved_context.rdx = woken_word;
169 exec.seal_context();
170
171 if let Ok(ep) = pool.write_as::<EndpointObject>(ep_id, ep_gen) {
172 super::endpoint::remove_from_recv(ep, pid, ptable);
173 }
174 true
175 }
176 Err(e) => {
177 crate::kprintln!(
178 "[notify] BUG: failed to unblock bound receiver pid {}: {:?}",
179 pid.raw(),
180 e
181 );
182 woke
183 }
184 }
185 })
186}