X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fuserprog%2Fprocess.c;h=58e81fd173d36c5bb0282f5ef29a05cb71ef8cff;hb=615bf3b3d2a8573ed6fb9ddc0055745e163ac999;hp=08e78c683e0024af747573e9fb78e48d63aee4e2;hpb=f0612244c44f4b4f0bc79e3fc882e9f74bd4a3f4;p=pintos-anon diff --git a/src/userprog/process.c b/src/userprog/process.c index 08e78c6..58e81fd 100644 --- a/src/userprog/process.c +++ b/src/userprog/process.c @@ -21,8 +21,9 @@ static thread_func execute_thread NO_RETURN; static bool load (const char *cmdline, void (**eip) (void), void **esp); /* Starts a new thread running a user program loaded from - FILENAME. The new thread may be scheduled before - process_execute() returns.*/ + FILENAME. The new thread may be scheduled (and may even exit) + before process_execute() returns. Returns the new process's + thread id, or TID_ERROR if the thread cannot be created. */ tid_t process_execute (const char *filename) { @@ -31,7 +32,7 @@ process_execute (const char *filename) /* Make a copy of FILENAME. Otherwise there's a race between the caller and load(). */ - fn_copy = palloc_get (0); + fn_copy = palloc_get_page (0); if (fn_copy == NULL) return TID_ERROR; strlcpy (fn_copy, filename, PGSIZE); @@ -39,7 +40,7 @@ process_execute (const char *filename) /* Create a new thread to execute FILENAME. */ tid = thread_create (filename, PRI_DEFAULT, execute_thread, fn_copy); if (tid == TID_ERROR) - palloc_free (fn_copy); + palloc_free_page (fn_copy); return tid; } @@ -54,45 +55,63 @@ execute_thread (void *filename_) /* Initialize interrupt frame and load executable. */ memset (&if_, 0, sizeof if_); - if_.es = SEL_UDSEG; - if_.ds = SEL_UDSEG; + if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG; if_.cs = SEL_UCSEG; if_.eflags = FLAG_IF | FLAG_MBS; - if_.ss = SEL_UDSEG; success = load (filename, &if_.eip, &if_.esp); /* If load failed, quit. */ - palloc_free (filename); + palloc_free_page (filename); if (!success) thread_exit (); - /* Switch page tables. */ - process_activate (); - /* Start the user process by simulating a return from an interrupt, implemented by intr_exit (in - threads/intr-stubs.pl). Because intr_exit takes all of its + threads/intr-stubs.S). Because intr_exit takes all of its arguments on the stack in the form of a `struct intr_frame', we just point the stack pointer (%esp) to our stack frame and jump to it. */ - asm ("mov %0, %%esp\n" - "jmp intr_exit\n" - : /* no outputs */ - : "g" (&if_)); + asm ("mov %%esp, %0; jmp intr_exit" :: "g" (&if_)); NOT_REACHED (); } -/* Destroys the user address space in T and frees all of its - resources. */ +/* 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. */ +int +process_wait (tid_t child_tid UNUSED) +{ + return -1; +} + +/* Free the current process's resources. */ void -process_destroy (struct thread *t) +process_exit (void) { - ASSERT (t != thread_current ()); + struct thread *cur = thread_current (); + uint32_t *pd; - if (t->pagedir != NULL) + /* Destroy the current process's page directory and switch back + to the kernel-only page directory. */ + pd = cur->pagedir; + if (pd != NULL) { - pagedir_destroy (t->pagedir); - t->pagedir = NULL; + /* Correct ordering here is crucial. We must set + cur->pagedir to NULL before switching page directories, + so that a timer interrupt can't switch back to the + process page directory. We must activate the base page + directory before destroying the process's page + directory, or our active page directory will be one + that's been freed (and cleared). */ + cur->pagedir = NULL; + pagedir_activate (NULL); + pagedir_destroy (pd); } } @@ -103,10 +122,11 @@ process_activate (void) { struct thread *t = thread_current (); - /* Activate T's page tables. */ + /* Activate thread's page tables. */ pagedir_activate (t->pagedir); - /* Set T's kernel stack for use in processing interrupts. */ + /* Set thread's kernel stack for use in processing + interrupts. */ tss_set_esp0 ((uint8_t *) t + PGSIZE); } @@ -176,15 +196,6 @@ struct Elf32_Phdr static bool load_segment (struct file *, const struct Elf32_Phdr *); static bool setup_stack (void **esp); -/* Aborts loading an executable, with an error message. */ -#define LOAD_ERROR(MSG) \ - do { \ - printf ("load: %s: ", filename); \ - printf MSG; \ - printf ("\n"); \ - goto done; \ - } while (0) - /* Loads an ELF executable from FILENAME into the current thread. Stores the executable's entry point into *EIP and its initial stack pointer into *ESP. @@ -199,32 +210,32 @@ load (const char *filename, void (**eip) (void), void **esp) bool success = false; int i; - /* Allocate page directory. */ + /* Allocate and activate page directory. */ t->pagedir = pagedir_create (); - if (t->pagedir == NULL) - LOAD_ERROR (("page directory allocation failed")); + if (t->pagedir == NULL) + goto done; + process_activate (); /* Open executable file. */ file = filesys_open (filename); - if (file == NULL) - LOAD_ERROR (("open failed")); + if (file == NULL) + { + printf ("load: %s: open failed\n", filename); + goto done; + } /* Read and verify executable header. */ - if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr) - LOAD_ERROR (("error reading executable header")); - if (memcmp (ehdr.e_ident, "\177ELF\1\1\1", 7) != 0) - LOAD_ERROR (("file is not ELF")); - if (ehdr.e_type != 2) - LOAD_ERROR (("ELF file is not an executable")); - if (ehdr.e_machine != 3) - LOAD_ERROR (("ELF executable is not x86")); - if (ehdr.e_version != 1) - LOAD_ERROR (("ELF executable hasunknown version %d", - (int) ehdr.e_version)); - if (ehdr.e_phentsize != sizeof (struct Elf32_Phdr)) - LOAD_ERROR (("bad ELF program header size")); - if (ehdr.e_phnum > 1024) - LOAD_ERROR (("too many ELF program headers")); + if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr + || memcmp (ehdr.e_ident, "\177ELF\1\1\1", 7) + || ehdr.e_type != 2 + || ehdr.e_machine != 3 + || ehdr.e_version != 1 + || ehdr.e_phentsize != sizeof (struct Elf32_Phdr) + || ehdr.e_phnum > 1024) + { + printf ("load: %s: error loading executable\n", filename); + goto done; + } /* Read program headers. */ file_ofs = ehdr.e_phoff; @@ -232,9 +243,12 @@ load (const char *filename, void (**eip) (void), void **esp) { struct Elf32_Phdr phdr; + if (file_ofs < 0 || file_ofs > file_length (file)) + goto done; file_seek (file, file_ofs); + if (file_read (file, &phdr, sizeof phdr) != sizeof phdr) - LOAD_ERROR (("error reading program header")); + goto done; file_ofs += sizeof phdr; switch (phdr.p_type) { @@ -242,17 +256,13 @@ load (const char *filename, void (**eip) (void), void **esp) case PT_NOTE: case PT_PHDR: case PT_STACK: + default: /* Ignore this segment. */ break; case PT_DYNAMIC: case PT_INTERP: case PT_SHLIB: - /* Reject the executable. */ - LOAD_ERROR (("unsupported ELF segment type %d\n", phdr.p_type)); - break; - default: - printf ("unknown ELF segment type %08x\n", phdr.p_type); - break; + goto done; case PT_LOAD: if (!load_segment (file, &phdr)) goto done; @@ -302,33 +312,25 @@ load_segment (struct file *file, const struct Elf32_Phdr *phdr) /* [ELF1] 2-2 says that p_offset and p_vaddr must be congruent modulo PGSIZE. */ if (phdr->p_offset % PGSIZE != phdr->p_vaddr % PGSIZE) - { - printf ("%#08"PE32Ox" and %#08"PE32Ax" not congruent modulo %#x\n", - phdr->p_offset, phdr->p_vaddr, (unsigned) PGSIZE); - return false; - } + return false; + + /* p_offset must point within file. */ + if (phdr->p_offset > (Elf32_Off) file_length (file)) + return false; /* [ELF1] 2-3 says that p_memsz must be at least as big as p_filesz. */ if (phdr->p_memsz < phdr->p_filesz) - { - printf ("p_memsz (%08"PE32Wx") < p_filesz (%08"PE32Wx")\n", - phdr->p_memsz, phdr->p_filesz); - return false; - } + return false; /* Validate virtual memory region to be mapped. The region must both start and end within the user address - space range starting at 0 and ending at PHYS_BASE (typically - 3 GB == 0xc0000000). */ + space range. We don't allow mapping page 0.*/ start = pg_round_down ((void *) phdr->p_vaddr); end = pg_round_up ((void *) (phdr->p_vaddr + phdr->p_memsz)); - if (start >= PHYS_BASE || end >= PHYS_BASE || end < start) - { - printf ("bad virtual region %08lx...%08lx\n", - (unsigned long) start, (unsigned long) end); - return false; - } + if (!is_user_vaddr (start) || !is_user_vaddr (end) || end < start + || start == 0) + return false; /* Load the segment page-by-page into memory. */ filesz_left = phdr->p_filesz + (phdr->p_vaddr & PGMASK); @@ -339,14 +341,14 @@ load_segment (struct file *file, const struct Elf32_Phdr *phdr) file into the page and zero the rest. */ size_t read_bytes = filesz_left >= PGSIZE ? PGSIZE : filesz_left; size_t zero_bytes = PGSIZE - read_bytes; - uint8_t *kpage = palloc_get (PAL_USER); + uint8_t *kpage = palloc_get_page (PAL_USER); if (kpage == NULL) return false; /* Do the reading and zeroing. */ if (file_read (file, kpage, read_bytes) != (int) read_bytes) { - palloc_free (kpage); + palloc_free_page (kpage); return false; } memset (kpage + read_bytes, 0, zero_bytes); @@ -355,7 +357,7 @@ load_segment (struct file *file, const struct Elf32_Phdr *phdr) /* Add the page to the process's address space. */ if (!install_page (upage, kpage)) { - palloc_free (kpage); + palloc_free_page (kpage); return false; } } @@ -371,18 +373,15 @@ setup_stack (void **esp) uint8_t *kpage; bool success = false; - kpage = palloc_get (PAL_USER | PAL_ZERO); + kpage = palloc_get_page (PAL_USER | PAL_ZERO); if (kpage != NULL) { success = install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage); if (success) *esp = PHYS_BASE; else - palloc_free (kpage); + palloc_free_page (kpage); } - else - printf ("failed to allocate process stack\n"); - return success; }