#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
/* Names for each interrupt, for debugging purposes. */
static const char *intr_names[INTR_CNT];
+/* Number of unexpected interrupts for each vector. An
+ unexpected interrupt is one that has no registered handler. */
+static unsigned int unexpected_cnt[INTR_CNT];
+
/* External interrupts are those generated by devices outside the
CPU, such as the timer. External interrupts run with
interrupts turned off, so they never nest, nor are they ever
/* Interrupt handlers. */
void intr_handler (struct intr_frame *args);
+static void unexpected_interrupt (const struct intr_frame *);
\f
/* Returns the current interrupt status. */
enum intr_level
/* Disable interrupts by clearing the interrupt flag.
See [IA32-v2b] "CLI" and [IA32-v3a] 5.8.1 "Masking Maskable
Hardware Interrupts". */
- asm volatile ("cli");
+ asm volatile ("cli" : : : "memory");
return old_level;
}
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));
+ asm volatile ("lidt %0" : : "m" (idtr_operand));
/* Initialize intr_names. */
for (i = 0; i < INTR_CNT; i++)
\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
- 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. */
- outb (0x21, 0xff);
- outb (0xa1, 0xff);
+ outb (PIC0_DATA, 0xff);
+ outb (PIC1_DATA, 0xff);
/* 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. */
- 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. */
- 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.
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];
- if (handler == NULL)
+ if (handler != NULL)
+ handler (frame);
+ else if (frame->vec_no == 0x27 || frame->vec_no == 0x2f)
{
- intr_dump_frame (frame);
- PANIC ("Unexpected interrupt");
+ /* There is no handler, but this interrupt can trigger
+ spuriously due to a hardware fault or hardware race
+ condition. Ignore it. */
}
- handler (frame);
+ else
+ unexpected_interrupt (frame);
/* Complete the processing of an external interrupt. */
if (external)
}
}
+/* Handles an unexpected interrupt with interrupt frame F. An
+ unexpected interrupt is one that has no registered handler. */
+static void
+unexpected_interrupt (const struct intr_frame *f)
+{
+ /* Count the number so far. */
+ unsigned int n = ++unexpected_cnt[f->vec_no];
+
+ /* If the number is a power of 2, print a message. This rate
+ limiting means that we get information about an uncommon
+ unexpected interrupt the first time and fairly often after
+ that, but one that occurs many times will not overwhelm the
+ console. */
+ if ((n & (n - 1)) == 0)
+ printf ("Unexpected interrupt %#04x (%s)\n",
+ f->vec_no, intr_names[f->vec_no]);
+}
+
/* Dumps interrupt frame F to the console, for debugging. */
void
intr_dump_frame (const struct intr_frame *f)