1use super::scheme::{
2 finalize_pseudo_stat, DirEntry, FileFlags, FileStat, OpenFlags, OpenResult, Scheme, DEV_IPCFS,
3 DT_DIR, DT_REG,
4};
5use crate::{
6 ipc::{
7 semaphore::{self, SemId},
8 shared_ring::{self, RingId},
9 },
10 memory::address_space::{VmaFlags, VmaPageSize, VmaType},
11 process::current_task_clone,
12 sync::SpinLock,
13 syscall::error::SyscallError,
14};
15use alloc::{collections::BTreeMap, string::ToString, vec, vec::Vec};
16use core::sync::atomic::{AtomicU64, Ordering};
17
18#[derive(Clone, Copy)]
19enum HandleKind {
20 Root,
21 ShmDir,
22 SemDir,
23 Ring(RingId),
24 Sem(SemId),
25}
26
27struct HandleState {
28 kind: HandleKind,
29 last_map: Option<(u64, u64)>,
30}
31
32pub struct IpcControlScheme {
33 next_file_id: AtomicU64,
34 handles: SpinLock<BTreeMap<u64, HandleState>>,
35}
36
37impl IpcControlScheme {
38 pub fn new() -> Self {
40 Self {
41 next_file_id: AtomicU64::new(1),
42 handles: SpinLock::new(BTreeMap::new()),
43 }
44 }
45
46 fn alloc_handle(&self, kind: HandleKind) -> u64 {
48 let id = self.next_file_id.fetch_add(1, Ordering::Relaxed);
49 self.handles.lock().insert(
50 id,
51 HandleState {
52 kind,
53 last_map: None,
54 },
55 );
56 id
57 }
58
59 fn parse_u64(path: &str) -> Result<u64, SyscallError> {
61 path.parse::<u64>()
62 .map_err(|_| SyscallError::InvalidArgument)
63 }
64
65 fn read_static(offset: u64, out: &mut [u8], s: &str) -> usize {
67 let bytes = s.as_bytes();
68 let start = offset as usize;
69 if start >= bytes.len() {
70 return 0;
71 }
72 let n = core::cmp::min(out.len(), bytes.len() - start);
73 out[..n].copy_from_slice(&bytes[start..start + n]);
74 n
75 }
76
77 fn handle_mut<R>(
79 &self,
80 file_id: u64,
81 f: impl FnOnce(&mut HandleState) -> Result<R, SyscallError>,
82 ) -> Result<R, SyscallError> {
83 let mut h = self.handles.lock();
84 let state = h.get_mut(&file_id).ok_or(SyscallError::BadHandle)?;
85 f(state)
86 }
87}
88
89impl Scheme for IpcControlScheme {
90 fn open(&self, path: &str, _flags: OpenFlags) -> Result<OpenResult, SyscallError> {
92 let p = path.trim_matches('/');
93 let kind = if p.is_empty() {
94 HandleKind::Root
95 } else if p == "shm" {
96 HandleKind::ShmDir
97 } else if p == "sem" {
98 HandleKind::SemDir
99 } else if let Some(rest) = p.strip_prefix("shm/new/") {
100 crate::silo::require_silo_admin()?;
101 let size = Self::parse_u64(rest)? as usize;
102 let id = shared_ring::create_ring(size).map_err(|e| match e {
103 shared_ring::RingError::InvalidSize => SyscallError::InvalidArgument,
104 shared_ring::RingError::Alloc => SyscallError::OutOfMemory,
105 shared_ring::RingError::NotFound => SyscallError::NotFound,
106 })?;
107 HandleKind::Ring(id)
108 } else if let Some(rest) = p.strip_prefix("sem/new/") {
109 crate::silo::require_silo_admin()?;
110 let initial = Self::parse_u64(rest)?;
111 let initial = u32::try_from(initial).map_err(|_| SyscallError::InvalidArgument)?;
112 let id = semaphore::create_semaphore(initial).map_err(|e| match e {
113 semaphore::SemaphoreError::InvalidValue => SyscallError::InvalidArgument,
114 semaphore::SemaphoreError::WouldBlock => SyscallError::Again,
115 semaphore::SemaphoreError::Destroyed => SyscallError::Pipe,
116 semaphore::SemaphoreError::NotFound => SyscallError::NotFound,
117 })?;
118 HandleKind::Sem(id)
119 } else if let Some(rest) = p.strip_prefix("shm/") {
120 let id = RingId::from_u64(Self::parse_u64(rest)?);
121 let _ = shared_ring::get_ring(id).ok_or(SyscallError::NotFound)?;
122 HandleKind::Ring(id)
123 } else if let Some(rest) = p.strip_prefix("sem/") {
124 let id = SemId::from_u64(Self::parse_u64(rest)?);
125 let _ = semaphore::get_semaphore(id).ok_or(SyscallError::NotFound)?;
126 HandleKind::Sem(id)
127 } else {
128 return Err(SyscallError::NotFound);
129 };
130
131 let (flags, size) = match kind {
132 HandleKind::Root | HandleKind::ShmDir | HandleKind::SemDir => {
133 (FileFlags::DIRECTORY, None)
134 }
135 HandleKind::Ring(id) => {
136 let ring = shared_ring::get_ring(id).ok_or(SyscallError::NotFound)?;
137 (FileFlags::empty(), Some(ring.size() as u64))
138 }
139 HandleKind::Sem(_) => (FileFlags::empty(), Some(0)),
140 };
141 let file_id = self.alloc_handle(kind);
142 Ok(OpenResult {
143 file_id,
144 size,
145 flags,
146 })
147 }
148
149 fn read(&self, file_id: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError> {
151 self.handle_mut(file_id, |state| match state.kind {
152 HandleKind::Root => Ok(Self::read_static(offset, buf, "shm\nsem\n")),
153 HandleKind::ShmDir => Ok(Self::read_static(offset, buf, "new/<bytes>\n<ring_id>\n")),
154 HandleKind::SemDir => Ok(Self::read_static(offset, buf, "new/<initial>\n<sem_id>\n")),
155 HandleKind::Ring(id) => {
156 let ring = shared_ring::get_ring(id).ok_or(SyscallError::NotFound)?;
157 let mut line = alloc::format!(
158 "ring={} size={} pages={}",
159 id.as_u64(),
160 ring.size(),
161 ring.page_count()
162 );
163 if let Some((addr, size)) = state.last_map {
164 line.push_str(&alloc::format!(" mapped={:#x} mapped_size={}", addr, size));
165 }
166 line.push('\n');
167 Ok(Self::read_static(offset, buf, &line))
168 }
169 HandleKind::Sem(id) => {
170 let sem = semaphore::get_semaphore(id).ok_or(SyscallError::NotFound)?;
171 let line = alloc::format!("sem={} count={}\n", id.as_u64(), sem.count());
172 Ok(Self::read_static(offset, buf, &line))
173 }
174 })
175 }
176
177 fn write(&self, file_id: u64, _offset: u64, buf: &[u8]) -> Result<usize, SyscallError> {
179 let cmd = core::str::from_utf8(buf)
180 .map_err(|_| SyscallError::InvalidArgument)?
181 .trim();
182
183 self.handle_mut(file_id, |state| match state.kind {
184 HandleKind::Sem(id) => {
185 let sem = semaphore::get_semaphore(id).ok_or(SyscallError::NotFound)?;
186 match cmd {
187 "post" => sem.post().map_err(|_| SyscallError::Pipe)?,
188 "wait" => sem.wait().map_err(|e| match e {
189 semaphore::SemaphoreError::WouldBlock => SyscallError::Again,
190 semaphore::SemaphoreError::Destroyed => SyscallError::Pipe,
191 semaphore::SemaphoreError::InvalidValue => SyscallError::InvalidArgument,
192 semaphore::SemaphoreError::NotFound => SyscallError::NotFound,
193 })?,
194 "trywait" => sem.try_wait().map_err(|e| match e {
195 semaphore::SemaphoreError::WouldBlock => SyscallError::Again,
196 semaphore::SemaphoreError::Destroyed => SyscallError::Pipe,
197 semaphore::SemaphoreError::InvalidValue => SyscallError::InvalidArgument,
198 semaphore::SemaphoreError::NotFound => SyscallError::NotFound,
199 })?,
200 _ => return Err(SyscallError::InvalidArgument),
201 }
202 Ok(buf.len())
203 }
204 HandleKind::Ring(id) => {
205 if cmd != "map" {
206 return Err(SyscallError::InvalidArgument);
207 }
208 let ring = shared_ring::get_ring(id).ok_or(SyscallError::NotFound)?;
209 let frame_phys_addrs = ring.frame_phys_addrs();
210 let page_count = ring.page_count();
211 let map_size = page_count
212 .checked_mul(4096)
213 .ok_or(SyscallError::InvalidArgument)? as u64;
214
215 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
216 let addr_space = unsafe { &*task.process.address_space.get() };
217
218 if let Some((old_base, old_size)) = state.last_map.take() {
220 let _ = addr_space.unmap_range(old_base, old_size);
221 }
222
223 let base = addr_space
224 .find_free_vma_range(
225 crate::syscall::mmap::MMAP_BASE,
226 page_count,
227 VmaPageSize::Small,
228 )
229 .ok_or(SyscallError::OutOfMemory)?;
230 addr_space
231 .map_shared_frames(
232 base,
233 &frame_phys_addrs,
234 VmaFlags {
235 readable: true,
236 writable: true,
237 executable: false,
238 user_accessible: true,
239 },
240 VmaType::Anonymous,
241 )
242 .map_err(|_| SyscallError::OutOfMemory)?;
243 state.last_map = Some((base, map_size));
244 Ok(buf.len())
245 }
246 HandleKind::Root | HandleKind::ShmDir | HandleKind::SemDir => {
247 Err(SyscallError::InvalidArgument)
248 }
249 })
250 }
251
252 fn close(&self, file_id: u64) -> Result<(), SyscallError> {
254 let state = self
255 .handles
256 .lock()
257 .remove(&file_id)
258 .ok_or(SyscallError::BadHandle)?;
259
260 if let Some((base, size)) = state.last_map {
262 if let Some(task) = current_task_clone() {
263 let addr_space = unsafe { &*task.process.address_space.get() };
264 let _ = addr_space.unmap_range(base, size);
265 }
266 }
267 Ok(())
268 }
269
270 fn unlink(&self, path: &str) -> Result<(), SyscallError> {
272 crate::silo::require_silo_admin()?;
273 let p = path.trim_matches('/');
274 if let Some(rest) = p.strip_prefix("shm/") {
275 let id = RingId::from_u64(Self::parse_u64(rest)?);
276 shared_ring::destroy_ring(id).map_err(|_| SyscallError::NotFound)?;
277 return Ok(());
278 }
279 if let Some(rest) = p.strip_prefix("sem/") {
280 let id = SemId::from_u64(Self::parse_u64(rest)?);
281 semaphore::destroy_semaphore(id).map_err(|_| SyscallError::NotFound)?;
282 return Ok(());
283 }
284 Err(SyscallError::InvalidArgument)
285 }
286
287 fn stat(&self, file_id: u64) -> Result<FileStat, SyscallError> {
289 self.handle_mut(file_id, |state| {
290 let (mode, size) = match state.kind {
291 HandleKind::Root | HandleKind::ShmDir | HandleKind::SemDir => (0o040755, 0),
292 HandleKind::Ring(id) => {
293 let ring = shared_ring::get_ring(id).ok_or(SyscallError::NotFound)?;
294 (0o100660, ring.size() as u64)
295 }
296 HandleKind::Sem(_) => (0o100660, 0),
297 };
298 Ok(finalize_pseudo_stat(
299 FileStat {
300 st_ino: file_id,
301 st_mode: mode,
302 st_nlink: 1,
303 st_size: size,
304 st_blksize: 4096,
305 st_blocks: (size + 511) / 512,
306 ..FileStat::zeroed()
307 },
308 DEV_IPCFS,
309 0,
310 ))
311 })
312 }
313
314 fn readdir(&self, file_id: u64) -> Result<Vec<DirEntry>, SyscallError> {
316 self.handle_mut(file_id, |state| match state.kind {
317 HandleKind::Root => Ok(vec![
318 DirEntry {
319 ino: 1,
320 file_type: DT_DIR,
321 name: "shm".to_string(),
322 },
323 DirEntry {
324 ino: 2,
325 file_type: DT_DIR,
326 name: "sem".to_string(),
327 },
328 ]),
329 HandleKind::ShmDir => Ok(vec![DirEntry {
330 ino: 3,
331 file_type: DT_REG,
332 name: "new".to_string(),
333 }]),
334 HandleKind::SemDir => Ok(vec![DirEntry {
335 ino: 4,
336 file_type: DT_REG,
337 name: "new".to_string(),
338 }]),
339 HandleKind::Ring(_) | HandleKind::Sem(_) => Err(SyscallError::InvalidArgument),
340 })
341 }
342}