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;
}
|