furix
cute os
arch/x86_64/gdt.c
4388 bytes raw
1#include <gdt.h>
2
3#include <io.h>
4#include <percpu.h>
5#include <types.h>
6
7struct gdt_entry {
8 uint16_t limit_low;
9 uint16_t base_low;
10 uint8_t base_mid;
11 uint8_t access;
12 uint8_t granularity;
13 uint8_t base_high;
14} __attribute__((packed));
15
16/* system descriptors are 16 bytes */
17struct tss_desc {
18 uint16_t limit_low;
19 uint16_t base_low;
20 uint8_t base_mid;
21 uint8_t access;
22 uint8_t granularity;
23 uint8_t base_high;
24 uint32_t base_upper;
25 uint32_t reserved;
26} __attribute__((packed));
27
28struct gdt_table {
29 struct gdt_entry null;
30 struct gdt_entry code;
31 struct gdt_entry data;
32 struct tss_desc tss;
33} __attribute__((packed));
34
35static struct gdt_table gdt;
36static struct desc_ptr gdtr;
37
38static struct tss tss_area __attribute__((aligned(16)));
39
40/* IST1: #DF, IST2: NMI. dedicated stacks, so a fault that overflowed (or
41 corrupted) the current stack is still handled */
42static uint8_t ist1_stack[IST_STACK_SIZE] __attribute__((aligned(16)));
43static uint8_t ist2_stack[IST_STACK_SIZE] __attribute__((aligned(16)));
44
45static struct gdt_entry gdt_make_entry(uint32_t base, uint32_t limit, uint8_t access, uint8_t flags) {
46 struct gdt_entry entry;
47
48 entry.limit_low = (uint16_t)(limit & 0xFFFF);
49 entry.base_low = (uint16_t)(base & 0xFFFF);
50 entry.base_mid = (uint8_t)((base >> 16) & 0xFF);
51 entry.access = access;
52 entry.granularity = (uint8_t)((flags & 0xF0) | ((limit >> 16) & 0x0F));
53 entry.base_high = (uint8_t)((base >> 24) & 0xFF);
54
55 return entry;
56}
57
58static struct tss_desc tss_make_desc(const struct tss *tss) {
59 uint64_t base = (uint64_t)tss;
60 uint32_t limit = (uint32_t)(sizeof(struct tss) - 1);
61 struct tss_desc desc;
62
63 desc.limit_low = (uint16_t)(limit & 0xFFFF);
64 desc.base_low = (uint16_t)(base & 0xFFFF);
65 desc.base_mid = (uint8_t)((base >> 16) & 0xFF);
66 desc.access = 0x89; /* present, ring0, 64-bit TSS available */
67 desc.granularity = (uint8_t)(((limit >> 16) & 0x0F) << 4 | 0x01);
68 desc.base_high = (uint8_t)((base >> 24) & 0xFF);
69 desc.base_upper = (uint32_t)((base >> 32) & 0xFFFFFFFF);
70 desc.reserved = 0;
71
72 return desc;
73}
74
75/*
76 * per-AP TSSes and their IST stacks.
77 *
78 * one pair per AP rather than one shared TSS: a TSS holds the IST stack
79 * POINTERS, so a single shared one would leave every AP using the same #DF
80 * stack. two processors pushing frames onto one stack is precisely the
81 * corruption an IST is meant to survive, so this cannot be shared.
82 *
83 * the AP's GDT (inside its trampoline page) gets a descriptor pointing at the
84 * TSS for that AP's slot; see patch_trampoline() in ap_boot.c.
85 */
86static struct tss ap_tss[PERCPU_MAX_CPUS] __attribute__((aligned(16)));
87static uint8_t ap_ist_df[PERCPU_MAX_CPUS][IST_STACK_SIZE] __attribute__((aligned(16)));
88static uint8_t ap_ist_nmi[PERCPU_MAX_CPUS][IST_STACK_SIZE] __attribute__((aligned(16)));
89
90/* zero a TSS and point both IST slots at the stacks for `slot` */
91static void tss_init_ist(struct tss *t, uint8_t slot) {
92 t->reserved0 = 0;
93 t->reserved0_pad = 0;
94 t->reserved1 = 0;
95 t->reserved2 = 0;
96 /* stacks grow down, so the usable top of each stack is its end */
97 t->ist[IST_DOUBLE_FAULT - 1] = (uint64_t)(&ap_ist_df[slot][0] + IST_STACK_SIZE);
98 t->ist[IST_NMI - 1] = (uint64_t)(&ap_ist_nmi[slot][0] + IST_STACK_SIZE);
99 /* no I/O permission bitmap: the IOMap Base field equals the TSS length */
100 t->iomap_base = (uint16_t)sizeof(struct tss);
101}
102
103uint64_t gdt_ap_tss_base(uint8_t slot) {
104 tss_init_ist(&ap_tss[slot], slot);
105 return (uint64_t)&ap_tss[slot];
106}
107
108void gdt_init(void) {
109 tss_area.reserved0 = 0;
110 tss_area.reserved0_pad = 0;
111 tss_area.reserved1 = 0;
112 tss_area.reserved2 = 0;
113 /* stacks grow down, so point at the top */
114 tss_area.ist[IST_DOUBLE_FAULT - 1] = (uint64_t)(ist1_stack + IST_STACK_SIZE);
115 tss_area.ist[IST_NMI - 1] = (uint64_t)(ist2_stack + IST_STACK_SIZE);
116 /* no I/O permission bitmap: the IOMap Base field equals the TSS length */
117 tss_area.iomap_base = (uint16_t)sizeof(struct tss);
118
119 gdt.null = gdt_make_entry(0, 0, 0x00, 0x00);
120 gdt.code = gdt_make_entry(0, 0xFFFFF, 0x9A, 0xA0);
121 gdt.data = gdt_make_entry(0, 0xFFFFF, 0x92, 0xA0);
122 gdt.tss = tss_make_desc(&tss_area);
123
124 gdtr.limit = (uint16_t)(sizeof(gdt) - 1);
125 gdtr.base = (uint64_t)&gdt;
126 lgdt(&gdtr);
127
128 load_data_segments(SEGSEL_KERNEL_DATA);
129 reload_cs(SEGSEL_KERNEL_CODE);
130
131 /* load the task register only after the new GDT is live */
132 ltr(SEGSEL_TSS);
133}