xref: /kvm-unit-tests/lib/x86/desc.c (revision d21b4f1216592171a358c7d17f81b4aab08aebea)
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 typedef struct {
31 	u16 prev;
32 	u16 res1;
33 	u32 esp0;
34 	u16 ss0;
35 	u16 res2;
36 	u32 esp1;
37 	u16 ss1;
38 	u16 res3;
39 	u32 esp2;
40 	u16 ss2;
41 	u16 res4;
42 	u32 cr3;
43 	u32 eip;
44 	u32 eflags;
45 	u32 eax, ecx, edx, ebx, esp, ebp, esi, edi;
46 	u16 es;
47 	u16 res5;
48 	u16 cs;
49 	u16 res6;
50 	u16 ss;
51 	u16 res7;
52 	u16 ds;
53 	u16 res8;
54 	u16 fs;
55 	u16 res9;
56 	u16 gs;
57 	u16 res10;
58 	u16 ldt;
59 	u16 res11;
60 	u16 t:1;
61 	u16 res12:15;
62 	u16 iomap_base;
63 } tss32_t;
64 
65 static idt_entry_t idt[256];
66 
67 void load_lidt(idt_entry_t *idt, int nentries)
68 {
69     struct descriptor_table_ptr dt;
70 
71     dt.limit = nentries * sizeof(*idt) - 1;
72     dt.base = (unsigned long)idt;
73     lidt(&dt);
74     asm volatile ("lidt %0" : : "m"(dt));
75 }
76 
77 void set_idt_entry(int vec, void *addr, int dpl)
78 {
79     idt_entry_t *e = &idt[vec];
80     memset(e, 0, sizeof *e);
81     e->offset0 = (unsigned long)addr;
82     e->selector = read_cs();
83     e->ist = 0;
84     e->type = 14;
85     e->dpl = dpl;
86     e->p = 1;
87     e->offset1 = (unsigned long)addr >> 16;
88 #ifdef __x86_64__
89     e->offset2 = (unsigned long)addr >> 32;
90 #endif
91 }
92 
93 struct ex_regs {
94     unsigned long rax, rcx, rdx, rbx;
95     unsigned long dummy, rbp, rsi, rdi;
96 #ifdef __x86_64__
97     unsigned long r8, r9, r10, r11;
98     unsigned long r12, r13, r14, r15;
99 #endif
100     unsigned long vector;
101     unsigned long error_code;
102     unsigned long rip;
103     unsigned long cs;
104     unsigned long rflags;
105 };
106 
107 struct ex_record {
108     unsigned long rip;
109     unsigned long handler;
110 };
111 
112 extern struct ex_record exception_table_start, exception_table_end;
113 
114 void do_handle_exception(struct ex_regs *regs)
115 {
116     struct ex_record *ex;
117     unsigned ex_val;
118 
119     ex_val = regs->vector | (regs->error_code << 16);
120 
121     asm("mov %0, %%gs:4" : : "r"(ex_val));
122 
123     for (ex = &exception_table_start; ex != &exception_table_end; ++ex) {
124         if (ex->rip == regs->rip) {
125             regs->rip = ex->handler;
126             return;
127         }
128     }
129     printf("unhandled excecption\n");
130     exit(7);
131 }
132 
133 #ifdef __x86_64__
134 #  define R "r"
135 #  define W "q"
136 #  define S "8"
137 #else
138 #  define R "e"
139 #  define W "l"
140 #  define S "4"
141 #endif
142 
143 asm (".pushsection .text \n\t"
144      "ud_fault: \n\t"
145      "push"W" $0 \n\t"
146      "push"W" $6 \n\t"
147      "jmp handle_exception \n\t"
148 
149      "gp_fault: \n\t"
150      "push"W" $13 \n\t"
151      "jmp handle_exception \n\t"
152 
153      "de_fault: \n\t"
154      "push"W" $0 \n\t"
155      "push"W" $0 \n\t"
156      "jmp handle_exception \n\t"
157 
158      "handle_exception: \n\t"
159 #ifdef __x86_64__
160      "push %r15; push %r14; push %r13; push %r12 \n\t"
161      "push %r11; push %r10; push %r9; push %r8 \n\t"
162 #endif
163      "push %"R"di; push %"R"si; push %"R"bp; sub $"S", %"R"sp \n\t"
164      "push %"R"bx; push %"R"dx; push %"R"cx; push %"R"ax \n\t"
165      "mov %"R"sp, %"R"di \n\t"
166      "call do_handle_exception \n\t"
167      "pop %"R"ax; pop %"R"cx; pop %"R"dx; pop %"R"bx \n\t"
168      "add $"S", %"R"sp; pop %"R"bp; pop %"R"si; pop %"R"di \n\t"
169 #ifdef __x86_64__
170      "pop %r8; pop %r9; pop %r10; pop %r11 \n\t"
171      "pop %r12; pop %r13; pop %r14; pop %r15 \n\t"
172 #endif
173      "add $"S", %"R"sp \n\t"
174      "add $"S", %"R"sp \n\t"
175      "iret"W" \n\t"
176      ".popsection");
177 
178 
179 void setup_idt(void)
180 {
181     extern char ud_fault, gp_fault, de_fault;
182 
183     load_lidt(idt, 256);
184     set_idt_entry(0, &de_fault, 0);
185     set_idt_entry(6, &ud_fault, 0);
186     set_idt_entry(13, &gp_fault, 0);
187 }
188 
189 unsigned exception_vector(void)
190 {
191     unsigned short vector;
192 
193     asm("mov %%gs:4, %0" : "=rm"(vector));
194     return vector;
195 }
196 
197 unsigned exception_error_code(void)
198 {
199     unsigned short error_code;
200 
201     asm("mov %%gs:6, %0" : "=rm"(error_code));
202     return error_code;
203 }
204 
205 #ifndef __x86_64__
206 /*
207  * GDT, with 6 entries:
208  * 0x00 - NULL descriptor
209  * 0x08 - Code segment
210  * 0x10 - Data segment
211  * 0x18 - Not presend code segment
212  * 0x20 - Primery task
213  * 0x28 - Interrupt task
214  */
215 
216 static gdt_entry_t gdt[6];
217 #define TSS_GDT_OFFSET 4
218 
219 void set_gdt_entry(int num, u32 base,  u32 limit, u8 access, u8 gran)
220 {
221 	/* Setup the descriptor base address */
222 	gdt[num].base_low = (base & 0xFFFF);
223 	gdt[num].base_middle = (base >> 16) & 0xFF;
224 	gdt[num].base_high = (base >> 24) & 0xFF;
225 
226 	/* Setup the descriptor limits */
227 	gdt[num].limit_low = (limit & 0xFFFF);
228 	gdt[num].granularity = ((limit >> 16) & 0x0F);
229 
230 	/* Finally, set up the granularity and access flags */
231 	gdt[num].granularity |= (gran & 0xF0);
232 	gdt[num].access = access;
233 }
234 
235 void setup_gdt(void)
236 {
237 	struct descriptor_table_ptr gp;
238 	/* Setup the GDT pointer and limit */
239 	gp.limit = sizeof(gdt) - 1;
240 	gp.base = (ulong)&gdt;
241 
242 	memset(gdt, 0, sizeof(gdt));
243 
244 	/* Our NULL descriptor */
245 	set_gdt_entry(0, 0, 0, 0, 0);
246 
247 	/* The second entry is our Code Segment. The base address
248 	 *  is 0, the limit is 4GBytes, it uses 4KByte granularity,
249 	 *  uses 32-bit opcodes, and is a Code Segment descriptor. */
250 	set_gdt_entry(1, 0, 0xFFFFFFFF, 0x9A, 0xcf);
251 
252 	/* The third entry is our Data Segment. It's EXACTLY the
253 	 *  same as our code segment, but the descriptor type in
254 	 *  this entry's access byte says it's a Data Segment */
255 	set_gdt_entry(2, 0, 0xFFFFFFFF, 0x92, 0xcf);
256 
257 	/* Same as code register above but not present */
258 	set_gdt_entry(3, 0, 0xFFFFFFFF, 0x1A, 0xcf);
259 
260 
261 	/* Flush out the old GDT and install the new changes! */
262 	lgdt(&gp);
263 
264 	asm volatile ("mov %0, %%ds\n\t"
265 		      "mov %0, %%es\n\t"
266 		      "mov %0, %%fs\n\t"
267 		      "mov %0, %%gs\n\t"
268 		      "mov %0, %%ss\n\t"
269 		      "jmp $0x08, $.Lflush2\n\t"
270 		      ".Lflush2: "::"r"(0x10));
271 }
272 
273 static void set_idt_task_gate(int vec, u16 sel)
274 {
275     idt_entry_t *e = &idt[vec];
276 
277     memset(e, 0, sizeof *e);
278 
279     e->selector = sel;
280     e->ist = 0;
281     e->type = 5;
282     e->dpl = 0;
283     e->p = 1;
284 }
285 
286 /*
287  * 0 - main task
288  * 1 - interrupt task
289  */
290 
291 static tss32_t tss[2];
292 static char tss_stack[2][4096];
293 
294 void setup_tss32(void)
295 {
296 	u16 desc_size = sizeof(tss32_t);
297 	int i;
298 
299 	for (i = 0; i < 2; i++) {
300 		tss[i].cr3 = read_cr3();
301 		tss[i].ss0 = tss[i].ss1 = tss[i].ss2 = 0x10;
302 		tss[i].esp = tss[i].esp0 = tss[i].esp1 = tss[i].esp2 =
303 			(u32)tss_stack[i];
304 		tss[i].cs = 0x08;
305 		tss[i].ds = tss[i].es = tss[i].fs = tss[i].gs = tss[i].ss = 0x10;
306 		tss[i].iomap_base = (u16)desc_size;
307 		set_gdt_entry(TSS_GDT_OFFSET + i, (u32)&tss[i],
308 			     desc_size - 1, 0x89, 0x0f);
309 	}
310 
311 	ltr(TSS_MAIN);
312 }
313 
314 void set_intr_task_gate(int e, void *fn)
315 {
316 	tss[1].eip = (u32)fn;
317 	set_idt_task_gate(e, TSS_INTR);
318 }
319 
320 void print_current_tss_info(void)
321 {
322 	u16 tr = str();
323 	int i = (tr == TSS_MAIN) ? 0 : 1;
324 
325 	if (tr != TSS_MAIN && tr != TSS_INTR)
326 		printf("Unknown TSS %x\n", tr);
327 	else
328 		printf("TR=%x Main TSS back link %x. Current TSS back link %x\n",
329                tr, tss[0].prev, tss[i].prev);
330 }
331 #endif
332