xref: /haiku/src/system/kernel/arch/x86/64/descriptors.cpp (revision 5e96d7d537fbec23bad4ae9b4c8e7b02e769f0c6)
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