diff options
| author | manuel <manuel@mausz.at> | 2012-03-27 11:51:08 +0200 |
|---|---|---|
| committer | manuel <manuel@mausz.at> | 2012-03-27 11:51:08 +0200 |
| commit | 4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b (patch) | |
| tree | 868c52e06f207b5ec8a3cc141f4b8b2bdfcc165c /userprog | |
| parent | eae0bd57f0a26314a94785061888d193d186944a (diff) | |
| download | progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.tar.gz progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.tar.bz2 progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.zip | |
reorganize file structure to match the upstream requirements
Diffstat (limited to 'userprog')
| -rw-r--r-- | userprog/.gitignore | 3 | ||||
| -rw-r--r-- | userprog/Make.vars | 7 | ||||
| -rw-r--r-- | userprog/Makefile | 1 | ||||
| -rw-r--r-- | userprog/exception.c | 174 | ||||
| -rw-r--r-- | userprog/exception.h | 12 | ||||
| -rw-r--r-- | userprog/gdt.c | 146 | ||||
| -rw-r--r-- | userprog/gdt.h | 15 | ||||
| -rw-r--r-- | userprog/pagedir.c | 263 | ||||
| -rw-r--r-- | userprog/pagedir.h | 18 | ||||
| -rw-r--r-- | userprog/process.c | 721 | ||||
| -rw-r--r-- | userprog/process.h | 47 | ||||
| -rw-r--r-- | userprog/syscall.c | 563 | ||||
| -rw-r--r-- | userprog/syscall.h | 5 | ||||
| -rw-r--r-- | userprog/tss.c | 106 | ||||
| -rw-r--r-- | userprog/tss.h | 11 |
15 files changed, 2092 insertions, 0 deletions
diff --git a/userprog/.gitignore b/userprog/.gitignore new file mode 100644 index 0000000..6d5357c --- /dev/null +++ b/userprog/.gitignore | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | build | ||
| 2 | bochsrc.txt | ||
| 3 | bochsout.txt | ||
diff --git a/userprog/Make.vars b/userprog/Make.vars new file mode 100644 index 0000000..e4dbb08 --- /dev/null +++ b/userprog/Make.vars | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | kernel.bin: DEFINES = -DUSERPROG -DFILESYS | ||
| 4 | KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys | ||
| 5 | TEST_SUBDIRS = tests/userprog tests/userprog/no-vm tests/filesys/base | ||
| 6 | GRADING_FILE = $(SRCDIR)/tests/userprog/Grading | ||
| 7 | SIMULATOR = --qemu | ||
diff --git a/userprog/Makefile b/userprog/Makefile new file mode 100644 index 0000000..34c10aa --- /dev/null +++ b/userprog/Makefile | |||
| @@ -0,0 +1 @@ | |||
| include ../Makefile.kernel | |||
diff --git a/userprog/exception.c b/userprog/exception.c new file mode 100644 index 0000000..17620ad --- /dev/null +++ b/userprog/exception.c | |||
| @@ -0,0 +1,174 @@ | |||
| 1 | #include "userprog/exception.h" | ||
| 2 | #include <inttypes.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | #include "userprog/gdt.h" | ||
| 5 | #include "threads/interrupt.h" | ||
| 6 | #include "threads/thread.h" | ||
| 7 | #include "threads/vaddr.h" | ||
| 8 | |||
| 9 | /* Number of page faults processed. */ | ||
| 10 | static long long page_fault_cnt; | ||
| 11 | |||
| 12 | static void kill (struct intr_frame *); | ||
| 13 | static void page_fault (struct intr_frame *); | ||
| 14 | |||
| 15 | /* Registers handlers for interrupts that can be caused by user | ||
| 16 | programs. | ||
| 17 | |||
| 18 | In a real Unix-like OS, most of these interrupts would be | ||
| 19 | passed along to the user process in the form of signals, as | ||
| 20 | described in [SV-386] 3-24 and 3-25, but we don't implement | ||
| 21 | signals. Instead, we'll make them simply kill the user | ||
| 22 | process. | ||
| 23 | |||
| 24 | Page faults are an exception. Here they are treated the same | ||
| 25 | way as other exceptions, but this will need to change to | ||
| 26 | implement virtual memory. | ||
| 27 | |||
| 28 | Refer to [IA32-v3a] section 5.15 "Exception and Interrupt | ||
| 29 | Reference" for a description of each of these exceptions. */ | ||
| 30 | void | ||
| 31 | exception_init (void) | ||
| 32 | { | ||
| 33 | /* These exceptions can be raised explicitly by a user program, | ||
| 34 | e.g. via the INT, INT3, INTO, and BOUND instructions. Thus, | ||
| 35 | we set DPL==3, meaning that user programs are allowed to | ||
| 36 | invoke them via these instructions. */ | ||
| 37 | intr_register_int (3, 3, INTR_ON, kill, "#BP Breakpoint Exception"); | ||
| 38 | intr_register_int (4, 3, INTR_ON, kill, "#OF Overflow Exception"); | ||
| 39 | intr_register_int (5, 3, INTR_ON, kill, | ||
| 40 | "#BR BOUND Range Exceeded Exception"); | ||
| 41 | |||
| 42 | /* These exceptions have DPL==0, preventing user processes from | ||
| 43 | invoking them via the INT instruction. They can still be | ||
| 44 | caused indirectly, e.g. #DE can be caused by dividing by | ||
| 45 | 0. */ | ||
| 46 | intr_register_int (0, 0, INTR_ON, kill, "#DE Divide Error"); | ||
| 47 | intr_register_int (1, 0, INTR_ON, kill, "#DB Debug Exception"); | ||
| 48 | intr_register_int (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception"); | ||
| 49 | intr_register_int (7, 0, INTR_ON, kill, | ||
| 50 | "#NM Device Not Available Exception"); | ||
| 51 | intr_register_int (11, 0, INTR_ON, kill, "#NP Segment Not Present"); | ||
| 52 | intr_register_int (12, 0, INTR_ON, kill, "#SS Stack Fault Exception"); | ||
| 53 | intr_register_int (13, 0, INTR_ON, kill, "#GP General Protection Exception"); | ||
| 54 | intr_register_int (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error"); | ||
| 55 | intr_register_int (19, 0, INTR_ON, kill, | ||
| 56 | "#XF SIMD Floating-Point Exception"); | ||
| 57 | |||
| 58 | /* Most exceptions can be handled with interrupts turned on. | ||
| 59 | We need to disable interrupts for page faults because the | ||
| 60 | fault address is stored in CR2 and needs to be preserved. */ | ||
| 61 | intr_register_int (14, 0, INTR_OFF, page_fault, "#PF Page-Fault Exception"); | ||
| 62 | } | ||
| 63 | |||
| 64 | /* Prints exception statistics. */ | ||
| 65 | void | ||
| 66 | exception_print_stats (void) | ||
| 67 | { | ||
| 68 | printf ("Exception: %lld page faults\n", page_fault_cnt); | ||
| 69 | } | ||
| 70 | |||
| 71 | /* Handler for an exception (probably) caused by a user process. */ | ||
| 72 | static void | ||
| 73 | kill (struct intr_frame *f) | ||
| 74 | { | ||
| 75 | /* This interrupt is one (probably) caused by a user process. | ||
| 76 | For example, the process might have tried to access unmapped | ||
| 77 | virtual memory (a page fault). For now, we simply kill the | ||
| 78 | user process. Later, we'll want to handle page faults in | ||
| 79 | the kernel. Real Unix-like operating systems pass most | ||
| 80 | exceptions back to the process via signals, but we don't | ||
| 81 | implement them. */ | ||
| 82 | |||
| 83 | /* The interrupt frame's code segment value tells us where the | ||
| 84 | exception originated. */ | ||
| 85 | switch (f->cs) | ||
| 86 | { | ||
| 87 | case SEL_UCSEG: | ||
| 88 | /* User's code segment, so it's a user exception, as we | ||
| 89 | expected. Kill the user process. */ | ||
| 90 | printf ("%s: dying due to interrupt %#04x (%s).\n", | ||
| 91 | thread_name (), f->vec_no, intr_name (f->vec_no)); | ||
| 92 | intr_dump_frame (f); | ||
| 93 | thread_exit (); | ||
| 94 | |||
| 95 | case SEL_KCSEG: | ||
| 96 | /* Kernel's code segment, which indicates a kernel bug. | ||
| 97 | Kernel code shouldn't throw exceptions. (Page faults | ||
| 98 | may cause kernel exceptions--but they shouldn't arrive | ||
| 99 | here.) Panic the kernel to make the point. */ | ||
| 100 | intr_dump_frame (f); | ||
| 101 | PANIC ("Kernel bug - unexpected interrupt in kernel"); | ||
| 102 | |||
| 103 | default: | ||
| 104 | /* Some other code segment? Shouldn't happen. Panic the | ||
| 105 | kernel. */ | ||
| 106 | printf ("Interrupt %#04x (%s) in unknown segment %04x\n", | ||
| 107 | f->vec_no, intr_name (f->vec_no), f->cs); | ||
| 108 | thread_exit (); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | /* Page fault handler. This is a skeleton that must be filled in | ||
| 113 | to implement virtual memory. Some solutions to project 2 may | ||
| 114 | also require modifying this code. | ||
| 115 | |||
| 116 | At entry, the address that faulted is in CR2 (Control Register | ||
| 117 | 2) and information about the fault, formatted as described in | ||
| 118 | the PF_* macros in exception.h, is in F's error_code member. The | ||
| 119 | example code here shows how to parse that information. You | ||
| 120 | can find more information about both of these in the | ||
| 121 | description of "Interrupt 14--Page Fault Exception (#PF)" in | ||
| 122 | [IA32-v3a] section 5.15 "Exception and Interrupt Reference". */ | ||
| 123 | static void | ||
| 124 | page_fault (struct intr_frame *f) | ||
| 125 | { | ||
| 126 | bool not_present; /* True: not-present page, false: writing r/o page. */ | ||
| 127 | bool write; /* True: access was write, false: access was read. */ | ||
| 128 | bool user; /* True: access by user, false: access by kernel. */ | ||
| 129 | void *fault_addr; /* Fault address. */ | ||
| 130 | |||
| 131 | /* Obtain faulting address, the virtual address that was | ||
| 132 | accessed to cause the fault. It may point to code or to | ||
| 133 | data. It is not necessarily the address of the instruction | ||
| 134 | that caused the fault (that's f->eip). | ||
| 135 | See [IA32-v2a] "MOV--Move to/from Control Registers" and | ||
| 136 | [IA32-v3a] 5.15 "Interrupt 14--Page Fault Exception | ||
| 137 | (#PF)". */ | ||
| 138 | asm ("movl %%cr2, %0" : "=r" (fault_addr)); | ||
| 139 | |||
| 140 | /* Turn interrupts back on (they were only off so that we could | ||
| 141 | be assured of reading CR2 before it changed). */ | ||
| 142 | intr_enable (); | ||
| 143 | |||
| 144 | /* Count page faults. */ | ||
| 145 | page_fault_cnt++; | ||
| 146 | |||
| 147 | /* Determine cause. */ | ||
| 148 | not_present = (f->error_code & PF_P) == 0; | ||
| 149 | write = (f->error_code & PF_W) != 0; | ||
| 150 | user = (f->error_code & PF_U) != 0; | ||
| 151 | |||
| 152 | /* To implement virtual memory, adapt the rest of the function | ||
| 153 | body, adding code that brings in the page to | ||
| 154 | which fault_addr refers. */ | ||
| 155 | if (is_user_vaddr(fault_addr)) { | ||
| 156 | if (! user) { | ||
| 157 | /* syscall exception; set eax and eip */ | ||
| 158 | f->eip = (void*)f->eax; | ||
| 159 | f->eax = 0xFFFFFFFF; | ||
| 160 | return; | ||
| 161 | } else { | ||
| 162 | /* user process access violation */ | ||
| 163 | thread_exit(); | ||
| 164 | } | ||
| 165 | } else { | ||
| 166 | printf ("Page fault at %p: %s error %s page in %s context.\n", | ||
| 167 | fault_addr, | ||
| 168 | not_present ? "not present" : "rights violation", | ||
| 169 | write ? "writing" : "reading", | ||
| 170 | user ? "user" : "kernel"); | ||
| 171 | kill (f); | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
diff --git a/userprog/exception.h b/userprog/exception.h new file mode 100644 index 0000000..f83e615 --- /dev/null +++ b/userprog/exception.h | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | #ifndef USERPROG_EXCEPTION_H | ||
| 2 | #define USERPROG_EXCEPTION_H | ||
| 3 | |||
| 4 | /* Page fault error code bits that describe the cause of the exception. */ | ||
| 5 | #define PF_P 0x1 /* 0: not-present page. 1: access rights violation. */ | ||
| 6 | #define PF_W 0x2 /* 0: read, 1: write. */ | ||
| 7 | #define PF_U 0x4 /* 0: kernel, 1: user process. */ | ||
| 8 | |||
| 9 | void exception_init (void); | ||
| 10 | void exception_print_stats (void); | ||
| 11 | |||
| 12 | #endif /* userprog/exception.h */ | ||
diff --git a/userprog/gdt.c b/userprog/gdt.c new file mode 100644 index 0000000..e866037 --- /dev/null +++ b/userprog/gdt.c | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | #include "userprog/gdt.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include "userprog/tss.h" | ||
| 4 | #include "threads/palloc.h" | ||
| 5 | #include "threads/vaddr.h" | ||
| 6 | |||
| 7 | /* The Global Descriptor Table (GDT). | ||
| 8 | |||
| 9 | The GDT, an x86-specific structure, defines segments that can | ||
| 10 | potentially be used by all processes in a system, subject to | ||
| 11 | their permissions. There is also a per-process Local | ||
| 12 | Descriptor Table (LDT) but that is not used by modern | ||
| 13 | operating systems. | ||
| 14 | |||
| 15 | Each entry in the GDT, which is known by its byte offset in | ||
| 16 | the table, identifies a segment. For our purposes only three | ||
| 17 | types of segments are of interest: code, data, and TSS or | ||
| 18 | Task-State Segment descriptors. The former two types are | ||
| 19 | exactly what they sound like. The TSS is used primarily for | ||
| 20 | stack switching on interrupts. | ||
| 21 | |||
| 22 | For more information on the GDT as used here, refer to | ||
| 23 | [IA32-v3a] 3.2 "Using Segments" through 3.5 "System Descriptor | ||
| 24 | Types". */ | ||
| 25 | static uint64_t gdt[SEL_CNT]; | ||
| 26 | |||
| 27 | /* GDT helpers. */ | ||
| 28 | static uint64_t make_code_desc (int dpl); | ||
| 29 | static uint64_t make_data_desc (int dpl); | ||
| 30 | static uint64_t make_tss_desc (void *laddr); | ||
| 31 | static uint64_t make_gdtr_operand (uint16_t limit, void *base); | ||
| 32 | |||
| 33 | /* Sets up a proper GDT. The bootstrap loader's GDT didn't | ||
| 34 | include user-mode selectors or a TSS, but we need both now. */ | ||
| 35 | void | ||
| 36 | gdt_init (void) | ||
| 37 | { | ||
| 38 | uint64_t gdtr_operand; | ||
| 39 | |||
| 40 | /* Initialize GDT. */ | ||
| 41 | gdt[SEL_NULL / sizeof *gdt] = 0; | ||
| 42 | gdt[SEL_KCSEG / sizeof *gdt] = make_code_desc (0); | ||
| 43 | gdt[SEL_KDSEG / sizeof *gdt] = make_data_desc (0); | ||
| 44 | gdt[SEL_UCSEG / sizeof *gdt] = make_code_desc (3); | ||
| 45 | gdt[SEL_UDSEG / sizeof *gdt] = make_data_desc (3); | ||
| 46 | gdt[SEL_TSS / sizeof *gdt] = make_tss_desc (tss_get ()); | ||
| 47 | |||
| 48 | /* Load GDTR, TR. See [IA32-v3a] 2.4.1 "Global Descriptor | ||
| 49 | Table Register (GDTR)", 2.4.4 "Task Register (TR)", and | ||
| 50 | 6.2.4 "Task Register". */ | ||
| 51 | gdtr_operand = make_gdtr_operand (sizeof gdt - 1, gdt); | ||
| 52 | asm volatile ("lgdt %0" : : "m" (gdtr_operand)); | ||
| 53 | asm volatile ("ltr %w0" : : "q" (SEL_TSS)); | ||
| 54 | } | ||
| 55 | |||
| 56 | /* System segment or code/data segment? */ | ||
| 57 | enum seg_class | ||
| 58 | { | ||
| 59 | CLS_SYSTEM = 0, /* System segment. */ | ||
| 60 | CLS_CODE_DATA = 1 /* Code or data segment. */ | ||
| 61 | }; | ||
| 62 | |||
| 63 | /* Limit has byte or 4 kB page granularity? */ | ||
| 64 | enum seg_granularity | ||
| 65 | { | ||
| 66 | GRAN_BYTE = 0, /* Limit has 1-byte granularity. */ | ||
| 67 | GRAN_PAGE = 1 /* Limit has 4 kB granularity. */ | ||
| 68 | }; | ||
| 69 | |||
| 70 | /* Returns a segment descriptor with the given 32-bit BASE and | ||
| 71 | 20-bit LIMIT (whose interpretation depends on GRANULARITY). | ||
| 72 | The descriptor represents a system or code/data segment | ||
| 73 | according to CLASS, and TYPE is its type (whose interpretation | ||
| 74 | depends on the class). | ||
| 75 | |||
| 76 | The segment has descriptor privilege level DPL, meaning that | ||
| 77 | it can be used in rings numbered DPL or lower. In practice, | ||
| 78 | DPL==3 means that user processes can use the segment and | ||
| 79 | DPL==0 means that only the kernel can use the segment. See | ||
| 80 | [IA32-v3a] 4.5 "Privilege Levels" for further discussion. */ | ||
| 81 | static uint64_t | ||
| 82 | make_seg_desc (uint32_t base, | ||
| 83 | uint32_t limit, | ||
| 84 | enum seg_class class, | ||
| 85 | int type, | ||
| 86 | int dpl, | ||
| 87 | enum seg_granularity granularity) | ||
| 88 | { | ||
| 89 | uint32_t e0, e1; | ||
| 90 | |||
| 91 | ASSERT (limit <= 0xfffff); | ||
| 92 | ASSERT (class == CLS_SYSTEM || class == CLS_CODE_DATA); | ||
| 93 | ASSERT (type >= 0 && type <= 15); | ||
| 94 | ASSERT (dpl >= 0 && dpl <= 3); | ||
| 95 | ASSERT (granularity == GRAN_BYTE || granularity == GRAN_PAGE); | ||
| 96 | |||
| 97 | e0 = ((limit & 0xffff) /* Limit 15:0. */ | ||
| 98 | | (base << 16)); /* Base 15:0. */ | ||
| 99 | |||
| 100 | e1 = (((base >> 16) & 0xff) /* Base 23:16. */ | ||
| 101 | | (type << 8) /* Segment type. */ | ||
| 102 | | (class << 12) /* 0=system, 1=code/data. */ | ||
| 103 | | (dpl << 13) /* Descriptor privilege. */ | ||
| 104 | | (1 << 15) /* Present. */ | ||
| 105 | | (limit & 0xf0000) /* Limit 16:19. */ | ||
| 106 | | (1 << 22) /* 32-bit segment. */ | ||
| 107 | | (granularity << 23) /* Byte/page granularity. */ | ||
| 108 | | (base & 0xff000000)); /* Base 31:24. */ | ||
| 109 | |||
| 110 | return e0 | ((uint64_t) e1 << 32); | ||
| 111 | } | ||
| 112 | |||
| 113 | /* Returns a descriptor for a readable code segment with base at | ||
| 114 | 0, a limit of 4 GB, and the given DPL. */ | ||
| 115 | static uint64_t | ||
| 116 | make_code_desc (int dpl) | ||
| 117 | { | ||
| 118 | return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 10, dpl, GRAN_PAGE); | ||
| 119 | } | ||
| 120 | |||
| 121 | /* Returns a descriptor for a writable data segment with base at | ||
| 122 | 0, a limit of 4 GB, and the given DPL. */ | ||
| 123 | static uint64_t | ||
| 124 | make_data_desc (int dpl) | ||
| 125 | { | ||
| 126 | return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 2, dpl, GRAN_PAGE); | ||
| 127 | } | ||
| 128 | |||
| 129 | /* Returns a descriptor for an "available" 32-bit Task-State | ||
| 130 | Segment with its base at the given linear address, a limit of | ||
| 131 | 0x67 bytes (the size of a 32-bit TSS), and a DPL of 0. | ||
| 132 | See [IA32-v3a] 6.2.2 "TSS Descriptor". */ | ||
| 133 | static uint64_t | ||
| 134 | make_tss_desc (void *laddr) | ||
| 135 | { | ||
| 136 | return make_seg_desc ((uint32_t) laddr, 0x67, CLS_SYSTEM, 9, 0, GRAN_BYTE); | ||
| 137 | } | ||
| 138 | |||
| 139 | |||
| 140 | /* Returns a descriptor that yields the given LIMIT and BASE when | ||
| 141 | used as an operand for the LGDT instruction. */ | ||
| 142 | static uint64_t | ||
| 143 | make_gdtr_operand (uint16_t limit, void *base) | ||
| 144 | { | ||
| 145 | return limit | ((uint64_t) (uint32_t) base << 16); | ||
| 146 | } | ||
diff --git a/userprog/gdt.h b/userprog/gdt.h new file mode 100644 index 0000000..81fe50c --- /dev/null +++ b/userprog/gdt.h | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #ifndef USERPROG_GDT_H | ||
| 2 | #define USERPROG_GDT_H | ||
| 3 | |||
| 4 | #include "threads/loader.h" | ||
| 5 | |||
| 6 | /* Segment selectors. | ||
| 7 | More selectors are defined by the loader in loader.h. */ | ||
| 8 | #define SEL_UCSEG 0x1B /* User code selector. */ | ||
| 9 | #define SEL_UDSEG 0x23 /* User data selector. */ | ||
| 10 | #define SEL_TSS 0x28 /* Task-state segment. */ | ||
| 11 | #define SEL_CNT 6 /* Number of segments. */ | ||
| 12 | |||
| 13 | void gdt_init (void); | ||
| 14 | |||
| 15 | #endif /* userprog/gdt.h */ | ||
diff --git a/userprog/pagedir.c b/userprog/pagedir.c new file mode 100644 index 0000000..a6a87b8 --- /dev/null +++ b/userprog/pagedir.c | |||
| @@ -0,0 +1,263 @@ | |||
| 1 | #include "userprog/pagedir.h" | ||
| 2 | #include <stdbool.h> | ||
| 3 | #include <stddef.h> | ||
| 4 | #include <string.h> | ||
| 5 | #include "threads/init.h" | ||
| 6 | #include "threads/pte.h" | ||
| 7 | #include "threads/palloc.h" | ||
| 8 | |||
| 9 | static uint32_t *active_pd (void); | ||
| 10 | static void invalidate_pagedir (uint32_t *); | ||
| 11 | |||
| 12 | /* Creates a new page directory that has mappings for kernel | ||
| 13 | virtual addresses, but none for user virtual addresses. | ||
| 14 | Returns the new page directory, or a null pointer if memory | ||
| 15 | allocation fails. */ | ||
| 16 | uint32_t * | ||
| 17 | pagedir_create (void) | ||
| 18 | { | ||
| 19 | uint32_t *pd = palloc_get_page (0); | ||
| 20 | if (pd != NULL) | ||
| 21 | memcpy (pd, init_page_dir, PGSIZE); | ||
| 22 | return pd; | ||
| 23 | } | ||
| 24 | |||
| 25 | /* Destroys page directory PD, freeing all the pages it | ||
| 26 | references. */ | ||
| 27 | void | ||
| 28 | pagedir_destroy (uint32_t *pd) | ||
| 29 | { | ||
| 30 | uint32_t *pde; | ||
| 31 | |||
| 32 | if (pd == NULL) | ||
| 33 | return; | ||
| 34 | |||
| 35 | ASSERT (pd != init_page_dir); | ||
| 36 | for (pde = pd; pde < pd + pd_no (PHYS_BASE); pde++) | ||
| 37 | if (*pde & PTE_P) | ||
| 38 | { | ||
| 39 | uint32_t *pt = pde_get_pt (*pde); | ||
| 40 | uint32_t *pte; | ||
| 41 | |||
| 42 | for (pte = pt; pte < pt + PGSIZE / sizeof *pte; pte++) | ||
| 43 | if (*pte & PTE_P) | ||
| 44 | palloc_free_page (pte_get_page (*pte)); | ||
| 45 | palloc_free_page (pt); | ||
| 46 | } | ||
| 47 | palloc_free_page (pd); | ||
| 48 | } | ||
| 49 | |||
| 50 | /* Returns the address of the page table entry for virtual | ||
| 51 | address VADDR in page directory PD. | ||
| 52 | If PD does not have a page table for VADDR, behavior depends | ||
| 53 | on CREATE. If CREATE is true, then a new page table is | ||
| 54 | created and a pointer into it is returned. Otherwise, a null | ||
| 55 | pointer is returned. */ | ||
| 56 | static uint32_t * | ||
| 57 | lookup_page (uint32_t *pd, const void *vaddr, bool create) | ||
| 58 | { | ||
| 59 | uint32_t *pt, *pde; | ||
| 60 | |||
| 61 | ASSERT (pd != NULL); | ||
| 62 | |||
| 63 | /* Shouldn't create new kernel virtual mappings. */ | ||
| 64 | ASSERT (!create || is_user_vaddr (vaddr)); | ||
| 65 | |||
| 66 | /* Check for a page table for VADDR. | ||
| 67 | If one is missing, create one if requested. */ | ||
| 68 | pde = pd + pd_no (vaddr); | ||
| 69 | if (*pde == 0) | ||
| 70 | { | ||
| 71 | if (create) | ||
| 72 | { | ||
| 73 | pt = palloc_get_page (PAL_ZERO); | ||
| 74 | if (pt == NULL) | ||
| 75 | return NULL; | ||
| 76 | |||
| 77 | *pde = pde_create (pt); | ||
| 78 | } | ||
| 79 | else | ||
| 80 | return NULL; | ||
| 81 | } | ||
| 82 | |||
| 83 | /* Return the page table entry. */ | ||
| 84 | pt = pde_get_pt (*pde); | ||
| 85 | return &pt[pt_no (vaddr)]; | ||
| 86 | } | ||
| 87 | |||
| 88 | /* Adds a mapping in page directory PD from user virtual page | ||
| 89 | UPAGE to the physical frame identified by kernel virtual | ||
| 90 | address KPAGE. | ||
| 91 | UPAGE must not already be mapped. | ||
| 92 | KPAGE should probably be a page obtained from the user pool | ||
| 93 | with palloc_get_page(). | ||
| 94 | If WRITABLE is true, the new page is read/write; | ||
| 95 | otherwise it is read-only. | ||
| 96 | Returns true if successful, false if memory allocation | ||
| 97 | failed. */ | ||
| 98 | bool | ||
| 99 | pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool writable) | ||
| 100 | { | ||
| 101 | uint32_t *pte; | ||
| 102 | |||
| 103 | ASSERT (pg_ofs (upage) == 0); | ||
| 104 | ASSERT (pg_ofs (kpage) == 0); | ||
| 105 | ASSERT (is_user_vaddr (upage)); | ||
| 106 | ASSERT (vtop (kpage) >> PTSHIFT < init_ram_pages); | ||
| 107 | ASSERT (pd != init_page_dir); | ||
| 108 | |||
| 109 | pte = lookup_page (pd, upage, true); | ||
| 110 | |||
| 111 | if (pte != NULL) | ||
| 112 | { | ||
| 113 | ASSERT ((*pte & PTE_P) == 0); | ||
| 114 | *pte = pte_create_user (kpage, writable); | ||
| 115 | return true; | ||
| 116 | } | ||
| 117 | else | ||
| 118 | return false; | ||
| 119 | } | ||
| 120 | |||
| 121 | /* Looks up the physical address that corresponds to user virtual | ||
| 122 | address UADDR in PD. Returns the kernel virtual address | ||
| 123 | corresponding to that physical address, or a null pointer if | ||
| 124 | UADDR is unmapped. */ | ||
| 125 | void * | ||
| 126 | pagedir_get_page (uint32_t *pd, const void *uaddr) | ||
| 127 | { | ||
| 128 | uint32_t *pte; | ||
| 129 | |||
| 130 | ASSERT (is_user_vaddr (uaddr)); | ||
| 131 | |||
| 132 | pte = lookup_page (pd, uaddr, false); | ||
| 133 | if (pte != NULL && (*pte & PTE_P) != 0) | ||
| 134 | return pte_get_page (*pte) + pg_ofs (uaddr); | ||
| 135 | else | ||
| 136 | return NULL; | ||
| 137 | } | ||
| 138 | |||
| 139 | /* Marks user virtual page UPAGE "not present" in page | ||
| 140 | directory PD. Later accesses to the page will fault. Other | ||
| 141 | bits in the page table entry are preserved. | ||
| 142 | UPAGE need not be mapped. */ | ||
| 143 | void | ||
| 144 | pagedir_clear_page (uint32_t *pd, void *upage) | ||
| 145 | { | ||
| 146 | uint32_t *pte; | ||
| 147 | |||
| 148 | ASSERT (pg_ofs (upage) == 0); | ||
| 149 | ASSERT (is_user_vaddr (upage)); | ||
| 150 | |||
| 151 | pte = lookup_page (pd, upage, false); | ||
| 152 | if (pte != NULL && (*pte & PTE_P) != 0) | ||
| 153 | { | ||
| 154 | *pte &= ~PTE_P; | ||
| 155 | invalidate_pagedir (pd); | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | /* Returns true if the PTE for virtual page VPAGE in PD is dirty, | ||
| 160 | that is, if the page has been modified since the PTE was | ||
| 161 | installed. | ||
| 162 | Returns false if PD contains no PTE for VPAGE. */ | ||
| 163 | bool | ||
| 164 | pagedir_is_dirty (uint32_t *pd, const void *vpage) | ||
| 165 | { | ||
| 166 | uint32_t *pte = lookup_page (pd, vpage, false); | ||
| 167 | return pte != NULL && (*pte & PTE_D) != 0; | ||
| 168 | } | ||
| 169 | |||
| 170 | /* Set the dirty bit to DIRTY in the PTE for virtual page VPAGE | ||
| 171 | in PD. */ | ||
| 172 | void | ||
| 173 | pagedir_set_dirty (uint32_t *pd, const void *vpage, bool dirty) | ||
| 174 | { | ||
| 175 | uint32_t *pte = lookup_page (pd, vpage, false); | ||
| 176 | if (pte != NULL) | ||
| 177 | { | ||
| 178 | if (dirty) | ||
| 179 | *pte |= PTE_D; | ||
| 180 | else | ||
| 181 | { | ||
| 182 | *pte &= ~(uint32_t) PTE_D; | ||
| 183 | invalidate_pagedir (pd); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | /* Returns true if the PTE for virtual page VPAGE in PD has been | ||
| 189 | accessed recently, that is, between the time the PTE was | ||
| 190 | installed and the last time it was cleared. Returns false if | ||
| 191 | PD contains no PTE for VPAGE. */ | ||
| 192 | bool | ||
| 193 | pagedir_is_accessed (uint32_t *pd, const void *vpage) | ||
| 194 | { | ||
| 195 | uint32_t *pte = lookup_page (pd, vpage, false); | ||
| 196 | return pte != NULL && (*pte & PTE_A) != 0; | ||
| 197 | } | ||
| 198 | |||
| 199 | /* Sets the accessed bit to ACCESSED in the PTE for virtual page | ||
| 200 | VPAGE in PD. */ | ||
| 201 | void | ||
| 202 | pagedir_set_accessed (uint32_t *pd, const void *vpage, bool accessed) | ||
| 203 | { | ||
| 204 | uint32_t *pte = lookup_page (pd, vpage, false); | ||
| 205 | if (pte != NULL) | ||
| 206 | { | ||
| 207 | if (accessed) | ||
| 208 | *pte |= PTE_A; | ||
| 209 | else | ||
| 210 | { | ||
| 211 | *pte &= ~(uint32_t) PTE_A; | ||
| 212 | invalidate_pagedir (pd); | ||
| 213 | } | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | /* Loads page directory PD into the CPU's page directory base | ||
| 218 | register. */ | ||
| 219 | void | ||
| 220 | pagedir_activate (uint32_t *pd) | ||
| 221 | { | ||
| 222 | if (pd == NULL) | ||
| 223 | pd = init_page_dir; | ||
| 224 | |||
| 225 | /* Store the physical address of the page directory into CR3 | ||
| 226 | aka PDBR (page directory base register). This activates our | ||
| 227 | new page tables immediately. See [IA32-v2a] "MOV--Move | ||
| 228 | to/from Control Registers" and [IA32-v3a] 3.7.5 "Base | ||
| 229 | Address of the Page Directory". */ | ||
| 230 | asm volatile ("movl %0, %%cr3" : : "r" (vtop (pd)) : "memory"); | ||
| 231 | } | ||
| 232 | |||
| 233 | /* Returns the currently active page directory. */ | ||
| 234 | static uint32_t * | ||
| 235 | active_pd (void) | ||
| 236 | { | ||
| 237 | /* Copy CR3, the page directory base register (PDBR), into | ||
| 238 | `pd'. | ||
| 239 | See [IA32-v2a] "MOV--Move to/from Control Registers" and | ||
| 240 | [IA32-v3a] 3.7.5 "Base Address of the Page Directory". */ | ||
| 241 | uintptr_t pd; | ||
| 242 | asm volatile ("movl %%cr3, %0" : "=r" (pd)); | ||
| 243 | return ptov (pd); | ||
| 244 | } | ||
| 245 | |||
| 246 | /* Seom page table changes can cause the CPU's translation | ||
| 247 | lookaside buffer (TLB) to become out-of-sync with the page | ||
| 248 | table. When this happens, we have to "invalidate" the TLB by | ||
| 249 | re-activating it. | ||
| 250 | |||
| 251 | This function invalidates the TLB if PD is the active page | ||
| 252 | directory. (If PD is not active then its entries are not in | ||
| 253 | the TLB, so there is no need to invalidate anything.) */ | ||
| 254 | static void | ||
| 255 | invalidate_pagedir (uint32_t *pd) | ||
| 256 | { | ||
| 257 | if (active_pd () == pd) | ||
| 258 | { | ||
| 259 | /* Re-activating PD clears the TLB. See [IA32-v3a] 3.12 | ||
| 260 | "Translation Lookaside Buffers (TLBs)". */ | ||
| 261 | pagedir_activate (pd); | ||
| 262 | } | ||
| 263 | } | ||
diff --git a/userprog/pagedir.h b/userprog/pagedir.h new file mode 100644 index 0000000..cd92447 --- /dev/null +++ b/userprog/pagedir.h | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | #ifndef USERPROG_PAGEDIR_H | ||
| 2 | #define USERPROG_PAGEDIR_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | |||
| 7 | uint32_t *pagedir_create (void); | ||
| 8 | void pagedir_destroy (uint32_t *pd); | ||
| 9 | bool pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool rw); | ||
| 10 | void *pagedir_get_page (uint32_t *pd, const void *upage); | ||
| 11 | void pagedir_clear_page (uint32_t *pd, void *upage); | ||
| 12 | bool pagedir_is_dirty (uint32_t *pd, const void *upage); | ||
| 13 | void pagedir_set_dirty (uint32_t *pd, const void *upage, bool dirty); | ||
| 14 | bool pagedir_is_accessed (uint32_t *pd, const void *upage); | ||
| 15 | void pagedir_set_accessed (uint32_t *pd, const void *upage, bool accessed); | ||
| 16 | void pagedir_activate (uint32_t *pd); | ||
| 17 | |||
| 18 | #endif /* userprog/pagedir.h */ | ||
diff --git a/userprog/process.c b/userprog/process.c new file mode 100644 index 0000000..adb6b66 --- /dev/null +++ b/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 */ | ||
| 23 | struct 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 */ | ||
| 31 | struct lock filesys_lock; | ||
| 32 | |||
| 33 | /* prototypes */ | ||
| 34 | static thread_func start_process NO_RETURN; | ||
| 35 | static bool load (char *filename, void (**eip) (void), void **esp); | ||
| 36 | static bool setup_stack (void **esp); | ||
| 37 | static bool init_fd_table (struct fd_table * table); | ||
| 38 | |||
| 39 | /* Initialize the filesystem lock */ | ||
| 40 | void | ||
| 41 | process_init () | ||
| 42 | { | ||
| 43 | lock_init (&filesys_lock); | ||
| 44 | } | ||
| 45 | |||
| 46 | /* Get current process (only valid for processes) */ | ||
| 47 | struct process* | ||
| 48 | process_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`. */ | ||
| 66 | tid_t | ||
| 67 | process_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. */ | ||
| 107 | static void | ||
| 108 | start_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. */ | ||
| 170 | int | ||
| 171 | process_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. */ | ||
| 196 | void | ||
| 197 | process_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. */ | ||
| 270 | void | ||
| 271 | process_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. */ | ||
| 286 | typedef uint32_t Elf32_Word, Elf32_Addr, Elf32_Off; | ||
| 287 | typedef 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. */ | ||
| 297 | struct 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). */ | ||
| 318 | struct 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 | |||
| 345 | static bool validate_segment (const struct Elf32_Phdr *, struct file *); | ||
| 346 | static 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. */ | ||
| 355 | bool | ||
| 356 | load (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 | |||
| 480 | static 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. */ | ||
| 484 | static bool | ||
| 485 | validate_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. */ | ||
| 541 | static bool | ||
| 542 | load_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 */ | ||
| 590 | static bool | ||
| 591 | setup_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. */ | ||
| 619 | static bool | ||
| 620 | install_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 | |||
| 630 | static | ||
| 631 | bool | ||
| 632 | init_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 */ | ||
| 646 | int | ||
| 647 | process_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 */ | ||
| 676 | struct file* | ||
| 677 | process_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 */ | ||
| 686 | void process_lock_filesys (void) | ||
| 687 | { | ||
| 688 | lock_acquire (&filesys_lock); | ||
| 689 | } | ||
| 690 | |||
| 691 | /* Release global filesystem lock */ | ||
| 692 | void 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 */ | ||
| 699 | bool | ||
| 700 | process_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 | } | ||
diff --git a/userprog/process.h b/userprog/process.h new file mode 100644 index 0000000..1609801 --- /dev/null +++ b/userprog/process.h | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | #ifndef USERPROG_PROCESS_H | ||
| 2 | #define USERPROG_PROCESS_H | ||
| 3 | |||
| 4 | #include "threads/thread.h" | ||
| 5 | |||
| 6 | /* In the current implementation, the capacity is fixed to 1024 (PGSIZE/4) */ | ||
| 7 | struct fd_table { | ||
| 8 | struct file** fds; | ||
| 9 | int fd_free; /* lowest-index free FD table entry */ | ||
| 10 | int fd_max; /* highest-index used FD table entry */ | ||
| 11 | int fd_cap; /* FD table capacity */ | ||
| 12 | }; | ||
| 13 | |||
| 14 | struct process { | ||
| 15 | /* process tree */ | ||
| 16 | tid_t thread_id; | ||
| 17 | tid_t parent_tid; | ||
| 18 | struct list_elem parentelem; /* Owned by parent */ | ||
| 19 | |||
| 20 | /* communication with parent process */ | ||
| 21 | struct semaphore exit_sem; | ||
| 22 | struct lock exit_lock; | ||
| 23 | int exit_status; | ||
| 24 | |||
| 25 | /* files */ | ||
| 26 | struct file *executable; /* Loaded executable, if any. */ | ||
| 27 | struct fd_table fd_table; /* File descriptor table */ | ||
| 28 | |||
| 29 | /* Owned by syscall.c */ | ||
| 30 | void* syscall_buffer; | ||
| 31 | size_t syscall_buffer_page_cnt; | ||
| 32 | }; | ||
| 33 | |||
| 34 | void process_init (void); | ||
| 35 | struct process* process_current (void); | ||
| 36 | tid_t process_execute (const char *file_name); | ||
| 37 | int process_wait (tid_t); | ||
| 38 | void process_exit (void); | ||
| 39 | void process_activate (void); | ||
| 40 | |||
| 41 | int process_open_file(const char* fname); | ||
| 42 | struct file* process_get_file(int fd); | ||
| 43 | void process_lock_filesys (void); | ||
| 44 | void process_unlock_filesys (void); | ||
| 45 | bool process_close_file(int fd); | ||
| 46 | |||
| 47 | #endif /* userprog/process.h */ | ||
diff --git a/userprog/syscall.c b/userprog/syscall.c new file mode 100644 index 0000000..f8e0197 --- /dev/null +++ b/userprog/syscall.c | |||
| @@ -0,0 +1,563 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <syscall-nr.h> | ||
| 3 | #include "devices/input.h" | ||
| 4 | #include "devices/shutdown.h" | ||
| 5 | #include "filesys/file.h" | ||
| 6 | #include "filesys/filesys.h" | ||
| 7 | #include "filesys/inode.h" | ||
| 8 | #include "lib/string.h" | ||
| 9 | #include "threads/interrupt.h" | ||
| 10 | #include "threads/palloc.h" | ||
| 11 | #include "threads/synch.h" | ||
| 12 | #include "threads/thread.h" | ||
| 13 | #include "threads/vaddr.h" | ||
| 14 | #include "userprog/pagedir.h" | ||
| 15 | #include "userprog/process.h" | ||
| 16 | #include "userprog/syscall.h" | ||
| 17 | |||
| 18 | #define STACK_SLOT_SIZE sizeof(int) | ||
| 19 | |||
| 20 | /* Prototypes for Utilities */ | ||
| 21 | static int get_user (const uint8_t *uaddr); | ||
| 22 | static bool put_user (uint8_t *udst, uint8_t byte); | ||
| 23 | static void* memcpy_from_user (void *kaddr, void *uaddr, size_t bytes); | ||
| 24 | static void* memcpy_to_user (void *kaddr, void *addr, size_t bytes); | ||
| 25 | static void* copy_string_arg (void *usp, bool *segfault); | ||
| 26 | static void free_string_arg_buf (void *kbuf); | ||
| 27 | |||
| 28 | /* Reads a byte at user virtual address UADDR. | ||
| 29 | UADDR must be below PHYS_BASE. | ||
| 30 | Returns the byte value if successful, -1 if a segfault | ||
| 31 | occurred. */ | ||
| 32 | static int | ||
| 33 | get_user (const uint8_t *uaddr) | ||
| 34 | { | ||
| 35 | int result; | ||
| 36 | asm ("movl $1f, %0; " /* save eip in eax */ | ||
| 37 | "movzbl %1, %0; " /* read byte from user memory into eax */ | ||
| 38 | "1:" /* continue here on page fault, with eax set to -1 */ | ||
| 39 | : "=&a" (result) : "m" (*uaddr)); | ||
| 40 | return result; | ||
| 41 | } | ||
| 42 | |||
| 43 | /* Writes BYTE to user address UDST. | ||
| 44 | UDST must be below PHYS_BASE. | ||
| 45 | Returns true if successful, false if a segfault occurred. */ | ||
| 46 | static bool | ||
| 47 | put_user (uint8_t *udst, uint8_t byte) | ||
| 48 | { | ||
| 49 | int error_code; | ||
| 50 | asm ("movl $1f, %0;" /* save EIP in EAX */ | ||
| 51 | "movb %b2, %1;" /* write byte to user memory */ | ||
| 52 | "1:" /* continue here on page fault, with eax set to -1 */ | ||
| 53 | : "=&a" (error_code), "=m" (*udst) : "q" (byte)); | ||
| 54 | return error_code != -1; | ||
| 55 | } | ||
| 56 | |||
| 57 | /* Copy bytes from user space; returns NULL if a segfault | ||
| 58 | occured, and kaddr otherwise */ | ||
| 59 | static void* | ||
| 60 | memcpy_from_user (void *kaddr, void *uaddr, size_t bytes) | ||
| 61 | { | ||
| 62 | uint8_t* kp = kaddr; | ||
| 63 | size_t i; | ||
| 64 | if (! is_user_vaddr (uaddr) || | ||
| 65 | ! is_user_vaddr (uaddr + bytes - 1)) | ||
| 66 | return false; | ||
| 67 | for (i=0;i<bytes;i++) { | ||
| 68 | int b = get_user (uaddr+i); | ||
| 69 | if (b < 0) | ||
| 70 | break; /* segfault */ | ||
| 71 | else | ||
| 72 | kp[i] = b; | ||
| 73 | } | ||
| 74 | if (i != bytes) | ||
| 75 | return NULL; /* segfault */ | ||
| 76 | return kaddr; | ||
| 77 | } | ||
| 78 | |||
| 79 | /* Copy bytes to user space; returns NULL if a segfault | ||
| 80 | occured, and uaddr otherwise */ | ||
| 81 | static void* | ||
| 82 | memcpy_to_user (void *uaddr, void *kaddr, size_t bytes) | ||
| 83 | { | ||
| 84 | uint8_t* kp = kaddr; | ||
| 85 | size_t i; | ||
| 86 | if (! is_user_vaddr (uaddr) || | ||
| 87 | ! is_user_vaddr (uaddr + bytes - 1)) | ||
| 88 | return false; | ||
| 89 | for (i=0;i<bytes;i++) { | ||
| 90 | if (! put_user (uaddr+i, kp[i])) | ||
| 91 | break; /* segfault */ | ||
| 92 | } | ||
| 93 | if (i != bytes) | ||
| 94 | return NULL; /* segfault */ | ||
| 95 | return uaddr; | ||
| 96 | } | ||
| 97 | |||
| 98 | /* Copy string (at most cnt bytes) from user memory to the kernel address | ||
| 99 | `kaddr`. The number of bytes copied is returned; 0 indicates a segfault. */ | ||
| 100 | static size_t | ||
| 101 | strncpy_from_user (void *kaddr, void *uaddr, size_t cnt) | ||
| 102 | { | ||
| 103 | uint8_t *kp = (uint8_t*) kaddr; | ||
| 104 | size_t i; | ||
| 105 | int c = 1; | ||
| 106 | for (i = 0; i < cnt && c; i++) { | ||
| 107 | if (! is_user_vaddr (uaddr+i)) | ||
| 108 | return 0; /* sefault */ | ||
| 109 | c = get_user (uaddr+i); | ||
| 110 | if (c < 0) | ||
| 111 | return 0; /* segfault */ | ||
| 112 | kp[i] = c; | ||
| 113 | } | ||
| 114 | return i; | ||
| 115 | } | ||
| 116 | |||
| 117 | /* Copy a value of scalar type `typeof(*kdst)` from user space pointer | ||
| 118 | `usrc` to kernel space variable pointed to by `kdst`. Returns | ||
| 119 | false on segfault, and true otherwise. */ | ||
| 120 | #define copy_from_user(kdst, usrc) \ | ||
| 121 | (memcpy_from_user (kdst, usrc, sizeof (*kdst)) != NULL) | ||
| 122 | |||
| 123 | /* Takes user space stack pointer, which points to a string | ||
| 124 | in user space. Copies that string (at most PGSIZE bytes) | ||
| 125 | to a freshly allocated buffer in kernel space. Reurns NULL on | ||
| 126 | error; on a segfault, additionally sets `segfault` to true */ | ||
| 127 | static void* | ||
| 128 | copy_string_arg (void *usp, bool *segfault) | ||
| 129 | { | ||
| 130 | size_t bytes_copied; | ||
| 131 | char *uptr; | ||
| 132 | char *kpage; | ||
| 133 | if (! copy_from_user (&uptr, usp)) { | ||
| 134 | *segfault = true; | ||
| 135 | return NULL; | ||
| 136 | } | ||
| 137 | kpage = palloc_get_page (PAL_ZERO); | ||
| 138 | if (kpage == NULL) | ||
| 139 | return NULL; | ||
| 140 | bytes_copied = strncpy_from_user (kpage, uptr, PGSIZE); | ||
| 141 | if (bytes_copied == 0) { | ||
| 142 | *segfault = true; | ||
| 143 | palloc_free_page (kpage); | ||
| 144 | return NULL; | ||
| 145 | } | ||
| 146 | if (kpage[bytes_copied-1] != '\0') { | ||
| 147 | palloc_free_page (kpage); | ||
| 148 | return NULL; | ||
| 149 | }; | ||
| 150 | return kpage; | ||
| 151 | } | ||
| 152 | |||
| 153 | /* free buffer allocated by `copy_string_arg` */ | ||
| 154 | static void free_string_arg_buf (void *kbuf) | ||
| 155 | { | ||
| 156 | palloc_free_page (kbuf); | ||
| 157 | } | ||
| 158 | |||
| 159 | /* Stack slot address */ | ||
| 160 | #define STACK_ADDR(sp, slot) (sp + STACK_SLOT_SIZE*slot) | ||
| 161 | |||
| 162 | /* syscall handler prototype */ | ||
| 163 | static void syscall_handler (struct intr_frame *); | ||
| 164 | |||
| 165 | typedef int (handler)(void *sp, bool *segfault); | ||
| 166 | |||
| 167 | /* Prototypes for syscall handlers */ | ||
| 168 | static handler | ||
| 169 | syscall_halt, | ||
| 170 | syscall_exit, | ||
| 171 | syscall_write, | ||
| 172 | syscall_wait , | ||
| 173 | syscall_exec, | ||
| 174 | syscall_create, | ||
| 175 | syscall_remove, | ||
| 176 | syscall_open, | ||
| 177 | syscall_filesize, | ||
| 178 | syscall_read, | ||
| 179 | syscall_seek, | ||
| 180 | syscall_tell, | ||
| 181 | syscall_close; | ||
| 182 | |||
| 183 | /* Register syscall_handler for interrupt 0x30 */ | ||
| 184 | void | ||
| 185 | syscall_init (void) | ||
| 186 | { | ||
| 187 | intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall"); | ||
| 188 | } | ||
| 189 | |||
| 190 | /* Syscall handler, delegating known syscalls to the respective implementations */ | ||
| 191 | static void | ||
| 192 | syscall_handler (struct intr_frame *f) | ||
| 193 | { | ||
| 194 | int syscall_nr; | ||
| 195 | handler* fp; | ||
| 196 | bool segfault = false; | ||
| 197 | int result; | ||
| 198 | void *sp = f->esp; | ||
| 199 | |||
| 200 | /* The system call number and the arguments are on the stack */ | ||
| 201 | if (! copy_from_user (&syscall_nr,sp)) | ||
| 202 | goto fail; | ||
| 203 | switch (syscall_nr) { | ||
| 204 | case SYS_HALT: fp = syscall_halt; break; | ||
| 205 | case SYS_EXIT: fp = syscall_exit; break; | ||
| 206 | case SYS_EXEC: fp = syscall_exec; break; | ||
| 207 | case SYS_WAIT: fp = syscall_wait; break; | ||
| 208 | case SYS_CREATE: fp = syscall_create; break; | ||
| 209 | case SYS_REMOVE: fp = syscall_remove; break; | ||
| 210 | case SYS_OPEN: fp = syscall_open; break; | ||
| 211 | case SYS_FILESIZE: fp = syscall_filesize; break; | ||
| 212 | case SYS_READ: fp = syscall_read; break; | ||
| 213 | case SYS_WRITE: fp = syscall_write; break; | ||
| 214 | case SYS_SEEK: fp = syscall_seek; break; | ||
| 215 | case SYS_TELL: fp = syscall_tell; break; | ||
| 216 | case SYS_CLOSE: fp = syscall_close; break; | ||
| 217 | default: | ||
| 218 | goto fail; | ||
| 219 | } | ||
| 220 | result = fp (sp, &segfault); | ||
| 221 | if (segfault) | ||
| 222 | goto fail; | ||
| 223 | f->eax = result; | ||
| 224 | return; | ||
| 225 | |||
| 226 | fail: | ||
| 227 | process_current()->exit_status = -1; | ||
| 228 | thread_exit (); | ||
| 229 | } | ||
| 230 | |||
| 231 | /* Shutdown machine */ | ||
| 232 | static int | ||
| 233 | syscall_halt (void *sp UNUSED, bool *segfault UNUSED) | ||
| 234 | { | ||
| 235 | shutdown (); | ||
| 236 | NOT_REACHED (); | ||
| 237 | } | ||
| 238 | |||
| 239 | /* Exit current process with given exit code */ | ||
| 240 | static int | ||
| 241 | syscall_exit (void *sp, bool *segfault) | ||
| 242 | { | ||
| 243 | int exit_status; | ||
| 244 | if (! copy_from_user (&exit_status, STACK_ADDR (sp,1))) { | ||
| 245 | *segfault = true; | ||
| 246 | return -1; | ||
| 247 | } | ||
| 248 | process_current()->exit_status = exit_status; | ||
| 249 | thread_exit (); | ||
| 250 | NOT_REACHED (); | ||
| 251 | } | ||
| 252 | |||
| 253 | /* Spawn new process executing the supplied command */ | ||
| 254 | static int | ||
| 255 | syscall_exec (void *sp, bool *segfault) | ||
| 256 | { | ||
| 257 | char *kbuf; | ||
| 258 | int result = TID_ERROR; | ||
| 259 | if ((kbuf = copy_string_arg (STACK_ADDR (sp, 1), segfault)) != NULL) { | ||
| 260 | result = process_execute (kbuf); | ||
| 261 | free_string_arg_buf (kbuf); | ||
| 262 | } | ||
| 263 | return result; | ||
| 264 | } | ||
| 265 | |||
| 266 | /* Wait until specified process exits */ | ||
| 267 | static int | ||
| 268 | syscall_wait (void *sp, bool *segfault) | ||
| 269 | { | ||
| 270 | tid_t arg; | ||
| 271 | if (! copy_from_user (&arg, STACK_ADDR (sp,1))) { | ||
| 272 | *segfault = true; | ||
| 273 | return 0; | ||
| 274 | } | ||
| 275 | return process_wait (arg); | ||
| 276 | } | ||
| 277 | |||
| 278 | /* Create a new file with given initial size */ | ||
| 279 | static int | ||
| 280 | syscall_create (void *sp, bool *segfault) | ||
| 281 | { | ||
| 282 | bool success = false; | ||
| 283 | char *fname; | ||
| 284 | int initial_size; | ||
| 285 | |||
| 286 | if (! copy_from_user (&initial_size, STACK_ADDR (sp,2))) { | ||
| 287 | *segfault = true; | ||
| 288 | return false; | ||
| 289 | } | ||
| 290 | if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL) | ||
| 291 | return false; | ||
| 292 | |||
| 293 | process_lock_filesys (); | ||
| 294 | success = filesys_create (fname, initial_size); | ||
| 295 | process_unlock_filesys (); | ||
| 296 | free_string_arg_buf (fname); | ||
| 297 | return success; | ||
| 298 | } | ||
| 299 | |||
| 300 | /* Remove name file, returns true if successful */ | ||
| 301 | static int | ||
| 302 | syscall_remove (void *sp, bool *segfault) | ||
| 303 | { | ||
| 304 | bool success; | ||
| 305 | char *fname; | ||
| 306 | |||
| 307 | if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL) | ||
| 308 | return false; | ||
| 309 | process_lock_filesys (); | ||
| 310 | success = filesys_remove (fname); | ||
| 311 | process_unlock_filesys (); | ||
| 312 | free_string_arg_buf (fname); | ||
| 313 | return (int)success; | ||
| 314 | } | ||
| 315 | |||
| 316 | /* Open file, returning non-negative file descriptor if successful */ | ||
| 317 | static int | ||
| 318 | syscall_open (void *sp, bool *segfault) | ||
| 319 | { | ||
| 320 | char *fname; | ||
| 321 | int fd; | ||
| 322 | if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL) | ||
| 323 | return false; | ||
| 324 | fd = process_open_file (fname); | ||
| 325 | free_string_arg_buf (fname); | ||
| 326 | return fd; | ||
| 327 | } | ||
| 328 | |||
| 329 | /* Return size of file described by file descriptor */ | ||
| 330 | static int | ||
| 331 | syscall_filesize (void *sp, bool *segfault) | ||
| 332 | { | ||
| 333 | int fd; | ||
| 334 | struct file *f; | ||
| 335 | int size; | ||
| 336 | |||
| 337 | if (! copy_from_user (&fd, STACK_ADDR (sp,1))) { | ||
| 338 | *segfault = true; | ||
| 339 | return -1; | ||
| 340 | } | ||
| 341 | if ((f = process_get_file (fd)) == NULL) | ||
| 342 | return -1; | ||
| 343 | process_lock_filesys (); | ||
| 344 | size = inode_length (file_get_inode (f)); | ||
| 345 | process_unlock_filesys (); | ||
| 346 | return size; | ||
| 347 | } | ||
| 348 | |||
| 349 | /* Read bytes from the file referenced by the given file | ||
| 350 | descriptor into the supplied user space buffer, returning | ||
| 351 | number of bytes read. */ | ||
| 352 | static int | ||
| 353 | syscall_read (void *sp, bool *segfault) | ||
| 354 | { | ||
| 355 | int fd; | ||
| 356 | uint8_t *user_buffer; | ||
| 357 | size_t size, bytes_to_read; | ||
| 358 | |||
| 359 | /* get arguments */ | ||
| 360 | if (! copy_from_user (&fd, STACK_ADDR (sp,1)) || | ||
| 361 | ! copy_from_user (&user_buffer, STACK_ADDR (sp, 2)) || | ||
| 362 | ! copy_from_user (&size, STACK_ADDR (sp,3))) { | ||
| 363 | *segfault = true; | ||
| 364 | return -1; | ||
| 365 | } | ||
| 366 | |||
| 367 | /* ensure buffer is in user space */ | ||
| 368 | if (! is_user_vaddr (user_buffer) || | ||
| 369 | ! is_user_vaddr (user_buffer + size - 1)) { | ||
| 370 | *segfault = true; | ||
| 371 | return -1; | ||
| 372 | } | ||
| 373 | |||
| 374 | bytes_to_read = size; | ||
| 375 | /* handle stdin */ | ||
| 376 | if (fd == STDIN_FILENO) { | ||
| 377 | char c; | ||
| 378 | while (bytes_to_read--) { | ||
| 379 | c = input_getc (); | ||
| 380 | if (! put_user (user_buffer++, c)) { | ||
| 381 | *segfault = true; | ||
| 382 | return -1; | ||
| 383 | } | ||
| 384 | } | ||
| 385 | return size; | ||
| 386 | } | ||
| 387 | /* get file */ | ||
| 388 | struct file *file = process_get_file (fd); | ||
| 389 | if (file == NULL) | ||
| 390 | return -1; | ||
| 391 | |||
| 392 | char *kbuf = palloc_get_page (0); | ||
| 393 | if (kbuf == NULL) | ||
| 394 | return -1; | ||
| 395 | |||
| 396 | /* read loop */ | ||
| 397 | do { | ||
| 398 | int bytes_read; | ||
| 399 | int blocksize = bytes_to_read; | ||
| 400 | if (bytes_to_read > PGSIZE) | ||
| 401 | blocksize = PGSIZE; | ||
| 402 | |||
| 403 | /* read bytes */ | ||
| 404 | process_lock_filesys (); | ||
| 405 | bytes_read = file_read (file, kbuf, blocksize); | ||
| 406 | process_unlock_filesys (); | ||
| 407 | |||
| 408 | /* Stop when EOF has been reached */ | ||
| 409 | if (bytes_read == 0) | ||
| 410 | break; | ||
| 411 | bytes_to_read -= bytes_read; | ||
| 412 | if (! memcpy_to_user (user_buffer, kbuf, bytes_read)) { | ||
| 413 | *segfault = true; | ||
| 414 | break; | ||
| 415 | } | ||
| 416 | user_buffer += bytes_read; | ||
| 417 | } while (bytes_to_read > 0); | ||
| 418 | |||
| 419 | palloc_free_page (kbuf); | ||
| 420 | return size - bytes_to_read; | ||
| 421 | } | ||
| 422 | |||
| 423 | /* Write bytes from user buffer into the specified | ||
| 424 | file, returning number of bytes written. */ | ||
| 425 | static int | ||
| 426 | syscall_write (void *sp, bool *segfault) | ||
| 427 | { | ||
| 428 | int fd; | ||
| 429 | size_t size, bytes_to_write; | ||
| 430 | char *user_buffer; | ||
| 431 | |||
| 432 | /* get arguments */ | ||
| 433 | if (! copy_from_user (&fd, STACK_ADDR (sp,1)) || | ||
| 434 | ! copy_from_user (&user_buffer, STACK_ADDR (sp, 2)) || | ||
| 435 | ! copy_from_user (&size, STACK_ADDR (sp,3))) { | ||
| 436 | *segfault = true; | ||
| 437 | return -1; | ||
| 438 | } | ||
| 439 | |||
| 440 | /* ensure buffer is in user space */ | ||
| 441 | if (! is_user_vaddr (user_buffer) || | ||
| 442 | ! is_user_vaddr (user_buffer + size - 1)) { | ||
| 443 | *segfault = true; | ||
| 444 | return -1; | ||
| 445 | } | ||
| 446 | |||
| 447 | /* get file handle */ | ||
| 448 | struct file *file = NULL; | ||
| 449 | if (fd != STDOUT_FILENO) { | ||
| 450 | file = process_get_file (fd); | ||
| 451 | if (file == NULL) | ||
| 452 | return -1; | ||
| 453 | } | ||
| 454 | |||
| 455 | /* allocate kernel buffer */ | ||
| 456 | char *kbuf = palloc_get_page (0); | ||
| 457 | if (kbuf == NULL) | ||
| 458 | return -1; | ||
| 459 | |||
| 460 | /* write loop */ | ||
| 461 | bytes_to_write = size; | ||
| 462 | do { | ||
| 463 | int blocksize = bytes_to_write; | ||
| 464 | if (bytes_to_write > PGSIZE) | ||
| 465 | blocksize = PGSIZE; | ||
| 466 | if (memcpy_from_user (kbuf, user_buffer, blocksize) == NULL) { | ||
| 467 | *segfault = true; | ||
| 468 | break; | ||
| 469 | } | ||
| 470 | if (fd == STDOUT_FILENO) { | ||
| 471 | putbuf (kbuf, blocksize); | ||
| 472 | bytes_to_write -= blocksize; | ||
| 473 | } else { | ||
| 474 | int bytes_written = 0; | ||
| 475 | int bytes_left_filesys = blocksize; | ||
| 476 | |||
| 477 | process_lock_filesys (); | ||
| 478 | while (bytes_left_filesys > 0) { | ||
| 479 | bytes_written = file_write (file, kbuf, bytes_left_filesys); | ||
| 480 | if (bytes_written <= 0) { | ||
| 481 | break; | ||
| 482 | } | ||
| 483 | bytes_left_filesys -= bytes_written; | ||
| 484 | } | ||
| 485 | process_unlock_filesys (); | ||
| 486 | |||
| 487 | if (bytes_written <= 0) | ||
| 488 | break; | ||
| 489 | bytes_to_write -= blocksize; | ||
| 490 | } | ||
| 491 | user_buffer += blocksize; | ||
| 492 | } while (bytes_to_write > 0); | ||
| 493 | |||
| 494 | /* return bytes written */ | ||
| 495 | palloc_free_page (kbuf); | ||
| 496 | return size - bytes_to_write; | ||
| 497 | } | ||
| 498 | |||
| 499 | /* Change the position where the next byte will be read or written */ | ||
| 500 | static int | ||
| 501 | syscall_seek (void *sp, bool *segfault) | ||
| 502 | { | ||
| 503 | int fd; | ||
| 504 | off_t new_pos; | ||
| 505 | |||
| 506 | /* get arguments */ | ||
| 507 | if (! copy_from_user (&fd, STACK_ADDR (sp,1)) || | ||
| 508 | ! copy_from_user (&new_pos, STACK_ADDR (sp, 2))) { | ||
| 509 | *segfault = true; | ||
| 510 | return 0; | ||
| 511 | } | ||
| 512 | |||
| 513 | /* no way to return something sensible (void function) */ | ||
| 514 | struct file *file = process_get_file (fd); | ||
| 515 | if (file == NULL) | ||
| 516 | return 0; | ||
| 517 | |||
| 518 | process_lock_filesys (); | ||
| 519 | file_seek (file, new_pos); | ||
| 520 | process_unlock_filesys (); | ||
| 521 | return 0; | ||
| 522 | } | ||
| 523 | |||
| 524 | /* Returns the position of the next byte to be read or written */ | ||
| 525 | static int | ||
| 526 | syscall_tell (void *sp, bool *segfault) | ||
| 527 | { | ||
| 528 | int fd; | ||
| 529 | unsigned r = 0; | ||
| 530 | |||
| 531 | /* get arguments */ | ||
| 532 | if (! copy_from_user (&fd, STACK_ADDR (sp,1))) { | ||
| 533 | *segfault = true; | ||
| 534 | return 0; | ||
| 535 | } | ||
| 536 | |||
| 537 | /* no way to return something sensible function */ | ||
| 538 | struct file *file = process_get_file (fd); | ||
| 539 | if (file == NULL) | ||
| 540 | return 0; | ||
| 541 | |||
| 542 | process_lock_filesys (); | ||
| 543 | r = file_tell (file); | ||
| 544 | process_unlock_filesys (); | ||
| 545 | return r; | ||
| 546 | } | ||
| 547 | |||
| 548 | /* Close the given file */ | ||
| 549 | static int | ||
| 550 | syscall_close (void *sp, bool *segfault) | ||
| 551 | { | ||
| 552 | int fd; | ||
| 553 | |||
| 554 | /* get arguments */ | ||
| 555 | if (! copy_from_user (&fd, STACK_ADDR (sp,1))) { | ||
| 556 | *segfault = true; | ||
| 557 | return 0; | ||
| 558 | } | ||
| 559 | |||
| 560 | /* no way to return something sensible function (void) */ | ||
| 561 | (void) process_close_file (fd); | ||
| 562 | return 0; | ||
| 563 | } | ||
diff --git a/userprog/syscall.h b/userprog/syscall.h new file mode 100644 index 0000000..f7ab2f3 --- /dev/null +++ b/userprog/syscall.h | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | #ifndef USERPROG_SYSCALL_H | ||
| 2 | #define USERPROG_SYSCALL_H | ||
| 3 | |||
| 4 | void syscall_init (void); | ||
| 5 | #endif /* userprog/syscall.h */ | ||
diff --git a/userprog/tss.c b/userprog/tss.c new file mode 100644 index 0000000..f8ed9a9 --- /dev/null +++ b/userprog/tss.c | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | #include "userprog/tss.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <stddef.h> | ||
| 4 | #include "userprog/gdt.h" | ||
| 5 | #include "threads/thread.h" | ||
| 6 | #include "threads/palloc.h" | ||
| 7 | #include "threads/vaddr.h" | ||
| 8 | |||
| 9 | /* The Task-State Segment (TSS). | ||
| 10 | |||
| 11 | Instances of the TSS, an x86-specific structure, are used to | ||
| 12 | define "tasks", a form of support for multitasking built right | ||
| 13 | into the processor. However, for various reasons including | ||
| 14 | portability, speed, and flexibility, most x86 OSes almost | ||
| 15 | completely ignore the TSS. We are no exception. | ||
| 16 | |||
| 17 | Unfortunately, there is one thing that can only be done using | ||
| 18 | a TSS: stack switching for interrupts that occur in user mode. | ||
| 19 | When an interrupt occurs in user mode (ring 3), the processor | ||
| 20 | consults the ss0 and esp0 members of the current TSS to | ||
| 21 | determine the stack to use for handling the interrupt. Thus, | ||
| 22 | we must create a TSS and initialize at least these fields, and | ||
| 23 | this is precisely what this file does. | ||
| 24 | |||
| 25 | When an interrupt is handled by an interrupt or trap gate | ||
| 26 | (which applies to all interrupts we handle), an x86 processor | ||
| 27 | works like this: | ||
| 28 | |||
| 29 | - If the code interrupted by the interrupt is in the same | ||
| 30 | ring as the interrupt handler, then no stack switch takes | ||
| 31 | place. This is the case for interrupts that happen when | ||
| 32 | we're running in the kernel. The contents of the TSS are | ||
| 33 | irrelevant for this case. | ||
| 34 | |||
| 35 | - If the interrupted code is in a different ring from the | ||
| 36 | handler, then the processor switches to the stack | ||
| 37 | specified in the TSS for the new ring. This is the case | ||
| 38 | for interrupts that happen when we're in user space. It's | ||
| 39 | important that we switch to a stack that's not already in | ||
| 40 | use, to avoid corruption. Because we're running in user | ||
| 41 | space, we know that the current process's kernel stack is | ||
| 42 | not in use, so we can always use that. Thus, when the | ||
| 43 | scheduler switches threads, it also changes the TSS's | ||
| 44 | stack pointer to point to the new thread's kernel stack. | ||
| 45 | (The call is in thread_schedule_tail() in thread.c.) | ||
| 46 | |||
| 47 | See [IA32-v3a] 6.2.1 "Task-State Segment (TSS)" for a | ||
| 48 | description of the TSS. See [IA32-v3a] 5.12.1 "Exception- or | ||
| 49 | Interrupt-Handler Procedures" for a description of when and | ||
| 50 | how stack switching occurs during an interrupt. */ | ||
| 51 | struct tss | ||
| 52 | { | ||
| 53 | uint16_t back_link, :16; | ||
| 54 | void *esp0; /* Ring 0 stack virtual address. */ | ||
| 55 | uint16_t ss0, :16; /* Ring 0 stack segment selector. */ | ||
| 56 | void *esp1; | ||
| 57 | uint16_t ss1, :16; | ||
| 58 | void *esp2; | ||
| 59 | uint16_t ss2, :16; | ||
| 60 | uint32_t cr3; | ||
| 61 | void (*eip) (void); | ||
| 62 | uint32_t eflags; | ||
| 63 | uint32_t eax, ecx, edx, ebx; | ||
| 64 | uint32_t esp, ebp, esi, edi; | ||
| 65 | uint16_t es, :16; | ||
| 66 | uint16_t cs, :16; | ||
| 67 | uint16_t ss, :16; | ||
| 68 | uint16_t ds, :16; | ||
| 69 | uint16_t fs, :16; | ||
| 70 | uint16_t gs, :16; | ||
| 71 | uint16_t ldt, :16; | ||
| 72 | uint16_t trace, bitmap; | ||
| 73 | }; | ||
| 74 | |||
| 75 | /* Kernel TSS. */ | ||
| 76 | static struct tss *tss; | ||
| 77 | |||
| 78 | /* Initializes the kernel TSS. */ | ||
| 79 | void | ||
| 80 | tss_init (void) | ||
| 81 | { | ||
| 82 | /* Our TSS is never used in a call gate or task gate, so only a | ||
| 83 | few fields of it are ever referenced, and those are the only | ||
| 84 | ones we initialize. */ | ||
| 85 | tss = palloc_get_page (PAL_ASSERT | PAL_ZERO); | ||
| 86 | tss->ss0 = SEL_KDSEG; | ||
| 87 | tss->bitmap = 0xdfff; | ||
| 88 | tss_update (); | ||
| 89 | } | ||
| 90 | |||
| 91 | /* Returns the kernel TSS. */ | ||
| 92 | struct tss * | ||
| 93 | tss_get (void) | ||
| 94 | { | ||
| 95 | ASSERT (tss != NULL); | ||
| 96 | return tss; | ||
| 97 | } | ||
| 98 | |||
| 99 | /* Sets the ring 0 stack pointer in the TSS to point to the end | ||
| 100 | of the thread stack. */ | ||
| 101 | void | ||
| 102 | tss_update (void) | ||
| 103 | { | ||
| 104 | ASSERT (tss != NULL); | ||
| 105 | tss->esp0 = (uint8_t *) thread_current () + PGSIZE; | ||
| 106 | } | ||
diff --git a/userprog/tss.h b/userprog/tss.h new file mode 100644 index 0000000..467bd19 --- /dev/null +++ b/userprog/tss.h | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | #ifndef USERPROG_TSS_H | ||
| 2 | #define USERPROG_TSS_H | ||
| 3 | |||
| 4 | #include <stdint.h> | ||
| 5 | |||
| 6 | struct tss; | ||
| 7 | void tss_init (void); | ||
| 8 | struct tss *tss_get (void); | ||
| 9 | void tss_update (void); | ||
| 10 | |||
| 11 | #endif /* userprog/tss.h */ | ||
