From c55906e0f0a85f4036dd5f403ff1cc1837a38443 Mon Sep 17 00:00:00 2001 From: Arnaud Pouliquen Date: Wed, 6 Jul 2022 18:12:18 +0200 Subject: [PATCH 1/5] libutee: add new remoteproc PTA API The remoteproc PTA is charge of providing interface to authenticate firmware images and managing the remote processor live cycle. The remoteproc PTA supports platform specificity in the management of a remote processor: - firmware authentication based on a platform key, - load of the segments in remote processor memories, - start/stop of the remote processor, - remote processor addresses conversion. Signed-off-by: Arnaud Pouliquen Reviewed-by: Etienne Carriere Acked-by: Jerome Forissier --- lib/libutee/include/remoteproc_pta.h | 150 +++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 lib/libutee/include/remoteproc_pta.h diff --git a/lib/libutee/include/remoteproc_pta.h b/lib/libutee/include/remoteproc_pta.h new file mode 100644 index 00000000000..e4be4becd77 --- /dev/null +++ b/lib/libutee/include/remoteproc_pta.h @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2023, STMicroelectronics + */ + +#ifndef __REMOTEPROC_PTA_H +#define __REMOTEPROC_PTA_H + +#include +#include + +/* + * Interface to the pseudo TA which provides platform implementation + * of the remote processor management + */ + +#define PTA_RPROC_UUID { 0x54af4a68, 0x19be, 0x40d7, \ + { 0xbb, 0xe6, 0x89, 0x50, 0x35, 0x0a, 0x87, 0x44 } } + +/* Hardware capability: firmware format */ +#define PTA_RPROC_HWCAP_FMT_ELF BIT32(0) + +/* Hardware capability: image protection method */ +/* The platform supports load of segment with hash protection */ +#define PTA_RPROC_HWCAP_PROT_HASH_TABLE BIT32(0) + +/** + * struct rproc_pta_key_info - public key information + * @algo: Algorithm, defined by public key algorithms TEE_ALG_* + * @info_size: Byte size of @info + * @info: Append key information data + */ +struct rproc_pta_key_info { + uint32_t algo; + uint32_t info_size; + uint8_t info[]; +}; + +static inline size_t rproc_pta_keyinfo_size(struct rproc_pta_key_info *keyinf) +{ + size_t s = 0; + + if (!keyinf || ADD_OVERFLOW(sizeof(*keyinf), keyinf->info_size, &s)) + return 0; + + return s; +} + +/* + * Platform capabilities. + * + * Get Platform firmware loader service capabilities. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [out] params[1].value.a: Firmware format (PTA_RPROC_HWCAP_FMT_*) + * [out] params[2].value.a: Image protection method (PTA_RPROC_HWCAP_PROT_*) + */ +#define PTA_RPROC_HW_CAPABILITIES 1 + +/* + * Firmware loading. + * + * Optional service to implement only in case of proprietary format. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[1].memref: Loadable firmware image + */ +#define PTA_RPROC_FIRMWARE_LOAD 2 + +/* + * Load a segment with a SHA256 hash. + * + * This command is used when the platform secure memory is too constrained to + * save the whole firmware image. Upon segment load, a successful completion + * ensures the loaded image complies with the provided hash. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[1].memref: Section data to load + * [in] params[2].value.a: 32bit LSB load device segment address + * [in] params[2].value.b: 32bit MSB load device segment address + * [in] params[3].memref: Expected hash (SHA256) of the payload + */ +#define PTA_RPROC_LOAD_SEGMENT_SHA256 3 + +/* + * Memory set. + * + * Fill a remote device memory with requested value. this is used for instance + * to clear a memory on the remote firmware load. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[1].value.a: 32bit LSB device memory address + * [in] params[1].value.b: 32bit MSB device memory address + * [in] params[2].value.a: 32bit LSB device memory size + * [in] params[2].value.b: 32bit MSB device memory size + * [in] params[3].value.a: Byte value to be set + */ +#define PTA_RPROC_SET_MEMORY 4 + +/* + * Firmware start. + * + * Start up a successfully loaded remote processor firmware. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + */ +#define PTA_RPROC_FIRMWARE_START 5 + +/* + * Firmware stop. + * + * Stop of the remote processor firmware and release/clean resources. + * After the command successful completion, remote processor firmware must be + * reloaded prior being started again. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + */ +#define PTA_RPROC_FIRMWARE_STOP 6 + +/* + * Firmware device to physical address conversion. + * + * Convert the physical address corresponding to an address got from the + * firmware address layout. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[1].value.a: 32bit LSB Device memory address + * [in] params[1].value.b: 32bit MSB Device memory address + * [in] params[2].value.a: 32bit LSB Device memory size + * [in] params[2].value.b: 32bit MSB Device memory size + * [out] params[3].value.a: 32bit LSB converted physical address + * [out] params[3].value.b: 32bit MSB converted physical address + */ +#define PTA_RPROC_FIRMWARE_DA_TO_PA 7 + +/* + * Verify the firmware digest against a signature + * + * Return TEE_SUCCESS if the signature is verified, + * TEE_ERROR_SIGNATURE_INVALID when signature is not valid, + * another error code for other error cases. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[1].memref: Key information (refer to @rproc_pta_key_info) + * [in] params[2].memref: Digest of the firmware authenticated data + * [in] params[3].memref: Signature of the firmware authenticated data + */ +#define PTA_RPROC_VERIFY_DIGEST 8 + +#endif /* __REMOTEPROC_PTA_H */ From 6eb03c6ebc89f7058fdbc7188ac5e2f53e9bdc4e Mon Sep 17 00:00:00 2001 From: Arnaud Pouliquen Date: Thu, 7 Sep 2023 12:37:38 +0200 Subject: [PATCH 2/5] drivers: Add stm32mp1 remoteproc driver This driver is responsible for configuring the registers and memories of the remote processor. - It stores information about memories assigned to the remote processor based on the device tree. - It ensures consistency between the registered memory and the addresses of the firmware segments to be loaded. - Additionally, it is responsible for starting and stopping the remote processor core. Signed-off-by: Arnaud Pouliquen Reviewed-by: Etienne Carriere Acked-by: Jerome Forissier --- core/arch/arm/plat-stm32mp1/conf.mk | 3 + core/drivers/remoteproc/stm32_remoteproc.c | 388 +++++++++++++++++++++ core/drivers/remoteproc/sub.mk | 1 + core/drivers/sub.mk | 1 + core/include/drivers/stm32_remoteproc.h | 70 ++++ mk/config.mk | 4 + 6 files changed, 467 insertions(+) create mode 100644 core/drivers/remoteproc/stm32_remoteproc.c create mode 100644 core/drivers/remoteproc/sub.mk create mode 100644 core/include/drivers/stm32_remoteproc.h diff --git a/core/arch/arm/plat-stm32mp1/conf.mk b/core/arch/arm/plat-stm32mp1/conf.mk index 3ae03bb7d5b..75dbae3c05c 100644 --- a/core/arch/arm/plat-stm32mp1/conf.mk +++ b/core/arch/arm/plat-stm32mp1/conf.mk @@ -149,6 +149,9 @@ CFG_MMAP_REGIONS ?= 23 CFG_DTB_MAX_SIZE ?= (256 * 1024) CFG_CORE_ASLR ?= n +CFG_STM32MP_REMOTEPROC ?= n +CFG_DRIVERS_REMOTEPROC ?= $(CFG_STM32MP_REMOTEPROC) + ifneq ($(CFG_WITH_LPAE),y) # Without LPAE, default TEE virtual address range is 1MB, we need at least 2MB. CFG_TEE_RAM_VA_SIZE ?= 0x00200000 diff --git a/core/drivers/remoteproc/stm32_remoteproc.c b/core/drivers/remoteproc/stm32_remoteproc.c new file mode 100644 index 00000000000..e4cd6c8b10e --- /dev/null +++ b/core/drivers/remoteproc/stm32_remoteproc.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2023, STMicroelectronics + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TIMEOUT_US_1MS U(1000) + +/** + * struct stm32_rproc_mem - Memory regions used by the remote processor + * + * @addr: physical base address from the CPU space perspective + * @da: device address corresponding to the physical base address + * from remote processor space perspective + * @size: size of the region + */ +struct stm32_rproc_mem { + paddr_t addr; + paddr_t da; + size_t size; +}; + +/** + * struct stm32_rproc_instance - rproc instance context + * + * @cdata: pointer to the device compatible data + * @link: the node in the rproc_list + * @n_regions: number of memory regions + * @regions: memory regions used + * @mcu_rst: remote processor reset control + * @hold_boot: remote processor hold boot control + */ +struct stm32_rproc_instance { + const struct stm32_rproc_compat_data *cdata; + SLIST_ENTRY(stm32_rproc_instance) link; + size_t n_regions; + struct stm32_rproc_mem *regions; + struct rstctrl *mcu_rst; + struct rstctrl *hold_boot; +}; + +/** + * struct stm32_rproc_compat_data - rproc associated data for compatible list + * + * @rproc_id: identifies the remote processor + */ +struct stm32_rproc_compat_data { + uint32_t rproc_id; +}; + +static SLIST_HEAD(, stm32_rproc_instance) rproc_list = + SLIST_HEAD_INITIALIZER(rproc_list); + +void *stm32_rproc_get(uint32_t rproc_id) +{ + struct stm32_rproc_instance *rproc = NULL; + + SLIST_FOREACH(rproc, &rproc_list, link) + if (rproc->cdata->rproc_id == rproc_id) + break; + + return rproc; +} + +TEE_Result stm32_rproc_start(uint32_t rproc_id) +{ + struct stm32_rproc_instance *rproc = stm32_rproc_get(rproc_id); + TEE_Result res = TEE_ERROR_GENERIC; + + if (!rproc || !rproc->hold_boot) + return TEE_ERROR_GENERIC; + + /* + * The firmware is started by de-asserting the hold boot and + * asserting it back to avoid auto restart on a crash. + * No need to release the MCU reset as it is automatically released by + * the hardware. + */ + res = rstctrl_deassert_to(rproc->hold_boot, TIMEOUT_US_1MS); + if (!res) + res = rstctrl_assert_to(rproc->hold_boot, TIMEOUT_US_1MS); + + return res; +} + +static TEE_Result rproc_stop(struct stm32_rproc_instance *rproc) +{ + TEE_Result res = TEE_ERROR_GENERIC; + + if (!rproc->hold_boot || !rproc->mcu_rst) + return TEE_ERROR_GENERIC; + + res = rstctrl_assert_to(rproc->hold_boot, TIMEOUT_US_1MS); + if (!res) + res = rstctrl_assert_to(rproc->mcu_rst, TIMEOUT_US_1MS); + + return res; +} + +TEE_Result stm32_rproc_stop(uint32_t rproc_id) +{ + struct stm32_rproc_instance *rproc = stm32_rproc_get(rproc_id); + + if (!rproc) + return TEE_ERROR_BAD_PARAMETERS; + + return rproc_stop(rproc); +} + +TEE_Result stm32_rproc_da_to_pa(uint32_t rproc_id, paddr_t da, size_t size, + paddr_t *pa) +{ + struct stm32_rproc_instance *rproc = stm32_rproc_get(rproc_id); + struct stm32_rproc_mem *mems = NULL; + unsigned int i = 0; + + if (!rproc) + return TEE_ERROR_BAD_PARAMETERS; + + mems = rproc->regions; + + for (i = 0; i < rproc->n_regions; i++) { + if (core_is_buffer_inside(da, size, mems[i].da, mems[i].size)) { + /* + * A match between the requested DA memory area and the + * registered regions has been found. + * The PA is the reserved-memory PA address plus the + * delta between the requested DA and the + * reserved-memory DA address. + */ + *pa = mems[i].addr + da - mems[i].da; + return TEE_SUCCESS; + } + } + + return TEE_ERROR_ACCESS_DENIED; +} + +TEE_Result stm32_rproc_map(uint32_t rproc_id, paddr_t pa, size_t size, + void **va) +{ + struct stm32_rproc_instance *rproc = stm32_rproc_get(rproc_id); + struct stm32_rproc_mem *mems = NULL; + unsigned int i = 0; + + if (!rproc) + return TEE_ERROR_BAD_PARAMETERS; + + mems = rproc->regions; + + for (i = 0; i < rproc->n_regions; i++) { + if (!core_is_buffer_inside(pa, size, mems[i].addr, + mems[i].size)) + continue; + *va = core_mmu_add_mapping(MEM_AREA_RAM_NSEC, pa, size); + if (!*va) { + EMSG("Can't map region %#"PRIxPA" size %zu", pa, size); + return TEE_ERROR_GENERIC; + } + + return TEE_SUCCESS; + } + + return TEE_ERROR_ACCESS_DENIED; +} + +TEE_Result stm32_rproc_unmap(uint32_t rproc_id, void *va, size_t size) +{ + struct stm32_rproc_instance *rproc = stm32_rproc_get(rproc_id); + struct stm32_rproc_mem *mems = NULL; + paddr_t pa = virt_to_phys(va); + unsigned int i = 0; + + if (!rproc || !pa) + return TEE_ERROR_BAD_PARAMETERS; + + mems = rproc->regions; + + for (i = 0; i < rproc->n_regions; i++) { + if (!core_is_buffer_inside(pa, size, mems[i].addr, + mems[i].size)) + continue; + + /* Flush the cache before unmapping the memory */ + dcache_clean_range(va, size); + + if (core_mmu_remove_mapping(MEM_AREA_RAM_NSEC, va, size)) { + EMSG("Can't unmap region %#"PRIxPA" size %zu", + pa, size); + return TEE_ERROR_GENERIC; + } + + return TEE_SUCCESS; + } + + return TEE_ERROR_ACCESS_DENIED; +} + +static TEE_Result stm32_rproc_get_dma_range(struct stm32_rproc_mem *region, + const void *fdt, int node) +{ + const fdt32_t *list = NULL; + int ahb_node = 0; + int len = 0; + int nranges = 0; + int i = 0; + + /* + * The match between local and remote processor memory mapping is + * described in the dma-ranges defined by the bus parent node. + */ + ahb_node = fdt_parent_offset(fdt, node); + + list = fdt_getprop(fdt, ahb_node, "dma-ranges", &len); + if (!list) { + if (len != -FDT_ERR_NOTFOUND) + return TEE_ERROR_GENERIC; + /* Same memory mapping */ + DMSG("No dma-ranges found in DT"); + region->da = region->addr; + return TEE_SUCCESS; + } + + if ((len % (sizeof(uint32_t) * 3))) + return TEE_ERROR_GENERIC; + + nranges = len / sizeof(uint32_t); + + for (i = 0; i < nranges; i += 3) { + uint32_t da = fdt32_to_cpu(list[i]); + uint32_t pa = fdt32_to_cpu(list[i + 1]); + uint32_t size = fdt32_to_cpu(list[i + 2]); + + if (core_is_buffer_inside(region->addr, region->size, + pa, size)) { + region->da = da + (region->addr - pa); + return TEE_SUCCESS; + } + } + + return TEE_ERROR_BAD_PARAMETERS; +} + +/* Get device tree memory regions reserved for the Cortex-M and the IPC */ +static TEE_Result stm32_rproc_parse_mems(struct stm32_rproc_instance *rproc, + const void *fdt, int node) +{ + const fdt32_t *list = NULL; + TEE_Result res = TEE_ERROR_GENERIC; + struct stm32_rproc_mem *regions = NULL; + int len = 0; + int n_regions = 0; + int i = 0; + + list = fdt_getprop(fdt, node, "memory-region", &len); + if (!list) { + EMSG("No memory regions found in DT"); + return TEE_ERROR_GENERIC; + } + + n_regions = len / sizeof(uint32_t); + + regions = calloc(n_regions, sizeof(*regions)); + if (!regions) + return TEE_ERROR_OUT_OF_MEMORY; + + for (i = 0; i < n_regions; i++) { + int pnode = 0; + + pnode = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(list[i])); + if (pnode < 0) { + res = TEE_ERROR_GENERIC; + goto err; + } + + regions[i].addr = fdt_reg_base_address(fdt, pnode); + regions[i].size = fdt_reg_size(fdt, pnode); + + if (regions[i].addr <= 0 || regions[i].size <= 0) { + res = TEE_ERROR_GENERIC; + goto err; + } + + res = stm32_rproc_get_dma_range(®ions[i], fdt, node); + if (res) + goto err; + + if (!regions[i].addr || !regions[i].size) { + res = TEE_ERROR_BAD_PARAMETERS; + goto err; + } + + DMSG("register region %#"PRIxPA" size %#zx", + regions[i].addr, regions[i].size); + } + + rproc->n_regions = n_regions; + rproc->regions = regions; + + return TEE_SUCCESS; + +err: + free(regions); + + return res; +} + +static void stm32_rproc_cleanup(struct stm32_rproc_instance *rproc) +{ + free(rproc->regions); + free(rproc); +} + +static TEE_Result stm32_rproc_probe(const void *fdt, int node, + const void *comp_data) +{ + struct stm32_rproc_instance *rproc = NULL; + TEE_Result res = TEE_ERROR_GENERIC; + + rproc = calloc(1, sizeof(*rproc)); + if (!rproc) + return TEE_ERROR_OUT_OF_MEMORY; + + rproc->cdata = comp_data; + + res = stm32_rproc_parse_mems(rproc, fdt, node); + if (res) + goto err; + + res = rstctrl_dt_get_by_name(fdt, node, "mcu_rst", &rproc->mcu_rst); + if (res) + goto err; + + res = rstctrl_dt_get_by_name(fdt, node, "hold_boot", &rproc->hold_boot); + if (res) + goto err; + + /* Ensure that the MCU is HOLD */ + if (rproc->mcu_rst) { + res = rproc_stop(rproc); + if (res) + goto err; + } + + /* + * The memory management should be enhance with firewall + * mechanism to map the memory in secure area for the firmware + * loading and then to give exclusive access right to the + * coprocessor (except for the shared memory). + */ + IMSG("Warning: the remoteproc memories are not protected by firewall"); + + SLIST_INSERT_HEAD(&rproc_list, rproc, link); + + return TEE_SUCCESS; + +err: + stm32_rproc_cleanup(rproc); + return res; +} + +static const struct stm32_rproc_compat_data stm32_rproc_m4_compat = { + .rproc_id = STM32_M4_RPROC_ID, +}; + +static const struct dt_device_match stm32_rproc_match_table[] = { + { + .compatible = "st,stm32mp1-m4-tee", + .compat_data = &stm32_rproc_m4_compat, + }, + { } +}; + +DEFINE_DT_DRIVER(stm32_rproc_dt_driver) = { + .name = "stm32-rproc", + .match_table = stm32_rproc_match_table, + .probe = &stm32_rproc_probe, +}; diff --git a/core/drivers/remoteproc/sub.mk b/core/drivers/remoteproc/sub.mk new file mode 100644 index 00000000000..4eb5ca3f29f --- /dev/null +++ b/core/drivers/remoteproc/sub.mk @@ -0,0 +1 @@ +srcs-$(CFG_STM32MP_REMOTEPROC) += stm32_remoteproc.c diff --git a/core/drivers/sub.mk b/core/drivers/sub.mk index 4cd4aab1925..0dffd7a9acb 100644 --- a/core/drivers/sub.mk +++ b/core/drivers/sub.mk @@ -86,6 +86,7 @@ subdirs-$(CFG_DRIVERS_I2C) += i2c subdirs-$(CFG_DRIVERS_PINCTRL) += pinctrl subdirs-$(CFG_DRIVERS_REGULATOR) += regulator subdirs-$(CFG_DRIVERS_RSTCTRL) += rstctrl +subdirs-$(CFG_DRIVERS_REMOTEPROC) += remoteproc subdirs-$(CFG_SCMI_MSG_DRIVERS) += scmi-msg subdirs-y += imx subdirs-y += pm diff --git a/core/include/drivers/stm32_remoteproc.h b/core/include/drivers/stm32_remoteproc.h new file mode 100644 index 00000000000..8cb1efd863c --- /dev/null +++ b/core/include/drivers/stm32_remoteproc.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2023, STMicroelectronics - All Rights Reserved + */ + +#ifndef __DRIVERS_STM32_REMOTEPROC_H +#define __DRIVERS_STM32_REMOTEPROC_H + +#include +#include +#include + +/* IDs of the supported remote processors*/ +#define STM32_M4_RPROC_ID 0 + +/* + * stm32_rproc_get() - get the rproc handle associated to a remote processor ID + * @rproc_id unique identifier of the remote processor + * Return a pointer to the rproc firmware handle related to @rproc_id or NULL. + */ +void *stm32_rproc_get(uint32_t rproc_id); + +/* + * stm32_rproc_da_to_pa() - Convert the coprocessor device address to a CPU + * physical address. + * @rproc_id unique identifier of the remote processor + * @da device memory address from the remote processor space + * perspective. + * @size size of the memory + * @pa Output CPU physical address associated to @da. + * Return TEE_SUCCESS or appropriate error. + */ +TEE_Result stm32_rproc_da_to_pa(uint32_t rproc_id, paddr_t da, size_t size, + paddr_t *pa); + +/* + * stm32_rproc_map() - map the physical address if valid + * @rproc_id unique identifier of the remote processor + * @pa physical address from the CPU space perspective + * @size size of the memory + * @va Output CPU virtual address associated to @pa. + * Return TEE_SUCCESS or appropriate error. + */ +TEE_Result stm32_rproc_map(uint32_t rproc_id, paddr_t pa, size_t size, + void **va); + +/* + * stm32_rproc_unmap() - ummap the virtual address mapped with stm32_rproc_map + * @rproc_id unique identifier of the remote processor + * @va virtual address + * @size size of the memory + * Return TEE_SUCCESS or appropriate error. + */ +TEE_Result stm32_rproc_unmap(uint32_t rproc_id, void *va, size_t size); + +/* + * stm32_rproc_start() - start the remote processor core + * @rproc_id unique identifier of the remote processor + * Return TEE_SUCCESS or appropriate error. + */ +TEE_Result stm32_rproc_start(uint32_t rproc_id); + +/* + * stm32_rproc_stop() - stop the remote processor core + * @rproc_id unique identifier of the remote processor + * Return TEE_SUCCESS or appropriate error. + */ +TEE_Result stm32_rproc_stop(uint32_t rproc_id); + +#endif /* __DRIVERS_STM32_REMOTEPROC_H */ diff --git a/mk/config.mk b/mk/config.mk index da9b0410589..b441e2cbe60 100644 --- a/mk/config.mk +++ b/mk/config.mk @@ -1137,3 +1137,7 @@ $(eval $(call cfg-depends-all,CFG_HALT_CORES_ON_PANIC,CFG_GIC)) # use that. Provides easier configuration of virtual platforms where the # maximal PA can vary. CFG_AUTO_MAX_PA_BITS ?= n + +# CFG_DRIVERS_REMOTEPROC, when enabled, embeds support for remote processor +# management including generic DT bindings for the configuration. +CFG_DRIVERS_REMOTEPROC ?= n From 25ba4d3fceba664986ad244117841e46d6ed9a88 Mon Sep 17 00:00:00 2001 From: Arnaud Pouliquen Date: Wed, 6 Jul 2022 18:12:18 +0200 Subject: [PATCH 3/5] pta: stm32mp: add new remoteproc PTA Add remoteproc PTA for the stm32mp1 platform. The PTA relies on the stm32_remoteproc driver for the remoteproc management. It is charge of providing interface for authenticating firmware images and managing the remote processor live cycle. Signed-off-by: Arnaud Pouliquen Reviewed-by: Etienne Carriere Acked-by: Jerome Forissier --- core/arch/arm/plat-stm32mp1/conf.mk | 1 + core/pta/stm32mp/remoteproc_pta.c | 356 ++++++++++++++++++++++++++++ core/pta/stm32mp/rproc_pub_key.h | 16 ++ core/pta/stm32mp/sub.mk | 1 + core/pta/sub.mk | 9 + mk/config.mk | 4 + 6 files changed, 387 insertions(+) create mode 100644 core/pta/stm32mp/remoteproc_pta.c create mode 100644 core/pta/stm32mp/rproc_pub_key.h diff --git a/core/arch/arm/plat-stm32mp1/conf.mk b/core/arch/arm/plat-stm32mp1/conf.mk index 75dbae3c05c..b79d6a3cb58 100644 --- a/core/arch/arm/plat-stm32mp1/conf.mk +++ b/core/arch/arm/plat-stm32mp1/conf.mk @@ -151,6 +151,7 @@ CFG_CORE_ASLR ?= n CFG_STM32MP_REMOTEPROC ?= n CFG_DRIVERS_REMOTEPROC ?= $(CFG_STM32MP_REMOTEPROC) +CFG_REMOTEPROC_PTA ?= $(CFG_STM32MP_REMOTEPROC) ifneq ($(CFG_WITH_LPAE),y) # Without LPAE, default TEE virtual address range is 1MB, we need at least 2MB. diff --git a/core/pta/stm32mp/remoteproc_pta.c b/core/pta/stm32mp/remoteproc_pta.c new file mode 100644 index 00000000000..b2eed7f257f --- /dev/null +++ b/core/pta/stm32mp/remoteproc_pta.c @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2023, STMicroelectronics - All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rproc_pub_key.h" + +#define PTA_NAME "remoteproc.pta" + +/* + * UUID of the remoteproc Trusted application authorized to communicate with + * the remoteproc pseudo TA. The UID should match the one defined in the + * ta_remoteproc.h header file. + */ +#define TA_REMOTEPROC_UUID \ + { 0x80a4c275, 0x0a47, 0x4905, \ + { 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08} } + +/* + * Firmware states + * REMOTEPROC_OFF: firmware is off + * REMOTEPROC_ON: firmware is running + */ +enum rproc_load_state { + REMOTEPROC_OFF = 0, + REMOTEPROC_ON, +}; + +/* Currently supporting a single remote processor instance */ +static enum rproc_load_state rproc_ta_state = REMOTEPROC_OFF; + +static TEE_Result rproc_pta_capabilities(uint32_t pt, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_OUTPUT, + TEE_PARAM_TYPE_VALUE_OUTPUT, + TEE_PARAM_TYPE_NONE); + + if (pt != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + if (!stm32_rproc_get(params[0].value.a)) + return TEE_ERROR_NOT_SUPPORTED; + + /* Support only ELF format */ + params[1].value.a = PTA_RPROC_HWCAP_FMT_ELF; + + /* + * Due to stm32mp1 pager, secure memory is too expensive. Support hash + * protected image only, so that firmware image can be loaded from + * non-secure memory. + */ + params[2].value.a = PTA_RPROC_HWCAP_PROT_HASH_TABLE; + + return TEE_SUCCESS; +} + +static TEE_Result rproc_pta_load_segment(uint32_t pt, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_MEMREF_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_MEMREF_INPUT); + TEE_Result res = TEE_ERROR_GENERIC; + paddr_t pa = 0; + void *dst = NULL; + uint8_t *src = params[1].memref.buffer; + size_t size = params[1].memref.size; + uint8_t *hash = params[3].memref.buffer; + paddr_t da = (paddr_t)reg_pair_to_64(params[2].value.b, + params[2].value.a); + + if (pt != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + if (!hash || params[3].memref.size != TEE_SHA256_HASH_SIZE) + return TEE_ERROR_BAD_PARAMETERS; + + if (rproc_ta_state != REMOTEPROC_OFF) + return TEE_ERROR_BAD_STATE; + + /* Get the physical address in local context mapping */ + res = stm32_rproc_da_to_pa(params[0].value.a, da, size, &pa); + if (res) + return res; + + if (stm32_rproc_map(params[0].value.a, pa, size, &dst)) { + EMSG("Can't map region %#"PRIxPA" size %zu", pa, size); + return TEE_ERROR_GENERIC; + } + + /* Copy the segment to the remote processor memory */ + memcpy(dst, src, size); + + /* Verify that loaded segment is valid */ + res = hash_sha256_check(hash, dst, size); + if (res) + memset(dst, 0, size); + + stm32_rproc_unmap(params[0].value.a, dst, size); + + return res; +} + +static TEE_Result rproc_pta_set_memory(uint32_t pt, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT); + TEE_Result res = TEE_ERROR_GENERIC; + paddr_t pa = 0; + void *dst = NULL; + paddr_t da = params[1].value.a; + size_t size = params[2].value.a; + uint8_t value = params[3].value.a && 0xFF; + + if (pt != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + if (rproc_ta_state != REMOTEPROC_OFF) + return TEE_ERROR_BAD_STATE; + + /* Get the physical address in CPU mapping */ + res = stm32_rproc_da_to_pa(params[0].value.a, da, size, &pa); + if (res) + return res; + + res = stm32_rproc_map(params[0].value.a, pa, size, &dst); + if (res) { + EMSG("Can't map region %#"PRIxPA" size %zu", pa, size); + return TEE_ERROR_GENERIC; + } + + memset(dst, value, size); + + return stm32_rproc_unmap(params[0].value.a, dst, size); +} + +static TEE_Result rproc_pta_da_to_pa(uint32_t pt, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_OUTPUT); + TEE_Result res = TEE_ERROR_GENERIC; + paddr_t da = params[1].value.a; + size_t size = params[2].value.a; + paddr_t pa = 0; + + DMSG("Conversion for address %#"PRIxPA" size %zu", da, size); + + if (pt != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + /* Target address is expected 32bit, ensure 32bit MSB are zero */ + if (params[1].value.b || params[2].value.b) + return TEE_ERROR_BAD_PARAMETERS; + + res = stm32_rproc_da_to_pa(params[0].value.a, da, size, &pa); + if (res) + return res; + + reg_pair_from_64((uint64_t)pa, ¶ms[3].value.b, ¶ms[3].value.a); + + return TEE_SUCCESS; +} + +static TEE_Result rproc_pta_start(uint32_t pt, + TEE_Param params[TEE_NUM_PARAMS]) +{ + TEE_Result res = TEE_ERROR_GENERIC; + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + + if (pt != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + if (rproc_ta_state != REMOTEPROC_OFF) + return TEE_ERROR_BAD_STATE; + + res = stm32_rproc_start(params[0].value.a); + if (res) + return res; + + rproc_ta_state = REMOTEPROC_ON; + + return TEE_SUCCESS; +} + +static TEE_Result rproc_pta_stop(uint32_t pt, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + TEE_Result res = TEE_ERROR_GENERIC; + + if (pt != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + if (rproc_ta_state != REMOTEPROC_ON) + return TEE_ERROR_BAD_STATE; + + res = stm32_rproc_stop(params[0].value.a); + if (res) + return res; + + rproc_ta_state = REMOTEPROC_OFF; + + return TEE_SUCCESS; +} + +static TEE_Result rproc_pta_verify_rsa_signature(TEE_Param *hash, + TEE_Param *sig, uint32_t algo) +{ + struct rsa_public_key key = { }; + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t e = TEE_U32_TO_BIG_ENDIAN(rproc_pub_key_exponent); + size_t hash_size = (size_t)hash->memref.size; + size_t sig_size = (size_t)sig->memref.size; + + res = crypto_acipher_alloc_rsa_public_key(&key, sig_size); + if (res) + return res; + + res = crypto_bignum_bin2bn((uint8_t *)&e, sizeof(e), key.e); + if (res) + goto out; + + res = crypto_bignum_bin2bn(rproc_pub_key_modulus, + rproc_pub_key_modulus_size, key.n); + if (res) + goto out; + + res = crypto_acipher_rsassa_verify(algo, &key, hash_size, + hash->memref.buffer, hash_size, + sig->memref.buffer, sig_size); + +out: + crypto_acipher_free_rsa_public_key(&key); + + return res; +} + +static TEE_Result rproc_pta_verify_digest(uint32_t pt, + TEE_Param params[TEE_NUM_PARAMS]) +{ + struct rproc_pta_key_info *keyinfo = NULL; + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_MEMREF_INPUT, + TEE_PARAM_TYPE_MEMREF_INPUT, + TEE_PARAM_TYPE_MEMREF_INPUT); + + if (pt != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + if (!stm32_rproc_get(params[0].value.a)) + return TEE_ERROR_NOT_SUPPORTED; + + if (rproc_ta_state != REMOTEPROC_OFF) + return TEE_ERROR_BAD_STATE; + + keyinfo = params[1].memref.buffer; + + if (!keyinfo || + rproc_pta_keyinfo_size(keyinfo) != params[1].memref.size) + return TEE_ERROR_BAD_PARAMETERS; + + if (keyinfo->algo != TEE_ALG_RSASSA_PKCS1_V1_5_SHA256) + return TEE_ERROR_NOT_SUPPORTED; + + return rproc_pta_verify_rsa_signature(¶ms[2], ¶ms[3], + keyinfo->algo); +} + +static TEE_Result rproc_pta_invoke_command(void *session __unused, + uint32_t cmd_id, + uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + switch (cmd_id) { + case PTA_RPROC_HW_CAPABILITIES: + return rproc_pta_capabilities(param_types, params); + case PTA_RPROC_LOAD_SEGMENT_SHA256: + return rproc_pta_load_segment(param_types, params); + case PTA_RPROC_SET_MEMORY: + return rproc_pta_set_memory(param_types, params); + case PTA_RPROC_FIRMWARE_START: + return rproc_pta_start(param_types, params); + case PTA_RPROC_FIRMWARE_STOP: + return rproc_pta_stop(param_types, params); + case PTA_RPROC_FIRMWARE_DA_TO_PA: + return rproc_pta_da_to_pa(param_types, params); + case PTA_RPROC_VERIFY_DIGEST: + return rproc_pta_verify_digest(param_types, params); + default: + return TEE_ERROR_NOT_IMPLEMENTED; + } +} + +/* + * Pseudo Trusted Application entry points + */ +static TEE_Result rproc_pta_open_session(uint32_t pt, + TEE_Param params[TEE_NUM_PARAMS], + void **sess_ctx __unused) +{ + struct ts_session *s = ts_get_calling_session(); + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + struct ts_ctx *ctx = NULL; + TEE_UUID ta_uuid = TA_REMOTEPROC_UUID; + + if (pt != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + if (!s || !is_user_ta_ctx(s->ctx)) + return TEE_ERROR_ACCESS_DENIED; + + /* Check that we're called by the remoteproc Trusted application*/ + ctx = s->ctx; + if (memcmp(&ctx->uuid, &ta_uuid, sizeof(TEE_UUID))) + return TEE_ERROR_ACCESS_DENIED; + + if (!stm32_rproc_get(params[0].value.a)) + return TEE_ERROR_NOT_SUPPORTED; + + return TEE_SUCCESS; +} + +pseudo_ta_register(.uuid = PTA_RPROC_UUID, .name = PTA_NAME, + .flags = PTA_DEFAULT_FLAGS, + .invoke_command_entry_point = rproc_pta_invoke_command, + .open_session_entry_point = rproc_pta_open_session); diff --git a/core/pta/stm32mp/rproc_pub_key.h b/core/pta/stm32mp/rproc_pub_key.h new file mode 100644 index 00000000000..b3625652373 --- /dev/null +++ b/core/pta/stm32mp/rproc_pub_key.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2023, STMicroelectronics - All Rights Reserved + */ + +#ifndef PTA_STM32MP_RPROC_PUB_KEY_H +#define PTA_STM32MP_RPROC_PUB_KEY_H + +#include + +extern const uint32_t rproc_pub_key_exponent; +extern const uint8_t rproc_pub_key_modulus[]; +extern const size_t rproc_pub_key_modulus_size; + +#endif /* PTA_STM32MP_RPROC_PUB_KEY_H */ + diff --git a/core/pta/stm32mp/sub.mk b/core/pta/stm32mp/sub.mk index ed16c5f471c..b950e9fcf6c 100644 --- a/core/pta/stm32mp/sub.mk +++ b/core/pta/stm32mp/sub.mk @@ -1 +1,2 @@ srcs-$(CFG_STM32_BSEC_PTA) += bsec_pta.c +srcs-$(CFG_RPROC_PTA) += remoteproc_pta.c diff --git a/core/pta/sub.mk b/core/pta/sub.mk index 41212d338b1..b7b057147e4 100644 --- a/core/pta/sub.mk +++ b/core/pta/sub.mk @@ -19,3 +19,12 @@ subdirs-y += bcm subdirs-y += stm32mp subdirs-y += imx subdirs-y += k3 + +ifeq ($(CFG_RPROC_PTA),y) +gensrcs-y += rproc_pub_key +produce-rproc_pub_key = rproc_pub_key.c +depends-rproc_pub_key = $(RPROC_SIGN_KEY) scripts/pem_to_pub_c.py +recipe-rproc_pub_key = $(PYTHON3) scripts/pem_to_pub_c.py \ + --prefix rproc_pub_key --key $(RPROC_SIGN_KEY) \ + --out $(sub-dir-out)/rproc_pub_key.c +endif \ No newline at end of file diff --git a/mk/config.mk b/mk/config.mk index b441e2cbe60..6dd685629aa 100644 --- a/mk/config.mk +++ b/mk/config.mk @@ -1141,3 +1141,7 @@ CFG_AUTO_MAX_PA_BITS ?= n # CFG_DRIVERS_REMOTEPROC, when enabled, embeds support for remote processor # management including generic DT bindings for the configuration. CFG_DRIVERS_REMOTEPROC ?= n + +# CFG_REMOTEPROC_PTA, when enabled, embeds remote processor management PTA +# service. +CFG_REMOTEPROC_PTA ?= n \ No newline at end of file From 3eb766e55e5553dc0a112c1a0a76fa5c08a40cc3 Mon Sep 17 00:00:00 2001 From: Arnaud Pouliquen Date: Fri, 17 Nov 2023 14:37:51 +0100 Subject: [PATCH 4/5] plat-stm32mp1: Add the remoteproc TA in early TA list On the stm32mp1 platform, it is possible to load firmware during the bootloader stage, for instance, by U-boot. To enable this feature, The remoteproc TA should be added to the list of early-TAs. Signed-off-by: Arnaud Pouliquen Acked-by: Etienne Carriere Acked-by: Jerome Forissier --- core/arch/arm/plat-stm32mp1/conf.mk | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/arch/arm/plat-stm32mp1/conf.mk b/core/arch/arm/plat-stm32mp1/conf.mk index b79d6a3cb58..63d55cc4f60 100644 --- a/core/arch/arm/plat-stm32mp1/conf.mk +++ b/core/arch/arm/plat-stm32mp1/conf.mk @@ -152,6 +152,12 @@ CFG_CORE_ASLR ?= n CFG_STM32MP_REMOTEPROC ?= n CFG_DRIVERS_REMOTEPROC ?= $(CFG_STM32MP_REMOTEPROC) CFG_REMOTEPROC_PTA ?= $(CFG_STM32MP_REMOTEPROC) +ifeq ($(CFG_REMOTEPROC_PTA),y) +# Remoteproc early TA for coprocessor firmware management in boot stages +CFG_IN_TREE_EARLY_TAS += remoteproc/80a4c275-0a47-4905-8285-1486a9771a08 +# Embed public part of this key in OP-TEE OS +RPROC_SIGN_KEY ?= keys/default.pem +endif ifneq ($(CFG_WITH_LPAE),y) # Without LPAE, default TEE virtual address range is 1MB, we need at least 2MB. From a1ff7cd11a3cc0c65701e6c94c73209a2b017811 Mon Sep 17 00:00:00 2001 From: Arnaud Pouliquen Date: Mon, 20 Mar 2023 11:02:27 +0100 Subject: [PATCH 5/5] dts: stm32: update m4_rproc to support the remoteproc OP-TEE framework Update device tree to support the load of the remoteproc firmware by OP-TEE. - declare m_ipc_shm memory region that can contain the remote processor resource table and trace buffer, - update reset to align declaration with the Linux devicetree To enable the load of the coprocessor firmware by OP-TEE, user have to update the m4_rproc node compatible property: -"st,stm32mp1-m4": the load is managed by Linux or U-boot, -"st,stm32mp1-m4-tee": the load is managed by OP-TEE. Signed-off-by: Arnaud Pouliquen Acked-by: Etienne Carriere Acked-by: Jerome Forissier --- core/arch/arm/dts/stm32mp151.dtsi | 4 ++-- core/arch/arm/dts/stm32mp157c-ed1.dts | 8 +++++++- core/arch/arm/dts/stm32mp15xx-dkx.dtsi | 8 +++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/core/arch/arm/dts/stm32mp151.dtsi b/core/arch/arm/dts/stm32mp151.dtsi index dce40957d7b..d6729c8c1fd 100644 --- a/core/arch/arm/dts/stm32mp151.dtsi +++ b/core/arch/arm/dts/stm32mp151.dtsi @@ -1839,8 +1839,8 @@ reg = <0x10000000 0x40000>, <0x30000000 0x40000>, <0x38000000 0x10000>; - resets = <&rcc MCU_R>; - st,syscfg-holdboot = <&rcc 0x10C 0x1>; + resets = <&rcc MCU_R>, <&rcc MCU_HOLD_BOOT_R>; + reset-names = "mcu_rst", "hold_boot"; st,syscfg-tz = <&rcc 0x000 0x1>; st,syscfg-pdds = <&pwr_mcu 0x0 0x1>; st,syscfg-rsc-tbl = <&tamp 0x144 0xFFFFFFFF>; diff --git a/core/arch/arm/dts/stm32mp157c-ed1.dts b/core/arch/arm/dts/stm32mp157c-ed1.dts index cee05c4729a..ee05a1830aa 100644 --- a/core/arch/arm/dts/stm32mp157c-ed1.dts +++ b/core/arch/arm/dts/stm32mp157c-ed1.dts @@ -54,6 +54,12 @@ no-map; }; + ipc_shmem: ipc-shmem@1004f000 { + compatible = "shared-dma-pool"; + reg = <0x10048000 0x8000>; + no-map; + }; + mcuram: mcuram@30000000 { compatible = "shared-dma-pool"; reg = <0x30000000 0x40000>; @@ -324,7 +330,7 @@ &m4_rproc { memory-region = <&retram>, <&mcuram>, <&mcuram2>, <&vdev0vring0>, - <&vdev0vring1>, <&vdev0buffer>; + <&vdev0vring1>, <&vdev0buffer>, <&ipc_shmem>; mboxes = <&ipcc 0>, <&ipcc 1>, <&ipcc 2>, <&ipcc 3>; mbox-names = "vq0", "vq1", "shutdown", "detach"; interrupt-parent = <&exti>; diff --git a/core/arch/arm/dts/stm32mp15xx-dkx.dtsi b/core/arch/arm/dts/stm32mp15xx-dkx.dtsi index 7ae62e7c00c..ef1f1b183cd 100644 --- a/core/arch/arm/dts/stm32mp15xx-dkx.dtsi +++ b/core/arch/arm/dts/stm32mp15xx-dkx.dtsi @@ -42,6 +42,12 @@ no-map; }; + ipc_shmem: ipc-shmem@1004f000 { + compatible = "shared-dma-pool"; + reg = <0x10048000 0x8000>; + no-map; + }; + mcuram: mcuram@30000000 { compatible = "shared-dma-pool"; reg = <0x30000000 0x40000>; @@ -476,7 +482,7 @@ &m4_rproc { memory-region = <&retram>, <&mcuram>, <&mcuram2>, <&vdev0vring0>, - <&vdev0vring1>, <&vdev0buffer>; + <&vdev0vring1>, <&vdev0buffer>, <&ipc_shmem>; mboxes = <&ipcc 0>, <&ipcc 1>, <&ipcc 2>, <&ipcc 3>; mbox-names = "vq0", "vq1", "shutdown", "detach"; interrupt-parent = <&exti>;