xref: /kvm-unit-tests/lib/x86/desc.c (revision 67961d185ad2860dd35ac2e3c17a8644c18da8b3)
1 #include "libcflat.h"
2 #include "desc.h"
3 #include "processor.h"
4 
5 typedef struct {
6     unsigned short offset0;
7     unsigned short selector;
8     unsigned short ist : 3;
9     unsigned short : 5;
10     unsigned short type : 4;
11     unsigned short : 1;
12     unsigned short dpl : 2;
13     unsigned short p : 1;
14     unsigned short offset1;
15 #ifdef __x86_64__
16     unsigned offset2;
17     unsigned reserved;
18 #endif
19 } idt_entry_t;
20 
21 typedef struct {
22 	u16 limit_low;
23 	u16 base_low;
24 	u8 base_middle;
25 	u8 access;
26 	u8 granularity;
27 	u8 base_high;
28 } gdt_entry_t;
29 
30 extern idt_entry_t boot_idt[256];
31 
32 void set_idt_entry(int vec, void *addr, int dpl)
33 {
34     idt_entry_t *e = &boot_idt[vec];
35     memset(e, 0, sizeof *e);
36     e->offset0 = (unsigned long)addr;
37     e->selector = read_cs();
38     e->ist = 0;
39     e->type = 14;
40     e->dpl = dpl;
41     e->p = 1;
42     e->offset1 = (unsigned long)addr >> 16;
43 #ifdef __x86_64__
44     e->offset2 = (unsigned long)addr >> 32;
45 #endif
46 }
47 
48 void set_idt_sel(int vec, u16 sel)
49 {
50     idt_entry_t *e = &boot_idt[vec];
51     e->selector = sel;
52 }
53 
54 struct ex_record {
55     unsigned long rip;
56     unsigned long handler;
57 };
58 
59 extern struct ex_record exception_table_start, exception_table_end;
60 
61 static void check_exception_table(struct ex_regs *regs)
62 {
63     struct ex_record *ex;
64     unsigned ex_val;
65 
66     ex_val = regs->vector | (regs->error_code << 16);
67 
68     asm("mov %0, %%gs:4" : : "r"(ex_val));
69 
70     for (ex = &exception_table_start; ex != &exception_table_end; ++ex) {
71         if (ex->rip == regs->rip) {
72             regs->rip = ex->handler;
73             return;
74         }
75     }
76     printf("unhandled excecption %d\n", regs->vector);
77     exit(7);
78 }
79 
80 static void (*exception_handlers[32])(struct ex_regs *regs);
81 
82 
83 void handle_exception(u8 v, void (*func)(struct ex_regs *regs))
84 {
85 	if (v < 32)
86 		exception_handlers[v] = func;
87 }
88 
89 #ifndef __x86_64__
90 __attribute__((regparm(1)))
91 #endif
92 void do_handle_exception(struct ex_regs *regs)
93 {
94 	if (regs->vector < 32 && exception_handlers[regs->vector]) {
95 		exception_handlers[regs->vector](regs);
96 		return;
97 	}
98 	printf("unhandled cpu excecption %d\n", regs->vector);
99 	if (regs->vector == 14)
100 		printf("PF at %p addr %p\n", regs->rip, read_cr2());
101 	exit(7);
102 }
103 
104 #define EX(NAME, N) extern char NAME##_fault;	\
105 	asm (".pushsection .text \n\t"		\
106 	     #NAME"_fault: \n\t"		\
107 	     "push"W" $0 \n\t"			\
108 	     "push"W" $"#N" \n\t"		\
109 	     "jmp __handle_exception \n\t"	\
110 	     ".popsection")
111 
112 #define EX_E(NAME, N) extern char NAME##_fault;	\
113 	asm (".pushsection .text \n\t"		\
114 	     #NAME"_fault: \n\t"		\
115 	     "push"W" $"#N" \n\t"		\
116 	     "jmp __handle_exception \n\t"	\
117 	     ".popsection")
118 
119 EX(de, 0);
120 EX(db, 1);
121 EX(nmi, 2);
122 EX(bp, 3);
123 EX(of, 4);
124 EX(br, 5);
125 EX(ud, 6);
126 EX(nm, 7);
127 EX_E(df, 8);
128 EX_E(ts, 10);
129 EX_E(np, 11);
130 EX_E(ss, 12);
131 EX_E(gp, 13);
132 EX_E(pf, 14);
133 EX(mf, 16);
134 EX_E(ac, 17);
135 EX(mc, 18);
136 EX(xm, 19);
137 
138 asm (".pushsection .text \n\t"
139      "__handle_exception: \n\t"
140 #ifdef __x86_64__
141      "push %r15; push %r14; push %r13; push %r12 \n\t"
142      "push %r11; push %r10; push %r9; push %r8 \n\t"
143 #endif
144      "push %"R"di; push %"R"si; push %"R"bp; sub $"S", %"R"sp \n\t"
145      "push %"R"bx; push %"R"dx; push %"R"cx; push %"R"ax \n\t"
146 #ifdef __x86_64__
147      "mov %"R"sp, %"R"di \n\t"
148 #else
149      "mov %"R"sp, %"R"ax \n\t"
150 #endif
151      "call do_handle_exception \n\t"
152      "pop %"R"ax; pop %"R"cx; pop %"R"dx; pop %"R"bx \n\t"
153      "add $"S", %"R"sp; pop %"R"bp; pop %"R"si; pop %"R"di \n\t"
154 #ifdef __x86_64__
155      "pop %r8; pop %r9; pop %r10; pop %r11 \n\t"
156      "pop %r12; pop %r13; pop %r14; pop %r15 \n\t"
157 #endif
158      "add $"S", %"R"sp \n\t"
159      "add $"S", %"R"sp \n\t"
160      "iret"W" \n\t"
161      ".popsection");
162 
163 static void *idt_handlers[32] = {
164 	[0] = &de_fault,
165 	[1] = &db_fault,
166 	[2] = &nmi_fault,
167 	[3] = &bp_fault,
168 	[4] = &of_fault,
169 	[5] = &br_fault,
170 	[6] = &ud_fault,
171 	[7] = &nm_fault,
172 	[8] = &df_fault,
173 	[10] = &ts_fault,
174 	[11] = &np_fault,
175 	[12] = &ss_fault,
176 	[13] = &gp_fault,
177 	[14] = &pf_fault,
178 	[16] = &mf_fault,
179 	[17] = &ac_fault,
180 	[18] = &mc_fault,
181 	[19] = &xm_fault,
182 };
183 
184 void setup_idt(void)
185 {
186     int i;
187     static bool idt_initialized = false;
188 
189     if (idt_initialized) {
190         return;
191     }
192     idt_initialized = true;
193     for (i = 0; i < 32; i++)
194 	    if (idt_handlers[i])
195 		    set_idt_entry(i, idt_handlers[i], 0);
196     handle_exception(0, check_exception_table);
197     handle_exception(6, check_exception_table);
198     handle_exception(13, check_exception_table);
199 }
200 
201 unsigned exception_vector(void)
202 {
203     unsigned short vector;
204 
205     asm("mov %%gs:4, %0" : "=rm"(vector));
206     return vector;
207 }
208 
209 unsigned exception_error_code(void)
210 {
211     unsigned short error_code;
212 
213     asm("mov %%gs:6, %0" : "=rm"(error_code));
214     return error_code;
215 }
216 
217 #ifndef __x86_64__
218 /*
219  * GDT, with 6 entries:
220  * 0x00 - NULL descriptor
221  * 0x08 - Code segment
222  * 0x10 - Data segment
223  * 0x18 - Not presend code segment
224  * 0x20 - Primery task
225  * 0x28 - Interrupt task
226  *
227  * 0x30 to 0x48 - Free to use for test cases
228  */
229 
230 static gdt_entry_t gdt[10];
231 
232 void set_gdt_entry(int sel, u32 base,  u32 limit, u8 access, u8 gran)
233 {
234 	int num = sel >> 3;
235 
236 	/* Setup the descriptor base address */
237 	gdt[num].base_low = (base & 0xFFFF);
238 	gdt[num].base_middle = (base >> 16) & 0xFF;
239 	gdt[num].base_high = (base >> 24) & 0xFF;
240 
241 	/* Setup the descriptor limits */
242 	gdt[num].limit_low = (limit & 0xFFFF);
243 	gdt[num].granularity = ((limit >> 16) & 0x0F);
244 
245 	/* Finally, set up the granularity and access flags */
246 	gdt[num].granularity |= (gran & 0xF0);
247 	gdt[num].access = access;
248 }
249 
250 void setup_gdt(void)
251 {
252 	struct descriptor_table_ptr gp;
253 	/* Setup the GDT pointer and limit */
254 	gp.limit = sizeof(gdt) - 1;
255 	gp.base = (ulong)&gdt;
256 
257 	memset(gdt, 0, sizeof(gdt));
258 
259 	/* Our NULL descriptor */
260 	set_gdt_entry(0, 0, 0, 0, 0);
261 
262 	/* The second entry is our Code Segment. The base address
263 	 *  is 0, the limit is 4GBytes, it uses 4KByte granularity,
264 	 *  uses 32-bit opcodes, and is a Code Segment descriptor. */
265 	set_gdt_entry(KERNEL_CS, 0, 0xFFFFFFFF, 0x9A, 0xcf);
266 
267 	/* The third entry is our Data Segment. It's EXACTLY the
268 	 *  same as our code segment, but the descriptor type in
269 	 *  this entry's access byte says it's a Data Segment */
270 	set_gdt_entry(KERNEL_DS, 0, 0xFFFFFFFF, 0x92, 0xcf);
271 
272 	/* Same as code register above but not present */
273 	set_gdt_entry(NP_SEL, 0, 0xFFFFFFFF, 0x1A, 0xcf);
274 
275 
276 	/* Flush out the old GDT and install the new changes! */
277 	lgdt(&gp);
278 
279 	asm volatile ("mov %0, %%ds\n\t"
280 		      "mov %0, %%es\n\t"
281 		      "mov %0, %%fs\n\t"
282 		      "mov %0, %%gs\n\t"
283 		      "mov %0, %%ss\n\t"
284 		      "jmp $" xstr(KERNEL_CS), $.Lflush2\n\t"
285 		      ".Lflush2: "::"r"(0x10));
286 }
287 
288 void set_idt_task_gate(int vec, u16 sel)
289 {
290     idt_entry_t *e = &boot_idt[vec];
291 
292     memset(e, 0, sizeof *e);
293 
294     e->selector = sel;
295     e->ist = 0;
296     e->type = 5;
297     e->dpl = 0;
298     e->p = 1;
299 }
300 
301 /*
302  * 0 - main task
303  * 1 - interrupt task
304  */
305 
306 static tss32_t tss[2];
307 static char tss_stack[2][4096];
308 
309 void setup_tss32(void)
310 {
311 	u16 desc_size = sizeof(tss32_t);
312 	int i;
313 
314 	for (i = 0; i < 2; i++) {
315 		tss[i].cr3 = read_cr3();
316 		tss[i].ss0 = tss[i].ss1 = tss[i].ss2 = 0x10;
317 		tss[i].esp = tss[i].esp0 = tss[i].esp1 = tss[i].esp2 =
318 			(u32)tss_stack[i] + 4096;
319 		tss[i].cs = KERNEL_CS;
320 		tss[i].ds = tss[i].es = tss[i].fs = tss[i].gs =
321 			tss[i].ss = KERNEL_DS;
322 		tss[i].iomap_base = (u16)desc_size;
323 		set_gdt_entry(TSS_MAIN + (i << 3), (u32)&tss[i],
324 			     desc_size - 1, 0x89, 0x0f);
325 	}
326 
327 	ltr(TSS_MAIN);
328 }
329 
330 void set_intr_task_gate(int e, void *fn)
331 {
332 	tss[1].eip = (u32)fn;
333 	set_idt_task_gate(e, TSS_INTR);
334 }
335 
336 void print_current_tss_info(void)
337 {
338 	u16 tr = str();
339 	int i = (tr == TSS_MAIN) ? 0 : 1;
340 
341 	if (tr != TSS_MAIN && tr != TSS_INTR)
342 		printf("Unknown TSS %x\n", tr);
343 	else
344 		printf("TR=%x Main TSS back link %x. Current TSS back link %x\n",
345                tr, tss[0].prev, tss[i].prev);
346 }
347 #endif
348 
349 static bool exception;
350 static void *exception_return;
351 
352 static void exception_handler(struct ex_regs *regs)
353 {
354 	exception = true;
355 	regs->rip = (unsigned long)exception_return;
356 }
357 
358 bool test_for_exception(unsigned int ex, void (*trigger_func)(void *data),
359 			void *data)
360 {
361 	handle_exception(ex, exception_handler);
362 	exception = false;
363 	trigger_func(data);
364 	handle_exception(ex, NULL);
365 	return exception;
366 }
367 
368 void set_exception_return(void *addr)
369 {
370 	exception_return = addr;
371 }
372