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

#include "hal.h"

//////////////////////////////////////////////////////////////// Image Loader.
//
#if PAGING
extern int syscallEntryTable[];

struct AbiStackHead
{
    uintptr    prevBegin;
    uintptr    prevLimit;
    uintptr    esp;
};

int FakeSyscallBegin(int abiIndex)
{
    // TODO: validate abiIndex, set fs, etc.
    return syscallEntryTable[abiIndex];
}

// TODO: these cannot be functions; we cannot trust the user stack
uintptr PushAbiStack(uintptr esp)
{
    Struct_Microsoft_Singularity_ThreadContext * context =
        Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext();
    AbiStackHead *head = (AbiStackHead *)(context->abiStackHead);
    head->prevBegin = context->stackBegin;
    head->prevLimit = context->stackLimit;
    head->esp = esp;
    context->stackBegin = context->abiStackBegin;
    context->stackLimit = context->abiStackLimit;
    return uintptr(head);
}

uintptr PopAbiStack(AbiStackHead *head)
{
    Struct_Microsoft_Singularity_ThreadContext * context =
        Class_Microsoft_Singularity_Processor::g_GetCurrentThreadContext();
    // HACK: if the ABI call changed stackBegin/stackLimit, keep the changes
    // (otherwise, switch back to original stack)
    if (context->stackBegin == context->abiStackBegin) {
        context->stackBegin = head->prevBegin;
        context->stackLimit = head->prevLimit;
    }
    return head->esp;
}

//#define SEGMENT_SELECTOR(s) \
//    (uint16)(offsetof(Struct_Microsoft_Singularity_CpuInfo,s) \
//             - offsetof(Struct_Microsoft_Singularity_CpuInfo,GdtNull))
//const uint16 ourFs = SEGMENT_SELECTOR(GdtPF);

__declspec(naked) void FakeSyscall()
{
    __asm
    {
#if 0
        push ecx
        push edx
        call AbiStackSegment
        pop edx
        pop ecx
        mov esp, eax // TODO: must set stackBase, stackLimit
        push edx
        call FakeSyscallBegin
        mov edx, eax
        mov eax, [esp] // eax = pointer to arg1
        call edx
        pop ecx // ecx = pointer to arg1
        sub ecx, 4 // ecx = pointer to return address
        mov esp, ecx
        ret 8
#else
        // user stack = ret arg1 arg2 ret arg3 ... argn ...
        // ecx = ABI index
        // edx = pointer to arg1
        push ecx
        push edx
        mov ecx, edx
        sub ecx, 4 // 1 word for return address
        call PushAbiStack
        pop edx
        pop ecx
        mov esp, eax

///        mov eax, edx
///        sub eax, 4 // 1 word for return address
///        mov esp, eax
        sti
        mov ax, ss
        mov ds, ax    // Copy stack segment selector to DS so we can access memory!
        mov es, ax    // Copy stack segment selector to ES so we can access memory!
//        push    (offsetof(Struct_Microsoft_Singularity_CpuInfo,GdtPF) - offsetof(Struct_Microsoft_Singularity_CpuInfo,GdtNull))
//        push    ourFs
//        pop     fs
        mov     ax, 0x38  // TODO: no more hexadecimal constants
        mov     fs, ax
//        push    fs;
//        pop     fs;    // refresh fs (TODO: don't trust user's fs)
        push edx
        call FakeSyscallBegin
        mov edx, eax
        pop eax // pointer to arg1
        // user stack = ret arg1 arg2 ret arg3 ... argn ...
        call edx
        call Class_Microsoft_Singularity_Isal_Isa::g_EnterUserMode

        // Any changes here must be reflected in EnterRing0
        mov ecx, esp
        push eax
        push edx
        call PopAbiStack
        mov ecx, eax
        pop edx
        pop eax
        mov esp, ecx

        ret 8
#endif
    }
}
__declspec(naked) void EnterRing0()
{
    __asm
    {
        add esp, 4 // discard return address (pointing to FakeSyscall)

        // Copied from FakeSyscall epilog:
        mov ecx, esp
        push eax
        push edx
        call PopAbiStack
        mov ecx, eax
        pop edx
        pop eax
        mov esp, ecx

        ret 8
    }
}
#endif // PAGING

int32 Class_Microsoft_Singularity_Loader_PEImage::
g_HalCallEntryPoint(UIntPtr entry, int threadIndex, bool runAtRing3)
{
    if (entry == 0) {
        __debugbreak();
        return -1;
    }

#if PAGING
    if (runAtRing3) {
        Class_Microsoft_Singularity_Isal_Isa::g_EnterUserMode();
    }
#else
    Assert(!runAtRing3);
#endif

    int32 retval = (( int32 (__fastcall *)(int32))entry)(threadIndex);

#if PAGING
    if (runAtRing3) {
        // We should still be running at ring-3 when the process unwinds
        Assert(Class_Microsoft_Singularity_Isal_Isa::g_IsInUserMode());

        // Call ABI zero, which drops us to ring-0. Why is this not
        // a gaping security hole? The kernel should verify that this
        // request only ever comes from its own (trusted) code (i.e.,
        // right here).
        __asm {
            mov ecx, 0
            push ecx
            push ecx
            mov edx, esp
            push done
            _emit   0x0f
            _emit   0x34  //sysenter
done:
        }
    }
#endif

    return retval;
}

//
///////////////////////////////////////////////////////////////// End of File.
