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                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    /// Performs the read operation.
152    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    /// Performs the write operation.
180    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                // Unmap the previous mapping if any, to avoid leaking VMA space.
224                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    /// Performs the close operation.
258    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        // Unmap shared memory that was mapped into the caller's address space.
266        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    /// Performs the unlink operation.
276    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    /// Performs the stat operation.
297    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    /// Performs the readdir operation.
324    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}