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(-100_i64 as usize, 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    /// Terminate all threads in the current thread group with the given exit `code`.
566    ///
567    /// This function never returns.
568    pub fn exit_group(code: usize) -> ! {
569        unsafe {
570            syscall1(number::SYS_EXIT_GROUP, code).ok();
571        }
572        #[allow(clippy::empty_loop)]
573        loop {
574            core::hint::spin_loop();
575        }
576    }
577
578    /// Yield the process's time slice to the kernel
579    ///
580    /// This function will return Ok(0) on success
581    pub fn sched_yield() -> error::Result<usize> {
582        unsafe { syscall0(number::SYS_PROC_YIELD) }
583    }
584
585    /// Fork the current process.
586    ///
587    /// Returns child PID in parent, and 0 in child.
588    pub fn fork() -> error::Result<usize> {
589        unsafe { syscall0(number::SYS_PROC_FORK) }
590    }
591
592    /// Replace the current process image with a new program.
593    ///
594    /// `path`: executable path as a nul-terminated C string byte slice.
595    /// `argv` and `envp`: pointers to null-terminated C-style arrays.
596    pub unsafe fn execve(path: &[u8], argv: usize, envp: usize) -> error::Result<usize> {
597        syscall3(number::SYS_PROC_EXECVE, path.as_ptr() as usize, argv, envp)
598    }
599
600    /// Return current process ID.
601    pub fn getpid() -> error::Result<usize> {
602        unsafe { syscall0(number::SYS_GETPID) }
603    }
604
605    /// Return current thread ID.
606    pub fn gettid() -> error::Result<usize> {
607        unsafe { syscall0(number::SYS_GETTID) }
608    }
609
610    /// Create a new userspace thread in the current process.
611    ///
612    /// - `entry`: user function address (RIP)
613    /// - `stack_top`: user stack top (RSP), 16-byte aligned
614    /// - `arg0`: first argument passed in RDI
615    /// - `tls_base`: optional FS base for thread-local storage
616    ///
617    /// Returns the new thread TID.
618    pub fn thread_create(
619        entry: usize,
620        stack_top: usize,
621        arg0: usize,
622        tls_base: usize,
623    ) -> error::Result<usize> {
624        unsafe {
625            syscall5(
626                number::SYS_THREAD_CREATE,
627                entry,
628                stack_top,
629                arg0,
630                0,
631                tls_base,
632            )
633        }
634    }
635
636    /// Join a thread previously created by the current task.
637    ///
638    /// Returns the joined thread TID.
639    pub fn thread_join(tid: usize, status: Option<&mut i32>) -> error::Result<usize> {
640        let status_ptr = status.map_or(0usize, |s| s as *mut i32 as usize);
641        unsafe { syscall3(number::SYS_THREAD_JOIN, tid, status_ptr, 0) }
642    }
643
644    /// Exit the current thread with `code`.
645    ///
646    /// This function never returns.
647    pub fn thread_exit(code: i32) -> ! {
648        unsafe {
649            syscall1(number::SYS_THREAD_EXIT, code as usize).ok();
650        }
651        #[allow(clippy::empty_loop)]
652        loop {
653            core::hint::spin_loop();
654        }
655    }
656
657    /// Return parent process ID.
658    pub fn getppid() -> error::Result<usize> {
659        unsafe { syscall0(number::SYS_GETPPID) }
660    }
661
662    /// Wait for a child process (raw, single attempt).
663    ///
664    /// `pid` supports POSIX values (`-1` = any child).
665    /// `status` receives encoded wait status (same layout as Linux waitpid).
666    ///
667    /// Prefer [`waitpid_blocking`] for blocking waits : it automatically
668    /// retries on `EINTR`.
669    pub fn waitpid(pid: isize, status: Option<&mut i32>, options: usize) -> error::Result<usize> {
670        let status_ptr = status.map_or(0usize, |s| s as *mut i32 as usize);
671        unsafe { syscall3(number::SYS_PROC_WAITPID, pid as usize, status_ptr, options) }
672    }
673
674    /// Wait for a child process, automatically retrying on `EINTR`.
675    ///
676    /// This is the recommended wrapper for blocking waits. It calls
677    /// [`waitpid`] in a loop and yields the CPU between retries when
678    /// the kernel interrupts the wait with `EINTR`.
679    pub fn waitpid_blocking(pid: isize, status: &mut i32) -> error::Result<usize> {
680        loop {
681            match waitpid(pid, Some(status), 0) {
682                Err(error::Error::Interrupted) => {
683                    let _ = sched_yield();
684                }
685                other => return other,
686            }
687        }
688    }
689
690    /// Set process group ID.
691    ///
692    /// `pid == 0` targets the current process, `pgid == 0` uses target pid.
693    pub fn setpgid(pid: isize, pgid: isize) -> error::Result<usize> {
694        unsafe { syscall2(number::SYS_SETPGID, pid as usize, pgid as usize) }
695    }
696
697    /// Return process group ID.
698    ///
699    /// `pid == 0` queries current process group.
700    pub fn getpgid(pid: isize) -> error::Result<usize> {
701        unsafe { syscall1(number::SYS_GETPGID, pid as usize) }
702    }
703
704    /// Create a new session and return its session ID.
705    pub fn setsid() -> error::Result<usize> {
706        unsafe { syscall0(number::SYS_SETSID) }
707    }
708
709    /// Return session ID for `pid` (`0` = current process).
710    pub fn getsid(pid: isize) -> error::Result<usize> {
711        unsafe { syscall1(number::SYS_GETSID, pid as usize) }
712    }
713
714    /// Return current process group ID.
715    pub fn getpgrp() -> error::Result<usize> {
716        unsafe { syscall0(number::SYS_GETPGRP) }
717    }
718
719    /// Send a file descriptor `fd`, handled by the scheme providing `receiver_socket`. `flags` is
720    /// currently unused (must be zero), and `arg` is included in the scheme call.
721    ///
722    /// The scheme can return an arbitrary value.
723    pub fn sendfd(
724        receiver_socket: usize,
725        fd: usize,
726        flags: usize,
727        arg: u64,
728    ) -> error::Result<usize> {
729        let _ = (receiver_socket, fd, flags, arg);
730        Err(error::Error::NotSupported)
731    }
732
733    /// SYS_CALL interface, read-only variant
734    pub fn call_ro(
735        fd: usize,
736        payload: &mut [u8],
737        flags: flag::CallFlags,
738        metadata: &[u64],
739    ) -> error::Result<usize> {
740        let _ = (fd, payload, flags, metadata);
741        Err(error::Error::NotSupported)
742    }
743
744    /// SYS_CALL interface, write-only variant
745    pub fn call_wo(
746        fd: usize,
747        payload: &[u8],
748        flags: flag::CallFlags,
749        metadata: &[u64],
750    ) -> error::Result<usize> {
751        let _ = (fd, payload, flags, metadata);
752        Err(error::Error::NotSupported)
753    }
754
755    /// SYS_CALL interface, read-write variant
756    pub fn call_rw(
757        fd: usize,
758        payload: &mut [u8],
759        flags: flag::CallFlags,
760        metadata: &[u64],
761    ) -> error::Result<usize> {
762        let _ = (fd, payload, flags, metadata);
763        Err(error::Error::NotSupported)
764    }
765
766    // -----------------------------------------------------------------------
767    // Handle management (block 0-99)
768    // -----------------------------------------------------------------------
769
770    /// Close a capability handle.
771    pub fn handle_close(handle: usize) -> error::Result<usize> {
772        unsafe { syscall1(number::SYS_HANDLE_CLOSE, handle) }
773    }
774
775    /// Wait on a capability handle until it becomes signaled.
776    pub fn handle_wait(handle: usize) -> error::Result<usize> {
777        unsafe { syscall2(number::SYS_HANDLE_WAIT, handle, usize::MAX) }
778    }
779
780    /// Wait on a capability handle with a timeout (nanoseconds).
781    pub fn handle_wait_timeout(handle: usize, timeout_ns: usize) -> error::Result<usize> {
782        unsafe { syscall2(number::SYS_HANDLE_WAIT, handle, timeout_ns) }
783    }
784
785    /// Grant a capability handle to another process.
786    pub fn handle_grant(handle: usize, target_pid: usize) -> error::Result<usize> {
787        unsafe { syscall2(number::SYS_HANDLE_GRANT, handle, target_pid) }
788    }
789
790    /// Revoke a capability handle.
791    pub fn handle_revoke(handle: usize) -> error::Result<usize> {
792        unsafe { syscall1(number::SYS_HANDLE_REVOKE, handle) }
793    }
794
795    /// Query metadata about a capability handle.
796    pub fn handle_info(handle: usize, out: &mut data::HandleInfo) -> error::Result<usize> {
797        unsafe {
798            syscall2(
799                number::SYS_HANDLE_INFO,
800                handle,
801                out as *mut data::HandleInfo as usize,
802            )
803        }
804    }
805
806    // -----------------------------------------------------------------------
807    // Memory management (block 100-199)
808    // -----------------------------------------------------------------------
809
810    /// Adjust the program break (heap boundary).
811    ///
812    /// `new_brk == 0` queries the current break without changing it.
813    /// Returns the new (or current) program break on success.
814    pub fn brk(new_brk: usize) -> error::Result<usize> {
815        unsafe { syscall1(number::SYS_BRK, new_brk) }
816    }
817
818    /// Remap a previously mapped memory region.
819    pub unsafe fn mremap(
820        old_addr: usize,
821        old_size: usize,
822        new_size: usize,
823        flags: usize,
824    ) -> error::Result<usize> {
825        syscall4(number::SYS_MREMAP, old_addr, old_size, new_size, flags)
826    }
827
828    /// Export a tracked mapped region as a public memory handle.
829    pub fn mem_region_export(addr: usize) -> error::Result<usize> {
830        unsafe { syscall1(number::SYS_MEM_REGION_EXPORT, addr) }
831    }
832
833    /// Map a public memory handle into the current address space.
834    pub fn mem_region_map(
835        handle: usize,
836        addr_hint: usize,
837        out_addr: &mut usize,
838    ) -> error::Result<usize> {
839        unsafe {
840            syscall3(
841                number::SYS_MEM_REGION_MAP,
842                handle,
843                addr_hint,
844                out_addr as *mut usize as usize,
845            )
846        }
847    }
848
849    /// Query metadata about a public memory handle.
850    pub fn mem_region_info(
851        handle: usize,
852        out: &mut data::MemoryRegionInfo,
853    ) -> error::Result<usize> {
854        unsafe {
855            syscall2(
856                number::SYS_MEM_REGION_INFO,
857                handle,
858                out as *mut data::MemoryRegionInfo as usize,
859            )
860        }
861    }
862
863    // -----------------------------------------------------------------------
864    // IPC : Ports (block 200-211)
865    // -----------------------------------------------------------------------
866
867    /// Create a new IPC port.
868    ///
869    /// `flags` is reserved (pass 0). Returns a capability handle for the port.
870    pub fn ipc_create_port(flags: usize) -> error::Result<usize> {
871        unsafe { syscall1(number::SYS_IPC_CREATE_PORT, flags) }
872    }
873
874    /// Send a message through an IPC port.
875    pub fn ipc_send(port_handle: usize, msg: &data::IpcMessage) -> error::Result<usize> {
876        unsafe {
877            syscall2(
878                number::SYS_IPC_SEND,
879                port_handle,
880                msg as *const data::IpcMessage as usize,
881            )
882        }
883    }
884
885    /// Receive a message from an IPC port (blocking).
886    pub fn ipc_recv(port_handle: usize, msg: &mut data::IpcMessage) -> error::Result<usize> {
887        unsafe {
888            syscall2(
889                number::SYS_IPC_RECV,
890                port_handle,
891                msg as *mut data::IpcMessage as usize,
892            )
893        }
894    }
895
896    /// Non-blocking receive from an IPC port.
897    ///
898    /// Returns `Err(Error::Again)` if the port is empty.
899    pub fn ipc_try_recv(port_handle: usize, msg: &mut data::IpcMessage) -> error::Result<usize> {
900        unsafe {
901            syscall2(
902                number::SYS_IPC_TRY_RECV,
903                port_handle,
904                msg as *mut data::IpcMessage as usize,
905            )
906        }
907    }
908
909    /// Connect to an IPC service bound in the namespace.
910    ///
911    /// Returns a port handle that can be used with `ipc_call`/`ipc_send`.
912    pub fn ipc_connect(path: &[u8]) -> error::Result<usize> {
913        unsafe { syscall2(number::SYS_IPC_CONNECT, path.as_ptr() as usize, path.len()) }
914    }
915
916    /// Send a message and block until a reply arrives (RPC-style).
917    ///
918    /// On return the message buffer is overwritten with the reply.
919    pub fn ipc_call(port_handle: usize, msg: &mut data::IpcMessage) -> error::Result<usize> {
920        unsafe {
921            syscall2(
922                number::SYS_IPC_CALL,
923                port_handle,
924                msg as *mut data::IpcMessage as usize,
925            )
926        }
927    }
928
929    /// Reply to the sender of the last received message.
930    pub fn ipc_reply(msg: &data::IpcMessage) -> error::Result<usize> {
931        unsafe {
932            syscall1(
933                number::SYS_IPC_REPLY,
934                msg as *const data::IpcMessage as usize,
935            )
936        }
937    }
938
939    /// Bind an IPC port to a named path in the namespace.
940    pub fn ipc_bind_port(port_handle: usize, path: &[u8]) -> error::Result<usize> {
941        unsafe {
942            syscall3(
943                number::SYS_IPC_BIND_PORT,
944                port_handle,
945                path.as_ptr() as usize,
946                path.len(),
947            )
948        }
949    }
950
951    /// Unbind a namespace path from its IPC port.
952    pub fn ipc_unbind_port(path: &[u8]) -> error::Result<usize> {
953        unsafe {
954            syscall2(
955                number::SYS_IPC_UNBIND_PORT,
956                path.as_ptr() as usize,
957                path.len(),
958            )
959        }
960    }
961
962    /// Create a shared-memory ring buffer.
963    ///
964    /// `size`: requested ring size in bytes.
965    /// Returns a capability handle for the ring.
966    pub fn ipc_ring_create(size: usize) -> error::Result<usize> {
967        unsafe { syscall1(number::SYS_IPC_RING_CREATE, size) }
968    }
969
970    /// Map a shared-memory ring buffer into the calling process.
971    ///
972    /// `ring_handle`: capability handle returned by [`ipc_ring_create`].
973    /// `out_ptr`: pointer to receive the mapped user-virtual address.
974    pub fn ipc_ring_map(ring_handle: usize, out_ptr: usize) -> error::Result<usize> {
975        unsafe { syscall2(number::SYS_IPC_RING_MAP, ring_handle, out_ptr) }
976    }
977
978    // -----------------------------------------------------------------------
979    // IPC : Channels (block 220-224)
980    // -----------------------------------------------------------------------
981
982    /// Create a typed MPMC sync-channel.
983    ///
984    /// `capacity`: maximum number of queued messages.
985    /// Returns a capability handle for the channel.
986    pub fn chan_create(capacity: usize) -> error::Result<usize> {
987        unsafe { syscall1(number::SYS_CHAN_CREATE, capacity) }
988    }
989
990    /// Send a message into a channel (blocking if full).
991    pub fn chan_send(chan_handle: usize, msg: &data::IpcMessage) -> error::Result<usize> {
992        unsafe {
993            syscall2(
994                number::SYS_CHAN_SEND,
995                chan_handle,
996                msg as *const data::IpcMessage as usize,
997            )
998        }
999    }
1000
1001    /// Receive a message from a channel (blocking if empty).
1002    pub fn chan_recv(chan_handle: usize, msg: &mut data::IpcMessage) -> error::Result<usize> {
1003        unsafe {
1004            syscall2(
1005                number::SYS_CHAN_RECV,
1006                chan_handle,
1007                msg as *mut data::IpcMessage as usize,
1008            )
1009        }
1010    }
1011
1012    /// Non-blocking receive from a channel.
1013    ///
1014    /// Returns `Err(Error::Again)` if the channel is empty.
1015    pub fn chan_try_recv(chan_handle: usize, msg: &mut data::IpcMessage) -> error::Result<usize> {
1016        unsafe {
1017            syscall2(
1018                number::SYS_CHAN_TRY_RECV,
1019                chan_handle,
1020                msg as *mut data::IpcMessage as usize,
1021            )
1022        }
1023    }
1024
1025    /// Close and destroy a channel.
1026    pub fn chan_close(chan_handle: usize) -> error::Result<usize> {
1027        unsafe { syscall1(number::SYS_CHAN_CLOSE, chan_handle) }
1028    }
1029
1030    /// Enumerate PCI devices matching `criteria`.
1031    ///
1032    /// Returns the number of entries written into `out`.
1033    pub fn pci_enum(
1034        criteria: &data::PciProbeCriteria,
1035        out: &mut [data::PciDeviceInfo],
1036    ) -> error::Result<usize> {
1037        unsafe {
1038            syscall3(
1039                number::SYS_PCI_ENUM,
1040                criteria as *const data::PciProbeCriteria as usize,
1041                out.as_mut_ptr() as usize,
1042                out.len(),
1043            )
1044        }
1045    }
1046
1047    /// Read a PCI configuration value from `addr`.
1048    ///
1049    /// `width` must be 1, 2 or 4.
1050    pub fn pci_cfg_read(addr: &data::PciAddress, offset: u8, width: u8) -> error::Result<usize> {
1051        unsafe {
1052            syscall3(
1053                number::SYS_PCI_CFG_READ,
1054                addr as *const data::PciAddress as usize,
1055                offset as usize,
1056                width as usize,
1057            )
1058        }
1059    }
1060
1061    /// Write a PCI configuration value to `addr`.
1062    ///
1063    /// `width` must be 1, 2 or 4.
1064    pub fn pci_cfg_write(
1065        addr: &data::PciAddress,
1066        offset: u8,
1067        width: u8,
1068        value: u32,
1069    ) -> error::Result<usize> {
1070        unsafe {
1071            syscall4(
1072                number::SYS_PCI_CFG_WRITE,
1073                addr as *const data::PciAddress as usize,
1074                offset as usize,
1075                width as usize,
1076                value as usize,
1077            )
1078        }
1079    }
1080
1081    // -----------------------------------------------------------------------
1082    // Signals (block 320-332)
1083    // -----------------------------------------------------------------------
1084
1085    /// Send a signal to a process.
1086    pub fn kill(pid: isize, signal: u32) -> error::Result<usize> {
1087        unsafe { syscall2(number::SYS_KILL, pid as usize, signal as usize) }
1088    }
1089
1090    /// Examine and change blocked signals.
1091    ///
1092    /// `how`: SIG_BLOCK / SIG_UNBLOCK / SIG_SETMASK.
1093    /// `set_ptr` / `oldset_ptr`: pointers to signal set bitmasks (0 = ignore).
1094    pub fn sigprocmask(how: i32, set_ptr: usize, oldset_ptr: usize) -> error::Result<usize> {
1095        unsafe { syscall3(number::SYS_SIGPROCMASK, how as usize, set_ptr, oldset_ptr) }
1096    }
1097
1098    /// Install a signal handler.
1099    ///
1100    /// `signum`: signal number.
1101    /// `act_ptr`: pointer to new `SigAction` (0 = query only).
1102    /// `oldact_ptr`: pointer to receive previous action (0 = ignore).
1103    pub fn sigaction(signum: usize, act_ptr: usize, oldact_ptr: usize) -> error::Result<usize> {
1104        unsafe { syscall3(number::SYS_SIGACTION, signum, act_ptr, oldact_ptr) }
1105    }
1106
1107    /// Set or query the alternate signal stack.
1108    pub fn sigaltstack(ss_ptr: usize, old_ss_ptr: usize) -> error::Result<usize> {
1109        unsafe { syscall2(number::SYS_SIGALTSTACK, ss_ptr, old_ss_ptr) }
1110    }
1111
1112    /// Return the set of pending signals.
1113    pub fn sigpending(set_ptr: usize) -> error::Result<usize> {
1114        unsafe { syscall1(number::SYS_SIGPENDING, set_ptr) }
1115    }
1116
1117    /// Temporarily replace the signal mask and suspend until a signal arrives.
1118    pub fn sigsuspend(mask: usize) -> error::Result<usize> {
1119        unsafe { syscall1(number::SYS_SIGSUSPEND, mask) }
1120    }
1121
1122    /// Wait for a signal from `set`, with optional timeout.
1123    pub fn sigtimedwait(
1124        set_ptr: usize,
1125        info_ptr: usize,
1126        timeout_ptr: usize,
1127    ) -> error::Result<usize> {
1128        unsafe { syscall3(number::SYS_SIGTIMEDWAIT, set_ptr, info_ptr, timeout_ptr) }
1129    }
1130
1131    /// Queue a signal with a value to a process.
1132    pub fn sigqueue(pid: isize, signal: u32, value: usize) -> error::Result<usize> {
1133        unsafe { syscall3(number::SYS_SIGQUEUE, pid as usize, signal as usize, value) }
1134    }
1135
1136    /// Send a signal to a process group.
1137    pub fn killpg(pgrp: usize, signal: u32) -> error::Result<usize> {
1138        unsafe { syscall2(number::SYS_KILLPG, pgrp, signal as usize) }
1139    }
1140
1141    /// Get the value of an interval timer.
1142    pub fn getitimer(which: u32, value_ptr: usize) -> error::Result<usize> {
1143        unsafe { syscall2(number::SYS_GETITIMER, which as usize, value_ptr) }
1144    }
1145
1146    /// Set an interval timer.
1147    pub fn setitimer(
1148        which: u32,
1149        new_value_ptr: usize,
1150        old_value_ptr: usize,
1151    ) -> error::Result<usize> {
1152        unsafe {
1153            syscall3(
1154                number::SYS_SETITIMER,
1155                which as usize,
1156                new_value_ptr,
1157                old_value_ptr,
1158            )
1159        }
1160    }
1161
1162    // -----------------------------------------------------------------------
1163    // Network (block 410-412)
1164    // -----------------------------------------------------------------------
1165
1166    /// Receive a network packet into `buf`.
1167    ///
1168    /// Returns the number of bytes received.
1169    pub fn net_recv(buf: &mut [u8]) -> error::Result<usize> {
1170        unsafe { syscall2(number::SYS_NET_RECV, buf.as_mut_ptr() as usize, buf.len()) }
1171    }
1172
1173    /// Send a network packet from `buf`.
1174    pub fn net_send(buf: &[u8]) -> error::Result<usize> {
1175        unsafe { syscall2(number::SYS_NET_SEND, buf.as_ptr() as usize, buf.len()) }
1176    }
1177
1178    /// Query network device information.
1179    ///
1180    /// `info_type`: type of info requested. `buf_ptr`: output buffer.
1181    pub fn net_info(info_type: usize, buf_ptr: usize) -> error::Result<usize> {
1182        unsafe { syscall2(number::SYS_NET_INFO, info_type, buf_ptr) }
1183    }
1184
1185    // -----------------------------------------------------------------------
1186    // Volumes / block devices (block 420-422)
1187    // -----------------------------------------------------------------------
1188
1189    /// Read sectors from a volume.
1190    ///
1191    /// `handle`: volume capability handle.
1192    /// `lba`: starting logical block address.
1193    /// `buf_ptr`: destination buffer in user memory.
1194    /// `sector_count`: number of 512-byte sectors to read.
1195    pub fn volume_read(
1196        handle: usize,
1197        lba: usize,
1198        buf_ptr: usize,
1199        sector_count: usize,
1200    ) -> error::Result<usize> {
1201        unsafe { syscall4(number::SYS_VOLUME_READ, handle, lba, buf_ptr, sector_count) }
1202    }
1203
1204    /// Write sectors to a volume.
1205    pub fn volume_write(
1206        handle: usize,
1207        lba: usize,
1208        buf_ptr: usize,
1209        sector_count: usize,
1210    ) -> error::Result<usize> {
1211        unsafe { syscall4(number::SYS_VOLUME_WRITE, handle, lba, buf_ptr, sector_count) }
1212    }
1213
1214    /// Query volume metadata (e.g. total sector count).
1215    pub fn volume_info(handle: usize) -> error::Result<usize> {
1216        unsafe { syscall1(number::SYS_VOLUME_INFO, handle) }
1217    }
1218
1219    // -----------------------------------------------------------------------
1220    // Debug (block 600)
1221    // -----------------------------------------------------------------------
1222
1223    /// Write a debug log message to the kernel serial console.
1224    pub fn debug_log(msg: &[u8]) -> error::Result<usize> {
1225        unsafe { syscall2(number::SYS_DEBUG_LOG, msg.as_ptr() as usize, msg.len()) }
1226    }
1227
1228    // -----------------------------------------------------------------------
1229    // Module management (block 700-703)
1230    // -----------------------------------------------------------------------
1231
1232    /// Load a kernel module by name.
1233    ///
1234    /// Returns a module ID on success.
1235    pub fn module_load(name: &[u8]) -> error::Result<usize> {
1236        unsafe { syscall2(number::SYS_MODULE_LOAD, name.as_ptr() as usize, name.len()) }
1237    }
1238
1239    /// Unload a previously loaded kernel module.
1240    pub fn module_unload(module_id: usize) -> error::Result<usize> {
1241        unsafe { syscall1(number::SYS_MODULE_UNLOAD, module_id) }
1242    }
1243
1244    /// Look up a symbol exported by a kernel module.
1245    ///
1246    /// `module_id`: module to query. `sym_name_ptr`: pointer to symbol name.
1247    /// Returns the symbol address.
1248    pub fn module_get_symbol(module_id: usize, sym_name_ptr: usize) -> error::Result<usize> {
1249        unsafe { syscall2(number::SYS_MODULE_GET_SYMBOL, module_id, sym_name_ptr) }
1250    }
1251
1252    /// Query metadata about a loaded module.
1253    pub fn module_query(module_id: usize, buf_ptr: usize) -> error::Result<usize> {
1254        unsafe { syscall2(number::SYS_MODULE_QUERY, module_id, buf_ptr) }
1255    }
1256
1257    // -----------------------------------------------------------------------
1258    // Silo management (block 800-808)
1259    // -----------------------------------------------------------------------
1260
1261    /// Create a new silo (isolated execution environment).
1262    ///
1263    /// `config_ptr`: pointer to silo configuration structure.
1264    /// Returns the silo ID.
1265    pub fn silo_create(config_ptr: usize) -> error::Result<usize> {
1266        unsafe { syscall1(number::SYS_SILO_CREATE, config_ptr) }
1267    }
1268
1269    /// Configure an existing silo.
1270    pub fn silo_config(silo_id: usize, config_ptr: usize) -> error::Result<usize> {
1271        unsafe { syscall2(number::SYS_SILO_CONFIG, silo_id, config_ptr) }
1272    }
1273
1274    /// Attach a loaded kernel module to a silo.
1275    pub fn silo_attach_module(silo_id: usize, module_id: usize) -> error::Result<usize> {
1276        unsafe { syscall2(number::SYS_SILO_ATTACH_MODULE, silo_id, module_id) }
1277    }
1278
1279    /// Start a silo (begin executing its init process).
1280    pub fn silo_start(silo_id: usize) -> error::Result<usize> {
1281        unsafe { syscall1(number::SYS_SILO_START, silo_id) }
1282    }
1283
1284    /// Gracefully stop a silo.
1285    pub fn silo_stop(silo_id: usize) -> error::Result<usize> {
1286        unsafe { syscall1(number::SYS_SILO_STOP, silo_id) }
1287    }
1288
1289    /// Force-kill a silo and all its processes.
1290    pub fn silo_kill(silo_id: usize) -> error::Result<usize> {
1291        unsafe { syscall1(number::SYS_SILO_KILL, silo_id) }
1292    }
1293
1294    /// Dequeue the next event from a silo's event queue.
1295    ///
1296    /// Returns the raw event value, or `Err(Error::Again)` if empty.
1297    pub fn silo_event_next(silo_id: usize) -> error::Result<usize> {
1298        unsafe { syscall1(number::SYS_SILO_EVENT_NEXT, silo_id) }
1299    }
1300
1301    /// Suspend a running silo.
1302    pub fn silo_suspend(silo_id: usize) -> error::Result<usize> {
1303        unsafe { syscall1(number::SYS_SILO_SUSPEND, silo_id) }
1304    }
1305
1306    /// Resume a suspended silo.
1307    pub fn silo_resume(silo_id: usize) -> error::Result<usize> {
1308        unsafe { syscall1(number::SYS_SILO_RESUME, silo_id) }
1309    }
1310
1311    /// Tighten sandbox mode for the current silo/process context.
1312    pub fn silo_pledge(new_mode: usize) -> error::Result<usize> {
1313        unsafe { syscall1(number::SYS_SILO_PLEDGE, new_mode) }
1314    }
1315
1316    /// Restrict path visibility/rights for the current sandbox context.
1317    pub fn silo_unveil(path: &[u8], rights_bits: usize) -> error::Result<usize> {
1318        unsafe {
1319            syscall3(
1320                number::SYS_SILO_UNVEIL,
1321                path.as_ptr() as usize,
1322                path.len(),
1323                rights_bits,
1324            )
1325        }
1326    }
1327
1328    /// Seal current context into sandboxed mode (no return to broader rights).
1329    pub fn silo_enter_sandbox() -> error::Result<usize> {
1330        unsafe { syscall0(number::SYS_SILO_ENTER_SANDBOX) }
1331    }
1332
1333    // -----------------------------------------------------------------------
1334    // ABI introspection (block 900)
1335    // -----------------------------------------------------------------------
1336
1337    /// Query the kernel ABI version. Returns (major << 16) | minor.
1338    pub fn abi_version() -> error::Result<(u16, u16)> {
1339        let raw = unsafe { syscall0(number::SYS_ABI_VERSION) }?;
1340        Ok(((raw >> 16) as u16, raw as u16))
1341    }
1342}