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