GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -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(¶ms, sizeof(struct stm32_sdmmc2_params));
|
||||
|
||||
mmc_info.mmc_dev_type = mmc_dev_type;
|
||||
|
||||
switch (boot_interface_instance) {
|
||||
case 1:
|
||||
params.reg_base = STM32MP_SDMMC1_BASE;
|
||||
break;
|
||||
case 2:
|
||||
params.reg_base = STM32MP_SDMMC2_BASE;
|
||||
break;
|
||||
case 3:
|
||||
params.reg_base = STM32MP_SDMMC3_BASE;
|
||||
break;
|
||||
default:
|
||||
WARN("SDMMC instance not found, using default\n");
|
||||
if (mmc_dev_type == MMC_IS_SD) {
|
||||
params.reg_base = STM32MP_SDMMC1_BASE;
|
||||
} else {
|
||||
params.reg_base = STM32MP_SDMMC2_BASE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (mmc_dev_type != MMC_IS_EMMC) {
|
||||
params.flags = MMC_FLAG_SD_CMD6;
|
||||
}
|
||||
|
||||
params.device_info = &mmc_info;
|
||||
if (stm32_sdmmc2_mmc_init(¶ms) != 0) {
|
||||
ERROR("SDMMC%u init failed\n", boot_interface_instance);
|
||||
panic();
|
||||
}
|
||||
|
||||
/* Open MMC as a block device to read FIP */
|
||||
io_result = register_io_dev_block(&mmc_dev_con);
|
||||
if (io_result != 0) {
|
||||
panic();
|
||||
}
|
||||
|
||||
io_result = io_dev_open(mmc_dev_con, (uintptr_t)&mmc_block_dev_spec,
|
||||
&storage_dev_handle);
|
||||
assert(io_result == 0);
|
||||
|
||||
#if STM32MP_EMMC_BOOT
|
||||
if (mmc_dev_type == MMC_IS_EMMC) {
|
||||
io_result = mmc_part_switch_current_boot();
|
||||
assert(io_result == 0);
|
||||
|
||||
if (get_boot_part_fip_header() != TOC_HEADER_NAME) {
|
||||
WARN("%s: Can't find FIP header on eMMC boot partition. Trying GPT\n",
|
||||
__func__);
|
||||
io_result = mmc_part_switch_user();
|
||||
assert(io_result == 0);
|
||||
return;
|
||||
}
|
||||
|
||||
VERBOSE("%s: FIP header found on eMMC boot partition\n",
|
||||
__func__);
|
||||
image_block_spec.offset = STM32MP_EMMC_BOOT_FIP_OFFSET;
|
||||
image_block_spec.length = mmc_boot_part_size() - STM32MP_EMMC_BOOT_FIP_OFFSET;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif /* STM32MP_SDMMC || STM32MP_EMMC */
|
||||
|
||||
#if STM32MP_SPI_NOR
|
||||
static void boot_spi_nor(boot_api_context_t *boot_context)
|
||||
{
|
||||
int io_result __unused;
|
||||
|
||||
io_result = stm32_qspi_init();
|
||||
assert(io_result == 0);
|
||||
|
||||
io_result = register_io_dev_mtd(&spi_dev_con);
|
||||
assert(io_result == 0);
|
||||
|
||||
/* Open connections to device */
|
||||
io_result = io_dev_open(spi_dev_con,
|
||||
(uintptr_t)&spi_nor_dev_spec,
|
||||
&storage_dev_handle);
|
||||
assert(io_result == 0);
|
||||
}
|
||||
#endif /* STM32MP_SPI_NOR */
|
||||
|
||||
#if STM32MP_RAW_NAND
|
||||
static void boot_fmc2_nand(boot_api_context_t *boot_context)
|
||||
{
|
||||
int io_result __unused;
|
||||
|
||||
io_result = stm32_fmc2_init();
|
||||
assert(io_result == 0);
|
||||
|
||||
/* Register the IO device on this platform */
|
||||
io_result = register_io_dev_mtd(&nand_dev_con);
|
||||
assert(io_result == 0);
|
||||
|
||||
/* Open connections to device */
|
||||
io_result = io_dev_open(nand_dev_con, (uintptr_t)&nand_dev_spec,
|
||||
&storage_dev_handle);
|
||||
assert(io_result == 0);
|
||||
}
|
||||
#endif /* STM32MP_RAW_NAND */
|
||||
|
||||
#if STM32MP_SPI_NAND
|
||||
static void boot_spi_nand(boot_api_context_t *boot_context)
|
||||
{
|
||||
int io_result __unused;
|
||||
|
||||
io_result = stm32_qspi_init();
|
||||
assert(io_result == 0);
|
||||
|
||||
io_result = register_io_dev_mtd(&spi_dev_con);
|
||||
assert(io_result == 0);
|
||||
|
||||
/* Open connections to device */
|
||||
io_result = io_dev_open(spi_dev_con,
|
||||
(uintptr_t)&spi_nand_dev_spec,
|
||||
&storage_dev_handle);
|
||||
assert(io_result == 0);
|
||||
}
|
||||
#endif /* STM32MP_SPI_NAND */
|
||||
|
||||
#if STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER
|
||||
static void mmap_io_setup(void)
|
||||
{
|
||||
int io_result __unused;
|
||||
|
||||
io_result = register_io_dev_memmap(&memmap_dev_con);
|
||||
assert(io_result == 0);
|
||||
|
||||
io_result = io_dev_open(memmap_dev_con, (uintptr_t)NULL,
|
||||
&storage_dev_handle);
|
||||
assert(io_result == 0);
|
||||
}
|
||||
|
||||
#if STM32MP_UART_PROGRAMMER
|
||||
static void stm32cubeprogrammer_uart(void)
|
||||
{
|
||||
int ret __unused;
|
||||
boot_api_context_t *boot_context =
|
||||
(boot_api_context_t *)stm32mp_get_boot_ctx_address();
|
||||
uintptr_t uart_base;
|
||||
|
||||
uart_base = get_uart_address(boot_context->boot_interface_instance);
|
||||
ret = stm32cubeprog_uart_load(uart_base, DWL_BUFFER_BASE, DWL_BUFFER_SIZE);
|
||||
assert(ret == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if STM32MP_USB_PROGRAMMER
|
||||
static void stm32cubeprogrammer_usb(void)
|
||||
{
|
||||
int ret __unused;
|
||||
struct usb_handle *pdev;
|
||||
|
||||
/* Init USB on platform */
|
||||
pdev = usb_dfu_plat_init();
|
||||
|
||||
ret = stm32cubeprog_usb_load(pdev, DWL_BUFFER_BASE, DWL_BUFFER_SIZE);
|
||||
assert(ret == 0);
|
||||
}
|
||||
#endif
|
||||
#endif /* STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER */
|
||||
|
||||
|
||||
void stm32mp_io_setup(void)
|
||||
{
|
||||
int io_result __unused;
|
||||
boot_api_context_t *boot_context =
|
||||
(boot_api_context_t *)stm32mp_get_boot_ctx_address();
|
||||
|
||||
print_boot_device(boot_context);
|
||||
|
||||
if ((boot_context->boot_partition_used_toboot == 1U) ||
|
||||
(boot_context->boot_partition_used_toboot == 2U)) {
|
||||
INFO("Boot used partition fsbl%u\n",
|
||||
boot_context->boot_partition_used_toboot);
|
||||
}
|
||||
|
||||
io_result = register_io_dev_fip(&fip_dev_con);
|
||||
assert(io_result == 0);
|
||||
|
||||
io_result = io_dev_open(fip_dev_con, (uintptr_t)NULL,
|
||||
&fip_dev_handle);
|
||||
|
||||
#ifndef DECRYPTION_SUPPORT_none
|
||||
io_result = register_io_dev_enc(&enc_dev_con);
|
||||
assert(io_result == 0);
|
||||
|
||||
io_result = io_dev_open(enc_dev_con, (uintptr_t)NULL,
|
||||
&enc_dev_handle);
|
||||
assert(io_result == 0);
|
||||
#endif
|
||||
|
||||
switch (boot_context->boot_interface_selected) {
|
||||
#if STM32MP_SDMMC
|
||||
case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_SD:
|
||||
dmbsy();
|
||||
boot_mmc(MMC_IS_SD, boot_context->boot_interface_instance);
|
||||
break;
|
||||
#endif
|
||||
#if STM32MP_EMMC
|
||||
case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC:
|
||||
dmbsy();
|
||||
boot_mmc(MMC_IS_EMMC, boot_context->boot_interface_instance);
|
||||
break;
|
||||
#endif
|
||||
#if STM32MP_SPI_NOR
|
||||
case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI:
|
||||
dmbsy();
|
||||
boot_spi_nor(boot_context);
|
||||
break;
|
||||
#endif
|
||||
#if STM32MP_RAW_NAND
|
||||
case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC:
|
||||
dmbsy();
|
||||
boot_fmc2_nand(boot_context);
|
||||
break;
|
||||
#endif
|
||||
#if STM32MP_SPI_NAND
|
||||
case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_QSPI:
|
||||
dmbsy();
|
||||
boot_spi_nand(boot_context);
|
||||
break;
|
||||
#endif
|
||||
#if STM32MP_UART_PROGRAMMER || STM32MP_USB_PROGRAMMER
|
||||
#if STM32MP_UART_PROGRAMMER
|
||||
case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART:
|
||||
#endif
|
||||
#if STM32MP_USB_PROGRAMMER
|
||||
case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB:
|
||||
#endif
|
||||
dmbsy();
|
||||
mmap_io_setup();
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ERROR("Boot interface %d not supported\n",
|
||||
boot_context->boot_interface_selected);
|
||||
panic();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int bl2_plat_handle_pre_image_load(unsigned int image_id)
|
||||
{
|
||||
static bool gpt_init_done __unused;
|
||||
uint16_t boot_itf = stm32mp_get_boot_itf_selected();
|
||||
|
||||
switch (boot_itf) {
|
||||
#if STM32MP_SDMMC || STM32MP_EMMC
|
||||
case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC:
|
||||
#if STM32MP_EMMC_BOOT
|
||||
if (image_block_spec.offset == STM32MP_EMMC_BOOT_FIP_OFFSET) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
/* fallthrough */
|
||||
case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_SD:
|
||||
if (!gpt_init_done) {
|
||||
/*
|
||||
* With FWU Multi Bank feature enabled, the selection of
|
||||
* the image to boot will be done by fwu_init calling the
|
||||
* platform hook, plat_fwu_set_images_source.
|
||||
*/
|
||||
#if !PSA_FWU_SUPPORT
|
||||
const partition_entry_t *entry;
|
||||
const struct efi_guid img_type_guid = STM32MP_FIP_GUID;
|
||||
uuid_t img_type_uuid;
|
||||
|
||||
guidcpy(&img_type_uuid, &img_type_guid);
|
||||
partition_init(GPT_IMAGE_ID);
|
||||
entry = get_partition_entry_by_type(&img_type_uuid);
|
||||
if (entry == NULL) {
|
||||
entry = get_partition_entry(FIP_IMAGE_NAME);
|
||||
if (entry == NULL) {
|
||||
ERROR("Could NOT find the %s partition!\n",
|
||||
FIP_IMAGE_NAME);
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
image_block_spec.offset = entry->start;
|
||||
image_block_spec.length = entry->length;
|
||||
#endif
|
||||
gpt_init_done = true;
|
||||
} else {
|
||||
bl_mem_params_node_t *bl_mem_params = get_bl_mem_params_node(image_id);
|
||||
assert(bl_mem_params != NULL);
|
||||
|
||||
mmc_block_dev_spec.buffer.offset = bl_mem_params->image_info.image_base;
|
||||
mmc_block_dev_spec.buffer.length = bl_mem_params->image_info.image_max_size;
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if STM32MP_RAW_NAND || STM32MP_SPI_NAND
|
||||
#if STM32MP_RAW_NAND
|
||||
case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC:
|
||||
#endif
|
||||
#if STM32MP_SPI_NAND
|
||||
case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_QSPI:
|
||||
#endif
|
||||
image_block_spec.offset = STM32MP_NAND_FIP_OFFSET;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if STM32MP_SPI_NOR
|
||||
case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI:
|
||||
image_block_spec.offset = STM32MP_NOR_FIP_OFFSET;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if STM32MP_UART_PROGRAMMER
|
||||
case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_UART:
|
||||
if (image_id == FW_CONFIG_ID) {
|
||||
stm32cubeprogrammer_uart();
|
||||
/* FIP loaded at DWL address */
|
||||
image_block_spec.offset = DWL_BUFFER_BASE;
|
||||
image_block_spec.length = DWL_BUFFER_SIZE;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if STM32MP_USB_PROGRAMMER
|
||||
case BOOT_API_CTX_BOOT_INTERFACE_SEL_SERIAL_USB:
|
||||
if (image_id == FW_CONFIG_ID) {
|
||||
stm32cubeprogrammer_usb();
|
||||
/* FIP loaded at DWL address */
|
||||
image_block_spec.offset = DWL_BUFFER_BASE;
|
||||
image_block_spec.length = DWL_BUFFER_SIZE;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ERROR("FIP Not found\n");
|
||||
panic();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return an IO device handle and specification which can be used to access
|
||||
* an image. Use this to enforce platform load policy.
|
||||
*/
|
||||
int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle,
|
||||
uintptr_t *image_spec)
|
||||
{
|
||||
int rc;
|
||||
const struct plat_io_policy *policy;
|
||||
|
||||
policy = FCONF_GET_PROPERTY(stm32mp, io_policies, image_id);
|
||||
rc = policy->check(policy->image_spec);
|
||||
if (rc == 0) {
|
||||
*image_spec = policy->image_spec;
|
||||
*dev_handle = *(policy->dev_handle);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if (STM32MP_SDMMC || STM32MP_EMMC) && PSA_FWU_SUPPORT
|
||||
/*
|
||||
* In each boot in non-trial mode, we set the BKP register to
|
||||
* FWU_MAX_TRIAL_REBOOT, and return the active_index from metadata.
|
||||
*
|
||||
* As long as the update agent didn't update the "accepted" field in metadata
|
||||
* (i.e. we are in trial mode), we select the new active_index.
|
||||
* To avoid infinite boot loop at trial boot we decrement a BKP register.
|
||||
* If this counter is 0:
|
||||
* - an unexpected TAMPER event raised (that resets the BKP registers to 0)
|
||||
* - a power-off occurs before the update agent was able to update the
|
||||
* "accepted' field
|
||||
* - we already boot FWU_MAX_TRIAL_REBOOT times in trial mode.
|
||||
* we select the previous_active_index.
|
||||
*/
|
||||
#define INVALID_BOOT_IDX 0xFFFFFFFF
|
||||
|
||||
uint32_t plat_fwu_get_boot_idx(void)
|
||||
{
|
||||
/*
|
||||
* Select boot index and update boot counter only once per boot
|
||||
* even if this function is called several times.
|
||||
*/
|
||||
static uint32_t boot_idx = INVALID_BOOT_IDX;
|
||||
const struct fwu_metadata *data;
|
||||
|
||||
data = fwu_get_metadata();
|
||||
|
||||
if (boot_idx == INVALID_BOOT_IDX) {
|
||||
boot_idx = data->active_index;
|
||||
if (fwu_is_trial_run_state()) {
|
||||
if (stm32_get_and_dec_fwu_trial_boot_cnt() == 0U) {
|
||||
WARN("Trial FWU fails %u times\n",
|
||||
FWU_MAX_TRIAL_REBOOT);
|
||||
boot_idx = data->previous_active_index;
|
||||
}
|
||||
} else {
|
||||
stm32_set_max_fwu_trial_boot_cnt();
|
||||
}
|
||||
}
|
||||
|
||||
return boot_idx;
|
||||
}
|
||||
|
||||
static void *stm32_get_image_spec(const uuid_t *img_type_uuid)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0U; i < MAX_NUMBER_IDS; i++) {
|
||||
if ((guidcmp(&policies[i].img_type_guid, img_type_uuid)) == 0) {
|
||||
return (void *)policies[i].image_spec;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void plat_fwu_set_images_source(const struct fwu_metadata *metadata)
|
||||
{
|
||||
unsigned int i;
|
||||
uint32_t boot_idx;
|
||||
const partition_entry_t *entry;
|
||||
const uuid_t *img_type_uuid, *img_uuid;
|
||||
io_block_spec_t *image_spec;
|
||||
|
||||
boot_idx = plat_fwu_get_boot_idx();
|
||||
assert(boot_idx < NR_OF_FW_BANKS);
|
||||
|
||||
for (i = 0U; i < NR_OF_IMAGES_IN_FW_BANK; i++) {
|
||||
img_type_uuid = &metadata->img_entry[i].img_type_uuid;
|
||||
image_spec = stm32_get_image_spec(img_type_uuid);
|
||||
if (image_spec == NULL) {
|
||||
ERROR("Unable to get image spec for the image in the metadata\n");
|
||||
panic();
|
||||
}
|
||||
|
||||
img_uuid =
|
||||
&metadata->img_entry[i].img_props[boot_idx].img_uuid;
|
||||
|
||||
entry = get_partition_entry_by_uuid(img_uuid);
|
||||
if (entry == NULL) {
|
||||
ERROR("Unable to find the partition with the uuid mentioned in metadata\n");
|
||||
panic();
|
||||
}
|
||||
|
||||
image_spec->offset = entry->start;
|
||||
image_spec->length = entry->length;
|
||||
}
|
||||
}
|
||||
|
||||
static int plat_set_image_source(unsigned int image_id,
|
||||
uintptr_t *handle,
|
||||
uintptr_t *image_spec,
|
||||
const char *part_name)
|
||||
{
|
||||
struct plat_io_policy *policy;
|
||||
io_block_spec_t *spec;
|
||||
const partition_entry_t *entry = get_partition_entry(part_name);
|
||||
|
||||
if (entry == NULL) {
|
||||
ERROR("Unable to find the %s partition\n", part_name);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
policy = &policies[image_id];
|
||||
|
||||
spec = (io_block_spec_t *)policy->image_spec;
|
||||
spec->offset = entry->start;
|
||||
spec->length = entry->length;
|
||||
|
||||
*image_spec = policy->image_spec;
|
||||
*handle = *policy->dev_handle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plat_fwu_set_metadata_image_source(unsigned int image_id,
|
||||
uintptr_t *handle,
|
||||
uintptr_t *image_spec)
|
||||
{
|
||||
char *part_name;
|
||||
|
||||
assert((image_id == FWU_METADATA_IMAGE_ID) ||
|
||||
(image_id == BKUP_FWU_METADATA_IMAGE_ID));
|
||||
|
||||
partition_init(GPT_IMAGE_ID);
|
||||
|
||||
if (image_id == FWU_METADATA_IMAGE_ID) {
|
||||
part_name = METADATA_PART_1;
|
||||
} else {
|
||||
part_name = METADATA_PART_2;
|
||||
}
|
||||
|
||||
return plat_set_image_source(image_id, handle, image_spec,
|
||||
part_name);
|
||||
}
|
||||
#endif /* (STM32MP_SDMMC || STM32MP_EMMC) && PSA_FWU_SUPPORT */
|
||||
+29
@@ -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 */
|
||||
+129
@@ -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 */
|
||||
+31
@@ -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 */
|
||||
+27
@@ -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 */
|
||||
+58
@@ -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 */
|
||||
+520
@@ -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);
|
||||
}
|
||||
+197
@@ -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, ¶ms);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mbedtls_oid_get_md_alg(&hash_oid, md_alg);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Length of hash must match the algorithm's size */
|
||||
if (len != BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*out = p;
|
||||
*out_len = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int crypto_verify_signature(void *data_ptr, unsigned int data_len,
|
||||
void *sig_ptr, unsigned int sig_len,
|
||||
void *sig_alg, unsigned int sig_alg_len,
|
||||
void *pk_ptr, unsigned int pk_len)
|
||||
{
|
||||
uint8_t image_hash[CRYPTO_HASH_MAX_SIZE] = {0};
|
||||
uint8_t sig[CRYPTO_SIGN_MAX_SIZE];
|
||||
uint8_t my_pk[CRYPTO_PUBKEY_MAX_SIZE];
|
||||
int ret;
|
||||
size_t len;
|
||||
mbedtls_asn1_sequence seq;
|
||||
mbedtls_asn1_sequence *cur;
|
||||
unsigned char *p, *end;
|
||||
int curve_id;
|
||||
mbedtls_asn1_buf sig_oid, sig_params;
|
||||
mbedtls_md_type_t md_alg;
|
||||
mbedtls_pk_type_t pk_alg;
|
||||
size_t bignum_len = sizeof(sig) / 2U;
|
||||
unsigned int seq_num = 0U;
|
||||
|
||||
if (!stm32mp_is_closed_device() && !stm32mp_is_auth_supported()) {
|
||||
return CRYPTO_SUCCESS;
|
||||
}
|
||||
|
||||
/* Get pointers to signature OID and parameters */
|
||||
p = (unsigned char *)sig_alg;
|
||||
end = (unsigned char *)(p + sig_alg_len);
|
||||
ret = mbedtls_asn1_get_alg(&p, end, &sig_oid, &sig_params);
|
||||
if (ret != 0) {
|
||||
VERBOSE("%s: mbedtls_asn1_get_alg (%d)\n", __func__, ret);
|
||||
return CRYPTO_ERR_SIGNATURE;
|
||||
}
|
||||
|
||||
/* Get the actual signature algorithm (MD + PK) */
|
||||
ret = mbedtls_oid_get_sig_alg(&sig_oid, &md_alg, &pk_alg);
|
||||
if (ret != 0) {
|
||||
VERBOSE("%s: mbedtls_oid_get_sig_alg (%d)\n", __func__, ret);
|
||||
return CRYPTO_ERR_SIGNATURE;
|
||||
}
|
||||
|
||||
if ((md_alg != MBEDTLS_MD_SHA256) || (pk_alg != MBEDTLS_PK_ECDSA)) {
|
||||
VERBOSE("%s: md_alg=%u pk_alg=%u\n", __func__, md_alg, pk_alg);
|
||||
return CRYPTO_ERR_SIGNATURE;
|
||||
}
|
||||
|
||||
ret = get_plain_pk_from_asn1(pk_ptr, pk_len, &pk_ptr, &pk_len, &curve_id);
|
||||
if (ret != 0) {
|
||||
VERBOSE("%s: get_plain_pk_from_asn1 (%d)\n", __func__, ret);
|
||||
return CRYPTO_ERR_SIGNATURE;
|
||||
}
|
||||
|
||||
/* We expect a known pk_len */
|
||||
if (pk_len != sizeof(my_pk)) {
|
||||
VERBOSE("%s: pk_len=%u sizeof(my_pk)=%zu)\n", __func__, pk_len, sizeof(my_pk));
|
||||
return CRYPTO_ERR_SIGNATURE;
|
||||
}
|
||||
|
||||
/* Need to copy as auth_ops.verify_signature
|
||||
* expects aligned public key.
|
||||
*/
|
||||
memcpy(my_pk, pk_ptr, sizeof(my_pk));
|
||||
|
||||
/* Get the signature (bitstring) */
|
||||
p = (unsigned char *)sig_ptr;
|
||||
end = (unsigned char *)(p + sig_len);
|
||||
ret = mbedtls_asn1_get_bitstring_null(&p, end, &len);
|
||||
if (ret != 0) {
|
||||
VERBOSE("%s: mbedtls_asn1_get_bitstring_null (%d)\n", __func__, ret);
|
||||
return CRYPTO_ERR_SIGNATURE;
|
||||
}
|
||||
|
||||
/* Get r and s from sequence */
|
||||
ret = mbedtls_asn1_get_sequence_of(&p, end, &seq, MBEDTLS_ASN1_INTEGER);
|
||||
if (ret != 0) {
|
||||
VERBOSE("%s: mbedtls_asn1_get_sequence_of (%d)\n", __func__, ret);
|
||||
return CRYPTO_ERR_SIGNATURE;
|
||||
}
|
||||
|
||||
/* We expect only 2 integers (r and s) from the sequence */
|
||||
if (seq.next->next != NULL) {
|
||||
cur = seq.next;
|
||||
mbedtls_asn1_sequence *next;
|
||||
|
||||
VERBOSE("%s: nb seq != 2\n", __func__);
|
||||
/* Free all the sequences */
|
||||
while (cur != NULL) {
|
||||
next = cur->next;
|
||||
mbedtls_free(cur);
|
||||
cur = next;
|
||||
}
|
||||
|
||||
return CRYPTO_ERR_SIGNATURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* ECDSA signatures are composed of a tuple (R,S) where R and S are between 0 and n.
|
||||
* This means that the R and S can have a maximum of 32 each, but can also be smaller.
|
||||
* Also seen the integer sequence may (sometime) start with 0x00 as MSB, but we can only
|
||||
* manage exactly 2*32 bytes, we remove this higher byte if there are not 00,
|
||||
* we will fail either.
|
||||
*/
|
||||
cur = &seq;
|
||||
memset(sig, 0U, sizeof(sig));
|
||||
|
||||
while (cur != NULL) {
|
||||
size_t skip = 0U;
|
||||
size_t seek = seq_num * bignum_len;
|
||||
|
||||
if (cur->buf.len > bignum_len) {
|
||||
/* Remove extra 0x00 bytes */
|
||||
skip = cur->buf.len - bignum_len;
|
||||
} else if (cur->buf.len < bignum_len) {
|
||||
/* Add padding to match HW required size */
|
||||
seek += (bignum_len % cur->buf.len);
|
||||
}
|
||||
|
||||
if (seek + cur->buf.len > sizeof(sig) + skip) {
|
||||
panic();
|
||||
}
|
||||
|
||||
memcpy(sig + seek, cur->buf.p + skip, cur->buf.len - skip);
|
||||
cur = cur->next;
|
||||
seq_num++;
|
||||
}
|
||||
|
||||
/* Need to free allocated 'next' in mbedtls_asn1_get_sequence_of */
|
||||
mbedtls_free(seq.next);
|
||||
|
||||
/* Compute hash for the data covered by the signature */
|
||||
stm32_hash_init(HASH_SHA256);
|
||||
|
||||
ret = stm32_hash_final_update((uint8_t *)data_ptr, data_len, image_hash);
|
||||
if (ret != 0) {
|
||||
VERBOSE("%s: stm32_hash_final_update (%d)\n", __func__, ret);
|
||||
return CRYPTO_ERR_SIGNATURE;
|
||||
}
|
||||
|
||||
return verify_signature(image_hash, my_pk, sig, curve_id);
|
||||
}
|
||||
|
||||
static int crypto_verify_hash(void *data_ptr, unsigned int data_len,
|
||||
void *digest_info_ptr,
|
||||
unsigned int digest_info_len)
|
||||
{
|
||||
int ret;
|
||||
uint8_t calc_hash[BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES];
|
||||
unsigned char *p;
|
||||
mbedtls_md_type_t md_alg;
|
||||
size_t len;
|
||||
|
||||
/* we receive an asn1 encapsulated digest, we flatten it */
|
||||
ret = get_plain_digest_from_asn1(digest_info_ptr,
|
||||
digest_info_len, &p, &len,
|
||||
&md_alg);
|
||||
if ((ret != 0) || (md_alg != MBEDTLS_MD_SHA256) || (len != sizeof(calc_hash))) {
|
||||
return CRYPTO_ERR_HASH;
|
||||
}
|
||||
|
||||
digest_info_ptr = p;
|
||||
digest_info_len = len;
|
||||
|
||||
stm32_hash_init(HASH_SHA256);
|
||||
|
||||
ret = stm32_hash_final_update(data_ptr, data_len, calc_hash);
|
||||
if (ret != 0) {
|
||||
VERBOSE("%s: hash failed\n", __func__);
|
||||
return CRYPTO_ERR_HASH;
|
||||
}
|
||||
|
||||
ret = memcmp(calc_hash, digest_info_ptr, digest_info_len);
|
||||
if (ret != 0) {
|
||||
VERBOSE("%s: not expected digest\n", __func__);
|
||||
ret = CRYPTO_ERR_HASH;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if !defined(DECRYPTION_SUPPORT_none)
|
||||
static int derive_key(uint8_t *key, size_t *key_len, size_t len,
|
||||
unsigned int *flags, const uint8_t *img_id, size_t img_id_len)
|
||||
{
|
||||
size_t i, j;
|
||||
|
||||
assert(*key_len >= 32U);
|
||||
|
||||
/*
|
||||
* Not a real derivation yet
|
||||
*
|
||||
* But we expect a 32 bytes key, and OTP is only 16 bytes
|
||||
* => duplicate.
|
||||
*/
|
||||
for (i = 0U, j = len; j < 32U;
|
||||
i += sizeof(uint32_t), j += sizeof(uint32_t)) {
|
||||
memcpy(key + j, key + i, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
*key_len = 32U;
|
||||
/* Variable 'key' store a real key */
|
||||
*flags = 0U;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plat_get_enc_key_info(enum fw_enc_status_t fw_enc_status, uint8_t *key,
|
||||
size_t *key_len, unsigned int *flags,
|
||||
const uint8_t *img_id, size_t img_id_len)
|
||||
{
|
||||
uint32_t otp_idx;
|
||||
uint32_t otp_len;
|
||||
size_t read_len;
|
||||
size_t i;
|
||||
|
||||
if (fw_enc_status == FW_ENC_WITH_BSSK) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (stm32_get_otp_index(ENCKEY_OTP, &otp_idx, &otp_len) != 0) {
|
||||
VERBOSE("%s: get %s index error\n", __func__, ENCKEY_OTP);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (otp_len > (*key_len * CHAR_BIT)) {
|
||||
VERBOSE("%s: length Error otp_len=%u key_len=%u\n", __func__,
|
||||
otp_len, *key_len * CHAR_BIT);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
read_len = otp_len / CHAR_BIT;
|
||||
assert(read_len % sizeof(uint32_t) == 0);
|
||||
|
||||
for (i = 0U; i < read_len / sizeof(uint32_t); i++) {
|
||||
uint32_t tmp;
|
||||
uint32_t otp_val;
|
||||
|
||||
if (stm32_get_otp_value_from_idx(otp_idx + i, &otp_val) != 0) {
|
||||
zeromem(key, *key_len);
|
||||
VERBOSE("%s: unable to read from otp\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tmp = bswap32(otp_val);
|
||||
memcpy(key + i * sizeof(uint32_t), &tmp, sizeof(tmp));
|
||||
}
|
||||
|
||||
/* Now we have the OTP values in key till read_len */
|
||||
|
||||
if (derive_key(key, key_len, read_len, flags, img_id,
|
||||
img_id_len) != 0) {
|
||||
zeromem(key, *key_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum stm32_saes_key_selection select_key(unsigned int key_flags)
|
||||
{
|
||||
if ((key_flags & ENC_KEY_IS_IDENTIFIER) != 0U) {
|
||||
panic();
|
||||
}
|
||||
|
||||
/* Use the provided key buffer */
|
||||
return STM32_SAES_KEY_SOFT;
|
||||
}
|
||||
|
||||
static int stm32_decrypt_aes_gcm(void *data, size_t data_len,
|
||||
const void *key, unsigned int key_len,
|
||||
unsigned int key_flags,
|
||||
const void *iv, unsigned int iv_len,
|
||||
const void *tag, unsigned int tag_len)
|
||||
{
|
||||
int ret;
|
||||
struct stm32_saes_context ctx;
|
||||
unsigned char tag_buf[CRYPTO_MAX_TAG_SIZE];
|
||||
enum stm32_saes_key_selection key_mode;
|
||||
unsigned int diff = 0U;
|
||||
unsigned int i;
|
||||
|
||||
key_mode = select_key(key_flags);
|
||||
|
||||
ret = stm32_saes_init(&ctx, true, STM32_SAES_MODE_GCM, key_mode, key,
|
||||
key_len, iv, iv_len);
|
||||
if (ret != 0) {
|
||||
return CRYPTO_ERR_INIT;
|
||||
}
|
||||
|
||||
ret = stm32_saes_update_assodata(&ctx, true, NULL, 0U);
|
||||
if (ret != 0) {
|
||||
return CRYPTO_ERR_DECRYPTION;
|
||||
}
|
||||
|
||||
ret = stm32_saes_update_load(&ctx, true, data, data, data_len);
|
||||
if (ret != 0) {
|
||||
return CRYPTO_ERR_DECRYPTION;
|
||||
}
|
||||
|
||||
ret = stm32_saes_final(&ctx, tag_buf, sizeof(tag_buf));
|
||||
if (ret != 0) {
|
||||
return CRYPTO_ERR_DECRYPTION;
|
||||
}
|
||||
|
||||
/* Check tag in "constant-time" */
|
||||
for (i = 0U; i < tag_len; i++) {
|
||||
diff |= ((const unsigned char *)tag)[i] ^ tag_buf[i];
|
||||
}
|
||||
|
||||
if (diff != 0U) {
|
||||
return CRYPTO_ERR_DECRYPTION;
|
||||
}
|
||||
|
||||
return CRYPTO_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Authenticated decryption of an image
|
||||
*
|
||||
*/
|
||||
static int crypto_auth_decrypt(enum crypto_dec_algo dec_algo, void *data_ptr, size_t len,
|
||||
const void *key, unsigned int key_len, unsigned int key_flags,
|
||||
const void *iv, unsigned int iv_len, const void *tag,
|
||||
unsigned int tag_len)
|
||||
{
|
||||
int rc = -1;
|
||||
uint32_t real_iv[4];
|
||||
|
||||
switch (dec_algo) {
|
||||
case CRYPTO_GCM_DECRYPT:
|
||||
/*
|
||||
* GCM expect a Nonce
|
||||
* The AES IV is the nonce (a uint32_t[3])
|
||||
* then a counter (a uint32_t big endian)
|
||||
* The counter starts at 2.
|
||||
*/
|
||||
memcpy(real_iv, iv, iv_len);
|
||||
real_iv[3] = htobe32(0x2U);
|
||||
|
||||
rc = stm32_decrypt_aes_gcm(data_ptr, len, key, key_len, key_flags,
|
||||
real_iv, sizeof(real_iv), tag, tag_len);
|
||||
break;
|
||||
default:
|
||||
rc = CRYPTO_ERR_DECRYPTION;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return CRYPTO_SUCCESS;
|
||||
}
|
||||
|
||||
REGISTER_CRYPTO_LIB("stm32_crypto_lib",
|
||||
crypto_lib_init,
|
||||
crypto_verify_signature,
|
||||
crypto_verify_hash,
|
||||
crypto_auth_decrypt);
|
||||
|
||||
#else /* No decryption support */
|
||||
REGISTER_CRYPTO_LIB("stm32_crypto_lib",
|
||||
crypto_lib_init,
|
||||
crypto_verify_signature,
|
||||
crypto_verify_hash,
|
||||
NULL);
|
||||
|
||||
#endif
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user