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. |