1 /* 2 * Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "interrupts.h" 8 9 #include <stdio.h> 10 #include <string.h> 11 12 #include <algorithm> 13 14 #include <KernelExport.h> 15 16 #include <boot/platform.h> 17 #include <boot/platform/generic/text_console.h> 18 19 #include <arch_cpu.h> 20 #include <descriptors.h> 21 22 #include "debug.h" 23 #include "keyboard.h" 24 25 26 //#define TRACE_INTERRUPTS 27 #ifdef TRACE_INTERRUPTS 28 # define TRACE(x...) dprintf(x) 29 #else 30 # define TRACE(x...) ; 31 #endif 32 33 34 // interrupt function prototypes 35 #define INTERRUPT_FUNCTION(vector) \ 36 extern "C" void interrupt_function##vector(); 37 INTERRUPT_FUNCTIONS() 38 #undef INTERRUPT_FUNCTION 39 40 #define INTERRUPT_FUNCTION(vector) \ 41 extern "C" void exception_interrupt_function##vector(); 42 INTERRUPT_FUNCTIONS() 43 #undef INTERRUPT_FUNCTION 44 45 46 static const char *kInterruptNames[DEBUG_IDT_SLOT_COUNT] = { 47 /* 0 */ "Divide Error Exception", 48 /* 1 */ "Debug Exception", 49 /* 2 */ "NMI Interrupt", 50 /* 3 */ "Breakpoint Exception", 51 /* 4 */ "Overflow Exception", 52 /* 5 */ "BOUND Range Exceeded Exception", 53 /* 6 */ "Invalid Opcode Exception", 54 /* 7 */ "Device Not Available Exception", 55 /* 8 */ "Double Fault Exception", 56 /* 9 */ "Coprocessor Segment Overrun", 57 /* 10 */ "Invalid TSS Exception", 58 /* 11 */ "Segment Not Present", 59 /* 12 */ "Stack Fault Exception", 60 /* 13 */ "General Protection Exception", 61 /* 14 */ "Page-Fault Exception", 62 /* 15 */ "-", 63 /* 16 */ "x87 FPU Floating-Point Error", 64 /* 17 */ "Alignment Check Exception", 65 /* 18 */ "Machine-Check Exception", 66 /* 19 */ "SIMD Floating-Point Exception", 67 }; 68 69 70 struct interrupt_frame { 71 uint32 vector; 72 uint32 edi; 73 uint32 esi; 74 uint32 ebp; 75 uint32 esp; 76 uint32 ebx; 77 uint32 edx; 78 uint32 ecx; 79 uint32 eax; 80 uint32 error_code; 81 uint32 eip; 82 uint32 cs; 83 uint32 eflags; 84 }; 85 86 struct interrupt_descriptor { 87 uint32 a; 88 uint32 b; 89 }; 90 91 92 static interrupt_descriptor sDebugIDT[DEBUG_IDT_SLOT_COUNT]; 93 94 static gdt_idt_descr sBIOSIDTDescriptor; 95 static gdt_idt_descr sDebugIDTDescriptor = { sizeof(sDebugIDT) - 1, sDebugIDT }; 96 97 98 static void 99 set_interrupt_gate(int n, void (*function)()) 100 { 101 if (n >= DEBUG_IDT_SLOT_COUNT) 102 return; 103 104 sDebugIDT[n].a = (KERNEL_CODE_SEG << 16) | (0x0000ffff & (addr_t)function); 105 sDebugIDT[n].b = (0xffff0000 & (addr_t)function) | 0x8e00; 106 } 107 108 109 static void 110 set_idt(const gdt_idt_descr& idtDescriptor) 111 { 112 asm("lidt %0;" : : "m" (idtDescriptor)); 113 } 114 115 116 extern "C" void 117 handle_exception_exception() 118 { 119 while (true); 120 } 121 122 123 extern "C" void 124 handle_exception(interrupt_frame frame) 125 { 126 // Before doing anything else, set the interrupt gates so that 127 // handle_exception_exception() is called. This way we may avoid 128 // triple-faulting, if e.g. the stack is messed up and the user has at 129 // least the already printed output. 130 #define INTERRUPT_FUNCTION(vector) \ 131 set_interrupt_gate(vector, &exception_interrupt_function##vector); 132 INTERRUPT_FUNCTIONS() 133 #undef INTERRUPT_FUNCTION 134 135 // If the console is already/still initialized, clear the screen and prepare 136 // for output. 137 if (stdout != NULL) { 138 console_set_color(RED, BLACK); 139 console_clear_screen(); 140 console_set_cursor(0, 0); 141 console_hide_cursor(); 142 } 143 144 // print exception name 145 if (frame.vector < DEBUG_IDT_SLOT_COUNT) 146 kprintf("%s", kInterruptNames[frame.vector]); 147 else 148 kprintf("Unknown exception %" B_PRIu32, frame.vector); 149 150 // additional info for page fault 151 if (frame.vector == 14) { 152 uint32 cr2; 153 asm("movl %%cr2, %0" : "=r"(cr2)); 154 kprintf(": %s fault at address: %#" B_PRIx32, 155 (frame.error_code & 0x2) != 0 ? "write" : "read", cr2); 156 } 157 158 kprintf("\n"); 159 160 // print greeting and registers 161 kprintf("Welcome to Boot Loader Death Land!\n"); 162 kprintf("\n"); 163 164 #define REG(name) " " #name " %-#10" B_PRIx32 165 166 kprintf(REG(eax) " " REG(ebx) " " REG(ecx) " " REG(edx) "\n", 167 frame.eax, frame.ebx, frame.ecx, frame.edx); 168 kprintf(REG(esi) " " REG(edi) " " REG(ebp) " " REG(esp) "\n", 169 frame.esi, frame.edi, frame.ebp, frame.esp); 170 kprintf(REG(eip) REG(eflags) "\n", frame.eip, frame.eflags); 171 172 #undef REG 173 174 // print stack trace (max 10 frames) 175 kprintf("\n frame return address\n"); 176 177 struct x86_stack_frame { 178 x86_stack_frame* next; 179 void* return_address; 180 }; 181 182 x86_stack_frame* stackFrame = (x86_stack_frame*)frame.ebp; 183 void* instructionPointer = (void*)frame.eip; 184 185 for (int32 i = 0; i < 10 && stackFrame != NULL; i++) { 186 kprintf(" %p %p\n", stackFrame, instructionPointer); 187 188 instructionPointer = stackFrame->return_address; 189 stackFrame = stackFrame->next; 190 } 191 192 if (stdout != NULL) { 193 kprintf("\nPress a key to reboot.\n"); 194 clear_key_buffer(); 195 wait_for_key(); 196 platform_exit(); 197 } 198 199 while (true); 200 } 201 202 203 void 204 interrupts_init() 205 { 206 // get the IDT 207 asm("sidt %0;" : : "m" (sBIOSIDTDescriptor)); 208 209 TRACE("IDT: base: %p, limit: %u\n", sBIOSIDTDescriptor.base, 210 sBIOSIDTDescriptor.limit); 211 212 // set interrupt gates 213 #define INTERRUPT_FUNCTION(vector) \ 214 set_interrupt_gate(vector, &interrupt_function##vector); 215 INTERRUPT_FUNCTIONS() 216 #undef INTERRUPT_FUNCTION 217 218 // set the debug IDT 219 set_debug_idt(); 220 } 221 222 223 void 224 restore_bios_idt() 225 { 226 set_idt(sBIOSIDTDescriptor); 227 } 228 229 230 void 231 set_debug_idt() 232 { 233 set_idt(sDebugIDTDescriptor); 234 } 235 236 237 void 238 interrupts_init_kernel_idt(void* idt, size_t idtSize) 239 { 240 // clear it but copy the descriptors we've set up for the exceptions 241 memset(idt, 0, idtSize); 242 memcpy(idt, sDebugIDT, sizeof(sDebugIDT)); 243 244 // load the idt 245 gdt_idt_descr idtDescriptor; 246 idtDescriptor.limit = idtSize - 1; 247 idtDescriptor.base = idt; 248 set_idt(idtDescriptor); 249 } 250