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    },
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    /// Creates a new instance.
39    pub fn new() -> Self {
40        Self {
41            next_file_id: AtomicU64::new(1),
42            handles: SpinLock::new(BTreeMap::new()),
43        }
44    }
45
46    /// Allocates handle.
47    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    /// Parses u64.
60    fn parse_u64(path: &str) -> Result<u64, SyscallError> {
61        path.parse::<u64>()
62            .map_err(|_| SyscallError::InvalidArgument)
63    }
64
65    /// Reads static.
66    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    /// Handles mut.
78    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    /// Performs the open operation.
91    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    /// Performs the read operation.
150    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    /// Performs the write operation.
178    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                // Unmap the previous mapping if any, to avoid leaking VMA space.
219                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    /// Performs the close operation.
253    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        // Unmap shared memory that was mapped into the caller's address space.
261        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    /// Performs the unlink operation.
271    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    /// Performs the stat operation.
288    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    /// Performs the readdir operation.
315    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}