/*
 * Copyright (c) 2026, Chloe M.
 * Provided under the BSD-3 clause.
 *
 * Description: Kernel processor control region
 * Author:      Chloe M.
 */

#include <hal/kpcr.h>
#include <ke/bugcheck.h>
#include <machine/idt.h>
#include <machine/trap.h>
#include <machine/msr.h>
#include <machine/lapic.h>
#include <machine/gdt.h>

/* Externs */
extern GDT_ENTRY g_GDT[GDT_ENTRY_COUNT];

/*
 * Initialize interrupts for the current processor
 */
static VOID
InitInterrupts(VOID)
{
    MdIdtSetGate(0x00, (UPTR)TrapDivError, IDT_TRAP_GATE, 0);
    MdIdtSetGate(0x01, (UPTR)TrapDebugException, IDT_TRAP_GATE, 0);
    MdIdtSetGate(0x02, (UPTR)TrapNmi, IDT_TRAP_GATE, IST_NMI);
    MdIdtSetGate(0x03, (UPTR)TrapBreakPoint, IDT_TRAP_GATE, 0);
    MdIdtSetGate(0x04, (UPTR)TrapOverflow, IDT_TRAP_GATE, 0);
    MdIdtSetGate(0x05, (UPTR)TrapBoundRange, IDT_TRAP_GATE, 0);
    MdIdtSetGate(0x06, (UPTR)TrapInvalidOpcode, IDT_TRAP_GATE, 0);
    MdIdtSetGate(0x07, (UPTR)TrapNoCoprocessor, IDT_TRAP_GATE, 0);
    MdIdtSetGate(0x08, (UPTR)TrapDoubleFault, IDT_TRAP_GATE, 0);
    MdIdtSetGate(0x09, (UPTR)TrapReserved, IDT_TRAP_GATE, 0);
    MdIdtSetGate(0x0A, (UPTR)TrapInvalidTss, IDT_TRAP_GATE, 0);
    MdIdtSetGate(0x0B, (UPTR)TrapSegNotPresent, IDT_TRAP_GATE, 0);
    MdIdtSetGate(0x0C, (UPTR)TrapStackSegmentFault, IDT_TRAP_GATE, 0);
    MdIdtSetGate(0x0D, (UPTR)TrapGeneralProtection, IDT_TRAP_GATE, 0);
    MdIdtSetGate(0x0E, (UPTR)TrapPageFault, IDT_TRAP_GATE, 0);
    MdIdtSetGate(0x2E, (UPTR)TrapLegacySyscall, IDT_USER_GATE, 0);
    MdIdtLoad();
}

KPCR *
HalKpcrCurrent(VOID)
{
    KPCR *Kpcr;

    Kpcr = (KPCR *)MdRdmsr(IA32_GS_BASE);
    if (Kpcr == NULL) {
        KeBugCheck(
            BUGCHECK_UNBOUND_RESRC,
            "KPCR not bound in IA32_GS_BASE\n"
        );
    }

    return Kpcr;
}

VOID
HalKpcrP1Init(KPCR *Kpcr)
{
    /* Initialize interrupts for this core */
    InitInterrupts();

    /* Save the current KPCR */
    MdWrmsr(IA32_GS_BASE, (UPTR)Kpcr);
}

VOID
HalKpcrP2Init(KPCR *Kpcr)
{
    TSS_DESCRIPTOR *TssDesc;

    /* Initialize per-processor pools */
    ExPoolRegionInit(&Kpcr->PoolRegion);

    /* Initialize the Local APIC unit */
    MdLapicInit(Kpcr);

    /* Load the Task State Segment */
    TssDesc = (TSS_DESCRIPTOR *)&g_GDT[GDT_TSS_INDEX];
    MdTssInit(TssDesc);
    ASMV(
        "mov %0, %%ax\n"
        "ltr %%ax\n"
        :
        : "i" (GDT_TSS)
        : "memory"
    );

    /* Setup interrupt stacks */
    MdTssAllocIst(IST_NMI, 1024);

    /* Initialize the scheduler queue */
    SCHED_QUEUE_INIT(&Kpcr->SchedQueue);

    /* Enable interrupts */
    ASMV("sti");
}
