Merge branch 'system-calls' into 'master'
Add support for some basic system calls and args handling correctly. See merge request lab2425_autumn/pintos_22!29
This commit is contained in:
@@ -659,6 +659,8 @@ init_thread (struct thread *t, const char *name, int nice, int priority,
|
|||||||
t->recent_cpu = recent_cpu;
|
t->recent_cpu = recent_cpu;
|
||||||
t->priority = t->base_priority;
|
t->priority = t->base_priority;
|
||||||
|
|
||||||
|
t->exit_status = -1;
|
||||||
|
|
||||||
old_level = intr_disable ();
|
old_level = intr_disable ();
|
||||||
list_push_back (&all_list, &t->allelem);
|
list_push_back (&all_list, &t->allelem);
|
||||||
intr_set_level (old_level);
|
intr_set_level (old_level);
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ struct thread
|
|||||||
/* Shared between thread.c and synch.c. */
|
/* Shared between thread.c and synch.c. */
|
||||||
struct list_elem elem; /* List element. */
|
struct list_elem elem; /* List element. */
|
||||||
|
|
||||||
|
int exit_status; /* Exit Status: 0 = successful exit. */
|
||||||
|
|
||||||
#ifdef USERPROG
|
#ifdef USERPROG
|
||||||
/* Owned by userprog/process.c. */
|
/* Owned by userprog/process.c. */
|
||||||
|
|||||||
@@ -14,9 +14,21 @@
|
|||||||
#include "threads/flags.h"
|
#include "threads/flags.h"
|
||||||
#include "threads/init.h"
|
#include "threads/init.h"
|
||||||
#include "threads/interrupt.h"
|
#include "threads/interrupt.h"
|
||||||
|
#include "threads/synch.h"
|
||||||
#include "threads/palloc.h"
|
#include "threads/palloc.h"
|
||||||
|
#include "threads/malloc.h"
|
||||||
#include "threads/thread.h"
|
#include "threads/thread.h"
|
||||||
#include "threads/vaddr.h"
|
#include "threads/vaddr.h"
|
||||||
|
#include "threads/synch.h"
|
||||||
|
#include "devices/timer.h"
|
||||||
|
|
||||||
|
/* Keeps track of the position of pointers to user program arguments
|
||||||
|
within a linked list. */
|
||||||
|
struct arg_elem
|
||||||
|
{
|
||||||
|
char* arg;
|
||||||
|
struct list_elem elem;
|
||||||
|
};
|
||||||
|
|
||||||
static thread_func start_process NO_RETURN;
|
static thread_func start_process NO_RETURN;
|
||||||
static bool load (const char *cmdline, void (**eip) (void), void **esp);
|
static bool load (const char *cmdline, void (**eip) (void), void **esp);
|
||||||
@@ -36,6 +48,9 @@ process_execute (const char *file_name)
|
|||||||
fn_copy = palloc_get_page (0);
|
fn_copy = palloc_get_page (0);
|
||||||
if (fn_copy == NULL)
|
if (fn_copy == NULL)
|
||||||
return TID_ERROR;
|
return TID_ERROR;
|
||||||
|
|
||||||
|
/* Imposing implicit limit that the command line arguments
|
||||||
|
including the user program name fit within a single page. */
|
||||||
strlcpy (fn_copy, file_name, PGSIZE);
|
strlcpy (fn_copy, file_name, PGSIZE);
|
||||||
|
|
||||||
/* Create a new thread to execute FILE_NAME. */
|
/* Create a new thread to execute FILE_NAME. */
|
||||||
@@ -45,15 +60,34 @@ process_execute (const char *file_name)
|
|||||||
return tid;
|
return tid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
push_to_stack (void **esp, void *data, size_t data_size)
|
||||||
|
{
|
||||||
|
*esp -= data_size;
|
||||||
|
memcpy (*esp, data, data_size);
|
||||||
|
return *esp;
|
||||||
|
}
|
||||||
|
|
||||||
/* A thread function that loads a user process and starts it
|
/* A thread function that loads a user process and starts it
|
||||||
running. */
|
running. */
|
||||||
static void
|
static void
|
||||||
start_process (void *file_name_)
|
start_process (void *file_name_)
|
||||||
{
|
{
|
||||||
char *file_name = file_name_;
|
|
||||||
struct intr_frame if_;
|
struct intr_frame if_;
|
||||||
bool success;
|
bool success;
|
||||||
|
|
||||||
|
/* Retrieve first argument of command, which is the file name
|
||||||
|
of the process. */
|
||||||
|
char *saveptr;
|
||||||
|
char *arg = strtok_r (file_name_, " ", &saveptr);
|
||||||
|
|
||||||
|
char file_name[15];
|
||||||
|
strlcpy (file_name, arg, 15);
|
||||||
|
|
||||||
|
/* TODO: Move naming of thread to process_execute, so start
|
||||||
|
tokenizing there. */
|
||||||
|
strlcpy (thread_current ()->name, file_name, 15);
|
||||||
|
|
||||||
/* Initialize interrupt frame and load executable. */
|
/* Initialize interrupt frame and load executable. */
|
||||||
memset (&if_, 0, sizeof if_);
|
memset (&if_, 0, sizeof if_);
|
||||||
if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
|
if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
|
||||||
@@ -61,8 +95,88 @@ start_process (void *file_name_)
|
|||||||
if_.eflags = FLAG_IF | FLAG_MBS;
|
if_.eflags = FLAG_IF | FLAG_MBS;
|
||||||
success = load (file_name, &if_.eip, &if_.esp);
|
success = load (file_name, &if_.eip, &if_.esp);
|
||||||
|
|
||||||
|
/* Load command line argument *data* to user process stack.
|
||||||
|
This can't cause overflow due to enforcing that the size of
|
||||||
|
command line input must fit in a page. Also keep track
|
||||||
|
of pointers to the argument data within a linked list. */
|
||||||
|
struct list arg_list;
|
||||||
|
list_init (&arg_list);
|
||||||
|
|
||||||
|
int arg_count = 0;
|
||||||
|
while (arg != NULL)
|
||||||
|
{
|
||||||
|
push_to_stack (&if_.esp, arg, (strlen (arg) + 1) * sizeof (char));
|
||||||
|
|
||||||
|
struct arg_elem *arg_elem = malloc (sizeof (struct arg_elem));
|
||||||
|
if (arg_elem == NULL)
|
||||||
|
{
|
||||||
|
printf("ERROR: Couldn't allocate argument pointer memory for %s!\n",
|
||||||
|
file_name);
|
||||||
|
palloc_free_page (file_name_);
|
||||||
|
if (success) process_exit ();
|
||||||
|
thread_exit ();
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_elem->arg = if_.esp;
|
||||||
|
list_push_front (&arg_list, &arg_elem->elem);
|
||||||
|
|
||||||
|
arg_count++;
|
||||||
|
arg = strtok_r (NULL, " ", &saveptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the remaining number of bytes that need to be written
|
||||||
|
to the user process stack in order to check for possible overflow. */
|
||||||
|
size_t align_size = ((unsigned int) if_.esp % 4) * sizeof (uint8_t);
|
||||||
|
size_t argv_data_size = (arg_count + 1) * sizeof (char *);
|
||||||
|
size_t argv_size = sizeof (char **);
|
||||||
|
size_t argc_size = sizeof (int);
|
||||||
|
size_t return_addr_size = sizeof (void *);
|
||||||
|
size_t remaining_size = align_size + argv_data_size + argv_size + argc_size
|
||||||
|
+ return_addr_size;
|
||||||
|
|
||||||
|
/* If pushing the rest of the data required for the stack would cause
|
||||||
|
overflow, allocate an extra page. */
|
||||||
|
if (PHYS_BASE - if_.esp + remaining_size > PGSIZE)
|
||||||
|
{
|
||||||
|
/* TODO: Allocate an extra page for the rest of the process stack. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Align stack pointer to word size before pushing argv elements for
|
||||||
|
performance. */
|
||||||
|
if_.esp -= align_size;
|
||||||
|
|
||||||
|
/* Push a null pointer sentinel inside argv. */
|
||||||
|
if_.esp -= sizeof (char *);
|
||||||
|
*(char *) if_.esp = 0;
|
||||||
|
|
||||||
|
/* Push pointer to the process file name to the stack. */
|
||||||
|
char **argv;
|
||||||
|
|
||||||
|
/* Push pointers to process arguments from argument linked list */
|
||||||
|
struct list_elem *e = list_begin (&arg_list);
|
||||||
|
struct list_elem *tail = list_tail (&arg_list);
|
||||||
|
while (e != tail)
|
||||||
|
{
|
||||||
|
struct arg_elem *arg_elem = list_entry (e, struct arg_elem, elem);
|
||||||
|
|
||||||
|
argv = push_to_stack (&if_.esp, &arg_elem->arg, sizeof (arg_elem->arg));
|
||||||
|
|
||||||
|
e = list_next (e);
|
||||||
|
free (arg_elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Push pointer to the start of argv array. */
|
||||||
|
push_to_stack (&if_.esp, &argv, sizeof(argv));
|
||||||
|
|
||||||
|
/* Push the number of arguments to the stack. */
|
||||||
|
push_to_stack (&if_.esp, &arg_count, sizeof (arg_count));
|
||||||
|
|
||||||
|
/* Push fake return address (null pointer). */
|
||||||
|
if_.esp -= sizeof (char *);
|
||||||
|
*(char *) if_.esp = 0;
|
||||||
|
|
||||||
/* If load failed, quit. */
|
/* If load failed, quit. */
|
||||||
palloc_free_page (file_name);
|
palloc_free_page (file_name_);
|
||||||
if (!success)
|
if (!success)
|
||||||
thread_exit ();
|
thread_exit ();
|
||||||
|
|
||||||
@@ -88,7 +202,12 @@ start_process (void *file_name_)
|
|||||||
int
|
int
|
||||||
process_wait (tid_t child_tid UNUSED)
|
process_wait (tid_t child_tid UNUSED)
|
||||||
{
|
{
|
||||||
return -1;
|
/* 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. */
|
/* Free the current process's resources. */
|
||||||
@@ -98,6 +217,8 @@ process_exit (void)
|
|||||||
struct thread *cur = thread_current ();
|
struct thread *cur = thread_current ();
|
||||||
uint32_t *pd;
|
uint32_t *pd;
|
||||||
|
|
||||||
|
printf ("%s: exit(%d)\n", cur->name, cur->exit_status);
|
||||||
|
|
||||||
/* Destroy the current process's page directory and switch back
|
/* Destroy the current process's page directory and switch back
|
||||||
to the kernel-only page directory. */
|
to the kernel-only page directory. */
|
||||||
pd = cur->pagedir;
|
pd = cur->pagedir;
|
||||||
|
|||||||
@@ -1,11 +1,68 @@
|
|||||||
#include "userprog/syscall.h"
|
#include "userprog/syscall.h"
|
||||||
#include <stdio.h>
|
#include "devices/shutdown.h"
|
||||||
#include <syscall-nr.h>
|
#include "devices/input.h"
|
||||||
|
#include "threads/vaddr.h"
|
||||||
#include "threads/interrupt.h"
|
#include "threads/interrupt.h"
|
||||||
#include "threads/thread.h"
|
#include "threads/thread.h"
|
||||||
|
#include "userprog/process.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <syscall-nr.h>
|
||||||
|
|
||||||
static void syscall_handler (struct intr_frame *);
|
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
|
void
|
||||||
syscall_init (void)
|
syscall_init (void)
|
||||||
{
|
{
|
||||||
@@ -13,8 +70,165 @@ syscall_init (void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static 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 ();
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#ifndef USERPROG_SYSCALL_H
|
#ifndef USERPROG_SYSCALL_H
|
||||||
#define USERPROG_SYSCALL_H
|
#define USERPROG_SYSCALL_H
|
||||||
|
|
||||||
|
typedef int pid_t;
|
||||||
|
|
||||||
void syscall_init (void);
|
void syscall_init (void);
|
||||||
|
|
||||||
#endif /* userprog/syscall.h */
|
#endif /* userprog/syscall.h */
|
||||||
|
|||||||
Reference in New Issue
Block a user