Improve command-line interface to run-tests.
[pintos-anon] / src / userprog / exception.c
index eaa069124e87a30de6fd0bbfc505f8ec8a792d68..8b2e8b641adf7b90168b7b3ff4b3ae81e2bf4d6c 100644 (file)
@@ -1,18 +1,24 @@
-#include "exception.h"
-#include "lib.h"
-#include "gdt.h"
-#include "interrupt.h"
-#include "thread.h"
+#include "userprog/exception.h"
+#include <inttypes.h>
+#include <stdio.h>
+#include "userprog/gdt.h"
+#include "threads/interrupt.h"
+#include "threads/thread.h"
+
+/* Number of page faults processed. */
+static long long page_fault_cnt;
 
 static void kill (struct intr_frame *);
 
 static void kill (struct intr_frame *);
+static void page_fault (struct intr_frame *);
 
 /* Registers handlers for interrupts that can be caused by user
    programs.
 
    In a real Unix-like OS, most of these interrupts would be
 
 /* Registers handlers for interrupts that can be caused by user
    programs.
 
    In a real Unix-like OS, most of these interrupts would be
-   passed along to the user process in the form of signals, but
-   we don't implement signals.  Instead, we'll make them simply
-   kill the user process.
+   passed along to the user process in the form of signals, as
+   described in [SV-386] 3-24 and 3-25, but we don't implement
+   signals.  Instead, we'll make them simply kill the user
+   process.
 
    Page faults are an exception.  Here they are treated the same
    way as other exceptions, but this will need to change to
 
    Page faults are an exception.  Here they are treated the same
    way as other exceptions, but this will need to change to
@@ -48,7 +54,14 @@ exception_init (void)
   /* Most exceptions can be handled with interrupts turned on.
      We need to disable interrupts for page faults because the
      fault address is stored in CR2 and needs to be preserved. */
   /* Most exceptions can be handled with interrupts turned on.
      We need to disable interrupts for page faults because the
      fault address is stored in CR2 and needs to be preserved. */
-  intr_register (14, 0, INTR_OFF, kill, "#PF Page-Fault Exception");
+  intr_register (14, 0, INTR_OFF, page_fault, "#PF Page-Fault Exception");
+}
+
+/* Prints exception statistics. */
+void
+exception_print_stats (void) 
+{
+  printf ("Exception: %lld page faults\n", page_fault_cnt);
 }
 
 /* Handler for an exception (probably) caused by a user process. */
 }
 
 /* Handler for an exception (probably) caused by a user process. */
@@ -69,10 +82,9 @@ kill (struct intr_frame *f)
     {
     case SEL_UCSEG:
       /* User's code segment, so it's a user exception, as we
     {
     case SEL_UCSEG:
       /* User's code segment, so it's a user exception, as we
-         expected. */
-      printk ("%s: dying due to interrupt %#04x (%s).\n",
-              thread_name (thread_current ()),
-              f->vec_no, intr_name (f->vec_no));
+         expected.  Kill the user process.  */
+      printf ("%s: dying due to interrupt %#04x (%s).\n",
+              thread_name (), f->vec_no, intr_name (f->vec_no));
       intr_dump_frame (f);
       thread_exit (); 
 
       intr_dump_frame (f);
       thread_exit (); 
 
@@ -80,15 +92,69 @@ kill (struct intr_frame *f)
       /* Kernel's code segment, which indicates a kernel bug.
          Kernel code shouldn't throw exceptions.  (Page faults
          may cause kernel exceptions--but they shouldn't arrive
       /* Kernel's code segment, which indicates a kernel bug.
          Kernel code shouldn't throw exceptions.  (Page faults
          may cause kernel exceptions--but they shouldn't arrive
-         here.) */
+         here.)  Panic the kernel to make the point.  */
       intr_dump_frame (f);
       PANIC ("Kernel bug - unexpected interrupt in kernel"); 
 
     default:
       intr_dump_frame (f);
       PANIC ("Kernel bug - unexpected interrupt in kernel"); 
 
     default:
-      /* Some other code segment?  Shouldn't happen. */
-      printk ("Interrupt %#04x (%s) in unknown segment %04x\n",
+      /* Some other code segment?  Shouldn't happen.  Panic the
+         kernel. */
+      printf ("Interrupt %#04x (%s) in unknown segment %04x\n",
              f->vec_no, intr_name (f->vec_no), f->cs);
       thread_exit ();
     }
 }
 
              f->vec_no, intr_name (f->vec_no), f->cs);
       thread_exit ();
     }
 }
 
+/* Page fault error code bits that describe the cause of the exception.  */
+#define PF_P 0x1    /* 0: not-present page. 1: access rights violation. */
+#define PF_W 0x2    /* 0: read, 1: write. */
+#define PF_U 0x4    /* 0: kernel, 1: user process. */
+
+/* Page fault handler.  This is a skeleton that must be filled in
+   to implement virtual memory.  Some solutions to project 2 may
+   also require modifying this code.
+
+   At entry, the address that faulted is in CR2 (Control Register
+   2) and information about the fault, formatted as described in
+   the PF_* macros above, is in F's error_code member.  The
+   example code here shows how to parse that information.  You
+   can find more information about both of these in the
+   description of "Interrupt 14--Page Fault Exception (#PF)" in
+   [IA32-v3] section 5.14, which is pages 5-46 to 5-49. */
+static void
+page_fault (struct intr_frame *f) 
+{
+  bool not_present;  /* True: not-present page, false: writing r/o page. */
+  bool write;        /* True: access was write, false: access was read. */
+  bool user;         /* True: access by user, false: access by kernel. */
+  void *fault_addr;  /* Fault address. */
+
+  /* Obtain faulting address, the virtual address that was
+     accessed to cause the fault.  It may point to code or to
+     data.  It is not necessarily the address of the instruction
+     that caused the fault (that's f->eip).  */
+  asm ("movl %%cr2, %0" : "=r" (fault_addr));
+
+  /* Turn interrupts back on (they were only off so that we could
+     be assured of reading CR2 before it changed). */
+  intr_enable ();
+
+  /* Count page faults. */
+  page_fault_cnt++;
+
+  /* Determine cause. */
+  not_present = (f->error_code & PF_P) == 0;
+  write = (f->error_code & PF_W) != 0;
+  user = (f->error_code & PF_U) != 0;
+
+  /* To implement virtual memory, delete the rest of the function
+     body, and replace it with code that brings in the page to
+     which fault_addr refers. */
+  printf ("Page fault at %p: %s error %s page in %s context.\n",
+          fault_addr,
+          not_present ? "not present" : "rights violation",
+          write ? "writing" : "reading",
+          user ? "user" : "kernel");
+  kill (f);
+}
+