aboutsummaryrefslogtreecommitdiff
path: root/kernel/main/gdt.c
blob: 9bc8282d8047b01a7ae5547a90473cf4b2172ff1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include "main/gdt.h"
#include "globals.h"

#include "util/debug.h"
#include "util/printf.h"
#include "util/string.h"

typedef struct gdt_entry
{
    uint16_t ge_limitlo;
    uint16_t ge_baselo;
    uint8_t ge_basemid;
    uint8_t ge_access;
    uint8_t ge_flags;
    uint8_t ge_basehi;
} packed gdt_entry_t;

static gdt_entry_t gdt[GDT_COUNT] CORE_SPECIFIC_DATA;

typedef struct tss_entry
{
    uint32_t ts_reserved1;
    uint64_t ts_rsp0;
    uint64_t ts_rsp1;
    uint64_t ts_rsp2;
    uint64_t ts_reserved2;
    uint64_t ts_ist1;
    uint64_t ts_ist2;
    uint64_t ts_ist3;
    uint64_t ts_ist4;
    uint64_t ts_ist5;
    uint64_t ts_ist6;
    uint64_t ts_ist7;
    uint64_t ts_reserved3;
    uint16_t ts_iopb;
    uint16_t ts_reserved4;
} packed tss_entry_t;

typedef struct gdt_location
{
    uint16_t gl_size;
    uint64_t gl_offset;
} packed gdt_location_t;

static gdt_location_t gdtl = {.gl_size = GDT_COUNT * sizeof(gdt_entry_t),
                              .gl_offset = (uint64_t)&gdt};

static tss_entry_t tss CORE_SPECIFIC_DATA;

void gdt_init(void)
{
    memset(gdt, 0, sizeof(gdt));
    gdt_set_entry(GDT_KERNEL_TEXT, 0x0, 0xFFFFF, 0, 1, 0, 1);
    gdt_set_entry(GDT_KERNEL_DATA, 0x0, 0xFFFFF, 0, 0, 0, 1);
    gdt_set_entry(GDT_USER_TEXT, 0x0, 0xFFFFF, 3, 1, 0, 1);
    gdt_set_entry(GDT_USER_DATA, 0x0, 0xFFFFF, 3, 0, 0, 1);

    uintptr_t tss_pointer = (uintptr_t)&tss;
    gdt_set_entry(GDT_TSS, (uint32_t)tss_pointer, sizeof(tss), 0, 1, 0, 0);
    gdt[GDT_TSS / 8].ge_access &= ~(0b10000);
    gdt[GDT_TSS / 8].ge_access |= 0b1;
    gdt[GDT_TSS / 8].ge_flags &= ~(0b10000000);

    uint64_t tss_higher_half = ((uint64_t)tss_pointer) >> 32;
    memcpy(&gdt[GDT_TSS / 8 + 1], &tss_higher_half, 8);

    memset(&tss, 0, sizeof(tss));
    tss.ts_iopb = sizeof(tss);

    gdt_location_t *data = &gdtl;
    int segment = GDT_TSS;

    dbg(DBG_CORE, "Installing GDT and TR\n");
    __asm__ volatile("lgdt (%0); ltr %1" ::"p"(data), "m"(segment));
}

void gdt_set_kernel_stack(void *addr) { tss.ts_rsp0 = (uint64_t)addr; }

void gdt_set_entry(uint32_t segment, uint32_t base, uint32_t limit,
                   uint8_t ring, int exec, int dir, int rw)
{
    KASSERT(segment < GDT_COUNT * 8 && 0 == segment % 8);
    KASSERT(ring <= 3);
    KASSERT(limit <= 0xFFFFF);

    int index = segment / 8;
    gdt[index].ge_limitlo = (uint16_t)limit;
    gdt[index].ge_baselo = (uint16_t)base;
    gdt[index].ge_basemid = (uint8_t)(base >> 16);
    gdt[index].ge_basehi = (uint8_t)(base >> 24);

    // For x86-64, set the L bit to indicate a 64-bit descriptor and clear Sz
    // Having both L and Sz set is reserved for future use
    gdt[index].ge_flags = (uint8_t)(0b10100000 | (limit >> 16));

    gdt[index].ge_access = 0b10000000;
    gdt[index].ge_access |= (ring << 5);
    gdt[index].ge_access |= 0b10000;
    if (exec)
    {
        gdt[index].ge_access |= 0b1000;
    }
    if (dir)
    {
        gdt[index].ge_access |= 0b100;
    }
    if (rw)
    {
        gdt[index].ge_access |= 0b10;
    }
}

void gdt_clear(uint32_t segment)
{
    KASSERT(segment < GDT_COUNT * 8 && 0 == segment % 8);
    memset(&gdt[segment / 8], 0, sizeof(gdt[segment / 8]));
}

size_t gdt_tss_info(const void *arg, char *buf, size_t osize)
{
    size_t size = osize;

    KASSERT(NULL == arg);

    iprintf(&buf, &size, "TSS:\n");
    iprintf(&buf, &size, "kstack: 0x%p\n", (void *)tss.ts_rsp0);

    return size;
}