Skip to main content

strat9_kernel/syscall/
dispatcher.rs

1//! Strat9-OS syscall dispatcher
2//!
3//! Routes syscall numbers to handler functions and converts results to RAX values.
4//! Called from the naked `syscall_entry` assembly with a pointer to `SyscallFrame`.
5//!
6//! The main dispatch function is `__strat9_syscall_dispatch`, which matches on
7//! the syscall number and calls the appropriate handler. Each handler returns a
8//! `Result<u64, SyscallError>`, which is converted to a raw value in RAX.
9//!
10//! Handler implementations live in sibling modules (`net`, `volume`, `ipc_port`,
11//! `ipc_ring`, `semaphore`, `pci`, `chan`, `debug`, etc.). Only the routing
12//! logic and a handful of capability / process / file-io helpers remain here.
13//
14//
15
16use crate::ipc::{channel, port, semaphore, shared_ring, ChanId, PortId, RingId, SemId};
17
18use super::{
19    chan, debug, error::SyscallError, exec::sys_execve, fork::sys_fork, ipc_port, ipc_ring, net,
20    numbers::*, pci, process as proc_sys, semaphore as sem_handler, transport, volume,
21    SyscallFrame,
22};
23use crate::{
24    async_io::syscall as async_sys,
25    capability::{release_capability, CapId, CapPermissions, ResourceType},
26    memory::{UserSliceRead, UserSliceWrite},
27    process::current_task_clone,
28    silo,
29};
30use core::sync::atomic::Ordering;
31/// One-shot diagnostic flag to confirm syscalls reach the dispatcher.
32static SYSCALL_DIAG_DONE: core::sync::atomic::AtomicBool =
33    core::sync::atomic::AtomicBool::new(false);
34
35/// Rate-limit counter for the per-syscall ENTER trace (avoid flooding FORCE_LOCK under SMP).
36/// Prints first 20 dispatches unconditionally, then every 10 000.
37static SYSCALL_TRACE_COUNT: core::sync::atomic::AtomicU64 = core::sync::atomic::AtomicU64::new(0);
38
39/// Main dispatch function called from `syscall_entry` assembly.
40///
41/// # Arguments
42/// * `frame` - Pointer to the SyscallFrame on the kernel stack.
43///
44/// # Returns
45/// The value to place in RAX (positive = success, negative = error).
46///
47/// # Safety
48/// Called from naked assembly. `frame` must be a valid pointer to a
49/// `SyscallFrame` on the current kernel stack.
50#[no_mangle]
51pub extern "C" fn __strat9_syscall_dispatch(frame: &mut SyscallFrame) -> u64 {
52    let syscall_num = frame.rax as usize;
53    let arg1 = frame.rdi;
54    let arg2 = frame.rsi;
55    let arg3 = frame.rdx;
56
57    // One-shot diagnostic: confirm first syscall reaches the dispatcher.
58    if !SYSCALL_DIAG_DONE.swap(true, core::sync::atomic::Ordering::Relaxed) {
59        crate::e9_println!(
60            "[syscall-FIRST] nr={} rip={:#x} tid={:?}",
61            syscall_num,
62            frame.rcx,
63            crate::process::current_task_id().map(|t| t.as_u64())
64        );
65    }
66    let arg4 = frame.r10;
67    let arg5 = frame.r8;
68    let _arg6 = frame.r9;
69
70    // Rate-limited trace with relaxed-count optimisation.
71    //
72    // Most syscalls never log. We avoid the expensive `lock xadd` (atomic
73    // RMW) by only doing it near sampling points. In the common case we use
74    // a plain `store(Relaxed)`.
75    // The count drifts slightly under SMP, but I think
76    // for diagnostic sampling that is perfectly acceptable...
77    // TODO : need review and refactor here. Not happy with the state
78    //
79    // First 20 calls logged unconditionally, then one sample every 10 000.
80    {
81        let n = SYSCALL_TRACE_COUNT.load(core::sync::atomic::Ordering::Relaxed);
82        if n < 20 || n % 10_000 == 0 || n == u64::MAX {
83            // Near a sampling point: synchronise with an accurate RMW.
84            let n = SYSCALL_TRACE_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
85            if n < 20 || n % 10_000 == 0 {
86                if let Some(tid) = crate::process::current_task_id() {
87                    crate::e9_println!(
88                        "[syscall] ENTER n={} tid={} nr={} arg1={:#x} arg2={:#x} rip={:#x} cpu={}",
89                        n,
90                        tid,
91                        syscall_num,
92                        arg1,
93                        arg2,
94                        frame.rcx,
95                        crate::arch::percpu::current_cpu_index()
96                    );
97                }
98            }
99        } else {
100            // Fast path: relaxed store avoids the `lock` bus stall entirely.
101            // A race between cores may lose an increment. Harmless for sampling.
102            SYSCALL_TRACE_COUNT.store(n + 1, core::sync::atomic::Ordering::Relaxed);
103        }
104    }
105
106    let result = match syscall_num {
107        SYS_NULL => sys_null(),
108        SYS_HANDLE_DUPLICATE => sys_handle_duplicate(arg1),
109        SYS_HANDLE_CLOSE => sys_handle_close(arg1),
110        SYS_HANDLE_WAIT => sys_handle_wait(arg1, arg2),
111        SYS_HANDLE_GRANT => sys_handle_grant(arg1, arg2),
112        SYS_HANDLE_REVOKE => sys_handle_revoke(arg1),
113        SYS_HANDLE_INFO => sys_handle_info(arg1, arg2),
114
115        // Memory management (block 100-199)
116        SYS_MMAP => super::mmap::sys_mmap(arg1, arg2, arg3 as u32, arg4 as u32, frame.r8, frame.r9),
117        SYS_MUNMAP => super::mmap::sys_munmap(arg1, arg2),
118        SYS_BRK => super::mmap::sys_brk(arg1),
119        SYS_MREMAP => super::mmap::sys_mremap(arg1, arg2, arg3, arg4),
120        SYS_MPROTECT => super::mmap::sys_mprotect(arg1, arg2, arg3),
121        SYS_MEM_REGION_EXPORT => super::mmap::sys_mem_region_export(arg1),
122        SYS_MEM_REGION_MAP => super::mmap::sys_mem_region_map(arg1, arg2, arg3),
123        SYS_MEM_REGION_INFO => super::mmap::sys_mem_region_info(arg1, arg2),
124
125        // Process management (block 300-399)
126        SYS_PROC_EXIT => sys_proc_exit(arg1),
127        SYS_PROC_YIELD => sys_proc_yield(),
128        SYS_PROC_FORK => sys_fork(frame).map(|result| result.child_pid as u64),
129        SYS_PROC_GETPID | SYS_GETPID => proc_sys::sys_getpid(),
130        SYS_PROC_GETPPID => proc_sys::sys_getppid(),
131        SYS_GETTID => proc_sys::sys_gettid(),
132        SYS_PROC_WAITPID => {
133            super::wait::sys_waitpid(arg1 as i64, arg2, arg3 as u32).map(|pid| pid as u64)
134        }
135        SYS_PROC_WAIT => super::wait::sys_wait(arg1),
136        SYS_PROC_EXECVE => sys_execve(frame, arg1, arg2, arg3),
137
138        // File I/O (block 400-499)
139        SYS_FCNTL => super::fcntl::sys_fcntl(arg1, arg2, arg3),
140        SYS_SETPGID => proc_sys::sys_setpgid(arg1 as i64, arg2 as i64),
141        SYS_GETPGID => proc_sys::sys_getpgid(arg1 as i64),
142        SYS_SETSID => proc_sys::sys_setsid(),
143        SYS_GETPGRP => proc_sys::sys_getpgrp(),
144        SYS_GETSID => proc_sys::sys_getsid(arg1 as i64),
145
146        SYS_GETUID => proc_sys::sys_getuid(),
147        SYS_GETEUID => proc_sys::sys_geteuid(),
148        SYS_GETGID => proc_sys::sys_getgid(),
149        SYS_GETEGID => proc_sys::sys_getegid(),
150        SYS_SETUID => proc_sys::sys_setuid(arg1),
151        SYS_SETGID => proc_sys::sys_setgid(arg1),
152        SYS_THREAD_CREATE => proc_sys::sys_thread_create(frame, arg1, arg2, arg3, arg4, frame.r8),
153        SYS_THREAD_JOIN => proc_sys::sys_thread_join(arg1, arg2, arg3),
154        SYS_THREAD_EXIT => proc_sys::sys_thread_exit(arg1),
155        SYS_UNAME => sys_uname(arg1),
156        //  Thread lifecycle (333-334) ===========================================
157        SYS_SET_TID_ADDRESS => proc_sys::sys_set_tid_address(arg1),
158        SYS_EXIT_GROUP => proc_sys::sys_exit_group(arg1),
159        //  Architecture-specific (350) ==========================================
160        SYS_ARCH_PRCTL => proc_sys::sys_arch_prctl(arg1, arg2),
161
162        //  tgkill (352) =========================================
163        SYS_TGKILL => proc_sys::sys_tgkill(arg1, arg2, arg3),
164        SYS_RT_SIGRETURN => super::signal::sys_rt_sigreturn(frame),
165        // Futex syscalls (400-409) ========================================
166        SYS_FUTEX_WAIT => super::futex::sys_futex_wait(arg1, arg2 as u32, arg3),
167        SYS_FUTEX_WAKE => super::futex::sys_futex_wake(arg1, arg2 as u32),
168        SYS_FUTEX_REQUEUE => super::futex::sys_futex_requeue(arg1, arg2 as u32, arg3 as u32, arg4),
169        SYS_FUTEX_CMP_REQUEUE => super::futex::sys_futex_cmp_requeue(
170            arg1,
171            arg2 as u32,
172            arg3 as u32,
173            arg4,
174            frame.r8 as u32,
175        ),
176        SYS_FUTEX_WAKE_OP => {
177            super::futex::sys_futex_wake_op(arg1, arg2 as u32, arg3 as u32, arg4, frame.r8 as u32)
178        }
179        SYS_KILL => super::signal::sys_kill(arg1 as i64, arg2 as u32),
180        SYS_SIGPROCMASK => sys_sigprocmask(arg1 as i32, arg2, arg3),
181        SYS_SIGACTION => super::signal::sys_sigaction(arg1, arg2, arg3),
182        SYS_SIGALTSTACK => super::signal::sys_sigaltstack(arg1, arg2),
183        SYS_SIGPENDING => super::signal::sys_sigpending(arg1),
184        SYS_SIGSUSPEND => super::signal::sys_sigsuspend(arg1),
185        SYS_SIGTIMEDWAIT => super::signal::sys_sigtimedwait(arg1, arg2, arg3),
186        SYS_SIGQUEUE => super::signal::sys_sigqueue(arg1 as i64, arg2 as u32, arg3),
187        SYS_KILLPG => super::signal::sys_killpg(arg1, arg2 as u32),
188        SYS_GETITIMER => super::signal::sys_getitimer(arg1 as u32, arg2),
189        SYS_SETITIMER => super::signal::sys_setitimer(arg1 as u32, arg2, arg3),
190
191        // IPC port syscalls ================================================
192        SYS_IPC_CREATE_PORT => ipc_port::sys_ipc_create_port(arg1),
193        SYS_IPC_SEND => ipc_port::sys_ipc_send(arg1, arg2),
194        SYS_IPC_RECV => ipc_port::sys_ipc_recv(arg1, arg2),
195        SYS_IPC_TRY_RECV => ipc_port::sys_ipc_try_recv(arg1, arg2),
196        SYS_IPC_CONNECT => ipc_port::sys_ipc_connect(arg1, arg2),
197        SYS_IPC_CALL => ipc_port::sys_ipc_call(arg1, arg2),
198        SYS_IPC_REPLY => ipc_port::sys_ipc_reply(arg1),
199        SYS_IPC_BIND_PORT => ipc_port::sys_ipc_bind_port(arg1, arg2, arg3),
200        SYS_IPC_UNBIND_PORT => ipc_port::sys_ipc_unbind_port(arg1, arg2),
201
202        // IPC ring-buffer syscalls ==========================================
203        SYS_IPC_RING_CREATE => ipc_ring::sys_ipc_ring_create(arg1),
204        SYS_IPC_RING_MAP => ipc_ring::sys_ipc_ring_map(arg1, arg2),
205
206        // Semaphore syscalls ================================================
207        SYS_SEM_CREATE => sem_handler::sys_sem_create(arg1),
208        SYS_SEM_WAIT => sem_handler::sys_sem_wait(arg1),
209        SYS_SEM_TRYWAIT => sem_handler::sys_sem_trywait(arg1),
210        SYS_SEM_POST => sem_handler::sys_sem_post(arg1),
211        SYS_SEM_CLOSE => sem_handler::sys_sem_close(arg1),
212
213        // Async I/O syscalls (250-254) ======================================
214        SYS_ASYNC_SETUP => async_sys::sys_async_setup(arg1, arg2),
215        SYS_ASYNC_ENTER => async_sys::sys_async_enter(arg1, arg2, arg3, arg4),
216        SYS_ASYNC_CANCEL => async_sys::sys_async_cancel(arg1, arg2, arg3),
217        SYS_ASYNC_MAP => async_sys::sys_async_map(arg1, arg2),
218        SYS_ASYNC_DESTROY => async_sys::sys_async_destroy(arg1, arg2),
219
220        // Transport syscalls (260-264) ======================================
221        SYS_TRANSPORT_CREATE => transport::sys_transport_create(arg1, arg2),
222        SYS_TRANSPORT_SEND => transport::sys_transport_send(arg1, arg2, arg3),
223        SYS_TRANSPORT_RECV => transport::sys_transport_recv(arg1, arg2, arg3),
224        SYS_TRANSPORT_CLOSE => transport::sys_transport_close(arg1),
225        SYS_TRANSPORT_INFO => transport::sys_transport_info(arg1, arg2),
226
227        // PCI syscalls ======================================================
228        SYS_PCI_ENUM => pci::sys_pci_enum(arg1, arg2, arg3),
229        SYS_PCI_CFG_READ => pci::sys_pci_cfg_read(arg1, arg2, arg3),
230        SYS_PCI_CFG_WRITE => pci::sys_pci_cfg_write(arg1, arg2, arg3, arg4),
231
232        // Typed MPMC sync-channel (IPC-02) ==================================
233        SYS_CHAN_CREATE => chan::sys_chan_create(arg1),
234        SYS_CHAN_SEND => chan::sys_chan_send(arg1, arg2),
235        SYS_CHAN_RECV => chan::sys_chan_recv(arg1, arg2),
236        SYS_CHAN_TRY_RECV => chan::sys_chan_try_recv(arg1, arg2),
237        SYS_CHAN_CLOSE => chan::sys_chan_close(arg1),
238        SYS_MODULE_LOAD => silo::sys_module_load(arg1, arg2),
239        SYS_MODULE_UNLOAD => silo::sys_module_unload(arg1),
240        SYS_MODULE_GET_SYMBOL => silo::sys_module_get_symbol(arg1, arg2),
241        SYS_MODULE_QUERY => silo::sys_module_query(arg1, arg2),
242        SYS_OPEN => sys_open(arg1, arg2, arg3),
243        SYS_WRITE => sys_write(arg1, arg2, arg3),
244        SYS_READ => sys_read(arg1, arg2, arg3),
245        SYS_CLOSE => sys_close(arg1),
246        SYS_LSEEK => sys_lseek(arg1, arg2, arg3),
247        SYS_FSTAT => sys_fstat(arg1, arg2),
248        SYS_STAT => sys_stat(arg1, arg2, arg3),
249        SYS_ACCESS => crate::vfs::sys_access(arg1, arg2, arg3),
250        SYS_GETDENTS => sys_getdents(arg1, arg2, arg3),
251        SYS_PIPE => sys_pipe(arg1),
252        SYS_DUP => sys_dup(arg1),
253        SYS_DUP2 => sys_dup2(arg1, arg2),
254
255        // VFS syscalls (440-455)  ========================================
256        SYS_CHDIR => crate::vfs::sys_chdir(arg1, arg2),
257        SYS_FCHDIR => crate::vfs::sys_fchdir(arg1 as u32),
258        SYS_GETCWD => crate::vfs::sys_getcwd(arg1, arg2),
259        SYS_IOCTL => crate::vfs::sys_ioctl(arg1 as u32, arg2, arg3),
260        SYS_UMASK => crate::vfs::sys_umask(arg1),
261        SYS_UNLINK => crate::vfs::sys_unlink(arg1, arg2),
262        SYS_RMDIR => crate::vfs::sys_rmdir(arg1, arg2),
263        SYS_MKDIR => crate::vfs::sys_mkdir(arg1, arg2, arg3),
264        SYS_RENAME => crate::vfs::sys_rename(arg1, arg2, arg3, arg4),
265        SYS_LINK => crate::vfs::sys_link(arg1, arg2, arg3, arg4),
266        SYS_SYMLINK => crate::vfs::sys_symlink(arg1, arg2, arg3, arg4),
267        SYS_READLINK => crate::vfs::sys_readlink(arg1, arg2, arg3, arg4),
268        SYS_CHMOD => crate::vfs::sys_chmod(arg1, arg2, arg3),
269        SYS_FCHMOD => crate::vfs::sys_fchmod(arg1 as u32, arg2),
270        SYS_TRUNCATE => crate::vfs::sys_truncate(arg1, arg2, arg3),
271        SYS_FTRUNCATE => crate::vfs::sys_ftruncate(arg1 as u32, arg2),
272        SYS_PREAD => crate::vfs::sys_pread(arg1 as u32, arg2, arg3, arg4),
273        SYS_PWRITE => crate::vfs::sys_pwrite(arg1 as u32, arg2, arg3, arg4),
274        SYS_POLL => super::poll::sys_poll(arg1, arg2, arg3),
275        SYS_PPOLL => super::poll::sys_poll(arg1, arg2, 0),
276
277        // *at() syscalls : FD-relative path resolution ======================
278        SYS_OPENAT => crate::vfs::sys_openat(arg1, arg2, arg3, arg4),
279        SYS_FSTATAT => crate::vfs::sys_fstatat(arg1, arg2, arg3, arg5),
280        SYS_FACCESSAT => crate::vfs::sys_faccessat(arg1, arg2, arg3, arg4, frame.r8),
281
282        // Network syscalls (500-599) ========================================
283        SYS_NET_RECV => net::sys_net_recv(arg1, arg2),
284        SYS_NET_SEND => net::sys_net_send(arg1, arg2),
285        SYS_NET_INFO => net::sys_net_info(arg1, arg2),
286        SYS_NET_REGISTER => net::sys_net_register(),
287
288        // Storage syscalls (600-699) ========================================
289        SYS_VOLUME_READ => volume::sys_volume_read(arg1, arg2, arg3, arg4),
290        SYS_VOLUME_WRITE => volume::sys_volume_write(arg1, arg2, arg3, arg4),
291        SYS_VOLUME_INFO => volume::sys_volume_info(arg1),
292        SYS_CLOCK_GETTIME => super::time::sys_clock_gettime(arg1 as u32, arg2),
293        SYS_NANOSLEEP => super::time::sys_nanosleep(arg1, arg2),
294        SYS_CLOCK_NANOSLEEP => {
295            super::time::sys_clock_nanosleep(arg1 as u32, arg2 as i32, arg3, arg4)
296        }
297        SYS_DEBUG_LOG => debug::sys_debug_log(arg1, arg2),
298        SYS_GETRANDOM => super::random::sys_getrandom(arg1, arg2 as usize, arg3 as u32),
299        SYS_SET_ROBUST_LIST => super::robust_list::sys_set_robust_list(arg1, arg2 as usize),
300        SYS_GET_ROBUST_LIST => super::robust_list::sys_get_robust_list(arg1 as i64, arg2, arg3),
301
302        // Silo management (700-799) ========================================
303        SYS_SILO_CREATE => silo::sys_silo_create(arg1),
304        SYS_SILO_CONFIG => silo::sys_silo_config(arg1, arg2),
305        SYS_SILO_ATTACH_MODULE => silo::sys_silo_attach_module(arg1, arg2),
306        SYS_SILO_START => silo::sys_silo_start(arg1),
307        SYS_SILO_STOP => silo::sys_silo_stop(arg1),
308        SYS_SILO_KILL => silo::sys_silo_kill(arg1),
309        SYS_SILO_EVENT_NEXT => silo::sys_silo_event_next(arg1),
310        SYS_SILO_SUSPEND => silo::sys_silo_suspend(arg1),
311        SYS_SILO_RESUME => silo::sys_silo_resume(arg1),
312        SYS_SILO_PLEDGE => silo::sys_silo_pledge(arg1),
313        SYS_SILO_UNVEIL => silo::sys_silo_unveil(arg1, arg2, arg3),
314        SYS_SILO_ENTER_SANDBOX => silo::sys_silo_enter_sandbox(),
315        SYS_SILO_RENAME => silo::sys_silo_rename(arg1, arg2, arg3),
316
317        // Architecture-specific (900-999) =========================================
318        SYS_ABI_VERSION => {
319            Ok(((strat9_abi::ABI_VERSION_MAJOR as u64) << 16)
320                | (strat9_abi::ABI_VERSION_MINOR as u64))
321        }
322        _ => {
323            log::warn!("Unknown syscall: {} (0x{:x})", syscall_num, syscall_num);
324            Err(SyscallError::NotImplemented)
325        }
326    };
327    //=============================================================================================================================
328
329    match result {
330        Ok(val) => {
331            if syscall_num == SYS_PROC_FORK {
332                crate::serial_println!("[syscall] FORK returning Ok({})", val);
333            }
334            frame.rax = val;
335        }
336        Err(e) => {
337            if syscall_num == SYS_PROC_FORK {
338                crate::serial_println!("[syscall] FORK returning err");
339            }
340            frame.rax = e.to_raw();
341        }
342    }
343
344    // Fast signal-pending hint: check the per-CPU flag set by `send_signal`
345    // when a signal targets the task currently running on this CPU. This
346    // avoids the expensive scheduler lock + Arc::clone when no signal is
347    // pending (the common case : +99.99% of syscalls).
348    //
349    // Cross-CPU signals (rare) are handled on the target's next syscall, or
350    // by the explicit `has_pending_signals()` check in blocking syscalls.
351    if crate::arch::percpu::test_and_clear_signal_pending_current() {
352        crate::process::signal::deliver_pending_signal(frame);
353    }
354
355    frame.rax
356}
357
358/// Alias used by the `call {dispatch}` in syscall_entry.
359/// Re-exports `__strat9_syscall_dispatch` under the symbol the assembly expects.
360#[no_mangle]
361pub extern "C" fn dispatch(frame: &mut SyscallFrame) -> u64 {
362    __strat9_syscall_dispatch(frame)
363}
364
365// ============================================================
366// Syscall handlers
367// ============================================================
368
369/// SYS_NULL (0): Ping/test syscall. Returns magic value 0x57A79 ("STRAT9").
370fn sys_null() -> Result<u64, SyscallError> {
371    Ok(0x57A79)
372}
373
374/// SYS_HANDLE_CLOSE (2): Close a handle. Stub : always succeeds.
375fn sys_handle_close(_handle: u64) -> Result<u64, SyscallError> {
376    crate::silo::enforce_cap_for_current_task(_handle)?;
377    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
378    let caps = unsafe { &mut *task.process.capabilities.get() };
379    if let Some(cap) = caps.remove(CapId::from_raw(_handle)) {
380        release_capability(&cap, Some(task.id));
381        log::trace!("syscall: HANDLE_CLOSE({})", _handle);
382        Ok(0)
383    } else {
384        Err(SyscallError::BadHandle)
385    }
386}
387
388pub(crate) fn insert_capability_with_retention(
389    caps: &mut crate::capability::CapabilityTable,
390    cap: crate::capability::Capability,
391) -> Result<CapId, SyscallError> {
392    let id = caps.insert(cap);
393    if let Some(inserted) = caps.get(id) {
394        if inserted.resource_type == ResourceType::MemoryRegion {
395            if let Err(error) =
396                crate::memory::memory_region_registry().retain_handle(inserted.resource as u64, id)
397            {
398                let _ = caps.remove(id);
399                return Err(match error {
400                    crate::memory::RegionCapError::NotFound => SyscallError::BadHandle,
401                    crate::memory::RegionCapError::InvalidRegion
402                    | crate::memory::RegionCapError::IncompleteRegion
403                    | crate::memory::RegionCapError::InvalidAddress => {
404                        SyscallError::InvalidArgument
405                    }
406                    crate::memory::RegionCapError::PermissionDenied => {
407                        SyscallError::PermissionDenied
408                    }
409                    crate::memory::RegionCapError::OutOfMemory => SyscallError::OutOfMemory,
410                    crate::memory::RegionCapError::InconsistentState => SyscallError::IoError,
411                });
412            }
413        }
414    }
415    Ok(id)
416}
417
418/// SYS_HANDLE_DUPLICATE (1): duplicate a handle (grant required).
419fn sys_handle_duplicate(handle: u64) -> Result<u64, SyscallError> {
420    crate::silo::enforce_cap_for_current_task(handle)?;
421    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
422    let caps = unsafe { &mut *task.process.capabilities.get() };
423    let dup = caps
424        .duplicate(CapId::from_raw(handle))
425        .ok_or(SyscallError::PermissionDenied)?;
426    let id = insert_capability_with_retention(caps, dup)?;
427    Ok(id.as_u64())
428}
429
430const HANDLE_EVENT_READABLE: u64 = 1 << 0;
431const HANDLE_EVENT_WRITABLE: u64 = 1 << 1;
432
433/// Performs the poll handle events operation.
434fn poll_handle_events(handle: u64) -> Result<u64, SyscallError> {
435    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
436    let caps = unsafe { &*task.process.capabilities.get() };
437    let cap = caps
438        .get(CapId::from_raw(handle))
439        .ok_or(SyscallError::BadHandle)?;
440
441    match cap.resource_type {
442        ResourceType::Semaphore => {
443            if !cap.permissions.read {
444                return Err(SyscallError::PermissionDenied);
445            }
446            let sem = semaphore::get_semaphore(SemId::from_u64(cap.resource as u64))
447                .ok_or(SyscallError::BadHandle)?;
448            if sem.is_destroyed() {
449                return Err(SyscallError::Pipe);
450            }
451            if sem.count() > 0 {
452                Ok(HANDLE_EVENT_READABLE)
453            } else {
454                Ok(0)
455            }
456        }
457        ResourceType::IpcPort => {
458            let port = port::get_port(PortId::from_u64(cap.resource as u64))
459                .ok_or(SyscallError::BadHandle)?;
460            if port.is_destroyed() {
461                return Err(SyscallError::Pipe);
462            }
463            let mut events = 0u64;
464            if cap.permissions.read && port.has_messages() {
465                events |= HANDLE_EVENT_READABLE;
466            }
467            if cap.permissions.write && port.can_send() {
468                events |= HANDLE_EVENT_WRITABLE;
469            }
470            if events == 0 && !cap.permissions.read && !cap.permissions.write {
471                return Err(SyscallError::PermissionDenied);
472            }
473            Ok(events)
474        }
475        ResourceType::Channel => {
476            let chan = channel::get_channel(ChanId::from_u64(cap.resource as u64))
477                .ok_or(SyscallError::BadHandle)?;
478            if chan.is_destroyed() {
479                return Err(SyscallError::Pipe);
480            }
481            let mut events = 0u64;
482            if cap.permissions.read && !chan.is_empty() {
483                events |= HANDLE_EVENT_READABLE;
484            }
485            if cap.permissions.write && chan.can_send() {
486                events |= HANDLE_EVENT_WRITABLE;
487            }
488            if events == 0 && !cap.permissions.read && !cap.permissions.write {
489                return Err(SyscallError::PermissionDenied);
490            }
491            Ok(events)
492        }
493        ResourceType::SharedRing => {
494            let _ = shared_ring::get_ring(RingId::from_u64(cap.resource as u64))
495                .ok_or(SyscallError::BadHandle)?;
496            let mut events = 0u64;
497            if cap.permissions.read {
498                events |= HANDLE_EVENT_READABLE;
499            }
500            if cap.permissions.write {
501                events |= HANDLE_EVENT_WRITABLE;
502            }
503            if events == 0 {
504                return Err(SyscallError::PermissionDenied);
505            }
506            Ok(events)
507        }
508        ResourceType::IpcTransport => {
509            let tid = crate::ipc::transport::TransportId::from_u64(cap.resource as u64);
510            let endpoint = crate::syscall::transport::TRANSPORT_MANAGER
511                .get_endpoint(tid)
512                .ok_or(SyscallError::BadHandle)?;
513            let mut events = 0u64;
514            if cap.permissions.read && endpoint.has_data() {
515                events |= HANDLE_EVENT_READABLE;
516            }
517            if cap.permissions.write && endpoint.has_space() {
518                events |= HANDLE_EVENT_WRITABLE;
519            }
520            if events == 0 && !cap.permissions.read && !cap.permissions.write {
521                return Err(SyscallError::PermissionDenied);
522            }
523            Ok(events)
524        }
525        _ => Err(SyscallError::NotSupported),
526    }
527}
528
529/// Performs the sys handle wait operation.
530fn sys_handle_wait(handle: u64, timeout_ns: u64) -> Result<u64, SyscallError> {
531    crate::silo::enforce_cap_for_current_task(handle)?;
532
533    let check_ready = || -> Result<u64, SyscallError> {
534        let events = poll_handle_events(handle)?;
535        if events != 0 {
536            Ok(events)
537        } else {
538            Err(SyscallError::Again)
539        }
540    };
541
542    if timeout_ns == 0 {
543        return check_ready();
544    }
545
546    let deadline = if timeout_ns == u64::MAX {
547        None
548    } else {
549        Some(crate::syscall::time::current_time_ns().saturating_add(timeout_ns))
550    };
551
552    loop {
553        if let Ok(events) = check_ready() {
554            return Ok(events);
555        }
556
557        if crate::process::has_pending_signals() {
558            return Err(SyscallError::Interrupted);
559        }
560
561        let now = crate::syscall::time::current_time_ns();
562        let wake_ns = if let Some(deadline_ns) = deadline {
563            if now >= deadline_ns {
564                return Err(SyscallError::TimedOut);
565            }
566            core::cmp::min(deadline_ns, now.saturating_add(10_000_000))
567        } else {
568            now.saturating_add(10_000_000)
569        };
570
571        if let Some(task) = current_task_clone() {
572            task.wake_deadline_ns.store(wake_ns, Ordering::Relaxed);
573        }
574        crate::process::block_current_task();
575        if let Some(task) = current_task_clone() {
576            task.wake_deadline_ns.store(0, Ordering::Relaxed);
577        }
578    }
579}
580
581/// Performs the sys handle grant operation.
582fn sys_handle_grant(handle: u64, target_pid: u64) -> Result<u64, SyscallError> {
583    crate::silo::enforce_cap_for_current_task(handle)?;
584    crate::silo::enforce_silo_may_grant()?;
585    let pid = u32::try_from(target_pid).map_err(|_| SyscallError::InvalidArgument)?;
586
587    let source = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
588    let granted = {
589        let source_caps = unsafe { &*source.process.capabilities.get() };
590        let cap = source_caps
591            .get(CapId::from_raw(handle))
592            .ok_or(SyscallError::BadHandle)?;
593        if !cap.permissions.grant {
594            return Err(SyscallError::PermissionDenied);
595        }
596        let mut dup = cap.clone();
597        dup.id = CapId::new();
598        dup
599    };
600
601    let target = crate::process::get_task_by_pid(pid).ok_or(SyscallError::NotFound)?;
602    let target_caps = unsafe { &mut *target.process.capabilities.get() };
603    let new_id = insert_capability_with_retention(target_caps, granted)?;
604    Ok(new_id.as_u64())
605}
606
607/// Performs the sys handle revoke operation.
608fn sys_handle_revoke(handle: u64) -> Result<u64, SyscallError> {
609    crate::silo::enforce_cap_for_current_task(handle)?;
610    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
611    let caps = unsafe { &mut *task.process.capabilities.get() };
612
613    {
614        let cap = caps
615            .get(CapId::from_raw(handle))
616            .ok_or(SyscallError::BadHandle)?;
617        if !cap.permissions.revoke {
618            return Err(SyscallError::PermissionDenied);
619        }
620    }
621
622    let cap = caps
623        .remove(CapId::from_raw(handle))
624        .ok_or(SyscallError::BadHandle)?;
625    release_capability(&cap, Some(task.id));
626    Ok(0)
627}
628
629use strat9_abi::data::HandleInfo as HandleInfoAbi;
630
631/// Performs the cap perm bits operation.
632fn cap_perm_bits(p: CapPermissions) -> u32 {
633    (if p.read { 1 } else { 0 })
634        | (if p.write { 1 << 1 } else { 0 })
635        | (if p.execute { 1 << 2 } else { 0 })
636        | (if p.grant { 1 << 3 } else { 0 })
637        | (if p.revoke { 1 << 4 } else { 0 })
638}
639
640/// Performs the resource type code operation.
641fn resource_type_code(rt: ResourceType) -> u32 {
642    match rt {
643        ResourceType::MemoryRegion => 1,
644        ResourceType::IoPortRange => 2,
645        ResourceType::InterruptLine => 3,
646        ResourceType::IpcPort => 4,
647        ResourceType::Channel => 5,
648        ResourceType::SharedRing => 6,
649        ResourceType::Semaphore => 7,
650        ResourceType::Device => 8,
651        ResourceType::AddressSpace => 9,
652        ResourceType::Silo => 10,
653        ResourceType::Module => 11,
654        ResourceType::File => 12,
655        ResourceType::Nic => 13,
656        ResourceType::FileSystem => 14,
657        ResourceType::Console => 15,
658        ResourceType::Keyboard => 16,
659        ResourceType::Volume => 17,
660        ResourceType::Namespace => 18,
661        ResourceType::IpcTransport => 19,
662    }
663}
664
665/// Performs the sys handle info operation.
666fn sys_handle_info(handle: u64, out_ptr: u64) -> Result<u64, SyscallError> {
667    crate::silo::enforce_cap_for_current_task(handle)?;
668    if out_ptr == 0 {
669        return Err(SyscallError::Fault);
670    }
671    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
672    let caps = unsafe { &*task.process.capabilities.get() };
673    let cap = caps
674        .get(CapId::from_raw(handle))
675        .ok_or(SyscallError::BadHandle)?;
676
677    let info = HandleInfoAbi {
678        resource_type: resource_type_code(cap.resource_type),
679        permissions: cap_perm_bits(cap.permissions),
680        resource: cap.resource as u64,
681    };
682    let user = UserSliceWrite::new(out_ptr, core::mem::size_of::<HandleInfoAbi>())?;
683    let bytes = unsafe {
684        core::slice::from_raw_parts(
685            &info as *const HandleInfoAbi as *const u8,
686            core::mem::size_of::<HandleInfoAbi>(),
687        )
688    };
689    user.copy_from(bytes);
690    Ok(0)
691}
692
693/// SYS_PROC_EXIT (300): Exit the current task.
694///
695/// Marks the task as Dead and yields. This function never returns to the caller.
696fn sys_proc_exit(exit_code: u64) -> Result<u64, SyscallError> {
697    log::info!("syscall: PROC_EXIT(code={})", exit_code);
698
699    // Mark current task as Dead and yield. The scheduler won't re-queue dead tasks.
700    // exit_current_task() diverges (-> !), so this function never returns.
701    crate::process::scheduler::exit_current_task(exit_code as i32)
702}
703
704/// SYS_PROC_YIELD (301): Yield the current time slice.
705fn sys_proc_yield() -> Result<u64, SyscallError> {
706    crate::process::yield_task();
707    Ok(0)
708}
709
710/// SYS_SIGPROCMASK (321): Examine and change blocked signals.
711///
712/// arg1 = how (0=BLOCK, 1=UNBLOCK, 2=SETMASK), arg2 = set_ptr (new mask), arg3 = oldset_ptr (old mask out)
713fn sys_sigprocmask(how: i32, set_ptr: u64, oldset_ptr: u64) -> Result<u64, SyscallError> {
714    use crate::process::current_task_clone;
715
716    const SIG_BLOCK: i32 = 0;
717    const SIG_UNBLOCK: i32 = 1;
718    const SIG_SETMASK: i32 = 2;
719
720    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
721
722    let blocked = &task.blocked_signals;
723
724    if oldset_ptr != 0 {
725        let old_mask = blocked.get_mask();
726        let user = UserSliceWrite::new(oldset_ptr, 8)?;
727        user.copy_from(&old_mask.to_ne_bytes());
728    }
729
730    if set_ptr != 0 {
731        let user = UserSliceRead::new(set_ptr, 8)?;
732        let mut buf = [0u8; 8];
733        user.copy_to(&mut buf);
734        let new_mask = u64::from_ne_bytes(buf);
735
736        let old_mask = blocked.get_mask();
737        let updated_mask = match how {
738            SIG_BLOCK => old_mask | new_mask,
739            SIG_UNBLOCK => old_mask & !new_mask,
740            SIG_SETMASK => new_mask,
741            _ => return Err(SyscallError::InvalidArgument),
742        };
743
744        blocked.set_mask(updated_mask);
745    }
746
747    Ok(0)
748}
749
750/// Performs the sys uname operation.
751fn sys_uname(uts_ptr: u64) -> Result<u64, SyscallError> {
752    const UTS_FIELD_LEN: usize = 65;
753    const UTS_TOTAL_LEN: usize = UTS_FIELD_LEN * 6;
754
755    if uts_ptr == 0 {
756        return Err(SyscallError::Fault);
757    }
758
759    /// Writes field.
760    fn write_field(dst: &mut [u8], src: &[u8]) {
761        let n = core::cmp::min(src.len(), dst.len().saturating_sub(1));
762        if n > 0 {
763            dst[..n].copy_from_slice(&src[..n]);
764        }
765        dst[n] = 0;
766    }
767
768    let mut uts = [0u8; UTS_TOTAL_LEN];
769    write_field(&mut uts[..UTS_FIELD_LEN], b"Strat9");
770    write_field(&mut uts[1 * UTS_FIELD_LEN..2 * UTS_FIELD_LEN], b"localhost");
771    write_field(&mut uts[2 * UTS_FIELD_LEN..3 * UTS_FIELD_LEN], b"0.1.0");
772    write_field(&mut uts[3 * UTS_FIELD_LEN..4 * UTS_FIELD_LEN], b"Strat9-OS");
773    write_field(&mut uts[4 * UTS_FIELD_LEN..5 * UTS_FIELD_LEN], b"x86_64");
774    write_field(
775        &mut uts[5 * UTS_FIELD_LEN..6 * UTS_FIELD_LEN],
776        b"localdomain",
777    );
778
779    let user = UserSliceWrite::new(uts_ptr, UTS_TOTAL_LEN)?;
780    user.copy_from(&uts);
781    Ok(0)
782}
783
784/// SYS_WRITE (404): Write bytes to a file descriptor.
785fn sys_write(fd: u64, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
786    crate::vfs::sys_write(fd as u32, buf_ptr, buf_len)
787}
788
789/// SYS_OPEN (403): Open a path from the minimal in-kernel namespace.
790fn sys_open(path_ptr: u64, path_len: u64, flags: u64) -> Result<u64, SyscallError> {
791    crate::vfs::sys_open(path_ptr, path_len, flags)
792}
793
794/// SYS_READ (405): Read bytes from a handle.
795fn sys_read(fd: u64, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
796    crate::vfs::sys_read(fd as u32, buf_ptr, buf_len)
797}
798
799/// SYS_CLOSE (406): Close a handle (fd).
800fn sys_close(fd: u64) -> Result<u64, SyscallError> {
801    crate::vfs::sys_close(fd as u32)
802}
803
804/// SYS_LSEEK (407): Seek in a file.
805fn sys_lseek(fd: u64, offset: u64, whence: u64) -> Result<u64, SyscallError> {
806    crate::vfs::sys_lseek(fd as u32, offset as i64, whence as u32)
807}
808
809/// SYS_FSTAT (408): Get metadata of an open file.
810fn sys_fstat(fd: u64, stat_ptr: u64) -> Result<u64, SyscallError> {
811    crate::vfs::sys_fstat(fd as u32, stat_ptr)
812}
813
814/// SYS_STAT (409): Get metadata by path.
815fn sys_stat(path_ptr: u64, path_len: u64, stat_ptr: u64) -> Result<u64, SyscallError> {
816    crate::vfs::sys_stat(path_ptr, path_len, stat_ptr)
817}
818
819/// SYS_GETDENTS (430): Read directory entries.
820fn sys_getdents(fd: u64, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
821    crate::vfs::sys_getdents(fd as u32, buf_ptr, buf_len)
822}
823
824/// SYS_PIPE (431): Create a pipe pair.
825fn sys_pipe(fds_ptr: u64) -> Result<u64, SyscallError> {
826    crate::vfs::sys_pipe(fds_ptr)
827}
828
829/// SYS_DUP (432): Duplicate a file descriptor.
830fn sys_dup(old_fd: u64) -> Result<u64, SyscallError> {
831    crate::vfs::sys_dup(old_fd as u32)
832}
833
834/// SYS_DUP2 (433): Duplicate fd to a specific number.
835fn sys_dup2(old_fd: u64, new_fd: u64) -> Result<u64, SyscallError> {
836    crate::vfs::sys_dup2(old_fd as u32, new_fd as u32)
837}