/*++

Copyright (c) 2000-2001  Microsoft Corporation

Module Name:

    bldr32.c

Abstract:

    This module implements the initialization sequence required to jump to
    XBOXKRNL.EXE.

--*/

#include "bldr32.h"
#include "bldr.h"
#include "xpcicfg.h"
#include "enckey.h"

#ifdef BLDRMEMTEST
#include <ldi.h>
#include <sha.h>
#include <rc4.h>
#endif


VOID
DbgInitialize(
    VOID
    );

VOID
DbgPrintCharacter(
    IN UCHAR Character
    );

//
// Prototype for the entry point of XBOXKRNL.EXE.
//

typedef
int
(__cdecl *NTOS_ENTRY_POINT)(
    IN PUCHAR LoadOptions,
    IN const UCHAR* CryptKeys
    );


VOID
BldrDisableInteralROM(
    VOID
    );
VOID
BldrEncoderFixup(
    VOID
    );
VOID
BldrSetupUSB(
    VOID
    );

#ifdef BLDRMEMTEST
VOID
BldrTestMemory(
    ULONG StartAddress,
    ULONG EndAddress,
    BOOLEAN CompareDigests
    );

ULONG MemTestErrorCount = 0;
#endif


VOID
BldrStartup2(
    VOID
    )
{
    PIMAGE_NT_HEADERS NtHeader;
    NTOS_ENTRY_POINT NtosEntryPoint;
    PBOOTLDRPARAM BootLdrParam;
    const UCHAR* KeyToDecryptKernel;
    BOOLEAN FirstTime;

#ifdef RETAILXM3

    BldrDisableInteralROM();

    BldrEncoderFixup();

#endif

    BldrPrint(("Bldr: Entered BldrStartup2"));

#ifdef BLDRMEMTEST
    //
    // Memory test before calibration
    //

    //BldrTestMemory(0, BLDR_RELOCATED_ORIGIN - 1, TRUE);
    BldrTestMemory(BLDR_RELOCATED_ORIGIN + BLDR_BLOCK_SIZE, 0x3FFFFFF, TRUE);
#endif


    //
    // The boot loader was relocated from BLDR_BOOT_ORIGIN to
    // BLDR_RELOCATED_ORIGIN.  Code should no longer attempt to access any
    // memory at the boot origin because it will be overwritten when we
    // decompress the kernel into low memory.  Fill the old memory with a
    // garbage value to ensure that we don't accidently use that memory.
    //

    RtlFillMemory((PVOID)(0x80000000 + BLDR_BOOT_ORIGIN), BLDR_BLOCK_SIZE, 0xCC);

    //
    // Calculate a pointer to the boot parameters stored at the begining of
    // of the boot loader image eg command line.  In case of ROM, entire image
    // including the parameter structure is decrypted so its safe to use
    // the command line which will normally be empty.
    //

    BootLdrParam = (PBOOTLDRPARAM)(0x80000000 + BLDR_RELOCATED_ORIGIN);

    FirstTime = TRUE;
#ifndef RETAILXM3
    if (strstr(BootLdrParam->CommandLine, "SHADOW") != NULL) {
        FirstTime = FALSE;
    }
#endif

    if (FirstTime) {
        
        //
        // Set drive/slew from ROM.
        //

        BldrSetDrvSlwFromROMData((PVOID)XPCICFG_GPU_MEMORY_REGISTER_BASE_0);
    
        //
        // Setup LDT bus including DWORD flow control
        //
 
        BldrSetupLDTBus();

        //
        // Configure the USB ASRC
        //

        BldrSetupUSB();
    
    }

#ifdef BLDRMEMTEST
    
    //
    // Memory test after calibration
    //

    //BldrTestMemory(0, BLDR_RELOCATED_ORIGIN - 1, FALSE);
    BldrTestMemory(BLDR_RELOCATED_ORIGIN + BLDR_BLOCK_SIZE, 0x3FFFFFF, FALSE);
#endif


    //
    // Decrypt and decompress the kernel image from ROM to its base address 
    // in RAM.  Kernel encryption key is the 3rd key in the XboxCryptKeys
    //
    
    KeyToDecryptKernel = XboxCryptKeys + (2 * XBOX_KEY_LENGTH);

    BldrCopyROMToRAM(KeyToDecryptKernel);

    //
    // Go find the entry point.
    //

    NtHeader = RtlImageNtHeader(PsNtosImageBase);

    if (NtHeader == NULL) {

        //
        // Image is invalid
        //
        
        BldrPrint(("Bldr: Entry point not found--invalid image"));

        BldrShutdownSystem();
    }

    NtosEntryPoint = (NTOS_ENTRY_POINT)((ULONG_PTR)PsNtosImageBase +
        NtHeader->OptionalHeader.AddressOfEntryPoint);

    //
    // Pass control to XBOXKRNL.EXE.
    //
    
    BldrPrint(("Bldr: Calling into the kernel"));

#ifdef BLDRMEMTEST
    
    //
    // While testing memory, if there are no errors, tell the SMC to reboot
    //

    if (MemTestErrorCount == 0) {
        DbgPrint("Memory test finished with no errors, rebooting\r\n...");
        BldrWriteSMBusByte(SMC_SLAVE_ADDRESS, 0x2, 0x40);
    } else {
        DbgPrint("Memory test FAILED with %d errors", MemTestErrorCount);
    }

#endif

    NtosEntryPoint(BootLdrParam->CommandLine, XboxCryptKeys);
}

VOID
BldrReencryptROM(
    VOID
    )
{
    BldrEncDec((PUCHAR)0 - ROM_DEC_SIZE - BLDR_BLOCK_SIZE,
        BLDR_BLOCK_SIZE, KeyToDecryptBldr, 16);
}


#ifdef RETAILXM3

VOID
BldrDisableInteralROM(
    VOID
    )
{
    _outpd(PCI_TYPE1_ADDR_PORT, 0x80000880);
    _outp(PCI_TYPE1_DATA_PORT, 2);
}


#include <bldrtran.h>


VOID
BldrEncoderFixup(
    VOID
    )
{
    UCHAR i0; 
    UCHAR i1; 
    UCHAR i2; 
    UCHAR i3;
    UCHAR o0;
    UCHAR o1;
    ULONG Val;

    //
    // Read the inputs
    //
    
    Val = 0;
    BldrReadSMBusByte(SMC_SLAVE_ADDRESS, 0x1c, &Val);
    i0 = (UCHAR)Val;
    BldrReadSMBusByte(SMC_SLAVE_ADDRESS, 0x1d, &Val);
    i1 = (UCHAR)Val;
    BldrReadSMBusByte(SMC_SLAVE_ADDRESS, 0x1e, &Val);
    i2 = (UCHAR)Val;
    BldrReadSMBusByte(SMC_SLAVE_ADDRESS, 0x1f, &Val);
    i3 = (UCHAR)Val;

    if (i0 == 0 && i1 == 0 && i2 == 0 && i3 == 0) {
        BldrShutdownSystem();    
    }

    BldrEncoderTranslate(i0, i1, i2, i3, &o0, &o1);

    //
    // Write the outputs
    //
    
    BldrWriteSMBusByte(SMC_SLAVE_ADDRESS, 0x20, o0);
    BldrWriteSMBusByte(SMC_SLAVE_ADDRESS, 0x21, o1);

    BldrPrint(("\n%d %d %d %d => %d %d", i0, i1, i2, i3, o0, o1));


    //
    // Reset the SMC revision pointer
    //
    
    BldrWriteSMBusByte(SMC_SLAVE_ADDRESS, 0x1, 0);

    //
    // Reenable all MCP devices.  Specifically NIC and IDE which
    // were disabled in the init table strap value
    //
    
    _outpd(PCI_TYPE1_ADDR_PORT, 0x8000088C);
    _outpd(PCI_TYPE1_DATA_PORT, 0x40000000);
}

#endif


VOID
BldrSetupUSB(
    VOID
    )
{

    UCHAR MCPRevisionID;

    //
    // Setup the USB ASRC (Automatic Slew Rate Compensation) which affects how the 
    // USB signal is driven.  This change is required only for MCP revision D01 and later.
    // Read the MCP revision to see if the change needs to be applied.
    //
    
    _outpd(PCI_TYPE1_ADDR_PORT, 0x80000808);
    
    MCPRevisionID = (UCHAR)_inp(PCI_TYPE1_DATA_PORT);    
    
    if (MCPRevisionID >= 0xD1) {
        _outpd(PCI_TYPE1_ADDR_PORT, 0x800008C8);
        _outpd(PCI_TYPE1_DATA_PORT, 0x00008F00);
    }
}


#ifdef BLDRMEMTEST
VOID
BldrTestMemory(
    ULONG StartAddress,
    ULONG EndAddress,
    BOOLEAN CompareDigests
    )                                                           
{
    UCHAR Key[16];
    ULONG Index;        
    UCHAR SHADigest[A_SHA_DIGEST_LEN];
    A_SHA_CTX SHAHash;

    if (CompareDigests) {
        A_SHAInit(&SHAHash);
        A_SHAUpdate(&SHAHash, (PVOID)BLDR_RELOCATED_ORIGIN, BLDR_BLOCK_SIZE);
        A_SHAFinal(&SHAHash, SHADigest);
        
        DbgPrintHex(SHADigest, 20);
    
        A_SHAInit(&SHAHash);
        A_SHAUpdate(&SHAHash, (PVOID)0xFFFF9E00, BLDR_BLOCK_SIZE);
        A_SHAFinal(&SHAHash, SHADigest);
        
        DbgPrintHex(SHADigest, 20);
    }


    DbgPrint("Testing memory from %x to %x with 00s", StartAddress, EndAddress);

    RtlFillMemory((PVOID)StartAddress, EndAddress - StartAddress + 1, 0);

    for (Index = 0; Index < 16; Index++) {
        Key[Index] = (UCHAR)Index;
    }
    BldrEncDec((PUCHAR)StartAddress, EndAddress - StartAddress + 1, Key, sizeof(Key));

    __asm wbinvd
    
    for (Index = 0; Index < 16; Index++) {
        Key[Index] = (UCHAR)Index;
    }
    BldrEncDec((PUCHAR)StartAddress, EndAddress - StartAddress + 1, Key, sizeof(Key));

    __asm wbinvd

    for (Index = StartAddress; Index <= EndAddress; Index++) {
        if ((*(UCHAR*)Index) != 0) {
            MemTestErrorCount++;
            DbgPrint("***Mem test failed at %x (%d)", Index, (*(UCHAR*)Index)); 
        }
    }
    
    DbgPrint("Testing memory from %x to %x with FFs", StartAddress, EndAddress);

    RtlFillMemory((PVOID)StartAddress, EndAddress - StartAddress + 1, 0xFF);

    for (Index = 0; Index < 16; Index++) {
        Key[Index] = (UCHAR)Index;
    }
    BldrEncDec((PUCHAR)StartAddress, EndAddress - StartAddress + 1, Key, sizeof(Key));

    __asm wbinvd
    
    for (Index = 0; Index < 16; Index++) {
        Key[Index] = (UCHAR)Index;
    }
    BldrEncDec((PUCHAR)StartAddress, EndAddress - StartAddress + 1, Key, sizeof(Key));

    __asm wbinvd

    for (Index = StartAddress; Index <= EndAddress; Index++) {
        if ((*(UCHAR*)Index) != 0xFF) {
            MemTestErrorCount++;
            DbgPrint("***Mem test failed at %x (%d)", Index, (*(UCHAR*)Index)); 
        }
    }
}
#endif
