Skip to main content

strat9_kernel/ipc/
shared_ring.rs

1use crate::{memory::PhysFrame, sync::SpinLock};
2use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
3use core::sync::atomic::{AtomicU64, Ordering};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
6pub struct RingId(pub u64);
7
8impl RingId {
9    /// Returns this as u64.
10    pub fn as_u64(self) -> u64 {
11        self.0
12    }
13    /// Builds this from u64.
14    pub fn from_u64(raw: u64) -> Self {
15        Self(raw)
16    }
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
20pub enum RingError {
21    #[error("invalid size")]
22    InvalidSize,
23    #[error("allocation failed")]
24    Alloc,
25    #[error("ring not found")]
26    NotFound,
27}
28
29pub struct SharedRing {
30    size: usize,
31    frames: Vec<PhysFrame>,
32}
33
34impl SharedRing {
35    /// Performs the size operation.
36    pub fn size(&self) -> usize {
37        self.size
38    }
39
40    /// Performs the page count operation.
41    pub fn page_count(&self) -> usize {
42        self.frames.len()
43    }
44
45    /// Performs the frame phys addrs operation.
46    pub fn frame_phys_addrs(&self) -> Vec<u64> {
47        self.frames
48            .iter()
49            .map(|f| f.start_address.as_u64())
50            .collect()
51    }
52}
53
54impl Drop for SharedRing {
55    /// Performs the drop operation.
56    fn drop(&mut self) {
57        for frame in self.frames.drain(..) {
58            crate::memory::cow::frame_dec_ref(frame);
59        }
60    }
61}
62
63static NEXT_RING_ID: AtomicU64 = AtomicU64::new(1);
64static RINGS: SpinLock<Option<BTreeMap<RingId, Arc<SharedRing>>>> = SpinLock::new(None);
65
66/// Performs the ensure registry operation.
67fn ensure_registry(guard: &mut Option<BTreeMap<RingId, Arc<SharedRing>>>) {
68    if guard.is_none() {
69        *guard = Some(BTreeMap::new());
70    }
71}
72
73/// Creates ring.
74pub fn create_ring(size: usize) -> Result<RingId, RingError> {
75    if size == 0 {
76        return Err(RingError::InvalidSize);
77    }
78    let page_count = (size.saturating_add(4095)) / 4096;
79    if page_count == 0 {
80        return Err(RingError::InvalidSize);
81    }
82
83    let mut frames = Vec::with_capacity(page_count);
84    let mut alloc_failed = false;
85    for _ in 0..page_count {
86        let frame = match crate::sync::with_irqs_disabled(|token| {
87            crate::memory::allocate_frame(token)
88        }) {
89            Ok(f) => f,
90            Err(_) => {
91                alloc_failed = true;
92                break;
93            }
94        };
95        let v = crate::memory::phys_to_virt(frame.start_address.as_u64());
96        unsafe { core::ptr::write_bytes(v as *mut u8, 0, 4096) };
97        crate::memory::cow::frame_inc_ref(frame);
98        frames.push(frame);
99    }
100    if alloc_failed {
101        for rollback in frames.drain(..) {
102            crate::memory::cow::frame_dec_ref(rollback);
103        }
104        return Err(RingError::Alloc);
105    }
106
107    let id = RingId(NEXT_RING_ID.fetch_add(1, Ordering::Relaxed));
108    let ring = Arc::new(SharedRing { size, frames });
109
110    let mut reg = RINGS.lock();
111    ensure_registry(&mut *reg);
112    reg.as_mut().unwrap().insert(id, ring);
113    Ok(id)
114}
115
116/// Returns ring.
117pub fn get_ring(id: RingId) -> Option<Arc<SharedRing>> {
118    let reg = RINGS.lock();
119    reg.as_ref().and_then(|map| map.get(&id).cloned())
120}
121
122/// Destroys ring.
123pub fn destroy_ring(id: RingId) -> Result<(), RingError> {
124    let mut reg = RINGS.lock();
125    let map = reg.as_mut().ok_or(RingError::NotFound)?;
126
127    // ==========================================================================
128    // CRITICAL: BTreeMap corruption guard
129    //
130    // A corrupted heap can produce a BTreeMap whose internal node pointers are
131    // invalid (e.g. NULL + offset 16 = 0x10), causing remove() to page-fault.
132    // Sanity-check `len()` before mutating: the ring registry never holds more
133    // than a few hundred entries under normal operation.  An absurd value
134    // indicates that the BTreeMap header itself has been overwritten, and
135    // calling remove() would immediately dereference a bad node pointer.
136    //
137    // In that case we bail early rather than crash.  The heap poison detector
138    // in heap.rs will identify the corrupting allocation on the next alloc/free.
139    // ==========================================================================
140    let len = map.len();
141    if len > 10_000 {
142        crate::serial_println!(
143            "\x1b[1;31m[ipc] RINGS BTreeMap corrupted: len={} for id={} \u{2014} aborting remove\x1b[0m",
144            len, id.as_u64()
145        );
146        return Err(RingError::NotFound);
147    }
148    crate::serial_println!("[ipc] destroy_ring(id={}) map.len={}", id.as_u64(), len);
149
150    map.remove(&id).ok_or(RingError::NotFound)?;
151    Ok(())
152}