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 non-protected areas of the ARM9 and ARM11 boot ROMs are identical for launch-day regular Old3DS, 2DS, and regular New3DS. | + | 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). |
| | | |
| For non-NAND booting, NCSD / FIRM-backup is not used. | | For non-NAND booting, NCSD / FIRM-backup is not used. |
Line 60: |
Line 60: |
| * ... | | * ... |
| * 0xffffd760: size 0x100-bytes: First 0x80-bytes is for retail, the remaining 0x80-bytes at 0xffffd7e0 is for devunit. This 0x80-byte block is copied to 0x07ffcd00 by a Boot9 function, however that code actually does the copy in two 0x40-bytes chunks. | | * 0xffffd760: size 0x100-bytes: First 0x80-bytes is for retail, the remaining 0x80-bytes at 0xffffd7e0 is for devunit. This 0x80-byte block is copied to 0x07ffcd00 by a Boot9 function, however that code actually does the copy in two 0x40-bytes chunks. |
− | * 0xffffd860(end-addr of the above area) size 0x400-bytes: This is the bootrom_dataptr passed to the aes-keyinit function for retail. | + | * 0xffffd860(end-addr of the above area) size 0x400-bytes: This is the bootrom_dataptr passed to the aes-keyinit function for retail. See the below Tools section for how this is processed. |
− | * 0xffffdc60(end-addr of the above area) size 0x400-bytes: This is the bootrom_dataptr passed to the aes-keyinit function for devunit. This is very last chunk of data in the boot9 data-section key-area: end addr for this area is 0xffffe060. | + | * 0xffffdc60(end-addr of the above area) size 0x400-bytes: This is the devunit version of the above the 0x400-byte chunk. This is very last chunk of data in the boot9 data-section key-area: end addr for this area is 0xffffe060. |
| | | |
| Layout of the 0x2600-byte RSA key-data at 0xffffb0e0: | | Layout of the 0x2600-byte RSA key-data at 0xffffb0e0: |
Line 94: |
Line 94: |
| * 0x0001c878..0x0001d078 size 0x800-bytes: These are the 3DS RSA-2048 modulus which are eventually copied to arm9_itcm+0x4900: on retail the first 4 are copied there by boot9, on devunit the last 4 are copied to itcm. | | * 0x0001c878..0x0001d078 size 0x800-bytes: These are the 3DS RSA-2048 modulus which are eventually copied to arm9_itcm+0x4900: on retail the first 4 are copied there by boot9, on devunit the last 4 are copied to itcm. |
| * 0x0001d078 size 0x120-bytes is the initial data for the .data section @ 0x1ffe8000, this is the very end of the protected arm11-bootrom. | | * 0x0001d078 size 0x120-bytes is the initial data for the .data section @ 0x1ffe8000, this is the very end of the protected arm11-bootrom. |
| + | |
| + | == AES keys == |
| + | See the Tools section for how Boot9 initializes the keyslots. |
| + | |
| + | See also [[AES_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. |
| + | |- |
| + | | 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 == |
| + | |
| + | 0xffff0000 jumps to 0xffff8000. 0xffff8000 is crt0: |
| + | * 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. |
| + | * 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. |
| + | * Then L_ffff80cc/mpu_init() is called. |
| + | * Then L_ffff0038() is called, which initializes the exception-handler addresses @ 0x08000000. |
| + | * Then L_ffff81b8() is called(r4 + lr are saved on the DTCM stack), which after calling a memclear function which doesn't do anything, it then clears 0x08000030 size 0x10. Here the DTCM at 0xfff00000 size 0x4000 is cleared. |
| + | * Then L_ffff81b4() is called, which branches to DTCM_init(). This copies the initial DTCM data from the Boot9 data image into boot9, then it clears 0xFFF00230 - 0xFFF01AC0. |
| + | * Then LT_ffff8228/main is jumped to, with LR set to the address of an infinite-branch-loop instruction. |
| + | |
| + | mpu_init(): |
| + | * Bitmask 0x000f9005 is cleared in the cp15 control register. MCR instructions which do then following are then executed: flush entire instruction cache, flush entire data cache, and drain write buffer. |
| + | * Then the 8 [[Memory_layout|MPU]] memregions are initialized. |
| + | * ITCM memregion reg = 0x24: baseaddr=0x0, size = 128MB(0x08000000). |
| + | * DTCM memregion reg = 0xfff0000a: baseaddr=0xfff00000, size=16KB(0x00004000). |
| + | * Then instruction cachable and data cachable/bufferable bits 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. |
| + | |
| + | == 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. |
− | * 2 seconds - ARM9 bootrom attempts to initialize the NAND. | + | **The ARM11 sysmodules included with FIRM are launched by ARM11-kernel, etc. |
− | **If the NAND is successfully initialized:
| + | **The [[Process_Manager_Services|PM]] module launches [[NS]]. |
− | ***the ARM9 bootrom loads the [[FIRM|firmware]] stored in the NAND [[FIRM]] partition which handles booting the rest of the system (if verification for NAND firm0 fails, the ARM9 bootrom will attempt to use firm1 instead).
| |
− | ***The ARM11 kernel loaded from FIRM then launches the [[NCCH#CXI|CXI]] ARM11 system modules loaded from FIRM (i.e. sm, fs, pm, loader, and pxi). (Note that the ARM11 kernel does not handle any encryption/RSA verification, this is handled by the [[FIRM|ARM9]].)
| |
− | **If the NAND cannot be initialized (i.e. the NAND chip is not connected/damaged/etc), a [[Bootloader#Error_Codes|blue error screen]] appears.
| |
− | | |
− | * 3 seconds - all essential hardware is active.
| |
− | **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. |