kernel/process/syscalls/
mod.rs

1use core::{ffi::CStr, mem};
2
3use alloc::{borrow::Cow, string::String, vec::Vec};
4use kernel_user_link::{
5    clock::ClockType,
6    file::{BlockingMode, DirEntry, FileMeta, OpenOptions, SeekFrom, SeekWhence},
7    graphics::{BlitCommand, FrameBufferInfo, GraphicsCommand},
8    process::{PriorityLevel, SpawnFileMapping},
9    sys_arg,
10    syscalls::{
11        syscall_arg_to_u64, syscall_handler_wrapper, SyscallArgError, SyscallError, SyscallResult,
12        NUM_SYSCALLS,
13    },
14    to_arg_err, verify_args, FD_STDERR,
15};
16
17use crate::{
18    cpu::{self, idt::InterruptAllSavedState},
19    devices::{self, clock},
20    executable::elf::Elf,
21    fs::{self, path::Path, FileSystemError},
22    graphics,
23    memory_management::memory_layout::{is_aligned, PAGE_4K},
24    process::{scheduler, Process},
25};
26
27use super::scheduler::{
28    exit_current_process, sleep_current_process, with_current_process, with_process,
29};
30
31type Syscall = fn(&mut InterruptAllSavedState) -> SyscallResult;
32
33const SYSCALLS: [Syscall; NUM_SYSCALLS] = [
34    sys_open,          // kernel_user_link::syscalls::SYS_OPEN
35    sys_write,         // kernel_user_link::syscalls::SYS_WRITE
36    sys_read,          // kernel_user_link::syscalls::SYS_READ
37    sys_close,         // kernel_user_link::syscalls::SYS_CLOSE
38    sys_blocking_mode, // kernel_user_link::syscalls::SYS_BLOCKING_MODE
39    sys_exit,          // kernel_user_link::syscalls::SYS_EXIT
40    sys_spawn,         // kernel_user_link::syscalls::SYS_SPAWN
41    sys_inc_heap,      // kernel_user_link::syscalls::SYS_INC_HEAP
42    sys_create_pipe,   // kernel_user_link::syscalls::SYS_CREATE_PIPE
43    sys_wait_pid,      // kernel_user_link::syscalls::SYS_WAIT_PID
44    sys_stat,          // kernel_user_link::syscalls::SYS_STAT
45    sys_open_dir,      // kernel_user_link::syscalls::SYS_OPEN_DIR
46    sys_read_dir,      // kernel_user_link::syscalls::SYS_READ_DIR
47    sys_get_cwd,       // kernel_user_link::syscalls::SYS_GET_CWD
48    sys_chdir,         // kernel_user_link::syscalls::SYS_CHDIR
49    sys_set_file_meta, // kernel_user_link::syscalls::SYS_SET_FILE_META
50    sys_get_file_meta, // kernel_user_link::syscalls::SYS_GET_FILE_META
51    sys_sleep,         // kernel_user_link::syscalls::SYS_SLEEP
52    sys_get_time,      // kernel_user_link::syscalls::SYS_GET_TIME
53    sys_graphics,      // kernel_user_link::syscalls::SYS_GRAPHICS
54    sys_seek,          // kernel_user_link::syscalls::SYS_SEEK
55    sys_priority,      // kernel_user_link::syscalls::SYS_PRIORITY
56];
57
58impl From<FileSystemError> for SyscallError {
59    fn from(e: FileSystemError) -> Self {
60        match e {
61            FileSystemError::InvalidPath => SyscallError::CouldNotOpenFile,
62            FileSystemError::FileNotFound => SyscallError::FileNotFound,
63            FileSystemError::ReadNotSupported => SyscallError::CouldNotReadFromFile,
64            FileSystemError::WriteNotSupported | FileSystemError::CouldNotSetFileLength => SyscallError::CouldNotWriteToFile,
65            FileSystemError::EndOfFile => SyscallError::EndOfFile,
66            FileSystemError::IsNotDirectory => SyscallError::IsNotDirectory,
67            FileSystemError::IsDirectory => SyscallError::IsDirectory,
68            FileSystemError::AlreadyExists => SyscallError::AlreadyExists,
69            FileSystemError::BufferNotLargeEnough(_) => SyscallError::BufferTooSmall,
70            FileSystemError::OperationNotSupported => SyscallError::OperationNotSupported,
71            FileSystemError::DiskReadError { .. }
72            | FileSystemError::FatError(_)
73            | FileSystemError::MappingError(_)
74            | FileSystemError::DeviceNotFound
75            | FileSystemError::MustBeAbsolute   // should not happen from user mode
76            | FileSystemError::PartitionTableNotFound => panic!("should not happen?"),
77        }
78    }
79}
80
81impl From<clock::ClockTime> for kernel_user_link::clock::ClockTime {
82    fn from(time: clock::ClockTime) -> Self {
83        assert!(time.nanoseconds < clock::NANOS_PER_SEC);
84        Self {
85            seconds: time.seconds,
86            nanoseconds: time.nanoseconds as u32,
87        }
88    }
89}
90
91#[inline]
92fn check_ptr(arg: *const u8, len: usize) -> Result<(), SyscallArgError> {
93    if arg.is_null() {
94        return Err(SyscallArgError::InvalidUserPointer);
95    }
96    if !with_current_process(|process| {
97        process.is_user_address_mapped(arg as _)
98        // very basic check, just check the last byte
99        // TODO: check all mapped pages
100            && process.is_user_address_mapped(arg as usize + len - 1 )
101    }) {
102        return Err(SyscallArgError::InvalidUserPointer);
103    }
104    Ok(())
105}
106
107#[inline]
108fn ptr_as_mut<T>(ptr: *mut u8) -> Result<*mut T, SyscallArgError> {
109    check_ptr(ptr as *const u8, mem::size_of::<T>())?;
110    Ok(ptr as *mut T)
111}
112
113#[inline]
114fn ptr_as_ref<T>(ptr: *const u8) -> Result<*const T, SyscallArgError> {
115    check_ptr(ptr, mem::size_of::<T>())?;
116    Ok(ptr as *const T)
117}
118
119// expects null terminated string
120fn sys_arg_to_str<'a>(arg: *const u8) -> Result<&'a str, SyscallArgError> {
121    check_ptr(arg, 1)?;
122
123    let slice = unsafe { CStr::from_ptr(arg as _) };
124    let string = CStr::to_str(slice).map_err(|_| SyscallArgError::NotValidUtf8)?;
125    Ok(string)
126}
127
128fn sys_arg_to_path<'a>(arg: *const u8) -> Result<&'a Path, SyscallArgError> {
129    sys_arg_to_str(arg).map(Path::new)
130}
131
132fn sys_arg_to_slice<'a, T: Sized>(buf: *const u8, len: usize) -> Result<&'a [T], SyscallArgError> {
133    if len == 0 {
134        return Ok(&[]);
135    }
136
137    check_ptr(buf, len * mem::size_of::<T>())?;
138
139    let slice = unsafe { core::slice::from_raw_parts(buf as _, len) };
140    Ok(slice)
141}
142
143fn sys_arg_to_mut_slice<'a, T: Sized>(
144    buf: *mut u8,
145    len: usize,
146) -> Result<&'a mut [T], SyscallArgError> {
147    if len == 0 {
148        return Ok(&mut []);
149    }
150
151    check_ptr(buf, len * mem::size_of::<T>())?;
152
153    let slice = unsafe { core::slice::from_raw_parts_mut(buf as _, len) };
154    Ok(slice)
155}
156
157/// Allocates space for the strings and copies them
158fn sys_arg_to_str_array(array_ptr: *const u8) -> Result<Vec<String>, SyscallArgError> {
159    let array_ptr = ptr_as_ref::<*const u8>(array_ptr)?;
160
161    let mut array = Vec::new();
162    let mut i = 0;
163    loop {
164        let ptr = unsafe { *array_ptr.offset(i) };
165        if ptr.is_null() {
166            break;
167        }
168        let str = sys_arg_to_str(ptr)?;
169        if str.is_empty() {
170            break;
171        }
172        array.push(String::from(str));
173        i += 1;
174    }
175
176    Ok(array)
177}
178
179/// Allocates space fro the mapping and copies them
180fn sys_arg_to_file_mappings_array<'a>(
181    array_ptr: *const u8,
182    array_size: usize,
183) -> Result<&'a [SpawnFileMapping], SyscallArgError> {
184    let mappings_array = sys_arg_to_slice::<SpawnFileMapping>(array_ptr, array_size)?;
185
186    for i in 0..array_size {
187        let mapping = mappings_array[i];
188
189        // before doing push check that we don't have duplicates
190        for other_mapping in mappings_array.iter().take(i) {
191            if mapping.src_fd == other_mapping.src_fd || mapping.dst_fd == other_mapping.dst_fd {
192                return Err(SyscallArgError::DuplicateFileMappings);
193            }
194        }
195    }
196
197    Ok(mappings_array)
198}
199
200/// Get the absolute path, if the `path` is relative, it will use the current process working directory to get the absolute path.
201/// If the `path` is absolute, it will return it as is.
202fn path_to_proc_absolute_path(path: &Path) -> Cow<'_, Path> {
203    let absolute_path = if path.is_absolute() {
204        Cow::Borrowed(path)
205    } else {
206        let current_dir =
207            with_current_process(|process| process.get_current_dir().path().to_path_buf());
208        Cow::Owned(current_dir.join(path))
209    };
210    assert!(absolute_path.is_absolute());
211
212    absolute_path
213}
214
215fn sys_open(all_state: &mut InterruptAllSavedState) -> SyscallResult {
216    let (path, open_options, flags, ..) = verify_args! {
217        sys_arg!(0, all_state.rest => sys_arg_to_path(*const u8)),
218        sys_arg!(1, all_state.rest => u64),
219        sys_arg!(2, all_state.rest => u64),
220    };
221
222    let open_options = OpenOptions::from_u64(open_options)
223        .ok_or(to_arg_err!(1, SyscallArgError::GeneralInvalid))?;
224
225    let blocking_mode = kernel_user_link::file::parse_flags(flags)
226        .ok_or(to_arg_err!(2, SyscallArgError::GeneralInvalid))?;
227
228    let absolute_path = path_to_proc_absolute_path(path);
229    let file = fs::File::open_blocking(absolute_path, blocking_mode, open_options)?;
230    let file_index = with_current_process(|process| process.push_fs_node(file));
231
232    SyscallResult::Ok(file_index as u64)
233}
234
235fn sys_write(all_state: &mut InterruptAllSavedState) -> SyscallResult {
236    let (file_index, buf, size, ..) = verify_args! {
237        sys_arg!(0, all_state.rest => usize),
238        sys_arg!(1, all_state.rest => *const u8),
239        sys_arg!(2, all_state.rest => usize),
240    };
241    let buf = sys_arg_to_slice(buf, size).map_err(|err| to_arg_err!(0, err))?;
242    let bytes_written = with_current_process(|process| -> Result<u64, SyscallError> {
243        let file = process
244            .get_fs_node(file_index)
245            .ok_or(SyscallError::InvalidFileIndex)?;
246
247        file.as_file_mut()?.write(buf).map_err(|e| e.into())
248    })?;
249    SyscallResult::Ok(bytes_written)
250}
251
252fn sys_read(all_state: &mut InterruptAllSavedState) -> SyscallResult {
253    let (file_index, buf, size, ..) = verify_args! {
254        sys_arg!(0, all_state.rest => usize),
255        sys_arg!(1, all_state.rest => *mut u8),
256        sys_arg!(2, all_state.rest => usize),
257    };
258    let buf = sys_arg_to_mut_slice(buf, size).map_err(|err| to_arg_err!(0, err))?;
259
260    // TODO: fix this hack
261    //
262    // So, that's this about?
263    // We want to read files in blocking mode, and some of these, for example the `/console` file
264    // relies on the keyboard interrupts, but while we are in `with_current_process` we don't get interrupts
265    // because we are inside a lock.
266    // So instead, we take the file out, read from it, and put it back
267    // this is only done for files that are blocking, otherwise we just read from it directly.
268    //
269    // This is a big issue because when threads come in view later, since reading from another thread will report that
270    // the file is not found which is not correct.
271    //
272    // A good solution would be to have waitable objects.
273    let (bytes_read, file) = with_current_process(|process| {
274        let file = process
275            .get_fs_node(file_index)
276            .ok_or(SyscallError::InvalidFileIndex)?
277            .as_file_mut()?;
278        if file.is_blocking() {
279            // take file now
280            let file = process
281                .take_fs_node(file_index)
282                .ok_or(SyscallError::InvalidFileIndex)?;
283            Ok((0, Some(file)))
284        } else {
285            let bytes_read = file.read(buf)?;
286            Ok::<_, SyscallError>((bytes_read, None))
287        }
288    })?;
289
290    let bytes_read = if let Some(mut file) = file {
291        let bytes_read = file.as_file_mut()?.read(buf)?;
292        // put file back
293        with_current_process(|process| process.put_fs_node(file_index, file));
294        bytes_read
295    } else {
296        bytes_read
297    };
298    SyscallResult::Ok(bytes_read)
299}
300
301fn sys_close(all_state: &mut InterruptAllSavedState) -> SyscallResult {
302    let (file_index, ..) = verify_args! {
303        sys_arg!(0, all_state.rest => usize),
304    };
305
306    with_current_process(|process| {
307        process
308            .take_fs_node(file_index)
309            .ok_or(SyscallError::InvalidFileIndex)?;
310        Ok::<_, SyscallError>(())
311    })?;
312
313    SyscallResult::Ok(0)
314}
315
316fn sys_blocking_mode(all_state: &mut InterruptAllSavedState) -> SyscallResult {
317    let (file_index, blocking_mode, ..) = verify_args! {
318        sys_arg!(0, all_state.rest => usize),
319        sys_arg!(1, all_state.rest => u64),
320    };
321
322    let blocking_mode = BlockingMode::try_from(blocking_mode)
323        .map_err(|_| to_arg_err!(1, SyscallArgError::GeneralInvalid))?;
324
325    with_current_process(|process| {
326        let file = process
327            .get_fs_node(file_index)
328            .ok_or(SyscallError::InvalidFileIndex)?;
329        file.as_file_mut()?.set_blocking(blocking_mode);
330        Ok::<_, SyscallError>(())
331    })?;
332
333    SyscallResult::Ok(0)
334}
335
336fn sys_exit(all_state: &mut InterruptAllSavedState) -> SyscallResult {
337    let (exit_code, ..) = verify_args! {
338        sys_arg!(0, all_state.rest => i32),
339    };
340
341    // modify the all_state to go back to the kernel, the current all_state will be dropped
342    exit_current_process(exit_code, all_state);
343    SyscallResult::Ok(exit_code as u64)
344}
345
346fn sys_spawn(all_state: &mut InterruptAllSavedState) -> SyscallResult {
347    let (path, argv, file_mappings, file_mappings_size, ..) = verify_args! {
348        sys_arg!(0, all_state.rest => sys_arg_to_path(*const u8)),
349        sys_arg!(1, all_state.rest => *const u8),   // array of pointers
350        sys_arg!(2, all_state.rest => *const u8),   // array of mappings or null
351        sys_arg!(3, all_state.rest => usize),       // size of the array
352    };
353    let argv = sys_arg_to_str_array(argv).map_err(|err| to_arg_err!(1, err))?;
354    let file_mappings = sys_arg_to_file_mappings_array(file_mappings, file_mappings_size)
355        .map_err(|err| to_arg_err!(2, err))?;
356
357    // don't go into lock if no need to
358    if !file_mappings.is_empty() {
359        // a bit unoptimal, but check all files first before taking them and doing any action
360        with_current_process(|process| {
361            for mapping in file_mappings {
362                process
363                    .get_fs_node(mapping.src_fd)
364                    .ok_or(SyscallError::InvalidFileIndex)?;
365            }
366            Ok::<_, SyscallError>(())
367        })?;
368    }
369
370    let absolute_path = path_to_proc_absolute_path(path);
371
372    let mut file = fs::File::open(absolute_path)?;
373    let elf = Elf::load(&mut file).map_err(|_| SyscallError::CouldNotLoadElf)?;
374    let (current_pid, current_dir) =
375        with_current_process(|process| (process.id, process.get_current_dir().clone()));
376    let mut new_process =
377        Process::allocate_process(current_pid, &elf, &mut file, argv, current_dir)
378            .map_err(|_| SyscallError::CouldNotAllocateProcess)?;
379
380    let mut std_needed = [true; 3];
381    with_current_process(|process| {
382        // take the files if any
383        for mapping in file_mappings.iter() {
384            let file = process
385                .take_fs_node(mapping.src_fd)
386                .ok_or(SyscallError::InvalidFileIndex)?;
387            new_process.attach_fs_node_to_fd(mapping.dst_fd, file);
388            if mapping.dst_fd <= FD_STDERR {
389                std_needed[mapping.dst_fd] = false;
390            }
391        }
392
393        // inherit files STD files if not set
394        for (i, _) in std_needed.iter().enumerate().filter(|(_, &b)| b) {
395            let file = process
396                .get_fs_node(i)
397                .ok_or(SyscallError::InvalidFileIndex)?;
398            let inherited_file = file.as_file()?.clone_inherit();
399            new_process.attach_fs_node_to_fd(i, inherited_file);
400        }
401
402        Ok::<_, SyscallError>(())
403    })?;
404
405    let new_pid = new_process.id();
406    // make sure fds are setup correctly
407    new_process.finish_stdio();
408    scheduler::push_process(new_process);
409
410    SyscallResult::Ok(new_pid)
411}
412
413fn sys_inc_heap(all_state: &mut InterruptAllSavedState) -> SyscallResult {
414    let (increment, ..) = verify_args! {
415        sys_arg!(0, all_state.rest => i64),
416    };
417
418    if !is_aligned(increment.unsigned_abs(), PAGE_4K) {
419        return Err(to_arg_err!(0, SyscallArgError::InvalidHeapIncrement));
420    }
421
422    let old_heap_end = with_current_process(|process| process.add_to_heap(increment as isize))
423        .ok_or(SyscallError::HeapRangesExceeded)?;
424
425    SyscallResult::Ok(old_heap_end as u64)
426}
427
428fn sys_create_pipe(all_state: &mut InterruptAllSavedState) -> SyscallResult {
429    let (read_fd_ptr, write_fd_ptr, ..) = verify_args! {
430        sys_arg!(0, all_state.rest => *mut usize),
431        sys_arg!(1, all_state.rest => *mut usize),
432    };
433    let read_fd_ptr = ptr_as_mut(read_fd_ptr as *mut u8).map_err(|err| to_arg_err!(0, err))?;
434    let write_fd_ptr = ptr_as_mut(write_fd_ptr as *mut u8).map_err(|err| to_arg_err!(1, err))?;
435
436    let (read_file, write_file) = devices::pipe::create_pipe_pair();
437    let (read_fd, write_fd) = with_current_process(|process| {
438        (
439            process.push_fs_node(read_file),
440            process.push_fs_node(write_file),
441        )
442    });
443
444    unsafe {
445        *read_fd_ptr = read_fd;
446        *write_fd_ptr = write_fd;
447    }
448
449    SyscallResult::Ok(0)
450}
451
452fn sys_wait_pid(all_state: &mut InterruptAllSavedState) -> SyscallResult {
453    let (pid, block, ..) = verify_args! {
454        sys_arg!(0, all_state.rest => u64),
455        sys_arg!(1, all_state.rest => u64),
456    };
457    let block = block != 0;
458
459    // see if this is a child process
460    let process_exit = with_current_process(|process| process.get_child_exit(pid));
461    if let Some(exit_code) = process_exit {
462        return SyscallResult::Ok(exit_code as u64);
463    }
464
465    if !block {
466        if scheduler::is_process_running(pid) {
467            return Err(SyscallError::ProcessStillRunning);
468        }
469        return Err(SyscallError::PidNotFound);
470    }
471
472    // if not, wait for it
473    // this stash the current process until the other process exits
474    if !scheduler::wait_for_pid(all_state, pid) {
475        return Err(SyscallError::PidNotFound);
476    }
477    // if we are waiting by the scheduler, this result is not important since it will be overwritten
478    // when we get back
479    SyscallResult::Ok(0)
480}
481
482fn sys_stat(all_state: &mut InterruptAllSavedState) -> SyscallResult {
483    let (path, stat_ptr, ..) = verify_args! {
484        sys_arg!(0, all_state.rest => sys_arg_to_path(*const u8)),
485        sys_arg!(1, all_state.rest => *mut u8),
486    };
487    let stat_ptr = ptr_as_mut(stat_ptr).map_err(|err| to_arg_err!(1, err))?;
488
489    let absolute_path = path_to_proc_absolute_path(path);
490    let (_, _, inode) = fs::open_inode(absolute_path)?;
491
492    unsafe {
493        *stat_ptr = inode.as_file_stat();
494    }
495
496    SyscallResult::Ok(0)
497}
498
499fn sys_open_dir(all_state: &mut InterruptAllSavedState) -> SyscallResult {
500    let (path, ..) = verify_args! {
501        sys_arg!(0, all_state.rest => sys_arg_to_path(*const u8)),
502    };
503
504    let absolute_path = path_to_proc_absolute_path(path);
505    let dir = fs::Directory::open(absolute_path)?;
506    let dir_index = with_current_process(|process| process.push_fs_node(dir));
507
508    SyscallResult::Ok(dir_index as u64)
509}
510
511fn sys_read_dir(all_state: &mut InterruptAllSavedState) -> SyscallResult {
512    let (dir_index, buf, len, ..) = verify_args! {
513        sys_arg!(0, all_state.rest => usize),
514        sys_arg!(1, all_state.rest => *mut u8),
515        sys_arg!(2, all_state.rest => usize),
516    };
517    let buf = sys_arg_to_mut_slice::<DirEntry>(buf, len).map_err(|err| to_arg_err!(1, err))?;
518
519    let entries_read = with_current_process(|process| -> Result<usize, SyscallError> {
520        let file = process
521            .get_fs_node(dir_index)
522            .ok_or(SyscallError::InvalidFileIndex)?;
523        file.as_dir_mut()?.read(buf).map_err(|e| e.into())
524    })?;
525
526    SyscallResult::Ok(entries_read as u64)
527}
528
529fn sys_get_cwd(all_state: &mut InterruptAllSavedState) -> SyscallResult {
530    let (buf, len, ..) = verify_args! {
531        sys_arg!(0, all_state.rest => *mut u8),
532        sys_arg!(1, all_state.rest => usize),
533    };
534    let buf = sys_arg_to_mut_slice::<u8>(buf, len).map_err(|err| to_arg_err!(0, err))?;
535
536    let needed_bytes = with_current_process(|process| -> Result<usize, SyscallError> {
537        let cwd = process.get_current_dir().path();
538        let needed_bytes = cwd.as_str().len();
539        if needed_bytes > len {
540            return Err(SyscallError::BufferTooSmall);
541        }
542        buf[..needed_bytes].copy_from_slice(cwd.as_str().as_bytes());
543        Ok(needed_bytes)
544    })?;
545
546    SyscallResult::Ok(needed_bytes as u64)
547}
548
549fn sys_chdir(all_state: &mut InterruptAllSavedState) -> SyscallResult {
550    let (path, ..) = verify_args! {
551        sys_arg!(0, all_state.rest => sys_arg_to_path(*const u8)),
552    };
553
554    let absolute_path = path_to_proc_absolute_path(path);
555    let dir = fs::Directory::open(absolute_path)?;
556    with_current_process(|process| process.set_current_dir(dir));
557
558    SyscallResult::Ok(0)
559}
560
561fn sys_set_file_meta(all_state: &mut InterruptAllSavedState) -> SyscallResult {
562    let (file_index, meta_id, meta_data, ..) = verify_args! {
563        sys_arg!(0, all_state.rest => usize),
564        sys_arg!(1, all_state.rest => u64),
565        sys_arg!(2, all_state.rest => u64),
566    };
567
568    let meta_op = FileMeta::try_from((meta_id, meta_data))
569        .ok()
570        .ok_or(to_arg_err!(1, SyscallArgError::GeneralInvalid))?;
571
572    let op_on_file = |op: &dyn Fn(&mut fs::File)| {
573        with_current_process(|process| {
574            let file = process
575                .get_fs_node(file_index)
576                .ok_or(SyscallError::InvalidFileIndex)?;
577            op(file.as_file_mut()?);
578            Ok::<_, SyscallError>(())
579        })
580    };
581
582    match meta_op {
583        FileMeta::BlockingMode(blocking_mode) => {
584            op_on_file(&|file| file.set_blocking(blocking_mode))?;
585        }
586        FileMeta::IsTerminal(is_terminal) => {
587            op_on_file(&|file| file.set_terminal(is_terminal))?;
588        }
589        _ => {
590            return Err(to_arg_err!(1, SyscallArgError::GeneralInvalid));
591        }
592    }
593
594    SyscallResult::Ok(0)
595}
596
597fn sys_get_file_meta(all_state: &mut InterruptAllSavedState) -> SyscallResult {
598    let (file_index, meta_id, meta_data_ptr, ..) = verify_args! {
599        sys_arg!(0, all_state.rest => usize),
600        sys_arg!(1, all_state.rest => u64),
601        sys_arg!(2, all_state.rest => *mut u64),
602    };
603    let meta_data_ptr = ptr_as_mut(meta_data_ptr as *mut u8).map_err(|err| to_arg_err!(2, err))?;
604
605    let meta_op = FileMeta::try_from((meta_id, 0))
606        .ok()
607        .ok_or(to_arg_err!(1, SyscallArgError::GeneralInvalid))?;
608
609    let data = with_current_process(|process| {
610        let file = process
611            .get_fs_node(file_index)
612            .ok_or(SyscallError::InvalidFileIndex)?;
613
614        let meta_data = match meta_op {
615            FileMeta::BlockingMode(..) => file.as_file()?.blocking_mode().to_u64(),
616            FileMeta::IsTerminal(..) => file.as_file()?.is_terminal() as u64,
617            _ => {
618                return Err(to_arg_err!(1, SyscallArgError::GeneralInvalid));
619            }
620        };
621
622        Ok::<_, SyscallError>(meta_data)
623    })?;
624
625    // Safety: we checked that the pointer is valid
626    unsafe { *meta_data_ptr = data }
627
628    SyscallResult::Ok(0)
629}
630
631fn sys_sleep(all_state: &mut InterruptAllSavedState) -> SyscallResult {
632    let (seconds, nanoseconds, ..) = verify_args! {
633        sys_arg!(0, all_state.rest => u64),
634        sys_arg!(1, all_state.rest => u64),
635    };
636
637    if nanoseconds >= clock::NANOS_PER_SEC {
638        return Err(to_arg_err!(1, SyscallArgError::InvalidNanoseconds));
639    }
640
641    let time = clock::ClockTime {
642        seconds,
643        nanoseconds,
644    };
645
646    // put the result manually, as we will go back to the kernel after the call below
647    all_state.rest.rax = 0;
648
649    // modify the all_state to go back to the kernel, the current all_state will be dropped
650    sleep_current_process(time, all_state);
651
652    // the result will be saved in kernel's all_state, so we should write the result we want before calling
653    // `sleep_current_process`
654    SyscallResult::Ok(0)
655}
656
657fn sys_get_time(all_state: &mut InterruptAllSavedState) -> SyscallResult {
658    let (time_type, time_ptr, ..) = verify_args! {
659        sys_arg!(0, all_state.rest => u64),
660        sys_arg!(1, all_state.rest => *mut u8),
661    };
662    let time_ptr: *mut kernel_user_link::clock::ClockTime =
663        ptr_as_mut(time_ptr).map_err(|err| to_arg_err!(1, err))?;
664
665    let time_type = ClockType::try_from(time_type)
666        .map_err(|_| to_arg_err!(0, SyscallArgError::GeneralInvalid))?;
667
668    let time = match time_type {
669        ClockType::RealTime => clock::clocks().time_since_unix_epoch().into(),
670        ClockType::SystemTime => clock::clocks().time_since_startup().into(),
671    };
672    // Safety: we checked that the pointer is valid
673    unsafe {
674        *time_ptr = time;
675    }
676
677    SyscallResult::Ok(0)
678}
679
680fn sys_graphics(all_state: &mut InterruptAllSavedState) -> SyscallResult {
681    let (command_id, extra, ..) = verify_args! {
682        sys_arg!(0, all_state.rest => u64),
683        sys_arg!(1, all_state.rest => *mut u8)
684    };
685
686    let command = GraphicsCommand::from_u64(command_id)
687        .ok_or(to_arg_err!(0, SyscallArgError::GeneralInvalid))?;
688
689    let pid = cpu::cpu().process_id;
690
691    match command {
692        GraphicsCommand::TakeOwnership => {
693            if !graphics::vga::controller()
694                .ok_or(SyscallError::GraphicsNotAvailable)?
695                .take_ownership(pid)
696            {
697                return Err(SyscallError::GraphicsAlreadyTaken);
698            }
699        }
700        GraphicsCommand::ReleaseOwnership => {
701            if !graphics::vga::controller()
702                .ok_or(SyscallError::GraphicsNotAvailable)?
703                .release(pid)
704            {
705                return Err(SyscallError::GraphicsNotOwned);
706            }
707        }
708        GraphicsCommand::GetFrameBufferInfo => {
709            let info = *graphics::vga::controller()
710                .ok_or(SyscallError::GraphicsNotAvailable)?
711                .framebuffer_info();
712            let info_ptr =
713                ptr_as_mut::<FrameBufferInfo>(extra).map_err(|err| to_arg_err!(1, err))?;
714            // Safety: we checked that the pointer is valid
715            unsafe {
716                *info_ptr = info;
717            }
718        }
719        GraphicsCommand::Blit => {
720            let blit = ptr_as_ref::<BlitCommand>(extra).map_err(|err| to_arg_err!(1, err))?;
721            // Safety: we checked that the pointer is valid
722            let blit = unsafe { *blit };
723
724            let buffer_len = blit.src_framebuffer_info.memory_size();
725            let buffer = sys_arg_to_slice(blit.memory, buffer_len)
726                .map_err(|_| SyscallError::InvalidGraphicsBuffer)?;
727
728            graphics::vga::controller()
729                .ok_or(SyscallError::GraphicsNotAvailable)?
730                .lock_process(pid)
731                .ok_or(SyscallError::GraphicsNotOwned)?
732                .blit(
733                    buffer,
734                    &blit.src_framebuffer_info,
735                    blit.src,
736                    blit.dst,
737                    blit.size.0,
738                    blit.size.1,
739                );
740        }
741        c => panic!("invalid graphics command {c:?}"),
742    }
743
744    SyscallResult::Ok(0)
745}
746
747fn sys_seek(all_state: &mut InterruptAllSavedState) -> SyscallResult {
748    let (file_index, whence, offset, ..) = verify_args! {
749        sys_arg!(0, all_state.rest => usize),
750        sys_arg!(1, all_state.rest => u64),
751        sys_arg!(2, all_state.rest => i64),
752    };
753    let seek = SeekFrom {
754        whence: whence
755            .try_into()
756            .map_err(|_| to_arg_err!(1, SyscallArgError::GeneralInvalid))?,
757        offset,
758    };
759
760    let new_position = with_current_process(|process| {
761        let file = process
762            .get_fs_node(file_index)
763            .ok_or(SyscallError::InvalidFileIndex)?;
764        let file = file.as_file_mut()?;
765
766        let size = file.size();
767
768        let new_location: u64 = match seek.whence {
769            SeekWhence::Start => seek
770                .offset
771                .try_into()
772                .map_err(|_| SyscallError::InvalidOffset)?,
773            SeekWhence::Current => {
774                let current: i64 = file.current_position().try_into().expect(
775                    "current position should be positive and would be less than i64 bit in size",
776                );
777                current
778                    .checked_add(seek.offset)
779                    .ok_or(SyscallError::InvalidOffset)?
780                    .try_into()
781                    .map_err(|_| SyscallError::InvalidOffset)?
782            }
783            SeekWhence::End => {
784                let end: i64 = size
785                    .try_into()
786                    .expect("size should be positive and would be less than i64 bit in size");
787                end.checked_add(seek.offset)
788                    .ok_or(SyscallError::InvalidOffset)?
789                    .try_into()
790                    .map_err(|_| SyscallError::InvalidOffset)?
791            }
792        };
793
794        file.seek(new_location)?;
795
796        Ok::<_, SyscallError>(new_location)
797    })?;
798
799    SyscallResult::Ok(new_position)
800}
801
802/// Set and Get process priority
803/// TODO: implement security levels, as now we can change the priority of any process
804fn sys_priority(all_state: &mut InterruptAllSavedState) -> SyscallResult {
805    let (pid, priority_level, ..) = verify_args! {
806        sys_arg!(0, all_state.rest => u64),
807        sys_arg!(1, all_state.rest => u64),
808    };
809
810    // if its `None`, just get the value
811    let priority_level = if priority_level == 0 {
812        None
813    } else {
814        Some(
815            PriorityLevel::from_u64(priority_level)
816                .ok_or(to_arg_err!(1, SyscallArgError::GeneralInvalid))?,
817        )
818    };
819
820    let current_priority = with_process(pid, |process| {
821        if let Some(priority_level) = priority_level {
822            process.set_priority(priority_level);
823        }
824
825        Ok::<_, SyscallError>(process.get_priority())
826    })?;
827
828    SyscallResult::Ok(current_priority.to_u64())
829}
830
831pub fn handle_syscall(all_state: &mut InterruptAllSavedState) {
832    let syscall_number = all_state.rest.rax;
833
834    // `syscall_handler_wrapper` will check the syscall number and return error if it exceed the
835    // number of syscalls (NUM_SYSCALLS)
836    all_state.rest.rax = syscall_handler_wrapper(syscall_number, || {
837        let syscall_func = SYSCALLS[syscall_number as usize];
838        syscall_func(all_state)
839    });
840
841    crate::scheduler::yield_current_if_any(all_state);
842}