boredos_mirror/src/sys/gdt.c
2026-03-17 21:44:21 +01:00

170 lines
5.5 KiB
C

// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
#include "gdt.h"
#include <stdint.h>
#include <stddef.h>
#include "memory_manager.h"
static void *gdt_memset(void *s, int c, size_t n) {
uint8_t *p = (uint8_t *)s;
while (n--) {
*p++ = (uint8_t)c;
}
return s;
}
// Base GDT: 5 segments + 1 TSS (2 entries) = 7 entries for BSP.
// For SMP: we add 2 entries per additional CPU for their TSS.
// Max supported: 32 CPUs → 5 + 2*32 = 69 entries max.
#define GDT_BASE_ENTRIES 5 // NULL, KCode, KData, UData, UCode
#define GDT_MAX_ENTRIES 69 // 5 + 2*32
struct gdt_entry gdt[GDT_MAX_ENTRIES];
struct gdt_ptr gdtr;
struct tss_entry tss; // BSP TSS (CPU 0)
// Per-CPU TSS array (dynamically allocated for AP cores)
static struct tss_entry *ap_tss_array = NULL;
static uint32_t ap_tss_count = 0;
extern void gdt_flush(uint64_t);
extern void tss_flush(void);
static void gdt_set_gate(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) {
gdt[num].base_low = (base & 0xFFFF);
gdt[num].base_middle = (base >> 16) & 0xFF;
gdt[num].base_high = (base >> 24) & 0xFF;
gdt[num].limit_low = (limit & 0xFFFF);
gdt[num].granularity = ((limit >> 16) & 0x0F);
gdt[num].granularity |= (gran & 0xF0);
gdt[num].access = access;
}
// Write a 16-byte TSS descriptor into GDT entries [num] and [num+1]
static void gdt_set_tss_gate_at(int num, uint64_t base, uint32_t limit, uint8_t access, uint8_t gran) {
struct {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access;
uint8_t granularity;
uint8_t base_high;
uint32_t base_upper;
uint32_t reserved;
} __attribute__((packed)) *tss_desc = (void*)&gdt[num];
tss_desc->base_low = (base & 0xFFFF);
tss_desc->base_middle = (base >> 16) & 0xFF;
tss_desc->base_high = (base >> 24) & 0xFF;
tss_desc->base_upper = (base >> 32);
tss_desc->limit_low = (limit & 0xFFFF);
tss_desc->granularity = ((limit >> 16) & 0x0F);
tss_desc->granularity |= (gran & 0xF0);
tss_desc->access = access;
tss_desc->reserved = 0;
}
void tss_set_stack(uint64_t kernel_stack) {
tss.rsp0 = kernel_stack;
}
void tss_set_stack_cpu(uint32_t cpu_id, uint64_t kernel_stack) {
if (cpu_id == 0) {
tss.rsp0 = kernel_stack;
} else if (ap_tss_array && cpu_id - 1 < ap_tss_count) {
ap_tss_array[cpu_id - 1].rsp0 = kernel_stack;
}
}
void gdt_init(void) {
// Start with 7 entries (5 segments + BSP TSS taking 2)
gdtr.limit = (sizeof(struct gdt_entry) * 7) - 1;
gdtr.base = (uint64_t)&gdt;
// NULL segment
gdt_set_gate(0, 0, 0, 0, 0);
// Kernel Code segment (Ring 0, 64-bit)
gdt_set_gate(1, 0, 0, 0x9A, 0xAF);
// Kernel Data segment (Ring 0)
gdt_set_gate(2, 0, 0, 0x92, 0xAF);
// User Data segment (Ring 3)
gdt_set_gate(3, 0, 0, 0xF2, 0xAF);
// User Code segment (Ring 3, 64-bit)
gdt_set_gate(4, 0, 0, 0xFA, 0xAF);
// BSP TSS segment (entries 5 and 6)
gdt_memset(&tss, 0, sizeof(struct tss_entry));
tss.iopb_offset = sizeof(struct tss_entry);
void* initial_tss_stack = kmalloc_aligned(4096, 4096);
if (initial_tss_stack) {
tss.rsp0 = (uint64_t)initial_tss_stack + 4096;
}
gdt_set_tss_gate_at(5, (uint64_t)&tss, sizeof(struct tss_entry) - 1, 0x89, 0x00);
gdt_flush((uint64_t)&gdtr);
tss_flush();
}
// SMP: Add TSS entries for all AP cores and reload the GDT.
void gdt_init_ap_tss(uint32_t cpu_count) {
if (cpu_count <= 1) return; // No APs
uint32_t ap_count = cpu_count - 1;
ap_tss_count = ap_count;
// Allocate per-CPU TSS structures
ap_tss_array = (struct tss_entry *)kmalloc(ap_count * sizeof(struct tss_entry));
if (!ap_tss_array) return;
gdt_memset(ap_tss_array, 0, ap_count * sizeof(struct tss_entry));
// Each AP TSS goes at GDT slot 7 + (i*2) (since slot 5-6 is BSP TSS)
for (uint32_t i = 0; i < ap_count; i++) {
int gdt_slot = 7 + (i * 2);
if (gdt_slot + 1 >= GDT_MAX_ENTRIES) break;
ap_tss_array[i].iopb_offset = sizeof(struct tss_entry);
// Allocate a kernel stack for this AP's interrupt handling
void *ap_int_stack = kmalloc_aligned(8192, 4096);
if (ap_int_stack) {
ap_tss_array[i].rsp0 = (uint64_t)ap_int_stack + 8192;
}
gdt_set_tss_gate_at(gdt_slot, (uint64_t)&ap_tss_array[i],
sizeof(struct tss_entry) - 1, 0x89, 0x00);
}
// Update GDT limit to include all new entries
uint32_t total_entries = 7 + (ap_count * 2);
if (total_entries > GDT_MAX_ENTRIES) total_entries = GDT_MAX_ENTRIES;
gdtr.limit = (sizeof(struct gdt_entry) * total_entries) - 1;
// Reload GDTR on BSP with the expanded limit.
// We must NOT call tss_flush() here — the BSP TSS is already loaded
// and marked "busy" (0x8B). Trying to LTR a busy TSS causes GPF.
asm volatile("lgdt %0" : : "m"(gdtr));
}
// SMP: Load the TSS for a specific AP. Called from ap_entry().
void gdt_load_ap_tss(uint32_t cpu_id) {
if (cpu_id == 0) {
// BSP uses slot 5 → selector 0x28
asm volatile("mov $0x28, %%ax; ltr %%ax" ::: "ax");
return;
}
// AP cpu_id maps to GDT slot 7 + ((cpu_id-1) * 2)
uint16_t selector = (uint16_t)((7 + ((cpu_id - 1) * 2)) * sizeof(struct gdt_entry));
asm volatile("ltr %0" : : "r"(selector));
}