#include #include #include #include #include #include /* interrupt gate, present, ring0, 64-bit */ #define IDT_GATE_INTERRUPT 0x8E /* IST indices, 1-based; 0 means "use the current stack" */ #define IDT_IST_NONE 0 struct idt_entry { uint16_t offset_low; uint16_t selector; uint8_t ist; uint8_t type_attr; uint16_t offset_mid; uint32_t offset_high; uint32_t zero; } __attribute__((packed)); static struct idt_entry idt[256]; static struct desc_ptr idtr; /* * a second table for application processors, published as idt_ap_pointer. * * the BSP's table cannot be reused as-is: its gates select 0x08, which is a * 32-bit code segment, and a long-mode interrupt gate must name a 64-bit one, * so that selector raises #GP instead of delivering the vector. * * IST indexes used to be a second reason, but are no longer: each AP now loads * its own TSS from its trampoline GDT (see gdt_ap_tss_base), so the same IST * stacks the BSP uses are safe here too and the gates carry the same indexes. * * 4 KiB, written once at bring-up, so the duplication costs nothing measurable. */ static struct idt_entry idt_ap[256]; struct desc_ptr idt_ap_pointer; /* one entry per vector; NULL means "report and halt" */ static void (*handlers[256])(void); /* filled in by isr.S: isr_stub_table[vector] is the stub for that vector */ extern uint64_t isr_stub_table[]; /* the stub table and this struct must agree or every frame is misinterpreted. */ typedef char assert_frame_vector[(offsetof(struct interrupt_frame, vector) == 15 * 8) ? 1 : -1]; typedef char assert_frame_error[(offsetof(struct interrupt_frame, error_code) == 16 * 8) ? 1 : -1]; typedef char assert_frame_rip[(offsetof(struct interrupt_frame, rip) == 17 * 8) ? 1 : -1]; typedef char assert_frame_rflags[(offsetof(struct interrupt_frame, rflags) == 19 * 8) ? 1 : -1]; typedef char assert_frame_rsp[(offsetof(struct interrupt_frame, rsp) == 20 * 8) ? 1 : -1]; static void idt_set_gate(uint16_t vector, uint64_t handler, uint8_t ist) { idt[vector].offset_low = (uint16_t)(handler & 0xFFFF); idt[vector].selector = 0x08; /* kernel code, from gdt.c */ idt[vector].ist = ist; idt[vector].type_attr = IDT_GATE_INTERRUPT; idt[vector].offset_mid = (uint16_t)((handler >> 16) & 0xFFFF); idt[vector].offset_high = (uint32_t)(handler >> 32); idt[vector].zero = 0; } /* * the AP twin of idt_set_gate: no IST, and the trampoline's long-mode code * selector (0x18) rather than the BSP's 32-bit one. see idt_ap above. */ static void idt_ap_set_gate(uint16_t vector, uint64_t handler, uint8_t ist) { idt_ap[vector].offset_low = (uint16_t)(handler & 0xFFFF); idt_ap[vector].selector = AP_CODE_SELECTOR_64; idt_ap[vector].ist = ist; idt_ap[vector].type_attr = IDT_GATE_INTERRUPT; idt_ap[vector].offset_mid = (uint16_t)((handler >> 16) & 0xFFFF); idt_ap[vector].offset_high = (uint32_t)(handler >> 32); idt_ap[vector].zero = 0; } static void spurious_handler(void); void idt_set_handler(uint8_t vector, void (*handler)(void)) { handlers[vector] = handler; } void idt_init(void) { uint16_t vector; for (vector = 0; vector < 256; vector++) { idt_set_gate(vector, isr_stub_table[vector], IDT_IST_NONE); } /* * IST1 for #DF and IST2 for NMI: dedicated stacks, so a fault that * overflowed (or corrupted) the current stack is still handled. */ idt[VECTOR_DOUBLE_FAULT].ist = IST_DOUBLE_FAULT; idt[VECTOR_NMI].ist = IST_NMI; handlers[VECTOR_SPURIOUS] = spurious_handler; idtr.limit = (uint16_t)(sizeof(idt) - 1); idtr.base = (uint64_t)&idt; lidt(&idtr); for (vector = 0; vector < 256; vector++) { idt_ap_set_gate(vector, isr_stub_table[vector], IDT_IST_NONE); } /* * same IST indexes as the BSP table above. these are only safe now that each * AP loads its own TSS before this table (the trampoline LTR precedes its * LIDT); an IST gate with no TSS loaded raises #GP, and the #GP that reports * it would itself fault, giving a triple fault and a reset that looks * exactly like "the AP never started". */ idt_ap[VECTOR_DOUBLE_FAULT].ist = IST_DOUBLE_FAULT; idt_ap[VECTOR_NMI].ist = IST_NMI; idt_ap_pointer.limit = (uint16_t)(sizeof(idt_ap) - 1); idt_ap_pointer.base = (uint64_t)&idt_ap; } /* * a spurious interrupt was raised with IF already clear and nothing to * acknowledge, so there is nothing to do but return. the LAPIC has already * been EOI'd by the dispatcher. */ static void spurious_handler(void) { } static void halt(void) { for (;;) { cpu_hlt(); } } static void print_error_bits(const char *label, uint64_t error_code) { serial_puts(" "); serial_puts(label); serial_puts(" = "); serial_put_hex((error_code >> 16) & 0xFFFF); serial_puts("\n"); } /* default for exceptions: decode what we can, then stop. */ static void report_exception(const struct interrupt_frame *frame) { uint64_t vector = frame->vector; uint64_t error_code = frame->error_code; serial_puts("\nCPU EXCEPTION vector="); serial_put_hex(vector); serial_puts("\n err = "); serial_put_hex(error_code); serial_puts("\n"); if (vector == VECTOR_PAGE_FAULT) { serial_puts(" page fault at "); serial_put_hex(read_cr2()); serial_puts("\n cause: "); serial_puts((error_code & 0x1) ? "present" : "not-present"); serial_puts(", "); serial_puts((error_code & 0x2) ? "write" : "read"); serial_puts(", "); serial_puts((error_code & 0x4) ? "user" : "supervisor"); serial_puts(", "); serial_puts((error_code & 0x10) ? "instruction fetch" : "data access"); serial_puts("\n"); } else { /* #DF / #GP / #SS style: bit0 IF, bit1 OTS, bit2 GPF, bit3 IDT, bits 4..15 selector index, bit 15 RPL */ serial_puts((error_code & 0x1) ? " [IF]\n" : ""); serial_puts((error_code & 0x2) ? " [OTS]\n" : ""); serial_puts((error_code & 0x4) ? " [GPF]\n" : ""); serial_puts((error_code & 0x8) ? " [IDT]\n" : ""); print_error_bits("selector", error_code); } serial_puts(" rip = "); serial_put_hex(frame->rip); serial_puts("\n halted.\n"); } /* * single entry point for every vector. the LAPIC is acked before dispatch so a * handler that itself takes a long time cannot block a second interrupt on * the same vector. */ void interrupt_dispatch(const struct interrupt_frame *frame) { uint8_t vector = (uint8_t)frame->vector; if (vector != VECTOR_PAGE_FAULT) { lapic_eoi(); } if (handlers[vector] != NULL) { handlers[vector](); return; } /* exceptions 0..31 are unrecoverable here; no fixup path exists yet */ if (vector < 32) { report_exception(frame); halt(); } serial_puts("\nUNEXPECTED INTERRUPT vector="); serial_put_hex(vector); serial_puts(" rip="); serial_put_hex(frame->rip); serial_puts(" apic_id="); serial_put_hex(lapic_id()); serial_puts("\n"); halt(); }