Difference between revisions of "FIRM"
Line 74: | Line 74: | ||
* On [[9.5.0-22|9.5.0-X]]: executes a nop instruction with r0=0 and r1=<address of arm9binhdr+0x50>. | * On [[9.5.0-22|9.5.0-X]]: executes a nop instruction with r0=0 and r1=<address of arm9binhdr+0x50>. | ||
* Hashes data from the region [[IO|0x10012000-0x10012090]] using SHA256 via the [[SHA_Registers|SHA]] hardware. | * Hashes data from the region [[IO|0x10012000-0x10012090]] using SHA256 via the [[SHA_Registers|SHA]] hardware. | ||
− | * Initializes AES keyslot 0x11 keyX, keyY to the lower and higher portion of that hash, respectively. | + | * Initializes AES keyslot 0x11 keyX, keyY to the lower and higher portion of that hash, respectively. Due to the above hashed data, the keyX+keyY here are console-unique. |
− | * Decrypts | + | * Reads [[Flash_Filesystem|NAND]] sector 0x96(NAND image offset 0x12C00), with size 0x200-bytes. Decrypts the first 0x10-byte block in that sector with keyslot 0x11 using AES-ECB. Then the normalkey, keyX, and keyY, for keyslot 0x11 are cleared to zero. Then it uses the output block to set the normalkey for keyslot 0x11. |
* Decrypts arm9_bin_buf+0 using keyslot 0x11 with AES-ECB, and initialises keyX for keyslot 0x15 with it. | * Decrypts arm9_bin_buf+0 using keyslot 0x11 with AES-ECB, and initialises keyX for keyslot 0x15 with it. | ||
* Initialises KeyX for keyslots 0x18..0x1F with the output of decrypting a 0x10-byte block with AES-ECB using keyslot 0x11. The last byte in this 0x10-byte input block is increased by 0x01 after initializing each keyslot. These are New3DS-specific keys. | * Initialises KeyX for keyslots 0x18..0x1F with the output of decrypting a 0x10-byte block with AES-ECB using keyslot 0x11. The last byte in this 0x10-byte input block is increased by 0x01 after initializing each keyslot. These are New3DS-specific keys. |
Revision as of 05:33, 7 February 2015
This page describes the file format for the 3DS' Firmware, it contains four 'sections' of ARM code (ARM9 and ARM11). The firmware sections are not encrypted in the FIRM format.
The ARM9 section contains the ARM9 kernel and the ARM9 process(exheader process name is "Process9"). The ARM11 section(s) contains the ARM11 kernel, and the ARM11 process(es). For NATIVE_FIRM/SAFE_MODE_FIRM these ARM11 processes are sm, fs, pm, loader, and pxi. Normally the 4th section is not used. The code loaded from FIRM is constantly running on the system until another FIRM is launched. The ARM11 kernel is hard-coded to always decompress the FIRM ARM11 modules ExeFS .code, the exheader compression bit is not checked.
FIRM Header
OFFSET | SIZE | DESCRIPTION |
---|---|---|
0x000 | 4 | Magic 'FIRM' |
0x004 | 4 | Reserved1 |
0x008 | 4 | ARM11 Entrypoint |
0x00C | 4 | ARM9 Entrypoint |
0x010 | 0x030 | Reserved2 |
0x040 | 0x0C0 (0x030*4) | Firmware Section Headers |
0x100 | 0x100 | RSA-2048 signature of the FIRM header, using SHA-256. |
Firmware Section Headers
OFFSET | SIZE | DESCRIPTION |
---|---|---|
0x000 | 4 | Offset |
0x004 | 4 | Address |
0x008 | 4 | Size |
0x00C | 4 | Firmware Type ('0'=ARM9/'1'=ARM11) |
0x010 | 0x020 | SHA-256 Hash of Firmware Section |
New_3DS FIRM
For New3DS firmwares (NATIVE_FIRM, TWL_FIRM, ..), the ARM9 FIRM binary has an additional layer of crypto. At the end of each ARM9 binary, there's a plaintext loader. The format of the FIRM header is identical to regular 3DS FIRM(the RSA modulo is the same as regular 3DS too).
If (u8*)0x10000000 bit 1 is clear (which means that this happens only on hard reboots), it does the following things:
- On 9.5.0-X: executes a nop instruction with r0=0 and r1=<address of arm9binhdr+0x50>.
- Hashes data from the region 0x10012000-0x10012090 using SHA256 via the SHA hardware.
- Initializes AES keyslot 0x11 keyX, keyY to the lower and higher portion of that hash, respectively. Due to the above hashed data, the keyX+keyY here are console-unique.
- Reads NAND sector 0x96(NAND image offset 0x12C00), with size 0x200-bytes. Decrypts the first 0x10-byte block in that sector with keyslot 0x11 using AES-ECB. Then the normalkey, keyX, and keyY, for keyslot 0x11 are cleared to zero. Then it uses the output block to set the normalkey for keyslot 0x11.
- Decrypts arm9_bin_buf+0 using keyslot 0x11 with AES-ECB, and initialises keyX for keyslot 0x15 with it.
- Initialises KeyX for keyslots 0x18..0x1F with the output of decrypting a 0x10-byte block with AES-ECB using keyslot 0x11. The last byte in this 0x10-byte input block is increased by 0x01 after initializing each keyslot. These are New3DS-specific keys.
- 9.5.0-X: Decrypts the 0x10-byte block at arm9binhdr+0x60 with AES-ECB using keyslot 0x11, then sets the keyX for keyslot 0x16 to the output data. The normalkey, keyX, and keyY, for keyslot 0x11 are cleared to zero.
When (u8*)0x10000000 bit 1 is set(which means this happens only when this loader runs again for firm-launch), the normalkey, keyX, and keyY, for keyslot 0x11 are cleared to zero.
It sets KeyY for keyslot 0x15(0x16 with 9.5.0-X) to arm9_bin_buf+16, the CTR to arm9_bin_buf+32 (both are unique for every version). It then proceeds to decrypt the binary with AES-CTR. When done, it decrypts arm9_bin_buf+64 using an hardcoded keyY for keyslot 0x15(0x16 with 9.5.0-X) and makes sure it's all zeroes. If it is, it does some cleanup then it jumps to the entrypoint for the decrypted binary. Otherwise it will just loop forever.
Thus, the ARM9 binary has the following header:
OFFSET | SIZE | DESCRIPTION |
---|---|---|
0x000 | 16 | Encrypted KeyX (same for all FIRM's) |
0x010 | 16 | KeyY |
0x020 | 16 | CTR |
0x030 | 16 | ? |
0x040 | 16 | Control block |
0x050 | 16 | Added with 9.5.0-X. Only used for hardware debugging: a nop instruction is executed with r0=0 and r1=<address of this data>. |
0x060 | 16 | Added with 9.5.0-X. Encrypted keyX for keyslot 0x16. |
Originally the padding after the header before offset 0x800(start of actual ARM9-binary) was 0xFF bytes, with 9.5.0-X this was changed to 0x0.
For the New3DS NATIVE_FIRM arm9-section header, the only difference between the 8.1.0-0_New3DS version and the 9.0.0-20 version is that the keyY, CTR, and the block at 0x30 in the header were updated.
New3DS ARM9 binary loader versions
FIRM system version(s) | Description |
---|---|
8.1.0-0_New3DS - 9.3.0-21 | Initial version. |
9.5.0-22 | Added keyX initialization for keyslot 0x16(see above), and added code for clearing keyslot 0x11 immediately after the code finishes using keyslot 0x11. The keyslot used for arm9bin decryption and the control-block were changed from 0x15 to 0x16. Added code for clearing keyslot 0x16 when control-block decryption fails. Added code for using arm9bin_hdr+0x50 with a nop instruction, at the very beginning of the main arm9-loader function. Added two new 0x10-blocks to the arm9bin-hdr. |
NATIVE_FIRM and SAFE_MODE_FIRM
NATIVE_FIRM is the FIRM which is installed to the NAND firm partitions, which is loaded by bootrom. SAFE_MODE_FIRM and NATIVE_FIRM for the initial versions are exactly the same, except for the system core version fields. SAFE_MODE is used for running the System Updater.
An overview of NATIVE_FIRM versions along with their contentID is given in Configuration Memory.
TWL_FIRM and AGB_FIRM
TWL_FIRM handles DS(i) backwards compatibility, while AGB_FIRM handles running GBA VC titles. The ARM9 FIRM section for TWL_FIRM and AGB_FIRM are exactly the same(for TWL_FIRM and AGB_FIRM versions which were updated with the same system-update).
TWL_FIRM
The 3DS-mode ARM9 core seems to switch into DSi-mode(for running DSi-mode ARM9 code) by writing to a PDN register(this changes the memory layout to DSi-mode / etc, therefore this register poke *must* be executed from ITCM). This is the final 3DS-mode register poke before the ARM9 switches into DSi-mode. It's unknown how exactly DS(i)-mode ARM7 code is run. Trying to read from the exception-vector region(address 0x0) under this DSi-mode ARM7 seems to only return 0x00/0xFF data. Also note that this DSi-mode ARM7 runs code(stored in TWL_FIRM) which pokes some DSi-mode registers that on the DSi were used for disabling access to the DSi bootROMs, however these registers do not affect the 3DS DSi-mode ARM9/ARM7 "bootrom" region(exceptionvector region + 0x8000) at all.
For shutting down the system(?), TWL_FIRM writes u8 value 8 to I2C MCU register 0x20. For returning to 3DS-mode, TWL_FIRM writes value 4 to that MCU register to trigger a hardware system reboot.
FIRM Launch Parameters
The FIRM-launch parameters structure is located at FCRAM+0, size 0x1000-bytes. The ARM11-kernel copies this structure elsewhere, then clears the 0x1000-bytes at FCRAM+0.
OFFSET | SIZE | DESCRIPTION |
---|---|---|
0x3C | 0x4 | CRC32, this is calculated starting at FIRM-params offset 0x0, with size 0x140(with this field cleared to zero during calculation). When invalid the kernel clears the entire buffer used for storing the FIRM-params, therefore no actual FIRM-params are handled after that. |
0x400 | 0x4 | Flags |
0x410 | 0xC | This is used for overriding the FIRM_* fields in Configuration_Memory, when the flag listed below is set, in the following order(basically just data-copy from here to 0x1FF80060): "FIRM_?", FIRM_VERSIONREVISION, FIRM_VERSIONMINOR, FIRM_VERSIONMAJOR, FIRM_SYSCOREVER, and FIRM_CTRSDKVERSION. |
0x438 | 0x4 | The kernel checks this field for value 0xFFFF, if it matches the kernel uses the rest of these parameter fields, otherwise FIRM-launch parameters fields are ignored by the kernel. |
0x440 | 0x10 | Titleinfo structure, used by NS during NS startup, to launch the specified title when the below flag is set. |
0x450 | 0x10 | Titleinfo structure. This might be used for returning to the specified title, once the above launched title terminates? |
0x460 | 0x4 | Bit0: 0 = titleinfo structure isn't set, 1 = titleinfo structure is set. |
0x480 | 0x20 | This can be set via buf1 for APT:SendDeliverArg/APT:StartApplication. |
0x4A0 | 0x10 | This can be set by NSS:SetFIRMParams4A0. |
0x4B0 | 0x10 | This can be set by NSS:SetFIRMParams4B0. |
0x500 | 0x40 | This is used by APT:LoadSysMenuArg and APT:StoreSysMenuArg. |
Flags from offset 0x400:
OFFSET | SIZE | DESCRIPTION |
---|---|---|
0x0 | 0x1 | This can be used for overriding the default FCRAM memory-regions allocation sizes(APPLICATION, SYSTEM, and BASE). The values for this is the same as Configmem-APPMEMTYPE. Values 0-1 are handled the same way by the kernel. However for NS, 0=titleinfo structure for launching a title isn't set, while non-zero=titleinfo structure is set. |
0x1 | 0x3 | Setting bit0 here enables overriding the FIRM_* fields in Configuration_Memory. |