Skip to main content

strat9_syscall/
lib.rs

1#![no_std]
2
3// Architecture-specific definitions
4pub mod arch;
5pub use arch::*;
6
7// Complex structures that are used for some system calls
8pub mod data;
9pub use data::*;
10
11// All errors that can be generated by a system call
12pub mod error;
13pub use error::*;
14
15// Flags used as an argument to many system calls
16pub mod flag;
17pub use flag::*;
18
19// Functions for low level hardware control
20pub mod io;
21pub use io::*;
22
23// Call numbers used by each system call
24pub mod number;
25pub use number::*;
26
27// ABI for shared memory based signals
28pub mod sigabi;
29pub use sigabi::*;
30
31// V2 scheme format
32pub mod schemev2;
33pub use schemev2::*;
34
35// Directory entries
36pub mod dirent;
37pub use dirent::*;
38
39// Clock IDs for clock_gettime (POSIX-compatible)
40pub const CLOCK_REALTIME: u32 = 0;
41pub const CLOCK_MONOTONIC: u32 = 1;
42
43// High-level call functions are defined inline below (after syscall0..6).
44
45// ---------------------------------------------------------------------------
46// Syscall raw invocation functions (x86_64 syscall ABI)
47// ---------------------------------------------------------------------------
48// Instead of a deeply-nested macro (which recent Rust nightlies have trouble
49// parsing), each function is written out explicitly. There are only 7 variants
50// (0..6 arguments) so the duplication is negligible and much more readable.
51
52#[cfg(feature = "userspace")]
53/// Invoke a syscall with no argument registers.
54pub unsafe fn syscall0(mut a: usize) -> error::Result<usize> {
55    core::arch::asm!(
56        "syscall",
57        inout("rax") a,
58        out("rcx") _,
59        out("r11") _,
60        clobber_abi("C"),
61        options(nostack),
62    );
63    error::Error::demux(a)
64}
65
66#[cfg(feature = "userspace")]
67/// Invoke a syscall with one argument (`rdi`).
68pub unsafe fn syscall1(mut a: usize, b: usize) -> error::Result<usize> {
69    core::arch::asm!(
70        "syscall",
71        inout("rax") a,
72        in("rdi") b,
73        out("rcx") _,
74        out("r11") _,
75        clobber_abi("C"),
76        options(nostack),
77    );
78    error::Error::demux(a)
79}
80
81#[cfg(feature = "userspace")]
82/// Invoke a syscall with two arguments (`rdi`, `rsi`).
83pub unsafe fn syscall2(mut a: usize, b: usize, c: usize) -> error::Result<usize> {
84    core::arch::asm!(
85        "syscall",
86        inout("rax") a,
87        in("rdi") b,
88        in("rsi") c,
89        out("rcx") _,
90        out("r11") _,
91        clobber_abi("C"),
92        options(nostack),
93    );
94    error::Error::demux(a)
95}
96
97#[cfg(feature = "userspace")]
98/// Invoke a syscall with three arguments (`rdi`, `rsi`, `rdx`).
99pub unsafe fn syscall3(mut a: usize, b: usize, c: usize, d: usize) -> error::Result<usize> {
100    core::arch::asm!(
101        "syscall",
102        inout("rax") a,
103        in("rdi") b,
104        in("rsi") c,
105        in("rdx") d,
106        out("rcx") _,
107        out("r11") _,
108        clobber_abi("C"),
109        options(nostack),
110    );
111    error::Error::demux(a)
112}
113
114#[cfg(feature = "userspace")]
115/// Invoke a syscall with four arguments (`rdi`, `rsi`, `rdx`, `r10`).
116pub unsafe fn syscall4(
117    mut a: usize,
118    b: usize,
119    c: usize,
120    d: usize,
121    e: usize,
122) -> error::Result<usize> {
123    core::arch::asm!(
124        "syscall",
125        inout("rax") a,
126        in("rdi") b,
127        in("rsi") c,
128        in("rdx") d,
129        in("r10") e,
130        out("rcx") _,
131        out("r11") _,
132        clobber_abi("C"),
133        options(nostack),
134    );
135    error::Error::demux(a)
136}
137
138#[cfg(feature = "userspace")]
139/// Invoke a syscall with five arguments (`rdi`, `rsi`, `rdx`, `r10`, `r8`).
140pub unsafe fn syscall5(
141    mut a: usize,
142    b: usize,
143    c: usize,
144    d: usize,
145    e: usize,
146    f: usize,
147) -> error::Result<usize> {
148    core::arch::asm!(
149        "syscall",
150        inout("rax") a,
151        in("rdi") b,
152        in("rsi") c,
153        in("rdx") d,
154        in("r10") e,
155        in("r8") f,
156        out("rcx") _,
157        out("r11") _,
158        clobber_abi("C"),
159        options(nostack),
160    );
161    error::Error::demux(a)
162}
163
164#[cfg(feature = "userspace")]
165/// Invoke a syscall with six arguments (`rdi`, `rsi`, `rdx`, `r10`, `r8`, `r9`).
166pub unsafe fn syscall6(
167    mut a: usize,
168    b: usize,
169    c: usize,
170    d: usize,
171    e: usize,
172    f: usize,
173    g: usize,
174) -> error::Result<usize> {
175    core::arch::asm!(
176        "syscall",
177        inout("rax") a,
178        in("rdi") b,
179        in("rsi") c,
180        in("rdx") d,
181        in("r10") e,
182        in("r8") f,
183        in("r9") g,
184        out("rcx") _,
185        out("r11") _,
186        clobber_abi("C"),
187        options(nostack),
188    );
189    error::Error::demux(a)
190}
191
192// High-level syscall functions
193#[cfg(feature = "userspace")]
194pub mod call {
195    use super::*;
196    const MAP_PRIVATE: usize = 1 << 1;
197    const MAP_ANONYMOUS: usize = 1 << 5;
198    pub const WNOHANG: usize = 1;
199
200    const FUTEX_WAIT: usize = 0;
201    const FUTEX_WAKE: usize = 1;
202    const FUTEX_REQUEUE: usize = 3;
203    const FUTEX_CMP_REQUEUE: usize = 4;
204    const FUTEX_WAKE_OP: usize = 5;
205
206    /// Close a file
207    pub fn close(fd: usize) -> error::Result<usize> {
208        unsafe { syscall1(number::SYS_CLOSE, fd) }
209    }
210
211    /// Get the current system time.
212    ///
213    /// # Arguments
214    /// * `clock_id` - Clock identifier (CLOCK_MONOTONIC or CLOCK_REALTIME)
215    /// * `tp` - Mutable reference to timespec structure to fill
216    ///
217    /// # Returns
218    /// * `Ok(0)` on success
219    /// * `Err(Error::InvalidArgument)` if clock_id is invalid
220    /// * `Err(Error::Fault)` if tp pointer is invalid
221    pub fn clock_gettime(clock_id: u32, tp: &mut data::TimeSpec) -> error::Result<usize> {
222        unsafe {
223            syscall2(
224                number::SYS_CLOCK_GETTIME,
225                clock_id as usize,
226                tp as *mut data::TimeSpec as usize,
227            )
228        }
229    }
230
231    /// Duplicate a capability handle (legacy).
232    pub fn handle_dup(fd: usize) -> error::Result<usize> {
233        unsafe { syscall1(number::SYS_HANDLE_DUPLICATE, fd) }
234    }
235
236    /// Duplicate a file descriptor (POSIX dup). Returns the new fd.
237    pub fn dup(fd: usize) -> error::Result<usize> {
238        unsafe { syscall1(number::SYS_DUP, fd) }
239    }
240
241    /// Duplicate a file descriptor to a specific number (POSIX dup2).
242    pub fn dup2(old_fd: usize, new_fd: usize) -> error::Result<usize> {
243        unsafe { syscall2(number::SYS_DUP2, old_fd, new_fd) }
244    }
245
246    /// Create a pipe. Returns (read_fd, write_fd).
247    pub fn pipe() -> error::Result<(u32, u32)> {
248        let mut fds = [0u32; 2];
249        unsafe {
250            syscall1(number::SYS_PIPE, fds.as_mut_ptr() as usize)?;
251        }
252        Ok((fds[0], fds[1]))
253    }
254
255    /// Change file permissions
256    pub fn fchmod(fd: usize, mode: u16) -> error::Result<usize> {
257        let _ = (fd, mode);
258        Err(error::Error::NotSupported)
259    }
260
261    /// Change file ownership
262    pub fn fchown(fd: usize, uid: u32, gid: u32) -> error::Result<usize> {
263        let _ = (fd, uid, gid);
264        Err(error::Error::NotSupported)
265    }
266
267    /// Change file descriptor flags
268    pub fn fcntl(fd: usize, cmd: usize, arg: usize) -> error::Result<usize> {
269        unsafe { syscall3(number::SYS_FCNTL, fd, cmd, arg) }
270    }
271
272    /// Map a file into memory, but with the ability to set the address to map into, either as a hint
273    /// or as a requirement of the map.
274    ///
275    /// # Errors
276    /// `EACCES` - the file descriptor was not open for reading
277    /// `EBADF` - if the file descriptor was invalid
278    /// `ENODEV` - mmapping was not supported
279    /// `EINVAL` - invalid combination of flags
280    /// `EEXIST` - if [`MapFlags::MAP_FIXED`] was set, and the address specified was already in use.
281    ///
282    pub unsafe fn fmap(fd: usize, map: &data::Map) -> error::Result<usize> {
283        // ABI v2 exposes SYS_MMAP directly: (addr, len, prot, flags, fd, offset).
284        let prot = (map.flags as usize) & 0x7;
285        let mut flags = map.flags as usize;
286        // Ensure kernel-required anonymous private default for legacy callers.
287        if flags & (1 << 0 | MAP_PRIVATE) == 0 {
288            flags |= MAP_PRIVATE;
289        }
290        flags |= MAP_ANONYMOUS;
291        syscall6(
292            number::SYS_MMAP,
293            map.addr,
294            map.size,
295            prot,
296            flags,
297            fd,
298            map.offset,
299        )
300    }
301
302    /// Unmap whole (or partial) continous memory-mapped files
303    pub unsafe fn funmap(addr: usize, len: usize) -> error::Result<usize> {
304        syscall2(number::SYS_MUNMAP, addr, len)
305    }
306
307    /// Retrieve the canonical path of a file
308    pub fn fpath(fd: usize, buf: &mut [u8]) -> error::Result<usize> {
309        let _ = (fd, buf);
310        Err(error::Error::NotSupported)
311    }
312
313    /// Create a link to a file
314    pub fn flink<T: AsRef<str>>(fd: usize, path: T) -> error::Result<usize> {
315        let _ = (fd, path.as_ref());
316        Err(error::Error::NotSupported)
317    }
318
319    /// Rename a file
320    pub fn frename<T: AsRef<str>>(fd: usize, path: T) -> error::Result<usize> {
321        let _ = (fd, path.as_ref());
322        Err(error::Error::NotSupported)
323    }
324
325    /// Get metadata about a file (legacy Stat struct).
326    pub fn fstat_legacy(fd: usize, stat: &mut data::Stat) -> error::Result<usize> {
327        let _ = (fd, stat);
328        Err(error::Error::NotSupported)
329    }
330
331    /// Get metadata about an open file descriptor.
332    pub fn fstat(fd: usize, stat: &mut data::FileStat) -> error::Result<usize> {
333        unsafe { syscall2(number::SYS_FSTAT, fd, stat as *mut data::FileStat as usize) }
334    }
335
336    /// Get metadata by path.
337    pub fn stat(path: &str, stat: &mut data::FileStat) -> error::Result<usize> {
338        unsafe {
339            syscall3(
340                number::SYS_STAT,
341                path.as_ptr() as usize,
342                path.len(),
343                stat as *mut data::FileStat as usize,
344            )
345        }
346    }
347
348    /// Read directory entries from an open directory fd.
349    ///
350    /// Fills `buf` with packed kernel dirent entries.
351    /// Returns the number of bytes written into `buf`.
352    pub fn getdents(fd: usize, buf: &mut [u8]) -> error::Result<usize> {
353        unsafe {
354            syscall3(
355                number::SYS_GETDENTS,
356                fd,
357                buf.as_mut_ptr() as usize,
358                buf.len(),
359            )
360        }
361    }
362
363    /// Get metadata about a filesystem
364    pub fn fstatvfs(fd: usize, stat: &mut data::StatVfs) -> error::Result<usize> {
365        let _ = (fd, stat);
366        Err(error::Error::NotSupported)
367    }
368
369    /// Sync a file descriptor to its underlying medium
370    pub fn fsync(fd: usize) -> error::Result<usize> {
371        let _ = fd;
372        Err(error::Error::NotSupported)
373    }
374
375    /// Truncate or extend a file to a specified length
376    pub fn ftruncate(fd: usize, len: usize) -> error::Result<usize> {
377        let _ = (fd, len);
378        Err(error::Error::NotSupported)
379    }
380
381    /// Set access/modify timestamps for an open file descriptor.
382    pub fn futimens(fd: usize, times: &[data::TimeSpec]) -> error::Result<usize> {
383        let _ = (fd, times);
384        Err(error::Error::NotSupported)
385    }
386
387    /// Fast userspace mutex
388    pub unsafe fn futex(
389        addr: *mut i32,
390        op: usize,
391        val: i32,
392        val2: usize,
393        addr2: *mut i32,
394    ) -> error::Result<usize> {
395        match op {
396            FUTEX_WAIT => syscall3(number::SYS_FUTEX_WAIT, addr as usize, val as usize, val2),
397            FUTEX_WAKE => syscall2(number::SYS_FUTEX_WAKE, addr as usize, val as usize),
398            FUTEX_REQUEUE => syscall4(
399                number::SYS_FUTEX_REQUEUE,
400                addr as usize,
401                val as usize,
402                val2,
403                addr2 as usize,
404            ),
405            FUTEX_CMP_REQUEUE => syscall5(
406                number::SYS_FUTEX_CMP_REQUEUE,
407                addr as usize,
408                val as usize,
409                val2,
410                addr2 as usize,
411                0,
412            ),
413            FUTEX_WAKE_OP => syscall5(
414                number::SYS_FUTEX_WAKE_OP,
415                addr as usize,
416                val as usize,
417                val2,
418                addr2 as usize,
419                0,
420            ),
421            _ => Err(error::Error::InvalidArgument),
422        }
423    }
424
425    /// Seek to `offset` bytes in a file descriptor.
426    ///
427    /// `whence`: 0=SEEK_SET, 1=SEEK_CUR, 2=SEEK_END.
428    /// Returns the new absolute offset.
429    pub fn lseek(fd: usize, offset: isize, whence: usize) -> error::Result<usize> {
430        unsafe { syscall3(number::SYS_LSEEK, fd, offset as usize, whence) }
431    }
432
433    /// Make a new scheme namespace
434    pub fn mkns(schemes: &[[usize; 2]]) -> error::Result<usize> {
435        let _ = schemes;
436        Err(error::Error::NotSupported)
437    }
438
439    /// Change mapping flags
440    pub unsafe fn mprotect(
441        addr: usize,
442        size: usize,
443        flags: flag::MapFlags,
444    ) -> error::Result<usize> {
445        syscall3(number::SYS_MPROTECT, addr, size, flags.bits() as usize)
446    }
447
448    /// Sleep for the time specified in `req`
449    pub fn nanosleep(req: &data::TimeSpec, rem: &mut data::TimeSpec) -> error::Result<usize> {
450        unsafe {
451            syscall2(
452                number::SYS_NANOSLEEP,
453                req as *const data::TimeSpec as usize,
454                rem as *mut data::TimeSpec as usize,
455            )
456        }
457    }
458
459    /// Open a file at a specific path
460    pub fn openat<T: AsRef<str>>(
461        fd: usize,
462        path: T,
463        flags: usize,
464        fcntl_flags: usize,
465    ) -> error::Result<usize> {
466        let _ = fd;
467        let path = path.as_ref();
468        unsafe {
469            syscall3(
470                number::SYS_OPEN,
471                path.as_ptr() as usize,
472                path.len(),
473                flags | fcntl_flags,
474            )
475        }
476    }
477
478    /// Open a file at a specific path with POSIX flags.
479    ///
480    /// This is a convenience wrapper that converts POSIX `O_*` flags to Strat9 ABI flags.
481    /// For direct Strat9 ABI usage, prefer [`openat`].
482    ///
483    /// # Arguments
484    /// * `path` - Path to the file to open
485    /// * `posix_flags` - POSIX O_* flags (e.g., `O_RDONLY`, `O_CREAT`, `O_WRONLY`)
486    ///
487    /// # Example
488    /// ```no_run
489    /// use strat9_syscall::{call, flag};
490    /// // Open a file read-only using POSIX flags
491    /// let fd = call::open("/etc/passwd", flag::O_RDONLY).unwrap();
492    /// ```
493    pub fn open<T: AsRef<str>>(path: T, posix_flags: u32) -> error::Result<usize> {
494        let strat9_flags = flag::posix_oflags_to_strat9(posix_flags);
495        openat(0, path, strat9_flags.bits() as usize, 0)
496    }
497
498    /// Open a file at a specific path with filter
499    pub fn openat_with_filter<T: AsRef<str>>(
500        fd: usize,
501        path: T,
502        flags: usize,
503        fcntl_flags: usize,
504        euid: u32,
505        egid: u32,
506    ) -> error::Result<usize> {
507        let _ = (euid, egid);
508        openat(fd, path, flags, fcntl_flags)
509    }
510
511    /// Remove a file at at specific path
512    pub fn unlinkat<T: AsRef<str>>(fd: usize, path: T, flags: usize) -> error::Result<usize> {
513        let _ = (fd, path.as_ref(), flags);
514        Err(error::Error::NotSupported)
515    }
516
517    /// Remove a file at at specific path with filter
518    pub fn unlinkat_with_filter<T: AsRef<str>>(
519        fd: usize,
520        path: T,
521        flags: usize,
522        euid: u32,
523        egid: u32,
524    ) -> error::Result<usize> {
525        let _ = (euid, egid);
526        unlinkat(fd, path, flags)
527    }
528
529    /// Read from a file descriptor into a buffer
530    pub fn read(fd: usize, buf: &mut [u8]) -> error::Result<usize> {
531        unsafe { syscall3(number::SYS_READ, fd, buf.as_mut_ptr() as usize, buf.len()) }
532    }
533
534    /// Write a buffer to a file descriptor
535    ///
536    /// The kernel will attempt to write the bytes in `buf` to the file descriptor `fd`, returning
537    /// either an `Err`, explained below, or `Ok(count)` where `count` is the number of bytes which
538    /// were written.
539    ///
540    /// # Errors
541    ///
542    /// * `EAGAIN` - the file descriptor was opened with `O_NONBLOCK` and writing would block
543    /// * `EBADF` - the file descriptor is not valid or is not open for writing
544    /// * `EFAULT` - `buf` does not point to the process's addressible memory
545    /// * `EIO` - an I/O error occurred
546    /// * `ENOSPC` - the device containing the file descriptor has no room for data
547    /// * `EPIPE` - the file descriptor refers to a pipe or socket whose reading end is closed
548    pub fn write(fd: usize, buf: &[u8]) -> error::Result<usize> {
549        unsafe { syscall3(number::SYS_WRITE, fd, buf.as_ptr() as usize, buf.len()) }
550    }
551
552    /// Terminate the current process with the given exit `code`.
553    ///
554    /// This function never returns.
555    pub fn exit(code: usize) -> ! {
556        unsafe {
557            syscall1(number::SYS_PROC_EXIT, code).ok();
558        }
559        #[allow(clippy::empty_loop)]
560        loop {
561            core::hint::spin_loop();
562        }
563    }
564
565    /// Yield the process's time slice to the kernel
566    ///
567    /// This function will return Ok(0) on success
568    pub fn sched_yield() -> error::Result<usize> {
569        unsafe { syscall0(number::SYS_PROC_YIELD) }
570    }
571
572    /// Fork the current process.
573    ///
574    /// Returns child PID in parent, and 0 in child.
575    pub fn fork() -> error::Result<usize> {
576        unsafe { syscall0(number::SYS_PROC_FORK) }
577    }
578
579    /// Replace the current process image with a new program.
580    ///
581    /// `path`: executable path as a nul-terminated C string byte slice.
582    /// `argv` and `envp`: pointers to null-terminated C-style arrays.
583    pub unsafe fn execve(path: &[u8], argv: usize, envp: usize) -> error::Result<usize> {
584        syscall3(number::SYS_PROC_EXECVE, path.as_ptr() as usize, argv, envp)
585    }
586
587    /// Return current process ID.
588    pub fn getpid() -> error::Result<usize> {
589        unsafe { syscall0(number::SYS_GETPID) }
590    }
591
592    /// Return current thread ID.
593    pub fn gettid() -> error::Result<usize> {
594        unsafe { syscall0(number::SYS_GETTID) }
595    }
596
597    /// Create a new userspace thread in the current process.
598    ///
599    /// - `entry`: user function address (RIP)
600    /// - `stack_top`: user stack top (RSP), 16-byte aligned
601    /// - `arg0`: first argument passed in RDI
602    /// - `tls_base`: optional FS base for thread-local storage
603    ///
604    /// Returns the new thread TID.
605    pub fn thread_create(
606        entry: usize,
607        stack_top: usize,
608        arg0: usize,
609        tls_base: usize,
610    ) -> error::Result<usize> {
611        unsafe {
612            syscall5(
613                number::SYS_THREAD_CREATE,
614                entry,
615                stack_top,
616                arg0,
617                0,
618                tls_base,
619            )
620        }
621    }
622
623    /// Join a thread previously created by the current task.
624    ///
625    /// Returns the joined thread TID.
626    pub fn thread_join(tid: usize, status: Option<&mut i32>) -> error::Result<usize> {
627        let status_ptr = status.map_or(0usize, |s| s as *mut i32 as usize);
628        unsafe { syscall3(number::SYS_THREAD_JOIN, tid, status_ptr, 0) }
629    }
630
631    /// Exit the current thread with `code`.
632    ///
633    /// This function never returns.
634    pub fn thread_exit(code: i32) -> ! {
635        unsafe {
636            syscall1(number::SYS_THREAD_EXIT, code as usize).ok();
637        }
638        #[allow(clippy::empty_loop)]
639        loop {
640            core::hint::spin_loop();
641        }
642    }
643
644    /// Return parent process ID.
645    pub fn getppid() -> error::Result<usize> {
646        unsafe { syscall0(number::SYS_GETPPID) }
647    }
648
649    /// Wait for a child process (raw, single attempt).
650    ///
651    /// `pid` supports POSIX values (`-1` = any child).
652    /// `status` receives encoded wait status (same layout as Linux waitpid).
653    ///
654    /// Prefer [`waitpid_blocking`] for blocking waits — it automatically
655    /// retries on `EINTR`.
656    pub fn waitpid(pid: isize, status: Option<&mut i32>, options: usize) -> error::Result<usize> {
657        let status_ptr = status.map_or(0usize, |s| s as *mut i32 as usize);
658        unsafe { syscall3(number::SYS_PROC_WAITPID, pid as usize, status_ptr, options) }
659    }
660
661    /// Wait for a child process, automatically retrying on `EINTR`.
662    ///
663    /// This is the recommended wrapper for blocking waits. It calls
664    /// [`waitpid`] in a loop and yields the CPU between retries when
665    /// the kernel interrupts the wait with `EINTR`.
666    pub fn waitpid_blocking(pid: isize, status: &mut i32) -> error::Result<usize> {
667        loop {
668            match waitpid(pid, Some(status), 0) {
669                Err(error::Error::Interrupted) => {
670                    let _ = sched_yield();
671                }
672                other => return other,
673            }
674        }
675    }
676
677    /// Set process group ID.
678    ///
679    /// `pid == 0` targets the current process, `pgid == 0` uses target pid.
680    pub fn setpgid(pid: isize, pgid: isize) -> error::Result<usize> {
681        unsafe { syscall2(number::SYS_SETPGID, pid as usize, pgid as usize) }
682    }
683
684    /// Return process group ID.
685    ///
686    /// `pid == 0` queries current process group.
687    pub fn getpgid(pid: isize) -> error::Result<usize> {
688        unsafe { syscall1(number::SYS_GETPGID, pid as usize) }
689    }
690
691    /// Create a new session and return its session ID.
692    pub fn setsid() -> error::Result<usize> {
693        unsafe { syscall0(number::SYS_SETSID) }
694    }
695
696    /// Return session ID for `pid` (`0` = current process).
697    pub fn getsid(pid: isize) -> error::Result<usize> {
698        unsafe { syscall1(number::SYS_GETSID, pid as usize) }
699    }
700
701    /// Return current process group ID.
702    pub fn getpgrp() -> error::Result<usize> {
703        unsafe { syscall0(number::SYS_GETPGRP) }
704    }
705
706    /// Send a file descriptor `fd`, handled by the scheme providing `receiver_socket`. `flags` is
707    /// currently unused (must be zero), and `arg` is included in the scheme call.
708    ///
709    /// The scheme can return an arbitrary value.
710    pub fn sendfd(
711        receiver_socket: usize,
712        fd: usize,
713        flags: usize,
714        arg: u64,
715    ) -> error::Result<usize> {
716        let _ = (receiver_socket, fd, flags, arg);
717        Err(error::Error::NotSupported)
718    }
719
720    /// SYS_CALL interface, read-only variant
721    pub fn call_ro(
722        fd: usize,
723        payload: &mut [u8],
724        flags: flag::CallFlags,
725        metadata: &[u64],
726    ) -> error::Result<usize> {
727        let _ = (fd, payload, flags, metadata);
728        Err(error::Error::NotSupported)
729    }
730
731    /// SYS_CALL interface, write-only variant
732    pub fn call_wo(
733        fd: usize,
734        payload: &[u8],
735        flags: flag::CallFlags,
736        metadata: &[u64],
737    ) -> error::Result<usize> {
738        let _ = (fd, payload, flags, metadata);
739        Err(error::Error::NotSupported)
740    }
741
742    /// SYS_CALL interface, read-write variant
743    pub fn call_rw(
744        fd: usize,
745        payload: &mut [u8],
746        flags: flag::CallFlags,
747        metadata: &[u64],
748    ) -> error::Result<usize> {
749        let _ = (fd, payload, flags, metadata);
750        Err(error::Error::NotSupported)
751    }
752
753    // -----------------------------------------------------------------------
754    // Handle management (block 0-99)
755    // -----------------------------------------------------------------------
756
757    /// Close a capability handle.
758    pub fn handle_close(handle: usize) -> error::Result<usize> {
759        unsafe { syscall1(number::SYS_HANDLE_CLOSE, handle) }
760    }
761
762    /// Wait on a capability handle until it becomes signaled.
763    pub fn handle_wait(handle: usize) -> error::Result<usize> {
764        unsafe { syscall2(number::SYS_HANDLE_WAIT, handle, usize::MAX) }
765    }
766
767    /// Wait on a capability handle with a timeout (nanoseconds).
768    pub fn handle_wait_timeout(handle: usize, timeout_ns: usize) -> error::Result<usize> {
769        unsafe { syscall2(number::SYS_HANDLE_WAIT, handle, timeout_ns) }
770    }
771
772    /// Grant a capability handle to another process.
773    pub fn handle_grant(handle: usize, target_pid: usize) -> error::Result<usize> {
774        unsafe { syscall2(number::SYS_HANDLE_GRANT, handle, target_pid) }
775    }
776
777    /// Revoke a capability handle.
778    pub fn handle_revoke(handle: usize) -> error::Result<usize> {
779        unsafe { syscall1(number::SYS_HANDLE_REVOKE, handle) }
780    }
781
782    /// Query metadata about a capability handle.
783    pub fn handle_info(handle: usize, out: &mut data::HandleInfo) -> error::Result<usize> {
784        unsafe {
785            syscall2(
786                number::SYS_HANDLE_INFO,
787                handle,
788                out as *mut data::HandleInfo as usize,
789            )
790        }
791    }
792
793    // -----------------------------------------------------------------------
794    // Memory management (block 100-199)
795    // -----------------------------------------------------------------------
796
797    /// Adjust the program break (heap boundary).
798    ///
799    /// `new_brk == 0` queries the current break without changing it.
800    /// Returns the new (or current) program break on success.
801    pub fn brk(new_brk: usize) -> error::Result<usize> {
802        unsafe { syscall1(number::SYS_BRK, new_brk) }
803    }
804
805    /// Remap a previously mapped memory region.
806    pub unsafe fn mremap(
807        old_addr: usize,
808        old_size: usize,
809        new_size: usize,
810        flags: usize,
811    ) -> error::Result<usize> {
812        syscall4(number::SYS_MREMAP, old_addr, old_size, new_size, flags)
813    }
814
815    // -----------------------------------------------------------------------
816    // IPC — Ports (block 200-211)
817    // -----------------------------------------------------------------------
818
819    /// Create a new IPC port.
820    ///
821    /// `flags` is reserved (pass 0). Returns a capability handle for the port.
822    pub fn ipc_create_port(flags: usize) -> error::Result<usize> {
823        unsafe { syscall1(number::SYS_IPC_CREATE_PORT, flags) }
824    }
825
826    /// Send a message through an IPC port.
827    pub fn ipc_send(port_handle: usize, msg: &data::IpcMessage) -> error::Result<usize> {
828        unsafe {
829            syscall2(
830                number::SYS_IPC_SEND,
831                port_handle,
832                msg as *const data::IpcMessage as usize,
833            )
834        }
835    }
836
837    /// Receive a message from an IPC port (blocking).
838    pub fn ipc_recv(port_handle: usize, msg: &mut data::IpcMessage) -> error::Result<usize> {
839        unsafe {
840            syscall2(
841                number::SYS_IPC_RECV,
842                port_handle,
843                msg as *mut data::IpcMessage as usize,
844            )
845        }
846    }
847
848    /// Non-blocking receive from an IPC port.
849    ///
850    /// Returns `Err(Error::Again)` if the port is empty.
851    pub fn ipc_try_recv(port_handle: usize, msg: &mut data::IpcMessage) -> error::Result<usize> {
852        unsafe {
853            syscall2(
854                number::SYS_IPC_TRY_RECV,
855                port_handle,
856                msg as *mut data::IpcMessage as usize,
857            )
858        }
859    }
860
861    /// Connect to an IPC service bound in the namespace.
862    ///
863    /// Returns a port handle that can be used with `ipc_call`/`ipc_send`.
864    pub fn ipc_connect(path: &[u8]) -> error::Result<usize> {
865        unsafe { syscall2(number::SYS_IPC_CONNECT, path.as_ptr() as usize, path.len()) }
866    }
867
868    /// Send a message and block until a reply arrives (RPC-style).
869    ///
870    /// On return the message buffer is overwritten with the reply.
871    pub fn ipc_call(port_handle: usize, msg: &mut data::IpcMessage) -> error::Result<usize> {
872        unsafe {
873            syscall2(
874                number::SYS_IPC_CALL,
875                port_handle,
876                msg as *mut data::IpcMessage as usize,
877            )
878        }
879    }
880
881    /// Reply to the sender of the last received message.
882    pub fn ipc_reply(msg: &data::IpcMessage) -> error::Result<usize> {
883        unsafe {
884            syscall1(
885                number::SYS_IPC_REPLY,
886                msg as *const data::IpcMessage as usize,
887            )
888        }
889    }
890
891    /// Bind an IPC port to a named path in the namespace.
892    pub fn ipc_bind_port(port_handle: usize, path: &[u8]) -> error::Result<usize> {
893        unsafe {
894            syscall3(
895                number::SYS_IPC_BIND_PORT,
896                port_handle,
897                path.as_ptr() as usize,
898                path.len(),
899            )
900        }
901    }
902
903    /// Unbind a namespace path from its IPC port.
904    pub fn ipc_unbind_port(path: &[u8]) -> error::Result<usize> {
905        unsafe {
906            syscall2(
907                number::SYS_IPC_UNBIND_PORT,
908                path.as_ptr() as usize,
909                path.len(),
910            )
911        }
912    }
913
914    /// Create a shared-memory ring buffer.
915    ///
916    /// `size`: requested ring size in bytes.
917    /// Returns a capability handle for the ring.
918    pub fn ipc_ring_create(size: usize) -> error::Result<usize> {
919        unsafe { syscall1(number::SYS_IPC_RING_CREATE, size) }
920    }
921
922    /// Map a shared-memory ring buffer into the calling process.
923    ///
924    /// `ring_handle`: capability handle returned by [`ipc_ring_create`].
925    /// `out_ptr`: pointer to receive the mapped user-virtual address.
926    pub fn ipc_ring_map(ring_handle: usize, out_ptr: usize) -> error::Result<usize> {
927        unsafe { syscall2(number::SYS_IPC_RING_MAP, ring_handle, out_ptr) }
928    }
929
930    // -----------------------------------------------------------------------
931    // IPC — Channels (block 220-224)
932    // -----------------------------------------------------------------------
933
934    /// Create a typed MPMC sync-channel.
935    ///
936    /// `capacity`: maximum number of queued messages.
937    /// Returns a capability handle for the channel.
938    pub fn chan_create(capacity: usize) -> error::Result<usize> {
939        unsafe { syscall1(number::SYS_CHAN_CREATE, capacity) }
940    }
941
942    /// Send a message into a channel (blocking if full).
943    pub fn chan_send(chan_handle: usize, msg: &data::IpcMessage) -> error::Result<usize> {
944        unsafe {
945            syscall2(
946                number::SYS_CHAN_SEND,
947                chan_handle,
948                msg as *const data::IpcMessage as usize,
949            )
950        }
951    }
952
953    /// Receive a message from a channel (blocking if empty).
954    pub fn chan_recv(chan_handle: usize, msg: &mut data::IpcMessage) -> error::Result<usize> {
955        unsafe {
956            syscall2(
957                number::SYS_CHAN_RECV,
958                chan_handle,
959                msg as *mut data::IpcMessage as usize,
960            )
961        }
962    }
963
964    /// Non-blocking receive from a channel.
965    ///
966    /// Returns `Err(Error::Again)` if the channel is empty.
967    pub fn chan_try_recv(chan_handle: usize, msg: &mut data::IpcMessage) -> error::Result<usize> {
968        unsafe {
969            syscall2(
970                number::SYS_CHAN_TRY_RECV,
971                chan_handle,
972                msg as *mut data::IpcMessage as usize,
973            )
974        }
975    }
976
977    /// Close and destroy a channel.
978    pub fn chan_close(chan_handle: usize) -> error::Result<usize> {
979        unsafe { syscall1(number::SYS_CHAN_CLOSE, chan_handle) }
980    }
981
982    /// Enumerate PCI devices matching `criteria`.
983    ///
984    /// Returns the number of entries written into `out`.
985    pub fn pci_enum(
986        criteria: &data::PciProbeCriteria,
987        out: &mut [data::PciDeviceInfo],
988    ) -> error::Result<usize> {
989        unsafe {
990            syscall3(
991                number::SYS_PCI_ENUM,
992                criteria as *const data::PciProbeCriteria as usize,
993                out.as_mut_ptr() as usize,
994                out.len(),
995            )
996        }
997    }
998
999    /// Read a PCI configuration value from `addr`.
1000    ///
1001    /// `width` must be 1, 2 or 4.
1002    pub fn pci_cfg_read(addr: &data::PciAddress, offset: u8, width: u8) -> error::Result<usize> {
1003        unsafe {
1004            syscall3(
1005                number::SYS_PCI_CFG_READ,
1006                addr as *const data::PciAddress as usize,
1007                offset as usize,
1008                width as usize,
1009            )
1010        }
1011    }
1012
1013    /// Write a PCI configuration value to `addr`.
1014    ///
1015    /// `width` must be 1, 2 or 4.
1016    pub fn pci_cfg_write(
1017        addr: &data::PciAddress,
1018        offset: u8,
1019        width: u8,
1020        value: u32,
1021    ) -> error::Result<usize> {
1022        unsafe {
1023            syscall4(
1024                number::SYS_PCI_CFG_WRITE,
1025                addr as *const data::PciAddress as usize,
1026                offset as usize,
1027                width as usize,
1028                value as usize,
1029            )
1030        }
1031    }
1032
1033    // -----------------------------------------------------------------------
1034    // Signals (block 320-332)
1035    // -----------------------------------------------------------------------
1036
1037    /// Send a signal to a process.
1038    pub fn kill(pid: isize, signal: u32) -> error::Result<usize> {
1039        unsafe { syscall2(number::SYS_KILL, pid as usize, signal as usize) }
1040    }
1041
1042    /// Examine and change blocked signals.
1043    ///
1044    /// `how`: SIG_BLOCK / SIG_UNBLOCK / SIG_SETMASK.
1045    /// `set_ptr` / `oldset_ptr`: pointers to signal set bitmasks (0 = ignore).
1046    pub fn sigprocmask(how: i32, set_ptr: usize, oldset_ptr: usize) -> error::Result<usize> {
1047        unsafe { syscall3(number::SYS_SIGPROCMASK, how as usize, set_ptr, oldset_ptr) }
1048    }
1049
1050    /// Install a signal handler.
1051    ///
1052    /// `signum`: signal number.
1053    /// `act_ptr`: pointer to new `SigAction` (0 = query only).
1054    /// `oldact_ptr`: pointer to receive previous action (0 = ignore).
1055    pub fn sigaction(signum: usize, act_ptr: usize, oldact_ptr: usize) -> error::Result<usize> {
1056        unsafe { syscall3(number::SYS_SIGACTION, signum, act_ptr, oldact_ptr) }
1057    }
1058
1059    /// Set or query the alternate signal stack.
1060    pub fn sigaltstack(ss_ptr: usize, old_ss_ptr: usize) -> error::Result<usize> {
1061        unsafe { syscall2(number::SYS_SIGALTSTACK, ss_ptr, old_ss_ptr) }
1062    }
1063
1064    /// Return the set of pending signals.
1065    pub fn sigpending(set_ptr: usize) -> error::Result<usize> {
1066        unsafe { syscall1(number::SYS_SIGPENDING, set_ptr) }
1067    }
1068
1069    /// Temporarily replace the signal mask and suspend until a signal arrives.
1070    pub fn sigsuspend(mask: usize) -> error::Result<usize> {
1071        unsafe { syscall1(number::SYS_SIGSUSPEND, mask) }
1072    }
1073
1074    /// Wait for a signal from `set`, with optional timeout.
1075    pub fn sigtimedwait(
1076        set_ptr: usize,
1077        info_ptr: usize,
1078        timeout_ptr: usize,
1079    ) -> error::Result<usize> {
1080        unsafe { syscall3(number::SYS_SIGTIMEDWAIT, set_ptr, info_ptr, timeout_ptr) }
1081    }
1082
1083    /// Queue a signal with a value to a process.
1084    pub fn sigqueue(pid: isize, signal: u32, value: usize) -> error::Result<usize> {
1085        unsafe { syscall3(number::SYS_SIGQUEUE, pid as usize, signal as usize, value) }
1086    }
1087
1088    /// Send a signal to a process group.
1089    pub fn killpg(pgrp: usize, signal: u32) -> error::Result<usize> {
1090        unsafe { syscall2(number::SYS_KILLPG, pgrp, signal as usize) }
1091    }
1092
1093    /// Get the value of an interval timer.
1094    pub fn getitimer(which: u32, value_ptr: usize) -> error::Result<usize> {
1095        unsafe { syscall2(number::SYS_GETITIMER, which as usize, value_ptr) }
1096    }
1097
1098    /// Set an interval timer.
1099    pub fn setitimer(
1100        which: u32,
1101        new_value_ptr: usize,
1102        old_value_ptr: usize,
1103    ) -> error::Result<usize> {
1104        unsafe {
1105            syscall3(
1106                number::SYS_SETITIMER,
1107                which as usize,
1108                new_value_ptr,
1109                old_value_ptr,
1110            )
1111        }
1112    }
1113
1114    // -----------------------------------------------------------------------
1115    // Network (block 410-412)
1116    // -----------------------------------------------------------------------
1117
1118    /// Receive a network packet into `buf`.
1119    ///
1120    /// Returns the number of bytes received.
1121    pub fn net_recv(buf: &mut [u8]) -> error::Result<usize> {
1122        unsafe { syscall2(number::SYS_NET_RECV, buf.as_mut_ptr() as usize, buf.len()) }
1123    }
1124
1125    /// Send a network packet from `buf`.
1126    pub fn net_send(buf: &[u8]) -> error::Result<usize> {
1127        unsafe { syscall2(number::SYS_NET_SEND, buf.as_ptr() as usize, buf.len()) }
1128    }
1129
1130    /// Query network device information.
1131    ///
1132    /// `info_type`: type of info requested. `buf_ptr`: output buffer.
1133    pub fn net_info(info_type: usize, buf_ptr: usize) -> error::Result<usize> {
1134        unsafe { syscall2(number::SYS_NET_INFO, info_type, buf_ptr) }
1135    }
1136
1137    // -----------------------------------------------------------------------
1138    // Volumes / block devices (block 420-422)
1139    // -----------------------------------------------------------------------
1140
1141    /// Read sectors from a volume.
1142    ///
1143    /// `handle`: volume capability handle.
1144    /// `lba`: starting logical block address.
1145    /// `buf_ptr`: destination buffer in user memory.
1146    /// `sector_count`: number of 512-byte sectors to read.
1147    pub fn volume_read(
1148        handle: usize,
1149        lba: usize,
1150        buf_ptr: usize,
1151        sector_count: usize,
1152    ) -> error::Result<usize> {
1153        unsafe { syscall4(number::SYS_VOLUME_READ, handle, lba, buf_ptr, sector_count) }
1154    }
1155
1156    /// Write sectors to a volume.
1157    pub fn volume_write(
1158        handle: usize,
1159        lba: usize,
1160        buf_ptr: usize,
1161        sector_count: usize,
1162    ) -> error::Result<usize> {
1163        unsafe { syscall4(number::SYS_VOLUME_WRITE, handle, lba, buf_ptr, sector_count) }
1164    }
1165
1166    /// Query volume metadata (e.g. total sector count).
1167    pub fn volume_info(handle: usize) -> error::Result<usize> {
1168        unsafe { syscall1(number::SYS_VOLUME_INFO, handle) }
1169    }
1170
1171    // -----------------------------------------------------------------------
1172    // Debug (block 600)
1173    // -----------------------------------------------------------------------
1174
1175    /// Write a debug log message to the kernel serial console.
1176    pub fn debug_log(msg: &[u8]) -> error::Result<usize> {
1177        unsafe { syscall2(number::SYS_DEBUG_LOG, msg.as_ptr() as usize, msg.len()) }
1178    }
1179
1180    // -----------------------------------------------------------------------
1181    // Module management (block 700-703)
1182    // -----------------------------------------------------------------------
1183
1184    /// Load a kernel module by name.
1185    ///
1186    /// Returns a module ID on success.
1187    pub fn module_load(name: &[u8]) -> error::Result<usize> {
1188        unsafe { syscall2(number::SYS_MODULE_LOAD, name.as_ptr() as usize, name.len()) }
1189    }
1190
1191    /// Unload a previously loaded kernel module.
1192    pub fn module_unload(module_id: usize) -> error::Result<usize> {
1193        unsafe { syscall1(number::SYS_MODULE_UNLOAD, module_id) }
1194    }
1195
1196    /// Look up a symbol exported by a kernel module.
1197    ///
1198    /// `module_id`: module to query. `sym_name_ptr`: pointer to symbol name.
1199    /// Returns the symbol address.
1200    pub fn module_get_symbol(module_id: usize, sym_name_ptr: usize) -> error::Result<usize> {
1201        unsafe { syscall2(number::SYS_MODULE_GET_SYMBOL, module_id, sym_name_ptr) }
1202    }
1203
1204    /// Query metadata about a loaded module.
1205    pub fn module_query(module_id: usize, buf_ptr: usize) -> error::Result<usize> {
1206        unsafe { syscall2(number::SYS_MODULE_QUERY, module_id, buf_ptr) }
1207    }
1208
1209    // -----------------------------------------------------------------------
1210    // Silo management (block 800-808)
1211    // -----------------------------------------------------------------------
1212
1213    /// Create a new silo (isolated execution environment).
1214    ///
1215    /// `config_ptr`: pointer to silo configuration structure.
1216    /// Returns the silo ID.
1217    pub fn silo_create(config_ptr: usize) -> error::Result<usize> {
1218        unsafe { syscall1(number::SYS_SILO_CREATE, config_ptr) }
1219    }
1220
1221    /// Configure an existing silo.
1222    pub fn silo_config(silo_id: usize, config_ptr: usize) -> error::Result<usize> {
1223        unsafe { syscall2(number::SYS_SILO_CONFIG, silo_id, config_ptr) }
1224    }
1225
1226    /// Attach a loaded kernel module to a silo.
1227    pub fn silo_attach_module(silo_id: usize, module_id: usize) -> error::Result<usize> {
1228        unsafe { syscall2(number::SYS_SILO_ATTACH_MODULE, silo_id, module_id) }
1229    }
1230
1231    /// Start a silo (begin executing its init process).
1232    pub fn silo_start(silo_id: usize) -> error::Result<usize> {
1233        unsafe { syscall1(number::SYS_SILO_START, silo_id) }
1234    }
1235
1236    /// Gracefully stop a silo.
1237    pub fn silo_stop(silo_id: usize) -> error::Result<usize> {
1238        unsafe { syscall1(number::SYS_SILO_STOP, silo_id) }
1239    }
1240
1241    /// Force-kill a silo and all its processes.
1242    pub fn silo_kill(silo_id: usize) -> error::Result<usize> {
1243        unsafe { syscall1(number::SYS_SILO_KILL, silo_id) }
1244    }
1245
1246    /// Dequeue the next event from a silo's event queue.
1247    ///
1248    /// Returns the raw event value, or `Err(Error::Again)` if empty.
1249    pub fn silo_event_next(silo_id: usize) -> error::Result<usize> {
1250        unsafe { syscall1(number::SYS_SILO_EVENT_NEXT, silo_id) }
1251    }
1252
1253    /// Suspend a running silo.
1254    pub fn silo_suspend(silo_id: usize) -> error::Result<usize> {
1255        unsafe { syscall1(number::SYS_SILO_SUSPEND, silo_id) }
1256    }
1257
1258    /// Resume a suspended silo.
1259    pub fn silo_resume(silo_id: usize) -> error::Result<usize> {
1260        unsafe { syscall1(number::SYS_SILO_RESUME, silo_id) }
1261    }
1262
1263    /// Tighten sandbox mode for the current silo/process context.
1264    pub fn silo_pledge(new_mode: usize) -> error::Result<usize> {
1265        unsafe { syscall1(number::SYS_SILO_PLEDGE, new_mode) }
1266    }
1267
1268    /// Restrict path visibility/rights for the current sandbox context.
1269    pub fn silo_unveil(path: &[u8], rights_bits: usize) -> error::Result<usize> {
1270        unsafe {
1271            syscall3(
1272                number::SYS_SILO_UNVEIL,
1273                path.as_ptr() as usize,
1274                path.len(),
1275                rights_bits,
1276            )
1277        }
1278    }
1279
1280    /// Seal current context into sandboxed mode (no return to broader rights).
1281    pub fn silo_enter_sandbox() -> error::Result<usize> {
1282        unsafe { syscall0(number::SYS_SILO_ENTER_SANDBOX) }
1283    }
1284
1285    // -----------------------------------------------------------------------
1286    // ABI introspection (block 900)
1287    // -----------------------------------------------------------------------
1288
1289    /// Query the kernel ABI version. Returns (major << 16) | minor.
1290    pub fn abi_version() -> error::Result<(u16, u16)> {
1291        let raw = unsafe { syscall0(number::SYS_ABI_VERSION) }?;
1292        Ok(((raw >> 16) as u16, raw as u16))
1293    }
1294}