1#![allow(dead_code, clippy::explicit_auto_deref)]
7
8#[cfg(feature = "alloc")]
9extern crate alloc;
10
11#[cfg(feature = "alloc")]
12use alloc::collections::BTreeMap;
13use core::sync::atomic::{AtomicU64, Ordering};
14
15use spin::Mutex;
16
17use super::{
18 capability::{EndpointId, IpcCapability, IpcPermissions, ProcessId},
19 channel::{Channel, Endpoint},
20 error::{IpcError, Result},
21 Message,
22};
23
24static IPC_REGISTRY: crate::sync::once_lock::OnceLock<Mutex<IpcRegistry>> =
26 crate::sync::once_lock::OnceLock::new();
27
28pub fn init() {
30 #[allow(unused_imports)]
31 use crate::println;
32
33 println!("[IPC-REG] Initializing IPC registry...");
34 let registry = IpcRegistry::new();
35 let registry_mutex = Mutex::new(registry);
36
37 match IPC_REGISTRY.set(registry_mutex) {
38 Ok(()) => println!("[IPC-REG] Registry initialized successfully"),
39 Err(_) => println!("[IPC-REG] Registry already initialized, skipping..."),
40 }
41}
42
43pub struct IpcRegistry {
45 #[cfg(feature = "alloc")]
47 endpoints: BTreeMap<EndpointId, Endpoint>,
48 #[cfg(feature = "alloc")]
50 channels: BTreeMap<EndpointId, Channel>,
51 #[cfg(feature = "alloc")]
53 process_endpoints: BTreeMap<ProcessId, BTreeMap<EndpointId, IpcCapability>>,
54 next_endpoint_id: AtomicU64,
56 stats: RegistryStats,
58}
59
60struct RegistryStats {
62 endpoints_created: AtomicU64,
63 endpoints_destroyed: AtomicU64,
64 channels_created: AtomicU64,
65 channels_destroyed: AtomicU64,
66 capability_lookups: AtomicU64,
67 capability_hits: AtomicU64,
68}
69
70impl IpcRegistry {
71 fn new() -> Self {
73 Self {
74 #[cfg(feature = "alloc")]
75 endpoints: BTreeMap::new(),
76 #[cfg(feature = "alloc")]
77 channels: BTreeMap::new(),
78 #[cfg(feature = "alloc")]
79 process_endpoints: BTreeMap::new(),
80 next_endpoint_id: AtomicU64::new(1),
81 stats: RegistryStats {
82 endpoints_created: AtomicU64::new(0),
83 endpoints_destroyed: AtomicU64::new(0),
84 channels_created: AtomicU64::new(0),
85 channels_destroyed: AtomicU64::new(0),
86 capability_lookups: AtomicU64::new(0),
87 capability_hits: AtomicU64::new(0),
88 },
89 }
90 }
91
92 #[cfg(feature = "alloc")]
94 pub fn create_endpoint(&mut self, owner: ProcessId) -> Result<(EndpointId, IpcCapability)> {
95 let endpoint_id = self.next_endpoint_id.fetch_add(1, Ordering::Relaxed);
96 let endpoint = Endpoint::new(owner);
97
98 let capability = IpcCapability::new(endpoint_id, IpcPermissions::all());
100
101 self.endpoints.insert(endpoint_id, endpoint);
103
104 self.process_endpoints
106 .entry(owner)
107 .or_default()
108 .insert(endpoint_id, capability);
109
110 self.stats.endpoints_created.fetch_add(1, Ordering::Relaxed);
111
112 Ok((endpoint_id, capability))
113 }
114
115 #[cfg(not(feature = "alloc"))]
116 pub fn create_endpoint(&mut self, _owner: ProcessId) -> Result<(EndpointId, IpcCapability)> {
117 Err(IpcError::OutOfMemory)
118 }
119
120 #[cfg(feature = "alloc")]
122 pub fn create_channel(
123 &mut self,
124 owner: ProcessId,
125 capacity: usize,
126 ) -> Result<(EndpointId, EndpointId, IpcCapability, IpcCapability)> {
127 let channel = Channel::new(owner, capacity);
128 let send_id = channel.send_id();
129 let recv_id = channel.receive_id();
130
131 let send_cap = IpcCapability::new(send_id, IpcPermissions::send_only());
133 let recv_cap = IpcCapability::new(recv_id, IpcPermissions::receive_only());
134
135 self.channels.insert(send_id, channel);
137
138 let process_eps = self.process_endpoints.entry(owner).or_default();
140 process_eps.insert(send_id, send_cap);
141 process_eps.insert(recv_id, recv_cap);
142
143 self.stats.channels_created.fetch_add(1, Ordering::Relaxed);
144
145 Ok((send_id, recv_id, send_cap, recv_cap))
146 }
147
148 #[cfg(not(feature = "alloc"))]
149 pub fn create_channel(
150 &mut self,
151 _owner: ProcessId,
152 _capacity: usize,
153 ) -> Result<(EndpointId, EndpointId, IpcCapability, IpcCapability)> {
154 Err(IpcError::OutOfMemory)
155 }
156
157 #[cfg(feature = "alloc")]
159 pub fn lookup_endpoint(&self, id: EndpointId) -> Option<&Endpoint> {
160 self.stats
161 .capability_lookups
162 .fetch_add(1, Ordering::Relaxed);
163
164 if let Some(endpoint) = self.endpoints.get(&id) {
165 self.stats.capability_hits.fetch_add(1, Ordering::Relaxed);
166 Some(endpoint)
167 } else {
168 None
169 }
170 }
171
172 #[cfg(not(feature = "alloc"))]
173 pub fn lookup_endpoint(&self, _id: EndpointId) -> Option<&Endpoint> {
174 None
175 }
176
177 #[cfg(feature = "alloc")]
180 pub fn validate_capability(
181 &self,
182 process: ProcessId,
183 capability: &IpcCapability,
184 ) -> Result<()> {
185 self.stats
186 .capability_lookups
187 .fetch_add(1, Ordering::Relaxed);
188
189 if let Some(process_caps) = self.process_endpoints.get(&process) {
191 if let Some(stored_cap) = process_caps.get(&capability.target()) {
192 if stored_cap.generation() == capability.generation() {
194 self.stats.capability_hits.fetch_add(1, Ordering::Relaxed);
195
196 if let Some(real_process) = crate::process::table::get_process(process) {
198 let cap_space = real_process.capability_space.lock();
199 let cap_token = crate::cap::CapabilityToken::from_u64(capability.id());
200 if cap_space.lookup(cap_token).is_some() {
201 return Ok(());
202 }
203 } else {
205 return Ok(());
207 }
208 }
209 }
210 }
211
212 Err(IpcError::InvalidCapability)
213 }
214
215 #[cfg(not(feature = "alloc"))]
216 pub fn validate_capability(
217 &self,
218 _process: ProcessId,
219 _capability: &IpcCapability,
220 ) -> Result<()> {
221 Err(IpcError::InvalidCapability)
222 }
223
224 #[cfg(feature = "alloc")]
226 pub fn remove_endpoint(&mut self, id: EndpointId, owner: ProcessId) -> Result<()> {
227 if let Some(endpoint) = self.endpoints.get(&id) {
229 if endpoint.owner != owner {
230 return Err(IpcError::PermissionDenied);
231 }
232 } else {
233 return Err(IpcError::EndpointNotFound);
234 }
235
236 self.endpoints.remove(&id);
238
239 if let Some(process_eps) = self.process_endpoints.get_mut(&owner) {
241 process_eps.remove(&id);
242 }
243
244 self.stats
245 .endpoints_destroyed
246 .fetch_add(1, Ordering::Relaxed);
247
248 Ok(())
249 }
250
251 #[cfg(not(feature = "alloc"))]
252 pub fn remove_endpoint(&mut self, _id: EndpointId, _owner: ProcessId) -> Result<()> {
253 Err(IpcError::EndpointNotFound)
254 }
255
256 pub fn get_stats(&self) -> RegistryStatsSummary {
258 RegistryStatsSummary {
259 endpoints_created: self.stats.endpoints_created.load(Ordering::Relaxed),
260 endpoints_destroyed: self.stats.endpoints_destroyed.load(Ordering::Relaxed),
261 channels_created: self.stats.channels_created.load(Ordering::Relaxed),
262 channels_destroyed: self.stats.channels_destroyed.load(Ordering::Relaxed),
263 capability_lookups: self.stats.capability_lookups.load(Ordering::Relaxed),
264 capability_hits: self.stats.capability_hits.load(Ordering::Relaxed),
265 cache_hit_rate: {
266 let lookups = self.stats.capability_lookups.load(Ordering::Relaxed);
267 let hits = self.stats.capability_hits.load(Ordering::Relaxed);
268 if lookups > 0 {
269 (hits * 100) / lookups
270 } else {
271 0
272 }
273 },
274 }
275 }
276}
277
278pub struct RegistryStatsSummary {
280 pub endpoints_created: u64,
281 pub endpoints_destroyed: u64,
282 pub channels_created: u64,
283 pub channels_destroyed: u64,
284 pub capability_lookups: u64,
285 pub capability_hits: u64,
286 pub cache_hit_rate: u64,
287}
288
289fn with_registry_mut<T, F>(f: F) -> Result<T>
292where
293 F: FnOnce(&mut IpcRegistry) -> Result<T>,
294{
295 let registry_mutex = IPC_REGISTRY.get().ok_or(IpcError::NotInitialized)?;
296 let mut registry_guard = registry_mutex.lock();
297 f(&mut *registry_guard)
298}
299
300fn with_registry<T, F>(f: F) -> Result<T>
302where
303 F: FnOnce(&IpcRegistry) -> Result<T>,
304{
305 let registry_mutex = IPC_REGISTRY.get().ok_or(IpcError::NotInitialized)?;
306 let registry_guard = registry_mutex.lock();
307 f(&*registry_guard)
308}
309
310pub fn create_endpoint(owner: ProcessId) -> Result<(EndpointId, IpcCapability)> {
313 with_registry_mut(|registry| registry.create_endpoint(owner))
314}
315
316pub fn create_channel(
318 owner: ProcessId,
319 capacity: usize,
320) -> Result<(EndpointId, EndpointId, IpcCapability, IpcCapability)> {
321 with_registry_mut(|registry| registry.create_channel(owner, capacity))
322}
323
324pub fn remove_channel(channel_id: u64) -> Result<()> {
326 with_registry_mut(|registry| {
327 let endpoint_id = channel_id;
330
331 #[cfg(feature = "alloc")]
333 {
334 if registry.channels.remove(&endpoint_id).is_some() {
335 registry
336 .stats
337 .channels_destroyed
338 .fetch_add(1, Ordering::Relaxed);
339 Ok(())
340 } else {
341 if registry.endpoints.remove(&endpoint_id).is_some() {
343 registry
344 .stats
345 .endpoints_destroyed
346 .fetch_add(1, Ordering::Relaxed);
347 Ok(())
348 } else {
349 Err(IpcError::EndpointNotFound)
350 }
351 }
352 }
353
354 #[cfg(not(feature = "alloc"))]
355 Err(IpcError::EndpointNotFound)
356 })
357}
358
359pub fn remove_process_endpoints(owner: ProcessId) -> Result<usize> {
361 with_registry_mut(|registry| {
362 #[cfg(feature = "alloc")]
363 {
364 let endpoint_ids: alloc::vec::Vec<EndpointId> = registry
366 .process_endpoints
367 .get(&owner)
368 .map(|eps| eps.keys().cloned().collect())
369 .unwrap_or_default();
370
371 let mut removed_count = 0;
372
373 for endpoint_id in &endpoint_ids {
375 if registry.endpoints.remove(endpoint_id).is_some() {
377 registry
378 .stats
379 .endpoints_destroyed
380 .fetch_add(1, Ordering::Relaxed);
381 removed_count += 1;
382 }
383
384 if registry.channels.remove(endpoint_id).is_some() {
386 registry
387 .stats
388 .channels_destroyed
389 .fetch_add(1, Ordering::Relaxed);
390 }
391 }
392
393 registry.process_endpoints.remove(&owner);
395
396 Ok(removed_count)
397 }
398
399 #[cfg(not(feature = "alloc"))]
400 {
401 let _ = owner;
402 Ok(0)
403 }
404 })
405}
406
407pub fn lookup_endpoint(id: EndpointId) -> Result<&'static Endpoint> {
409 with_registry(|registry| {
410 unsafe {
420 let registry_ptr = registry as *const IpcRegistry;
421 (*registry_ptr)
422 .lookup_endpoint(id)
423 .ok_or(IpcError::EndpointNotFound)
424 .map(|ep| &*(ep as *const Endpoint))
425 }
426 })
427}
428
429pub fn try_receive_from_endpoint(endpoint_id: u64) -> Option<Message> {
435 with_registry(|registry| {
436 Ok(if let Some(ep) = registry.lookup_endpoint(endpoint_id) {
437 ep.try_receive().ok()
438 } else {
439 None
440 })
441 })
442 .ok()
443 .flatten()
444}
445
446pub fn validate_capability(process: ProcessId, capability: &IpcCapability) -> Result<()> {
448 with_registry(|registry| registry.validate_capability(process, capability))
449}
450
451pub fn get_registry_stats() -> Result<RegistryStatsSummary> {
453 with_registry(|registry| Ok(registry.get_stats()))
454}
455
456#[cfg(all(test, not(target_os = "none")))]
457mod tests {
458 use super::*;
459 use crate::process::ProcessId;
460
461 #[test]
462 fn test_registry_init() {
463 init();
464 let result = create_endpoint(ProcessId(1));
465 assert!(result.is_ok());
466 }
467
468 #[test]
469 fn test_endpoint_creation() {
470 init();
471 let (id, cap) = create_endpoint(ProcessId(1)).unwrap();
472 assert_eq!(cap.target(), id);
473 assert!(cap.has_permission(super::super::capability::Permission::Send));
474 }
475
476 #[test]
477 fn test_channel_creation() {
478 init();
479 let (send_id, recv_id, send_cap, recv_cap) = create_channel(ProcessId(1), 100).unwrap();
480 assert_ne!(send_id, recv_id);
481 assert!(send_cap.has_permission(super::super::capability::Permission::Send));
482 assert!(!send_cap.has_permission(super::super::capability::Permission::Receive));
483 assert!(recv_cap.has_permission(super::super::capability::Permission::Receive));
484 assert!(!recv_cap.has_permission(super::super::capability::Permission::Send));
485 }
486}