boot1

From WiiUBrew
Jump to navigation Jump to search

Just like it's Wii counterpart, boot1 is the second-stage bootloader which is loaded by boot0. boot1 is stored inside the first SLC NAND blocks in the form of an ancast image. Unlike the Wii boot1, this boot1 is the final bootloader of the Wii U, much like the Wii boot2. There are always two copies of boot1 inside the SLC NAND and, together, these images form the only blocks that are left as plain data (not encrypted with the OTP SLC key).

boot1 runs from on-die SRAM starting at address 0x0D400000 and its size must not exceed the total of 0xF800 bytes (it's size is checked by boot0).

During the boot process, boot0 loads boot1 from NAND and decrypts it using an AES key stored in the console's OTP. Immediately after, boot0 permanently disables access to this key by clearing the appropriate value in the LT_EFUSEPROT register.

boot1 then loads IOSU from the filesystem as an ancast image, and decrypts and verifies it, similar to what boot0 does with boot1.

What follows are general descriptions and pseudo-code that illustrates the several sub-stages of the Wii U's boot1.

Initialization

boot1 is decrypted in place at address 0x0D400000, but runs from address 0x0D400200 due to the first 0x200 bytes containing it's ancast header. The first step in boot1's execution is setting up the stack and jumping to the main routine:

// Clear all memory from the end of boot1
// up to the middle of boot0 (hides main functions)
memset_range(0x0D40DF40, 0x0D4125F0, 0, 0x04);

u32 stack_size = *(u32 *)0x0D40DA80;

// Setup boot1's stack pointer
SP = 0x0D40EDB0 + stack_size

// Change into THUMB mode and call main
sub_D40C82C(0x0D400200);

// Deadlock
loc_D400278();

Main

This is the bulk of the second-stage bootloader. Contrary to boot0, boot1 doesn't signal execution stages to debug ports via GPIO, but it can use the NDEV_LED GPIO for outputting error and status codes. Nonetheless, what follows is a breakdown of the main routine into relevant snippets of pseudo-code.

Start

boot1 grabs the PRSH/PRST IV from memory, disables boot0 mapping and configures IOPI, IOMEM and EXI.

// Copy the PRSH IV from memory
memcpy(prsh_iv_buf, 0x0D40CC11, 0x10);

// Get timer value
u32 time_boot1_start = *(u32 *)HW_TIMER;

// Clear NDEV_LED state
*(u32 *)(0x0D40E258) = 0;

u32 spare1_val = *(u32 *)HW_SPARE1;

// Disable boot0 mapping
*(u32 *)HW_SPARE1 = spare1_val | 0x1000;

// Assert RSTB_IOPI and RSTB_IOMEM
u32 resets_val = *(u32 *)HW_RSTCTRL;
resets_val &= 0xFFF7FFFF;
resets_val |= 0x80000;
resets_val &= 0xFFFBFFFF;
resets_val |= 0x40000;
*(u32 *)HW_RSTCTRL = resets_val;

// Enable EXI
u32 aip_prot_val = *(u32 *)HW_AIPPROT;
aip_prot_val &= ~(0x01);
aip_prot_val |= 0x01;
*(u32 *)HW_AIPPROT = aip_prot_val;

TestHardware

boot1 setups the fan's speed and power, initializes I2C for SMC and validates the hardware version.

// Send NDEV_LED marker
send_ndev_led(0x30);

// Set FanSpeed state
sub_D405570(0x01);

// Set FanPower state
sub_D4055BC(0x01);

u32 i2c_clock = 0x0A;
u32 i2c_channel = 0x01;

// Setup SMC I2C
i2c_init(i2c_clock, i2c_channel);

// Pulse the OFFIndicator via SMC
onoff_write(0x1F);

u32 iop2x_val = *(u32 *)LT_IOP2X;

// Adjust IOP clock multiplier
if (iop2x_val & 0x04)
{
   // Enable IRQ 12 (LT)
   *(u32 *)LT_ARMIRQFLAGLT = 0x1000;
   *(u32 *)LT_ARMIRQMASKLT = 0x1000;

   // Switch the multiplier back to 1x
   *(u32 *)LT_IOP2X = 0x01;

   // Wait for hardware interrupt
   sub_D40C870();

   // Disable IRQ 12 (LT)
   *(u32 *)LT_ARMIRQMASKLT = 0;
}

u32 get_hw_ver_res = get_hw_version(hw_ver_buf);

// Failed to get the hardware version
if (get_hw_ver_res)
{
   // Write the error message in the stack
   printf("BOOT1_FAIL: Unable to get ASIC version. (0x1F)");

   // Flash the NDEV_LED with the error code
   send_ndev_led(0xF1);

   // Deadlock
   loc_D400A3A();
}

u32 hw_ver = *(u32 *)hw_ver_buf;

// Hardware is not Latte
if (!(hw_ver & 0x0F000000))
{
   // Write the error message in the stack
   printf("BOOT1_FAIL: NOT supported hardware version. (0x1F)");
   
   // Flash the NDEV_LED with the error code
   send_ndev_led(0xF1);

   // Deadlock
   loc_D400A3A();
}

// Hardware is Latte A11 or A12
if ((hw_ver >> 0x18) == 0x21)
{
   // Write the error message in the stack
   printf("BOOT1_FAIL: NOT supported hardware version. (0x1F)");
   
   // Flash the NDEV_LED with the error code
   send_ndev_led(0xF1);

   // Deadlock
   loc_D400A3A();
}

u32 get_board_rev_res = get_board_revision(board_rev_buf);
u32 board_rev = *(u32 *)board_rev_buf;

// Failed to get board revision
if (get_board_rev_res)
{
   // Write the error message in the stack
   printf("BOOT1_FAIL: Unable to get board revision. (0x1F)");
   
   // Flash the NDEV_LED with the error code
   send_ndev_led(0xF1);

   // Deadlock
   loc_D400A3A();
}

GetRtcEvents

boot1 setups EXI for the RTC and collects events from RTC_CONTROL0 and RTC_CONTROL1.

// Setup EXI0 for RTC
config_exi_rtc();

u32 RTC_CONTROL0 = 0x21000C00;

// Read RTC_CONTROL0
u32 exi_read_res = exi_read(RTC_CONTROL0, rtc_control0_buf);

// Failed to read
if (exi_read_res)
{	
    // Pulse the ONIndicator
    onoff_write(0x13);

    goto init_mem;
}

u32 RTC_CONTROL1 = 0x21000D00;

// Read RTC_CONTROL1
u32 exi_read_res = exi_read(RTC_CONTROL1, rtc_control1_buf);

// Failed to read
if (exi_read_res)
{	
    // Pulse the ONIndicator
    onoff_write(0x13);

    goto init_mem;
}

u32 syscfg1_val = *(u32 *)LT_SYSCFG1;
u32 pflags_val = 0;

// Check if the CMPT_RETSTAT0 flag is raised
if (syscfg1_val & 0x04)
   pflags_val = 0x100000;        // Set CMPT_RETSTAT0 power flag

// Check if the CMPT_RETSTAT1 flag is raised
if (syscfg1_val & 0x08)
   pflags_val |= 0x80000;        // Set CMPT_RETSTAT1 power flag

u32 rtc_ctrl0 = *(u32 *)rtc_control0_buf;

// Check if the POFFLG_FPOFF flag is raised
if (rtc_ctrl0 & 0x04000000)
   pflags_val |= 0x01000000;     // Set POFF_FORCED power flag

// Check if the POFFLG_4S flag is raised
if (rtc_ctrl0 & 0x02000000)
   pflags_val |= 0x00800000;     // Set POFF_4S power flag

// Check if the POFFLG_TMR flag is raised
if (rtc_ctrl0 & 0x01000000)
   pflags_val |= 0x00400000;     // Set POFF_TMR power flag

// Check if the PONLG_TMR flag is raised
if (rtc_ctrl0 & 0x00010000)
   pflags_val |= 0x02000000;     // Set PON_TMR power flag

// Check if PONFLG_SYS is raised
if (rtc_ctrl0 & 0x00020000)
{
   pflags_val |= 0x04000000;     // Set PON_SMC power flag
   
   // Read the SystemEventFlag from SMC
   sub_D400BD0(0x41, sys_event_buf);

   u32 sys_event = *(u32 *)sys_event_buf;

   // POWER button was pressed
   if (sys_event & 0x00000040)
      pflags_val |= 0x80000000;  // Set PON_POWER_BTN power flag

   // EJECT button was pressed
   if (sys_event & 0x00000020)
      pflags_val |= 0x40000000;  // Set PON_EJECT_BTN power flag

   // Wake 1 signal is active
   if (sys_event & 0x00000001)
      pflags_val |= 0x08000000;  // Set PON_WAKEREQ1_EVENT power flag

   // Wake 0 signal is active
   if (sys_event & 0x00000002)
      pflags_val |= 0x10000000;  // Set PON_WAKEREQ0_EVENT power flag

   // BT interrupt request is active
   if (sys_event & 0x00000004)
      pflags_val |= 0x20000000;  // Set PON_WAKEBT_EVENT power flag

   // Timer signal is active
   if (sys_event & 0x00000008)
      pflags_val |= 0x00020000;  // Set PON_SMC_TIMER power flag
}

// Raise POFFLG_TMR, PONFLG_SYS and some unknown flags
exi_write(RTC_CONTROL0, 0x01C20000);

u32 rtc_ctrl1 = *(u32 *)rtc_control1_buf;

// Check if SLEEP_EN is raised
if (rtc_ctrl1 & 0x00000100)
   pflags_val |= 0x00200000;     // Set DDR_SREFRESH power flag

u32 mem_mode = 0;

// DDR_SREFRESH power flag is set
if (pflags_val & 0x00200000)
    mem_mode = 0x08;

// PON_SMC_TIMER power flag is set
if (pflags_val & 0x00020000)
{
   // Pulse the CCIndicator
   onoff_write(0x16);

   mem_mode |= 0x02;
   	
   // Set FanSpeed state
   sub_D405570(0);
}
else
{
   // Pulse the ONIndicator
   onoff_write(0x13);
}

InitializeMemory

boot1 initializes the memory controller.

// Initialize memory
u32 mem_init_res = sub_D4056E8(mem_mode);

// Failed to initialize memory
if (mem_init_res)
{
   // Write the error message in the stack
   printf("BOOT1_FAIL: Unable to initialize memory (0x2F)");
   
   // Flash the NDEV_LED with the error code
   send_ndev_led(0xF2);

   // Deadlock
   loc_D400A3A();
}

WriteMem2

boot1 tests MEM2 by writing a pattern over it's first 0x400 bytes.

u32 error_flag = 0;
u32 error_count = 0;

// Memory is not refreshing
if (error_count)
{
   // DDR_SREFRESH power flag is set
   if (pflags_val & 0x00200000)
   {
      // Check memory
      u32 mem_check_res = sub_D4056B0(mem_mode | 0x60 | 0x04);
      error_flag += mem_check_res;
   }
   else
      error_flag = 0;

   // Check memory
   u32 mem_check_res = sub_D4056B0(mem_mode | 0x60 | 0x08);
   error_flag |= mem_check_res;

   // Failed to setup memory
   if (error_flag != 0)
   {
      // Flash the NDEV_LED with the error code
      send_ndev_led(0xF2);

     // Deadlock
     loc_D400A3A();
   }

   u32 mem2_addr = 0x10000000;

   // Set random data in MEM2 region
   while (mem2_addr != 0x10000400)
   {
      *(u32 *)mem2_addr = 0x12345678;
      *(u32 *)(mem2_addr + 0x04) = 0x9ABCDEF0;
      
      mem2_addr += 0x08;
   }
}

TestMem2

boot1 checks if MEM2 refreshing is working.

u32 byte_count = 0xFF;
u32 mem2_test_pattern0 = *(u32 *)mem2_addr;
u32 mem2_test_pattern1 = *(u32 *)(mem2_addr + 0x04);

// Memory has not refreshed
if (mem2_test_pattern0 != 0x12345678)
{
   // Increase error count
   error_count++;
	
   if (error_count < 0x10)
      goto mem2_test;
   else
   {
      // Deadlock
      loc_D400A3A();
   }
}

// Memory has not refreshed
if (mem2_test_pattern1 != 0x9ABCDEF0)
{
   if (byte_count)
   {
      // Increase error count
      error_count++;
	
      if (error_count < 0x10)
         goto mem2_test;
      else
      {
         // Deadlock
         loc_D400A3A();
      }
   }
}

byte_count -= 0x02;

// Loop until all memory is checked
if (mem2_addr != 0x100003F8)
{
   mem2_addr -= 0x08;
   goto mem2_check;
}

// Too many errors
if (error_count == 0x10)
{
   // Deadlock
   loc_D400A3A();
}

ClearMem0

boot1 clears all MEM0 and adjusts IOP clock multiplier if necessary.

// Clear all MEM0
memset(0x08000000, 0, 0x002E0000);

// PON_SMC_TIMER power flag is not set
if (!(pflags_val & 0x00020000))
{
   // Change NDEV_LED state
   *(u32 *)(0x0D40E258) = ~(0x80);

    u32 iop2x_val = *(u32 *)LT_IOP2X;

    // Check if the clock multiplier hasn't been changed
    if (!(iop2x_val & 0x04))
    {
       // Enable IRQ 12 (LT)
       *(u32 *)LT_ARMIRQFLAGLT = 0x1000;
       *(u32 *)LT_ARMIRQMASKLT = 0x1000;

       // Change IOP clock multiplier to 3x
       *(u32 *)LT_IOP2X = 0x03;

       // Wait for hardware interrupt
       sub_D40C870();

       // Disable IRQ 12 (LT)
       *(u32 *)LT_ARMIRQMASKLT = 0;
    }
}

GetBootInfo

boot1 decrypts the PRSH/PRST from MEM2 and parses the "boot_info" structure.

// Send NDEV_LED marker
send_ndev_led(0x31);

// Store something in MEM1
*(u32 *)0x0000000C = 0x20008000;

// Read security level flag from OTP
ReadOTP(0x20, sec_lvl_buf, 0x04);

u32 sec_lvl = *(u32 *)sec_lvl_buf;

bool use_crypto = false;

// We are in retail/debug mode
if (sec_lvl < 0)
{
   // Read Wii U Starbuck ancast key from OTP
   ReadOTP(0x24, 0x0D40E240, 0x10);
	
   // Store a pointer to the ancast key
   *(u32 *)0x0D40E250 = ancast_key_addr;

   // Store a pointer to the ancast modulus
   *(u32 *)0x0D40E254 = ancast_modulus_addr;
   
   use_crypto = true;
}

// Decrypt PRSH/PRST with Starbuck ancast key
sub_D400320(0x10000400, 0x7C00, prsh_iv_buf);

// Parse PRSH/PRST
sub_D40B030(0x10000400, 0x7C00);

// Locate or create new "boot_info"
sub_D40AF10(0);

// DDR_SREFRESH power flag is set
if ((pflags_val & 0x01E00001) == 0x00200000)
{
   *(u32 *)boot_info_08_addr = 0;
	
   // Read from boot_info + 0x08
   u32 result = sub_D40AB84(boot_info_08_addr);
	
   // Got boot_info_08
   if (result == 0)
   {
      u32 boot_info_08 = *(u32 *)boot_info_08_addr;
      pflags_val |= (boot_info_08 & 0x101E);
   }
}
else
{
   // Clear bit 6 (IsWarmboot) in boot_info_04
   sub_D40AE4C();
	
   // Clear bit 3 (HasRamFwImg) in boot_info_04 and reset the RamFwImg fields
   sub_D40AC7C();
}

// Set boot_info_08
sub_D40AC30(pflags_val);

LoadFwImg

boot1 loads, validates and decrypts the IOSU fw.img file.

// Get timer value
u32 time_before_fw_load = *(u32 *)HW_TIMER;

// Load fw.img
u32 fw_load_res = sub_D4017B0(use_crypto);

if (fw_load_res)
{
   // Write the error message in the stack
   printf("BOOT1_FAIL: Unable to load firmware.\n");
   
   // Deadlock
   loc_D400A3A();
}

// Get timer value
u32 time_after_fw_load = *(u32 *)HW_TIMER;

u32 time_fw_verify = 0;
u32 time_fw_decrypt = 0;

if (use_crypto)
{
   // Get timer value
   u32 time_before_fw_verify = *(u32 *)HW_TIMER;

   // Initialize SHA-1 engine 1 (SHA)
   sub_D40C878(0x01);

   // Copy ancast signature (from ancast_hdr + 0x20 to ancast_hdr + 0x1A0)
   memcpy(0x0D40E000, 0x01000020, 0x180);

   // Copy ancast hash block (from ancast_hdr + 0x1A0 to ancast_hdr + 0x200)
   memcpy(0x0D40E180, 0x010001A0, 0x60);

   u32 ancast_img_size = *(u32 *)0x0D40E18C;

   // Check ancast image size
   if (ancast_img_size > 0x00F80000)
       printf("BOOT1: Firmware image of size 0x%x is too big.", ancast_img_size);
   else
       printf("BOOT1: Loaded firmware image size is 0x%x.", ancast_img_size);

   if (board_rev >= 0x04)
   {
       u32 ancast_version = *(u32 *)0x0D40E1A4;

       // Version is too old
       if (ancast_version <= 0x01)
       {
           printf("BOOT1_FAIL: Not able to boot older SDK.");
           printf("BOOT1_FAIL: Unable to authenticate firmware image (0x6F).");
         
           // Flash the NDEV_LED with the error code
           send_ndev_led(0xF6);

           // Deadlock
           loc_D400A3A();
       }
   }
   
   // Clear up the SHA-1 context
   memset(sha_ctx_buf, 0, 0x18);

   // Calculate empty hash
   u32 sha_calc_res = sha_calc(0x01, 0x00, 0x00, sha_ctx_buf);

   // Failed to calculate
   if (!sha_calc_res)
   {
       // Send execution stage marker
       send_ndev_led(0x41);
       
       printf("BOOT1_FAIL: Unable to authenticate firmware image (0x6F).");
			
       // Flash the NDEV_LED with the error code
       send_ndev_led(0xF6);

       // Deadlock
       loc_D400A3A();
   }

   // Clear up the SHA-1 context
   memset(sha_ctx_buf, 0, 0x18);

   // Setup the SHA-1 context
   *(u32 *)sha_ctx_buf = sha_ctx_buf + 0x18;      // end
   *(u32 *)(sha_ctx_buf + 0x04) = 0x02;           // mode (final)
   *(u32 *)(sha_ctx_buf + 0x08) = 0x0D40E180;     // in_buf
   *(u32 *)(sha_ctx_buf + 0x0C) = 0x60;           // in_size
   *(u32 *)(sha_ctx_buf + 0x10) = 0x0D40E200;     // out_buf
   *(u32 *)(sha_ctx_buf + 0x14) = 0x14;           // out_size

   // Calculate the ancast_hash_blk hash
   sha_calc_res = sha_calc(0x01, 0x00, 0x00, sha_ctx_buf);

   // Failed to calculate
   if (!sha_calc_res)
   {
       // Send execution stage marker
       send_ndev_led(0x42);

       printf("BOOT1_FAIL: Unable to authenticate firmware image (0x6F).");
		
       // Flash the NDEV_LED with the error code
       send_ndev_led(0xF6);

       // Deadlock
       loc_D400A3A();
   }

   u32 ancast_header_addr = 0x0D40E000;
   u32 rsa_exponent_addr = 0x0D40DC00;
   u32 ancast_hash_addr = 0x0D40E200;
   u32 ancast_modulus_addr = *(u32 *)0x0D40E254;

   // Calculate ancast image signature
   u32 rsa_calc_res = rsa_calc(ancast_hash_addr, ancast_modulus_addr, ancast_header_addr + 0x04, rsa_exponent_addr, 0x04);

   // Failed to calculate
   if (rsa_calc_res)
   {
       // Send execution stage marker
       send_ndev_led(0x43);

       printf("BOOT1_FAIL: Unable to authenticate firmware image (0x6F).");
		 
       // Flash the NDEV_LED with the error code
       send_ndev_led(0xF6);

       // Deadlock
       loc_D400A3A();
   }
   
   u16 ancast_header_1A0 = *(u32 *)0x0D40E180;

   // This field must be NULL
   if (ancast_header_1A0)
   {
       // Send execution stage marker
       send_ndev_led(0x44);

       printf("BOOT1_FAIL: Unable to authenticate firmware image (0x6F).");
		
       // Flash the NDEV_LED with the error code
       send_ndev_led(0xF6);

       // Deadlock
       loc_D400A3A();
   }

   u8 ancast_header_1A2 = *(u32 *)0x0D40E182;

   // This field must be NULL
   if (ancast_header_1A2)
   {
       // Send execution stage marker
       send_ndev_led(0x45);

       printf("BOOT1_FAIL: Unable to authenticate firmware image (0x6F).");
		
       // Flash the NDEV_LED with the error code
       send_ndev_led(0xF6);

       // Deadlock
       loc_D400A3A();
   }

   u8 ancast_header_1A3 = *(u32 *)0x0D40E183;

   // This field must be NULL
   if (ancast_header_1A3)
   {
       // Send execution stage marker
       send_ndev_led(0x45);

       printf("BOOT1_FAIL: Unable to authenticate firmware image (0x6F).");
			
       // Flash the NDEV_LED with the error code
       send_ndev_led(0xF6);
       	
       // Deadlock
       loc_D400A3A();
   }

   u32 ancast_header_1C8_start_addr = 0x0D40E1A8;
   u32 ancast_header_1C8_end_addr = 0x0D40E1E0;

   // Last 0x38 bytes in the ancast header must be NULL
   while (ancast_header_1C8_start_addr != ancast_header_1C8_end_addr)
   {
       u32 ancast_header_unk = *(u32 *)ancast_header_1C8_start_addr;
		
       // Must be NULL
       if (ancast_header_unk)
       {
          // Send execution stage marker
          send_ndev_led(0x46);

          printf("BOOT1_FAIL: Unable to authenticate firmware image (0x6F).");
	
          // Flash the NDEV_LED with the error code
          send_ndev_led(0xF6);

          // Deadlock
          loc_D400A3A();
       }
		
       ancast_header_1C8_start_addr++;
   }

   u32 ancast_img_body_size = *(u32 *)0x0D40E18C;
   u32 ancast_img_body_block_count = (ancast_img_body_size >> 0x0C);

   // Bad image size
   if (!ancast_img_body_block_count)
   {
       // Send execution stage marker
       send_ndev_led(0x48);

       printf("BOOT1_FAIL: Unable to authenticate firmware image (0x6F).");
		
       // Flash the NDEV_LED with the error code
       send_ndev_led(0xF6);
        
       // Deadlock
       loc_D400A3A();
   }

   // Bad image size alignment
   if (ancast_img_body_size & 0x00000FFF)
   {
       // Send execution stage marker
       send_ndev_led(0x48);

       printf("BOOT1_FAIL: Unable to authenticate firmware image (0x6F).");
			
       // Flash the NDEV_LED with the error code
       send_ndev_led(0xF6);

       // Deadlock
       loc_D400A3A();
   }

   // Initialize SHA-1 engine 1 (SHA)
   sub_D40C878(0x01);

   // Clear up the SHA-1 context
   memset(sha_ctx_buf, 0, 0x18);

   // Calculate empty hash
   sha_calc_res = sha_calc(0x01, 0x00, 0x00, sha_ctx_buf);

   // Failed to calculate
   if (!sha_calc_res)
   {
       // Send execution stage marker
       send_ndev_led(0x49);

       printf("BOOT1_FAIL: Unable to authenticate firmware image (0x6F).");
		
       // Flash the NDEV_LED with the error code
       send_ndev_led(0xF6);

       // Deadlock
       loc_D400A3A();
   }
   
   u32 block_count = 0;
   u32 ancast_img_body_addr = 0x01000200;

   // Calculate the ancast image body hash
   while (block_count < ancast_img_body_block_count)
   {
       // Clear up the SHA-1 context
       memset(sha_ctx_buf, 0, 0x18);

       // Setup the SHA-1 context
       *(u32 *)sha_ctx_buf = sha_ctx_buf + 0x18;            // end
       *(u32 *)(sha_ctx_buf + 0x04) = 0x01;                 // mode (update)
       *(u32 *)(sha_ctx_buf + 0x08) = ancast_img_body_addr; // in_buf
       *(u32 *)(sha_ctx_buf + 0x0C) = 0x1000;               // in_size
       *(u32 *)(sha_ctx_buf + 0x10) = 0x0D40E200;           // out_buf
       *(u32 *)(sha_ctx_buf + 0x14) = 0x14;                 // out_size

       // Calculate (update) ancast body hash (0x1000 bytes at a time)
       sha_calc_res = sha_calc(0x01, 0x00, 0x00, sha_ctx_buf);

       // Failed to calculate
       if (!sha_calc_res)
       {
          // Send execution stage marker
          send_ndev_led(0x4A);

          printf("BOOT1_FAIL: Unable to authenticate firmware image (0x6F).");
	
          // Flash the NDEV_LED with the error code
          send_ndev_led(0xF6);

          // Deadlock
          loc_D400A3A();
       }
       
       block_count++;
       ancast_img_body_addr += 0x1000;
   }

   // Clear up the SHA-1 context
   memset(sha_ctx_buf, 0, 0x18);
   
   // Setup the SHA-1 context
   *(u32 *)sha_ctx_buf = sha_ctx_buf + 0x18;           // end
   *(u32 *)(sha_ctx_buf + 0x04) = 0x02;                // mode (final)
   *(u32 *)(sha_ctx_buf + 0x08) = 0;                   // in_buf
   *(u32 *)(sha_ctx_buf + 0x0C) = 0;                   // in_size
   *(u32 *)(sha_ctx_buf + 0x10) = 0x0D40E200;          // out_buf
   *(u32 *)(sha_ctx_buf + 0x14) = 0x14;                // out_size

   // Calculate ancast body hash
   sha_calc_res = sha_calc(0x01, 0x00, 0x00, sha_ctx_buf);

   // Failed to calculate
   if (!sha_calc_res)
   {
       // Send execution stage marker
       send_ndev_led(0x4A);

       printf("BOOT1_FAIL: Unable to authenticate firmware image (0x6F).");
		
       // Flash the NDEV_LED with the error code
       send_ndev_led(0xF6);
	
       // Deadlock
       loc_D400A3A();
   }

   u32 body_hash_addr = 0x0D40E200;
   u32 ancast_body_hash_addr = 0x0D40E190;

   // Compare generated hash with the hash in the ancast header
   u32 memcmp_res = memcmp(ancast_body_hash_addr, body_hash_addr, 0x14);

   // Hashes don't match
   if (memcmp_res)
   {
       // Send execution stage marker
       send_ndev_led(0x4B);

       printf("BOOT1_FAIL: Unable to authenticate firmware image (0x6F).");
		
       // Flash the NDEV_LED with the error code
       send_ndev_led(0xF6);
       
       // Deadlock
       loc_D400A3A();
   }

   // Get timer value
   u32 time_after_fw_verify = *(u32 *)HW_TIMER;
   u32 time_before_fw_decrypt = *(u32 *)HW_TIMER;

   u32 starbuck_ancast_iv_addr = 0x0D40DAC0;

   // Decrypt Starbuck ancast image
   u32 decrypt_res = sub_D400320(ancast_img_body_addr, ancast_img_body_size, starbuck_ancast_iv_addr);

   // Failed to decrypt
   if (!decrypt_res)
   {
       printf("BOOT1_FAIL: Unable to decrypt firmware image (0x7F).");
       		
       // Flash the NDEV_LED with the error code
       send_ndev_led(0xF7);
	
       // Deadlock
       loc_D400A3A();
   }

   // Calculate time spent verifying fw.img
   u32 time_fw_verify = (time_before_fw_verify - time_after_fw_verify);
	
   // Get timer value
   u32 time_after_fw_decrypt = *(u32 *)HW_TIMER;

   // Calculate time spent decrypting fw.img
   u32 time_fw_decrypt = (time_after_fw_decrypt - time_before_fw_decrypt);
}

Cleanup

boot1 clears all sensitive data from memory, locks out two blocks in the OTP and turns on the ODD.

// Clear Wii U Starbuck ancast key from memory
memset(0x0D40E240, 0, 0x10);

// Clear the exponent from memory
memset(0x0D40DC00, 0, 0x04);

// Clear the RSA modulus from memory
memset(0x0D40DB00, 0, 0x100);

// Clear Wii U Starbuck ancast IV from memory
memset(0x0D40DAC0, 0, 0x10);

// Lock out two additional blocks in the OTP
u32 efuseprot_val = *(u32 *)LT_EFUSEPROT;
efuseprot_val &= 0xEF7FFFFF;
*(u32 *)LT_EFUSEPROT = efuseprot_val;

u32 onoff_val = 0;

// PON_SMC_TIMER and an unknown power flag are set
if (pflags_val & 0x00020010)
{
   // Set DcdcPowerControl2 GPIO's state
   sub_D405524(0x00);
	
   onoff_val = 0x15;
}
else
{
   // Turn on ODDPower via SMC
   onoff_write(0x00);
	
   // Set DcdcPowerControl2 GPIO's state
   sub_D405524(0x01);
	
   onoff_val = 0x10;
}

// Turn on the ONIndicator (0x10) or the CCIndicator (0x15)
onoff_write(onoff_val);

Finish

boot1 writes the time spent on various stages to the "boot_info" structure and jumps to the ELF loading stub in the decrypted IOSU fw.img file.

// Get timer value
u32 time_boot1_end = *(u32 *)HW_TIMER;

// Calculate time spent running boot1
u32 time_boot1 = (time_boot1_end - time_boot1_start);

// Write to boot_info_38
sub_D40AD2C(0x00, time_boot1);

// Calculate time spent reading fw.img
u32 time_fw_load = (time_before_fw_verify - time_before_fw_load)

// Write to boot_info_3C
sub_D40AD2C(0x01, time_fw_load);

// Write to boot_info_40
sub_D40AD2C(0x02, time_fw_verify);

// Write to boot_info_44
sub_D40AD2C(0x03, time_fw_decrypt);

// Read times stored by boot0
u32 time_boot0 = *(u32 *)0x0D417FE0;
u32 time_boot1_load = *(u32 *)0x0D417FE4;
u32 time_boot1_verify = *(u32 *)0x0D417FE8;
u32 time_boot1_decrypt = *(u32 *)0x0D417FEC;

// Write to boot_info_48
sub_D40AD2C(0x04, time_boot0);

// Write to boot_info_4C
sub_D40AD2C(0x05, time_boot1_load);

// Write to boot_info_50
sub_D40AD2C(0x06, time_boot1_verify);

// Write to boot_info_54
sub_D40AD2C(0x07, time_boot1_decrypt);

// Set bit 2 (HasBootTimeStats) in boot_info_04
sub_D40ABCC();

// Increase boot_info_0C by 1
sub_D40AEB0();

printf("BOOT1: Starting firmware relocator.\n");

// Flash the NDEV_LED with the status code
send_ndev_led(0x88);

// Jump to IOSU's ELF loader
sub_0x01000300();

// Deadlock
loc_D400A3A();

BootInfo

This is the "boot_info" structure used for sharing information between boot1 and IOS-MCP.

Offset Size Description
0x0 0x4 Version
0x4 0x4 BootFlags
0x8 0x4 PowerFlags
0xC 0x4 BootCount
0x10 0x4 CurrentSystemMode
0x14 0x4 NextSystemMode
0x18 0x4
0x1C 0x4
0x20 0x4
0x24 0x4
0x28 0x4 RamFwImgOsIdHigh
0x2C 0x4 RamFwImgOsIdLow
0x30 0x4 RamFwImgAddress
0x34 0x4 RamFwImgSize
0x38 0x4 Boot1MainTime
0x3C 0x4 Boot1ReadTime
0x40 0x4 Boot1VerifyTime
0x44 0x4 Boot1DecryptTime
0x48 0x4 Boot0MainTime
0x4C 0x4 Boot0ReadTime
0x50 0x4 Boot0VerifyTime
0x54 0x4 Boot0DecryptTime

BootFlags

Bits Description
0 IsPowerTransitionsEnabled
1 IsProd
2 HasBootTimeStats
3 HasRamFwImg
4
5 IsBootModeNand
6 IsWarmboot
7 HasBootInfo
8
9
10
11
12
13
14
15 IsPonEjectBtn
16-31

PowerFlags

Bits Description
0 PFLAGS_INVALID
1 PON_WAKEREQ1_EVENT_SW
2 PON_WAKEBT_EVENT_SW
3 PON_POWER_BTN_SW
4 ENTER_BG_NORMAL_MODE
5
6
7
8
9 PON_SMC_DISC
10 PON_SYNC_BTN
11
12
13 PON_RESTART
14
15
16 PON_COLDBOOT
17 PON_SMC_TIMER
18
19 CMPT_RETSTAT1
20 CMPT_RETSTAT0
21 DDR_SREFRESH
22 POFF_TMR
23 POFF_4S
24 POFF_FORCED
25 PON_TMR
26 PON_SMC
27 PON_WAKEREQ1_EVENT
28 PON_WAKEREQ0_EVENT
29 PON_WAKEBT_EVENT
30 PON_EJECT_BTN
31 PON_POWER_BTN

SystemMode

Bits Description
0 Off
1 Compat
2 Standby
3 Restart
4-14
15 WiiReturn
16-17
18 Test
19 ECO
20 Normal
21-31

Error codes

boot1 sends error and status codes through the NDEV_LED GPIO.

Value Description
0xF1 Invalid hardware version
0xF2 Failed to initialize memory
0xF3 Failed to initialize Toucan
0xF4 Failed to read packages from Toucan
0xF5 Failed to read firmware image from Toucan
0xF6 Failed to authenticate firmware image
0xF7 Failed to decrypt firmware image