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 <arch/x86/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 87 static interrupt_descriptor sDebugIDT[DEBUG_IDT_SLOT_COUNT]; 88 89 static gdt_idt_descr sBIOSIDTDescriptor; 90 static gdt_idt_descr sDebugIDTDescriptor = { sizeof(sDebugIDT) - 1, sDebugIDT }; 91 92 93 static void 94 set_interrupt_gate(int n, void (*function)()) 95 { 96 if (n >= DEBUG_IDT_SLOT_COUNT) 97 return; 98 99 sDebugIDT[n].a 100 = (KERNEL_CODE_SELECTOR << 16) | (0x0000ffff & (addr_t)function); 101 sDebugIDT[n].b = (0xffff0000 & (addr_t)function) | 0x8e00; 102 } 103 104 105 static void 106 set_idt(const gdt_idt_descr& idtDescriptor) 107 { 108 asm("lidt %0;" : : "m" (idtDescriptor)); 109 } 110 111 112 extern "C" void 113 handle_exception_exception() 114 { 115 while (true); 116 } 117 118 119 extern "C" void 120 handle_exception(interrupt_frame frame) 121 { 122 // Before doing anything else, set the interrupt gates so that 123 // handle_exception_exception() is called. This way we may avoid 124 // triple-faulting, if e.g. the stack is messed up and the user has at 125 // least the already printed output. 126 #define INTERRUPT_FUNCTION(vector) \ 127 set_interrupt_gate(vector, &exception_interrupt_function##vector); 128 INTERRUPT_FUNCTIONS() 129 #undef INTERRUPT_FUNCTION 130 131 // If the console is already/still initialized, clear the screen and prepare 132 // for output. 133 if (stdout != NULL) { 134 console_set_color(RED, BLACK); 135 console_clear_screen(); 136 console_set_cursor(0, 0); 137 console_hide_cursor(); 138 } 139 140 // print exception name 141 if (frame.vector < DEBUG_IDT_SLOT_COUNT) 142 kprintf("%s", kInterruptNames[frame.vector]); 143 else 144 kprintf("Unknown exception %" B_PRIu32, frame.vector); 145 146 // additional info for page fault 147 if (frame.vector == 14) { 148 uint32 cr2; 149 asm("movl %%cr2, %0" : "=r"(cr2)); 150 kprintf(": %s fault at address: %#" B_PRIx32, 151 (frame.error_code & 0x2) != 0 ? "write" : "read", cr2); 152 } 153 154 kprintf("\n"); 155 156 // print greeting and registers 157 kprintf("Welcome to Boot Loader Death Land!\n"); 158 kprintf("\n"); 159 160 #define REG(name) " " #name " %-#10" B_PRIx32 161 162 kprintf(REG(eax) " " REG(ebx) " " REG(ecx) " " REG(edx) "\n", 163 frame.eax, frame.ebx, frame.ecx, frame.edx); 164 kprintf(REG(esi) " " REG(edi) " " REG(ebp) " " REG(esp) "\n", 165 frame.esi, frame.edi, frame.ebp, frame.esp); 166 kprintf(REG(eip) REG(eflags) "\n", frame.eip, frame.eflags); 167 168 #undef REG 169 170 // print stack trace (max 10 frames) 171 kprintf("\n frame return address\n"); 172 173 struct x86_stack_frame { 174 x86_stack_frame* next; 175 void* return_address; 176 }; 177 178 x86_stack_frame* stackFrame = (x86_stack_frame*)frame.ebp; 179 void* instructionPointer = (void*)frame.eip; 180 181 for (int32 i = 0; i < 10 && stackFrame != NULL; i++) { 182 kprintf(" %p %p\n", stackFrame, instructionPointer); 183 184 instructionPointer = stackFrame->return_address; 185 stackFrame = stackFrame->next; 186 } 187 188 if (stdout != NULL) { 189 kprintf("\nPress a key to reboot.\n"); 190 clear_key_buffer(); 191 wait_for_key(); 192 platform_exit(); 193 } 194 195 while (true); 196 } 197 198 199 void 200 interrupts_init() 201 { 202 // get the IDT 203 asm("sidt %0;" : : "m" (sBIOSIDTDescriptor)); 204 205 TRACE("IDT: base: %p, limit: %u\n", sBIOSIDTDescriptor.base, 206 sBIOSIDTDescriptor.limit); 207 208 // set interrupt gates 209 #define INTERRUPT_FUNCTION(vector) \ 210 set_interrupt_gate(vector, &interrupt_function##vector); 211 INTERRUPT_FUNCTIONS() 212 #undef INTERRUPT_FUNCTION 213 214 // set the debug IDT 215 set_debug_idt(); 216 } 217 218 219 void 220 restore_bios_idt() 221 { 222 set_idt(sBIOSIDTDescriptor); 223 } 224 225 226 void 227 set_debug_idt() 228 { 229 set_idt(sDebugIDTDescriptor); 230 } 231 232 233 void 234 interrupts_init_kernel_idt(void* idt, size_t idtSize) 235 { 236 // clear it but copy the descriptors we've set up for the exceptions 237 memset(idt, 0, idtSize); 238 memcpy(idt, sDebugIDT, sizeof(sDebugIDT)); 239 240 // load the idt 241 gdt_idt_descr idtDescriptor; 242 idtDescriptor.limit = idtSize - 1; 243 idtDescriptor.base = idt; 244 set_idt(idtDescriptor); 245 } 246