Line 67:
Line 67:
// Set start time
// Set start time
*(u32 *)0x0D413768 = 0;
*(u32 *)0x0D413768 = 0;
−
+
===Stage 0x00===
===Stage 0x00===
boot0 sets a flag in LT_BOOT0 and configures debug ports' GPIOs.
boot0 sets a flag in LT_BOOT0 and configures debug ports' GPIOs.
Line 87:
Line 87:
===Stage 0x01===
===Stage 0x01===
−
Set something for memory swap.
+
boot0 sets something for memory swap.
// Send debug mark
// Send debug mark
SendGPIODebugOut(0x01);
SendGPIODebugOut(0x01);
Line 137:
Line 137:
===Stages 0x05, 0x06 and 0x07===
===Stages 0x05, 0x06 and 0x07===
+
boot0 asserts some resets and enables EXI.
These three stages are all merged together and the only signal sent to the debug ports is effectively 0x05.
These three stages are all merged together and the only signal sent to the debug ports is effectively 0x05.
−
boot0 asserts some resets and enables EXI.
// Send debug mark
// Send debug mark
SendGPIODebugOut(0x05);
SendGPIODebugOut(0x05);
Line 176:
Line 176:
===Stage 0x09===
===Stage 0x09===
boot0 analyses the IOStrength flags read in the previous stage and sets the strength of various devices.
boot0 analyses the IOStrength flags read in the previous stage and sets the strength of various devices.
+
// Send debug mark
+
SendGPIODebugOut(0x09);
+
+
// Setup devices' strength
+
u32 iostrength_flags = *(u32 *)0x0D41375C;
+
u32 iostrength_ctrl0_val = *(u32 *)LT_IOSTRENGTH_CTRL0;
+
u32 iostrength_ctrl1_val = *(u32 *)LT_IOSTRENGTH_CTRL1;
+
+
if (((iostrength_flags >> 0x0F) & 0x01) != 0)
+
{
+
iostrength_ctrl0_val &= 0xFFFC7FFF;
+
iostrength_ctrl0_val |= ((iostrength_flags << 0x11) >> 0x1D) << 0x0F;
+
}
+
+
*(u32 *)LT_IOSTRENGTH_CTRL0 = iostrength_ctrl0_val;
+
+
if (((iostrength_flags >> 0x0F) & 0x01) != 0)
+
{
+
iostrength_ctrl1_val &= 0xFFFC7FFF;
+
iostrength_ctrl1_val |= (iostrength_flags & 0x07) << 0x0F;
+
}
+
+
if (((iostrength_flags >> 0x07) & 0x01) != 0)
+
{
+
iostrength_ctrl1_val &= 0xFFE3FFFF;
+
iostrength_ctrl1_val |= ((iostrength_flags << 0x19) >> 0x1D) << 0x12;
+
}
+
+
if (((iostrength_flags >> 0x0B) & 0x01) != 0)
+
{
+
iostrength_ctrl1_val &= 0xFFFF8FFF;
+
iostrength_ctrl1_val |= ((iostrength_flags << 0x15) >> 0x1D) << 0x0C;
+
}
+
+
if (((iostrength_flags >> 0x13) & 0x01) != 0)
+
{
+
iostrength_ctrl1_val &= 0xFFFF8FFF;
+
iostrength_ctrl1_val |= ((iostrength_flags << 0x0D) >> 0x1D) << 0x15;
+
}
+
+
*(u32 *)LT_IOSTRENGTH_CTRL1 = iostrength_ctrl1_val;
===Stage 0x0A===
===Stage 0x0A===
boot0 sets the SEEPROM's pulse length and configures the SEEPROM GPIOs.
boot0 sets the SEEPROM's pulse length and configures the SEEPROM GPIOs.
+
// Send debug mark
+
SendGPIODebugOut(0x0A);
+
+
u32 seeprom_pulse = *(u32 *)0x0D413758;
+
+
// Set default value (retail)
+
if (seeprom_pulse == 0)
+
seeprom_pulse = 0xFA;
+
+
// Store pulse value at 0x0D413460
+
sub_D411774();
+
+
// Configure SEEPROM GPIOs
+
Set_SEEPROM_GPIO();
===Stage 0x0B===
===Stage 0x0B===
Line 211:
Line 266:
===Stage 0x0C===
===Stage 0x0C===
boot0 generates a CRC32 table in it's stack.
boot0 generates a CRC32 table in it's stack.
+
// Send debug mark
+
SendGPIODebugOut(0x0C);
+
+
// Generate CRC32 table from
+
// 0x0D413D44 to 0x0D414140
+
CRC32_Gen();
===Stage 0x0D===
===Stage 0x0D===
−
boot0 uses the SEEPROM key to decrypt SEEPROM data related to boot1.
+
boot0 uses the SEEPROM key to decrypt SEEPROM data related to the boot process.
// Send debug mark
// Send debug mark
SendGPIODebugOut(0x0D);
SendGPIODebugOut(0x0D);
Line 232:
Line 293:
boot0 validates the data decrypted from the SEEPROM at offset 0x1C0 in the previous stage using CRC32.
boot0 validates the data decrypted from the SEEPROM at offset 0x1C0 in the previous stage using CRC32.
This data is used to set miscellaneous settings during boot0's execution.
This data is used to set miscellaneous settings during boot0's execution.
+
// Send debug mark
+
SendGPIODebugOut(0x0E);
+
+
// Factory mode uses an empty SEEPROM key
+
if (!aes_seeprom_key)
+
{
+
// Match CRC32 with the table
+
// CRC32 is at seeprom_1C + 0x0C
+
bool match = sub_D410168(seeprom_1C, 0x10);
+
+
// CRC32 didn't match, but we are in factory
+
// mode, so we just set everything to 0
+
if (!match)
+
memset(seeprom_1C, 0, 0x10);
+
}
+
else // Normal mode has a valid SEEPROM key
+
{
+
// Match CRC32 with the table
+
// CRC32 is at seeprom_1C + 0x0C
+
bool match = sub_D410168(seeprom_1C, 0x10);
+
+
// CRC32 didn't match, throw an error
+
if (!match)
+
throw_error();
+
}
+
+
// Read SEEPROM boot flags
+
u16 seeprom_1C_00 = *(u16 *)0x0D41378C;
+
u16 seeprom_1C_02 = *(u16 *)0x0D41378C + 0x02;
+
u32 seeprom_1C_04 = *(u32 *)0x0D41378C + 0x04;
+
u32 seeprom_1C_08 = *(u32 *)0x0D41378C + 0x08;
+
+
// Factory mode doesn't need boot1's data
+
if (!aes_seeprom_key)
+
{
+
boot1_version = 0;
+
boot1_sector = 0;
+
}
===Stage 0x0F===
===Stage 0x0F===
Line 237:
Line 336:
This data is used to determine boot1's version and sector inside the NAND.
This data is used to determine boot1's version and sector inside the NAND.
This stage is skipped in factory mode.
This stage is skipped in factory mode.
+
// Send debug mark
+
SendGPIODebugOut(0x0F);
+
+
// Match CRC32 with the table
+
// CRC32 is at seeprom_1D + 0x0C
+
bool match1 = sub_D410168(seeprom_1D, 0x10);
+
+
// Match CRC32 with the table
+
// CRC32 is at seeprom_1E + 0x0C
+
bool match2 = sub_D410168(seeprom_1E, 0x10);
+
+
// Both sections have invalid CRC32
+
if (!match1 && !match2)
+
throw_error();
+
+
// The SEEPROM specifies the versions and sectors
+
// of the two boot1 images stored in the NAND
+
u16 boot1_version1 = (u16)seeprom_1D;
+
u16 boot1_version2 = (u16)seeprom_1E;
+
u16 boot1_sector1 = (u16)seeprom_1D + 0x02;
+
u16 boot1_sector2 = (u16)seeprom_1E + 0x02;
+
+
// The second version is always the most recent
+
u16 boot1_version = (boot1_version2 > boot1_version1) ? boot1_version2 : boot1_version1;
+
u16 boot1_sector = (boot1_version2 > boot1_version1) ? boot1_sector2 : boot1_sector1;
+
+
// Check if seeprom_1C_00 is not 0x400
+
if ((seeprom_1C_00 & 0x3FF) != 0)
+
{
+
// Store at 0x0D414200
+
sub_D41203C(seeprom_1C_00 & 0x3FF);
+
}
===Stages 0x10, 0x11 and 0x12===
===Stages 0x10, 0x11 and 0x12===
Line 258:
Line 389:
===Stage 0x13===
===Stage 0x13===
boot0 analyses the remaining data read from SEEPROM at offset 0x1C0 and uses it to configure the NAND_CONFIG and NAND_BANK registers. It then initializes the NAND engine.
boot0 analyses the remaining data read from SEEPROM at offset 0x1C0 and uses it to configure the NAND_CONFIG and NAND_BANK registers. It then initializes the NAND engine.
+
// Send debug mark
+
SendGPIODebugOut(0x13);
+
+
// Use supplied NAND configuration
+
if (((seeprom_1C_02 >> 0x0E) & 0x01) != 0)
+
*(u32 *)NAND_CONFIG = (u32)seeprom_1C_04;
+
+
// Use supplied NAND bank
+
if (((seeprom_1C_02 >> 0x0D) & 0x01) != 0)
+
*(u32 *)NAND_BANK = (u32)seeprom_1C_08;
+
+
// Determine if boot1 is in SLC or SLCCMPT
+
bool boot1_isSLC = (boot1_sector >> 0x0C) ? true : false;
+
+
// Init NAND
+
u32 result = sub_D411564(boot1_isSLC);
===Stage 0x14===
===Stage 0x14===
+
boot0 checks if it's start time is valid (must be 0, otherwise boot0 throws an error) and saves the current time (in order to track how long boot1's reading took).
This stage only executes if NAND engine's initialization was successful.
This stage only executes if NAND engine's initialization was successful.
−
boot0 checks if it's start time is valid (must be 0, otherwise boot0 throws an error).
+
// Send debug mark
+
SendGPIODebugOut(0x14);
+
+
// Bad start time
+
if (time_at_boot != 0)
+
throw_error();
+
+
// Take note of the current time
+
u32 boot1_read_time = *(u32 *)LT_TIMER;
===Stages 0x15, 0x16 and 0x17===
===Stages 0x15, 0x16 and 0x17===
−
These stages only execute if NAND engine's initialization was successful. Only signal 0x15 is sent to the debug ports.
boot0 flushes AHB memory, reads boot1's ancast header from NAND and checks the boot1's image size by looking into the respective field inside the header. If the size is valid (must not exceed 0xF800, so it doesn't overflow boot1's memory region), boot0 then proceeds to read the full boot1's image from NAND into address 0x0D400000:
boot0 flushes AHB memory, reads boot1's ancast header from NAND and checks the boot1's image size by looking into the respective field inside the header. If the size is valid (must not exceed 0xF800, so it doesn't overflow boot1's memory region), boot0 then proceeds to read the full boot1's image from NAND into address 0x0D400000:
Finally, boot0 calculates how long this operation took and stores this value for the IOS-MCP to read later on.
Finally, boot0 calculates how long this operation took and stores this value for the IOS-MCP to read later on.
+
These stages only execute if NAND engine's initialization was successful and only signal 0x15 is sent to the debug ports.
+
// Send debug mark
+
SendGPIODebugOut(0x15);
+
+
// AHB memory flush
+
ahbMemFlush(0);
+
+
// Bit 15 in seeprom_1C_02 tells to ignore NAND errors
+
bool ignore_errors = ((seeprom_1C_02 >> 0x0F) & 0x01) ? true : false;
+
+
// Read boot1's header from NAND into 0x0D400000
+
u32 result = NAND_Read(0x0D400000, boot1_sector << 0x06, 0x01, ignore_errors);
+
+
// Failed to read from NAND
+
if (!result)
+
throw_error();
+
+
// Copy the last 0x60 bytes of
+
// boot1's ancast image's header
+
memcpy(0x0D413940, 0x0D4001A0, 0x60);
+
+
// Check ancast image's body size
+
u32 ancast_body_size = *(u32 *)0x0D41394C;
+
+
// Calculate boot1's actual size
+
u32 boot1_image_size = ((ancast_body_size + 0x9FF) & 0xFFFFF800) - 0x01;
+
+
// The image is too big
+
if (boot1_image_size > 0xF7FF)
+
throw_error();
+
+
// AHB memory flush
+
ahbMemFlush(0);
+
+
// Read the full boot1 image
+
result = NAND_Read(0x0D400000, boot1_sector << 0x06, boot1_image_size >> 0x0B, ignore_errors);
+
+
// Failed to read from NAND
+
if (!result)
+
throw_error();
+
+
// Calculate how long it took to
+
// read boot1 (MCP later reads this)
+
u32 time_now = *(u32 *)LT_TIMER;
+
*(u32 *)0x0D417FE4 = time_now - boot1_read_time;
===Stage 0x18===
===Stage 0x18===
+
boot0 checks if boot1 is encrypted or not (boot1 is not encrypted in factory mode).
This stage only executes if NAND engine's initialization was successful.
This stage only executes if NAND engine's initialization was successful.
−
boot0 checks if boot1 is encrypted or not (boot1 is not encrypted in factory mode).
+
// Send debug mark
+
SendGPIODebugOut(0x18);
+
+
// No need to decrypt in factory mode
+
if (!aes_boot1_key)
+
goto skip_boot1_decrypt;
===Stage 0x19===
===Stage 0x19===
+
boot0 verifies boot1's hash (SHA-1) and signature (RSA).
This stage only executes if NAND engine's initialization was successful.
This stage only executes if NAND engine's initialization was successful.
−
boot0 verifies boot1's hash (SHA-1) and signature (RSA).
+
// Send debug mark
+
SendGPIODebugOut(0x19);
+
+
// Hash and verify boot1's image
+
u32 image_blocks = Calc_SHA1_RSA(0, 0x0D400000);
+
+
// Failed to verify
+
if (image_blocks == 0)
+
throw_error();
===Stage 0x1A===
===Stage 0x1A===
+
boot0 decrypts boot1 (using the AES engine) in place.
This stage only executes if NAND engine's initialization was successful.
This stage only executes if NAND engine's initialization was successful.
−
boot0 decrypts boot1 (using the AES engine) in place.
+
// Send debug mark
+
SendGPIODebugOut(0x1A);
+
+
// Decrypt boot1
+
AES_Decrypt(boot1_key, 0x0D400000, image_blocks);
===Stage 0x1B===
===Stage 0x1B===
boot0 reads a flag from SEEPROM to determine how long it should wait before attempting to initialize the SD card host.
boot0 reads a flag from SEEPROM to determine how long it should wait before attempting to initialize the SD card host.
+
// Send debug mark
+
SendGPIODebugOut(0x1B);
+
+
// Delay execution arbitrarily
+
if ((seeprom_1C_00 & 0x7C00) != 0)
+
{
+
u32 time_now = *(u32 *)LT_TIMER;
+
+
// Delay in multiples of 10000 ms
+
u32 seeprom_delay = (u32)(seeprom_1C_00 >> 0x0A) * 0x2710;
+
+
// Calculate delay
+
u32 delay = sub_D412060(seeprom_delay);
+
+
while (time_now < delay)
+
{
+
time_now = *(u32 *)LT_TIMER;
+
delay = sub_D412060(seeprom_delay);
+
}
+
}
===Stage 0x1C===
===Stage 0x1C===
Line 296:
Line 536:
===Stage 0x1D===
===Stage 0x1D===
−
boot0 uses EXI to capture events coming from surface mounted components (SMC). If a special button combo (power + eject) is being held, boot0 will attempt to load a recovery boot1 image from a SD card.
+
boot0 uses EXI to capture events coming from surface mounted components (SMC). If a special button combo is being held (additional hardware may be required, like in the case of kiosk units), boot0 will attempt to load a recovery signed boot1 image from a SD card.
+
// Send debug mark
+
SendGPIODebugOut(0x1D);
+
+
// Request from EXI0
+
u32 result = sub_D410D78(exi0_out_buf);
+
+
// We got a response from EXI0
+
if (result)
+
{
+
u32 exi0_reply = *(u32 *)exi0_out_buf;
+
+
// SD card button combo was pressed
+
if ((exi0_reply << 0x14) < 0)
+
load_sd = true;
+
}
+
+
// No SD card combo, we're done
+
if (!load_sd)
+
goto exit;
===Stage 0x1E===
===Stage 0x1E===
+
boot0 starts by configuring the SDC0S0Power GPIO. It then initializes and configures the SD host controller, flushes AHB memory and loads the recovery image's ancast header from the SD card. It checks the recovery image's size by looking at the size field in it's header (must not exceed 0xF800, so it doesn't overflow boot1's memory region) and then reads in the full image into memory address 0x0D400000 (replacing what was read from the NAND).
This stage only executes if EXI told boot0 to load an image from the SD card.
This stage only executes if EXI told boot0 to load an image from the SD card.
−
boot0 starts by configuring the SDC0S0Power GPIO. It then initializes and configures the SD host controller, flushes AHB memory and loads the recovery image's ancast header from the SD card. It checks the recovery image's size by looking at the size field in it's header (must not exceed 0xF800, so it doesn't overflow boot1's memory region) and then reads in the full image into memory address 0x0D400000 (replacing what was read from the NAND).
+
// Send debug mark
+
SendGPIODebugOut(0x1E);
+
+
// Disable SDC0S0Power interrupts
+
u32 gpio_intmask_val = *(u32 *)LT_GPIO_INTMASK;
+
*(u32 *)LT_GPIO_INTMASK = gpio_intmask_val & 0xBFFFFFFF;
+
+
// Set SDC0S0Power GPIO direction to output
+
u32 gpio_dir_val = *(u32 *)LT_GPIO_DIR;
+
*(u32 *)LT_GPIO_DIR = gpio_dir_val | 0x40000000;
+
+
// Enable SDC0S0Power GPIO
+
u32 gpio_enable_val = *(u32 *)LT_GPIO_ENABLE;
+
*(u32 *)LT_GPIO_ENABLE = gpio_enable_val | 0x40000000;
+
+
// Clear SDC0S0Power GPIO output
+
u32 gpio_out_val = *(u32 *)LT_GPIO_OUT;
+
*(u32 *)LT_GPIO_OUT = gpio_out_val & 0xBFFFFFFF;
+
+
// Delay execution arbitrarily again
+
u32 time_now = *(u32 *)LT_TIMER;
+
+
// Delay in multiples of 20000 ms
+
u32 seeprom_delay = (u32)((seeprom_1C_02 << 0x16) >> 0x1E) * 0x4E20;
+
+
// Calculate delay
+
u32 delay = sub_D412060(seeprom_delay);
+
+
while (time_now < delay)
+
{
+
time_now = *(u32 *)LT_TIMER;
+
delay = sub_D412060(seeprom_delay);
+
}
+
+
// Initialize host controller
+
u32 host_id = 0;
+
u32 reg_offset = 0;
+
u32 result = sub_D411290(host_id, reg_offset, sd_handle_buf);
+
+
// Failed to initialize the controller
+
if (!result)
+
throw_error();
+
+
// Grab the handle returned from initialization
+
u32 sd_handle = *(u32 *)sd_handle_buf;
+
+
// It's possible to specify the clock and switch_func
+
// values for the SD card from flags in the SEEPROM
+
u32 sd_clk = 0;
+
u32 sd_switch_func = 0;
+
+
u8 seeprom_sd_flag1 = (u8)(seeprom_1C_02 << 0x14);
+
u8 seeprom_sd_flag2 = (u8)((seeprom_1C_02 << 0x15) >> 0x1F);
+
+
// Set the SD card's clock value
+
if (seeprom_sd_flag1 > 0)
+
sd_clk = 0x01;
+
else
+
sd_clk = seeprom_sd_flag1 & 0xFF;
+
+
// Set the SD card's switch_func value
+
// This is passed to SD card CMD6 and can be
+
// used to turn high speed on
+
sd_switch_func = seeprom_sd_flag2;
+
+
// Setup the SD card
+
result = sub_D41139C(host_id, reg_offset, sd_handle, sd_clk, sd_switch_func);
+
+
// Failed to setup the SD card
+
if (!result)
+
throw_error();
+
+
// AHB memory flush
+
ahbMemFlush(0);
+
+
// Read SD recovery image's ancast header
+
result = sub_D411544(host_id, reg_offset, 0x0D400000, 0x400);
+
+
// Failed to read the recovery image's ancast header
+
if (!result)
+
throw_error();
+
+
// Copy the last 0x60 bytes of
+
// boot1's ancast image's header
+
memcpy(0x0D413940, 0x0D4001A0, 0x60);
+
+
// Check ancast image's body size
+
u32 ancast_body_size = *(u32 *)0x0D41394C;
+
+
// Calculate boot1's actual size
+
u32 boot1_image_size = ((ancast_body_size + 0x3FF) & 0xFFFFF800) - 0x01;
+
+
// The image is too big
+
if (boot1_image_size > 0xF7FF)
+
throw_error();
+
+
// AHB memory flush
+
ahbMemFlush(0);
+
+
// Read the SD recovery image
+
result = sub_D411544(host_id, reg_offset, 0x0D400000, boot1_image_size);
+
+
// Failed to read the recovery image
+
if (!result)
+
throw_error();
===Stage 0x1F===
===Stage 0x1F===
+
boot0 checks if the recovery image is encrypted or not (it is not encrypted in factory mode).
This stage only executes if EXI told boot0 to load an image from the SD card.
This stage only executes if EXI told boot0 to load an image from the SD card.
−
boot0 checks if the recovery image is encrypted or not (it is not encrypted in factory mode).
+
// Send debug mark
+
SendGPIODebugOut(0x1F);
+
+
// No need to decrypt in factory mode
+
if (!aes_boot1_key)
+
goto skip_boot1_decrypt;
===Stage 0x20===
===Stage 0x20===
+
boot0 verifies the recovery image's hash (SHA-1) and signature (RSA).
This stage only executes if EXI told boot0 to load an image from the SD card.
This stage only executes if EXI told boot0 to load an image from the SD card.
−
boot0 verifies the recovery image's hash (SHA-1) and signature (RSA).
+
// Send debug mark
+
SendGPIODebugOut(0x20);
+
+
// Hash and verify boot1's image
+
u32 image_blocks = Calc_SHA1_RSA(0, 0x0D400000);
+
+
// Failed to verify
+
if (image_blocks == 0)
+
throw_error();
===Stage 0x21===
===Stage 0x21===
+
boot0 decrypts the recovery image (using the AES engine) in place.
This stage only executes if EXI told boot0 to load an image from the SD card.
This stage only executes if EXI told boot0 to load an image from the SD card.
−
boot0 decrypts the recovery image (using the AES engine) in place.
+
// Send debug mark
+
SendGPIODebugOut(0x21);
+
+
// Decrypt boot1
+
AES_Decrypt(boot1_key, 0x0D400000, image_blocks);
===Stages 0x22, 0x23 and 0x24===
===Stages 0x22, 0x23 and 0x24===
Line 358:
Line 742:
==Error codes==
==Error codes==
+
In addition to sending debug markers during execution, boot0 also sends error codes through debug ports using GPIO.
+
// Send the error code
+
SendGPIODebugOut(error_code);
+
+
// Clear boot1 key
+
memset(0x0D41377C, 0, 0x10);
+
+
// Clear SEEPROM key
+
memset(0x0D41376C, 0, 0x10);
+
+
// Lock execution and output error code
+
while (1)
+
{
+
// Output 0x02
+
SendGPIODebugOut(0x02);
+
+
// Wait
+
UDelay(0x7A120);
+
+
// Output time
+
u32 time = *(u32 *)0x0D413768;
+
SendGPIODebugOut(time);
+
+
// Wait
+
UDelay(0x7A120);
+
+
// Output error code
+
u32 gpio_out_val = *(u32 *)LT_GPIO_OUT;
+
SendGPIODebugOut((gpio_out_val << 0x08) >> 0x18);
+
+
// Wait
+
UDelay(0x7A120);
+
}
+
+
{| class="wikitable"
+
|-
+
! Error code
+
! Notes
+
|-
+
| 0xC1
+
| OTP security level flag is 0x40000000
+
|-
+
| 0xC2
+
| OTP security level flag is invalid
+
|-
+
| 0xC3
+
| SEEPROM CRC32 mismatch from data at offset 0x1C0
+
|-
+
| 0xC4
+
| SEEPROM CRC32 mismatch from data at offset 0x1D0 and 0x1E0
+
|-
+
| 0xD1
+
| Failed to read boot1's ancast header from NAND
+
|-
+
| 0xD2
+
| Failed to start SD host controller
+
|-
+
| 0xD3
+
| Failed to setup SD card
+
|-
+
| 0xD4
+
| Failed to read boot1's ancast header from SD card
+
|-
+
| 0xD5
+
| Bad start time during NAND initialization
+
|-
+
| 0xD6
+
| Ancast image size overflow (from NAND)
+
|-
+
| 0xD7
+
| Failed to read boot1 image from NAND
+
|-
+
| 0xD8
+
| Ancast image size overflow (from SD card)
+
|-
+
| 0xD7
+
| Failed to read boot1 image from SD card
+
|-
+
| 0xDA
+
| Failed to initialize NAND engine
+
|}