Skip to main content

strat9_kernel/vfs/
ipcfs.rs

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    /// Creates a new instance.
40    pub fn new() -> Self {
41        Self {
42            next_file_id: AtomicU64::new(1),
43            handles: SpinLock::new(BTreeMap::new()),
44        }
45    }
46
47    /// Allocates handle.
48    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    /// Parses u64.
61    fn parse_u64(path: &str) -> Result<u64, SyscallError> {
62        path.parse::<u64>()
63            .map_err(|_| SyscallError::InvalidArgument)
64    }
65
66    /// Reads static.
67    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    /// Handles mut.
79    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    /// Performs the open operation.
92    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    /// Performs the read operation.
151    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    /// Performs the write operation.
179    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                // Unmap the previous mapping if any, to avoid leaking VMA space.
221                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    /// Performs the close operation.
259    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        // Unmap shared memory that was mapped into the caller's address space.
267        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    /// Performs the unlink operation.
277    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    /// Performs the stat operation.
298    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    /// Performs the readdir operation.
325    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}