////////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   Hal.cpp
//
//  Note:   Kernel Only
//

#include "hal.h"
#include "halkd.h"

#include "strformat.cpp"

int __cdecl _cinit(void);

#if ISA_ARM
void SerialInit(UINT32 * uartBase);
void SerialOutHex(uint32 value);
void SerialOut(const char * str);
#define ArmSerialInit(x) SerialInit(x)
#define ArmSerialOut(x) SerialOut(x)
#define ArmSerialOutHex(x) SerialOutHex(x)
#else // ISA_ARM
#define ArmSerialInit(x)
#define ArmSerialOut(x)
#define ArmSerialOutHex(x)
#endif // ISA_ARM

//////////////////////////////////////////////////////////////////////////////
// Kernel methods
//
extern wchar_t _LinkDate[];

bartok_char *
Class_Microsoft_Singularity_Kernel::g_HalGetLinkDate()
{
    return (bartok_char *)_LinkDate;
}

void fail_assert(const char *expr)
{
    // TODO: wire up something useful here.
    __debugbreak();
}

//////////////////////////////////////////////////////////////////////////////////////////////////
//
Class_System_RuntimeType *CpuRuntimeType = &Class_Microsoft_Singularity_Hal_Cpu::_type;
Class_System_RuntimeType *PlatformRuntimeType = &Class_Microsoft_Singularity_Hal_Platform::_type;

Class_Microsoft_Singularity_Hal_Cpu *Class_Microsoft_Singularity_Hal_Platform::g_GetCurrentCpu()
{
    return (Class_Microsoft_Singularity_Hal_Cpu *)
        Class_Microsoft_Singularity_Processor::g_GetCurrentProcessorContext()->halCpu;
}

void Struct_Microsoft_Singularity_MpBootInfo::g_HalReleaseMpStartupLock()
{
    uint32 lockAddr =
        (uint32)Class_Microsoft_Singularity_Hal_Platform::c_thePlatform->MpStartupLock32;
    if (lockAddr != 0) {
        *((uint16*) lockAddr) = 0;
    }
}

void InitCpu(Class_Microsoft_Singularity_Hal_Cpu *pi)
{
    ArmSerialOut("InitCpu0\n");
    ArmSerialOutHex(pi->Id);
    ArmSerialOut("\n");

    Class_Microsoft_Singularity_Hal_Cpu *hpi = (Class_Microsoft_Singularity_Hal_Cpu*) pi;

    hpi->postHeader.vtableObject = CpuRuntimeType->classVtable;

    ArmSerialOut("InitCpu1\n");

    Class_Microsoft_Singularity_Hal_Cpu::m_Initialize(hpi);

    ArmSerialOut("InitCpu2\n");

    Class_Microsoft_Singularity_Isal_Isa::g_InitializeCpuDispatchTable();

    ArmSerialOut("InitCpu3\n");
}

void ShutdownCpu(Class_Microsoft_Singularity_Hal_Cpu *pi)
{
#if ISA_IX86 || ISA_IX64
    // Disable Interrupts and then spin a screen icon waiting to be reset.
    Class_Microsoft_Singularity_Processor::g_DisableInterrupts();

    int cpu = pi->Id;
    for (int i = 0; i != i + 1; i++) {
        uint16* p = (uint16*)(0xb8000 + (cpu - 1) * 2 * 8);
        for (int r = 0; r < 8; r++) {
            uint16 t = (uint16)(i >> (28 - r * 4));
            t &= 0xf;
            if (t > 9) {
                t += 0x1f00 + 'a' - 10;
            }
            else {
                t += 0x1f00 + '0';
            }
            *p++ = t;
            if (Class_Microsoft_Singularity_DebugStub::g_PollForBreak() == true) {
                __debugbreak();
            }
            for (int i = 0; i < 50000; i++) {
                __nop();
            }
        }
    }
#endif
}

void InitPlatform(Class_Microsoft_Singularity_Hal_Platform *bi,
                  Class_Microsoft_Singularity_Hal_Cpu *pi)
{
    ArmSerialOut("InitPlatform0");

#ifndef ISA_ARM
    Class_Microsoft_Singularity_Isal_Isa::g_InitializeDispatchTable();
#endif

    InitCpu(pi);

    ArmSerialOut("InitPlatform2");

    Class_Microsoft_Singularity_Hal_Platform *nbi = (Class_Microsoft_Singularity_Hal_Platform *) bi;
    nbi->postHeader.vtableObject = PlatformRuntimeType->classVtable;

    // Call early init routines

    Class_Microsoft_Singularity_Hal_Platform::m_Initialize(nbi,
                                                      (Class_Microsoft_Singularity_Hal_Cpu *) pi);

    ArmSerialOut("InitPlatform3");

    kdprintf("DebugPort: %04x\n", nbi->DebugBasePort);

    ArmSerialOut("InitPlatform4");

    // Initialize the debugger hardware

    if (KdpSerialInit(nbi)) {
        nbi->DebuggerType = Class_Microsoft_Singularity_Hal_Platform_DEBUGGER_SERIAL;
    }
    else {
        nbi->DebuggerType = Class_Microsoft_Singularity_Hal_Platform_DEBUGGER_NONE;
    }

    ArmSerialOut("Calling Kernel.Main:\n");
}

void ShutdownPlatform(Class_Microsoft_Singularity_Hal_Platform *bi)
{
}

//////////////////////////////////////////////////////////////////////////////
//
//  These are the first instruction executed in the kernel.
//
extern "C" int __fastcall HalEntryPoint(
    Class_Microsoft_Singularity_Hal_Platform  *bi,
    Class_Microsoft_Singularity_Hal_Cpu *pi)
{
    ArmSerialInit((UINT32 *) 0xffd82300);
    ArmSerialOut("HalEntry 1\n");

    if (bi->Size != sizeof(Class_Microsoft_Singularity_Hal_Platform)) {
        __debugbreak();
        return Class_Microsoft_Singularity_Hal_Platform_ERROR_BAD_SIZE;
    }

    if (pi->Id == 0) {

        // Static C++ constructors.  Ideally, there would be none.
        _cinit();

        ArmSerialOut("HalEntry 2\n");

        // Initialize the target.
        Class_Microsoft_Singularity_Isal_Isa::g_Initialize(bi->CpuRecordPointerOffset,
                                                             bi->ThreadRecordPointerOffset);

        Class_Microsoft_Singularity_Isal_Isa::g_InitializeCpu(
            (Struct_Microsoft_Singularity_Isal_CpuRecord *)pi->CpuRecordPage,
            pi->Id,
            (UIntPtr) pi->KernelStackLimit);

        ArmSerialOut("HalEntry 3\n");

        // Initialize processor context
        ProcessorInitialize(pi);

        ArmSerialOut("HalEntry 4\n");

        // Assert that processor local store is working correctly
        if (Class_Microsoft_Singularity_Hal_Platform::g_GetCurrentCpu() != pi) {
            __debugbreak();
        }

        ArmSerialOut("HalEntry 6\n");

        // Set up native-only constants
        // Initialize platform (in a platform-specific way)
        InitPlatform(bi, pi);
        ArmSerialOut("HalEntry 7\n");

        // Initialize tracing
        Class_Microsoft_Singularity_Tracing::g_Initialize();

        ArmSerialOut("HalEntry 8\n");

        // Initialize debugger
        KdInitialize(bi);

        ArmSerialOut("HalEntry 9\n");

        // Initial breakpoint - uncomment me to do early debugging
        //if (bi->DebuggerType != Class_Microsoft_Singularity_Hal_Platform_DEBUGGER_NONE) {
        //    __debugbreak();
        //}

        Class_Microsoft_Singularity_Kernel::g_Main();

        ShutdownPlatform(bi);
        // Native exits by returning back to the undump/bootloader
        return 0;
    }
    else {
        // Initialize the target cpu.
        Class_Microsoft_Singularity_Isal_Isa::g_InitializeCpu(
            (Struct_Microsoft_Singularity_Isal_CpuRecord *)pi->CpuRecordPage,
            pi->Id,
            (UIntPtr) pi->KernelStackLimit);

        // Initialize processor context
        ProcessorInitialize(pi);

        // Initialize platform processor
        InitCpu(pi);

        // Assert that processor local store is working correctly
        if (Class_Microsoft_Singularity_Hal_Platform::g_GetCurrentCpu() != pi) {
            __debugbreak();
        }

        int result = Class_Microsoft_Singularity_Kernel::g_MpMain(pi->Id);

        ShutdownCpu(pi);

        return Class_Microsoft_Singularity_Hal_Platform_SUCCESS;
    }
}
