///////////////////////////////////////////////////////////////////////////////
//
//  Microsoft Research Singularity
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   halkdx86.cpp - Processor-specific support routines for halkd.cpp.
//
#include "hal.h"
#include "halkd.h"

#define KDDBG if (0) kdprintf

extern "C" void *  __cdecl memcpy(void *, const void *, size_t);

//////////////////////////////////////////////// User visible progress beacon.
//
static UINT16 KdpSpinBase = 0x2f00;
void KdpSpin()
{
    if (KdpSpinBase != 0) {
        static UINT8 state = 0;

        // Write the spinner to the screen.
        *((UINT16 *)0xb809e) = KdpSpinBase + ("+-|*" [state++ & 0x3]);
    }
}

///////////////////////////////////////// Debugger Unique Interrupts Routines.
//
//  NB: Without these, we share routines with the mainline code and we get
//  caught in a loop when the debugger inserts a break after the pushfd when
//  someone tries to single step through Processor:g_DisableInterrupts!
//
bool __declspec(naked) KdpDisableInterruptsInline()
{
    __asm {
        pushfd;
        pop eax;
        test eax, Struct_Microsoft_Singularity_Isal_IX_EFlags_IF;
        setnz al;
        nop;    // required so that the linker doesn't combine with g_Disable
        cli;
        ret;
    }
}

void __declspec(naked) KdpRestoreInterruptsInline(bool enabled)
{
    __asm {
        nop;
        test cl, cl;
        je done;
        nop;    // required so that the linker doesn't combine with g_Restore
        sti;
      done:
        ret;
    }
}

// Pause processor in deference to other hardware threads on the same core.
void __declspec(naked) KdpPause()
{
    __asm {
        pause;
        ret;
    }
}

// Flush instruction cache.
void __declspec(naked) KdpFlushInstCache()
{
    __asm {
        wbinvd;   // privileged instruction
        ret;
    }
}

// Break into the debugger.
void __declspec(naked) Class_Microsoft_Singularity_DebugStub::g_Break()
{
    __asm {
        int 3;
        ret;
    }
}

// Read a machine specific register (MSR on x86 & x64).
bool KdpReadMsr(UINT32 msr, UINT32 *plo, UINT32 *phi)
{
    UINT32 lo;
    UINT32 hi;

    __asm {
        mov ecx, msr;
        rdmsr;
        mov hi, edx;
        mov lo, eax;
    }

    *phi = hi;
    *plo = lo;

    return true;
}

// Write a machine specific register (MSR on x86 & x64).
bool KdpWriteMsr(UINT32 msr, UINT32 lo, UINT32 hi)
{
    __asm {
        mov ecx, msr;
        mov edx, hi;
        mov eax, lo;
        wrmsr;
    }
    return true;
}

//////////////////////////////////////////////////////////////////////////////
//
void KdpToKdContext(IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *singularity,
                    OUT CONTEXT *windbg)
{
    windbg->ContextFlags = (CONTEXT_CONTROL |
                            CONTEXT_INTEGER |
                            CONTEXT_SEGMENTS |
                            CONTEXT_FLOATING_POINT |
                            CONTEXT_EXTENDED_REGISTERS);

    // CONTEXT_FULL;
    windbg->Eax = singularity->ax;
    windbg->Ebx = singularity->bx;
    windbg->Ecx = singularity->cx;
    windbg->Edx = singularity->dx;
    windbg->Esp = singularity->sp;
    windbg->Ebp = singularity->bp;
    windbg->Esi = singularity->si;
    windbg->Edi = singularity->di;
    windbg->Eip = singularity->ip;
    windbg->EFlags = singularity->fl;

    windbg->FloatSave.ControlWord = singularity->mmx.fcw;
    windbg->FloatSave.StatusWord = singularity->mmx.fsw;
    windbg->FloatSave.TagWord = singularity->mmx.ftw;
    windbg->FloatSave.ErrorOffset = singularity->mmx.ip;
    windbg->FloatSave.ErrorSelector = singularity->mmx.cs;
    windbg->FloatSave.DataOffset = singularity->mmx.dp;
    windbg->FloatSave.DataSelector = singularity->mmx.ds;

    memcpy(&windbg->FloatSave.St0, &singularity->mmx.st0, 10);
    memcpy(&windbg->FloatSave.St1, &singularity->mmx.st1, 10);
    memcpy(&windbg->FloatSave.St2, &singularity->mmx.st2, 10);
    memcpy(&windbg->FloatSave.St3, &singularity->mmx.st3, 10);
    memcpy(&windbg->FloatSave.St4, &singularity->mmx.st4, 10);
    memcpy(&windbg->FloatSave.St5, &singularity->mmx.st5, 10);
    memcpy(&windbg->FloatSave.St6, &singularity->mmx.st6, 10);
    memcpy(&windbg->FloatSave.St7, &singularity->mmx.st7, 10);

    memcpy(&windbg->ExtendedRegisters, &singularity->mmx, 512);

    // Segment registers other than cs don't change so they are live
    windbg->SegCs = singularity->cs;
    __asm {
        mov ebx, windbg;

        xor eax, eax;
        mov ax, gs;
        mov [ebx]CONTEXT.SegGs, eax;
        mov ax, fs;
        mov [ebx]CONTEXT.SegFs, eax;
        mov ax, es;
        mov [ebx]CONTEXT.SegEs, eax;
        mov ax, ds;
        mov [ebx]CONTEXT.SegDs, eax;
        mov ax, ss;
        mov [ebx]CONTEXT.SegSs, eax;
    }
}

void KdpFromKdContext(IN CONST CONTEXT *windbg,
                      OUT Struct_Microsoft_Singularity_Isal_SpillContext *singularity)
{
    singularity->ax = windbg->Eax;
    singularity->bx = windbg->Ebx;
    singularity->cx = windbg->Ecx;
    singularity->dx = windbg->Edx;
    singularity->sp = windbg->Esp;
    singularity->bp = windbg->Ebp;
    singularity->si = windbg->Esi;
    singularity->di = windbg->Edi;
    singularity->ip = windbg->Eip;
    singularity->fl = windbg->EFlags;

    // CONTEXT_FLOATING_POINT
    if (windbg->ContextFlags & CONTEXT_FLOATING_POINT) {
        singularity->mmx.fcw = windbg->FloatSave.ControlWord;
        singularity->mmx.fsw = windbg->FloatSave.StatusWord;
        singularity->mmx.ftw = windbg->FloatSave.TagWord;
        singularity->mmx.cs = windbg->FloatSave.ErrorSelector;
        singularity->mmx.ip = windbg->FloatSave.ErrorOffset;
        singularity->mmx.ds = windbg->FloatSave.DataSelector;
        singularity->mmx.dp = windbg->FloatSave.DataOffset;
        memcpy(&singularity->mmx.st0, &windbg->FloatSave.St0, 10);
        memcpy(&singularity->mmx.st1, &windbg->FloatSave.St1, 10);
        memcpy(&singularity->mmx.st2, &windbg->FloatSave.St2, 10);
        memcpy(&singularity->mmx.st3, &windbg->FloatSave.St3, 10);
        memcpy(&singularity->mmx.st4, &windbg->FloatSave.St4, 10);
        memcpy(&singularity->mmx.st5, &windbg->FloatSave.St5, 10);
        memcpy(&singularity->mmx.st6, &windbg->FloatSave.St6, 10);
        memcpy(&singularity->mmx.st7, &windbg->FloatSave.St7, 10);
    }

    // CONTEXT_EXTENDED_REGISTERS
    if (windbg->ContextFlags & CONTEXT_EXTENDED_REGISTERS) {
        memcpy(&singularity->mmx, &windbg->ExtendedRegisters, 512);
    }
}

void KdpSetControlReport(IN OUT PDBGKD_CONTROL_REPORT report,
                         IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *x86Context)
    //  Routine Description:
    //      Fill in the Wait_State_Change message record.
    //
    //  Arguments:
    //      WaitStateChange - Supplies pointer to record to fill in
    //      x86Context - Supplies a pointer to a context record.
{
    UINT32 _dr6;
    UINT32 _dr7;
    UINT16 _cs;
    UINT16 _ds;
    UINT16 _es;
    UINT16 _fs;

    __asm {
        mov eax, dr6;
        mov _dr6, eax;
        mov eax, dr7;
        mov _dr7, eax;

        mov ax, cs;
        mov _cs, ax;
        mov ax, ds;
        mov _ds, ax;
        mov ax, es;
        mov _es, ax;
        mov ax, fs;
        mov _fs, ax;
    }

    report->Dr6 = _dr6;
    report->Dr7 = _dr7;
    report->SegCs  = _cs;
    report->SegDs  = _ds;
    report->SegEs  = _es;
    report->SegFs  = _fs;
    report->EFlags = x86Context->fl;
    report->ReportFlags = X86_REPORT_INCLUDES_SEGS;

#if !PAGING
    // Let the debugger know so that it doesn't have to retrieve the CS descriptor.
    report->ReportFlags |= X86_REPORT_STANDARD_CS;
#endif
}

void KdpSetControlSet(IN CONST DBGKD_CONTROL_SET * control,
                      IN OUT Struct_Microsoft_Singularity_Isal_SpillContext *x86Context)
{
    if (control->TraceFlag) {
        //KDDBG("KD: Warning - trace flag set prev efl=%x\n",x86Context->efl);
        x86Context->fl |= Struct_Microsoft_Singularity_Isal_IX_EFlags_TF;
    }
    else {
        //KDDBG("KD: turning off tracing in efl\n");
        x86Context->fl &= ~Struct_Microsoft_Singularity_Isal_IX_EFlags_TF;
    }

    UINT32 _dr7 = control->Dr7;

    __asm {
        mov eax, _dr7;
        mov dr7, eax
    }
}

//////////////////////////////////////////////////////////////////////////////

void KdpReadSpecialRegisters(KSPECIAL_REGISTERS *pksp,
                             IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *x86Context)
{
    Struct_Microsoft_Singularity_Kd_X86Descriptor idtp;
    Struct_Microsoft_Singularity_Kd_X86Descriptor gdtp;

    __asm {
        mov ebx, pksp;

        sidt idtp.Limit;
        sgdt gdtp.Limit;

        mov eax, cr0;
        mov [ebx]KSPECIAL_REGISTERS.Cr0, eax;
        mov eax, cr2;
        mov [ebx]KSPECIAL_REGISTERS.Cr2, eax;
        mov eax, cr3;
        mov [ebx]KSPECIAL_REGISTERS.Cr3, eax;
        _emit 0x0f;  // mov eax,cr4
        _emit 0x20;
        _emit 0xe0;
        mov [ebx]KSPECIAL_REGISTERS.Cr4, eax;

        // Should we save segment regs as well?
        str ax;
        mov [ebx]KSPECIAL_REGISTERS.Tr, ax;
    }

    pksp->Idtr = idtp;
    pksp->Gdtr = gdtp;
}

void KdpWriteSpecialRegisters(CONST KSPECIAL_REGISTERS *pksp)
{
    __asm {
        mov ebx, pksp;

        mov eax, [ebx]KSPECIAL_REGISTERS.KernelDr0;
        mov dr0, eax;
        mov eax, [ebx]KSPECIAL_REGISTERS.KernelDr1;
        mov dr1, eax;
        mov eax, [ebx]KSPECIAL_REGISTERS.KernelDr2;
        mov dr2, eax;
        mov eax, [ebx]KSPECIAL_REGISTERS.KernelDr3;
        mov dr3, eax;
        mov eax, [ebx]KSPECIAL_REGISTERS.KernelDr6;
        mov dr6, eax;
        mov eax, [ebx]KSPECIAL_REGISTERS.KernelDr7;
        mov dr7, eax;
    }
}

//////////////////////////////////////////////////////////////////////////////

KdDebugTrapData * KdpIsDebugTrap(IN CONST Struct_Microsoft_Singularity_Isal_SpillContext *context,
                                 int id)
{
    if (id == Struct_Microsoft_Singularity_Isal_IX_EVectors_FirstChanceException) {
        return (KdDebugTrapData *)(context->ax);
    }
    return NULL;
}

// Convert a trap into a exception record.
void KdpConvertTrapToException(IN OUT EXCEPTION_RECORD64 *per,
                               IN OUT Struct_Microsoft_Singularity_Isal_SpillContext *context,
                               int id)
{
    // Breakpoints:
    switch (id) {

        case Struct_Microsoft_Singularity_Isal_IX_EVectors_SingleStep:
            KDDBG("SingleStep\n");
            per->ExceptionCode = STATUS_SINGLE_STEP;
            per->ExceptionAddress = SIGN_EXTEND(context->ip);
            // context->efl &= ~Struct_Microsoft_Singularity_Isal_IX_EFlags_TF;
            break;

        case Struct_Microsoft_Singularity_Isal_IX_EVectors_Breakpoint:
            KDDBG("Breakpoint\n");
            context->ip -= 1;
            per->ExceptionCode = STATUS_BREAKPOINT;
            per->NumberParameters = 1;
            per->ExceptionInformation0 = BREAKPOINT_BREAK;
            per->ExceptionAddress = SIGN_EXTEND(context->ip);
            break;

        case Struct_Microsoft_Singularity_Isal_IX_EVectors_IllegalInstruction:
            KDDBG("Illegal Instruction\n");
            per->ExceptionCode = STATUS_ILLEGAL_INSTRUCTION;
            break;

        case Struct_Microsoft_Singularity_Isal_IX_EVectors_PageFault:
            KDDBG("KD: 0x0E %d\n", id);
            per->ExceptionCode = STATUS_ACCESS_VIOLATION;
            per->ExceptionAddress = SIGN_EXTEND(context->ip);
            per->NumberParameters = 1;
            {
                UINT32 _cr2;
                __asm {
                    mov eax, cr2;
                    mov _cr2, eax;
                }
                per->ExceptionInformation0 = SIGN_EXTEND(_cr2);
            }
            break;

        case Struct_Microsoft_Singularity_Isal_IX_EVectors_FirstChanceException: {
            KdDebugTrapData *trapData = (KdDebugTrapData *) (context->ax);
            switch (trapData->tag) {
                case KdDebugTrapData::FIRST_CHANCE_EXCEPTION:
                    context->ax = trapData->firstChanceException.throwAddr;
                    KDDBG("KD: First chance C# exception\n");
                    // per->ExceptionCode = STATUS_CPP_EH_EXCEPTION; //0xe06d7363;
                    per->ExceptionCode = STATUS_VCPP_EXCEPTION; //0x8000ff1f;
                    per->ExceptionAddress = SIGN_EXTEND(context->ip);
                    per->NumberParameters = 1;
                    per->ExceptionInformation0 = BREAKPOINT_BREAK;
                    break;
                default:
                    KDDBG("KD: Unexpected interrupt %d\n", id);
                    per->ExceptionCode = 0x80000000 + id;
                    per->ExceptionAddress = SIGN_EXTEND(context->ip);
                    break;
            }
            break;
        }

        case Struct_Microsoft_Singularity_Isal_IX_EVectors_SecondChanceException:
            KDDBG("KD: Second chance C# exception\n");
            per->ExceptionCode = STATUS_VCPP_EXCEPTION;
            per->ExceptionAddress = SIGN_EXTEND(context->ip);
            break;

        case Struct_Microsoft_Singularity_Isal_IX_EVectors_DebuggerBreakRequest:
            KDDBG("KD: Debugger ctrl-break\n");
            per->ExceptionCode = STATUS_BREAKPOINT;
            per->ExceptionInformation0 = BREAKPOINT_BREAK;
            per->ExceptionAddress = SIGN_EXTEND(context->ip);
            break;

        case Struct_Microsoft_Singularity_Isal_IX_EVectors_Nmi:
            KDDBG("KD: NMI exception\n");
            per->ExceptionCode = STATUS_UNHANDLED_EXCEPTION;
            per->ExceptionInformation0 = BREAKPOINT_BREAK;
            per->ExceptionAddress = SIGN_EXTEND(context->ip);
            break;

        default:
            KDDBG("KD: Unexpected interrupt %d\n", id);
            per->ExceptionCode = 0x80000000 + id;
            per->ExceptionAddress = SIGN_EXTEND(context->ip);
            break;

    }

    KDDBG("Trap: Context at %p\n", context);
    KDDBG("  CXT=%08x  THR=%08x\n",
          context,
          Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext()->_thread);
    KDDBG("  EIP=%08x  EFL=%08x\n",
          context->ip, context->fl);
    KDDBG("  EAX=%08x  EBX=%08x  ECX=%08x  EDX=%08x\n",
          context->ax, context->bx, context->cx, context->dx);
    KDDBG("  ESP=%08x  EBP=%08x  ESI=%08x  EDI=%08x\n",
          context->sp, context->bp, context->si, context->di);
}

//
// Read or Write I/O Space.
//
// Return:
//
// iowrite == 0: value read from port
//
// iowrite !=0:  zero
//
int KdpReadWriteIoSpace(
    int      size,    // 1, 2, 4
    int      iowrite, // true if write, false if read
    unsigned short addr,
    unsigned int   value
    )
{
    unsigned int retValue = 0;

    if (iowrite != 0) {
        // I/O Write's

        if (size == 1) {
            unsigned char byteValue = (unsigned char)value;
            __outbyte(addr, value);
        }
        else if (size == 2) {
            unsigned short wordValue = (unsigned short)value;
            __outword(addr, value);
        }
        else if (size == 4) {
            __outdword(addr, value);
        }
    }
    else {
        // I/O Read's

        if (size == 1) {
            retValue = __inbyte(addr);
        }
        else if (size == 2) {
            retValue = __inword(addr);
        }
        else if (size == 4) {
            retValue = __indword(addr);
        }
    }

    return retValue;
}

