299 lines
7.7 KiB
C
299 lines
7.7 KiB
C
#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 <stdio.h>
|
|
#include <syscall-nr.h>
|
|
|
|
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 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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|