diff --git a/src/threads/synch.c b/src/threads/synch.c index 09fc71f..d706d19 100644 --- a/src/threads/synch.c +++ b/src/threads/synch.c @@ -113,28 +113,33 @@ void sema_up (struct semaphore *sema) { enum intr_level old_level; + bool thread_unblocked = false; /* Flag to track if any thread was woken up. */ ASSERT (sema != NULL); old_level = intr_disable (); if (!list_empty (&sema->waiters)) - { - /* Enforces wake-up of the highest priority thread waiting for the - semaphore. */ - struct list_elem *e = list_max (&sema->waiters, priority_less, NULL); - list_remove (e); - thread_unblock (list_entry (e, struct thread, elem)); - } + { + /* Enforces wake-up of the highest priority thread waiting for the + semaphore. */ + struct list_elem *e = list_max (&sema->waiters, priority_less, NULL); + list_remove (e); + thread_unblock (list_entry (e, struct thread, elem)); + thread_unblocked = true; + } sema->value++; intr_set_level (old_level); /* Yields the CPU in case the thread that has been woken up has a higher priority that the current running thread, including the case when called within an interrupt handler. */ - if (intr_context ()) - intr_yield_on_return (); - else - thread_yield (); + if (thread_unblocked) + { + if (intr_context ()) + intr_yield_on_return (); + else + thread_yield (); + } } static void sema_test_helper (void *sema_); @@ -347,7 +352,6 @@ lock_release (struct lock *lock) lock->holder = NULL; sema_up (&lock->semaphore); - thread_yield (); } /* Returns true if the current thread holds LOCK, false diff --git a/src/threads/thread.c b/src/threads/thread.c index 7102ad2..457f7b9 100644 --- a/src/threads/thread.c +++ b/src/threads/thread.c @@ -659,6 +659,8 @@ init_thread (struct thread *t, const char *name, int nice, int priority, t->recent_cpu = recent_cpu; t->priority = t->base_priority; + t->exit_status = -1; + old_level = intr_disable (); list_push_back (&all_list, &t->allelem); intr_set_level (old_level); diff --git a/src/threads/thread.h b/src/threads/thread.h index c7cc364..1c05030 100644 --- a/src/threads/thread.h +++ b/src/threads/thread.h @@ -111,6 +111,7 @@ struct thread /* Shared between thread.c and synch.c. */ struct list_elem elem; /* List element. */ + int exit_status; /* Exit Status: 0 = successful exit. */ #ifdef USERPROG /* Owned by userprog/process.c. */ diff --git a/src/userprog/process.c b/src/userprog/process.c index 7f38374..20e2677 100644 --- a/src/userprog/process.c +++ b/src/userprog/process.c @@ -14,6 +14,7 @@ #include "threads/flags.h" #include "threads/init.h" #include "threads/interrupt.h" +#include "threads/synch.h" #include "threads/palloc.h" #include "threads/malloc.h" #include "threads/thread.h" @@ -201,12 +202,12 @@ start_process (void *file_name_) int process_wait (tid_t child_tid UNUSED) { - /* TODO: Implement correct process waiting behaviour. - Currently an infinite loop for testing purposes. */ - while (1) - { - barrier (); - } + /* As a temporary wait, waiting will just put the thread to sleep for one + second (TIMER_FREQ = 100 ticks ~ 1 second). */ + /* TODO: Implement process_wait () correctly. Remove the next line. */ + timer_sleep (TIMER_FREQ); + + return 0; /* TODO: Change this too */ } /* Free the current process's resources. */ diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c index 370c89b..1dbe73b 100644 --- a/src/userprog/syscall.c +++ b/src/userprog/syscall.c @@ -1,11 +1,68 @@ #include "userprog/syscall.h" -#include -#include +#include "devices/shutdown.h" +#include "devices/input.h" +#include "threads/vaddr.h" #include "threads/interrupt.h" #include "threads/thread.h" +#include "userprog/process.h" +#include +#include 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 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) { @@ -13,8 +70,165 @@ syscall_init (void) } static void -syscall_handler (struct intr_frame *f UNUSED) +syscall_handler (struct intr_frame *f) { - printf ("system call!\n"); + /* 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 UNUSED) +{ + //TODO + return 0; +} + +static int +syscall_wait (pid_t pid) +{ + return process_wait (pid); +} + +static bool +syscall_create (const char *file UNUSED, unsigned initial_size UNUSED) +{ + //TODO + return 0; +} + +static bool +syscall_remove (const char *file UNUSED) +{ + //TODO + return 0; +} + +static int +syscall_open (const char *file UNUSED) +{ + //TODO + return 0; +} + +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 +} + +/* 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))) + thread_exit (); + + return ptr; +} diff --git a/src/userprog/syscall.h b/src/userprog/syscall.h index 9059096..702a6c7 100644 --- a/src/userprog/syscall.h +++ b/src/userprog/syscall.h @@ -1,6 +1,8 @@ #ifndef USERPROG_SYSCALL_H #define USERPROG_SYSCALL_H +typedef int pid_t; + void syscall_init (void); #endif /* userprog/syscall.h */