Bootloader: Difference between revisions
No edit summary |
Fix typo. |
||
(22 intermediate revisions by 5 users not shown) | |||
Line 4: | Line 4: | ||
== Boot ROM == | == Boot ROM == | ||
Upon boot, parts of the ARM9 and ARM11 boot ROMs are protected by writing to [[CONFIG#CFG_SYSPROT9|CFG_SYSPROT9]] and [[CONFIG#CFG_SYSPROT11|CFG_SYSPROT11]], respectively. The | Upon boot, parts of the ARM9 and ARM11 boot ROMs are protected by writing to [[CONFIG#CFG_SYSPROT9|CFG_SYSPROT9]] and [[CONFIG#CFG_SYSPROT11|CFG_SYSPROT11]], respectively. The ARM9 and ARM11 boot ROMs are identical for all Old 3DS, 2DS and New 3DS consoles. | ||
== NAND FIRM boot == | == NAND FIRM boot == | ||
Line 14: | Line 14: | ||
Boot9 can also boot from non-NAND. For this a different set of RSA pubks are used(separate pubks for retail/devunit like NAND). The spiflash FIRM image for this is also encrypted with AES-CBC using a normalkey stored in prot_boot9(separate for retail/devunit). This encryption is basically used instead of what is used for NAND-firm-partitions. This encryption is only used for the FIRM sections, the FIRM header is used raw. The AES keyslot for this is only overwritten afterwards when booting from non-NAND fails. AES keyslot 0x3F is used for this. | Boot9 can also boot from non-NAND. For this a different set of RSA pubks are used(separate pubks for retail/devunit like NAND). The spiflash FIRM image for this is also encrypted with AES-CBC using a normalkey stored in prot_boot9(separate for retail/devunit). This encryption is basically used instead of what is used for NAND-firm-partitions. This encryption is only used for the FIRM sections, the FIRM header is used raw. The AES keyslot for this is only overwritten afterwards when booting from non-NAND fails. AES keyslot 0x3F is used for this. | ||
CTR_word[0] = firmimageoffset; | CTR_word[0] = firmimageoffset;//FIRM section offset from FIRM header | ||
CTR_word[1] = outbufaddr; | CTR_word[1] = outbufaddr;//FIRM section load addr | ||
CTR_word[2] = readsize; | CTR_word[2] = readsize;//FIRM section size | ||
CTR_word[3] = readsize; | CTR_word[3] = readsize;//FIRM section size | ||
When booting from NAND fails, boot9 will then attempt to boot from Wifi SPI-flash(this only triggers when the wifi module hw is properly accessible/connected, which is normally the case). The base offset for spiflash FIRM is 0x400. Note that this region is write-protected by the spiflash. | When booting from NAND fails, boot9 will then attempt to boot from Wifi SPI-flash(this only triggers when the wifi module hw is properly accessible/connected, which is normally the case). The base offset for spiflash FIRM is 0x400. Note that this region(all data prior to offset 0x1F300) is write-protected by the spiflash(not writable from 3DS-mode / DS-mode). | ||
Additionally, if the shell is closed and a special key combination (Start + Select + X) is held, boot9 will attempt to boot from an inserted NTR cartridge before booting from NAND. Note: While normally on O3DS/2DS the console will not turn on if the shell is closed (or this is faked by holding a magnet to the console), when this special key combination is held holding down the power button will cause boot to occur anyway. | |||
For non-NAND booting, NCSD / FIRM-backup is not used. | For non-NAND booting, NCSD / FIRM-backup is not used. | ||
Line 25: | Line 27: | ||
== SDMMC == | == SDMMC == | ||
Boot9 has code implemented for using SD(HC) cards, but the input deviceids used by boot9 for those functions are hard-coded for NAND. | Boot9 has code implemented for using SD(HC) cards, but the input deviceids used by boot9 for those functions are hard-coded for NAND. However, it is possible to use an SD(HC) card in place of the NAND if the NAND chip is first disconnected, and a SD card connected to the bus. Due to the CID being different, partitions will need to be re-encrypted and TWL mode will not work, due to the MBR being in the NCSD header. Using sighax, it may be possible to replace the NCSD header. | ||
== Boot9 RSA keyslots == | == Boot9 RSA keyslots == | ||
Line 101: | Line 103: | ||
For an issue with console-unique key-init, see [[OTP_Registers|here]]. | For an issue with console-unique key-init, see [[OTP_Registers|here]]. | ||
== BootROM Errors == | |||
Sample error-screen(where firm0+firm1 RSA signatures were corrupted): | |||
BOOTROM 8046 | |||
ERRCODE: 00F800FF | |||
DEDEFFFF FFFFFFFF | |||
00000000 00000000 | |||
* 1st line is: <code>print_string(..., "BOOTROM %X", 0x8046);//This last param comes from the .pool.</code> | |||
* 2nd line is: <code>print_string(..., "ERRCODE: %08X", *((unsigned int*)(0x1FFFE000+0xC)));//See below memory notes.</code> | |||
* 3rd line is: <code>print_string(..., "%08X %08X", *((unsigned int*)(0x1FFFE000+0x10))`, `*((unsigned int*)(0x1fffe000+0x14)));//See below memory notes.</code> | |||
* 4th line is: <code>print_string(..., "%08X %08X",*((unsigned int*)(0x1FFFE000+0x18))`, `*((unsigned int*)(0x1fffe000+0x1C)));//See below memory notes.</code> | |||
== 0x1FFFE000 memory == | |||
This memory is used by boot9 mainly for sending info to the arm11 for the error-screen. The data in this region is still stored in memory by the time the ARM9+ARM11 jumps to FIRM. | |||
Among boot9/boot11, the 3 words at 0x1FFFE000 seem to be ''only'' accessed by the boot11 function initializing those words. | |||
* u32 0x1FFFE000+0: ARM11 MPCore "Cycle Counter Register (CCNT)". | |||
* u32 0x1FFFE000+4: ARM11 MPCore "Count Register 0 (PMN0)". | |||
* u32 0x1FFFE000+8: ARM11 MPCore "Count Register 1 (PMN0)". | |||
* 8bit-entry-array 0x1FFFE000+0xC: 8bit status-codes initialized by boot9 main(), for the FIRM-boot devices. +0 is NAND and +2 is wifi-spiflash. | |||
* ... | |||
* 8bit-entry-array 0x1FFFE000+0x10: Status-codes originally from nand_findfirmpartition_loadfirm(), for each of the 8 NCSD partitions. | |||
== BootROM Status Codes == | |||
{| class="wikitable" border="1" | |||
|- | |||
! Value | |||
! Description | |||
|- | |||
| 0x00 | |||
| Success | |||
|- | |||
| 0xEE(~17) | |||
| NCSD header validation function failed: NCSD magicnum is invalid or RSA verification failed. | |||
|- | |||
| 0xDE(~33) | |||
| FIRM header validation function failed: FIRM magicnum is invalid or RSA verification failed. | |||
|- | |||
| 0xDF(~32) | |||
| Failed to read sector data from the device. | |||
|- | |||
| 0xCF(~48) | |||
| FIRM section validation function failed: FIRM section is invalid. | |||
|- | |||
| 0xF7(~8) | |||
| A NAND FIRM from another partition was already found with a priority(firmhdr+4) >= to the value for the current partition's FIRM priority. | |||
|- | |||
| 0xF8(~7) | |||
| The FIRM magicnum(firmhdr+0) is invalid. | |||
|- | |||
| 0xFF(~0) | |||
| Initial value for each entry in the 8-entry array of status-codes for the NAND NCSD partitions. Indicates that the partition is not a FIRM partition(partition fs type isn't 0x3 or partition fs crypt-type isn't 0x2). | |||
|} | |||
== Boot9 startup == | == Boot9 startup == | ||
0xffff0000 jumps to 0xffff8000. 0xffff8000 is crt0: | 0xffff0000 jumps to 0xffff8000. 0xffff8000 is crt0: | ||
* Very first thing this does is clear u8 register 0x10000002 | * Very first thing this does is clear u8 register 0x10000002 ([[CONFIG_Registers#CFG_RST11|CFG_RST11]]) bit 0 to zero. | ||
* Then sp is initialized for each cpumode, IRQs/FIQs are disabled during the first mode-switch. | * Then sp is initialized for each cpumode, IRQs/FIQs are disabled during the first mode-switch. | ||
* Order of mode-switches + sp initialization: svc-mode = 0xfff04000, irq-mode = 0xfff03f00, system-mode = 0xfff03b00. Hence, the rest of the code following this runs in system-mode. | * Order of mode-switches + sp initialization: svc-mode = 0xfff04000, irq-mode = 0xfff03f00, system-mode = 0xfff03b00. Hence, the rest of the code following this runs in system-mode. | ||
Line 122: | Line 180: | ||
* Then the instruction/data access permissions for the MPU regions are setup. | * Then the instruction/data access permissions for the MPU regions are setup. | ||
* Lastly bitmask 0x0005707d is orred in the cp15 control register. | * Lastly bitmask 0x0005707d is orred in the cp15 control register. | ||
== Boot9 main() == | |||
The following functions are called: LT_ffff2024(), LT_ffff1ff8(), pxi_init(), rsa_init(), initialize_rsakeyslots_pubk(), crypto_initialize(), and aesengine_reset(). | |||
Then AES keyslot 0x3F is setup: aesengine_setnormalkey(0x3f, 5, ptr) is called. ptr on retail(CFG_UNITINFO check) is 0xffffd6e0, 0xffffd700 for devunit. Then essentially, aesengine_setctr(5, ptr+0x10) is executed. | |||
Then AES keyslot 0x3f is selected. | |||
When calling the following functions, if any of them return zero, it will immediately jump to setting ptr to 0x10012000(otp), otherwise when all of them return non-zero ptr = sp+0x94. otp_decrypt(sp+4), otp_verify(sp+4), initialize_consoleunique_itcm(sp+4, 0x07ffb800). | |||
Then the following is executed: initialize_aeskeys_wrap(ptr, 0x70); | |||
Then sp+4 size 0x100 is cleared to zero. | |||
... | |||
NAND firm-boot code-block, is described below. Note that boot9 is basically hard-coded to use deviceid NAND, not SD. | |||
{ | |||
timer_updatestoredstate() is called, then the AES keyslot for NAND-FIRM is selected(0x6). | |||
Then LT_ffff56c8() is called, if that returns non-zero the statuscode variable is set to ~2 then it jumps to NAND_BOOTEND. | |||
Then LT_ffff5774(0x201) is called, if that returns non-zero the statuscode variable is set to ~1 then it jumps to NAND_BOOTEND. | |||
Then fsdriver_setup_mmc() is called. Then nand_findfirmpartition_loadfirm(0) is called, with the statuscode variable set to the retval. | |||
Executes a loop which runs 8 times: write the output from get_errorcode_arrayentry_xfff005e8(loopindex) to u8 0x1fffe000+0x10+loopindex(copy the array of 32bit error-codes for all 8 NCSD partitions initialized by nand_findfirmpartition_loadfirm() to the array of 8bit entries at 0x1fffe000+0x10). | |||
NAND_BOOTEND: | |||
Then the statuscode variable is written to u8 0x1fffe000+0xc. | |||
Then LT_ffff5690(0x201, 0x1fffe018, 0x1fffe01c) is called. | |||
Then LT_ffff5644() is called. | |||
Then timer_updatestoredstate() is called. | |||
When statuscode==0 for success, it jumps to FIRMLOAD_END. Otherwise, it continues to the next code-block. | |||
} | |||
Wifi spi-flash firm-boot code-block, executed when no FIRM was loaded successfully so far. | |||
{ | |||
timer_updatestoredstate() is called. | |||
Then spi_wififlash_cmdgetstatusreg(sp+0x100) is executed. When bit0 of the output u8 at sp+0x100 is clear, it will continue this code-block, otherwise it will set the statuscode variable to ~1 then jump to SPIFLASH_BOOTEND. | |||
Then fsdriver_setup_wififlash() is called. | |||
Here read_firmhdr_validate_loadfirm(0, 2) is called, with the statuscode variable set to the retval. | |||
SPIFLASH_BOOTEND: | |||
Then the statuscode variable is written to u8 0x1fffe000+0xe. | |||
Then timer_updatestoredstate() is called. | |||
When statuscode==0 for success, it jumps to FIRMLOAD_END. Otherwise, it executes writenormalkey_keyslot3f(), then jumps to FIRMLOAD_FAILURE. | |||
} | |||
FIRMLOAD_END: | |||
Here it calls firmhdr_getarm11_entrypoint() and firmhdr_getarm9_entrypoint(). Immediately after calling each function it checks if the retval is 0, if so it then jumps to FIRMLOAD_FAILURE. | |||
After calling initialize_x07ffbd00_x07ffc100_rsakeyslotsprivk(), it jumps to FIRMLOAD_EXIT. | |||
FIRMLOAD_FAILURE: | |||
Here it clears 0x07ffb800 size 0x3c70 to zero, endaddr = 0x07fff470. | |||
Then it continues to FIRMLOAD_EXIT. | |||
FIRMLOAD_EXIT: | |||
Here firmboot() is called, which should never return. The instruction after this bl is a call for panic(). | |||
== Boot11 == | |||
* ... | |||
main(): | |||
LT_1263c(); | |||
... | |||
LT_13944() | |||
... | |||
pxi_init(); | |||
initializefuncptr_firmboot_start(firmbootbegin_funcptr); | |||
firmboot(); | |||
return; | |||
LT_12220/initializefuncptr_firmboot_start | |||
inr0=funcptr | |||
This writes inr0 to address 0x1ffe8028, then returns. | |||
This initializes the funcptr which firmboot() can call after the very first func-call. | |||
LT_13944 | |||
if([[I2C_Registers|i2cmcu_readregf]](sp+0)==0) | |||
{ | |||
return (*((u8*)0x10147000) >> 4) & 1;//Reads [[GPIO_Registers|GPIO]] when reading I2C fails. | |||
} | |||
Here it basically does "return <byte loaded from sp+0> ^ 0x2". Hence in this case, it will return 0x2 when the system shell is closed(sleep-mode), otherwise 0x0 is returned. | |||
LT_12454/firmboot | |||
This is the arm11 version of the boot9 firmboot() function, like boot9 this is the final function called from main(). The functionality for these two functions are identical, minus addresses. | |||
ptr = firmboot_loadentrypoint11(); | |||
funcptr = *(0x1ffe8028); | |||
if(funcptr)funcptr(ptr); | |||
LT_11ffc(ptr); | |||
return; | |||
== Boot Procedure == | == Boot Procedure == | ||
* 0 seconds - unit is powered on. The ARM9 and ARM11 [[Memory_layout|bootroms]] begin execution. | * 0 seconds - unit is powered on. The ARM9 and ARM11 [[Memory_layout|bootroms]] begin execution. | ||
* <= ~1 second - BootROMs fully run, load FIRM, etc. The loaded FIRM begins running. | |||
* | **The ARM11 sysmodules included with FIRM are launched by ARM11-kernel, etc. | ||
**The [[Process_Manager_Services|PM]] module launches [[NS]]. | |||
**The [[Process_Manager_Services|PM]] module launches [[NS]] | |||
**If [[Home_Menu#Auto-Boot_Function|auto-booting]] is needed, NS will [[NS#Auto-boot|auto-boot]] titles. | **If [[Home_Menu#Auto-Boot_Function|auto-booting]] is needed, NS will [[NS#Auto-boot|auto-boot]] titles. | ||
**Otherwise, NS will instead launch [[ErrDisp]] and the [[Configuration Memory#ACTIVEMENUTID|current active menu]] via the PM module. For retail units, this menu is usually the [[Home Menu]]. Note that the PM module first launches the module dependencies when launching a process, prior to actually launching the process. | **Otherwise, NS will instead launch [[ErrDisp]] and the [[Configuration Memory#ACTIVEMENUTID|current active menu]] via the PM module. For retail units, this menu is usually the [[Home Menu]]. Note that the PM module first launches the module dependencies when launching a process, prior to actually launching the process. | ||
**The further Home Menu startup process is described [[Home_Menu#Home_Menu_startup|here]]. | **The further Home Menu startup process is described [[Home_Menu#Home_Menu_startup|here]]. This includes Home Menu manually launching various sysmodules. | ||
* 4 seconds - the LCD screens are initialized. | * 4 seconds - the LCD screens are initialized. |