safa_api/
syscalls.rs

1use super::errors::ErrorStatus;
2
3#[cfg(not(feature = "rustc-dep-of-std"))]
4extern crate alloc;
5use super::raw::{RawSlice, RawSliceMut};
6use alloc::vec::Vec;
7use core::arch::asm;
8use core::{ops, ptr};
9use safa_abi::errors::SysResult;
10pub use safa_abi::syscalls::SyscallTable as SyscallNum;
11
12macro_rules! err_from_u16 {
13    ($result:expr) => {
14        unsafe {
15            Into::<Result<(), ErrorStatus>>::into(
16                TryInto::<SysResult>::try_into($result).unwrap_unchecked(),
17            )
18        }
19    };
20    ($result:expr, $ok:expr) => {
21        err_from_u16!($result).map(|()| $ok)
22    };
23}
24
25#[inline(always)]
26fn syscall1(num: SyscallNum, arg1: usize) -> u16 {
27    let result: u16;
28    unsafe {
29        asm!(
30            "int 0x80",
31            in("rax") num as usize,
32            in("rdi") arg1,
33            lateout("rax") result,
34        );
35        result
36    }
37}
38
39#[inline(always)]
40fn syscall2(num: SyscallNum, arg1: usize, arg2: usize) -> u16 {
41    let result: u16;
42    unsafe {
43        asm!(
44            "int 0x80",
45            in("rax") num as usize,
46            in("rdi") arg1,
47            in("rsi") arg2,
48            lateout("rax") result,
49        );
50        result
51    }
52}
53
54#[inline(always)]
55fn syscall3(num: SyscallNum, arg1: usize, arg2: usize, arg3: usize) -> u16 {
56    let result: u16;
57    unsafe {
58        asm!(
59            "int 0x80",
60            in("rax") num as usize,
61            in("rdi") arg1,
62            in("rsi") arg2,
63            in("rdx") arg3,
64            lateout("rax") result,
65        );
66        result
67    }
68}
69
70#[inline(always)]
71fn syscall5(
72    num: SyscallNum,
73    arg1: usize,
74    arg2: usize,
75    arg3: usize,
76    arg4: usize,
77    arg5: usize,
78) -> u16 {
79    let result: u16;
80    unsafe {
81        asm!(
82            "int 0x80",
83            in("rax") num as usize,
84            in("rdi") arg1,
85            in("rsi") arg2,
86            in("rdx") arg3,
87            in("rcx") arg4,
88            in("r8") arg5,
89            lateout("rax") result,
90        );
91        result
92    }
93}
94
95#[inline(always)]
96fn syscall4(num: SyscallNum, arg1: usize, arg2: usize, arg3: usize, arg4: usize) -> u16 {
97    let result: u16;
98    unsafe {
99        asm!(
100            "int 0x80",
101            in("rax") num as usize,
102            in("rdi") arg1,
103            in("rsi") arg2,
104            in("rdx") arg3,
105            in("rcx") arg4,
106            lateout("rax") result,
107        );
108        result
109    }
110}
111
112#[cfg_attr(
113    not(any(feature = "std", feature = "rustc-dep-of-std")),
114    unsafe(no_mangle)
115)]
116#[inline(always)]
117extern "C" fn syswrite(
118    fd: usize,
119    offset: isize,
120    buf: *const u8,
121    len: usize,
122    dest_wrote: &mut usize,
123) -> u16 {
124    syscall5(
125        SyscallNum::SysWrite,
126        fd,
127        offset as usize,
128        buf as usize,
129        len,
130        dest_wrote as *mut _ as usize,
131    )
132}
133
134#[inline]
135pub fn write(fd: usize, offset: isize, buf: &[u8]) -> Result<usize, ErrorStatus> {
136    let mut dest_wrote = 0;
137    err_from_u16!(
138        syswrite(fd, offset, buf.as_ptr(), buf.len(), &mut dest_wrote),
139        dest_wrote
140    )
141}
142#[cfg_attr(
143    not(any(feature = "std", feature = "rustc-dep-of-std")),
144    unsafe(no_mangle)
145)]
146#[inline(always)]
147extern "C" fn sysread(
148    fd: usize,
149    offset: isize,
150    buf: *mut u8,
151    len: usize,
152    dest_read: &mut usize,
153) -> u16 {
154    syscall5(
155        SyscallNum::SysRead,
156        fd,
157        offset as usize,
158        buf as usize,
159        len,
160        dest_read as *mut _ as usize,
161    )
162}
163
164#[inline]
165pub fn read(fd: usize, offset: isize, buf: &mut [u8]) -> Result<usize, ErrorStatus> {
166    let mut dest_read = 0;
167    err_from_u16!(
168        sysread(fd, offset, buf.as_mut_ptr(), buf.len(), &mut dest_read),
169        dest_read
170    )
171}
172
173#[cfg_attr(
174    not(any(feature = "std", feature = "rustc-dep-of-std")),
175    unsafe(no_mangle)
176)]
177#[inline(always)]
178extern "C" fn syssync(fd: usize) -> u16 {
179    syscall1(SyscallNum::SysSync, fd)
180}
181
182#[inline]
183pub fn sync(fd: usize) -> Result<(), ErrorStatus> {
184    err_from_u16!(syssync(fd))
185}
186#[cfg_attr(
187    not(any(feature = "std", feature = "rustc-dep-of-std")),
188    unsafe(no_mangle)
189)]
190#[inline(always)]
191extern "C" fn syssbrk(size: isize, target_ptr: &mut *mut u8) -> u16 {
192    syscall2(
193        SyscallNum::SysSbrk,
194        size as usize,
195        target_ptr as *mut _ as usize,
196    )
197}
198
199#[inline]
200pub fn sbrk(size: isize) -> Result<*mut u8, ErrorStatus> {
201    let mut target_ptr: *mut u8 = core::ptr::null_mut();
202    err_from_u16!(syssbrk(size, &mut target_ptr), target_ptr)
203}
204
205#[cfg_attr(
206    not(any(feature = "std", feature = "rustc-dep-of-std")),
207    unsafe(no_mangle)
208)]
209#[inline(always)]
210extern "C" fn sysexit(code: usize) -> ! {
211    syscall1(SyscallNum::SysExit, code);
212    unreachable!()
213}
214
215#[inline]
216pub fn exit(code: usize) -> ! {
217    sysexit(code)
218}
219#[cfg_attr(
220    not(any(feature = "std", feature = "rustc-dep-of-std")),
221    unsafe(no_mangle)
222)]
223#[inline(always)]
224extern "C" fn syschdir(buf_ptr: *const u8, buf_len: usize) -> u16 {
225    syscall2(SyscallNum::SysCHDir, buf_ptr as usize, buf_len)
226}
227
228#[inline]
229pub fn chdir(path: &str) -> Result<(), ErrorStatus> {
230    let path = path.as_bytes();
231    err_from_u16!(syschdir(path.as_ptr(), path.len()))
232}
233
234#[cfg_attr(
235    not(any(feature = "std", feature = "rustc-dep-of-std")),
236    unsafe(no_mangle)
237)]
238/// Gets the current working directory
239/// returns Err(ErrorStatus::Generic) if the buffer is too small to hold the cwd
240#[inline(always)]
241extern "C" fn sysgetcwd(cwd_buf_ptr: *mut u8, cwd_buf_len: usize, dest_len: &mut usize) -> u16 {
242    syscall3(
243        SyscallNum::SysGetCWD,
244        cwd_buf_ptr as usize,
245        cwd_buf_len,
246        dest_len as *mut _ as usize,
247    )
248}
249
250#[inline]
251pub fn getcwd() -> Result<Vec<u8>, ErrorStatus> {
252    let do_syscall = |cwd_buf: &mut [u8]| {
253        let mut dest_len = 0;
254        err_from_u16!(
255            sysgetcwd(cwd_buf.as_mut_ptr(), cwd_buf.len(), &mut dest_len),
256            dest_len
257        )
258    };
259
260    let extend = |cwd_buf: &mut Vec<u8>| unsafe {
261        cwd_buf.reserve(128);
262        cwd_buf.set_len(cwd_buf.capacity());
263    };
264
265    let mut cwd_buf = Vec::new();
266    extend(&mut cwd_buf);
267
268    loop {
269        match do_syscall(&mut cwd_buf) {
270            Ok(len) => unsafe {
271                cwd_buf.set_len(len);
272                return Ok(cwd_buf);
273            },
274            Err(err) => {
275                if err == ErrorStatus::Generic {
276                    extend(&mut cwd_buf);
277                } else {
278                    return Err(err);
279                }
280            }
281        }
282    }
283}
284
285#[derive(Debug, Clone, Copy)]
286#[repr(C)]
287pub struct SpawnFlags(u8);
288impl SpawnFlags {
289    pub const CLONE_RESOURCES: Self = Self(1 << 0);
290    pub const CLONE_CWD: Self = Self(1 << 1);
291}
292
293impl ops::BitOr for SpawnFlags {
294    type Output = Self;
295    fn bitor(self, rhs: Self) -> Self::Output {
296        Self(self.0 | rhs.0)
297    }
298}
299#[cfg_attr(
300    not(any(feature = "std", feature = "rustc-dep-of-std")),
301    unsafe(no_mangle)
302)]
303#[inline(always)]
304extern "C" fn syspspawn(
305    name_ptr: *const u8,
306    name_len: usize,
307    path_ptr: *const u8,
308    path_len: usize,
309    argv_ptr: *const RawSlice<u8>,
310    argv_len: usize,
311    flags: SpawnFlags,
312    dest_pid: &mut usize,
313) -> u16 {
314    /// the temporary config struct for the spawn syscall, passed to the syscall
315    /// because if it was passed as a bunch of arguments it would be too big to fit
316    /// inside the registers
317    #[repr(C)]
318    struct SpawnConfig {
319        name: RawSlice<u8>,
320        argv: RawSlice<RawSlice<u8>>,
321        flags: SpawnFlags,
322    }
323
324    let config = SpawnConfig {
325        name: unsafe { RawSlice::from_raw_parts(name_ptr, name_len) },
326        argv: unsafe { RawSlice::from_raw_parts(argv_ptr, argv_len) },
327        flags,
328    };
329    syscall4(
330        SyscallNum::SysPSpawn,
331        path_ptr as usize,
332        path_len,
333        (&raw const config) as usize,
334        dest_pid as *mut _ as usize,
335    )
336}
337
338/// spawns a new process
339/// # Safety
340/// `argv` must be a valid pointer to a slice of slices of `&str`
341/// `argv` will become invaild after use, using it is UB
342#[inline]
343pub unsafe fn unsafe_pspawn(
344    name: Option<&str>,
345    path: &str,
346    argv: *mut [&str],
347    flags: SpawnFlags,
348) -> Result<usize, ErrorStatus> {
349    let mut pid = 0;
350
351    let name = name.map(|s| s.as_bytes());
352    let name_ptr = name.map(|s| s.as_ptr()).unwrap_or(ptr::null());
353    let name_len = name.map(|s| s.len()).unwrap_or(0);
354
355    let argv: *mut [&[u8]] = argv as *mut [&[u8]];
356    let argv = unsafe { RawSliceMut::from_slices(argv) };
357    err_from_u16!(
358        syspspawn(
359            name_ptr,
360            name_len,
361            path.as_ptr(),
362            path.len(),
363            argv.as_ptr(),
364            argv.len(),
365            flags,
366            &mut pid,
367        ),
368        pid
369    )
370}
371
372/// same as [`pspawn`] but safe because it makes it clear that `argv` is consumed`
373#[inline]
374pub fn pspawn(
375    name: Option<&str>,
376    path: &str,
377    mut argv: Vec<&str>,
378    flags: SpawnFlags,
379) -> Result<usize, ErrorStatus> {
380    let argv: &mut [&str] = &mut argv;
381    unsafe { unsafe_pspawn(name, path, argv as *mut _, flags) }
382}
383#[cfg_attr(
384    not(any(feature = "std", feature = "rustc-dep-of-std")),
385    unsafe(no_mangle)
386)]
387#[inline(always)]
388extern "C" fn syswait(pid: usize, exit_code: &mut usize) -> u16 {
389    syscall2(SyscallNum::SysWait, pid, exit_code as *mut _ as usize)
390}
391
392#[inline]
393pub fn wait(pid: usize) -> Result<usize, ErrorStatus> {
394    let mut dest_exit_code = 0;
395    err_from_u16!(syswait(pid, &mut dest_exit_code), dest_exit_code)
396}