Package1: Difference between revisions
No edit summary  | 
				|||
| (20 intermediate revisions by 6 users not shown) | |||
| Line 1: | Line 1: | ||
Present   | Present in the firmware package titles (0100000000000819, 010000000000081A, 010000000000081B and 010000000000081C) and installed into eMMC storage's [[Flash_Filesystem#Boot_Partitions|boot partitions 0 and 1]], "package1" contains the first Switch bootloader ("Package1ldr") to run under the NVIDIA boot processor (an ARM7TDMI called "BPMP", "BPMP-Lite", "AVP" or "COP"), as well as the actual encrypted package1 ("PK11") blob containing the second Switch Bootloader and TrustZone code.  | ||
The boot ROM validates, copies to IRAM and executes this package by parsing it's information block from the [[  | The boot ROM validates, copies to IRAM and executes this package by parsing it's information block from the [[BCT|BCT]].  | ||
=   | = Format =  | ||
== Erista ==  | |||
This package is distributed as a plaintext initial bootloader (package1ldr) and a secondary encrypted blob ("PK11"). Execution starts at plaintext package1ldr which will set up hardware, generate keys and decrypt the next stage.  | |||
==   | === Header ===  | ||
{| class="wikitable" border="1"  | |||
|-  | |||
! Offset  | |||
! Size  | |||
! Description  | |||
|-  | |||
| 0x0  | |||
| 0x4  | |||
| Package1ldr hash (first four bytes of SHA256(package1ldr))  | |||
|-  | |||
| 0x4  | |||
| 0x4  | |||
| Secure Monitor hash (first four bytes of SHA256(secure_monitor))  | |||
|-  | |||
| 0x8  | |||
| 0x4  | |||
| NX Bootloader hash (first four bytes of SHA256(nx_bootloader))  | |||
|-  | |||
| 0xC  | |||
| 0x4  | |||
| Build ID  | |||
|-  | |||
| 0x10  | |||
| 0xE  | |||
| Build Timestamp (yyyyMMddHHmmss)  | |||
|-  | |||
| 0x1E  | |||
| 0x2  | |||
| Version  | |||
|}  | |||
=== Initialization ===  | === Package1ldr ===  | ||
The code for this stage is stored in plaintext inside the package. By looking into the BCT's bootloader0_info (normal) or bootloader1_info (safe mode), the boot ROM starts executing this stage at address 0x40010020 in IRAM (0x40010040 for 4.0.0+).  | |||
==== Initialization ====  | |||
The stack pointer is set.  | The stack pointer is set.  | ||
<syntaxhighlight lang="c">  | |||
  // Set the stack pointer  |   // Set the stack pointer  | ||
  *(u32 *)sp = 0x40008000;  |   *(u32 *)sp = 0x40008000;  | ||
| Line 20: | Line 54: | ||
  // Infinite loop  |   // Infinite loop  | ||
  deadlock();  |   deadlock();  | ||
</syntaxhighlight>  | |||
=== Main ===  | ==== Main ====  | ||
The bootloader poisons the exception vectors, cleans up memory (.bss and init_array), sets up hardware devices (including the security engine and fuses), does all the necessary checks, generates keys and finally decrypts and executes the next stage.  | The bootloader poisons the exception vectors, cleans up memory (.bss and init_array), sets up hardware devices (including the security engine and fuses), does all the necessary checks, generates keys and finally decrypts and executes the next stage.  | ||
<syntaxhighlight lang="c">  | |||
  // Poison all exception vectors  |   // Poison all exception vectors  | ||
  *(u32 *)0x6000F200 = panic();  |   *(u32 *)0x6000F200 = panic();  | ||
| Line 60: | Line 96: | ||
  // Setup I2S1, I2S2, I2S3, I2S4, DISPLAY and VIC  |   // Setup I2S1, I2S2, I2S3, I2S4, DISPLAY and VIC  | ||
  mbist_workaround();  | |||
  // Program the SE clock and resets  |   // Program the SE clock and resets  | ||
| Line 100: | Line 136: | ||
  keygen(bct_iram_addr);  |   keygen(bct_iram_addr);  | ||
  u32   |   u32 pk11_blob_addr = 0x40013FE0;  | ||
  // Decrypt next stage  |   // Decrypt the PK11 blob and get the next stage's entrypoint  | ||
  nx_boot_addr = decrypt_pk11_blob(pk11_blob_addr);  | |||
  u32   |   u32 nx_boot_sp = 0x40007000;  | ||
  // Set the stack pointer and jump to a stub responsible  |   // Set the stack pointer and jump to a stub responsible  | ||
  // for cleaning up and branching into the   |   // for cleaning up and branching into the next stage  | ||
  exec_nx_boot_stub(nx_boot_addr, nx_boot_stub_addr, nx_boot_sp);  | |||
  return;  |   return;  | ||
</syntaxhighlight>  | |||
==== Panic ====  | [6.2.0+] The bootloader maintains most of its design, but passes execution to a [[TSEC]] payload and is left in an infinite loop.  | ||
<syntaxhighlight lang="c">  | |||
 // Poison all exception vectors  | |||
 *(u32 *)0x6000F200 = panic();  | |||
 *(u32 *)0x6000F204 = panic();  | |||
 *(u32 *)0x6000F208 = panic();  | |||
 *(u32 *)0x6000F20C = panic();  | |||
 *(u32 *)0x6000F210 = panic();  | |||
 *(u32 *)0x6000F214 = panic();  | |||
 *(u32 *)0x6000F218 = panic();  | |||
 *(u32 *)0x6000F21C = panic();  | |||
 u32 bss_addr_end = bss_addr_start;  | |||
 u32 bss_offset = 0;  | |||
 u32 bss_size = bss_addr_end - bss_addr_start;  | |||
 // Clear .bss region  | |||
 // Never happens due to bss_size being set to 0  | |||
 while (bss_offset < bss_size)  | |||
 {  | |||
    *(u32 *)bss_addr_start + bss_offset = 0;  | |||
    bss_offset += 0x04;  | |||
 }  | |||
 u32 init_array_addr_end = init_array_addr_start;  | |||
 u32 init_array_offset = init_array_addr_start;  | |||
 // Call init methods  | |||
 // Never happens due to init_array_addr_end being set to init_array_addr_start  | |||
 while (init_array_offset < init_array_addr_end)  | |||
 {  | |||
    u32 init_method_offset = *(u32 *)init_array_offset;  | |||
    call_init_method(init_method_offset + init_array_offset);  | |||
    init_array_offset += 0x04;  | |||
 }  | |||
 // Setup I2S1, I2S2, I2S3, I2S4, DISPLAY and VIC  | |||
 mbist_workaround();  | |||
 // Program the SE clock and resets  | |||
 // Uses RST_DEVICES_V, CLK_OUT_ENB_V, CLK_SOURCE_SE and CLK_V_SE  | |||
 enable_se_clkrst();  | |||
 // Set MISC_CLK_ENB  | |||
 // This makes fuse registers visible  | |||
 enable_misc_clk(0x01);  | |||
 // Setup the security engine's address  | |||
 set_se_addr(0x70012000);  | |||
 // Check SE global config  | |||
 check_se_status();  | |||
 // Read FUSE_SKU_INFO and compare with 0x83  | |||
 check_sku();  | |||
 // Check configuration fuses  | |||
 check_config_fuses();  | |||
 u32 bct_iram_addr = 0x40000000;  | |||
 // Check bootloader version from BCT  | |||
 check_bootloader_ver(bct_iram_addr);  | |||
 // Check anti-downgrade fuses  | |||
 check_downgrade();  | |||
 // Setup memory controllers  | |||
 enable_mem_ctl();  | |||
 // Clear SYS_CLK_DIVISOR  | |||
 *(u32 *)CLK_SOURCE_SYS = 0;  | |||
 // Place I2C5 in reset  | |||
 u32 rst_dev_h_val = *(u32 *)RST_DEVICES_H;  | |||
 rst_dev_h_val &= ~(0x8000);  | |||
 rst_dev_h_val |= 0x8000;  | |||
 *(u32 *)RST_DEVICES_H = rst_dev_h_val;  | |||
 // Program the HOST1X clock and resets  | |||
 // Uses RST_DEVICES_L, CLK_OUT_ENB_L, CLK_SOURCE_HOST1X and CLK_L_HOST1X  | |||
 enable_host1x_clkrst();  | |||
 // Program the TSEC clock and resets  | |||
 // Uses RST_DEVICES_U, CLK_OUT_ENB_U, CLK_SOURCE_TSEC and CLK_U_TSEC  | |||
 enable_tsec_clkrst();  | |||
 // Program the SOR_SAFE clock and resets  | |||
 // Uses RST_DEVICES_Y, CLK_OUT_ENB_Y and CLK_Y_SOR_SAFE  | |||
 enable_sor_safe_clkrst();  | |||
 // Program the SOR0 clock and resets  | |||
 // Uses RST_DEVICES_X, CLK_OUT_ENB_X and CLK_X_SOR0  | |||
 enable_sor0_clkrst();  | |||
 // Program the SOR1 clock and resets  | |||
 // Uses RST_DEVICES_X, CLK_OUT_ENB_X, CLK_SOURCE_SOR1 and CLK_X_SOR1  | |||
 enable_sor1_clkrst();  | |||
 // Program the KFUSE clock resets  | |||
 // Uses RST_DEVICES_H, CLK_OUT_ENB_H and CLK_H_KFUSE  | |||
 enable_kfuse_clkrst();  | |||
 // Clear the Falcon DMA control register  | |||
 *(u32 *)FALCON_DMACTL = 0;  | |||
 // Enable Falcon IRQs  | |||
 *(u32 *)FALCON_IRQMSET = 0xFFF2;  | |||
 // Enable Falcon IRQs  | |||
 *(u32 *)FALCON_IRQDEST = 0xFFF0;  | |||
 // Enable Falcon interfaces  | |||
 *(u32 *)FALCON_ITFEN = 0x03;  | |||
 // Wait for Falcon's DMA engine to be idle  | |||
 wait_flcn_dma_idle();  | |||
 // Set DMA transfer base address to 0x40010E00>> 0x08  | |||
 *(u32 *)FALCON_DMATRFBASE = 0x40010E;  | |||
 u32 trf_mode = 0;     // A value of 0 sets FALCON_DMATRFCMD_IMEM  | |||
 u32 dst_offset = 0;  | |||
 u32 src_offset = 0;  | |||
 // Load code into Falcon (0x100 bytes at a time)  | |||
 while (src_offset < 0x2900)  | |||
 {  | |||
    flcn_load_firm(trf_mode, src_offset, dst_offset);  | |||
    src_offset += 0x100;  | |||
    dst_offset += 0x100;  | |||
 }  | |||
 // Set magic value in host1x scratch space  | |||
 *(u32 *)0x50003300 = 0x34C2E1DA;  | |||
 // Clear Falcon scratch1 MMIO  | |||
 *(u32 *)FALCON_SCRATCH1 = 0;  | |||
 // Set Falcon boot key version in scratch0 MMIO  | |||
 *(u32 *)FALCON_SCRATCH0 = 0x01;  | |||
 // Set Falcon's boot vector address  | |||
 *(u32 *)FALCON_BOOTVEC = 0;  | |||
 // Signal Falcon's CPU  | |||
 *(u32 *)FALCON_CPUCTL = 0x02;  | |||
 // Infinite loop  | |||
 deadlock();  | |||
</syntaxhighlight>  | |||
===== Panic =====  | |||
If a panic occurs, all sensitive memory contents are cleared, the security engine and fuse programming are disabled and the boot processor is left in a halted state.  | If a panic occurs, all sensitive memory contents are cleared, the security engine and fuse programming are disabled and the boot processor is left in a halted state.  | ||
<syntaxhighlight lang="c">  | |||
  // Clear all stack contents  |   // Clear all stack contents  | ||
  clear_stack();  |   clear_stack();  | ||
| Line 129: | Line 321: | ||
  clear_mem();  |   clear_mem();  | ||
  // Clear the   |   // Clear the PK11 blob from memory  | ||
  clear_pk11_blob();  | |||
  // Halt the boot processor  |   // Halt the boot processor  | ||
  while (true)  |   while (true)  | ||
     *(u32 *)FLOW_CTLR_HALT_COP_EVENTS = (FLOW_MODE_STOP | HALT_COP_EVENT_JTAG);  |      *(u32 *)FLOW_CTLR_HALT_COP_EVENTS = (FLOW_MODE_STOP | HALT_COP_EVENT_JTAG);  | ||
</syntaxhighlight>  | |||
==== Anti-downgrade ====  | ===== Anti-downgrade =====  | ||
See [[Fuses#Anti-downgrade|Anti-downgrade]].  | See [[Fuses#Anti-downgrade|Anti-downgrade]].  | ||
==== Memory controllers ====  | ===== Memory controllers =====  | ||
After disabling fuse programming, the bootloader configures the EMC and MEM/MC. It additionally disables QSPI resets and programs a special aperture designed for AHB redirected access to IRAM.  | After disabling fuse programming, the bootloader configures the EMC and MEM/MC. It additionally disables QSPI resets and programs a special aperture designed for AHB redirected access to IRAM.  | ||
<syntaxhighlight lang="c">   | |||
  // Initialize EMC's clock source  |   // Initialize EMC's clock source  | ||
  u32 emc_clk_src_val = *(u32 *)PERIPH_CLK_SOURCE_EMC;  |   u32 emc_clk_src_val = *(u32 *)PERIPH_CLK_SOURCE_EMC;  | ||
| Line 203: | Line 384: | ||
  return mc_iram_reg_ctrl_val;  |   return mc_iram_reg_ctrl_val;  | ||
</syntaxhighlight>  | |||
[6.2.0+] MC_IRAM_TOM is now set to 0x80000000 to allow TSEC to access IRAM and all MMIO.  | |||
==== Key generation ====  | ===== Key generation =====  | ||
After the security engine is ready and before decrypting the next stage, the bootloader initializes and generates several keys into hardware keyslots.  | After the security engine is ready and before decrypting the next stage, the bootloader initializes and generates several keys into hardware keyslots.  | ||
For more details on the Switch's cryptosystem, please see [[Cryptosystem|this page]].  | For more details on the Switch's cryptosystem, please see [[Cryptosystem|this page]].  | ||
===== Selection =====  | [6.2.0+] The key generation process was moved into an encrypted [[TSEC]] payload.  | ||
====== Selection ======  | |||
Depending on [[Fuses#FUSE_RESERVED_ODM4|FUSE_RESERVED_ODM4]] and [[Fuses#FUSE_SPARE_BIT_5|FUSE_SPARE_BIT_5]] different static seeds are selected for key generation.  | Depending on [[Fuses#FUSE_RESERVED_ODM4|FUSE_RESERVED_ODM4]] and [[Fuses#FUSE_SPARE_BIT_5|FUSE_SPARE_BIT_5]] different static seeds are selected for key generation.  | ||
<syntaxhighlight lang="c">  | |||
  // Initialize keyslots 0x0C and 0x0D as readable  |   // Initialize keyslots 0x0C and 0x0D as readable  | ||
  init_keyslot(0x0C, 0x15);  |   init_keyslot(0x0C, 0x15);  | ||
| Line 296: | Line 483: | ||
  return;  |   return;  | ||
</syntaxhighlight>  | |||
===== generate_retail_keys =====  | ====== generate_retail_keys ======  | ||
In order to generate retail keys, the bootloader starts by initializing TSEC and grabbing it's [[TSEC#  | In order to generate retail keys, the bootloader starts by initializing TSEC and grabbing it's [[TSEC#TSEC_key_generation|device key]]. Using static seeds and the SBK, the keyblob injected into the BCT's [[BCT#customer_data|customer_data]] is validated and decrypted. The resulting keys will then be used to generate the master static key and the master device key.  | ||
See the pseudocode bellow for the detailed process.  | See the pseudocode bellow for the detailed process.  | ||
<syntaxhighlight lang="c">  | |||
  u32 in_addr = 0;  |   u32 in_addr = 0;  | ||
  u32 in_size = 0;  |   u32 in_size = 0;  | ||
| Line 359: | Line 548: | ||
  aes_cmac(out_addr, out_size, keyslot, in_addr, in_size);  |   aes_cmac(out_addr, out_size, keyslot, in_addr, in_size);  | ||
  //   |   // Compare the generated hash with the first  | ||
  // 0x10 bytes of bct_customer_data  |   // 0x10 bytes of bct_customer_data  | ||
  bool match = safe_memcmp(mac_addr, bct_customer_data_addr, 0x10);  |   bool match = safe_memcmp(mac_addr, bct_customer_data_addr, 0x10);  | ||
| Line 412: | Line 601: | ||
  return;  |   return;  | ||
</syntaxhighlight>  | |||
===== generate_debug_keys =====  | ====== generate_debug_keys ======  | ||
In order to generate debug keys, the bootloader only uses static seeds, the SBK and the SSK.  | In order to generate debug keys, the bootloader only uses static seeds, the SBK and the SSK.  | ||
See the pseudocode bellow for the detailed process.  | See the pseudocode bellow for the detailed process.  | ||
<syntaxhighlight lang="c">  | |||
  u32 in_addr = 0;  |   u32 in_addr = 0;  | ||
  u32 in_size = 0;  |   u32 in_size = 0;  | ||
| Line 478: | Line 669: | ||
  return;  |   return;  | ||
</syntaxhighlight>  | |||
===  | === Package1 (PK11) ===  | ||
This blob is stored encrypted inside the package and is decrypted by package1ldr.  | |||
====  | ==== Encryption ====  | ||
The encrypted   | The encrypted blob is prepended with it's CTR and total image size. After checking the image's size against an hardcoded value (can change on firmware updates), the image is AES-CTR decrypted and the keyslot used for decryption is immediately cleared.  | ||
  // Maximum   | <syntaxhighlight lang="c">  | ||
  u32   |   // Maximum encrypted blob's size on firmware version 1.0.0  | ||
  u32 max_pk11_enc_blob_size = 0x29000;  | |||
  u32   |   u32 pk11_enc_blob_size = *(u32 *)pk11_blob_addr;  | ||
  u32   |   u32 pk11_enc_blob_ctr_addr = pk11_blob_addr + 0x10;    | ||
  u32   |   u32 pk11_enc_blob_addr = pk11_blob_addr + 0x20;  | ||
  // Validate   |   // Validate the encrypted blob's size  | ||
  if (  |   if (pk11_enc_blob_size > max_pk11_enc_blob_size)  | ||
     panic();  |      panic();  | ||
  u32 in_addr =   |   u32 in_addr = pk11_enc_blob_addr;  | ||
  u32 in_size =   |   u32 in_size = pk11_enc_blob_size;  | ||
  u32 ctr_addr =   |   u32 ctr_addr = pk11_enc_blob_ctr_addr;  | ||
  u32 ctr_size = 0x10;  |   u32 ctr_size = 0x10;  | ||
  u32 out_addr =   |   u32 out_addr = pk11_dec_blob_addr;  | ||
  u32 out_size =   |   u32 out_size = pk11_dec_blob_size;  | ||
  u32 keyslot = 0x0B;  |   u32 keyslot = 0x0B;  | ||
  // AES-CTR decrypt  |   // AES-CTR decrypt  | ||
  // Use the pk11_key (keyslot 0x0B) to decrypt   |   // Use the pk11_key (keyslot 0x0B) to decrypt the blob in place  | ||
  aes_ctr_decrypt(out_addr, out_size, keyslot, in_addr, in_size, ctr_addr, ctr_size);  |   aes_ctr_decrypt(out_addr, out_size, keyslot, in_addr, in_size, ctr_addr, ctr_size);  | ||
| Line 511: | Line 704: | ||
  clear_keyslot(0x0B);  |   clear_keyslot(0x0B);  | ||
  // Validate the decrypted   |   // Validate the decrypted blob  | ||
  // Checks the "PK11" magic and some pk11 header fields  |   // Checks the "PK11" magic and some pk11 header fields  | ||
  bool is_valid =   |   bool is_valid = check_pk11_header(pk11_dec_blob_addr, pk11_dec_blob_size);  | ||
  // Invalid   |   // Invalid PK11 image  | ||
  if (!is_valid)  |   if (!is_valid)  | ||
     panic();  |      panic();  | ||
  u32 pk11_header_size = 0x20;  |   u32 pk11_header_size = 0x20;  | ||
  u32   |   u32 pk11_sec1_offset = *(u32 *)pk11_dec_blob_addr + 0x14;  | ||
  u32   |   u32 pk11_sec2_size = *(u32 *)pk11_dec_blob_addr + 0x18;  | ||
  // Calculate   |   // Calculate NX bootloader's entrypoint  | ||
  u32   |   u32 nx_boot_addr = (pk11_dec_blob_addr + pk11_header_size + pk11_sec1_offset + pk11_sec2_size);  | ||
  return   |   return nx_boot_addr;  | ||
</syntaxhighlight>  | |||
==   | ==== Header ====  | ||
When decrypted, the blob is encapsulated in the following header.  | |||
{| class="wikitable" border="1"  | {| class="wikitable" border="1"  | ||
|-  | |-  | ||
| Line 544: | Line 737: | ||
| 0x4  | | 0x4  | ||
| 4  | | 4  | ||
|   | | Section 0 size  | ||
|-  | |-  | ||
| 0x8  | | 0x8  | ||
|   | | 4  | ||
| Section 0 offset  | |||
|-  | |||
| 0xC  | |||
| 4  | |||
| Unknown  | | Unknown  | ||
|-  | |-  | ||
| 0x10  | | 0x10  | ||
| 4  | | 4  | ||
| Section   | | Section 1 size  | ||
|-  | |-  | ||
| 0x14  | | 0x14  | ||
| 4  | | 4  | ||
| Section   | | Section 1 offset  | ||
|-  | |-  | ||
| 0x18  | | 0x18  | ||
| 4  | | 4  | ||
| Section   | | Section 2 size  | ||
|-  | |-  | ||
| 0x1C  | | 0x1C  | ||
| 4  | | 4  | ||
| Section   | | Section 2 offset  | ||
|}  | |}  | ||
=   | What each section is used for may vary per system-version.  | ||
==   | |||
==== Section 0 ====  | |||
This section contains the warmboot binary.  | |||
==== Section 1 ====  | |||
This section contains the NX bootloader, which is run after the initial bootloader in package1.  | |||
==== Section 2 ====  | |||
This section contains the Secure Monitor binary.  | |||
== Mariko ==  | |||
This package is now distributed in a custom, signed and encrypted format.  | |||
{| class="wikitable" border="1"  | |||
|-  | |||
! Offset  | |||
! Size  | |||
! Description  | |||
|-  | |||
| 0x0  | |||
| 0x110  | |||
| Cryptographic signature  | |||
 0x0000: CryptoHash (empty)  | |||
 0x0010: RsaPssSig  | |||
|-  | |||
| 0x110  | |||
| 0x20  | |||
| Random block  | |||
|-  | |||
| 0x130  | |||
| 0x20  | |||
| SHA256 hash over package1 data  | |||
|-  | |||
| 0x150  | |||
| 0x4  | |||
| Version  | |||
|-  | |||
| 0x154  | |||
| 0x4  | |||
| Length  | |||
|-  | |||
| 0x158  | |||
| 0x4  | |||
| LoadAddress  | |||
|-  | |||
| 0x15C  | |||
| 0x4  | |||
| EntryPoint  | |||
|-  | |||
| 0x160  | |||
| 0x10  | |||
| Reserved  | |||
|-  | |||
| 0x170  | |||
| Variable  | |||
| Package1 data  | |||
 0x0170: [[Package1#Header|Header]]  | |||
 0x0190: Body (encrypted)  | |||
|}  | |||