Revert Intel-style assembly back to AT&T-style.
[pintos-anon] / src / userprog / exception.c
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
8 /* Number of page faults processed. */
9 static long long page_fault_cnt;
10
11 static void kill (struct intr_frame *);
12 static void page_fault (struct intr_frame *);
13
14 /* Registers handlers for interrupts that can be caused by user
15    programs.
16
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
21    process.
22
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.
26
27    Refer to [IA32-v3] section 5.14 for a description of each of
28    these exceptions. */
29 void
30 exception_init (void) 
31 {
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");
40
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
44      0.  */
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");
56
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");
61 }
62
63 /* Prints exception statistics. */
64 void
65 exception_print_stats (void) 
66 {
67   printf ("Exception: %lld page faults\n", page_fault_cnt);
68 }
69
70 /* Handler for an exception (probably) caused by a user process. */
71 static void
72 kill (struct intr_frame *f) 
73 {
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
80      implement them. */
81      
82   /* The interrupt frame's code segment value tells us where the
83      exception originated. */
84   switch (f->cs)
85     {
86     case SEL_UCSEG:
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));
91       intr_dump_frame (f);
92       thread_exit (); 
93
94     case SEL_KCSEG:
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.  */
99       intr_dump_frame (f);
100       PANIC ("Kernel bug - unexpected interrupt in kernel"); 
101
102     default:
103       /* Some other code segment?  Shouldn't happen.  Panic the
104          kernel. */
105       printf ("Interrupt %#04x (%s) in unknown segment %04x\n",
106              f->vec_no, intr_name (f->vec_no), f->cs);
107       thread_exit ();
108     }
109 }
110
111 /* Page fault error code bits that describe the cause of the exception.  */
112 #define PF_P 0x1    /* 0: not-present page. 1: access rights violation. */
113 #define PF_W 0x2    /* 0: read, 1: write. */
114 #define PF_U 0x4    /* 0: kernel, 1: user process. */
115
116 /* Page fault handler.  This is a skeleton that must be filled in
117    to implement virtual memory.  Some solutions to project 2 may
118    also require modifying this code.
119
120    At entry, the address that faulted is in CR2 (Control Register
121    2) and information about the fault, formatted as described in
122    the PF_* macros above, is in F's error_code member.  The
123    example code here shows how to parse that information.  You
124    can find more information about both of these in the
125    description of "Interrupt 14--Page Fault Exception (#PF)" in
126    [IA32-v3] section 5.14, which is pages 5-46 to 5-49. */
127 static void
128 page_fault (struct intr_frame *f) 
129 {
130   bool not_present;  /* True: not-present page, false: writing r/o page. */
131   bool write;        /* True: access was write, false: access was read. */
132   bool user;         /* True: access by user, false: access by kernel. */
133   void *fault_addr;  /* Fault address. */
134
135   /* Obtain faulting address, the virtual address that was
136      accessed to cause the fault.  It may point to code or to
137      data.  It is not necessarily the address of the instruction
138      that caused the fault (that's f->eip).
139      See [IA32-v2a] "MOV--Move to/from Control Registers" and
140      [IA32-v3] 5.14 "Interrupt 14--Page Fault Exception
141      (#PF)". */
142   asm ("movl %%cr2, %0" : "=r" (fault_addr));
143
144   /* Turn interrupts back on (they were only off so that we could
145      be assured of reading CR2 before it changed). */
146   intr_enable ();
147
148   /* Count page faults. */
149   page_fault_cnt++;
150
151   /* Determine cause. */
152   not_present = (f->error_code & PF_P) == 0;
153   write = (f->error_code & PF_W) != 0;
154   user = (f->error_code & PF_U) != 0;
155
156   /* To implement virtual memory, delete the rest of the function
157      body, and replace it with code that brings in the page to
158      which fault_addr refers. */
159   printf ("Page fault at %p: %s error %s page in %s context.\n",
160           fault_addr,
161           not_present ? "not present" : "rights violation",
162           write ? "writing" : "reading",
163           user ? "user" : "kernel");
164   kill (f);
165 }
166