From 102b0d2daa97dae68d3eed54d8fe37a9cc38a892 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 11:13:47 +0200 Subject: Adding upstream version 2.8.0+dfsg. Signed-off-by: Daniel Baumann --- plat/st/common/bl2_io_storage.c | 731 ++++++++++++++++++++++ plat/st/common/include/stm32cubeprogrammer.h | 29 + plat/st/common/include/stm32mp_common.h | 129 ++++ plat/st/common/include/stm32mp_dt.h | 46 ++ plat/st/common/include/stm32mp_efi.h | 15 + plat/st/common/include/stm32mp_fconf_getter.h | 31 + plat/st/common/include/stm32mp_io_storage.h | 27 + plat/st/common/include/stm32mp_shared_resources.h | 58 ++ plat/st/common/include/usb_dfu.h | 80 +++ plat/st/common/stm32cubeprogrammer_uart.c | 520 +++++++++++++++ plat/st/common/stm32cubeprogrammer_usb.c | 197 ++++++ plat/st/common/stm32mp_common.c | 279 +++++++++ plat/st/common/stm32mp_crypto_lib.c | 661 +++++++++++++++++++ plat/st/common/stm32mp_dt.c | 429 +++++++++++++ plat/st/common/stm32mp_fconf_io.c | 181 ++++++ plat/st/common/stm32mp_trusted_boot.c | 204 ++++++ plat/st/common/usb_dfu.c | 538 ++++++++++++++++ 17 files changed, 4155 insertions(+) create mode 100644 plat/st/common/bl2_io_storage.c create mode 100644 plat/st/common/include/stm32cubeprogrammer.h create mode 100644 plat/st/common/include/stm32mp_common.h create mode 100644 plat/st/common/include/stm32mp_dt.h create mode 100644 plat/st/common/include/stm32mp_efi.h create mode 100644 plat/st/common/include/stm32mp_fconf_getter.h create mode 100644 plat/st/common/include/stm32mp_io_storage.h create mode 100644 plat/st/common/include/stm32mp_shared_resources.h create mode 100644 plat/st/common/include/usb_dfu.h create mode 100644 plat/st/common/stm32cubeprogrammer_uart.c create mode 100644 plat/st/common/stm32cubeprogrammer_usb.c create mode 100644 plat/st/common/stm32mp_common.c create mode 100644 plat/st/common/stm32mp_crypto_lib.c create mode 100644 plat/st/common/stm32mp_dt.c create mode 100644 plat/st/common/stm32mp_fconf_io.c create mode 100644 plat/st/common/stm32mp_trusted_boot.c create mode 100644 plat/st/common/usb_dfu.c (limited to 'plat/st/common') 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* 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(¶ms, 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(¶ms) != 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 + +#include + +/* 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 + +#include + +#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 +#include + +#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 + +#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 + +#include +#include + +/* 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 + +#include + +/* 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 +#include + +#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 + +#include + +#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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* 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 +#include +#include + +#include + +#include +#include + +/* 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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, ¶ms); + 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 +#include + +#include +#include +#include +#include +#include + +#include +#include + +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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 +#include + +#include + +#include +#include + +/* 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; +} -- cgit v1.2.3