Use macros for 8259A PIC registers, instead of writing them literally.
[pintos-anon] / src / threads / interrupt.c
index eb10cd603669cabfa417fd48d9dd87f227945a17..9ffd873540b803ca3414b83f10c6da4e8c4b2f54 100644 (file)
@@ -1,19 +1,30 @@
-#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"
+
+/* Programmable Interrupt Controller (PIC) registers.
+   A PC has two PICs, called the master and slave PICs, with the
+   slave attached ("cascaded") to the master IRQ line 2. */
+#define PIC0_CTRL      0x20    /* Master PIC control register address. */
+#define PIC0_DATA      0x21    /* Master PIC data register address. */
+#define PIC1_CTRL      0xa0    /* Slave PIC control register address. */
+#define PIC1_DATA      0xa1    /* Slave PIC data register address. */
 
 /* 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. */
@@ -49,8 +60,12 @@ enum intr_level
 intr_get_level (void) 
 {
   uint32_t flags;
 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;
 }
@@ -68,7 +83,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;
 }
 
@@ -77,7 +99,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
@@ -93,11 +120,13 @@ intr_init (void)
 
   /* Initialize IDT. */
   for (i = 0; i < INTR_CNT; i++)
 
   /* Initialize IDT. */
   for (i = 0; i < INTR_CNT; i++)
-    idt[i] = make_trap_gate (intr_stubs[i], 0);
+    idt[i] = make_intr_gate (intr_stubs[i], 0);
 
 
-  /* Load IDT register. */
+  /* 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);
   idtr_operand = make_idtr_operand (sizeof idt - 1, idt);
-  asm volatile ("lidt %0" :: "m" (idtr_operand));
+  asm volatile ("lidt %0" : : "m" (idtr_operand));
 
   /* Initialize intr_names. */
   for (i = 0; i < INTR_CNT; i++)
 
   /* Initialize intr_names. */
   for (i = 0; i < INTR_CNT; i++)
@@ -116,15 +145,44 @@ intr_init (void)
   intr_names[11] = "#NP Segment Not Present";
   intr_names[12] = "#SS Stack Fault Exception";
   intr_names[13] = "#GP General Protection 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";
 }
 
   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
-   NAME for debugging purposes.  The interrupt handler will be
-   invoked with interrupt status set to LEVEL.
+/* 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 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
@@ -132,29 +190,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
@@ -178,42 +222,35 @@ intr_yield_on_return (void)
 \f
 /* 8259A Programmable Interrupt Controller. */
 
 \f
 /* 8259A Programmable Interrupt Controller. */
 
-/* Every PC has two 8259A Programmable Interrupt Controller (PIC)
-   chips.  One is a "master" accessible at ports 0x20 and 0x21.
-   The other is a "slave" cascaded onto the master's IRQ 2 line
-   and accessible at ports 0xa0 and 0xa1.  Accesses to port 0x20
-   set the A0 line to 0 and accesses to 0x21 set the A1 line to
-   1.  The situation is similar for the slave PIC.
+/* Initializes the PICs.  Refer to [8259A] for details.
 
    By default, interrupts 0...15 delivered by the PICs will go to
 
    By default, interrupts 0...15 delivered by the PICs will go to
-   interrupt vectors 0...15.  Unfortunately, those vectors are
-   also used for CPU traps and exceptions.  We reprogram the PICs
-   so that interrupts 0...15 are delivered to interrupt vectors
-   32...47 (0x20...0x2f) instead. */
-
-/* Initializes the PICs.  Refer to [8259A] for details. */
+   interrupt vectors 0...15.  Those vectors are also used for CPU
+   traps and exceptions, so we reprogram the PICs so that
+   interrupts 0...15 are delivered to interrupt vectors 32...47
+   (0x20...0x2f) instead. */
 static void
 pic_init (void)
 {
   /* Mask all interrupts on both PICs. */
 static void
 pic_init (void)
 {
   /* Mask all interrupts on both PICs. */
-  outb (0x21, 0xff);
-  outb (0xa1, 0xff);
+  outb (PIC0_DATA, 0xff);
+  outb (PIC1_DATA, 0xff);
 
   /* Initialize master. */
 
   /* Initialize master. */
-  outb (0x20, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
-  outb (0x21, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */
-  outb (0x21, 0x04); /* ICW3: slave PIC on line IR2. */
-  outb (0x21, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
+  outb (PIC0_CTRL, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
+  outb (PIC0_DATA, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */
+  outb (PIC0_DATA, 0x04); /* ICW3: slave PIC on line IR2. */
+  outb (PIC0_DATA, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
 
   /* Initialize slave. */
 
   /* Initialize slave. */
-  outb (0xa0, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
-  outb (0xa1, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */
-  outb (0xa1, 0x02); /* ICW3: slave ID is 2. */
-  outb (0xa1, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
+  outb (PIC1_CTRL, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
+  outb (PIC1_DATA, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */
+  outb (PIC1_DATA, 0x02); /* ICW3: slave ID is 2. */
+  outb (PIC1_DATA, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
 
   /* Unmask all interrupts. */
 
   /* Unmask all interrupts. */
-  outb (0x21, 0x00);
-  outb (0xa1, 0x00);
+  outb (PIC0_DATA, 0x00);
+  outb (PIC1_DATA, 0x00);
 }
 
 /* Sends an end-of-interrupt signal to the PIC for the given IRQ.
 }
 
 /* Sends an end-of-interrupt signal to the PIC for the given IRQ.
@@ -239,13 +276,15 @@ 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.
 
    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
    for further 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-v3] section 5.12.1.2 for discussion. */
+   [IA32-v3a] section 5.12.1.2 "Flag Usage By Exception- or
+   Interrupt-Handler Procedure" for discussion. */
 static uint64_t
 make_gate (void (*function) (void), int dpl, int type)
 {
 static uint64_t
 make_gate (void (*function) (void), int dpl, int type)
 {
@@ -295,14 +334,18 @@ make_idtr_operand (uint16_t limit, void *base)
 
 /* 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).  FRAME describes the
-   interrupt and the interrupted thread's registers. */
+   intr-stubs.S.  FRAME describes the interrupt and the
+   interrupted thread's registers. */
 void
 intr_handler (struct intr_frame *frame) 
 {
   bool external;
   intr_handler_func *handler;
 
 void
 intr_handler (struct intr_frame *frame) 
 {
   bool external;
   intr_handler_func *handler;
 
+  /* 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) 
     {
   external = frame->vec_no >= 0x20 && frame->vec_no < 0x30;
   if (external) 
     {
@@ -313,16 +356,23 @@ intr_handler (struct intr_frame *frame)
       yield_on_return = false;
     }
 
       yield_on_return = false;
     }
 
-  /* Invoke the interrupt's handler.
-     If there is no handler, invoke the unexpected interrupt
-     handler. */
+  /* Invoke the interrupt's handler. */
   handler = intr_handlers[frame->vec_no];
   handler = intr_handlers[frame->vec_no];
-  if (handler == NULL)
+  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);
       intr_dump_frame (frame);
-      PANIC ("Unexpected interrupt");
+      PANIC ("Unexpected interrupt"); 
     }
     }
-  handler (frame);
 
   /* Complete the processing of an external interrupt. */
   if (external) 
 
   /* Complete the processing of an external interrupt. */
   if (external) 
@@ -342,19 +392,24 @@ intr_handler (struct intr_frame *frame)
 void
 intr_dump_frame (const 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. */
 }
 
 /* Returns the name of interrupt VEC. */