diff options
Diffstat (limited to 'userprog/exception.c')
| -rw-r--r-- | userprog/exception.c | 174 |
1 files changed, 174 insertions, 0 deletions
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 | |||
