Line 1:
Line 1:
−
Just like it's Wii counterpart, boot1 is the second-stage bootloader which is loaded by [[boot0]].
+
{{lowercase title}}
−
boot1 is stored inside the first SLC NAND blocks in the form of an [[Ancast_Image | ancast image]]. There are always two copies of boot1 inside the SLC NAND and, together, the 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 it's 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 [[Hardware/OTP | OTP]]. Immediately after, boot0 permanently disables access to this key by clearing the appropriate value in the [[Hardware/Latte_Registers#LT_OTPPROT | LT_OTPPROT]] register.
+
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|ancast image]]. Unlike the Wii boot1, this boot1 is the final bootloader of the Wii U, much like the Wii [https://wiibrew.org/wiki/boot2 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).
−
Thanks to this mechanism, the contents of boot1 and it's key still remain unknown. However, it's possible to predict boot1's role in the boot process to some extent.
+
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).
−
boot1 is responsible for loading the [[IOSU]] from NAND, therefore it must read it, verify it's signature and decrypt it. It's also speculated that boot1 must configure external DDR3 memory.
−
Attempting to read the LT_OTPPROT register immediately after the IOSU begins executing will return the value 0xCF7FFFFF.
+
During the boot process, boot0 loads boot1 from NAND and decrypts it using an AES key stored in the console's [[Hardware/OTP|OTP]]. Immediately after, boot0 permanently disables access to this key by clearing the appropriate value in the [[Hardware/Latte_Registers#LT_EFUSEPROT|LT_EFUSEPROT]] register.
−
boot0 only sets LT_OTPPROT once to 0xDFFFFFFF, which means that boot1 also locks 2 additional OTP blocks for unknown reasons.
+
+
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_Image#Header|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 [[Hardware/Latte_GPIOs|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.
+
+
{| class="wikitable" border="1"
+
|-
+
! Offset || Size || Description
+
|-
+
| 0x0 || 0x4 || Version
+
|-
+
| 0x4 || 0x4 || [[#BootFlags|BootFlags]]
+
|-
+
| 0x8 || 0x4 || [[#PowerFlags|PowerFlags]]
+
|-
+
| 0xC || 0x4 || BootCount
+
|-
+
| 0x10 || 0x4 || [[#SystemMode|CurrentSystemMode]]
+
|-
+
| 0x14 || 0x4 || [[#SystemMode|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 ===
+
{| class="wikitable" border="1"
+
|-
+
! 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 ===
+
{| class="wikitable" border="1"
+
|-
+
! 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 ===
+
{| class="wikitable" border="1"
+
|-
+
! Bits
+
! Description
+
|-
+
| 0
+
| Off
+
|-
+
| 1
+
| Compat
+
|-
+
| 2
+
| Standby
+
|-
+
| 3
+
| Restart
+
|-
+
| 4-14
+
|
+
|-
+
| 15
+
| WiiReturn
+
|-
+
| 16-17
+
|
+
|-
+
| 18
+
| Recovery
+
|-
+
| 19
+
| Eco
+
|-
+
| 20
+
| Normal
+
|-
+
| 21-31
+
|
+
|}
+
+
== Error codes ==
+
boot1 sends error and status codes through the [[Hardware/Latte_GPIOs|NDEV_LED GPIO]].
+
+
{| class="wikitable"
+
|-
+
! Value
+
! Description
+
|-
+
| 0xF1
+
| Invalid hardware version
+
|-
+
| 0xF2
+
| Failed to initialize memory
+
|-
+
| 0xF3
+
| Failed to initialize [[Hardware/Toucan|Toucan]]
+
|-
+
| 0xF4
+
| Failed to read packages from [[Hardware/Toucan|Toucan]]
+
|-
+
| 0xF5
+
| Failed to read firmware image from [[Hardware/Toucan|Toucan]]
+
|-
+
| 0xF6
+
| Failed to authenticate firmware image
+
|-
+
| 0xF7
+
| Failed to decrypt firmware image
+
|}