Line 10: |
Line 10: |
| | | |
| === Header === | | === Header === |
− |
| |
| {| class="wikitable" border="1" | | {| class="wikitable" border="1" |
| |- | | |- |
Line 39: |
Line 38: |
| | 0x1E | | | 0x1E |
| | 0x2 | | | 0x2 |
− | | Unknown (version?) | + | | Version |
− | |-
| |
| |} | | |} |
| | | |
Line 58: |
Line 56: |
| | | |
| === 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.
| + | From firmware versions 1.0.0 to 6.1.0, 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"> | | <syntaxhighlight lang="c"> |
Line 97: |
Line 95: |
| | | |
| // Setup I2S1, I2S2, I2S3, I2S4, DISPLAY and VIC | | // Setup I2S1, I2S2, I2S3, I2S4, DISPLAY and VIC |
− | enable_hw_devices(); | + | mbist_workaround(); |
| | | |
| // Program the SE clock and resets | | // Program the SE clock and resets |
Line 149: |
Line 147: |
| | | |
| return; | | return; |
| + | </syntaxhighlight> |
| + | |
| + | Starting with firmware version 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> | | </syntaxhighlight> |
| | | |
Line 170: |
Line 322: |
| // Clear the PK11 blob from memory | | // Clear the PK11 blob from memory |
| clear_pk11_blob(); | | clear_pk11_blob(); |
− |
| |
− | u32 FLOW_CTLR_HALT_COP_EVENTS = 0x60007004;
| |
− | u32 FLOW_MODE_STOP = 0x40000000;
| |
− | u32 HALT_COP_EVENT_JTAG = 0x10000000;
| |
| | | |
| // Halt the boot processor | | // Halt the boot processor |
Line 186: |
Line 334: |
| 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"> | + | <syntaxhighlight lang="c"> |
− | u32 PERIPH_CLK_SOURCE_EMC = 0x6000619C;
| |
− | u32 CLK_OUT_ENB_SET_H = 0x60006328;
| |
− | u32 CLK_OUT_ENB_SET_X = 0x60006284;
| |
− | u32 RST_DEVICES_SET_H = 0x60006308;
| |
− | u32 RST_DEVICES_CLR_Y = 0x600062AC;
| |
− | u32 MC_IRAM_REG_CTRL = 0x70019964;
| |
− | u32 MC_IRAM_BOM = 0x7001965C;
| |
− | u32 MC_IRAM_TOM = 0x70019660;
| |
− |
| |
| // 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 249: |
Line 388: |
| 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]]. |
| + | |
| + | [6.2.0+] The key generation process was moved into an encrypted [[TSEC]] payload. |
| | | |
| ===== Selection ===== | | ===== Selection ===== |