1 /* 2 * Copyright 2012, Alex Smith, alex@alex-smith.me.uk. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <arch/x86/descriptors.h> 8 9 #include <boot/kernel_args.h> 10 #include <cpu.h> 11 #include <vm/vm.h> 12 #include <vm/vm_priv.h> 13 14 #include <arch/int.h> 15 #include <arch/user_debugger.h> 16 17 18 #define IDT_GATES_COUNT 256 19 20 21 typedef void interrupt_handler_function(iframe* frame); 22 23 24 static segment_descriptor sGDT[GDT_SEGMENT_COUNT]; 25 static interrupt_descriptor sIDT[IDT_GATES_COUNT]; 26 27 static const uint32 kInterruptHandlerTableSize = IDT_GATES_COUNT; 28 interrupt_handler_function* gInterruptHandlerTable[kInterruptHandlerTableSize]; 29 30 extern uint8 isr_array[kInterruptHandlerTableSize][16]; 31 32 33 static inline void 34 load_tss(int cpu) 35 { 36 uint16 segment = (TSS_SEGMENT(cpu) << 3) | DPL_KERNEL; 37 asm volatile("ltr %w0" : : "a" (segment)); 38 } 39 40 41 static inline void 42 load_gdt() 43 { 44 struct { 45 uint16 limit; 46 void* address; 47 } _PACKED gdtDescriptor = { 48 GDT_SEGMENT_COUNT * sizeof(segment_descriptor) - 1, 49 sGDT 50 }; 51 52 asm volatile("lgdt %0" : : "m" (gdtDescriptor)); 53 } 54 55 56 static inline void 57 load_idt() 58 { 59 struct { 60 uint16 limit; 61 void* address; 62 } _PACKED idtDescriptor = { 63 IDT_GATES_COUNT * sizeof(interrupt_descriptor) - 1, 64 sIDT 65 }; 66 67 asm volatile("lidt %0" : : "m" (idtDescriptor)); 68 } 69 70 71 // #pragma mark - Exception handlers 72 73 74 static void 75 x86_64_general_protection_fault(iframe* frame) 76 { 77 if (debug_debugger_running()) { 78 // Handle GPFs if there is a debugger fault handler installed, for 79 // non-canonical address accesses. 80 cpu_ent* cpu = &gCPU[smp_get_current_cpu()]; 81 if (cpu->fault_handler != 0) { 82 debug_set_page_fault_info(0, frame->ip, DEBUG_PAGE_FAULT_NO_INFO); 83 frame->ip = cpu->fault_handler; 84 frame->bp = cpu->fault_handler_stack_pointer; 85 return; 86 } 87 } 88 89 x86_unexpected_exception(frame); 90 } 91 92 93 // #pragma mark - 94 95 96 void 97 x86_descriptors_preboot_init_percpu(kernel_args* args, int cpu) 98 { 99 if (cpu == 0) { 100 STATIC_ASSERT(GDT_SEGMENT_COUNT <= 8192); 101 102 set_segment_descriptor(&sGDT[KERNEL_CODE_SEGMENT], DT_CODE_EXECUTE_ONLY, 103 DPL_KERNEL); 104 set_segment_descriptor(&sGDT[KERNEL_DATA_SEGMENT], DT_DATA_WRITEABLE, 105 DPL_KERNEL); 106 set_segment_descriptor(&sGDT[USER_CODE_SEGMENT], DT_CODE_EXECUTE_ONLY, 107 DPL_USER); 108 set_segment_descriptor(&sGDT[USER_DATA_SEGMENT], DT_DATA_WRITEABLE, 109 DPL_USER); 110 } 111 112 memset(&gCPU[cpu].arch.tss, 0, sizeof(struct tss)); 113 gCPU[cpu].arch.tss.io_map_base = sizeof(struct tss); 114 115 // Set up the descriptor for this TSS. 116 set_tss_descriptor(&sGDT[TSS_SEGMENT(cpu)], (addr_t)&gCPU[cpu].arch.tss, 117 sizeof(struct tss)); 118 119 // Set up the double fault IST entry (see x86_descriptors_init()). 120 struct tss* tss = &gCPU[cpu].arch.tss; 121 size_t stackSize; 122 tss->ist1 = (addr_t)x86_get_double_fault_stack(cpu, &stackSize); 123 tss->ist1 += stackSize; 124 125 load_idt(); 126 load_gdt(); 127 load_tss(cpu); 128 } 129 130 131 void 132 x86_descriptors_init(kernel_args* args) 133 { 134 // Fill out the IDT, pointing each entry to the corresponding entry in the 135 // ISR array created in arch_interrupts.S (see there to see how this works). 136 for(uint32 i = 0; i < kInterruptHandlerTableSize; i++) { 137 // x86_64 removes task gates, therefore we cannot use a separate TSS 138 // for the double fault exception. However, instead it adds a new stack 139 // switching mechanism, the IST. The IST is a table of stack addresses 140 // in the TSS. If the IST field of an interrupt descriptor is non-zero, 141 // the CPU will switch to the stack specified by that IST entry when 142 // handling that interrupt. So, we use IST entry 1 to store the double 143 // fault stack address (set up in x86_descriptors_init_post_vm()). 144 uint32 ist = (i == 8) ? 1 : 0; 145 146 // Breakpoint exception can be raised from userland. 147 uint32 dpl = (i == 3) ? DPL_USER : DPL_KERNEL; 148 149 set_interrupt_descriptor(&sIDT[i], (addr_t)&isr_array[i], 150 GATE_INTERRUPT, KERNEL_CODE_SELECTOR, dpl, ist); 151 } 152 153 // Initialize the interrupt handler table. 154 interrupt_handler_function** table = gInterruptHandlerTable; 155 for (uint32 i = 0; i < ARCH_INTERRUPT_BASE; i++) 156 table[i] = x86_invalid_exception; 157 for (uint32 i = ARCH_INTERRUPT_BASE; i < kInterruptHandlerTableSize; i++) 158 table[i] = x86_hardware_interrupt; 159 160 table[0] = x86_unexpected_exception; // Divide Error Exception (#DE) 161 table[1] = x86_handle_debug_exception; // Debug Exception (#DB) 162 table[2] = x86_fatal_exception; // NMI Interrupt 163 table[3] = x86_handle_breakpoint_exception; // Breakpoint Exception (#BP) 164 table[4] = x86_unexpected_exception; // Overflow Exception (#OF) 165 table[5] = x86_unexpected_exception; // BOUND Range Exceeded Exception (#BR) 166 table[6] = x86_unexpected_exception; // Invalid Opcode Exception (#UD) 167 table[7] = x86_fatal_exception; // Device Not Available Exception (#NM) 168 table[8] = x86_fatal_exception; // Double Fault Exception (#DF) 169 table[9] = x86_fatal_exception; // Coprocessor Segment Overrun 170 table[10] = x86_fatal_exception; // Invalid TSS Exception (#TS) 171 table[11] = x86_fatal_exception; // Segment Not Present (#NP) 172 table[12] = x86_fatal_exception; // Stack Fault Exception (#SS) 173 table[13] = x86_64_general_protection_fault; // General Protection Exception (#GP) 174 table[14] = x86_page_fault_exception; // Page-Fault Exception (#PF) 175 table[16] = x86_unexpected_exception; // x87 FPU Floating-Point Error (#MF) 176 table[17] = x86_unexpected_exception; // Alignment Check Exception (#AC) 177 table[18] = x86_fatal_exception; // Machine-Check Exception (#MC) 178 table[19] = x86_unexpected_exception; // SIMD Floating-Point Exception (#XF) 179 } 180 181