summaryrefslogtreecommitdiffstats
path: root/plat/st/common
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:13:47 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:13:47 +0000
commit102b0d2daa97dae68d3eed54d8fe37a9cc38a892 (patch)
treebcf648efac40ca6139842707f0eba5a4496a6dd2 /plat/st/common
parentInitial commit. (diff)
downloadarm-trusted-firmware-102b0d2daa97dae68d3eed54d8fe37a9cc38a892.tar.xz
arm-trusted-firmware-102b0d2daa97dae68d3eed54d8fe37a9cc38a892.zip
Adding upstream version 2.8.0+dfsg.upstream/2.8.0+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plat/st/common')
-rw-r--r--plat/st/common/bl2_io_storage.c731
-rw-r--r--plat/st/common/include/stm32cubeprogrammer.h29
-rw-r--r--plat/st/common/include/stm32mp_common.h129
-rw-r--r--plat/st/common/include/stm32mp_dt.h46
-rw-r--r--plat/st/common/include/stm32mp_efi.h15
-rw-r--r--plat/st/common/include/stm32mp_fconf_getter.h31
-rw-r--r--plat/st/common/include/stm32mp_io_storage.h27
-rw-r--r--plat/st/common/include/stm32mp_shared_resources.h58
-rw-r--r--plat/st/common/include/usb_dfu.h80
-rw-r--r--plat/st/common/stm32cubeprogrammer_uart.c520
-rw-r--r--plat/st/common/stm32cubeprogrammer_usb.c197
-rw-r--r--plat/st/common/stm32mp_common.c279
-rw-r--r--plat/st/common/stm32mp_crypto_lib.c661
-rw-r--r--plat/st/common/stm32mp_dt.c429
-rw-r--r--plat/st/common/stm32mp_fconf_io.c181
-rw-r--r--plat/st/common/stm32mp_trusted_boot.c204
-rw-r--r--plat/st/common/usb_dfu.c538
17 files changed, 4155 insertions, 0 deletions
diff --git a/plat/st/common/bl2_io_storage.c b/plat/st/common/bl2_io_storage.c
new file mode 100644
index 0000000..b271ed6
--- /dev/null
+++ b/plat/st/common/bl2_io_storage.c
@@ -0,0 +1,731 @@
+/*
+ * Copyright (c) 2015-2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <common/desc_image_load.h>
+#include <drivers/fwu/fwu.h>
+#include <drivers/fwu/fwu_metadata.h>
+#include <drivers/io/io_block.h>
+#include <drivers/io/io_driver.h>
+#include <drivers/io/io_encrypted.h>
+#include <drivers/io/io_fip.h>
+#include <drivers/io/io_memmap.h>
+#include <drivers/io/io_mtd.h>
+#include <drivers/io/io_storage.h>
+#include <drivers/mmc.h>
+#include <drivers/partition/efi.h>
+#include <drivers/partition/partition.h>
+#include <drivers/raw_nand.h>
+#include <drivers/spi_nand.h>
+#include <drivers/spi_nor.h>
+#include <drivers/st/io_mmc.h>
+#include <drivers/st/stm32_fmc2_nand.h>
+#include <drivers/st/stm32_qspi.h>
+#include <drivers/st/stm32_sdmmc2.h>
+#include <drivers/usb_device.h>
+#include <lib/fconf/fconf.h>
+#include <lib/mmio.h>
+#include <lib/utils.h>
+#include <plat/common/platform.h>
+#include <tools_share/firmware_image_package.h>
+
+#include <platform_def.h>
+#include <stm32cubeprogrammer.h>
+#include <stm32mp_efi.h>
+#include <stm32mp_fconf_getter.h>
+#include <stm32mp_io_storage.h>
+#include <usb_dfu.h>
+
+/* IO devices */
+uintptr_t fip_dev_handle;
+uintptr_t storage_dev_handle;
+
+static const io_dev_connector_t *fip_dev_con;
+
+#ifndef DECRYPTION_SUPPORT_none
+static const io_dev_connector_t *enc_dev_con;
+uintptr_t enc_dev_handle;
+#endif
+
+#if STM32MP_SDMMC || STM32MP_EMMC
+static struct mmc_device_info mmc_info;
+
+static uint32_t block_buffer[MMC_BLOCK_SIZE] __aligned(MMC_BLOCK_SIZE);
+
+static io_block_dev_spec_t mmc_block_dev_spec = {
+ /* It's used as temp buffer in block driver */
+ .buffer = {
+ .offset = (size_t)&block_buffer,
+ .length = MMC_BLOCK_SIZE,
+ },
+ .ops = {
+ .read = mmc_read_blocks,
+ .write = NULL,
+ },
+ .block_size = MMC_BLOCK_SIZE,
+};
+
+static const io_dev_connector_t *mmc_dev_con;
+#endif /* STM32MP_SDMMC || STM32MP_EMMC */
+
+#if STM32MP_SPI_NOR
+static io_mtd_dev_spec_t spi_nor_dev_spec = {
+ .ops = {
+ .init = spi_nor_init,
+ .read = spi_nor_read,
+ },
+};
+#endif
+
+#if STM32MP_RAW_NAND
+static io_mtd_dev_spec_t nand_dev_spec = {
+ .ops = {
+ .init = nand_raw_init,
+ .read = nand_read,
+ .seek = nand_seek_bb
+ },
+};
+
+static const io_dev_connector_t *nand_dev_con;
+#endif
+
+#if STM32MP_SPI_NAND
+static io_mtd_dev_spec_t spi_nand_dev_spec = {
+ .ops = {
+ .init = spi_nand_init,
+ .read = nand_read,
+ .seek = nand_seek_bb
+ },
+};
+#endif
+
+#if STM32MP_SPI_NAND || STM32MP_SPI_NOR
+static const io_dev_connector_t *spi_dev_con;
+#endif
+
+#if STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER
+static const io_dev_connector_t *memmap_dev_con;
+#endif
+
+io_block_spec_t image_block_spec = {
+ .offset = 0U,
+ .length = 0U,
+};
+
+int open_fip(const uintptr_t spec)
+{
+ return io_dev_init(fip_dev_handle, (uintptr_t)FIP_IMAGE_ID);
+}
+
+#ifndef DECRYPTION_SUPPORT_none
+int open_enc_fip(const uintptr_t spec)
+{
+ int result;
+ uintptr_t local_image_handle;
+
+ result = io_dev_init(enc_dev_handle, (uintptr_t)ENC_IMAGE_ID);
+ if (result != 0) {
+ return result;
+ }
+
+ result = io_open(enc_dev_handle, spec, &local_image_handle);
+ if (result != 0) {
+ return result;
+ }
+
+ VERBOSE("Using encrypted FIP\n");
+ io_close(local_image_handle);
+
+ return 0;
+}
+#endif
+
+int open_storage(const uintptr_t spec)
+{
+ return io_dev_init(storage_dev_handle, 0);
+}
+
+#if STM32MP_EMMC_BOOT
+static uint32_t get_boot_part_fip_header(void)
+{
+ io_block_spec_t emmc_boot_fip_block_spec = {
+ .offset = STM32MP_EMMC_BOOT_FIP_OFFSET,
+ .length = MMC_BLOCK_SIZE, /* We are interested only in first 4 bytes */
+ };
+ uint32_t magic = 0U;
+ int io_result;
+ size_t bytes_read;
+ uintptr_t fip_hdr_handle;
+
+ io_result = io_open(storage_dev_handle, (uintptr_t)&emmc_boot_fip_block_spec,
+ &fip_hdr_handle);
+ assert(io_result == 0);
+
+ io_result = io_read(fip_hdr_handle, (uintptr_t)&magic, sizeof(magic),
+ &bytes_read);
+ if ((io_result != 0) || (bytes_read != sizeof(magic))) {
+ panic();
+ }
+
+ io_close(fip_hdr_handle);
+
+ VERBOSE("%s: eMMC boot magic at offset 256K: %08x\n",
+ __func__, magic);
+
+ return magic;
+}
+#endif
+
+static void print_boot_device(boot_api_context_t *boot_context)
+{
+ switch (boot_context->boot_interface_selected) {
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_SD:
+ INFO("Using SDMMC\n");
+ break;
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC:
+ INFO("Using EMMC\n");
+ break;
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI:
+ INFO("Using QSPI NOR\n");
+ break;
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC:
+ INFO("Using FMC NAND\n");
+ break;
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_QSPI:
+ INFO("Using SPI NAND\n");
+ break;
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART:
+ INFO("Using UART\n");
+ break;
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB:
+ INFO("Using USB\n");
+ break;
+ default:
+ ERROR("Boot interface %u not found\n",
+ boot_context->boot_interface_selected);
+ panic();
+ break;
+ }
+
+ if (boot_context->boot_interface_instance != 0U) {
+ INFO(" Instance %d\n", boot_context->boot_interface_instance);
+ }
+}
+
+#if STM32MP_SDMMC || STM32MP_EMMC
+static void boot_mmc(enum mmc_device_type mmc_dev_type,
+ uint16_t boot_interface_instance)
+{
+ int io_result __unused;
+ struct stm32_sdmmc2_params params;
+
+ zeromem(&params, sizeof(struct stm32_sdmmc2_params));
+
+ mmc_info.mmc_dev_type = mmc_dev_type;
+
+ switch (boot_interface_instance) {
+ case 1:
+ params.reg_base = STM32MP_SDMMC1_BASE;
+ break;
+ case 2:
+ params.reg_base = STM32MP_SDMMC2_BASE;
+ break;
+ case 3:
+ params.reg_base = STM32MP_SDMMC3_BASE;
+ break;
+ default:
+ WARN("SDMMC instance not found, using default\n");
+ if (mmc_dev_type == MMC_IS_SD) {
+ params.reg_base = STM32MP_SDMMC1_BASE;
+ } else {
+ params.reg_base = STM32MP_SDMMC2_BASE;
+ }
+ break;
+ }
+
+ if (mmc_dev_type != MMC_IS_EMMC) {
+ params.flags = MMC_FLAG_SD_CMD6;
+ }
+
+ params.device_info = &mmc_info;
+ if (stm32_sdmmc2_mmc_init(&params) != 0) {
+ ERROR("SDMMC%u init failed\n", boot_interface_instance);
+ panic();
+ }
+
+ /* Open MMC as a block device to read FIP */
+ io_result = register_io_dev_block(&mmc_dev_con);
+ if (io_result != 0) {
+ panic();
+ }
+
+ io_result = io_dev_open(mmc_dev_con, (uintptr_t)&mmc_block_dev_spec,
+ &storage_dev_handle);
+ assert(io_result == 0);
+
+#if STM32MP_EMMC_BOOT
+ if (mmc_dev_type == MMC_IS_EMMC) {
+ io_result = mmc_part_switch_current_boot();
+ assert(io_result == 0);
+
+ if (get_boot_part_fip_header() != TOC_HEADER_NAME) {
+ WARN("%s: Can't find FIP header on eMMC boot partition. Trying GPT\n",
+ __func__);
+ io_result = mmc_part_switch_user();
+ assert(io_result == 0);
+ return;
+ }
+
+ VERBOSE("%s: FIP header found on eMMC boot partition\n",
+ __func__);
+ image_block_spec.offset = STM32MP_EMMC_BOOT_FIP_OFFSET;
+ image_block_spec.length = mmc_boot_part_size() - STM32MP_EMMC_BOOT_FIP_OFFSET;
+ }
+#endif
+}
+#endif /* STM32MP_SDMMC || STM32MP_EMMC */
+
+#if STM32MP_SPI_NOR
+static void boot_spi_nor(boot_api_context_t *boot_context)
+{
+ int io_result __unused;
+
+ io_result = stm32_qspi_init();
+ assert(io_result == 0);
+
+ io_result = register_io_dev_mtd(&spi_dev_con);
+ assert(io_result == 0);
+
+ /* Open connections to device */
+ io_result = io_dev_open(spi_dev_con,
+ (uintptr_t)&spi_nor_dev_spec,
+ &storage_dev_handle);
+ assert(io_result == 0);
+}
+#endif /* STM32MP_SPI_NOR */
+
+#if STM32MP_RAW_NAND
+static void boot_fmc2_nand(boot_api_context_t *boot_context)
+{
+ int io_result __unused;
+
+ io_result = stm32_fmc2_init();
+ assert(io_result == 0);
+
+ /* Register the IO device on this platform */
+ io_result = register_io_dev_mtd(&nand_dev_con);
+ assert(io_result == 0);
+
+ /* Open connections to device */
+ io_result = io_dev_open(nand_dev_con, (uintptr_t)&nand_dev_spec,
+ &storage_dev_handle);
+ assert(io_result == 0);
+}
+#endif /* STM32MP_RAW_NAND */
+
+#if STM32MP_SPI_NAND
+static void boot_spi_nand(boot_api_context_t *boot_context)
+{
+ int io_result __unused;
+
+ io_result = stm32_qspi_init();
+ assert(io_result == 0);
+
+ io_result = register_io_dev_mtd(&spi_dev_con);
+ assert(io_result == 0);
+
+ /* Open connections to device */
+ io_result = io_dev_open(spi_dev_con,
+ (uintptr_t)&spi_nand_dev_spec,
+ &storage_dev_handle);
+ assert(io_result == 0);
+}
+#endif /* STM32MP_SPI_NAND */
+
+#if STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER
+static void mmap_io_setup(void)
+{
+ int io_result __unused;
+
+ io_result = register_io_dev_memmap(&memmap_dev_con);
+ assert(io_result == 0);
+
+ io_result = io_dev_open(memmap_dev_con, (uintptr_t)NULL,
+ &storage_dev_handle);
+ assert(io_result == 0);
+}
+
+#if STM32MP_UART_PROGRAMMER
+static void stm32cubeprogrammer_uart(void)
+{
+ int ret __unused;
+ boot_api_context_t *boot_context =
+ (boot_api_context_t *)stm32mp_get_boot_ctx_address();
+ uintptr_t uart_base;
+
+ uart_base = get_uart_address(boot_context->boot_interface_instance);
+ ret = stm32cubeprog_uart_load(uart_base, DWL_BUFFER_BASE, DWL_BUFFER_SIZE);
+ assert(ret == 0);
+}
+#endif
+
+#if STM32MP_USB_PROGRAMMER
+static void stm32cubeprogrammer_usb(void)
+{
+ int ret __unused;
+ struct usb_handle *pdev;
+
+ /* Init USB on platform */
+ pdev = usb_dfu_plat_init();
+
+ ret = stm32cubeprog_usb_load(pdev, DWL_BUFFER_BASE, DWL_BUFFER_SIZE);
+ assert(ret == 0);
+}
+#endif
+#endif /* STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER */
+
+
+void stm32mp_io_setup(void)
+{
+ int io_result __unused;
+ boot_api_context_t *boot_context =
+ (boot_api_context_t *)stm32mp_get_boot_ctx_address();
+
+ print_boot_device(boot_context);
+
+ if ((boot_context->boot_partition_used_toboot == 1U) ||
+ (boot_context->boot_partition_used_toboot == 2U)) {
+ INFO("Boot used partition fsbl%u\n",
+ boot_context->boot_partition_used_toboot);
+ }
+
+ io_result = register_io_dev_fip(&fip_dev_con);
+ assert(io_result == 0);
+
+ io_result = io_dev_open(fip_dev_con, (uintptr_t)NULL,
+ &fip_dev_handle);
+
+#ifndef DECRYPTION_SUPPORT_none
+ io_result = register_io_dev_enc(&enc_dev_con);
+ assert(io_result == 0);
+
+ io_result = io_dev_open(enc_dev_con, (uintptr_t)NULL,
+ &enc_dev_handle);
+ assert(io_result == 0);
+#endif
+
+ switch (boot_context->boot_interface_selected) {
+#if STM32MP_SDMMC
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_SD:
+ dmbsy();
+ boot_mmc(MMC_IS_SD, boot_context->boot_interface_instance);
+ break;
+#endif
+#if STM32MP_EMMC
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC:
+ dmbsy();
+ boot_mmc(MMC_IS_EMMC, boot_context->boot_interface_instance);
+ break;
+#endif
+#if STM32MP_SPI_NOR
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI:
+ dmbsy();
+ boot_spi_nor(boot_context);
+ break;
+#endif
+#if STM32MP_RAW_NAND
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC:
+ dmbsy();
+ boot_fmc2_nand(boot_context);
+ break;
+#endif
+#if STM32MP_SPI_NAND
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_QSPI:
+ dmbsy();
+ boot_spi_nand(boot_context);
+ break;
+#endif
+#if STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER
+#if STM32MP_UART_PROGRAMMER
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART:
+#endif
+#if STM32MP_USB_PROGRAMMER
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB:
+#endif
+ dmbsy();
+ mmap_io_setup();
+ break;
+#endif
+
+ default:
+ ERROR("Boot interface %d not supported\n",
+ boot_context->boot_interface_selected);
+ panic();
+ break;
+ }
+}
+
+int bl2_plat_handle_pre_image_load(unsigned int image_id)
+{
+ static bool gpt_init_done __unused;
+ uint16_t boot_itf = stm32mp_get_boot_itf_selected();
+
+ switch (boot_itf) {
+#if STM32MP_SDMMC || STM32MP_EMMC
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC:
+#if STM32MP_EMMC_BOOT
+ if (image_block_spec.offset == STM32MP_EMMC_BOOT_FIP_OFFSET) {
+ break;
+ }
+#endif
+ /* fallthrough */
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_SD:
+ if (!gpt_init_done) {
+/*
+ * With FWU Multi Bank feature enabled, the selection of
+ * the image to boot will be done by fwu_init calling the
+ * platform hook, plat_fwu_set_images_source.
+ */
+#if !PSA_FWU_SUPPORT
+ const partition_entry_t *entry;
+ const struct efi_guid img_type_guid = STM32MP_FIP_GUID;
+ uuid_t img_type_uuid;
+
+ guidcpy(&img_type_uuid, &img_type_guid);
+ partition_init(GPT_IMAGE_ID);
+ entry = get_partition_entry_by_type(&img_type_uuid);
+ if (entry == NULL) {
+ entry = get_partition_entry(FIP_IMAGE_NAME);
+ if (entry == NULL) {
+ ERROR("Could NOT find the %s partition!\n",
+ FIP_IMAGE_NAME);
+
+ return -ENOENT;
+ }
+ }
+
+ image_block_spec.offset = entry->start;
+ image_block_spec.length = entry->length;
+#endif
+ gpt_init_done = true;
+ } else {
+ bl_mem_params_node_t *bl_mem_params = get_bl_mem_params_node(image_id);
+ assert(bl_mem_params != NULL);
+
+ mmc_block_dev_spec.buffer.offset = bl_mem_params->image_info.image_base;
+ mmc_block_dev_spec.buffer.length = bl_mem_params->image_info.image_max_size;
+ }
+
+ break;
+#endif
+
+#if STM32MP_RAW_NAND || STM32MP_SPI_NAND
+#if STM32MP_RAW_NAND
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC:
+#endif
+#if STM32MP_SPI_NAND
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_QSPI:
+#endif
+ image_block_spec.offset = STM32MP_NAND_FIP_OFFSET;
+ break;
+#endif
+
+#if STM32MP_SPI_NOR
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI:
+ image_block_spec.offset = STM32MP_NOR_FIP_OFFSET;
+ break;
+#endif
+
+#if STM32MP_UART_PROGRAMMER
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART:
+ if (image_id == FW_CONFIG_ID) {
+ stm32cubeprogrammer_uart();
+ /* FIP loaded at DWL address */
+ image_block_spec.offset = DWL_BUFFER_BASE;
+ image_block_spec.length = DWL_BUFFER_SIZE;
+ }
+ break;
+#endif
+#if STM32MP_USB_PROGRAMMER
+ case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB:
+ if (image_id == FW_CONFIG_ID) {
+ stm32cubeprogrammer_usb();
+ /* FIP loaded at DWL address */
+ image_block_spec.offset = DWL_BUFFER_BASE;
+ image_block_spec.length = DWL_BUFFER_SIZE;
+ }
+ break;
+#endif
+
+ default:
+ ERROR("FIP Not found\n");
+ panic();
+ }
+
+ return 0;
+}
+
+/*
+ * Return an IO device handle and specification which can be used to access
+ * an image. Use this to enforce platform load policy.
+ */
+int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle,
+ uintptr_t *image_spec)
+{
+ int rc;
+ const struct plat_io_policy *policy;
+
+ policy = FCONF_GET_PROPERTY(stm32mp, io_policies, image_id);
+ rc = policy->check(policy->image_spec);
+ if (rc == 0) {
+ *image_spec = policy->image_spec;
+ *dev_handle = *(policy->dev_handle);
+ }
+
+ return rc;
+}
+
+#if (STM32MP_SDMMC || STM32MP_EMMC) && PSA_FWU_SUPPORT
+/*
+ * In each boot in non-trial mode, we set the BKP register to
+ * FWU_MAX_TRIAL_REBOOT, and return the active_index from metadata.
+ *
+ * As long as the update agent didn't update the "accepted" field in metadata
+ * (i.e. we are in trial mode), we select the new active_index.
+ * To avoid infinite boot loop at trial boot we decrement a BKP register.
+ * If this counter is 0:
+ * - an unexpected TAMPER event raised (that resets the BKP registers to 0)
+ * - a power-off occurs before the update agent was able to update the
+ * "accepted' field
+ * - we already boot FWU_MAX_TRIAL_REBOOT times in trial mode.
+ * we select the previous_active_index.
+ */
+#define INVALID_BOOT_IDX 0xFFFFFFFF
+
+uint32_t plat_fwu_get_boot_idx(void)
+{
+ /*
+ * Select boot index and update boot counter only once per boot
+ * even if this function is called several times.
+ */
+ static uint32_t boot_idx = INVALID_BOOT_IDX;
+ const struct fwu_metadata *data;
+
+ data = fwu_get_metadata();
+
+ if (boot_idx == INVALID_BOOT_IDX) {
+ boot_idx = data->active_index;
+ if (fwu_is_trial_run_state()) {
+ if (stm32_get_and_dec_fwu_trial_boot_cnt() == 0U) {
+ WARN("Trial FWU fails %u times\n",
+ FWU_MAX_TRIAL_REBOOT);
+ boot_idx = data->previous_active_index;
+ }
+ } else {
+ stm32_set_max_fwu_trial_boot_cnt();
+ }
+ }
+
+ return boot_idx;
+}
+
+static void *stm32_get_image_spec(const uuid_t *img_type_uuid)
+{
+ unsigned int i;
+
+ for (i = 0U; i < MAX_NUMBER_IDS; i++) {
+ if ((guidcmp(&policies[i].img_type_guid, img_type_uuid)) == 0) {
+ return (void *)policies[i].image_spec;
+ }
+ }
+
+ return NULL;
+}
+
+void plat_fwu_set_images_source(const struct fwu_metadata *metadata)
+{
+ unsigned int i;
+ uint32_t boot_idx;
+ const partition_entry_t *entry;
+ const uuid_t *img_type_uuid, *img_uuid;
+ io_block_spec_t *image_spec;
+
+ boot_idx = plat_fwu_get_boot_idx();
+ assert(boot_idx < NR_OF_FW_BANKS);
+
+ for (i = 0U; i < NR_OF_IMAGES_IN_FW_BANK; i++) {
+ img_type_uuid = &metadata->img_entry[i].img_type_uuid;
+ image_spec = stm32_get_image_spec(img_type_uuid);
+ if (image_spec == NULL) {
+ ERROR("Unable to get image spec for the image in the metadata\n");
+ panic();
+ }
+
+ img_uuid =
+ &metadata->img_entry[i].img_props[boot_idx].img_uuid;
+
+ entry = get_partition_entry_by_uuid(img_uuid);
+ if (entry == NULL) {
+ ERROR("Unable to find the partition with the uuid mentioned in metadata\n");
+ panic();
+ }
+
+ image_spec->offset = entry->start;
+ image_spec->length = entry->length;
+ }
+}
+
+static int plat_set_image_source(unsigned int image_id,
+ uintptr_t *handle,
+ uintptr_t *image_spec,
+ const char *part_name)
+{
+ struct plat_io_policy *policy;
+ io_block_spec_t *spec;
+ const partition_entry_t *entry = get_partition_entry(part_name);
+
+ if (entry == NULL) {
+ ERROR("Unable to find the %s partition\n", part_name);
+ return -ENOENT;
+ }
+
+ policy = &policies[image_id];
+
+ spec = (io_block_spec_t *)policy->image_spec;
+ spec->offset = entry->start;
+ spec->length = entry->length;
+
+ *image_spec = policy->image_spec;
+ *handle = *policy->dev_handle;
+
+ return 0;
+}
+
+int plat_fwu_set_metadata_image_source(unsigned int image_id,
+ uintptr_t *handle,
+ uintptr_t *image_spec)
+{
+ char *part_name;
+
+ assert((image_id == FWU_METADATA_IMAGE_ID) ||
+ (image_id == BKUP_FWU_METADATA_IMAGE_ID));
+
+ partition_init(GPT_IMAGE_ID);
+
+ if (image_id == FWU_METADATA_IMAGE_ID) {
+ part_name = METADATA_PART_1;
+ } else {
+ part_name = METADATA_PART_2;
+ }
+
+ return plat_set_image_source(image_id, handle, image_spec,
+ part_name);
+}
+#endif /* (STM32MP_SDMMC || STM32MP_EMMC) && PSA_FWU_SUPPORT */
diff --git a/plat/st/common/include/stm32cubeprogrammer.h b/plat/st/common/include/stm32cubeprogrammer.h
new file mode 100644
index 0000000..0f5a64d
--- /dev/null
+++ b/plat/st/common/include/stm32cubeprogrammer.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2021, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef STM32CUBEPROGRAMMER_H
+#define STM32CUBEPROGRAMMER_H
+
+#include <stdint.h>
+
+#include <usb_dfu.h>
+
+/* Phase definition */
+#define PHASE_FLASHLAYOUT 0U
+#define PHASE_SSBL 3U
+#define PHASE_CMD 0xF1U
+#define PHASE_RESET 0xFFU
+
+/* Functions provided by plat */
+uint8_t usb_dfu_get_phase(uint8_t alt);
+
+int stm32cubeprog_usb_load(struct usb_handle *usb_core_handle,
+ uintptr_t ssbl_base,
+ size_t ssbl_len);
+
+int stm32cubeprog_uart_load(uintptr_t instance, uintptr_t base, size_t len);
+
+#endif /* STM32CUBEPROGRAMMER_H */
diff --git a/plat/st/common/include/stm32mp_common.h b/plat/st/common/include/stm32mp_common.h
new file mode 100644
index 0000000..a5316b6
--- /dev/null
+++ b/plat/st/common/include/stm32mp_common.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018-2022, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef STM32MP_COMMON_H
+#define STM32MP_COMMON_H
+
+#include <stdbool.h>
+
+#include <platform_def.h>
+
+#define JEDEC_ST_BKID U(0x0)
+#define JEDEC_ST_MFID U(0x20)
+
+/* Functions to save and get boot context address given by ROM code */
+void stm32mp_save_boot_ctx_address(uintptr_t address);
+uintptr_t stm32mp_get_boot_ctx_address(void);
+uint16_t stm32mp_get_boot_itf_selected(void);
+
+bool stm32mp_is_single_core(void);
+bool stm32mp_is_closed_device(void);
+bool stm32mp_is_auth_supported(void);
+
+/* Return the base address of the DDR controller */
+uintptr_t stm32mp_ddrctrl_base(void);
+
+/* Return the base address of the DDR PHY */
+uintptr_t stm32mp_ddrphyc_base(void);
+
+/* Return the base address of the PWR peripheral */
+uintptr_t stm32mp_pwr_base(void);
+
+/* Return the base address of the RCC peripheral */
+uintptr_t stm32mp_rcc_base(void);
+
+/* Check MMU status to allow spinlock use */
+bool stm32mp_lock_available(void);
+
+int stm32_get_otp_index(const char *otp_name, uint32_t *otp_idx,
+ uint32_t *otp_len);
+int stm32_get_otp_value(const char *otp_name, uint32_t *otp_val);
+int stm32_get_otp_value_from_idx(const uint32_t otp_idx, uint32_t *otp_val);
+
+/* Get IWDG platform instance ID from peripheral IO memory base address */
+uint32_t stm32_iwdg_get_instance(uintptr_t base);
+
+/* Return bitflag mask for expected IWDG configuration from OTP content */
+uint32_t stm32_iwdg_get_otp_config(uint32_t iwdg_inst);
+
+#if defined(IMAGE_BL2)
+/* Update OTP shadow registers with IWDG configuration from device tree */
+uint32_t stm32_iwdg_shadow_update(uint32_t iwdg_inst, uint32_t flags);
+#endif
+
+#if STM32MP_UART_PROGRAMMER || !defined(IMAGE_BL2)
+/* Get the UART address from its instance number */
+uintptr_t get_uart_address(uint32_t instance_nb);
+#endif
+
+/* Setup the UART console */
+int stm32mp_uart_console_setup(void);
+
+#if STM32MP_EARLY_CONSOLE
+void stm32mp_setup_early_console(void);
+#else
+static inline void stm32mp_setup_early_console(void)
+{
+}
+#endif
+
+/*
+ * Platform util functions for the GPIO driver
+ * @bank: Target GPIO bank ID as per DT bindings
+ *
+ * Platform shall implement these functions to provide to stm32_gpio
+ * driver the resource reference for a target GPIO bank. That are
+ * memory mapped interface base address, interface offset (see below)
+ * and clock identifier.
+ *
+ * stm32_get_gpio_bank_offset() returns a bank offset that is used to
+ * check DT configuration matches platform implementation of the banks
+ * description.
+ */
+uintptr_t stm32_get_gpio_bank_base(unsigned int bank);
+unsigned long stm32_get_gpio_bank_clock(unsigned int bank);
+uint32_t stm32_get_gpio_bank_offset(unsigned int bank);
+bool stm32_gpio_is_secure_at_reset(unsigned int bank);
+
+/* Return node offset for target GPIO bank ID @bank or a FDT error code */
+int stm32_get_gpio_bank_pinctrl_node(void *fdt, unsigned int bank);
+
+/* Get the chip revision */
+uint32_t stm32mp_get_chip_version(void);
+/* Get the chip device ID */
+uint32_t stm32mp_get_chip_dev_id(void);
+
+/* Get SOC name */
+#define STM32_SOC_NAME_SIZE 20
+void stm32mp_get_soc_name(char name[STM32_SOC_NAME_SIZE]);
+
+/* Print CPU information */
+void stm32mp_print_cpuinfo(void);
+
+/* Print board information */
+void stm32mp_print_boardinfo(void);
+
+/* Initialise the IO layer and register platform IO devices */
+void stm32mp_io_setup(void);
+
+/* Functions to map DDR in MMU with non-cacheable attribute, and unmap it */
+int stm32mp_map_ddr_non_cacheable(void);
+int stm32mp_unmap_ddr(void);
+
+/* Functions to save and get boot peripheral info */
+void stm32_save_boot_interface(uint32_t interface, uint32_t instance);
+void stm32_get_boot_interface(uint32_t *interface, uint32_t *instance);
+
+/* Functions to save and get boot authentication status and partition used */
+void stm32_save_boot_auth(uint32_t auth_status, uint32_t boot_partition);
+
+#if PSA_FWU_SUPPORT
+void stm32mp1_fwu_set_boot_idx(void);
+uint32_t stm32_get_and_dec_fwu_trial_boot_cnt(void);
+void stm32_set_max_fwu_trial_boot_cnt(void);
+#endif /* PSA_FWU_SUPPORT */
+
+#endif /* STM32MP_COMMON_H */
diff --git a/plat/st/common/include/stm32mp_dt.h b/plat/st/common/include/stm32mp_dt.h
new file mode 100644
index 0000000..b7bf1d0
--- /dev/null
+++ b/plat/st/common/include/stm32mp_dt.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020-2022, STMicroelectronics - All Rights Reserved
+ * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef STM32MP_DT_H
+#define STM32MP_DT_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define DT_DISABLED U(0)
+#define DT_NON_SECURE U(1)
+#define DT_SECURE U(2)
+#define DT_SHARED (DT_NON_SECURE | DT_SECURE)
+
+struct dt_node_info {
+ uint32_t base;
+ int32_t clock;
+ int32_t reset;
+ uint32_t status;
+};
+
+/*******************************************************************************
+ * Function and variable prototypes
+ ******************************************************************************/
+int dt_open_and_check(uintptr_t dt_addr);
+int fdt_get_address(void **fdt_addr);
+bool fdt_check_node(int node);
+uint8_t fdt_get_status(int node);
+int dt_set_stdout_pinctrl(void);
+void dt_fill_device_info(struct dt_node_info *info, int node);
+int dt_get_node(struct dt_node_info *info, int offset, const char *compat);
+int dt_get_stdout_uart_info(struct dt_node_info *info);
+int dt_match_instance_by_compatible(const char *compatible, uintptr_t address);
+uint32_t dt_get_ddr_size(void);
+uint32_t dt_get_pwr_vdd_voltage(void);
+struct rdev *dt_get_vdd_regulator(void);
+struct rdev *dt_get_cpu_regulator(void);
+const char *dt_get_board_model(void);
+int dt_find_otp_name(const char *name, uint32_t *otp, uint32_t *otp_len);
+int fdt_get_gpio_bank_pin_count(unsigned int bank);
+
+#endif /* STM32MP_DT_H */
diff --git a/plat/st/common/include/stm32mp_efi.h b/plat/st/common/include/stm32mp_efi.h
new file mode 100644
index 0000000..490560f
--- /dev/null
+++ b/plat/st/common/include/stm32mp_efi.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (c) 2021, Linaro Limited
+ */
+
+#ifndef STM32MP_EFI_H
+#define STM32MP_EFI_H
+
+#include <drivers/partition/efi.h>
+
+#define STM32MP_FIP_GUID \
+ EFI_GUID(0x19d5df83, 0x11b0, 0x457b, \
+ 0xbe, 0x2c, 0x75, 0x59, 0xc1, 0x31, 0x42, 0xa5)
+
+#endif /* STM32MP_EFI_H */
diff --git a/plat/st/common/include/stm32mp_fconf_getter.h b/plat/st/common/include/stm32mp_fconf_getter.h
new file mode 100644
index 0000000..18884ae
--- /dev/null
+++ b/plat/st/common/include/stm32mp_fconf_getter.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2021, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef STM32MP_FCONF_GETTER
+#define STM32MP_FCONF_GETTER
+
+#include <assert.h>
+
+#include <lib/fconf/fconf.h>
+#include <tools_share/uuid.h>
+
+/* IO policies */
+#define stm32mp__io_policies_getter(id) __extension__ ({ \
+ assert((id) < MAX_NUMBER_IDS); \
+ &policies[id]; \
+})
+
+struct plat_io_policy {
+ uintptr_t *dev_handle;
+ uintptr_t image_spec;
+ struct efi_guid img_type_guid;
+ int (*check)(const uintptr_t spec);
+};
+
+extern struct plat_io_policy policies[];
+int fconf_populate_stm32mp_io_policies(uintptr_t config);
+
+#endif /* STM32MP_FCONF_GETTER */
diff --git a/plat/st/common/include/stm32mp_io_storage.h b/plat/st/common/include/stm32mp_io_storage.h
new file mode 100644
index 0000000..3c04c47
--- /dev/null
+++ b/plat/st/common/include/stm32mp_io_storage.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2021-2022, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#ifndef STM32MP_IO_STORAGE_H
+#define STM32MP_IO_STORAGE_H
+
+#include <stdint.h>
+
+#include <drivers/io/io_storage.h>
+
+/* IO devices handle */
+extern uintptr_t storage_dev_handle;
+extern uintptr_t fip_dev_handle;
+extern uintptr_t enc_dev_handle;
+
+extern io_block_spec_t image_block_spec;
+
+/* Function declarations */
+int open_fip(const uintptr_t spec);
+#ifndef DECRYPTION_SUPPORT_none
+int open_enc_fip(const uintptr_t spec);
+#endif
+int open_storage(const uintptr_t spec);
+
+#endif /* STM32MP_IO_STORAGE_H */
diff --git a/plat/st/common/include/stm32mp_shared_resources.h b/plat/st/common/include/stm32mp_shared_resources.h
new file mode 100644
index 0000000..13f4b13
--- /dev/null
+++ b/plat/st/common/include/stm32mp_shared_resources.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017-2020, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef STM32MP_SHARED_RESOURCES_H
+#define STM32MP_SHARED_RESOURCES_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef STM32MP_SHARED_RESOURCES
+enum stm32mp_shres;
+
+/* Return true if @clock_id is shared by secure and non-secure worlds */
+bool stm32mp_nsec_can_access_clock(unsigned long clock_id);
+
+/* Return true if and only if @reset_id relates to a non-secure peripheral */
+bool stm32mp_nsec_can_access_reset(unsigned int reset_id);
+
+/* Register a shared resource assigned to the secure world */
+void stm32mp_register_secure_periph(enum stm32mp_shres id);
+
+/* Register a shared resource assigned to the non-secure world */
+void stm32mp_register_non_secure_periph(enum stm32mp_shres id);
+
+/* Register a peripheral as secure or non-secure based on IO base address */
+void stm32mp_register_secure_periph_iomem(uintptr_t base);
+void stm32mp_register_non_secure_periph_iomem(uintptr_t base);
+
+/* Register a GPIO as secure or non-secure based on its bank and pin numbers */
+void stm32mp_register_secure_gpio(unsigned int bank, unsigned int pin);
+void stm32mp_register_non_secure_gpio(unsigned int bank, unsigned int pin);
+
+/* Consolidate peripheral states and lock against new peripheral registering */
+void stm32mp_lock_periph_registering(void);
+#else
+static inline void stm32mp_register_secure_periph_iomem(uintptr_t base __unused)
+{
+}
+
+static inline
+void stm32mp_register_non_secure_periph_iomem(uintptr_t base __unused)
+{
+}
+
+static inline void stm32mp_register_secure_gpio(unsigned int bank __unused,
+ unsigned int pin __unused)
+{
+}
+
+static inline void stm32mp_register_non_secure_gpio(unsigned int bank __unused,
+ unsigned int pin __unused)
+{
+}
+#endif /* STM32MP_SHARED_RESOURCES */
+#endif /* STM32MP_SHARED_RESOURCES_H */
diff --git a/plat/st/common/include/usb_dfu.h b/plat/st/common/include/usb_dfu.h
new file mode 100644
index 0000000..f7d4245
--- /dev/null
+++ b/plat/st/common/include/usb_dfu.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2021, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef USB_DFU_H
+#define USB_DFU_H
+
+#include <stdint.h>
+
+#include <drivers/usb_device.h>
+
+#define DFU_DESCRIPTOR_TYPE 0x21U
+
+/* Max DFU Packet Size = 1024 bytes */
+#define USBD_DFU_XFER_SIZE 1024U
+
+#define TRANSFER_SIZE_BYTES(size) \
+ ((uint8_t)((size) & 0xFF)), /* XFERSIZEB0 */\
+ ((uint8_t)((size) >> 8)) /* XFERSIZEB1 */
+
+/*
+ * helper for descriptor of DFU interface 0 Alternate setting n
+ * with iInterface = index of string descriptor, assumed Nth user string
+ */
+#define USBD_DFU_IF_DESC(n) 0x09U, /* Interface Descriptor size */\
+ USB_DESC_TYPE_INTERFACE, /* descriptor type */\
+ 0x00U, /* Number of Interface */\
+ (n), /* Alternate setting */\
+ 0x00U, /* bNumEndpoints*/\
+ 0xFEU, /* Application Specific Class Code */\
+ 0x01U, /* Device Firmware Upgrade Code */\
+ 0x02U, /* DFU mode protocol */ \
+ USBD_IDX_USER0_STR + (n) /* iInterface */
+
+/* DFU1.1 Standard */
+#define USB_DFU_VERSION 0x0110U
+#define USB_DFU_ITF_SIZ 9U
+#define USB_DFU_DESC_SIZ(itf) (USB_DFU_ITF_SIZ * ((itf) + 2U))
+
+/*
+ * bmAttribute value for DFU:
+ * bitCanDnload = 1(bit 0)
+ * bitCanUpload = 1(bit 1)
+ * bitManifestationTolerant = 1 (bit 2)
+ * bitWillDetach = 1(bit 3)
+ * Reserved (bit4-6)
+ * bitAcceleratedST = 0(bit 7)
+ */
+#define DFU_BM_ATTRIBUTE 0x0FU
+
+#define DFU_STATUS_SIZE 6U
+
+/* Callback for media access */
+struct usb_dfu_media {
+ int (*upload)(uint8_t alt, uintptr_t *buffer, uint32_t *len,
+ void *user_data);
+ int (*download)(uint8_t alt, uintptr_t *buffer, uint32_t *len,
+ void *user_data);
+ int (*manifestation)(uint8_t alt, void *user_data);
+};
+
+/* Internal DFU handle */
+struct usb_dfu_handle {
+ uint8_t status[DFU_STATUS_SIZE];
+ uint8_t dev_state;
+ uint8_t dev_status;
+ uint8_t alt_setting;
+ const struct usb_dfu_media *callback;
+};
+
+void usb_dfu_register(struct usb_handle *pdev, struct usb_dfu_handle *phandle);
+
+int usb_dfu_loop(struct usb_handle *pdev, const struct usb_dfu_media *pmedia);
+
+/* Function provided by plat */
+struct usb_handle *usb_dfu_plat_init(void);
+
+#endif /* USB_DFU_H */
diff --git a/plat/st/common/stm32cubeprogrammer_uart.c b/plat/st/common/stm32cubeprogrammer_uart.c
new file mode 100644
index 0000000..d004dcf
--- /dev/null
+++ b/plat/st/common/stm32cubeprogrammer_uart.c
@@ -0,0 +1,520 @@
+/*
+ * Copyright (c) 2021-2022, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <endian.h>
+#include <errno.h>
+#include <string.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <drivers/st/stm32_iwdg.h>
+#include <drivers/st/stm32_uart.h>
+#include <drivers/st/stm32_uart_regs.h>
+#include <lib/mmio.h>
+#include <tools_share/firmware_image_package.h>
+
+#include <platform_def.h>
+#include <stm32cubeprogrammer.h>
+
+/* USART bootloader protocol version V4.0 */
+#define USART_BL_VERSION 0x40U
+
+/* Command definition */
+#define GET_CMD_COMMAND 0x00U
+#define GET_VER_COMMAND 0x01U
+#define GET_ID_COMMAND 0x02U
+#define PHASE_COMMAND 0x03U
+#define READ_PART_COMMAND 0x12U
+#define START_COMMAND 0x21U
+#define DOWNLOAD_COMMAND 0x31U
+
+/* Answer defines */
+#define INIT_BYTE 0x7FU
+#define ACK_BYTE 0x79U
+#define NACK_BYTE 0x1FU
+#define ABORT 0x5FU
+
+#define UNDEFINED_DOWN_ADDR U(0xFFFFFFFF)
+#define PROGRAMMER_TIMEOUT_US 20000U
+
+static const uint8_t command_tab[] = {
+ GET_CMD_COMMAND,
+ GET_VER_COMMAND,
+ GET_ID_COMMAND,
+ PHASE_COMMAND,
+ START_COMMAND,
+ DOWNLOAD_COMMAND
+};
+
+/* STM32CubeProgrammer over UART handle */
+struct stm32prog_uart_handle_s {
+ struct stm32_uart_handle_s uart;
+ uint32_t packet;
+ uint8_t *addr;
+ uint32_t len;
+ uint8_t phase;
+ /* Error msg buffer: max 255 in UART protocol, reduced in TF-A */
+ uint8_t error[64];
+} handle;
+
+/* Trace and handle unrecoverable UART protocol error */
+#define STM32PROG_ERROR(...) \
+ { \
+ ERROR(__VA_ARGS__); \
+ if (handle.phase != PHASE_RESET) { \
+ snprintf((char *)&handle.error, sizeof(handle.error), __VA_ARGS__); \
+ handle.phase = PHASE_RESET; \
+ handle.addr = (uint8_t *)UNDEFINED_DOWN_ADDR; \
+ handle.len = 0U; \
+ handle.packet = 0U; \
+ } \
+ }
+
+static int uart_write(const uint8_t *addr, uint16_t size)
+{
+ while (size != 0U) {
+ if (stm32_uart_putc(&handle.uart, *addr) != 0) {
+ return -EIO;
+ }
+ size--;
+ addr++;
+ }
+
+ return 0;
+}
+
+static int uart_write_8(uint8_t byte)
+{
+ return stm32_uart_putc(&handle.uart, byte);
+}
+
+static int uart_write_32(uint32_t value)
+{
+ return uart_write((uint8_t *)&value, 4U);
+}
+
+static int uart_read_8(uint8_t *byte)
+{
+ int ret;
+ uint64_t timeout_ref = timeout_init_us(PROGRAMMER_TIMEOUT_US);
+
+ do {
+ ret = stm32_uart_getc(&handle.uart);
+ if (ret == -EAGAIN) {
+ if (timeout_elapsed(timeout_ref)) {
+ return -ETIMEDOUT;
+ }
+ } else if (ret < 0) {
+ return ret;
+ }
+ } while (ret == -EAGAIN);
+
+ *byte = (uint8_t)ret;
+
+ return 0;
+}
+
+static int uart_send_result(uint8_t byte)
+{
+ int ret;
+
+ /* Always flush fifo before to send result = read all pending data */
+ do {
+ ret = stm32_uart_getc(&handle.uart);
+ } while (ret >= 0);
+
+ return uart_write_8(byte);
+}
+
+static bool is_valid_header(fip_toc_header_t *header)
+{
+ return (header->name == TOC_HEADER_NAME) &&
+ (header->serial_number != 0U);
+}
+
+static int uart_receive_command(uint8_t *command)
+{
+ uint8_t byte = 0U;
+ uint8_t xor = 0U;
+ unsigned int count;
+ bool found = false;
+ int ret;
+
+ /* Repeat read until something is received */
+ do {
+ stm32_iwdg_refresh();
+ ret = uart_read_8(&byte);
+ } while (ret == -ETIMEDOUT);
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* Handle reconnection request */
+ if (byte == INIT_BYTE) {
+ *command = byte;
+ return 0;
+ }
+
+ for (count = 0U; count < ARRAY_SIZE(command_tab); count++) {
+ if (command_tab[count] == byte) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ VERBOSE("UART: Command unknown (byte=0x%x)\n", byte);
+ return -EPROTO;
+ }
+
+ ret = uart_read_8(&xor);
+ if (ret != 0) {
+ return ret;
+ }
+ if ((byte ^ xor) != 0xFF) {
+ VERBOSE("UART: Command XOR check fail (byte=0x%x, xor=0x%x)\n",
+ byte, xor);
+ return -EPROTO;
+ }
+
+ *command = byte;
+
+ return 0;
+}
+
+static int get_cmd_command(void)
+{
+ const uint8_t msg[2] = {
+ sizeof(command_tab), /* Length of data - 1 */
+ USART_BL_VERSION
+ };
+ int ret;
+
+ ret = uart_write(msg, sizeof(msg));
+ if (ret != 0) {
+ return ret;
+ }
+
+ return uart_write(command_tab, sizeof(command_tab));
+}
+
+static int get_version_command(void)
+{
+ return uart_write_8(STM32_TF_VERSION);
+}
+
+static int get_id_command(void)
+{
+ uint8_t msg[3] = {
+ sizeof(msg) - 1 /* Length of data - 1 */
+ };
+ uint32_t chip_id = stm32mp_get_chip_dev_id();
+
+ be16enc(&msg[1], chip_id);
+
+ return uart_write(msg, sizeof(msg));
+}
+
+static int uart_send_phase(uint32_t address)
+{
+ int ret;
+ uint8_t msg_size = 5U; /* Length of data - 1 */
+ uint8_t error_size = 0U;
+
+ /* Additional information only for RESET phase */
+ if (handle.phase == PHASE_RESET) {
+ error_size = strnlen((char *)&handle.error, sizeof(handle.error));
+ }
+ ret = uart_write_8(msg_size + error_size);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* Send the ID of next partition */
+ ret = uart_write_8(handle.phase);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* Destination address */
+ ret = uart_write_32(address);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = uart_write_8(error_size);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* Additional information: message error */
+ if (error_size > 0U) {
+ ret = uart_write(handle.error, error_size);
+ }
+
+ return ret;
+}
+
+static int uart_download_part(void)
+{
+ uint8_t operation = 0U;
+ uint8_t xor;
+ uint8_t byte = 0U;
+ uint32_t packet_number = 0U;
+ uint32_t packet_size = 0U;
+ uint32_t i = 0U;
+ int ret;
+
+ /* Get operation number */
+ ret = uart_read_8(&operation);
+ if (ret != 0) {
+ return ret;
+ }
+
+ xor = operation;
+
+ /* Get packet number */
+ for (i = 3U; i != 0U; i--) {
+ ret = uart_read_8(&byte);
+ if (ret != 0) {
+ return ret;
+ }
+
+ xor ^= byte;
+ packet_number = (packet_number << 8) | byte;
+ }
+
+ if (packet_number != handle.packet) {
+ WARN("UART: Bad packet number receive: %u, expected %u\n",
+ packet_number, handle.packet);
+ return -EPROTO;
+ }
+
+ /* Checksum */
+ ret = uart_read_8(&byte);
+ if (ret != 0) {
+ return ret;
+ }
+ if (xor != byte) {
+ VERBOSE("UART: Download Command checksum xor: %x, received %x\n",
+ xor, byte);
+ return -EPROTO;
+ }
+
+ ret = uart_send_result(ACK_BYTE);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = uart_read_8(&byte);
+ if (ret != 0) {
+ return ret;
+ }
+ xor = byte;
+ packet_size = byte + 1U;
+ if (handle.len < packet_size) {
+ STM32PROG_ERROR("Download overflow at %p\n", handle.addr + packet_size);
+ return 0;
+ }
+
+ for (i = 0U; i < packet_size; i++) {
+ ret = uart_read_8(&byte);
+ if (ret != 0) {
+ return ret;
+ }
+
+ *(handle.addr + i) = byte;
+ xor ^= byte;
+ }
+
+ /* Checksum */
+ ret = uart_read_8(&byte) != 0;
+ if (ret != 0) {
+ return ret;
+ }
+ if (xor != byte) {
+ VERBOSE("UART: Download Data checksum xor: %x, received %x\n",
+ xor, byte);
+ return -EPROTO;
+ }
+
+ /* Packet treated */
+ handle.packet++;
+ handle.addr += packet_size;
+ handle.len -= packet_size;
+
+ return 0;
+}
+
+static int uart_start_cmd(uintptr_t buffer)
+{
+ uint8_t byte = 0U;
+ uint8_t xor = 0U;
+ uint32_t i;
+ uint32_t start_address = 0U;
+ int ret;
+
+ /* Get address */
+ for (i = 4U; i != 0U; i--) {
+ ret = uart_read_8(&byte);
+ if (ret != 0U) {
+ return ret;
+ }
+
+ xor ^= byte;
+ start_address = (start_address << 8) | byte;
+ }
+
+ /* Checksum */
+ ret = uart_read_8(&byte);
+ if (ret != 0) {
+ return ret;
+ }
+
+ if (xor != byte) {
+ VERBOSE("UART: Start Command checksum xor: %x, received %x\n",
+ xor, byte);
+ return -EPROTO;
+ }
+
+ if (start_address != UNDEFINED_DOWN_ADDR) {
+ STM32PROG_ERROR("Invalid start at %x, for phase %u\n",
+ start_address, handle.phase);
+ return 0;
+ }
+
+ if (!is_valid_header((fip_toc_header_t *)buffer)) {
+ STM32PROG_ERROR("FIP Header check failed %lx, for phase %u\n",
+ buffer, handle.phase);
+ return -EIO;
+ }
+ VERBOSE("FIP header looks OK.\n");
+
+ return 0;
+}
+
+static int uart_read(uint8_t id, uintptr_t buffer, size_t length)
+{
+ bool start_done = false;
+ int ret;
+ uint8_t command = 0U;
+
+ handle.phase = id;
+ handle.packet = 0U;
+ handle.addr = (uint8_t *)buffer;
+ handle.len = length;
+
+ INFO("UART: read phase %u at 0x%lx size 0x%x\n",
+ id, buffer, length);
+ while (!start_done) {
+ ret = uart_receive_command(&command);
+ if (ret != 0) {
+ /* Delay to wait STM32CubeProgrammer end of transmission */
+ mdelay(3);
+
+ ret = uart_send_result(NACK_BYTE);
+ if (ret != 0U) {
+ return ret;
+ }
+
+ continue;
+ }
+
+ uart_send_result(ACK_BYTE);
+
+ switch (command) {
+ case INIT_BYTE:
+ INFO("UART: Connected\n");
+ /* Nothing to do */
+ continue;
+
+ case GET_CMD_COMMAND:
+ ret = get_cmd_command();
+ break;
+
+ case GET_VER_COMMAND:
+ ret = get_version_command();
+ break;
+
+ case GET_ID_COMMAND:
+ ret = get_id_command();
+ break;
+
+ case PHASE_COMMAND:
+ ret = uart_send_phase((uint32_t)buffer);
+ if ((ret == 0) && (handle.phase == PHASE_RESET)) {
+ start_done = true;
+ INFO("UART: Reset\n");
+ }
+ break;
+
+ case DOWNLOAD_COMMAND:
+ ret = uart_download_part();
+ break;
+
+ case START_COMMAND:
+ ret = uart_start_cmd(buffer);
+ if ((ret == 0) && (handle.phase == id)) {
+ INFO("UART: Start phase %u\n", handle.phase);
+ start_done = true;
+ }
+ break;
+
+ default:
+ WARN("UART: Unknown command\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret == 0) {
+ ret = uart_send_result(ACK_BYTE);
+ } else {
+ ret = uart_send_result(NACK_BYTE);
+ }
+ if (ret != 0) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* Init UART: 115200, 8bit 1stop parity even and enable FIFO mode */
+const struct stm32_uart_init_s init = {
+ .baud_rate = STM32MP_UART_BAUDRATE,
+ .word_length = STM32_UART_WORDLENGTH_9B,
+ .stop_bits = STM32_UART_STOPBITS_1,
+ .parity = STM32_UART_PARITY_EVEN,
+ .hw_flow_control = STM32_UART_HWCONTROL_NONE,
+ .mode = STM32_UART_MODE_TX_RX,
+ .fifo_mode = STM32_UART_FIFOMODE_EN,
+};
+
+int stm32cubeprog_uart_load(uintptr_t instance, uintptr_t base, size_t len)
+{
+ int ret;
+
+ if (stm32_uart_init(&handle.uart, instance, &init) != 0) {
+ return -EIO;
+ }
+
+ /*
+ * The following NACK_BYTE is written because STM32CubeProgrammer has
+ * already sent its command before TF-A has reached this point, and
+ * because FIFO was not configured by BootROM.
+ * The byte in the UART_RX register is then the checksum and not the
+ * command. NACK_BYTE has to be written, so that the programmer will
+ * re-send the good command.
+ */
+ ret = uart_send_result(NACK_BYTE);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return uart_read(PHASE_SSBL, base, len);
+}
diff --git a/plat/st/common/stm32cubeprogrammer_usb.c b/plat/st/common/stm32cubeprogrammer_usb.c
new file mode 100644
index 0000000..75e8038
--- /dev/null
+++ b/plat/st/common/stm32cubeprogrammer_usb.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2021-2022, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+#include <tools_share/firmware_image_package.h>
+
+#include <stm32cubeprogrammer.h>
+#include <usb_dfu.h>
+
+/* Undefined download address */
+#define UNDEFINED_DOWN_ADDR 0xFFFFFFFF
+
+struct dfu_state {
+ uint8_t phase;
+ uintptr_t base;
+ size_t len;
+ uintptr_t address;
+ /* working buffer */
+ uint8_t buffer[UCHAR_MAX];
+};
+
+static struct dfu_state dfu_state;
+
+/* minimal size of Get Pḧase = offset for additionnl information */
+#define GET_PHASE_LEN 9
+
+#define DFU_ERROR(...) \
+ { \
+ ERROR(__VA_ARGS__); \
+ if (dfu->phase != PHASE_RESET) { \
+ snprintf((char *)&dfu->buffer[GET_PHASE_LEN], \
+ sizeof(dfu->buffer) - GET_PHASE_LEN, \
+ __VA_ARGS__); \
+ dfu->phase = PHASE_RESET; \
+ dfu->address = UNDEFINED_DOWN_ADDR; \
+ dfu->len = 0; \
+ } \
+ }
+
+static bool is_valid_header(fip_toc_header_t *header)
+{
+ if ((header->name == TOC_HEADER_NAME) && (header->serial_number != 0U)) {
+ return true;
+ }
+
+ return false;
+}
+
+static int dfu_callback_upload(uint8_t alt, uintptr_t *buffer, uint32_t *len,
+ void *user_data)
+{
+ int result = 0;
+ uint32_t length = 0;
+ struct dfu_state *dfu = (struct dfu_state *)user_data;
+
+ switch (usb_dfu_get_phase(alt)) {
+ case PHASE_CMD:
+ /* Get Pḧase */
+ dfu->buffer[0] = dfu->phase;
+ dfu->buffer[1] = (uint8_t)(dfu->address);
+ dfu->buffer[2] = (uint8_t)(dfu->address >> 8);
+ dfu->buffer[3] = (uint8_t)(dfu->address >> 16);
+ dfu->buffer[4] = (uint8_t)(dfu->address >> 24);
+ dfu->buffer[5] = 0x00;
+ dfu->buffer[6] = 0x00;
+ dfu->buffer[7] = 0x00;
+ dfu->buffer[8] = 0x00;
+ length = GET_PHASE_LEN;
+ if (dfu->phase == PHASE_FLASHLAYOUT &&
+ dfu->address == UNDEFINED_DOWN_ADDR) {
+ INFO("Send detach request\n");
+ dfu->buffer[length++] = 0x01;
+ }
+ if (dfu->phase == PHASE_RESET) {
+ /* error information is added by DFU_ERROR macro */
+ length += strnlen((char *)&dfu->buffer[GET_PHASE_LEN],
+ sizeof(dfu->buffer) - GET_PHASE_LEN)
+ - 1;
+ }
+ break;
+
+ default:
+ DFU_ERROR("phase ID :%i, alternate %i for phase %i\n",
+ dfu->phase, alt, usb_dfu_get_phase(alt));
+ result = -EIO;
+ break;
+ }
+
+ if (result == 0) {
+ *len = length;
+ *buffer = (uintptr_t)dfu->buffer;
+ }
+
+ return result;
+}
+
+static int dfu_callback_download(uint8_t alt, uintptr_t *buffer, uint32_t *len,
+ void *user_data)
+{
+ struct dfu_state *dfu = (struct dfu_state *)user_data;
+
+ if ((dfu->phase != usb_dfu_get_phase(alt)) ||
+ (dfu->address == UNDEFINED_DOWN_ADDR)) {
+ DFU_ERROR("phase ID :%i, alternate %i, address %x\n",
+ dfu->phase, alt, (uint32_t)dfu->address);
+ return -EIO;
+ }
+
+ VERBOSE("Download %d %lx %x\n", alt, dfu->address, *len);
+ *buffer = dfu->address;
+ dfu->address += *len;
+
+ if (dfu->address - dfu->base > dfu->len) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int dfu_callback_manifestation(uint8_t alt, void *user_data)
+{
+ struct dfu_state *dfu = (struct dfu_state *)user_data;
+
+ if (dfu->phase != usb_dfu_get_phase(alt)) {
+ ERROR("Manifestation phase ID :%i, alternate %i, address %lx\n",
+ dfu->phase, alt, dfu->address);
+ return -EIO;
+ }
+
+ INFO("phase ID :%i, Manifestation %d at %lx\n",
+ dfu->phase, alt, dfu->address);
+
+ switch (dfu->phase) {
+ case PHASE_SSBL:
+ if (!is_valid_header((fip_toc_header_t *)dfu->base)) {
+ DFU_ERROR("FIP Header check failed for phase %d\n", alt);
+ return -EIO;
+ }
+ VERBOSE("FIP header looks OK.\n");
+
+ /* Configure End with request detach */
+ dfu->phase = PHASE_FLASHLAYOUT;
+ dfu->address = UNDEFINED_DOWN_ADDR;
+ dfu->len = 0;
+ break;
+ default:
+ DFU_ERROR("Unknown phase\n");
+ }
+
+ return 0;
+}
+
+/* Open a connection to the USB device */
+static const struct usb_dfu_media usb_dfu_fops = {
+ .upload = dfu_callback_upload,
+ .download = dfu_callback_download,
+ .manifestation = dfu_callback_manifestation,
+};
+
+int stm32cubeprog_usb_load(struct usb_handle *usb_core_handle,
+ uintptr_t base,
+ size_t len)
+{
+ int ret;
+
+ usb_core_handle->user_data = (void *)&dfu_state;
+
+ INFO("DFU USB START...\n");
+ ret = usb_core_start(usb_core_handle);
+ if (ret != USBD_OK) {
+ return -EIO;
+ }
+
+ dfu_state.phase = PHASE_SSBL;
+ dfu_state.address = base;
+ dfu_state.base = base;
+ dfu_state.len = len;
+
+ ret = usb_dfu_loop(usb_core_handle, &usb_dfu_fops);
+ if (ret != USBD_OK) {
+ return -EIO;
+ }
+
+ INFO("DFU USB STOP...\n");
+ ret = usb_core_stop(usb_core_handle);
+ if (ret != USBD_OK) {
+ return -EIO;
+ }
+
+ return 0;
+}
diff --git a/plat/st/common/stm32mp_common.c b/plat/st/common/stm32mp_common.c
new file mode 100644
index 0000000..bb56bac
--- /dev/null
+++ b/plat/st/common/stm32mp_common.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2015-2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/clk.h>
+#include <drivers/delay_timer.h>
+#include <drivers/st/stm32_console.h>
+#include <drivers/st/stm32mp_clkfunc.h>
+#include <drivers/st/stm32mp_reset.h>
+#include <lib/smccc.h>
+#include <lib/xlat_tables/xlat_tables_v2.h>
+#include <plat/common/platform.h>
+#include <services/arm_arch_svc.h>
+
+#include <platform_def.h>
+
+#define HEADER_VERSION_MAJOR_MASK GENMASK(23, 16)
+#define RESET_TIMEOUT_US_1MS 1000U
+
+static console_t console;
+
+uintptr_t plat_get_ns_image_entrypoint(void)
+{
+ return BL33_BASE;
+}
+
+unsigned int plat_get_syscnt_freq2(void)
+{
+ return read_cntfrq_el0();
+}
+
+static uintptr_t boot_ctx_address;
+static uint16_t boot_itf_selected;
+
+void stm32mp_save_boot_ctx_address(uintptr_t address)
+{
+ boot_api_context_t *boot_context = (boot_api_context_t *)address;
+
+ boot_ctx_address = address;
+ boot_itf_selected = boot_context->boot_interface_selected;
+}
+
+uintptr_t stm32mp_get_boot_ctx_address(void)
+{
+ return boot_ctx_address;
+}
+
+uint16_t stm32mp_get_boot_itf_selected(void)
+{
+ return boot_itf_selected;
+}
+
+uintptr_t stm32mp_ddrctrl_base(void)
+{
+ return DDRCTRL_BASE;
+}
+
+uintptr_t stm32mp_ddrphyc_base(void)
+{
+ return DDRPHYC_BASE;
+}
+
+uintptr_t stm32mp_pwr_base(void)
+{
+ return PWR_BASE;
+}
+
+uintptr_t stm32mp_rcc_base(void)
+{
+ return RCC_BASE;
+}
+
+bool stm32mp_lock_available(void)
+{
+ const uint32_t c_m_bits = SCTLR_M_BIT | SCTLR_C_BIT;
+
+ /* The spinlocks are used only when MMU and data cache are enabled */
+ return (read_sctlr() & c_m_bits) == c_m_bits;
+}
+
+int stm32mp_map_ddr_non_cacheable(void)
+{
+ return mmap_add_dynamic_region(STM32MP_DDR_BASE, STM32MP_DDR_BASE,
+ STM32MP_DDR_MAX_SIZE,
+ MT_NON_CACHEABLE | MT_RW | MT_SECURE);
+}
+
+int stm32mp_unmap_ddr(void)
+{
+ return mmap_remove_dynamic_region(STM32MP_DDR_BASE,
+ STM32MP_DDR_MAX_SIZE);
+}
+
+int stm32_get_otp_index(const char *otp_name, uint32_t *otp_idx,
+ uint32_t *otp_len)
+{
+ assert(otp_name != NULL);
+ assert(otp_idx != NULL);
+
+ return dt_find_otp_name(otp_name, otp_idx, otp_len);
+}
+
+int stm32_get_otp_value(const char *otp_name, uint32_t *otp_val)
+{
+ uint32_t otp_idx;
+
+ assert(otp_name != NULL);
+ assert(otp_val != NULL);
+
+ if (stm32_get_otp_index(otp_name, &otp_idx, NULL) != 0) {
+ return -1;
+ }
+
+ if (stm32_get_otp_value_from_idx(otp_idx, otp_val) != 0) {
+ ERROR("BSEC: %s Read Error\n", otp_name);
+ return -1;
+ }
+
+ return 0;
+}
+
+int stm32_get_otp_value_from_idx(const uint32_t otp_idx, uint32_t *otp_val)
+{
+ uint32_t ret = BSEC_NOT_SUPPORTED;
+
+ assert(otp_val != NULL);
+
+#if defined(IMAGE_BL2)
+ ret = bsec_shadow_read_otp(otp_val, otp_idx);
+#elif defined(IMAGE_BL32)
+ ret = bsec_read_otp(otp_val, otp_idx);
+#else
+#error "Not supported"
+#endif
+ if (ret != BSEC_OK) {
+ ERROR("BSEC: idx=%u Read Error\n", otp_idx);
+ return -1;
+ }
+
+ return 0;
+}
+
+#if defined(IMAGE_BL2)
+static void reset_uart(uint32_t reset)
+{
+ int ret;
+
+ ret = stm32mp_reset_assert(reset, RESET_TIMEOUT_US_1MS);
+ if (ret != 0) {
+ panic();
+ }
+
+ udelay(2);
+
+ ret = stm32mp_reset_deassert(reset, RESET_TIMEOUT_US_1MS);
+ if (ret != 0) {
+ panic();
+ }
+
+ mdelay(1);
+}
+#endif
+
+static void set_console(uintptr_t base, uint32_t clk_rate)
+{
+ unsigned int console_flags;
+
+ if (console_stm32_register(base, clk_rate,
+ (uint32_t)STM32MP_UART_BAUDRATE, &console) == 0) {
+ panic();
+ }
+
+ console_flags = CONSOLE_FLAG_BOOT | CONSOLE_FLAG_CRASH |
+ CONSOLE_FLAG_TRANSLATE_CRLF;
+#if !defined(IMAGE_BL2) && defined(DEBUG)
+ console_flags |= CONSOLE_FLAG_RUNTIME;
+#endif
+
+ console_set_scope(&console, console_flags);
+}
+
+int stm32mp_uart_console_setup(void)
+{
+ struct dt_node_info dt_uart_info;
+ uint32_t clk_rate = 0U;
+ int result;
+ uint32_t boot_itf __unused;
+ uint32_t boot_instance __unused;
+
+ result = dt_get_stdout_uart_info(&dt_uart_info);
+
+ if ((result <= 0) ||
+ (dt_uart_info.status == DT_DISABLED)) {
+ return -ENODEV;
+ }
+
+#if defined(IMAGE_BL2)
+ if ((dt_uart_info.clock < 0) ||
+ (dt_uart_info.reset < 0)) {
+ return -ENODEV;
+ }
+#endif
+
+#if STM32MP_UART_PROGRAMMER || !defined(IMAGE_BL2)
+ stm32_get_boot_interface(&boot_itf, &boot_instance);
+
+ if ((boot_itf == BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART) &&
+ (get_uart_address(boot_instance) == dt_uart_info.base)) {
+ return -EACCES;
+ }
+#endif
+
+#if defined(IMAGE_BL2)
+ if (dt_set_stdout_pinctrl() != 0) {
+ return -ENODEV;
+ }
+
+ clk_enable((unsigned long)dt_uart_info.clock);
+
+ reset_uart((uint32_t)dt_uart_info.reset);
+
+ clk_rate = clk_get_rate((unsigned long)dt_uart_info.clock);
+#endif
+
+ set_console(dt_uart_info.base, clk_rate);
+
+ return 0;
+}
+
+#if STM32MP_EARLY_CONSOLE
+void stm32mp_setup_early_console(void)
+{
+#if defined(IMAGE_BL2) || STM32MP_RECONFIGURE_CONSOLE
+ plat_crash_console_init();
+#endif
+ set_console(STM32MP_DEBUG_USART_BASE, STM32MP_DEBUG_USART_CLK_FRQ);
+ NOTICE("Early console setup\n");
+}
+#endif /* STM32MP_EARLY_CONSOLE */
+
+/*****************************************************************************
+ * plat_is_smccc_feature_available() - This function checks whether SMCCC
+ * feature is availabile for platform.
+ * @fid: SMCCC function id
+ *
+ * Return SMC_ARCH_CALL_SUCCESS if SMCCC feature is available and
+ * SMC_ARCH_CALL_NOT_SUPPORTED otherwise.
+ *****************************************************************************/
+int32_t plat_is_smccc_feature_available(u_register_t fid)
+{
+ switch (fid) {
+ case SMCCC_ARCH_SOC_ID:
+ return SMC_ARCH_CALL_SUCCESS;
+ default:
+ return SMC_ARCH_CALL_NOT_SUPPORTED;
+ }
+}
+
+/* Get SOC version */
+int32_t plat_get_soc_version(void)
+{
+ uint32_t chip_id = stm32mp_get_chip_dev_id();
+ uint32_t manfid = SOC_ID_SET_JEP_106(JEDEC_ST_BKID, JEDEC_ST_MFID);
+
+ return (int32_t)(manfid | (chip_id & SOC_ID_IMPL_DEF_MASK));
+}
+
+/* Get SOC revision */
+int32_t plat_get_soc_revision(void)
+{
+ return (int32_t)(stm32mp_get_chip_version() & SOC_ID_REV_MASK);
+}
diff --git a/plat/st/common/stm32mp_crypto_lib.c b/plat/st/common/stm32mp_crypto_lib.c
new file mode 100644
index 0000000..d644242
--- /dev/null
+++ b/plat/st/common/stm32mp_crypto_lib.c
@@ -0,0 +1,661 @@
+/*
+ * Copyright (c) 2022, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <endian.h>
+#include <errno.h>
+
+#include <common/debug.h>
+#include <drivers/auth/crypto_mod.h>
+#include <drivers/io/io_storage.h>
+#include <drivers/st/bsec.h>
+#include <drivers/st/stm32_hash.h>
+#include <drivers/st/stm32_pka.h>
+#include <drivers/st/stm32_rng.h>
+#include <drivers/st/stm32_saes.h>
+#include <lib/xlat_tables/xlat_tables_v2.h>
+#include <mbedtls/asn1.h>
+#include <mbedtls/md.h>
+#include <mbedtls/oid.h>
+#include <mbedtls/platform.h>
+#include <mbedtls/x509.h>
+#include <plat/common/platform.h>
+#include <tools_share/firmware_encrypted.h>
+
+#include <platform_def.h>
+
+#define CRYPTO_HASH_MAX_SIZE 32U
+#define CRYPTO_SIGN_MAX_SIZE 64U
+#define CRYPTO_PUBKEY_MAX_SIZE 64U
+#define CRYPTO_MAX_TAG_SIZE 16U
+
+/* brainpoolP256t1 OID is not defined in mbedTLS */
+#define OID_EC_GRP_BP256T1 MBEDTLS_OID_EC_BRAINPOOL_V1 "\x08"
+
+#if STM32MP_CRYPTO_ROM_LIB
+struct stm32mp_auth_ops {
+ uint32_t (*verify_signature)(uint8_t *hash_in, uint8_t *pubkey_in,
+ uint8_t *signature, uint32_t ecc_algo);
+};
+
+static struct stm32mp_auth_ops auth_ops;
+#endif
+
+static void crypto_lib_init(void)
+{
+ boot_api_context_t *boot_context __maybe_unused;
+ int ret;
+
+ NOTICE("TRUSTED_BOARD_BOOT support enabled\n");
+
+ ret = stm32_hash_register();
+ if (ret != 0) {
+ ERROR("HASH init (%d)\n", ret);
+ panic();
+ }
+
+ if (stm32mp_is_closed_device() || stm32mp_is_auth_supported()) {
+#if STM32MP_CRYPTO_ROM_LIB
+ boot_context = (boot_api_context_t *)stm32mp_get_boot_ctx_address();
+ auth_ops.verify_signature = boot_context->bootrom_ecdsa_verify_signature;
+#else
+ /* Use hardware peripherals */
+ if (stm32_rng_init() != 0) {
+ panic();
+ }
+
+ if (stm32_saes_driver_init() != 0) {
+ panic();
+ }
+
+ if (stm32_pka_init() != 0) {
+ panic();
+ }
+#endif
+ }
+}
+
+int get_plain_pk_from_asn1(void *pk_ptr, unsigned int pk_len, void **plain_pk,
+ unsigned int *len, int *pk_alg)
+{
+ int ret;
+ mbedtls_pk_context mbedtls_pk = {0};
+ unsigned char *p, *end;
+ mbedtls_asn1_buf alg_params = {0};
+ mbedtls_asn1_buf alg_oid = {0};
+
+ *plain_pk = NULL;
+ *len = 0U;
+
+ /* Parse the public key */
+ mbedtls_pk_init(&mbedtls_pk);
+ p = (unsigned char *)pk_ptr;
+ end = (unsigned char *)(p + pk_len);
+
+ ret = mbedtls_asn1_get_tag(&p, end, len,
+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+ if (ret != 0) {
+ return -EINVAL;
+ }
+
+ end = p + *len;
+ ret = mbedtls_asn1_get_alg(&p, end, &alg_oid, &alg_params);
+ if (ret != 0) {
+ VERBOSE("%s: mbedtls_asn1_get_alg (%d)\n", __func__, ret);
+ return -EINVAL;
+ }
+
+ if (pk_alg != NULL) {
+ if ((strlen(MBEDTLS_OID_EC_GRP_SECP256R1) == alg_params.len) &&
+ (memcmp(MBEDTLS_OID_EC_GRP_SECP256R1, alg_params.p, alg_params.len) == 0)) {
+ *pk_alg = BOOT_API_ECDSA_ALGO_TYPE_P256NIST;
+ } else if ((strlen(OID_EC_GRP_BP256T1) == alg_params.len) &&
+ (memcmp(OID_EC_GRP_BP256T1, alg_params.p, alg_params.len) == 0)) {
+ *pk_alg = BOOT_API_ECDSA_ALGO_TYPE_BRAINPOOL256;
+ } else {
+ ERROR("%s: Algorithm is not supported\n", __func__);
+ return -EINVAL;
+ }
+ }
+
+ ret = mbedtls_asn1_get_bitstring_null(&p, end, len);
+ if (ret != 0) {
+ VERBOSE("%s: mbedtls_asn1_get_bitstring_null (%d)\n", __func__, ret);
+ return -EINVAL;
+ }
+
+ /* We remove the ident (0x04) first byte. */
+ if ((*len < 1U) || (p[0] != MBEDTLS_ASN1_OCTET_STRING)) {
+ VERBOSE("%s: not expected len or tag\n", __func__);
+ return -EINVAL;
+ }
+
+ *len = *len - 1U;
+ *plain_pk = p + 1U;
+
+ return 0;
+}
+
+#if STM32MP_CRYPTO_ROM_LIB
+uint32_t verify_signature(uint8_t *hash_in, uint8_t *pubkey_in,
+ uint8_t *signature, uint32_t ecc_algo)
+{
+ int ret;
+
+ ret = mmap_add_dynamic_region(STM32MP_ROM_BASE, STM32MP_ROM_BASE,
+ STM32MP_ROM_SIZE_2MB_ALIGNED, MT_CODE | MT_SECURE);
+ if (ret != 0) {
+ VERBOSE("%s: mmap_add_dynamic_region (%d)\n", __func__, ret);
+ return CRYPTO_ERR_SIGNATURE;
+ }
+
+ ret = auth_ops.verify_signature(hash_in, pubkey_in, signature, ecc_algo);
+
+ if (ret != BOOT_API_RETURN_OK) {
+ VERBOSE("%s: auth_ops.verify_sign (%d)\n", __func__, ret);
+ ret = CRYPTO_ERR_SIGNATURE;
+ } else {
+ ret = 0;
+ }
+
+ mmap_remove_dynamic_region(STM32MP_ROM_BASE, STM32MP_ROM_SIZE_2MB_ALIGNED);
+
+ return ret;
+}
+
+int plat_convert_pk(void *full_pk_ptr, unsigned int full_pk_len,
+ void **hashed_pk_ptr, unsigned int *hashed_pk_len)
+{
+ return get_plain_pk_from_asn1(full_pk_ptr, full_pk_len, hashed_pk_ptr, hashed_pk_len, NULL);
+}
+#else /* STM32MP_CRYPTO_ROM_LIB*/
+static uint32_t verify_signature(uint8_t *hash_in, uint8_t *pubkey_in,
+ uint8_t *signature, uint32_t ecc_algo)
+{
+ int ret = -1;
+ enum stm32_pka_ecdsa_curve_id cid;
+
+ switch (ecc_algo) {
+ case BOOT_API_ECDSA_ALGO_TYPE_P256NIST:
+#if PKA_USE_NIST_P256
+ cid = PKA_NIST_P256;
+ ret = 0;
+#else
+ WARN("%s nist_p256 requested but not included\n", __func__);
+#endif
+ break;
+ case BOOT_API_ECDSA_ALGO_TYPE_BRAINPOOL256:
+#if PKA_USE_BRAINPOOL_P256T1
+ cid = PKA_BRAINPOOL_P256T1;
+ ret = 0;
+#else
+ WARN("%s brainpool_p256t1 requested but not included\n", __func__);
+#endif
+ break;
+ default:
+ WARN("%s unexpected ecc_algo(%u)\n", __func__, ecc_algo);
+ break;
+ }
+
+ if (ret < 0) {
+ return CRYPTO_ERR_SIGNATURE;
+ }
+
+ ret = stm32_pka_ecdsa_verif(hash_in,
+ BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES,
+ signature, BOOT_API_ECDSA_SIGNATURE_LEN_IN_BYTES / 2U,
+ signature + BOOT_API_ECDSA_SIGNATURE_LEN_IN_BYTES / 2U,
+ BOOT_API_ECDSA_SIGNATURE_LEN_IN_BYTES / 2U,
+ pubkey_in, BOOT_API_ECDSA_PUB_KEY_LEN_IN_BYTES / 2U,
+ pubkey_in + BOOT_API_ECDSA_PUB_KEY_LEN_IN_BYTES / 2U,
+ BOOT_API_ECDSA_PUB_KEY_LEN_IN_BYTES / 2U, cid);
+ if (ret < 0) {
+ return CRYPTO_ERR_SIGNATURE;
+ }
+
+ return 0;
+}
+
+int plat_convert_pk(void *full_pk_ptr, unsigned int full_pk_len,
+ void **hashed_pk_ptr, unsigned int *hashed_pk_len)
+{
+ static uint8_t st_pk[CRYPTO_PUBKEY_MAX_SIZE + sizeof(uint32_t)];
+ int ret;
+ void *plain_pk;
+ unsigned int len;
+ int curve_id;
+ uint32_t cid;
+
+ ret = get_plain_pk_from_asn1(full_pk_ptr, full_pk_len, &plain_pk, &len, &curve_id);
+ if ((ret != 0) || (len > CRYPTO_PUBKEY_MAX_SIZE)) {
+ return -EINVAL;
+ }
+
+ cid = curve_id; /* we want value of curve_id (1 or 2) in a uint32_t */
+
+ memcpy(st_pk, &cid, sizeof(cid));
+ memcpy(st_pk + sizeof(cid), plain_pk, len);
+
+ *hashed_pk_ptr = st_pk;
+ *hashed_pk_len = len + sizeof(cid);
+
+ return 0;
+}
+#endif /* STM32MP_CRYPTO_ROM_LIB */
+
+static int get_plain_digest_from_asn1(void *digest_ptr, unsigned int digest_len,
+ uint8_t **out, size_t *out_len, mbedtls_md_type_t *md_alg)
+{
+ int ret;
+ mbedtls_asn1_buf hash_oid, params;
+ size_t len;
+ unsigned char *p, *end;
+
+ *out = NULL;
+ *out_len = 0U;
+
+ /* Digest info should be an MBEDTLS_ASN1_SEQUENCE */
+ p = (unsigned char *)digest_ptr;
+ end = p + digest_len;
+ ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
+ MBEDTLS_ASN1_SEQUENCE);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* Get the hash algorithm */
+ ret = mbedtls_asn1_get_alg(&p, end, &hash_oid, &params);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = mbedtls_oid_get_md_alg(&hash_oid, md_alg);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* Length of hash must match the algorithm's size */
+ if (len != BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES) {
+ return -1;
+ }
+
+ *out = p;
+ *out_len = len;
+
+ return 0;
+}
+
+static int crypto_verify_signature(void *data_ptr, unsigned int data_len,
+ void *sig_ptr, unsigned int sig_len,
+ void *sig_alg, unsigned int sig_alg_len,
+ void *pk_ptr, unsigned int pk_len)
+{
+ uint8_t image_hash[CRYPTO_HASH_MAX_SIZE] = {0};
+ uint8_t sig[CRYPTO_SIGN_MAX_SIZE];
+ uint8_t my_pk[CRYPTO_PUBKEY_MAX_SIZE];
+ int ret;
+ size_t len;
+ mbedtls_asn1_sequence seq;
+ mbedtls_asn1_sequence *cur;
+ unsigned char *p, *end;
+ int curve_id;
+ mbedtls_asn1_buf sig_oid, sig_params;
+ mbedtls_md_type_t md_alg;
+ mbedtls_pk_type_t pk_alg;
+ size_t bignum_len = sizeof(sig) / 2U;
+ unsigned int seq_num = 0U;
+
+ if (!stm32mp_is_closed_device() && !stm32mp_is_auth_supported()) {
+ return CRYPTO_SUCCESS;
+ }
+
+ /* Get pointers to signature OID and parameters */
+ p = (unsigned char *)sig_alg;
+ end = (unsigned char *)(p + sig_alg_len);
+ ret = mbedtls_asn1_get_alg(&p, end, &sig_oid, &sig_params);
+ if (ret != 0) {
+ VERBOSE("%s: mbedtls_asn1_get_alg (%d)\n", __func__, ret);
+ return CRYPTO_ERR_SIGNATURE;
+ }
+
+ /* Get the actual signature algorithm (MD + PK) */
+ ret = mbedtls_oid_get_sig_alg(&sig_oid, &md_alg, &pk_alg);
+ if (ret != 0) {
+ VERBOSE("%s: mbedtls_oid_get_sig_alg (%d)\n", __func__, ret);
+ return CRYPTO_ERR_SIGNATURE;
+ }
+
+ if ((md_alg != MBEDTLS_MD_SHA256) || (pk_alg != MBEDTLS_PK_ECDSA)) {
+ VERBOSE("%s: md_alg=%u pk_alg=%u\n", __func__, md_alg, pk_alg);
+ return CRYPTO_ERR_SIGNATURE;
+ }
+
+ ret = get_plain_pk_from_asn1(pk_ptr, pk_len, &pk_ptr, &pk_len, &curve_id);
+ if (ret != 0) {
+ VERBOSE("%s: get_plain_pk_from_asn1 (%d)\n", __func__, ret);
+ return CRYPTO_ERR_SIGNATURE;
+ }
+
+ /* We expect a known pk_len */
+ if (pk_len != sizeof(my_pk)) {
+ VERBOSE("%s: pk_len=%u sizeof(my_pk)=%zu)\n", __func__, pk_len, sizeof(my_pk));
+ return CRYPTO_ERR_SIGNATURE;
+ }
+
+ /* Need to copy as auth_ops.verify_signature
+ * expects aligned public key.
+ */
+ memcpy(my_pk, pk_ptr, sizeof(my_pk));
+
+ /* Get the signature (bitstring) */
+ p = (unsigned char *)sig_ptr;
+ end = (unsigned char *)(p + sig_len);
+ ret = mbedtls_asn1_get_bitstring_null(&p, end, &len);
+ if (ret != 0) {
+ VERBOSE("%s: mbedtls_asn1_get_bitstring_null (%d)\n", __func__, ret);
+ return CRYPTO_ERR_SIGNATURE;
+ }
+
+ /* Get r and s from sequence */
+ ret = mbedtls_asn1_get_sequence_of(&p, end, &seq, MBEDTLS_ASN1_INTEGER);
+ if (ret != 0) {
+ VERBOSE("%s: mbedtls_asn1_get_sequence_of (%d)\n", __func__, ret);
+ return CRYPTO_ERR_SIGNATURE;
+ }
+
+ /* We expect only 2 integers (r and s) from the sequence */
+ if (seq.next->next != NULL) {
+ cur = seq.next;
+ mbedtls_asn1_sequence *next;
+
+ VERBOSE("%s: nb seq != 2\n", __func__);
+ /* Free all the sequences */
+ while (cur != NULL) {
+ next = cur->next;
+ mbedtls_free(cur);
+ cur = next;
+ }
+
+ return CRYPTO_ERR_SIGNATURE;
+ }
+
+ /*
+ * ECDSA signatures are composed of a tuple (R,S) where R and S are between 0 and n.
+ * This means that the R and S can have a maximum of 32 each, but can also be smaller.
+ * Also seen the integer sequence may (sometime) start with 0x00 as MSB, but we can only
+ * manage exactly 2*32 bytes, we remove this higher byte if there are not 00,
+ * we will fail either.
+ */
+ cur = &seq;
+ memset(sig, 0U, sizeof(sig));
+
+ while (cur != NULL) {
+ size_t skip = 0U;
+ size_t seek = seq_num * bignum_len;
+
+ if (cur->buf.len > bignum_len) {
+ /* Remove extra 0x00 bytes */
+ skip = cur->buf.len - bignum_len;
+ } else if (cur->buf.len < bignum_len) {
+ /* Add padding to match HW required size */
+ seek += (bignum_len % cur->buf.len);
+ }
+
+ if (seek + cur->buf.len > sizeof(sig) + skip) {
+ panic();
+ }
+
+ memcpy(sig + seek, cur->buf.p + skip, cur->buf.len - skip);
+ cur = cur->next;
+ seq_num++;
+ }
+
+ /* Need to free allocated 'next' in mbedtls_asn1_get_sequence_of */
+ mbedtls_free(seq.next);
+
+ /* Compute hash for the data covered by the signature */
+ stm32_hash_init(HASH_SHA256);
+
+ ret = stm32_hash_final_update((uint8_t *)data_ptr, data_len, image_hash);
+ if (ret != 0) {
+ VERBOSE("%s: stm32_hash_final_update (%d)\n", __func__, ret);
+ return CRYPTO_ERR_SIGNATURE;
+ }
+
+ return verify_signature(image_hash, my_pk, sig, curve_id);
+}
+
+static int crypto_verify_hash(void *data_ptr, unsigned int data_len,
+ void *digest_info_ptr,
+ unsigned int digest_info_len)
+{
+ int ret;
+ uint8_t calc_hash[BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES];
+ unsigned char *p;
+ mbedtls_md_type_t md_alg;
+ size_t len;
+
+ /* we receive an asn1 encapsulated digest, we flatten it */
+ ret = get_plain_digest_from_asn1(digest_info_ptr,
+ digest_info_len, &p, &len,
+ &md_alg);
+ if ((ret != 0) || (md_alg != MBEDTLS_MD_SHA256) || (len != sizeof(calc_hash))) {
+ return CRYPTO_ERR_HASH;
+ }
+
+ digest_info_ptr = p;
+ digest_info_len = len;
+
+ stm32_hash_init(HASH_SHA256);
+
+ ret = stm32_hash_final_update(data_ptr, data_len, calc_hash);
+ if (ret != 0) {
+ VERBOSE("%s: hash failed\n", __func__);
+ return CRYPTO_ERR_HASH;
+ }
+
+ ret = memcmp(calc_hash, digest_info_ptr, digest_info_len);
+ if (ret != 0) {
+ VERBOSE("%s: not expected digest\n", __func__);
+ ret = CRYPTO_ERR_HASH;
+ }
+
+ return ret;
+}
+
+#if !defined(DECRYPTION_SUPPORT_none)
+static int derive_key(uint8_t *key, size_t *key_len, size_t len,
+ unsigned int *flags, const uint8_t *img_id, size_t img_id_len)
+{
+ size_t i, j;
+
+ assert(*key_len >= 32U);
+
+ /*
+ * Not a real derivation yet
+ *
+ * But we expect a 32 bytes key, and OTP is only 16 bytes
+ * => duplicate.
+ */
+ for (i = 0U, j = len; j < 32U;
+ i += sizeof(uint32_t), j += sizeof(uint32_t)) {
+ memcpy(key + j, key + i, sizeof(uint32_t));
+ }
+
+ *key_len = 32U;
+ /* Variable 'key' store a real key */
+ *flags = 0U;
+
+ return 0;
+}
+
+int plat_get_enc_key_info(enum fw_enc_status_t fw_enc_status, uint8_t *key,
+ size_t *key_len, unsigned int *flags,
+ const uint8_t *img_id, size_t img_id_len)
+{
+ uint32_t otp_idx;
+ uint32_t otp_len;
+ size_t read_len;
+ size_t i;
+
+ if (fw_enc_status == FW_ENC_WITH_BSSK) {
+ return -EINVAL;
+ }
+
+ if (stm32_get_otp_index(ENCKEY_OTP, &otp_idx, &otp_len) != 0) {
+ VERBOSE("%s: get %s index error\n", __func__, ENCKEY_OTP);
+ return -EINVAL;
+ }
+
+ if (otp_len > (*key_len * CHAR_BIT)) {
+ VERBOSE("%s: length Error otp_len=%u key_len=%u\n", __func__,
+ otp_len, *key_len * CHAR_BIT);
+ return -EINVAL;
+ }
+
+ read_len = otp_len / CHAR_BIT;
+ assert(read_len % sizeof(uint32_t) == 0);
+
+ for (i = 0U; i < read_len / sizeof(uint32_t); i++) {
+ uint32_t tmp;
+ uint32_t otp_val;
+
+ if (stm32_get_otp_value_from_idx(otp_idx + i, &otp_val) != 0) {
+ zeromem(key, *key_len);
+ VERBOSE("%s: unable to read from otp\n", __func__);
+ return -EINVAL;
+ }
+
+ tmp = bswap32(otp_val);
+ memcpy(key + i * sizeof(uint32_t), &tmp, sizeof(tmp));
+ }
+
+ /* Now we have the OTP values in key till read_len */
+
+ if (derive_key(key, key_len, read_len, flags, img_id,
+ img_id_len) != 0) {
+ zeromem(key, *key_len);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static enum stm32_saes_key_selection select_key(unsigned int key_flags)
+{
+ if ((key_flags & ENC_KEY_IS_IDENTIFIER) != 0U) {
+ panic();
+ }
+
+ /* Use the provided key buffer */
+ return STM32_SAES_KEY_SOFT;
+}
+
+static int stm32_decrypt_aes_gcm(void *data, size_t data_len,
+ const void *key, unsigned int key_len,
+ unsigned int key_flags,
+ const void *iv, unsigned int iv_len,
+ const void *tag, unsigned int tag_len)
+{
+ int ret;
+ struct stm32_saes_context ctx;
+ unsigned char tag_buf[CRYPTO_MAX_TAG_SIZE];
+ enum stm32_saes_key_selection key_mode;
+ unsigned int diff = 0U;
+ unsigned int i;
+
+ key_mode = select_key(key_flags);
+
+ ret = stm32_saes_init(&ctx, true, STM32_SAES_MODE_GCM, key_mode, key,
+ key_len, iv, iv_len);
+ if (ret != 0) {
+ return CRYPTO_ERR_INIT;
+ }
+
+ ret = stm32_saes_update_assodata(&ctx, true, NULL, 0U);
+ if (ret != 0) {
+ return CRYPTO_ERR_DECRYPTION;
+ }
+
+ ret = stm32_saes_update_load(&ctx, true, data, data, data_len);
+ if (ret != 0) {
+ return CRYPTO_ERR_DECRYPTION;
+ }
+
+ ret = stm32_saes_final(&ctx, tag_buf, sizeof(tag_buf));
+ if (ret != 0) {
+ return CRYPTO_ERR_DECRYPTION;
+ }
+
+ /* Check tag in "constant-time" */
+ for (i = 0U; i < tag_len; i++) {
+ diff |= ((const unsigned char *)tag)[i] ^ tag_buf[i];
+ }
+
+ if (diff != 0U) {
+ return CRYPTO_ERR_DECRYPTION;
+ }
+
+ return CRYPTO_SUCCESS;
+}
+
+/*
+ * Authenticated decryption of an image
+ *
+ */
+static int crypto_auth_decrypt(enum crypto_dec_algo dec_algo, void *data_ptr, size_t len,
+ const void *key, unsigned int key_len, unsigned int key_flags,
+ const void *iv, unsigned int iv_len, const void *tag,
+ unsigned int tag_len)
+{
+ int rc = -1;
+ uint32_t real_iv[4];
+
+ switch (dec_algo) {
+ case CRYPTO_GCM_DECRYPT:
+ /*
+ * GCM expect a Nonce
+ * The AES IV is the nonce (a uint32_t[3])
+ * then a counter (a uint32_t big endian)
+ * The counter starts at 2.
+ */
+ memcpy(real_iv, iv, iv_len);
+ real_iv[3] = htobe32(0x2U);
+
+ rc = stm32_decrypt_aes_gcm(data_ptr, len, key, key_len, key_flags,
+ real_iv, sizeof(real_iv), tag, tag_len);
+ break;
+ default:
+ rc = CRYPTO_ERR_DECRYPTION;
+ break;
+ }
+
+ if (rc != 0) {
+ return rc;
+ }
+
+ return CRYPTO_SUCCESS;
+}
+
+REGISTER_CRYPTO_LIB("stm32_crypto_lib",
+ crypto_lib_init,
+ crypto_verify_signature,
+ crypto_verify_hash,
+ crypto_auth_decrypt);
+
+#else /* No decryption support */
+REGISTER_CRYPTO_LIB("stm32_crypto_lib",
+ crypto_lib_init,
+ crypto_verify_signature,
+ crypto_verify_hash,
+ NULL);
+
+#endif
diff --git a/plat/st/common/stm32mp_dt.c b/plat/st/common/stm32mp_dt.c
new file mode 100644
index 0000000..c9efeb5
--- /dev/null
+++ b/plat/st/common/stm32mp_dt.c
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+
+#include <common/debug.h>
+#include <common/fdt_wrappers.h>
+#include <drivers/st/regulator.h>
+#include <drivers/st/stm32_gpio.h>
+#include <libfdt.h>
+
+#include <platform_def.h>
+#include <stm32mp_dt.h>
+
+static void *fdt;
+
+/*******************************************************************************
+ * This function checks device tree file with its header.
+ * Returns 0 on success and a negative FDT error code on failure.
+ ******************************************************************************/
+int dt_open_and_check(uintptr_t dt_addr)
+{
+ int ret;
+
+ ret = fdt_check_header((void *)dt_addr);
+ if (ret == 0) {
+ fdt = (void *)dt_addr;
+ }
+
+ return ret;
+}
+
+/*******************************************************************************
+ * This function gets the address of the DT.
+ * If DT is OK, fdt_addr is filled with DT address.
+ * Returns 1 if success, 0 otherwise.
+ ******************************************************************************/
+int fdt_get_address(void **fdt_addr)
+{
+ if (fdt == NULL) {
+ return 0;
+ }
+
+ *fdt_addr = fdt;
+
+ return 1;
+}
+
+/*******************************************************************************
+ * This function check the presence of a node (generic use of fdt library).
+ * Returns true if present, else return false.
+ ******************************************************************************/
+bool fdt_check_node(int node)
+{
+ int len;
+ const char *cchar;
+
+ cchar = fdt_get_name(fdt, node, &len);
+
+ return (cchar != NULL) && (len >= 0);
+}
+
+/*******************************************************************************
+ * This function return global node status (generic use of fdt library).
+ ******************************************************************************/
+uint8_t fdt_get_status(int node)
+{
+ uint8_t status = DT_DISABLED;
+ const char *cchar;
+
+ cchar = fdt_getprop(fdt, node, "status", NULL);
+ if ((cchar == NULL) ||
+ (strncmp(cchar, "okay", strlen("okay")) == 0)) {
+ status |= DT_NON_SECURE;
+ }
+
+ cchar = fdt_getprop(fdt, node, "secure-status", NULL);
+ if (cchar == NULL) {
+ if (status == DT_NON_SECURE) {
+ status |= DT_SECURE;
+ }
+ } else if (strncmp(cchar, "okay", strlen("okay")) == 0) {
+ status |= DT_SECURE;
+ }
+
+ return status;
+}
+
+#if ENABLE_ASSERTIONS
+/*******************************************************************************
+ * This function returns the address cells from the node parent.
+ * Returns:
+ * - #address-cells value if success.
+ * - invalid value if error.
+ * - a default value if undefined #address-cells property as per libfdt
+ * implementation.
+ ******************************************************************************/
+static int fdt_get_node_parent_address_cells(int node)
+{
+ int parent;
+
+ parent = fdt_parent_offset(fdt, node);
+ if (parent < 0) {
+ return -FDT_ERR_NOTFOUND;
+ }
+
+ return fdt_address_cells(fdt, parent);
+}
+#endif
+
+/*******************************************************************************
+ * This function gets the stdout pin configuration information from the DT.
+ * And then calls the sub-function to treat it and set GPIO registers.
+ * Returns 0 on success and a negative FDT error code on failure.
+ ******************************************************************************/
+int dt_set_stdout_pinctrl(void)
+{
+ int node;
+
+ node = fdt_get_stdout_node_offset(fdt);
+ if (node < 0) {
+ return -FDT_ERR_NOTFOUND;
+ }
+
+ return dt_set_pinctrl_config(node);
+}
+
+/*******************************************************************************
+ * This function fills the generic information from a given node.
+ ******************************************************************************/
+void dt_fill_device_info(struct dt_node_info *info, int node)
+{
+ const fdt32_t *cuint;
+
+ assert(fdt_get_node_parent_address_cells(node) == 1);
+
+ cuint = fdt_getprop(fdt, node, "reg", NULL);
+ if (cuint != NULL) {
+ info->base = fdt32_to_cpu(*cuint);
+ } else {
+ info->base = 0;
+ }
+
+ cuint = fdt_getprop(fdt, node, "clocks", NULL);
+ if (cuint != NULL) {
+ cuint++;
+ info->clock = (int)fdt32_to_cpu(*cuint);
+ } else {
+ info->clock = -1;
+ }
+
+ cuint = fdt_getprop(fdt, node, "resets", NULL);
+ if (cuint != NULL) {
+ cuint++;
+ info->reset = (int)fdt32_to_cpu(*cuint);
+ } else {
+ info->reset = -1;
+ }
+
+ info->status = fdt_get_status(node);
+}
+
+/*******************************************************************************
+ * This function retrieve the generic information from DT.
+ * Returns node on success and a negative FDT error code on failure.
+ ******************************************************************************/
+int dt_get_node(struct dt_node_info *info, int offset, const char *compat)
+{
+ int node;
+
+ node = fdt_node_offset_by_compatible(fdt, offset, compat);
+ if (node < 0) {
+ return -FDT_ERR_NOTFOUND;
+ }
+
+ dt_fill_device_info(info, node);
+
+ return node;
+}
+
+/*******************************************************************************
+ * This function gets the UART instance info of stdout from the DT.
+ * Returns node on success and a negative FDT error code on failure.
+ ******************************************************************************/
+int dt_get_stdout_uart_info(struct dt_node_info *info)
+{
+ int node;
+
+ node = fdt_get_stdout_node_offset(fdt);
+ if (node < 0) {
+ return -FDT_ERR_NOTFOUND;
+ }
+
+ dt_fill_device_info(info, node);
+
+ return node;
+}
+
+/*******************************************************************************
+ * This function returns the node offset matching compatible string in the DT,
+ * and also matching the reg property with the given address.
+ * Returns value on success, and error value on failure.
+ ******************************************************************************/
+int dt_match_instance_by_compatible(const char *compatible, uintptr_t address)
+{
+ int node;
+
+ fdt_for_each_compatible_node(fdt, node, compatible) {
+ const fdt32_t *cuint;
+
+ assert(fdt_get_node_parent_address_cells(node) == 1);
+
+ cuint = fdt_getprop(fdt, node, "reg", NULL);
+ if (cuint == NULL) {
+ continue;
+ }
+
+ if ((uintptr_t)fdt32_to_cpu(*cuint) == address) {
+ return node;
+ }
+ }
+
+ return -FDT_ERR_NOTFOUND;
+}
+
+/*******************************************************************************
+ * This function gets DDR size information from the DT.
+ * Returns value in bytes on success, and 0 on failure.
+ ******************************************************************************/
+uint32_t dt_get_ddr_size(void)
+{
+ static uint32_t size;
+ int node;
+
+ if (size != 0U) {
+ return size;
+ }
+
+ node = fdt_node_offset_by_compatible(fdt, -1, DT_DDR_COMPAT);
+ if (node < 0) {
+ INFO("%s: Cannot read DDR node in DT\n", __func__);
+ return 0;
+ }
+
+ size = fdt_read_uint32_default(fdt, node, "st,mem-size", 0U);
+
+ flush_dcache_range((uintptr_t)&size, sizeof(uint32_t));
+
+ return size;
+}
+
+/*******************************************************************************
+ * This function gets PWR VDD regulator voltage information from the DT.
+ * Returns value in microvolts on success, and 0 on failure.
+ ******************************************************************************/
+uint32_t dt_get_pwr_vdd_voltage(void)
+{
+ struct rdev *regul = dt_get_vdd_regulator();
+ uint16_t min;
+
+ if (regul == NULL) {
+ return 0;
+ }
+
+ regulator_get_range(regul, &min, NULL);
+
+ return (uint32_t)min * 1000U;
+}
+
+/*******************************************************************************
+ * This function retrieves VDD supply regulator from DT.
+ * Returns an rdev taken from supply node, NULL otherwise.
+ ******************************************************************************/
+struct rdev *dt_get_vdd_regulator(void)
+{
+ int node = fdt_node_offset_by_compatible(fdt, -1, DT_PWR_COMPAT);
+
+ if (node < 0) {
+ return NULL;
+ }
+
+ return regulator_get_by_supply_name(fdt, node, "vdd");
+}
+
+/*******************************************************************************
+ * This function retrieves CPU supply regulator from DT.
+ * Returns an rdev taken from supply node, NULL otherwise.
+ ******************************************************************************/
+struct rdev *dt_get_cpu_regulator(void)
+{
+ int node = fdt_path_offset(fdt, "/cpus/cpu@0");
+
+ if (node < 0) {
+ return NULL;
+ }
+
+ return regulator_get_by_supply_name(fdt, node, "cpu");
+}
+
+/*******************************************************************************
+ * This function retrieves board model from DT
+ * Returns string taken from model node, NULL otherwise
+ ******************************************************************************/
+const char *dt_get_board_model(void)
+{
+ int node = fdt_path_offset(fdt, "/");
+
+ if (node < 0) {
+ return NULL;
+ }
+
+ return (const char *)fdt_getprop(fdt, node, "model", NULL);
+}
+
+/*******************************************************************************
+ * dt_find_otp_name: get OTP ID and length in DT.
+ * name: sub-node name to look up.
+ * otp: pointer to read OTP number or NULL.
+ * otp_len: pointer to read OTP length in bits or NULL.
+ * return value: 0 if no error, an FDT error value otherwise.
+ ******************************************************************************/
+int dt_find_otp_name(const char *name, uint32_t *otp, uint32_t *otp_len)
+{
+ int node;
+ int len;
+ const fdt32_t *cuint;
+
+ if ((name == NULL) || (otp == NULL)) {
+ return -FDT_ERR_BADVALUE;
+ }
+
+ node = fdt_node_offset_by_compatible(fdt, -1, DT_BSEC_COMPAT);
+ if (node < 0) {
+ return node;
+ }
+
+ node = fdt_subnode_offset(fdt, node, name);
+ if (node < 0) {
+ ERROR("nvmem node %s not found\n", name);
+ return node;
+ }
+
+ cuint = fdt_getprop(fdt, node, "reg", &len);
+ if ((cuint == NULL) || (len != (2 * (int)sizeof(uint32_t)))) {
+ ERROR("Malformed nvmem node %s: ignored\n", name);
+ return -FDT_ERR_BADVALUE;
+ }
+
+ if (fdt32_to_cpu(*cuint) % sizeof(uint32_t)) {
+ ERROR("Misaligned nvmem %s element: ignored\n", name);
+ return -FDT_ERR_BADVALUE;
+ }
+
+ if (otp != NULL) {
+ *otp = fdt32_to_cpu(*cuint) / sizeof(uint32_t);
+ }
+
+ if (otp_len != NULL) {
+ cuint++;
+ *otp_len = fdt32_to_cpu(*cuint) * CHAR_BIT;
+ }
+
+ return 0;
+}
+
+/*******************************************************************************
+ * This function gets the pin count for a GPIO bank based from the FDT.
+ * It also checks node consistency.
+ ******************************************************************************/
+int fdt_get_gpio_bank_pin_count(unsigned int bank)
+{
+ int pinctrl_node;
+ int node;
+ uint32_t bank_offset;
+
+ pinctrl_node = stm32_get_gpio_bank_pinctrl_node(fdt, bank);
+ if (pinctrl_node < 0) {
+ return -FDT_ERR_NOTFOUND;
+ }
+
+ bank_offset = stm32_get_gpio_bank_offset(bank);
+
+ fdt_for_each_subnode(node, fdt, pinctrl_node) {
+ const fdt32_t *cuint;
+ int pin_count;
+ int len;
+ int i;
+
+ if (fdt_getprop(fdt, node, "gpio-controller", NULL) == NULL) {
+ continue;
+ }
+
+ cuint = fdt_getprop(fdt, node, "reg", NULL);
+ if (cuint == NULL) {
+ continue;
+ }
+
+ if (fdt32_to_cpu(*cuint) != bank_offset) {
+ continue;
+ }
+
+ if (fdt_get_status(node) == DT_DISABLED) {
+ return 0;
+ }
+
+ /* Parse gpio-ranges with its 4 parameters */
+ cuint = fdt_getprop(fdt, node, "gpio-ranges", &len);
+ len /= sizeof(*cuint);
+ if ((len % 4) != 0) {
+ return -FDT_ERR_BADVALUE;
+ }
+
+ /* Get the last defined gpio line (offset + nb of pins) */
+ pin_count = fdt32_to_cpu(*(cuint + 1)) + fdt32_to_cpu(*(cuint + 3));
+ for (i = 0; i < len / 4; i++) {
+ pin_count = MAX(pin_count, (int)(fdt32_to_cpu(*(cuint + 1)) +
+ fdt32_to_cpu(*(cuint + 3))));
+ cuint += 4;
+ }
+
+ return pin_count;
+ }
+
+ return 0;
+}
diff --git a/plat/st/common/stm32mp_fconf_io.c b/plat/st/common/stm32mp_fconf_io.c
new file mode 100644
index 0000000..0b6cc78
--- /dev/null
+++ b/plat/st/common/stm32mp_fconf_io.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2021-2022, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+
+#include <common/debug.h>
+#include <common/fdt_wrappers.h>
+#include <drivers/io/io_storage.h>
+#include <drivers/mmc.h>
+#include <lib/fconf/fconf.h>
+#include <lib/object_pool.h>
+#include <libfdt.h>
+#include <tools_share/firmware_image_package.h>
+
+#include <platform_def.h>
+#include <stm32mp_efi.h>
+#include <stm32mp_fconf_getter.h>
+#include <stm32mp_io_storage.h>
+
+#if STM32MP_SDMMC || STM32MP_EMMC
+static io_block_spec_t gpt_block_spec = {
+ .offset = 0U,
+ .length = 34U * MMC_BLOCK_SIZE, /* Size of GPT table */
+};
+#endif
+
+#if (STM32MP_SDMMC || STM32MP_EMMC) && PSA_FWU_SUPPORT
+io_block_spec_t metadata_block_spec = {
+ .offset = 0, /* To be filled at runtime */
+ .length = 0, /* To be filled at runtime */
+};
+#endif /* (STM32MP_SDMMC || STM32MP_EMMC) && PSA_FWU_SUPPORT */
+
+/* By default, STM32 platforms load images from the FIP */
+struct plat_io_policy policies[MAX_NUMBER_IDS] = {
+ [FIP_IMAGE_ID] = {
+ .dev_handle = &storage_dev_handle,
+ .image_spec = (uintptr_t)&image_block_spec,
+ .img_type_guid = STM32MP_FIP_GUID,
+ .check = open_storage
+ },
+#ifndef DECRYPTION_SUPPORT_none
+ [ENC_IMAGE_ID] = {
+ .dev_handle = &fip_dev_handle,
+ .image_spec = (uintptr_t)NULL,
+ .img_type_guid = NULL_GUID,
+ .check = open_fip
+ },
+#endif
+#if STM32MP_SDMMC || STM32MP_EMMC
+ [GPT_IMAGE_ID] = {
+ .dev_handle = &storage_dev_handle,
+ .image_spec = (uintptr_t)&gpt_block_spec,
+ .img_type_guid = NULL_GUID,
+ .check = open_storage
+ },
+#endif
+#if (STM32MP_SDMMC || STM32MP_EMMC) && PSA_FWU_SUPPORT
+ [FWU_METADATA_IMAGE_ID] = {
+ .dev_handle = &storage_dev_handle,
+ .image_spec = (uintptr_t)&metadata_block_spec,
+ .img_type_guid = NULL_GUID,
+ .check = open_storage
+ },
+ [BKUP_FWU_METADATA_IMAGE_ID] = {
+ .dev_handle = &storage_dev_handle,
+ .image_spec = (uintptr_t)&metadata_block_spec,
+ .img_type_guid = NULL_GUID,
+ .check = open_storage
+ },
+#endif /* (STM32MP_SDMMC || STM32MP_EMMC) && PSA_FWU_SUPPORT */
+};
+
+#define DEFAULT_UUID_NUMBER U(7)
+
+#if TRUSTED_BOARD_BOOT
+#define TBBR_UUID_NUMBER U(6)
+#else
+#define TBBR_UUID_NUMBER U(0)
+#endif
+
+#define FCONF_ST_IO_UUID_NUMBER (DEFAULT_UUID_NUMBER + \
+ TBBR_UUID_NUMBER)
+
+static io_uuid_spec_t fconf_stm32mp_uuids[FCONF_ST_IO_UUID_NUMBER];
+static OBJECT_POOL_ARRAY(fconf_stm32mp_uuids_pool, fconf_stm32mp_uuids);
+
+struct policies_load_info {
+ unsigned int image_id;
+ const char *name;
+};
+
+/* image id to property name table */
+static const struct policies_load_info load_info[FCONF_ST_IO_UUID_NUMBER] = {
+ {FW_CONFIG_ID, "fw_cfg_uuid"},
+ {BL32_IMAGE_ID, "bl32_uuid"},
+ {BL32_EXTRA1_IMAGE_ID, "bl32_extra1_uuid"},
+ {BL32_EXTRA2_IMAGE_ID, "bl32_extra2_uuid"},
+ {BL33_IMAGE_ID, "bl33_uuid"},
+ {HW_CONFIG_ID, "hw_cfg_uuid"},
+ {TOS_FW_CONFIG_ID, "tos_fw_cfg_uuid"},
+#if TRUSTED_BOARD_BOOT
+ {STM32MP_CONFIG_CERT_ID, "stm32mp_cfg_cert_uuid"},
+ {TRUSTED_KEY_CERT_ID, "t_key_cert_uuid"},
+ {TRUSTED_OS_FW_KEY_CERT_ID, "tos_fw_key_cert_uuid"},
+ {NON_TRUSTED_FW_KEY_CERT_ID, "nt_fw_key_cert_uuid"},
+ {TRUSTED_OS_FW_CONTENT_CERT_ID, "tos_fw_content_cert_uuid"},
+ {NON_TRUSTED_FW_CONTENT_CERT_ID, "nt_fw_content_cert_uuid"},
+#endif /* TRUSTED_BOARD_BOOT */
+};
+
+int fconf_populate_stm32mp_io_policies(uintptr_t config)
+{
+ int node;
+ unsigned int i;
+
+ /* As libfdt uses void *, we can't avoid this cast */
+ const void *dtb = (void *)config;
+
+ /* Assert the node offset point to "st,io-fip-handle" compatible property */
+ const char *compatible_str = "st,io-fip-handle";
+
+ node = fdt_node_offset_by_compatible(dtb, -1, compatible_str);
+ if (node < 0) {
+ ERROR("FCONF: Can't find %s compatible in dtb\n", compatible_str);
+ return node;
+ }
+
+ /* Locate the uuid cells and read the value for all the load info uuid */
+ for (i = 0U; i < FCONF_ST_IO_UUID_NUMBER; i++) {
+ union uuid_helper_t uuid_helper;
+ io_uuid_spec_t *uuid_ptr;
+ int err;
+
+ uuid_ptr = pool_alloc(&fconf_stm32mp_uuids_pool);
+ err = fdtw_read_uuid(dtb, node, load_info[i].name, 16,
+ (uint8_t *)&uuid_helper);
+ if (err < 0) {
+ WARN("FCONF: Read cell failed for %s\n", load_info[i].name);
+ return err;
+ }
+
+ VERBOSE("FCONF: stm32mp-io_policies.%s cell found with value = "
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ load_info[i].name,
+ uuid_helper.uuid_struct.time_low[0], uuid_helper.uuid_struct.time_low[1],
+ uuid_helper.uuid_struct.time_low[2], uuid_helper.uuid_struct.time_low[3],
+ uuid_helper.uuid_struct.time_mid[0], uuid_helper.uuid_struct.time_mid[1],
+ uuid_helper.uuid_struct.time_hi_and_version[0],
+ uuid_helper.uuid_struct.time_hi_and_version[1],
+ uuid_helper.uuid_struct.clock_seq_hi_and_reserved,
+ uuid_helper.uuid_struct.clock_seq_low,
+ uuid_helper.uuid_struct.node[0], uuid_helper.uuid_struct.node[1],
+ uuid_helper.uuid_struct.node[2], uuid_helper.uuid_struct.node[3],
+ uuid_helper.uuid_struct.node[4], uuid_helper.uuid_struct.node[5]);
+
+ uuid_ptr->uuid = uuid_helper.uuid_struct;
+ policies[load_info[i].image_id].image_spec = (uintptr_t)uuid_ptr;
+ switch (load_info[i].image_id) {
+#if ENCRYPT_BL32 && !defined(DECRYPTION_SUPPORT_none)
+ case BL32_IMAGE_ID:
+ case BL32_EXTRA1_IMAGE_ID:
+ case BL32_EXTRA2_IMAGE_ID:
+ policies[load_info[i].image_id].dev_handle = &enc_dev_handle;
+ policies[load_info[i].image_id].check = open_enc_fip;
+ break;
+#endif
+ default:
+ policies[load_info[i].image_id].dev_handle = &fip_dev_handle;
+ policies[load_info[i].image_id].check = open_fip;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+FCONF_REGISTER_POPULATOR(TB_FW, stm32mp_io, fconf_populate_stm32mp_io_policies);
diff --git a/plat/st/common/stm32mp_trusted_boot.c b/plat/st/common/stm32mp_trusted_boot.c
new file mode 100644
index 0000000..051d6fc
--- /dev/null
+++ b/plat/st/common/stm32mp_trusted_boot.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2022, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <endian.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <common/debug.h>
+#include <common/tbbr/cot_def.h>
+#include <drivers/st/stm32_hash.h>
+#include <lib/fconf/fconf.h>
+#include <lib/fconf/fconf_dyn_cfg_getter.h>
+#include <lib/fconf/fconf_tbbr_getter.h>
+#include <lib/mmio.h>
+#include <lib/xlat_tables/xlat_tables_v2.h>
+#include <plat/common/platform.h>
+
+#include <boot_api.h>
+#include <platform_def.h>
+
+#define HEADER_AND_EXT_TOTAL_SIZE 512
+
+static uint8_t der_sha256_header[] = {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60,
+ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
+static uint8_t root_pk_hash[HASH_DER_LEN];
+
+static int copy_hash_from_otp(const char *otp_name, uint8_t *hash, size_t len)
+{
+ uint32_t otp_idx;
+ uint32_t otp_len;
+ size_t i;
+ bool valid = false;
+
+ assert(len % sizeof(uint32_t) == 0);
+
+ if (stm32_get_otp_index(otp_name, &otp_idx, &otp_len) != 0) {
+ VERBOSE("%s: get %s index error\n", __func__, otp_name);
+ return -EINVAL;
+ }
+ if (otp_len != (len * CHAR_BIT)) {
+ VERBOSE("%s: length Error\n", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0U; i < len / sizeof(uint32_t); i++) {
+ uint32_t tmp;
+ uint32_t otp_val;
+ uint32_t first;
+
+ if (stm32_get_otp_value_from_idx(otp_idx + i, &otp_val) != 0) {
+ VERBOSE("%s: unable to read from otp\n", __func__);
+ return -EINVAL;
+ }
+
+ tmp = bswap32(otp_val);
+ memcpy(hash + i * sizeof(uint32_t), &tmp, sizeof(tmp));
+
+ if (i == 0U) {
+ first = tmp;
+ }
+
+ /*
+ * Check if key hash values in OTP are 0 or 0xFFFFFFFFF
+ * programmed : Invalid Key
+ */
+ if (!stm32mp_is_closed_device() && !valid) {
+ if ((tmp != 0U) && (tmp != 0xFFFFFFFFU) && (tmp != first)) {
+ valid = true;
+ }
+ }
+ }
+
+ if (!stm32mp_is_closed_device() && !valid) {
+ return 0;
+ }
+
+ return len;
+}
+
+#if STM32_HEADER_VERSION_MAJOR == 1
+static int get_rotpk_hash(void *cookie, uint8_t *hash, size_t len)
+{
+ if (cookie != NULL) {
+ return -EINVAL;
+ }
+
+ return copy_hash_from_otp(PKH_OTP, hash, len);
+}
+#else
+static int get_rotpk_hash(void *cookie, uint8_t *hash, size_t len)
+{
+ int ret;
+ uint32_t pk_idx = 0U;
+ uint8_t calc_hash[BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES];
+ uint8_t otp_hash[BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES];
+ boot_api_image_header_t *hdr = (boot_api_image_header_t *)(SRAM3_BASE + SRAM3_SIZE -
+ HEADER_AND_EXT_TOTAL_SIZE);
+ boot_extension_header_t *ext_header = (boot_extension_header_t *)hdr->ext_header;
+ boot_ext_header_params_authentication_t *param;
+
+ if (cookie != NULL) {
+ return -EINVAL;
+ }
+
+ if (hdr->header_version != BOOT_API_HEADER_VERSION) {
+ VERBOSE("%s: unexpected header_version\n", __func__);
+ return -EINVAL;
+ }
+
+ param = (boot_ext_header_params_authentication_t *)ext_header->params;
+
+ pk_idx = param->pk_idx;
+
+ stm32_hash_init(HASH_SHA256);
+ ret = stm32_hash_final_update((uint8_t *)param->pk_hashes,
+ param->nb_pk * sizeof(boot_api_sha256_t), calc_hash);
+ if (ret != 0) {
+ VERBOSE("%s: hash failed\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = copy_hash_from_otp(PKH_OTP, otp_hash, len);
+ if (ret < 0) {
+ return -EINVAL;
+ }
+
+ if (ret != 0) {
+ ret = memcmp(calc_hash, otp_hash, sizeof(calc_hash));
+ if (ret != 0) {
+ VERBOSE("%s: not expected digest\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = sizeof(otp_hash);
+ }
+
+ memcpy(hash, param->pk_hashes[pk_idx], sizeof(otp_hash));
+
+ return ret;
+}
+#endif
+
+int plat_get_rotpk_info(void *cookie, void **key_ptr, unsigned int *key_len,
+ unsigned int *flags)
+{
+ size_t start_copy_idx = 0U;
+ int res;
+
+ memcpy(root_pk_hash, der_sha256_header, sizeof(der_sha256_header));
+ start_copy_idx = sizeof(der_sha256_header);
+
+ res = get_rotpk_hash(cookie, root_pk_hash + start_copy_idx,
+ BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES);
+ if (res < 0) {
+ return -EINVAL;
+ }
+
+ *key_len = HASH_DER_LEN;
+ *key_ptr = &root_pk_hash;
+ *flags = ROTPK_IS_HASH;
+
+ if ((res == 0) && !stm32mp_is_closed_device()) {
+ *flags |= ROTPK_NOT_DEPLOYED;
+ }
+
+ return 0;
+}
+
+int plat_get_nv_ctr(void *cookie, unsigned int *nv_ctr)
+{
+ *nv_ctr = mmio_read_32(TAMP_BASE + TAMP_COUNTR);
+
+ return 0;
+}
+
+int plat_set_nv_ctr(void *cookie, unsigned int nv_ctr)
+{
+ while (mmio_read_32(TAMP_BASE + TAMP_COUNTR) != nv_ctr) {
+ mmio_write_32(TAMP_BASE + TAMP_COUNTR, 1U);
+ }
+
+ return 0;
+}
+
+int plat_get_mbedtls_heap(void **heap_addr, size_t *heap_size)
+{
+ assert(heap_addr != NULL);
+ assert(heap_size != NULL);
+
+#if STM32MP_USE_EXTERNAL_HEAP
+ /* Retrieve the already allocated heap's info from DTB */
+ *heap_addr = FCONF_GET_PROPERTY(tbbr, dyn_config, mbedtls_heap_addr);
+ *heap_size = FCONF_GET_PROPERTY(tbbr, dyn_config, mbedtls_heap_size);
+
+ /* We expect heap already statically mapped */
+
+ return 0;
+#else
+ return get_mbedtls_heap_helper(heap_addr, heap_size);
+#endif
+}
diff --git a/plat/st/common/usb_dfu.c b/plat/st/common/usb_dfu.c
new file mode 100644
index 0000000..8bb0994
--- /dev/null
+++ b/plat/st/common/usb_dfu.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright (c) 2021, STMicroelectronics - All Rights Reserved
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <common/debug.h>
+
+#include <platform_def.h>
+#include <usb_dfu.h>
+
+/* Device states as defined in DFU spec */
+#define STATE_APP_IDLE 0
+#define STATE_APP_DETACH 1
+#define STATE_DFU_IDLE 2
+#define STATE_DFU_DNLOAD_SYNC 3
+#define STATE_DFU_DNLOAD_BUSY 4
+#define STATE_DFU_DNLOAD_IDLE 5
+#define STATE_DFU_MANIFEST_SYNC 6
+#define STATE_DFU_MANIFEST 7
+#define STATE_DFU_MANIFEST_WAIT_RESET 8
+#define STATE_DFU_UPLOAD_IDLE 9
+#define STATE_DFU_ERROR 10
+
+/* DFU errors */
+#define DFU_ERROR_NONE 0x00
+#define DFU_ERROR_TARGET 0x01
+#define DFU_ERROR_FILE 0x02
+#define DFU_ERROR_WRITE 0x03
+#define DFU_ERROR_ERASE 0x04
+#define DFU_ERROR_CHECK_ERASED 0x05
+#define DFU_ERROR_PROG 0x06
+#define DFU_ERROR_VERIFY 0x07
+#define DFU_ERROR_ADDRESS 0x08
+#define DFU_ERROR_NOTDONE 0x09
+#define DFU_ERROR_FIRMWARE 0x0A
+#define DFU_ERROR_VENDOR 0x0B
+#define DFU_ERROR_USB 0x0C
+#define DFU_ERROR_POR 0x0D
+#define DFU_ERROR_UNKNOWN 0x0E
+#define DFU_ERROR_STALLEDPKT 0x0F
+
+/* DFU request */
+#define DFU_DETACH 0
+#define DFU_DNLOAD 1
+#define DFU_UPLOAD 2
+#define DFU_GETSTATUS 3
+#define DFU_CLRSTATUS 4
+#define DFU_GETSTATE 5
+#define DFU_ABORT 6
+
+static bool usb_dfu_detach_req;
+
+/*
+ * usb_dfu_init
+ * Initialize the DFU interface
+ * pdev: device instance
+ * cfgidx: Configuration index
+ * return: status
+ */
+static uint8_t usb_dfu_init(struct usb_handle *pdev, uint8_t cfgidx)
+{
+ (void)pdev;
+ (void)cfgidx;
+
+ /* Nothing to do in this stage */
+ return USBD_OK;
+}
+
+/*
+ * usb_dfu_de_init
+ * De-Initialize the DFU layer
+ * pdev: device instance
+ * cfgidx: Configuration index
+ * return: status
+ */
+static uint8_t usb_dfu_de_init(struct usb_handle *pdev, uint8_t cfgidx)
+{
+ (void)pdev;
+ (void)cfgidx;
+
+ /* Nothing to do in this stage */
+ return USBD_OK;
+}
+
+/*
+ * usb_dfu_data_in
+ * handle data IN Stage
+ * pdev: device instance
+ * epnum: endpoint index
+ * return: status
+ */
+static uint8_t usb_dfu_data_in(struct usb_handle *pdev, uint8_t epnum)
+{
+ (void)pdev;
+ (void)epnum;
+
+ return USBD_OK;
+}
+
+/*
+ * usb_dfu_ep0_rx_ready
+ * handle EP0 Rx Ready event
+ * pdev: device
+ * return: status
+ */
+static uint8_t usb_dfu_ep0_rx_ready(struct usb_handle *pdev)
+{
+ (void)pdev;
+
+ return USBD_OK;
+}
+
+/*
+ * usb_dfu_ep0_tx_ready
+ * handle EP0 TRx Ready event
+ * pdev: device instance
+ * return: status
+ */
+static uint8_t usb_dfu_ep0_tx_ready(struct usb_handle *pdev)
+{
+ (void)pdev;
+
+ return USBD_OK;
+}
+
+/*
+ * usb_dfu_sof
+ * handle SOF event
+ * pdev: device instance
+ * return: status
+ */
+static uint8_t usb_dfu_sof(struct usb_handle *pdev)
+{
+ (void)pdev;
+
+ return USBD_OK;
+}
+
+/*
+ * usb_dfu_iso_in_incomplete
+ * handle data ISO IN Incomplete event
+ * pdev: device instance
+ * epnum: endpoint index
+ * return: status
+ */
+static uint8_t usb_dfu_iso_in_incomplete(struct usb_handle *pdev, uint8_t epnum)
+{
+ (void)pdev;
+ (void)epnum;
+
+ return USBD_OK;
+}
+
+/*
+ * usb_dfu_iso_out_incomplete
+ * handle data ISO OUT Incomplete event
+ * pdev: device instance
+ * epnum: endpoint index
+ * return: status
+ */
+static uint8_t usb_dfu_iso_out_incomplete(struct usb_handle *pdev,
+ uint8_t epnum)
+{
+ (void)pdev;
+ (void)epnum;
+
+ return USBD_OK;
+}
+
+/*
+ * usb_dfu_data_out
+ * handle data OUT Stage
+ * pdev: device instance
+ * epnum: endpoint index
+ * return: status
+ */
+static uint8_t usb_dfu_data_out(struct usb_handle *pdev, uint8_t epnum)
+{
+ (void)pdev;
+ (void)epnum;
+
+ return USBD_OK;
+}
+
+/*
+ * usb_dfu_detach
+ * Handles the DFU DETACH request.
+ * pdev: device instance
+ * req: pointer to the request structure.
+ */
+static void usb_dfu_detach(struct usb_handle *pdev, struct usb_setup_req *req)
+{
+ struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
+
+ INFO("Receive DFU Detach\n");
+
+ if ((hdfu->dev_state == STATE_DFU_IDLE) ||
+ (hdfu->dev_state == STATE_DFU_DNLOAD_SYNC) ||
+ (hdfu->dev_state == STATE_DFU_DNLOAD_IDLE) ||
+ (hdfu->dev_state == STATE_DFU_MANIFEST_SYNC) ||
+ (hdfu->dev_state == STATE_DFU_UPLOAD_IDLE)) {
+ /* Update the state machine */
+ hdfu->dev_state = STATE_DFU_IDLE;
+ hdfu->dev_status = DFU_ERROR_NONE;
+ }
+
+ usb_dfu_detach_req = true;
+}
+
+/*
+ * usb_dfu_download
+ * Handles the DFU DNLOAD request.
+ * pdev: device instance
+ * req: pointer to the request structure
+ */
+static void usb_dfu_download(struct usb_handle *pdev, struct usb_setup_req *req)
+{
+ struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
+ uintptr_t data_ptr;
+ uint32_t length;
+ int ret;
+
+ /* Data setup request */
+ if (req->length > 0) {
+ /* Unsupported state */
+ if ((hdfu->dev_state != STATE_DFU_IDLE) &&
+ (hdfu->dev_state != STATE_DFU_DNLOAD_IDLE)) {
+ /* Call the error management function (command will be nacked) */
+ usb_core_ctl_error(pdev);
+ return;
+ }
+
+ /* Get the data address */
+ length = req->length;
+ ret = hdfu->callback->download(hdfu->alt_setting, &data_ptr,
+ &length, pdev->user_data);
+ if (ret == 0U) {
+ /* Update the state machine */
+ hdfu->dev_state = STATE_DFU_DNLOAD_SYNC;
+ /* Start the transfer */
+ usb_core_receive_ep0(pdev, (uint8_t *)data_ptr, length);
+ } else {
+ usb_core_ctl_error(pdev);
+ }
+ } else {
+ /* End of DNLOAD operation*/
+ if (hdfu->dev_state != STATE_DFU_DNLOAD_IDLE) {
+ /* Call the error management function (command will be nacked) */
+ usb_core_ctl_error(pdev);
+ return;
+ }
+ /* End of DNLOAD operation*/
+ hdfu->dev_state = STATE_DFU_MANIFEST_SYNC;
+ ret = hdfu->callback->manifestation(hdfu->alt_setting, pdev->user_data);
+ if (ret == 0U) {
+ hdfu->dev_state = STATE_DFU_MANIFEST_SYNC;
+ } else {
+ usb_core_ctl_error(pdev);
+ }
+ }
+}
+
+/*
+ * usb_dfu_upload
+ * Handles the DFU UPLOAD request.
+ * pdev: instance
+ * req: pointer to the request structure
+ */
+static void usb_dfu_upload(struct usb_handle *pdev, struct usb_setup_req *req)
+{
+ struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
+ uintptr_t data_ptr;
+ uint32_t length;
+ int ret;
+
+ /* Data setup request */
+ if (req->length == 0) {
+ /* No Data setup request */
+ hdfu->dev_state = STATE_DFU_IDLE;
+ return;
+ }
+
+ /* Unsupported state */
+ if ((hdfu->dev_state != STATE_DFU_IDLE) && (hdfu->dev_state != STATE_DFU_UPLOAD_IDLE)) {
+ ERROR("UPLOAD : Unsupported State\n");
+ /* Call the error management function (command will be nacked) */
+ usb_core_ctl_error(pdev);
+ return;
+ }
+
+ /* Update the data address */
+ length = req->length;
+ ret = hdfu->callback->upload(hdfu->alt_setting, &data_ptr, &length, pdev->user_data);
+ if (ret == 0U) {
+ /* Short frame */
+ hdfu->dev_state = (req->length > length) ? STATE_DFU_IDLE : STATE_DFU_UPLOAD_IDLE;
+
+ /* Start the transfer */
+ usb_core_transmit_ep0(pdev, (uint8_t *)data_ptr, length);
+ } else {
+ ERROR("UPLOAD : bad block %i on alt %i\n", req->value, req->index);
+ hdfu->dev_state = STATE_DFU_ERROR;
+ hdfu->dev_status = DFU_ERROR_STALLEDPKT;
+
+ /* Call the error management function (command will be nacked) */
+ usb_core_ctl_error(pdev);
+ }
+}
+
+/*
+ * usb_dfu_get_status
+ * Handles the DFU GETSTATUS request.
+ * pdev: instance
+ */
+static void usb_dfu_get_status(struct usb_handle *pdev)
+{
+ struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
+
+ hdfu->status[0] = hdfu->dev_status; /* bStatus */
+ hdfu->status[1] = 0; /* bwPollTimeout[3] */
+ hdfu->status[2] = 0;
+ hdfu->status[3] = 0;
+ hdfu->status[4] = hdfu->dev_state; /* bState */
+ hdfu->status[5] = 0; /* iString */
+
+ /* next step */
+ switch (hdfu->dev_state) {
+ case STATE_DFU_DNLOAD_SYNC:
+ hdfu->dev_state = STATE_DFU_DNLOAD_IDLE;
+ break;
+ case STATE_DFU_MANIFEST_SYNC:
+ /* the device is 'ManifestationTolerant' */
+ hdfu->status[4] = STATE_DFU_MANIFEST;
+ hdfu->status[1] = 1U; /* bwPollTimeout = 1ms */
+ hdfu->dev_state = STATE_DFU_IDLE;
+ break;
+
+ default:
+ break;
+ }
+
+ /* Start the transfer */
+ usb_core_transmit_ep0(pdev, (uint8_t *)&hdfu->status[0], sizeof(hdfu->status));
+}
+
+/*
+ * usb_dfu_clear_status
+ * Handles the DFU CLRSTATUS request.
+ * pdev: device instance
+ */
+static void usb_dfu_clear_status(struct usb_handle *pdev)
+{
+ struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
+
+ if (hdfu->dev_state == STATE_DFU_ERROR) {
+ hdfu->dev_state = STATE_DFU_IDLE;
+ hdfu->dev_status = DFU_ERROR_NONE;
+ } else {
+ /* State Error */
+ hdfu->dev_state = STATE_DFU_ERROR;
+ hdfu->dev_status = DFU_ERROR_UNKNOWN;
+ }
+}
+
+/*
+ * usb_dfu_get_state
+ * Handles the DFU GETSTATE request.
+ * pdev: device instance
+ */
+static void usb_dfu_get_state(struct usb_handle *pdev)
+{
+ struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
+
+ /* Return the current state of the DFU interface */
+ usb_core_transmit_ep0(pdev, &hdfu->dev_state, 1);
+}
+
+/*
+ * usb_dfu_abort
+ * Handles the DFU ABORT request.
+ * pdev: device instance
+ */
+static void usb_dfu_abort(struct usb_handle *pdev)
+{
+ struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
+
+ if ((hdfu->dev_state == STATE_DFU_IDLE) ||
+ (hdfu->dev_state == STATE_DFU_DNLOAD_SYNC) ||
+ (hdfu->dev_state == STATE_DFU_DNLOAD_IDLE) ||
+ (hdfu->dev_state == STATE_DFU_MANIFEST_SYNC) ||
+ (hdfu->dev_state == STATE_DFU_UPLOAD_IDLE)) {
+ hdfu->dev_state = STATE_DFU_IDLE;
+ hdfu->dev_status = DFU_ERROR_NONE;
+ }
+}
+
+/*
+ * usb_dfu_setup
+ * Handle the DFU specific requests
+ * pdev: instance
+ * req: usb requests
+ * return: status
+ */
+static uint8_t usb_dfu_setup(struct usb_handle *pdev, struct usb_setup_req *req)
+{
+ uint8_t *pbuf = NULL;
+ uint16_t len = 0U;
+ uint8_t ret = USBD_OK;
+ struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
+
+ switch (req->bm_request & USB_REQ_TYPE_MASK) {
+ case USB_REQ_TYPE_CLASS:
+ switch (req->b_request) {
+ case DFU_DNLOAD:
+ usb_dfu_download(pdev, req);
+ break;
+
+ case DFU_UPLOAD:
+ usb_dfu_upload(pdev, req);
+ break;
+
+ case DFU_GETSTATUS:
+ usb_dfu_get_status(pdev);
+ break;
+
+ case DFU_CLRSTATUS:
+ usb_dfu_clear_status(pdev);
+ break;
+
+ case DFU_GETSTATE:
+ usb_dfu_get_state(pdev);
+ break;
+
+ case DFU_ABORT:
+ usb_dfu_abort(pdev);
+ break;
+
+ case DFU_DETACH:
+ usb_dfu_detach(pdev, req);
+ break;
+
+ default:
+ ERROR("unknown request %x on alternate %i\n",
+ req->b_request, hdfu->alt_setting);
+ usb_core_ctl_error(pdev);
+ ret = USBD_FAIL;
+ break;
+ }
+ break;
+ case USB_REQ_TYPE_STANDARD:
+ switch (req->b_request) {
+ case USB_REQ_GET_DESCRIPTOR:
+ if (HIBYTE(req->value) == DFU_DESCRIPTOR_TYPE) {
+ pbuf = pdev->desc->get_config_desc(&len);
+ /* DFU descriptor at the end of the USB */
+ pbuf += len - 9U;
+ len = 9U;
+ len = MIN(len, req->length);
+ }
+
+ /* Start the transfer */
+ usb_core_transmit_ep0(pdev, pbuf, len);
+
+ break;
+
+ case USB_REQ_GET_INTERFACE:
+ /* Start the transfer */
+ usb_core_transmit_ep0(pdev, (uint8_t *)&hdfu->alt_setting, 1U);
+ break;
+
+ case USB_REQ_SET_INTERFACE:
+ hdfu->alt_setting = LOBYTE(req->value);
+ break;
+
+ default:
+ usb_core_ctl_error(pdev);
+ ret = USBD_FAIL;
+ break;
+ }
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static const struct usb_class usb_dfu = {
+ .init = usb_dfu_init,
+ .de_init = usb_dfu_de_init,
+ .setup = usb_dfu_setup,
+ .ep0_tx_sent = usb_dfu_ep0_tx_ready,
+ .ep0_rx_ready = usb_dfu_ep0_rx_ready,
+ .data_in = usb_dfu_data_in,
+ .data_out = usb_dfu_data_out,
+ .sof = usb_dfu_sof,
+ .iso_in_incomplete = usb_dfu_iso_in_incomplete,
+ .iso_out_incomplete = usb_dfu_iso_out_incomplete,
+};
+
+void usb_dfu_register(struct usb_handle *pdev, struct usb_dfu_handle *phandle)
+{
+ pdev->class = (struct usb_class *)&usb_dfu;
+ pdev->class_data = phandle;
+
+ phandle->dev_state = STATE_DFU_IDLE;
+ phandle->dev_status = DFU_ERROR_NONE;
+}
+
+int usb_dfu_loop(struct usb_handle *pdev, const struct usb_dfu_media *pmedia)
+{
+ uint32_t it_count;
+ enum usb_status ret;
+ struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
+
+ hdfu->callback = pmedia;
+ usb_dfu_detach_req = false;
+ /* Continue to handle USB core IT to assure complete data transmission */
+ it_count = 100U;
+
+ /* DFU infinite loop until DETACH_REQ */
+ while (it_count != 0U) {
+ ret = usb_core_handle_it(pdev);
+ if (ret != USBD_OK) {
+ return -EIO;
+ }
+
+ /* Detach request received */
+ if (usb_dfu_detach_req) {
+ it_count--;
+ }
+ }
+
+ return 0;
+}