Nothing to see here, move along
at main 186 lines 5.9 kB view raw
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}