| 1 | #include <boot/gdt.h> |
| 2 | #include <boot/tss.h> |
| 3 | #include <console/panic.h> |
| 4 | #include <console/printf.h> |
| 5 | #include <mem/alloc.h> |
| 6 | #include <mem/alloc_or_die.h> |
| 7 | #include <mem/page.h> |
| 8 | #include <stdint.h> |
| 9 | #include <string.h> |
| 10 | |
| 11 | #define GDT_ENTRIES 7 |
| 12 | |
| 13 | void gdt_set_tss(struct gdt_entry_tss *tss_desc, uint64_t base, |
| 14 | uint32_t limit) { |
| 15 | tss_desc->limit_low = limit & 0xFFFF; |
| 16 | tss_desc->base_low = base & 0xFFFF; |
| 17 | tss_desc->base_middle = (base >> 16) & 0xFF; |
| 18 | tss_desc->access = 0x89; // Present, type = 64-bit TSS (available) |
| 19 | tss_desc->granularity = (limit >> 16) & 0x0F; |
| 20 | tss_desc->granularity |= 0x00; // No granularity for TSS |
| 21 | tss_desc->base_high = (base >> 24) & 0xFF; |
| 22 | tss_desc->base_upper = (base >> 32) & 0xFFFFFFFF; |
| 23 | tss_desc->reserved = 0; |
| 24 | } |
| 25 | |
| 26 | void gdt_set_gate(struct gdt_entry *gdt, int num, uint64_t base, uint32_t limit, |
| 27 | uint8_t access, uint8_t gran) { |
| 28 | gdt[num].limit_low = (limit & 0xFFFF); |
| 29 | gdt[num].base_low = (base & 0xFFFF); |
| 30 | gdt[num].base_middle = (base >> 16) & 0xFF; |
| 31 | gdt[num].access = access; |
| 32 | gdt[num].granularity = (limit >> 16) & 0x0F; |
| 33 | gdt[num].granularity |= (gran & 0xF0); |
| 34 | gdt[num].base_high = (base >> 24) & 0xFF; |
| 35 | } |
| 36 | |
| 37 | void gdt_load(struct gdt_entry *gdt, uint64_t n_entries) { |
| 38 | struct gdt_ptr gp = { |
| 39 | .limit = (sizeof(struct gdt_entry) * n_entries) - 1, |
| 40 | .base = (uint64_t) gdt, |
| 41 | }; |
| 42 | asm volatile("lgdt %0" : : "m" (gp)); |
| 43 | } |
| 44 | |
| 45 | void reload_segment_registers(uint16_t cs_selector, uint16_t ds_selector) { |
| 46 | asm volatile("pushq %0\n\t" |
| 47 | "leaq 1f(%%rip), %%rax\n\t" |
| 48 | "pushq %%rax\n\t" |
| 49 | "lretq\n\t" |
| 50 | "1:\n\t" |
| 51 | "movw %w1, %%ax\n\t" |
| 52 | "movw %%ax, %%ds\n\t" |
| 53 | "movw %%ax, %%es\n\t" |
| 54 | "movw %%ax, %%fs\n\t" |
| 55 | "movw %%ax, %%gs\n\t" |
| 56 | "movw %%ax, %%ss\n\t" |
| 57 | : |
| 58 | : "r" ((uint64_t) cs_selector), "r" (ds_selector) |
| 59 | : "rax" , "memory" ); |
| 60 | } |
| 61 | |
| 62 | void gdt_init(struct gdt_entry *gdt, struct tss *tss) { |
| 63 | gdt_set_gate(gdt, num: 0, base: 0, limit: 0, access: 0, gran: 0); // Null |
| 64 | |
| 65 | gdt_set_gate(gdt, num: 1, base: 0, limit: 0xFFFFFFFF, ACCESS_CODE_RING0, GRAN_CODE); |
| 66 | gdt_set_gate(gdt, num: 2, base: 0, limit: 0xFFFFFFFF, ACCESS_DATA_RING0, GRAN_DATA); |
| 67 | gdt_set_gate(gdt, num: 5, base: 0, limit: 0xFFFFFFFF, ACCESS_CODE_RING3, GRAN_CODE); |
| 68 | gdt_set_gate(gdt, num: 6, base: 0, limit: 0xFFFFFFFF, ACCESS_DATA_RING3, GRAN_DATA); |
| 69 | |
| 70 | // TSS (occupies entries 3 and 4) |
| 71 | gdt_set_tss(tss_desc: (struct gdt_entry_tss *) &gdt[3], base: (uint64_t) tss, |
| 72 | limit: sizeof(struct tss) - 1); |
| 73 | |
| 74 | tss->io_map_base = sizeof(struct tss); |
| 75 | |
| 76 | gdt_load(gdt, GDT_ENTRIES); |
| 77 | tss->ist1 = |
| 78 | (uint64_t) alloc_or_die(kmalloc_aligned(8 * PAGE_SIZE, PAGE_SIZE)); |
| 79 | tss->rsp0 = |
| 80 | (uint64_t) alloc_or_die(kmalloc_aligned(8 * PAGE_SIZE, PAGE_SIZE)); |
| 81 | |
| 82 | /* stacks grow down */ |
| 83 | tss->ist1 += 8 * PAGE_SIZE; |
| 84 | tss->rsp0 += 8 * PAGE_SIZE; |
| 85 | |
| 86 | reload_segment_registers(GDT_KERNEL_CODE, GDT_KERNEL_DATA); |
| 87 | |
| 88 | asm volatile("ltr %w0" : : "r" (0x18)); // TSS selector |
| 89 | } |
| 90 | |
| 91 | void gdt_install(void) { |
| 92 | struct gdt_entry *gdt = kmalloc_aligned( |
| 93 | sizeof(struct gdt_entry) * GDT_ENTRIES, 64, ALLOC_FLAGS_ZERO); |
| 94 | struct tss *tss = kmalloc_aligned(sizeof(struct tss), 64, ALLOC_FLAGS_ZERO); |
| 95 | if (!gdt || !tss) |
| 96 | panic("GDT INIT NOT OK!!!" ); |
| 97 | |
| 98 | gdt_init(gdt, tss); |
| 99 | |
| 100 | return; |
| 101 | } |
| 102 | |