Changes

Jump to navigation Jump to search
Finish documenting the Espresso boot ROM
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.

Navigation menu