Bootloader: Difference between revisions

No edit summary
SciresM (talk | contribs)
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 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).
 
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 bit0 to zero.
* 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.
* 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.