00001 #include "userprog/exception.h" 00002 //86042529452850820 00003 #include <inttypes.h> 00004 #include <stdio.h> 00005 #include "userprog/gdt.h" 00006 #include "threads/interrupt.h" 00007 #include "threads/thread.h" 00008 00009 /* Number of page faults processed. */ 00010 static long long page_fault_cnt; 00011 00012 static void kill (struct intr_frame *); 00013 static void page_fault (struct intr_frame *); 00014 00015 /* Registers handlers for interrupts that can be caused by user 00016 programs. 00017 00018 In a real Unix-like OS, most of these interrupts would be 00019 passed along to the user process in the form of signals, as 00020 described in [SV-386] 3-24 and 3-25, but we don't implement 00021 signals. Instead, we'll make them simply kill the user 00022 process. 00023 00024 Page faults are an exception. Here they are treated the same 00025 way as other exceptions, but this will need to change to 00026 implement virtual memory. 00027 00028 Refer to [IA32-v3a] section 5.15 "Exception and Interrupt 00029 Reference" for a description of each of these exceptions. */ 00030 void 00031 exception_init (void) 00032 { 00033 /* These exceptions can be raised explicitly by a user program, 00034 e.g. via the INT, INT3, INTO, and BOUND instructions. Thus, 00035 we set DPL==3, meaning that user programs are allowed to 00036 invoke them via these instructions. */ 00037 intr_register_int (3, 3, INTR_ON, kill, "#BP Breakpoint Exception"); 00038 intr_register_int (4, 3, INTR_ON, kill, "#OF Overflow Exception"); 00039 intr_register_int (5, 3, INTR_ON, kill, 00040 "#BR BOUND Range Exceeded Exception"); 00041 00042 /* These exceptions have DPL==0, preventing user processes from 00043 invoking them via the INT instruction. They can still be 00044 caused indirectly, e.g. #DE can be caused by dividing by 00045 0. */ 00046 intr_register_int (0, 0, INTR_ON, kill, "#DE Divide Error"); 00047 intr_register_int (1, 0, INTR_ON, kill, "#DB Debug Exception"); 00048 intr_register_int (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception"); 00049 intr_register_int (7, 0, INTR_ON, kill, 00050 "#NM Device Not Available Exception"); 00051 intr_register_int (11, 0, INTR_ON, kill, "#NP Segment Not Present"); 00052 intr_register_int (12, 0, INTR_ON, kill, "#SS Stack Fault Exception"); 00053 intr_register_int (13, 0, INTR_ON, kill, "#GP General Protection Exception"); 00054 intr_register_int (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error"); 00055 intr_register_int (19, 0, INTR_ON, kill, 00056 "#XF SIMD Floating-Point Exception"); 00057 00058 /* Most exceptions can be handled with interrupts turned on. 00059 We need to disable interrupts for page faults because the 00060 fault address is stored in CR2 and needs to be preserved. */ 00061 intr_register_int (14, 0, INTR_OFF, page_fault, "#PF Page-Fault Exception"); 00062 } 00063 00064 /* Prints exception statistics. */ 00065 void 00066 exception_print_stats (void) 00067 { 00068 printf ("Exception: %lld page faults\n", page_fault_cnt); 00069 } 00070 00071 /* Handler for an exception (probably) caused by a user process. */ 00072 static void 00073 kill (struct intr_frame *f) 00074 { 00075 /* This interrupt is one (probably) caused by a user process. 00076 For example, the process might have tried to access unmapped 00077 virtual memory (a page fault). For now, we simply kill the 00078 user process. Later, we'll want to handle page faults in 00079 the kernel. Real Unix-like operating systems pass most 00080 exceptions back to the process via signals, but we don't 00081 implement them. */ 00082 00083 /* The interrupt frame's code segment value tells us where the 00084 exception originated. */ 00085 switch (f->cs) 00086 { 00087 case SEL_UCSEG: 00088 /* User's code segment, so it's a user exception, as we 00089 expected. Kill the user process. */ 00090 printf ("%s: dying due to interrupt %#04x (%s).\n", 00091 thread_name (), f->vec_no, intr_name (f->vec_no)); 00092 intr_dump_frame (f); 00093 thread_exit (); 00094 00095 case SEL_KCSEG: 00096 /* Kernel's code segment, which indicates a kernel bug. 00097 Kernel code shouldn't throw exceptions. (Page faults 00098 may cause kernel exceptions--but they shouldn't arrive 00099 here.) Panic the kernel to make the point. */ 00100 intr_dump_frame (f); 00101 PANIC ("Kernel bug - unexpected interrupt in kernel"); 00102 00103 default: 00104 /* Some other code segment? Shouldn't happen. Panic the 00105 kernel. */ 00106 printf ("Interrupt %#04x (%s) in unknown segment %04x\n", 00107 f->vec_no, intr_name (f->vec_no), f->cs); 00108 thread_exit (); 00109 } 00110 } 00111 00112 /* Page fault handler. This is a skeleton that must be filled in 00113 to implement virtual memory. Some solutions to project 2 may 00114 also require modifying this code. 00115 00116 At entry, the address that faulted is in CR2 (Control Register 00117 2) and information about the fault, formatted as described in 00118 the PF_* macros in exception.h, is in F's error_code member. The 00119 example code here shows how to parse that information. You 00120 can find more information about both of these in the 00121 description of "Interrupt 14--Page Fault Exception (#PF)" in 00122 [IA32-v3a] section 5.15 "Exception and Interrupt Reference". */ 00123 static void 00124 page_fault (struct intr_frame *f) 00125 { 00126 bool not_present; /* True: not-present page, false: writing r/o page. */ 00127 bool write; /* True: access was write, false: access was read. */ 00128 bool user; /* True: access by user, false: access by kernel. */ 00129 void *fault_addr; /* Fault address. */ 00130 00131 /* Obtain faulting address, the virtual address that was 00132 accessed to cause the fault. It may point to code or to 00133 data. It is not necessarily the address of the instruction 00134 that caused the fault (that's f->eip). 00135 See [IA32-v2a] "MOV--Move to/from Control Registers" and 00136 [IA32-v3a] 5.15 "Interrupt 14--Page Fault Exception 00137 (#PF)". */ 00138 asm ("movl %%cr2, %0" : "=r" (fault_addr)); 00139 00140 /* Turn interrupts back on (they were only off so that we could 00141 be assured of reading CR2 before it changed). */ 00142 intr_enable (); 00143 00144 /* Count page faults. */ 00145 page_fault_cnt++; 00146 00147 /* Determine cause. */ 00148 not_present = (f->error_code & PF_P) == 0; 00149 write = (f->error_code & PF_W) != 0; 00150 user = (f->error_code & PF_U) != 0; 00151 00152 /* To implement virtual memory, delete the rest of the function 00153 body, and replace it with code that brings in the page to 00154 which fault_addr refers. */ 00155 printf ("Page fault at %p: %s error %s page in %s context.\n", 00156 fault_addr, 00157 not_present ? "not present" : "rights violation", 00158 write ? "writing" : "reading", 00159 user ? "user" : "kernel"); 00160 kill (f); 00161 } 00162