summaryrefslogtreecommitdiffstats
path: root/pintos-progos/userprog/process.c
diff options
context:
space:
mode:
Diffstat (limited to 'pintos-progos/userprog/process.c')
-rw-r--r--pintos-progos/userprog/process.c721
1 files changed, 721 insertions, 0 deletions
diff --git a/pintos-progos/userprog/process.c b/pintos-progos/userprog/process.c
new file mode 100644
index 0000000..adb6b66
--- /dev/null
+++ b/pintos-progos/userprog/process.c
@@ -0,0 +1,721 @@
1#include "userprog/process.h"
2#include <debug.h>
3#include <inttypes.h>
4#include <round.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include "userprog/gdt.h"
9#include "userprog/pagedir.h"
10#include "userprog/tss.h"
11#include "filesys/directory.h"
12#include "filesys/file.h"
13#include "filesys/filesys.h"
14#include "threads/flags.h"
15#include "threads/init.h"
16#include "threads/interrupt.h"
17#include "threads/palloc.h"
18#include "threads/synch.h"
19#include "threads/thread.h"
20#include "threads/vaddr.h"
21
22/* data structure to communicate with the thread initializing a new process */
23struct start_aux_data {
24 char *filename;
25 struct semaphore startup_sem;
26 struct thread *parent_thread;
27 struct process *new_process;
28};
29
30/* filesystem lock */
31struct lock filesys_lock;
32
33/* prototypes */
34static thread_func start_process NO_RETURN;
35static bool load (char *filename, void (**eip) (void), void **esp);
36static bool setup_stack (void **esp);
37static bool init_fd_table (struct fd_table * table);
38
39/* Initialize the filesystem lock */
40void
41process_init ()
42{
43 lock_init (&filesys_lock);
44}
45
46/* Get current process (only valid for processes) */
47struct process*
48process_current ()
49{
50 ASSERT (thread_current()->process != NULL);
51 return thread_current()->process;
52}
53
54/* Starts a new thread running a user program loaded from
55 `filename`.
56 The new thread may be scheduled (and may even exit)
57 before process_execute() returns. Returns the new process's
58 thread id, or TID_ERROR if the thread cannot be created.
59
60 In the first assignment, you should change this to function to
61
62 process_execute (const char *cmd)
63
64 and support command strings such as "echo A B C". You
65 will also need to change `load` and `setup_stack`. */
66tid_t
67process_execute (const char *filename)
68{
69 tid_t tid = TID_ERROR;
70 char *fn_copy = NULL;
71 struct start_aux_data *aux_data = NULL;
72
73 /* Setup the auxiliary data for starting up the new process */
74 fn_copy = palloc_get_page (0);
75 aux_data = palloc_get_page (0);
76 if (aux_data == NULL || fn_copy == NULL)
77 goto done;
78 strlcpy (fn_copy, filename, PGSIZE);
79 aux_data->filename = fn_copy;
80 aux_data->parent_thread = thread_current ();
81 aux_data->new_process = NULL;
82 sema_init (&aux_data->startup_sem, 0);
83
84 /* Create a new thread to execute FILE_NAME. */
85 tid = thread_create (fn_copy, PRI_DEFAULT, start_process, aux_data);
86 if (tid == TID_ERROR)
87 goto done;
88
89 /* wait for startup */
90 sema_down (&aux_data->startup_sem);
91 if (aux_data->new_process == NULL) {
92 tid = TID_ERROR;
93 goto done;
94 }
95 /* register child process */
96 list_push_back (&thread_current()->children,
97 &aux_data->new_process->parentelem);
98
99 done:
100 palloc_free_page (fn_copy);
101 palloc_free_page (aux_data);
102 return tid;
103}
104
105/* A thread function that loads a user process and starts it
106 running. */
107static void
108start_process (void *aux)
109{
110 struct intr_frame if_;
111 struct start_aux_data *aux_data = (struct start_aux_data*) aux;
112 struct thread *thread = thread_current ();
113
114 /* Initialize Process */
115 struct process *process = palloc_get_page (PAL_ZERO);
116 if (process == NULL)
117 goto signal;
118 sema_init (&process->exit_sem, 0);
119 lock_init (&process->exit_lock);
120 process->exit_status = -1;
121 if (! init_fd_table (&process->fd_table))
122 goto signal;
123
124 /* register process */
125 process->thread_id = thread->tid;
126 process->parent_tid = aux_data->parent_thread->tid;
127 thread->process = process;
128
129 /* Initialize interrupt frame and load executable. */
130 memset (&if_, 0, sizeof if_);
131 if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
132 if_.cs = SEL_UCSEG;
133 if_.eflags = FLAG_IF | FLAG_MBS;
134 if (! load (aux_data->filename, &if_.eip, &if_.esp)) {
135 thread->process = NULL;
136 } else {
137 aux_data->new_process = thread->process;
138 }
139
140 signal:
141 /* Signal the parent process that loading is finished */
142 sema_up (&aux_data->startup_sem); /* transfer ownership of aux_data */
143
144 /* If process startup failed, quit. */
145 if (thread->process == NULL) {
146 if (process != NULL) {
147 if (process->fd_table.fds != NULL)
148 palloc_free_page (process->fd_table.fds);
149 palloc_free_page (process);
150 }
151 thread_exit ();
152 }
153
154 /* Start the user process by simulating a return from an
155 interrupt, implemented by intr_exit (in
156 threads/intr-stubs.S). Because intr_exit takes all of its
157 arguments on the stack in the form of a `struct intr_frame',
158 we just point the stack pointer (%esp) to our stack frame
159 and jump to it. */
160 asm volatile ("movl %0, %%esp; jmp intr_exit" : : "g" (&if_) : "memory");
161 NOT_REACHED ();
162}
163
164/* Waits for thread TID to die and returns its exit status. If
165 it was terminated by the kernel (i.e. killed due to an
166 exception), returns -1. If TID is invalid or if it was not a
167 child of the calling process, or if process_wait() has already
168 been successfully called for the given TID, returns -1
169 immediately, without waiting. */
170int
171process_wait (tid_t child_tid)
172{
173 struct thread *cur = thread_current ();
174 struct process *child = NULL;
175
176 /* iterate over child processes */
177 struct list_elem *e = list_head (&cur->children);
178 while ((e = list_next (e)) != list_end (&cur->children)) {
179 struct process* t = list_entry (e, struct process, parentelem);
180 if (t->thread_id == child_tid) {
181 list_remove (e);
182 child = t;
183 break;
184 }
185 }
186 if (child == NULL) {
187 return -1;
188 }
189 sema_down (&child->exit_sem);
190 int exit_status = child->exit_status;
191 palloc_free_page (child);
192 return exit_status;
193}
194
195/* Free the current process's resources. */
196void
197process_exit (void)
198{
199 struct thread *thread = thread_current ();
200 ASSERT (thread != NULL);
201
202 /* remove (and if necessary clean up) child processes */
203 struct list_elem *e = list_head (&thread->children);
204 while ((e = list_next (e)) != list_end (&thread->children)) {
205 struct process *p = list_entry (e, struct process, parentelem);
206 bool process_dying;
207 lock_acquire (&p->exit_lock);
208 process_dying = p->parent_tid < 0;
209 p->parent_tid = -1;
210 lock_release (&p->exit_lock);
211 if (process_dying)
212 palloc_free_page (p);
213 }
214
215 if (thread->process == NULL)
216 return; /* not a process, nothing else left to do */
217
218 struct process *proc = thread->process;
219 uint32_t *pd;
220
221 printf ("%s: exit(%d)\n", thread->name, proc->exit_status);
222
223 /* close executable, allow write */
224 if (proc->executable != NULL) {
225 lock_acquire (&filesys_lock);
226 file_close (proc->executable);
227 lock_release (&filesys_lock);
228 }
229
230 int fd;
231 for (fd = 2; fd <= proc->fd_table.fd_max; fd++) {
232 process_close_file (fd);
233 }
234 palloc_free_page (proc->fd_table.fds);
235
236 /* Destroy the current process's page directory and switch back
237 to the kernel-only page directory. */
238 pd = thread->pagedir;
239 if (pd != NULL) {
240 /* Correct ordering here is crucial. We must set
241 cur->pagedir to NULL before switching page directories,
242 so that a timer interrupt can't switch back to the
243 process page directory. We must activate the base page
244 directory before destroying the process's page
245 directory, or our active page directory will be one
246 that's been freed (and cleared). */
247 thread->pagedir = NULL;
248 pagedir_activate (NULL);
249 pagedir_destroy (pd);
250 }
251
252 /* Destroy the process structure if the parent is not alive
253 * any more. Atomic test and set would be sufficient here.
254 */
255 bool parent_dead = false;
256 lock_acquire (&proc->exit_lock);
257 parent_dead = proc->parent_tid < 0;
258 proc->parent_tid = -1;
259 lock_release (&proc->exit_lock);
260 if (parent_dead) {
261 palloc_free_page (proc);
262 } else {
263 sema_up (&proc->exit_sem);
264 }
265}
266
267/* Sets up the CPU for running user code in the current
268 thread.
269 This function is called on every context switch. */
270void
271process_activate (void)
272{
273 struct thread *t = thread_current ();
274 /* Activate thread's page tables. */
275 pagedir_activate (t->pagedir);
276
277 /* Set thread's kernel stack for use in processing
278 interrupts. */
279 tss_update ();
280}
281
282/* We load ELF binaries. The following definitions are taken
283 from the ELF specification, [ELF1], more-or-less verbatim. */
284
285/* ELF types. See [ELF1] 1-2. */
286typedef uint32_t Elf32_Word, Elf32_Addr, Elf32_Off;
287typedef uint16_t Elf32_Half;
288
289/* For use with ELF types in printf(). */
290#define PE32Wx PRIx32 /* Print Elf32_Word in hexadecimal. */
291#define PE32Ax PRIx32 /* Print Elf32_Addr in hexadecimal. */
292#define PE32Ox PRIx32 /* Print Elf32_Off in hexadecimal. */
293#define PE32Hx PRIx16 /* Print Elf32_Half in hexadecimal. */
294
295/* Executable header. See [ELF1] 1-4 to 1-8.
296 This appears at the very beginning of an ELF binary. */
297struct Elf32_Ehdr
298{
299 unsigned char e_ident[16];
300 Elf32_Half e_type;
301 Elf32_Half e_machine;
302 Elf32_Word e_version;
303 Elf32_Addr e_entry;
304 Elf32_Off e_phoff;
305 Elf32_Off e_shoff;
306 Elf32_Word e_flags;
307 Elf32_Half e_ehsize;
308 Elf32_Half e_phentsize;
309 Elf32_Half e_phnum;
310 Elf32_Half e_shentsize;
311 Elf32_Half e_shnum;
312 Elf32_Half e_shstrndx;
313};
314
315/* Program header. See [ELF1] 2-2 to 2-4.
316 There are e_phnum of these, starting at file offset e_phoff
317 (see [ELF1] 1-6). */
318struct Elf32_Phdr
319{
320 Elf32_Word p_type;
321 Elf32_Off p_offset;
322 Elf32_Addr p_vaddr;
323 Elf32_Addr p_paddr;
324 Elf32_Word p_filesz;
325 Elf32_Word p_memsz;
326 Elf32_Word p_flags;
327 Elf32_Word p_align;
328};
329
330/* Values for p_type. See [ELF1] 2-3. */
331#define PT_NULL 0 /* Ignore. */
332#define PT_LOAD 1 /* Loadable segment. */
333#define PT_DYNAMIC 2 /* Dynamic linking info. */
334#define PT_INTERP 3 /* Name of dynamic loader. */
335#define PT_NOTE 4 /* Auxiliary info. */
336#define PT_SHLIB 5 /* Reserved. */
337#define PT_PHDR 6 /* Program header table. */
338#define PT_STACK 0x6474e551 /* Stack segment. */
339
340/* Flags for p_flags. See [ELF3] 2-3 and 2-4. */
341#define PF_X 1 /* Executable. */
342#define PF_W 2 /* Writable. */
343#define PF_R 4 /* Readable. */
344
345static bool validate_segment (const struct Elf32_Phdr *, struct file *);
346static bool load_segment (struct file *file, off_t ofs, uint8_t *upage,
347 uint32_t read_bytes, uint32_t zero_bytes,
348 bool writable);
349
350/* Loads an ELF executable from file_name (the first word of
351 cmd) into the current thread.
352 Stores the executable's entry point into *EIP
353 and its initial stack pointer into *ESP.
354 Returns true if successful, false otherwise. */
355bool
356load (char *file_name, void (**eip) (void), void **esp)
357{
358 struct thread *t = thread_current ();
359 struct Elf32_Ehdr ehdr;
360 struct file *file = NULL;
361 off_t file_ofs;
362 bool success = false;
363 int i;
364
365 /* Allocate and activate page directory. */
366 t->pagedir = pagedir_create ();
367 if (t->pagedir == NULL)
368 return false;
369 process_activate ();
370
371 /* Coarse grained filesystem lock for loading */
372 lock_acquire (&filesys_lock);
373
374 /* Open executable file. */
375 file = filesys_open (file_name);
376 if (file == NULL)
377 goto done;
378
379 /* Deny writes to the file during loading */
380 file_deny_write (file);
381
382 /* Read and verify executable header. */
383 if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr
384 || memcmp (ehdr.e_ident, "\177ELF\1\1\1", 7)
385 || ehdr.e_type != 2
386 || ehdr.e_machine != 3
387 || ehdr.e_version != 1
388 || ehdr.e_phentsize != sizeof (struct Elf32_Phdr)
389 || ehdr.e_phnum > 1024)
390 {
391 printf ("load: %s: error loading executable\n", file_name);
392 goto done;
393 }
394
395 /* Read program headers. */
396 file_ofs = ehdr.e_phoff;
397 for (i = 0; i < ehdr.e_phnum; i++)
398 {
399 struct Elf32_Phdr phdr;
400
401 if (file_ofs < 0 || file_ofs > file_length (file))
402 goto done;
403 file_seek (file, file_ofs);
404
405 if (file_read (file, &phdr, sizeof phdr) != sizeof phdr)
406 goto done;
407 file_ofs += sizeof phdr;
408 if (phdr.p_vaddr < PGSIZE)
409 continue; /* Ignore build-id segment */
410 switch (phdr.p_type)
411 {
412 case PT_NULL:
413 case PT_NOTE:
414 case PT_PHDR:
415 case PT_STACK:
416 default:
417 /* Ignore this segment. */
418 break;
419 case PT_DYNAMIC:
420 case PT_INTERP:
421 case PT_SHLIB:
422 goto done;
423 case PT_LOAD:
424 if (phdr.p_vaddr == 0)
425 break; // Ignore the .note.gnu.build-i segment
426 if (validate_segment (&phdr, file))
427 {
428 bool writable = (phdr.p_flags & PF_W) != 0;
429 uint32_t file_page = phdr.p_offset & ~PGMASK;
430 uint32_t mem_page = phdr.p_vaddr & ~PGMASK;
431 uint32_t page_offset = phdr.p_vaddr & PGMASK;
432 uint32_t read_bytes, zero_bytes;
433 if (phdr.p_filesz > 0)
434 {
435 /* Normal segment.
436 Read initial part from disk and zero the rest. */
437 read_bytes = page_offset + phdr.p_filesz;
438 zero_bytes = (ROUND_UP (page_offset + phdr.p_memsz, PGSIZE)
439 - read_bytes);
440 }
441 else
442 {
443 /* Entirely zero.
444 Don't read anything from disk. */
445 read_bytes = 0;
446 zero_bytes = ROUND_UP (page_offset + phdr.p_memsz, PGSIZE);
447 }
448 if (!load_segment (file, file_page, (void *) mem_page,
449 read_bytes, zero_bytes, writable))
450 goto done;
451 }
452 else
453 goto done;
454 break;
455 }
456 }
457
458 /* Set up stack. */
459 if (!setup_stack (esp))
460 goto done;
461
462 /* Start address. */
463 *eip = (void (*) (void)) ehdr.e_entry;
464
465 success = true;
466
467 done:
468 /* We arrive here whether the load is successful or not. */
469 if (success) {
470 process_current()->executable = file;
471 } else {
472 file_close (file);
473 }
474 lock_release (&filesys_lock);
475 return success;
476}
477
478/* load() helpers. */
479
480static bool install_page (void *upage, void *kpage, bool writable);
481
482/* Checks whether PHDR describes a valid, loadable segment in
483 FILE and returns true if so, false otherwise. */
484static bool
485validate_segment (const struct Elf32_Phdr *phdr, struct file *file)
486{
487 /* p_offset and p_vaddr must have the same page offset. */
488 if ((phdr->p_offset & PGMASK) != (phdr->p_vaddr & PGMASK))
489 return false;
490
491 /* p_offset must point within FILE. */
492 if (phdr->p_offset > (Elf32_Off) file_length (file))
493 return false;
494
495 /* p_memsz must be at least as big as p_filesz. */
496 if (phdr->p_memsz < phdr->p_filesz)
497 return false;
498
499 /* The segment must not be empty. */
500 if (phdr->p_memsz == 0)
501 return false;
502
503 /* The virtual memory region must both start and end within the
504 user address space range. */
505 if (!is_user_vaddr ((void *) phdr->p_vaddr))
506 return false;
507 if (!is_user_vaddr ((void *) (phdr->p_vaddr + phdr->p_memsz)))
508 return false;
509
510 /* The region cannot "wrap around" across the kernel virtual
511 address space. */
512 if (phdr->p_vaddr + phdr->p_memsz < phdr->p_vaddr)
513 return false;
514
515 /* Disallow mapping page 0.
516 Not only is it a bad idea to map page 0, but if we allowed
517 it then user code that passed a null pointer to system calls
518 could quite likely panic the kernel by way of null pointer
519 assertions in memcpy(), etc. */
520 if (phdr->p_vaddr < PGSIZE)
521 return false;
522
523 /* It's okay. */
524 return true;
525}
526
527/* Loads a segment starting at offset OFS in FILE at address
528 UPAGE. In total, READ_BYTES + ZERO_BYTES bytes of virtual
529 memory are initialized, as follows:
530
531 - READ_BYTES bytes at UPAGE must be read from FILE
532 starting at offset OFS.
533
534 - ZERO_BYTES bytes at UPAGE + READ_BYTES must be zeroed.
535
536 The pages initialized by this function must be writable by the
537 user process if WRITABLE is true, read-only otherwise.
538
539 Return true if successful, false if a memory allocation error
540 or disk read error occurs. */
541static bool
542load_segment (struct file *file, off_t ofs, uint8_t *upage,
543 uint32_t read_bytes, uint32_t zero_bytes, bool writable)
544{
545 ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0);
546 ASSERT (pg_ofs (upage) == 0);
547 ASSERT (ofs % PGSIZE == 0);
548
549 file_seek (file, ofs);
550 while (read_bytes > 0 || zero_bytes > 0)
551 {
552 /* Calculate how to fill this page.
553 We will read PAGE_READ_BYTES bytes from FILE
554 and zero the final PAGE_ZERO_BYTES bytes. */
555 size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
556 size_t page_zero_bytes = PGSIZE - page_read_bytes;
557
558 /* Get a page of memory. */
559 uint8_t *kpage = palloc_get_page (PAL_USER);
560 if (kpage == NULL)
561 return false;
562
563 /* Load this page. */
564 if (file_read (file, kpage, page_read_bytes) != (int) page_read_bytes)
565 {
566 palloc_free_page (kpage);
567 return false;
568 }
569 memset (kpage + page_read_bytes, 0, page_zero_bytes);
570
571 /* Add the page to the process's address space. */
572 if (!install_page (upage, kpage, writable))
573 {
574 palloc_free_page (kpage);
575 return false;
576 }
577
578 /* Advance. */
579 read_bytes -= page_read_bytes;
580 zero_bytes -= page_zero_bytes;
581 upage += PGSIZE;
582 }
583 return true;
584}
585
586/* Create a minimal stack by mapping a zeroed page at the top of
587 user virtual memory.
588 You will implement this function in the Project 0.
589 Consider using `hex_dump` for debugging purposes */
590static bool
591setup_stack (void **esp)
592{
593 uint8_t *kpage = NULL;
594
595 kpage = palloc_get_page (PAL_USER | PAL_ZERO);
596 if (kpage == NULL)
597 return false;
598
599 if (! install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage, true)) {
600 palloc_free_page (kpage);
601 return false;
602 }
603
604 /* Currently we assume that 'argc = 0' */
605 *esp = PHYS_BASE - 12;
606
607 return true;
608}
609
610/* Adds a mapping from user virtual address UPAGE to kernel
611 virtual address KPAGE to the page table.
612 If WRITABLE is true, the user process may modify the page;
613 otherwise, it is read-only.
614 UPAGE must not already be mapped.
615 KPAGE should probably be a page obtained from the user pool
616 with palloc_get_page().
617 Returns true on success, false if UPAGE is already mapped or
618 if memory allocation fails. */
619static bool
620install_page (void *upage, void *kpage, bool writable)
621{
622 struct thread *t = thread_current ();
623
624 /* Verify that there's not already a page at that virtual
625 address, then map our page there. */
626 return (pagedir_get_page (t->pagedir, upage) == NULL
627 && pagedir_set_page (t->pagedir, upage, kpage, writable));
628}
629
630static
631bool
632init_fd_table (struct fd_table *table)
633{
634 table->fds = palloc_get_page (PAL_ZERO);
635 if (table->fds == NULL)
636 return false;
637 table->fd_cap = PGSIZE / sizeof (table->fds[0]);
638 table->fd_free = 2;
639 table->fd_max = 1;
640 return true;
641}
642
643/* Open the file with the given name; returns
644 a file descriptor for this file if successful,
645 and a negative value otherwise */
646int
647process_open_file (const char* fname)
648{
649 struct fd_table *fdt = &process_current()->fd_table;
650 if (fdt->fd_free >= fdt->fd_cap)
651 return -1;
652
653 lock_acquire (&filesys_lock);
654 struct file *f = filesys_open (fname);
655 lock_release (&filesys_lock);
656
657 if (f == NULL)
658 return -1;
659
660 int fd = fdt->fd_free++;
661 fdt->fds[fd] = f;
662
663 /* update index of free/max file descriptor index*/
664 if (fd > fdt->fd_max) fdt->fd_max = fd;
665 while (fdt->fds[fdt->fd_free] != NULL) {
666 fdt->fd_free++;
667 if (fdt->fd_free >= fdt->fd_cap)
668 break;
669 }
670 return fd;
671}
672
673/* Get the file associated with the given file
674 descriptor; return NULL if no file is associated
675 with the given descriptor */
676struct file*
677process_get_file (int fd)
678{
679 struct fd_table *fdt = &process_current()->fd_table;
680 if (fd < 2 || fd >= fdt->fd_cap || ! fdt->fds[fd])
681 return NULL;
682 return fdt->fds[fd];
683}
684
685/* Acquire global lock for the filesystem */
686void process_lock_filesys (void)
687{
688 lock_acquire (&filesys_lock);
689}
690
691/* Release global filesystem lock */
692void process_unlock_filesys (void)
693{
694 lock_release (&filesys_lock);
695}
696
697/* Close the file associated with the given file
698 descriptor; returns true if close was successful */
699bool
700process_close_file (int fd)
701{
702 struct file *file = process_get_file (fd);
703 if (file == NULL)
704 return false;
705
706 lock_acquire (&filesys_lock);
707 file_close (file);
708 lock_release (&filesys_lock);
709
710 struct fd_table *fdt = &process_current()->fd_table;
711 fdt->fds[fd] = NULL;
712
713 /* update index of free/max file descriptor index*/
714 if (fd < fdt->fd_free) fdt->fd_free = fd;
715 while (fdt->fds[fdt->fd_max] == NULL) {
716 fdt->fd_max--;
717 if (fdt->fd_max < 2)
718 break;
719 }
720 return true;
721}