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/nofail.h>
7#include <mem/page.h>
8#include <stdint.h>
9#include <string.h>
10
11#define GDT_ENTRIES 7
12
13static 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
26static void gdt_set_gate(struct gdt_entry *gdt, int num, uint64_t base,
27 uint32_t limit, 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
37static void gdt_lgdt(struct gdt_entry *gdt, uint64_t entry_count) {
38 struct gdt_ptr gp = {
39 .limit = (sizeof(struct gdt_entry) * entry_count) - 1,
40 .base = (uint64_t) gdt,
41 };
42 asm volatile("lgdt %0" : : "m"(gp));
43}
44
45static void reload_segment_registers(uint16_t cs_selector,
46 uint16_t ds_selector) {
47 asm volatile("pushq %0\n\t"
48 "leaq 1f(%%rip), %%rax\n\t"
49 "pushq %%rax\n\t"
50 "lretq\n\t"
51 "1:\n\t"
52 "movw %w1, %%ax\n\t"
53 "movw %%ax, %%ds\n\t"
54 "movw %%ax, %%es\n\t"
55 "movw %%ax, %%fs\n\t"
56 "movw %%ax, %%gs\n\t"
57 "movw %%ax, %%ss\n\t"
58 :
59 : "r"((uint64_t) cs_selector), "r"(ds_selector)
60 : "rax", "memory");
61}
62
63static void gdt_init(struct gdt_entry *gdt, struct tss *tss) {
64 gdt_set_gate(gdt, num: 0, base: 0, limit: 0, access: 0, gran: 0); // Null
65
66 gdt_set_gate(gdt, num: 1, base: 0, limit: 0xFFFFFFFF, ACCESS_CODE_RING0, GRAN_CODE);
67 gdt_set_gate(gdt, num: 2, base: 0, limit: 0xFFFFFFFF, ACCESS_DATA_RING0, GRAN_DATA);
68 gdt_set_gate(gdt, num: 5, base: 0, limit: 0xFFFFFFFF, ACCESS_CODE_RING3, GRAN_CODE);
69 gdt_set_gate(gdt, num: 6, base: 0, limit: 0xFFFFFFFF, ACCESS_DATA_RING3, GRAN_DATA);
70
71 // TSS (occupies entries 3 and 4)
72 gdt_set_tss(tss_desc: (struct gdt_entry_tss *) &gdt[3], base: (uint64_t) tss,
73 limit: sizeof(struct tss) - 1);
74
75 tss->io_map_base = sizeof(struct tss);
76
77 gdt_lgdt(gdt, GDT_ENTRIES);
78 tss->ist1 = (uint64_t) nofail(valloc_aligned(8 * PAGE_SIZE, PAGE_SIZE));
79 tss->rsp0 = (uint64_t) nofail(valloc_aligned(8 * PAGE_SIZE, PAGE_SIZE));
80
81 /* stacks grow down */
82 tss->ist1 += 8 * PAGE_SIZE;
83 tss->rsp0 += 8 * PAGE_SIZE;
84
85 reload_segment_registers(GDT_KERNEL_CODE, GDT_KERNEL_DATA);
86
87 asm volatile("ltr %w0" : : "r"(0x18)); // TSS selector
88}
89
90void gdt_load(void) {
91 struct gdt_entry *gdt =
92 valloc_aligned(sizeof(struct gdt_entry) * GDT_ENTRIES, 64, ALLOC_ZERO);
93 struct tss *tss = valloc_aligned(sizeof(struct tss), 64, ALLOC_ZERO);
94 if (!gdt || !tss)
95 panic(INIT_FAILED, "GDT");
96
97 gdt_init(gdt, tss);
98
99 return;
100}
101