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