Skip to main content

syscall/
call.rs

1use super::{
2    arch::*,
3    data::{Map, Stat, StdFsCallMeta, TimeSpec},
4    error::Result,
5    flag::*,
6    number::*,
7};
8
9use core::mem;
10
11/// Get the current system time
12pub fn clock_gettime(clock: usize, tp: &mut TimeSpec) -> Result<usize> {
13    unsafe { syscall2(SYS_CLOCK_GETTIME, clock, tp as *mut TimeSpec as usize) }
14}
15
16/// Copy and transform a file descriptor into specified fd number
17pub fn dup_into(fd: usize, out: usize, buf: &[u8]) -> Result<usize> {
18    unsafe { syscall4(SYS_DUP_INTO, fd, buf.as_ptr() as usize, buf.len(), out) }
19}
20
21/// Copy and transform a file descriptor
22pub fn dup2(fd: usize, newfd: usize, buf: &[u8]) -> Result<usize> {
23    unsafe { syscall4(SYS_DUP2, fd, newfd, buf.as_ptr() as usize, buf.len()) }
24}
25
26/// Change file ownership
27pub fn fchown(fd: usize, uid: u32, gid: u32) -> Result<usize> {
28    unsafe { syscall3(SYS_FCHOWN, fd, uid as usize, gid as usize) }
29}
30
31/// Change file descriptor flags
32pub fn fcntl(fd: usize, cmd: usize, arg: usize) -> Result<usize> {
33    unsafe { syscall3(SYS_FCNTL, fd, cmd, arg) }
34}
35
36/// Map a file into memory, but with the ability to set the address to map into, either as a hint
37/// or as a requirement of the map.
38///
39/// # Errors
40/// `EACCES` - the file descriptor was not open for reading
41/// `EBADF` - if the file descriptor was invalid
42/// `ENODEV` - mmapping was not supported
43/// `EINVAL` - invalid combination of flags
44/// `EEXIST` - if [`MapFlags::MAP_FIXED`] was set, and the address specified was already in use.
45///
46pub unsafe fn fmap(fd: usize, map: &Map) -> Result<usize> {
47    syscall3(
48        SYS_FMAP,
49        fd,
50        map as *const Map as usize,
51        mem::size_of::<Map>(),
52    )
53}
54
55/// Unmap whole (or partial) continous memory-mapped files
56pub unsafe fn funmap(addr: usize, len: usize) -> Result<usize> {
57    syscall2(SYS_FUNMAP, addr, len)
58}
59
60/// Retrieve the canonical path of a file
61pub fn fpath(fd: usize, buf: &mut [u8]) -> Result<usize> {
62    unsafe { syscall3(SYS_FPATH, fd, buf.as_mut_ptr() as usize, buf.len()) }
63}
64
65/// Create a link to a file
66pub fn flink<T: AsRef<str>>(fd: usize, path: T) -> Result<usize> {
67    let path = path.as_ref();
68    unsafe { syscall3(SYS_FLINK, fd, path.as_ptr() as usize, path.len()) }
69}
70
71/// Rename a file
72pub fn frename<T: AsRef<str>>(fd: usize, path: T) -> Result<usize> {
73    let path = path.as_ref();
74    unsafe { syscall3(SYS_FRENAME, fd, path.as_ptr() as usize, path.len()) }
75}
76
77/// Get metadata about a file
78pub fn fstat(fd: usize, stat: &mut Stat) -> Result<usize> {
79    unsafe {
80        syscall3(
81            SYS_FSTAT,
82            fd,
83            stat as *mut Stat as usize,
84            mem::size_of::<Stat>(),
85        )
86    }
87}
88
89/// Sync a file descriptor to its underlying medium
90pub fn fsync(fd: usize) -> Result<usize> {
91    unsafe { syscall1(SYS_FSYNC, fd) }
92}
93
94/// Fast userspace mutex
95pub unsafe fn futex(
96    addr: *mut i32,
97    op: usize,
98    val: i32,
99    val2: usize,
100    addr2: *mut i32,
101) -> Result<usize> {
102    syscall5(
103        SYS_FUTEX,
104        addr as usize,
105        op,
106        (val as isize) as usize,
107        val2,
108        addr2 as usize,
109    )
110}
111
112/// Seek to `offset` bytes in a file descriptor
113pub fn lseek(fd: usize, offset: isize, whence: usize) -> Result<usize> {
114    unsafe { syscall3(SYS_LSEEK, fd, offset as usize, whence) }
115}
116
117/// Change mapping flags
118pub unsafe fn mprotect(addr: usize, size: usize, flags: MapFlags) -> Result<usize> {
119    syscall3(SYS_MPROTECT, addr, size, flags.bits())
120}
121
122/// Sleep for the time specified in `req`
123pub fn nanosleep(req: &TimeSpec, rem: &mut TimeSpec) -> Result<usize> {
124    unsafe {
125        syscall2(
126            SYS_NANOSLEEP,
127            req as *const TimeSpec as usize,
128            rem as *mut TimeSpec as usize,
129        )
130    }
131}
132
133/// Open a file at a specific path into specified fd number
134pub fn openat_into(
135    fd: usize,
136    out: usize,
137    path: impl AsRef<str>,
138    flags: usize,
139    fcntl_flags: usize,
140) -> Result<usize> {
141    let path = path.as_ref();
142    unsafe {
143        syscall5(
144            SYS_OPENAT_INTO2,
145            fd,
146            path.as_ptr() as usize,
147            path.len() | (fcntl_flags & O_FCNTL_MASK),
148            flags,
149            out,
150        )
151    }
152}
153
154/// Remove a file at at specific path
155pub fn unlinkat<T: AsRef<str>>(fd: usize, path: T, flags: usize) -> Result<usize> {
156    let path = path.as_ref();
157    unsafe { syscall4(SYS_UNLINKAT, fd, path.as_ptr() as usize, path.len(), flags) }
158}
159/// Read from a file descriptor into a buffer
160pub fn read(fd: usize, buf: &mut [u8]) -> Result<usize> {
161    unsafe { syscall3(SYS_READ, fd, buf.as_mut_ptr() as usize, buf.len()) }
162}
163
164/// Write a buffer to a file descriptor
165///
166/// The kernel will attempt to write the bytes in `buf` to the file descriptor `fd`, returning
167/// either an `Err`, explained below, or `Ok(count)` where `count` is the number of bytes which
168/// were written.
169///
170/// # Errors
171///
172/// * `EAGAIN` - the file descriptor was opened with `O_NONBLOCK` and writing would block
173/// * `EBADF` - the file descriptor is not valid or is not open for writing
174/// * `EFAULT` - `buf` does not point to the process's addressible memory
175/// * `EIO` - an I/O error occurred
176/// * `ENOSPC` - the device containing the file descriptor has no room for data
177/// * `EPIPE` - the file descriptor refers to a pipe or socket whose reading end is closed
178pub fn write(fd: usize, buf: &[u8]) -> Result<usize> {
179    unsafe { syscall3(SYS_WRITE, fd, buf.as_ptr() as usize, buf.len()) }
180}
181
182/// Yield the process's time slice to the kernel
183///
184/// This function will return Ok(0) on success
185pub fn sched_yield() -> Result<usize> {
186    unsafe { syscall0(SYS_YIELD) }
187}
188
189pub trait Call {
190    #[inline]
191    fn extra_flags(&self) -> CallFlags {
192        CallFlags::empty()
193    }
194    unsafe fn raw_call(
195        &self,
196        payload_ptr: *const u8,
197        len: usize,
198        flags: CallFlags,
199        metadata: &[u64],
200    ) -> Result<usize>;
201}
202
203impl Call for usize {
204    unsafe fn raw_call(
205        &self,
206        payload_ptr: *const u8,
207        len: usize,
208        flags: CallFlags,
209        metadata: &[u64],
210    ) -> Result<usize> {
211        unsafe {
212            syscall5(
213                SYS_CALL,
214                *self,
215                payload_ptr as usize,
216                len,
217                metadata.len() | flags.bits(),
218                metadata.as_ptr() as usize,
219            )
220        }
221    }
222}
223
224impl Call for &[usize] {
225    #[inline]
226    fn extra_flags(&self) -> CallFlags {
227        CallFlags::MULTIPLE_FDS2
228    }
229    unsafe fn raw_call(
230        &self,
231        payload_ptr: *const u8,
232        len: usize,
233        flags: CallFlags,
234        metadata: &[u64],
235    ) -> Result<usize> {
236        // align_of::<usize>() is >= 4 on all architectures supported by Redox, whereas the kernel
237        // currently only allows MULTIPLE_FDS2 calls to use at most 2.
238        if self.len() > core::mem::align_of::<usize>() {
239            return Err(crate::error::Error::new(crate::error::EINVAL));
240        }
241        unsafe {
242            syscall5(
243                SYS_CALL,
244                self.as_ptr() as usize | self.len(),
245                payload_ptr as usize,
246                len,
247                metadata.len() | flags.bits(),
248                metadata.as_ptr() as usize,
249            )
250        }
251    }
252}
253
254/// SYS_CALL interface, read-only variant
255pub fn call_ro<T: Call>(
256    fd: T,
257    payload: &mut [u8],
258    flags: CallFlags,
259    metadata: &[u64],
260) -> Result<usize> {
261    let combined_flags = flags | fd.extra_flags();
262    unsafe {
263        fd.raw_call(
264            payload.as_mut_ptr(),
265            payload.len(),
266            combined_flags | CallFlags::READ,
267            metadata,
268        )
269    }
270}
271/// SYS_CALL interface, write-only variant
272pub fn call_wo<T: Call>(
273    fd: T,
274    payload: &[u8],
275    flags: CallFlags,
276    metadata: &[u64],
277) -> Result<usize> {
278    let combined_flags = flags | fd.extra_flags();
279    unsafe {
280        fd.raw_call(
281            payload.as_ptr(),
282            payload.len(),
283            combined_flags | CallFlags::WRITE,
284            metadata,
285        )
286    }
287}
288/// SYS_CALL interface, read-write variant
289pub fn call_rw<T: Call>(
290    fd: T,
291    payload: &mut [u8],
292    flags: CallFlags,
293    metadata: &[u64],
294) -> Result<usize> {
295    let combined_flags = flags | fd.extra_flags();
296    unsafe {
297        fd.raw_call(
298            payload.as_mut_ptr(),
299            payload.len(),
300            combined_flags | CallFlags::READ | CallFlags::WRITE,
301            metadata,
302        )
303    }
304}
305
306pub fn std_fs_call<T: Call>(fd: T, payload: &mut [u8], metadata: &StdFsCallMeta) -> Result<usize> {
307    call_rw(fd, payload, CallFlags::STD_FS, metadata)
308}