emerald_kernel_user_link/
syscalls.rs

1mod types_conversions;
2
3/// must be one of user interrupts, i.e. 0x20+
4///
5/// This is the number of the interrupts that we are going to use between the
6/// user-kernel
7pub const SYSCALL_INTERRUPT_NUMBER: u8 = 0xFE;
8
9pub const NUM_SYSCALLS: usize = 22;
10
11mod numbers {
12    pub const SYS_OPEN: u64 = 0;
13    pub const SYS_WRITE: u64 = 1;
14    pub const SYS_READ: u64 = 2;
15    pub const SYS_CLOSE: u64 = 3;
16    #[deprecated(note = "Use SYS_SET_FILE_META instead")]
17    pub const SYS_BLOCKING_MODE: u64 = 4;
18    pub const SYS_EXIT: u64 = 5;
19    pub const SYS_SPAWN: u64 = 6;
20    pub const SYS_INC_HEAP: u64 = 7;
21    pub const SYS_CREATE_PIPE: u64 = 8;
22    pub const SYS_WAIT_PID: u64 = 9;
23    pub const SYS_STAT: u64 = 10;
24    pub const SYS_OPEN_DIR: u64 = 11;
25    pub const SYS_READ_DIR: u64 = 12;
26    pub const SYS_GET_CWD: u64 = 13;
27    pub const SYS_CHDIR: u64 = 14;
28    pub const SYS_SET_FILE_META: u64 = 15;
29    pub const SYS_GET_FILE_META: u64 = 16;
30    pub const SYS_SLEEP: u64 = 17;
31    pub const SYS_GET_TIME: u64 = 18;
32    pub const SYS_GRAPHICS: u64 = 19;
33    pub const SYS_SEEK: u64 = 20;
34    pub const SYS_PRIORITY: u64 = 21;
35}
36pub use numbers::*;
37
38/// Creates a syscall, the first argument is the syscall number (in RAX), then the arguments are as follows
39/// RCX, RDX, RSI, RDI, R8, R9, R10 (7 arguments max)
40#[macro_export]
41macro_rules! call_syscall {
42    ($syscall_num:expr $(,)?) => {
43        call_syscall!(@step $syscall_num; { }; { })
44    };
45    ($syscall_num:expr, $($args:expr),* $(,)?) => {
46        call_syscall!(@step $syscall_num; { }; {$($args),*})
47    };
48    (@step $syscall_num: expr; {$($generated:tt)*}; {}) => {
49        call_syscall!(@final $syscall_num, {$($generated)*})
50    };
51    (@step $syscall_num: expr; {$($generated:tt)*}; {$one:expr}) => {
52        call_syscall!(@step $syscall_num; {in("rcx") $one, $($generated)*}; {})
53    };
54    (@step $syscall_num: expr; {$($generated:tt)*}; {$one:expr, $two:expr}) => {
55        call_syscall!(@step $syscall_num; {in("rdx") $two, $($generated)*}; {$one})
56    };
57    (@step $syscall_num: expr; {$($generated:tt)*}; {$one:expr, $two:expr, $three:expr}) => {
58        call_syscall!(@step $syscall_num; {in("rsi") $three, $($generated)*}; {$one, $two})
59    };
60    (@step $syscall_num: expr; {$($generated:tt)*}; {$one:expr, $two:expr, $three:expr, $four:expr}) => {
61        call_syscall!(@step $syscall_num; {in("rdi") $four, $($generated)*}; {$one, $two, $three})
62    };
63    (@step $syscall_num: expr; {$($generated:tt)*}; {$one:expr, $two:expr, $three:expr, $four:expr, $five:expr}) => {
64        call_syscall!(@step $syscall_num; {in("r8") $five, $($generated)*}; {$one, $two, $three, $four})
65    };
66    (@step $syscall_num: expr; {$($generated:tt)*}; {$one:expr, $two:expr, $three:expr, $four:expr, $five:expr, $six:expr}) => {
67        call_syscall!(@step $syscall_num; {in("r9") $six, $($generated)*}; {$one, $two, $three, $four, $five})
68    };
69    (@step $syscall_num: expr; {$($generated:tt)*}; {$one:expr, $two:expr, $three:expr, $four:expr, $five:expr, $six:expr, $seven:expr}) => {
70        call_syscall!(@step $syscall_num; {in("r10") $seven, $($generated)*}; {$one, $two, $three, $four, $five, $six})
71    };
72    (@step $syscall_num: expr; {$($generated:tt)*}; {$($args:expr),*}) => {
73        compile_error!("Too many arguments for syscall")
74    };
75    (@final $syscall_num: expr, {$($generated:tt)*}) => {
76        {
77            let result: u64;
78            ::core::arch::asm!("int 0xFE",
79                            inout("rax") $syscall_num => result,
80                            $($generated)*
81                            options(nomem, nostack, preserves_flags));
82            $crate::syscalls::syscall_result_from_u64(result)
83        }
84    };
85}
86
87/// Get the syscall arguments from the interrupt state, the arguments come from
88/// the registers RCX, RDX, RSI, RDI, R8, R9, R10
89#[macro_export]
90macro_rules! sys_arg {
91    ($num:tt, $context_struct:expr) => {
92        sys_arg!(@impl $num, $context_struct => u64)
93    };
94    ($num:tt, $context_struct:expr => $func:ident($ty:ty)) => {
95        sys_arg!($num, $context_struct => $ty).and_then($func)
96    };
97    ($num:tt, $context_struct:expr => $ty:ty) => {
98        syscall_arg_to_u64::<$ty>(sys_arg!(@impl $num, $context_struct))
99    };
100    (@impl 0, $context_struct:expr) => {
101        $context_struct.rcx
102    };
103    (@impl 1, $context_struct:expr) => {
104        $context_struct.rdx
105    };
106    (@impl 2, $context_struct:expr) => {
107        $context_struct.rsi
108    };
109    (@impl 3, $context_struct:expr) => {
110        $context_struct.rdi
111    };
112    (@impl 4, $context_struct:expr) => {
113        $context_struct.r8
114    };
115    (@impl 5, $context_struct:expr) => {
116        $context_struct.r9
117    };
118    (@impl 6, $context_struct:expr) => {
119        $context_struct.r10
120    };
121    (@impl $rest:tt, $context_struct:expr) => {
122        compile_error!("Not valid argument number")
123    };
124}
125
126#[macro_export]
127macro_rules! to_arg_err {
128    ($num:tt, $err:expr) => {
129        to_arg_err!(@impl $num, $err)
130    };
131    (@impl 0, $err:expr) => {
132        $crate::syscalls::SyscallError::InvalidArgument(::core::option::Option::Some($err), None, None, None, None, None, None)
133    };
134    (@impl 1, $err:expr) => {
135        $crate::syscalls::SyscallError::InvalidArgument(None, ::core::option::Option::Some($err), None, None, None, None, None)
136    };
137    (@impl 2, $err:expr) => {
138        $crate::syscalls::SyscallError::InvalidArgument(None, None, ::core::option::Option::Some($err), None, None, None, None)
139    };
140    (@impl 3, $err:expr) => {
141        $crate::syscalls::SyscallError::InvalidArgument(None, None, None, ::core::option::Option::Some($err), None, None, None)
142    };
143    (@impl 4, $err:expr) => {
144        $crate::syscalls::SyscallError::InvalidArgument(None, None, None, None, ::core::option::Option::Some($err), None, None)
145    };
146    (@impl 5, $err:expr) => {
147        $crate::syscalls::SyscallError::InvalidArgument(None, None, None, None, None, ::core::option::Option::Some($err), None)
148    };
149    (@impl 6, $err:expr) => {
150        $crate::syscalls::SyscallError::InvalidArgument(None, None, None, None, None, None, ::core::option::Option::Some($err))
151    };
152    (@impl $rest:tt, $err:expr) => {
153        compile_error!("Not valid argument number")
154    };
155}
156
157#[macro_export]
158macro_rules! verify_args {
159    () => {((), (), (), (), (), (), ())};
160    ($arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr, $arg6:expr, $arg7:expr) => {
161        match ($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7) {
162            (
163                Ok(a),
164                Ok(b),
165                Ok(c),
166                Ok(d),
167                Ok(e),
168                Ok(f),
169                Ok(g),
170            ) => {(a, b, c, d, e, f, g)}
171            err => {
172                return $crate::syscalls::SyscallResult::Err(
173                    $crate::syscalls::SyscallError::InvalidArgument(
174                        err.0.err(), err.1.err(), err.2.err(), err.3.err(), err.4.err(), err.5.err(), err.6.err(),
175                    ),
176                )
177            }
178        }
179    };
180    ($arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr, $arg6:expr, $arg7:expr, $extra:expr) => {
181        compile_error!("Too many arguments for syscall")
182    };
183    // general
184    ($($args:expr),* $(,)?) => {
185        verify_args!($($args ,)* Ok(()))
186    };
187}
188
189pub trait FromSyscallArgU64 {
190    fn from_syscall_arg_u64(value: u64) -> Result<Self, SyscallArgError>
191    where
192        Self: Sized;
193}
194
195pub fn syscall_arg_to_u64<T: FromSyscallArgU64>(value: u64) -> Result<T, SyscallArgError> {
196    T::from_syscall_arg_u64(value)
197}
198
199#[derive(Debug, Clone, Copy, PartialEq, Eq)]
200#[repr(u8)]
201#[non_exhaustive]
202pub enum SyscallArgError {
203    // 0 is valid (used by Option::None)
204    GeneralInvalid = 1,
205    InvalidUserPointer = 2,
206    NotValidUtf8 = 3,
207    InvalidHeapIncrement = 4,
208    DuplicateFileMappings = 5,
209    InvalidNanoseconds = 6,
210}
211
212impl SyscallArgError {
213    fn try_from(value: u8) -> Result<Option<Self>, ()> {
214        match value {
215            0 => Ok(None),
216            1 => Ok(Some(SyscallArgError::GeneralInvalid)),
217            2 => Ok(Some(SyscallArgError::InvalidUserPointer)),
218            3 => Ok(Some(SyscallArgError::NotValidUtf8)),
219            4 => Ok(Some(SyscallArgError::InvalidHeapIncrement)),
220            5 => Ok(Some(SyscallArgError::DuplicateFileMappings)),
221            6 => Ok(Some(SyscallArgError::InvalidNanoseconds)),
222            _ => Err(()),
223        }
224    }
225}
226
227#[repr(align(8))]
228#[derive(Debug, Clone, Copy)]
229#[repr(u8)]
230#[non_exhaustive]
231pub enum SyscallError {
232    SyscallNotFound = 0,
233    InvalidError = 1,
234    CouldNotOpenFile = 2,
235    InvalidFileIndex = 3,
236    CouldNotWriteToFile = 4,
237    CouldNotReadFromFile = 5,
238    CouldNotLoadElf = 6,
239    CouldNotAllocateProcess = 7,
240    HeapRangesExceeded = 8,
241    EndOfFile = 9,
242    FileNotFound = 10,
243    PidNotFound = 11,
244    ProcessStillRunning = 12,
245    IsNotDirectory = 13,
246    IsDirectory = 14,
247    BufferTooSmall = 15,
248    GraphicsNotAvailable = 16,
249    GraphicsAlreadyTaken = 17,
250    GraphicsNotOwned = 18,
251    InvalidGraphicsBuffer = 19,
252    InvalidOffset = 20,
253    AlreadyExists = 21,
254    OperationNotSupported = 22,
255    InvalidArgument(
256        Option<SyscallArgError>,
257        Option<SyscallArgError>,
258        Option<SyscallArgError>,
259        Option<SyscallArgError>,
260        Option<SyscallArgError>,
261        Option<SyscallArgError>,
262        Option<SyscallArgError>,
263    ),
264}
265
266pub type SyscallResult = Result<u64, SyscallError>;
267
268impl From<SyscallError> for SyscallResult {
269    fn from(error: SyscallError) -> Self {
270        SyscallResult::Err(error)
271    }
272}
273
274/// Creates an error u64 value for syscall, each byte controls the error for that
275/// argument, if the byte is 0, then the argument is valid, otherwise it is
276/// invalid. and it represent the error code
277/// the error value contains up to 7 arguments errors, the most significant bit
278/// indicate a negative number.
279///
280/// This function will always set the msb to 1
281fn create_syscall_error(
282    arg1: Option<SyscallArgError>,
283    arg2: Option<SyscallArgError>,
284    arg3: Option<SyscallArgError>,
285    arg4: Option<SyscallArgError>,
286    arg5: Option<SyscallArgError>,
287    arg6: Option<SyscallArgError>,
288    arg7: Option<SyscallArgError>,
289) -> u64 {
290    let mut error = 0u64;
291    error |= arg1.map(|e| e as u64).unwrap_or(0);
292    error |= arg2.map(|e| e as u64).unwrap_or(0) << 8;
293    error |= arg3.map(|e| e as u64).unwrap_or(0) << 16;
294    error |= arg4.map(|e| e as u64).unwrap_or(0) << 24;
295    error |= arg5.map(|e| e as u64).unwrap_or(0) << 32;
296    error |= arg6.map(|e| e as u64).unwrap_or(0) << 40;
297    error |= arg7.map(|e| e as u64).unwrap_or(0) << 48;
298    error |= 1 << 63;
299    error
300}
301
302#[inline(always)]
303pub fn syscall_handler_wrapper<F>(syscall_number: u64, f: F) -> u64
304where
305    F: FnOnce() -> SyscallResult,
306{
307    if syscall_number >= NUM_SYSCALLS as u64 {
308        return syscall_result_to_u64(SyscallResult::Err(SyscallError::SyscallNotFound));
309    }
310    let result = f();
311    syscall_result_to_u64(result)
312}
313
314pub fn syscall_result_to_u64(result: SyscallResult) -> u64 {
315    match result {
316        SyscallResult::Ok(value) => {
317            assert!(
318                value & (1 << 63) == 0,
319                "syscall result should not have msb set"
320            );
321            value
322        }
323        SyscallResult::Err(error) => {
324            let err_upper = match error {
325                SyscallError::SyscallNotFound => -1i64 as u64,
326                SyscallError::InvalidArgument(arg1, arg2, arg3, arg4, arg5, arg6, arg7) => {
327                    create_syscall_error(arg1, arg2, arg3, arg4, arg5, arg6, arg7)
328                }
329                SyscallError::CouldNotOpenFile => 2 << 56,
330                SyscallError::InvalidFileIndex => 3 << 56,
331                SyscallError::CouldNotWriteToFile => 4 << 56,
332                SyscallError::CouldNotReadFromFile => 5 << 56,
333                SyscallError::CouldNotLoadElf => 6 << 56,
334                SyscallError::CouldNotAllocateProcess => 7 << 56,
335                SyscallError::HeapRangesExceeded => 8 << 56,
336                SyscallError::EndOfFile => 9 << 56,
337                SyscallError::FileNotFound => 10 << 56,
338                SyscallError::PidNotFound => 11 << 56,
339                SyscallError::ProcessStillRunning => 12 << 56,
340                SyscallError::IsNotDirectory => 13 << 56,
341                SyscallError::IsDirectory => 14 << 56,
342                SyscallError::BufferTooSmall => 15 << 56,
343                SyscallError::GraphicsNotAvailable => 16 << 56,
344                SyscallError::GraphicsAlreadyTaken => 17 << 56,
345                SyscallError::GraphicsNotOwned => 18 << 56,
346                SyscallError::InvalidGraphicsBuffer => 19 << 56,
347                SyscallError::InvalidOffset => 20 << 56,
348                SyscallError::AlreadyExists => 21 << 56,
349                SyscallError::OperationNotSupported => 22 << 56,
350                SyscallError::InvalidError => panic!("Should never be used"),
351            };
352
353            err_upper | (1 << 63)
354        }
355    }
356}
357
358pub fn syscall_result_from_u64(value: u64) -> SyscallResult {
359    if value & (1 << 63) == 0 {
360        SyscallResult::Ok(value)
361    } else {
362        // remove last bit
363        let value = value & !(1 << 63);
364        // last byte
365        let err_byte = (value >> 56) as u8;
366
367        let invalid_error_code = |_| SyscallError::InvalidError;
368
369        let err = match err_byte {
370            0 => {
371                let arg1 =
372                    SyscallArgError::try_from((value & 0xFF) as u8).map_err(invalid_error_code)?;
373                let arg2 = SyscallArgError::try_from(((value >> 8) & 0xFF) as u8)
374                    .map_err(invalid_error_code)?;
375                let arg3 = SyscallArgError::try_from(((value >> 16) & 0xFF) as u8)
376                    .map_err(invalid_error_code)?;
377                let arg4 = SyscallArgError::try_from(((value >> 24) & 0xFF) as u8)
378                    .map_err(invalid_error_code)?;
379                let arg5 = SyscallArgError::try_from(((value >> 32) & 0xFF) as u8)
380                    .map_err(invalid_error_code)?;
381                let arg6 = SyscallArgError::try_from(((value >> 40) & 0xFF) as u8)
382                    .map_err(invalid_error_code)?;
383                let arg7 = SyscallArgError::try_from(((value >> 48) & 0xFF) as u8)
384                    .map_err(invalid_error_code)?;
385
386                SyscallError::InvalidArgument(arg1, arg2, arg3, arg4, arg5, arg6, arg7)
387            }
388            1 => SyscallError::InvalidError,
389            2 => SyscallError::CouldNotOpenFile,
390            3 => SyscallError::InvalidFileIndex,
391            4 => SyscallError::CouldNotWriteToFile,
392            5 => SyscallError::CouldNotReadFromFile,
393            6 => SyscallError::CouldNotLoadElf,
394            7 => SyscallError::CouldNotAllocateProcess,
395            8 => SyscallError::HeapRangesExceeded,
396            9 => SyscallError::EndOfFile,
397            10 => SyscallError::FileNotFound,
398            11 => SyscallError::PidNotFound,
399            12 => SyscallError::ProcessStillRunning,
400            13 => SyscallError::IsNotDirectory,
401            14 => SyscallError::IsDirectory,
402            15 => SyscallError::BufferTooSmall,
403            16 => SyscallError::GraphicsNotAvailable,
404            17 => SyscallError::GraphicsAlreadyTaken,
405            18 => SyscallError::GraphicsNotOwned,
406            19 => SyscallError::InvalidGraphicsBuffer,
407            20 => SyscallError::InvalidOffset,
408            21 => SyscallError::AlreadyExists,
409            22 => SyscallError::OperationNotSupported,
410            _ => SyscallError::InvalidError,
411        };
412        SyscallResult::Err(err)
413    }
414}