Files
nt-lite/code/kernel/ke_idt.c
T

241 lines
7.6 KiB
C

// Kernel descriptor-table setup. This file owns GDT, IDT, and TSS initialization.
// GDT layout (matches segment selectors in ke_switch.asm / ke_syscall.asm):
// 0x00 null
// 0x08 ring-0 code (64-bit)
// 0x10 ring-0 data
// 0x18 ring-3 code (64-bit, DPL=3)
// 0x20 ring-3 data (DPL=3) - note: 0x1B/0x23 = 0x18|3 / 0x20|3
// 0x28 TSS low
// 0x30 TSS high.
#include <stdint.h>
#include <string.h>
#include <kernel/ke_idt.h>
#include <kernel/ke_cpu.h>
// External assembly procedures.
extern void KeFlushGdt(void* pGdtr, uint16_t codeSel, uint16_t dataSel, uint16_t tssSel);
extern void KeFlushIdt(void* pIdtr);
extern void KeIrqStub0(void);
extern void KeIrqStub1(void);
extern void KeIrqStub2(void);
extern void KeIrqStub3(void);
extern void KeIrqStub4(void);
extern void KeIrqStub5(void);
extern void KeIrqStub6(void);
extern void KeIrqStub7(void);
extern void KeIrqStub8(void);
extern void KeIrqStub9(void);
extern void KeIrqStub10(void);
extern void KeIrqStub11(void);
extern void KeIrqStub12(void);
extern void KeIrqStub13(void);
extern void KeIrqStub14(void);
extern void KeIrqStub15(void);
extern void KeIrqStub16(void);
extern void KeIrqStub17(void);
extern void KeIrqStub18(void);
extern void KeIrqStub19(void);
extern void KeIrqStub20(void);
extern void KeIrqStub21(void);
extern void KeIrqStub22(void);
extern void KeIrqStub23(void);
extern void KeRescheduleIpiStub(void);
extern void KeFaultDB(void);
extern void KeFaultNMI(void);
extern void KeNmiIpiStub(void);
extern void KeFaultBP(void);
extern void KeFaultOF(void);
extern void KeFaultBR(void);
extern void KeFaultUD(void);
extern void KeFaultNM(void);
extern void KeFaultDF(void);
extern void KeFaultCSO(void);
extern void KeFaultTS(void);
extern void KeFaultNP(void);
extern void KeFaultSS(void);
extern void KeFaultRSV15(void);
extern void KeFaultMF(void);
extern void KeFaultAC(void);
extern void KeFaultMC(void);
extern void KeFaultXM(void);
extern void KeFaultVE(void);
extern void KeFaultCP(void);
extern void KeFaultRSV22(void);
extern void KeFaultRSV23(void);
extern void KeFaultRSV24(void);
extern void KeFaultRSV25(void);
extern void KeFaultRSV26(void);
extern void KeFaultRSV27(void);
extern void KeFaultHV(void);
extern void KeFaultVC(void);
extern void KeFaultSX(void);
extern void KeFaultRSV31(void);
#define NUM_IRQS 24
// GDT.
static void KiGdtSetEntry(uint64_t* gdt, int idx, uint32_t base, uint32_t limit,
uint8_t access, uint8_t flags) {
gdt[idx] =
((uint64_t)(limit & 0xFFFF)) |
((uint64_t)(base & 0xFFFFFF) << 16) |
((uint64_t)access << 40) |
((uint64_t)(limit >> 16 & 0xF)<< 48) |
((uint64_t)(flags & 0xF) << 52) |
((uint64_t)(base >> 24 & 0xFF)<< 56);
}
// Implements the gdt Set Tss operation.
static void KiGdtSetTss(uint64_t* gdt, int idx, uintptr_t base, uint32_t limit) {
// TSS descriptor is 16 bytes (two GDT slots).
gdt[idx] = 0;
gdt[idx+1] = 0;
gdt[idx] =
((uint64_t)(limit & 0xFFFF)) |
((uint64_t)(base & 0xFFFFFF) << 16) |
(0x89ULL << 40) | // P=1,DPL=0,type=TSS64 avail
((uint64_t)((limit>>16)&0xF) << 48) |
((uint64_t)(base>>24 & 0xFF) << 56);
gdt[idx+1] = (base >> 32);
}
// Initializes gdt For Cpu.
void KeInitializeGdtForCpu(KCPU* cpu) {
if (!cpu) return;
TSS64* tss = &cpu->Tss;
uint64_t* gdt = cpu->Gdt;
memset(gdt, 0, sizeof(cpu->Gdt));
// 0x00: Null.
KiGdtSetEntry(gdt, 0, 0, 0, 0, 0);
// 0x08: Ring-0 Code (Access 0x9A, Flags 0xA for 64-bit).
KiGdtSetEntry(gdt, 1, 0, 0xFFFFF, 0x9A, 0xA);
// 0x10: Ring-0 Data.
KiGdtSetEntry(gdt, 2, 0, 0xFFFFF, 0x92, 0xC);
// 0x18: Ring-3 Code.
KiGdtSetEntry(gdt, 3, 0, 0xFFFFF, 0xFA, 0xA);
// 0x20: Ring-3 Data.
KiGdtSetEntry(gdt, 4, 0, 0xFFFFF, 0xF2, 0xC);
// 0x28: CPU-local TSS (occupies slots 0x28 and 0x30).
tss->IoMapBase = sizeof(TSS64);
if (!tss->RSP0)
tss->RSP0 = cpu->KernelStackTop;
KiGdtSetTss(gdt, 5, (uintptr_t)tss, sizeof(TSS64) - 1);
cpu->Gdtr.Limit = sizeof(cpu->Gdt) - 1;
cpu->Gdtr.Base = (uint64_t)cpu->Gdt;
}
// Loads gdt For Current Cpu.
void KeLoadGdtForCurrentCpu(KCPU* cpu) {
if (!cpu) cpu = KeGetCurrentCpu();
KeFlushGdt(&cpu->Gdtr, 0x08, 0x10, 0x28);
// Reloading GS as a segment register during GDT reload can disturb the
// hidden GS base. Reinstall the kernel per-CPU base immediately after
// the descriptor reload so KeGetCurrentCpu() remains GS-backed.
KeLoadKernelGsBase(cpu);
}
// Initializes gdt.
void KeInitializeGdt(void) {
KCPU* cpu = KeGetCurrentCpu();
KeInitializeGdtForCpu(cpu);
KeLoadGdtForCurrentCpu(cpu);
}
// Updates current Tss Rsp0.
void KeSetCurrentTssRsp0(uintptr_t rsp0) {
TSS64* tss = KeGetCurrentTss();
if (tss)
tss->RSP0 = rsp0;
}
// IDT.
#define IDT_ENTRIES 256
#pragma pack(push, 1)
typedef struct {
uint16_t OffsetLow;
uint16_t Selector;
uint8_t Ist;
uint8_t TypeAttr;
uint16_t OffsetMid;
uint32_t OffsetHigh;
uint32_t MustBeZero;
} IDT_ENTRY;
#pragma pack(pop)
static IDT_ENTRY gIdt[IDT_ENTRIES];
#pragma pack(push, 1)
typedef struct { uint16_t Limit; uint64_t Base; } IDTR;
#pragma pack(pop)
static void KiIdtSetGate(int vec, uintptr_t handler,
uint8_t typeAttr, uint8_t ist) {
gIdt[vec].OffsetLow = (uint16_t)(handler & 0xFFFF);
gIdt[vec].Selector = 0x08; // ring-0 code segment
gIdt[vec].Ist = ist;
gIdt[vec].TypeAttr = typeAttr;
gIdt[vec].OffsetMid = (uint16_t)((handler >> 16) & 0xFFFF);
gIdt[vec].OffsetHigh = (uint32_t)((handler >> 32) & 0xFFFFFFFF);
gIdt[vec].MustBeZero = 0;
}
// Initializes idt.
void KeInitializeIdt(void) {
memset(gIdt, 0, sizeof(gIdt));
static void* faultStubs[32] = {
(void*)KeFaultDE, (void*)KeFaultDB, (void*)KeNmiIpiStub,
(void*)KeFaultBP, (void*)KeFaultOF, (void*)KeFaultBR,
(void*)KeFaultUD, (void*)KeFaultNM, (void*)KeFaultDF,
(void*)KeFaultCSO, (void*)KeFaultTS, (void*)KeFaultNP,
(void*)KeFaultSS, (void*)KeFaultGP, (void*)KeFaultPF,
(void*)KeFaultRSV15, (void*)KeFaultMF, (void*)KeFaultAC,
(void*)KeFaultMC, (void*)KeFaultXM, (void*)KeFaultVE,
(void*)KeFaultCP, (void*)KeFaultRSV22, (void*)KeFaultRSV23,
(void*)KeFaultRSV24, (void*)KeFaultRSV25, (void*)KeFaultRSV26,
(void*)KeFaultRSV27, (void*)KeFaultHV, (void*)KeFaultVC,
(void*)KeFaultSX, (void*)KeFaultRSV31,
};
for (int i = 0; i < 32; i++)
KiIdtSetGate(i, (uintptr_t)faultStubs[i], 0x8E, 0);
KiIdtSetGate(0x40, (uintptr_t)KeSyscallEntry, 0xEE, 0);
static void *irqStubs[NUM_IRQS] = {
(void*)KeIrqStub0, (void*)KeIrqStub1, (void*)KeIrqStub2,
(void*)KeIrqStub3, (void*)KeIrqStub4, (void*)KeIrqStub5,
(void*)KeIrqStub6, (void*)KeIrqStub7, (void*)KeIrqStub8,
(void*)KeIrqStub9, (void*)KeIrqStub10, (void*)KeIrqStub11,
(void*)KeIrqStub12, (void*)KeIrqStub13, (void*)KeIrqStub14,
(void*)KeIrqStub15, (void*)KeIrqStub16, (void*)KeIrqStub17,
(void*)KeIrqStub18, (void*)KeIrqStub19, (void*)KeIrqStub20,
(void*)KeIrqStub21, (void*)KeIrqStub22, (void*)KeIrqStub23,
};
for (int i = 0; i < NUM_IRQS; i++)
KiIdtSetGate(0x20 + i, (uintptr_t)irqStubs[i], 0x8E, 0);
KiIdtSetGate(KE_VECTOR_RESCHEDULE_IPI, (uintptr_t)KeRescheduleIpiStub, 0x8E, 0);
KeLoadIdtForCurrentCpu();
}
// Loads idt For Current Cpu.
void KeLoadIdtForCurrentCpu(void) {
IDTR idtr = { sizeof(gIdt) - 1, (uint64_t)gIdt };
KeFlushIdt(&idtr);
}