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