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 the syscall number and calls the appropriate handler. Each handler returns a `Result<u64, SyscallError>`, which is converted to a raw value in RAX (positive for success, negative for error).
7//! The `dispatch` function is an alias for `__strat9_syscall_dispatch` used by the assembly entry point.
8//! The syscall handlers are defined in this module and in submodules (e.g. `mmap`, `process`, `signal`, etc.) and cover a wide range of functionality including process management, memory management, IPC, file I/O, networking, and more.
9//! The dispatcher also includes diagnostic logging with rate-limiting to avoid log spam under high syscall rates. For example, it logs the first 20 syscalls unconditionally, then every 10,000 syscalls thereafter. It also has budgeted logging for network send/recv errors and DHCP frame tracing.
10//!
11//!
12use super::{
13    error::SyscallError, exec::sys_execve, fork::sys_fork, numbers::*, process as proc_sys,
14    SyscallFrame,
15};
16use crate::{
17    arch::x86_64::pci,
18    capability::{get_capability_manager, release_capability, CapId, CapPermissions, ResourceType},
19    hardware::storage::{
20        ahci,
21        virtio_block::{self, BlockDevice, SECTOR_SIZE},
22    },
23    ipc::{
24        channel::{self, ChanId},
25        message::IpcMessage,
26        port::{self, PortId},
27        reply,
28        semaphore::{self, SemId},
29        shared_ring::{self, RingId},
30    },
31    memory::{UserSliceRead, UserSliceWrite},
32    process::current_task_clone,
33    silo,
34};
35use alloc::{sync::Arc, vec};
36use core::sync::atomic::{AtomicU32, Ordering};
37
38/// One-shot diagnostic flag to confirm syscalls reach the dispatcher.
39static SYSCALL_DIAG_DONE: core::sync::atomic::AtomicBool =
40    core::sync::atomic::AtomicBool::new(false);
41
42/// Rate-limit counter for the per-syscall ENTER trace (avoid flooding FORCE_LOCK under SMP).
43/// Prints first 20 dispatches unconditionally, then every 10 000.
44static SYSCALL_TRACE_COUNT: core::sync::atomic::AtomicU64 = core::sync::atomic::AtomicU64::new(0);
45
46/// Budget for logging network send errors to prevent log spam.
47static NET_SEND_ERR_LOG_BUDGET: AtomicU32 = AtomicU32::new(32);
48/// Budget for logging network receive errors to prevent log spam.
49static NET_RECV_ERR_LOG_BUDGET: AtomicU32 = AtomicU32::new(32);
50/// Budget for logging DHCP trace frames to prevent log spam.
51static DHCP_TRACE_LOG_BUDGET: AtomicU32 = AtomicU32::new(64);
52
53/// Performs the trace dhcp frame operation.
54fn trace_dhcp_frame(tag: &str, frame: &[u8]) {
55    if DHCP_TRACE_LOG_BUDGET.fetch_sub(1, Ordering::Relaxed) == 0 {
56        return;
57    }
58    if frame.len() < 14 {
59        return;
60    }
61    let ethertype = u16::from_be_bytes([frame[12], frame[13]]);
62    if ethertype != 0x0800 {
63        return;
64    }
65    if frame.len() < 34 {
66        return;
67    }
68    let ip_hlen = ((frame[14] & 0x0f) as usize) * 4;
69    if ip_hlen < 20 || frame.len() < 14 + ip_hlen + 8 {
70        return;
71    }
72    if frame[23] != 17 {
73        return;
74    }
75    let udp = 14 + ip_hlen;
76    let src_port = u16::from_be_bytes([frame[udp], frame[udp + 1]]);
77    let dst_port = u16::from_be_bytes([frame[udp + 2], frame[udp + 3]]);
78    let is_dhcp = (src_port == 68 && dst_port == 67) || (src_port == 67 && dst_port == 68);
79    if !is_dhcp {
80        return;
81    }
82    let mut xid: u32 = 0;
83    let bootp = udp + 8;
84    if frame.len() >= bootp + 8 {
85        xid = u32::from_be_bytes([
86            frame[bootp + 4],
87            frame[bootp + 5],
88            frame[bootp + 6],
89            frame[bootp + 7],
90        ]);
91    }
92    crate::serial_println!(
93        "[dhcp-trace] {} src_port={} dst_port={} len={} xid=0x{:08x}",
94        tag,
95        src_port,
96        dst_port,
97        frame.len(),
98        xid
99    );
100}
101
102/// Main dispatch function called from `syscall_entry` assembly.
103///
104/// # Arguments
105/// * `frame` - Pointer to the SyscallFrame on the kernel stack.
106///
107/// # Returns
108/// The value to place in RAX (positive = success, negative = error).
109///
110/// # Safety
111/// Called from naked assembly. `frame` must be a valid pointer to a
112/// `SyscallFrame` on the current kernel stack.
113#[no_mangle]
114pub extern "C" fn __strat9_syscall_dispatch(frame: &mut SyscallFrame) -> u64 {
115    let syscall_num = frame.rax as usize;
116    let arg1 = frame.rdi;
117    let arg2 = frame.rsi;
118    let arg3 = frame.rdx;
119
120    // One-shot diagnostic: confirm first syscall reaches the dispatcher.
121    if !SYSCALL_DIAG_DONE.swap(true, core::sync::atomic::Ordering::Relaxed) {
122        crate::e9_println!(
123            "[syscall-FIRST] nr={} rip={:#x} tid={:?}",
124            syscall_num,
125            frame.rcx,
126            crate::process::current_task_id().map(|t| t.as_u64())
127        );
128    }
129    let arg4 = frame.r10;
130    let _arg5 = frame.r8;
131    let _arg6 = frame.r9;
132
133    // Rate-limited trace to avoid saturating FORCE_LOCK under SMP.
134    // First 20 calls unconditional, then a sample every 10_000.
135    {
136        let n = SYSCALL_TRACE_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
137        if n < 20 || n % 10_000 == 0 {
138            if let Some(tid) = crate::process::current_task_id() {
139                crate::e9_println!(
140                    "[syscall] ENTER n={} tid={} nr={} arg1={:#x} arg2={:#x} rip={:#x} cpu={}",
141                    n,
142                    tid,
143                    syscall_num,
144                    arg1,
145                    arg2,
146                    frame.rcx,
147                    crate::arch::x86_64::percpu::current_cpu_index()
148                );
149            }
150        }
151    }
152
153    let result = match syscall_num {
154        SYS_NULL => sys_null(),
155        SYS_HANDLE_DUPLICATE => sys_handle_duplicate(arg1),
156        SYS_HANDLE_CLOSE => sys_handle_close(arg1),
157        SYS_HANDLE_WAIT => sys_handle_wait(arg1, arg2),
158        SYS_HANDLE_GRANT => sys_handle_grant(arg1, arg2),
159        SYS_HANDLE_REVOKE => sys_handle_revoke(arg1),
160        SYS_HANDLE_INFO => sys_handle_info(arg1, arg2),
161
162        // Memory management (block 100-199)
163        SYS_MMAP => super::mmap::sys_mmap(arg1, arg2, arg3 as u32, arg4 as u32, frame.r8, frame.r9),
164        SYS_MUNMAP => super::mmap::sys_munmap(arg1, arg2),
165        SYS_BRK => super::mmap::sys_brk(arg1),
166        SYS_MREMAP => super::mmap::sys_mremap(arg1, arg2, arg3, arg4),
167        SYS_MPROTECT => super::mmap::sys_mprotect(arg1, arg2, arg3),
168        SYS_MEM_REGION_EXPORT => super::mmap::sys_mem_region_export(arg1),
169        SYS_MEM_REGION_MAP => super::mmap::sys_mem_region_map(arg1, arg2, arg3),
170        SYS_MEM_REGION_INFO => super::mmap::sys_mem_region_info(arg1, arg2),
171
172        // Process management (block 300-399)
173        SYS_PROC_EXIT => sys_proc_exit(arg1),
174        SYS_PROC_YIELD => sys_proc_yield(),
175        SYS_PROC_FORK => sys_fork(frame).map(|result| result.child_pid as u64),
176        SYS_PROC_GETPID | SYS_GETPID => proc_sys::sys_getpid(),
177        SYS_PROC_GETPPID => proc_sys::sys_getppid(),
178        SYS_GETTID => proc_sys::sys_gettid(),
179        SYS_PROC_WAITPID => {
180            super::wait::sys_waitpid(arg1 as i64, arg2, arg3 as u32).map(|pid| pid as u64)
181        }
182        SYS_PROC_WAIT => super::wait::sys_wait(arg1),
183        SYS_PROC_EXECVE => sys_execve(frame, arg1, arg2, arg3),
184
185        // File I/O (block 400-499)
186        SYS_FCNTL => super::fcntl::sys_fcntl(arg1, arg2, arg3),
187        SYS_SETPGID => proc_sys::sys_setpgid(arg1 as i64, arg2 as i64),
188        SYS_GETPGID => proc_sys::sys_getpgid(arg1 as i64),
189        SYS_SETSID => proc_sys::sys_setsid(),
190        SYS_GETPGRP => proc_sys::sys_getpgrp(),
191        SYS_GETSID => proc_sys::sys_getsid(arg1 as i64),
192
193        SYS_GETUID => proc_sys::sys_getuid(),
194        SYS_GETEUID => proc_sys::sys_geteuid(),
195        SYS_GETGID => proc_sys::sys_getgid(),
196        SYS_GETEGID => proc_sys::sys_getegid(),
197        SYS_SETUID => proc_sys::sys_setuid(arg1),
198        SYS_SETGID => proc_sys::sys_setgid(arg1),
199        SYS_THREAD_CREATE => proc_sys::sys_thread_create(frame, arg1, arg2, arg3, arg4, frame.r8),
200        SYS_THREAD_JOIN => proc_sys::sys_thread_join(arg1, arg2, arg3),
201        SYS_THREAD_EXIT => proc_sys::sys_thread_exit(arg1),
202        SYS_UNAME => sys_uname(arg1),
203        //  Thread lifecycle (333-334) ===========================================
204        SYS_SET_TID_ADDRESS => proc_sys::sys_set_tid_address(arg1),
205        SYS_EXIT_GROUP => proc_sys::sys_exit_group(arg1),
206        //  Architecture-specific (350) ==========================================
207        SYS_ARCH_PRCTL => proc_sys::sys_arch_prctl(arg1, arg2),
208
209        //  tgkill (352) =========================================
210        SYS_TGKILL => proc_sys::sys_tgkill(arg1, arg2, arg3),
211        SYS_RT_SIGRETURN => super::signal::sys_rt_sigreturn(frame),
212        // Futex syscalls (400-409) ========================================
213        SYS_FUTEX_WAIT => super::futex::sys_futex_wait(arg1, arg2 as u32, arg3),
214        SYS_FUTEX_WAKE => super::futex::sys_futex_wake(arg1, arg2 as u32),
215        SYS_FUTEX_REQUEUE => super::futex::sys_futex_requeue(arg1, arg2 as u32, arg3 as u32, arg4),
216        SYS_FUTEX_CMP_REQUEUE => super::futex::sys_futex_cmp_requeue(
217            arg1,
218            arg2 as u32,
219            arg3 as u32,
220            arg4,
221            frame.r8 as u32,
222        ),
223        SYS_FUTEX_WAKE_OP => {
224            super::futex::sys_futex_wake_op(arg1, arg2 as u32, arg3 as u32, arg4, frame.r8 as u32)
225        }
226        SYS_KILL => super::signal::sys_kill(arg1 as i64, arg2 as u32),
227        SYS_SIGPROCMASK => sys_sigprocmask(arg1 as i32, arg2, arg3),
228        SYS_SIGACTION => super::signal::sys_sigaction(arg1, arg2, arg3),
229        SYS_SIGALTSTACK => super::signal::sys_sigaltstack(arg1, arg2),
230        SYS_SIGPENDING => super::signal::sys_sigpending(arg1),
231        SYS_SIGSUSPEND => super::signal::sys_sigsuspend(arg1),
232        SYS_SIGTIMEDWAIT => super::signal::sys_sigtimedwait(arg1, arg2, arg3),
233        SYS_SIGQUEUE => super::signal::sys_sigqueue(arg1 as i64, arg2 as u32, arg3),
234        SYS_KILLPG => super::signal::sys_killpg(arg1, arg2 as u32),
235        SYS_GETITIMER => super::signal::sys_getitimer(arg1 as u32, arg2),
236        SYS_SETITIMER => super::signal::sys_setitimer(arg1 as u32, arg2, arg3),
237
238        // IPC syscalls (600-699) ========================================
239        SYS_IPC_CREATE_PORT => sys_ipc_create_port(arg1),
240        SYS_IPC_SEND => sys_ipc_send(arg1, arg2),
241        SYS_IPC_RECV => sys_ipc_recv(arg1, arg2),
242        SYS_IPC_TRY_RECV => sys_ipc_try_recv(arg1, arg2),
243        SYS_IPC_CONNECT => sys_ipc_connect(arg1, arg2),
244        SYS_IPC_CALL => sys_ipc_call(arg1, arg2),
245        SYS_IPC_REPLY => sys_ipc_reply(arg1),
246        SYS_IPC_BIND_PORT => sys_ipc_bind_port(arg1, arg2, arg3),
247        SYS_IPC_UNBIND_PORT => sys_ipc_unbind_port(arg1, arg2),
248        SYS_IPC_RING_CREATE => sys_ipc_ring_create(arg1),
249        SYS_IPC_RING_MAP => sys_ipc_ring_map(arg1, arg2),
250        SYS_SEM_CREATE => sys_sem_create(arg1),
251        SYS_SEM_WAIT => sys_sem_wait(arg1),
252        SYS_SEM_TRYWAIT => sys_sem_trywait(arg1),
253        SYS_SEM_POST => sys_sem_post(arg1),
254        SYS_SEM_CLOSE => sys_sem_close(arg1),
255        SYS_PCI_ENUM => sys_pci_enum(arg1, arg2, arg3),
256        SYS_PCI_CFG_READ => sys_pci_cfg_read(arg1, arg2, arg3),
257        SYS_PCI_CFG_WRITE => sys_pci_cfg_write(arg1, arg2, arg3, arg4),
258
259        // Typed MPMC sync-channel (IPC-02) ========================================
260        SYS_CHAN_CREATE => sys_chan_create(arg1),
261        SYS_CHAN_SEND => sys_chan_send(arg1, arg2),
262        SYS_CHAN_RECV => sys_chan_recv(arg1, arg2),
263        SYS_CHAN_TRY_RECV => sys_chan_try_recv(arg1, arg2),
264        SYS_CHAN_CLOSE => sys_chan_close(arg1),
265        SYS_MODULE_LOAD => silo::sys_module_load(arg1, arg2),
266        SYS_MODULE_UNLOAD => silo::sys_module_unload(arg1),
267        SYS_MODULE_GET_SYMBOL => silo::sys_module_get_symbol(arg1, arg2),
268        SYS_MODULE_QUERY => silo::sys_module_query(arg1, arg2),
269        SYS_OPEN => sys_open(arg1, arg2, arg3),
270        SYS_WRITE => sys_write(arg1, arg2, arg3),
271        SYS_READ => sys_read(arg1, arg2, arg3),
272        SYS_CLOSE => sys_close(arg1),
273        SYS_LSEEK => sys_lseek(arg1, arg2, arg3),
274        SYS_FSTAT => sys_fstat(arg1, arg2),
275        SYS_STAT => sys_stat(arg1, arg2, arg3),
276        SYS_GETDENTS => sys_getdents(arg1, arg2, arg3),
277        SYS_PIPE => sys_pipe(arg1),
278        SYS_DUP => sys_dup(arg1),
279        SYS_DUP2 => sys_dup2(arg1, arg2),
280
281        // VFS syscalls (440-455)  ========================================
282        SYS_CHDIR => crate::vfs::sys_chdir(arg1, arg2),
283        SYS_FCHDIR => crate::vfs::sys_fchdir(arg1 as u32),
284        SYS_GETCWD => crate::vfs::sys_getcwd(arg1, arg2),
285        SYS_IOCTL => crate::vfs::sys_ioctl(arg1 as u32, arg2, arg3),
286        SYS_UMASK => crate::vfs::sys_umask(arg1),
287        SYS_UNLINK => crate::vfs::sys_unlink(arg1, arg2),
288        SYS_RMDIR => crate::vfs::sys_rmdir(arg1, arg2),
289        SYS_MKDIR => crate::vfs::sys_mkdir(arg1, arg2, arg3),
290        SYS_RENAME => crate::vfs::sys_rename(arg1, arg2, arg3, arg4),
291        SYS_LINK => crate::vfs::sys_link(arg1, arg2, arg3, arg4),
292        SYS_SYMLINK => crate::vfs::sys_symlink(arg1, arg2, arg3, arg4),
293        SYS_READLINK => crate::vfs::sys_readlink(arg1, arg2, arg3, arg4),
294        SYS_CHMOD => crate::vfs::sys_chmod(arg1, arg2, arg3),
295        SYS_FCHMOD => crate::vfs::sys_fchmod(arg1 as u32, arg2),
296        SYS_TRUNCATE => crate::vfs::sys_truncate(arg1, arg2, arg3),
297        SYS_FTRUNCATE => crate::vfs::sys_ftruncate(arg1 as u32, arg2),
298        SYS_PREAD => crate::vfs::sys_pread(arg1 as u32, arg2, arg3, arg4),
299        SYS_PWRITE => crate::vfs::sys_pwrite(arg1 as u32, arg2, arg3, arg4),
300        SYS_POLL => super::poll::sys_poll(arg1, arg2, arg3),
301        SYS_PPOLL => super::poll::sys_poll(arg1, arg2, 0),
302
303        // *at() syscalls : FD-relative path resolution ======================
304        SYS_OPENAT => crate::vfs::sys_openat(arg1, arg2, arg3, arg4),
305        SYS_FSTATAT => crate::vfs::sys_fstatat(arg1, arg2, arg3, arg4),
306
307        // Network syscalls (500-599) ========================================
308        SYS_NET_RECV => sys_net_recv(arg1, arg2),
309        SYS_NET_SEND => sys_net_send(arg1, arg2),
310        SYS_NET_INFO => sys_net_info(arg1, arg2),
311
312        // Storage syscalls (600-699) ========================================
313        SYS_VOLUME_READ => sys_volume_read(arg1, arg2, arg3, arg4),
314        SYS_VOLUME_WRITE => sys_volume_write(arg1, arg2, arg3, arg4),
315        SYS_VOLUME_INFO => sys_volume_info(arg1),
316        SYS_CLOCK_GETTIME => super::time::sys_clock_gettime(arg1 as u32, arg2),
317        SYS_NANOSLEEP => super::time::sys_nanosleep(arg1, arg2),
318        SYS_DEBUG_LOG => sys_debug_log(arg1, arg2),
319
320        // Silo management (700-799) ========================================
321        SYS_SILO_CREATE => silo::sys_silo_create(arg1),
322        SYS_SILO_CONFIG => silo::sys_silo_config(arg1, arg2),
323        SYS_SILO_ATTACH_MODULE => silo::sys_silo_attach_module(arg1, arg2),
324        SYS_SILO_START => silo::sys_silo_start(arg1),
325        SYS_SILO_STOP => silo::sys_silo_stop(arg1),
326        SYS_SILO_KILL => silo::sys_silo_kill(arg1),
327        SYS_SILO_EVENT_NEXT => silo::sys_silo_event_next(arg1),
328        SYS_SILO_SUSPEND => silo::sys_silo_suspend(arg1),
329        SYS_SILO_RESUME => silo::sys_silo_resume(arg1),
330        SYS_SILO_PLEDGE => silo::sys_silo_pledge(arg1),
331        SYS_SILO_UNVEIL => silo::sys_silo_unveil(arg1, arg2, arg3),
332        SYS_SILO_ENTER_SANDBOX => silo::sys_silo_enter_sandbox(),
333
334        // Architecture-specific (900-999) =========================================
335        SYS_ABI_VERSION => {
336            Ok(((strat9_abi::ABI_VERSION_MAJOR as u64) << 16)
337                | (strat9_abi::ABI_VERSION_MINOR as u64))
338        }
339        _ => {
340            log::warn!("Unknown syscall: {} (0x{:x})", syscall_num, syscall_num);
341            Err(SyscallError::NotImplemented)
342        }
343    };
344
345    match result {
346        Ok(val) => {
347            if syscall_num == SYS_PROC_FORK {
348                crate::serial_println!("[syscall] FORK returning Ok({})", val);
349            }
350            frame.rax = val;
351        }
352        Err(e) => {
353            if syscall_num == SYS_PROC_FORK {
354                crate::serial_println!("[syscall] FORK returning err");
355            }
356            frame.rax = e.to_raw();
357        }
358    }
359
360    crate::process::signal::deliver_pending_signal(frame);
361
362    frame.rax
363}
364
365/// Alias used by the `call {dispatch}` in syscall_entry.
366/// Re-exports `__strat9_syscall_dispatch` under the symbol the assembly expects.
367#[no_mangle]
368pub extern "C" fn dispatch(frame: &mut SyscallFrame) -> u64 {
369    __strat9_syscall_dispatch(frame)
370}
371
372// ============================================================
373// Syscall handlers
374// ============================================================
375
376/// SYS_NULL (0): Ping/test syscall. Returns magic value 0x57A79 ("STRAT9").
377fn sys_null() -> Result<u64, SyscallError> {
378    Ok(0x57A79)
379}
380
381/// SYS_HANDLE_CLOSE (2): Close a handle. Stub : always succeeds.
382fn sys_handle_close(_handle: u64) -> Result<u64, SyscallError> {
383    crate::silo::enforce_cap_for_current_task(_handle)?;
384    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
385    let caps = unsafe { &mut *task.process.capabilities.get() };
386    if let Some(cap) = caps.remove(CapId::from_raw(_handle)) {
387        release_capability(&cap, Some(task.id));
388        log::trace!("syscall: HANDLE_CLOSE({})", _handle);
389        Ok(0)
390    } else {
391        Err(SyscallError::BadHandle)
392    }
393}
394
395fn insert_capability_with_retention(
396    caps: &mut crate::capability::CapabilityTable,
397    cap: crate::capability::Capability,
398) -> Result<CapId, SyscallError> {
399    let id = caps.insert(cap);
400    if let Some(inserted) = caps.get(id) {
401        if inserted.resource_type == ResourceType::MemoryRegion {
402            if let Err(error) =
403                crate::memory::memory_region_registry().retain_handle(inserted.resource as u64, id)
404            {
405                let _ = caps.remove(id);
406                return Err(match error {
407                    crate::memory::RegionCapError::NotFound => SyscallError::BadHandle,
408                    crate::memory::RegionCapError::InvalidRegion
409                    | crate::memory::RegionCapError::IncompleteRegion
410                    | crate::memory::RegionCapError::InvalidAddress => {
411                        SyscallError::InvalidArgument
412                    }
413                    crate::memory::RegionCapError::PermissionDenied => {
414                        SyscallError::PermissionDenied
415                    }
416                    crate::memory::RegionCapError::OutOfMemory => SyscallError::OutOfMemory,
417                    crate::memory::RegionCapError::InconsistentState => SyscallError::IoError,
418                });
419            }
420        }
421    }
422    Ok(id)
423}
424
425/// SYS_HANDLE_DUPLICATE (1): duplicate a handle (grant required).
426fn sys_handle_duplicate(handle: u64) -> Result<u64, SyscallError> {
427    crate::silo::enforce_cap_for_current_task(handle)?;
428    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
429    let caps = unsafe { &mut *task.process.capabilities.get() };
430    let dup = caps
431        .duplicate(CapId::from_raw(handle))
432        .ok_or(SyscallError::PermissionDenied)?;
433    let id = insert_capability_with_retention(caps, dup)?;
434    Ok(id.as_u64())
435}
436
437const HANDLE_EVENT_READABLE: u64 = 1 << 0;
438const HANDLE_EVENT_WRITABLE: u64 = 1 << 1;
439
440/// Performs the poll handle events operation.
441fn poll_handle_events(handle: u64) -> Result<u64, SyscallError> {
442    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
443    let caps = unsafe { &*task.process.capabilities.get() };
444    let cap = caps
445        .get(CapId::from_raw(handle))
446        .ok_or(SyscallError::BadHandle)?;
447
448    match cap.resource_type {
449        ResourceType::Semaphore => {
450            if !cap.permissions.read {
451                return Err(SyscallError::PermissionDenied);
452            }
453            let sem = semaphore::get_semaphore(SemId::from_u64(cap.resource as u64))
454                .ok_or(SyscallError::BadHandle)?;
455            if sem.is_destroyed() {
456                return Err(SyscallError::Pipe);
457            }
458            if sem.count() > 0 {
459                Ok(HANDLE_EVENT_READABLE)
460            } else {
461                Ok(0)
462            }
463        }
464        ResourceType::IpcPort => {
465            let port = port::get_port(PortId::from_u64(cap.resource as u64))
466                .ok_or(SyscallError::BadHandle)?;
467            if port.is_destroyed() {
468                return Err(SyscallError::Pipe);
469            }
470            let mut events = 0u64;
471            if cap.permissions.read && port.has_messages() {
472                events |= HANDLE_EVENT_READABLE;
473            }
474            if cap.permissions.write && port.can_send() {
475                events |= HANDLE_EVENT_WRITABLE;
476            }
477            if events == 0 && !cap.permissions.read && !cap.permissions.write {
478                return Err(SyscallError::PermissionDenied);
479            }
480            Ok(events)
481        }
482        ResourceType::Channel => {
483            let chan = channel::get_channel(ChanId::from_u64(cap.resource as u64))
484                .ok_or(SyscallError::BadHandle)?;
485            if chan.is_destroyed() {
486                return Err(SyscallError::Pipe);
487            }
488            let mut events = 0u64;
489            if cap.permissions.read && !chan.is_empty() {
490                events |= HANDLE_EVENT_READABLE;
491            }
492            if cap.permissions.write && chan.can_send() {
493                events |= HANDLE_EVENT_WRITABLE;
494            }
495            if events == 0 && !cap.permissions.read && !cap.permissions.write {
496                return Err(SyscallError::PermissionDenied);
497            }
498            Ok(events)
499        }
500        ResourceType::SharedRing => {
501            let _ = shared_ring::get_ring(RingId::from_u64(cap.resource as u64))
502                .ok_or(SyscallError::BadHandle)?;
503            let mut events = 0u64;
504            if cap.permissions.read {
505                events |= HANDLE_EVENT_READABLE;
506            }
507            if cap.permissions.write {
508                events |= HANDLE_EVENT_WRITABLE;
509            }
510            if events == 0 {
511                return Err(SyscallError::PermissionDenied);
512            }
513            Ok(events)
514        }
515        _ => Err(SyscallError::NotSupported),
516    }
517}
518
519/// Performs the sys handle wait operation.
520fn sys_handle_wait(handle: u64, timeout_ns: u64) -> Result<u64, SyscallError> {
521    crate::silo::enforce_cap_for_current_task(handle)?;
522
523    let check_ready = || -> Result<u64, SyscallError> {
524        let events = poll_handle_events(handle)?;
525        if events != 0 {
526            Ok(events)
527        } else {
528            Err(SyscallError::Again)
529        }
530    };
531
532    if timeout_ns == 0 {
533        return check_ready();
534    }
535
536    let deadline = if timeout_ns == u64::MAX {
537        None
538    } else {
539        Some(crate::syscall::time::current_time_ns().saturating_add(timeout_ns))
540    };
541
542    loop {
543        if let Ok(events) = check_ready() {
544            return Ok(events);
545        }
546
547        if crate::process::has_pending_signals() {
548            return Err(SyscallError::Interrupted);
549        }
550
551        let now = crate::syscall::time::current_time_ns();
552        let wake_ns = if let Some(deadline_ns) = deadline {
553            if now >= deadline_ns {
554                return Err(SyscallError::TimedOut);
555            }
556            core::cmp::min(deadline_ns, now.saturating_add(10_000_000))
557        } else {
558            now.saturating_add(10_000_000)
559        };
560
561        if let Some(task) = current_task_clone() {
562            task.wake_deadline_ns.store(wake_ns, Ordering::Relaxed);
563        }
564        crate::process::block_current_task();
565        if let Some(task) = current_task_clone() {
566            task.wake_deadline_ns.store(0, Ordering::Relaxed);
567        }
568    }
569}
570
571/// Performs the sys handle grant operation.
572fn sys_handle_grant(handle: u64, target_pid: u64) -> Result<u64, SyscallError> {
573    crate::silo::enforce_cap_for_current_task(handle)?;
574    crate::silo::enforce_silo_may_grant()?;
575    let pid = u32::try_from(target_pid).map_err(|_| SyscallError::InvalidArgument)?;
576
577    let source = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
578    let granted = {
579        let source_caps = unsafe { &*source.process.capabilities.get() };
580        let cap = source_caps
581            .get(CapId::from_raw(handle))
582            .ok_or(SyscallError::BadHandle)?;
583        if !cap.permissions.grant {
584            return Err(SyscallError::PermissionDenied);
585        }
586        let mut dup = cap.clone();
587        dup.id = CapId::new();
588        dup
589    };
590
591    let target = crate::process::get_task_by_pid(pid).ok_or(SyscallError::NotFound)?;
592    let target_caps = unsafe { &mut *target.process.capabilities.get() };
593    let new_id = insert_capability_with_retention(target_caps, granted)?;
594    Ok(new_id.as_u64())
595}
596
597/// Performs the sys handle revoke operation.
598fn sys_handle_revoke(handle: u64) -> Result<u64, SyscallError> {
599    crate::silo::enforce_cap_for_current_task(handle)?;
600    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
601    let caps = unsafe { &mut *task.process.capabilities.get() };
602
603    {
604        let cap = caps
605            .get(CapId::from_raw(handle))
606            .ok_or(SyscallError::BadHandle)?;
607        if !cap.permissions.revoke {
608            return Err(SyscallError::PermissionDenied);
609        }
610    }
611
612    let cap = caps
613        .remove(CapId::from_raw(handle))
614        .ok_or(SyscallError::BadHandle)?;
615    release_capability(&cap, Some(task.id));
616    Ok(0)
617}
618
619use strat9_abi::data::HandleInfo as HandleInfoAbi;
620
621/// Performs the cap perm bits operation.
622fn cap_perm_bits(p: CapPermissions) -> u32 {
623    (if p.read { 1 } else { 0 })
624        | (if p.write { 1 << 1 } else { 0 })
625        | (if p.execute { 1 << 2 } else { 0 })
626        | (if p.grant { 1 << 3 } else { 0 })
627        | (if p.revoke { 1 << 4 } else { 0 })
628}
629
630/// Performs the resource type code operation.
631fn resource_type_code(rt: ResourceType) -> u32 {
632    match rt {
633        ResourceType::MemoryRegion => 1,
634        ResourceType::IoPortRange => 2,
635        ResourceType::InterruptLine => 3,
636        ResourceType::IpcPort => 4,
637        ResourceType::Channel => 5,
638        ResourceType::SharedRing => 6,
639        ResourceType::Semaphore => 7,
640        ResourceType::Device => 8,
641        ResourceType::AddressSpace => 9,
642        ResourceType::Silo => 10,
643        ResourceType::Module => 11,
644        ResourceType::File => 12,
645        ResourceType::Nic => 13,
646        ResourceType::FileSystem => 14,
647        ResourceType::Console => 15,
648        ResourceType::Keyboard => 16,
649        ResourceType::Volume => 17,
650        ResourceType::Namespace => 18,
651    }
652}
653
654/// Performs the sys handle info operation.
655fn sys_handle_info(handle: u64, out_ptr: u64) -> Result<u64, SyscallError> {
656    crate::silo::enforce_cap_for_current_task(handle)?;
657    if out_ptr == 0 {
658        return Err(SyscallError::Fault);
659    }
660    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
661    let caps = unsafe { &*task.process.capabilities.get() };
662    let cap = caps
663        .get(CapId::from_raw(handle))
664        .ok_or(SyscallError::BadHandle)?;
665
666    let info = HandleInfoAbi {
667        resource_type: resource_type_code(cap.resource_type),
668        permissions: cap_perm_bits(cap.permissions),
669        resource: cap.resource as u64,
670    };
671    let user = UserSliceWrite::new(out_ptr, core::mem::size_of::<HandleInfoAbi>())?;
672    let bytes = unsafe {
673        core::slice::from_raw_parts(
674            &info as *const HandleInfoAbi as *const u8,
675            core::mem::size_of::<HandleInfoAbi>(),
676        )
677    };
678    user.copy_from(bytes);
679    Ok(0)
680}
681
682/// SYS_PROC_EXIT (300): Exit the current task.
683///
684/// Marks the task as Dead and yields. This function never returns to the caller.
685fn sys_proc_exit(exit_code: u64) -> Result<u64, SyscallError> {
686    log::info!("syscall: PROC_EXIT(code={})", exit_code);
687
688    // Mark current task as Dead and yield. The scheduler won't re-queue dead tasks.
689    // exit_current_task() diverges (-> !), so this function never returns.
690    crate::process::scheduler::exit_current_task(exit_code as i32)
691}
692
693/// SYS_PROC_YIELD (301): Yield the current time slice.
694fn sys_proc_yield() -> Result<u64, SyscallError> {
695    crate::process::yield_task();
696    Ok(0)
697}
698
699/// SYS_SIGPROCMASK (321): Examine and change blocked signals.
700///
701/// arg1 = how (0=BLOCK, 1=UNBLOCK, 2=SETMASK), arg2 = set_ptr (new mask), arg3 = oldset_ptr (old mask out)
702fn sys_sigprocmask(how: i32, set_ptr: u64, oldset_ptr: u64) -> Result<u64, SyscallError> {
703    use crate::process::current_task_clone;
704
705    const SIG_BLOCK: i32 = 0;
706    const SIG_UNBLOCK: i32 = 1;
707    const SIG_SETMASK: i32 = 2;
708
709    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
710
711    let blocked = &task.blocked_signals;
712
713    if oldset_ptr != 0 {
714        let old_mask = blocked.get_mask();
715        let user = UserSliceWrite::new(oldset_ptr, 8)?;
716        user.copy_from(&old_mask.to_ne_bytes());
717    }
718
719    if set_ptr != 0 {
720        let user = UserSliceRead::new(set_ptr, 8)?;
721        let mut buf = [0u8; 8];
722        user.copy_to(&mut buf);
723        let new_mask = u64::from_ne_bytes(buf);
724
725        let old_mask = blocked.get_mask();
726        let updated_mask = match how {
727            SIG_BLOCK => old_mask | new_mask,
728            SIG_UNBLOCK => old_mask & !new_mask,
729            SIG_SETMASK => new_mask,
730            _ => return Err(SyscallError::InvalidArgument),
731        };
732
733        blocked.set_mask(updated_mask);
734    }
735
736    Ok(0)
737}
738
739/// Performs the sys uname operation.
740fn sys_uname(uts_ptr: u64) -> Result<u64, SyscallError> {
741    const UTS_FIELD_LEN: usize = 65;
742    const UTS_TOTAL_LEN: usize = UTS_FIELD_LEN * 6;
743
744    if uts_ptr == 0 {
745        return Err(SyscallError::Fault);
746    }
747
748    /// Writes field.
749    fn write_field(dst: &mut [u8], src: &[u8]) {
750        let n = core::cmp::min(src.len(), dst.len().saturating_sub(1));
751        if n > 0 {
752            dst[..n].copy_from_slice(&src[..n]);
753        }
754        dst[n] = 0;
755    }
756
757    let mut uts = [0u8; UTS_TOTAL_LEN];
758    write_field(&mut uts[0 * UTS_FIELD_LEN..1 * UTS_FIELD_LEN], b"Strat9");
759    write_field(&mut uts[1 * UTS_FIELD_LEN..2 * UTS_FIELD_LEN], b"localhost");
760    write_field(&mut uts[2 * UTS_FIELD_LEN..3 * UTS_FIELD_LEN], b"0.1.0");
761    write_field(&mut uts[3 * UTS_FIELD_LEN..4 * UTS_FIELD_LEN], b"Strat9-OS");
762    write_field(&mut uts[4 * UTS_FIELD_LEN..5 * UTS_FIELD_LEN], b"x86_64");
763    write_field(
764        &mut uts[5 * UTS_FIELD_LEN..6 * UTS_FIELD_LEN],
765        b"localdomain",
766    );
767
768    let user = UserSliceWrite::new(uts_ptr, UTS_TOTAL_LEN)?;
769    user.copy_from(&uts);
770    Ok(0)
771}
772
773/// SYS_WRITE (404): Write bytes to a file descriptor.
774fn sys_write(fd: u64, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
775    crate::vfs::sys_write(fd as u32, buf_ptr, buf_len)
776}
777
778/// SYS_OPEN (403): Open a path from the minimal in-kernel namespace.
779fn sys_open(path_ptr: u64, path_len: u64, flags: u64) -> Result<u64, SyscallError> {
780    crate::vfs::sys_open(path_ptr, path_len, flags)
781}
782
783/// SYS_READ (405): Read bytes from a handle.
784fn sys_read(fd: u64, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
785    crate::vfs::sys_read(fd as u32, buf_ptr, buf_len)
786}
787
788/// SYS_CLOSE (406): Close a handle (fd).
789fn sys_close(fd: u64) -> Result<u64, SyscallError> {
790    crate::vfs::sys_close(fd as u32)
791}
792
793/// SYS_LSEEK (407): Seek in a file.
794fn sys_lseek(fd: u64, offset: u64, whence: u64) -> Result<u64, SyscallError> {
795    crate::vfs::sys_lseek(fd as u32, offset as i64, whence as u32)
796}
797
798/// SYS_FSTAT (408): Get metadata of an open file.
799fn sys_fstat(fd: u64, stat_ptr: u64) -> Result<u64, SyscallError> {
800    crate::vfs::sys_fstat(fd as u32, stat_ptr)
801}
802
803/// SYS_STAT (409): Get metadata by path.
804fn sys_stat(path_ptr: u64, path_len: u64, stat_ptr: u64) -> Result<u64, SyscallError> {
805    crate::vfs::sys_stat(path_ptr, path_len, stat_ptr)
806}
807
808/// SYS_GETDENTS (430): Read directory entries.
809fn sys_getdents(fd: u64, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
810    crate::vfs::sys_getdents(fd as u32, buf_ptr, buf_len)
811}
812
813/// SYS_PIPE (431): Create a pipe pair.
814fn sys_pipe(fds_ptr: u64) -> Result<u64, SyscallError> {
815    crate::vfs::sys_pipe(fds_ptr)
816}
817
818/// SYS_DUP (432): Duplicate a file descriptor.
819fn sys_dup(old_fd: u64) -> Result<u64, SyscallError> {
820    crate::vfs::sys_dup(old_fd as u32)
821}
822
823/// SYS_DUP2 (433): Duplicate fd to a specific number.
824fn sys_dup2(old_fd: u64, new_fd: u64) -> Result<u64, SyscallError> {
825    crate::vfs::sys_dup2(old_fd as u32, new_fd as u32)
826}
827
828// ============================================================
829// Volume / Block device syscalls
830// ============================================================
831
832const MAX_SECTORS_PER_CALL: u64 = 256;
833
834enum VolumeDeviceRef {
835    Virtio(&'static virtio_block::VirtioBlockDevice),
836    Ahci(&'static ahci::AhciController),
837}
838
839impl VolumeDeviceRef {
840    /// Performs the sector count operation.
841    fn sector_count(&self) -> u64 {
842        match self {
843            VolumeDeviceRef::Virtio(dev) => BlockDevice::sector_count(*dev),
844            VolumeDeviceRef::Ahci(dev) => BlockDevice::sector_count(*dev),
845        }
846    }
847
848    /// Reads sector.
849    fn read_sector(&self, sector: u64, buf: &mut [u8]) -> Result<(), SyscallError> {
850        match self {
851            VolumeDeviceRef::Virtio(dev) => {
852                BlockDevice::read_sector(*dev, sector, buf).map_err(SyscallError::from)
853            }
854            VolumeDeviceRef::Ahci(dev) => {
855                BlockDevice::read_sector(*dev, sector, buf).map_err(SyscallError::from)
856            }
857        }
858    }
859
860    /// Writes sector.
861    fn write_sector(&self, sector: u64, buf: &[u8]) -> Result<(), SyscallError> {
862        match self {
863            VolumeDeviceRef::Virtio(dev) => {
864                BlockDevice::write_sector(*dev, sector, buf).map_err(SyscallError::from)
865            }
866            VolumeDeviceRef::Ahci(dev) => {
867                BlockDevice::write_sector(*dev, sector, buf).map_err(SyscallError::from)
868            }
869        }
870    }
871}
872
873/// Performs the resolve volume device operation.
874fn resolve_volume_device(
875    handle: u64,
876    required: CapPermissions,
877) -> Result<VolumeDeviceRef, SyscallError> {
878    crate::silo::enforce_cap_for_current_task(handle)?;
879    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
880    let caps = unsafe { &*task.process.capabilities.get() };
881    let cap = caps
882        .get_with_permissions(CapId::from_raw(handle), required)
883        .ok_or(SyscallError::PermissionDenied)?;
884    if cap.resource_type != ResourceType::Volume {
885        return Err(SyscallError::BadHandle);
886    }
887
888    if let Some(device) = virtio_block::get_device() {
889        let device_ptr = device as *const virtio_block::VirtioBlockDevice as usize;
890        if cap.resource == device_ptr {
891            return Ok(VolumeDeviceRef::Virtio(device));
892        }
893    }
894    if let Some(device) = ahci::get_device() {
895        let device_ptr = device as *const ahci::AhciController as usize;
896        if cap.resource == device_ptr {
897            return Ok(VolumeDeviceRef::Ahci(device));
898        }
899    }
900    Err(SyscallError::BadHandle)
901}
902
903/// Performs the sys volume read operation.
904fn sys_volume_read(
905    handle: u64,
906    sector: u64,
907    buf_ptr: u64,
908    sector_count: u64,
909) -> Result<u64, SyscallError> {
910    if sector_count == 0 {
911        return Ok(0);
912    }
913    if sector_count > MAX_SECTORS_PER_CALL {
914        return Err(SyscallError::InvalidArgument);
915    }
916
917    let required = CapPermissions {
918        read: true,
919        write: false,
920        execute: false,
921        grant: false,
922        revoke: false,
923    };
924    let device = resolve_volume_device(handle, required)?;
925    let total_sectors = device.sector_count();
926    if sector >= total_sectors || sector.saturating_add(sector_count) > total_sectors {
927        return Err(SyscallError::InvalidArgument);
928    }
929
930    let probe_trace = sector == 0 && sector_count == 1;
931    if probe_trace {
932        crate::serial_println!(
933            "[volume-read] start handle={} sector={} total_sectors={} buf_ptr={:#x}",
934            handle,
935            sector,
936            total_sectors,
937            buf_ptr
938        );
939    }
940
941    let mut kbuf = [0u8; SECTOR_SIZE];
942    for i in 0..sector_count {
943        let cur_sector = sector.checked_add(i).ok_or(SyscallError::InvalidArgument)?;
944        if probe_trace {
945            crate::serial_println!(
946                "[volume-read] before read_sector handle={} sector={}",
947                handle,
948                cur_sector
949            );
950        }
951        match device.read_sector(cur_sector, &mut kbuf) {
952            Ok(()) => {
953                if probe_trace {
954                    crate::serial_println!(
955                        "[volume-read] after read_sector handle={} sector={} magic={:02x}{:02x}{:02x}{:02x}",
956                        handle,
957                        cur_sector,
958                        kbuf[0],
959                        kbuf[1],
960                        kbuf[2],
961                        kbuf[3]
962                    );
963                }
964            }
965            Err(err) => {
966                if probe_trace {
967                    crate::serial_println!(
968                        "[volume-read] read_sector failed handle={} sector={} err={:?}",
969                        handle,
970                        cur_sector,
971                        err
972                    );
973                }
974                return Err(err);
975            }
976        }
977        let offset = (i as usize)
978            .checked_mul(SECTOR_SIZE)
979            .ok_or(SyscallError::InvalidArgument)?;
980        let ptr = buf_ptr
981            .checked_add(offset as u64)
982            .ok_or(SyscallError::Fault)?;
983        let user = match UserSliceWrite::new(ptr, SECTOR_SIZE) {
984            Ok(user) => user,
985            Err(err) => {
986                if probe_trace {
987                    crate::serial_println!(
988                        "[volume-read] user slice failed handle={} sector={} ptr={:#x} err={:?}",
989                        handle,
990                        cur_sector,
991                        ptr,
992                        err
993                    );
994                }
995                return Err(SyscallError::from(err));
996            }
997        };
998        user.copy_from(&kbuf);
999        if probe_trace {
1000            crate::serial_println!(
1001                "[volume-read] copied to user handle={} sector={} ptr={:#x}",
1002                handle,
1003                cur_sector,
1004                ptr
1005            );
1006        }
1007    }
1008
1009    Ok(sector_count)
1010}
1011
1012/// Performs the sys volume write operation.
1013fn sys_volume_write(
1014    handle: u64,
1015    sector: u64,
1016    buf_ptr: u64,
1017    sector_count: u64,
1018) -> Result<u64, SyscallError> {
1019    if sector_count == 0 {
1020        return Ok(0);
1021    }
1022    if sector_count > MAX_SECTORS_PER_CALL {
1023        return Err(SyscallError::InvalidArgument);
1024    }
1025
1026    let required = CapPermissions {
1027        read: false,
1028        write: true,
1029        execute: false,
1030        grant: false,
1031        revoke: false,
1032    };
1033    let device = resolve_volume_device(handle, required)?;
1034    let total_sectors = device.sector_count();
1035    if sector >= total_sectors || sector.saturating_add(sector_count) > total_sectors {
1036        return Err(SyscallError::InvalidArgument);
1037    }
1038
1039    let mut kbuf = [0u8; SECTOR_SIZE];
1040    for i in 0..sector_count {
1041        let cur_sector = sector.checked_add(i).ok_or(SyscallError::InvalidArgument)?;
1042        let offset = (i as usize)
1043            .checked_mul(SECTOR_SIZE)
1044            .ok_or(SyscallError::InvalidArgument)?;
1045        let ptr = buf_ptr
1046            .checked_add(offset as u64)
1047            .ok_or(SyscallError::Fault)?;
1048        let user = UserSliceRead::new(ptr, SECTOR_SIZE)?;
1049        let data = user.read_to_vec();
1050        if data.len() != SECTOR_SIZE {
1051            return Err(SyscallError::InvalidArgument);
1052        }
1053        kbuf.copy_from_slice(&data);
1054        device.write_sector(cur_sector, &kbuf)?;
1055    }
1056
1057    Ok(sector_count)
1058}
1059
1060/// Performs the sys volume info operation.
1061fn sys_volume_info(handle: u64) -> Result<u64, SyscallError> {
1062    let required = CapPermissions {
1063        read: true,
1064        write: false,
1065        execute: false,
1066        grant: false,
1067        revoke: false,
1068    };
1069    let device = resolve_volume_device(handle, required)?;
1070    Ok(device.sector_count())
1071}
1072
1073/// SYS_DEBUG_LOG (600): Write a debug message to serial output.
1074///
1075/// arg1 = buffer pointer, arg2 = buffer length.
1076fn sys_debug_log(buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
1077    if buf_len == 0 {
1078        return Ok(0);
1079    }
1080
1081    // Restrict debug logging to admin or console-capable tasks.
1082    crate::silo::enforce_console_access()?;
1083
1084    let len = core::cmp::min(buf_len as usize, 4096);
1085
1086    // Validate the user buffer via UserSlice
1087    let user_buf = UserSliceRead::new(buf_ptr, len)?;
1088
1089    // Copy into kernel buffer
1090    let mut kbuf = [0u8; 4096];
1091    let copied = user_buf.copy_to(&mut kbuf);
1092
1093    let msg = core::str::from_utf8(&kbuf[..copied]).unwrap_or("<invalid utf8>");
1094
1095    // Write to E9 (lock-free) to prevent deadlocks
1096    crate::e9_println!("[user-debug] {}", msg);
1097
1098    // Mirror critical boot/network userspace logs to the serial console so
1099    // early silo failures are visible without attaching to per-silo output.
1100    if msg.starts_with("[init]")
1101        || msg.starts_with("[strate-net]")
1102        || msg.starts_with("[dhcp-client]")
1103    {
1104        crate::serial_print!("{}", msg);
1105    }
1106
1107    if let Some(task) = crate::process::current_task_clone() {
1108        if let Some(silo_id) = crate::silo::task_silo_id(task.id) {
1109            crate::silo::silo_output_write(silo_id, &kbuf[..copied]);
1110        }
1111    }
1112
1113    Ok(copied as u64)
1114}
1115
1116// ============================================================
1117// Network syscalls
1118// ============================================================
1119
1120/// Performs the sys net recv operation.
1121pub fn sys_net_recv(buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
1122    let device = crate::hardware::nic::get_default_device().ok_or(SyscallError::Again)?;
1123    let mut kbuf = vec![0u8; buf_len as usize];
1124
1125    let n = match device.receive(&mut kbuf) {
1126        Ok(n) => n,
1127        Err(e) => {
1128            let se = SyscallError::from(e);
1129            if se != SyscallError::Again
1130                && NET_RECV_ERR_LOG_BUDGET.fetch_sub(1, Ordering::Relaxed) > 0
1131            {
1132                crate::serial_println!("[net-sys] recv error: {:?} -> {}", e, se.name());
1133            }
1134            return Err(se);
1135        }
1136    };
1137    trace_dhcp_frame("rx", &kbuf[..n]);
1138
1139    let user = UserSliceWrite::new(buf_ptr, n)?;
1140    user.copy_from(&kbuf[..n]);
1141    Ok(n as u64)
1142}
1143
1144/// Performs the sys net send operation.
1145pub fn sys_net_send(buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
1146    let device = crate::hardware::nic::get_default_device().ok_or(SyscallError::Again)?;
1147    let user = UserSliceRead::new(buf_ptr, buf_len as usize)?;
1148    let kbuf = user.read_to_vec();
1149    trace_dhcp_frame("tx", &kbuf);
1150
1151    if let Err(e) = device.transmit(&kbuf) {
1152        let se = SyscallError::from(e);
1153        if NET_SEND_ERR_LOG_BUDGET.fetch_sub(1, Ordering::Relaxed) > 0 {
1154            crate::serial_println!("[net-sys] send error: {:?} -> {}", e, se.name());
1155        }
1156        return Err(se);
1157    }
1158
1159    Ok(buf_len)
1160}
1161
1162/// Performs the sys net info operation.
1163pub fn sys_net_info(info_type: u64, buf_ptr: u64) -> Result<u64, SyscallError> {
1164    let device = crate::hardware::nic::get_default_device().ok_or(SyscallError::Again)?;
1165
1166    match info_type {
1167        0 => {
1168            let mac = device.mac_address();
1169            let user = UserSliceWrite::new(buf_ptr, 6)?;
1170            user.copy_from(&mac);
1171            Ok(6)
1172        }
1173        _ => Err(SyscallError::InvalidArgument),
1174    }
1175}
1176
1177// ============================================================
1178// IPC syscalls (with capability enforcement)
1179// ============================================================
1180
1181/// Performs the sys ipc create port operation.
1182fn sys_ipc_create_port(_flags: u64) -> Result<u64, SyscallError> {
1183    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1184    let port_id = port::create_port(task.id);
1185    let cap = get_capability_manager().create_capability(
1186        ResourceType::IpcPort,
1187        port_id.as_u64() as usize,
1188        CapPermissions::all(),
1189    );
1190    let cap_id = unsafe { (&mut *task.process.capabilities.get()).insert(cap) };
1191    Ok(cap_id.as_u64())
1192}
1193
1194/// Performs the sys ipc send operation.
1195fn sys_ipc_send(port: u64, _msg_ptr: u64) -> Result<u64, SyscallError> {
1196    crate::silo::enforce_cap_for_current_task(port)?;
1197    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1198    let caps = unsafe { &*task.process.capabilities.get() };
1199    let required = CapPermissions {
1200        read: false,
1201        write: true,
1202        execute: false,
1203        grant: false,
1204        revoke: false,
1205    };
1206    let cap = caps
1207        .get_with_permissions(CapId::from_raw(port), required)
1208        .ok_or(SyscallError::PermissionDenied)?;
1209    if cap.resource_type != ResourceType::IpcPort {
1210        return Err(SyscallError::BadHandle);
1211    }
1212
1213    const MSG_SIZE: usize = core::mem::size_of::<IpcMessage>();
1214    let user = UserSliceRead::new(_msg_ptr, MSG_SIZE)?;
1215    let mut buf = [0u8; MSG_SIZE];
1216    user.copy_to(&mut buf);
1217    let mut msg = crate::ipc::message::ipc_message_from_raw(&buf);
1218
1219    // Stamp identity
1220    msg.sender = task.id.as_u64();
1221
1222    if msg.flags == 0 {
1223        if let Some((sid, _label, _mem_used, _mem_min, _mem_max)) =
1224            crate::silo::silo_info_for_task(task.id)
1225        {
1226            if let Some(snapshot) = crate::silo::list_silos_snapshot()
1227                .into_iter()
1228                .find(|s| s.id == sid)
1229            {
1230                let structured_label = crate::ipc::message::IpcLabel {
1231                    tier: snapshot.tier as u8,
1232                    family: 5,
1233                    compartment: sid as u16,
1234                };
1235                msg.flags = unsafe { core::mem::transmute(structured_label) };
1236            }
1237        }
1238    }
1239
1240    let port_id = PortId::from_u64(cap.resource as u64);
1241    let port = port::get_port(port_id).ok_or(SyscallError::BadHandle)?;
1242    port.send(msg).map_err(SyscallError::from)?;
1243    Ok(0)
1244}
1245
1246/// Performs the sys ipc recv operation.
1247fn sys_ipc_recv(port: u64, _msg_ptr: u64) -> Result<u64, SyscallError> {
1248    crate::silo::enforce_cap_for_current_task(port)?;
1249    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1250    let caps = unsafe { &*task.process.capabilities.get() };
1251    let required = CapPermissions {
1252        read: true,
1253        write: false,
1254        execute: false,
1255        grant: false,
1256        revoke: false,
1257    };
1258    let cap = caps
1259        .get_with_permissions(CapId::from_raw(port), required)
1260        .ok_or(SyscallError::PermissionDenied)?;
1261    if cap.resource_type != ResourceType::IpcPort {
1262        return Err(SyscallError::BadHandle);
1263    }
1264
1265    let port_id = PortId::from_u64(cap.resource as u64);
1266    let port = port::get_port(port_id).ok_or(SyscallError::BadHandle)?;
1267    let mut msg = port.recv().map_err(SyscallError::from)?;
1268
1269    // Handle transfer (optional): msg.flags contains a handle in the sender table.
1270    if msg.flags != 0 {
1271        let sender_id = crate::process::TaskId::from_u64(msg.sender);
1272        let sender = crate::process::get_task_by_id(sender_id).ok_or(SyscallError::BadHandle)?;
1273        let sender_caps = unsafe { &mut *sender.process.capabilities.get() };
1274        let dup = sender_caps
1275            .duplicate(CapId::from_raw(msg.flags as u64))
1276            .ok_or(SyscallError::PermissionDenied)?;
1277
1278        let receiver = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1279        let receiver_caps = unsafe { &mut *receiver.process.capabilities.get() };
1280        let new_id = insert_capability_with_retention(receiver_caps, dup)?;
1281        if new_id.as_u64() > u32::MAX as u64 {
1282            return Err(SyscallError::InvalidArgument);
1283        }
1284        msg.flags = new_id.as_u64() as u32;
1285    }
1286
1287    const MSG_SIZE: usize = core::mem::size_of::<IpcMessage>();
1288    let mut buf = [0u8; MSG_SIZE];
1289    crate::ipc::message::ipc_message_to_raw(&msg, &mut buf);
1290    let user = UserSliceWrite::new(_msg_ptr, MSG_SIZE)?;
1291    user.copy_from(&buf);
1292    Ok(0)
1293}
1294
1295/// Performs the sys ipc try recv operation.
1296fn sys_ipc_try_recv(port: u64, _msg_ptr: u64) -> Result<u64, SyscallError> {
1297    crate::silo::enforce_cap_for_current_task(port)?;
1298    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1299    let caps = unsafe { &*task.process.capabilities.get() };
1300    let required = CapPermissions {
1301        read: true,
1302        write: false,
1303        execute: false,
1304        grant: false,
1305        revoke: false,
1306    };
1307    let cap = caps
1308        .get_with_permissions(CapId::from_raw(port), required)
1309        .ok_or(SyscallError::PermissionDenied)?;
1310    if cap.resource_type != ResourceType::IpcPort {
1311        return Err(SyscallError::BadHandle);
1312    }
1313
1314    let port_id = PortId::from_u64(cap.resource as u64);
1315    let port = port::get_port(port_id).ok_or(SyscallError::BadHandle)?;
1316    let msg_opt = port.try_recv().map_err(SyscallError::from)?;
1317
1318    let mut msg = match msg_opt {
1319        Some(m) => m,
1320        None => return Err(SyscallError::Again),
1321    };
1322
1323    // Handle transfer (optional): msg.flags contains a handle in the sender table.
1324    if msg.flags != 0 {
1325        let sender_id = crate::process::TaskId::from_u64(msg.sender);
1326        let sender = crate::process::get_task_by_id(sender_id).ok_or(SyscallError::BadHandle)?;
1327        let sender_caps = unsafe { &mut *sender.process.capabilities.get() };
1328        let dup = sender_caps
1329            .duplicate(CapId::from_raw(msg.flags as u64))
1330            .ok_or(SyscallError::PermissionDenied)?;
1331
1332        let receiver = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1333        let receiver_caps = unsafe { &mut *receiver.process.capabilities.get() };
1334        let new_id = insert_capability_with_retention(receiver_caps, dup)?;
1335        if new_id.as_u64() > u32::MAX as u64 {
1336            return Err(SyscallError::InvalidArgument);
1337        }
1338        msg.flags = new_id.as_u64() as u32;
1339    }
1340
1341    const MSG_SIZE: usize = core::mem::size_of::<IpcMessage>();
1342    let mut buf = [0u8; MSG_SIZE];
1343    crate::ipc::message::ipc_message_to_raw(&msg, &mut buf);
1344    let user = UserSliceWrite::new(_msg_ptr, MSG_SIZE)?;
1345    user.copy_from(&buf);
1346    Ok(0)
1347}
1348
1349/// Performs the sys ipc connect operation.
1350fn sys_ipc_connect(path_ptr: u64, path_len: u64) -> Result<u64, SyscallError> {
1351    if path_ptr == 0 || path_len == 0 {
1352        return Err(SyscallError::Fault);
1353    }
1354    const MAX_PATH_LEN: usize = 4096;
1355    if path_len as usize > MAX_PATH_LEN {
1356        return Err(SyscallError::InvalidArgument);
1357    }
1358    let user = UserSliceRead::new(path_ptr, path_len as usize)?;
1359    let bytes = user.read_to_vec();
1360    let path = core::str::from_utf8(&bytes).map_err(SyscallError::from)?;
1361
1362    let (port_raw, _remaining) = crate::namespace::resolve(path).ok_or(SyscallError::NotFound)?;
1363    let port_id = PortId::from_u64(port_raw);
1364    if port::get_port(port_id).is_none() {
1365        return Err(SyscallError::BadHandle);
1366    }
1367
1368    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1369    let cap = get_capability_manager().create_capability(
1370        ResourceType::IpcPort,
1371        port_raw as usize,
1372        CapPermissions {
1373            read: true,
1374            write: true,
1375            execute: false,
1376            grant: false,
1377            revoke: false,
1378        },
1379    );
1380    let cap_id = unsafe { (&mut *task.process.capabilities.get()).insert(cap) };
1381    Ok(cap_id.as_u64())
1382}
1383
1384/// Performs the sys ipc call operation.
1385fn sys_ipc_call(port: u64, _msg_ptr: u64) -> Result<u64, SyscallError> {
1386    crate::silo::enforce_cap_for_current_task(port)?;
1387    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1388    let caps = unsafe { &*task.process.capabilities.get() };
1389    let required = CapPermissions {
1390        read: false,
1391        write: true,
1392        execute: false,
1393        grant: false,
1394        revoke: false,
1395    };
1396    let cap = caps
1397        .get_with_permissions(CapId::from_raw(port), required)
1398        .ok_or(SyscallError::PermissionDenied)?;
1399    if cap.resource_type != ResourceType::IpcPort {
1400        return Err(SyscallError::BadHandle);
1401    }
1402
1403    const MSG_SIZE: usize = core::mem::size_of::<IpcMessage>();
1404    let user = UserSliceRead::new(_msg_ptr, MSG_SIZE)?;
1405    let mut buf = [0u8; MSG_SIZE];
1406    user.copy_to(&mut buf);
1407    let mut msg = crate::ipc::message::ipc_message_from_raw(&buf);
1408    msg.sender = task.id.as_u64();
1409    if msg.flags != 0 {
1410        let transfer_required = CapPermissions {
1411            read: false,
1412            write: false,
1413            execute: false,
1414            grant: true,
1415            revoke: false,
1416        };
1417        if caps
1418            .get_with_permissions(CapId::from_raw(msg.flags as u64), transfer_required)
1419            .is_none()
1420        {
1421            return Err(SyscallError::PermissionDenied);
1422        }
1423    }
1424
1425    let port_id = PortId::from_u64(cap.resource as u64);
1426    let port = port::get_port(port_id).ok_or(SyscallError::BadHandle)?;
1427    let port_owner = port.owner;
1428    port.send(msg).map_err(SyscallError::from)?;
1429
1430    let reply_msg = reply::wait_for_reply(task.id, port_owner);
1431    let mut out_buf = [0u8; MSG_SIZE];
1432    crate::ipc::message::ipc_message_to_raw(&reply_msg, &mut out_buf);
1433    let user = UserSliceWrite::new(_msg_ptr, MSG_SIZE)?;
1434    user.copy_from(&out_buf);
1435    Ok(0)
1436}
1437
1438/// Performs the sys ipc reply operation.
1439fn sys_ipc_reply(_msg_ptr: u64) -> Result<u64, SyscallError> {
1440    if _msg_ptr == 0 {
1441        return Err(SyscallError::Fault);
1442    }
1443    const MSG_SIZE: usize = core::mem::size_of::<IpcMessage>();
1444    let user = UserSliceRead::new(_msg_ptr, MSG_SIZE)?;
1445    let mut buf = [0u8; MSG_SIZE];
1446    user.copy_to(&mut buf);
1447    let msg = crate::ipc::message::ipc_message_from_raw(&buf);
1448
1449    let target = crate::process::TaskId::from_u64(msg.sender);
1450    let mut msg = msg;
1451    if msg.flags != 0 {
1452        let sender = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1453        let sender_caps = unsafe { &mut *sender.process.capabilities.get() };
1454        let dup = sender_caps
1455            .duplicate(CapId::from_raw(msg.flags as u64))
1456            .ok_or(SyscallError::PermissionDenied)?;
1457
1458        let receiver = crate::process::get_task_by_id(target).ok_or(SyscallError::BadHandle)?;
1459        let receiver_caps = unsafe { &mut *receiver.process.capabilities.get() };
1460        let new_id = insert_capability_with_retention(receiver_caps, dup)?;
1461        if new_id.as_u64() > u32::MAX as u64 {
1462            return Err(SyscallError::InvalidArgument);
1463        }
1464        msg.flags = new_id.as_u64() as u32;
1465    }
1466
1467    reply::deliver_reply(target, msg).map_err(|_| SyscallError::BadHandle)?;
1468    Ok(0)
1469}
1470
1471/// Performs the sys ipc bind port operation.
1472fn sys_ipc_bind_port(port: u64, _path_ptr: u64, _path_len: u64) -> Result<u64, SyscallError> {
1473    crate::silo::enforce_registry_bind_for_current_task()?;
1474    crate::silo::enforce_cap_for_current_task(port)?;
1475    if _path_ptr == 0 || _path_len == 0 {
1476        return Err(SyscallError::Fault);
1477    }
1478    const MAX_PATH_LEN: usize = 4096;
1479    if _path_len as usize > MAX_PATH_LEN {
1480        return Err(SyscallError::InvalidArgument);
1481    }
1482    let user = UserSliceRead::new(_path_ptr, _path_len as usize)?;
1483    let bytes = user.read_to_vec();
1484    let path = core::str::from_utf8(&bytes).map_err(SyscallError::from)?;
1485
1486    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1487    let caps = unsafe { &*task.process.capabilities.get() };
1488    let cap = caps
1489        .get_with_permissions(
1490            CapId::from_raw(port),
1491            CapPermissions {
1492                read: true,
1493                write: true,
1494                execute: false,
1495                grant: true,
1496                revoke: false,
1497            },
1498        )
1499        .ok_or(SyscallError::PermissionDenied)?;
1500    if cap.resource_type != ResourceType::IpcPort {
1501        return Err(SyscallError::BadHandle);
1502    }
1503
1504    crate::vfs::mount(
1505        path,
1506        Arc::new(crate::vfs::IpcScheme::new(PortId::from_u64(
1507            cap.resource as u64,
1508        ))),
1509    )?;
1510    let _ = crate::namespace::bind(path, cap.resource as u64);
1511    let _ = crate::silo::set_current_silo_label_from_path(path);
1512
1513    // Bootstrap convenience: if a privileged userspace server binds root `/`
1514    // or a strate mountpoint, queue a bootstrap message.
1515    //
1516    // Volume capability seeding is optional (depends on a block device), but
1517    // bootstrap delivery must still happen so strate servers can bind their
1518    // label alias (`/srv/strate-fs-*/<label>`).
1519    let should_bootstrap = path == "/" || path.starts_with("/srv/strate-fs-");
1520    if should_bootstrap {
1521        let mut seeded_handle: u32 = 0;
1522        if let Some(device) = crate::hardware::storage::virtio_block::get_device() {
1523            let volume_resource = device as *const _ as usize;
1524            let volume_perms = CapPermissions {
1525                read: true,
1526                write: true,
1527                execute: false,
1528                grant: true,
1529                revoke: true,
1530            };
1531            let volume_cap = crate::capability::get_capability_manager().create_capability(
1532                ResourceType::Volume,
1533                volume_resource,
1534                volume_perms,
1535            );
1536            let task_caps = unsafe { &mut *task.process.capabilities.get() };
1537            let id = task_caps.insert(volume_cap);
1538            let _ = crate::silo::register_current_task_granted_resource(
1539                ResourceType::Volume,
1540                volume_resource,
1541                volume_perms,
1542            );
1543            if id.as_u64() <= u32::MAX as u64 {
1544                seeded_handle = id.as_u64() as u32;
1545            }
1546            log::info!(
1547                "ipc_bind_port('/'): seeded volume capability handle={} for task {:?}",
1548                id.as_u64(),
1549                task.id
1550            );
1551        }
1552
1553        // Send a bootstrap message to the just-bound filesystem server.
1554        // Message format:
1555        // - msg_type = 0x10
1556        // - flags = optional volume capability handle (0 if none)
1557        // - payload[0] = label length (u8)
1558        // - payload[1..] = UTF-8 label bytes (truncated to fit)
1559        const BOOTSTRAP_MSG_TYPE: u32 = 0x10;
1560        let mut boot_msg = IpcMessage::new(BOOTSTRAP_MSG_TYPE);
1561        // Use the bound task as sender so capability transfer path can
1562        // duplicate `flags` from a valid capability table when present.
1563        boot_msg.sender = task.id.as_u64();
1564        boot_msg.flags = seeded_handle;
1565        let label_owned = crate::silo::current_task_silo_label().unwrap_or_else(|| {
1566            if path == "/" {
1567                alloc::string::String::from("root")
1568            } else {
1569                alloc::string::String::from(
1570                    path.rsplit('/')
1571                        .find(|part| !part.is_empty())
1572                        .unwrap_or("default"),
1573                )
1574            }
1575        });
1576        let label_bytes = label_owned.as_bytes();
1577        let max_len = boot_msg.payload.len().saturating_sub(1);
1578        let copy_len = core::cmp::min(label_bytes.len(), max_len);
1579        boot_msg.payload[0] = copy_len as u8;
1580        if copy_len > 0 {
1581            boot_msg.payload[1..1 + copy_len].copy_from_slice(&label_bytes[..copy_len]);
1582        }
1583
1584        let port_id = PortId::from_u64(cap.resource as u64);
1585        if let Some(p) = port::get_port(port_id) {
1586            if p.send(boot_msg).is_ok() {
1587                log::info!(
1588                    "ipc_bind_port('{}'): queued bootstrap message (handle={}, label={})",
1589                    path,
1590                    seeded_handle,
1591                    label_owned
1592                );
1593            } else {
1594                log::warn!(
1595                    "ipc_bind_port('{}'): failed to queue bootstrap message",
1596                    path
1597                );
1598            }
1599        } else {
1600            log::warn!(
1601                "ipc_bind_port('{}'): bound port disappeared before bootstrap",
1602                path
1603            );
1604        }
1605    }
1606    Ok(0)
1607}
1608
1609/// Performs the sys ipc unbind port operation.
1610fn sys_ipc_unbind_port(path_ptr: u64, path_len: u64) -> Result<u64, SyscallError> {
1611    crate::silo::require_silo_admin()?;
1612    if path_ptr == 0 || path_len == 0 {
1613        return Err(SyscallError::Fault);
1614    }
1615    const MAX_PATH_LEN: usize = 4096;
1616    if path_len as usize > MAX_PATH_LEN {
1617        return Err(SyscallError::InvalidArgument);
1618    }
1619    let user = UserSliceRead::new(path_ptr, path_len as usize)?;
1620    let bytes = user.read_to_vec();
1621    let path = core::str::from_utf8(&bytes).map_err(SyscallError::from)?;
1622    let _ = crate::namespace::unbind(path);
1623    crate::vfs::unmount(path)?;
1624    Ok(0)
1625}
1626
1627/// Performs the sys ipc ring create operation.
1628fn sys_ipc_ring_create(_size: u64) -> Result<u64, SyscallError> {
1629    let size = usize::try_from(_size).map_err(|_| SyscallError::InvalidArgument)?;
1630    let ring_id = shared_ring::create_ring(size).map_err(|e| match e {
1631        shared_ring::RingError::InvalidSize => SyscallError::InvalidArgument,
1632        shared_ring::RingError::Alloc => SyscallError::OutOfMemory,
1633        shared_ring::RingError::NotFound => SyscallError::NotFound,
1634    })?;
1635
1636    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1637    let cap = get_capability_manager().create_capability(
1638        ResourceType::SharedRing,
1639        ring_id.as_u64() as usize,
1640        CapPermissions {
1641            read: true,
1642            write: true,
1643            execute: false,
1644            grant: true,
1645            revoke: true,
1646        },
1647    );
1648    let cap_id = unsafe { (&mut *task.process.capabilities.get()).insert(cap) };
1649    Ok(cap_id.as_u64())
1650}
1651
1652/// Performs the sys ipc ring map operation.
1653fn sys_ipc_ring_map(ring: u64, _out_ptr: u64) -> Result<u64, SyscallError> {
1654    crate::silo::enforce_cap_for_current_task(ring)?;
1655    if _out_ptr == 0 {
1656        return Err(SyscallError::Fault);
1657    }
1658
1659    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1660    let caps = unsafe { &*task.process.capabilities.get() };
1661    let required = CapPermissions {
1662        read: true,
1663        write: true,
1664        execute: false,
1665        grant: false,
1666        revoke: false,
1667    };
1668    let cap = caps
1669        .get_with_permissions(CapId::from_raw(ring), required)
1670        .ok_or(SyscallError::PermissionDenied)?;
1671    if cap.resource_type != ResourceType::SharedRing {
1672        return Err(SyscallError::BadHandle);
1673    }
1674
1675    let ring_id = RingId::from_u64(cap.resource as u64);
1676    let ring_obj = shared_ring::get_ring(ring_id).ok_or(SyscallError::BadHandle)?;
1677    let frame_phys_addrs = ring_obj.frame_phys_addrs();
1678    let mapping_cap_ids = ring_obj.mapping_cap_ids().to_vec();
1679    let page_count = ring_obj.page_count();
1680    let map_size = page_count
1681        .checked_mul(4096)
1682        .ok_or(SyscallError::InvalidArgument)? as u64;
1683
1684    let addr_space = task.process.address_space_arc();
1685    let base = addr_space
1686        .find_free_vma_range(
1687            super::mmap::MMAP_BASE,
1688            page_count,
1689            crate::memory::address_space::VmaPageSize::Small,
1690        )
1691        .ok_or(SyscallError::OutOfMemory)?;
1692
1693    addr_space
1694        .map_shared_frames_with_cap_ids(
1695            base,
1696            &frame_phys_addrs,
1697            Some(&mapping_cap_ids),
1698            crate::memory::address_space::VmaFlags {
1699                readable: true,
1700                writable: true,
1701                executable: false,
1702                user_accessible: true,
1703            },
1704            crate::memory::address_space::VmaType::Anonymous,
1705        )
1706        .map_err(|_| SyscallError::OutOfMemory)?;
1707
1708    let out = UserSliceWrite::new(_out_ptr, core::mem::size_of::<u64>())?;
1709    out.copy_from(&base.to_ne_bytes());
1710    Ok(map_size)
1711}
1712
1713/// Performs the sys sem create operation.
1714fn sys_sem_create(initial: u64) -> Result<u64, SyscallError> {
1715    let initial = u32::try_from(initial).map_err(|_| SyscallError::InvalidArgument)?;
1716    let sem_id = semaphore::create_semaphore(initial).map_err(|e| match e {
1717        semaphore::SemaphoreError::InvalidValue => SyscallError::InvalidArgument,
1718        semaphore::SemaphoreError::WouldBlock => SyscallError::Again,
1719        semaphore::SemaphoreError::Destroyed => SyscallError::Pipe,
1720        semaphore::SemaphoreError::NotFound => SyscallError::NotFound,
1721    })?;
1722
1723    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1724    let cap = get_capability_manager().create_capability(
1725        ResourceType::Semaphore,
1726        sem_id.as_u64() as usize,
1727        CapPermissions {
1728            read: true,
1729            write: true,
1730            execute: false,
1731            grant: true,
1732            revoke: true,
1733        },
1734    );
1735    let cap_id = unsafe { (&mut *task.process.capabilities.get()).insert(cap) };
1736    Ok(cap_id.as_u64())
1737}
1738
1739/// Performs the resolve sem operation.
1740fn resolve_sem(
1741    handle: u64,
1742    required: CapPermissions,
1743) -> Result<alloc::sync::Arc<semaphore::PosixSemaphore>, SyscallError> {
1744    crate::silo::enforce_cap_for_current_task(handle)?;
1745    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1746    let caps = unsafe { &*task.process.capabilities.get() };
1747    let cap = caps
1748        .get_with_permissions(CapId::from_raw(handle), required)
1749        .ok_or(SyscallError::PermissionDenied)?;
1750    if cap.resource_type != ResourceType::Semaphore {
1751        return Err(SyscallError::BadHandle);
1752    }
1753    let id = SemId::from_u64(cap.resource as u64);
1754    semaphore::get_semaphore(id).ok_or(SyscallError::BadHandle)
1755}
1756
1757/// Performs the sys sem wait operation.
1758fn sys_sem_wait(handle: u64) -> Result<u64, SyscallError> {
1759    let sem = resolve_sem(
1760        handle,
1761        CapPermissions {
1762            read: true,
1763            write: false,
1764            execute: false,
1765            grant: false,
1766            revoke: false,
1767        },
1768    )?;
1769    sem.wait().map_err(|e| match e {
1770        semaphore::SemaphoreError::WouldBlock => SyscallError::Again,
1771        semaphore::SemaphoreError::Destroyed => SyscallError::Pipe,
1772        semaphore::SemaphoreError::InvalidValue => SyscallError::InvalidArgument,
1773        semaphore::SemaphoreError::NotFound => SyscallError::NotFound,
1774    })?;
1775    Ok(0)
1776}
1777
1778/// Performs the sys sem trywait operation.
1779fn sys_sem_trywait(handle: u64) -> Result<u64, SyscallError> {
1780    let sem = resolve_sem(
1781        handle,
1782        CapPermissions {
1783            read: true,
1784            write: false,
1785            execute: false,
1786            grant: false,
1787            revoke: false,
1788        },
1789    )?;
1790    sem.try_wait().map_err(|e| match e {
1791        semaphore::SemaphoreError::WouldBlock => SyscallError::Again,
1792        semaphore::SemaphoreError::Destroyed => SyscallError::Pipe,
1793        semaphore::SemaphoreError::InvalidValue => SyscallError::InvalidArgument,
1794        semaphore::SemaphoreError::NotFound => SyscallError::NotFound,
1795    })?;
1796    Ok(0)
1797}
1798
1799/// Performs the sys sem post operation.
1800fn sys_sem_post(handle: u64) -> Result<u64, SyscallError> {
1801    let sem = resolve_sem(
1802        handle,
1803        CapPermissions {
1804            read: false,
1805            write: true,
1806            execute: false,
1807            grant: false,
1808            revoke: false,
1809        },
1810    )?;
1811    sem.post().map_err(|e| match e {
1812        semaphore::SemaphoreError::WouldBlock => SyscallError::Again,
1813        semaphore::SemaphoreError::Destroyed => SyscallError::Pipe,
1814        semaphore::SemaphoreError::InvalidValue => SyscallError::InvalidArgument,
1815        semaphore::SemaphoreError::NotFound => SyscallError::NotFound,
1816    })?;
1817    Ok(0)
1818}
1819
1820/// Performs the sys sem close operation.
1821fn sys_sem_close(handle: u64) -> Result<u64, SyscallError> {
1822    crate::silo::enforce_cap_for_current_task(handle)?;
1823    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1824    let caps = unsafe { &mut *task.process.capabilities.get() };
1825    let cap = caps
1826        .get(CapId::from_raw(handle))
1827        .ok_or(SyscallError::BadHandle)?;
1828    if cap.resource_type != ResourceType::Semaphore {
1829        return Err(SyscallError::BadHandle);
1830    }
1831    let cap = caps
1832        .remove(CapId::from_raw(handle))
1833        .ok_or(SyscallError::BadHandle)?;
1834    debug_assert_eq!(cap.resource_type, ResourceType::Semaphore);
1835    release_capability(&cap, Some(task.id));
1836    Ok(0)
1837}
1838
1839use strat9_abi::data::{
1840    PciAddress as PciAddressAbi, PciDeviceInfo as PciDeviceInfoAbi,
1841    PciProbeCriteria as PciProbeCriteriaAbi, PCI_MATCH_CLASS_CODE, PCI_MATCH_DEVICE_ID,
1842    PCI_MATCH_PROG_IF, PCI_MATCH_SUBCLASS, PCI_MATCH_VENDOR_ID,
1843};
1844
1845/// Reads pci address.
1846fn read_pci_address(addr_ptr: u64) -> Result<pci::PciAddress, SyscallError> {
1847    if addr_ptr == 0 {
1848        return Err(SyscallError::Fault);
1849    }
1850    let user = UserSliceRead::new(addr_ptr, core::mem::size_of::<PciAddressAbi>())?;
1851    let mut raw = [0u8; core::mem::size_of::<PciAddressAbi>()];
1852    user.copy_to(&mut raw);
1853    let abi = unsafe { core::ptr::read_unaligned(raw.as_ptr() as *const PciAddressAbi) };
1854    if abi.device > 31 || abi.function > 7 {
1855        return Err(SyscallError::InvalidArgument);
1856    }
1857    Ok(pci::PciAddress::new(abi.bus, abi.device, abi.function))
1858}
1859
1860/// Performs the sys pci enum operation.
1861fn sys_pci_enum(criteria_ptr: u64, out_ptr: u64, max_entries: u64) -> Result<u64, SyscallError> {
1862    if criteria_ptr == 0 || out_ptr == 0 {
1863        return Err(SyscallError::Fault);
1864    }
1865    if max_entries == 0 {
1866        return Ok(0);
1867    }
1868    let max_entries = core::cmp::min(max_entries as usize, 4096);
1869    let user_criteria =
1870        UserSliceRead::new(criteria_ptr, core::mem::size_of::<PciProbeCriteriaAbi>())?;
1871    let mut criteria_bytes = [0u8; core::mem::size_of::<PciProbeCriteriaAbi>()];
1872    user_criteria.copy_to(&mut criteria_bytes);
1873    let criteria_abi =
1874        unsafe { core::ptr::read_unaligned(criteria_bytes.as_ptr() as *const PciProbeCriteriaAbi) };
1875
1876    let criteria = pci::ProbeCriteria {
1877        vendor_id: if (criteria_abi.match_flags & PCI_MATCH_VENDOR_ID) != 0 {
1878            Some(criteria_abi.vendor_id)
1879        } else {
1880            None
1881        },
1882        device_id: if (criteria_abi.match_flags & PCI_MATCH_DEVICE_ID) != 0 {
1883            Some(criteria_abi.device_id)
1884        } else {
1885            None
1886        },
1887        class_code: if (criteria_abi.match_flags & PCI_MATCH_CLASS_CODE) != 0 {
1888            Some(criteria_abi.class_code)
1889        } else {
1890            None
1891        },
1892        subclass: if (criteria_abi.match_flags & PCI_MATCH_SUBCLASS) != 0 {
1893            Some(criteria_abi.subclass)
1894        } else {
1895            None
1896        },
1897        prog_if: if (criteria_abi.match_flags & PCI_MATCH_PROG_IF) != 0 {
1898            Some(criteria_abi.prog_if)
1899        } else {
1900            None
1901        },
1902    };
1903
1904    let devices = pci::probe_all(criteria);
1905    let count = core::cmp::min(devices.len(), max_entries);
1906    let mut out = alloc::vec::Vec::<PciDeviceInfoAbi>::with_capacity(count);
1907    for dev in devices.into_iter().take(count) {
1908        out.push(PciDeviceInfoAbi {
1909            address: PciAddressAbi {
1910                bus: dev.address.bus,
1911                device: dev.address.device,
1912                function: dev.address.function,
1913                _reserved: 0,
1914            },
1915            vendor_id: dev.vendor_id,
1916            device_id: dev.device_id,
1917            class_code: dev.class_code,
1918            subclass: dev.subclass,
1919            prog_if: dev.prog_if,
1920            revision: dev.revision,
1921            header_type: dev.header_type,
1922            interrupt_line: dev.interrupt_line,
1923            interrupt_pin: dev.interrupt_pin,
1924            _reserved: 0,
1925        });
1926    }
1927    let out_bytes_len = out
1928        .len()
1929        .checked_mul(core::mem::size_of::<PciDeviceInfoAbi>())
1930        .ok_or(SyscallError::InvalidArgument)?;
1931    let user_out = UserSliceWrite::new(out_ptr, out_bytes_len)?;
1932    let out_bytes =
1933        unsafe { core::slice::from_raw_parts(out.as_ptr() as *const u8, out_bytes_len) };
1934    user_out.copy_from(out_bytes);
1935    Ok(out.len() as u64)
1936}
1937
1938/// Performs the sys pci cfg read operation.
1939fn sys_pci_cfg_read(addr_ptr: u64, offset: u64, width: u64) -> Result<u64, SyscallError> {
1940    let dev_addr = read_pci_address(addr_ptr)?;
1941    let offset = u8::try_from(offset).map_err(|_| SyscallError::InvalidArgument)?;
1942    let width = u8::try_from(width).map_err(|_| SyscallError::InvalidArgument)?;
1943    if !matches!(width, 1 | 2 | 4) {
1944        return Err(SyscallError::InvalidArgument);
1945    }
1946    if offset > 0xFC || (offset as u16 + width as u16) > 0x100 {
1947        return Err(SyscallError::InvalidArgument);
1948    }
1949    if (width == 2 && (offset & 1) != 0) || (width == 4 && (offset & 3) != 0) {
1950        return Err(SyscallError::InvalidArgument);
1951    }
1952    let dev = pci::all_devices()
1953        .into_iter()
1954        .find(|d| d.address == dev_addr)
1955        .ok_or(SyscallError::NotFound)?;
1956    let value = match width {
1957        1 => dev.read_config_u8(offset) as u32,
1958        2 => dev.read_config_u16(offset) as u32,
1959        _ => dev.read_config_u32(offset),
1960    };
1961    Ok(value as u64)
1962}
1963
1964/// Performs the sys pci cfg write operation.
1965fn sys_pci_cfg_write(
1966    addr_ptr: u64,
1967    offset: u64,
1968    width: u64,
1969    value: u64,
1970) -> Result<u64, SyscallError> {
1971    let dev_addr = read_pci_address(addr_ptr)?;
1972    let offset = u8::try_from(offset).map_err(|_| SyscallError::InvalidArgument)?;
1973    let width = u8::try_from(width).map_err(|_| SyscallError::InvalidArgument)?;
1974    if !matches!(width, 1 | 2 | 4) {
1975        return Err(SyscallError::InvalidArgument);
1976    }
1977    if offset > 0xFC || (offset as u16 + width as u16) > 0x100 {
1978        return Err(SyscallError::InvalidArgument);
1979    }
1980    if (width == 2 && (offset & 1) != 0) || (width == 4 && (offset & 3) != 0) {
1981        return Err(SyscallError::InvalidArgument);
1982    }
1983    let dev = pci::all_devices()
1984        .into_iter()
1985        .find(|d| d.address == dev_addr)
1986        .ok_or(SyscallError::NotFound)?;
1987    match width {
1988        1 => dev.write_config_u8(offset, value as u8),
1989        2 => dev.write_config_u16(offset, value as u16),
1990        _ => dev.write_config_u32(offset, value as u32),
1991    }
1992    Ok(0)
1993}
1994
1995//  Typed MPMC sync-channel syscall handlers (IPC-02) ================================================================================
1996
1997/// SYS_CHAN_CREATE (220): create a bounded sync-channel.
1998///
1999/// arg1 = capacity (clamped to [1, 1024]).
2000/// Returns a capability handle whose `resource` field encodes the `ChanId`.
2001fn sys_chan_create(capacity: u64) -> Result<u64, SyscallError> {
2002    let cap = capacity.clamp(1, 1024) as usize;
2003    let chan_id = channel::create_channel(cap);
2004
2005    // Register a Channel capability in the current task's capability table.
2006    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
2007    let caps = unsafe { &mut *task.process.capabilities.get() };
2008    let cap_id = caps.insert(crate::capability::Capability {
2009        id: crate::capability::CapId::new(),
2010        permissions: crate::capability::CapPermissions {
2011            read: true,
2012            write: true,
2013            execute: false,
2014            grant: true,
2015            revoke: false,
2016        },
2017        resource_type: ResourceType::Channel,
2018        resource: chan_id.as_u64() as usize,
2019    });
2020
2021    log::debug!(
2022        "syscall: CHAN_CREATE(cap={}) → chan={} handle={}",
2023        cap,
2024        chan_id,
2025        cap_id.as_u64()
2026    );
2027    Ok(cap_id.as_u64())
2028}
2029
2030/// SYS_CHAN_SEND (221): send one `IpcMessage` to a channel, blocking if full.
2031///
2032/// arg1 = channel handle (CapId), arg2 = user pointer to 64-byte IpcMessage.
2033fn sys_chan_send(handle: u64, msg_ptr: u64) -> Result<u64, SyscallError> {
2034    crate::silo::enforce_cap_for_current_task(handle)?;
2035
2036    // Validate and copy the message from userspace.
2037    let user_slice = UserSliceRead::new(msg_ptr, 64).map_err(SyscallError::from)?;
2038    let mut msg = IpcMessage::new(0);
2039    // SAFETY: IpcMessage is repr(C), 64 bytes, fully initialised above.
2040    let n = user_slice.copy_to(unsafe {
2041        core::slice::from_raw_parts_mut(&mut msg as *mut IpcMessage as *mut u8, 64)
2042    });
2043    if n != 64 {
2044        return Err(SyscallError::Fault);
2045    }
2046
2047    // Fill in the sender task ID.
2048    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
2049    msg.sender = task.id.as_u64();
2050
2051    // Look up the channel capability.
2052    let caps = unsafe { &*task.process.capabilities.get() };
2053    let cap = caps
2054        .get(crate::capability::CapId::from_raw(handle))
2055        .ok_or(SyscallError::BadHandle)?;
2056    if cap.resource_type != ResourceType::Channel || !cap.permissions.write {
2057        return Err(SyscallError::PermissionDenied);
2058    }
2059    let chan_id = ChanId::from_u64(cap.resource as u64);
2060
2061    let chan = channel::get_channel(chan_id).ok_or(SyscallError::BadHandle)?;
2062    chan.send(msg).map_err(SyscallError::from)?;
2063
2064    Ok(0)
2065}
2066
2067/// SYS_CHAN_RECV (222): receive one `IpcMessage` from a channel, blocking if empty.
2068///
2069/// arg1 = channel handle (CapId), arg2 = user pointer to 64-byte output buffer.
2070fn sys_chan_recv(handle: u64, msg_ptr: u64) -> Result<u64, SyscallError> {
2071    crate::silo::enforce_cap_for_current_task(handle)?;
2072
2073    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
2074    let caps = unsafe { &*task.process.capabilities.get() };
2075    let cap = caps
2076        .get(crate::capability::CapId::from_raw(handle))
2077        .ok_or(SyscallError::BadHandle)?;
2078    if cap.resource_type != ResourceType::Channel || !cap.permissions.read {
2079        return Err(SyscallError::PermissionDenied);
2080    }
2081    let chan_id = ChanId::from_u64(cap.resource as u64);
2082
2083    let chan = channel::get_channel(chan_id).ok_or(SyscallError::BadHandle)?;
2084    let msg = chan.recv().map_err(SyscallError::from)?;
2085
2086    // Write the received message to userspace.
2087    let user_slice = UserSliceWrite::new(msg_ptr, 64).map_err(SyscallError::from)?;
2088    // SAFETY: IpcMessage is repr(C), 64 bytes.
2089    let n = user_slice.copy_from(unsafe {
2090        core::slice::from_raw_parts(&msg as *const IpcMessage as *const u8, 64)
2091    });
2092    if n != 64 {
2093        return Err(SyscallError::Fault);
2094    }
2095
2096    Ok(0)
2097}
2098
2099/// SYS_CHAN_TRY_RECV (223): non-blocking receive.
2100///
2101/// Returns 0 if a message was delivered, -EWOULDBLOCK if the channel is empty.
2102fn sys_chan_try_recv(handle: u64, msg_ptr: u64) -> Result<u64, SyscallError> {
2103    crate::silo::enforce_cap_for_current_task(handle)?;
2104
2105    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
2106    let caps = unsafe { &*task.process.capabilities.get() };
2107    let cap = caps
2108        .get(crate::capability::CapId::from_raw(handle))
2109        .ok_or(SyscallError::BadHandle)?;
2110    if cap.resource_type != ResourceType::Channel || !cap.permissions.read {
2111        return Err(SyscallError::PermissionDenied);
2112    }
2113    let chan_id = ChanId::from_u64(cap.resource as u64);
2114
2115    let chan = channel::get_channel(chan_id).ok_or(SyscallError::BadHandle)?;
2116    match chan.try_recv() {
2117        Ok(msg) => {
2118            let user_slice = UserSliceWrite::new(msg_ptr, 64).map_err(SyscallError::from)?;
2119            // SAFETY: IpcMessage is repr(C), 64 bytes.
2120            let n = user_slice.copy_from(unsafe {
2121                core::slice::from_raw_parts(&msg as *const IpcMessage as *const u8, 64)
2122            });
2123            if n != 64 {
2124                return Err(SyscallError::Fault);
2125            }
2126            Ok(0)
2127        }
2128        Err(e) => Err(SyscallError::from(e)),
2129    }
2130}
2131
2132/// SYS_CHAN_CLOSE (224): destroy a channel and remove it from the registry.
2133///
2134/// Wakes all tasks blocked on this channel with `Disconnected`.
2135fn sys_chan_close(handle: u64) -> Result<u64, SyscallError> {
2136    crate::silo::enforce_cap_for_current_task(handle)?;
2137
2138    let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
2139    let caps = unsafe { &mut *task.process.capabilities.get() };
2140    let cap = caps
2141        .get(crate::capability::CapId::from_raw(handle))
2142        .ok_or(SyscallError::BadHandle)?;
2143    if cap.resource_type != ResourceType::Channel {
2144        return Err(SyscallError::BadHandle);
2145    }
2146    let chan_id = ChanId::from_u64(cap.resource as u64);
2147    let cap = caps
2148        .remove(crate::capability::CapId::from_raw(handle))
2149        .ok_or(SyscallError::BadHandle)?;
2150    debug_assert_eq!(cap.resource_type, ResourceType::Channel);
2151    release_capability(&cap, Some(task.id));
2152
2153    log::debug!("syscall: CHAN_CLOSE(handle={}) → chan={}", handle, chan_id);
2154    Ok(0)
2155}