/* * L4T Loader for Tegra X1 * * Copyright (c) 2020-2024 CTCaer * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "../hos/hos.h" #include "../hos/pkg1.h" #include "l4t.h" #include "l4t_config.inl" /* * API Revision info * * 0: Base. * 1: SDMMC1 LA programming for SDMMC1 UHS DDR200. * 2: Arachne Register Cell v1. * 3: Arachne Register Cell v2. PTSA Rework support. */ #define L4T_LOADER_API_REV 4 #define L4T_FIRMWARE_REV 0x34524556 // REV4. #ifdef DEBUG_UART_PORT #include #define UPRINTF(...) uart_printf(__VA_ARGS__) #else #define UPRINTF(...) #endif #if CARVEOUT_NVDEC_TSEC_ENABLE && CARVEOUT_SECFW_ENABLE #error "NVDEC and SECFW carveouts can't be used together!" #endif #if CARVEOUT_NVDEC_TSEC_ENABLE #define TZ_SIZE SZ_8M #else #define TZ_SIZE SZ_1M #endif // TZDRAM addresses and sizes. #define TZDRAM_SIZE TZ_SIZE // Secure Element. #define TZDRAM_BASE (0xFFFFFFFF - TZDRAM_SIZE + 1) // 0xFFF00000 or 0xFF800000. #define TZDRAM_COLD_ENTRY (TZDRAM_BASE) #define TZDRAM_WARM_ENTRY (TZDRAM_BASE + 0x200) // Carveout sizes. #define CARVEOUT_NVDEC_SIZE SZ_1M #define CARVEOUT_TSEC_SIZE SZ_1M #define CARVEOUT_SECFW_SIZE SZ_1M #define CARVEOUT_GPUFW_SIZE SZ_256K #if CARVEOUT_NVDEC_TSEC_ENABLE #define CARVEOUT_GPUWPR_SIZE CARVEOUT_GPUWPR_SIZE_CFG #else #define CARVEOUT_GPUWPR_SIZE (SZ_512K + SZ_256K) #endif #define SC7ENTRY_HDR_SIZE 0x400 // Secure Elements addresses for T210. #define SECFW_BASE (TZDRAM_BASE - SZ_1M) // 0xFFE00000 or 0xFF700000. #define SC7ENTRY_HDR_BASE (SECFW_BASE + 0) #define SC7ENTRY_BASE (SECFW_BASE + SC7ENTRY_HDR_SIZE) // After header. #define SC7EXIT_BASE (SECFW_BASE + SZ_64K) // 64KB after SECFW_BASE. #define R2P_PAYLOAD_BASE (SECFW_BASE + SZ_256K) // 256KB after SECFW_BASE. #define MTCTABLE_BASE (SECFW_BASE + SZ_512K) // 512KB after SECFW_BASE. #define BPMPFW_BASE (SECFW_BASE + SZ_512K + SZ_256K) // 768KB after SECFW_BASE. #define BPMPFW_ENTRYPOINT (BPMPFW_BASE + 0x100) // Used internally also. // Secure Elements addresses for T210B01. #define BPMPFW_B01_BASE (SECFW_BASE) // !! DTS carveout-start must match !! #define BPMPFW_B01_ENTRYPOINT (BPMPFW_B01_BASE + 0x40) // Used internally also. #define BPMPFW_B01_HEAP_BASE (BPMPFW_B01_BASE + SZ_256K - SZ_1K) // 255KB after BPMPFW_B01_BASE. #define BPMPFW_B01_EDTB_BASE (BPMPFW_B01_BASE + SZ_1M - 0) // Top BPMPFW carveout minus EMC DTB size. #define BPMPFW_B01_ADTB_BASE (BPMPFW_B01_BASE + 0x26008) // Attached BPMP-FW DTB address. #define SC7EXIT_B01_BASE (BPMPFW_B01_HEAP_BASE - SZ_4K) // 4KB before BPMP heap. // BPMP-FW defines. Offsets are 0xD8 below real main binary. #define BPMPFW_B01_DTB_ADDR (BPMPFW_B01_BASE + 0x14) // u32. DTB address if not attached. #define BPMPFW_B01_CC_INIT_OP (BPMPFW_B01_BASE + 0x17324) // u8. Initial table training OP. 0: OP_SWITCH, 1: OP_TRAIN, 2: OP_TRAIN_SWITCH. Default: OP_TRAIN. #define BPMPFW_B01_LOGLEVEL (BPMPFW_B01_BASE + 0x2547C) // u32. Log level. Default 3. #define BPMPFW_B01_LOGLEVEL (BPMPFW_B01_BASE + 0x2547C) // u32. Log level. Default 3. #define BPMPFW_B01_CC_PT_TIME (BPMPFW_B01_BASE + 0x25644) // u32. Periodic training period (in ms). Default 100 ms. #define BPMPFW_B01_CC_DEBUG (BPMPFW_B01_BASE + 0x257F8) // u32. EMC Clock Change debug mask. Default: 0x50000101. // BPMP-FW attached DTB defines. Can be generalized. #define BPMPFW_B01_DTB_EMC_ENTRIES 4 #define BPMPFW_B01_DTB_SERIAL_PORT_VAL (BPMPFW_B01_ADTB_BASE + 0x5B) // u8. DTB UART port offset. 0: Disabled. #define BPMPFW_B01_DTB_SET_SERIAL_PORT(port) (*(u8 *)BPMPFW_B01_DTB_SERIAL_PORT_VAL = port) #define BPMPFW_B01_DTB_EMC_TBL_OFF (BPMPFW_B01_ADTB_BASE + 0xA0) #define BPMPFW_B01_DTB_EMC_TBL_SZ 0x1120 #define BPMPFW_B01_DTB_EMC_NAME_VAL 0xA #define BPMPFW_B01_DTB_EMC_ENABLE_OFF 0x20 #define BPMPFW_B01_DTB_EMC_VALUES_OFF 0x4C #define BPMPFW_B01_DTB_EMC_FREQ_VAL 0x8C #define BPMPFW_B01_DTB_EMC_OPT_VAL 0xEDC #define BPMPFW_B01_DTB_EMC_TBL_START(idx) (BPMPFW_B01_DTB_EMC_TBL_OFF + BPMPFW_B01_DTB_EMC_TBL_SZ * (idx)) #define BPMPFW_B01_DTB_EMC_TBL_SET_VAL(idx, off, val) (*(u32 *)(BPMPFW_B01_DTB_EMC_TBL_START(idx) + (off)) = (val)) #define BPMPFW_B01_DTB_EMC_TBL_SET_FREQ(idx, freq) (*(u32 *)(BPMPFW_B01_DTB_EMC_TBL_START(idx) + BPMPFW_B01_DTB_EMC_FREQ_VAL) = (freq)) #define BPMPFW_B01_DTB_EMC_TBL_SET_OPTC(idx, opt) (*(u32 *)(BPMPFW_B01_DTB_EMC_TBL_START(idx) + BPMPFW_B01_DTB_EMC_OPT_VAL) = (opt)) #define BPMPFW_B01_DTB_EMC_TBL_SET_NAME(idx, name) (strcpy((char *)(BPMPFW_B01_DTB_EMC_TBL_START(idx) + BPMPFW_B01_DTB_EMC_NAME_VAL), (name))) #define BPMPFW_B01_DTB_EMC_TBL_ENABLE(idx) (*(char *)(BPMPFW_B01_DTB_EMC_TBL_START(idx) + BPMPFW_B01_DTB_EMC_ENABLE_OFF) = 'n') #define BPMPFW_B01_DTB_EMC_TBL_OFFSET(idx) ((void *)(BPMPFW_B01_DTB_EMC_TBL_START(idx) + BPMPFW_B01_DTB_EMC_VALUES_OFF)) // MTC table defines for T210B01. #define BPMPFW_B01_MTC_TABLE_BASE 0xA0000000 #define BPMPFW_B01_MTC_FREQ_TABLE_SIZE 4300 #define BPMPFW_B01_MTC_TABLE_SIZE (BPMPFW_B01_MTC_FREQ_TABLE_SIZE * 3) #define BPMPFW_B01_MTC_TABLE(idx) (BPMPFW_B01_MTC_TABLE_BASE + BPMPFW_B01_MTC_TABLE_SIZE * (idx)) #define BPMPFW_B01_MTC_TABLE_OFFSET(idx, fidx) ((void *)(BPMPFW_B01_MTC_TABLE(idx) + BPMPFW_B01_MTC_FREQ_TABLE_SIZE * (fidx))) // BL31 Enable IRAM based config. #define BL31_IRAM_PARAMS 0x4D415249 // "IRAM". #define BL31_EXTRA_FEATURES_ENABLE 0x52545845 // "EXTR". // BL31 Flags. #define FLAGS_PMC_NON_SECURE BIT(0) #define FLAGS_SC7_NO_BASE_RESTRICTION BIT(1) // BL31 config. #define PARAM_EP 1 #define PARAM_BL31 3 #define PARAM_EP_SECURE 0 #define PARAM_EP_NON_SECURE 1 #define VERSION_1 1 #define SPSR_EL2T BIT(3) // BL33 config. #define BL33_LOAD_BASE 0xAA000000 // DTB is loaded at 0xA8000000, so 32MB above. #define BL33_ENV_BASE (BL33_LOAD_BASE - SZ_256K) // Before BL33_LOAD_BASE. #define BL33_ENV_MAGIC_OFFSET (BL33_ENV_BASE - 4) #define BL33_ENV_MAGIC 0x33334C42 #define BL33_DTB_OFFSET (BL33_LOAD_BASE + 0x10) // After code end. #define BL33_DTB_BASE (BL33_LOAD_BASE + *(u32 *)BL33_DTB_OFFSET) #define BL33_DTB_UART_STATUS 0x1C94 #define BL33_DTB_UART_STS_OF 0x12C #define BL33_DTB_STDOUT_PATH 0x3F34 #define BL33_DTB_STDERR_PATH 0x3F54 #define BL33_DTB_SET_UART_STATUS(port) (strcpy((char *)(BL33_DTB_BASE + BL33_DTB_UART_STATUS + (port - 1) * BL33_DTB_UART_STS_OF), "okay")) #define BL33_DTB_SET_STDOUT_PATH(path) (strcpy((char *)(BL33_DTB_BASE + BL33_DTB_STDOUT_PATH), (path))) #define BL33_DTB_SET_STDERR_PATH(path) (strcpy((char *)(BL33_DTB_BASE + BL33_DTB_STDERR_PATH), (path))) // Misc. #define DTB_MAGIC 0xEDFE0DD0 // D00DFEED. #define FALCON_DMA_PAGE_SIZE 0x100 #define ACR_GSC3_ENABLE_MAGIC 0xC0EDBBCC #define SOC_ID_T210 0x210 #define SOC_ID_T210B01 0x214 #define SKU_NX 0x83 #define HDCP22 2 typedef struct _tsec_init_t { u32 sku; u32 hdcp; u32 soc; } tsec_init_t; typedef struct _param_header { u8 type; // type of the structure. u8 version; // version of this structure. u16 size; // size of this structure in bytes. u32 attr; // attributes: unused bits SBZ. } param_header_t; typedef struct _aapcs64_params { u64 x0; u64 x1; u64 x2; u64 x3; u64 x4; u64 x5; u64 x6; u64 x7; } aapcs64_params_t; typedef struct _entry_point_info { param_header_t hdr; u64 pc; u32 spsr; u32 align0; // Alignment to u64 for the above member. aapcs64_params_t args; } entry_point_info_t; typedef struct _image_info { param_header_t hdr; u64 image_base; // physical address of base of image. u32 image_size; // bytes read from image file. u32 image_max_size; } image_info_t; typedef struct _bl_v1_params { param_header_t hdr; u64 bl31_image_info; u64 bl32_ep_info; u64 bl32_image_info; u64 bl33_ep_info; u64 bl33_image_info; } bl_v1_params_t; typedef struct _plat_params_from_bl2 { // TZDRAM. u64 tzdram_size; u64 tzdram_base; s32 uart_id; // UART port ID. s32 l2_ecc_parity_prot_dis; // L2 ECC parity protection disable flag. u64 boot_profiler_shmem_base; // SHMEM base address for storing the boot logs. // SC7-Entry firmware. u64 sc7entry_fw_size; u64 sc7entry_fw_base; s32 enable_ccplex_lock_step; // Enable dual execution. // Enable below features. s32 enable_extra_features; // MTC table. u64 emc_table_size; u64 emc_table_base; // Initial R2P payload. u64 r2p_payload_size; u64 r2p_payload_base; u64 flags; // Platform flags. } plat_params_from_bl2_t; typedef struct _l4t_fw_t { u32 addr; char *name; } l4t_fw_t; typedef struct _l4t_ctxt_t { char *path; char *ram_oc_txt; int ram_oc_freq; int ram_oc_vdd2; int ram_oc_vddq; int ram_oc_opt; u32 serial_port; u32 sc7entry_size; emc_table_t *mtc_table; } l4t_ctxt_t; #define DRAM_VDD2_OC_MIN_VOLTAGE 1050 #define DRAM_VDD2_OC_MAX_VOLTAGE 1175 #define DRAM_VDD2Q_OC_MAX_VOLTAGE 1237 #define DRAM_VDDQ_OC_MIN_VOLTAGE 550 #define DRAM_VDDQ_OC_MAX_VOLTAGE 650 #define DRAM_T210B01_TBL_MAX_FREQ 1600000 //! TODO: Update on dram config changes. static const u8 mtc_table_idx_t210b01[] = { /* 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 */ -1, -1, -1, 7, -1, 7, 7, -1, 0, 1, 2, 3, 0, 1, 2, 3, -1, 4, 5, 4, 8, 8, 8, 5, 4, 6, 6, 6, 5, 9, 9, 9, 10, 10, 10 }; static const l4t_fw_t l4t_fw[] = { { TZDRAM_BASE, "bl31.bin" }, { BL33_LOAD_BASE, "bl33.bin" }, { SC7ENTRY_BASE, "sc7entry.bin" }, { SC7EXIT_BASE, "sc7exit.bin" }, { SC7EXIT_B01_BASE, "sc7exit_b01.bin" }, { BPMPFW_BASE, "bpmpfw.bin" }, { BPMPFW_B01_BASE, "bpmpfw_b01.bin" }, { BPMPFW_B01_MTC_TABLE_BASE, "mtc_tbl_b01.bin" }, }; enum { BL31_FW = 0, BL33_FW = 1, SC7ENTRY_FW = 2, SC7EXIT_FW = 3, SC7EXIT_B01_FW = 4, BPMPFW_FW = 5, BPMPFW_B01_FW = 6, BPMPFW_B01_MTC_TBL = 7 }; static void _l4t_crit_error(const char *text, bool needs_update) { gfx_con.mute = false; gfx_printf("%kL4T Error: %s!%sFailed to launch L4T!\n%k", TXT_CLR_ERROR, text, needs_update ? "\nUpdate bootloader folder!\n\n" : "\n\n", TXT_CLR_DEFAULT); } char *sd_path; u32 sd_path_len; static int _l4t_sd_load(u32 idx) { FIL fp; void *load_address = (void *)l4t_fw[idx].addr; if (idx == SC7EXIT_B01_FW) load_address -= sizeof(u32); strcpy(sd_path + sd_path_len, l4t_fw[idx].name); if (f_open(&fp, sd_path, FA_READ) != FR_OK) return 0; u32 size = f_size(&fp); if (f_read(&fp, load_address, size, NULL) != FR_OK) size = 0; f_close(&fp); u32 rev = *(u32 *)(load_address + size - sizeof(u32)); if (idx >= SC7ENTRY_FW && rev != L4T_FIRMWARE_REV) return 0; return size; } static void _l4t_sdram_lp0_save_params(bool t210b01) { struct tegra_pmc_regs *pmc = (struct tegra_pmc_regs *)PMC_BASE; #define _REG_S(base, off) *(u32 *)((base) + (off)) #define MC_S(off) _REG_S(MC_BASE, off) #define pack(src, src_bits, dst, dst_bits) { \ u32 mask = 0xffffffff >> (31 - ((1 ? src_bits) - (0 ? src_bits))); \ dst &= ~(mask << (0 ? dst_bits)); \ dst |= ((src >> (0 ? src_bits)) & mask) << (0 ? dst_bits); } #define s(param, src_bits, pmcreg, dst_bits) \ pack(MC_S(param), src_bits, pmc->pmcreg, dst_bits) // 32 bits version of s macro. #define s32(param, pmcreg) pmc->pmcreg = MC_S(param) // Only save changed carveout registers into PMC for SC7 Exit. // VPR. s(MC_VIDEO_PROTECT_BOM, 31:20, secure_scratch52, 26:15); s(MC_VIDEO_PROTECT_SIZE_MB, 11:0, secure_scratch53, 11:0); if (!t210b01) { s(MC_VIDEO_PROTECT_REG_CTRL, 1:0, secure_scratch13, 31:30); } else { s(MC_VIDEO_PROTECT_REG_CTRL, 1:0, secure_scratch14, 31:30); } // TZD. s(MC_SEC_CARVEOUT_BOM, 31:20, secure_scratch53, 23:12); s(MC_SEC_CARVEOUT_SIZE_MB, 11:0, secure_scratch54, 11:0); if (!t210b01) { s(MC_SEC_CARVEOUT_REG_CTRL, 0:0, secure_scratch18, 30:30); } else { s(MC_SEC_CARVEOUT_REG_CTRL, 0:0, secure_scratch19, 29:29); } // NVDEC or SECFW. s(MC_SECURITY_CARVEOUT1_BOM, 31:17, secure_scratch50, 29:15); s(MC_SECURITY_CARVEOUT1_SIZE_128KB, 11:0, secure_scratch57, 11:0); s32(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS0, secure_scratch59); s32(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS1, secure_scratch60); s32(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS2, secure_scratch61); s32(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS3, secure_scratch62); if (!t210b01) { s(MC_SECURITY_CARVEOUT1_CFG0, 2:0, secure_scratch56, 27:25); s(MC_SECURITY_CARVEOUT1_CFG0, 6:3, secure_scratch41, 28:25); s(MC_SECURITY_CARVEOUT1_CFG0, 13:7, secure_scratch42, 31:25); s(MC_SECURITY_CARVEOUT1_CFG0, 17:14, secure_scratch43, 28:25); s(MC_SECURITY_CARVEOUT1_CFG0, 21:18, secure_scratch44, 28:25); s(MC_SECURITY_CARVEOUT1_CFG0, 25:22, secure_scratch56, 31:28); s(MC_SECURITY_CARVEOUT1_CFG0, 26:26, secure_scratch57, 24:24); } else { s(MC_SECURITY_CARVEOUT1_CFG0, 1:0, secure_scratch56, 31:30); s(MC_SECURITY_CARVEOUT1_CFG0, 2:2, secure_scratch57, 24:24); s(MC_SECURITY_CARVEOUT1_CFG0, 6:3, secure_scratch42, 28:25); s(MC_SECURITY_CARVEOUT1_CFG0, 10:7, secure_scratch43, 28:25); s(MC_SECURITY_CARVEOUT1_CFG0, 13:11, secure_scratch42, 31:29); s(MC_SECURITY_CARVEOUT1_CFG0, 17:14, secure_scratch44, 28:25); s(MC_SECURITY_CARVEOUT1_CFG0, 21:18, secure_scratch47, 25:22); s(MC_SECURITY_CARVEOUT1_CFG0, 26:22, secure_scratch57, 29:25); } // GPU FW. s(MC_SECURITY_CARVEOUT2_BOM, 31:17, secure_scratch51, 29:15); s(MC_SECURITY_CARVEOUT2_SIZE_128KB, 11:0, secure_scratch56, 23:12); if (!t210b01) { s(MC_SECURITY_CARVEOUT2_CFG0, 2:0, secure_scratch55, 27:25); s(MC_SECURITY_CARVEOUT2_CFG0, 6:3, secure_scratch19, 31:28); s(MC_SECURITY_CARVEOUT2_CFG0, 10:7, secure_scratch20, 31:28); s(MC_SECURITY_CARVEOUT2_CFG0, 13:11, secure_scratch41, 31:29); s(MC_SECURITY_CARVEOUT2_CFG0, 17:14, secure_scratch39, 30:27); s(MC_SECURITY_CARVEOUT2_CFG0, 21:18, secure_scratch40, 30:27); s(MC_SECURITY_CARVEOUT2_CFG0, 25:22, secure_scratch55, 31:28); s(MC_SECURITY_CARVEOUT2_CFG0, 26:26, secure_scratch56, 24:24); } else { s(MC_SECURITY_CARVEOUT2_CFG0, 1:0, secure_scratch55, 31:30); s(MC_SECURITY_CARVEOUT2_CFG0, 2:2, secure_scratch56, 24:24); s(MC_SECURITY_CARVEOUT2_CFG0, 6:3, secure_scratch20, 31:28); s(MC_SECURITY_CARVEOUT2_CFG0, 10:7, secure_scratch39, 30:27); s(MC_SECURITY_CARVEOUT2_CFG0, 13:11, secure_scratch41, 31:29); s(MC_SECURITY_CARVEOUT2_CFG0, 17:14, secure_scratch40, 30:27); s(MC_SECURITY_CARVEOUT2_CFG0, 21:18, secure_scratch41, 28:25); s(MC_SECURITY_CARVEOUT2_CFG0, 26:22, secure_scratch56, 29:25); } // GPU WPR. s(MC_SECURITY_CARVEOUT3_BOM, 31:17, secure_scratch50, 14:0); s(MC_SECURITY_CARVEOUT3_SIZE_128KB, 11:0, secure_scratch56, 11:0); s32(MC_SECURITY_CARVEOUT3_CLIENT_ACCESS2, secure_scratch71); s32(MC_SECURITY_CARVEOUT3_CLIENT_ACCESS4, secure_scratch73); if (!t210b01) { s(MC_SECURITY_CARVEOUT3_CFG0, 2:0, secure_scratch57, 27:25); s(MC_SECURITY_CARVEOUT3_CFG0, 10:3, secure_scratch47, 29:22); s(MC_SECURITY_CARVEOUT3_CFG0, 13:11, secure_scratch43, 31:29); s(MC_SECURITY_CARVEOUT3_CFG0, 21:14, secure_scratch48, 29:22); s(MC_SECURITY_CARVEOUT3_CFG0, 25:22, secure_scratch57, 31:28); s(MC_SECURITY_CARVEOUT3_CFG0, 26:26, secure_scratch58, 0:0); } else { s(MC_SECURITY_CARVEOUT3_CFG0, 1:0, secure_scratch57, 31:30); s(MC_SECURITY_CARVEOUT3_CFG0, 2:2, secure_scratch58, 0:0); s(MC_SECURITY_CARVEOUT3_CFG0, 6:3, secure_scratch47, 29:26); s(MC_SECURITY_CARVEOUT3_CFG0, 10:7, secure_scratch48, 25:22); s(MC_SECURITY_CARVEOUT3_CFG0, 13:11, secure_scratch43, 31:29); s(MC_SECURITY_CARVEOUT3_CFG0, 17:14, secure_scratch48, 29:26); s(MC_SECURITY_CARVEOUT3_CFG0, 21:18, secure_scratch52, 30:27); s(MC_SECURITY_CARVEOUT3_CFG0, 26:22, secure_scratch58, 5:1); } // TSECA. s(MC_SECURITY_CARVEOUT4_BOM, 31:17, secure_scratch51, 14:0); s(MC_SECURITY_CARVEOUT4_SIZE_128KB, 11:0, secure_scratch55, 23:12); s(MC_SECURITY_CARVEOUT4_CFG0, 26:0, secure_scratch39, 26:0); // TSECB. s(MC_SECURITY_CARVEOUT5_BOM, 31:17, secure_scratch52, 14:0); s(MC_SECURITY_CARVEOUT5_SIZE_128KB, 11:0, secure_scratch57, 23:12); s(MC_SECURITY_CARVEOUT5_CFG0, 26:0, secure_scratch40, 26:0); } static void _l4t_mc_config_carveout(bool t210b01) { // Disabled VPR carveout. DT decides if enabled or not. MC(MC_VIDEO_PROTECT_BOM) = 0xFFF00000; MC(MC_VIDEO_PROTECT_SIZE_MB) = 0; MC(MC_VIDEO_PROTECT_REG_CTRL) = VPR_CTRL_TZ_SECURE | VPR_CTRL_LOCKED; // Temporarily disable TZDRAM carveout. For launching coldboot TZ. MC(MC_SEC_CARVEOUT_BOM) = 0xFFF00000; MC(MC_SEC_CARVEOUT_SIZE_MB) = 0; MC(MC_SEC_CARVEOUT_REG_CTRL) = 0; // Print real one, not temp disabled. UPRINTF("TZD: TZDRAM Carveout: %08X - %08X\n", TZDRAM_BASE, TZDRAM_BASE - 1 + TZDRAM_SIZE); // Configure generalized security carveouts. u32 carveout_base = TZDRAM_BASE - SZ_1M; // Always leave space for Secure Firmware. #if CARVEOUT_NVDEC_TSEC_ENABLE // Set NVDEC keyslot sticky bits. clock_enable_nvdec(); clock_enable_nvjpg(); NVDEC(NVDEC_SA_KEYSLOT_GLOBAL_RW) = 0xFFFF; // Read disable. NVDEC(NVDEC_SA_KEYSLOT_TZ) = 0xFFFFFFFF; // TZ enable. NVDEC(NVDEC_SA_KEYSLOT_FALCON) = 0; // Falcon disable. NVDEC(NVDEC_SA_KEYSLOT_OTF) = 0; // OTF disable. NVDEC(NVDEC_VPR_ALL_OTF_GOTO_VPR) = 1; // Enable. clock_disable_nvjpg(); clock_disable_nvdec(); // Set NVDEC carveout. Only for NVDEC bl/prod. carveout_base -= ALIGN(CARVEOUT_NVDEC_SIZE, SZ_1M); MC(MC_SECURITY_CARVEOUT1_BOM) = carveout_base; MC(MC_SECURITY_CARVEOUT1_BOM_HI) = 0; MC(MC_SECURITY_CARVEOUT1_SIZE_128KB) = CARVEOUT_NVDEC_SIZE / SZ_128K; MC(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS0) = 0; MC(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS1) = 0; MC(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS2) = SEC_CARVEOUT_CA2_R_TSEC | SEC_CARVEOUT_CA2_W_TSEC; MC(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS3) = SEC_CARVEOUT_CA3_R_NVDEC | SEC_CARVEOUT_CA3_W_NVDEC; MC(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS4) = 0; MC(MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS0) = 0; MC(MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS1) = 0; MC(MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS2) = 0; MC(MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS3) = 0; MC(MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS4) = 0; MC(MC_SECURITY_CARVEOUT1_CFG0) = SEC_CARVEOUT_CFG_LOCKED | SEC_CARVEOUT_CFG_UNTRANSLATED_ONLY | SEC_CARVEOUT_CFG_RD_SEC | SEC_CARVEOUT_CFG_RD_FALCON_LS | SEC_CARVEOUT_CFG_RD_FALCON_HS | SEC_CARVEOUT_CFG_WR_FALCON_HS | SEC_CARVEOUT_CFG_APERTURE_ID(1) | SEC_CARVEOUT_CFG_FORCE_APERTURE_ID_MATCH; UPRINTF("GSC1: NVDEC Carveout: %08X - %08X\n", MC(MC_SECURITY_CARVEOUT1_BOM), MC(MC_SECURITY_CARVEOUT1_BOM) + MC(MC_SECURITY_CARVEOUT1_SIZE_128KB) * SZ_128K); #elif CARVEOUT_SECFW_ENABLE // Flush data to ram. bpmp_mmu_maintenance(BPMP_MMU_MAINT_INVALID_WAY, false); // Set SC7-Entry/SC7-Exit/R2P/MTC Table or SC7-Exit/BPMP-FW carveout. Only BPMP, CCPLEX and AHB have R/W access. MC(MC_SECURITY_CARVEOUT1_BOM) = carveout_base; MC(MC_SECURITY_CARVEOUT1_BOM_HI) = 0; MC(MC_SECURITY_CARVEOUT1_SIZE_128KB) = CARVEOUT_SECFW_SIZE / SZ_128K; MC(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS0) = SEC_CARVEOUT_CA0_R_BPMP_C | SEC_CARVEOUT_CA0_R_PPCSAHBSLV; MC(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS1) = SEC_CARVEOUT_CA1_W_BPMP_C | SEC_CARVEOUT_CA1_R_CCPLEX_C | SEC_CARVEOUT_CA1_R_CCPLEXLP_C | SEC_CARVEOUT_CA1_W_CCPLEX_C | SEC_CARVEOUT_CA1_W_CCPLEXLP_C | SEC_CARVEOUT_CA1_W_PPCSAHBSLV; MC(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS2) = 0; MC(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS3) = 0; MC(MC_SECURITY_CARVEOUT1_CLIENT_ACCESS4) = 0; MC(MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS0) = 0; MC(MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS1) = 0; MC(MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS2) = 0; MC(MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS3) = 0; MC(MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS4) = 0; MC(MC_SECURITY_CARVEOUT1_CFG0) = SEC_CARVEOUT_CFG_RD_NS | SEC_CARVEOUT_CFG_WR_NS | SEC_CARVEOUT_CFG_LOCKED; UPRINTF("GSC1: SECFW Carveout: %08X - %08X\n", MC(MC_SECURITY_CARVEOUT1_BOM), MC(MC_SECURITY_CARVEOUT1_BOM) + MC(MC_SECURITY_CARVEOUT1_SIZE_128KB) * SZ_128K); #endif // Set GPU FW WPR carveout. Same value is used for GPU WPR carveout calculation if not full TOS. carveout_base -= ALIGN(CARVEOUT_GPUFW_SIZE, SZ_1M); // Sanitize it and enable GSC3 for ACR. memset((void *)carveout_base, 0, CARVEOUT_GPUFW_SIZE); *(u32 *)(carveout_base + CARVEOUT_GPUFW_SIZE - sizeof(u32)) = ACR_GSC3_ENABLE_MAGIC; tsec_init_t *tsec_init = (tsec_init_t *)(carveout_base + CARVEOUT_GPUFW_SIZE - FALCON_DMA_PAGE_SIZE); // Set TSEC init info. tsec_init->sku = SKU_NX; tsec_init->hdcp = HDCP22; tsec_init->soc = t210b01 ? SOC_ID_T210B01 : SOC_ID_T210; // Flush data to ram. bpmp_mmu_maintenance(BPMP_MMU_MAINT_INVALID_WAY, false); // Set carveout config. MC(MC_SECURITY_CARVEOUT2_BOM) = carveout_base; MC(MC_SECURITY_CARVEOUT2_BOM_HI) = 0; MC(MC_SECURITY_CARVEOUT2_SIZE_128KB) = CARVEOUT_GPUFW_SIZE / SZ_128K; MC(MC_SECURITY_CARVEOUT2_CLIENT_ACCESS0) = 0; MC(MC_SECURITY_CARVEOUT2_CLIENT_ACCESS1) = 0; MC(MC_SECURITY_CARVEOUT2_CLIENT_ACCESS2) = SEC_CARVEOUT_CA2_R_GPU | SEC_CARVEOUT_CA2_W_GPU | SEC_CARVEOUT_CA2_R_TSEC; MC(MC_SECURITY_CARVEOUT2_CLIENT_ACCESS3) = 0; MC(MC_SECURITY_CARVEOUT2_CLIENT_ACCESS4) = SEC_CARVEOUT_CA4_R_GPU2 | SEC_CARVEOUT_CA4_W_GPU2; MC(MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS0) = 0; MC(MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS1) = 0; MC(MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS2) = 0; MC(MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS3) = 0; MC(MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS4) = 0; MC(MC_SECURITY_CARVEOUT2_CFG0) = SEC_CARVEOUT_CFG_LOCKED | SEC_CARVEOUT_CFG_UNTRANSLATED_ONLY | SEC_CARVEOUT_CFG_RD_NS | SEC_CARVEOUT_CFG_RD_SEC | SEC_CARVEOUT_CFG_RD_FALCON_LS | SEC_CARVEOUT_CFG_RD_FALCON_HS | SEC_CARVEOUT_CFG_WR_FALCON_LS | SEC_CARVEOUT_CFG_WR_FALCON_HS | SEC_CARVEOUT_CFG_APERTURE_ID(2) | SEC_CARVEOUT_CFG_SEND_CFG_TO_GPU | SEC_CARVEOUT_CFG_FORCE_APERTURE_ID_MATCH; // SEC_CARVEOUT_CFG_IS_WPR is set from GPU. UPRINTF("GSC2: GPUFW Carveout: %08X - %08X\n", MC(MC_SECURITY_CARVEOUT2_BOM), MC(MC_SECURITY_CARVEOUT2_BOM) + MC(MC_SECURITY_CARVEOUT2_SIZE_128KB) * SZ_128K); // Set GPU WPR carveout. #if CARVEOUT_NVDEC_TSEC_ENABLE carveout_base -= ALIGN(CARVEOUT_GPUWPR_SIZE, SZ_1M); MC(MC_SECURITY_CARVEOUT3_BOM) = carveout_base; #else MC(MC_SECURITY_CARVEOUT3_BOM) = carveout_base + CARVEOUT_GPUFW_SIZE; #endif MC(MC_SECURITY_CARVEOUT3_BOM_HI) = 0x0; MC(MC_SECURITY_CARVEOUT3_SIZE_128KB) = CARVEOUT_GPUWPR_SIZE / SZ_128K; MC(MC_SECURITY_CARVEOUT3_CLIENT_ACCESS0) = 0; MC(MC_SECURITY_CARVEOUT3_CLIENT_ACCESS1) = 0; MC(MC_SECURITY_CARVEOUT3_CLIENT_ACCESS2) = 0; // HOS: SEC_CARVEOUT_CA2_R_GPU | SEC_CARVEOUT_CA2_W_GPU MC(MC_SECURITY_CARVEOUT3_CLIENT_ACCESS3) = 0; MC(MC_SECURITY_CARVEOUT3_CLIENT_ACCESS4) = 0; // HOS: SEC_CARVEOUT_CA4_R_GPU2 | SEC_CARVEOUT_CA4_W_GPU2 MC(MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS0) = 0; MC(MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS1) = 0; MC(MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS2) = 0; MC(MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS3) = 0; MC(MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS4) = 0; MC(MC_SECURITY_CARVEOUT3_CFG0) = SEC_CARVEOUT_CFG_LOCKED | SEC_CARVEOUT_CFG_UNTRANSLATED_ONLY | SEC_CARVEOUT_CFG_RD_NS | SEC_CARVEOUT_CFG_RD_SEC | SEC_CARVEOUT_CFG_RD_FALCON_LS | SEC_CARVEOUT_CFG_RD_FALCON_HS | SEC_CARVEOUT_CFG_WR_FALCON_LS | SEC_CARVEOUT_CFG_WR_FALCON_HS | SEC_CARVEOUT_CFG_APERTURE_ID(3) | SEC_CARVEOUT_CFG_SEND_CFG_TO_GPU | SEC_CARVEOUT_CFG_FORCE_APERTURE_ID_MATCH; // SEC_CARVEOUT_CFG_IS_WPR is set from GPU. UPRINTF("GSC3: GPUW2 Carveout: %08X - %08X\n", MC(MC_SECURITY_CARVEOUT3_BOM), MC(MC_SECURITY_CARVEOUT3_BOM) + MC(MC_SECURITY_CARVEOUT3_SIZE_128KB) * SZ_128K); /* * Set TSECA/B carveout. Only for NVDEC bl/prod and TSEC. * * Otherwise disabled. * * With HOS SC7-Exit fw, it gets set to disallow RAM access for BPMP. But TZ can change it. * We lock the carveout and save it in restore scratch registers so SC7-Exit can't touch it. */ carveout_base -= CARVEOUT_NVDEC_TSEC_ENABLE ? ALIGN(CARVEOUT_TSEC_SIZE, SZ_1M) : 0; MC(MC_SECURITY_CARVEOUT4_BOM) = CARVEOUT_NVDEC_TSEC_ENABLE ? carveout_base : 0; MC(MC_SECURITY_CARVEOUT4_SIZE_128KB) = CARVEOUT_NVDEC_TSEC_ENABLE ? CARVEOUT_TSEC_SIZE / SZ_128K : 0; MC(MC_SECURITY_CARVEOUT4_CFG0) = SEC_CARVEOUT_CFG_LOCKED | SEC_CARVEOUT_CFG_RD_FALCON_HS | SEC_CARVEOUT_CFG_WR_FALCON_HS | SEC_CARVEOUT_CFG_APERTURE_ID(4) | SEC_CARVEOUT_CFG_FORCE_APERTURE_ID_MATCH; UPRINTF("GSC4: TSEC1 Carveout: %08X - %08X\n", MC(MC_SECURITY_CARVEOUT4_BOM), MC(MC_SECURITY_CARVEOUT4_BOM) + MC(MC_SECURITY_CARVEOUT4_SIZE_128KB) * SZ_128K); // Set TSECA carveout. Only for NVDEC bl/prod and TSEC. Otherwise disabled. carveout_base -= CARVEOUT_NVDEC_TSEC_ENABLE ? ALIGN(CARVEOUT_TSEC_SIZE, SZ_1M) : 0; MC(MC_SECURITY_CARVEOUT5_BOM) = CARVEOUT_NVDEC_TSEC_ENABLE ? carveout_base : 0; MC(MC_SECURITY_CARVEOUT5_SIZE_128KB) = CARVEOUT_NVDEC_TSEC_ENABLE ? CARVEOUT_TSEC_SIZE / SZ_128K : 0; MC(MC_SECURITY_CARVEOUT5_CFG0) = SEC_CARVEOUT_CFG_LOCKED | SEC_CARVEOUT_CFG_RD_FALCON_HS | SEC_CARVEOUT_CFG_WR_FALCON_HS | SEC_CARVEOUT_CFG_APERTURE_ID(5) | SEC_CARVEOUT_CFG_FORCE_APERTURE_ID_MATCH; UPRINTF("GSC5: TSEC2 Carveout: %08X - %08X\n", MC(MC_SECURITY_CARVEOUT5_BOM), MC(MC_SECURITY_CARVEOUT5_BOM) + MC(MC_SECURITY_CARVEOUT5_SIZE_128KB) * SZ_128K); UPRINTF("DRAM Bank 0 TOP: %08X\n", carveout_base); // Save carveouts to lp0 pmc registers. _l4t_sdram_lp0_save_params(t210b01); } static void _l4t_late_hw_config(bool t210b01) { // Reset System Counters. for (u32 i = 0; i < SYSCTR0_COUNTERS; i += sizeof(u32)) SYSCTR0(SYSCTR0_COUNTERS_BASE + i) = 0; /* * PMIC config scratch register * * bit00: active cluster slow * bit01: Set: max77621/max77812. Unset: OVR/OVR2. * bit02-07: unused * bit08-15: pmic cpu i2c address * bit16:23: pmic cpu i2c en reg * bit24:31: pmic cpu i2c en val */ PMC(APBDEV_PMC_SCRATCH201) = BIT(1); // Clear PLLM override for SC7. PMC(APBDEV_PMC_PLLP_WB0_OVERRIDE) &= ~PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE_ENABLE; // Set spare reg to 0xE0000 and clear everything else. if (t210b01 && (SYSREG(AHB_AHB_SPARE_REG) & 0xE0000000) != 0xE0000000) SYSREG(AHB_AHB_SPARE_REG) = 0xE0000 << 12; // HDA loopback disable on prod. PMC(APBDEV_PMC_STICKY_BITS) = PMC_STICKY_BITS_HDA_LPBK_DIS; // Clear any MC error. MC(MC_INTSTATUS) = MC(MC_INTSTATUS); #if LOCK_PMC_REGISTERS // Lock LP0 parameters and misc secure registers. Always happens on warmboot. #if CARVEOUT_NVDEC_TSEC_ENABLE pmc_scratch_lock(PMC_SEC_LOCK_CARVEOUTS_L4T | PMC_SEC_LOCK_SE_SRK); #else pmc_scratch_lock(PMC_SEC_LOCK_LP0_PARAMS | PMC_SEC_LOCK_MISC | PMC_SEC_LOCK_SE_SRK); #endif // Lock SE2 SRK and misc secure regs. Also lock writes on normal LP0 scratch regs. if (t210b01) pmc_scratch_lock(PMC_SEC_LOCK_MISC_B01 | PMC_SEC_LOCK_SE2_SRK_B01 | PMC_SEC_LOCK_LP0_PARAMS_B01); #endif } static void _l4t_bpmpfw_b01_config(l4t_ctxt_t *ctxt) { char *ram_oc_txt = ctxt->ram_oc_txt; u32 ram_oc_freq = ctxt->ram_oc_freq; u32 ram_oc_opt = ctxt->ram_oc_opt; u32 ram_id = fuse_read_dramid(true); // Set default parameters. *(u32 *)BPMPFW_B01_DTB_ADDR = 0; *(u8 *)BPMPFW_B01_CC_INIT_OP = OP_TRAIN; *(u32 *)BPMPFW_B01_CC_PT_TIME = 100; #if DEBUG_LOG_BPMPFW // Set default debug parameters. *(u32 *)BPMPFW_B01_LOGLEVEL = 3; *(u32 *)BPMPFW_B01_CC_DEBUG = 0x50000101; // Set serial debug port. if (*(u32 *)BPMPFW_B01_ADTB_BASE == DTB_MAGIC) BPMPFW_B01_DTB_SET_SERIAL_PORT(ctxt->serial_port); #endif // Set and copy MTC tables. u32 mtc_idx = mtc_table_idx_t210b01[ram_id]; for (u32 i = 0; i < 3; i++) { minerva_sdmmc_la_program(BPMPFW_B01_MTC_TABLE_OFFSET(mtc_idx, i), true); memcpy(BPMPFW_B01_DTB_EMC_TBL_OFFSET(i), BPMPFW_B01_MTC_TABLE_OFFSET(mtc_idx, i), BPMPFW_B01_MTC_FREQ_TABLE_SIZE); } // Always use Arachne Register Cell (ARC) for setting rated frequencies if no ram_oc is defined. if (!ram_oc_freq) { if (ram_id >= LPDDR4X_IOWA_4GB_SAMSUNG_K4U6E3S4AM_MGCJ && ram_id <= LPDDR4X_HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTE) { ram_oc_txt = "1866000"; ram_oc_freq = 1866000; } else { ram_oc_txt = "2133000"; ram_oc_freq = 2133000; } } // Set DRAM voltage. if (ctxt->ram_oc_vdd2) max7762x_regulator_set_voltage(REGULATOR_SD1, ctxt->ram_oc_vdd2 * 1000); if (ctxt->ram_oc_vddq) max7762x_regulator_set_voltage(REGULATOR_RAM0, ctxt->ram_oc_vddq * 1000); // A frequency of lower or equal with stock max will skip ARC. if (ram_oc_freq > DRAM_T210B01_TBL_MAX_FREQ) { // Final table. const u32 tbl_idx = BPMPFW_B01_DTB_EMC_ENTRIES - 1; // Copy table and prep it for Arachne. memcpy(BPMPFW_B01_DTB_EMC_TBL_OFFSET(tbl_idx), BPMPFW_B01_MTC_TABLE_OFFSET(mtc_idx, 2), BPMPFW_B01_MTC_FREQ_TABLE_SIZE); BPMPFW_B01_DTB_EMC_TBL_SET_NAME(tbl_idx, ram_oc_txt); BPMPFW_B01_DTB_EMC_TBL_SET_FREQ(tbl_idx, ram_oc_freq); BPMPFW_B01_DTB_EMC_TBL_SET_OPTC(tbl_idx, ram_oc_opt); // Enable table. BPMPFW_B01_DTB_EMC_TBL_ENABLE(tbl_idx); UPRINTF("RAM Frequency set to: %d KHz. Voltage: %d mV\n", ram_oc_freq, ctxt->ram_oc_vdd2); } // Save BPMP-FW entrypoint for TZ. PMC(APBDEV_PMC_SCRATCH39) = BPMPFW_B01_ENTRYPOINT; PMC(APBDEV_PMC_SCRATCH_WRITE_DISABLE1) |= BIT(15); } static int _l4t_sc7_exit_config(bool t210b01) { if (!t210b01) { // Apply Nintendo Switch (2017) RSA modulus to SC7-Exit firmware. emmc_initialize(false); pkg1_warmboot_rsa_mod(SC7EXIT_BASE); emmc_end(); // Set SC7-Exit firmware address for bootrom to find. PMC(APBDEV_PMC_SCRATCH1) = SC7EXIT_BASE; } else { launch_ctxt_t hos_ctxt = {0}; u32 fw_fuses = *(u32 *)(SC7EXIT_B01_BASE - sizeof(u32)); // Fuses count in front of actual firmware. // Get latest SC7-Exit if needed and setup PA id. if (!pkg1_warmboot_config(&hos_ctxt, 0, fw_fuses, 0)) { gfx_con.mute = false; gfx_wputs("\nFailed to match warmboot with fuses!\nIf you continue, sleep wont work!"); gfx_puts("\nPress POWER to continue.\nPress VOL to go to the menu.\n"); if (!(btn_wait() & BTN_POWER)) return 0; } // Copy loaded warmboot fw to address if from storage. if (hos_ctxt.warmboot) memcpy((void *)SC7EXIT_B01_BASE, hos_ctxt.warmboot, hos_ctxt.warmboot_size); // Set SC7-Exit firmware address for bootrom to find. PMC(APBDEV_PMC_SECURE_SCRATCH119) = SC7EXIT_B01_BASE; PMC(APBDEV_PMC_SEC_DISABLE8) |= BIT(30); } return 1; } static void _l4t_bl33_cfg_set_key(char *env, char *key, char *val) { strcat(env, key); strcat(env, "="); strcat(env, val); strcat(env, "\n"); } static void _l4t_set_config(l4t_ctxt_t *ctxt, const ini_sec_t *ini_sec, int entry_idx, int is_list, bool t210b01) { char *bl33_env = (char *)BL33_ENV_BASE; bl33_env[0] = '\0'; char val[4] = {0}; // Parse ini section and prepare BL33 env. LIST_FOREACH_ENTRY(ini_kv_t, kv, &ini_sec->kvs, link) { if (!strcmp("boot_prefixes", kv->key)) ctxt->path = kv->val; else if (!strcmp("ram_oc", kv->key)) { ctxt->ram_oc_txt = kv->val; ctxt->ram_oc_freq = atoi(kv->val); } else if (!strcmp("ram_oc_vdd2", kv->key)) { ctxt->ram_oc_vdd2 = atoi(kv->val); if (t210b01 && ctxt->ram_oc_vdd2 > DRAM_VDD2_OC_MAX_VOLTAGE) ctxt->ram_oc_vdd2 = DRAM_VDD2_OC_MAX_VOLTAGE; else if (!t210b01 && ctxt->ram_oc_vdd2 > DRAM_VDD2Q_OC_MAX_VOLTAGE) ctxt->ram_oc_vdd2 = DRAM_VDD2Q_OC_MAX_VOLTAGE; else if (ctxt->ram_oc_vdd2 < DRAM_VDD2_OC_MIN_VOLTAGE) ctxt->ram_oc_vdd2 = DRAM_VDD2_OC_MIN_VOLTAGE; } else if (!strcmp("ram_oc_vddq", kv->key)) { ctxt->ram_oc_vddq = atoi(kv->val); if (ctxt->ram_oc_vddq > DRAM_VDDQ_OC_MAX_VOLTAGE) ctxt->ram_oc_vddq = DRAM_VDDQ_OC_MAX_VOLTAGE; else if (ctxt->ram_oc_vddq < DRAM_VDDQ_OC_MIN_VOLTAGE) ctxt->ram_oc_vddq = 0; } else if (!strcmp("ram_oc_opt", kv->key)) ctxt->ram_oc_opt = atoi(kv->val); else if (!strcmp("uart_port", kv->key)) ctxt->serial_port = atoi(kv->val); // Set key/val to BL33 env. _l4t_bl33_cfg_set_key(bl33_env, kv->key, kv->val); } #ifdef DEBUG_UART_PORT // Override port if bootloader UART debug is enabled. ctxt->serial_port = DEBUG_UART_PORT + 1; #endif // Set r2p parameters. if (entry_idx >= 10) { val[0] = '1'; val[1] = '0' + (entry_idx % 10); } else val[0] = '0' + entry_idx; _l4t_bl33_cfg_set_key(bl33_env, "autoboot", val); // NULL terminate at single char for the next env sets. val[1] = 0; val[0] = '0' + is_list; _l4t_bl33_cfg_set_key(bl33_env, "autoboot_list", val); val[0] = '0' + L4T_LOADER_API_REV; _l4t_bl33_cfg_set_key(bl33_env, "loader_rev", val); // Enable BL33 memory env import. *(u32 *)(BL33_ENV_MAGIC_OFFSET) = BL33_ENV_MAGIC; // Set boot path. sd_path = (char *)malloc(512); sd_path_len = strlen(ctxt->path); strcpy(sd_path, ctxt->path); } void launch_l4t(const ini_sec_t *ini_sec, int entry_idx, int is_list, bool t210b01) { l4t_ctxt_t ctxt = {0}; bl_v1_params_t bl_v1_params = {0}; plat_params_from_bl2_t plat_params = {0}; entry_point_info_t bl33_ep_info = {0}; gfx_con_setpos(0, 0); // Parse config. _l4t_set_config(&ctxt, ini_sec, entry_idx, is_list, t210b01); if (!ctxt.path) { _l4t_crit_error("Path missing", false); return; } // Get MTC table. ctxt.mtc_table = minerva_get_mtc_table(); if (!t210b01 && !ctxt.mtc_table) { _l4t_crit_error("Minerva missing", true); return; } // Load BL31 (ATF/TrustZone fw). if (!_l4t_sd_load(BL31_FW)) { _l4t_crit_error("BL31 missing", false); return; } // Load BL33 (U-BOOT/CBOOT). if (!_l4t_sd_load(BL33_FW)) { _l4t_crit_error("BL33 missing", false); return; } // Set firmware path. strcpy(sd_path, "bootloader/sys/l4t/"); sd_path_len = strlen(sd_path); if (!t210b01) { // Load SC7-Entry firmware. ctxt.sc7entry_size = _l4t_sd_load(SC7ENTRY_FW); if (!ctxt.sc7entry_size) { _l4t_crit_error("loading SC7-Entry", true); return; } // Load BPMP-FW. Does power management. if (!_l4t_sd_load(BPMPFW_FW)) { _l4t_crit_error("loading BPMP-FW", true); return; } } else { // Load BPMP-FW. Manages SC7-Entry also. if (!_l4t_sd_load(BPMPFW_B01_FW)) { _l4t_crit_error("loading BPMP-FW", true); return; } // Load BPMP-FW MTC table. if (!_l4t_sd_load(BPMPFW_B01_MTC_TBL)) { _l4t_crit_error("loading BPMP-FW MTC", true); return; } } // Load SC7-Exit firmware. if (!_l4t_sd_load(!t210b01 ? SC7EXIT_FW : SC7EXIT_B01_FW)) { _l4t_crit_error("loading SC7-Exit", true); return; } // Set SC7-Exit firmware address to PMC for bootrom and do further setup. if (!_l4t_sc7_exit_config(t210b01)) return; // Done loading bootloaders/firmware. sd_end(); // We don't need AHB aperture open. mc_disable_ahb_redirect(); // Enable debug port. if (ctxt.serial_port) { pinmux_config_uart(ctxt.serial_port - 1); clock_enable_uart(ctxt.serial_port - 1); } // Restore UARTB/C TX pins to SPIO. gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO); gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO); // Configure BL33 parameters. if (*(u32 *)BL33_DTB_BASE == DTB_MAGIC) { // Defaults are for UARTA. char *bl33_serial_port = NULL; switch (ctxt.serial_port) { case 0: // Disable. break; case 1: // UARTA. bl33_serial_port = "70006000"; break; case 2: // UARTB. bl33_serial_port = "70006040"; break; case 3: // UARTC. bl33_serial_port = "70006200"; break; } if (bl33_serial_port) { BL33_DTB_SET_STDOUT_PATH(bl33_serial_port); BL33_DTB_SET_STDERR_PATH(bl33_serial_port); BL33_DTB_SET_UART_STATUS(ctxt.serial_port); } } // Set BL31 params. bl_v1_params.hdr.type = PARAM_BL31; bl_v1_params.hdr.version = VERSION_1; bl_v1_params.hdr.size = sizeof(bl_v1_params_t); bl_v1_params.hdr.attr = PARAM_EP_SECURE; bl_v1_params.bl33_ep_info = (u64)(u32)&bl33_ep_info; // Set BL33 params. bl33_ep_info.hdr.type = PARAM_EP; bl33_ep_info.hdr.version = VERSION_1; bl33_ep_info.hdr.size = sizeof(entry_point_info_t); bl33_ep_info.hdr.attr = PARAM_EP_NON_SECURE; bl33_ep_info.pc = BL33_LOAD_BASE; bl33_ep_info.spsr = SPSR_EL2T; // Set Platform parameters. plat_params.tzdram_base = TZDRAM_BASE; plat_params.tzdram_size = TZDRAM_SIZE; #if DEBUG_LOG_ATF plat_params.uart_id = ctxt.serial_port; #endif if (!t210b01) { // Set SC7-Entry fw parameters. For now BPMP-FW is not used on T210. plat_params.sc7entry_fw_base = SC7ENTRY_HDR_BASE; plat_params.sc7entry_fw_size = ALIGN(ctxt.sc7entry_size + SC7ENTRY_HDR_SIZE, SZ_PAGE); } // Enable below features. plat_params.enable_extra_features = BL31_EXTRA_FEATURES_ENABLE; if (!t210b01) { // Set R2P payload. reloc_meta_t *reloc = (reloc_meta_t *)(IPL_LOAD_ADDR + 0x7C); memcpy((u8 *)R2P_PAYLOAD_BASE, (u8 *)reloc->start, reloc->end - reloc->start); memset((u8 *)R2P_PAYLOAD_BASE + 0x94, 0, sizeof(boot_cfg_t)); // Clear Boot Config Storage. // Set R2P payload fw parameters. plat_params.r2p_payload_base = R2P_PAYLOAD_BASE; plat_params.r2p_payload_size = ALIGN(reloc->end - reloc->start, SZ_PAGE); } // Set PMC access security. NS is mandatory for T210B01. plat_params.flags = FLAGS_PMC_NON_SECURE; // Unsecure it unconditionally to reduce SMC calls to a minimum. // Lift SC7 placement restrictions. Disables TZDRAM increased carveout too. plat_params.flags |= FLAGS_SC7_NO_BASE_RESTRICTION; // Prepare EMC table. if (ctxt.mtc_table) { // Set DRAM voltage. if (ctxt.ram_oc_vdd2) max7762x_regulator_set_voltage(REGULATOR_SD1, ctxt.ram_oc_vdd2 * 1000); // Train the rest of the table, apply FSP WAR, set RAM to 800 MHz. minerva_prep_boot_l4t(ctxt.ram_oc_freq, ctxt.ram_oc_opt); // Set emc table parameters and copy it. int table_entries = minerva_get_mtc_table_entries(); plat_params.emc_table_base = MTCTABLE_BASE; plat_params.emc_table_size = sizeof(emc_table_t) * table_entries; memcpy((u32 *)MTCTABLE_BASE, ctxt.mtc_table, sizeof(emc_table_t) * table_entries); } // Set and enable IRAM based BL31 config. PMC(APBDEV_PMC_SECURE_SCRATCH112) = PMC(APBDEV_PMC_SECURE_SCRATCH108); PMC(APBDEV_PMC_SECURE_SCRATCH114) = PMC(APBDEV_PMC_SECURE_SCRATCH109); PMC(APBDEV_PMC_SECURE_SCRATCH108) = (u32)&bl_v1_params; PMC(APBDEV_PMC_SECURE_SCRATCH109) = (u32)&plat_params; PMC(APBDEV_PMC_SECURE_SCRATCH110) = BL31_IRAM_PARAMS; // Set panel model. PMC(APBDEV_PMC_SECURE_SCRATCH113) = display_get_decoded_panel_id(); // Set charging limit parameters. if (fuse_read_hw_type() == FUSE_NX_HW_TYPE_HOAG) { int in_volt_lim = 0; bq24193_get_property(BQ24193_ChargeVoltageLimit, &in_volt_lim); PMC(APBDEV_PMC_SECURE_SCRATCH113) |= in_volt_lim << 16; } // Disable writes to above registers. PMC(APBDEV_PMC_SEC_DISABLE8) |= BIT(18) | BIT(16) | BIT(12) | BIT(10) | BIT(8); // Set BPMP-FW parameters. if (t210b01) _l4t_bpmpfw_b01_config(&ctxt); // Set carveouts and save them to PMC for SC7 Exit. _l4t_mc_config_carveout(t210b01); // Deinit any unneeded HW. hw_reinit_workaround(false, BL_MAGIC_L4TLDR_SLD); // Do late hardware config. _l4t_late_hw_config(t210b01); if (t210b01) { // Launch BL31. ccplex_boot_cpu0(TZDRAM_COLD_ENTRY, true); // Enable Wrap burst for BPMP, GPU and PCIE. MSELECT(MSELECT_CONFIG) = (MSELECT(MSELECT_CONFIG) & (~(MSELECT_CFG_ERR_RESP_EN_GPU | MSELECT_CFG_ERR_RESP_EN_PCIE))) | (MSELECT_CFG_WRAP_TO_INCR_GPU | MSELECT_CFG_WRAP_TO_INCR_PCIE | MSELECT_CFG_WRAP_TO_INCR_BPMP); // For T210B01, prep reset vector for SC7 save state and start BPMP-FW. EXCP_VEC(EVP_COP_RESET_VECTOR) = BPMPFW_B01_ENTRYPOINT; void (*bpmp_fw_ptr)() = (void *)BPMPFW_B01_ENTRYPOINT; (*bpmp_fw_ptr)(); } else { // If T210, BPMP-FW runs BL31. void (*bpmp_fw_ptr)() = (void *)BPMPFW_ENTRYPOINT; (*bpmp_fw_ptr)(); } // Halt BPMP. while (true) bpmp_halt(); }