1 #include "userprog/exception.h"
4 #include "userprog/gdt.h"
5 #include "threads/interrupt.h"
6 #include "threads/thread.h"
8 /* Number of page faults processed. */
9 static long long page_fault_cnt;
11 static void kill (struct intr_frame *);
12 static void page_fault (struct intr_frame *);
14 /* Registers handlers for interrupts that can be caused by user
17 In a real Unix-like OS, most of these interrupts would be
18 passed along to the user process in the form of signals, as
19 described in [SV-386] 3-24 and 3-25, but we don't implement
20 signals. Instead, we'll make them simply kill the user
23 Page faults are an exception. Here they are treated the same
24 way as other exceptions, but this will need to change to
25 implement virtual memory.
27 Refer to [IA32-v3a] section 5.15 "Exception and Interrupt
28 Reference" for a description of each of these exceptions. */
32 /* These exceptions can be raised explicitly by a user program,
33 e.g. via the INT, INT3, INTO, and BOUND instructions. Thus,
34 we set DPL==3, meaning that user programs are allowed to
35 invoke them via these instructions. */
36 intr_register_int (3, 3, INTR_ON, kill, "#BP Breakpoint Exception");
37 intr_register_int (4, 3, INTR_ON, kill, "#OF Overflow Exception");
38 intr_register_int (5, 3, INTR_ON, kill,
39 "#BR BOUND Range Exceeded Exception");
41 /* These exceptions have DPL==0, preventing user processes from
42 invoking them via the INT instruction. They can still be
43 caused indirectly, e.g. #DE can be caused by dividing by
45 intr_register_int (0, 0, INTR_ON, kill, "#DE Divide Error");
46 intr_register_int (1, 0, INTR_ON, kill, "#DB Debug Exception");
47 intr_register_int (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception");
48 intr_register_int (7, 0, INTR_ON, kill,
49 "#NM Device Not Available Exception");
50 intr_register_int (11, 0, INTR_ON, kill, "#NP Segment Not Present");
51 intr_register_int (12, 0, INTR_ON, kill, "#SS Stack Fault Exception");
52 intr_register_int (13, 0, INTR_ON, kill, "#GP General Protection Exception");
53 intr_register_int (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error");
54 intr_register_int (19, 0, INTR_ON, kill,
55 "#XF SIMD Floating-Point Exception");
57 /* Most exceptions can be handled with interrupts turned on.
58 We need to disable interrupts for page faults because the
59 fault address is stored in CR2 and needs to be preserved. */
60 intr_register_int (14, 0, INTR_OFF, page_fault, "#PF Page-Fault Exception");
63 /* Prints exception statistics. */
65 exception_print_stats (void)
67 printf ("Exception: %lld page faults\n", page_fault_cnt);
70 /* Handler for an exception (probably) caused by a user process. */
72 kill (struct intr_frame *f)
74 /* This interrupt is one (probably) caused by a user process.
75 For example, the process might have tried to access unmapped
76 virtual memory (a page fault). For now, we simply kill the
77 user process. Later, we'll want to handle page faults in
78 the kernel. Real Unix-like operating systems pass most
79 exceptions back to the process via signals, but we don't
82 /* The interrupt frame's code segment value tells us where the
83 exception originated. */
87 /* User's code segment, so it's a user exception, as we
88 expected. Kill the user process. */
89 printf ("%s: dying due to interrupt %#04x (%s).\n",
90 thread_name (), f->vec_no, intr_name (f->vec_no));
95 /* Kernel's code segment, which indicates a kernel bug.
96 Kernel code shouldn't throw exceptions. (Page faults
97 may cause kernel exceptions--but they shouldn't arrive
98 here.) Panic the kernel to make the point. */
100 PANIC ("Kernel bug - unexpected interrupt in kernel");
103 /* Some other code segment? Shouldn't happen. Panic the
105 printf ("Interrupt %#04x (%s) in unknown segment %04x\n",
106 f->vec_no, intr_name (f->vec_no), f->cs);
111 /* Page fault handler. This is a skeleton that must be filled in
112 to implement virtual memory. Some solutions to project 2 may
113 also require modifying this code.
115 At entry, the address that faulted is in CR2 (Control Register
116 2) and information about the fault, formatted as described in
117 the PF_* macros in exception.h, is in F's error_code member. The
118 example code here shows how to parse that information. You
119 can find more information about both of these in the
120 description of "Interrupt 14--Page Fault Exception (#PF)" in
121 [IA32-v3a] section 5.15 "Exception and Interrupt Reference". */
123 page_fault (struct intr_frame *f)
125 bool not_present; /* True: not-present page, false: writing r/o page. */
126 bool write; /* True: access was write, false: access was read. */
127 bool user; /* True: access by user, false: access by kernel. */
128 void *fault_addr; /* Fault address. */
130 /* Obtain faulting address, the virtual address that was
131 accessed to cause the fault. It may point to code or to
132 data. It is not necessarily the address of the instruction
133 that caused the fault (that's f->eip).
134 See [IA32-v2a] "MOV--Move to/from Control Registers" and
135 [IA32-v3a] 5.15 "Interrupt 14--Page Fault Exception
137 asm ("movl %%cr2, %0" : "=r" (fault_addr));
139 /* Turn interrupts back on (they were only off so that we could
140 be assured of reading CR2 before it changed). */
143 /* Count page faults. */
146 /* Determine cause. */
147 not_present = (f->error_code & PF_P) == 0;
148 write = (f->error_code & PF_W) != 0;
149 user = (f->error_code & PF_U) != 0;
151 /* To implement virtual memory, delete the rest of the function
152 body, and replace it with code that brings in the page to
153 which fault_addr refers. */
154 printf ("Page fault at %p: %s error %s page in %s context.\n",
156 not_present ? "not present" : "rights violation",
157 write ? "writing" : "reading",
158 user ? "user" : "kernel");