+/* Initializes the interrupt system. */
+void
+intr_init (void)
+{
+ uint64_t idtr_operand;
+ int i;
+
+ /* Initialize interrupt controller. */
+ 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-v3] 5.10. */
+ 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";
+ 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";
+}
+
+/* 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.
+
+ The handler will have descriptor privilege level DPL, meaning
+ that it can be invoked intentionally when the processor is in
+ the DPL or lower-numbered ring. In practice, DPL==3 allows
+ 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. */
+void
+intr_register (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;
+}
+
+/* Returns true during processing of an external interrupt
+ and false at all other times. */
+bool
+intr_context (void)
+{
+ return in_external_intr;
+}
+
+/* During processing of an external interrupt, directs the
+ interrupt handler to yield to a new process just before
+ returning from the interrupt. May not be called at any other
+ time. */
+void
+intr_yield_on_return (void)
+{
+ ASSERT (intr_context ());
+ yield_on_return = true;
+}
+\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.
+
+ 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. */