GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

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