Invent tid_t data type and use it in many places where we current use
[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 static void kill (struct intr_frame *);
9 static void page_fault (struct intr_frame *);
10
11 /* Registers handlers for interrupts that can be caused by user
12    programs.
13
14    In a real Unix-like OS, most of these interrupts would be
15    passed along to the user process in the form of signals, as
16    described in [SV-386] 3-24 and 3-25, but we don't implement
17    signals.  Instead, we'll make them simply kill the user
18    process.
19
20    Page faults are an exception.  Here they are treated the same
21    way as other exceptions, but this will need to change to
22    implement virtual memory.
23
24    Refer to [IA32-v3] section 5.14 for a description of each of
25    these exceptions. */
26 void
27 exception_init (void) 
28 {
29   /* These exceptions can be raised explicitly by a user program,
30      e.g. via the INT, INT3, INTO, and BOUND instructions.  Thus,
31      we set DPL==3, meaning that user programs are allowed to
32      invoke them via these instructions. */
33   intr_register (3, 3, INTR_ON, kill, "#BP Breakpoint Exception");
34   intr_register (4, 3, INTR_ON, kill, "#OF Overflow Exception");
35   intr_register (5, 3, INTR_ON, kill, "#BR BOUND Range Exceeded Exception");
36
37   /* These exceptions have DPL==0, preventing user processes from
38      invoking them via the INT instruction.  They can still be
39      caused indirectly, e.g. #DE can be caused by dividing by
40      0.  */
41   intr_register (0, 0, INTR_ON, kill, "#DE Divide Error");
42   intr_register (1, 0, INTR_ON, kill, "#DB Debug Exception");
43   intr_register (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception");
44   intr_register (7, 0, INTR_ON, kill, "#NM Device Not Available Exception");
45   intr_register (11, 0, INTR_ON, kill, "#NP Segment Not Present");
46   intr_register (12, 0, INTR_ON, kill, "#SS Stack Fault Exception");
47   intr_register (13, 0, INTR_ON, kill, "#GP General Protection Exception");
48   intr_register (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error");
49   intr_register (19, 0, INTR_ON, kill, "#XF SIMD Floating-Point Exception");
50
51   /* Most exceptions can be handled with interrupts turned on.
52      We need to disable interrupts for page faults because the
53      fault address is stored in CR2 and needs to be preserved. */
54   intr_register (14, 0, INTR_OFF, page_fault, "#PF Page-Fault Exception");
55 }
56
57 /* Handler for an exception (probably) caused by a user process. */
58 static void
59 kill (struct intr_frame *f) 
60 {
61   /* This interrupt is one (probably) caused by a user process.
62      For example, the process might have tried to access unmapped
63      virtual memory (a page fault).  For now, we simply kill the
64      user process.  Later, we'll want to handle page faults in
65      the kernel.  Real Unix-like operating systems pass most
66      exceptions back to the process via signals, but we don't
67      implement them. */
68      
69   /* The interrupt frame's code segment value tells us where the
70      exception originated. */
71   switch (f->cs)
72     {
73     case SEL_UCSEG:
74       /* User's code segment, so it's a user exception, as we
75          expected.  Kill the user process.  */
76       printf ("%s: dying due to interrupt %#04x (%s).\n",
77               thread_name (), f->vec_no, intr_name (f->vec_no));
78       intr_dump_frame (f);
79       thread_exit (); 
80
81     case SEL_KCSEG:
82       /* Kernel's code segment, which indicates a kernel bug.
83          Kernel code shouldn't throw exceptions.  (Page faults
84          may cause kernel exceptions--but they shouldn't arrive
85          here.)  Panic the kernel to make the point.  */
86       intr_dump_frame (f);
87       PANIC ("Kernel bug - unexpected interrupt in kernel"); 
88
89     default:
90       /* Some other code segment?  Shouldn't happen.  Panic the
91          kernel. */
92       printf ("Interrupt %#04x (%s) in unknown segment %04x\n",
93              f->vec_no, intr_name (f->vec_no), f->cs);
94       thread_exit ();
95     }
96 }
97
98 /* Page fault error code bits that describe the cause of the exception.  */
99 #define PF_P 0x1    /* 0: not-present page. 1: access rights violation. */
100 #define PF_W 0x2    /* 0: read, 1: write. */
101 #define PF_U 0x4    /* 0: kernel, 1: user process. */
102
103 /* Page fault handler.  This is a skeleton that must be filled in
104    to implement virtual memory.
105
106    At entry, the address that faulted is in CR2 (Control Register
107    2) and information about the fault, formatted as described in
108    the PF_* macros above, is in F's error_code member.  The
109    example code here shows how to parse that information.  You
110    can find more information about both of these in the
111    description of "Interrupt 14--Page Fault Exception (#PF)" in
112    [IA32-v3] section 5.14, which is pages 5-46 to 5-49. */
113 static void
114 page_fault (struct intr_frame *f) 
115 {
116   bool not_present, write, user;
117   uint32_t fault_addr;
118
119   /* Obtain faulting address, then turn interrupts back on.
120      (Interrupts were only off so that we could be assured of
121      reading CR2 before it changed.)
122
123      The faulting address is not necesarily the address of the
124      instruction that caused the fault--that's in F's eip
125      member.  Rather, it's the linear address that was accessed
126      to cause the fault, which is probably an address of data,
127      not code. */
128   asm ("movl %%cr2, %0" : "=r" (fault_addr));
129   intr_enable ();
130
131   /* Determine cause. */
132   not_present = (f->error_code & PF_P) == 0;
133   write = (f->error_code & PF_W) != 0;
134   user = (f->error_code & PF_U) != 0;
135
136   /* To implement virtual memory, delete the rest of the function
137      body, and replace it with code that brings in the page to
138      which fault_addr refers. */
139   printf ("Page fault at %08"PRIx32": %s error %s page in %s context.\n",
140           fault_addr,
141           not_present ? "not present" : "rights violation",
142           write ? "writing" : "reading",
143           user ? "user" : "kernel");
144   kill (f);
145 }
146