Free60-Wiki

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;
}