Add memory clobbers to several asm statements,
[pintos-anon] / src / threads / interrupt.c
index 268e3b530c4c5ab16034e134db28f49ad3bf41f0..3e37213876768fcded5b4ad671636671231e3ea4 100644 (file)
@@ -1,19 +1,22 @@
-#include "interrupt.h"
+#include "threads/interrupt.h"
+#include <debug.h>
 #include <inttypes.h>
 #include <stdint.h>
 #include <inttypes.h>
 #include <stdint.h>
-#include "intr-stubs.h"
-#include "debug.h"
-#include "io.h"
-#include "lib.h"
-#include "mmu.h"
-#include "thread.h"
-#include "timer.h"
+#include <stdio.h>
+#include "threads/flags.h"
+#include "threads/intr-stubs.h"
+#include "threads/io.h"
+#include "threads/thread.h"
+#include "threads/vaddr.h"
+#include "devices/timer.h"
 
 /* Number of x86 interrupts. */
 #define INTR_CNT 256
 
 /* The Interrupt Descriptor Table (IDT).  The format is fixed by
 
 /* Number of x86 interrupts. */
 #define INTR_CNT 256
 
 /* The Interrupt Descriptor Table (IDT).  The format is fixed by
-   the CPU.  See [IA32-v3] sections 5.10, 5.11, 5.12.1.2. */
+   the CPU.  See [IA32-v3a] sections 5.10 "Interrupt Descriptor
+   Table (IDT)", 5.11 "IDT Descriptors", 5.12.1.2 "Flag Usage By
+   Exception- or Interrupt-Handler Procedure". */
 static uint64_t idt[INTR_CNT];
 
 /* Interrupt handler functions for each interrupt. */
 static uint64_t idt[INTR_CNT];
 
 /* Interrupt handler functions for each interrupt. */
@@ -39,19 +42,22 @@ 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);
 /* 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);
 
 /* 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
 intr_get_level (void) 
 {
   uint32_t flags;
 \f
 /* Returns the current interrupt status. */
 enum intr_level
 intr_get_level (void) 
 {
   uint32_t flags;
-  
-  asm ("pushfl; popl %0" : "=g" (flags));
+
+  /* Push the flags register on the processor stack, then pop the
+     value off the stack into `flags'.  See [IA32-v2b] "PUSHF"
+     and "POP" and [IA32-v3a] 5.8.1 "Masking Maskable Hardware
+     Interrupts". */
+  asm volatile ("pushfl; popl %0" : "=g" (flags));
 
   return flags & FLAG_IF ? INTR_ON : INTR_OFF;
 }
 
   return flags & FLAG_IF ? INTR_ON : INTR_OFF;
 }
@@ -69,7 +75,14 @@ enum intr_level
 intr_enable (void) 
 {
   enum intr_level old_level = intr_get_level ();
 intr_enable (void) 
 {
   enum intr_level old_level = intr_get_level ();
+  ASSERT (!intr_context ());
+
+  /* Enable interrupts by setting the interrupt flag.
+
+     See [IA32-v2b] "STI" and [IA32-v3a] 5.8.1 "Masking Maskable
+     Hardware Interrupts". */
   asm volatile ("sti");
   asm volatile ("sti");
+
   return old_level;
 }
 
   return old_level;
 }
 
@@ -78,7 +91,12 @@ enum intr_level
 intr_disable (void) 
 {
   enum intr_level old_level = intr_get_level ();
 intr_disable (void) 
 {
   enum intr_level old_level = intr_get_level ();
-  asm volatile ("cli");
+
+  /* Disable interrupts by clearing the interrupt flag.
+     See [IA32-v2b] "CLI" and [IA32-v3a] 5.8.1 "Masking Maskable
+     Hardware Interrupts". */
+  asm volatile ("cli" : : : "memory");
+
   return old_level;
 }
 \f
   return old_level;
 }
 \f
@@ -89,45 +107,74 @@ intr_init (void)
   uint64_t idtr_operand;
   int i;
 
   uint64_t idtr_operand;
   int i;
 
+  /* Initialize interrupt controller. */
   pic_init ();
 
   pic_init ();
 
+  /* Initialize IDT. */
+  for (i = 0; i < INTR_CNT; i++)
+    idt[i] = make_intr_gate (intr_stubs[i], 0);
+
+  /* Load IDT register.
+     See [IA32-v2a] "LIDT" and [IA32-v3a] 5.10 "Interrupt
+     Descriptor Table (IDT)". */
+  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";
   /* Initialize intr_names. */
   for (i = 0; i < INTR_CNT; i++)
     intr_names[i] = "unknown";
+  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[14] = "#PF Page-Fault 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";
+}
 
 
-  /* 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));
+/* Registers interrupt VEC_NO to invoke HANDLER with descriptor
+   privilege level DPL.  Names the interrupt NAME for debugging
+   purposes.  The interrupt handler will be invoked with
+   interrupt status set to LEVEL. */
+static void
+register_handler (uint8_t vec_no, int dpl, enum intr_level level,
+                  intr_handler_func *handler, const char *name)
+{
+  ASSERT (intr_handlers[vec_no] == NULL);
+  if (level == INTR_ON)
+    idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
+  else
+    idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
+  intr_handlers[vec_no] = handler;
+  intr_names[vec_no] = name;
+}
+
+/* Registers external interrupt VEC_NO to invoke HANDLER, which
+   is named NAME for debugging purposes.  The handler will
+   execute with interrupts disabled. */
+void
+intr_register_ext (uint8_t vec_no, intr_handler_func *handler,
+                   const char *name) 
+{
+  ASSERT (vec_no >= 0x20 && vec_no <= 0x2f);
+  register_handler (vec_no, 0, INTR_OFF, handler, name);
 }
 
 }
 
-/* Registers interrupt VEC_NO to invoke HANDLER, which is named
-   NAME for debugging purposes.  The interrupt handler will be
-   invoked with interrupt status set to LEVEL.
+/* Registers internal interrupt VEC_NO to invoke HANDLER, which
+   is named NAME for debugging purposes.  The interrupt handler
+   will be invoked with interrupt status LEVEL.
 
    The handler will have descriptor privilege level DPL, meaning
    that it can be invoked intentionally when the processor is in
 
    The handler will have descriptor privilege level DPL, meaning
    that it can be invoked intentionally when the processor is in
@@ -135,29 +182,15 @@ intr_init (void)
    user mode to invoke the interrupts and DPL==0 prevents such
    invocation.  Faults and exceptions that occur in user mode
    still cause interrupts with DPL==0 to be invoked.  See
    user mode to invoke the interrupts and DPL==0 prevents such
    invocation.  Faults and exceptions that occur in user mode
    still cause interrupts with DPL==0 to be invoked.  See
-   [IA32-v3] sections 4.5 and 4.8.1.1 for further discussion. */
+   [IA32-v3a] sections 4.5 "Privilege Levels" and 4.8.1.1
+   "Accessing Nonconforming Code Segments" for further
+   discussion. */
 void
 void
-intr_register (uint8_t vec_no, int dpl, enum intr_level level,
-               intr_handler_func *handler,
-               const char *name) 
+intr_register_int (uint8_t vec_no, int dpl, enum intr_level level,
+                   intr_handler_func *handler, const char *name)
 {
 {
-  /* Make sure this handler isn't already registered to someone
-     else. */
-  ASSERT (intr_handlers[vec_no] == NULL);
-
-  /* Interrupts generated by external hardware (0x20 <= VEC_NO <=
-     0x2f) should specify INTR_OFF for LEVEL.  Otherwise a timer
-     interrupt could cause a task switch during interrupt
-     handling.  Most other interrupts can and should be handled
-     with interrupts enabled. */
-  ASSERT (vec_no < 0x20 || vec_no > 0x2f || level == INTR_OFF);
-
-  if (level == INTR_ON)
-    idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
-  else
-    idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
-  intr_handlers[vec_no] = handler;
-  intr_names[vec_no] = name;
+  ASSERT (vec_no < 0x20 || vec_no > 0x2f);
+  register_handler (vec_no, dpl, level, handler, name);
 }
 
 /* Returns true during processing of an external interrupt
 }
 
 /* Returns true during processing of an external interrupt
@@ -242,25 +275,33 @@ pic_end_of_interrupt (int irq)
    or lower-numbered ring.  In practice, DPL==3 allows user mode
    to call into the gate and DPL==0 prevents such calls.  Faults
    and exceptions that occur in user mode still cause gates with
    or lower-numbered ring.  In practice, DPL==3 allows user mode
    to call into the gate and DPL==0 prevents such calls.  Faults
    and exceptions that occur in user mode still cause gates with
-   DPL==0 to be invoked.  See [IA32-v3] sections 4.5 and 4.8.1.1
+   DPL==0 to be invoked.  See [IA32-v3a] sections 4.5 "Privilege
+   Levels" and 4.8.1.1 "Accessing Nonconforming Code Segments"
    for further discussion.
 
    for further discussion.
 
-   TYPE must be either TYPE_INT_32 (for an interrupt gate) or
-   TYPE_TRAP_32 (for a trap gate).  The difference is that
-   entering an interrupt gate disables interrupts, but entering a
-   trap gate does not.  See [IA32-v3] section 5.12.1.2 for
-   discussion. */
+   TYPE must be either 14 (for an interrupt gate) or 15 (for a
+   trap gate).  The difference is that entering an interrupt gate
+   disables interrupts, but entering a trap gate does not.  See
+   [IA32-v3a] section 5.12.1.2 "Flag Usage By Exception- or
+   Interrupt-Handler Procedure" for discussion. */
 static uint64_t
 static uint64_t
-make_gate (void (*function) (void), int dpl, enum seg_type type)
+make_gate (void (*function) (void), int dpl, int type)
 {
 {
-  uint32_t offset = (uint32_t) function;
-  uint32_t e0 = ((offset & 0xffff)            /* Offset 15:0. */
-                 | (SEL_KCSEG << 16));        /* Target code segment. */
-  uint32_t e1 = ((offset & 0xffff0000)        /* Offset 31:16. */
-                 | (1 << 15)                  /* Present. */
-                 | ((uint32_t) dpl << 13)     /* Descriptor privilege. */
-                 | (SYS_SYSTEM << 12)         /* System. */
-                 | ((uint32_t) type << 8));   /* Gate type. */
+  uint32_t e0, e1;
+
+  ASSERT (function != NULL);
+  ASSERT (dpl >= 0 && dpl <= 3);
+  ASSERT (type >= 0 && type <= 15);
+
+  e0 = (((uint32_t) function & 0xffff)     /* Offset 15:0. */
+        | (SEL_KCSEG << 16));              /* Target code segment. */
+
+  e1 = (((uint32_t) function & 0xffff0000) /* Offset 31:16. */
+        | (1 << 15)                        /* Present. */
+        | ((uint32_t) dpl << 13)           /* Descriptor privilege level. */
+        | (0 << 12)                        /* System. */
+        | ((uint32_t) type << 8));         /* Gate type. */
+
   return e0 | ((uint64_t) e1 << 32);
 }
 
   return e0 | ((uint64_t) e1 << 32);
 }
 
@@ -269,7 +310,7 @@ make_gate (void (*function) (void), int dpl, enum seg_type type)
 static uint64_t
 make_intr_gate (void (*function) (void), int dpl)
 {
 static uint64_t
 make_intr_gate (void (*function) (void), int dpl)
 {
-  return make_gate (function, dpl, TYPE_INT_32);
+  return make_gate (function, dpl, 14);
 }
 
 /* Creates a trap gate that invokes FUNCTION with the given
 }
 
 /* Creates a trap gate that invokes FUNCTION with the given
@@ -277,24 +318,34 @@ make_intr_gate (void (*function) (void), int dpl)
 static uint64_t
 make_trap_gate (void (*function) (void), int dpl)
 {
 static uint64_t
 make_trap_gate (void (*function) (void), int dpl)
 {
-  return make_gate (function, dpl, TYPE_TRAP_32);
+  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. */
 
 }
 \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
 /* 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
-   interrupt and the interrupted thread's registers. */
+   intr-stubs.S.  FRAME describes the interrupt and the
+   interrupted thread's registers. */
 void
 void
-intr_handler (struct intr_frame *args
+intr_handler (struct intr_frame *frame
 {
   bool external;
   intr_handler_func *handler;
 
 {
   bool external;
   intr_handler_func *handler;
 
-  external = args->vec_no >= 0x20 && args->vec_no < 0x30;
+  /* External interrupts are special.
+     We only handle one at a time (so interrupts must be off)
+     and they need to be acknowledged on the PIC (see below).
+     An external interrupt handler cannot sleep. */
+  external = frame->vec_no >= 0x20 && frame->vec_no < 0x30;
   if (external) 
     {
       ASSERT (intr_get_level () == INTR_OFF);
   if (external) 
     {
       ASSERT (intr_get_level () == INTR_OFF);
@@ -304,13 +355,23 @@ intr_handler (struct intr_frame *args)
       yield_on_return = false;
     }
 
       yield_on_return = false;
     }
 
-  /* Invoke the interrupt's handler.
-     If there is no handler, invoke the unexpected interrupt
-     handler. */
-  handler = intr_handlers[args->vec_no];
-  if (handler == NULL)
-    handler = panic;
-  handler (args);
+  /* Invoke the interrupt's handler. */
+  handler = intr_handlers[frame->vec_no];
+  if (handler != NULL)
+    handler (frame);
+  else if (frame->vec_no == 0x27 || frame->vec_no == 0x2f)
+    {
+      /* There is no handler, but this interrupt can trigger
+         spuriously due to a hardware fault or hardware race
+         condition.  Ignore it. */
+    }
+  else 
+    {
+      /* No handler and not spurious.  Invoke the unexpected
+         interrupt handler. */
+      intr_dump_frame (frame);
+      PANIC ("Unexpected interrupt"); 
+    }
 
   /* Complete the processing of an external interrupt. */
   if (external) 
 
   /* Complete the processing of an external interrupt. */
   if (external) 
@@ -319,78 +380,40 @@ intr_handler (struct intr_frame *args)
       ASSERT (intr_context ());
 
       in_external_intr = false;
       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 (); 
     }
 }
 
 
       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. */
 /* 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;
+  uint32_t cr2;
+
+  /* Store current value of CR2 into `cr2'.
+     CR2 is the linear address of the last page fault.
+     See [IA32-v2a] "MOV--Move to/from Control Registers" and
+     [IA32-v3a] 5.14 "Interrupt 14--Page Fault Exception
+     (#PF)". */
   asm ("movl %%cr2, %0" : "=r" (cr2));
   asm ("movl %%cr2, %0" : "=r" (cr2));
-  asm ("movl %%ss, %0" : "=r" (ss));
 
 
-  printk ("Interrupt %#04x (%s) at eip=%p\n",
+  printf ("Interrupt %#04x (%s) at eip=%p\n",
           f->vec_no, intr_names[f->vec_no], f->eip);
           f->vec_no, intr_names[f->vec_no], f->eip);
-  printk (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
-  printk (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
+  printf (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
+  printf (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
           f->eax, f->ebx, f->ecx, f->edx);
           f->eax, f->ebx, f->ecx, f->edx);
-  printk (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
+  printf (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
           f->esi, f->edi, (uint32_t) f->esp, f->ebp);
           f->esi, f->edi, (uint32_t) f->esp, f->ebp);
-  printk (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
-          f->cs, f->ds, f->es, f->cs != SEL_KCSEG ? f->ss : ss);
+  printf (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
+          f->cs, f->ds, f->es, f->ss);
 }
 
 }
 
+/* Returns the name of interrupt VEC. */
+const char *
+intr_name (uint8_t vec) 
+{
+  return intr_names[vec];
+}