diff options
author | nthnluu <nate1299@me.com> | 2024-01-28 21:20:27 -0500 |
---|---|---|
committer | nthnluu <nate1299@me.com> | 2024-01-28 21:20:27 -0500 |
commit | c63f340d90800895f007de64b7d2d14624263331 (patch) | |
tree | 2c0849fa597dd6da831c8707b6f2603403778d7b /kernel/api/syscall.c |
Created student weenix repository
Diffstat (limited to 'kernel/api/syscall.c')
-rw-r--r-- | kernel/api/syscall.c | 757 |
1 files changed, 757 insertions, 0 deletions
diff --git a/kernel/api/syscall.c b/kernel/api/syscall.c new file mode 100644 index 0000000..1be5276 --- /dev/null +++ b/kernel/api/syscall.c @@ -0,0 +1,757 @@ +#include "errno.h" +#include "globals.h" +#include "kernel.h" +#include <fs/vfs.h> +#include <util/time.h> + +#include "main/inits.h" +#include "main/interrupt.h" + +#include "mm/kmalloc.h" +#include "mm/mman.h" + +#include "fs/vfs_syscall.h" +#include "fs/vnode.h" + +#include "drivers/tty/tty.h" +#include "test/kshell/kshell.h" + +#include "vm/brk.h" +#include "vm/mmap.h" + +#include "api/access.h" +#include "api/exec.h" +#include "api/syscall.h" +#include "api/utsname.h" + +static long syscall_handler(regs_t *regs); + +static long syscall_dispatch(size_t sysnum, uintptr_t args, regs_t *regs); + +extern size_t active_tty; + +static const char *syscall_strings[49] = { + "syscall", "exit", "fork", "read", "write", "open", + "close", "waitpid", "link", "unlink", "execve", "chdir", + "sleep", "unknown", "lseek", "sync", "nuke", "dup", + "pipe", "ioctl", "unknown", "rmdir", "mkdir", "getdents", + "mmap", "mprotect", "munmap", "rename", "uname", "thr_create", + "thr_cancel", "thr_exit", "thr_yield", "thr_join", "gettid", "getpid", + "unknown", "unkown", "unknown", "errno", "halt", "get_free_mem", + "set_errno", "dup2", "brk", "mount", "umount", "stat", "usleep"}; + +void syscall_init(void) { intr_register(INTR_SYSCALL, syscall_handler); } + +// if condition, set errno to err and return -1 +#define ERROR_OUT(condition, err) \ + if (condition) \ + { \ + curthr->kt_errno = (err); \ + return -1; \ + } + +// if ret < 0, set errno to -ret and return -1 +#define ERROR_OUT_RET(ret) ERROR_OUT(ret < 0, -ret) + +/* + * Be sure to look at other examples of implemented system calls to see how + * this should be done - the general outline is as follows. + * + * - Initialize a read_args_t struct locally in kernel space and copy from + * userland args. + * - Allocate a temporary buffer (a page-aligned block of n pages that are + * enough space to store the number of bytes to read) + * - Call do_read() with the buffer and then copy the buffer to the userland + * args after the system call + * - Make sure to free the temporary buffer allocated + * - Return the number of bytes read, or return -1 and set the current thread's + * errno appropriately using ERROR_OUT_RET. + */ +static long sys_read(read_args_t *args) +{ + NOT_YET_IMPLEMENTED("VM: ***none***"); + return -1; +} + +/* + * Be sure to look at other examples of implemented system calls to see how + * this should be done - the general outline is as follows. + * + * This function is very similar to sys_read - see above comments. You'll need + * to use the functions copy_from_user() and do_write(). Make sure to + * allocate a new temporary buffer for the data that is being written. This + * is to ensure that pagefaults within kernel mode do not happen. + */ +static long sys_write(write_args_t *args) +{ + NOT_YET_IMPLEMENTED("VM: ***none***"); + return -1; +} + +/* + * This similar to the other system calls that you have implemented above. + * + * The general steps are as follows: + * - Copy the arguments from user memory + * - Check that the count field is at least the size of a dirent_t + * - Use a while loop to read count / sizeof(dirent_t) directory entries into + * the provided dirp and call do_getdent + * - Return the number of bytes read + */ +static long sys_getdents(getdents_args_t *args) +{ + NOT_YET_IMPLEMENTED("VM: ***none***"); + return -1; +} + +#ifdef __MOUNTING__ +static long sys_mount(mount_args_t *arg) +{ + mount_args_t kern_args; + char *source; + char *target; + char *type; + long ret; + + if (copy_from_user(&kern_args, arg, sizeof(kern_args)) < 0) + { + curthr->kt_errno = EFAULT; + return -1; + } + + /* null is okay only for the source */ + source = user_strdup(&kern_args.spec); + if (NULL == (target = user_strdup(&kern_args.dir))) + { + kfree(source); + curthr->kt_errno = EINVAL; + return -1; + } + if (NULL == (type = user_strdup(&kern_args.fstype))) + { + kfree(source); + kfree(target); + curthr->kt_errno = EINVAL; + return -1; + } + + ret = do_mount(source, target, type); + kfree(source); + kfree(target); + kfree(type); + + if (ret) + { + curthr->kt_errno = -ret; + return -1; + } + + return 0; +} + +static long sys_umount(argstr_t *input) +{ + argstr_t kstr; + char *target; + long ret; + + if (copy_from_user(&kstr, input, sizeof(kstr)) < 0) + { + curthr->kt_errno = EFAULT; + return -1; + } + + if (NULL == (target = user_strdup(&kstr))) + { + curthr->kt_errno = EINVAL; + return -1; + } + + ret = do_umount(target); + kfree(target); + + if (ret) + { + curthr->kt_errno = -ret; + return -1; + } + + return 0; +} +#endif + +static long sys_close(int fd) +{ + long ret = do_close(fd); + ERROR_OUT_RET(ret); + return ret; +} + +static long sys_dup(int fd) +{ + long ret = do_dup(fd); + ERROR_OUT_RET(ret); + return ret; +} + +static long sys_dup2(const dup2_args_t *args) +{ + dup2_args_t kargs; + long ret = copy_from_user(&kargs, args, sizeof(kargs)); + ERROR_OUT_RET(ret); + ret = do_dup2(kargs.ofd, kargs.nfd); + ERROR_OUT_RET(ret); + return ret; +} + +static long sys_mkdir(mkdir_args_t *args) +{ + mkdir_args_t kargs; + long ret = copy_from_user(&kargs, args, sizeof(kargs)); + ERROR_OUT_RET(ret); + + char *path; + ret = user_strdup(&kargs.path, &path); + ERROR_OUT_RET(ret); + + ret = do_mkdir(path); + kfree(path); + + ERROR_OUT_RET(ret); + return ret; +} + +static long sys_rmdir(argstr_t *args) +{ + argstr_t kargs; + long ret = copy_from_user(&kargs, args, sizeof(kargs)); + ERROR_OUT_RET(ret); + + char *path; + ret = user_strdup(&kargs, &path); + ERROR_OUT_RET(ret); + + ret = do_rmdir(path); + kfree(path); + + ERROR_OUT_RET(ret); + return ret; +} + +static long sys_unlink(argstr_t *args) +{ + argstr_t kargs; + long ret = copy_from_user(&kargs, args, sizeof(kargs)); + ERROR_OUT_RET(ret); + + char *path; + ret = user_strdup(&kargs, &path); + ERROR_OUT_RET(ret); + + ret = do_unlink(path); + kfree(path); + + ERROR_OUT_RET(ret); + return ret; +} + +static long sys_link(link_args_t *args) +{ + link_args_t kargs; + long ret = copy_from_user(&kargs, args, sizeof(kargs)); + ERROR_OUT_RET(ret); + + char *to, *from; + ret = user_strdup(&kargs.to, &to); + ERROR_OUT_RET(ret); + + ret = user_strdup(&kargs.from, &from); + if (ret) + { + kfree(to); + ERROR_OUT_RET(ret); + } + + ret = do_link(from, to); + kfree(to); + kfree(from); + + ERROR_OUT_RET(ret); + return ret; +} + +static long sys_rename(rename_args_t *args) +{ + rename_args_t kargs; + long ret = copy_from_user(&kargs, args, sizeof(kargs)); + ERROR_OUT_RET(ret); + + char *oldpath, *newpath; + ret = user_strdup(&kargs.oldpath, &oldpath); + ERROR_OUT_RET(ret); + + ret = user_strdup(&kargs.newpath, &newpath); + if (ret) + { + kfree(oldpath); + ERROR_OUT_RET(ret); + } + + ret = do_rename(oldpath, newpath); + kfree(oldpath); + kfree(newpath); + + ERROR_OUT_RET(ret); + return ret; +} + +static long sys_chdir(argstr_t *args) +{ + argstr_t kargs; + long ret = copy_from_user(&kargs, args, sizeof(kargs)); + ERROR_OUT_RET(ret); + + char *path; + ret = user_strdup(&kargs, &path); + ERROR_OUT_RET(ret); + + ret = do_chdir(path); + kfree(path); + + ERROR_OUT_RET(ret); + return ret; +} + +static long sys_lseek(lseek_args_t *args) +{ + lseek_args_t kargs; + long ret = copy_from_user(&kargs, args, sizeof(kargs)); + ERROR_OUT_RET(ret); + + ret = do_lseek(kargs.fd, kargs.offset, kargs.whence); + + ERROR_OUT_RET(ret); + return ret; +} + +static long sys_open(open_args_t *args) +{ + open_args_t kargs; + long ret = copy_from_user(&kargs, args, sizeof(kargs)); + ERROR_OUT_RET(ret); + + char *path; + ret = user_strdup(&kargs.filename, &path); + ERROR_OUT_RET(ret); + + ret = do_open(path, kargs.flags); + kfree(path); + + ERROR_OUT_RET(ret); + return ret; +} + +static long sys_munmap(munmap_args_t *args) +{ + munmap_args_t kargs; + long ret = copy_from_user(&kargs, args, sizeof(kargs)); + ERROR_OUT_RET(ret); + + ret = do_munmap(kargs.addr, kargs.len); + + ERROR_OUT_RET(ret); + return ret; +} + +static void *sys_mmap(mmap_args_t *arg) +{ + mmap_args_t kargs; + + if (copy_from_user(&kargs, arg, sizeof(mmap_args_t))) + { + curthr->kt_errno = EFAULT; + return MAP_FAILED; + } + + void *ret; + long err = do_mmap(kargs.mma_addr, kargs.mma_len, kargs.mma_prot, + kargs.mma_flags, kargs.mma_fd, kargs.mma_off, &ret); + if (err) + { + curthr->kt_errno = -err; + return MAP_FAILED; + } + return ret; +} + +static pid_t sys_waitpid(waitpid_args_t *args) +{ + waitpid_args_t kargs; + long ret = copy_from_user(&kargs, args, sizeof(kargs)); + ERROR_OUT_RET(ret); + + int status; + pid_t pid = do_waitpid(kargs.wpa_pid, &status, kargs.wpa_options); + ERROR_OUT_RET(pid); + + if (kargs.wpa_status) + { + ret = copy_to_user(kargs.wpa_status, &status, sizeof(int)); + ERROR_OUT_RET(ret); + } + + return pid; +} + +static void *sys_brk(void *addr) +{ + void *new_brk; + long ret = do_brk(addr, &new_brk); + if (ret) + { + curthr->kt_errno = -ret; + return (void *)-1; + } + return new_brk; +} + +static void sys_halt(void) { proc_kill_all(); } + +static long sys_stat(stat_args_t *args) +{ + stat_args_t kargs; + long ret = copy_from_user(&kargs, args, sizeof(kargs)); + ERROR_OUT_RET(ret); + + char *path; + ret = user_strdup(&kargs.path, &path); + ERROR_OUT_RET(ret); + + stat_t stat_buf; + ret = do_stat(path, &stat_buf); + kfree(path); + ERROR_OUT_RET(ret); + + ret = copy_to_user(kargs.buf, &stat_buf, sizeof(stat_buf)); + ERROR_OUT_RET(ret); + + return ret; +} + +static long sys_pipe(int args[2]) +{ + int kargs[2]; + long ret = do_pipe(kargs); + ERROR_OUT_RET(ret); + + ret = copy_to_user(args, kargs, sizeof(kargs)); + ERROR_OUT_RET(ret); + + return ret; +} + +static long sys_uname(struct utsname *arg) +{ + static const char sysname[] = "Weenix"; + static const char release[] = "1.2"; + /* Version = last compilation time */ + static const char version[] = "#1 " __DATE__ " " __TIME__; + static const char nodename[] = ""; + static const char machine[] = ""; + long ret = 0; + + ret = copy_to_user(arg->sysname, sysname, sizeof(sysname)); + ERROR_OUT_RET(ret); + ret = copy_to_user(arg->release, release, sizeof(release)); + ERROR_OUT_RET(ret); + ret = copy_to_user(arg->version, version, sizeof(version)); + ERROR_OUT_RET(ret); + ret = copy_to_user(arg->nodename, nodename, sizeof(nodename)); + ERROR_OUT_RET(ret); + ret = copy_to_user(arg->machine, machine, sizeof(machine)); + ERROR_OUT_RET(ret); + return ret; +} + +static long sys_time(time_t *utloc) +{ + time_t time = do_time(); + if (utloc) + { + long ret = copy_to_user(utloc, &time, sizeof(time_t)); + ERROR_OUT_RET(ret); + } + return time; +} + +static long sys_fork(regs_t *regs) +{ + long ret = do_fork(regs); + ERROR_OUT_RET(ret); + return ret; +} + +static void free_vector(char **vect) +{ + char **temp; + for (temp = vect; *temp; temp++) + { + kfree(*temp); + } + kfree(vect); +} + +static long sys_execve(execve_args_t *args, regs_t *regs) +{ + execve_args_t kargs; + char *filename = NULL; + char **argv = NULL; + char **envp = NULL; + + long ret; + if ((ret = copy_from_user(&kargs, args, sizeof(kargs)))) + goto cleanup; + + if ((ret = user_strdup(&kargs.filename, &filename))) + goto cleanup; + + if (kargs.argv.av_vec && (ret = user_vecdup(&kargs.argv, &argv))) + goto cleanup; + + if (kargs.envp.av_vec && (ret = user_vecdup(&kargs.envp, &envp))) + goto cleanup; + + ret = do_execve(filename, argv, envp, regs); + +cleanup: + if (filename) + kfree(filename); + if (argv) + free_vector(argv); + if (envp) + free_vector(envp); + ERROR_OUT_RET(ret); + return ret; +} + +static long sys_debug(argstr_t *args) +{ + argstr_t kargs; + long ret = copy_from_user(&kargs, args, sizeof(kargs)); + ERROR_OUT_RET(ret); + + char *str; + ret = user_strdup(&kargs, &str); + ERROR_OUT_RET(ret); + dbg(DBG_USER, "%s\n", str); + kfree(str); + return ret; +} + +static long sys_kshell(int ttyid) +{ + // ignoring the ttyid passed in as it always defaults to 0, + // instead using the active_tty value + kshell_t *ksh = kshell_create(active_tty); + ERROR_OUT(!ksh, ENODEV); + + long ret; + while ((ret = kshell_execute_next(ksh)) > 0) + ; + kshell_destroy(ksh); + + ERROR_OUT_RET(ret); + return ret; +} + +static long sys_usleep(usleep_args_t *args) +{ + return do_usleep(args->usec); +} + +static inline void check_curthr_cancelled() +{ + KASSERT(list_empty(&curthr->kt_mutexes)); + long cancelled = curthr->kt_cancelled; + void *retval = curthr->kt_retval; + + if (cancelled) + { + dbg(DBG_SYSCALL, "CANCELLING: thread 0x%p of P%d (%s)\n", curthr, + curproc->p_pid, curproc->p_name); + kthread_exit(retval); + } +} + +static long syscall_handler(regs_t *regs) +{ + size_t sysnum = (size_t)regs->r_rax; + uintptr_t args = (uintptr_t)regs->r_rdx; + + const char *syscall_string; + if (sysnum <= 47) + { + syscall_string = syscall_strings[sysnum]; + } + else + { + if (sysnum == 9001) + { + syscall_string = "debug"; + } + else if (sysnum == 9002) + { + syscall_string = "kshell"; + } + else + { + syscall_string = "unknown"; + } + } + + if (sysnum != SYS_errno) + dbg(DBG_SYSCALL, ">> pid %d, sysnum: %lu (%s), arg: %lu (0x%p)\n", + curproc->p_pid, sysnum, syscall_string, args, (void *)args); + + check_curthr_cancelled(); + long ret = syscall_dispatch(sysnum, args, regs); + check_curthr_cancelled(); + + if (sysnum != SYS_errno) + dbg(DBG_SYSCALL, "<< pid %d, sysnum: %lu (%s), returned: %lu (%#lx)\n", + curproc->p_pid, sysnum, syscall_string, ret, ret); + + regs->r_rax = (uint64_t)ret; + return 0; +} + +static long syscall_dispatch(size_t sysnum, uintptr_t args, regs_t *regs) +{ + switch (sysnum) + { + case SYS_waitpid: + return sys_waitpid((waitpid_args_t *)args); + + case SYS_exit: + do_exit((int)args); + panic("exit failed!\n"); + + case SYS_thr_exit: + kthread_exit((void *)args); + panic("thr_exit failed!\n"); + + case SYS_sched_yield: + sched_yield(); + return 0; + + case SYS_fork: + return sys_fork(regs); + + case SYS_getpid: + return curproc->p_pid; + + case SYS_sync: + do_sync(); + return 0; + +#ifdef __MOUNTING__ + case SYS_mount: + return sys_mount((mount_args_t *)args); + + case SYS_umount: + return sys_umount((argstr_t *)args); +#endif + + case SYS_mmap: + return (long)sys_mmap((mmap_args_t *)args); + + case SYS_munmap: + return sys_munmap((munmap_args_t *)args); + + case SYS_open: + return sys_open((open_args_t *)args); + + case SYS_close: + return sys_close((int)args); + + case SYS_read: + return sys_read((read_args_t *)args); + + case SYS_write: + return sys_write((write_args_t *)args); + + case SYS_dup: + return sys_dup((int)args); + + case SYS_dup2: + return sys_dup2((dup2_args_t *)args); + + case SYS_mkdir: + return sys_mkdir((mkdir_args_t *)args); + + case SYS_rmdir: + return sys_rmdir((argstr_t *)args); + + case SYS_unlink: + return sys_unlink((argstr_t *)args); + + case SYS_link: + return sys_link((link_args_t *)args); + + case SYS_rename: + return sys_rename((rename_args_t *)args); + + case SYS_chdir: + return sys_chdir((argstr_t *)args); + + case SYS_getdents: + return sys_getdents((getdents_args_t *)args); + + case SYS_brk: + return (long)sys_brk((void *)args); + + case SYS_lseek: + return sys_lseek((lseek_args_t *)args); + + case SYS_halt: + sys_halt(); + return -1; + + case SYS_set_errno: + curthr->kt_errno = (long)args; + return 0; + + case SYS_errno: + return curthr->kt_errno; + + case SYS_execve: + return sys_execve((execve_args_t *)args, regs); + + case SYS_stat: + return sys_stat((stat_args_t *)args); + + case SYS_pipe: + return sys_pipe((int *)args); + + case SYS_uname: + return sys_uname((struct utsname *)args); + + case SYS_time: + return sys_time((time_t *)args); + + case SYS_debug: + return sys_debug((argstr_t *)args); + + case SYS_kshell: + return sys_kshell((int)args); + + case SYS_usleep: + return sys_usleep((usleep_args_t *)args); + + default: + dbg(DBG_ERROR, "ERROR: unknown system call: %lu (args: 0x%p)\n", + sysnum, (void *)args); + curthr->kt_errno = ENOSYS; + return -1; + } +} |