summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/atf.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-imx/atf.c')
-rw-r--r--arch/arm/mach-imx/atf.c418
1 files changed, 398 insertions, 20 deletions
diff --git a/arch/arm/mach-imx/atf.c b/arch/arm/mach-imx/atf.c
index 4b6ac8a2ae..8b80460268 100644
--- a/arch/arm/mach-imx/atf.c
+++ b/arch/arm/mach-imx/atf.c
@@ -1,5 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <asm/sections.h>
#include <common.h>
-#include <mach/atf.h>
+#include <firmware.h>
+#include <mach/imx/atf.h>
+#include <mach/imx/generic.h>
+#include <mach/imx/xload.h>
+#include <mach/imx/romapi.h>
+#include <mach/imx/esdctl.h>
+#include <asm-generic/memory_layout.h>
+#include <asm/barebox-arm.h>
+#include <mach/imx/imx8m-regs.h>
+#include <mach/imx/imx9-regs.h>
+#include <soc/fsl/fsl_udc.h>
+#include <soc/fsl/caam.h>
+#include <tee/optee.h>
+#include <mach/imx/ele.h>
/**
* imx8m_atf_load_bl31 - Load ATF BL31 blob and transfer control to it
@@ -11,28 +27,29 @@
* This function:
*
* 1. Copies built-in BL31 blob to an address i.MX8M's BL31
- * expects to be placed
+ * expects to be placed (TF-A v2.8+ is position-independent)
*
* 2. Sets up temporary stack pointer for EL2, which is execution
* level that BL31 will drop us off at after it completes its
* initialization routine
*
* 3. Transfers control to BL31
- *
- * NOTE: This function expects NXP's implementation of ATF that can be
- * found at:
- * https://source.codeaurora.org/external/imx/imx-atf
- *
- * any other implementation may or may not work
- *
*/
-static void imx8m_atf_load_bl31(const void *fw, size_t fw_size, void *atf_dest)
+static __noreturn void imx8m_atf_start_bl31(const void *fw, size_t fw_size, void *atf_dest)
{
void __noreturn (*bl31)(void) = atf_dest;
+ int ret;
- if (WARN_ON(fw_size > MX8M_ATF_BL31_SIZE_LIMIT))
- return;
+ BUG_ON(fw_size > MX8M_ATF_BL31_SIZE_LIMIT);
+
+ if (IS_ENABLED(CONFIG_FSL_CAAM_RNG_PBL_INIT)) {
+ ret = imx_early_caam_init(MX8M_CAAM_BASE_ADDR);
+ if (ret)
+ pr_debug("CAAM early init failed: %d\n", ret);
+ else
+ pr_debug("CAAM early init successful\n");
+ }
memcpy(bl31, fw, fw_size);
@@ -40,24 +57,385 @@ static void imx8m_atf_load_bl31(const void *fw, size_t fw_size, void *atf_dest)
"r" (atf_dest - 16) :
"cc");
bl31();
+ __builtin_unreachable();
}
-void imx8mm_atf_load_bl31(const void *fw, size_t fw_size)
+void imx8mm_load_bl33(void *bl33)
{
- imx8m_atf_load_bl31(fw, fw_size, (void *)MX8MM_ATF_BL31_BASE_ADDR);
+ enum bootsource src;
+ int instance;
+
+ imx8mm_get_boot_source(&src, &instance);
+ switch (src) {
+ case BOOTSOURCE_MMC:
+ imx8m_esdhc_load_image(instance, bl33);
+ break;
+ case BOOTSOURCE_SERIAL:
+ if (!IS_ENABLED(CONFIG_USB_GADGET_DRIVER_ARC_PBL)) {
+ printf("Serial bootmode not supported\n");
+ break;
+ }
+
+ /*
+ * Traditionally imx-usb-loader sends the PBL twice. The first
+ * PBL is loaded to OCRAM and executed. Then the full barebox
+ * image including the PBL is sent again and received here. We
+ * might change that in the future in imx-usb-loader so that the
+ * PBL is sent only once and we only receive the rest of the
+ * image here. To prepare that step we check if we get a full
+ * barebox image or piggydata only. When it's piggydata only move
+ * it to the place where it would be if it would have been a
+ * full image.
+ */
+ imx8mm_barebox_load_usb(bl33);
+
+ if (!strcmp("barebox", bl33 + 0x20)) {
+ /* Found the barebox marker, so this is a PBL + piggydata */
+ pr_debug("received PBL + piggydata\n");
+ } else {
+ /* no barebox marker, so this is piggydata only */
+ pr_debug("received piggydata\n");
+ memmove(bl33 + barebox_pbl_size, bl33,
+ barebox_image_size - barebox_pbl_size);
+ }
+
+ break;
+ case BOOTSOURCE_SPI:
+ imx8mm_qspi_load_image(instance, bl33);
+ break;
+ default:
+ printf("Unsupported bootsource BOOTSOURCE_%d\n", src);
+ hang();
+ }
+
+ /*
+ * On completion the TF-A will jump to MX8M_ATF_BL33_BASE_ADDR
+ * in EL2. Copy the image there, but replace the PBL part of
+ * that image with ourselves. On a high assurance boot only the
+ * currently running code is validated and contains the checksum
+ * for the piggy data, so we need to ensure that we are running
+ * the same code in DRAM.
+ */
+ memcpy(bl33, __image_start, barebox_pbl_size);
}
-void imx8mn_atf_load_bl31(const void *fw, size_t fw_size)
+static void imx_adjust_optee_memory(void **bl32, void **bl32_image, size_t *bl32_size)
{
- imx8m_atf_load_bl31(fw, fw_size, (void *)MX8MN_ATF_BL31_BASE_ADDR);
+ struct optee_header *hdr = *bl32_image;
+ u64 membase;
+
+ if (optee_verify_header(hdr))
+ return;
+
+ imx_scratch_save_optee_hdr(hdr);
+
+ membase = (u64)hdr->init_load_addr_hi << 32;
+ membase |= hdr->init_load_addr_lo;
+
+ *bl32 = (void *)membase;
+ *bl32_size -= sizeof(*hdr);
+ *bl32_image += sizeof(*hdr);
}
-void imx8mp_atf_load_bl31(const void *fw, size_t fw_size)
+__noreturn void imx8mm_load_and_start_image_via_tfa(void)
{
- imx8m_atf_load_bl31(fw, fw_size, (void *)MX8MP_ATF_BL31_BASE_ADDR);
+ __imx8mm_load_and_start_image_via_tfa((void *)MX8M_ATF_BL33_BASE_ADDR);
}
-void imx8mq_atf_load_bl31(const void *fw, size_t fw_size)
+__noreturn void __imx8mm_load_and_start_image_via_tfa(void *bl33)
{
- imx8m_atf_load_bl31(fw, fw_size, (void *)MX8MQ_ATF_BL31_BASE_ADDR);
+ const void *bl31;
+ size_t bl31_size;
+ unsigned long endmem = MX8M_DDR_CSD1_BASE_ADDR + imx8m_barebox_earlymem_size(32);
+
+ imx_set_cpu_type(IMX_CPU_IMX8MM);
+ imx8mm_init_scratch_space();
+ imx8m_save_bootrom_log();
+ imx8mm_load_bl33(bl33);
+
+ if (IS_ENABLED(CONFIG_FIRMWARE_IMX8MM_OPTEE)) {
+ void *bl32 = (void *)arm_mem_optee(endmem);
+ size_t bl32_size;
+ void *bl32_image;
+
+ imx8m_tzc380_init();
+ get_builtin_firmware_ext(imx8mm_bl32_bin,
+ bl33, &bl32_image,
+ &bl32_size);
+
+ imx_adjust_optee_memory(&bl32, &bl32_image, &bl32_size);
+
+ memcpy(bl32, bl32_image, bl32_size);
+
+ get_builtin_firmware(imx8mm_bl31_bin_optee, &bl31, &bl31_size);
+ } else {
+ get_builtin_firmware(imx8mm_bl31_bin, &bl31, &bl31_size);
+ }
+
+ imx8m_atf_start_bl31(bl31, bl31_size, (void *)MX8MM_ATF_BL31_BASE_ADDR);
+}
+
+void imx8mp_load_bl33(void *bl33)
+{
+ enum bootsource src;
+ int instance;
+
+ imx8mp_get_boot_source(&src, &instance);
+ switch (src) {
+ case BOOTSOURCE_MMC:
+ imx8mp_esdhc_load_image(instance, bl33);
+ break;
+ case BOOTSOURCE_SERIAL:
+ imx8mp_romapi_load_image(bl33);
+ break;
+ case BOOTSOURCE_SPI:
+ imx8mp_qspi_load_image(instance, bl33);
+ break;
+ default:
+ printf("Unhandled bootsource BOOTSOURCE_%d\n", src);
+ hang();
+ }
+
+
+ /*
+ * On completion the TF-A will jump to MX8M_ATF_BL33_BASE_ADDR
+ * in EL2. Copy the image there, but replace the PBL part of
+ * that image with ourselves. On a high assurance boot only the
+ * currently running code is validated and contains the checksum
+ * for the piggy data, so we need to ensure that we are running
+ * the same code in DRAM.
+ */
+ memcpy(bl33, __image_start, barebox_pbl_size);
+}
+
+__noreturn void imx8mp_load_and_start_image_via_tfa(void)
+{
+ __imx8mp_load_and_start_image_via_tfa((void *)MX8M_ATF_BL33_BASE_ADDR);
+}
+
+__noreturn void __imx8mp_load_and_start_image_via_tfa(void *bl33)
+{
+ const void *bl31;
+ size_t bl31_size;
+ unsigned long endmem = MX8M_DDR_CSD1_BASE_ADDR + imx8m_barebox_earlymem_size(32);
+
+ imx_set_cpu_type(IMX_CPU_IMX8MP);
+ imx8mp_init_scratch_space();
+ imx8m_save_bootrom_log();
+ imx8mp_load_bl33(bl33);
+
+ if (IS_ENABLED(CONFIG_FIRMWARE_IMX8MP_OPTEE)) {
+ void *bl32 = (void *)arm_mem_optee(endmem);
+ size_t bl32_size;
+ void *bl32_image;
+
+ imx8m_tzc380_init();
+ get_builtin_firmware_ext(imx8mp_bl32_bin,
+ bl33, &bl32_image,
+ &bl32_size);
+
+ imx_adjust_optee_memory(&bl32, &bl32_image, &bl32_size);
+
+ memcpy(bl32, bl32_image, bl32_size);
+
+ get_builtin_firmware(imx8mp_bl31_bin_optee, &bl31, &bl31_size);
+ } else {
+ get_builtin_firmware(imx8mp_bl31_bin, &bl31, &bl31_size);
+ }
+
+ imx8m_atf_start_bl31(bl31, bl31_size, (void *)MX8MP_ATF_BL31_BASE_ADDR);
+}
+
+
+void imx8mn_load_bl33(void *bl33)
+{
+ enum bootsource src;
+ int instance;
+
+ imx8mn_get_boot_source(&src, &instance);
+ switch (src) {
+ case BOOTSOURCE_MMC:
+ imx8mn_esdhc_load_image(instance, bl33);
+ break;
+ case BOOTSOURCE_SERIAL:
+ imx8mn_romapi_load_image(bl33);
+ break;
+ case BOOTSOURCE_SPI:
+ imx8mn_qspi_load_image(instance, bl33);
+ break;
+ default:
+ printf("Unhandled bootsource BOOTSOURCE_%d\n", src);
+ hang();
+ }
+
+
+ /*
+ * On completion the TF-A will jump to MX8M_ATF_BL33_BASE_ADDR
+ * in EL2. Copy the image there, but replace the PBL part of
+ * that image with ourselves. On a high assurance boot only the
+ * currently running code is validated and contains the checksum
+ * for the piggy data, so we need to ensure that we are running
+ * the same code in DRAM.
+ */
+ memcpy(bl33, __image_start, barebox_pbl_size);
+}
+
+__noreturn void imx8mn_load_and_start_image_via_tfa(void)
+{
+ __imx8mn_load_and_start_image_via_tfa((void *)MX8M_ATF_BL33_BASE_ADDR);
+}
+
+__noreturn void __imx8mn_load_and_start_image_via_tfa(void *bl33)
+{
+ const void *bl31;
+ size_t bl31_size;
+ unsigned long endmem = MX8M_DDR_CSD1_BASE_ADDR + imx8m_barebox_earlymem_size(16);
+
+ imx_set_cpu_type(IMX_CPU_IMX8MN);
+ imx8mn_init_scratch_space();
+ imx8m_save_bootrom_log();
+ imx8mn_load_bl33(bl33);
+
+ if (IS_ENABLED(CONFIG_FIRMWARE_IMX8MN_OPTEE)) {
+ void *bl32 = (void *)arm_mem_optee(endmem);
+ size_t bl32_size;
+ void *bl32_image;
+
+ imx8m_tzc380_init();
+ get_builtin_firmware_ext(imx8mn_bl32_bin,
+ bl33, &bl32_image,
+ &bl32_size);
+
+ imx_adjust_optee_memory(&bl32, &bl32_image, &bl32_size);
+
+ memcpy(bl32, bl32_image, bl32_size);
+
+ get_builtin_firmware(imx8mn_bl31_bin_optee, &bl31, &bl31_size);
+ } else {
+ get_builtin_firmware(imx8mn_bl31_bin, &bl31, &bl31_size);
+ }
+
+ imx8m_atf_start_bl31(bl31, bl31_size, (void *)MX8MN_ATF_BL31_BASE_ADDR);
+}
+
+void imx8mq_load_bl33(void *bl33)
+{
+ enum bootsource src;
+ int instance;
+
+ imx8mq_get_boot_source(&src, &instance);
+ switch (src) {
+ case BOOTSOURCE_MMC:
+ imx8m_esdhc_load_image(instance, bl33);
+ break;
+ default:
+ printf("Unhandled bootsource BOOTSOURCE_%d\n", src);
+ hang();
+ }
+
+
+ /*
+ * On completion the TF-A will jump to MX8M_ATF_BL33_BASE_ADDR
+ * in EL2. Copy the image there, but replace the PBL part of
+ * that image with ourselves. On a high assurance boot only the
+ * currently running code is validated and contains the checksum
+ * for the piggy data, so we need to ensure that we are running
+ * the same code in DRAM.
+ */
+ memcpy(bl33, __image_start, barebox_pbl_size);
+}
+
+__noreturn void imx8mq_load_and_start_image_via_tfa(void)
+{
+ __imx8mq_load_and_start_image_via_tfa((void *)MX8M_ATF_BL33_BASE_ADDR);
+}
+
+__noreturn void __imx8mq_load_and_start_image_via_tfa(void *bl33)
+{
+ const void *bl31;
+ size_t bl31_size;
+ unsigned long endmem = MX8M_DDR_CSD1_BASE_ADDR + imx8m_barebox_earlymem_size(32);
+
+ imx_set_cpu_type(IMX_CPU_IMX8MQ);
+ imx8mq_init_scratch_space();
+ imx8m_save_bootrom_log();
+ imx8mq_load_bl33(bl33);
+
+ if (IS_ENABLED(CONFIG_FIRMWARE_IMX8MQ_OPTEE)) {
+ void *bl32 = (void *)arm_mem_optee(endmem);
+ size_t bl32_size;
+ void *bl32_image;
+
+ imx8m_tzc380_init();
+ get_builtin_firmware_ext(imx8mq_bl32_bin,
+ bl33, &bl32_image,
+ &bl32_size);
+
+ imx_adjust_optee_memory(&bl32, &bl32_image, &bl32_size);
+
+ memcpy(bl32, bl32_image, bl32_size);
+
+ get_builtin_firmware(imx8mq_bl31_bin_optee, &bl31, &bl31_size);
+ } else {
+ get_builtin_firmware(imx8mq_bl31_bin, &bl31, &bl31_size);
+ }
+
+ imx8m_atf_start_bl31(bl31, bl31_size, (void *)MX8MQ_ATF_BL31_BASE_ADDR);
+}
+
+void __noreturn imx93_load_and_start_image_via_tfa(void)
+{
+ unsigned long atf_dest = MX93_ATF_BL31_BASE_ADDR;
+ void __noreturn (*bl31)(void) = (void *)atf_dest;
+ const void *tfa;
+ size_t tfa_size;
+ void *bl33 = (void *)MX93_ATF_BL33_BASE_ADDR;
+ unsigned long endmem = MX9_DDR_CSD1_BASE_ADDR + imx9_ddrc_sdram_size();
+
+ imx_set_cpu_type(IMX_CPU_IMX93);
+ imx93_init_scratch_space(true);
+
+ /*
+ * On completion the TF-A will jump to MX93_ATF_BL33_BASE_ADDR
+ * in EL2. Copy the image there, but replace the PBL part of
+ * that image with ourselves. On a high assurance boot only the
+ * currently running code is validated and contains the checksum
+ * for the piggy data, so we need to ensure that we are running
+ * the same code in DRAM.
+ *
+ * The second purpose of this memcpy is for USB booting. When booting
+ * from USB the image comes in as a stream, so the PBL is transferred
+ * only once. As we jump into the PBL again in SDRAM we need to copy
+ * it there. The USB protocol transfers data in chunks of 1024 bytes,
+ * so align the copy size up to the next 1KiB boundary.
+ */
+ memcpy((void *)MX93_ATF_BL33_BASE_ADDR, __image_start, ALIGN(barebox_pbl_size, 1024));
+
+ if (IS_ENABLED(CONFIG_FIRMWARE_IMX93_OPTEE)) {
+ void *bl32 = (void *)arm_mem_optee(endmem);
+ size_t bl32_size;
+ void *bl32_image;
+
+ imx93_ele_load_fw(bl33);
+
+ get_builtin_firmware_ext(imx93_bl32_bin,
+ bl33, &bl32_image,
+ &bl32_size);
+
+ imx_adjust_optee_memory(&bl32, &bl32_image, &bl32_size);
+
+ memcpy(bl32, bl32_image, bl32_size);
+
+ get_builtin_firmware(imx93_bl31_bin_optee, &tfa, &tfa_size);
+ } else {
+ get_builtin_firmware(imx93_bl31_bin, &tfa, &tfa_size);
+ }
+
+ memcpy(bl31, tfa, tfa_size);
+
+ asm volatile("msr sp_el2, %0" : :
+ "r" (MX93_ATF_BL33_BASE_ADDR - 16) :
+ "cc");
+ bl31();
+ __builtin_unreachable();
}