furix
cute os
arch/x86_64/idt.c
6932 bytes raw
1#include <idt.h>
2
3#include <gdt.h>
4#include <io.h>
5#include <lapic.h>
6#include <serial.h>
7#include <types.h>
8
9/* interrupt gate, present, ring0, 64-bit */
10#define IDT_GATE_INTERRUPT 0x8E
11
12/* IST indices, 1-based; 0 means "use the current stack" */
13#define IDT_IST_NONE 0
14
15struct idt_entry {
16 uint16_t offset_low;
17 uint16_t selector;
18 uint8_t ist;
19 uint8_t type_attr;
20 uint16_t offset_mid;
21 uint32_t offset_high;
22 uint32_t zero;
23} __attribute__((packed));
24
25static struct idt_entry idt[256];
26static struct desc_ptr idtr;
27
28/*
29 * a second table for application processors, published as idt_ap_pointer.
30 *
31 * the BSP's table cannot be reused as-is: its gates select 0x08, which is a
32 * 32-bit code segment, and a long-mode interrupt gate must name a 64-bit one,
33 * so that selector raises #GP instead of delivering the vector.
34 *
35 * IST indexes used to be a second reason, but are no longer: each AP now loads
36 * its own TSS from its trampoline GDT (see gdt_ap_tss_base), so the same IST
37 * stacks the BSP uses are safe here too and the gates carry the same indexes.
38 *
39 * 4 KiB, written once at bring-up, so the duplication costs nothing measurable.
40 */
41static struct idt_entry idt_ap[256];
42struct desc_ptr idt_ap_pointer;
43
44/* one entry per vector; NULL means "report and halt" */
45static void (*handlers[256])(void);
46
47/* filled in by isr.S: isr_stub_table[vector] is the stub for that vector */
48extern uint64_t isr_stub_table[];
49
50/* the stub table and this struct must agree or every frame is misinterpreted. */
51typedef char assert_frame_vector[(offsetof(struct interrupt_frame, vector) == 15 * 8) ? 1 : -1];
52typedef char assert_frame_error[(offsetof(struct interrupt_frame, error_code) == 16 * 8) ? 1 : -1];
53typedef char assert_frame_rip[(offsetof(struct interrupt_frame, rip) == 17 * 8) ? 1 : -1];
54typedef char assert_frame_rflags[(offsetof(struct interrupt_frame, rflags) == 19 * 8) ? 1 : -1];
55typedef char assert_frame_rsp[(offsetof(struct interrupt_frame, rsp) == 20 * 8) ? 1 : -1];
56
57static void idt_set_gate(uint16_t vector, uint64_t handler, uint8_t ist) {
58 idt[vector].offset_low = (uint16_t)(handler & 0xFFFF);
59 idt[vector].selector = 0x08; /* kernel code, from gdt.c */
60 idt[vector].ist = ist;
61 idt[vector].type_attr = IDT_GATE_INTERRUPT;
62 idt[vector].offset_mid = (uint16_t)((handler >> 16) & 0xFFFF);
63 idt[vector].offset_high = (uint32_t)(handler >> 32);
64 idt[vector].zero = 0;
65}
66
67/*
68 * the AP twin of idt_set_gate: no IST, and the trampoline's long-mode code
69 * selector (0x18) rather than the BSP's 32-bit one. see idt_ap above.
70 */
71static void idt_ap_set_gate(uint16_t vector, uint64_t handler, uint8_t ist) {
72 idt_ap[vector].offset_low = (uint16_t)(handler & 0xFFFF);
73 idt_ap[vector].selector = AP_CODE_SELECTOR_64;
74 idt_ap[vector].ist = ist;
75 idt_ap[vector].type_attr = IDT_GATE_INTERRUPT;
76 idt_ap[vector].offset_mid = (uint16_t)((handler >> 16) & 0xFFFF);
77 idt_ap[vector].offset_high = (uint32_t)(handler >> 32);
78 idt_ap[vector].zero = 0;
79}
80
81static void spurious_handler(void);
82
83void idt_set_handler(uint8_t vector, void (*handler)(void)) {
84 handlers[vector] = handler;
85}
86
87void idt_init(void) {
88 uint16_t vector;
89
90 for (vector = 0; vector < 256; vector++) {
91 idt_set_gate(vector, isr_stub_table[vector], IDT_IST_NONE);
92 }
93
94 /*
95 * IST1 for #DF and IST2 for NMI: dedicated stacks, so a fault that
96 * overflowed (or corrupted) the current stack is still handled.
97 */
98 idt[VECTOR_DOUBLE_FAULT].ist = IST_DOUBLE_FAULT;
99 idt[VECTOR_NMI].ist = IST_NMI;
100
101 handlers[VECTOR_SPURIOUS] = spurious_handler;
102
103 idtr.limit = (uint16_t)(sizeof(idt) - 1);
104 idtr.base = (uint64_t)&idt;
105 lidt(&idtr);
106
107 for (vector = 0; vector < 256; vector++) {
108 idt_ap_set_gate(vector, isr_stub_table[vector], IDT_IST_NONE);
109 }
110
111 /*
112 * same IST indexes as the BSP table above. these are only safe now that each
113 * AP loads its own TSS before this table (the trampoline LTR precedes its
114 * LIDT); an IST gate with no TSS loaded raises #GP, and the #GP that reports
115 * it would itself fault, giving a triple fault and a reset that looks
116 * exactly like "the AP never started".
117 */
118 idt_ap[VECTOR_DOUBLE_FAULT].ist = IST_DOUBLE_FAULT;
119 idt_ap[VECTOR_NMI].ist = IST_NMI;
120
121 idt_ap_pointer.limit = (uint16_t)(sizeof(idt_ap) - 1);
122 idt_ap_pointer.base = (uint64_t)&idt_ap;
123}
124
125/*
126 * a spurious interrupt was raised with IF already clear and nothing to
127 * acknowledge, so there is nothing to do but return. the LAPIC has already
128 * been EOI'd by the dispatcher.
129 */
130static void spurious_handler(void) {
131}
132
133static void halt(void) {
134 for (;;) {
135 cpu_hlt();
136 }
137}
138
139static void print_error_bits(const char *label, uint64_t error_code) {
140 serial_puts(" ");
141 serial_puts(label);
142 serial_puts(" = ");
143 serial_put_hex((error_code >> 16) & 0xFFFF);
144 serial_puts("\n");
145}
146
147/* default for exceptions: decode what we can, then stop. */
148static void report_exception(const struct interrupt_frame *frame) {
149 uint64_t vector = frame->vector;
150 uint64_t error_code = frame->error_code;
151
152 serial_puts("\nCPU EXCEPTION vector=");
153 serial_put_hex(vector);
154 serial_puts("\n err = ");
155 serial_put_hex(error_code);
156 serial_puts("\n");
157
158 if (vector == VECTOR_PAGE_FAULT) {
159 serial_puts(" page fault at ");
160 serial_put_hex(read_cr2());
161 serial_puts("\n cause: ");
162 serial_puts((error_code & 0x1) ? "present" : "not-present");
163 serial_puts(", ");
164 serial_puts((error_code & 0x2) ? "write" : "read");
165 serial_puts(", ");
166 serial_puts((error_code & 0x4) ? "user" : "supervisor");
167 serial_puts(", ");
168 serial_puts((error_code & 0x10) ? "instruction fetch" : "data access");
169 serial_puts("\n");
170 } else {
171 /* #DF / #GP / #SS style: bit0 IF, bit1 OTS, bit2 GPF, bit3 IDT,
172 bits 4..15 selector index, bit 15 RPL */
173 serial_puts((error_code & 0x1) ? " [IF]\n" : "");
174 serial_puts((error_code & 0x2) ? " [OTS]\n" : "");
175 serial_puts((error_code & 0x4) ? " [GPF]\n" : "");
176 serial_puts((error_code & 0x8) ? " [IDT]\n" : "");
177 print_error_bits("selector", error_code);
178 }
179
180 serial_puts(" rip = ");
181 serial_put_hex(frame->rip);
182 serial_puts("\n halted.\n");
183}
184
185/*
186 * single entry point for every vector. the LAPIC is acked before dispatch so a
187 * handler that itself takes a long time cannot block a second interrupt on
188 * the same vector.
189 */
190void interrupt_dispatch(const struct interrupt_frame *frame) {
191 uint8_t vector = (uint8_t)frame->vector;
192
193 if (vector != VECTOR_PAGE_FAULT) {
194 lapic_eoi();
195 }
196
197 if (handlers[vector] != NULL) {
198 handlers[vector]();
199 return;
200 }
201
202 /* exceptions 0..31 are unrecoverable here; no fixup path exists yet */
203 if (vector < 32) {
204 report_exception(frame);
205 halt();
206 }
207
208 serial_puts("\nUNEXPECTED INTERRUPT vector=");
209 serial_put_hex(vector);
210 serial_puts(" rip=");
211 serial_put_hex(frame->rip);
212 serial_puts(" apic_id=");
213 serial_put_hex(lapic_id());
214 serial_puts("\n");
215
216 halt();
217}