Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
318 lines (270 sloc) 9.17 KB

Pseudocode

Have not reversed all of the hardware error subroutines.

// version 0x583

#define STACK 0x800002000001F700 // r1
#define TOCP 0x8000020000000000 // r2
#define SRAM 0x8000020000010000
#define NAND 0x80000200C8000000
#define PCI 0x80000200D0000000

#define _HW_REG_POST 0x8000020000061010
#define _HW_REG_61008 0x8000020000061008
#define HW_REG_POST (*((volatile QWORD *)_HW_REG_POST))
#define HW_REG_61008 (*((volatile QWORD *)_HW_REG_61008))

#define BITMASK32(n) ((~0ul) >> 32-bits)
#define BITMASK32_L(n) ~((~0ul) >> bits) // left justified bitmask, assumes the hardware makes bits shifted in 0
#define BITMASK64(n) ((~0ull) >> 64-bits)
#define BITMASK64_L(n) ~((~0ull) >> bits) // left justified bitmask, assumes the hardware makes bits shifted in 0
#define ROTL32(data, bits) ((data << bits) | data >> 32-bits) & ~0ul
#define ROTR32(data, bits) ((data >> bits) | data << 32-bits) & ~0ul
#define ROTL64(data, bits) ((data << bits) | data >> 64-bits) & ~0ull
#define ROTR64(data, bits) ((data >> bits) | data << 64-bits) & ~0ull

BYTE Salt[0xB] = <redacted>;
BYTE BLKey[0x10] = { <redacted> };
XECRYPT_RSAPUB_2048 xRSA;
xRSA = <redacted>

typedef struct _BLHeader
{
    WORD Magic;         // 0 : 2
    WORD Version;       // 2 : 2
    DWORD Flags;        // 4 : 4
    DWORD EntryPoint;   // 8 : 4
    DWORD Size;         // 0xC : 4
    BYTE key[0x10];     // 0x10 : 0x10
    QWORD Pad[4];       // 0x20 : 0x20
    XECRYPT_SIG Sig;    // 0x40 : 0x100
    // Header: 0x140
}BLHeader, *PBLHeader;

// write to the post bus
void POST(QWORD postCode)
{
    HW_REG_POST = (postCode << 56);
}

// outputs a given byte, then the same byte OR'ed with 0x80
// probably for their internal post sniffer
void POST_DATA(BYTE outPost)
{
    POST(outPost);
    POST(outPost | 0x80);
}

// outputs a given amount of bytes from a given address in high-low format
void POST_ADDRESS(QWORD pqwAddy, DWORD cbAddy)
{
    for(int i = 0;i < cbAddy;i++)
    {
        BYTE bData = *(BYTE*)pqwAddy+i;
        POST(data >> 4); // output high
        POST(data & 0xF); // output low
    }
}

void PanicGen()
{
    while(1)
        continue;
}

void Panic(QWORD postCode)
{
    POST(postCode);
    PanicGen();
}

QWORD ReadHighestByte(QWORD Address)
{
    return ((*(QWORD*)Address) >> 56);
}

DWORD sub_36A8()
{
    DWORD ret = ReadHighestByte(HW_REG_61008);
    if((ret & 0x80) != 0)
        ret = (~ret) & 0xFF;
    return = ret & 0xF8;
}

// rough translation for the cntlzw instruction
DWORD countLeadingZeros(DWORD data)
{
    DWORD count = 0;
    for(int i = 0;i < 31;i++)
    {
        if(data >> 31-i)
            return count;
        count++;
    }
}

/*
Basically this happens when its detected that the
SOC doesn't carry correct values, it goes into a loop
and starts outputting data from a certain SOC register
(0x8000020000061008) - possibly error register?

It doesn't repeat it's output unless the SOC register changes
*/
void HARDWARE_ERROR_PRINT(DWORD dwUnk1)
{
    while(1)
    {
        BYTE bUnk1_p = ROTL32(dwUnk1, 3) & 0x1F;
        POST_DATA(bUnk1_p | 0x60);

        BYTE tmp = 0;
        BYTE bUnk1 = dwUnk1 & FF;

        if(bUnk1 == 0x78)
            tmp = 1;
        else
        {
            if(bUnk1 == 0)
                sub_3878(tmp);
            else if(bUnk1 == 8)
                sub_38B8(tmp);
            else if(bUnk1 == 0x10)
                sub_3AE0(tmp);
            else if(bUnk1 == 0x18)
                sub_3B30(tmp);
            else if(bUnk1 == 0x20)
                sub_3BB0(tmp);
            else if(bUnk1 == 0x28)
                sub_39C8(tmp);
            else if(bUnk1 == 0x30)
                sub_3F88(tmp, PCI + 0x8000);
            else if(bUnk1 == 0x38)
                sub_3C78(tmp);
            else if(bUnk1 == 0x40)
                sub_3D08(tmp);
            else if(bUnk1 == 0x48)
                sub_3DE0(tmp);
            else if(bUnk1 == 0x50)
                POST_ADDRESS(TOCP+2, 2);
            else if(bUnk1 == 0x58)
                sub_3F88(tmp, PCI);
            else if(bUnk1 == 0x60)
                sub_4008(tmp);
            tmp = 0;
        }

        POST_DATA(bUnk1_p | 0x70);

        DWORD r30 = (countLeadingZeros(bUnk1-0x50) >> 27) & 1;
        do
        {
            for(int i = 1;i < 6;i++)
            {
                DWORD dwUnk2 = sub_36A8();
                if(dwUnk1 & 0xFF != dwUnk2 & 0xFF)
                {
                    dwUnk1 = dwUnk2;
                    i = 0;
                }
            }
        } while(((countLeadingZeros((dwUnk1 & 0xFF) - 0x50) >> 27) & 1) == r30);
    }
}

bool CB_VerifyOffset(DWORD offset, DWORD arg2)
{
    if(offset != (offset + 0xF) & 0xFFFFFFF0)
        return false;
    if(offset - 0x80 > 0x7FFFF7F)
        return false;
    if((arg2 + 0xF) & 0xFFFFFFF0 >= offset - 0x8000000)
        return false;
    return true;
}

// Copies by 0x10 byte blocks
// cBlocks: how many 0x10 byte blocks to copy
void CB_Copy(QWORD dest, QWORD src, DWORD cBlocks)
{
    for(int i = 0; i < cBlocks; i++)
    {
        *(QWORD*)dest+(i*0x10) = *(QWORD*)src+(i*0x10);
        *(QWORD*)dest+(i*0x10)+8 = *(QWORD*)src+(i*0x10)+8;
    }
}

void CB_Jump(QWORD address, QWORD arg2)
{
    // grabs data from the CB before nulling the area
    QWORD r27 = *(QWORD*)SRAM+0x20;
    QWORD r28 = *(QWORD*)SRAM+0x28;
    QWORD r29 = *(QWORD*)SRAM+0x30;
    QWORD r30 = *(QWORD*)SRAM+0x38;
    // nulls 0x20-0x140(?)
    QWORD tmp = SRAM+0x20;
    for(int i = 0; i < 0x12; i++)
    {
        *tmp+(i*0x10) = 0ULL;
        *tmp+(i*0x10)+8 = 0ULL;
    }

    // check the size
    tmp = (((*(DWORD*)SRAM+0xC) + 0xF) & 0xFFFFFFF0);
    if(tmp >= 0x10000)
        Panic(0x98);

    // nulls the area after the CB
    QWORD addy = tmp + SRAM;
    for(int i = 0; i < (tmp - 0x10000) >> 4; i++)
    {
        *addy+(i*0x10) = 0ULL;
        *addy+(i*0x10)+8 = 0ULL;
    }

    // sets up tlb page
    // sets registers r0-r26 to 0
    // jump to CB
    goto (address & 0xFFFF) + 0x2000000;
    return;
}

void CB_Load()
{
    POST(0x11);
    FSB1(); // sub_3450

    POST(0x12);
    FSB2(); // sub_34D0

    POST(0x13);
    FSB3(); // sub_35A8

    POST(0x14);
    FSB4(); // sub_3658

    POST(0x15);
    DWORD cbOffset = *(DWORD*)NAND+8; // r25
    if(!CB_VerifyOffset(cbOffset, 0x10))
        Panic(0x94);

    POST(0x16);
    QWORD cbNAddy = NAND+cbOffset; // r26
    CB_Copy(SRAM, cbNAddy, 1);

    POST(0x17);
    PBLHeader cbHeader = (PBLHeader)SRAM;
    if((cbHeader->Size - 0x264) > 0xBD9C
        || (cbHeader->Magic & 0xFFF) != 0x342
        || (cbHeader->EntryPoint & 0x3)
        || (cbHeader->EntryPoint) < 0x264 // on slim its < 0x3B8
        || (cbHeader->Size & 0xFFFFFFFC) >= (cbHeader->EntryPoint & 0x3) // doesn't make sense, check later offset 0x4340 - On slim it makes sense: if(entrypoint >= size & 0xFFFFFFFC) panic
        || !CB_VerifyOffset(cbOffset, cbHeader->Size))
        Panic(0x95);

    POST(0x18);
    QWORD tmp = ((cbHeader->Size + 0xF) & 0xFFFFFFF0);
    CB_Copy(SRAM+0x10, cbNAddy+0x10, (tmp - 0x10) >> 4);

    POST(0x19);
    // overwrites the old key with the new one
    XeCryptHmacSha(BLKey, 0x10, &cbHeader->key, 0x10, 0, 0, 0, 0, &cbHeader->key, tmp);

    POST(0x1A);
    XECRYPT_RC4_STATE rc4;
    XeCryptRc4Key(&rc4, cbHeader->key, 0x10); // key = HmacSha(1BLKey, cbKey, 0x10)

    POST(0x1B);
    XeCryptRc4Ecb(&rc4, SRAM+0x20, tmp - 0x20); // Decrypts everything after the header

    POST(0x1C);
    BYTE Hash[0x14] = { 0 };
    XeCryptRotSumSha(SRAM, 0x10, SRAM+0x140, tmp - 0x140, Hash, 0x14); // hashes everything after the sig

    POST(0x1D);
    if(XeCryptBnQwBeSigDifference(&cbHeader->Sig, Hash, Salt, &xRSA)) // checks sig against hash with public rsa key
        Panic(0x96);

    POST(0x1E);
    CB_Jump(cbHeader->EntryPoint, tmp+cbOffset); // sets up tbl page and loads some registers before jumping to cb
    return;
}

void BL_1()
{
    // thread check?

    POST(0x10); // entered 1bl

    // null the sram area
    for(int i = 0; i < 0x1000; i++)
    {
        *(QWORD*)SRAM+(i*0x10) = 0ULL;
        *(QWORD*)SRAM+((i*0x10)+8) = 0ULL;
    }

    DWORD tmp = sub_36A8();
    if((tmp & 0xFF) == 0x50)
        HARDWARE_ERROR_PRINT(tmp); // look into later

    // load and execute the CB
    CB_Load();

    // CB_Load shouldn't return...
    PanicGen();

    return;
}