Move user exception support into userprog.
authorBen Pfaff <blp@cs.stanford.edu>
Thu, 2 Sep 2004 03:25:54 +0000 (03:25 +0000)
committerBen Pfaff <blp@cs.stanford.edu>
Thu, 2 Sep 2004 03:25:54 +0000 (03:25 +0000)
18 files changed:
src/Makefile.inc
src/threads/gdt.c [deleted file]
src/threads/gdt.h [deleted file]
src/threads/init.c
src/threads/interrupt.c
src/threads/interrupt.h
src/threads/intr-stubs.pl
src/threads/loader.S
src/threads/loader.h
src/threads/thread.c
src/threads/tss.c [deleted file]
src/threads/tss.h [deleted file]
src/userprog/exception.c [new file with mode: 0644]
src/userprog/exception.h [new file with mode: 0644]
src/userprog/gdt.c [new file with mode: 0644]
src/userprog/gdt.h [new file with mode: 0644]
src/userprog/tss.c [new file with mode: 0644]
src/userprog/tss.h [new file with mode: 0644]

index c125ebaeb7be4447f9258b2736c354a73c1538d9..982b5e800f5e5b63f9ddb5f0a7772beb80ab626c 100644 (file)
@@ -17,8 +17,6 @@ ASFLAGS = -Wa,--gstabs+ $(INCLUDES) $(DEFINES)
 # Core kernel.
 THREADS_SRC  = start.S         # Must be linked first.
 THREADS_SRC += init.c          # Main program.
-THREADS_SRC += gdt.c           # GDT initialization.
-THREADS_SRC += tss.c           # TSS management.
 THREADS_SRC += thread.c                # Thread management core.
 THREADS_SRC += switch.S                # Thread switch routine.
 THREADS_SRC += interrupt.c     # Interrupt core.
@@ -51,13 +49,16 @@ FILESYS_SRC += fsutil.c             # Utilities.
 
 # User process code.
 USERPROG_SRC  = addrspace.c    # Address spaces.
+USERPROG_SRC += exception.c    # User exception handler.
+USERPROG_SRC += gdt.c          # GDT initialization.
+USERPROG_SRC += tss.c          # TSS management.
 
 # Objects.
 OBJECTS = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(SOURCES)))
 
 all: diskimage.bin
 
-intr-stubs.S: $(TOP_SRCDIR)/threads/intr-stubs.pl $(TOP_SRCDIR)/threads/gdt.h
+intr-stubs.S: $(TOP_SRCDIR)/threads/intr-stubs.pl $(TOP_SRCDIR)/threads/loader.h
        $< > $@
 
 kernel.lds.s: $(TOP_SRCDIR)/threads/kernel.lds.S $(TOP_SRCDIR)/threads/loader.h
diff --git a/src/threads/gdt.c b/src/threads/gdt.c
deleted file mode 100644 (file)
index 7a6005f..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-#include "gdt.h"
-#include "debug.h"
-#include "mmu.h"
-#include "palloc.h"
-#include "tss.h"
-
-/* The Global Descriptor Table (GDT).
-
-   The GDT, an x86-specific structure, defines segments that can
-   potentially be used by all processes in a system, subject to
-   their permissions.  There is also a per-process Local
-   Descriptor Table (LDT) but that is not used by modern
-   operating systems.
-
-   Each entry in the GDT, which is known by its byte offset in
-   the table, identifies a segment.  For our purposes only three
-   types of segments are of interest: code, data, and TSS or
-   Task-State Segment descriptors.  The former two types are
-   exactly what they sound like.  The TSS is used primarily for
-   stack switching on interrupts.
-
-   For more information on the GDT as used here, refer to
-   [IA32-v3] sections 3.2 through 3.5. */
-static uint64_t gdt[SEL_CNT];
-
-/* Creating descriptors. */
-static uint64_t make_code_desc (int dpl);
-static uint64_t make_data_desc (int dpl);
-static uint64_t make_tss_desc (void *laddr);
-
-/* Sets up a proper GDT.  The bootstrap loader's GDT didn't
-   include user-mode selectors or a TSS, but we need both now. */
-void
-gdt_init (void)
-{
-  uint64_t gdtr_operand;
-
-  /* Initialize GDT. */
-  gdt[SEL_NULL / sizeof *gdt] = 0;
-  gdt[SEL_KCSEG / sizeof *gdt] = make_code_desc (0);
-  gdt[SEL_KDSEG / sizeof *gdt] = make_data_desc (0);
-  gdt[SEL_UCSEG / sizeof *gdt] = make_code_desc (3);
-  gdt[SEL_UDSEG / sizeof *gdt] = make_data_desc (3);
-  gdt[SEL_TSS / sizeof *gdt] = make_tss_desc (tss_get ());
-
-  /* Load GDTR, TR. */
-  gdtr_operand = make_dtr_operand (sizeof gdt - 1, gdt);
-  asm volatile ("lgdt %0" :: "m" (gdtr_operand));
-  asm volatile ("ltr %w0" :: "r" (SEL_TSS));
-}
-\f
-/* System segment or code/data segment? */
-enum seg_class
-  {
-    CLS_SYSTEM = 0,             /* System segment. */
-    CLS_CODE_DATA = 1           /* Code or data segment. */
-  };
-
-/* Limit has byte or 4 kB page granularity? */
-enum seg_granularity
-  {
-    GRAN_BYTE = 0,              /* Limit has 1-byte granularity. */
-    GRAN_PAGE = 1               /* Limit has 4 kB granularity. */
-  };
-
-/* Returns a segment descriptor with the given 32-bit BASE and
-   20-bit LIMIT (whose interpretation depends on GRANULARITY).
-   The descriptor represents a system or code/data segment
-   according to CLASS, and TYPE is its type (whose interpretation
-   depends on the class).
-
-   The segment has descriptor privilege level DPL, meaning that
-   it can be used in rings numbered DPL or lower.  In practice,
-   DPL==3 means that user processes can use the segment and
-   DPL==0 means that only the kernel can use the segment.  See
-   [IA32-v3] section 4.5 for further discussion. */
-static uint64_t
-make_seg_desc (uint32_t base,
-               uint32_t limit,
-               enum seg_class class,
-               int type,
-               int dpl,
-               enum seg_granularity granularity)
-{
-  uint32_t e0, e1;
-
-  ASSERT (limit <= 0xfffff);
-  ASSERT (class == CLS_SYSTEM || class == CLS_CODE_DATA);
-  ASSERT (type >= 0 && type <= 15);
-  ASSERT (dpl >= 0 && dpl <= 3);
-  ASSERT (granularity == GRAN_BYTE || granularity == GRAN_PAGE);
-
-  e0 = ((limit & 0xffff)             /* Limit 15:0. */
-        | (base << 16));             /* Base 15:0. */
-
-  e1 = (((base >> 16) & 0xff)        /* Base 23:16. */
-        | (type << 8)                /* Segment type. */
-        | (class << 12)              /* 0=system, 1=code/data. */
-        | (dpl << 13)                /* Descriptor privilege. */
-        | (1 << 15)                  /* Present. */
-        | (limit & 0xf0000)          /* Limit 16:19. */
-        | (1 << 22)                  /* 32-bit segment. */
-        | (granularity << 23)        /* Byte/page granularity. */
-        | (base & 0xff000000));      /* Base 31:24. */
-
-  return e0 | ((uint64_t) e1 << 32);
-}
-
-/* Returns a descriptor for a readable code segment with base at
-   0, a limit of 4 GB, and the given DPL. */
-static uint64_t
-make_code_desc (int dpl)
-{
-  return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 10, dpl, GRAN_PAGE);
-}
-
-/* Returns a descriptor for a writable data segment with base at
-   0, a limit of 4 GB, and the given DPL. */
-static uint64_t
-make_data_desc (int dpl)
-{
-  return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 2, dpl, GRAN_PAGE);
-}
-
-/* Returns a descriptor for an "available" 32-bit Task-State
-   Segment with its base at the given linear address, a limit of
-   0x67 bytes (the size of a 32-bit TSS), and a DPL of 0. */
-static uint64_t
-make_tss_desc (void *laddr)
-{
-  return make_seg_desc ((uint32_t) laddr, 0x67, CLS_SYSTEM, 9, 0, GRAN_BYTE);
-}
-
diff --git a/src/threads/gdt.h b/src/threads/gdt.h
deleted file mode 100644 (file)
index c32ceab..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef HEADER_GDT_H
-#define HEADER_GDT_H 1
-
-/* Segment selectors. */
-#define SEL_NULL        0x00    /* Null selector. */
-#define SEL_KCSEG       0x08    /* Kernel code selector. */
-#define SEL_KDSEG       0x10    /* Kernel data selector. */
-#define SEL_UCSEG       0x1B    /* User code selector. */
-#define SEL_UDSEG       0x23    /* User data selector. */
-#define SEL_TSS         0x28    /* Task-state segment. */
-#define SEL_CNT         6       /* Number of segments. */
-
-#ifndef __ASSEMBLER__
-#include <stdint.h>
-
-static inline uint64_t
-make_dtr_operand (uint16_t limit, void *base)
-{
-  return limit | ((uint64_t) (uint32_t) base << 16);
-}
-
-void gdt_init (void);
-#endif
-
-#endif /* gdt.h */
index ae864981341d110464c59985d3ccd94e8acf193a..ec71cec8432b4670a5473f0486760dea595489c1 100644 (file)
@@ -3,7 +3,6 @@
 #include <stddef.h>
 #include <limits.h>
 #include "debug.h"
-#include "gdt.h"
 #include "interrupt.h"
 #include "io.h"
 #include "kbd.h"
 #include "serial.h"
 #include "thread.h"
 #include "timer.h"
-#include "tss.h"
 #include "vga.h"
+#ifdef USERPROG
+#include "exception.h"
+#include "gdt.h"
+#include "tss.h"
+#endif
 #ifdef FILESYS
 #include "filesys.h"
 #include "disk.h"
@@ -59,8 +62,10 @@ main (void)
   /* Initialize memory system, segments, paging. */
   palloc_init ();
   paging_init ();
+#ifdef USERPROG
   tss_init ();
   gdt_init ();
+#endif
   malloc_init ();
 
   /* Set random seed if not already done. */
@@ -70,6 +75,9 @@ main (void)
   intr_init ();
   timer_init ();
   kbd_init ();
+#ifdef USERPROG
+  exception_init ();
+#endif
 
   /* Do everything else in a system thread. */
   thread_init ();
@@ -92,6 +100,8 @@ main_thread (void *aux UNUSED)
     thread_execute (initial_program);
   else
     PANIC ("no initial program specified");
+#else
+  PANIC ("boot successful");
 #endif
 }
 \f
@@ -174,8 +184,9 @@ argv_init (void)
           " -p FILENAME         Print the contents of FILENAME\n"
           " -r FILENAME         Delete FILENAME\n"
           " -ls                 List the files in the filesystem\n"
-          " -D                  Dump complete filesystem contents\n");
+          " -D                  Dump complete filesystem contents\n"
 #endif
+          );
       }
     else 
       PANIC ("unknown option `%s'", argv[i]);
index 7559447307db9ee59f672bf042e7e5f88a5b868c..8173ec11dcc44767c7cd5d02158651e48433eab4 100644 (file)
@@ -3,7 +3,6 @@
 #include <stdint.h>
 #include "intr-stubs.h"
 #include "debug.h"
-#include "gdt.h"
 #include "io.h"
 #include "lib.h"
 #include "mmu.h"
@@ -40,11 +39,10 @@ static void pic_end_of_interrupt (int irq);
 /* Interrupt Descriptor Table helpers. */
 static uint64_t make_intr_gate (void (*) (void), int dpl);
 static uint64_t make_trap_gate (void (*) (void), int dpl);
+static inline uint64_t make_idtr_operand (uint16_t limit, void *base);
 
 /* Interrupt handlers. */
 void intr_handler (struct intr_frame *args);
-static intr_handler_func panic NO_RETURN;
-static intr_handler_func kill NO_RETURN;
 \f
 /* Returns the current interrupt status. */
 enum intr_level
@@ -90,40 +88,34 @@ intr_init (void)
   uint64_t idtr_operand;
   int i;
 
+  /* Initialize interrupt controller. */
   pic_init ();
 
+  /* Load IDT register. */
+  idtr_operand = make_idtr_operand (sizeof idt - 1, idt);
+  asm volatile ("lidt %0" :: "m" (idtr_operand));
+
   /* Initialize intr_names. */
   for (i = 0; i < INTR_CNT; i++)
     intr_names[i] = "unknown";
-
-  /* Most exceptions require ring 0.
-     Exceptions 3, 4, and 5 can be caused by ring 3 directly. */
-  intr_register (0, 0, INTR_ON, kill, "#DE Divide Error");
-  intr_register (1, 0, INTR_ON, kill, "#DB Debug Exception");
-  intr_register (2, 0, INTR_ON, panic, "NMI Interrupt");
-  intr_register (3, 3, INTR_ON, kill, "#BP Breakpoint Exception");
-  intr_register (4, 3, INTR_ON, kill, "#OF Overflow Exception");
-  intr_register (5, 3, INTR_ON, kill, "#BR BOUND Range Exceeded Exception");
-  intr_register (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception");
-  intr_register (7, 0, INTR_ON, kill, "#NM Device Not Available Exception");
-  intr_register (8, 0, INTR_ON, panic, "#DF Double Fault Exception");
-  intr_register (9, 0, INTR_ON, panic, "Coprocessor Segment Overrun");
-  intr_register (10, 0, INTR_ON, panic, "#TS Invalid TSS Exception");
-  intr_register (11, 0, INTR_ON, kill, "#NP Segment Not Present");
-  intr_register (12, 0, INTR_ON, kill, "#SS Stack Fault Exception");
-  intr_register (13, 0, INTR_ON, kill, "#GP General Protection Exception");
-  intr_register (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error");
-  intr_register (17, 0, INTR_ON, panic, "#AC Alignment Check Exception");
-  intr_register (18, 0, INTR_ON, panic, "#MC Machine-Check Exception");
-  intr_register (19, 0, INTR_ON, kill, "#XF SIMD Floating-Point Exception");
-
-  /* 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");
-
-  idtr_operand = make_dtr_operand (sizeof idt - 1, idt);
-  asm volatile ("lidt %0" :: "m" (idtr_operand));
+  intr_names[0] = "#DE Divide Error";
+  intr_names[1] = "#DB Debug Exception";
+  intr_names[2] = "NMI Interrupt";
+  intr_names[3] = "#BP Breakpoint Exception";
+  intr_names[4] = "#OF Overflow Exception";
+  intr_names[5] = "#BR BOUND Range Exceeded Exception";
+  intr_names[6] = "#UD Invalid Opcode Exception";
+  intr_names[7] = "#NM Device Not Available Exception";
+  intr_names[8] = "#DF Double Fault Exception";
+  intr_names[9] = "Coprocessor Segment Overrun";
+  intr_names[10] = "#TS Invalid TSS Exception";
+  intr_names[11] = "#NP Segment Not Present";
+  intr_names[12] = "#SS Stack Fault Exception";
+  intr_names[13] = "#GP General Protection Exception";
+  intr_names[16] = "#MF x87 FPU Floating-Point Error";
+  intr_names[17] = "#AC Alignment Check Exception";
+  intr_names[18] = "#MC Machine-Check Exception";
+  intr_names[19] = "#XF SIMD Floating-Point Exception";
 }
 
 /* Registers interrupt VEC_NO to invoke HANDLER, which is named
@@ -286,22 +278,28 @@ make_trap_gate (void (*function) (void), int dpl)
 {
   return make_gate (function, dpl, 15);
 }
+
+/* Returns a descriptor that yields the given LIMIT and BASE when
+   used as an operand for the LIDT instruction. */
+static inline uint64_t
+make_idtr_operand (uint16_t limit, void *base)
+{
+  return limit | ((uint64_t) (uint32_t) base << 16);
+}
 \f
 /* Interrupt handlers. */
 
-static void dump_intr_frame (struct intr_frame *);
-
 /* Handler for all interrupts, faults, and exceptions.  This
    function is called by the assembly language interrupt stubs in
-   intr-stubs.S (see intr-stubs.pl).  ARGS describes the
+   intr-stubs.S (see intr-stubs.pl).  FRAME describes the
    interrupt and the interrupted thread's registers. */
 void
-intr_handler (struct intr_frame *args
+intr_handler (struct intr_frame *frame
 {
   bool external;
   intr_handler_func *handler;
 
-  external = args->vec_no >= 0x20 && args->vec_no < 0x30;
+  external = frame->vec_no >= 0x20 && frame->vec_no < 0x30;
   if (external) 
     {
       ASSERT (intr_get_level () == INTR_OFF);
@@ -314,10 +312,13 @@ intr_handler (struct intr_frame *args)
   /* Invoke the interrupt's handler.
      If there is no handler, invoke the unexpected interrupt
      handler. */
-  handler = intr_handlers[args->vec_no];
+  handler = intr_handlers[frame->vec_no];
   if (handler == NULL)
-    handler = panic;
-  handler (args);
+    {
+      intr_dump_frame (frame);
+      PANIC ("Unexpected interrupt");
+    }
+  handler (frame);
 
   /* Complete the processing of an external interrupt. */
   if (external) 
@@ -326,65 +327,16 @@ intr_handler (struct intr_frame *args)
       ASSERT (intr_context ());
 
       in_external_intr = false;
-      pic_end_of_interrupt (args->vec_no); 
+      pic_end_of_interrupt (frame->vec_no); 
 
       if (yield_on_return) 
         thread_yield (); 
     }
 }
 
-/* Handler for an interrupt that should not have been invoked. */
-static void
-panic (struct intr_frame *regs) 
-{
-  dump_intr_frame (regs);
-  PANIC ("Panic!");
-}
-
-/* Handler for an exception (probably) caused by a user process. */
-static void
-kill (struct intr_frame *f) 
-{
-  /* This interrupt is one (probably) caused by a user process.
-     For example, the process might have tried to access unmapped
-     virtual memory (a page fault).  For now, we simply kill the
-     user process.  Later, we'll want to handle page faults in
-     the kernel.  Real Unix-like operating systems pass most
-     exceptions back to the process via signals, but we don't
-     implement them. */
-     
-  /* The interrupt frame's code segment value tells us where the
-     exception originated. */
-  switch (f->cs)
-    {
-    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_names[f->vec_no]);
-      dump_intr_frame (f);
-      thread_exit (); 
-
-    case SEL_KCSEG:
-      /* 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.) */
-      printk ("Kernel bug - unexpected interrupt in kernel\n");
-      panic (f);
-
-    default:
-      /* Some other code segment?  Shouldn't happen. */
-      printk ("Interrupt %#04x (%s) in unknown segment %04x\n",
-             f->vec_no, intr_names[f->vec_no], f->cs);
-      thread_exit ();
-    }
-}
-
 /* Dumps interrupt frame F to the console, for debugging. */
-static void
-dump_intr_frame (struct intr_frame *f) 
+void
+intr_dump_frame (const struct intr_frame *f) 
 {
   uint32_t cr2, ss;
   asm ("movl %%cr2, %0" : "=r" (cr2));
@@ -401,3 +353,9 @@ dump_intr_frame (struct intr_frame *f)
           f->cs, f->ds, f->es, f->cs != SEL_KCSEG ? f->ss : ss);
 }
 
+/* Returns the name of interrupt VEC. */
+const char *
+intr_name (uint8_t vec) 
+{
+  return intr_names[vec];
+}
index fe26b3d521abd6e3c6be3b692347788d4cfa54ba..40d7b6e139671b584d54ef7fdd5ce49ed25e11d9 100644 (file)
@@ -56,4 +56,7 @@ void intr_register (uint8_t vec, int dpl, enum intr_level, intr_handler_func *,
 bool intr_context (void);
 void intr_yield_on_return (void);
 
+void intr_dump_frame (const struct intr_frame *);
+const char *intr_name (uint8_t vec);
+
 #endif /* interrupt.h */
index d9e0dd35508fee62e9d3f8b758525bf4855c82c6..22eaf59d339f4ee216e77cf9938f82140a5f6b70 100755 (executable)
@@ -1,7 +1,7 @@
 #! /usr/bin/perl
 
 print <<'EOF';
-#include "gdt.h"
+#include "loader.h"
 
        .data
 .globl intr_stubs
index 41289d1281f82ac3ccd6a62a2199f9ecbbaa2d8c..4e4fbb4c3e162ace5f0c0d6d1477a92d99e49bbd 100644 (file)
@@ -1,6 +1,5 @@
 #include "loader.h"
 #include "mmu.h"
-#include "gdt.h"
        
 ##############################################################################
 # Kernel loader.
index 9507a6b4ad4426fc0e8e1ce6f1337950cfd09927..f5d7a9ba794bf2e045789deea123ef37541f122d 100644 (file)
                                                 /* Kernel command line. */
 #define LOADER_RAM_PAGES (LOADER_CMD_LINE - 4)  /* # of pages of RAM. */
 
+/* GDT selectors defined by loader.
+   More selectors are defined by userprog/gdt.h. */
+#define SEL_NULL        0x00    /* Null selector. */
+#define SEL_KCSEG       0x08    /* Kernel code selector. */
+#define SEL_KDSEG       0x10    /* Kernel data selector. */
+
 #endif /* loader.h */
index c9bc8ee2bb8c9fc75bfc799c10e615fd033a2fe3..d89edd6c8dcad5dcbe9cae5f21c2273c19be1f4d 100644 (file)
@@ -4,11 +4,13 @@
 #include "interrupt.h"
 #include "intr-stubs.h"
 #include "lib.h"
-#include "gdt.h"
 #include "mmu.h"
 #include "palloc.h"
 #include "random.h"
 #include "switch.h"
+#ifdef USERPROG
+#include "gdt.h"
+#endif
 
 #define THREAD_MAGIC 0x1234abcdu
 
diff --git a/src/threads/tss.c b/src/threads/tss.c
deleted file mode 100644 (file)
index 37d075b..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#include "tss.h"
-#include <stddef.h>
-#include "debug.h"
-#include "gdt.h"
-#include "mmu.h"
-#include "palloc.h"
-
-struct tss
-  {
-    uint16_t back_link, :16;
-    void *esp0;
-    uint16_t ss0, :16;
-    void *esp1;
-    uint16_t ss1, :16;
-    void *esp2;
-    uint16_t ss2, :16;
-    uint32_t cr3;
-    void (*eip) (void);
-    uint32_t eflags;
-    uint32_t eax, ecx, edx, ebx;
-    uint32_t esp, ebp, esi, edi;
-    uint16_t es, :16;
-    uint16_t cs, :16;
-    uint16_t ss, :16;
-    uint16_t ds, :16;
-    uint16_t fs, :16;
-    uint16_t gs, :16;
-    uint16_t ldt, :16;
-    uint16_t trace, bitmap;
-  };
-
-static struct tss *tss;
-
-void
-tss_init (void) 
-{
-  /* Our TSS is never used in a call gate or task gate, so only a
-     few fields of it are ever referenced, and those are the only
-     ones we initialize. */
-  tss = palloc_get (PAL_ASSERT | PAL_ZERO);
-  tss->esp0 = ptov(0x20000);
-  tss->ss0 = SEL_KDSEG;
-  tss->bitmap = 0xdfff;
-}
-
-struct tss *
-tss_get (void) 
-{
-  ASSERT (tss != NULL);
-  return tss;
-}
-
-void
-tss_set_esp0 (uint8_t *esp0) 
-{
-  ASSERT (tss != NULL);
-  tss->esp0 = esp0;
-}
diff --git a/src/threads/tss.h b/src/threads/tss.h
deleted file mode 100644 (file)
index a5476c7..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef HEADER_TSS_H
-#define HEADER_TSS_H
-
-#include <stdint.h>
-
-struct tss;
-void tss_init (void);
-struct tss *tss_get (void);
-void tss_set_esp0 (uint8_t *);
-
-#endif /* tss.h */
diff --git a/src/userprog/exception.c b/src/userprog/exception.c
new file mode 100644 (file)
index 0000000..eaa0691
--- /dev/null
@@ -0,0 +1,94 @@
+#include "exception.h"
+#include "lib.h"
+#include "gdt.h"
+#include "interrupt.h"
+#include "thread.h"
+
+static void kill (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
+   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.
+
+   Page faults are an exception.  Here they are treated the same
+   way as other exceptions, but this will need to change to
+   implement virtual memory.
+
+   Refer to [IA32-v3] section 5.14 for a description of each of
+   these exceptions. */
+void
+exception_init (void) 
+{
+  /* These exceptions can be raised explicitly by a user program,
+     e.g. via the INT, INT3, INTO, and BOUND instructions.  Thus,
+     we set DPL==3, meaning that user programs are allowed to
+     invoke them via these instructions. */
+  intr_register (3, 3, INTR_ON, kill, "#BP Breakpoint Exception");
+  intr_register (4, 3, INTR_ON, kill, "#OF Overflow Exception");
+  intr_register (5, 3, INTR_ON, kill, "#BR BOUND Range Exceeded Exception");
+
+  /* These exceptions have DPL==0, preventing user processes from
+     invoking them via the INT instruction.  They can still be
+     caused indirectly, e.g. #DE can be caused by dividing by
+     0.  */
+  intr_register (0, 0, INTR_ON, kill, "#DE Divide Error");
+  intr_register (1, 0, INTR_ON, kill, "#DB Debug Exception");
+  intr_register (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception");
+  intr_register (7, 0, INTR_ON, kill, "#NM Device Not Available Exception");
+  intr_register (11, 0, INTR_ON, kill, "#NP Segment Not Present");
+  intr_register (12, 0, INTR_ON, kill, "#SS Stack Fault Exception");
+  intr_register (13, 0, INTR_ON, kill, "#GP General Protection Exception");
+  intr_register (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error");
+  intr_register (19, 0, INTR_ON, kill, "#XF SIMD Floating-Point Exception");
+
+  /* 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");
+}
+
+/* Handler for an exception (probably) caused by a user process. */
+static void
+kill (struct intr_frame *f) 
+{
+  /* This interrupt is one (probably) caused by a user process.
+     For example, the process might have tried to access unmapped
+     virtual memory (a page fault).  For now, we simply kill the
+     user process.  Later, we'll want to handle page faults in
+     the kernel.  Real Unix-like operating systems pass most
+     exceptions back to the process via signals, but we don't
+     implement them. */
+     
+  /* The interrupt frame's code segment value tells us where the
+     exception originated. */
+  switch (f->cs)
+    {
+    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));
+      intr_dump_frame (f);
+      thread_exit (); 
+
+    case SEL_KCSEG:
+      /* 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.) */
+      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",
+             f->vec_no, intr_name (f->vec_no), f->cs);
+      thread_exit ();
+    }
+}
+
diff --git a/src/userprog/exception.h b/src/userprog/exception.h
new file mode 100644 (file)
index 0000000..37b6142
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef HEADER_EXCEPTION_H
+#define HEADER_EXCEPTION_H 1
+
+void exception_init (void);
+
+#endif /* exception.h */
diff --git a/src/userprog/gdt.c b/src/userprog/gdt.c
new file mode 100644 (file)
index 0000000..e3e1dde
--- /dev/null
@@ -0,0 +1,142 @@
+#include "gdt.h"
+#include "debug.h"
+#include "mmu.h"
+#include "palloc.h"
+#include "tss.h"
+
+/* The Global Descriptor Table (GDT).
+
+   The GDT, an x86-specific structure, defines segments that can
+   potentially be used by all processes in a system, subject to
+   their permissions.  There is also a per-process Local
+   Descriptor Table (LDT) but that is not used by modern
+   operating systems.
+
+   Each entry in the GDT, which is known by its byte offset in
+   the table, identifies a segment.  For our purposes only three
+   types of segments are of interest: code, data, and TSS or
+   Task-State Segment descriptors.  The former two types are
+   exactly what they sound like.  The TSS is used primarily for
+   stack switching on interrupts.
+
+   For more information on the GDT as used here, refer to
+   [IA32-v3] sections 3.2 through 3.5. */
+static uint64_t gdt[SEL_CNT];
+
+/* GDT helpers. */
+static uint64_t make_code_desc (int dpl);
+static uint64_t make_data_desc (int dpl);
+static uint64_t make_tss_desc (void *laddr);
+static uint64_t make_gdtr_operand (uint16_t limit, void *base);
+
+/* Sets up a proper GDT.  The bootstrap loader's GDT didn't
+   include user-mode selectors or a TSS, but we need both now. */
+void
+gdt_init (void)
+{
+  uint64_t gdtr_operand;
+
+  /* Initialize GDT. */
+  gdt[SEL_NULL / sizeof *gdt] = 0;
+  gdt[SEL_KCSEG / sizeof *gdt] = make_code_desc (0);
+  gdt[SEL_KDSEG / sizeof *gdt] = make_data_desc (0);
+  gdt[SEL_UCSEG / sizeof *gdt] = make_code_desc (3);
+  gdt[SEL_UDSEG / sizeof *gdt] = make_data_desc (3);
+  gdt[SEL_TSS / sizeof *gdt] = make_tss_desc (tss_get ());
+
+  /* Load GDTR, TR. */
+  gdtr_operand = make_gdtr_operand (sizeof gdt - 1, gdt);
+  asm volatile ("lgdt %0" :: "m" (gdtr_operand));
+  asm volatile ("ltr %w0" :: "r" (SEL_TSS));
+}
+\f
+/* System segment or code/data segment? */
+enum seg_class
+  {
+    CLS_SYSTEM = 0,             /* System segment. */
+    CLS_CODE_DATA = 1           /* Code or data segment. */
+  };
+
+/* Limit has byte or 4 kB page granularity? */
+enum seg_granularity
+  {
+    GRAN_BYTE = 0,              /* Limit has 1-byte granularity. */
+    GRAN_PAGE = 1               /* Limit has 4 kB granularity. */
+  };
+
+/* Returns a segment descriptor with the given 32-bit BASE and
+   20-bit LIMIT (whose interpretation depends on GRANULARITY).
+   The descriptor represents a system or code/data segment
+   according to CLASS, and TYPE is its type (whose interpretation
+   depends on the class).
+
+   The segment has descriptor privilege level DPL, meaning that
+   it can be used in rings numbered DPL or lower.  In practice,
+   DPL==3 means that user processes can use the segment and
+   DPL==0 means that only the kernel can use the segment.  See
+   [IA32-v3] section 4.5 for further discussion. */
+static uint64_t
+make_seg_desc (uint32_t base,
+               uint32_t limit,
+               enum seg_class class,
+               int type,
+               int dpl,
+               enum seg_granularity granularity)
+{
+  uint32_t e0, e1;
+
+  ASSERT (limit <= 0xfffff);
+  ASSERT (class == CLS_SYSTEM || class == CLS_CODE_DATA);
+  ASSERT (type >= 0 && type <= 15);
+  ASSERT (dpl >= 0 && dpl <= 3);
+  ASSERT (granularity == GRAN_BYTE || granularity == GRAN_PAGE);
+
+  e0 = ((limit & 0xffff)             /* Limit 15:0. */
+        | (base << 16));             /* Base 15:0. */
+
+  e1 = (((base >> 16) & 0xff)        /* Base 23:16. */
+        | (type << 8)                /* Segment type. */
+        | (class << 12)              /* 0=system, 1=code/data. */
+        | (dpl << 13)                /* Descriptor privilege. */
+        | (1 << 15)                  /* Present. */
+        | (limit & 0xf0000)          /* Limit 16:19. */
+        | (1 << 22)                  /* 32-bit segment. */
+        | (granularity << 23)        /* Byte/page granularity. */
+        | (base & 0xff000000));      /* Base 31:24. */
+
+  return e0 | ((uint64_t) e1 << 32);
+}
+
+/* Returns a descriptor for a readable code segment with base at
+   0, a limit of 4 GB, and the given DPL. */
+static uint64_t
+make_code_desc (int dpl)
+{
+  return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 10, dpl, GRAN_PAGE);
+}
+
+/* Returns a descriptor for a writable data segment with base at
+   0, a limit of 4 GB, and the given DPL. */
+static uint64_t
+make_data_desc (int dpl)
+{
+  return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 2, dpl, GRAN_PAGE);
+}
+
+/* Returns a descriptor for an "available" 32-bit Task-State
+   Segment with its base at the given linear address, a limit of
+   0x67 bytes (the size of a 32-bit TSS), and a DPL of 0. */
+static uint64_t
+make_tss_desc (void *laddr)
+{
+  return make_seg_desc ((uint32_t) laddr, 0x67, CLS_SYSTEM, 9, 0, GRAN_BYTE);
+}
+
+
+/* Returns a descriptor that yields the given LIMIT and BASE when
+   used as an operand for the LGDT instruction. */
+static uint64_t
+make_gdtr_operand (uint16_t limit, void *base)
+{
+  return limit | ((uint64_t) (uint32_t) base << 16);
+}
diff --git a/src/userprog/gdt.h b/src/userprog/gdt.h
new file mode 100644 (file)
index 0000000..ae89681
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef HEADER_GDT_H
+#define HEADER_GDT_H 1
+
+#include "loader.h"
+
+/* Segment selectors.
+   More selectors are defined by the loader in loader.h. */
+#define SEL_UCSEG       0x1B    /* User code selector. */
+#define SEL_UDSEG       0x23    /* User data selector. */
+#define SEL_TSS         0x28    /* Task-state segment. */
+#define SEL_CNT         6       /* Number of segments. */
+
+void gdt_init (void);
+
+#endif /* gdt.h */
diff --git a/src/userprog/tss.c b/src/userprog/tss.c
new file mode 100644 (file)
index 0000000..37d075b
--- /dev/null
@@ -0,0 +1,58 @@
+#include "tss.h"
+#include <stddef.h>
+#include "debug.h"
+#include "gdt.h"
+#include "mmu.h"
+#include "palloc.h"
+
+struct tss
+  {
+    uint16_t back_link, :16;
+    void *esp0;
+    uint16_t ss0, :16;
+    void *esp1;
+    uint16_t ss1, :16;
+    void *esp2;
+    uint16_t ss2, :16;
+    uint32_t cr3;
+    void (*eip) (void);
+    uint32_t eflags;
+    uint32_t eax, ecx, edx, ebx;
+    uint32_t esp, ebp, esi, edi;
+    uint16_t es, :16;
+    uint16_t cs, :16;
+    uint16_t ss, :16;
+    uint16_t ds, :16;
+    uint16_t fs, :16;
+    uint16_t gs, :16;
+    uint16_t ldt, :16;
+    uint16_t trace, bitmap;
+  };
+
+static struct tss *tss;
+
+void
+tss_init (void) 
+{
+  /* Our TSS is never used in a call gate or task gate, so only a
+     few fields of it are ever referenced, and those are the only
+     ones we initialize. */
+  tss = palloc_get (PAL_ASSERT | PAL_ZERO);
+  tss->esp0 = ptov(0x20000);
+  tss->ss0 = SEL_KDSEG;
+  tss->bitmap = 0xdfff;
+}
+
+struct tss *
+tss_get (void) 
+{
+  ASSERT (tss != NULL);
+  return tss;
+}
+
+void
+tss_set_esp0 (uint8_t *esp0) 
+{
+  ASSERT (tss != NULL);
+  tss->esp0 = esp0;
+}
diff --git a/src/userprog/tss.h b/src/userprog/tss.h
new file mode 100644 (file)
index 0000000..a5476c7
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef HEADER_TSS_H
+#define HEADER_TSS_H
+
+#include <stdint.h>
+
+struct tss;
+void tss_init (void);
+struct tss *tss_get (void);
+void tss_set_esp0 (uint8_t *);
+
+#endif /* tss.h */