summaryrefslogtreecommitdiffstats
path: root/src/userprog
diff options
context:
space:
mode:
Diffstat (limited to 'src/userprog')
-rw-r--r--src/userprog/Make.vars2
-rw-r--r--src/userprog/process.c177
-rwxr-xr-x[-rw-r--r--]src/userprog/start_pfs.sh4
-rwxr-xr-x[-rw-r--r--]src/userprog/start_recursor.sh10
-rw-r--r--src/userprog/syscall.c334
5 files changed, 501 insertions, 26 deletions
diff --git a/src/userprog/Make.vars b/src/userprog/Make.vars
index eebac65..22aec79 100644
--- a/src/userprog/Make.vars
+++ b/src/userprog/Make.vars
@@ -2,6 +2,6 @@
os.dsk: DEFINES = -DUSERPROG -DFILESYS
KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys
-TEST_SUBDIRS = tests/userprog #tests/userprog/no-vm tests/filesys/base
+TEST_SUBDIRS = tests/userprog tests/filesys/base
GRADING_FILE = $(SRCDIR)/tests/userprog/Grading
SIMULATOR = --qemu
diff --git a/src/userprog/process.c b/src/userprog/process.c
index b3e16bb..6170b53 100644
--- a/src/userprog/process.c
+++ b/src/userprog/process.c
@@ -14,10 +14,24 @@
#include "threads/flags.h"
#include "threads/init.h"
#include "threads/interrupt.h"
+#include "threads/malloc.h"
#include "threads/palloc.h"
+#include "threads/synch.h"
#include "threads/thread.h"
#include "threads/vaddr.h"
+struct start_process_args
+ {
+ struct semaphore sema;
+
+ // parent -> child
+ char *file_name;
+
+ // child -> parent
+ bool success;
+ struct parent_child *child;
+ };
+
static thread_func start_process NO_RETURN;
static bool load (const char *cmdline, void (**eip) (void), void **esp);
@@ -28,42 +42,75 @@ static bool load (const char *cmdline, void (**eip) (void), void **esp);
tid_t
process_execute (const char *file_name)
{
- char *fn_copy;
+ struct start_process_args args; // stack allocated since we know we wait for thread_create to finish reading
tid_t tid;
+ sema_init (&args.sema, 0);
+
/* Make a copy of FILE_NAME.
Otherwise there's a race between the caller and load(). */
- fn_copy = palloc_get_page (0);
- if (fn_copy == NULL)
+ args.file_name = palloc_get_page (0);
+ if (args.file_name == NULL)
return TID_ERROR;
- strlcpy (fn_copy, file_name, PGSIZE);
-
- /* Create a new thread to execute FILE_NAME. */
- tid = thread_create (file_name, PRI_DEFAULT, start_process, fn_copy);
- if (tid == TID_ERROR)
- palloc_free_page (fn_copy);
+ strlcpy (args.file_name, file_name, PGSIZE);
+
+ /* Create a new thread to execute FILE_NAME.
+ If thread_create fails we free and return immediately since we know
+ that the thread didn't start.
+ Otherwise, the child thread can be scheduled freely so we explicitly
+ wait for the child to signal that it has read from and written to
+ the args. Only then can we free resources and return the tid. */
+ tid = thread_create (file_name, PRI_DEFAULT, start_process, &args);
+ if (tid != TID_ERROR) {
+ sema_down (&args.sema);
+
+ if (args.success) {
+ list_push_back (&thread_current ()->children, &args.child->elem);
+ } else {
+ tid = -1;
+ }
+ }
+ palloc_free_page (args.file_name);
return tid;
}
/* A thread function that loads a user process and starts it
running. */
static void
-start_process (void *file_name_)
+start_process (void *args_)
{
- char *file_name = file_name_;
+ struct start_process_args *args = args_;
struct intr_frame if_;
+ struct thread *t;
bool success;
+ t = thread_current ();
+
/* Initialize interrupt frame and load executable. */
memset (&if_, 0, sizeof if_);
if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
if_.cs = SEL_UCSEG;
if_.eflags = FLAG_IF | FLAG_MBS;
- success = load (file_name, &if_.eip, &if_.esp);
+ success = load (args->file_name, &if_.eip, &if_.esp);
+ args->success = success;
+ t->load_success = success;
+
+ if (success) {
+ t->parent = malloc (sizeof (struct parent_child));
+
+ sema_init (&t->parent->exit_sema, 0);
+ lock_init (&t->parent->l);
+
+ t->parent->child_tid = t->tid;
+ t->parent->alive_count = 2;
+
+ args->child = t->parent;
+ }
+
+ sema_up (&args->sema);
/* If load failed, quit. */
- palloc_free_page (file_name);
- if (!success)
+ if (!success)
thread_exit ();
/* Start the user process by simulating a return from an
@@ -86,11 +133,39 @@ start_process (void *file_name_)
This function will be implemented in problem 2-2. For now, it
does nothing. */
int
-process_wait (tid_t child_tid UNUSED)
+process_wait (tid_t child_tid)
{
+ struct thread *t = thread_current ();
+ struct list_elem *e;
+ for (e = list_begin (&t->children); e != list_end (&t->children);
+ e = list_next (e))
+ {
+ struct parent_child *pc = list_entry (e, struct parent_child, elem);
+ if (child_tid == pc->child_tid) {
+ sema_down (&pc->exit_sema);
+ int exit_status = pc->exit_status;
+ pc->exit_status = -1;
+ sema_up (&pc->exit_sema); // a bit of a hack
+ // the child is killed so we can read again if we want to
+ return exit_status;
+ }
+ }
return -1;
}
+static void
+free_pc (struct parent_child *pc)
+{
+ lock_acquire (&pc->l);
+
+ pc->alive_count--;
+ if (pc->alive_count == 0) {
+ free (pc);
+ } else {
+ lock_release (&pc->l);
+ }
+}
+
/* Free the current process's resources. */
void
process_exit (void)
@@ -98,6 +173,20 @@ process_exit (void)
struct thread *cur = thread_current ();
uint32_t *pd;
+ if (cur->load_success) {
+ sema_up (&cur->parent->exit_sema);
+ free_pc (cur->parent);
+ printf("%s: exit(%d)\n", cur->name, cur->parent->exit_status);
+
+ struct list_elem *e;
+ struct parent_child *child;
+ while (!list_empty (&cur->children)) {
+ e = list_pop_front (&cur->children);
+ child = list_entry (e, struct parent_child, elem);
+ free_pc (child);
+ }
+ }
+
/* Destroy the current process's page directory and switch back
to the kernel-only page directory. */
pd = cur->pagedir;
@@ -226,10 +315,66 @@ load (const char *file_name, void (**eip) (void), void **esp)
goto done;
}
+ size_t cmd_len, word_alignment;
+ char *esp_cmd, **esp_argv_first, **esp_argv_entry, ***esp_argv;
+ int *esp_argc, argc = 0;
+
+ /* Copy passed command to start of user stack. */
+ cmd_len = strlen (file_name);
+ esp_cmd = *esp;
+ esp_cmd -= cmd_len + 1; // +1 makes room for '\0'
+ for (i = 0; i < cmd_len; i++) {
+ esp_cmd[i] = file_name[i];
+ }
+ esp_cmd[cmd_len] = '\0';
+
+ /* Tokenize passed command in-place. */
+ // esp_cmd still points to first character of passed command
+ char *token, *save_ptr;
+ for (token = strtok_r (esp_cmd, " ", &save_ptr); token != NULL;
+ token = strtok_r (NULL, " ", &save_ptr))
+ {
+ argc++;
+ }
+
+ file_name = esp_cmd;
+ strlcpy (thread_current ()->name, file_name, 16);
+
+ /* argv entries are pointers so they need to be aligned to the pointer size. */
+ size_t psize = sizeof (char *);
+ word_alignment = (cmd_len + psize) / psize;
+
+ /* Write argv entries. */
+ esp_argv_entry = *esp;
+ esp_argv_entry -= word_alignment + argc + 1; // +1 makes room for argv[argc] = NULL
+ esp_argv_first = esp_argv_entry; // save for later
+
+ // esp_cmd points to first entry so write directly
+ *esp_argv_entry = esp_cmd;
+ esp_argv_entry++;
+
+ for (i = 0; i < argc - 1; i++) {
+ // step until a \0 is found
+ while (*(++esp_cmd) != '\0') {}
+ // step over any ' '
+ while (*(++esp_cmd) == ' ') {}
+ *esp_argv_entry = esp_cmd; // point to next character
+ esp_argv_entry++;
+ }
+
+ esp_argv = esp_argv_first - 1;
+
+ *esp_argv = esp_argv_first;
+
+ esp_argc = esp_argv - 1;
+ *esp_argc = argc;
+
+ *esp = esp_argc - 1; // return address
+
/* Uncomment the following line to print some debug
information. This will be useful when you debug the program
stack.*/
-/*#define STACK_DEBUG*/
+// #define STACK_DEBUG
#ifdef STACK_DEBUG
printf("*esp is %p\nstack contents:\n", *esp);
diff --git a/src/userprog/start_pfs.sh b/src/userprog/start_pfs.sh
index 6ae76ad..26b9eb9 100644..100755
--- a/src/userprog/start_pfs.sh
+++ b/src/userprog/start_pfs.sh
@@ -1,4 +1,4 @@
-make -j4
+make -j12
cd build
pintos-mkdisk fs.dsk 800
dd if=/dev/urandom of=random bs=1 count=100
@@ -7,4 +7,4 @@ pintos --qemu -v -p random -a random -- -q
pintos --qemu -v -p ../../examples/pfs -a pfs -- -q
pintos --qemu -v -p ../../examples/pfs_writer -a pfs_writer -- -q
pintos --qemu -v -p ../../examples/pfs_reader -a pfs_reader -- -q
-pintos --qemu -v -- run pfs
+pintos --qemu -v -- -q run pfs
diff --git a/src/userprog/start_recursor.sh b/src/userprog/start_recursor.sh
index 22a69ac..6f5e99b 100644..100755
--- a/src/userprog/start_recursor.sh
+++ b/src/userprog/start_recursor.sh
@@ -1,6 +1,6 @@
-make -j4
-cd build
+make -j12
+cd build
pintos-mkdisk fs.dsk 400
-pintos --qemu -v -- -f -q
-pintos --qemu -v -p ../../examples/recursor_ng -a recursor_ng -- -q
-pintos --qemu -v -m 128 -- run "recursor_ng pintosmaster 6 1"
+pintos -v --qemu -- -f -q
+pintos -v --qemu -p ../../examples/recursor_ng -a recursor_ng -- -q
+pintos -v --qemu -m 128 -- run "recursor_ng pintosmaster 6 1"
diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c
index 370c89b..c1308ca 100644
--- a/src/userprog/syscall.c
+++ b/src/userprog/syscall.c
@@ -4,6 +4,17 @@
#include "threads/interrupt.h"
#include "threads/thread.h"
+#include "devices/input.h"
+#include "filesys/file.h"
+#include "filesys/filesys.h"
+#include "filesys/off_t.h"
+#include "lib/pid_t.h"
+#include "userprog/pagedir.h"
+#include "userprog/process.h"
+#include "threads/init.h"
+#include "threads/malloc.h"
+#include "threads/vaddr.h"
+
static void syscall_handler (struct intr_frame *);
void
@@ -13,8 +24,327 @@ syscall_init (void)
}
static void
-syscall_handler (struct intr_frame *f UNUSED)
+halt (void)
+{
+ power_off();
+}
+
+static bool
+create (const char *filename, off_t initial_size)
+{
+ return filesys_create (filename, initial_size);
+}
+
+static bool
+remove (const char *filename)
+{
+ return filesys_remove (filename);
+}
+
+static int
+open (const char *filename)
+{
+ struct thread *t = thread_current ();
+
+ if (!t->fds) {
+ t->fds = (struct file **) calloc (MAX_FDS, sizeof (struct file *));
+ }
+
+ struct file *file = filesys_open (filename);
+ if (!file) {
+ return -1;
+ }
+
+ for (int i = 0; i < MAX_FDS; i++) {
+ struct file **fd = t->fds + i;
+ if (*fd == NULL) {
+ *fd = file;
+ return i + 2; // 0 and 1 are reserved for stdin and stdout
+ }
+ }
+ free (file);
+ return -1;
+}
+
+static struct file **
+get_fd (struct thread *thread, int fd_i)
+{
+ if (!thread->fds) {
+ return NULL;
+ }
+
+ return thread->fds + fd_i - 2; // -2 since 0 and 1 are reserved and not present in the array
+}
+
+static void
+exit (int status)
{
- printf ("system call!\n");
+ struct thread *thread = thread_current ();
+
+ if (thread->fds) {
+ for (int i = 0; i < MAX_FDS; i++) {
+ struct file **fd = thread->fds + i;
+ if (fd && *fd) {
+ file_close (*fd);
+ *fd = NULL;
+ }
+ }
+ free (thread->fds);
+ }
+
+ lock_acquire (&thread->parent->l);
+ thread->parent->exit_status = status;
+ lock_release (&thread->parent->l);
+
thread_exit ();
}
+
+static int
+wait (tid_t child_tid)
+{
+ return process_wait (child_tid);
+}
+
+static int
+read (int fd_i, void *buf, unsigned size)
+{
+ struct thread *thread = thread_current ();
+
+ if (fd_i == 0) {
+ // stdin
+ unsigned i;
+ for (i = 0; i < size; i++) {
+ ((char *)buf)[i] = input_getc ();
+ }
+ return i;
+ } else if (fd_i == 1) {
+ // can't read from stdout
+ return -1;
+ }
+
+ struct file **fd = get_fd (thread, fd_i);
+ if (fd && *fd) {
+ return file_read (*fd, buf, size);
+ } else {
+ return -1;
+ }
+}
+
+static int
+write (int fd_i, const void *buf, unsigned size)
+{
+ struct thread *thread = thread_current ();
+
+ if (fd_i == 1) {
+ // stdout
+ putbuf ((const char *)buf, size);
+ return size;
+ } else if (fd_i == 0) {
+ // can't write to stdout
+ return -1;
+ }
+
+ struct file **fd = get_fd (thread, fd_i);
+ if (fd && *fd) {
+ return file_write (*fd, buf, size);
+ } else {
+ return -1;
+ }
+}
+
+static int
+filesize (int fd_i)
+{
+ struct thread *thread = thread_current ();
+ struct file **fd = get_fd (thread, fd_i);
+ if (fd && *fd) {
+ return file_length (*fd);
+ }
+ return -1;
+}
+
+static void
+seek (int fd_i, unsigned position)
+{
+ struct thread *thread = thread_current ();
+ struct file **fd = get_fd (thread, fd_i);
+ if (fd && *fd) {
+ file_seek (*fd, position);
+ }
+}
+
+static unsigned
+tell (int fd_i)
+{
+ struct thread *thread = thread_current ();
+ struct file **fd = get_fd (thread, fd_i);
+ if (fd && *fd) {
+ return file_tell (*fd);
+ }
+ return 0;
+}
+
+
+static void
+close (int fd_i)
+{
+ struct thread *thread = thread_current ();
+
+ struct file **fd = get_fd (thread, fd_i);
+ if (fd && *fd) {
+ file_close (*fd);
+ *fd = NULL;
+ }
+}
+
+static pid_t
+exec (const char *filename)
+{
+ return process_execute (filename);
+}
+
+static bool
+ptr_is_valid (const void *ptr)
+{
+ // uintptr_t ptr = _ptr;
+ if (!is_user_vaddr (ptr))
+ return false;
+
+ struct thread *t = thread_current ();
+
+ if (pagedir_get_page (t->pagedir, ptr) == NULL)
+ return false;
+
+ return true;
+}
+
+// cast argument N from f->esp to TYPE without dereferencing
+#define INTR_ESP(N, TYPE) (TYPE *)(f->esp+(4*(N)))
+
+#define CHECK_PTR_AND_MAYBE_EXIT(PTR) \
+ do { \
+ if (!ptr_is_valid (PTR)) { \
+ exit (-1); \
+ return; \
+ } \
+ } while (0)
+
+
+static void
+syscall_handler (struct intr_frame *f UNUSED)
+{
+ // check esp
+ int *syscall_number = INTR_ESP (0, int);
+
+ CHECK_PTR_AND_MAYBE_EXIT (syscall_number);
+
+ char **filename;
+ int *status, *fd_i;
+ off_t *initial_size;
+ tid_t *child_tid;
+ unsigned *size, *position;
+ void **buf;
+
+ switch (*syscall_number) {
+ case 0:
+ // halt
+ halt ();
+ break;
+ case 1:
+ // exit
+ status = INTR_ESP (1, int);
+ CHECK_PTR_AND_MAYBE_EXIT (status);
+ exit (*status);
+ break;
+ case 2:
+ // exec
+ filename = INTR_ESP (1, char *);
+ CHECK_PTR_AND_MAYBE_EXIT (filename);
+ CHECK_PTR_AND_MAYBE_EXIT (*filename);
+ f->eax = exec (*filename);
+ break;
+ case 3:
+ // wait
+ child_tid = INTR_ESP (1, tid_t);
+ CHECK_PTR_AND_MAYBE_EXIT (child_tid);
+ f->eax = wait (*child_tid);
+ break;
+ case 4:
+ // create
+ filename = INTR_ESP (1, char *);
+ initial_size = INTR_ESP (2, off_t);
+ CHECK_PTR_AND_MAYBE_EXIT (filename);
+ CHECK_PTR_AND_MAYBE_EXIT (*filename);
+ CHECK_PTR_AND_MAYBE_EXIT (initial_size);
+ f->eax = create (*filename, *initial_size);
+ break;
+ case 5:
+ // remove
+ filename = INTR_ESP (1, char *);
+ CHECK_PTR_AND_MAYBE_EXIT (filename);
+ CHECK_PTR_AND_MAYBE_EXIT (*filename);
+ f->eax = remove (*filename);
+ break;
+ case 6:
+ // open
+ filename = INTR_ESP (1, char *);
+ CHECK_PTR_AND_MAYBE_EXIT (filename);
+ CHECK_PTR_AND_MAYBE_EXIT (*filename);
+ f->eax = open (*filename);
+ break;
+ case 7:
+ // filesize
+ fd_i = INTR_ESP (1, int);
+ CHECK_PTR_AND_MAYBE_EXIT (fd_i);
+ f->eax = filesize (*fd_i);
+ break;
+ case 8:
+ // read
+ fd_i = INTR_ESP (1, int);
+ buf = INTR_ESP (2, void *);
+ size = INTR_ESP (3, unsigned);
+ CHECK_PTR_AND_MAYBE_EXIT (fd_i);
+ CHECK_PTR_AND_MAYBE_EXIT (buf);
+ CHECK_PTR_AND_MAYBE_EXIT (*buf);
+ CHECK_PTR_AND_MAYBE_EXIT (size);
+ CHECK_PTR_AND_MAYBE_EXIT (*buf + *size);
+ f->eax = read (*fd_i, *buf, *size);
+ break;
+ case 9:
+ // write
+ fd_i = INTR_ESP (1, int);
+ buf = INTR_ESP (2, void *);
+ size = INTR_ESP (3, unsigned);
+ CHECK_PTR_AND_MAYBE_EXIT (fd_i);
+ CHECK_PTR_AND_MAYBE_EXIT (buf);
+ CHECK_PTR_AND_MAYBE_EXIT (*buf);
+ CHECK_PTR_AND_MAYBE_EXIT (size);
+ CHECK_PTR_AND_MAYBE_EXIT (*buf + *size);
+ f->eax = write (*fd_i, *buf, *size);
+ break;
+ case 10:
+ // seek
+ fd_i = INTR_ESP (1, int);
+ position = INTR_ESP (2, unsigned);
+ CHECK_PTR_AND_MAYBE_EXIT (fd_i);
+ CHECK_PTR_AND_MAYBE_EXIT (position);
+ seek (*fd_i, *position);
+ break;
+ case 11:
+ // tell
+ fd_i = INTR_ESP (1, int);
+ CHECK_PTR_AND_MAYBE_EXIT (fd_i);
+ f->eax = tell (*fd_i);
+ break;
+ case 12:
+ // close
+ fd_i = INTR_ESP (1, int);
+ close (*fd_i);
+ break;
+ default:
+ printf ("kernel: unknown syscall '%d'\n", *syscall_number);
+ break;
+ }
+}
+
+#undef INTR_ESP