safa_api/syscalls/
raw.rs

1//! This module defines exposes raw SafaOS syscalls
2//! and their rust counterparts
3
4#[cfg(not(feature = "rustc-dep-of-std"))]
5extern crate alloc;
6
7use super::types::{
8    OptionalPtrMut, OptionalStrPtr, Pid, RequiredPtr, RequiredPtrMut, Ri, StrPtr, StrPtrMut,
9    SyscallResult,
10};
11use super::SyscallNum;
12use super::{define_syscall, DirEntry, FileAttr, RawSlice, RawSliceMut};
13use super::{err_from_u16, ErrorStatus};
14use crate::process::{sysmeta_stderr, sysmeta_stdin, sysmeta_stdout};
15use alloc::vec::Vec;
16use core::ptr;
17use safa_abi::raw::processes::SpawnFlags;
18use safa_abi::raw::Optional;
19
20define_syscall!(SyscallNum::SysGetDirEntry => {
21    /// Gets the directory entry for the path `path` and puts it in `dest_direntry`
22    /// path must be valid utf-8
23    /// if `dest_direntry` is not null, it will be set to the directory entry
24    sysgetdirentry(path_ptr: StrPtr, path_len: usize, dest_direntry: OptionalPtrMut<DirEntry>)
25});
26
27#[inline]
28pub fn getdirentry(path: &str) -> Result<DirEntry, ErrorStatus> {
29    let mut dest_direntry: DirEntry = unsafe { core::mem::zeroed() };
30    err_from_u16!(
31        sysgetdirentry(path.as_ptr(), path.len(), &raw mut dest_direntry),
32        dest_direntry
33    )
34}
35
36define_syscall! {
37    SyscallNum::SysOpen => {
38        /// Opens the file with the path `path` and puts the resource id in `dest_fd`
39        ///
40        /// path must be valid utf-8
41        sysopen(path_ptr: StrPtr, path_len: usize, dest_fd: *mut Ri)
42    },
43    SyscallNum::SysCreate => {
44        /// Creates the file with the path `path`
45        /// path must be valid utf-8
46        syscreate_file(path_ptr: StrPtr, path_len: usize)
47    },
48    SyscallNum::SysCreateDir => {
49        /// Creates the directory with the path `path`
50        ///
51        /// path must be valid utf-8
52        syscreate_dir(path_ptr: StrPtr, path_len: usize)
53    },
54    SyscallNum::SysClose => {
55        /// Closes the file with the resource id `fd`
56        sysclose(fd: Ri)
57    }
58}
59
60#[inline]
61/// Opens the path `path` and returns the resource id of the file descriptor
62///
63/// see [`sysopen`] for underlying syscall
64pub fn open(path: &str) -> Result<Ri, ErrorStatus> {
65    let mut dest_fd = 0xAAAAAAAAAAAAAAAAusize;
66    err_from_u16!(
67        sysopen(path.as_ptr(), path.len(), &raw mut dest_fd),
68        dest_fd
69    )
70}
71
72#[inline]
73/// Creates the file with the path `path`
74///
75/// see [`syscreate_file`] for underlying syscall
76pub fn create(path: &str) -> Result<(), ErrorStatus> {
77    err_from_u16!(syscreate_file(path.as_ptr(), path.len()))
78}
79
80#[inline]
81/// Creates the directory with the path `path`
82///
83/// see [`syscreate_dir`] for underlying syscall
84pub fn createdir(path: &str) -> Result<(), ErrorStatus> {
85    err_from_u16!(syscreate_dir(path.as_ptr(), path.len()))
86}
87
88#[inline]
89/// Closes the file with the resource id `fd`
90///
91/// see [`sysclose`] for underlying syscall
92pub fn close(fd: Ri) -> Result<(), ErrorStatus> {
93    err_from_u16!(sysclose(fd))
94}
95
96// Directory Iterator related syscalls
97define_syscall! {
98    SyscallNum::SysDirIterOpen =>
99    {
100        /// Opens a directory iterator for the directory with the resource id `dir_ri`
101        sysdiriter_open(dir_ri: Ri, dest_ri: RequiredPtrMut<Ri>)
102    },
103    SyscallNum::SysDirIterClose => {
104        /// Closes a directory iterator
105        sysdiriter_close(fd: Ri)
106    },
107    SyscallNum::SysDirIterNext => {
108        /// Gets the next directory entry from a directory iterator,
109        ///
110        /// puts the results in `dest_direntry`,
111        ///
112        /// puts zeroed DirEntry in `dest_direntry` if there are no more entries
113        ///
114        /// returns [`ErrorStatus::Generic`] (1) if there are no more entries
115        sysdiriter_next(dir_ri: Ri, dest_direntry: OptionalPtrMut<DirEntry>)
116    }
117}
118
119#[inline]
120/// Closes the directory iterator with the resource id `fd`
121///
122/// see [`sysdiriter_close`] for underlying syscall
123pub fn diriter_close(fd: Ri) -> Result<(), ErrorStatus> {
124    err_from_u16!(sysdiriter_close(fd))
125}
126
127#[inline]
128/// Opens a directory iterator for the directory with the resource id `dir_ri`,
129/// returns the resource id of the directory iterator
130///
131/// see [`sysdiriter_open`] for underlying syscall
132pub fn diriter_open(dir_ri: Ri) -> Result<Ri, ErrorStatus> {
133    let mut dest_fd: usize = 0xAAAAAAAAAAAAAAAAusize;
134    err_from_u16!(sysdiriter_open(dir_ri, &raw mut dest_fd), dest_fd)
135}
136
137#[inline]
138/// Gets the next directory entry from a directory iterator,
139///
140/// see [`sysdiriter_next`] for underlying syscall
141pub fn diriter_next(dir_ri: Ri) -> Result<DirEntry, ErrorStatus> {
142    let mut dest_direntry: DirEntry = unsafe { core::mem::zeroed() };
143    err_from_u16!(
144        sysdiriter_next(dir_ri, &raw mut dest_direntry),
145        dest_direntry
146    )
147}
148
149// File related syscalls
150define_syscall! {
151    SyscallNum::SysWrite => {
152        /// Writes `len` bytes from `buf` to the file with the resource id `fd` at offset `offset`
153        ///
154        /// if `dest_wrote` is not null, it will be set to the number of bytes written
155        syswrite(fd: Ri, offset: isize, buf: RequiredPtr<u8>, len: usize, dest_wrote: OptionalPtrMut<usize>)
156    },
157    SyscallNum::SysTruncate => {
158        /// Truncates the file with the resource id `fd` to `len` bytes
159        systruncate(fd: Ri, len: usize)
160    },
161    SyscallNum::SysFSize => {
162        /// Gets the size of the file with the resource id `fd` and puts it in `dest_size`
163        sysfsize(fd: Ri, dest_size: OptionalPtrMut<usize>)
164    },
165    SyscallNum::SysFAttrs => {
166        /// Gets the file attributes of the file with the resource id `fd` and puts them in `dest_attrs`
167        sysfattrs(fd: Ri, dest_attrs: OptionalPtrMut<FileAttr>)
168    },
169    SyscallNum::SysRead => {
170        /// Reads `len` bytes from the file with the resource id `fd` at offset `offset` into `buf`
171        ///
172        /// if `dest_read` is not null, it will be set to the number of bytes read
173        sysread(fd: Ri, offset: isize, buf: RequiredPtrMut<u8>, len: usize, dest_read: OptionalPtrMut<usize>)
174    },
175    SyscallNum::SysSync => {
176        /// Syncs the resource with the resource id `fd`
177        syssync(ri: Ri)
178    },
179    SyscallNum::SysDup => {
180        /// Duplicates the resource refered to by the resource id `ri` and puts the new resource id in `dest_ri`
181        sysdup(ri: Ri, dest_ri: RequiredPtrMut<Ri>)
182    }
183}
184
185#[inline]
186/// Writes `buf.len()` bytes from `buf` to the file with the resource id `fd` at offset `offset`
187/// and returns the number of bytes written
188pub fn write(fd: Ri, offset: isize, buf: &[u8]) -> Result<usize, ErrorStatus> {
189    let mut dest_wrote = 0;
190    err_from_u16!(
191        syswrite(fd, offset, buf.as_ptr(), buf.len(), &mut dest_wrote),
192        dest_wrote
193    )
194}
195
196#[inline]
197/// Truncates the file with the resource id `fd` to `len` bytes
198pub fn truncate(fd: Ri, len: usize) -> Result<(), ErrorStatus> {
199    err_from_u16!(systruncate(fd, len))
200}
201
202#[inline]
203/// Gets the size of the file with the resource id `fd`
204pub fn fsize(fd: Ri) -> Result<usize, ErrorStatus> {
205    let mut dest_size = 0;
206    err_from_u16!(sysfsize(fd, &raw mut dest_size), dest_size)
207}
208
209#[inline]
210/// Gets the file attributes of the file with the resource id `fd`
211pub fn fattrs(fd: Ri) -> Result<FileAttr, ErrorStatus> {
212    let mut attrs: FileAttr = unsafe { core::mem::zeroed() };
213    err_from_u16!(sysfattrs(fd, &raw mut attrs), attrs)
214}
215
216#[inline]
217/// Reads `buf.len()` bytes from the file with the resource id `fd` at offset `offset` into `buf`
218pub fn read(fd: Ri, offset: isize, buf: &mut [u8]) -> Result<Ri, ErrorStatus> {
219    let mut dest_read = 0;
220    err_from_u16!(
221        sysread(fd, offset, buf.as_mut_ptr(), buf.len(), &mut dest_read),
222        dest_read
223    )
224}
225
226#[inline]
227/// Syncs the resource with the resource id `ri`
228pub fn sync(ri: Ri) -> Result<(), ErrorStatus> {
229    err_from_u16!(syssync(ri))
230}
231
232#[inline]
233/// Duplicates the resource refered to by the resource id `ri`
234/// and returns the new resource id
235pub fn dup(ri: Ri) -> Result<Ri, ErrorStatus> {
236    let mut dest_ri = 0xAAAAAAAAAAAAAAAAusize;
237    err_from_u16!(sysdup(ri, &mut dest_ri), dest_ri)
238}
239
240// Process related syscalls
241define_syscall! {
242    SyscallNum::SysSbrk => {
243        /// Increases the range of the process's data break by `size` bytes and puts the new break pointer in `target_ptr`
244        syssbrk(size: isize, target_ptr: OptionalPtrMut<*mut u8>)
245    },
246    SyscallNum::SysExit => {
247        /// Exits the process with the exit code `code`
248        sysexit(code: usize) unreachable
249    },
250    SyscallNum::SysYield => {
251        /// Switches to the next process/thread
252        sysyield()
253    },
254    SyscallNum::SysWait => {
255        /// Waits for the process with the resource id `pid` to exit
256        /// if `exit_code` is not null, it will be set to the exit code of the process
257        syswait(pid: Pid, exit_code: *mut usize)
258    },
259    SyscallNum::SysCHDir => {
260        /// Changes the current working directory to the path `buf` with length `buf_len`
261        /// (expects given buffer to be utf-8)
262        syschdir(buf_ptr: StrPtr, buf_len: usize)
263    },
264    SyscallNum::SysGetCWD => {
265        /// Gets the current working directory and puts it in `cwd_buf` with length `cwd_buf_len`
266        /// if `dest_len` is not null, it will be set to the length of the cwd
267        /// if the cwd is too long to fit in `cwd_buf`, the syscall will return [`ErrorStatus::Generic`] (1)
268        /// the cwd is currently maximumally 1024 bytes
269        sysgetcwd(cwd_buf: StrPtrMut, cwd_buf_len: usize, dest_len: OptionalPtrMut<usize>)
270    }
271}
272
273#[inline]
274/// Increases the range of the process's data break by `size` bytes
275/// returns the new break pointer
276///
277/// you should probably use [`crate::alloc::GLOBAL_SYSTEM_ALLOCATOR`] instead for allocating memory
278pub fn sbrk(size: isize) -> Result<*mut u8, ErrorStatus> {
279    let mut target_ptr: *mut u8 = core::ptr::null_mut();
280    err_from_u16!(syssbrk(size, &mut target_ptr), target_ptr)
281}
282
283#[inline]
284pub fn exit(code: usize) -> ! {
285    sysexit(code)
286}
287
288#[inline]
289pub fn yield_now() {
290    debug_assert!(sysyield().is_success())
291}
292
293#[inline]
294/// Waits for the process with the resource id `pid` to exit
295/// and returns the exit code of the process
296///
297/// will return 0 as an exit code if process wasn't found
298pub fn wait(pid: Pid) -> Result<usize, ErrorStatus> {
299    let mut dest_exit_code = 0;
300    err_from_u16!(syswait(pid, &mut dest_exit_code), dest_exit_code)
301}
302
303#[inline]
304/// Changes the current work dir to `path`
305pub fn chdir(path: &str) -> Result<(), ErrorStatus> {
306    let path = path.as_bytes();
307    err_from_u16!(syschdir(path.as_ptr(), path.len()))
308}
309
310use alloc::string::String;
311#[inline]
312/// Retrives the current work dir
313pub fn getcwd() -> Result<String, ErrorStatus> {
314    let mut buffer = Vec::with_capacity(safa_abi::consts::MAX_PATH_LENGTH);
315    unsafe {
316        buffer.set_len(buffer.capacity());
317    }
318
319    let mut dest_len: usize = 0xAAAAAAAAAAAAAAAAusize;
320    let len = err_from_u16!(
321        sysgetcwd(buffer.as_mut_ptr(), buffer.len(), &raw mut dest_len),
322        dest_len
323    )?;
324
325    buffer.truncate(len);
326    unsafe { Ok(String::from_utf8_unchecked(buffer)) }
327}
328
329define_syscall! {
330    SyscallNum::SysShutdown => {
331        /// Shuts down the system
332        sysshutdown() unreachable
333    },
334    SyscallNum::SysReboot => {
335        /// Reboots the system
336        sysreboot() unreachable
337    }
338}
339
340#[inline]
341pub fn shutdown() -> ! {
342    sysshutdown()
343}
344
345#[inline]
346pub fn reboot() -> ! {
347    sysreboot()
348}
349
350// doesn't use define_syscall because we use a different signature then the rest of the syscalls
351#[cfg_attr(
352    not(any(feature = "std", feature = "rustc-dep-of-std")),
353    unsafe(no_mangle)
354)]
355#[inline(always)]
356/// Spawns a new process with the path `path` with arguments `argv` and flags `flags`
357/// name_ptr can be null, in which case the name will be the path
358/// path and name must be valid utf-8
359/// if `dest_pid` is not null, it will be set to the pid of the new process
360/// if `stdin`, `stdout`, or `stderr` are not `None`, the corresponding file descriptors will be inherited from the parent
361/// if they are None they will be inherited from the parent
362extern "C" fn syspspawn(
363    name_ptr: OptionalStrPtr,
364    name_len: usize,
365    path_ptr: StrPtr,
366    path_len: usize,
367    argv_ptr: OptionalPtrMut<RawSlice<u8>>,
368    argv_len: usize,
369    flags: SpawnFlags,
370    dest_pid: OptionalPtrMut<Pid>,
371    stdin: Optional<usize>,
372    stdout: Optional<usize>,
373    stderr: Optional<usize>,
374) -> SyscallResult {
375    use safa_abi::raw::processes::SpawnConfig;
376    use safa_abi::raw::processes::TaskMetadata;
377    let (mut stdin, mut stdout, mut stderr): (Option<_>, Option<_>, Option<_>) =
378        (stdin.into(), stdout.into(), stderr.into());
379
380    let metadata = {
381        if stdin.is_none() && stdout.is_none() && stderr.is_none() {
382            None
383        } else {
384            stdout.get_or_insert_with(|| sysmeta_stdout());
385            stdin.get_or_insert_with(|| sysmeta_stdin());
386            stderr.get_or_insert_with(|| sysmeta_stderr());
387
388            Some(TaskMetadata::new(stdout, stdin, stderr))
389        }
390    };
391
392    let metadata = metadata.as_ref();
393    let meta_ptr = metadata.map(|m| m as *const _).unwrap_or(core::ptr::null());
394
395    let config = SpawnConfig {
396        version: 1,
397        name: unsafe { RawSlice::from_raw_parts(name_ptr, name_len) },
398        argv: unsafe { RawSliceMut::from_raw_parts(argv_ptr, argv_len) },
399        flags,
400        metadata: meta_ptr,
401    };
402    syscall!(
403        SyscallNum::SysPSpawn,
404        path_ptr as usize,
405        path_len,
406        (&raw const config) as usize,
407        dest_pid as *mut _ as usize,
408    )
409}
410
411/// spawns a new process
412/// # Arguments
413/// * `stdin`, `stdout`, `stderr` are the file descriptors of stdio, if None, they will be inherited from the parent
414/// # Safety
415/// `argv` must be a valid pointer to a slice of slices of `&str`
416/// `argv` will become invaild after use, using it is UB
417#[inline]
418pub unsafe fn unsafe_pspawn(
419    name: Option<&str>,
420    path: &str,
421    argv: *mut [&str],
422    flags: SpawnFlags,
423    stdin: Option<usize>,
424    stdout: Option<usize>,
425    stderr: Option<usize>,
426) -> Result<usize, ErrorStatus> {
427    let mut pid = 0;
428
429    let name = name.map(|s| s.as_bytes());
430    let name_ptr = name.map(|s| s.as_ptr()).unwrap_or(ptr::null());
431    let name_len = name.map(|s| s.len()).unwrap_or(0);
432
433    let argv: *mut [&[u8]] = argv as *mut [&[u8]];
434    let argv = unsafe { RawSliceMut::from_slices(argv) };
435    err_from_u16!(
436        syspspawn(
437            name_ptr,
438            name_len,
439            path.as_ptr(),
440            path.len(),
441            argv.as_mut_ptr(),
442            argv.len(),
443            flags,
444            &mut pid,
445            stdin.into(),
446            stdout.into(),
447            stderr.into(),
448        ),
449        pid
450    )
451}
452
453/// same as [`unsafe_pspawn`] but safe because it makes it clear that `argv` is consumed`
454#[inline]
455pub fn pspawn(
456    name: Option<&str>,
457    path: &str,
458    mut argv: Vec<&str>,
459    flags: SpawnFlags,
460    stdin: Option<usize>,
461    stdout: Option<usize>,
462    stderr: Option<usize>,
463) -> Result<usize, ErrorStatus> {
464    let argv: &mut [&str] = &mut argv;
465    unsafe { unsafe_pspawn(name, path, argv as *mut _, flags, stdin, stdout, stderr) }
466}