#include "interrupt.h"
+#include <inttypes.h>
#include <stdint.h>
+#include "intr-stubs.h"
#include "debug.h"
#include "io.h"
#include "lib.h"
If we don't acknowledge the IRQ, we'll never get it again, so
this is important. */
static void
-pic_eoi (void)
+pic_eoi (int irq)
{
/* FIXME? The Linux code is much more complicated. */
+ ASSERT (irq >= 0x20 && irq < 0x30);
outb (0x20, 0x20);
+ if (irq >= 0x28)
+ outb (0xa0, 0x20);
}
\f
-uint64_t idt[256];
+#define INTR_CNT 256
-extern void (*intr_stubs[256]) (void);
+static uint64_t idt[INTR_CNT];
+static intr_handler_func *intr_handlers[INTR_CNT];
+static const char *intr_names[INTR_CNT];
-intr_handler_func *intr_handlers[256];
+void intr_handler (struct intr_frame *args);
-void intr_handler (struct intr_args *args);
+static bool intr_in_progress;
+static bool yield_on_return;
-bool intr_in_progress;
-bool yield_on_return;
+const char *
+intr_name (int vec)
+{
+ if (vec < 0 || vec >= INTR_CNT || intr_names[vec] == NULL)
+ return "unknown";
+ else
+ return intr_names[vec];
+}
void
-intr_handler (struct intr_args *args)
+intr_handler (struct intr_frame *args)
{
- bool external;
-
- yield_on_return = false;
-
- external = args->vec_no >= 0x20 && args->vec_no < 0x30;
+ bool external = args->vec_no >= 0x20 && args->vec_no < 0x30;
if (external)
{
ASSERT (intr_get_level () == IF_OFF);
ASSERT (!intr_context ());
intr_in_progress = true;
+ yield_on_return = false;
}
intr_handlers[args->vec_no] (args);
ASSERT (intr_get_level () == IF_OFF);
ASSERT (intr_context ());
intr_in_progress = false;
- pic_eoi ();
- }
+ pic_eoi (args->vec_no);
- if (yield_on_return)
- {
- printk (".");
- thread_yield ();
+ if (yield_on_return)
+ thread_yield ();
}
}
void
intr_yield_on_return (void)
{
+ ASSERT (intr_context ());
yield_on_return = true;
}
-/* Handles interrupts we don't know about. */
-intr_handler_func intr_unexpected;
-
-/* Handlers for CPU exceptions. */
-intr_handler_func excp00_divide_error;
-intr_handler_func excp01_debug;
-intr_handler_func excp02_nmi;
-intr_handler_func excp03_breakpoint;
-intr_handler_func excp04_overflow;
-intr_handler_func excp05_bound;
-intr_handler_func excp06_invalid_opcode;
-intr_handler_func excp07_device_not_available;
-intr_handler_func excp08_double_fault;
-intr_handler_func excp09_coprocessor_overrun;
-intr_handler_func excp0a_invalid_tss;
-intr_handler_func excp0b_segment_not_present;
-intr_handler_func excp0c_stack_fault;
-intr_handler_func excp0d_general_protection;
-intr_handler_func excp0e_page_fault;
-intr_handler_func excp10_fp_error;
-intr_handler_func excp11_alignment;
-intr_handler_func excp12_machine_check;
-intr_handler_func excp13_simd_error;
+intr_handler_func intr_panic NO_RETURN;
+intr_handler_func intr_kill NO_RETURN;
static uint64_t
-make_intr_gate (void (*target) (void),
- int dpl)
+make_gate (void (*target) (void), int dpl, enum seg_type type)
{
uint32_t offset = (uint32_t) target;
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. */
- | (dpl << 13) /* Descriptor privilege. */
+ | ((uint32_t) dpl << 13) /* Descriptor privilege. */
| (SYS_SYSTEM << 12) /* System. */
- | (TYPE_INT_32 << 8)); /* 32-bit interrupt gate. */
+ | ((uint32_t) type << 8)); /* Gate type. */
return e0 | ((uint64_t) e1 << 32);
}
static uint64_t
-make_trap_gate (void (*target) (void),
- int dpl)
+make_intr_gate (void (*target) (void), int dpl)
{
- return make_intr_gate (target, dpl) | (1 << 8);
+ return make_gate (target, dpl, TYPE_INT_32);
+}
+
+static uint64_t
+make_trap_gate (void (*target) (void), int dpl)
+{
+ return make_gate (target, dpl, TYPE_TRAP_32);
}
-/* We don't support nested interrupts generated by external
- hardware, so these interrupts (vec_no 0x20...0x2f) should
- specify IF_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. */
void
intr_register (uint8_t vec_no, int dpl, enum if_level level,
- intr_handler_func *handler)
+ intr_handler_func *handler,
+ const char *name)
{
+ /* Interrupts generated by external hardware (0x20 <= VEC_NO <=
+ 0x2f) should specify IF_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 == IF_OFF);
+
if (level == IF_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;
}
void
/* Install default handlers. */
for (i = 0; i < 256; i++)
- intr_register (i, 0, IF_OFF, intr_unexpected);
+ intr_register (i, 0, IF_OFF, intr_panic, NULL);
/* Most exceptions require ring 0.
- Exceptions 3, 4, and 5 can be caused by ring 3 directly.
-
- Most exceptions can be handled with interrupts turned on.
+ Exceptions 3, 4, and 5 can be caused by ring 3 directly. */
+ intr_register (0, 0, IF_ON, intr_kill, "#DE Divide Error");
+ intr_register (1, 0, IF_ON, intr_kill, "#DB Debug Exception");
+ intr_register (2, 0, IF_ON, intr_panic, "NMI Interrupt");
+ intr_register (3, 3, IF_ON, intr_kill, "#BP Breakpoint Exception");
+ intr_register (4, 3, IF_ON, intr_kill, "#OF Overflow Exception");
+ intr_register (5, 3, IF_ON, intr_kill, "#BR BOUND Range Exceeded Exception");
+ intr_register (6, 0, IF_ON, intr_kill, "#UD Invalid Opcode Exception");
+ intr_register (7, 0, IF_ON, intr_kill, "#NM Device Not Available Exception");
+ intr_register (8, 0, IF_ON, intr_panic, "#DF Double Fault Exception");
+ intr_register (9, 0, IF_ON, intr_panic, "Coprocessor Segment Overrun");
+ intr_register (10, 0, IF_ON, intr_panic, "#TS Invalid TSS Exception");
+ intr_register (11, 0, IF_ON, intr_kill, "#NP Segment Not Present");
+ intr_register (12, 0, IF_ON, intr_kill, "#SS Stack Fault Exception");
+ intr_register (13, 0, IF_ON, intr_kill, "#GP General Protection Exception");
+ intr_register (16, 0, IF_ON, intr_kill, "#MF x87 FPU Floating-Point Error");
+ intr_register (17, 0, IF_ON, intr_panic, "#AC Alignment Check Exception");
+ intr_register (18, 0, IF_ON, intr_panic, "#MC Machine-Check Exception");
+ intr_register (19, 0, IF_ON, intr_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.
- */
-#if 0
- intr_register (0x00, 0, IF_ON, excp00_divide_error);
- intr_register (0x01, 0, IF_ON, excp01_debug);
- intr_register (0x02, 0, IF_ON, excp02_nmi);
- intr_register (0x03, 3, IF_ON, excp03_breakpoint);
- intr_register (0x04, 3, IF_ON, excp04_overflow);
- intr_register (0x05, 3, IF_ON, excp05_bound);
- intr_register (0x06, 0, IF_ON, excp06_invalid_opcode);
- intr_register (0x07, 0, IF_ON, excp07_device_not_available);
- intr_register (0x08, 0, IF_ON, excp08_double_fault);
- intr_register (0x09, 0, IF_ON, excp09_coprocessor_overrun);
- intr_register (0x0a, 0, IF_ON, excp0a_invalid_tss);
- intr_register (0x0b, 0, IF_ON, excp0b_segment_not_present);
- intr_register (0x0c, 0, IF_ON, excp0c_stack_fault);
- intr_register (0x0d, 0, IF_ON, excp0d_general_protection);
- intr_register (0x0e, 0, IF_OFF, excp0e_page_fault);
- intr_register (0x10, 0, IF_ON, excp10_fp_error);
- intr_register (0x11, 0, IF_ON, excp11_alignment);
- intr_register (0x12, 0, IF_ON, excp12_machine_check);
- intr_register (0x13, 0, IF_ON, excp13_simd_error);
-#endif
+ fault address is stored in CR2 and needs to be preserved. */
+ intr_register (14, 0, IF_OFF, intr_kill, "#PF Page-Fault Exception");
idtr_operand = make_dtr_operand (sizeof idt - 1, idt);
asm volatile ("lidt %0" :: "m" (idtr_operand));
}
-void
-intr_unexpected (struct intr_args *regs)
+static void
+dump_intr_frame (struct intr_frame *f)
{
- uint32_t cr2;
+ uint32_t cr2, ss;
asm ("movl %%cr2, %0" : "=r" (cr2));
- printk ("Unexpected interrupt 0x%02x, error code %08x, cr2=%08x, eip=%08x\n",
- regs->vec_no, regs->error_code, cr2, regs->eip);
- for (;;);
+ asm ("movl %%ss, %0" : "=r" (ss));
+
+ printk ("Interrupt %#04x (%s) at eip=%p\n",
+ f->vec_no, intr_name (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",
+ f->eax, f->ebx, f->ecx, f->edx);
+ printk (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
+ 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);
}
+
+void
+intr_panic (struct intr_frame *regs)
+{
+ dump_intr_frame (regs);
+ PANIC ("Panic!");
+}
+
+void
+intr_kill (struct intr_frame *f)
+{
+ switch (f->cs)
+ {
+ case SEL_UCSEG:
+ printk ("%s: dying due to interrupt %#04x (%s).\n",
+ thread_current ()->name, f->vec_no, intr_name (f->vec_no));
+ thread_exit ();
+
+ case SEL_KCSEG:
+ printk ("Kernel bug - unexpected interrupt in kernel context\n");
+ intr_panic (f);
+
+ default:
+ printk ("Interrupt %#04x (%s) in unknown segment %04x\n",
+ f->vec_no, intr_name (f->vec_no), f->cs);
+ thread_exit ();
+ }
+}
+
+