mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
170 lines
5.5 KiB
C
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));
|
|
}
|