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