| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "mos/x86/descriptors/descriptors.hpp" |
| 4 | |
| 5 | #include <mos/platform/platform.hpp> |
| 6 | #include <mos/syslog/printk.hpp> |
| 7 | #include <mos/x86/x86_platform.hpp> |
| 8 | #include <mos_string.hpp> |
| 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 | |