#include "userprog/syscall.h" #include "devices/shutdown.h" #include "devices/input.h" #include "filesys/filesys.h" #include "threads/vaddr.h" #include "threads/interrupt.h" #include "threads/malloc.h" #include "threads/thread.h" #include "threads/synch.h" #include "userprog/process.h" #include "userprog/pagedir.h" #include #include static struct lock filesys_lock; static unsigned fd_counter = MIN_USER_FD; struct open_file { int fd; /* File Descriptor / Identifier */ struct file *file; /* Pointer to the associated file */ struct hash_elem elem; /* elem for a hash table */ }; static void syscall_handler (struct intr_frame *); /* A syscall_function is a function that receives up to 3 arguments, the arguments to the functions are either ints or pointers taking up to 32 bits in size. */ typedef uintptr_t (*syscall_function) (uintptr_t, uintptr_t, uintptr_t); /* System call function prototypes */ static void syscall_halt (void); static void syscall_exit (int status); static pid_t syscall_exec (const char *cmd_line); static int syscall_wait (pid_t pid); static bool syscall_create (const char *file, unsigned initial_size); static bool syscall_remove (const char *file); static int syscall_open (const char *file); static int syscall_filesize (int fd); static int syscall_read (int fd, void *buffer, unsigned size); static int syscall_write (int fd, const void *buffer, unsigned size); static void syscall_seek (int fd, unsigned position); static unsigned syscall_tell (int fd); static void syscall_close (int fd); static struct open_file *fd_get_file (int fd); static void *validate_user_pointer (const void *ptr, size_t size); /* A struct defining a syscall_function pointer along with its arity. */ typedef struct { syscall_function function; /* Function pointer. */ int arity; /* Number of arguments of the function. */ } syscall_arguments; /* A look-up table mapping numbers to system call functions with their number of arguments. */ static const syscall_arguments syscall_lookup[] = { [SYS_HALT] = {(syscall_function) syscall_halt, 0}, [SYS_EXIT] = {(syscall_function) syscall_exit, 1}, [SYS_EXEC] = {(syscall_function) syscall_exec, 1}, [SYS_WAIT] = {(syscall_function) syscall_wait, 1}, [SYS_CREATE] = {(syscall_function) syscall_create, 2}, [SYS_REMOVE] = {(syscall_function) syscall_remove, 1}, [SYS_OPEN] = {(syscall_function) syscall_open, 1}, [SYS_FILESIZE] = {(syscall_function) syscall_filesize, 1}, [SYS_READ] = {(syscall_function) syscall_read, 3}, [SYS_WRITE] = {(syscall_function) syscall_write, 3}, [SYS_SEEK] = {(syscall_function) syscall_seek, 2}, [SYS_TELL] = {(syscall_function) syscall_tell, 1}, [SYS_CLOSE] = {(syscall_function) syscall_close, 1}, }; /* The number of syscall functions (i.e, number of elements) within the syscall_lookup table. */ static const int LOOKUP_SIZE = sizeof (syscall_lookup) / sizeof (syscall_arguments); void syscall_init (void) { intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall"); lock_init (&filesys_lock); } static void syscall_handler (struct intr_frame *f) { /* First, read the system call number from the stack. */ validate_user_pointer (f->esp, 1); unsigned syscall_number = *(int *) f->esp; /* Ensures the number corresponds to a system call that can be handled. */ if (syscall_number >= LOOKUP_SIZE) thread_exit (); syscall_arguments syscall = syscall_lookup[syscall_number]; /* Next, read and copy the arguments from the stack pointer. */ validate_user_pointer (f->esp + sizeof (uintptr_t), syscall.arity * sizeof (uintptr_t)); uintptr_t args[3] = {0}; for (int i=0; i < syscall.arity; i++) args[i] = *(uintptr_t *) (f->esp + sizeof (uintptr_t) * (i + 1)); /* Call the function that handles this system call with the arguments. When there is a return value it is stored in f->eax. */ f->eax = syscall.function (args[0], args[1], args[2]); } static void syscall_halt (void) { shutdown_power_off (); } static void syscall_exit (int status) { /* Sets exit_status of the thread to status. thread_exit () will call process_exit () if user programs are allowed. */ thread_current ()->exit_status = status; thread_exit (); } static pid_t syscall_exec (const char *cmd_line) { validate_user_pointer (cmd_line, 1); lock_acquire (&filesys_lock); pid_t pid = process_execute(cmd_line); lock_release (&filesys_lock); return pid; } static int syscall_wait (pid_t pid) { return process_wait (pid); } static bool syscall_create (const char *file UNUSED, unsigned initial_size UNUSED) { validate_user_pointer (file, 1); lock_acquire (&filesys_lock); bool status = filesys_create (file, initial_size); lock_release (&filesys_lock); return status; } static bool syscall_remove (const char *file) { validate_user_pointer (file, 1); lock_acquire (&filesys_lock); bool status = filesys_remove (file); lock_release (&filesys_lock); return status; } static int syscall_open (const char *file) { validate_user_pointer (file, 1); lock_acquire (&filesys_lock); struct file *ptr = filesys_open (file); lock_release (&filesys_lock); if (ptr == NULL) return -1; struct open_file *file_info = (struct open_file*) malloc (sizeof (struct open_file)); if (file_info == NULL) return -1; file_info->fd = fd_counter++; file_info->file = ptr; hash_insert (&thread_current ()->open_files, &file_info->elem); return file_info->fd; } static int syscall_filesize (int fd UNUSED) { //TODO return 0; } static int syscall_read (int fd, void *buffer, unsigned size) { /* Only console (fd = 0) or other files, not including STDOUT, (fd > 1) are allowed. */ if (fd < 0 && fd != STDOUT_FILENO) return -1; validate_user_pointer (buffer, size); if (fd == STDIN_FILENO) { /* Reading from the console. */ char *write_buffer = buffer; for (int i = 0; i < size; i++) write_buffer[i] = input_getc (); return size; } else { /* Reading from a file. */ return 0; // TODO: Implement Write to Files } } static int syscall_write (int fd, const void *buffer, unsigned size) { /* Only console (fd = 1) or other files, not including STDIN, (fd > 1) are allowed. */ if (fd <= 0) return 0; validate_user_pointer (buffer, size); if (fd == STDOUT_FILENO) { /* Writing to the console. */ putbuf (buffer, size); return size; } else { /* Writing to a file. */ return 0; // TODO: Implement Write to Files } } static void syscall_seek (int fd UNUSED, unsigned position UNUSED) { //TODO } static unsigned syscall_tell (int fd UNUSED) { //TODO return 0; } static void syscall_close (int fd UNUSED) { //TODO } unsigned fd_hash (const struct hash_elem *element, void *aux UNUSED) { return hash_int (hash_entry (element, struct open_file, elem)->fd); } bool fd_less (const struct hash_elem *a_, const struct hash_elem *b_, void *aux UNUSED) { struct open_file *a = hash_entry (a_, struct open_file, elem); struct open_file *b = hash_entry (b_, struct open_file, elem); return a->fd < b->fd; } /* Gets a file from its descriptor (FD number). If there is no file with the fd FD it returns NULL. */ static struct open_file * fd_get_file (int fd) { /* We have to set up a fake open_file in order to be able to search the hash table. See hash.h. */ struct open_file *fake_file_info; fake_file_info->fd = fd; struct hash_elem *e = hash_find (&thread_current ()->open_files, &fake_file_info->elem); return hash_entry (e, struct open_file, elem); } /* Validates if a block of memory starting at PTR and of size SIZE bytes is fully contained within user virtual memory. Kills the thread (by calling thread_exit) if the memory is invalid. Otherwise, returns the PTR given. If the size is 0, the function does no checks and returns PTR.*/ static void * validate_user_pointer (const void *ptr, size_t size) { if (size > 0 && (ptr == NULL || !is_user_vaddr (ptr) || !is_user_vaddr (ptr + size - 1) || pagedir_get_page (thread_current()->pagedir, ptr) == NULL)) thread_exit (); return (void *) ptr; }