Line 294:
Line 294:
=== LoadImg ===
=== LoadImg ===
+
The Espresso boot ROM loads, verifies and decrypts the PPC ancast image.
+
+
u8 aes_key[0x10] = {0};
+
u32 aes_key_addr = 0;
+
u32 ecdsa_key_addr = 0;
+
u32 img_addr = 0;
+
u32 img_state = 0;
+
ImgHeader img_header;
+
u8 img_hash[0x14] = {0};
+
u32 is_evaluation = 0;
+
u32 time_fw_verify_start = 0;
+
u32 time_fw_verify_end = 0;
+
u32 time_fw_decrypt_start = 0;
+
u32 time_fw_decrypt_end = 0;
+
+
// Does nothing, just returns
+
sub_784();
+
+
// Check if SCR bits 30 and 31 are set
+
if ((GetSCR() & 0xF0000000) == 0xC0000000)
+
{
+
// Set an error if bit 26 is not set
+
if ((GetSCR() & 0x2000000) == 0)
+
{
+
SetState(0x11000000);
+
+
// Deadlock
+
sub_20000();
+
}
+
+
// Use a secret static key
+
memcpy(aes_key, aes_static_key, 0x10);
+
aes_key_addr = &aes_key;
+
+
SetImgState(0x80);
+
+
// Use production ECDSA key
+
ecdsa_key_addr = &ecdsa_prod_key;
+
+
SetImgState(0x20000);
+
}
+
else
+
{
+
// Read the fuse type from eFuses
+
u32 fuse_type = *(u32 *)(efuse_addr + 0x3C);
+
+
// Set SCR bit 28
+
SetSCR(GetSCR() | 0x10000000);
+
+
if ((fuse_type & 0x4000000) != 0)
+
{
+
if (IsVWii())
+
{
+
// Copy the vWii AES key from eFuses
+
memcpy(aes_key, efuse_addr + 0x10, 0x10);
+
+
SetImgState(2);
+
}
+
else
+
{
+
// Copy the WiiU AES key from eFuses
+
memcpy(aes_key, efuse_addr, 0x10);
+
+
SetImgState(1);
+
}
+
+
aes_key_addr = &aes_key;
+
+
// Read the fuse type from eFuses
+
fuse_type = *(u32 *)(efuse_addr + 0x3C);
+
+
if ((fuse_type & 0x18000000) == 0x8000000)
+
{
+
// Use development ECDSA key
+
ecdsa_key_addr = &ecdsa_dev_key;
+
+
SetImgState(0x10000);
+
}
+
else if ((fuse_type & 0x18000000) == 0x10000000)
+
{
+
// Use production ECDSA key
+
ecdsa_key_addr = &ecdsa_prod_key;
+
+
SetImgState(0x20000);
+
}
+
else
+
{
+
SetState(0x12000000);
+
+
// Deadlock
+
sub_20000();
+
}
+
}
+
else
+
{
+
aes_key_addr = 0;
+
ecdsa_key_addr = 0;
+
is_evaluation = 1;
+
}
+
}
+
+
if (IsVWii())
+
{
+
img_addr = 0x1330000;
+
+
if ((GetHID1() & 0x8000000) != 0)
+
SetImgState(0x400);
+
else
+
SetImgState(0x200);
+
}
+
else
+
{
+
img_addr = 0x8000000;
+
+
SetImgState(0x100);
+
}
+
+
if (aes_key_addr)
+
{
+
// Get the current time
+
time_fw_verify_start = GetTB();
+
+
// Load the ancast image over DMA
+
DmaLoad(dma_addr, img_addr, 0);
+
DmaBarrier(0);
+
+
// Copy the ancast header
+
memcpy(&img_header, dma_addr, 0x100);
+
+
// Verify the ancast header's ECDSA signature
+
if (!EcdsaVerify(ecdsa_key_addr, (u8 *)&img_header.reserved4, 0x60, img_header.signature))
+
{
+
SetState(0x21000000);
+
+
// Deadlock
+
sub_20000();
+
}
+
+
// Ensure 2 bytes at offset 0xA0 are set to 0
+
if (img_header.reserved4 != 0)
+
{
+
SetState(0x22000000);
+
+
// Deadlock
+
sub_20000();
+
}
+
+
// Ensure bytes at offsets 0xA2 and 0xA3 are set to 0
+
if ((img_header.reserved5 != 0) || (img_header.reserved6 != 0))
+
{
+
SetState(0x23000000);
+
+
// Deadlock
+
sub_20000();
+
}
+
+
// Ensure 0x3C bytes at offset 0xC4 are set to 0
+
for (int i = 0; i < 0x3C; i++)
+
{
+
if (img_header.reserved7[i] != 0)
+
{
+
SetState(0x24000000);
+
+
// Deadlock
+
sub_20000();
+
}
+
}
+
+
if ((img_state & 0xFF) == 0x80)
+
{
+
if (img_header.targetDevice != 0x14)
+
{
+
SetState(0x29000000);
+
+
// Deadlock
+
sub_20000();
+
}
+
}
+
else
+
{
+
if (img_header.targetDevice == 0x11)
+
{
+
if ((img_state & 0xFF00) != 0x100)
+
{
+
SetState(0x25000000);
+
+
// Deadlock
+
sub_20000();
+
}
+
}
+
else if (img_header.targetDevice == 0x12)
+
{
+
if ((img_state & 0xFF00) != 0x400)
+
{
+
SetState(0x25000000);
+
+
// Deadlock
+
sub_20000();
+
}
+
}
+
else if (img_header.targetDevice == 0x13)
+
{
+
if ((img_state & 0xFF00) != 0x200)
+
{
+
SetState(0x25000000);
+
+
// Deadlock
+
sub_20000();
+
}
+
}
+
else
+
{
+
SetState(0x25000000);
+
+
// Deadlock
+
sub_20000();
+
}
+
}
+
+
if (img_header.consoleType == 1)
+
{
+
if ((img_state & 0xFF0000) != 0x10000)
+
{
+
SetState(0x26000000);
+
+
// Deadlock
+
sub_20000();
+
}
+
}
+
else if (img_header.consoleType == 2)
+
{
+
+
if ((img_state & 0xFF0000) != 0x20000)
+
{
+
SetState(0x26000000);
+
+
// Deadlock
+
sub_20000();
+
}
+
}
+
else
+
{
+
SetState(0x26000000);
+
+
// Deadlock
+
sub_20000();
+
}
+
+
u32 img_blk_count = (img_header.imgSize >> 0xC);
+
+
if (!img_blk_count)
+
{
+
SetState(0x27000000);
+
+
// Deadlock
+
sub_20000();
+
}
+
else
+
{
+
u32 img_offset = img_addr + 0x100;
+
+
// Initialize the SHA1 context
+
Sha1Init(&sha1_ctx);
+
+
// Load the ancast image and update the SHA1 context
+
for (int i = 0; i < img_blk_count; i++)
+
{
+
DmaLoad(dma_addr + ((i << 12) & 0x1000), img_offset, 0);
+
Sha1Update(&sha1_ctx, dma_addr + ((i << 12) & 0x1000), 0x1000);
+
DmaBarrier(0);
+
img_offset += 0x1000;
+
}
+
+
// Calculate the ancast image's hash
+
Sha1Final(&sha1_ctx, img_hash);
+
+
// Compare the calculated hash with the expected one
+
if (memcmp(img_header.imgHash, img_hash, 0x14))
+
{
+
SetState(0x28000000);
+
+
// Deadlock
+
sub_20000();
+
}
+
else
+
{
+
// Get the current time
+
time_fw_verify_end = GetTB();
+
+
// Save the time spent verifying the image
+
*(u32 *)0xC16FFFF8 = time_fw_verify_end - time_fw_verify_start;
+
+
if (!img_blk_count)
+
{
+
// Deadlock
+
sub_20000();
+
}
+
else
+
{
+
// Get the current time
+
time_fw_decrypt_start = GetTB();
+
+
// Initialize the AES context
+
AesCtxIni(&aes_ctx, aes_key_addr, 0x10, aes_iv, 0);
+
+
img_offset = img_addr + 0x100;
+
+
// Load, decrypt and store the ancast image
+
for (int i = 0; i < img_blk_count; i++)
+
{
+
DmaLoad(dma_addr + ((i << 12) & 0x1000), img_offset, 0);
+
AesDecrypt(&aes_ctx, dma_addr + ((i << 12) & 0x1000), 0x1000, dma_addr + ((i << 12) & 0x1000));
+
DmaStore(img_offset, dma_addr + ((i << 12) & 0x1000), 0);
+
DmaBarrier(0);
+
img_offset += 0x1000;
+
}
+
+
// Does nothing, just returns
+
sub_20950();
+
+
// Get the current time
+
time_fw_decrypt_end = GetTB();
+
+
// Save the time spent decrypting the image
+
*(u32 *)0xC16FFFF4 = time_fw_decrypt_end - time_fw_decrypt_start;
+
}
+
}
+
}
+
}
+
+
return img_addr + 0x100;
+
=== Finish ===
=== Finish ===
The Espresso boot ROM cleans up and jumps to the entrypoint address returned by [[#LoadImg|LoadImg]] minus 4 bytes.
The Espresso boot ROM cleans up and jumps to the entrypoint address returned by [[#LoadImg|LoadImg]] minus 4 bytes.