diff --git a/kernel/syscall_handler.c b/kernel/syscall_handler.c new file mode 100644 index 0000000..f1bda5e --- /dev/null +++ b/kernel/syscall_handler.c @@ -0,0 +1,639 @@ +/* +Copyright (C) 2015-2019 The University of Notre Dame +This software is distributed under the GNU General Public License. +See the file LICENSE for details. +*/ + +#include "kernel/syscall.h" +#include "kernel/gfxstream.h" +#include "syscall_handler.h" +#include "console.h" +#include "keyboard.h" +#include "process.h" +#include "kmalloc.h" +#include "kobject.h" +#include "cdromfs.h" +#include "string.h" +#include "memorylayout.h" +#include "main.h" +#include "fs.h" +#include "kobject.h" +#include "pagetable.h" +#include "clock.h" +#include "rtc.h" +#include "elf.h" +#include "kmalloc.h" +#include "page.h" +#include "ata.h" +#include "window.h" +#include "is_valid.h" +#include "bcache.h" + +/* +syscall_handler() is responsible for decoding system calls +as they arrive, converting raw integers into the appropriate +types, depending on the system call number. Then, each +individual handler routine checks the validity of each +argument (fd in range, valid path, etc) and invokes the +underlying system within the kernel. Ideally, each of these +handler functions should be short (only a few lines) +and simply make use of functionality within other kernel modules. + +sys_run/fork/exec are notable exceptions and could benefit +from simplification. +*/ + +/* +Here follow the handlers for each individual system call +For all of these system calls, a return value of zero or +greater indiciates success, and return of less than zero +indicates an error and the reason. +*/ + +int sys_debug(const char *str) +{ + if(!is_valid_string(str)) return KERROR_INVALID_ADDRESS; + printf("%s", str); + return 0; +} + +int sys_process_yield() +{ + process_yield(); + return 0; +} + +int sys_process_exit(int status) +{ + process_exit(status); + return 0; +} + +/* Helper routines to duplicate/free an argv array locally */ + +static char **argv_copy(int argc, const char **argv) +{ + char **pp; + + pp = kmalloc(sizeof(char *) * argc); + int i; + for(i = 0; i < argc; i++) { + pp[i] = strdup(argv[i]); + } + + return pp; +} + +static void argv_delete(int argc, char **argv) +{ + int i; + for(i = 0; i < argc; i++) { + kfree(argv[i]); + } + kfree(argv); +} + +/* +process_run() creates a child process in a more efficient +way than fork/exec by creating the child without duplicating +the memory state, then loading +*/ + +int sys_process_run( int fd, int argc, const char **argv) +{ + if(!is_valid_object_type(fd,KOBJECT_FILE)) return KERROR_INVALID_OBJECT; + + struct kobject *k = current->ktable[fd]; + + /* Copy argv into kernel memory. */ + char **copy_argv = argv_copy(argc, argv); + + /* Create the child process */ + struct process *p = process_create(); + process_inherit(current, p); + + /* SWITCH TO ADDRESS SPACE OF CHILD PROCESS */ + struct pagetable *old_pagetable = current->pagetable; + current->pagetable = p->pagetable; + pagetable_load(p->pagetable); + + /* Attempt to load the program image. */ + addr_t entry; + int r = elf_load(p, k->data.file, &entry); + if(r >= 0) { + /* If load succeeded, reset stack and pass arguments */ + process_stack_reset(p, PAGE_SIZE); + process_kstack_reset(p, entry); + process_pass_arguments(p, argc, copy_argv); + } + + /* SWITCH BACK TO ADDRESS SPACE OF PARENT PROCESS */ + current->pagetable = old_pagetable; + pagetable_load(old_pagetable); + + /* Delete the argument and path copies. */ + argv_delete(argc, copy_argv); + + /* If any error happened, return in the context of the parent */ + if(r < 0) { + if(r == KERROR_EXECUTION_FAILED) { + process_delete(p); + } + return r; + } + + /* Otherwise, launch the new child process. */ + process_launch(p); + return p->pid; +} + +/* Function creates a child process with the standard window replaced by wd */ +int sys_process_wrun( int fd, int argc, const char **argv, int *fds, int fd_len) +{ + if(!is_valid_object_type(fd,KOBJECT_FILE)) return KERROR_INVALID_OBJECT; + struct kobject *k = current->ktable[fd]; + + /* Copy argv array into kernel memory. */ + char **copy_argv = argv_copy(argc, argv); + + /* Create the child process */ + struct process *p = process_create(); + + process_selective_inherit(current, p, fds, fd_len); + + /* SWITCH TO ADDRESS SPACE OF CHILD PROCESS */ + struct pagetable *old_pagetable = current->pagetable; + current->pagetable = p->pagetable; + pagetable_load(p->pagetable); + + /* Attempt to load the program image. */ + addr_t entry; + int r = elf_load(p, k->data.file, &entry); + if(r >= 0) { + /* If load succeeded, reset stack and pass arguments */ + process_stack_reset(p, PAGE_SIZE); + process_kstack_reset(p, entry); + process_pass_arguments(p, argc, copy_argv); + } + + /* SWITCH BACK TO ADDRESS SPACE OF PARENT PROCESS */ + current->pagetable = old_pagetable; + pagetable_load(old_pagetable); + + /* Delete the argument copy. */ + argv_delete(argc, copy_argv); + + /* If any error happened, return in the context of the parent */ + if(r < 0) { + if(r == KERROR_EXECUTION_FAILED) { + process_delete(p); + } + return r; + } + + /* Otherwise, launch the new child process. */ + process_launch(p); + return p->pid; +} + +int sys_process_exec( int fd, int argc, const char **argv) +{ + if(!is_valid_object_type(fd,KOBJECT_FILE)) return KERROR_INVALID_OBJECT; + struct kobject *k = current->ktable[fd]; + + addr_t entry; + + /* Duplicate the arguments into kernel space */ + char **copy_argv = argv_copy(argc, argv); + + /* Attempt to load the program image into this process. */ + int r = elf_load(current, k->data.file, &entry); + + /* On failure, return only if our address space is not corrupted. */ + if(r < 0) { + if(r == KERROR_EXECUTION_FAILED) { + process_kill(current->pid); + } + argv_delete(argc, copy_argv); + return r; + } + + /* Reset the stack and pass in the program arguments */ + process_stack_reset(current, PAGE_SIZE); + process_kstack_reset(current, entry); + process_pass_arguments(current, argc, copy_argv); + + /* Delete the local copy of the arguments. */ + argv_delete(argc, copy_argv); + + /* + IMPORTANT: Following a successful exec, we cannot return via + the normal path, because our stack has been reset to that + of a fresh process. We must switch in order to jump + to the new stack properly. + */ + + process_yield(); + + /* NOTREACHED */ + return 0; +} + +int sys_process_fork() +{ + struct process *p = process_create(); + p->ppid = current->pid; + pagetable_delete(p->pagetable); + p->pagetable = pagetable_duplicate(current->pagetable); + process_inherit(current, p); + process_kstack_copy(current, p); + process_launch(p); + return p->pid; +} + +int sys_process_self() +{ + return current->pid; +} + +int sys_process_parent() +{ + return current->ppid; +} + +int sys_process_kill(int pid) +{ + return process_kill(pid); +} + +int sys_process_reap(int pid) +{ + return process_reap(pid); +} + +int sys_process_wait(struct process_info *info, int timeout) +{ + if(!is_valid_pointer(info,sizeof(*info))) return KERROR_INVALID_ADDRESS; + return process_wait_child(0, info, timeout); +} + +int sys_process_sleep(unsigned int ms) +{ + clock_wait(ms); + return 0; +} + +int sys_process_stats(struct process_stats *s, int pid) +{ + if(!is_valid_pointer(s,sizeof(*s))) return KERROR_INVALID_ADDRESS; + return process_stats(pid, s); +} + +int sys_process_heap(int delta) +{ + process_data_size_set(current, current->vm_data_size + delta); + return PROCESS_ENTRY_POINT + current->vm_data_size; +} + +int sys_object_list( int fd, char *buffer, int length) +{ + if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT; + if(!is_valid_pointer(buffer,length)) return KERROR_INVALID_ADDRESS; + if(kobject_get_type(current->ktable[fd])!=KOBJECT_DIR) return KERROR_NOT_A_DIRECTORY; + return kobject_list(current->ktable[fd],buffer,length); +} + +int sys_open_file( int fd, const char *path, int mode, kernel_flags_t flags) +{ + if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT; + if(!is_valid_path(path)) return KERROR_INVALID_PATH; + + int newfd = process_available_fd(current); + if(newfd<0) return KERROR_OUT_OF_OBJECTS; + + struct kobject *newobj; + int result = kobject_lookup(current->ktable[fd],path,&newobj); + + if(result>=0) { + current->ktable[newfd] = newobj; + return newfd; + } else { + return result; + } +} + +int sys_open_dir( int fd, const char *path, kernel_flags_t flags ) +{ + if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT; + if(!is_valid_path(path)) return KERROR_INVALID_PATH; + + int newfd = process_available_fd(current); + if(newfd<0) return KERROR_OUT_OF_OBJECTS; + + struct kobject *newobj; + int result; + + if(flags&KERNEL_FLAGS_CREATE) { + newobj = kobject_create_dir_from_dir( current->ktable[fd], path ); + if(newobj) { + result = 0; + } else { + result = KERROR_NOT_FOUND; + } + } else { + result = kobject_lookup( current->ktable[fd], path, &newobj ); + } + + if(result>=0) { + current->ktable[newfd] = newobj; + return newfd; + } else { + return result; + } +} + +int sys_open_console(int wd) +{ + if(!is_valid_object_type(wd,KOBJECT_WINDOW)) return KERROR_INVALID_OBJECT; + + int fd = process_available_fd(current); + if(fd<0) return KERROR_OUT_OF_OBJECTS; + + current->ktable[fd] = kobject_create_console_from_window(current->ktable[wd]); + return fd; +} + + +int sys_open_window(int wd, int x, int y, int w, int h) +{ + if(!is_valid_object_type(wd,KOBJECT_WINDOW)) return KERROR_INVALID_OBJECT; + + struct kobject *k = current->ktable[wd]; + + int fd = process_available_fd(current); + if(fd<0) return KERROR_OUT_OF_OBJECTS; + + k = kobject_create_window_from_window(k,x,y,w,h); + if(!k) { + // XXX choose better errno + return KERROR_INVALID_REQUEST; + } + + current->ktable[fd] = k; + + return fd; +} + +int sys_open_pipe() +{ + int fd = process_available_fd(current); + if(fd < 0) { + return KERROR_NOT_FOUND; + } + struct pipe *p = pipe_create(); + if(!p) { + return KERROR_NOT_FOUND; + } + current->ktable[fd] = kobject_create_pipe(p); + return fd; +} + +int sys_object_type(int fd) +{ + if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT; + + int fd_type = kobject_get_type(current->ktable[fd]); + if(!fd_type) + return 0; + return fd_type; +} + +int sys_object_copy( int src, int dst ) +{ + if(!is_valid_object(src)) return KERROR_INVALID_OBJECT; + if(dst>PROCESS_MAX_OBJECTS) return KERROR_INVALID_OBJECT; + + if(dst < 0) { + dst = process_available_fd(current); + if(dst<0) return KERROR_NOT_FOUND; + } + + sys_object_close(dst); + + current->ktable[dst] = kobject_copy(current->ktable[src]); + + return src; +} + +int sys_object_read(int fd, void *data, int length, kernel_io_flags_t flags ) +{ + if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT; + if(!is_valid_pointer(data,length)) return KERROR_INVALID_ADDRESS; + + struct kobject *p = current->ktable[fd]; + return kobject_read(p, data, length, flags); +} + +int sys_object_write(int fd, void *data, int length, kernel_io_flags_t flags ) +{ + if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT; + if(!is_valid_pointer(data,length)) return KERROR_INVALID_ADDRESS; + + struct kobject *p = current->ktable[fd]; + return kobject_write(p, data, length, flags); +} + +int sys_object_seek(int fd, int offset, int whence) +{ + if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT; + + // XXX add kobject method here + return KERROR_NOT_IMPLEMENTED; +} + +int sys_object_remove( int fd, const char *name ) +{ + if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT; + if(!is_valid_path(name)) return KERROR_INVALID_PATH; + return kobject_remove( current->ktable[fd], name ); +} + +int sys_object_close(int fd) +{ + if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT; + + struct kobject *p = current->ktable[fd]; + kobject_close(p); + current->ktable[fd] = 0; + return 0; +} + +int sys_object_set_tag(int fd, char *tag) +{ + if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT; + kobject_set_tag(current->ktable[fd], tag); + return 0; +} + +int sys_object_get_tag(int fd, char *buffer, int buffer_size) +{ + if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT; + return kobject_get_tag(current->ktable[fd], buffer, buffer_size); +} + +int sys_object_size(int fd, int *dims, int n) +{ + if(!is_valid_object(fd)) return KERROR_INVALID_OBJECT; + if(!is_valid_pointer(dims,sizeof(*dims)*n)) return KERROR_INVALID_ADDRESS; + + struct kobject *p = current->ktable[fd]; + return kobject_size(p, dims, n); +} + +int sys_object_max() +{ + int max_fd = process_object_max(current); + return max_fd; +} + +int sys_system_stats(struct system_stats *s) +{ + if(!is_valid_pointer(s,sizeof(*s))) return KERROR_INVALID_ADDRESS; + + struct rtc_time t = { 0 }; + rtc_read(&t); + s->time = rtc_time_to_timestamp(&t) - boottime; + + struct ata_count a = ata_stats(); + for(int i = 0; i < 4; i++) { + s->blocks_written[i] = a.blocks_written[i]; + s->blocks_read[i] = a.blocks_read[i]; + } + + return 0; +} + +int sys_bcache_stats(struct bcache_stats * s) +{ + if(!is_valid_pointer(s,sizeof(*s))) return KERROR_INVALID_ADDRESS; + bcache_get_stats( s ); + return 0; +} + +int sys_bcache_flush() +{ + bcache_flush_all(); + return 0; +} + +int sys_system_time( uint32_t *tm ) +{ + if(!is_valid_pointer(tm,sizeof(*tm))) return KERROR_INVALID_ADDRESS; + struct rtc_time t; + rtc_read(&t); + *tm = rtc_time_to_timestamp(&t); + return 0; +} + +int sys_system_rtc( struct rtc_time *t ) +{ + if(!is_valid_pointer(t,sizeof(*t))) return KERROR_INVALID_ADDRESS; + rtc_read(t); + return 0; +} + +int sys_device_driver_stats(const char * name, struct device_driver_stats * stats) +{ + if(!is_valid_string(name)) return KERROR_INVALID_ADDRESS; + if(!is_valid_pointer(stats,sizeof(*stats))) return KERROR_INVALID_ADDRESS; + + device_driver_get_stats(name, stats); + return 0; +} + +int32_t syscall_handler(syscall_t n, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e) +{ + if((n < MAX_SYSCALL) && current) { + current->stats.syscall_count[n]++; + } + switch (n) { + case SYSCALL_DEBUG: + return sys_debug((const char *) a); + case SYSCALL_PROCESS_YIELD: + return sys_process_yield(); + case SYSCALL_PROCESS_EXIT: + return sys_process_exit(a); + case SYSCALL_PROCESS_RUN: + return sys_process_run(a, b, (const char **)c); + case SYSCALL_PROCESS_WRUN: + return sys_process_wrun(a, b, (const char **) c, (int *) d, e); + case SYSCALL_PROCESS_FORK: + return sys_process_fork(); + case SYSCALL_PROCESS_EXEC: + return sys_process_exec(a, b, (const char **) c); + case SYSCALL_PROCESS_SELF: + return sys_process_self(); + case SYSCALL_PROCESS_PARENT: + return sys_process_parent(); + case SYSCALL_PROCESS_KILL: + return sys_process_kill(a); + case SYSCALL_PROCESS_REAP: + return sys_process_reap(a); + case SYSCALL_PROCESS_WAIT: + return sys_process_wait((struct process_info *) a, b); + case SYSCALL_PROCESS_SLEEP: + return sys_process_sleep(a); + case SYSCALL_PROCESS_STATS: + return sys_process_stats((struct process_stats *) a, b); + case SYSCALL_PROCESS_HEAP: + return sys_process_heap(a); + case SYSCALL_OPEN_FILE: + return sys_open_file(a, (const char *)b, c, d); + case SYSCALL_OPEN_DIR: + return sys_open_dir(a, (const char *)b, c ); + case SYSCALL_OPEN_WINDOW: + return sys_open_window(a, b, c, d, e); + case SYSCALL_OPEN_CONSOLE: + return sys_open_console(a); + case SYSCALL_OPEN_PIPE: + return sys_open_pipe(); + case SYSCALL_OBJECT_TYPE: + return sys_object_type(a); + case SYSCALL_OBJECT_COPY: + return sys_object_copy(a,b); + case SYSCALL_OBJECT_READ: + return sys_object_read(a, (void *) b, c, d ); + case SYSCALL_OBJECT_LIST: + return sys_object_list(a, (char *) b, (int) c); + case SYSCALL_OBJECT_WRITE: + return sys_object_write(a, (void *) b, c, d); + case SYSCALL_OBJECT_SEEK: + return sys_object_seek(a, b, c); + case SYSCALL_OBJECT_REMOVE: + return sys_object_remove(a,(const char*)b); + case SYSCALL_OBJECT_CLOSE: + return sys_object_close(a); + case SYSCALL_OBJECT_SET_TAG: + return sys_object_set_tag(a, (char *) b); + case SYSCALL_OBJECT_GET_TAG: + return sys_object_get_tag(a, (char *) b, c); + case SYSCALL_OBJECT_SIZE: + return sys_object_size(a, (int *) b, c); + case SYSCALL_OBJECT_MAX: + return sys_object_max(a); + case SYSCALL_SYSTEM_STATS: + return sys_system_stats((struct system_stats *) a); + case SYSCALL_BCACHE_STATS: + return sys_bcache_stats((struct bcache_stats *) a); + case SYSCALL_BCACHE_FLUSH: + return sys_bcache_flush(); + case SYSCALL_SYSTEM_TIME: + return sys_system_time((uint32_t*)a); + case SYSCALL_SYSTEM_RTC: + return sys_system_rtc((struct rtc_time *) a); + case SYSCALL_DEVICE_DRIVER_STATS: + return sys_device_driver_stats((char *) a, (struct device_driver_stats *) b); + default: + return KERROR_INVALID_SYSCALL; + } +}