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 semaphore::SemaphoreError::Interrupted => SyscallError::Interrupted,
119 })?;
120 HandleKind::Sem(id)
121 } else if let Some(rest) = p.strip_prefix("shm/") {
122 let id = RingId::from_u64(Self::parse_u64(rest)?);
123 let _ = shared_ring::get_ring(id).ok_or(SyscallError::NotFound)?;
124 HandleKind::Ring(id)
125 } else if let Some(rest) = p.strip_prefix("sem/") {
126 let id = SemId::from_u64(Self::parse_u64(rest)?);
127 let _ = semaphore::get_semaphore(id).ok_or(SyscallError::NotFound)?;
128 HandleKind::Sem(id)
129 } else {
130 return Err(SyscallError::NotFound);
131 };
132
133 let (flags, size) = match kind {
134 HandleKind::Root | HandleKind::ShmDir | HandleKind::SemDir => {
135 (FileFlags::DIRECTORY, None)
136 }
137 HandleKind::Ring(id) => {
138 let ring = shared_ring::get_ring(id).ok_or(SyscallError::NotFound)?;
139 (FileFlags::empty(), Some(ring.size() as u64))
140 }
141 HandleKind::Sem(_) => (FileFlags::empty(), Some(0)),
142 };
143 let file_id = self.alloc_handle(kind);
144 Ok(OpenResult {
145 file_id,
146 size,
147 flags,
148 })
149 }
150
151 fn read(&self, file_id: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError> {
153 self.handle_mut(file_id, |state| match state.kind {
154 HandleKind::Root => Ok(Self::read_static(offset, buf, "shm\nsem\n")),
155 HandleKind::ShmDir => Ok(Self::read_static(offset, buf, "new/<bytes>\n<ring_id>\n")),
156 HandleKind::SemDir => Ok(Self::read_static(offset, buf, "new/<initial>\n<sem_id>\n")),
157 HandleKind::Ring(id) => {
158 let ring = shared_ring::get_ring(id).ok_or(SyscallError::NotFound)?;
159 let mut line = alloc::format!(
160 "ring={} size={} pages={}",
161 id.as_u64(),
162 ring.size(),
163 ring.page_count()
164 );
165 if let Some((_, addr, size)) = state.last_map {
166 line.push_str(&alloc::format!(" mapped={:#x} mapped_size={}", addr, size));
167 }
168 line.push('\n');
169 Ok(Self::read_static(offset, buf, &line))
170 }
171 HandleKind::Sem(id) => {
172 let sem = semaphore::get_semaphore(id).ok_or(SyscallError::NotFound)?;
173 let line = alloc::format!("sem={} count={}\n", id.as_u64(), sem.count());
174 Ok(Self::read_static(offset, buf, &line))
175 }
176 })
177 }
178
179 fn write(&self, file_id: u64, _offset: u64, buf: &[u8]) -> Result<usize, SyscallError> {
181 let cmd = core::str::from_utf8(buf)
182 .map_err(|_| SyscallError::InvalidArgument)?
183 .trim();
184
185 self.handle_mut(file_id, |state| match state.kind {
186 HandleKind::Sem(id) => {
187 let sem = semaphore::get_semaphore(id).ok_or(SyscallError::NotFound)?;
188 match cmd {
189 "post" => sem.post().map_err(|_| SyscallError::Pipe)?,
190 "wait" => sem.wait().map_err(|e| match e {
191 semaphore::SemaphoreError::WouldBlock => SyscallError::Again,
192 semaphore::SemaphoreError::Destroyed => SyscallError::Pipe,
193 semaphore::SemaphoreError::InvalidValue => SyscallError::InvalidArgument,
194 semaphore::SemaphoreError::NotFound => SyscallError::NotFound,
195 semaphore::SemaphoreError::Interrupted => SyscallError::Interrupted,
196 })?,
197 "trywait" => sem.try_wait().map_err(|e| match e {
198 semaphore::SemaphoreError::WouldBlock => SyscallError::Again,
199 semaphore::SemaphoreError::Destroyed => SyscallError::Pipe,
200 semaphore::SemaphoreError::InvalidValue => SyscallError::InvalidArgument,
201 semaphore::SemaphoreError::NotFound => SyscallError::NotFound,
202 semaphore::SemaphoreError::Interrupted => SyscallError::Interrupted,
203 })?,
204 _ => return Err(SyscallError::InvalidArgument),
205 }
206 Ok(buf.len())
207 }
208 HandleKind::Ring(id) => {
209 if cmd != "map" {
210 return Err(SyscallError::InvalidArgument);
211 }
212 let ring = shared_ring::get_ring(id).ok_or(SyscallError::NotFound)?;
213 let frame_phys_addrs = ring.frame_phys_addrs();
214 let mapping_cap_ids = ring.mapping_cap_ids().to_vec();
215 let page_count = ring.page_count();
216 let map_size = page_count
217 .checked_mul(4096)
218 .ok_or(SyscallError::InvalidArgument)? as u64;
219
220 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
221 let addr_space = task.process.address_space_arc();
222
223 if let Some((old_pid, old_base, old_size)) = state.last_map.take() {
225 if let Some(old_task) = get_task_by_pid(old_pid) {
226 let old_as = old_task.process.address_space_arc();
227 let _ = old_as.unmap_range(old_base, old_size);
228 }
229 }
230
231 let base = addr_space
232 .find_free_vma_range(crate::kaslr::mmap_base(), page_count, VmaPageSize::Small)
233 .ok_or(SyscallError::OutOfMemory)?;
234 addr_space
235 .map_shared_frames_with_cap_ids(
236 base,
237 &frame_phys_addrs,
238 Some(&mapping_cap_ids),
239 VmaFlags {
240 readable: true,
241 writable: true,
242 executable: false,
243 user_accessible: true,
244 },
245 VmaType::Anonymous,
246 )
247 .map_err(|_| SyscallError::OutOfMemory)?;
248 state.last_map = Some((task.pid, base, map_size));
249 Ok(buf.len())
250 }
251 HandleKind::Root | HandleKind::ShmDir | HandleKind::SemDir => {
252 Err(SyscallError::InvalidArgument)
253 }
254 })
255 }
256
257 fn close(&self, file_id: u64) -> Result<(), SyscallError> {
259 let state = self
260 .handles
261 .lock()
262 .remove(&file_id)
263 .ok_or(SyscallError::BadHandle)?;
264
265 if let Some((pid, base, size)) = state.last_map {
267 if let Some(task) = get_task_by_pid(pid) {
268 let addr_space = task.process.address_space_arc();
269 let _ = addr_space.unmap_range(base, size);
270 }
271 }
272 Ok(())
273 }
274
275 fn unlink(&self, path: &str) -> Result<(), SyscallError> {
277 crate::silo::require_silo_admin()?;
278 let p = path.trim_matches('/');
279 if let Some(rest) = p.strip_prefix("shm/") {
280 let id = RingId::from_u64(Self::parse_u64(rest)?);
281 MultiHandleResource::SharedRing(id)
282 .destroy()
283 .map_err(|_| SyscallError::NotFound)?;
284 return Ok(());
285 }
286 if let Some(rest) = p.strip_prefix("sem/") {
287 let id = SemId::from_u64(Self::parse_u64(rest)?);
288 MultiHandleResource::Semaphore(id)
289 .destroy()
290 .map_err(|_| SyscallError::NotFound)?;
291 return Ok(());
292 }
293 Err(SyscallError::InvalidArgument)
294 }
295
296 fn stat(&self, file_id: u64) -> Result<FileStat, SyscallError> {
298 self.handle_mut(file_id, |state| {
299 let (mode, size) = match state.kind {
300 HandleKind::Root | HandleKind::ShmDir | HandleKind::SemDir => (0o040755, 0),
301 HandleKind::Ring(id) => {
302 let ring = shared_ring::get_ring(id).ok_or(SyscallError::NotFound)?;
303 (0o100660, ring.size() as u64)
304 }
305 HandleKind::Sem(_) => (0o100660, 0),
306 };
307 Ok(finalize_pseudo_stat(
308 FileStat {
309 st_ino: file_id,
310 st_mode: mode,
311 st_nlink: 1,
312 st_size: size,
313 st_blksize: 4096,
314 st_blocks: (size + 511) / 512,
315 ..FileStat::zeroed()
316 },
317 DEV_IPCFS,
318 0,
319 ))
320 })
321 }
322
323 fn readdir(&self, file_id: u64) -> Result<Vec<DirEntry>, SyscallError> {
325 self.handle_mut(file_id, |state| match state.kind {
326 HandleKind::Root => Ok(vec![
327 DirEntry {
328 ino: 1,
329 file_type: DT_DIR,
330 name: "shm".to_string(),
331 },
332 DirEntry {
333 ino: 2,
334 file_type: DT_DIR,
335 name: "sem".to_string(),
336 },
337 ]),
338 HandleKind::ShmDir => Ok(vec![DirEntry {
339 ino: 3,
340 file_type: DT_REG,
341 name: "new".to_string(),
342 }]),
343 HandleKind::SemDir => Ok(vec![DirEntry {
344 ino: 4,
345 file_type: DT_REG,
346 name: "new".to_string(),
347 }]),
348 HandleKind::Ring(_) | HandleKind::Sem(_) => Err(SyscallError::InvalidArgument),
349 })
350 }
351}