1bl Code

From Xenon Wiki
Jump to navigation Jump to search

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