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

#include <xtos.hh>


/**
 * Bootstraps an Application Processor (AP) into the active kernel. This routine is executed exclusively by secondary
 * processors after being awakened by the BSP. It is called directly from the startup trampoline.
 *
 * @param StartBlock
 *        Supplies a pointer to the processor start block containing initialization information provided by the kernel.
 *
 * @return This routine does not return any value.
 *
 * @since XT 1.0
 */
XTAPI
VOID
KE::KernelInit::BootstrapApplicationProcessor(IN PPROCESSOR_START_BLOCK StartBlock)
{
    PKPROCESSOR_CONTROL_BLOCK ControlBlock;

    /* Initialize application CPU */
    AR::ProcessorSupport::InitializeProcessor(StartBlock->ProcessorStructures);

    /* Get processor control block */
    ControlBlock = KE::Processor::GetCurrentProcessorControlBlock();

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

    /* Raise to HIGH runlevel */
    KE::RunLevel::RaiseRunLevel(HIGH_LEVEL);

    /* Mark processor as started */
    StartBlock->Started = TRUE;

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

    /* Save processor state */
    KE::Processor::SaveProcessorControlState(&ControlBlock->ProcessorState);

    /* Initialize per-CPU spin lock queues */
    KE::SpinLock::InitializeLockQueues();

    /* Initialize interrupt handlers */
    InitializeInterruptHandlers();

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

    /* Initialize local clock for this CPU */
    HL::Timer::InitializeLocalClock();

    /* Create and initialize IDLE thread */
    PS::Thread::CreateIdleThread(ControlBlock, StartBlock->Stack);

    /* Register DISPATCH interrupt handler */
    HL::Irq::RegisterSystemInterruptHandler(APIC_VECTOR_DPC, KE::Dispatcher::HandleDispatchInterrupt);

    /* Enter idle loop */
    DebugPrint(L"KernelInit::BootstrapApplicationProcessor() finished for CPU #%lu. Entering IDLE loop.\n",
               ControlBlock->CpuNumber);
    KE::Dispatcher::EnterIdleLoop();
}

/**
 * Bootstraps the XT kernel and global subsystems. This routine is executed exclusively by the Bootstrap Processor
 * and it is called immediately after switching to the kernel boot stack.
 *
 * @return This routine does not return any value.
 *
 * @since XT 1.0
 */
XTAPI
VOID
KE::KernelInit::BootstrapKernel(VOID)
{
    PKPROCESSOR_CONTROL_BLOCK Prcb;

    /* Get processor control block */
    Prcb = KE::Processor::GetCurrentProcessorControlBlock();

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

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

    /* Initialize spin locks */
    KE::SpinLock::InitializeAllLocks();
    KE::SpinLock::InitializeLockQueues();

    /* Initialize interrupt handlers */
    InitializeInterruptHandlers();

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

    /* Initialize XTOS kernel */
    InitializeKernel();

    /* Initialize Memory Manager */
    MM::Manager::InitializeMemoryManager();

    /* Enable shadow buffer for framebuffer */
    HL::FrameBuffer::EnableShadowBuffer();

    /* Create and initialize IDLE process */
    PS::Process::CreateIdleProcess(Prcb);

    /* Start all application processors */
    KE::Processor::InitializeProcessorBlocks();
    HL::Cpu::InitializeProcessorAffinity();
    HL::Cpu::StartAllProcessors();

    /* Register DISPATCH interrupt handler */
    HL::Irq::RegisterSystemInterruptHandler(APIC_VECTOR_DPC, KE::Dispatcher::HandleDispatchInterrupt);

    /* Enter idle loop */
    DebugPrint(L"KernelInit::BootstrapKernel() finished. Entering IDLE loop.\n");
    KE::Dispatcher::EnterIdleLoop();
}

/**
 * Initializes and registers the core system interrupt handlers.
 *
 * @return This routine does not return any value.
 *
 * @since XT 1.0
 */
XTAPI
VOID
KE::KernelInit::InitializeInterruptHandlers(VOID)
{
    /* Register interrupt handlers */
    HL::Irq::RegisterSystemInterruptHandler(APIC_VECTOR_APC, KE::Apc::HandleApcInterrupt);
    HL::Irq::RegisterSystemInterruptHandler(APIC_VECTOR_DPC, KE::Dispatcher::HandleDispatchInterrupt);
    HL::Irq::RegisterSystemInterruptHandler(APIC_VECTOR_IPI, KE::Ipi::HandleIpiInterrupt);
}

/**
 * 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");
        KE::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 page map support */
    MM::Paging::InitializePageMapSupport();

    /* Initialize Kernel Shared Data (KSD) */
    KE::SharedData::InitializeKernelSharedData();

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

/**
 * 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::ProcessorSupport::GetBootStack() & ~(STACK_ALIGNMENT - 1));

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

    /* Discard old stack frame, switch stack, make space for NPX and jump to KernelInit::StartKernel() */
    __asm__ volatile("movl %[Stack], %%esp\n"
                     "subl %[TotalSize], %%esp\n"
                     "xorl %%ebp, %%ebp\n"
                     "pushl %[Cr0Value]\n"
                     "jmp *%[TargetRoutine]\n"
                     :
                     : [Cr0Value] "i" (CR0_EM | CR0_MP | CR0_TS),
                       [Stack] "r" (Stack),
                       [TargetRoutine] "r" (StartKernel),
                       [TotalSize] "i" (KTHREAD_STACK_OFFSET)
                     : "ebp", "esp", "memory");
}
