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::x86_64::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, arg4),
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
287        // Storage syscalls (600-699) ========================================
288        SYS_VOLUME_READ => volume::sys_volume_read(arg1, arg2, arg3, arg4),
289        SYS_VOLUME_WRITE => volume::sys_volume_write(arg1, arg2, arg3, arg4),
290        SYS_VOLUME_INFO => volume::sys_volume_info(arg1),
291        SYS_CLOCK_GETTIME => super::time::sys_clock_gettime(arg1 as u32, arg2),
292        SYS_NANOSLEEP => super::time::sys_nanosleep(arg1, arg2),
293        SYS_CLOCK_NANOSLEEP => {
294            super::time::sys_clock_nanosleep(arg1 as u32, arg2 as i32, arg3, arg4)
295        }
296        SYS_DEBUG_LOG => debug::sys_debug_log(arg1, arg2),
297        SYS_GETRANDOM => super::random::sys_getrandom(arg1, arg2 as usize, arg3 as u32),
298        SYS_SET_ROBUST_LIST => super::robust_list::sys_set_robust_list(arg1, arg2 as usize),
299        SYS_GET_ROBUST_LIST => super::robust_list::sys_get_robust_list(arg1 as i64, arg2, arg3),
300
301        // Silo management (700-799) ========================================
302        SYS_SILO_CREATE => silo::sys_silo_create(arg1),
303        SYS_SILO_CONFIG => silo::sys_silo_config(arg1, arg2),
304        SYS_SILO_ATTACH_MODULE => silo::sys_silo_attach_module(arg1, arg2),
305        SYS_SILO_START => silo::sys_silo_start(arg1),
306        SYS_SILO_STOP => silo::sys_silo_stop(arg1),
307        SYS_SILO_KILL => silo::sys_silo_kill(arg1),
308        SYS_SILO_EVENT_NEXT => silo::sys_silo_event_next(arg1),
309        SYS_SILO_SUSPEND => silo::sys_silo_suspend(arg1),
310        SYS_SILO_RESUME => silo::sys_silo_resume(arg1),
311        SYS_SILO_PLEDGE => silo::sys_silo_pledge(arg1),
312        SYS_SILO_UNVEIL => silo::sys_silo_unveil(arg1, arg2, arg3),
313        SYS_SILO_ENTER_SANDBOX => silo::sys_silo_enter_sandbox(),
314        SYS_SILO_RENAME => silo::sys_silo_rename(arg1, arg2, arg3),
315
316        // Architecture-specific (900-999) =========================================
317        SYS_ABI_VERSION => {
318            Ok(((strat9_abi::ABI_VERSION_MAJOR as u64) << 16)
319                | (strat9_abi::ABI_VERSION_MINOR as u64))
320        }
321        _ => {
322            log::warn!("Unknown syscall: {} (0x{:x})", syscall_num, syscall_num);
323            Err(SyscallError::NotImplemented)
324        }
325    };
326    //=============================================================================================================================
327
328    match result {
329        Ok(val) => {
330            if syscall_num == SYS_PROC_FORK {
331                crate::serial_println!("[syscall] FORK returning Ok({})", val);
332            }
333            frame.rax = val;
334        }
335        Err(e) => {
336            if syscall_num == SYS_PROC_FORK {
337                crate::serial_println!("[syscall] FORK returning err");
338            }
339            frame.rax = e.to_raw();
340        }
341    }
342
343    // Fast signal-pending hint: check the per-CPU flag set by `send_signal`
344    // when a signal targets the task currently running on this CPU. This
345    // avoids the expensive scheduler lock + Arc::clone when no signal is
346    // pending (the common case : +99.99% of syscalls).
347    //
348    // Cross-CPU signals (rare) are handled on the target's next syscall, or
349    // by the explicit `has_pending_signals()` check in blocking syscalls.
350    if crate::arch::x86_64::percpu::test_and_clear_signal_pending_current() {
351        crate::process::signal::deliver_pending_signal(frame);
352    }
353
354    frame.rax
355}
356
357/// Alias used by the `call {dispatch}` in syscall_entry.
358/// Re-exports `__strat9_syscall_dispatch` under the symbol the assembly expects.
359#[no_mangle]
360pub extern "C" fn dispatch(frame: &mut SyscallFrame) -> u64 {
361    __strat9_syscall_dispatch(frame)
362}
363
364// ============================================================
365// Syscall handlers
366// ============================================================
367
368/// SYS_NULL (0): Ping/test syscall. Returns magic value 0x57A79 ("STRAT9").
369fn sys_null() -> Result<u64, SyscallError> {
370    Ok(0x57A79)
371}
372
373/// SYS_HANDLE_CLOSE (2): Close a handle. Stub : always succeeds.
374fn sys_handle_close(_handle: u64) -> Result<u64, SyscallError> {
375    crate::silo::enforce_cap_for_current_task(_handle)?;
376    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
377    let caps = unsafe { &mut *task.process.capabilities.get() };
378    if let Some(cap) = caps.remove(CapId::from_raw(_handle)) {
379        release_capability(&cap, Some(task.id));
380        log::trace!("syscall: HANDLE_CLOSE({})", _handle);
381        Ok(0)
382    } else {
383        Err(SyscallError::BadHandle)
384    }
385}
386
387pub(crate) fn insert_capability_with_retention(
388    caps: &mut crate::capability::CapabilityTable,
389    cap: crate::capability::Capability,
390) -> Result<CapId, SyscallError> {
391    let id = caps.insert(cap);
392    if let Some(inserted) = caps.get(id) {
393        if inserted.resource_type == ResourceType::MemoryRegion {
394            if let Err(error) =
395                crate::memory::memory_region_registry().retain_handle(inserted.resource as u64, id)
396            {
397                let _ = caps.remove(id);
398                return Err(match error {
399                    crate::memory::RegionCapError::NotFound => SyscallError::BadHandle,
400                    crate::memory::RegionCapError::InvalidRegion
401                    | crate::memory::RegionCapError::IncompleteRegion
402                    | crate::memory::RegionCapError::InvalidAddress => {
403                        SyscallError::InvalidArgument
404                    }
405                    crate::memory::RegionCapError::PermissionDenied => {
406                        SyscallError::PermissionDenied
407                    }
408                    crate::memory::RegionCapError::OutOfMemory => SyscallError::OutOfMemory,
409                    crate::memory::RegionCapError::InconsistentState => SyscallError::IoError,
410                });
411            }
412        }
413    }
414    Ok(id)
415}
416
417/// SYS_HANDLE_DUPLICATE (1): duplicate a handle (grant required).
418fn sys_handle_duplicate(handle: u64) -> Result<u64, SyscallError> {
419    crate::silo::enforce_cap_for_current_task(handle)?;
420    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
421    let caps = unsafe { &mut *task.process.capabilities.get() };
422    let dup = caps
423        .duplicate(CapId::from_raw(handle))
424        .ok_or(SyscallError::PermissionDenied)?;
425    let id = insert_capability_with_retention(caps, dup)?;
426    Ok(id.as_u64())
427}
428
429const HANDLE_EVENT_READABLE: u64 = 1 << 0;
430const HANDLE_EVENT_WRITABLE: u64 = 1 << 1;
431
432/// Performs the poll handle events operation.
433fn poll_handle_events(handle: u64) -> Result<u64, SyscallError> {
434    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
435    let caps = unsafe { &*task.process.capabilities.get() };
436    let cap = caps
437        .get(CapId::from_raw(handle))
438        .ok_or(SyscallError::BadHandle)?;
439
440    match cap.resource_type {
441        ResourceType::Semaphore => {
442            if !cap.permissions.read {
443                return Err(SyscallError::PermissionDenied);
444            }
445            let sem = semaphore::get_semaphore(SemId::from_u64(cap.resource as u64))
446                .ok_or(SyscallError::BadHandle)?;
447            if sem.is_destroyed() {
448                return Err(SyscallError::Pipe);
449            }
450            if sem.count() > 0 {
451                Ok(HANDLE_EVENT_READABLE)
452            } else {
453                Ok(0)
454            }
455        }
456        ResourceType::IpcPort => {
457            let port = port::get_port(PortId::from_u64(cap.resource as u64))
458                .ok_or(SyscallError::BadHandle)?;
459            if port.is_destroyed() {
460                return Err(SyscallError::Pipe);
461            }
462            let mut events = 0u64;
463            if cap.permissions.read && port.has_messages() {
464                events |= HANDLE_EVENT_READABLE;
465            }
466            if cap.permissions.write && port.can_send() {
467                events |= HANDLE_EVENT_WRITABLE;
468            }
469            if events == 0 && !cap.permissions.read && !cap.permissions.write {
470                return Err(SyscallError::PermissionDenied);
471            }
472            Ok(events)
473        }
474        ResourceType::Channel => {
475            let chan = channel::get_channel(ChanId::from_u64(cap.resource as u64))
476                .ok_or(SyscallError::BadHandle)?;
477            if chan.is_destroyed() {
478                return Err(SyscallError::Pipe);
479            }
480            let mut events = 0u64;
481            if cap.permissions.read && !chan.is_empty() {
482                events |= HANDLE_EVENT_READABLE;
483            }
484            if cap.permissions.write && chan.can_send() {
485                events |= HANDLE_EVENT_WRITABLE;
486            }
487            if events == 0 && !cap.permissions.read && !cap.permissions.write {
488                return Err(SyscallError::PermissionDenied);
489            }
490            Ok(events)
491        }
492        ResourceType::SharedRing => {
493            let _ = shared_ring::get_ring(RingId::from_u64(cap.resource as u64))
494                .ok_or(SyscallError::BadHandle)?;
495            let mut events = 0u64;
496            if cap.permissions.read {
497                events |= HANDLE_EVENT_READABLE;
498            }
499            if cap.permissions.write {
500                events |= HANDLE_EVENT_WRITABLE;
501            }
502            if events == 0 {
503                return Err(SyscallError::PermissionDenied);
504            }
505            Ok(events)
506        }
507        ResourceType::IpcTransport => {
508            let tid = crate::ipc::transport::TransportId::from_u64(cap.resource as u64);
509            let endpoint = crate::syscall::transport::TRANSPORT_MANAGER
510                .get_endpoint(tid)
511                .ok_or(SyscallError::BadHandle)?;
512            let mut events = 0u64;
513            if cap.permissions.read && endpoint.has_data() {
514                events |= HANDLE_EVENT_READABLE;
515            }
516            if cap.permissions.write && endpoint.has_space() {
517                events |= HANDLE_EVENT_WRITABLE;
518            }
519            if events == 0 && !cap.permissions.read && !cap.permissions.write {
520                return Err(SyscallError::PermissionDenied);
521            }
522            Ok(events)
523        }
524        _ => Err(SyscallError::NotSupported),
525    }
526}
527
528/// Performs the sys handle wait operation.
529fn sys_handle_wait(handle: u64, timeout_ns: u64) -> Result<u64, SyscallError> {
530    crate::silo::enforce_cap_for_current_task(handle)?;
531
532    let check_ready = || -> Result<u64, SyscallError> {
533        let events = poll_handle_events(handle)?;
534        if events != 0 {
535            Ok(events)
536        } else {
537            Err(SyscallError::Again)
538        }
539    };
540
541    if timeout_ns == 0 {
542        return check_ready();
543    }
544
545    let deadline = if timeout_ns == u64::MAX {
546        None
547    } else {
548        Some(crate::syscall::time::current_time_ns().saturating_add(timeout_ns))
549    };
550
551    loop {
552        if let Ok(events) = check_ready() {
553            return Ok(events);
554        }
555
556        if crate::process::has_pending_signals() {
557            return Err(SyscallError::Interrupted);
558        }
559
560        let now = crate::syscall::time::current_time_ns();
561        let wake_ns = if let Some(deadline_ns) = deadline {
562            if now >= deadline_ns {
563                return Err(SyscallError::TimedOut);
564            }
565            core::cmp::min(deadline_ns, now.saturating_add(10_000_000))
566        } else {
567            now.saturating_add(10_000_000)
568        };
569
570        if let Some(task) = current_task_clone() {
571            task.wake_deadline_ns.store(wake_ns, Ordering::Relaxed);
572        }
573        crate::process::block_current_task();
574        if let Some(task) = current_task_clone() {
575            task.wake_deadline_ns.store(0, Ordering::Relaxed);
576        }
577    }
578}
579
580/// Performs the sys handle grant operation.
581fn sys_handle_grant(handle: u64, target_pid: u64) -> Result<u64, SyscallError> {
582    crate::silo::enforce_cap_for_current_task(handle)?;
583    crate::silo::enforce_silo_may_grant()?;
584    let pid = u32::try_from(target_pid).map_err(|_| SyscallError::InvalidArgument)?;
585
586    let source = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
587    let granted = {
588        let source_caps = unsafe { &*source.process.capabilities.get() };
589        let cap = source_caps
590            .get(CapId::from_raw(handle))
591            .ok_or(SyscallError::BadHandle)?;
592        if !cap.permissions.grant {
593            return Err(SyscallError::PermissionDenied);
594        }
595        let mut dup = cap.clone();
596        dup.id = CapId::new();
597        dup
598    };
599
600    let target = crate::process::get_task_by_pid(pid).ok_or(SyscallError::NotFound)?;
601    let target_caps = unsafe { &mut *target.process.capabilities.get() };
602    let new_id = insert_capability_with_retention(target_caps, granted)?;
603    Ok(new_id.as_u64())
604}
605
606/// Performs the sys handle revoke operation.
607fn sys_handle_revoke(handle: u64) -> Result<u64, SyscallError> {
608    crate::silo::enforce_cap_for_current_task(handle)?;
609    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
610    let caps = unsafe { &mut *task.process.capabilities.get() };
611
612    {
613        let cap = caps
614            .get(CapId::from_raw(handle))
615            .ok_or(SyscallError::BadHandle)?;
616        if !cap.permissions.revoke {
617            return Err(SyscallError::PermissionDenied);
618        }
619    }
620
621    let cap = caps
622        .remove(CapId::from_raw(handle))
623        .ok_or(SyscallError::BadHandle)?;
624    release_capability(&cap, Some(task.id));
625    Ok(0)
626}
627
628use strat9_abi::data::HandleInfo as HandleInfoAbi;
629
630/// Performs the cap perm bits operation.
631fn cap_perm_bits(p: CapPermissions) -> u32 {
632    (if p.read { 1 } else { 0 })
633        | (if p.write { 1 << 1 } else { 0 })
634        | (if p.execute { 1 << 2 } else { 0 })
635        | (if p.grant { 1 << 3 } else { 0 })
636        | (if p.revoke { 1 << 4 } else { 0 })
637}
638
639/// Performs the resource type code operation.
640fn resource_type_code(rt: ResourceType) -> u32 {
641    match rt {
642        ResourceType::MemoryRegion => 1,
643        ResourceType::IoPortRange => 2,
644        ResourceType::InterruptLine => 3,
645        ResourceType::IpcPort => 4,
646        ResourceType::Channel => 5,
647        ResourceType::SharedRing => 6,
648        ResourceType::Semaphore => 7,
649        ResourceType::Device => 8,
650        ResourceType::AddressSpace => 9,
651        ResourceType::Silo => 10,
652        ResourceType::Module => 11,
653        ResourceType::File => 12,
654        ResourceType::Nic => 13,
655        ResourceType::FileSystem => 14,
656        ResourceType::Console => 15,
657        ResourceType::Keyboard => 16,
658        ResourceType::Volume => 17,
659        ResourceType::Namespace => 18,
660        ResourceType::IpcTransport => 19,
661    }
662}
663
664/// Performs the sys handle info operation.
665fn sys_handle_info(handle: u64, out_ptr: u64) -> Result<u64, SyscallError> {
666    crate::silo::enforce_cap_for_current_task(handle)?;
667    if out_ptr == 0 {
668        return Err(SyscallError::Fault);
669    }
670    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
671    let caps = unsafe { &*task.process.capabilities.get() };
672    let cap = caps
673        .get(CapId::from_raw(handle))
674        .ok_or(SyscallError::BadHandle)?;
675
676    let info = HandleInfoAbi {
677        resource_type: resource_type_code(cap.resource_type),
678        permissions: cap_perm_bits(cap.permissions),
679        resource: cap.resource as u64,
680    };
681    let user = UserSliceWrite::new(out_ptr, core::mem::size_of::<HandleInfoAbi>())?;
682    let bytes = unsafe {
683        core::slice::from_raw_parts(
684            &info as *const HandleInfoAbi as *const u8,
685            core::mem::size_of::<HandleInfoAbi>(),
686        )
687    };
688    user.copy_from(bytes);
689    Ok(0)
690}
691
692/// SYS_PROC_EXIT (300): Exit the current task.
693///
694/// Marks the task as Dead and yields. This function never returns to the caller.
695fn sys_proc_exit(exit_code: u64) -> Result<u64, SyscallError> {
696    log::info!("syscall: PROC_EXIT(code={})", exit_code);
697
698    // Mark current task as Dead and yield. The scheduler won't re-queue dead tasks.
699    // exit_current_task() diverges (-> !), so this function never returns.
700    crate::process::scheduler::exit_current_task(exit_code as i32)
701}
702
703/// SYS_PROC_YIELD (301): Yield the current time slice.
704fn sys_proc_yield() -> Result<u64, SyscallError> {
705    crate::process::yield_task();
706    Ok(0)
707}
708
709/// SYS_SIGPROCMASK (321): Examine and change blocked signals.
710///
711/// arg1 = how (0=BLOCK, 1=UNBLOCK, 2=SETMASK), arg2 = set_ptr (new mask), arg3 = oldset_ptr (old mask out)
712fn sys_sigprocmask(how: i32, set_ptr: u64, oldset_ptr: u64) -> Result<u64, SyscallError> {
713    use crate::process::current_task_clone;
714
715    const SIG_BLOCK: i32 = 0;
716    const SIG_UNBLOCK: i32 = 1;
717    const SIG_SETMASK: i32 = 2;
718
719    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
720
721    let blocked = &task.blocked_signals;
722
723    if oldset_ptr != 0 {
724        let old_mask = blocked.get_mask();
725        let user = UserSliceWrite::new(oldset_ptr, 8)?;
726        user.copy_from(&old_mask.to_ne_bytes());
727    }
728
729    if set_ptr != 0 {
730        let user = UserSliceRead::new(set_ptr, 8)?;
731        let mut buf = [0u8; 8];
732        user.copy_to(&mut buf);
733        let new_mask = u64::from_ne_bytes(buf);
734
735        let old_mask = blocked.get_mask();
736        let updated_mask = match how {
737            SIG_BLOCK => old_mask | new_mask,
738            SIG_UNBLOCK => old_mask & !new_mask,
739            SIG_SETMASK => new_mask,
740            _ => return Err(SyscallError::InvalidArgument),
741        };
742
743        blocked.set_mask(updated_mask);
744    }
745
746    Ok(0)
747}
748
749/// Performs the sys uname operation.
750fn sys_uname(uts_ptr: u64) -> Result<u64, SyscallError> {
751    const UTS_FIELD_LEN: usize = 65;
752    const UTS_TOTAL_LEN: usize = UTS_FIELD_LEN * 6;
753
754    if uts_ptr == 0 {
755        return Err(SyscallError::Fault);
756    }
757
758    /// Writes field.
759    fn write_field(dst: &mut [u8], src: &[u8]) {
760        let n = core::cmp::min(src.len(), dst.len().saturating_sub(1));
761        if n > 0 {
762            dst[..n].copy_from_slice(&src[..n]);
763        }
764        dst[n] = 0;
765    }
766
767    let mut uts = [0u8; UTS_TOTAL_LEN];
768    write_field(&mut uts[0 * UTS_FIELD_LEN..1 * UTS_FIELD_LEN], b"Strat9");
769    write_field(&mut uts[1 * UTS_FIELD_LEN..2 * UTS_FIELD_LEN], b"localhost");
770    write_field(&mut uts[2 * UTS_FIELD_LEN..3 * UTS_FIELD_LEN], b"0.1.0");
771    write_field(&mut uts[3 * UTS_FIELD_LEN..4 * UTS_FIELD_LEN], b"Strat9-OS");
772    write_field(&mut uts[4 * UTS_FIELD_LEN..5 * UTS_FIELD_LEN], b"x86_64");
773    write_field(
774        &mut uts[5 * UTS_FIELD_LEN..6 * UTS_FIELD_LEN],
775        b"localdomain",
776    );
777
778    let user = UserSliceWrite::new(uts_ptr, UTS_TOTAL_LEN)?;
779    user.copy_from(&uts);
780    Ok(0)
781}
782
783/// SYS_WRITE (404): Write bytes to a file descriptor.
784fn sys_write(fd: u64, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
785    crate::vfs::sys_write(fd as u32, buf_ptr, buf_len)
786}
787
788/// SYS_OPEN (403): Open a path from the minimal in-kernel namespace.
789fn sys_open(path_ptr: u64, path_len: u64, flags: u64) -> Result<u64, SyscallError> {
790    crate::vfs::sys_open(path_ptr, path_len, flags)
791}
792
793/// SYS_READ (405): Read bytes from a handle.
794fn sys_read(fd: u64, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
795    crate::vfs::sys_read(fd as u32, buf_ptr, buf_len)
796}
797
798/// SYS_CLOSE (406): Close a handle (fd).
799fn sys_close(fd: u64) -> Result<u64, SyscallError> {
800    crate::vfs::sys_close(fd as u32)
801}
802
803/// SYS_LSEEK (407): Seek in a file.
804fn sys_lseek(fd: u64, offset: u64, whence: u64) -> Result<u64, SyscallError> {
805    crate::vfs::sys_lseek(fd as u32, offset as i64, whence as u32)
806}
807
808/// SYS_FSTAT (408): Get metadata of an open file.
809fn sys_fstat(fd: u64, stat_ptr: u64) -> Result<u64, SyscallError> {
810    crate::vfs::sys_fstat(fd as u32, stat_ptr)
811}
812
813/// SYS_STAT (409): Get metadata by path.
814fn sys_stat(path_ptr: u64, path_len: u64, stat_ptr: u64) -> Result<u64, SyscallError> {
815    crate::vfs::sys_stat(path_ptr, path_len, stat_ptr)
816}
817
818/// SYS_GETDENTS (430): Read directory entries.
819fn sys_getdents(fd: u64, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
820    crate::vfs::sys_getdents(fd as u32, buf_ptr, buf_len)
821}
822
823/// SYS_PIPE (431): Create a pipe pair.
824fn sys_pipe(fds_ptr: u64) -> Result<u64, SyscallError> {
825    crate::vfs::sys_pipe(fds_ptr)
826}
827
828/// SYS_DUP (432): Duplicate a file descriptor.
829fn sys_dup(old_fd: u64) -> Result<u64, SyscallError> {
830    crate::vfs::sys_dup(old_fd as u32)
831}
832
833/// SYS_DUP2 (433): Duplicate fd to a specific number.
834fn sys_dup2(old_fd: u64, new_fd: u64) -> Result<u64, SyscallError> {
835    crate::vfs::sys_dup2(old_fd as u32, new_fd as u32)
836}