//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
/* ++

    Bootloader test app to debug as an app

-- */
#include <windows.h>
#include <pcireg.h>
#include <ethdbg.h>
#include <drv_glob.h>
#include <nkintr.h>
#include <pehdr.h>
#include <romldr.h>
#include <blcommon.h>
#include <bootpart.h>

#include "s2440.h"
#include "loader.h"
#include "warning.h"

extern void main (void);

DWORD OEMAddressTable[1];

WCHAR wcImageName[256] = _T("\\Release\\nk.bin");
//WCHAR wcImageName[256] = _T("\\Release\\eboot.bin");

HANDLE  hFile = INVALID_HANDLE_VALUE;
//PUINT16 g_pwReadBuf = NULL;
PUCHAR  g_pucReadBuf = NULL;
PUCHAR  g_pucReadBufBase = NULL;
DWORD   g_dwFileSize = 0;
DWORD   g_dwBytesRead = 0;
BOOL    g_bMode;

const DWORD c_SegmentSize =  0xFFFF; // 64 kb
//const DWORD c_SegmentSize =  0xFFFF*2; // 128 kb

// Use for 64-bit math
ULARGE_INTEGER g_FilePointer = {
	0, // low
	0  // high
};

void CleanExit(DWORD dwExitCode);

void
ExeMain(
    void
    )
{
    DWORD dwFileSize=0, dwBytesRead=0, dwErr=0;

    // we are testing bootloader code, so...
    g_bMode = SetKMode(TRUE);

    /* ++

    Open & read image into local buffer.

    To set this up you need to either
        a) use the \Release facility in CE.NET
        b) s ppshcopy ppsh:nk.bin \

    You can also use ActiveSync, network, etc. to get your image onto the device's
    filesystem. The caveat of dragging large images to the deivce is lack of SDRAM.
    Use method a) if possible.

    -- */
    if (!wcImageName[0])
        return;

    RETAILMSG(1, (TEXT("\r\n\r\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n")));
    RETAILMSG(1, (TEXT("Microsoft Windows CE SMDK2440 Eboot Simulator \n")));

    RETAILMSG(1, (TEXT("Opening image: '%s' \n"), wcImageName));

    hFile = CreateFile(wcImageName,
                   GENERIC_READ,
                   0, // exclusive access
                   NULL,
                   OPEN_EXISTING,
                   0, 0);

    if (hFile == INVALID_HANDLE_VALUE ) {
        RETAILMSG(1, (TEXT("CreateFile ERROR:%d on file:%s\n"), GetLastError(), wcImageName));
        goto _Error;
    }

    dwFileSize = GetFileSize(hFile, NULL);
    if (!dwFileSize || dwFileSize == 0xFFFFFFFF) {
        RETAILMSG(1, (TEXT("GetFileSize(%u) ERROR: %dn"), dwFileSize, GetLastError()));
        goto _Error;
    }
    RETAILMSG(1, (TEXT("Image size: %u \n"), dwFileSize));
    g_dwFileSize = dwFileSize;

    // we read a page at time to handle large files
    g_pucReadBufBase = (PUCHAR)LocalAlloc(LPTR, c_SegmentSize);
    if ( !g_pucReadBufBase ) {
        RETAILMSG(1, (TEXT("LocalAlloc ERROR:%d for %d bytes\n"), GetLastError(), c_SegmentSize));
        goto _Error;
    }
    g_pucReadBuf = g_pucReadBufBase;

#if 0
    // pre-fetch the first segment, #0
    b = ReadFile(hFile, g_pucReadBufBase, c_SegmentSize, &dwBytesRead, NULL);
    if ( !b || !dwBytesRead) {
        RETAILMSG(1, (TEXT("ReadFile ERROR:%d on file:%s, %u bytes read\n"), GetLastError(), wcImageName, dwBytesRead));
        goto _Error;
    }
    if ( dwBytesRead < min(dwFileSize, c_SegmentSize) ) {
        RETAILMSG(1, (TEXT("ReadFile ERROR:%u bytes read, expected: %u\n"), dwBytesRead, min(dwFileSize, c_SegmentSize)));
        goto _Error;
    }
#endif

    __try {
        main();
    } __except(EXCEPTION_EXECUTE_HANDLER) {
        RETAILMSG(1, (TEXT("*** EBoot Simulator: EXCEPTION: 0x%x ***\n"), GetExceptionCode()));
    }

_Error:
    // working case should never get here
    CleanExit(1);
}


void CleanExit(DWORD dwExitCode)
{
    if (dwExitCode) {
        RETAILMSG(1, (_T("*** EBoot CleanExit ERROR: %u ***\n"), dwExitCode ));
    }

    if (hFile != INVALID_HANDLE_VALUE)
        CloseHandle(hFile);

    if (g_pucReadBufBase)
        LocalFree(g_pucReadBufBase);

    SetKMode(g_bMode);

    RETAILMSG(1, (_T("*** EBoot Simulator Complete on '%s', Result: %s ***\n"),
        wcImageName, dwExitCode ? _T("FAILED") : _T("PASSED")));

    RETAILMSG(1, (_T("--------------------------------------------------------\n")));

    exit(dwExitCode);
}


void Launch(unsigned int dwLaunchAddr)
{
    RETAILMSG(1, (_T("*** EBoot Simulator: Launch: 0x%X ***\n"), dwLaunchAddr));

    CleanExit(ERROR_SUCCESS);
}


//
// Functions to simulate Ethernet download
//
static BYTE volatile *pbEthernetBase=(BYTE *)SMC_ETHERNET_IO_BASE_U_VIRTUAL;

#define READ_PORT_USHORT(port)         (*(volatile PUSHORT const)(port))
#define WRITE_PORT_USHORT(port, value) (*(volatile PUSHORT const)(port) = value)

static __inline USHORT ReadWord( USHORT wOffset )
{
	return READ_PORT_USHORT((PUSHORT)(pbEthernetBase + (wOffset)));
};

static __inline VOID WriteWord( USHORT wOffset, USHORT Value )
{
    WRITE_PORT_USHORT((PUSHORT)(pbEthernetBase + (wOffset)),(USHORT)(Value));
};

BOOL SMCInit(BYTE *pbBaseAddress, DWORD dwMultiplier, USHORT MacAddr[3] )
{
    // just get the MAC addr

    #define BANKSEL_REG     14
    #define BANK1           0x3301
    #define MACADDR_REG0	4
    #define MACADDR_REG1	6
    #define MACADDR_REG2	8

    WriteWord( BANKSEL_REG, BANK1 );

	MacAddr[0] = ReadWord( MACADDR_REG0 );
    MacAddr[1] = ReadWord( MACADDR_REG1 );
    MacAddr[2] = ReadWord( MACADDR_REG2 );

    return TRUE;
}

UINT16 SMCSendFrame( BYTE *pbData, DWORD dwLength )
{
    RETAILMSG(1, (_T("vSMCSendFrame\n")));
    return 0; // success
}


//
// SMCGetFrame: simulated SMC.LIB function.
// Gets frames from the NIC by reading from our buffer.
// returns number of bytes read.
//
UINT16 SMCGetFrame( BYTE *pbData, UINT16 *pwLength )
{
    static DWORD dwSegment = 0;
    DWORD dwBytesRead=0;
    DWORD dwRemain = 0;
    PUCHAR pucData;
    WORD wBufLen;
    BOOL b;

    if ( !g_pucReadBufBase )
        return 0;

    if (!pbData || !pwLength || !*pwLength) {
        RETAILMSG(1, (_T("!!! SMCGetFrame ERROR: no user buffer !!!\n")));
        DebugBreak();
        return 0;
    }

    pucData = pbData;
    wBufLen = *pwLength;

    RETAILMSG(1, (_T("SMCGetFrame: Length:%u\n"), wBufLen));

    __try {

        while ( (g_dwBytesRead <= g_dwFileSize) && wBufLen && pbData)
        {
            // fetch next file segment?
            if ( (g_dwBytesRead % c_SegmentSize) == 0)
            {
                dwRemain = g_dwFileSize - g_dwBytesRead;

                RETAILMSG(1, (TEXT("Read: %u bytes so far, %u bytes remain. Fetching file segment %u \n"),
                    g_dwBytesRead, dwRemain, dwSegment++));

                // fetch it
                b = ReadFile(hFile, g_pucReadBufBase, c_SegmentSize, &dwBytesRead, NULL);
                if ( !b ) {
                    RETAILMSG(1, (TEXT("ReadFile ERROR:%d on image:'%s', %u bytes read\n"),
                        GetLastError(), wcImageName, dwBytesRead));
                    DebugBreak();
                    return 0; // terminate download
                }

                RETAILMSG(1, (TEXT("ReadFile: returned %u bytes, expected: %u\n"),
                    dwBytesRead, min(dwRemain, c_SegmentSize)));

                if ( dwBytesRead < min(dwRemain, c_SegmentSize) ) {
                    RETAILMSG(1, (TEXT("ReadFile WARNING: %u bytes read, expected: %u\n"),
                        dwBytesRead, min(dwRemain, c_SegmentSize)));
                    DebugBreak();
                }

                // reset worker pointer
                g_pucReadBuf = g_pucReadBufBase;
            }

            // read from our local file buffer
            *pucData++ = *g_pucReadBuf++;

            // bump counters
            wBufLen--;
            g_dwBytesRead++;
        }

    } __except(EXCEPTION_EXECUTE_HANDLER) {
        RETAILMSG(1, (TEXT("*** EBoot Simulator: SMCGetFrame EXCEPTION: 0x%x ***\n"),
            GetExceptionCode()));
    }

    return (g_dwBytesRead <= g_dwFileSize ? 1 : 0);
}


//
// EbootEtherReadData: simluated EBOOT.LIB function.
// Handles large buffer requests for SMCGetFrame.
// Note SMCGetFrame only handles UINT16 lengths.
//
BOOL EbootEtherReadData (DWORD cbData, LPBYTE pbData)
{
    UINT16 wLength;

    while (cbData) {

        wLength = (cbData > 0xFFFF) ? 0xFFFF : (UINT16)cbData;

        if ( !SMCGetFrame(pbData, &wLength) )
            return FALSE;

        cbData -= wLength;
        pbData += wLength;

    }
    return TRUE;
}

