X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=solutions%2Fp2.patch;h=0940c141ee2d9b97c4c898cf3e8c2c57d1110041;hb=a4d72a2eec532fd923e507399bc33e75bb1c3fe5;hp=68331bd87b80895da485cac29b216f8c922e481f;hpb=8a8db36c5a9ca4d8a8c7323105328beb57a6ae75;p=pintos-anon diff --git a/solutions/p2.patch b/solutions/p2.patch index 68331bd..0940c14 100644 --- a/solutions/p2.patch +++ b/solutions/p2.patch @@ -1,74 +1,7 @@ -diff -urpN pintos.orig/src/constants.h pintos/src/constants.h ---- pintos.orig/src/constants.h 2004-09-21 17:26:39.000000000 -0700 -+++ pintos/src/constants.h 2004-09-27 16:41:17.000000000 -0700 -@@ -8,4 +8,4 @@ - /*#define MACRONAME 1 */ - - /* Uncomment if if you've implemented thread_join(). */ --/*#define THREAD_JOIN_IMPLEMENTED 1*/ -+#define THREAD_JOIN_IMPLEMENTED 1 -diff -urpN pintos.orig/src/threads/synch.c pintos/src/threads/synch.c ---- pintos.orig/src/threads/synch.c 2004-09-19 21:29:53.000000000 -0700 -+++ pintos/src/threads/synch.c 2004-09-27 16:41:17.000000000 -0700 -@@ -330,3 +330,35 @@ cond_name (const struct condition *cond) - - return cond->name; - } -+ -+void -+latch_init (struct latch *latch, const char *name) -+{ -+ latch->released = false; -+ lock_init (&latch->monitor_lock, name); -+ cond_init (&latch->rel_cond, name); -+} -+ -+void -+latch_acquire (struct latch *latch) -+{ -+ lock_acquire (&latch->monitor_lock); -+ if (!latch->released) -+ { -+ cond_wait (&latch->rel_cond, &latch->monitor_lock); -+ ASSERT (latch->released); -+ } -+ lock_release (&latch->monitor_lock); -+} -+ -+void -+latch_release (struct latch *latch) -+{ -+ lock_acquire (&latch->monitor_lock); -+ if (!latch->released) -+ { -+ latch->released = true; -+ cond_signal (&latch->rel_cond, &latch->monitor_lock); -+ } -+ lock_release (&latch->monitor_lock); -+} -diff -urpN pintos.orig/src/threads/synch.h pintos/src/threads/synch.h ---- pintos.orig/src/threads/synch.h 2004-09-19 21:29:53.000000000 -0700 -+++ pintos/src/threads/synch.h 2004-09-27 16:41:17.000000000 -0700 -@@ -44,4 +44,16 @@ void cond_signal (struct condition *, st - void cond_broadcast (struct condition *, struct lock *); - const char *cond_name (const struct condition *); - -+/* Latch. */ -+struct latch -+ { -+ bool released; /* Released yet? */ -+ struct lock monitor_lock; /* Monitor lock. */ -+ struct condition rel_cond; /* Signaled when released. */ -+ }; -+ -+void latch_init (struct latch *, const char *); -+void latch_acquire (struct latch *); -+void latch_release (struct latch *); -+ - #endif /* threads/synch.h */ -diff -urpN pintos.orig/src/threads/thread.c pintos/src/threads/thread.c ---- pintos.orig/src/threads/thread.c 2004-09-26 14:15:17.000000000 -0700 -+++ pintos/src/threads/thread.c 2004-09-27 16:41:17.000000000 -0700 +Index: src/threads/thread.c +diff -u src/threads/thread.c~ src/threads/thread.c +--- src/threads/thread.c~ ++++ src/threads/thread.c @@ -13,6 +13,7 @@ #include "threads/synch.h" #ifdef USERPROG @@ -77,52 +10,20 @@ diff -urpN pintos.orig/src/threads/thread.c pintos/src/threads/thread.c #endif /* Random value for struct thread's `magic' member. -@@ -80,6 +81,7 @@ thread_init (void) - init_thread (initial_thread, "main", PRI_DEFAULT); - initial_thread->status = THREAD_RUNNING; - initial_thread->tid = allocate_tid (); -+ sema_up (&initial_thread->can_die); - } - - /* Starts preemptive thread scheduling by enabling interrupts. -@@ -148,6 +150,7 @@ thread_create (const char *name, int pri - /* Initialize thread. */ - init_thread (t, name, priority); - tid = t->tid = allocate_tid (); -+ list_push_back (&thread_current ()->children, &t->children_elem); - - /* Stack frame for kernel_thread(). */ - kf = alloc_frame (t, sizeof *kf); -@@ -224,16 +227,36 @@ thread_tid (void) +@@ -251,16 +252,19 @@ thread_tid (void) void thread_exit (void) { + struct thread *t = thread_current (); -+ list_elem *e, *next; + ASSERT (!intr_context ()); #ifdef USERPROG process_exit (); #endif +- + syscall_exit (); + -+ /* Notify our parent that we're dying. */ -+ latch_release (&t->ready_to_die); -+ -+ /* Notify our children that they can die. */ -+ for (e = list_begin (&t->children); e != list_end (&t->children); -+ e = next) -+ { -+ struct thread *child = list_entry (e, struct thread, children_elem); -+ next = list_next (e); -+ list_remove (e); -+ sema_up (&child->can_die); -+ } -+ -+ /* Wait until our parent is ready for us to die. */ -+ sema_down (&t->can_die); - /* Just set our status to dying and schedule another process. We will be destroyed during the call to schedule_tail(). */ intr_disable (); @@ -131,49 +32,21 @@ diff -urpN pintos.orig/src/threads/thread.c pintos/src/threads/thread.c schedule (); NOT_REACHED (); } -@@ -270,6 +293,26 @@ thread_block (void) - thread_current ()->status = THREAD_BLOCKED; - schedule (); - } -+ -+/* Waits for thread with tid CHILD_TID to die. */ -+int -+thread_join (tid_t child_tid) -+{ -+ struct thread *cur = thread_current (); -+ list_elem *e; -+ -+ for (e = list_begin (&cur->children); e != list_end (&cur->children); ) -+ { -+ struct thread *child = list_entry (e, struct thread, children_elem); -+ e = list_next (e); -+ if (child->tid == child_tid) -+ { -+ latch_acquire (&child->ready_to_die); -+ return child->ret_code; -+ } -+ } -+ return -1; -+} - - /* Idle thread. Executes when no other thread is ready to run. */ - static void -@@ -335,6 +378,12 @@ init_thread (struct thread *t, const cha +@@ -400,6 +404,10 @@ init_thread (struct thread *t, const cha strlcpy (t->name, name, sizeof t->name); t->stack = (uint8_t *) t + PGSIZE; t->priority = priority; -+ latch_init (&t->ready_to_die, "ready-to-die"); -+ sema_init (&t->can_die, 0, "can-die"); + list_init (&t->children); -+ t->ret_code = -1; ++ t->wait_status = NULL; + list_init (&t->fds); + t->next_handle = 2; t->magic = THREAD_MAGIC; } -diff -urpN pintos.orig/src/threads/thread.h pintos/src/threads/thread.h ---- pintos.orig/src/threads/thread.h 2004-09-26 14:15:17.000000000 -0700 -+++ pintos/src/threads/thread.h 2004-09-27 16:41:17.000000000 -0700 +Index: src/threads/thread.h +diff -u src/threads/thread.h~ src/threads/thread.h +--- src/threads/thread.h~ ++++ src/threads/thread.h @@ -4,6 +4,7 @@ #include #include @@ -182,46 +55,58 @@ diff -urpN pintos.orig/src/threads/thread.h pintos/src/threads/thread.h /* States in a thread's life cycle. */ enum thread_status -@@ -89,12 +90,23 @@ struct thread +@@ -89,6 +90,10 @@ struct thread uint8_t *stack; /* Saved stack pointer. */ int priority; /* Priority. */ -+ /* Members for implementing thread_join(). */ -+ struct latch ready_to_die; /* Release when thread about to die. */ -+ struct semaphore can_die; /* Up when thread allowed to die. */ -+ struct list children; /* List of child threads. */ -+ list_elem children_elem; /* Element of `children' list. */ -+ int ret_code; /* Return status. */ ++ /* Owned by process.c. */ ++ struct wait_status *wait_status; /* This process's completion status. */ ++ struct list children; /* Completion status of children. */ + /* Shared between thread.c and synch.c. */ - list_elem elem; /* List element. */ + struct list_elem elem; /* List element. */ - #ifdef USERPROG +@@ -96,11 +102,31 @@ struct thread /* Owned by userprog/process.c. */ uint32_t *pagedir; /* Page directory. */ + #endif ++ struct file *bin_file; /* Executable. */ + + /* Owned by syscall.c. */ + struct list fds; /* List of file descriptors. */ + int next_handle; /* Next handle value. */ - #endif - - /* Owned by thread.c */ -@@ -119,7 +131,7 @@ void thread_yield (void); - void thread_block (void); - /* This function will be implemented in problem 1-2. */ --void thread_join (tid_t); -+int thread_join (tid_t); + /* Owned by thread.c. */ + unsigned magic; /* Detects stack overflow. */ + }; - /* These functions will be implemented in problem 1-3. */ - void thread_set_priority (int); -diff -urpN pintos.orig/src/userprog/exception.c pintos/src/userprog/exception.c ---- pintos.orig/src/userprog/exception.c 2004-09-26 14:15:17.000000000 -0700 -+++ pintos/src/userprog/exception.c 2004-09-27 16:41:17.000000000 -0700 -@@ -147,6 +147,13 @@ page_fault (struct intr_frame *f) ++/* Tracks the completion of a process. ++ Reference held by both the parent, in its `children' list, ++ and by the child, in its `wait_status' pointer. */ ++struct wait_status ++ { ++ struct list_elem elem; /* `children' list element. */ ++ struct lock lock; /* Protects ref_cnt. */ ++ int ref_cnt; /* 2=child and parent both alive, ++ 1=either child or parent alive, ++ 0=child and parent both dead. */ ++ tid_t tid; /* Child thread id. */ ++ int exit_code; /* Child exit code, if dead. */ ++ struct semaphore dead; /* 1=child alive, 0=child dead. */ ++ }; ++ + /* If false (default), use round-robin scheduler. + If true, use multi-level feedback queue scheduler. + Controlled by kernel command-line options "-o mlfqs". +Index: src/userprog/exception.c +diff -u src/userprog/exception.c~ src/userprog/exception.c +--- src/userprog/exception.c~ ++++ src/userprog/exception.c +@@ -150,6 +150,14 @@ page_fault (struct intr_frame *f) write = (f->error_code & PF_W) != 0; user = (f->error_code & PF_U) != 0; ++ /* Handle bad dereferences from system call implementations. */ + if (!user) + { + f->eip = (void (*) (void)) f->eax; @@ -232,27 +117,220 @@ diff -urpN pintos.orig/src/userprog/exception.c pintos/src/userprog/exception.c /* To implement virtual memory, delete the rest of the function body, and replace it with code that brings in the page to which fault_addr refers. */ -diff -urpN pintos.orig/src/userprog/process.c pintos/src/userprog/process.c ---- pintos.orig/src/userprog/process.c 2004-09-22 17:58:29.000000000 -0700 -+++ pintos/src/userprog/process.c 2004-09-27 16:41:17.000000000 -0700 -@@ -182,7 +182,7 @@ struct Elf32_Phdr +Index: src/userprog/process.c +diff -u src/userprog/process.c~ src/userprog/process.c +--- src/userprog/process.c~ ++++ src/userprog/process.c +@@ -14,11 +14,23 @@ + #include "threads/init.h" + #include "threads/interrupt.h" ++#include "threads/malloc.h" + #include "threads/palloc.h" + #include "threads/thread.h" + #include "threads/vaddr.h" + + static thread_func execute_thread NO_RETURN; +-static bool load (const char *cmdline, void (**eip) (void), void **esp); ++static bool load (const char *cmd_line, void (**eip) (void), void **esp); ++ ++/* Data structure shared between process_execute() in the ++ invoking thread and execute_thread() in the newly invoked ++ thread. */ ++struct exec_info ++ { ++ const char *file_name; /* Program to load. */ ++ struct semaphore load_done; /* "Up"ed when loading complete. */ ++ struct wait_status *wait_status; /* Child process. */ ++ bool success; /* Program successfully loaded? */ ++ }; + + /* Starts a new thread running a user program loaded from + FILE_NAME. The new thread may be scheduled (and may even exit) +@@ -27,29 +39,37 @@ static bool load (const char *cmdline, v + tid_t + process_execute (const char *file_name) + { +- char *fn_copy; ++ struct exec_info exec; ++ char thread_name[16]; ++ char *save_ptr; + tid_t tid; + +- /* 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) +- return TID_ERROR; +- strlcpy (fn_copy, file_name, PGSIZE); ++ /* Initialize exec_info. */ ++ exec.file_name = file_name; ++ sema_init (&exec.load_done, 0); + + /* Create a new thread to execute FILE_NAME. */ +- tid = thread_create (file_name, PRI_DEFAULT, execute_thread, fn_copy); +- if (tid == TID_ERROR) +- palloc_free_page (fn_copy); ++ strlcpy (thread_name, file_name, sizeof thread_name); ++ strtok_r (thread_name, " ", &save_ptr); ++ tid = thread_create (thread_name, PRI_DEFAULT, execute_thread, &exec); ++ if (tid != TID_ERROR) ++ { ++ sema_down (&exec.load_done); ++ if (exec.success) ++ list_push_back (&thread_current ()->children, &exec.wait_status->elem); ++ else ++ tid = TID_ERROR; ++ } ++ + return tid; + } + + /* A thread function that loads a user process and starts it + running. */ + static void +-execute_thread (void *file_name_) ++execute_thread (void *exec_) + { +- char *file_name = file_name_; ++ struct exec_info *exec = exec_; + struct intr_frame if_; + bool success; + +@@ -58,10 +78,29 @@ execute_thread (void *file_name_) + 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 (exec->file_name, &if_.eip, &if_.esp); ++ ++ /* Allocate wait_status. */ ++ if (success) ++ { ++ exec->wait_status = thread_current ()->wait_status ++ = malloc (sizeof *exec->wait_status); ++ success = exec->wait_status != NULL; ++ } + +- /* If load failed, quit. */ +- palloc_free_page (file_name); ++ /* Initialize wait_status. */ ++ if (success) ++ { ++ lock_init (&exec->wait_status->lock); ++ exec->wait_status->ref_cnt = 2; ++ exec->wait_status->tid = thread_current ()->tid; ++ exec->wait_status->exit_code = -1; ++ sema_init (&exec->wait_status->dead, 0); ++ } ++ ++ /* Notify parent thread and clean up. */ ++ exec->success = success; ++ sema_up (&exec->load_done); + if (!success) + thread_exit (); + +@@ -75,18 +113,47 @@ execute_thread (void *file_name_) + NOT_REACHED (); + } + ++/* Releases one reference to CS and, if it is now unreferenced, ++ frees it. */ ++static void ++release_child (struct wait_status *cs) ++{ ++ int new_ref_cnt; ++ ++ lock_acquire (&cs->lock); ++ new_ref_cnt = --cs->ref_cnt; ++ lock_release (&cs->lock); ++ ++ if (new_ref_cnt == 0) ++ free (cs); ++} ++ + /* Waits for thread TID to die and returns its exit status. If + it was terminated by the kernel (i.e. killed due to an + exception), returns -1. If TID is invalid or if it was not a + child of the calling process, or if process_wait() has already + been successfully called for the given TID, returns -1 +- immediately, without waiting. +- +- This function will be implemented in problem 2-2. For now, it +- does nothing. */ ++ immediately, without waiting. */ + int +-process_wait (tid_t child_tid UNUSED) ++process_wait (tid_t child_tid) + { ++ struct thread *cur = thread_current (); ++ struct list_elem *e; ++ ++ for (e = list_begin (&cur->children); e != list_end (&cur->children); ++ e = list_next (e)) ++ { ++ struct wait_status *cs = list_entry (e, struct wait_status, elem); ++ if (cs->tid == child_tid) ++ { ++ int exit_code; ++ list_remove (e); ++ sema_down (&cs->dead); ++ exit_code = cs->exit_code; ++ release_child (cs); ++ return exit_code; ++ } ++ } + return -1; + } + +@@ -95,8 +162,30 @@ void + process_exit (void) + { + struct thread *cur = thread_current (); ++ struct list_elem *e, *next; + uint32_t *pd; + ++ /* Close executable (and allow writes). */ ++ file_close (cur->bin_file); ++ ++ /* Notify parent that we're dead. */ ++ if (cur->wait_status != NULL) ++ { ++ struct wait_status *cs = cur->wait_status; ++ printf ("%s: exit(%d)\n", cur->name, cs->exit_code); ++ sema_up (&cs->dead); ++ release_child (cs); ++ } ++ ++ /* Free entries of children list. */ ++ for (e = list_begin (&cur->children); e != list_end (&cur->children); ++ e = next) ++ { ++ struct wait_status *cs = list_entry (e, struct wait_status, elem); ++ next = list_remove (e); ++ release_child (cs); ++ } ++ + /* Destroy the current process's page directory and switch back + to the kernel-only page directory. */ + pd = cur->pagedir; +@@ -193,7 +284,7 @@ struct Elf32_Phdr + #define PF_W 2 /* Writable. */ #define PF_R 4 /* Readable. */ - static bool load_segment (struct file *, const struct Elf32_Phdr *); -static bool setup_stack (void **esp); -+static bool setup_stack (const char *cmdline, void **esp); - - /* Aborts loading an executable, with an error message. */ - #define LOAD_ERROR(MSG) \ -@@ -198,13 +198,15 @@ static bool setup_stack (void **esp); ++static bool setup_stack (const char *cmd_line, void **esp); + static bool validate_segment (const struct Elf32_Phdr *, struct file *); + static bool load_segment (struct file *file, off_t ofs, uint8_t *upage, + bool writable); +@@ -209,13 +300,15 @@ static bool setup_stack (void **esp); and its initial stack pointer into *ESP. Returns true if successful, false otherwise. */ bool --load (const char *filename, void (**eip) (void), void **esp) -+load (const char *cmdline, void (**eip) (void), void **esp) +-load (const char *file_name, void (**eip) (void), void **esp) ++load (const char *cmd_line, void (**eip) (void), void **esp) { struct thread *t = thread_current (); -+ char filename[NAME_MAX + 2]; ++ char file_name[NAME_MAX + 2]; struct Elf32_Ehdr ehdr; struct file *file = NULL; off_t file_ofs; @@ -260,37 +338,55 @@ diff -urpN pintos.orig/src/userprog/process.c pintos/src/userprog/process.c + char *cp; int i; - /* Allocate page directory. */ -@@ -212,6 +214,14 @@ load (const char *filename, void (**eip) - if (t->pagedir == NULL) - LOAD_ERROR (("page directory allocation failed")); + /* Allocate and activate page directory. */ +@@ -224,13 +317,22 @@ load (const char *file_name, void (**eip) + goto done; + process_activate (); -+ /* Extract filename from command line. */ -+ while (*cmdline == ' ') -+ cmdline++; -+ strlcpy (filename, cmdline, sizeof filename); -+ cp = strchr (filename, ' '); ++ /* Extract file_name from command line. */ ++ while (*cmd_line == ' ') ++ cmd_line++; ++ strlcpy (file_name, cmd_line, sizeof file_name); ++ cp = strchr (file_name, ' '); + if (cp != NULL) + *cp = '\0'; + /* Open executable file. */ - file = filesys_open (filename); - if (file == NULL) -@@ -269,7 +279,7 @@ load (const char *filename, void (**eip) +- file = filesys_open (file_name); ++ t->bin_file = file = filesys_open (file_name); + if (file == NULL) + { + printf ("load: %s: open failed\n", file_name); + goto done; + } ++ file_deny_write (file); + + /* Read and verify executable header. */ + if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr +@@ -284,7 +386,7 @@ load (const char *file_name, void (**eip) } /* Set up stack. */ - if (!setup_stack (esp)) -+ if (!setup_stack (cmdline, esp)) ++ if (!setup_stack (cmd_line, esp)) goto done; /* Start address. */ -@@ -371,10 +381,80 @@ load_segment (struct file *file, const s +@@ -294,7 +396,6 @@ load (const char *file_name, void (**eip) + + done: + /* We arrive here whether the load is successful or not. */ +- file_close (file); + return success; + } + +@@ -393,10 +494,92 @@ load_segment (struct file *file, const s return true; } -/* Create a minimal stack by mapping a zeroed page at the top of - user virtual memory. */ ++/* Reverse the order of the ARGC pointers to char in ARGV. */ +static void +reverse (int argc, char **argv) +{ @@ -302,6 +398,13 @@ diff -urpN pintos.orig/src/userprog/process.c pintos/src/userprog/process.c + } +} + ++/* Pushes the SIZE bytes in BUF onto the stack in KPAGE, whose ++ page-relative stack pointer is *OFS, and then adjusts *OFS ++ appropriately. The bytes pushed are rounded to a 32-bit ++ boundary. ++ ++ If successful, returns a pointer to the newly pushed object. ++ On failure, returns a null pointer. */ +static void * +push (uint8_t *kpage, size_t *ofs, const void *buf, size_t size) +{ @@ -314,20 +417,24 @@ diff -urpN pintos.orig/src/userprog/process.c pintos/src/userprog/process.c + return kpage + *ofs + (padsize - size); +} + ++/* Sets up command line arguments in KPAGE, which will be mapped ++ to UPAGE in user space. The command line arguments are taken ++ from CMD_LINE, separated by spaces. Sets *ESP to the initial ++ stack pointer for the process. */ +static bool -+init_cmdline (uint8_t *kpage, uint8_t *upage, const char *cmdline, -+ void **esp) ++init_cmd_line (uint8_t *kpage, uint8_t *upage, const char *cmd_line, ++ void **esp) +{ + size_t ofs = PGSIZE; + char *const null = NULL; -+ char *cmdline_copy; ++ char *cmd_line_copy; + char *karg, *saveptr; + int argc; + char **argv; + + /* Push command line string. */ -+ cmdline_copy = push (kpage, &ofs, cmdline, strlen (cmdline) + 1); -+ if (cmdline_copy == NULL) ++ cmd_line_copy = push (kpage, &ofs, cmd_line, strlen (cmd_line) + 1); ++ if (cmd_line_copy == NULL) + return false; + + if (push (kpage, &ofs, &null, sizeof null) == NULL) @@ -336,10 +443,10 @@ diff -urpN pintos.orig/src/userprog/process.c pintos/src/userprog/process.c + /* Parse command line into arguments + and push them in reverse order. */ + argc = 0; -+ for (karg = strtok_r (cmdline_copy, " ", &saveptr); karg != NULL; ++ for (karg = strtok_r (cmd_line_copy, " ", &saveptr); karg != NULL; + karg = strtok_r (NULL, " ", &saveptr)) + { -+ char *uarg = upage + (karg - (char *) kpage); ++ void *uarg = upage + (karg - (char *) kpage); + if (push (kpage, &ofs, &uarg, sizeof uarg) == NULL) + return false; + argc++; @@ -361,54 +468,54 @@ diff -urpN pintos.orig/src/userprog/process.c pintos/src/userprog/process.c +} + +/* Create a minimal stack for T by mapping a page at the -+ top of user virtual memory. Fills in the page using CMDLINE ++ top of user virtual memory. Fills in the page using CMD_LINE + and sets *ESP to the stack pointer. */ static bool -setup_stack (void **esp) -+setup_stack (const char *cmdline, void **esp) ++setup_stack (const char *cmd_line, void **esp) { uint8_t *kpage; bool success = false; -@@ -382,9 +462,9 @@ setup_stack (void **esp) +@@ -404,9 +587,9 @@ setup_stack (void **esp) kpage = palloc_get_page (PAL_USER | PAL_ZERO); if (kpage != NULL) { -- success = install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage); +- success = install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage, true); - if (success) - *esp = PHYS_BASE; + uint8_t *upage = ((uint8_t *) PHYS_BASE) - PGSIZE; -+ if (install_page (upage, kpage)) -+ success = init_cmdline (kpage, upage, cmdline, esp); ++ if (install_page (upage, kpage, true)) ++ success = init_cmd_line (kpage, upage, cmd_line, esp); else palloc_free_page (kpage); } -diff -urpN pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c ---- pintos.orig/src/userprog/syscall.c 2004-09-26 14:15:17.000000000 -0700 -+++ pintos/src/userprog/syscall.c 2004-09-27 16:43:00.000000000 -0700 -@@ -1,20 +1,429 @@ +Index: src/userprog/syscall.c +diff -u src/userprog/syscall.c~ src/userprog/syscall.c +--- src/userprog/syscall.c~ ++++ src/userprog/syscall.c +@@ -1,20 +1,486 @@ #include "userprog/syscall.h" #include +#include #include +#include "userprog/process.h" +#include "userprog/pagedir.h" -+#include "devices/kbd.h" ++#include "devices/input.h" +#include "filesys/filesys.h" +#include "filesys/file.h" +#include "threads/init.h" #include "threads/interrupt.h" +#include "threads/malloc.h" -+#include "threads/mmu.h" +#include "threads/palloc.h" #include "threads/thread.h" ++#include "threads/vaddr.h" - + -+typedef int syscall_function (int, int, int); + +static int sys_halt (void); +static int sys_exit (int status); +static int sys_exec (const char *ufile); -+static int sys_join (tid_t); ++static int sys_wait (tid_t); +static int sys_create (const char *ufile, unsigned initial_size); +static int sys_remove (const char *ufile); +static int sys_open (const char *ufile); @@ -418,89 +525,110 @@ diff -urpN pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c +static int sys_seek (int handle, unsigned position); +static int sys_tell (int handle); +static int sys_close (int handle); -+ -+struct syscall -+ { -+ size_t arg_cnt; -+ syscall_function *func; -+ }; -+ -+struct syscall syscall_table[] = -+ { -+ {0, (syscall_function *) sys_halt}, -+ {1, (syscall_function *) sys_exit}, -+ {1, (syscall_function *) sys_exec}, -+ {1, (syscall_function *) sys_join}, -+ {2, (syscall_function *) sys_create}, -+ {1, (syscall_function *) sys_remove}, -+ {1, (syscall_function *) sys_open}, -+ {1, (syscall_function *) sys_filesize}, -+ {3, (syscall_function *) sys_read}, -+ {3, (syscall_function *) sys_write}, -+ {2, (syscall_function *) sys_seek}, -+ {1, (syscall_function *) sys_tell}, -+ {1, (syscall_function *) sys_close}, -+ }; -+static const int syscall_cnt = sizeof syscall_table / sizeof *syscall_table; + static void syscall_handler (struct intr_frame *); - +static void copy_in (void *, const void *, size_t); + ++/* Serializes file system operations. */ +static struct lock fs_lock; + void syscall_init (void) { - intr_register (0x30, 3, INTR_ON, syscall_handler, "syscall"); -+ lock_init (&fs_lock, "fs"); + intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall"); ++ lock_init (&fs_lock); } -- + - static void --syscall_handler (struct intr_frame *f UNUSED) ++/* System call handler. */ ++static void +syscall_handler (struct intr_frame *f) - { -- printf ("system call!\n"); -+ struct syscall *s; -+ int call_nr; ++{ ++ typedef int syscall_function (int, int, int); ++ ++ /* A system call. */ ++ struct syscall ++ { ++ size_t arg_cnt; /* Number of arguments. */ ++ syscall_function *func; /* Implementation. */ ++ }; ++ ++ /* Table of system calls. */ ++ static const struct syscall syscall_table[] = ++ { ++ {0, (syscall_function *) sys_halt}, ++ {1, (syscall_function *) sys_exit}, ++ {1, (syscall_function *) sys_exec}, ++ {1, (syscall_function *) sys_wait}, ++ {2, (syscall_function *) sys_create}, ++ {1, (syscall_function *) sys_remove}, ++ {1, (syscall_function *) sys_open}, ++ {1, (syscall_function *) sys_filesize}, ++ {3, (syscall_function *) sys_read}, ++ {3, (syscall_function *) sys_write}, ++ {2, (syscall_function *) sys_seek}, ++ {1, (syscall_function *) sys_tell}, ++ {1, (syscall_function *) sys_close}, ++ }; ++ ++ const struct syscall *sc; ++ unsigned call_nr; + int args[3]; -+ ++ ++ /* Get the system call. */ + copy_in (&call_nr, f->esp, sizeof call_nr); -+ if (call_nr < 0 || call_nr >= syscall_cnt) -+ { -+ printf ("bad syscall number %d\n", call_nr); -+ thread_exit (); -+ } -+ -+ s = syscall_table + call_nr; -+ ASSERT (s->arg_cnt <= sizeof args / sizeof *args); ++ if (call_nr >= sizeof syscall_table / sizeof *syscall_table) ++ thread_exit (); ++ sc = syscall_table + call_nr; + ++ /* Get the system call arguments. */ ++ ASSERT (sc->arg_cnt <= sizeof args / sizeof *args); + memset (args, 0, sizeof args); -+ copy_in (args, (uint32_t *) f->esp + 1, sizeof *args * s->arg_cnt); -+ f->eax = s->func (args[0], args[1], args[2]); ++ copy_in (args, (uint32_t *) f->esp + 1, sizeof *args * sc->arg_cnt); ++ ++ /* Execute the system call, ++ and set the return value. */ ++ f->eax = sc->func (args[0], args[1], args[2]); +} + ++/* Returns true if UADDR is a valid, mapped user address, ++ false otherwise. */ +static bool +verify_user (const void *uaddr) +{ -+ return pagedir_get_page (thread_current ()->pagedir, uaddr) != NULL; ++ return (uaddr < PHYS_BASE ++ && pagedir_get_page (thread_current ()->pagedir, uaddr) != NULL); +} + -+static inline bool get_user (uint8_t *dst, const uint8_t *usrc) { ++/* Copies a byte from user address USRC to kernel address DST. ++ USRC must be below PHYS_BASE. ++ Returns true if successful, false if a segfault occurred. */ ++static inline bool ++get_user (uint8_t *dst, const uint8_t *usrc) ++{ + int eax; + asm ("movl $1f, %%eax; movb %2, %%al; movb %%al, %0; 1:" + : "=m" (*dst), "=&a" (eax) : "m" (*usrc)); + return eax != 0; +} + -+static inline bool put_user (uint8_t *udst, uint8_t byte) { ++/* Writes BYTE to user address UDST. ++ UDST must be below PHYS_BASE. ++ Returns true if successful, false if a segfault occurred. */ ++static inline bool ++put_user (uint8_t *udst, uint8_t byte) ++{ + int eax; + asm ("movl $1f, %%eax; movb %b2, %0; 1:" + : "=m" (*udst), "=&a" (eax) : "r" (byte)); + return eax != 0; +} + -+static void ++/* Copies SIZE bytes from user address USRC to kernel address ++ DST. ++ Call thread_exit() if any of the user accesses are invalid. */ + static void +-syscall_handler (struct intr_frame *f UNUSED) +copy_in (void *dst_, const void *usrc_, size_t size) +{ + uint8_t *dst = dst_; @@ -511,6 +639,11 @@ diff -urpN pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + thread_exit (); +} + ++/* Creates a copy of user string US in kernel memory ++ and returns it as a page that must be freed with ++ palloc_free_page(). ++ Truncates the string at PGSIZE bytes in size. ++ Call thread_exit() if any of the user accesses are invalid. */ +static char * +copy_in_string (const char *us) +{ @@ -519,42 +652,40 @@ diff -urpN pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + + ks = palloc_get_page (0); + if (ks == NULL) -+ { -+ printf ("copy_in_string: out of memory\n"); -+ thread_exit (); -+ } ++ thread_exit (); + + for (length = 0; length < PGSIZE; length++) + { + if (us >= (char *) PHYS_BASE || !get_user (ks + length, us++)) + { -+ printf ("bad user reference (%p)\n", us + length); ++ palloc_free_page (ks); + thread_exit (); + } + + if (ks[length] == '\0') + return ks; + } -+ -+ printf ("copy_in_string: string too long\n"); -+ palloc_free_page (ks); -+ thread_exit (); ++ ks[PGSIZE - 1] = '\0'; ++ return ks; +} + ++/* Halt system call. */ +static int +sys_halt (void) +{ + power_off (); +} + ++/* Exit system call. */ +static int -+sys_exit (int ret_code) ++sys_exit (int exit_code) +{ -+ thread_current ()->ret_code = ret_code; ++ thread_current ()->wait_status->exit_code = exit_code; + thread_exit (); + NOT_REACHED (); +} + ++/* Exec system call. */ +static int +sys_exec (const char *ufile) +{ @@ -570,12 +701,14 @@ diff -urpN pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + return tid; +} + ++/* Wait system call. */ +static int -+sys_join (tid_t child) ++sys_wait (tid_t child) +{ -+ return thread_join (child); ++ return process_wait (child); +} + ++/* Create system call. */ +static int +sys_create (const char *ufile, unsigned initial_size) +{ @@ -591,9 +724,11 @@ diff -urpN pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + return ok; +} + ++/* Remove system call. */ +static int +sys_remove (const char *ufile) -+{ + { +- printf ("system call!\n"); + char *kfile = copy_in_string (ufile); + bool ok; + @@ -606,63 +741,68 @@ diff -urpN pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + return ok; +} + -+struct fildes ++/* A file descriptor, for binding a file handle to a file. */ ++struct file_descriptor + { -+ list_elem elem; -+ struct file *file; -+ int handle; ++ struct list_elem elem; /* List element. */ ++ struct file *file; /* File. */ ++ int handle; /* File handle. */ + }; + ++/* Open system call. */ +static int +sys_open (const char *ufile) +{ + char *kfile = copy_in_string (ufile); -+ struct fildes *fd; ++ struct file_descriptor *fd; + int handle = -1; + + fd = malloc (sizeof *fd); -+ if (fd == NULL) -+ goto exit; -+ -+ lock_acquire (&fs_lock); -+ fd->file = filesys_open (kfile); -+ if (fd->file != NULL) ++ if (fd != NULL) + { -+ struct thread *cur = thread_current (); -+ handle = fd->handle = cur->next_handle++; -+ list_push_front (&cur->fds, &fd->elem); ++ lock_acquire (&fs_lock); ++ fd->file = filesys_open (kfile); ++ if (fd->file != NULL) ++ { ++ struct thread *cur = thread_current (); ++ handle = fd->handle = cur->next_handle++; ++ list_push_front (&cur->fds, &fd->elem); ++ } ++ else ++ free (fd); ++ lock_release (&fs_lock); + } -+ else -+ free (fd); -+ lock_release (&fs_lock); -+ -+ exit: ++ + palloc_free_page (kfile); + return handle; +} + -+static struct fildes * ++/* Returns the file descriptor associated with the given handle. ++ Terminates the process if HANDLE is not associated with an ++ open file. */ ++static struct file_descriptor * +lookup_fd (int handle) +{ + struct thread *cur = thread_current (); -+ list_elem *e; ++ struct list_elem *e; + + for (e = list_begin (&cur->fds); e != list_end (&cur->fds); + e = list_next (e)) + { -+ struct fildes *fd = list_entry (e, struct fildes, elem); ++ struct file_descriptor *fd; ++ fd = list_entry (e, struct file_descriptor, elem); + if (fd->handle == handle) + return fd; + } + -+ printf ("no handle %d\n", handle); thread_exit (); } + ++/* Filesize system call. */ +static int +sys_filesize (int handle) +{ -+ struct fildes *fd = lookup_fd (handle); ++ struct file_descriptor *fd = lookup_fd (handle); + int size; + + lock_acquire (&fs_lock); @@ -672,35 +812,41 @@ diff -urpN pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + return size; +} + ++/* Read system call. */ +static int +sys_read (int handle, void *udst_, unsigned size) +{ + uint8_t *udst = udst_; -+ struct fildes *fd; ++ struct file_descriptor *fd; + int bytes_read = 0; -+ ++ ++ /* Handle keyboard reads. */ + if (handle == STDIN_FILENO) + { + for (bytes_read = 0; (size_t) bytes_read < size; bytes_read++) -+ if (udst >= (uint8_t *) PHYS_BASE || !put_user (udst++, kbd_getc ())) ++ if (udst >= (uint8_t *) PHYS_BASE || !put_user (udst++, input_getc ())) + thread_exit (); + return bytes_read; + } -+ -+ lock_acquire (&fs_lock); ++ ++ /* Handle all other reads. */ + fd = lookup_fd (handle); ++ lock_acquire (&fs_lock); + while (size > 0) + { ++ /* How much to read into this page? */ + size_t page_left = PGSIZE - pg_ofs (udst); + size_t read_amt = size < page_left ? size : page_left; + off_t retval; -+ ++ ++ /* Check that touching this page is okay. */ + if (!verify_user (udst)) + { + lock_release (&fs_lock); + thread_exit (); + } -+ ++ ++ /* Read from file into page. */ + retval = file_read (fd->file, udst, read_amt); + if (retval < 0) + { @@ -708,11 +854,13 @@ diff -urpN pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + bytes_read = -1; + break; + } -+ + bytes_read += retval; ++ ++ /* If it was a short read we're done. */ + if (retval != (off_t) read_amt) + break; -+ ++ ++ /* Advance. */ + udst += retval; + size -= retval; + } @@ -721,28 +869,34 @@ diff -urpN pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + return bytes_read; +} + ++/* Write system call. */ +static int +sys_write (int handle, void *usrc_, unsigned size) +{ + uint8_t *usrc = usrc_; -+ struct fildes *fd = NULL; ++ struct file_descriptor *fd = NULL; + int bytes_written = 0; -+ -+ lock_acquire (&fs_lock); ++ ++ /* Lookup up file descriptor. */ + if (handle != STDOUT_FILENO) + fd = lookup_fd (handle); ++ ++ lock_acquire (&fs_lock); + while (size > 0) + { ++ /* How much bytes to write to this page? */ + size_t page_left = PGSIZE - pg_ofs (usrc); + size_t write_amt = size < page_left ? size : page_left; + off_t retval; -+ ++ ++ /* Check that we can touch this user page. */ + if (!verify_user (usrc)) + { + lock_release (&fs_lock); + thread_exit (); + } -+ ++ ++ /* Do the write. */ + if (handle == STDOUT_FILENO) + { + putbuf (usrc, write_amt); @@ -756,11 +910,13 @@ diff -urpN pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + bytes_written = -1; + break; + } -+ + bytes_written += retval; ++ ++ /* If it was a short write we're done. */ + if (retval != (off_t) write_amt) + break; -+ ++ ++ /* Advance. */ + usrc += retval; + size -= retval; + } @@ -769,22 +925,25 @@ diff -urpN pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + return bytes_written; +} + ++/* Seek system call. */ +static int +sys_seek (int handle, unsigned position) +{ -+ struct fildes *fd = lookup_fd (handle); ++ struct file_descriptor *fd = lookup_fd (handle); + + lock_acquire (&fs_lock); -+ file_seek (fd->file, position); ++ if ((off_t) position >= 0) ++ file_seek (fd->file, position); + lock_release (&fs_lock); + + return 0; +} + ++/* Tell system call. */ +static int +sys_tell (int handle) +{ -+ struct fildes *fd = lookup_fd (handle); ++ struct file_descriptor *fd = lookup_fd (handle); + unsigned position; + + lock_acquire (&fs_lock); @@ -794,10 +953,11 @@ diff -urpN pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + return position; +} + ++/* Close system call. */ +static int +sys_close (int handle) +{ -+ struct fildes *fd = lookup_fd (handle); ++ struct file_descriptor *fd = lookup_fd (handle); + lock_acquire (&fs_lock); + file_close (fd->file); + lock_release (&fs_lock); @@ -806,23 +966,28 @@ diff -urpN pintos.orig/src/userprog/syscall.c pintos/src/userprog/syscall.c + return 0; +} + ++/* On thread exit, close all open files. */ +void +syscall_exit (void) +{ + struct thread *cur = thread_current (); -+ list_elem *e, *next; ++ struct list_elem *e, *next; + + for (e = list_begin (&cur->fds); e != list_end (&cur->fds); e = next) + { -+ struct fildes *fd = list_entry (e, struct fildes, elem); ++ struct file_descriptor *fd; ++ fd = list_entry (e, struct file_descriptor, elem); + next = list_next (e); ++ lock_acquire (&fs_lock); + file_close (fd->file); ++ lock_release (&fs_lock); + free (fd); + } +} -diff -urpN pintos.orig/src/userprog/syscall.h pintos/src/userprog/syscall.h ---- pintos.orig/src/userprog/syscall.h 2004-09-05 22:38:45.000000000 -0700 -+++ pintos/src/userprog/syscall.h 2004-09-27 16:41:17.000000000 -0700 +Index: src/userprog/syscall.h +diff -u src/userprog/syscall.h~ src/userprog/syscall.h +--- src/userprog/syscall.h~ ++++ src/userprog/syscall.h @@ -2,5 +2,6 @@ #define USERPROG_SYSCALL_H