/**
 * PROJECT:         ExectOS
 * COPYRIGHT:       See COPYING.md in the top level directory
 * FILE:            xtoskrnl/ke/amd64/krnlinit.cc
 * DESCRIPTION:     CPU architecture specific kernel initialization
 * DEVELOPERS:      Rafal Kupiec <belliash@codingworkshop.eu.org>
 */

#include <xtos.hh>


/**
 * This routine initializes XT kernel.
 *
 * @return This routine does not return any value.
 *
 * @since XT 1.0
 */
XTAPI
VOID
KE::KernelInit::InitializeKernel(VOID)
{
    XTSTATUS Status;

    /* Initialize hardware layer subsystem */
    Status = HL::Init::InitializeSystem();
    if(Status != STATUS_SUCCESS)
    {
        /* Hardware layer initialization failed, kernel panic */
        DebugPrint(L"Failed to initialize hardware layer subsystem!\n");
        Crash::Panic(0);
    }
}

/**
 * Performs architecture-specific initialization for the kernel executive.
 *
 * @return This routine does not return any value.
 *
 * @since XT 1.0
 */
XTAPI
VOID
KE::KernelInit::InitializeMachine(VOID)
{
    /* Re-enable IDE interrupts */
    HL::IoPort::WritePort8(0x376, 0);
    HL::IoPort::WritePort8(0x3F6, 0);

    /* Initialize frame buffer */
    HL::FrameBuffer::InitializeFrameBuffer();

    /* Initialize processor */
    HL::Cpu::InitializeProcessor();

    /* Initialize page map support */
    MM::Paging::InitializePageMapSupport();
}

/**
 * This routine starts up the XT kernel. It is called after switching boot stack.
 *
 * @return This routine does not return any value.
 *
 * @since XT 1.0
 */
XTAPI
VOID
KE::KernelInit::StartKernel(VOID)
{
    PKPROCESSOR_CONTROL_BLOCK Prcb;
    ULONG_PTR PageDirectory[2];
    PKPROCESS CurrentProcess;
    PKTHREAD CurrentThread;

    /* Get processor control block and current thread */
    Prcb = Processor::GetCurrentProcessorControlBlock();
    CurrentThread = Processor::GetCurrentThread();

    /* Get current process */
    CurrentProcess = CurrentThread->ApcState.Process;

    /* Initialize CPU power state structures */
    PO::Idle::InitializeProcessorIdleState(Prcb);

    /* Save processor state */
    Processor::SaveProcessorState(&Prcb->ProcessorState);

    /* Lower to APC runlevel */
    RunLevel::LowerRunLevel(APC_LEVEL);

    /* Initialize XTOS kernel */
    InitializeKernel();

    /* Initialize Idle process */
    PageDirectory[0] = 0;
    PageDirectory[1] = 0;
    KProcess::InitializeProcess(CurrentProcess, 0, MAXULONG_PTR, PageDirectory, FALSE);
    CurrentProcess->Quantum = MAXCHAR;

    /* Initialize Idle thread */
    KThread::InitializeThread(CurrentProcess, CurrentThread, NULLPTR, NULLPTR, NULLPTR,
                              NULLPTR, NULLPTR, AR::ProcSup::GetBootStack(), TRUE);
    CurrentThread->NextProcessor = Prcb->CpuNumber;
    CurrentThread->Priority = THREAD_HIGH_PRIORITY;
    CurrentThread->State = Running;
    CurrentThread->Affinity = (ULONG_PTR)1 << Prcb->CpuNumber;
    CurrentThread->WaitRunLevel = DISPATCH_LEVEL;
    CurrentProcess->ActiveProcessors |= (ULONG_PTR)1 << Prcb->CpuNumber;

    /* Enter infinite loop */
    DebugPrint(L"KernelInit::StartKernel() finished. Entering infinite loop.\n");
    Crash::HaltSystem();
}

/**
 * Switches execution to a new boot stack and transfers control to the KernelInit::StartKernel() routine.
 *
 * @return This routine does not return any value.
 *
 * @since XT 1.0
 */
XTAPI
VOID
KE::KernelInit::SwitchBootStack(VOID)
{
    ULONG_PTR Stack;
    PVOID StartKernel;

    /* Calculate the stack pointer at the top of the buffer, ensuring it is properly aligned as required by the ABI */
    Stack = ((ULONG_PTR)AR::ProcSup::GetBootStack() + KERNEL_STACK_SIZE) & ~(STACK_ALIGNMENT - 1);

    /* Get address of KernelInit::StartKernel() */
    StartKernel = (PVOID)KernelInit::StartKernel;

    /* Discard old stack frame, switch stack and jump to KernelInit::StartKernel() */
    __asm__ volatile("mov %0, %%rdx\n"
                     "xor %%rbp, %%rbp\n"
                     "mov %%rdx, %%rsp\n"
                     "sub %1, %%rsp\n"
                     "jmp *%2\n"
                     :
                     : "m" (Stack),
                       "i" (FLOATING_SAVE_AREA_SIZE | KEXCEPTION_FRAME_SIZE | KSWITCH_FRAME_SIZE | KRETURN_ADDRESS_SIZE),
                       "r" (StartKernel)
                     : "rdx", "rbp", "rsp", "memory");
}
