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
10typeof(x86_cpu_descriptor) x86_cpu_descriptor = { 0 };
11
12typedef enum
13{
14 GDT_ENTRY_CODE,
15 GDT_ENTRY_DATA,
16} gdt_entry_type_t;
17
18typedef 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
26typedef enum
27{
28 GDT_GRAN_BYTE = 0,
29 GDT_GRAN_PAGE = 1,
30} gdt_gran_t;
31
32static 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
59void 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
86void 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