1use alloc::{
17 collections::BTreeMap,
18 string::{String, ToString},
19};
20use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
21
22use spin::Mutex;
23
24use crate::{
25 error::{KernelError, KernelResult},
26 process::ProcessId,
27};
28
29pub const SHM_NAME_MAX: usize = 255;
35
36pub const SHM_MAX_OBJECTS: usize = 256;
38
39pub const SHM_MAX_SIZE: usize = 256 * 1024 * 1024;
41
42#[derive(Debug, Clone, Copy)]
48pub struct ShmOpenFlags {
49 pub create: bool,
51 pub exclusive: bool,
53 pub read_only: bool,
55}
56
57impl ShmOpenFlags {
58 pub const CREATE_RDWR: Self = Self {
60 create: true,
61 exclusive: false,
62 read_only: false,
63 };
64
65 pub const RDONLY: Self = Self {
67 create: false,
68 exclusive: false,
69 read_only: true,
70 };
71}
72
73#[derive(Debug, Clone)]
75pub struct ShmMapping {
76 pub virt_addr: u64,
78 pub size: usize,
80 pub read_only: bool,
82}
83
84pub struct ShmObject {
86 pub name: String,
88 pub id: u64,
90 pub size: usize,
92 pub phys_frame: usize,
94 pub num_frames: usize,
96 pub ref_count: AtomicU32,
98 pub mappings: Mutex<BTreeMap<u64, ShmMapping>>, pub owner: ProcessId,
102 pub unlinked: bool,
105}
106
107static NEXT_SHM_ID: AtomicU64 = AtomicU64::new(1);
113
114static SHM_REGISTRY: Mutex<BTreeMap<String, ShmObject>> = Mutex::new(BTreeMap::new());
116
117pub fn shm_open(name: &str, flags: ShmOpenFlags, owner: ProcessId) -> KernelResult<u64> {
126 if name.is_empty() || name.len() > SHM_NAME_MAX {
127 return Err(KernelError::InvalidArgument {
128 name: "name",
129 value: "empty or exceeds SHM_NAME_MAX",
130 });
131 }
132
133 let mut registry = SHM_REGISTRY.lock();
134
135 if let Some(existing) = registry.get(name) {
136 if flags.exclusive {
137 return Err(KernelError::AlreadyExists {
138 resource: "shm_object",
139 id: existing.id,
140 });
141 }
142 existing.ref_count.fetch_add(1, Ordering::Relaxed);
143 return Ok(existing.id);
144 }
145
146 if !flags.create {
148 return Err(KernelError::NotFound {
149 resource: "shm_object",
150 id: 0,
151 });
152 }
153
154 if registry.len() >= SHM_MAX_OBJECTS {
155 return Err(KernelError::ResourceExhausted {
156 resource: "shm_objects",
157 });
158 }
159
160 let id = NEXT_SHM_ID.fetch_add(1, Ordering::Relaxed);
161 let obj = ShmObject {
162 name: name.to_string(),
163 id,
164 size: 0,
165 phys_frame: 0,
166 num_frames: 0,
167 ref_count: AtomicU32::new(1),
168 mappings: Mutex::new(BTreeMap::new()),
169 owner,
170 unlinked: false,
171 };
172
173 registry.insert(name.to_string(), obj);
174
175 println!("[SHM] Created shared memory object '{}' (id={})", name, id);
176 Ok(id)
177}
178
179pub fn shm_unlink(name: &str) -> KernelResult<()> {
185 let mut registry = SHM_REGISTRY.lock();
186
187 if let Some(obj) = registry.get_mut(name) {
188 obj.unlinked = true;
189 let refs = obj.ref_count.load(Ordering::Relaxed);
190 if refs == 0 {
191 if obj.num_frames > 0 {
193 let frame = crate::mm::FrameNumber::new(obj.phys_frame as u64);
194 let _ = crate::mm::FRAME_ALLOCATOR
195 .lock()
196 .free_frames(frame, obj.num_frames);
197 }
198 registry.remove(name);
199 println!("[SHM] Unlinked and destroyed '{}'", name);
200 } else {
201 println!(
202 "[SHM] Unlinked '{}' (deferred destroy, {} refs remaining)",
203 name, refs
204 );
205 }
206 Ok(())
207 } else {
208 Err(KernelError::NotFound {
209 resource: "shm_object",
210 id: 0,
211 })
212 }
213}
214
215pub fn shm_truncate(name: &str, size: usize) -> KernelResult<()> {
220 if size > SHM_MAX_SIZE {
221 return Err(KernelError::InvalidArgument {
222 name: "size",
223 value: "exceeds SHM_MAX_SIZE",
224 });
225 }
226
227 let mut registry = SHM_REGISTRY.lock();
228 let obj = registry.get_mut(name).ok_or(KernelError::NotFound {
229 resource: "shm_object",
230 id: 0,
231 })?;
232
233 if obj.num_frames > 0 {
235 let frame = crate::mm::FrameNumber::new(obj.phys_frame as u64);
236 let _ = crate::mm::FRAME_ALLOCATOR
237 .lock()
238 .free_frames(frame, obj.num_frames);
239 obj.phys_frame = 0;
240 obj.num_frames = 0;
241 }
242
243 if size == 0 {
244 obj.size = 0;
245 return Ok(());
246 }
247
248 let num_frames = size.div_ceil(4096);
250 let frame = crate::mm::FRAME_ALLOCATOR
251 .lock()
252 .allocate_frames(num_frames, None)
253 .map_err(|_| KernelError::OutOfMemory {
254 requested: size,
255 available: 0,
256 })?;
257
258 let phys_addr = frame.as_u64() * 4096;
260 let virt_addr = crate::mm::phys_to_virt_addr(phys_addr);
261 unsafe {
263 core::ptr::write_bytes(virt_addr as *mut u8, 0, num_frames * 4096);
264 }
265
266 obj.phys_frame = frame.as_u64() as usize;
267 obj.num_frames = num_frames;
268 obj.size = size;
269
270 println!(
271 "[SHM] Truncated '{}' to {} bytes ({} frames)",
272 name, size, num_frames
273 );
274 Ok(())
275}
276
277pub fn shm_close(name: &str) -> KernelResult<()> {
282 let mut registry = SHM_REGISTRY.lock();
283 let should_destroy = if let Some(obj) = registry.get(name) {
284 let prev = obj.ref_count.fetch_sub(1, Ordering::Release);
285 prev == 1 && obj.unlinked
286 } else {
287 return Ok(());
288 };
289
290 if should_destroy {
291 if let Some(obj) = registry.remove(name) {
292 if obj.num_frames > 0 {
293 let frame = crate::mm::FrameNumber::new(obj.phys_frame as u64);
294 let _ = crate::mm::FRAME_ALLOCATOR
295 .lock()
296 .free_frames(frame, obj.num_frames);
297 }
298 println!("[SHM] Destroyed '{}' (last reference closed)", name);
299 }
300 }
301 Ok(())
302}
303
304pub fn shm_stat(name: &str) -> KernelResult<(u64, usize, u32)> {
306 let registry = SHM_REGISTRY.lock();
307 let obj = registry.get(name).ok_or(KernelError::NotFound {
308 resource: "shm_object",
309 id: 0,
310 })?;
311 Ok((obj.id, obj.size, obj.ref_count.load(Ordering::Relaxed)))
312}
313
314pub fn shm_count() -> usize {
316 SHM_REGISTRY.lock().len()
317}