1 | // SPDX-License-Identifier: GPL-3.0-or-later |
2 | |
3 | #include "mos/x86/descriptors/descriptors.h" |
4 | |
5 | #include <mos/platform/platform.h> |
6 | #include <mos/syslog/printk.h> |
7 | #include <mos/x86/x86_platform.h> |
8 | #include <mos_string.h> |
9 | |
10 | typeof(x86_cpu_descriptor) x86_cpu_descriptor = { 0 }; |
11 | |
12 | typedef enum |
13 | { |
14 | GDT_ENTRY_CODE, |
15 | GDT_ENTRY_DATA, |
16 | } gdt_entry_type_t; |
17 | |
18 | typedef enum |
19 | { |
20 | GDT_RING_KERNEL = 0, |
21 | GDT_RING_1 = 1, |
22 | GDT_RING_2 = 2, |
23 | GDT_RING_USER = 3, |
24 | } gdt_ring_t; |
25 | |
26 | typedef enum |
27 | { |
28 | GDT_GRAN_BYTE = 0, |
29 | GDT_GRAN_PAGE = 1, |
30 | } gdt_gran_t; |
31 | |
32 | static gdt_entry_t *gdt_set_entry(gdt_entry_t *entry, ptr_t base, u32 limit, gdt_entry_type_t entry_type, gdt_ring_t dpl, gdt_gran_t gran) |
33 | { |
34 | entry->base_low = MASK_BITS(base, 24); |
35 | entry->base_high = MASK_BITS((base >> 24), 8); |
36 | entry->base_veryhigh = base >> 32; |
37 | entry->long_mode_code = entry_type == GDT_ENTRY_CODE; |
38 | entry->pm32_segment = !entry->long_mode_code; // it's one or the other |
39 | |
40 | entry->limit_low = MASK_BITS(limit, 16); |
41 | entry->limit_high = MASK_BITS((limit >> 16), 4); |
42 | entry->present = 1; |
43 | entry->available = 1; |
44 | entry->read_write = true; |
45 | entry->code_data_segment = true; |
46 | entry->dpl = dpl; |
47 | entry->executable = (entry_type == GDT_ENTRY_CODE); |
48 | entry->granularity = gran; |
49 | entry->accessed = false; // "Best left clear (0)," |
50 | |
51 | // ! This has to be false forever, for: |
52 | // * 1) allow system calls to be executed in ring 0, |
53 | // * otherwise, it means this segment "can" be executed by all rings outer than `dpl` |
54 | // * 2) always 0 for a TSS segment. |
55 | entry->conforming_expand_down = false; |
56 | return entry; |
57 | } |
58 | |
59 | void x86_init_percpu_gdt() |
60 | { |
61 | x86_cpu_descriptor_t *this_cpu_desc = per_cpu(x86_cpu_descriptor); |
62 | memzero(s: this_cpu_desc, n: sizeof(x86_cpu_descriptor_t)); |
63 | |
64 | // {Kernel,User}{Code,Data} Segments |
65 | // We are using a flat memory model, so the base is 0 and the limit is all the way up to the end of the address space. |
66 | gdt_set_entry(entry: &this_cpu_desc->gdt[1], base: 0x00000000, limit: 0xFFFFFFFF, entry_type: GDT_ENTRY_CODE, dpl: GDT_RING_KERNEL, gran: GDT_GRAN_PAGE); |
67 | gdt_set_entry(entry: &this_cpu_desc->gdt[2], base: 0x00000000, limit: 0xFFFFFFFF, entry_type: GDT_ENTRY_DATA, dpl: GDT_RING_KERNEL, gran: GDT_GRAN_PAGE); |
68 | gdt_set_entry(entry: &this_cpu_desc->gdt[3], base: 0x00000000, limit: 0xFFFFFFFF, entry_type: GDT_ENTRY_CODE, dpl: GDT_RING_USER, gran: GDT_GRAN_PAGE); |
69 | gdt_set_entry(entry: &this_cpu_desc->gdt[4], base: 0x00000000, limit: 0xFFFFFFFF, entry_type: GDT_ENTRY_DATA, dpl: GDT_RING_USER, gran: GDT_GRAN_PAGE); |
70 | |
71 | // TSS segment |
72 | gdt_entry_t *tss_seg = gdt_set_entry(entry: &this_cpu_desc->gdt[5], base: (ptr_t) &this_cpu_desc->tss, limit: sizeof(tss64_t), entry_type: GDT_ENTRY_CODE, dpl: GDT_RING_KERNEL, gran: GDT_GRAN_BYTE); |
73 | |
74 | // ! Set special attributes for the TSS segment. |
75 | tss_seg->code_data_segment = 0; // indicates TSS/LDT (see also `accessed`) |
76 | tss_seg->accessed = 1; // With a system entry (`code_data_segment` = 0), 1 indicates TSS and 0 indicates LDT |
77 | tss_seg->read_write = 0; // For a TSS, indicates busy (1) or not busy (0). |
78 | tss_seg->executable = 1; // For a TSS, 1 indicates 32-bit (1) or 16-bit (0). |
79 | tss_seg->available = 0; // 0 for a TSS |
80 | |
81 | this_cpu_desc->gdt_ptr.base = this_cpu_desc->gdt; |
82 | this_cpu_desc->gdt_ptr.limit = sizeof(this_cpu_desc->gdt) - 1; |
83 | gdt_flush(gdt_ptr: &this_cpu_desc->gdt_ptr); |
84 | } |
85 | |
86 | void x86_init_percpu_tss() |
87 | { |
88 | x86_cpu_descriptor_t *this_cpu_desc = per_cpu(x86_cpu_descriptor); |
89 | memzero(s: &this_cpu_desc->tss, n: sizeof(tss64_t)); |
90 | tss_flush(GDT_SEGMENT_TSS); |
91 | } |
92 | |