Main updates from version V7[1]
Update the series based on Mathieu Poirier's comments. Details of the updates are listed in the commit messages of the patches.
[1] https://lore.kernel.org/linux-arm-kernel/20240611073904.475019-1-arnaud.poul...
base-commit: 1613e604df0cd359cf2a7fbd9be7a0bcfacfabd0
Description of the feature: -------------------------- This series proposes the implementation of a remoteproc tee driver to communicate with a TEE trusted application responsible for authenticating and loading the remoteproc firmware image in an Arm secure context.
1) Principle:
The remoteproc tee driver provides services to communicate with the OP-TEE trusted application running on the Trusted Execution Context (TEE). The trusted application in TEE manages the remote processor lifecycle:
- authenticating and loading firmware images, - isolating and securing the remote processor memories, - supporting multi-firmware (e.g., TF-M + Zephyr on a Cortex-M33), - managing the start and stop of the firmware by the TEE.
2) Format of the signed image:
Refer to: https://github.com/OP-TEE/optee_os/blob/master/ta/remoteproc/src/remoteproc_...
3) OP-TEE trusted application API:
Refer to: https://github.com/OP-TEE/optee_os/blob/master/ta/remoteproc/include/ta_remo...
4) OP-TEE signature script
Refer to: https://github.com/OP-TEE/optee_os/blob/master/scripts/sign_rproc_fw.py
Example of usage: sign_rproc_fw.py --in <fw1.elf> --in <fw2.elf> --out <signed_fw.sign> --key ${OP-TEE_PATH}/keys/default.pem
5) Impact on User space Application
No sysfs impact.the user only needs to provide the signed firmware image instead of the ELF image.
For more information about the implementation, a presentation is available here (note that the format of the signed image has evolved between the presentation and the integration in OP-TEE).
https://resources.linaro.org/en/resource/6c5bGvZwUAjX56fvxthxds
Arnaud Pouliquen (5): remoteproc: core: Introduce rproc_pa_to_va helper remoteproc: Add TEE support dt-bindings: remoteproc: Add compatibility for TEE support remoteproc: stm32: Create sub-functions to request shutdown and release remoteproc: stm32: Add support of an OP-TEE TA to load the firmware
.../bindings/remoteproc/st,stm32-rproc.yaml | 58 ++- drivers/remoteproc/Kconfig | 10 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/remoteproc_core.c | 46 ++ drivers/remoteproc/remoteproc_tee.c | 446 ++++++++++++++++++ drivers/remoteproc/stm32_rproc.c | 147 ++++-- include/linux/remoteproc.h | 5 + include/linux/remoteproc_tee.h | 100 ++++ 8 files changed, 769 insertions(+), 44 deletions(-) create mode 100644 drivers/remoteproc/remoteproc_tee.c create mode 100644 include/linux/remoteproc_tee.h
base-commit: 1613e604df0cd359cf2a7fbd9be7a0bcfacfabd0
When a resource table is loaded by an external entity such as U-boot or OP-TEE, we do not necessarily get the device address(da) but the physical address(pa). This helper performs similar translation than the rproc_da_to_va() but based on a physical address.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@foss.st.com --- updates vs previous version: - remove pa_to_va in the rproc_ops structure as not used - simplify the rproc_pa_to_va() documentation header - fix typos --- drivers/remoteproc/remoteproc_core.c | 46 ++++++++++++++++++++++++++++ include/linux/remoteproc.h | 1 + 2 files changed, 47 insertions(+)
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index f276956f2c5c..ace11ea17097 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -230,6 +230,52 @@ void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) } EXPORT_SYMBOL(rproc_da_to_va);
+/** + * rproc_pa_to_va() - lookup the kernel virtual address for a physical address of a remoteproc + * memory + * + * @rproc: handle of a remote processor + * @pa: remoteproc physical address + * @len: length of the memory region @pa is pointing to + * @is_iomem: optional pointer filled in to indicate if @da is iomapped memory + * + * This function is a helper function similar to rproc_da_to_va() but it deals with physical + * addresses instead of device addresses. + * + * Return: a valid kernel address on success or NULL on failure + */ +void *rproc_pa_to_va(struct rproc *rproc, phys_addr_t pa, size_t len, bool *is_iomem) +{ + struct rproc_mem_entry *carveout; + void *ptr = NULL; + + list_for_each_entry(carveout, &rproc->carveouts, node) { + int offset = pa - carveout->dma; + + /* Verify that carveout is allocated */ + if (!carveout->va) + continue; + + /* try next carveout if da is too small */ + if (offset < 0) + continue; + + /* try next carveout if da is too large */ + if (offset + len > carveout->len) + continue; + + ptr = carveout->va + offset; + + if (is_iomem) + *is_iomem = carveout->is_iomem; + + break; + } + + return ptr; +} +EXPORT_SYMBOL(rproc_pa_to_va); + /** * rproc_find_carveout_by_name() - lookup the carveout region by a name * @rproc: handle of a remote processor diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index b4795698d8c2..8fd0d7f63c8e 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -690,6 +690,7 @@ int rproc_detach(struct rproc *rproc); int rproc_set_firmware(struct rproc *rproc, const char *fw_name); void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type); void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem); +void *rproc_pa_to_va(struct rproc *rproc, phys_addr_t pa, size_t len, bool *is_iomem);
/* from remoteproc_coredump.c */ void rproc_coredump_cleanup(struct rproc *rproc);
Add a remoteproc TEE (Trusted Execution Environment) driver that will be probed by the TEE bus. If the associated Trusted application is supported on secure part this driver offers a client interface to load a firmware by the secure part. This firmware could be authenticated by the secure trusted application.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@foss.st.com --- Updates vs previous version: - rename tee_remoteproc.* file to rmeoteproc_tee.*, - rename TEE_REMOTEPROC config to REMOTEPROC_TEE, - remove "stm32" in some variable declarations, - remove useless "remoteproc_internal.h" include, - fix tee_rproc_ctx devm_kzalloc. - rework module description --- drivers/remoteproc/Kconfig | 10 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/remoteproc_tee.c | 446 ++++++++++++++++++++++++++++ include/linux/remoteproc.h | 4 + include/linux/remoteproc_tee.h | 100 +++++++ 5 files changed, 561 insertions(+) create mode 100644 drivers/remoteproc/remoteproc_tee.c create mode 100644 include/linux/remoteproc_tee.h
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 48845dc8fa85..278f197acb90 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -365,6 +365,16 @@ config XLNX_R5_REMOTEPROC
It's safe to say N if not interested in using RPU r5f cores.
+ +config REMOTEPROC_TEE + tristate "Remoteproc support by a TEE application" + depends on OPTEE + help + Support a remote processor with a TEE application. The Trusted + Execution Context is responsible for loading the trusted firmware + image and managing the remote processor's lifecycle. + This can be either built-in or a loadable module. + endif # REMOTEPROC
endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 91314a9b43ce..b4eb37177005 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_RCAR_REMOTEPROC) += rcar_rproc.o obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c new file mode 100644 index 000000000000..70cb67451767 --- /dev/null +++ b/drivers/remoteproc/remoteproc_tee.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) STMicroelectronics 2024 + * Author: Arnaud Pouliquen arnaud.pouliquen@foss.st.com + */ + +#include <linux/firmware.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/remoteproc.h> +#include <linux/remoteproc_tee.h> +#include <linux/slab.h> +#include <linux/tee_drv.h> + +#define MAX_TEE_PARAM_ARRY_MEMBER 4 + +/* + * Authentication of the firmware and load in the remote processor memory + * + * [in] params[0].value.a: unique 32bit identifier of the remote processor + * [in] params[1].memref: buffer containing the image of the buffer + */ +#define TA_RPROC_FW_CMD_LOAD_FW 1 + +/* + * Start the remote processor + * + * [in] params[0].value.a: unique 32bit identifier of the remote processor + */ +#define TA_RPROC_FW_CMD_START_FW 2 + +/* + * Stop the remote processor + * + * [in] params[0].value.a: unique 32bit identifier of the remote processor + */ +#define TA_RPROC_FW_CMD_STOP_FW 3 + +/* + * Return the address of the resource table, or 0 if not found + * No check is done to verify that the address returned is accessible by + * the non secure context. If the resource table is loaded in a protected + * memory the access by the non secure context will lead to a data abort. + * + * [in] params[0].value.a: unique 32bit identifier of the remote processor + * [out] params[1].value.a: 32bit LSB resource table memory address + * [out] params[1].value.b: 32bit MSB resource table memory address + * [out] params[2].value.a: 32bit LSB resource table memory size + * [out] params[2].value.b: 32bit MSB resource table memory size + */ +#define TA_RPROC_FW_CMD_GET_RSC_TABLE 4 + +/* + * Return the address of the core dump + * + * [in] params[0].value.a: unique 32bit identifier of the remote processor + * [out] params[1].memref: address of the core dump image if exist, + * else return Null + */ +#define TA_RPROC_FW_CMD_GET_COREDUMP 5 + +struct tee_rproc_context { + struct list_head sessions; + struct tee_context *tee_ctx; + struct device *dev; +}; + +static struct tee_rproc_context *tee_rproc_ctx; + +static void tee_rproc_prepare_args(struct tee_rproc *trproc, int cmd, + struct tee_ioctl_invoke_arg *arg, + struct tee_param *param, + unsigned int num_params) +{ + memset(arg, 0, sizeof(*arg)); + memset(param, 0, MAX_TEE_PARAM_ARRY_MEMBER * sizeof(*param)); + + arg->func = cmd; + arg->session = trproc->session_id; + arg->num_params = num_params + 1; + + param[0] = (struct tee_param) { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = trproc->rproc_id, + }; +} + +int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) +{ + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER]; + struct tee_rproc *trproc = rproc->tee_interface; + struct tee_ioctl_invoke_arg arg; + struct tee_shm *fw_shm; + int ret; + + if (!trproc) + return -EINVAL; + + fw_shm = tee_shm_register_kernel_buf(tee_rproc_ctx->tee_ctx, (void *)fw->data, fw->size); + if (IS_ERR(fw_shm)) + return PTR_ERR(fw_shm); + + tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_LOAD_FW, &arg, param, 1); + + /* Provide the address of the firmware image */ + param[1] = (struct tee_param) { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT, + .u.memref = { + .shm = fw_shm, + .size = fw->size, + .shm_offs = 0, + }, + }; + + ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param); + if (ret < 0 || arg.ret != 0) { + dev_err(tee_rproc_ctx->dev, + "TA_RPROC_FW_CMD_LOAD_FW invoke failed TEE err: %x, ret:%x\n", + arg.ret, ret); + if (!ret) + ret = -EIO; + } + + tee_shm_free(fw_shm); + + return ret; +} +EXPORT_SYMBOL_GPL(tee_rproc_load_fw); + +static int tee_rproc_get_loaded_rsc_table(struct rproc *rproc, phys_addr_t *rsc_pa, + size_t *table_sz) +{ + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER]; + struct tee_rproc *trproc = rproc->tee_interface; + struct tee_ioctl_invoke_arg arg; + int ret; + + tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_GET_RSC_TABLE, &arg, param, 2); + + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; + param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; + + ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param); + if (ret < 0 || arg.ret != 0) { + dev_err(tee_rproc_ctx->dev, + "TA_RPROC_FW_CMD_GET_RSC_TABLE invoke failed TEE err: %x, ret:%x\n", + arg.ret, ret); + return -EIO; + } + + *table_sz = param[2].u.value.a; + + if (*table_sz) + *rsc_pa = param[1].u.value.a; + else + *rsc_pa = 0; + + return 0; +} + +int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{ + phys_addr_t rsc_table; + void __iomem *rsc_va; + size_t table_sz; + int ret; + + ret = tee_rproc_load_fw(rproc, fw); + if (ret) + return ret; + + ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz); + if (ret) + return ret; + + /* + * We assume here that the memory mapping is the same between the TEE and Linux kernel + * contexts. Else a new TEE remoteproc service could be needed to get a copy of the + * resource table + */ + rsc_va = ioremap_wc(rsc_table, table_sz); + if (IS_ERR_OR_NULL(rsc_va)) { + dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %pa+%zx\n", + &rsc_table, table_sz); + return -ENOMEM; + } + + /* + * Create a copy of the resource table to have the same behavior as the elf loader. + * This cached table will be used after the remoteproc stops to free resources, and for + * crash recovery to reapply the settings. + */ + rproc->cached_table = kmemdup((__force void *)rsc_va, table_sz, GFP_KERNEL); + if (!rproc->cached_table) { + ret = -ENOMEM; + goto out; + } + + rproc->table_ptr = rproc->cached_table; + rproc->table_sz = table_sz; + +out: + iounmap(rsc_va); + return ret; +} +EXPORT_SYMBOL_GPL(tee_rproc_parse_fw); + +struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc, + const struct firmware *fw) +{ + struct tee_rproc *trproc = rproc->tee_interface; + phys_addr_t rsc_table; + size_t table_sz; + int ret; + + if (!trproc) + return ERR_PTR(-EINVAL); + + ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz); + if (ret) + return ERR_PTR(ret); + + rproc->table_sz = table_sz; + if (!table_sz) + return NULL; + + /* + * At this step the memory area that contains the resource table should have be declared + * in the remote proc platform driver and allocated by rproc_alloc_registered_carveouts(). + */ + return (struct resource_table *)rproc_pa_to_va(rproc, rsc_table, table_sz, NULL); +} +EXPORT_SYMBOL_GPL(tee_rproc_find_loaded_rsc_table); + +int tee_rproc_start(struct rproc *rproc) +{ + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER]; + struct tee_rproc *trproc = rproc->tee_interface; + struct tee_ioctl_invoke_arg arg; + int ret = 0; + + if (!trproc) + return -EINVAL; + + tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_START_FW, &arg, param, 0); + + ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param); + if (ret < 0 || arg.ret != 0) { + dev_err(tee_rproc_ctx->dev, + "TA_RPROC_FW_CMD_START_FW invoke failed TEE err: %x, ret:%x\n", + arg.ret, ret); + if (!ret) + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(tee_rproc_start); + +int tee_rproc_stop(struct rproc *rproc) +{ + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER]; + struct tee_rproc *trproc = rproc->tee_interface; + struct tee_ioctl_invoke_arg arg; + int ret; + + if (!trproc) + return -EINVAL; + + tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_STOP_FW, &arg, param, 0); + + ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param); + if (ret < 0 || arg.ret != 0) { + dev_err(tee_rproc_ctx->dev, + "TA_RPROC_FW_CMD_STOP_FW invoke failed TEE err: %x, ret:%x\n", + arg.ret, ret); + if (!ret) + ret = -EIO; + } + + return ret; +} +EXPORT_SYMBOL_GPL(tee_rproc_stop); + +static const struct tee_client_device_id tee_rproc_id_table[] = { + {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)}, + {} +}; + +struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id) +{ + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER]; + struct tee_ioctl_open_session_arg sess_arg; + struct tee_client_device *tee_device; + struct tee_rproc *trproc, *p_err; + int ret; + + /* + * Test if the device has been probed by the TEE bus. In case of failure, we ignore the + * reason. The bus could be not yet probed or the service not available in the secure + * firmware.The assumption in such a case is that the TEE remoteproc is not probed. + */ + if (!tee_rproc_ctx) + return ERR_PTR(-EPROBE_DEFER); + + /* Prevent tee rproc module from being removed */ + if (!try_module_get(THIS_MODULE)) { + dev_err(tee_rproc_ctx->dev, "can't get owner\n"); + return ERR_PTR(-ENODEV); + } + + trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL); + if (!trproc) { + p_err = ERR_PTR(-ENOMEM); + goto module_put; + } + tee_device = to_tee_client_device(tee_rproc_ctx->dev); + memset(&sess_arg, 0, sizeof(sess_arg)); + + memcpy(sess_arg.uuid, tee_device->id.uuid.b, TEE_IOCTL_UUID_LEN); + + sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; + sess_arg.num_params = 1; + + param[0] = (struct tee_param) { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = rproc_id, + }; + + ret = tee_client_open_session(tee_rproc_ctx->tee_ctx, &sess_arg, param); + if (ret < 0 || sess_arg.ret != 0) { + dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret); + p_err = ERR_PTR(-EINVAL); + goto module_put; + } + + trproc->parent = dev; + trproc->rproc_id = rproc_id; + trproc->session_id = sess_arg.session; + + trproc->rproc = rproc; + rproc->tee_interface = trproc; + + list_add_tail(&trproc->node, &tee_rproc_ctx->sessions); + + return trproc; + +module_put: + module_put(THIS_MODULE); + return p_err; +} +EXPORT_SYMBOL_GPL(tee_rproc_register); + +int tee_rproc_unregister(struct tee_rproc *trproc) +{ + struct rproc *rproc = trproc->rproc; + int ret; + + ret = tee_client_close_session(tee_rproc_ctx->tee_ctx, trproc->session_id); + if (ret < 0) + dev_err(trproc->parent, "tee_client_close_session failed, err: %x\n", ret); + + list_del(&trproc->node); + rproc->tee_interface = NULL; + + module_put(THIS_MODULE); + + return ret; +} +EXPORT_SYMBOL_GPL(tee_rproc_unregister); + +static int tee_rproc_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{ + /* Today we support only the OP-TEE, could be extend to other tees */ + return (ver->impl_id == TEE_IMPL_ID_OPTEE); +} + +static int tee_rproc_probe(struct device *dev) +{ + struct tee_context *tee_ctx; + int ret; + + /* Open context with TEE driver */ + tee_ctx = tee_client_open_context(NULL, tee_rproc_ctx_match, NULL, NULL); + if (IS_ERR(tee_ctx)) + return PTR_ERR(tee_ctx); + + tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_rproc_ctx), GFP_KERNEL); + if (!tee_rproc_ctx) { + ret = -ENOMEM; + goto err; + } + + tee_rproc_ctx->dev = dev; + tee_rproc_ctx->tee_ctx = tee_ctx; + INIT_LIST_HEAD(&tee_rproc_ctx->sessions); + + return 0; +err: + tee_client_close_context(tee_ctx); + + return ret; +} + +static int tee_rproc_remove(struct device *dev) +{ + struct tee_rproc *entry, *tmp; + + list_for_each_entry_safe(entry, tmp, &tee_rproc_ctx->sessions, node) { + tee_client_close_session(tee_rproc_ctx->tee_ctx, entry->session_id); + list_del(&entry->node); + kfree(entry); + } + + tee_client_close_context(tee_rproc_ctx->tee_ctx); + + return 0; +} + +MODULE_DEVICE_TABLE(tee, tee_rproc_id_table); + +static struct tee_client_driver tee_rproc_fw_driver = { + .id_table = tee_rproc_id_table, + .driver = { + .name = KBUILD_MODNAME, + .bus = &tee_bus_type, + .probe = tee_rproc_probe, + .remove = tee_rproc_remove, + }, +}; + +static int __init tee_rproc_fw_mod_init(void) +{ + return driver_register(&tee_rproc_fw_driver.driver); +} + +static void __exit tee_rproc_fw_mod_exit(void) +{ + driver_unregister(&tee_rproc_fw_driver.driver); +} + +module_init(tee_rproc_fw_mod_init); +module_exit(tee_rproc_fw_mod_exit); + +MODULE_DESCRIPTION(" remote processor support with a TEE application"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 8fd0d7f63c8e..fbe1d6793709 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -503,6 +503,8 @@ enum rproc_features { RPROC_MAX_FEATURES, };
+struct tee_rproc; + /** * struct rproc - represents a physical remote processor device * @node: list node of this rproc object @@ -545,6 +547,7 @@ enum rproc_features { * @cdev: character device of the rproc * @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release * @features: indicate remoteproc features + * @tee_interface: pointer to the remoteproc tee context */ struct rproc { struct list_head node; @@ -586,6 +589,7 @@ struct rproc { struct cdev cdev; bool cdev_put_on_release; DECLARE_BITMAP(features, RPROC_MAX_FEATURES); + struct tee_rproc *tee_interface; };
/** diff --git a/include/linux/remoteproc_tee.h b/include/linux/remoteproc_tee.h new file mode 100644 index 000000000000..ccf34a218d54 --- /dev/null +++ b/include/linux/remoteproc_tee.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright(c) 2024 STMicroelectronics + */ + +#ifndef REMOTEPROC_TEE_H +#define REMOTEPROC_TEE_H + +#include <linux/tee_drv.h> +#include <linux/firmware.h> +#include <linux/remoteproc.h> + +struct rproc; + +/** + * struct tee_rproc - TEE remoteproc structure + * @node: Reference in list + * @rproc: Remoteproc reference + * @parent: Parent device + * @rproc_id: Identifier of the target firmware + * @session_id: TEE session identifier + */ +struct tee_rproc { + struct list_head node; + struct rproc *rproc; + struct device *parent; + u32 rproc_id; + u32 session_id; +}; + +#if IS_REACHABLE(CONFIG_REMOTEPROC_TEE) + +struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc, + unsigned int rproc_id); +int tee_rproc_unregister(struct tee_rproc *trproc); +int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw); +int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw); +struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc, + const struct firmware *fw); +int tee_rproc_start(struct rproc *rproc); +int tee_rproc_stop(struct rproc *rproc); + +#else + +static inline struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc, + unsigned int rproc_id) +{ + return ERR_PTR(-ENODEV); +} + +static int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{ + /* This shouldn't be possible */ + WARN_ON(1); + + return 0; +} + +static inline int tee_rproc_unregister(struct tee_rproc *trproc) +{ + /* This shouldn't be possible */ + WARN_ON(1); + + return 0; +} + +static inline int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) +{ + /* This shouldn't be possible */ + WARN_ON(1); + + return 0; +} + +static inline int tee_rproc_start(struct rproc *rproc) +{ + /* This shouldn't be possible */ + WARN_ON(1); + + return 0; +} + +static inline int tee_rproc_stop(struct rproc *rproc) +{ + /* This shouldn't be possible */ + WARN_ON(1); + + return 0; +} + +static inline struct resource_table * +tee_rproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw) +{ + /* This shouldn't be possible */ + WARN_ON(1); + + return NULL; +} +#endif /* CONFIG_REMOTEPROC_TEE */ +#endif /* REMOTEPROC_TEE_H */
Good morning,
On Fri, Jun 21, 2024 at 04:37:56PM +0200, Arnaud Pouliquen wrote:
Add a remoteproc TEE (Trusted Execution Environment) driver that will be probed by the TEE bus. If the associated Trusted application is supported on secure part this driver offers a client interface to load a firmware by the secure part. This firmware could be authenticated by the secure trusted application.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@foss.st.com
Updates vs previous version:
- rename tee_remoteproc.* file to rmeoteproc_tee.*,
- rename TEE_REMOTEPROC config to REMOTEPROC_TEE,
- remove "stm32" in some variable declarations,
- remove useless "remoteproc_internal.h" include,
- fix tee_rproc_ctx devm_kzalloc.
- rework module description
drivers/remoteproc/Kconfig | 10 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/remoteproc_tee.c | 446 ++++++++++++++++++++++++++++ include/linux/remoteproc.h | 4 + include/linux/remoteproc_tee.h | 100 +++++++ 5 files changed, 561 insertions(+) create mode 100644 drivers/remoteproc/remoteproc_tee.c create mode 100644 include/linux/remoteproc_tee.h
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 48845dc8fa85..278f197acb90 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -365,6 +365,16 @@ config XLNX_R5_REMOTEPROC It's safe to say N if not interested in using RPU r5f cores.
+config REMOTEPROC_TEE
- tristate "Remoteproc support by a TEE application"
- depends on OPTEE
- help
Support a remote processor with a TEE application. The Trusted
Execution Context is responsible for loading the trusted firmware
image and managing the remote processor's lifecycle.
This can be either built-in or a loadable module.
endif # REMOTEPROC endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 91314a9b43ce..b4eb37177005 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_RCAR_REMOTEPROC) += rcar_rproc.o obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c new file mode 100644 index 000000000000..70cb67451767 --- /dev/null +++ b/drivers/remoteproc/remoteproc_tee.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (C) STMicroelectronics 2024
- Author: Arnaud Pouliquen arnaud.pouliquen@foss.st.com
- */
+#include <linux/firmware.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/remoteproc.h> +#include <linux/remoteproc_tee.h> +#include <linux/slab.h> +#include <linux/tee_drv.h>
+#define MAX_TEE_PARAM_ARRY_MEMBER 4
+/*
- Authentication of the firmware and load in the remote processor memory
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [in] params[1].memref: buffer containing the image of the buffer
- */
+#define TA_RPROC_FW_CMD_LOAD_FW 1
+/*
- Start the remote processor
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- */
+#define TA_RPROC_FW_CMD_START_FW 2
+/*
- Stop the remote processor
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- */
+#define TA_RPROC_FW_CMD_STOP_FW 3
+/*
- Return the address of the resource table, or 0 if not found
- No check is done to verify that the address returned is accessible by
- the non secure context. If the resource table is loaded in a protected
- memory the access by the non secure context will lead to a data abort.
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [out] params[1].value.a: 32bit LSB resource table memory address
- [out] params[1].value.b: 32bit MSB resource table memory address
- [out] params[2].value.a: 32bit LSB resource table memory size
- [out] params[2].value.b: 32bit MSB resource table memory size
- */
+#define TA_RPROC_FW_CMD_GET_RSC_TABLE 4
+/*
- Return the address of the core dump
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [out] params[1].memref: address of the core dump image if exist,
else return Null
- */
+#define TA_RPROC_FW_CMD_GET_COREDUMP 5
+struct tee_rproc_context {
- struct list_head sessions;
- struct tee_context *tee_ctx;
- struct device *dev;
+};
+static struct tee_rproc_context *tee_rproc_ctx;
+static void tee_rproc_prepare_args(struct tee_rproc *trproc, int cmd,
struct tee_ioctl_invoke_arg *arg,
struct tee_param *param,
unsigned int num_params)
+{
- memset(arg, 0, sizeof(*arg));
- memset(param, 0, MAX_TEE_PARAM_ARRY_MEMBER * sizeof(*param));
- arg->func = cmd;
- arg->session = trproc->session_id;
- arg->num_params = num_params + 1;
- param[0] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
.u.value.a = trproc->rproc_id,
- };
+}
+int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- struct tee_shm *fw_shm;
- int ret;
- if (!trproc)
return -EINVAL;
- fw_shm = tee_shm_register_kernel_buf(tee_rproc_ctx->tee_ctx, (void *)fw->data, fw->size);
- if (IS_ERR(fw_shm))
return PTR_ERR(fw_shm);
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_LOAD_FW, &arg, param, 1);
- /* Provide the address of the firmware image */
- param[1] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
.u.memref = {
.shm = fw_shm,
.size = fw->size,
.shm_offs = 0,
},
- };
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_LOAD_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
ret = -EIO;
- }
- tee_shm_free(fw_shm);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_load_fw);
+static int tee_rproc_get_loaded_rsc_table(struct rproc *rproc, phys_addr_t *rsc_pa,
size_t *table_sz)
+{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_GET_RSC_TABLE, &arg, param, 2);
- param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
- param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_GET_RSC_TABLE invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
return -EIO;
- }
- *table_sz = param[2].u.value.a;
- if (*table_sz)
*rsc_pa = param[1].u.value.a;
- else
*rsc_pa = 0;
- return 0;
+}
+int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{
- phys_addr_t rsc_table;
- void __iomem *rsc_va;
- size_t table_sz;
- int ret;
- ret = tee_rproc_load_fw(rproc, fw);
- if (ret)
return ret;
- ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
- if (ret)
return ret;
Do we need an API to unload the firware from memory too? If anything fails we still have firmware loaded into memory.
More comments to come later.
Mathieu
- /*
* We assume here that the memory mapping is the same between the TEE and Linux kernel
* contexts. Else a new TEE remoteproc service could be needed to get a copy of the
* resource table
*/
- rsc_va = ioremap_wc(rsc_table, table_sz);
- if (IS_ERR_OR_NULL(rsc_va)) {
dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %pa+%zx\n",
&rsc_table, table_sz);
return -ENOMEM;
- }
- /*
* Create a copy of the resource table to have the same behavior as the elf loader.
* This cached table will be used after the remoteproc stops to free resources, and for
* crash recovery to reapply the settings.
*/
- rproc->cached_table = kmemdup((__force void *)rsc_va, table_sz, GFP_KERNEL);
- if (!rproc->cached_table) {
ret = -ENOMEM;
goto out;
- }
- rproc->table_ptr = rproc->cached_table;
- rproc->table_sz = table_sz;
+out:
- iounmap(rsc_va);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_parse_fw);
+struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc,
const struct firmware *fw)
+{
- struct tee_rproc *trproc = rproc->tee_interface;
- phys_addr_t rsc_table;
- size_t table_sz;
- int ret;
- if (!trproc)
return ERR_PTR(-EINVAL);
- ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
- if (ret)
return ERR_PTR(ret);
- rproc->table_sz = table_sz;
- if (!table_sz)
return NULL;
- /*
* At this step the memory area that contains the resource table should have be declared
* in the remote proc platform driver and allocated by rproc_alloc_registered_carveouts().
*/
- return (struct resource_table *)rproc_pa_to_va(rproc, rsc_table, table_sz, NULL);
+} +EXPORT_SYMBOL_GPL(tee_rproc_find_loaded_rsc_table);
+int tee_rproc_start(struct rproc *rproc) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret = 0;
- if (!trproc)
return -EINVAL;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_START_FW, &arg, param, 0);
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_START_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
return -EIO;
- }
- return 0;
+} +EXPORT_SYMBOL_GPL(tee_rproc_start);
+int tee_rproc_stop(struct rproc *rproc) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret;
- if (!trproc)
return -EINVAL;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_STOP_FW, &arg, param, 0);
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_STOP_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
ret = -EIO;
- }
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_stop);
+static const struct tee_client_device_id tee_rproc_id_table[] = {
- {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)},
- {}
+};
+struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_ioctl_open_session_arg sess_arg;
- struct tee_client_device *tee_device;
- struct tee_rproc *trproc, *p_err;
- int ret;
- /*
* Test if the device has been probed by the TEE bus. In case of failure, we ignore the
* reason. The bus could be not yet probed or the service not available in the secure
* firmware.The assumption in such a case is that the TEE remoteproc is not probed.
*/
- if (!tee_rproc_ctx)
return ERR_PTR(-EPROBE_DEFER);
- /* Prevent tee rproc module from being removed */
- if (!try_module_get(THIS_MODULE)) {
dev_err(tee_rproc_ctx->dev, "can't get owner\n");
return ERR_PTR(-ENODEV);
- }
- trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
- if (!trproc) {
p_err = ERR_PTR(-ENOMEM);
goto module_put;
- }
- tee_device = to_tee_client_device(tee_rproc_ctx->dev);
- memset(&sess_arg, 0, sizeof(sess_arg));
- memcpy(sess_arg.uuid, tee_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
- sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
- sess_arg.num_params = 1;
- param[0] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
.u.value.a = rproc_id,
- };
- ret = tee_client_open_session(tee_rproc_ctx->tee_ctx, &sess_arg, param);
- if (ret < 0 || sess_arg.ret != 0) {
dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret);
p_err = ERR_PTR(-EINVAL);
goto module_put;
- }
- trproc->parent = dev;
- trproc->rproc_id = rproc_id;
- trproc->session_id = sess_arg.session;
- trproc->rproc = rproc;
- rproc->tee_interface = trproc;
- list_add_tail(&trproc->node, &tee_rproc_ctx->sessions);
- return trproc;
+module_put:
- module_put(THIS_MODULE);
- return p_err;
+} +EXPORT_SYMBOL_GPL(tee_rproc_register);
+int tee_rproc_unregister(struct tee_rproc *trproc) +{
- struct rproc *rproc = trproc->rproc;
- int ret;
- ret = tee_client_close_session(tee_rproc_ctx->tee_ctx, trproc->session_id);
- if (ret < 0)
dev_err(trproc->parent, "tee_client_close_session failed, err: %x\n", ret);
- list_del(&trproc->node);
- rproc->tee_interface = NULL;
- module_put(THIS_MODULE);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_unregister);
+static int tee_rproc_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{
- /* Today we support only the OP-TEE, could be extend to other tees */
- return (ver->impl_id == TEE_IMPL_ID_OPTEE);
+}
+static int tee_rproc_probe(struct device *dev) +{
- struct tee_context *tee_ctx;
- int ret;
- /* Open context with TEE driver */
- tee_ctx = tee_client_open_context(NULL, tee_rproc_ctx_match, NULL, NULL);
- if (IS_ERR(tee_ctx))
return PTR_ERR(tee_ctx);
- tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_rproc_ctx), GFP_KERNEL);
- if (!tee_rproc_ctx) {
ret = -ENOMEM;
goto err;
- }
- tee_rproc_ctx->dev = dev;
- tee_rproc_ctx->tee_ctx = tee_ctx;
- INIT_LIST_HEAD(&tee_rproc_ctx->sessions);
- return 0;
+err:
- tee_client_close_context(tee_ctx);
- return ret;
+}
+static int tee_rproc_remove(struct device *dev) +{
- struct tee_rproc *entry, *tmp;
- list_for_each_entry_safe(entry, tmp, &tee_rproc_ctx->sessions, node) {
tee_client_close_session(tee_rproc_ctx->tee_ctx, entry->session_id);
list_del(&entry->node);
kfree(entry);
- }
- tee_client_close_context(tee_rproc_ctx->tee_ctx);
- return 0;
+}
+MODULE_DEVICE_TABLE(tee, tee_rproc_id_table);
+static struct tee_client_driver tee_rproc_fw_driver = {
- .id_table = tee_rproc_id_table,
- .driver = {
.name = KBUILD_MODNAME,
.bus = &tee_bus_type,
.probe = tee_rproc_probe,
.remove = tee_rproc_remove,
- },
+};
+static int __init tee_rproc_fw_mod_init(void) +{
- return driver_register(&tee_rproc_fw_driver.driver);
+}
+static void __exit tee_rproc_fw_mod_exit(void) +{
- driver_unregister(&tee_rproc_fw_driver.driver);
+}
+module_init(tee_rproc_fw_mod_init); +module_exit(tee_rproc_fw_mod_exit);
+MODULE_DESCRIPTION(" remote processor support with a TEE application"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 8fd0d7f63c8e..fbe1d6793709 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -503,6 +503,8 @@ enum rproc_features { RPROC_MAX_FEATURES, }; +struct tee_rproc;
/**
- struct rproc - represents a physical remote processor device
- @node: list node of this rproc object
@@ -545,6 +547,7 @@ enum rproc_features {
- @cdev: character device of the rproc
- @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release
- @features: indicate remoteproc features
*/
- @tee_interface: pointer to the remoteproc tee context
struct rproc { struct list_head node; @@ -586,6 +589,7 @@ struct rproc { struct cdev cdev; bool cdev_put_on_release; DECLARE_BITMAP(features, RPROC_MAX_FEATURES);
- struct tee_rproc *tee_interface;
}; /** diff --git a/include/linux/remoteproc_tee.h b/include/linux/remoteproc_tee.h new file mode 100644 index 000000000000..ccf34a218d54 --- /dev/null +++ b/include/linux/remoteproc_tee.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/*
- Copyright(c) 2024 STMicroelectronics
- */
+#ifndef REMOTEPROC_TEE_H +#define REMOTEPROC_TEE_H
+#include <linux/tee_drv.h> +#include <linux/firmware.h> +#include <linux/remoteproc.h>
+struct rproc;
+/**
- struct tee_rproc - TEE remoteproc structure
- @node: Reference in list
- @rproc: Remoteproc reference
- @parent: Parent device
- @rproc_id: Identifier of the target firmware
- @session_id: TEE session identifier
- */
+struct tee_rproc {
- struct list_head node;
- struct rproc *rproc;
- struct device *parent;
- u32 rproc_id;
- u32 session_id;
+};
+#if IS_REACHABLE(CONFIG_REMOTEPROC_TEE)
+struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc,
unsigned int rproc_id);
+int tee_rproc_unregister(struct tee_rproc *trproc); +int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw); +int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw); +struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc,
const struct firmware *fw);
+int tee_rproc_start(struct rproc *rproc); +int tee_rproc_stop(struct rproc *rproc);
+#else
+static inline struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc,
unsigned int rproc_id)
+{
- return ERR_PTR(-ENODEV);
+}
+static int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_unregister(struct tee_rproc *trproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_start(struct rproc *rproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_stop(struct rproc *rproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline struct resource_table * +tee_rproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return NULL;
+} +#endif /* CONFIG_REMOTEPROC_TEE */
+#endif /* REMOTEPROC_TEE_H */
2.25.1
Hello Mathieu
On 7/2/24 18:44, Mathieu Poirier wrote:
Good morning,
On Fri, Jun 21, 2024 at 04:37:56PM +0200, Arnaud Pouliquen wrote:
Add a remoteproc TEE (Trusted Execution Environment) driver that will be probed by the TEE bus. If the associated Trusted application is supported on secure part this driver offers a client interface to load a firmware by the secure part. This firmware could be authenticated by the secure trusted application.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@foss.st.com
Updates vs previous version:
- rename tee_remoteproc.* file to rmeoteproc_tee.*,
- rename TEE_REMOTEPROC config to REMOTEPROC_TEE,
- remove "stm32" in some variable declarations,
- remove useless "remoteproc_internal.h" include,
- fix tee_rproc_ctx devm_kzalloc.
- rework module description
drivers/remoteproc/Kconfig | 10 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/remoteproc_tee.c | 446 ++++++++++++++++++++++++++++ include/linux/remoteproc.h | 4 + include/linux/remoteproc_tee.h | 100 +++++++ 5 files changed, 561 insertions(+) create mode 100644 drivers/remoteproc/remoteproc_tee.c create mode 100644 include/linux/remoteproc_tee.h
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 48845dc8fa85..278f197acb90 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -365,6 +365,16 @@ config XLNX_R5_REMOTEPROC It's safe to say N if not interested in using RPU r5f cores.
+config REMOTEPROC_TEE
- tristate "Remoteproc support by a TEE application"
- depends on OPTEE
- help
Support a remote processor with a TEE application. The Trusted
Execution Context is responsible for loading the trusted firmware
image and managing the remote processor's lifecycle.
This can be either built-in or a loadable module.
endif # REMOTEPROC endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 91314a9b43ce..b4eb37177005 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_RCAR_REMOTEPROC) += rcar_rproc.o obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c new file mode 100644 index 000000000000..70cb67451767 --- /dev/null +++ b/drivers/remoteproc/remoteproc_tee.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (C) STMicroelectronics 2024
- Author: Arnaud Pouliquen arnaud.pouliquen@foss.st.com
- */
+#include <linux/firmware.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/remoteproc.h> +#include <linux/remoteproc_tee.h> +#include <linux/slab.h> +#include <linux/tee_drv.h>
+#define MAX_TEE_PARAM_ARRY_MEMBER 4
+/*
- Authentication of the firmware and load in the remote processor memory
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [in] params[1].memref: buffer containing the image of the buffer
- */
+#define TA_RPROC_FW_CMD_LOAD_FW 1
+/*
- Start the remote processor
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- */
+#define TA_RPROC_FW_CMD_START_FW 2
+/*
- Stop the remote processor
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- */
+#define TA_RPROC_FW_CMD_STOP_FW 3
+/*
- Return the address of the resource table, or 0 if not found
- No check is done to verify that the address returned is accessible by
- the non secure context. If the resource table is loaded in a protected
- memory the access by the non secure context will lead to a data abort.
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [out] params[1].value.a: 32bit LSB resource table memory address
- [out] params[1].value.b: 32bit MSB resource table memory address
- [out] params[2].value.a: 32bit LSB resource table memory size
- [out] params[2].value.b: 32bit MSB resource table memory size
- */
+#define TA_RPROC_FW_CMD_GET_RSC_TABLE 4
+/*
- Return the address of the core dump
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [out] params[1].memref: address of the core dump image if exist,
else return Null
- */
+#define TA_RPROC_FW_CMD_GET_COREDUMP 5
+struct tee_rproc_context {
- struct list_head sessions;
- struct tee_context *tee_ctx;
- struct device *dev;
+};
+static struct tee_rproc_context *tee_rproc_ctx;
+static void tee_rproc_prepare_args(struct tee_rproc *trproc, int cmd,
struct tee_ioctl_invoke_arg *arg,
struct tee_param *param,
unsigned int num_params)
+{
- memset(arg, 0, sizeof(*arg));
- memset(param, 0, MAX_TEE_PARAM_ARRY_MEMBER * sizeof(*param));
- arg->func = cmd;
- arg->session = trproc->session_id;
- arg->num_params = num_params + 1;
- param[0] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
.u.value.a = trproc->rproc_id,
- };
+}
+int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- struct tee_shm *fw_shm;
- int ret;
- if (!trproc)
return -EINVAL;
- fw_shm = tee_shm_register_kernel_buf(tee_rproc_ctx->tee_ctx, (void *)fw->data, fw->size);
- if (IS_ERR(fw_shm))
return PTR_ERR(fw_shm);
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_LOAD_FW, &arg, param, 1);
- /* Provide the address of the firmware image */
- param[1] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
.u.memref = {
.shm = fw_shm,
.size = fw->size,
.shm_offs = 0,
},
- };
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_LOAD_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
ret = -EIO;
- }
- tee_shm_free(fw_shm);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_load_fw);
+static int tee_rproc_get_loaded_rsc_table(struct rproc *rproc, phys_addr_t *rsc_pa,
size_t *table_sz)
+{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_GET_RSC_TABLE, &arg, param, 2);
- param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
- param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_GET_RSC_TABLE invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
return -EIO;
- }
- *table_sz = param[2].u.value.a;
- if (*table_sz)
*rsc_pa = param[1].u.value.a;
- else
*rsc_pa = 0;
- return 0;
+}
+int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{
- phys_addr_t rsc_table;
- void __iomem *rsc_va;
- size_t table_sz;
- int ret;
- ret = tee_rproc_load_fw(rproc, fw);
- if (ret)
return ret;
- ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
- if (ret)
return ret;
Do we need an API to unload the firware from memory too? If anything fails we still have firmware loaded into memory.
For the time being, the clean-up of the memory is not implemented in OP-TEE. I plan to upstream this part in a second step, associated with a memory protection strategy. This should be directly implemented in the OP-TEE service to avoid reliance on Linux error management. Nonetheless, an API could be added to clean the memory if needed.
Thanks, Arnaud
More comments to come later.
Mathieu
- /*
* We assume here that the memory mapping is the same between the TEE and Linux kernel
* contexts. Else a new TEE remoteproc service could be needed to get a copy of the
* resource table
*/
- rsc_va = ioremap_wc(rsc_table, table_sz);
- if (IS_ERR_OR_NULL(rsc_va)) {
dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %pa+%zx\n",
&rsc_table, table_sz);
return -ENOMEM;
- }
- /*
* Create a copy of the resource table to have the same behavior as the elf loader.
* This cached table will be used after the remoteproc stops to free resources, and for
* crash recovery to reapply the settings.
*/
- rproc->cached_table = kmemdup((__force void *)rsc_va, table_sz, GFP_KERNEL);
- if (!rproc->cached_table) {
ret = -ENOMEM;
goto out;
- }
- rproc->table_ptr = rproc->cached_table;
- rproc->table_sz = table_sz;
+out:
- iounmap(rsc_va);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_parse_fw);
+struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc,
const struct firmware *fw)
+{
- struct tee_rproc *trproc = rproc->tee_interface;
- phys_addr_t rsc_table;
- size_t table_sz;
- int ret;
- if (!trproc)
return ERR_PTR(-EINVAL);
- ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
- if (ret)
return ERR_PTR(ret);
- rproc->table_sz = table_sz;
- if (!table_sz)
return NULL;
- /*
* At this step the memory area that contains the resource table should have be declared
* in the remote proc platform driver and allocated by rproc_alloc_registered_carveouts().
*/
- return (struct resource_table *)rproc_pa_to_va(rproc, rsc_table, table_sz, NULL);
+} +EXPORT_SYMBOL_GPL(tee_rproc_find_loaded_rsc_table);
+int tee_rproc_start(struct rproc *rproc) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret = 0;
- if (!trproc)
return -EINVAL;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_START_FW, &arg, param, 0);
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_START_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
return -EIO;
- }
- return 0;
+} +EXPORT_SYMBOL_GPL(tee_rproc_start);
+int tee_rproc_stop(struct rproc *rproc) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret;
- if (!trproc)
return -EINVAL;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_STOP_FW, &arg, param, 0);
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_STOP_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
ret = -EIO;
- }
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_stop);
+static const struct tee_client_device_id tee_rproc_id_table[] = {
- {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)},
- {}
+};
+struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_ioctl_open_session_arg sess_arg;
- struct tee_client_device *tee_device;
- struct tee_rproc *trproc, *p_err;
- int ret;
- /*
* Test if the device has been probed by the TEE bus. In case of failure, we ignore the
* reason. The bus could be not yet probed or the service not available in the secure
* firmware.The assumption in such a case is that the TEE remoteproc is not probed.
*/
- if (!tee_rproc_ctx)
return ERR_PTR(-EPROBE_DEFER);
- /* Prevent tee rproc module from being removed */
- if (!try_module_get(THIS_MODULE)) {
dev_err(tee_rproc_ctx->dev, "can't get owner\n");
return ERR_PTR(-ENODEV);
- }
- trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
- if (!trproc) {
p_err = ERR_PTR(-ENOMEM);
goto module_put;
- }
- tee_device = to_tee_client_device(tee_rproc_ctx->dev);
- memset(&sess_arg, 0, sizeof(sess_arg));
- memcpy(sess_arg.uuid, tee_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
- sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
- sess_arg.num_params = 1;
- param[0] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
.u.value.a = rproc_id,
- };
- ret = tee_client_open_session(tee_rproc_ctx->tee_ctx, &sess_arg, param);
- if (ret < 0 || sess_arg.ret != 0) {
dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret);
p_err = ERR_PTR(-EINVAL);
goto module_put;
- }
- trproc->parent = dev;
- trproc->rproc_id = rproc_id;
- trproc->session_id = sess_arg.session;
- trproc->rproc = rproc;
- rproc->tee_interface = trproc;
- list_add_tail(&trproc->node, &tee_rproc_ctx->sessions);
- return trproc;
+module_put:
- module_put(THIS_MODULE);
- return p_err;
+} +EXPORT_SYMBOL_GPL(tee_rproc_register);
+int tee_rproc_unregister(struct tee_rproc *trproc) +{
- struct rproc *rproc = trproc->rproc;
- int ret;
- ret = tee_client_close_session(tee_rproc_ctx->tee_ctx, trproc->session_id);
- if (ret < 0)
dev_err(trproc->parent, "tee_client_close_session failed, err: %x\n", ret);
- list_del(&trproc->node);
- rproc->tee_interface = NULL;
- module_put(THIS_MODULE);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_unregister);
+static int tee_rproc_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{
- /* Today we support only the OP-TEE, could be extend to other tees */
- return (ver->impl_id == TEE_IMPL_ID_OPTEE);
+}
+static int tee_rproc_probe(struct device *dev) +{
- struct tee_context *tee_ctx;
- int ret;
- /* Open context with TEE driver */
- tee_ctx = tee_client_open_context(NULL, tee_rproc_ctx_match, NULL, NULL);
- if (IS_ERR(tee_ctx))
return PTR_ERR(tee_ctx);
- tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_rproc_ctx), GFP_KERNEL);
- if (!tee_rproc_ctx) {
ret = -ENOMEM;
goto err;
- }
- tee_rproc_ctx->dev = dev;
- tee_rproc_ctx->tee_ctx = tee_ctx;
- INIT_LIST_HEAD(&tee_rproc_ctx->sessions);
- return 0;
+err:
- tee_client_close_context(tee_ctx);
- return ret;
+}
+static int tee_rproc_remove(struct device *dev) +{
- struct tee_rproc *entry, *tmp;
- list_for_each_entry_safe(entry, tmp, &tee_rproc_ctx->sessions, node) {
tee_client_close_session(tee_rproc_ctx->tee_ctx, entry->session_id);
list_del(&entry->node);
kfree(entry);
- }
- tee_client_close_context(tee_rproc_ctx->tee_ctx);
- return 0;
+}
+MODULE_DEVICE_TABLE(tee, tee_rproc_id_table);
+static struct tee_client_driver tee_rproc_fw_driver = {
- .id_table = tee_rproc_id_table,
- .driver = {
.name = KBUILD_MODNAME,
.bus = &tee_bus_type,
.probe = tee_rproc_probe,
.remove = tee_rproc_remove,
- },
+};
+static int __init tee_rproc_fw_mod_init(void) +{
- return driver_register(&tee_rproc_fw_driver.driver);
+}
+static void __exit tee_rproc_fw_mod_exit(void) +{
- driver_unregister(&tee_rproc_fw_driver.driver);
+}
+module_init(tee_rproc_fw_mod_init); +module_exit(tee_rproc_fw_mod_exit);
+MODULE_DESCRIPTION(" remote processor support with a TEE application"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 8fd0d7f63c8e..fbe1d6793709 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -503,6 +503,8 @@ enum rproc_features { RPROC_MAX_FEATURES, }; +struct tee_rproc;
/**
- struct rproc - represents a physical remote processor device
- @node: list node of this rproc object
@@ -545,6 +547,7 @@ enum rproc_features {
- @cdev: character device of the rproc
- @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release
- @features: indicate remoteproc features
*/
- @tee_interface: pointer to the remoteproc tee context
struct rproc { struct list_head node; @@ -586,6 +589,7 @@ struct rproc { struct cdev cdev; bool cdev_put_on_release; DECLARE_BITMAP(features, RPROC_MAX_FEATURES);
- struct tee_rproc *tee_interface;
}; /** diff --git a/include/linux/remoteproc_tee.h b/include/linux/remoteproc_tee.h new file mode 100644 index 000000000000..ccf34a218d54 --- /dev/null +++ b/include/linux/remoteproc_tee.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/*
- Copyright(c) 2024 STMicroelectronics
- */
+#ifndef REMOTEPROC_TEE_H +#define REMOTEPROC_TEE_H
+#include <linux/tee_drv.h> +#include <linux/firmware.h> +#include <linux/remoteproc.h>
+struct rproc;
+/**
- struct tee_rproc - TEE remoteproc structure
- @node: Reference in list
- @rproc: Remoteproc reference
- @parent: Parent device
- @rproc_id: Identifier of the target firmware
- @session_id: TEE session identifier
- */
+struct tee_rproc {
- struct list_head node;
- struct rproc *rproc;
- struct device *parent;
- u32 rproc_id;
- u32 session_id;
+};
+#if IS_REACHABLE(CONFIG_REMOTEPROC_TEE)
+struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc,
unsigned int rproc_id);
+int tee_rproc_unregister(struct tee_rproc *trproc); +int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw); +int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw); +struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc,
const struct firmware *fw);
+int tee_rproc_start(struct rproc *rproc); +int tee_rproc_stop(struct rproc *rproc);
+#else
+static inline struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc,
unsigned int rproc_id)
+{
- return ERR_PTR(-ENODEV);
+}
+static int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_unregister(struct tee_rproc *trproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_start(struct rproc *rproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_stop(struct rproc *rproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline struct resource_table * +tee_rproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return NULL;
+} +#endif /* CONFIG_REMOTEPROC_TEE */
+#endif /* REMOTEPROC_TEE_H */
2.25.1
On Wed, Jul 03, 2024 at 09:19:44AM +0200, Arnaud POULIQUEN wrote:
Hello Mathieu
On 7/2/24 18:44, Mathieu Poirier wrote:
Good morning,
On Fri, Jun 21, 2024 at 04:37:56PM +0200, Arnaud Pouliquen wrote:
Add a remoteproc TEE (Trusted Execution Environment) driver that will be probed by the TEE bus. If the associated Trusted application is supported on secure part this driver offers a client interface to load a firmware by the secure part. This firmware could be authenticated by the secure trusted application.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@foss.st.com
Updates vs previous version:
- rename tee_remoteproc.* file to rmeoteproc_tee.*,
- rename TEE_REMOTEPROC config to REMOTEPROC_TEE,
- remove "stm32" in some variable declarations,
- remove useless "remoteproc_internal.h" include,
- fix tee_rproc_ctx devm_kzalloc.
- rework module description
drivers/remoteproc/Kconfig | 10 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/remoteproc_tee.c | 446 ++++++++++++++++++++++++++++ include/linux/remoteproc.h | 4 + include/linux/remoteproc_tee.h | 100 +++++++ 5 files changed, 561 insertions(+) create mode 100644 drivers/remoteproc/remoteproc_tee.c create mode 100644 include/linux/remoteproc_tee.h
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 48845dc8fa85..278f197acb90 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -365,6 +365,16 @@ config XLNX_R5_REMOTEPROC It's safe to say N if not interested in using RPU r5f cores.
+config REMOTEPROC_TEE
- tristate "Remoteproc support by a TEE application"
- depends on OPTEE
- help
Support a remote processor with a TEE application. The Trusted
Execution Context is responsible for loading the trusted firmware
image and managing the remote processor's lifecycle.
This can be either built-in or a loadable module.
endif # REMOTEPROC endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 91314a9b43ce..b4eb37177005 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_RCAR_REMOTEPROC) += rcar_rproc.o obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c new file mode 100644 index 000000000000..70cb67451767 --- /dev/null +++ b/drivers/remoteproc/remoteproc_tee.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (C) STMicroelectronics 2024
- Author: Arnaud Pouliquen arnaud.pouliquen@foss.st.com
- */
+#include <linux/firmware.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/remoteproc.h> +#include <linux/remoteproc_tee.h> +#include <linux/slab.h> +#include <linux/tee_drv.h>
+#define MAX_TEE_PARAM_ARRY_MEMBER 4
+/*
- Authentication of the firmware and load in the remote processor memory
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [in] params[1].memref: buffer containing the image of the buffer
- */
+#define TA_RPROC_FW_CMD_LOAD_FW 1
+/*
- Start the remote processor
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- */
+#define TA_RPROC_FW_CMD_START_FW 2
+/*
- Stop the remote processor
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- */
+#define TA_RPROC_FW_CMD_STOP_FW 3
+/*
- Return the address of the resource table, or 0 if not found
- No check is done to verify that the address returned is accessible by
- the non secure context. If the resource table is loaded in a protected
- memory the access by the non secure context will lead to a data abort.
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [out] params[1].value.a: 32bit LSB resource table memory address
- [out] params[1].value.b: 32bit MSB resource table memory address
- [out] params[2].value.a: 32bit LSB resource table memory size
- [out] params[2].value.b: 32bit MSB resource table memory size
- */
+#define TA_RPROC_FW_CMD_GET_RSC_TABLE 4
+/*
- Return the address of the core dump
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [out] params[1].memref: address of the core dump image if exist,
else return Null
- */
+#define TA_RPROC_FW_CMD_GET_COREDUMP 5
+struct tee_rproc_context {
- struct list_head sessions;
- struct tee_context *tee_ctx;
- struct device *dev;
+};
+static struct tee_rproc_context *tee_rproc_ctx;
+static void tee_rproc_prepare_args(struct tee_rproc *trproc, int cmd,
struct tee_ioctl_invoke_arg *arg,
struct tee_param *param,
unsigned int num_params)
+{
- memset(arg, 0, sizeof(*arg));
- memset(param, 0, MAX_TEE_PARAM_ARRY_MEMBER * sizeof(*param));
- arg->func = cmd;
- arg->session = trproc->session_id;
- arg->num_params = num_params + 1;
- param[0] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
.u.value.a = trproc->rproc_id,
- };
+}
+int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- struct tee_shm *fw_shm;
- int ret;
- if (!trproc)
return -EINVAL;
- fw_shm = tee_shm_register_kernel_buf(tee_rproc_ctx->tee_ctx, (void *)fw->data, fw->size);
- if (IS_ERR(fw_shm))
return PTR_ERR(fw_shm);
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_LOAD_FW, &arg, param, 1);
- /* Provide the address of the firmware image */
- param[1] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
.u.memref = {
.shm = fw_shm,
.size = fw->size,
.shm_offs = 0,
},
- };
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_LOAD_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
ret = -EIO;
- }
- tee_shm_free(fw_shm);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_load_fw);
+static int tee_rproc_get_loaded_rsc_table(struct rproc *rproc, phys_addr_t *rsc_pa,
size_t *table_sz)
+{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_GET_RSC_TABLE, &arg, param, 2);
- param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
- param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_GET_RSC_TABLE invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
return -EIO;
- }
- *table_sz = param[2].u.value.a;
- if (*table_sz)
*rsc_pa = param[1].u.value.a;
- else
*rsc_pa = 0;
- return 0;
+}
+int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{
- phys_addr_t rsc_table;
- void __iomem *rsc_va;
- size_t table_sz;
- int ret;
- ret = tee_rproc_load_fw(rproc, fw);
- if (ret)
return ret;
- ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
- if (ret)
return ret;
Do we need an API to unload the firware from memory too? If anything fails we still have firmware loaded into memory.
For the time being, the clean-up of the memory is not implemented in OP-TEE.
In my opinion cleaning up the firmware image loaded to memory when something fails is important and should be part of this patchset. Looking at the error path in this function, it is clear something is missing.
I plan to upstream this part in a second step, associated with a memory protection strategy. This should be directly implemented in the OP-TEE service to avoid reliance on Linux error management. Nonetheless, an API could be added to clean the memory if needed.
Thanks, Arnaud
More comments to come later.
Mathieu
- /*
* We assume here that the memory mapping is the same between the TEE and Linux kernel
* contexts. Else a new TEE remoteproc service could be needed to get a copy of the
* resource table
*/
- rsc_va = ioremap_wc(rsc_table, table_sz);
- if (IS_ERR_OR_NULL(rsc_va)) {
dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %pa+%zx\n",
&rsc_table, table_sz);
return -ENOMEM;
- }
- /*
* Create a copy of the resource table to have the same behavior as the elf loader.
* This cached table will be used after the remoteproc stops to free resources, and for
* crash recovery to reapply the settings.
*/
- rproc->cached_table = kmemdup((__force void *)rsc_va, table_sz, GFP_KERNEL);
- if (!rproc->cached_table) {
ret = -ENOMEM;
goto out;
- }
- rproc->table_ptr = rproc->cached_table;
- rproc->table_sz = table_sz;
+out:
- iounmap(rsc_va);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_parse_fw);
+struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc,
const struct firmware *fw)
+{
- struct tee_rproc *trproc = rproc->tee_interface;
- phys_addr_t rsc_table;
- size_t table_sz;
- int ret;
- if (!trproc)
return ERR_PTR(-EINVAL);
- ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
- if (ret)
return ERR_PTR(ret);
- rproc->table_sz = table_sz;
- if (!table_sz)
return NULL;
- /*
* At this step the memory area that contains the resource table should have be declared
* in the remote proc platform driver and allocated by rproc_alloc_registered_carveouts().
*/
- return (struct resource_table *)rproc_pa_to_va(rproc, rsc_table, table_sz, NULL);
+} +EXPORT_SYMBOL_GPL(tee_rproc_find_loaded_rsc_table);
+int tee_rproc_start(struct rproc *rproc) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret = 0;
- if (!trproc)
return -EINVAL;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_START_FW, &arg, param, 0);
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_START_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
return -EIO;
- }
- return 0;
+} +EXPORT_SYMBOL_GPL(tee_rproc_start);
+int tee_rproc_stop(struct rproc *rproc) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret;
- if (!trproc)
return -EINVAL;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_STOP_FW, &arg, param, 0);
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_STOP_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
ret = -EIO;
- }
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_stop);
+static const struct tee_client_device_id tee_rproc_id_table[] = {
- {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)},
- {}
+};
+struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_ioctl_open_session_arg sess_arg;
- struct tee_client_device *tee_device;
- struct tee_rproc *trproc, *p_err;
- int ret;
- /*
* Test if the device has been probed by the TEE bus. In case of failure, we ignore the
* reason. The bus could be not yet probed or the service not available in the secure
* firmware.The assumption in such a case is that the TEE remoteproc is not probed.
*/
- if (!tee_rproc_ctx)
return ERR_PTR(-EPROBE_DEFER);
- /* Prevent tee rproc module from being removed */
- if (!try_module_get(THIS_MODULE)) {
dev_err(tee_rproc_ctx->dev, "can't get owner\n");
return ERR_PTR(-ENODEV);
- }
- trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
- if (!trproc) {
p_err = ERR_PTR(-ENOMEM);
goto module_put;
- }
- tee_device = to_tee_client_device(tee_rproc_ctx->dev);
- memset(&sess_arg, 0, sizeof(sess_arg));
- memcpy(sess_arg.uuid, tee_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
- sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
- sess_arg.num_params = 1;
- param[0] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
.u.value.a = rproc_id,
- };
- ret = tee_client_open_session(tee_rproc_ctx->tee_ctx, &sess_arg, param);
- if (ret < 0 || sess_arg.ret != 0) {
dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret);
p_err = ERR_PTR(-EINVAL);
goto module_put;
- }
- trproc->parent = dev;
- trproc->rproc_id = rproc_id;
- trproc->session_id = sess_arg.session;
- trproc->rproc = rproc;
- rproc->tee_interface = trproc;
- list_add_tail(&trproc->node, &tee_rproc_ctx->sessions);
- return trproc;
+module_put:
- module_put(THIS_MODULE);
- return p_err;
+} +EXPORT_SYMBOL_GPL(tee_rproc_register);
+int tee_rproc_unregister(struct tee_rproc *trproc) +{
- struct rproc *rproc = trproc->rproc;
- int ret;
- ret = tee_client_close_session(tee_rproc_ctx->tee_ctx, trproc->session_id);
- if (ret < 0)
dev_err(trproc->parent, "tee_client_close_session failed, err: %x\n", ret);
- list_del(&trproc->node);
- rproc->tee_interface = NULL;
- module_put(THIS_MODULE);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_unregister);
+static int tee_rproc_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{
- /* Today we support only the OP-TEE, could be extend to other tees */
- return (ver->impl_id == TEE_IMPL_ID_OPTEE);
+}
+static int tee_rproc_probe(struct device *dev) +{
- struct tee_context *tee_ctx;
- int ret;
- /* Open context with TEE driver */
- tee_ctx = tee_client_open_context(NULL, tee_rproc_ctx_match, NULL, NULL);
- if (IS_ERR(tee_ctx))
return PTR_ERR(tee_ctx);
- tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_rproc_ctx), GFP_KERNEL);
- if (!tee_rproc_ctx) {
ret = -ENOMEM;
goto err;
- }
- tee_rproc_ctx->dev = dev;
- tee_rproc_ctx->tee_ctx = tee_ctx;
- INIT_LIST_HEAD(&tee_rproc_ctx->sessions);
- return 0;
+err:
- tee_client_close_context(tee_ctx);
- return ret;
+}
+static int tee_rproc_remove(struct device *dev) +{
- struct tee_rproc *entry, *tmp;
- list_for_each_entry_safe(entry, tmp, &tee_rproc_ctx->sessions, node) {
tee_client_close_session(tee_rproc_ctx->tee_ctx, entry->session_id);
list_del(&entry->node);
kfree(entry);
- }
- tee_client_close_context(tee_rproc_ctx->tee_ctx);
- return 0;
+}
+MODULE_DEVICE_TABLE(tee, tee_rproc_id_table);
+static struct tee_client_driver tee_rproc_fw_driver = {
- .id_table = tee_rproc_id_table,
- .driver = {
.name = KBUILD_MODNAME,
.bus = &tee_bus_type,
.probe = tee_rproc_probe,
.remove = tee_rproc_remove,
- },
+};
+static int __init tee_rproc_fw_mod_init(void) +{
- return driver_register(&tee_rproc_fw_driver.driver);
+}
+static void __exit tee_rproc_fw_mod_exit(void) +{
- driver_unregister(&tee_rproc_fw_driver.driver);
+}
+module_init(tee_rproc_fw_mod_init); +module_exit(tee_rproc_fw_mod_exit);
+MODULE_DESCRIPTION(" remote processor support with a TEE application"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 8fd0d7f63c8e..fbe1d6793709 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -503,6 +503,8 @@ enum rproc_features { RPROC_MAX_FEATURES, }; +struct tee_rproc;
/**
- struct rproc - represents a physical remote processor device
- @node: list node of this rproc object
@@ -545,6 +547,7 @@ enum rproc_features {
- @cdev: character device of the rproc
- @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release
- @features: indicate remoteproc features
*/
- @tee_interface: pointer to the remoteproc tee context
struct rproc { struct list_head node; @@ -586,6 +589,7 @@ struct rproc { struct cdev cdev; bool cdev_put_on_release; DECLARE_BITMAP(features, RPROC_MAX_FEATURES);
- struct tee_rproc *tee_interface;
}; /** diff --git a/include/linux/remoteproc_tee.h b/include/linux/remoteproc_tee.h new file mode 100644 index 000000000000..ccf34a218d54 --- /dev/null +++ b/include/linux/remoteproc_tee.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/*
- Copyright(c) 2024 STMicroelectronics
- */
+#ifndef REMOTEPROC_TEE_H +#define REMOTEPROC_TEE_H
+#include <linux/tee_drv.h> +#include <linux/firmware.h> +#include <linux/remoteproc.h>
+struct rproc;
+/**
- struct tee_rproc - TEE remoteproc structure
- @node: Reference in list
- @rproc: Remoteproc reference
- @parent: Parent device
- @rproc_id: Identifier of the target firmware
- @session_id: TEE session identifier
- */
+struct tee_rproc {
- struct list_head node;
- struct rproc *rproc;
- struct device *parent;
- u32 rproc_id;
- u32 session_id;
+};
+#if IS_REACHABLE(CONFIG_REMOTEPROC_TEE)
+struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc,
unsigned int rproc_id);
+int tee_rproc_unregister(struct tee_rproc *trproc); +int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw); +int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw); +struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc,
const struct firmware *fw);
+int tee_rproc_start(struct rproc *rproc); +int tee_rproc_stop(struct rproc *rproc);
+#else
+static inline struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc,
unsigned int rproc_id)
+{
- return ERR_PTR(-ENODEV);
+}
+static int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_unregister(struct tee_rproc *trproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_start(struct rproc *rproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_stop(struct rproc *rproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline struct resource_table * +tee_rproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return NULL;
+} +#endif /* CONFIG_REMOTEPROC_TEE */
+#endif /* REMOTEPROC_TEE_H */
2.25.1
On 7/3/24 17:14, Mathieu Poirier wrote:
On Wed, Jul 03, 2024 at 09:19:44AM +0200, Arnaud POULIQUEN wrote:
Hello Mathieu
On 7/2/24 18:44, Mathieu Poirier wrote:
Good morning,
On Fri, Jun 21, 2024 at 04:37:56PM +0200, Arnaud Pouliquen wrote:
Add a remoteproc TEE (Trusted Execution Environment) driver that will be probed by the TEE bus. If the associated Trusted application is supported on secure part this driver offers a client interface to load a firmware by the secure part. This firmware could be authenticated by the secure trusted application.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@foss.st.com
Updates vs previous version:
- rename tee_remoteproc.* file to rmeoteproc_tee.*,
- rename TEE_REMOTEPROC config to REMOTEPROC_TEE,
- remove "stm32" in some variable declarations,
- remove useless "remoteproc_internal.h" include,
- fix tee_rproc_ctx devm_kzalloc.
- rework module description
drivers/remoteproc/Kconfig | 10 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/remoteproc_tee.c | 446 ++++++++++++++++++++++++++++ include/linux/remoteproc.h | 4 + include/linux/remoteproc_tee.h | 100 +++++++ 5 files changed, 561 insertions(+) create mode 100644 drivers/remoteproc/remoteproc_tee.c create mode 100644 include/linux/remoteproc_tee.h
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 48845dc8fa85..278f197acb90 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -365,6 +365,16 @@ config XLNX_R5_REMOTEPROC It's safe to say N if not interested in using RPU r5f cores.
+config REMOTEPROC_TEE
- tristate "Remoteproc support by a TEE application"
- depends on OPTEE
- help
Support a remote processor with a TEE application. The Trusted
Execution Context is responsible for loading the trusted firmware
image and managing the remote processor's lifecycle.
This can be either built-in or a loadable module.
endif # REMOTEPROC endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 91314a9b43ce..b4eb37177005 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_RCAR_REMOTEPROC) += rcar_rproc.o obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c new file mode 100644 index 000000000000..70cb67451767 --- /dev/null +++ b/drivers/remoteproc/remoteproc_tee.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (C) STMicroelectronics 2024
- Author: Arnaud Pouliquen arnaud.pouliquen@foss.st.com
- */
+#include <linux/firmware.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/remoteproc.h> +#include <linux/remoteproc_tee.h> +#include <linux/slab.h> +#include <linux/tee_drv.h>
+#define MAX_TEE_PARAM_ARRY_MEMBER 4
+/*
- Authentication of the firmware and load in the remote processor memory
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [in] params[1].memref: buffer containing the image of the buffer
- */
+#define TA_RPROC_FW_CMD_LOAD_FW 1
+/*
- Start the remote processor
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- */
+#define TA_RPROC_FW_CMD_START_FW 2
+/*
- Stop the remote processor
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- */
+#define TA_RPROC_FW_CMD_STOP_FW 3
+/*
- Return the address of the resource table, or 0 if not found
- No check is done to verify that the address returned is accessible by
- the non secure context. If the resource table is loaded in a protected
- memory the access by the non secure context will lead to a data abort.
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [out] params[1].value.a: 32bit LSB resource table memory address
- [out] params[1].value.b: 32bit MSB resource table memory address
- [out] params[2].value.a: 32bit LSB resource table memory size
- [out] params[2].value.b: 32bit MSB resource table memory size
- */
+#define TA_RPROC_FW_CMD_GET_RSC_TABLE 4
+/*
- Return the address of the core dump
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [out] params[1].memref: address of the core dump image if exist,
else return Null
- */
+#define TA_RPROC_FW_CMD_GET_COREDUMP 5
+struct tee_rproc_context {
- struct list_head sessions;
- struct tee_context *tee_ctx;
- struct device *dev;
+};
+static struct tee_rproc_context *tee_rproc_ctx;
+static void tee_rproc_prepare_args(struct tee_rproc *trproc, int cmd,
struct tee_ioctl_invoke_arg *arg,
struct tee_param *param,
unsigned int num_params)
+{
- memset(arg, 0, sizeof(*arg));
- memset(param, 0, MAX_TEE_PARAM_ARRY_MEMBER * sizeof(*param));
- arg->func = cmd;
- arg->session = trproc->session_id;
- arg->num_params = num_params + 1;
- param[0] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
.u.value.a = trproc->rproc_id,
- };
+}
+int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- struct tee_shm *fw_shm;
- int ret;
- if (!trproc)
return -EINVAL;
- fw_shm = tee_shm_register_kernel_buf(tee_rproc_ctx->tee_ctx, (void *)fw->data, fw->size);
- if (IS_ERR(fw_shm))
return PTR_ERR(fw_shm);
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_LOAD_FW, &arg, param, 1);
- /* Provide the address of the firmware image */
- param[1] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
.u.memref = {
.shm = fw_shm,
.size = fw->size,
.shm_offs = 0,
},
- };
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_LOAD_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
ret = -EIO;
- }
- tee_shm_free(fw_shm);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_load_fw);
+static int tee_rproc_get_loaded_rsc_table(struct rproc *rproc, phys_addr_t *rsc_pa,
size_t *table_sz)
+{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_GET_RSC_TABLE, &arg, param, 2);
- param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
- param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_GET_RSC_TABLE invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
return -EIO;
- }
- *table_sz = param[2].u.value.a;
- if (*table_sz)
*rsc_pa = param[1].u.value.a;
- else
*rsc_pa = 0;
- return 0;
+}
+int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{
- phys_addr_t rsc_table;
- void __iomem *rsc_va;
- size_t table_sz;
- int ret;
- ret = tee_rproc_load_fw(rproc, fw);
- if (ret)
return ret;
- ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
- if (ret)
return ret;
Do we need an API to unload the firware from memory too? If anything fails we still have firmware loaded into memory.
For the time being, the clean-up of the memory is not implemented in OP-TEE.
In my opinion cleaning up the firmware image loaded to memory when something fails is important and should be part of this patchset. Looking at the error path in this function, it is clear something is missing.
Please, could you detail what risks you see in not implementing the memory clean-up at this first step?
Currently, OP-TEE upstream does not clean the memory. Upon the next load, it will either be overwritten or left unused by the newly authenticated firmware.
If your concern is about the ability to read the code, the code is already accessible in the binary firmware image on the Linux filesystem.
As a next step a platform-dependent strategy has to be implemented to enhance firmware protection: - Ensure the integrity of the code. - Preserve secrets.
However, OP-TEE cannot trust the Non-secure context for this. Expecting Linux to call a clean-up service does not seem reliable to me.
In the downstream stm32mp platform, we have implemented the clean-up but above all, ensured that Linux never has access to the remote processor firmware code and data by using firewall configurations. For time being we print a warning message on OP-TEE upstream[1]
[1]https://elixir.bootlin.com/op-tee/latest/source/core/drivers/remoteproc/stm3...
Thanks, Arnaud
I plan to upstream this part in a second step, associated with a memory protection strategy. This should be directly implemented in the OP-TEE service to avoid reliance on Linux error management. Nonetheless, an API could be added to clean the memory if needed.
Thanks, Arnaud
More comments to come later.
Mathieu
- /*
* We assume here that the memory mapping is the same between the TEE and Linux kernel
* contexts. Else a new TEE remoteproc service could be needed to get a copy of the
* resource table
*/
- rsc_va = ioremap_wc(rsc_table, table_sz);
- if (IS_ERR_OR_NULL(rsc_va)) {
dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %pa+%zx\n",
&rsc_table, table_sz);
return -ENOMEM;
- }
- /*
* Create a copy of the resource table to have the same behavior as the elf loader.
* This cached table will be used after the remoteproc stops to free resources, and for
* crash recovery to reapply the settings.
*/
- rproc->cached_table = kmemdup((__force void *)rsc_va, table_sz, GFP_KERNEL);
- if (!rproc->cached_table) {
ret = -ENOMEM;
goto out;
- }
- rproc->table_ptr = rproc->cached_table;
- rproc->table_sz = table_sz;
+out:
- iounmap(rsc_va);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_parse_fw);
+struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc,
const struct firmware *fw)
+{
- struct tee_rproc *trproc = rproc->tee_interface;
- phys_addr_t rsc_table;
- size_t table_sz;
- int ret;
- if (!trproc)
return ERR_PTR(-EINVAL);
- ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
- if (ret)
return ERR_PTR(ret);
- rproc->table_sz = table_sz;
- if (!table_sz)
return NULL;
- /*
* At this step the memory area that contains the resource table should have be declared
* in the remote proc platform driver and allocated by rproc_alloc_registered_carveouts().
*/
- return (struct resource_table *)rproc_pa_to_va(rproc, rsc_table, table_sz, NULL);
+} +EXPORT_SYMBOL_GPL(tee_rproc_find_loaded_rsc_table);
+int tee_rproc_start(struct rproc *rproc) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret = 0;
- if (!trproc)
return -EINVAL;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_START_FW, &arg, param, 0);
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_START_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
return -EIO;
- }
- return 0;
+} +EXPORT_SYMBOL_GPL(tee_rproc_start);
+int tee_rproc_stop(struct rproc *rproc) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret;
- if (!trproc)
return -EINVAL;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_STOP_FW, &arg, param, 0);
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_STOP_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
ret = -EIO;
- }
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_stop);
+static const struct tee_client_device_id tee_rproc_id_table[] = {
- {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)},
- {}
+};
+struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_ioctl_open_session_arg sess_arg;
- struct tee_client_device *tee_device;
- struct tee_rproc *trproc, *p_err;
- int ret;
- /*
* Test if the device has been probed by the TEE bus. In case of failure, we ignore the
* reason. The bus could be not yet probed or the service not available in the secure
* firmware.The assumption in such a case is that the TEE remoteproc is not probed.
*/
- if (!tee_rproc_ctx)
return ERR_PTR(-EPROBE_DEFER);
- /* Prevent tee rproc module from being removed */
- if (!try_module_get(THIS_MODULE)) {
dev_err(tee_rproc_ctx->dev, "can't get owner\n");
return ERR_PTR(-ENODEV);
- }
- trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
- if (!trproc) {
p_err = ERR_PTR(-ENOMEM);
goto module_put;
- }
- tee_device = to_tee_client_device(tee_rproc_ctx->dev);
- memset(&sess_arg, 0, sizeof(sess_arg));
- memcpy(sess_arg.uuid, tee_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
- sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
- sess_arg.num_params = 1;
- param[0] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
.u.value.a = rproc_id,
- };
- ret = tee_client_open_session(tee_rproc_ctx->tee_ctx, &sess_arg, param);
- if (ret < 0 || sess_arg.ret != 0) {
dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret);
p_err = ERR_PTR(-EINVAL);
goto module_put;
- }
- trproc->parent = dev;
- trproc->rproc_id = rproc_id;
- trproc->session_id = sess_arg.session;
- trproc->rproc = rproc;
- rproc->tee_interface = trproc;
- list_add_tail(&trproc->node, &tee_rproc_ctx->sessions);
- return trproc;
+module_put:
- module_put(THIS_MODULE);
- return p_err;
+} +EXPORT_SYMBOL_GPL(tee_rproc_register);
+int tee_rproc_unregister(struct tee_rproc *trproc) +{
- struct rproc *rproc = trproc->rproc;
- int ret;
- ret = tee_client_close_session(tee_rproc_ctx->tee_ctx, trproc->session_id);
- if (ret < 0)
dev_err(trproc->parent, "tee_client_close_session failed, err: %x\n", ret);
- list_del(&trproc->node);
- rproc->tee_interface = NULL;
- module_put(THIS_MODULE);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_unregister);
+static int tee_rproc_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{
- /* Today we support only the OP-TEE, could be extend to other tees */
- return (ver->impl_id == TEE_IMPL_ID_OPTEE);
+}
+static int tee_rproc_probe(struct device *dev) +{
- struct tee_context *tee_ctx;
- int ret;
- /* Open context with TEE driver */
- tee_ctx = tee_client_open_context(NULL, tee_rproc_ctx_match, NULL, NULL);
- if (IS_ERR(tee_ctx))
return PTR_ERR(tee_ctx);
- tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_rproc_ctx), GFP_KERNEL);
- if (!tee_rproc_ctx) {
ret = -ENOMEM;
goto err;
- }
- tee_rproc_ctx->dev = dev;
- tee_rproc_ctx->tee_ctx = tee_ctx;
- INIT_LIST_HEAD(&tee_rproc_ctx->sessions);
- return 0;
+err:
- tee_client_close_context(tee_ctx);
- return ret;
+}
+static int tee_rproc_remove(struct device *dev) +{
- struct tee_rproc *entry, *tmp;
- list_for_each_entry_safe(entry, tmp, &tee_rproc_ctx->sessions, node) {
tee_client_close_session(tee_rproc_ctx->tee_ctx, entry->session_id);
list_del(&entry->node);
kfree(entry);
- }
- tee_client_close_context(tee_rproc_ctx->tee_ctx);
- return 0;
+}
+MODULE_DEVICE_TABLE(tee, tee_rproc_id_table);
+static struct tee_client_driver tee_rproc_fw_driver = {
- .id_table = tee_rproc_id_table,
- .driver = {
.name = KBUILD_MODNAME,
.bus = &tee_bus_type,
.probe = tee_rproc_probe,
.remove = tee_rproc_remove,
- },
+};
+static int __init tee_rproc_fw_mod_init(void) +{
- return driver_register(&tee_rproc_fw_driver.driver);
+}
+static void __exit tee_rproc_fw_mod_exit(void) +{
- driver_unregister(&tee_rproc_fw_driver.driver);
+}
+module_init(tee_rproc_fw_mod_init); +module_exit(tee_rproc_fw_mod_exit);
+MODULE_DESCRIPTION(" remote processor support with a TEE application"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 8fd0d7f63c8e..fbe1d6793709 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -503,6 +503,8 @@ enum rproc_features { RPROC_MAX_FEATURES, }; +struct tee_rproc;
/**
- struct rproc - represents a physical remote processor device
- @node: list node of this rproc object
@@ -545,6 +547,7 @@ enum rproc_features {
- @cdev: character device of the rproc
- @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release
- @features: indicate remoteproc features
*/
- @tee_interface: pointer to the remoteproc tee context
struct rproc { struct list_head node; @@ -586,6 +589,7 @@ struct rproc { struct cdev cdev; bool cdev_put_on_release; DECLARE_BITMAP(features, RPROC_MAX_FEATURES);
- struct tee_rproc *tee_interface;
}; /** diff --git a/include/linux/remoteproc_tee.h b/include/linux/remoteproc_tee.h new file mode 100644 index 000000000000..ccf34a218d54 --- /dev/null +++ b/include/linux/remoteproc_tee.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/*
- Copyright(c) 2024 STMicroelectronics
- */
+#ifndef REMOTEPROC_TEE_H +#define REMOTEPROC_TEE_H
+#include <linux/tee_drv.h> +#include <linux/firmware.h> +#include <linux/remoteproc.h>
+struct rproc;
+/**
- struct tee_rproc - TEE remoteproc structure
- @node: Reference in list
- @rproc: Remoteproc reference
- @parent: Parent device
- @rproc_id: Identifier of the target firmware
- @session_id: TEE session identifier
- */
+struct tee_rproc {
- struct list_head node;
- struct rproc *rproc;
- struct device *parent;
- u32 rproc_id;
- u32 session_id;
+};
+#if IS_REACHABLE(CONFIG_REMOTEPROC_TEE)
+struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc,
unsigned int rproc_id);
+int tee_rproc_unregister(struct tee_rproc *trproc); +int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw); +int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw); +struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc,
const struct firmware *fw);
+int tee_rproc_start(struct rproc *rproc); +int tee_rproc_stop(struct rproc *rproc);
+#else
+static inline struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc,
unsigned int rproc_id)
+{
- return ERR_PTR(-ENODEV);
+}
+static int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_unregister(struct tee_rproc *trproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_start(struct rproc *rproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_stop(struct rproc *rproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline struct resource_table * +tee_rproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return NULL;
+} +#endif /* CONFIG_REMOTEPROC_TEE */
+#endif /* REMOTEPROC_TEE_H */
2.25.1
On Thu, Jul 04, 2024 at 10:05:24AM +0200, Arnaud POULIQUEN wrote:
On 7/3/24 17:14, Mathieu Poirier wrote:
On Wed, Jul 03, 2024 at 09:19:44AM +0200, Arnaud POULIQUEN wrote:
Hello Mathieu
On 7/2/24 18:44, Mathieu Poirier wrote:
Good morning,
On Fri, Jun 21, 2024 at 04:37:56PM +0200, Arnaud Pouliquen wrote:
Add a remoteproc TEE (Trusted Execution Environment) driver that will be probed by the TEE bus. If the associated Trusted application is supported on secure part this driver offers a client interface to load a firmware by the secure part. This firmware could be authenticated by the secure trusted application.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@foss.st.com
Updates vs previous version:
- rename tee_remoteproc.* file to rmeoteproc_tee.*,
- rename TEE_REMOTEPROC config to REMOTEPROC_TEE,
- remove "stm32" in some variable declarations,
- remove useless "remoteproc_internal.h" include,
- fix tee_rproc_ctx devm_kzalloc.
- rework module description
drivers/remoteproc/Kconfig | 10 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/remoteproc_tee.c | 446 ++++++++++++++++++++++++++++ include/linux/remoteproc.h | 4 + include/linux/remoteproc_tee.h | 100 +++++++ 5 files changed, 561 insertions(+) create mode 100644 drivers/remoteproc/remoteproc_tee.c create mode 100644 include/linux/remoteproc_tee.h
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 48845dc8fa85..278f197acb90 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -365,6 +365,16 @@ config XLNX_R5_REMOTEPROC It's safe to say N if not interested in using RPU r5f cores.
+config REMOTEPROC_TEE
- tristate "Remoteproc support by a TEE application"
- depends on OPTEE
- help
Support a remote processor with a TEE application. The Trusted
Execution Context is responsible for loading the trusted firmware
image and managing the remote processor's lifecycle.
This can be either built-in or a loadable module.
endif # REMOTEPROC endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 91314a9b43ce..b4eb37177005 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_RCAR_REMOTEPROC) += rcar_rproc.o obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c new file mode 100644 index 000000000000..70cb67451767 --- /dev/null +++ b/drivers/remoteproc/remoteproc_tee.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (C) STMicroelectronics 2024
- Author: Arnaud Pouliquen arnaud.pouliquen@foss.st.com
- */
+#include <linux/firmware.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/remoteproc.h> +#include <linux/remoteproc_tee.h> +#include <linux/slab.h> +#include <linux/tee_drv.h>
+#define MAX_TEE_PARAM_ARRY_MEMBER 4
+/*
- Authentication of the firmware and load in the remote processor memory
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [in] params[1].memref: buffer containing the image of the buffer
- */
+#define TA_RPROC_FW_CMD_LOAD_FW 1
+/*
- Start the remote processor
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- */
+#define TA_RPROC_FW_CMD_START_FW 2
+/*
- Stop the remote processor
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- */
+#define TA_RPROC_FW_CMD_STOP_FW 3
+/*
- Return the address of the resource table, or 0 if not found
- No check is done to verify that the address returned is accessible by
- the non secure context. If the resource table is loaded in a protected
- memory the access by the non secure context will lead to a data abort.
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [out] params[1].value.a: 32bit LSB resource table memory address
- [out] params[1].value.b: 32bit MSB resource table memory address
- [out] params[2].value.a: 32bit LSB resource table memory size
- [out] params[2].value.b: 32bit MSB resource table memory size
- */
+#define TA_RPROC_FW_CMD_GET_RSC_TABLE 4
+/*
- Return the address of the core dump
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [out] params[1].memref: address of the core dump image if exist,
else return Null
- */
+#define TA_RPROC_FW_CMD_GET_COREDUMP 5
+struct tee_rproc_context {
- struct list_head sessions;
- struct tee_context *tee_ctx;
- struct device *dev;
+};
+static struct tee_rproc_context *tee_rproc_ctx;
+static void tee_rproc_prepare_args(struct tee_rproc *trproc, int cmd,
struct tee_ioctl_invoke_arg *arg,
struct tee_param *param,
unsigned int num_params)
+{
- memset(arg, 0, sizeof(*arg));
- memset(param, 0, MAX_TEE_PARAM_ARRY_MEMBER * sizeof(*param));
- arg->func = cmd;
- arg->session = trproc->session_id;
- arg->num_params = num_params + 1;
- param[0] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
.u.value.a = trproc->rproc_id,
- };
+}
+int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- struct tee_shm *fw_shm;
- int ret;
- if (!trproc)
return -EINVAL;
- fw_shm = tee_shm_register_kernel_buf(tee_rproc_ctx->tee_ctx, (void *)fw->data, fw->size);
- if (IS_ERR(fw_shm))
return PTR_ERR(fw_shm);
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_LOAD_FW, &arg, param, 1);
- /* Provide the address of the firmware image */
- param[1] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
.u.memref = {
.shm = fw_shm,
.size = fw->size,
.shm_offs = 0,
},
- };
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_LOAD_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
ret = -EIO;
- }
- tee_shm_free(fw_shm);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_load_fw);
+static int tee_rproc_get_loaded_rsc_table(struct rproc *rproc, phys_addr_t *rsc_pa,
size_t *table_sz)
+{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_GET_RSC_TABLE, &arg, param, 2);
- param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
- param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_GET_RSC_TABLE invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
return -EIO;
- }
- *table_sz = param[2].u.value.a;
- if (*table_sz)
*rsc_pa = param[1].u.value.a;
- else
*rsc_pa = 0;
- return 0;
+}
+int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{
- phys_addr_t rsc_table;
- void __iomem *rsc_va;
- size_t table_sz;
- int ret;
- ret = tee_rproc_load_fw(rproc, fw);
- if (ret)
return ret;
- ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
- if (ret)
return ret;
Do we need an API to unload the firware from memory too? If anything fails we still have firmware loaded into memory.
For the time being, the clean-up of the memory is not implemented in OP-TEE.
In my opinion cleaning up the firmware image loaded to memory when something fails is important and should be part of this patchset. Looking at the error path in this function, it is clear something is missing.
Please, could you detail what risks you see in not implementing the memory clean-up at this first step?
Almost everything in the Linux kernel is about allocation and release of resources, the pattern is ubiquitous. Reading function tee_rproc_parse_fw(), the firmware is loaded by tee_rproc_load_fw() but never released - people reading this driver will wonder about that. There is also the possibility that other implementations will use the free'd memory for other purposes. This interface driver is meant to be generic and as such needs to take care of those corner cases.
Currently, OP-TEE upstream does not clean the memory. Upon the next load, it will either be overwritten or left unused by the newly authenticated firmware.
If your concern is about the ability to read the code, the code is already accessible in the binary firmware image on the Linux filesystem.
As a next step a platform-dependent strategy has to be implemented to enhance firmware protection:
- Ensure the integrity of the code.
- Preserve secrets.
However, OP-TEE cannot trust the Non-secure context for this. Expecting Linux to call a clean-up service does not seem reliable to me.
In the downstream stm32mp platform, we have implemented the clean-up but above all, ensured that Linux never has access to the remote processor firmware code and data by using firewall configurations. For time being we print a warning message on OP-TEE upstream[1]
[1]https://elixir.bootlin.com/op-tee/latest/source/core/drivers/remoteproc/stm3...
Thanks, Arnaud
I plan to upstream this part in a second step, associated with a memory protection strategy. This should be directly implemented in the OP-TEE service to avoid reliance on Linux error management. Nonetheless, an API could be added to clean the memory if needed.
Thanks, Arnaud
More comments to come later.
Mathieu
- /*
* We assume here that the memory mapping is the same between the TEE and Linux kernel
* contexts. Else a new TEE remoteproc service could be needed to get a copy of the
* resource table
*/
- rsc_va = ioremap_wc(rsc_table, table_sz);
- if (IS_ERR_OR_NULL(rsc_va)) {
dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %pa+%zx\n",
&rsc_table, table_sz);
return -ENOMEM;
- }
- /*
* Create a copy of the resource table to have the same behavior as the elf loader.
* This cached table will be used after the remoteproc stops to free resources, and for
* crash recovery to reapply the settings.
*/
- rproc->cached_table = kmemdup((__force void *)rsc_va, table_sz, GFP_KERNEL);
- if (!rproc->cached_table) {
ret = -ENOMEM;
goto out;
- }
- rproc->table_ptr = rproc->cached_table;
- rproc->table_sz = table_sz;
+out:
- iounmap(rsc_va);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_parse_fw);
+struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc,
const struct firmware *fw)
+{
- struct tee_rproc *trproc = rproc->tee_interface;
- phys_addr_t rsc_table;
- size_t table_sz;
- int ret;
- if (!trproc)
return ERR_PTR(-EINVAL);
- ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
- if (ret)
return ERR_PTR(ret);
- rproc->table_sz = table_sz;
- if (!table_sz)
return NULL;
- /*
* At this step the memory area that contains the resource table should have be declared
* in the remote proc platform driver and allocated by rproc_alloc_registered_carveouts().
*/
- return (struct resource_table *)rproc_pa_to_va(rproc, rsc_table, table_sz, NULL);
+} +EXPORT_SYMBOL_GPL(tee_rproc_find_loaded_rsc_table);
+int tee_rproc_start(struct rproc *rproc) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret = 0;
- if (!trproc)
return -EINVAL;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_START_FW, &arg, param, 0);
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_START_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
return -EIO;
- }
- return 0;
+} +EXPORT_SYMBOL_GPL(tee_rproc_start);
+int tee_rproc_stop(struct rproc *rproc) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret;
- if (!trproc)
return -EINVAL;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_STOP_FW, &arg, param, 0);
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_STOP_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
ret = -EIO;
- }
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_stop);
+static const struct tee_client_device_id tee_rproc_id_table[] = {
- {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)},
- {}
+};
+struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_ioctl_open_session_arg sess_arg;
- struct tee_client_device *tee_device;
- struct tee_rproc *trproc, *p_err;
- int ret;
- /*
* Test if the device has been probed by the TEE bus. In case of failure, we ignore the
* reason. The bus could be not yet probed or the service not available in the secure
* firmware.The assumption in such a case is that the TEE remoteproc is not probed.
*/
- if (!tee_rproc_ctx)
return ERR_PTR(-EPROBE_DEFER);
- /* Prevent tee rproc module from being removed */
- if (!try_module_get(THIS_MODULE)) {
dev_err(tee_rproc_ctx->dev, "can't get owner\n");
return ERR_PTR(-ENODEV);
- }
- trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
- if (!trproc) {
p_err = ERR_PTR(-ENOMEM);
goto module_put;
- }
- tee_device = to_tee_client_device(tee_rproc_ctx->dev);
- memset(&sess_arg, 0, sizeof(sess_arg));
- memcpy(sess_arg.uuid, tee_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
- sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
- sess_arg.num_params = 1;
- param[0] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
.u.value.a = rproc_id,
- };
- ret = tee_client_open_session(tee_rproc_ctx->tee_ctx, &sess_arg, param);
- if (ret < 0 || sess_arg.ret != 0) {
dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret);
p_err = ERR_PTR(-EINVAL);
goto module_put;
- }
- trproc->parent = dev;
- trproc->rproc_id = rproc_id;
- trproc->session_id = sess_arg.session;
- trproc->rproc = rproc;
- rproc->tee_interface = trproc;
- list_add_tail(&trproc->node, &tee_rproc_ctx->sessions);
- return trproc;
+module_put:
- module_put(THIS_MODULE);
- return p_err;
+} +EXPORT_SYMBOL_GPL(tee_rproc_register);
+int tee_rproc_unregister(struct tee_rproc *trproc) +{
- struct rproc *rproc = trproc->rproc;
- int ret;
- ret = tee_client_close_session(tee_rproc_ctx->tee_ctx, trproc->session_id);
- if (ret < 0)
dev_err(trproc->parent, "tee_client_close_session failed, err: %x\n", ret);
- list_del(&trproc->node);
- rproc->tee_interface = NULL;
- module_put(THIS_MODULE);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_unregister);
+static int tee_rproc_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{
- /* Today we support only the OP-TEE, could be extend to other tees */
- return (ver->impl_id == TEE_IMPL_ID_OPTEE);
+}
+static int tee_rproc_probe(struct device *dev) +{
- struct tee_context *tee_ctx;
- int ret;
- /* Open context with TEE driver */
- tee_ctx = tee_client_open_context(NULL, tee_rproc_ctx_match, NULL, NULL);
- if (IS_ERR(tee_ctx))
return PTR_ERR(tee_ctx);
- tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_rproc_ctx), GFP_KERNEL);
- if (!tee_rproc_ctx) {
ret = -ENOMEM;
goto err;
- }
- tee_rproc_ctx->dev = dev;
- tee_rproc_ctx->tee_ctx = tee_ctx;
- INIT_LIST_HEAD(&tee_rproc_ctx->sessions);
- return 0;
+err:
- tee_client_close_context(tee_ctx);
- return ret;
+}
+static int tee_rproc_remove(struct device *dev) +{
- struct tee_rproc *entry, *tmp;
- list_for_each_entry_safe(entry, tmp, &tee_rproc_ctx->sessions, node) {
tee_client_close_session(tee_rproc_ctx->tee_ctx, entry->session_id);
list_del(&entry->node);
kfree(entry);
- }
- tee_client_close_context(tee_rproc_ctx->tee_ctx);
- return 0;
+}
+MODULE_DEVICE_TABLE(tee, tee_rproc_id_table);
+static struct tee_client_driver tee_rproc_fw_driver = {
- .id_table = tee_rproc_id_table,
- .driver = {
.name = KBUILD_MODNAME,
.bus = &tee_bus_type,
.probe = tee_rproc_probe,
.remove = tee_rproc_remove,
- },
+};
+static int __init tee_rproc_fw_mod_init(void) +{
- return driver_register(&tee_rproc_fw_driver.driver);
+}
+static void __exit tee_rproc_fw_mod_exit(void) +{
- driver_unregister(&tee_rproc_fw_driver.driver);
+}
+module_init(tee_rproc_fw_mod_init); +module_exit(tee_rproc_fw_mod_exit);
+MODULE_DESCRIPTION(" remote processor support with a TEE application"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 8fd0d7f63c8e..fbe1d6793709 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -503,6 +503,8 @@ enum rproc_features { RPROC_MAX_FEATURES, }; +struct tee_rproc;
/**
- struct rproc - represents a physical remote processor device
- @node: list node of this rproc object
@@ -545,6 +547,7 @@ enum rproc_features {
- @cdev: character device of the rproc
- @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release
- @features: indicate remoteproc features
*/
- @tee_interface: pointer to the remoteproc tee context
struct rproc { struct list_head node; @@ -586,6 +589,7 @@ struct rproc { struct cdev cdev; bool cdev_put_on_release; DECLARE_BITMAP(features, RPROC_MAX_FEATURES);
- struct tee_rproc *tee_interface;
}; /** diff --git a/include/linux/remoteproc_tee.h b/include/linux/remoteproc_tee.h new file mode 100644 index 000000000000..ccf34a218d54 --- /dev/null +++ b/include/linux/remoteproc_tee.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/*
- Copyright(c) 2024 STMicroelectronics
- */
+#ifndef REMOTEPROC_TEE_H +#define REMOTEPROC_TEE_H
+#include <linux/tee_drv.h> +#include <linux/firmware.h> +#include <linux/remoteproc.h>
+struct rproc;
+/**
- struct tee_rproc - TEE remoteproc structure
- @node: Reference in list
- @rproc: Remoteproc reference
- @parent: Parent device
- @rproc_id: Identifier of the target firmware
- @session_id: TEE session identifier
- */
+struct tee_rproc {
- struct list_head node;
- struct rproc *rproc;
- struct device *parent;
- u32 rproc_id;
- u32 session_id;
+};
+#if IS_REACHABLE(CONFIG_REMOTEPROC_TEE)
+struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc,
unsigned int rproc_id);
+int tee_rproc_unregister(struct tee_rproc *trproc); +int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw); +int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw); +struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc,
const struct firmware *fw);
+int tee_rproc_start(struct rproc *rproc); +int tee_rproc_stop(struct rproc *rproc);
+#else
+static inline struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc,
unsigned int rproc_id)
+{
- return ERR_PTR(-ENODEV);
+}
+static int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_unregister(struct tee_rproc *trproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_start(struct rproc *rproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_stop(struct rproc *rproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline struct resource_table * +tee_rproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return NULL;
+} +#endif /* CONFIG_REMOTEPROC_TEE */
+#endif /* REMOTEPROC_TEE_H */
2.25.1
On 7/4/24 17:32, Mathieu Poirier wrote:
On Thu, Jul 04, 2024 at 10:05:24AM +0200, Arnaud POULIQUEN wrote:
On 7/3/24 17:14, Mathieu Poirier wrote:
On Wed, Jul 03, 2024 at 09:19:44AM +0200, Arnaud POULIQUEN wrote:
Hello Mathieu
On 7/2/24 18:44, Mathieu Poirier wrote:
Good morning,
On Fri, Jun 21, 2024 at 04:37:56PM +0200, Arnaud Pouliquen wrote:
Add a remoteproc TEE (Trusted Execution Environment) driver that will be probed by the TEE bus. If the associated Trusted application is supported on secure part this driver offers a client interface to load a firmware by the secure part. This firmware could be authenticated by the secure trusted application.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@foss.st.com
Updates vs previous version:
- rename tee_remoteproc.* file to rmeoteproc_tee.*,
- rename TEE_REMOTEPROC config to REMOTEPROC_TEE,
- remove "stm32" in some variable declarations,
- remove useless "remoteproc_internal.h" include,
- fix tee_rproc_ctx devm_kzalloc.
- rework module description
drivers/remoteproc/Kconfig | 10 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/remoteproc_tee.c | 446 ++++++++++++++++++++++++++++ include/linux/remoteproc.h | 4 + include/linux/remoteproc_tee.h | 100 +++++++ 5 files changed, 561 insertions(+) create mode 100644 drivers/remoteproc/remoteproc_tee.c create mode 100644 include/linux/remoteproc_tee.h
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 48845dc8fa85..278f197acb90 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -365,6 +365,16 @@ config XLNX_R5_REMOTEPROC It's safe to say N if not interested in using RPU r5f cores.
+config REMOTEPROC_TEE
- tristate "Remoteproc support by a TEE application"
- depends on OPTEE
- help
Support a remote processor with a TEE application. The Trusted
Execution Context is responsible for loading the trusted firmware
image and managing the remote processor's lifecycle.
This can be either built-in or a loadable module.
endif # REMOTEPROC endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 91314a9b43ce..b4eb37177005 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_RCAR_REMOTEPROC) += rcar_rproc.o obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c new file mode 100644 index 000000000000..70cb67451767 --- /dev/null +++ b/drivers/remoteproc/remoteproc_tee.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (C) STMicroelectronics 2024
- Author: Arnaud Pouliquen arnaud.pouliquen@foss.st.com
- */
+#include <linux/firmware.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/remoteproc.h> +#include <linux/remoteproc_tee.h> +#include <linux/slab.h> +#include <linux/tee_drv.h>
+#define MAX_TEE_PARAM_ARRY_MEMBER 4
+/*
- Authentication of the firmware and load in the remote processor memory
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [in] params[1].memref: buffer containing the image of the buffer
- */
+#define TA_RPROC_FW_CMD_LOAD_FW 1
+/*
- Start the remote processor
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- */
+#define TA_RPROC_FW_CMD_START_FW 2
+/*
- Stop the remote processor
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- */
+#define TA_RPROC_FW_CMD_STOP_FW 3
+/*
- Return the address of the resource table, or 0 if not found
- No check is done to verify that the address returned is accessible by
- the non secure context. If the resource table is loaded in a protected
- memory the access by the non secure context will lead to a data abort.
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [out] params[1].value.a: 32bit LSB resource table memory address
- [out] params[1].value.b: 32bit MSB resource table memory address
- [out] params[2].value.a: 32bit LSB resource table memory size
- [out] params[2].value.b: 32bit MSB resource table memory size
- */
+#define TA_RPROC_FW_CMD_GET_RSC_TABLE 4
+/*
- Return the address of the core dump
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [out] params[1].memref: address of the core dump image if exist,
else return Null
- */
+#define TA_RPROC_FW_CMD_GET_COREDUMP 5
+struct tee_rproc_context {
- struct list_head sessions;
- struct tee_context *tee_ctx;
- struct device *dev;
+};
+static struct tee_rproc_context *tee_rproc_ctx;
+static void tee_rproc_prepare_args(struct tee_rproc *trproc, int cmd,
struct tee_ioctl_invoke_arg *arg,
struct tee_param *param,
unsigned int num_params)
+{
- memset(arg, 0, sizeof(*arg));
- memset(param, 0, MAX_TEE_PARAM_ARRY_MEMBER * sizeof(*param));
- arg->func = cmd;
- arg->session = trproc->session_id;
- arg->num_params = num_params + 1;
- param[0] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
.u.value.a = trproc->rproc_id,
- };
+}
+int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- struct tee_shm *fw_shm;
- int ret;
- if (!trproc)
return -EINVAL;
- fw_shm = tee_shm_register_kernel_buf(tee_rproc_ctx->tee_ctx, (void *)fw->data, fw->size);
- if (IS_ERR(fw_shm))
return PTR_ERR(fw_shm);
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_LOAD_FW, &arg, param, 1);
- /* Provide the address of the firmware image */
- param[1] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
.u.memref = {
.shm = fw_shm,
.size = fw->size,
.shm_offs = 0,
},
- };
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_LOAD_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
ret = -EIO;
- }
- tee_shm_free(fw_shm);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_load_fw);
+static int tee_rproc_get_loaded_rsc_table(struct rproc *rproc, phys_addr_t *rsc_pa,
size_t *table_sz)
+{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_GET_RSC_TABLE, &arg, param, 2);
- param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
- param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_GET_RSC_TABLE invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
return -EIO;
- }
- *table_sz = param[2].u.value.a;
- if (*table_sz)
*rsc_pa = param[1].u.value.a;
- else
*rsc_pa = 0;
- return 0;
+}
+int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{
- phys_addr_t rsc_table;
- void __iomem *rsc_va;
- size_t table_sz;
- int ret;
- ret = tee_rproc_load_fw(rproc, fw);
- if (ret)
return ret;
- ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
- if (ret)
return ret;
Do we need an API to unload the firware from memory too? If anything fails we still have firmware loaded into memory.
For the time being, the clean-up of the memory is not implemented in OP-TEE.
In my opinion cleaning up the firmware image loaded to memory when something fails is important and should be part of this patchset. Looking at the error path in this function, it is clear something is missing.
Please, could you detail what risks you see in not implementing the memory clean-up at this first step?
Almost everything in the Linux kernel is about allocation and release of resources, the pattern is ubiquitous. Reading function tee_rproc_parse_fw(), the firmware is loaded by tee_rproc_load_fw() but never released - people reading this driver will wonder about that. There is also the possibility that other implementations will use the free'd memory for other purposes. This interface driver is meant to be generic and as such needs to take care of those corner cases.
Thank you for the clarification. If we want to support this, handling the error here would not be sufficient. For instance, many things can go wrong in rproc_start().
So either we do nothing, as with the ELF support, and we put a comment in the remoteproc tee; or we may need to introduce new rproc ops to clean up/free resources in case of errors in the remoteproc core.
Or perhaps do you have another alternative to propose?
Thanks, Arnaud
Currently, OP-TEE upstream does not clean the memory. Upon the next load, it will either be overwritten or left unused by the newly authenticated firmware.
If your concern is about the ability to read the code, the code is already accessible in the binary firmware image on the Linux filesystem.
As a next step a platform-dependent strategy has to be implemented to enhance firmware protection:
- Ensure the integrity of the code.
- Preserve secrets.
However, OP-TEE cannot trust the Non-secure context for this. Expecting Linux to call a clean-up service does not seem reliable to me.
In the downstream stm32mp platform, we have implemented the clean-up but above all, ensured that Linux never has access to the remote processor firmware code and data by using firewall configurations. For time being we print a warning message on OP-TEE upstream[1]
[1]https://elixir.bootlin.com/op-tee/latest/source/core/drivers/remoteproc/stm3...
Thanks, Arnaud
I plan to upstream this part in a second step, associated with a memory protection strategy. This should be directly implemented in the OP-TEE service to avoid reliance on Linux error management. Nonetheless, an API could be added to clean the memory if needed.
Thanks, Arnaud
More comments to come later.
Mathieu
- /*
* We assume here that the memory mapping is the same between the TEE and Linux kernel
* contexts. Else a new TEE remoteproc service could be needed to get a copy of the
* resource table
*/
- rsc_va = ioremap_wc(rsc_table, table_sz);
- if (IS_ERR_OR_NULL(rsc_va)) {
dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %pa+%zx\n",
&rsc_table, table_sz);
return -ENOMEM;
- }
- /*
* Create a copy of the resource table to have the same behavior as the elf loader.
* This cached table will be used after the remoteproc stops to free resources, and for
* crash recovery to reapply the settings.
*/
- rproc->cached_table = kmemdup((__force void *)rsc_va, table_sz, GFP_KERNEL);
- if (!rproc->cached_table) {
ret = -ENOMEM;
goto out;
- }
- rproc->table_ptr = rproc->cached_table;
- rproc->table_sz = table_sz;
+out:
- iounmap(rsc_va);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_parse_fw);
+struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc,
const struct firmware *fw)
+{
- struct tee_rproc *trproc = rproc->tee_interface;
- phys_addr_t rsc_table;
- size_t table_sz;
- int ret;
- if (!trproc)
return ERR_PTR(-EINVAL);
- ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
- if (ret)
return ERR_PTR(ret);
- rproc->table_sz = table_sz;
- if (!table_sz)
return NULL;
- /*
* At this step the memory area that contains the resource table should have be declared
* in the remote proc platform driver and allocated by rproc_alloc_registered_carveouts().
*/
- return (struct resource_table *)rproc_pa_to_va(rproc, rsc_table, table_sz, NULL);
+} +EXPORT_SYMBOL_GPL(tee_rproc_find_loaded_rsc_table);
+int tee_rproc_start(struct rproc *rproc) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret = 0;
- if (!trproc)
return -EINVAL;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_START_FW, &arg, param, 0);
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_START_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
return -EIO;
- }
- return 0;
+} +EXPORT_SYMBOL_GPL(tee_rproc_start);
+int tee_rproc_stop(struct rproc *rproc) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret;
- if (!trproc)
return -EINVAL;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_STOP_FW, &arg, param, 0);
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_STOP_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
ret = -EIO;
- }
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_stop);
+static const struct tee_client_device_id tee_rproc_id_table[] = {
- {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)},
- {}
+};
+struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_ioctl_open_session_arg sess_arg;
- struct tee_client_device *tee_device;
- struct tee_rproc *trproc, *p_err;
- int ret;
- /*
* Test if the device has been probed by the TEE bus. In case of failure, we ignore the
* reason. The bus could be not yet probed or the service not available in the secure
* firmware.The assumption in such a case is that the TEE remoteproc is not probed.
*/
- if (!tee_rproc_ctx)
return ERR_PTR(-EPROBE_DEFER);
- /* Prevent tee rproc module from being removed */
- if (!try_module_get(THIS_MODULE)) {
dev_err(tee_rproc_ctx->dev, "can't get owner\n");
return ERR_PTR(-ENODEV);
- }
- trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
- if (!trproc) {
p_err = ERR_PTR(-ENOMEM);
goto module_put;
- }
- tee_device = to_tee_client_device(tee_rproc_ctx->dev);
- memset(&sess_arg, 0, sizeof(sess_arg));
- memcpy(sess_arg.uuid, tee_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
- sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
- sess_arg.num_params = 1;
- param[0] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
.u.value.a = rproc_id,
- };
- ret = tee_client_open_session(tee_rproc_ctx->tee_ctx, &sess_arg, param);
- if (ret < 0 || sess_arg.ret != 0) {
dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret);
p_err = ERR_PTR(-EINVAL);
goto module_put;
- }
- trproc->parent = dev;
- trproc->rproc_id = rproc_id;
- trproc->session_id = sess_arg.session;
- trproc->rproc = rproc;
- rproc->tee_interface = trproc;
- list_add_tail(&trproc->node, &tee_rproc_ctx->sessions);
- return trproc;
+module_put:
- module_put(THIS_MODULE);
- return p_err;
+} +EXPORT_SYMBOL_GPL(tee_rproc_register);
+int tee_rproc_unregister(struct tee_rproc *trproc) +{
- struct rproc *rproc = trproc->rproc;
- int ret;
- ret = tee_client_close_session(tee_rproc_ctx->tee_ctx, trproc->session_id);
- if (ret < 0)
dev_err(trproc->parent, "tee_client_close_session failed, err: %x\n", ret);
- list_del(&trproc->node);
- rproc->tee_interface = NULL;
- module_put(THIS_MODULE);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_unregister);
+static int tee_rproc_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{
- /* Today we support only the OP-TEE, could be extend to other tees */
- return (ver->impl_id == TEE_IMPL_ID_OPTEE);
+}
+static int tee_rproc_probe(struct device *dev) +{
- struct tee_context *tee_ctx;
- int ret;
- /* Open context with TEE driver */
- tee_ctx = tee_client_open_context(NULL, tee_rproc_ctx_match, NULL, NULL);
- if (IS_ERR(tee_ctx))
return PTR_ERR(tee_ctx);
- tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_rproc_ctx), GFP_KERNEL);
- if (!tee_rproc_ctx) {
ret = -ENOMEM;
goto err;
- }
- tee_rproc_ctx->dev = dev;
- tee_rproc_ctx->tee_ctx = tee_ctx;
- INIT_LIST_HEAD(&tee_rproc_ctx->sessions);
- return 0;
+err:
- tee_client_close_context(tee_ctx);
- return ret;
+}
+static int tee_rproc_remove(struct device *dev) +{
- struct tee_rproc *entry, *tmp;
- list_for_each_entry_safe(entry, tmp, &tee_rproc_ctx->sessions, node) {
tee_client_close_session(tee_rproc_ctx->tee_ctx, entry->session_id);
list_del(&entry->node);
kfree(entry);
- }
- tee_client_close_context(tee_rproc_ctx->tee_ctx);
- return 0;
+}
+MODULE_DEVICE_TABLE(tee, tee_rproc_id_table);
+static struct tee_client_driver tee_rproc_fw_driver = {
- .id_table = tee_rproc_id_table,
- .driver = {
.name = KBUILD_MODNAME,
.bus = &tee_bus_type,
.probe = tee_rproc_probe,
.remove = tee_rproc_remove,
- },
+};
+static int __init tee_rproc_fw_mod_init(void) +{
- return driver_register(&tee_rproc_fw_driver.driver);
+}
+static void __exit tee_rproc_fw_mod_exit(void) +{
- driver_unregister(&tee_rproc_fw_driver.driver);
+}
+module_init(tee_rproc_fw_mod_init); +module_exit(tee_rproc_fw_mod_exit);
+MODULE_DESCRIPTION(" remote processor support with a TEE application"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 8fd0d7f63c8e..fbe1d6793709 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -503,6 +503,8 @@ enum rproc_features { RPROC_MAX_FEATURES, }; +struct tee_rproc;
/**
- struct rproc - represents a physical remote processor device
- @node: list node of this rproc object
@@ -545,6 +547,7 @@ enum rproc_features {
- @cdev: character device of the rproc
- @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release
- @features: indicate remoteproc features
*/
- @tee_interface: pointer to the remoteproc tee context
struct rproc { struct list_head node; @@ -586,6 +589,7 @@ struct rproc { struct cdev cdev; bool cdev_put_on_release; DECLARE_BITMAP(features, RPROC_MAX_FEATURES);
- struct tee_rproc *tee_interface;
}; /** diff --git a/include/linux/remoteproc_tee.h b/include/linux/remoteproc_tee.h new file mode 100644 index 000000000000..ccf34a218d54 --- /dev/null +++ b/include/linux/remoteproc_tee.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/*
- Copyright(c) 2024 STMicroelectronics
- */
+#ifndef REMOTEPROC_TEE_H +#define REMOTEPROC_TEE_H
+#include <linux/tee_drv.h> +#include <linux/firmware.h> +#include <linux/remoteproc.h>
+struct rproc;
+/**
- struct tee_rproc - TEE remoteproc structure
- @node: Reference in list
- @rproc: Remoteproc reference
- @parent: Parent device
- @rproc_id: Identifier of the target firmware
- @session_id: TEE session identifier
- */
+struct tee_rproc {
- struct list_head node;
- struct rproc *rproc;
- struct device *parent;
- u32 rproc_id;
- u32 session_id;
+};
+#if IS_REACHABLE(CONFIG_REMOTEPROC_TEE)
+struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc,
unsigned int rproc_id);
+int tee_rproc_unregister(struct tee_rproc *trproc); +int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw); +int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw); +struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc,
const struct firmware *fw);
+int tee_rproc_start(struct rproc *rproc); +int tee_rproc_stop(struct rproc *rproc);
+#else
+static inline struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc,
unsigned int rproc_id)
+{
- return ERR_PTR(-ENODEV);
+}
+static int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_unregister(struct tee_rproc *trproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_start(struct rproc *rproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_stop(struct rproc *rproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline struct resource_table * +tee_rproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return NULL;
+} +#endif /* CONFIG_REMOTEPROC_TEE */
+#endif /* REMOTEPROC_TEE_H */
2.25.1
On Fri, Jul 05, 2024 at 09:33:55AM +0200, Arnaud POULIQUEN wrote:
On 7/4/24 17:32, Mathieu Poirier wrote:
On Thu, Jul 04, 2024 at 10:05:24AM +0200, Arnaud POULIQUEN wrote:
On 7/3/24 17:14, Mathieu Poirier wrote:
On Wed, Jul 03, 2024 at 09:19:44AM +0200, Arnaud POULIQUEN wrote:
Hello Mathieu
On 7/2/24 18:44, Mathieu Poirier wrote:
Good morning,
On Fri, Jun 21, 2024 at 04:37:56PM +0200, Arnaud Pouliquen wrote: > Add a remoteproc TEE (Trusted Execution Environment) driver > that will be probed by the TEE bus. If the associated Trusted > application is supported on secure part this driver offers a client > interface to load a firmware by the secure part. > This firmware could be authenticated by the secure trusted application. > > Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@foss.st.com > --- > Updates vs previous version: > - rename tee_remoteproc.* file to rmeoteproc_tee.*, > - rename TEE_REMOTEPROC config to REMOTEPROC_TEE, > - remove "stm32" in some variable declarations, > - remove useless "remoteproc_internal.h" include, > - fix tee_rproc_ctx devm_kzalloc. > - rework module description > --- > drivers/remoteproc/Kconfig | 10 + > drivers/remoteproc/Makefile | 1 + > drivers/remoteproc/remoteproc_tee.c | 446 ++++++++++++++++++++++++++++ > include/linux/remoteproc.h | 4 + > include/linux/remoteproc_tee.h | 100 +++++++ > 5 files changed, 561 insertions(+) > create mode 100644 drivers/remoteproc/remoteproc_tee.c > create mode 100644 include/linux/remoteproc_tee.h > > diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig > index 48845dc8fa85..278f197acb90 100644 > --- a/drivers/remoteproc/Kconfig > +++ b/drivers/remoteproc/Kconfig > @@ -365,6 +365,16 @@ config XLNX_R5_REMOTEPROC > > It's safe to say N if not interested in using RPU r5f cores. > > + > +config REMOTEPROC_TEE > + tristate "Remoteproc support by a TEE application" > + depends on OPTEE > + help > + Support a remote processor with a TEE application. The Trusted > + Execution Context is responsible for loading the trusted firmware > + image and managing the remote processor's lifecycle. > + This can be either built-in or a loadable module. > + > endif # REMOTEPROC > > endmenu > diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile > index 91314a9b43ce..b4eb37177005 100644 > --- a/drivers/remoteproc/Makefile > +++ b/drivers/remoteproc/Makefile > @@ -36,6 +36,7 @@ obj-$(CONFIG_RCAR_REMOTEPROC) += rcar_rproc.o > obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o > obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o > obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o > +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o > obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o > obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o > obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o > diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c > new file mode 100644 > index 000000000000..70cb67451767 > --- /dev/null > +++ b/drivers/remoteproc/remoteproc_tee.c > @@ -0,0 +1,446 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * Copyright (C) STMicroelectronics 2024 > + * Author: Arnaud Pouliquen arnaud.pouliquen@foss.st.com > + */ > + > +#include <linux/firmware.h> > +#include <linux/io.h> > +#include <linux/module.h> > +#include <linux/remoteproc.h> > +#include <linux/remoteproc_tee.h> > +#include <linux/slab.h> > +#include <linux/tee_drv.h> > + > +#define MAX_TEE_PARAM_ARRY_MEMBER 4 > + > +/* > + * Authentication of the firmware and load in the remote processor memory > + * > + * [in] params[0].value.a: unique 32bit identifier of the remote processor > + * [in] params[1].memref: buffer containing the image of the buffer > + */ > +#define TA_RPROC_FW_CMD_LOAD_FW 1 > + > +/* > + * Start the remote processor > + * > + * [in] params[0].value.a: unique 32bit identifier of the remote processor > + */ > +#define TA_RPROC_FW_CMD_START_FW 2 > + > +/* > + * Stop the remote processor > + * > + * [in] params[0].value.a: unique 32bit identifier of the remote processor > + */ > +#define TA_RPROC_FW_CMD_STOP_FW 3 > + > +/* > + * Return the address of the resource table, or 0 if not found > + * No check is done to verify that the address returned is accessible by > + * the non secure context. If the resource table is loaded in a protected > + * memory the access by the non secure context will lead to a data abort. > + * > + * [in] params[0].value.a: unique 32bit identifier of the remote processor > + * [out] params[1].value.a: 32bit LSB resource table memory address > + * [out] params[1].value.b: 32bit MSB resource table memory address > + * [out] params[2].value.a: 32bit LSB resource table memory size > + * [out] params[2].value.b: 32bit MSB resource table memory size > + */ > +#define TA_RPROC_FW_CMD_GET_RSC_TABLE 4 > + > +/* > + * Return the address of the core dump > + * > + * [in] params[0].value.a: unique 32bit identifier of the remote processor > + * [out] params[1].memref: address of the core dump image if exist, > + * else return Null > + */ > +#define TA_RPROC_FW_CMD_GET_COREDUMP 5 > + > +struct tee_rproc_context { > + struct list_head sessions; > + struct tee_context *tee_ctx; > + struct device *dev; > +}; > + > +static struct tee_rproc_context *tee_rproc_ctx; > + > +static void tee_rproc_prepare_args(struct tee_rproc *trproc, int cmd, > + struct tee_ioctl_invoke_arg *arg, > + struct tee_param *param, > + unsigned int num_params) > +{ > + memset(arg, 0, sizeof(*arg)); > + memset(param, 0, MAX_TEE_PARAM_ARRY_MEMBER * sizeof(*param)); > + > + arg->func = cmd; > + arg->session = trproc->session_id; > + arg->num_params = num_params + 1; > + > + param[0] = (struct tee_param) { > + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, > + .u.value.a = trproc->rproc_id, > + }; > +} > + > +int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) > +{ > + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER]; > + struct tee_rproc *trproc = rproc->tee_interface; > + struct tee_ioctl_invoke_arg arg; > + struct tee_shm *fw_shm; > + int ret; > + > + if (!trproc) > + return -EINVAL; > + > + fw_shm = tee_shm_register_kernel_buf(tee_rproc_ctx->tee_ctx, (void *)fw->data, fw->size); > + if (IS_ERR(fw_shm)) > + return PTR_ERR(fw_shm); > + > + tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_LOAD_FW, &arg, param, 1); > + > + /* Provide the address of the firmware image */ > + param[1] = (struct tee_param) { > + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT, > + .u.memref = { > + .shm = fw_shm, > + .size = fw->size, > + .shm_offs = 0, > + }, > + }; > + > + ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param); > + if (ret < 0 || arg.ret != 0) { > + dev_err(tee_rproc_ctx->dev, > + "TA_RPROC_FW_CMD_LOAD_FW invoke failed TEE err: %x, ret:%x\n", > + arg.ret, ret); > + if (!ret) > + ret = -EIO; > + } > + > + tee_shm_free(fw_shm); > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(tee_rproc_load_fw); > + > +static int tee_rproc_get_loaded_rsc_table(struct rproc *rproc, phys_addr_t *rsc_pa, > + size_t *table_sz) > +{ > + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER]; > + struct tee_rproc *trproc = rproc->tee_interface; > + struct tee_ioctl_invoke_arg arg; > + int ret; > + > + tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_GET_RSC_TABLE, &arg, param, 2); > + > + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; > + param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; > + > + ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param); > + if (ret < 0 || arg.ret != 0) { > + dev_err(tee_rproc_ctx->dev, > + "TA_RPROC_FW_CMD_GET_RSC_TABLE invoke failed TEE err: %x, ret:%x\n", > + arg.ret, ret); > + return -EIO; > + } > + > + *table_sz = param[2].u.value.a; > + > + if (*table_sz) > + *rsc_pa = param[1].u.value.a; > + else > + *rsc_pa = 0; > + > + return 0; > +} > + > +int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) > +{ > + phys_addr_t rsc_table; > + void __iomem *rsc_va; > + size_t table_sz; > + int ret; > + > + ret = tee_rproc_load_fw(rproc, fw); > + if (ret) > + return ret; > + > + ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz); > + if (ret) > + return ret;
Do we need an API to unload the firware from memory too? If anything fails we still have firmware loaded into memory.
For the time being, the clean-up of the memory is not implemented in OP-TEE.
In my opinion cleaning up the firmware image loaded to memory when something fails is important and should be part of this patchset. Looking at the error path in this function, it is clear something is missing.
Please, could you detail what risks you see in not implementing the memory clean-up at this first step?
Almost everything in the Linux kernel is about allocation and release of resources, the pattern is ubiquitous. Reading function tee_rproc_parse_fw(), the firmware is loaded by tee_rproc_load_fw() but never released - people reading this driver will wonder about that. There is also the possibility that other implementations will use the free'd memory for other purposes. This interface driver is meant to be generic and as such needs to take care of those corner cases.
Thank you for the clarification. If we want to support this, handling the error here would not be sufficient. For instance, many things can go wrong in rproc_start().
I agree.
So either we do nothing, as with the ELF support, and we put a comment in the remoteproc tee; or we may need to introduce new rproc ops to clean up/free resources in case of errors in the remoteproc core.
Doing nothing is not an option. The ELF case is easy because the cleanup only involves rproc->cached_table - for the TEE it also involves freeing the FW image.
I suggest introducting a new core function, i.e rproc_release_fw(), to counterbalance rproc_parse_fw():
rproc_release_fw() { if (rproc->tee_interface) tee_rproc_unload_fw(); kfree(rproc->cached_table); rproc->cached_table = NULL; rproc->table_ptr = NULL; }
Function rproc_release_fw() would be called in rproc_fw_boot()'s error path and in rproc_shutdown().
Or perhaps do you have another alternative to propose?
Thanks, Arnaud
Currently, OP-TEE upstream does not clean the memory. Upon the next load, it will either be overwritten or left unused by the newly authenticated firmware.
If your concern is about the ability to read the code, the code is already accessible in the binary firmware image on the Linux filesystem.
As a next step a platform-dependent strategy has to be implemented to enhance firmware protection:
- Ensure the integrity of the code.
- Preserve secrets.
However, OP-TEE cannot trust the Non-secure context for this. Expecting Linux to call a clean-up service does not seem reliable to me.
In the downstream stm32mp platform, we have implemented the clean-up but above all, ensured that Linux never has access to the remote processor firmware code and data by using firewall configurations. For time being we print a warning message on OP-TEE upstream[1]
[1]https://elixir.bootlin.com/op-tee/latest/source/core/drivers/remoteproc/stm3...
Thanks, Arnaud
I plan to upstream this part in a second step, associated with a memory protection strategy. This should be directly implemented in the OP-TEE service to avoid reliance on Linux error management. Nonetheless, an API could be added to clean the memory if needed.
Thanks, Arnaud
More comments to come later.
Mathieu
> + > + /* > + * We assume here that the memory mapping is the same between the TEE and Linux kernel > + * contexts. Else a new TEE remoteproc service could be needed to get a copy of the > + * resource table > + */ > + rsc_va = ioremap_wc(rsc_table, table_sz); > + if (IS_ERR_OR_NULL(rsc_va)) { > + dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %pa+%zx\n", > + &rsc_table, table_sz); > + return -ENOMEM; > + } > + > + /* > + * Create a copy of the resource table to have the same behavior as the elf loader. > + * This cached table will be used after the remoteproc stops to free resources, and for > + * crash recovery to reapply the settings. > + */ > + rproc->cached_table = kmemdup((__force void *)rsc_va, table_sz, GFP_KERNEL); > + if (!rproc->cached_table) { > + ret = -ENOMEM; > + goto out; > + } > + > + rproc->table_ptr = rproc->cached_table; > + rproc->table_sz = table_sz; > + > +out: > + iounmap(rsc_va); > + return ret; > +} > +EXPORT_SYMBOL_GPL(tee_rproc_parse_fw); > + > +struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc, > + const struct firmware *fw) > +{ > + struct tee_rproc *trproc = rproc->tee_interface; > + phys_addr_t rsc_table; > + size_t table_sz; > + int ret; > + > + if (!trproc) > + return ERR_PTR(-EINVAL); > + > + ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz); > + if (ret) > + return ERR_PTR(ret); > + > + rproc->table_sz = table_sz; > + if (!table_sz) > + return NULL; > + > + /* > + * At this step the memory area that contains the resource table should have be declared > + * in the remote proc platform driver and allocated by rproc_alloc_registered_carveouts(). > + */ > + return (struct resource_table *)rproc_pa_to_va(rproc, rsc_table, table_sz, NULL); > +} > +EXPORT_SYMBOL_GPL(tee_rproc_find_loaded_rsc_table); > + > +int tee_rproc_start(struct rproc *rproc) > +{ > + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER]; > + struct tee_rproc *trproc = rproc->tee_interface; > + struct tee_ioctl_invoke_arg arg; > + int ret = 0; > + > + if (!trproc) > + return -EINVAL; > + > + tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_START_FW, &arg, param, 0); > + > + ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param); > + if (ret < 0 || arg.ret != 0) { > + dev_err(tee_rproc_ctx->dev, > + "TA_RPROC_FW_CMD_START_FW invoke failed TEE err: %x, ret:%x\n", > + arg.ret, ret); > + if (!ret) > + return -EIO; > + } > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(tee_rproc_start); > + > +int tee_rproc_stop(struct rproc *rproc) > +{ > + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER]; > + struct tee_rproc *trproc = rproc->tee_interface; > + struct tee_ioctl_invoke_arg arg; > + int ret; > + > + if (!trproc) > + return -EINVAL; > + > + tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_STOP_FW, &arg, param, 0); > + > + ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param); > + if (ret < 0 || arg.ret != 0) { > + dev_err(tee_rproc_ctx->dev, > + "TA_RPROC_FW_CMD_STOP_FW invoke failed TEE err: %x, ret:%x\n", > + arg.ret, ret); > + if (!ret) > + ret = -EIO; > + } > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(tee_rproc_stop); > + > +static const struct tee_client_device_id tee_rproc_id_table[] = { > + {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)}, > + {} > +}; > + > +struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id) > +{ > + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER]; > + struct tee_ioctl_open_session_arg sess_arg; > + struct tee_client_device *tee_device; > + struct tee_rproc *trproc, *p_err; > + int ret; > + > + /* > + * Test if the device has been probed by the TEE bus. In case of failure, we ignore the > + * reason. The bus could be not yet probed or the service not available in the secure > + * firmware.The assumption in such a case is that the TEE remoteproc is not probed. > + */ > + if (!tee_rproc_ctx) > + return ERR_PTR(-EPROBE_DEFER); > + > + /* Prevent tee rproc module from being removed */ > + if (!try_module_get(THIS_MODULE)) { > + dev_err(tee_rproc_ctx->dev, "can't get owner\n"); > + return ERR_PTR(-ENODEV); > + } > + > + trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL); > + if (!trproc) { > + p_err = ERR_PTR(-ENOMEM); > + goto module_put; > + } > + tee_device = to_tee_client_device(tee_rproc_ctx->dev); > + memset(&sess_arg, 0, sizeof(sess_arg)); > + > + memcpy(sess_arg.uuid, tee_device->id.uuid.b, TEE_IOCTL_UUID_LEN); > + > + sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; > + sess_arg.num_params = 1; > + > + param[0] = (struct tee_param) { > + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, > + .u.value.a = rproc_id, > + }; > + > + ret = tee_client_open_session(tee_rproc_ctx->tee_ctx, &sess_arg, param); > + if (ret < 0 || sess_arg.ret != 0) { > + dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret); > + p_err = ERR_PTR(-EINVAL); > + goto module_put; > + } > + > + trproc->parent = dev; > + trproc->rproc_id = rproc_id; > + trproc->session_id = sess_arg.session; > + > + trproc->rproc = rproc; > + rproc->tee_interface = trproc; > + > + list_add_tail(&trproc->node, &tee_rproc_ctx->sessions); > + > + return trproc; > + > +module_put: > + module_put(THIS_MODULE); > + return p_err; > +} > +EXPORT_SYMBOL_GPL(tee_rproc_register); > + > +int tee_rproc_unregister(struct tee_rproc *trproc) > +{ > + struct rproc *rproc = trproc->rproc; > + int ret; > + > + ret = tee_client_close_session(tee_rproc_ctx->tee_ctx, trproc->session_id); > + if (ret < 0) > + dev_err(trproc->parent, "tee_client_close_session failed, err: %x\n", ret); > + > + list_del(&trproc->node); > + rproc->tee_interface = NULL; > + > + module_put(THIS_MODULE); > + > + return ret; > +} > +EXPORT_SYMBOL_GPL(tee_rproc_unregister); > + > +static int tee_rproc_ctx_match(struct tee_ioctl_version_data *ver, const void *data) > +{ > + /* Today we support only the OP-TEE, could be extend to other tees */ > + return (ver->impl_id == TEE_IMPL_ID_OPTEE); > +} > + > +static int tee_rproc_probe(struct device *dev) > +{ > + struct tee_context *tee_ctx; > + int ret; > + > + /* Open context with TEE driver */ > + tee_ctx = tee_client_open_context(NULL, tee_rproc_ctx_match, NULL, NULL); > + if (IS_ERR(tee_ctx)) > + return PTR_ERR(tee_ctx); > + > + tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_rproc_ctx), GFP_KERNEL); > + if (!tee_rproc_ctx) { > + ret = -ENOMEM; > + goto err; > + } > + > + tee_rproc_ctx->dev = dev; > + tee_rproc_ctx->tee_ctx = tee_ctx; > + INIT_LIST_HEAD(&tee_rproc_ctx->sessions); > + > + return 0; > +err: > + tee_client_close_context(tee_ctx); > + > + return ret; > +} > + > +static int tee_rproc_remove(struct device *dev) > +{ > + struct tee_rproc *entry, *tmp; > + > + list_for_each_entry_safe(entry, tmp, &tee_rproc_ctx->sessions, node) { > + tee_client_close_session(tee_rproc_ctx->tee_ctx, entry->session_id); > + list_del(&entry->node); > + kfree(entry); > + } > + > + tee_client_close_context(tee_rproc_ctx->tee_ctx); > + > + return 0; > +} > + > +MODULE_DEVICE_TABLE(tee, tee_rproc_id_table); > + > +static struct tee_client_driver tee_rproc_fw_driver = { > + .id_table = tee_rproc_id_table, > + .driver = { > + .name = KBUILD_MODNAME, > + .bus = &tee_bus_type, > + .probe = tee_rproc_probe, > + .remove = tee_rproc_remove, > + }, > +}; > + > +static int __init tee_rproc_fw_mod_init(void) > +{ > + return driver_register(&tee_rproc_fw_driver.driver); > +} > + > +static void __exit tee_rproc_fw_mod_exit(void) > +{ > + driver_unregister(&tee_rproc_fw_driver.driver); > +} > + > +module_init(tee_rproc_fw_mod_init); > +module_exit(tee_rproc_fw_mod_exit); > + > +MODULE_DESCRIPTION(" remote processor support with a TEE application"); > +MODULE_LICENSE("GPL"); > diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h > index 8fd0d7f63c8e..fbe1d6793709 100644 > --- a/include/linux/remoteproc.h > +++ b/include/linux/remoteproc.h > @@ -503,6 +503,8 @@ enum rproc_features { > RPROC_MAX_FEATURES, > }; > > +struct tee_rproc; > + > /** > * struct rproc - represents a physical remote processor device > * @node: list node of this rproc object > @@ -545,6 +547,7 @@ enum rproc_features { > * @cdev: character device of the rproc > * @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release > * @features: indicate remoteproc features > + * @tee_interface: pointer to the remoteproc tee context > */ > struct rproc { > struct list_head node; > @@ -586,6 +589,7 @@ struct rproc { > struct cdev cdev; > bool cdev_put_on_release; > DECLARE_BITMAP(features, RPROC_MAX_FEATURES); > + struct tee_rproc *tee_interface; > }; > > /** > diff --git a/include/linux/remoteproc_tee.h b/include/linux/remoteproc_tee.h > new file mode 100644 > index 000000000000..ccf34a218d54 > --- /dev/null > +++ b/include/linux/remoteproc_tee.h > @@ -0,0 +1,100 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * Copyright(c) 2024 STMicroelectronics > + */ > + > +#ifndef REMOTEPROC_TEE_H > +#define REMOTEPROC_TEE_H > + > +#include <linux/tee_drv.h> > +#include <linux/firmware.h> > +#include <linux/remoteproc.h> > + > +struct rproc; > + > +/** > + * struct tee_rproc - TEE remoteproc structure > + * @node: Reference in list > + * @rproc: Remoteproc reference > + * @parent: Parent device > + * @rproc_id: Identifier of the target firmware > + * @session_id: TEE session identifier > + */ > +struct tee_rproc { > + struct list_head node; > + struct rproc *rproc; > + struct device *parent; > + u32 rproc_id; > + u32 session_id; > +}; > + > +#if IS_REACHABLE(CONFIG_REMOTEPROC_TEE) > + > +struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc, > + unsigned int rproc_id); > +int tee_rproc_unregister(struct tee_rproc *trproc); > +int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw); > +int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw); > +struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc, > + const struct firmware *fw); > +int tee_rproc_start(struct rproc *rproc); > +int tee_rproc_stop(struct rproc *rproc); > + > +#else > + > +static inline struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc, > + unsigned int rproc_id) > +{ > + return ERR_PTR(-ENODEV); > +} > + > +static int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) > +{ > + /* This shouldn't be possible */ > + WARN_ON(1); > + > + return 0; > +} > + > +static inline int tee_rproc_unregister(struct tee_rproc *trproc) > +{ > + /* This shouldn't be possible */ > + WARN_ON(1); > + > + return 0; > +} > + > +static inline int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) > +{ > + /* This shouldn't be possible */ > + WARN_ON(1); > + > + return 0; > +} > + > +static inline int tee_rproc_start(struct rproc *rproc) > +{ > + /* This shouldn't be possible */ > + WARN_ON(1); > + > + return 0; > +} > + > +static inline int tee_rproc_stop(struct rproc *rproc) > +{ > + /* This shouldn't be possible */ > + WARN_ON(1); > + > + return 0; > +} > + > +static inline struct resource_table * > +tee_rproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw) > +{ > + /* This shouldn't be possible */ > + WARN_ON(1); > + > + return NULL; > +} > +#endif /* CONFIG_REMOTEPROC_TEE */ > +#endif /* REMOTEPROC_TEE_H */ > -- > 2.25.1 >
On 7/8/24 17:43, Mathieu Poirier wrote:
On Fri, Jul 05, 2024 at 09:33:55AM +0200, Arnaud POULIQUEN wrote:
On 7/4/24 17:32, Mathieu Poirier wrote:
On Thu, Jul 04, 2024 at 10:05:24AM +0200, Arnaud POULIQUEN wrote:
On 7/3/24 17:14, Mathieu Poirier wrote:
On Wed, Jul 03, 2024 at 09:19:44AM +0200, Arnaud POULIQUEN wrote:
Hello Mathieu
On 7/2/24 18:44, Mathieu Poirier wrote: > Good morning, > > On Fri, Jun 21, 2024 at 04:37:56PM +0200, Arnaud Pouliquen wrote: >> Add a remoteproc TEE (Trusted Execution Environment) driver >> that will be probed by the TEE bus. If the associated Trusted >> application is supported on secure part this driver offers a client >> interface to load a firmware by the secure part. >> This firmware could be authenticated by the secure trusted application. >> >> Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@foss.st.com >> --- >> Updates vs previous version: >> - rename tee_remoteproc.* file to rmeoteproc_tee.*, >> - rename TEE_REMOTEPROC config to REMOTEPROC_TEE, >> - remove "stm32" in some variable declarations, >> - remove useless "remoteproc_internal.h" include, >> - fix tee_rproc_ctx devm_kzalloc. >> - rework module description >> --- >> drivers/remoteproc/Kconfig | 10 + >> drivers/remoteproc/Makefile | 1 + >> drivers/remoteproc/remoteproc_tee.c | 446 ++++++++++++++++++++++++++++ >> include/linux/remoteproc.h | 4 + >> include/linux/remoteproc_tee.h | 100 +++++++ >> 5 files changed, 561 insertions(+) >> create mode 100644 drivers/remoteproc/remoteproc_tee.c >> create mode 100644 include/linux/remoteproc_tee.h >> >> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig >> index 48845dc8fa85..278f197acb90 100644 >> --- a/drivers/remoteproc/Kconfig >> +++ b/drivers/remoteproc/Kconfig >> @@ -365,6 +365,16 @@ config XLNX_R5_REMOTEPROC >> >> It's safe to say N if not interested in using RPU r5f cores. >> >> + >> +config REMOTEPROC_TEE >> + tristate "Remoteproc support by a TEE application" >> + depends on OPTEE >> + help >> + Support a remote processor with a TEE application. The Trusted >> + Execution Context is responsible for loading the trusted firmware >> + image and managing the remote processor's lifecycle. >> + This can be either built-in or a loadable module. >> + >> endif # REMOTEPROC >> >> endmenu >> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile >> index 91314a9b43ce..b4eb37177005 100644 >> --- a/drivers/remoteproc/Makefile >> +++ b/drivers/remoteproc/Makefile >> @@ -36,6 +36,7 @@ obj-$(CONFIG_RCAR_REMOTEPROC) += rcar_rproc.o >> obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o >> obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o >> obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o >> +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o >> obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o >> obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o >> obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o >> diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c >> new file mode 100644 >> index 000000000000..70cb67451767 >> --- /dev/null >> +++ b/drivers/remoteproc/remoteproc_tee.c >> @@ -0,0 +1,446 @@ >> +// SPDX-License-Identifier: GPL-2.0-or-later >> +/* >> + * Copyright (C) STMicroelectronics 2024 >> + * Author: Arnaud Pouliquen arnaud.pouliquen@foss.st.com >> + */ >> + >> +#include <linux/firmware.h> >> +#include <linux/io.h> >> +#include <linux/module.h> >> +#include <linux/remoteproc.h> >> +#include <linux/remoteproc_tee.h> >> +#include <linux/slab.h> >> +#include <linux/tee_drv.h> >> + >> +#define MAX_TEE_PARAM_ARRY_MEMBER 4 >> + >> +/* >> + * Authentication of the firmware and load in the remote processor memory >> + * >> + * [in] params[0].value.a: unique 32bit identifier of the remote processor >> + * [in] params[1].memref: buffer containing the image of the buffer >> + */ >> +#define TA_RPROC_FW_CMD_LOAD_FW 1 >> + >> +/* >> + * Start the remote processor >> + * >> + * [in] params[0].value.a: unique 32bit identifier of the remote processor >> + */ >> +#define TA_RPROC_FW_CMD_START_FW 2 >> + >> +/* >> + * Stop the remote processor >> + * >> + * [in] params[0].value.a: unique 32bit identifier of the remote processor >> + */ >> +#define TA_RPROC_FW_CMD_STOP_FW 3 >> + >> +/* >> + * Return the address of the resource table, or 0 if not found >> + * No check is done to verify that the address returned is accessible by >> + * the non secure context. If the resource table is loaded in a protected >> + * memory the access by the non secure context will lead to a data abort. >> + * >> + * [in] params[0].value.a: unique 32bit identifier of the remote processor >> + * [out] params[1].value.a: 32bit LSB resource table memory address >> + * [out] params[1].value.b: 32bit MSB resource table memory address >> + * [out] params[2].value.a: 32bit LSB resource table memory size >> + * [out] params[2].value.b: 32bit MSB resource table memory size >> + */ >> +#define TA_RPROC_FW_CMD_GET_RSC_TABLE 4 >> + >> +/* >> + * Return the address of the core dump >> + * >> + * [in] params[0].value.a: unique 32bit identifier of the remote processor >> + * [out] params[1].memref: address of the core dump image if exist, >> + * else return Null >> + */ >> +#define TA_RPROC_FW_CMD_GET_COREDUMP 5 >> + >> +struct tee_rproc_context { >> + struct list_head sessions; >> + struct tee_context *tee_ctx; >> + struct device *dev; >> +}; >> + >> +static struct tee_rproc_context *tee_rproc_ctx; >> + >> +static void tee_rproc_prepare_args(struct tee_rproc *trproc, int cmd, >> + struct tee_ioctl_invoke_arg *arg, >> + struct tee_param *param, >> + unsigned int num_params) >> +{ >> + memset(arg, 0, sizeof(*arg)); >> + memset(param, 0, MAX_TEE_PARAM_ARRY_MEMBER * sizeof(*param)); >> + >> + arg->func = cmd; >> + arg->session = trproc->session_id; >> + arg->num_params = num_params + 1; >> + >> + param[0] = (struct tee_param) { >> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, >> + .u.value.a = trproc->rproc_id, >> + }; >> +} >> + >> +int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) >> +{ >> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER]; >> + struct tee_rproc *trproc = rproc->tee_interface; >> + struct tee_ioctl_invoke_arg arg; >> + struct tee_shm *fw_shm; >> + int ret; >> + >> + if (!trproc) >> + return -EINVAL; >> + >> + fw_shm = tee_shm_register_kernel_buf(tee_rproc_ctx->tee_ctx, (void *)fw->data, fw->size); >> + if (IS_ERR(fw_shm)) >> + return PTR_ERR(fw_shm); >> + >> + tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_LOAD_FW, &arg, param, 1); >> + >> + /* Provide the address of the firmware image */ >> + param[1] = (struct tee_param) { >> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT, >> + .u.memref = { >> + .shm = fw_shm, >> + .size = fw->size, >> + .shm_offs = 0, >> + }, >> + }; >> + >> + ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param); >> + if (ret < 0 || arg.ret != 0) { >> + dev_err(tee_rproc_ctx->dev, >> + "TA_RPROC_FW_CMD_LOAD_FW invoke failed TEE err: %x, ret:%x\n", >> + arg.ret, ret); >> + if (!ret) >> + ret = -EIO; >> + } >> + >> + tee_shm_free(fw_shm); >> + >> + return ret; >> +} >> +EXPORT_SYMBOL_GPL(tee_rproc_load_fw); >> + >> +static int tee_rproc_get_loaded_rsc_table(struct rproc *rproc, phys_addr_t *rsc_pa, >> + size_t *table_sz) >> +{ >> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER]; >> + struct tee_rproc *trproc = rproc->tee_interface; >> + struct tee_ioctl_invoke_arg arg; >> + int ret; >> + >> + tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_GET_RSC_TABLE, &arg, param, 2); >> + >> + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; >> + param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; >> + >> + ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param); >> + if (ret < 0 || arg.ret != 0) { >> + dev_err(tee_rproc_ctx->dev, >> + "TA_RPROC_FW_CMD_GET_RSC_TABLE invoke failed TEE err: %x, ret:%x\n", >> + arg.ret, ret); >> + return -EIO; >> + } >> + >> + *table_sz = param[2].u.value.a; >> + >> + if (*table_sz) >> + *rsc_pa = param[1].u.value.a; >> + else >> + *rsc_pa = 0; >> + >> + return 0; >> +} >> + >> +int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) >> +{ >> + phys_addr_t rsc_table; >> + void __iomem *rsc_va; >> + size_t table_sz; >> + int ret; >> + >> + ret = tee_rproc_load_fw(rproc, fw); >> + if (ret) >> + return ret; >> + >> + ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz); >> + if (ret) >> + return ret; > > Do we need an API to unload the firware from memory too? If anything fails we > still have firmware loaded into memory.
For the time being, the clean-up of the memory is not implemented in OP-TEE.
In my opinion cleaning up the firmware image loaded to memory when something fails is important and should be part of this patchset. Looking at the error path in this function, it is clear something is missing.
Please, could you detail what risks you see in not implementing the memory clean-up at this first step?
Almost everything in the Linux kernel is about allocation and release of resources, the pattern is ubiquitous. Reading function tee_rproc_parse_fw(), the firmware is loaded by tee_rproc_load_fw() but never released - people reading this driver will wonder about that. There is also the possibility that other implementations will use the free'd memory for other purposes. This interface driver is meant to be generic and as such needs to take care of those corner cases.
Thank you for the clarification. If we want to support this, handling the error here would not be sufficient. For instance, many things can go wrong in rproc_start().
I agree.
So either we do nothing, as with the ELF support, and we put a comment in the remoteproc tee; or we may need to introduce new rproc ops to clean up/free resources in case of errors in the remoteproc core.
Doing nothing is not an option. The ELF case is easy because the cleanup only involves rproc->cached_table - for the TEE it also involves freeing the FW image.
I suggest introducting a new core function, i.e rproc_release_fw(), to counterbalance rproc_parse_fw():
rproc_release_fw() { if (rproc->tee_interface) tee_rproc_unload_fw(); kfree(rproc->cached_table); rproc->cached_table = NULL; rproc->table_ptr = NULL; }
Function rproc_release_fw() would be called in rproc_fw_boot()'s error path and in rproc_shutdown().
I will implement this for the next version. This will also impact OP-TEE to implement the associated service, so it could take some time before the next version.
Thanks, Arnaud
Or perhaps do you have another alternative to propose?
Thanks, Arnaud
Currently, OP-TEE upstream does not clean the memory. Upon the next load, it will either be overwritten or left unused by the newly authenticated firmware.
If your concern is about the ability to read the code, the code is already accessible in the binary firmware image on the Linux filesystem.
As a next step a platform-dependent strategy has to be implemented to enhance firmware protection:
- Ensure the integrity of the code.
- Preserve secrets.
However, OP-TEE cannot trust the Non-secure context for this. Expecting Linux to call a clean-up service does not seem reliable to me.
In the downstream stm32mp platform, we have implemented the clean-up but above all, ensured that Linux never has access to the remote processor firmware code and data by using firewall configurations. For time being we print a warning message on OP-TEE upstream[1]
[1]https://elixir.bootlin.com/op-tee/latest/source/core/drivers/remoteproc/stm3...
Thanks, Arnaud
I plan to upstream this part in a second step, associated with a memory protection strategy. This should be directly implemented in the OP-TEE service to avoid reliance on Linux error management. Nonetheless, an API could be added to clean the memory if needed.
Thanks, Arnaud
> > More comments to come later. > > Mathieu > >> + >> + /* >> + * We assume here that the memory mapping is the same between the TEE and Linux kernel >> + * contexts. Else a new TEE remoteproc service could be needed to get a copy of the >> + * resource table >> + */ >> + rsc_va = ioremap_wc(rsc_table, table_sz); >> + if (IS_ERR_OR_NULL(rsc_va)) { >> + dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %pa+%zx\n", >> + &rsc_table, table_sz); >> + return -ENOMEM; >> + } >> + >> + /* >> + * Create a copy of the resource table to have the same behavior as the elf loader. >> + * This cached table will be used after the remoteproc stops to free resources, and for >> + * crash recovery to reapply the settings. >> + */ >> + rproc->cached_table = kmemdup((__force void *)rsc_va, table_sz, GFP_KERNEL); >> + if (!rproc->cached_table) { >> + ret = -ENOMEM; >> + goto out; >> + } >> + >> + rproc->table_ptr = rproc->cached_table; >> + rproc->table_sz = table_sz; >> + >> +out: >> + iounmap(rsc_va); >> + return ret; >> +} >> +EXPORT_SYMBOL_GPL(tee_rproc_parse_fw); >> + >> +struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc, >> + const struct firmware *fw) >> +{ >> + struct tee_rproc *trproc = rproc->tee_interface; >> + phys_addr_t rsc_table; >> + size_t table_sz; >> + int ret; >> + >> + if (!trproc) >> + return ERR_PTR(-EINVAL); >> + >> + ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz); >> + if (ret) >> + return ERR_PTR(ret); >> + >> + rproc->table_sz = table_sz; >> + if (!table_sz) >> + return NULL; >> + >> + /* >> + * At this step the memory area that contains the resource table should have be declared >> + * in the remote proc platform driver and allocated by rproc_alloc_registered_carveouts(). >> + */ >> + return (struct resource_table *)rproc_pa_to_va(rproc, rsc_table, table_sz, NULL); >> +} >> +EXPORT_SYMBOL_GPL(tee_rproc_find_loaded_rsc_table); >> + >> +int tee_rproc_start(struct rproc *rproc) >> +{ >> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER]; >> + struct tee_rproc *trproc = rproc->tee_interface; >> + struct tee_ioctl_invoke_arg arg; >> + int ret = 0; >> + >> + if (!trproc) >> + return -EINVAL; >> + >> + tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_START_FW, &arg, param, 0); >> + >> + ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param); >> + if (ret < 0 || arg.ret != 0) { >> + dev_err(tee_rproc_ctx->dev, >> + "TA_RPROC_FW_CMD_START_FW invoke failed TEE err: %x, ret:%x\n", >> + arg.ret, ret); >> + if (!ret) >> + return -EIO; >> + } >> + >> + return 0; >> +} >> +EXPORT_SYMBOL_GPL(tee_rproc_start); >> + >> +int tee_rproc_stop(struct rproc *rproc) >> +{ >> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER]; >> + struct tee_rproc *trproc = rproc->tee_interface; >> + struct tee_ioctl_invoke_arg arg; >> + int ret; >> + >> + if (!trproc) >> + return -EINVAL; >> + >> + tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_STOP_FW, &arg, param, 0); >> + >> + ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param); >> + if (ret < 0 || arg.ret != 0) { >> + dev_err(tee_rproc_ctx->dev, >> + "TA_RPROC_FW_CMD_STOP_FW invoke failed TEE err: %x, ret:%x\n", >> + arg.ret, ret); >> + if (!ret) >> + ret = -EIO; >> + } >> + >> + return ret; >> +} >> +EXPORT_SYMBOL_GPL(tee_rproc_stop); >> + >> +static const struct tee_client_device_id tee_rproc_id_table[] = { >> + {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)}, >> + {} >> +}; >> + >> +struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id) >> +{ >> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER]; >> + struct tee_ioctl_open_session_arg sess_arg; >> + struct tee_client_device *tee_device; >> + struct tee_rproc *trproc, *p_err; >> + int ret; >> + >> + /* >> + * Test if the device has been probed by the TEE bus. In case of failure, we ignore the >> + * reason. The bus could be not yet probed or the service not available in the secure >> + * firmware.The assumption in such a case is that the TEE remoteproc is not probed. >> + */ >> + if (!tee_rproc_ctx) >> + return ERR_PTR(-EPROBE_DEFER); >> + >> + /* Prevent tee rproc module from being removed */ >> + if (!try_module_get(THIS_MODULE)) { >> + dev_err(tee_rproc_ctx->dev, "can't get owner\n"); >> + return ERR_PTR(-ENODEV); >> + } >> + >> + trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL); >> + if (!trproc) { >> + p_err = ERR_PTR(-ENOMEM); >> + goto module_put; >> + } >> + tee_device = to_tee_client_device(tee_rproc_ctx->dev); >> + memset(&sess_arg, 0, sizeof(sess_arg)); >> + >> + memcpy(sess_arg.uuid, tee_device->id.uuid.b, TEE_IOCTL_UUID_LEN); >> + >> + sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; >> + sess_arg.num_params = 1; >> + >> + param[0] = (struct tee_param) { >> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, >> + .u.value.a = rproc_id, >> + }; >> + >> + ret = tee_client_open_session(tee_rproc_ctx->tee_ctx, &sess_arg, param); >> + if (ret < 0 || sess_arg.ret != 0) { >> + dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret); >> + p_err = ERR_PTR(-EINVAL); >> + goto module_put; >> + } >> + >> + trproc->parent = dev; >> + trproc->rproc_id = rproc_id; >> + trproc->session_id = sess_arg.session; >> + >> + trproc->rproc = rproc; >> + rproc->tee_interface = trproc; >> + >> + list_add_tail(&trproc->node, &tee_rproc_ctx->sessions); >> + >> + return trproc; >> + >> +module_put: >> + module_put(THIS_MODULE); >> + return p_err; >> +} >> +EXPORT_SYMBOL_GPL(tee_rproc_register); >> + >> +int tee_rproc_unregister(struct tee_rproc *trproc) >> +{ >> + struct rproc *rproc = trproc->rproc; >> + int ret; >> + >> + ret = tee_client_close_session(tee_rproc_ctx->tee_ctx, trproc->session_id); >> + if (ret < 0) >> + dev_err(trproc->parent, "tee_client_close_session failed, err: %x\n", ret); >> + >> + list_del(&trproc->node); >> + rproc->tee_interface = NULL; >> + >> + module_put(THIS_MODULE); >> + >> + return ret; >> +} >> +EXPORT_SYMBOL_GPL(tee_rproc_unregister); >> + >> +static int tee_rproc_ctx_match(struct tee_ioctl_version_data *ver, const void *data) >> +{ >> + /* Today we support only the OP-TEE, could be extend to other tees */ >> + return (ver->impl_id == TEE_IMPL_ID_OPTEE); >> +} >> + >> +static int tee_rproc_probe(struct device *dev) >> +{ >> + struct tee_context *tee_ctx; >> + int ret; >> + >> + /* Open context with TEE driver */ >> + tee_ctx = tee_client_open_context(NULL, tee_rproc_ctx_match, NULL, NULL); >> + if (IS_ERR(tee_ctx)) >> + return PTR_ERR(tee_ctx); >> + >> + tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_rproc_ctx), GFP_KERNEL); >> + if (!tee_rproc_ctx) { >> + ret = -ENOMEM; >> + goto err; >> + } >> + >> + tee_rproc_ctx->dev = dev; >> + tee_rproc_ctx->tee_ctx = tee_ctx; >> + INIT_LIST_HEAD(&tee_rproc_ctx->sessions); >> + >> + return 0; >> +err: >> + tee_client_close_context(tee_ctx); >> + >> + return ret; >> +} >> + >> +static int tee_rproc_remove(struct device *dev) >> +{ >> + struct tee_rproc *entry, *tmp; >> + >> + list_for_each_entry_safe(entry, tmp, &tee_rproc_ctx->sessions, node) { >> + tee_client_close_session(tee_rproc_ctx->tee_ctx, entry->session_id); >> + list_del(&entry->node); >> + kfree(entry); >> + } >> + >> + tee_client_close_context(tee_rproc_ctx->tee_ctx); >> + >> + return 0; >> +} >> + >> +MODULE_DEVICE_TABLE(tee, tee_rproc_id_table); >> + >> +static struct tee_client_driver tee_rproc_fw_driver = { >> + .id_table = tee_rproc_id_table, >> + .driver = { >> + .name = KBUILD_MODNAME, >> + .bus = &tee_bus_type, >> + .probe = tee_rproc_probe, >> + .remove = tee_rproc_remove, >> + }, >> +}; >> + >> +static int __init tee_rproc_fw_mod_init(void) >> +{ >> + return driver_register(&tee_rproc_fw_driver.driver); >> +} >> + >> +static void __exit tee_rproc_fw_mod_exit(void) >> +{ >> + driver_unregister(&tee_rproc_fw_driver.driver); >> +} >> + >> +module_init(tee_rproc_fw_mod_init); >> +module_exit(tee_rproc_fw_mod_exit); >> + >> +MODULE_DESCRIPTION(" remote processor support with a TEE application"); >> +MODULE_LICENSE("GPL"); >> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h >> index 8fd0d7f63c8e..fbe1d6793709 100644 >> --- a/include/linux/remoteproc.h >> +++ b/include/linux/remoteproc.h >> @@ -503,6 +503,8 @@ enum rproc_features { >> RPROC_MAX_FEATURES, >> }; >> >> +struct tee_rproc; >> + >> /** >> * struct rproc - represents a physical remote processor device >> * @node: list node of this rproc object >> @@ -545,6 +547,7 @@ enum rproc_features { >> * @cdev: character device of the rproc >> * @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release >> * @features: indicate remoteproc features >> + * @tee_interface: pointer to the remoteproc tee context >> */ >> struct rproc { >> struct list_head node; >> @@ -586,6 +589,7 @@ struct rproc { >> struct cdev cdev; >> bool cdev_put_on_release; >> DECLARE_BITMAP(features, RPROC_MAX_FEATURES); >> + struct tee_rproc *tee_interface; >> }; >> >> /** >> diff --git a/include/linux/remoteproc_tee.h b/include/linux/remoteproc_tee.h >> new file mode 100644 >> index 000000000000..ccf34a218d54 >> --- /dev/null >> +++ b/include/linux/remoteproc_tee.h >> @@ -0,0 +1,100 @@ >> +/* SPDX-License-Identifier: GPL-2.0-or-later */ >> +/* >> + * Copyright(c) 2024 STMicroelectronics >> + */ >> + >> +#ifndef REMOTEPROC_TEE_H >> +#define REMOTEPROC_TEE_H >> + >> +#include <linux/tee_drv.h> >> +#include <linux/firmware.h> >> +#include <linux/remoteproc.h> >> + >> +struct rproc; >> + >> +/** >> + * struct tee_rproc - TEE remoteproc structure >> + * @node: Reference in list >> + * @rproc: Remoteproc reference >> + * @parent: Parent device >> + * @rproc_id: Identifier of the target firmware >> + * @session_id: TEE session identifier >> + */ >> +struct tee_rproc { >> + struct list_head node; >> + struct rproc *rproc; >> + struct device *parent; >> + u32 rproc_id; >> + u32 session_id; >> +}; >> + >> +#if IS_REACHABLE(CONFIG_REMOTEPROC_TEE) >> + >> +struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc, >> + unsigned int rproc_id); >> +int tee_rproc_unregister(struct tee_rproc *trproc); >> +int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw); >> +int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw); >> +struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc, >> + const struct firmware *fw); >> +int tee_rproc_start(struct rproc *rproc); >> +int tee_rproc_stop(struct rproc *rproc); >> + >> +#else >> + >> +static inline struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc, >> + unsigned int rproc_id) >> +{ >> + return ERR_PTR(-ENODEV); >> +} >> + >> +static int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) >> +{ >> + /* This shouldn't be possible */ >> + WARN_ON(1); >> + >> + return 0; >> +} >> + >> +static inline int tee_rproc_unregister(struct tee_rproc *trproc) >> +{ >> + /* This shouldn't be possible */ >> + WARN_ON(1); >> + >> + return 0; >> +} >> + >> +static inline int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) >> +{ >> + /* This shouldn't be possible */ >> + WARN_ON(1); >> + >> + return 0; >> +} >> + >> +static inline int tee_rproc_start(struct rproc *rproc) >> +{ >> + /* This shouldn't be possible */ >> + WARN_ON(1); >> + >> + return 0; >> +} >> + >> +static inline int tee_rproc_stop(struct rproc *rproc) >> +{ >> + /* This shouldn't be possible */ >> + WARN_ON(1); >> + >> + return 0; >> +} >> + >> +static inline struct resource_table * >> +tee_rproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw) >> +{ >> + /* This shouldn't be possible */ >> + WARN_ON(1); >> + >> + return NULL; >> +} >> +#endif /* CONFIG_REMOTEPROC_TEE */ >> +#endif /* REMOTEPROC_TEE_H */ >> -- >> 2.25.1 >>
On Fri, Jun 21, 2024 at 04:37:56PM +0200, Arnaud Pouliquen wrote:
Add a remoteproc TEE (Trusted Execution Environment) driver that will be probed by the TEE bus. If the associated Trusted application is supported on secure part this driver offers a client interface to load a firmware by the secure part. This firmware could be authenticated by the secure trusted application.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@foss.st.com
Updates vs previous version:
- rename tee_remoteproc.* file to rmeoteproc_tee.*,
- rename TEE_REMOTEPROC config to REMOTEPROC_TEE,
- remove "stm32" in some variable declarations,
- remove useless "remoteproc_internal.h" include,
- fix tee_rproc_ctx devm_kzalloc.
- rework module description
drivers/remoteproc/Kconfig | 10 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/remoteproc_tee.c | 446 ++++++++++++++++++++++++++++ include/linux/remoteproc.h | 4 + include/linux/remoteproc_tee.h | 100 +++++++ 5 files changed, 561 insertions(+) create mode 100644 drivers/remoteproc/remoteproc_tee.c create mode 100644 include/linux/remoteproc_tee.h
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 48845dc8fa85..278f197acb90 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -365,6 +365,16 @@ config XLNX_R5_REMOTEPROC It's safe to say N if not interested in using RPU r5f cores.
+config REMOTEPROC_TEE
- tristate "Remoteproc support by a TEE application"
- depends on OPTEE
- help
Support a remote processor with a TEE application. The Trusted
Execution Context is responsible for loading the trusted firmware
image and managing the remote processor's lifecycle.
This can be either built-in or a loadable module.
endif # REMOTEPROC endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 91314a9b43ce..b4eb37177005 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_RCAR_REMOTEPROC) += rcar_rproc.o obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c new file mode 100644 index 000000000000..70cb67451767 --- /dev/null +++ b/drivers/remoteproc/remoteproc_tee.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*
- Copyright (C) STMicroelectronics 2024
- Author: Arnaud Pouliquen arnaud.pouliquen@foss.st.com
- */
+#include <linux/firmware.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/remoteproc.h> +#include <linux/remoteproc_tee.h> +#include <linux/slab.h> +#include <linux/tee_drv.h>
+#define MAX_TEE_PARAM_ARRY_MEMBER 4
+/*
- Authentication of the firmware and load in the remote processor memory
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [in] params[1].memref: buffer containing the image of the buffer
- */
+#define TA_RPROC_FW_CMD_LOAD_FW 1
+/*
- Start the remote processor
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- */
+#define TA_RPROC_FW_CMD_START_FW 2
+/*
- Stop the remote processor
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- */
+#define TA_RPROC_FW_CMD_STOP_FW 3
+/*
- Return the address of the resource table, or 0 if not found
- No check is done to verify that the address returned is accessible by
- the non secure context. If the resource table is loaded in a protected
- memory the access by the non secure context will lead to a data abort.
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [out] params[1].value.a: 32bit LSB resource table memory address
- [out] params[1].value.b: 32bit MSB resource table memory address
- [out] params[2].value.a: 32bit LSB resource table memory size
- [out] params[2].value.b: 32bit MSB resource table memory size
- */
+#define TA_RPROC_FW_CMD_GET_RSC_TABLE 4
+/*
- Return the address of the core dump
- [in] params[0].value.a: unique 32bit identifier of the remote processor
- [out] params[1].memref: address of the core dump image if exist,
else return Null
- */
+#define TA_RPROC_FW_CMD_GET_COREDUMP 5
+struct tee_rproc_context {
- struct list_head sessions;
- struct tee_context *tee_ctx;
- struct device *dev;
+};
+static struct tee_rproc_context *tee_rproc_ctx;
+static void tee_rproc_prepare_args(struct tee_rproc *trproc, int cmd,
struct tee_ioctl_invoke_arg *arg,
struct tee_param *param,
unsigned int num_params)
+{
- memset(arg, 0, sizeof(*arg));
- memset(param, 0, MAX_TEE_PARAM_ARRY_MEMBER * sizeof(*param));
- arg->func = cmd;
- arg->session = trproc->session_id;
- arg->num_params = num_params + 1;
- param[0] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
.u.value.a = trproc->rproc_id,
- };
+}
+int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- struct tee_shm *fw_shm;
- int ret;
- if (!trproc)
return -EINVAL;
- fw_shm = tee_shm_register_kernel_buf(tee_rproc_ctx->tee_ctx, (void *)fw->data, fw->size);
- if (IS_ERR(fw_shm))
return PTR_ERR(fw_shm);
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_LOAD_FW, &arg, param, 1);
- /* Provide the address of the firmware image */
- param[1] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
.u.memref = {
.shm = fw_shm,
.size = fw->size,
.shm_offs = 0,
},
- };
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_LOAD_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
ret = -EIO;
- }
- tee_shm_free(fw_shm);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_load_fw);
+static int tee_rproc_get_loaded_rsc_table(struct rproc *rproc, phys_addr_t *rsc_pa,
size_t *table_sz)
+{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_GET_RSC_TABLE, &arg, param, 2);
- param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
- param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_GET_RSC_TABLE invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
return -EIO;
- }
- *table_sz = param[2].u.value.a;
- if (*table_sz)
*rsc_pa = param[1].u.value.a;
- else
*rsc_pa = 0;
- return 0;
+}
+int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{
- phys_addr_t rsc_table;
- void __iomem *rsc_va;
- size_t table_sz;
- int ret;
- ret = tee_rproc_load_fw(rproc, fw);
- if (ret)
return ret;
- ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
- if (ret)
return ret;
- /*
* We assume here that the memory mapping is the same between the TEE and Linux kernel
* contexts. Else a new TEE remoteproc service could be needed to get a copy of the
* resource table
*/
- rsc_va = ioremap_wc(rsc_table, table_sz);
- if (IS_ERR_OR_NULL(rsc_va)) {
dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %pa+%zx\n",
&rsc_table, table_sz);
return -ENOMEM;
- }
- /*
* Create a copy of the resource table to have the same behavior as the elf loader.
* This cached table will be used after the remoteproc stops to free resources, and for
* crash recovery to reapply the settings.
*/
- rproc->cached_table = kmemdup((__force void *)rsc_va, table_sz, GFP_KERNEL);
- if (!rproc->cached_table) {
ret = -ENOMEM;
goto out;
- }
- rproc->table_ptr = rproc->cached_table;
- rproc->table_sz = table_sz;
+out:
- iounmap(rsc_va);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_parse_fw);
+struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc,
const struct firmware *fw)
+{
- struct tee_rproc *trproc = rproc->tee_interface;
- phys_addr_t rsc_table;
- size_t table_sz;
- int ret;
- if (!trproc)
return ERR_PTR(-EINVAL);
- ret = tee_rproc_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
- if (ret)
return ERR_PTR(ret);
- rproc->table_sz = table_sz;
- if (!table_sz)
return NULL;
- /*
* At this step the memory area that contains the resource table should have be declared
s/be/been
* in the remote proc platform driver and allocated by rproc_alloc_registered_carveouts().
remoteproc platform driver or DT?
I am done reviewing this set.
Thanks, Mathieu
*/
- return (struct resource_table *)rproc_pa_to_va(rproc, rsc_table, table_sz, NULL);
+} +EXPORT_SYMBOL_GPL(tee_rproc_find_loaded_rsc_table);
+int tee_rproc_start(struct rproc *rproc) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret = 0;
- if (!trproc)
return -EINVAL;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_START_FW, &arg, param, 0);
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_START_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
return -EIO;
- }
- return 0;
+} +EXPORT_SYMBOL_GPL(tee_rproc_start);
+int tee_rproc_stop(struct rproc *rproc) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_rproc *trproc = rproc->tee_interface;
- struct tee_ioctl_invoke_arg arg;
- int ret;
- if (!trproc)
return -EINVAL;
- tee_rproc_prepare_args(trproc, TA_RPROC_FW_CMD_STOP_FW, &arg, param, 0);
- ret = tee_client_invoke_func(tee_rproc_ctx->tee_ctx, &arg, param);
- if (ret < 0 || arg.ret != 0) {
dev_err(tee_rproc_ctx->dev,
"TA_RPROC_FW_CMD_STOP_FW invoke failed TEE err: %x, ret:%x\n",
arg.ret, ret);
if (!ret)
ret = -EIO;
- }
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_stop);
+static const struct tee_client_device_id tee_rproc_id_table[] = {
- {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)},
- {}
+};
+struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id) +{
- struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
- struct tee_ioctl_open_session_arg sess_arg;
- struct tee_client_device *tee_device;
- struct tee_rproc *trproc, *p_err;
- int ret;
- /*
* Test if the device has been probed by the TEE bus. In case of failure, we ignore the
* reason. The bus could be not yet probed or the service not available in the secure
* firmware.The assumption in such a case is that the TEE remoteproc is not probed.
*/
- if (!tee_rproc_ctx)
return ERR_PTR(-EPROBE_DEFER);
- /* Prevent tee rproc module from being removed */
- if (!try_module_get(THIS_MODULE)) {
dev_err(tee_rproc_ctx->dev, "can't get owner\n");
return ERR_PTR(-ENODEV);
- }
- trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
- if (!trproc) {
p_err = ERR_PTR(-ENOMEM);
goto module_put;
- }
- tee_device = to_tee_client_device(tee_rproc_ctx->dev);
- memset(&sess_arg, 0, sizeof(sess_arg));
- memcpy(sess_arg.uuid, tee_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
- sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
- sess_arg.num_params = 1;
- param[0] = (struct tee_param) {
.attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
.u.value.a = rproc_id,
- };
- ret = tee_client_open_session(tee_rproc_ctx->tee_ctx, &sess_arg, param);
- if (ret < 0 || sess_arg.ret != 0) {
dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret);
p_err = ERR_PTR(-EINVAL);
goto module_put;
- }
- trproc->parent = dev;
- trproc->rproc_id = rproc_id;
- trproc->session_id = sess_arg.session;
- trproc->rproc = rproc;
- rproc->tee_interface = trproc;
- list_add_tail(&trproc->node, &tee_rproc_ctx->sessions);
- return trproc;
+module_put:
- module_put(THIS_MODULE);
- return p_err;
+} +EXPORT_SYMBOL_GPL(tee_rproc_register);
+int tee_rproc_unregister(struct tee_rproc *trproc) +{
- struct rproc *rproc = trproc->rproc;
- int ret;
- ret = tee_client_close_session(tee_rproc_ctx->tee_ctx, trproc->session_id);
- if (ret < 0)
dev_err(trproc->parent, "tee_client_close_session failed, err: %x\n", ret);
- list_del(&trproc->node);
- rproc->tee_interface = NULL;
- module_put(THIS_MODULE);
- return ret;
+} +EXPORT_SYMBOL_GPL(tee_rproc_unregister);
+static int tee_rproc_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{
- /* Today we support only the OP-TEE, could be extend to other tees */
- return (ver->impl_id == TEE_IMPL_ID_OPTEE);
+}
+static int tee_rproc_probe(struct device *dev) +{
- struct tee_context *tee_ctx;
- int ret;
- /* Open context with TEE driver */
- tee_ctx = tee_client_open_context(NULL, tee_rproc_ctx_match, NULL, NULL);
- if (IS_ERR(tee_ctx))
return PTR_ERR(tee_ctx);
- tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_rproc_ctx), GFP_KERNEL);
- if (!tee_rproc_ctx) {
ret = -ENOMEM;
goto err;
- }
- tee_rproc_ctx->dev = dev;
- tee_rproc_ctx->tee_ctx = tee_ctx;
- INIT_LIST_HEAD(&tee_rproc_ctx->sessions);
- return 0;
+err:
- tee_client_close_context(tee_ctx);
- return ret;
+}
+static int tee_rproc_remove(struct device *dev) +{
- struct tee_rproc *entry, *tmp;
- list_for_each_entry_safe(entry, tmp, &tee_rproc_ctx->sessions, node) {
tee_client_close_session(tee_rproc_ctx->tee_ctx, entry->session_id);
list_del(&entry->node);
kfree(entry);
- }
- tee_client_close_context(tee_rproc_ctx->tee_ctx);
- return 0;
+}
+MODULE_DEVICE_TABLE(tee, tee_rproc_id_table);
+static struct tee_client_driver tee_rproc_fw_driver = {
- .id_table = tee_rproc_id_table,
- .driver = {
.name = KBUILD_MODNAME,
.bus = &tee_bus_type,
.probe = tee_rproc_probe,
.remove = tee_rproc_remove,
- },
+};
+static int __init tee_rproc_fw_mod_init(void) +{
- return driver_register(&tee_rproc_fw_driver.driver);
+}
+static void __exit tee_rproc_fw_mod_exit(void) +{
- driver_unregister(&tee_rproc_fw_driver.driver);
+}
+module_init(tee_rproc_fw_mod_init); +module_exit(tee_rproc_fw_mod_exit);
+MODULE_DESCRIPTION(" remote processor support with a TEE application"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 8fd0d7f63c8e..fbe1d6793709 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -503,6 +503,8 @@ enum rproc_features { RPROC_MAX_FEATURES, }; +struct tee_rproc;
/**
- struct rproc - represents a physical remote processor device
- @node: list node of this rproc object
@@ -545,6 +547,7 @@ enum rproc_features {
- @cdev: character device of the rproc
- @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release
- @features: indicate remoteproc features
*/
- @tee_interface: pointer to the remoteproc tee context
struct rproc { struct list_head node; @@ -586,6 +589,7 @@ struct rproc { struct cdev cdev; bool cdev_put_on_release; DECLARE_BITMAP(features, RPROC_MAX_FEATURES);
- struct tee_rproc *tee_interface;
}; /** diff --git a/include/linux/remoteproc_tee.h b/include/linux/remoteproc_tee.h new file mode 100644 index 000000000000..ccf34a218d54 --- /dev/null +++ b/include/linux/remoteproc_tee.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/*
- Copyright(c) 2024 STMicroelectronics
- */
+#ifndef REMOTEPROC_TEE_H +#define REMOTEPROC_TEE_H
+#include <linux/tee_drv.h> +#include <linux/firmware.h> +#include <linux/remoteproc.h>
+struct rproc;
+/**
- struct tee_rproc - TEE remoteproc structure
- @node: Reference in list
- @rproc: Remoteproc reference
- @parent: Parent device
- @rproc_id: Identifier of the target firmware
- @session_id: TEE session identifier
- */
+struct tee_rproc {
- struct list_head node;
- struct rproc *rproc;
- struct device *parent;
- u32 rproc_id;
- u32 session_id;
+};
+#if IS_REACHABLE(CONFIG_REMOTEPROC_TEE)
+struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc,
unsigned int rproc_id);
+int tee_rproc_unregister(struct tee_rproc *trproc); +int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw); +int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw); +struct resource_table *tee_rproc_find_loaded_rsc_table(struct rproc *rproc,
const struct firmware *fw);
+int tee_rproc_start(struct rproc *rproc); +int tee_rproc_stop(struct rproc *rproc);
+#else
+static inline struct tee_rproc *tee_rproc_register(struct device *dev, struct rproc *rproc,
unsigned int rproc_id)
+{
- return ERR_PTR(-ENODEV);
+}
+static int tee_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_unregister(struct tee_rproc *trproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_load_fw(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_start(struct rproc *rproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline int tee_rproc_stop(struct rproc *rproc) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return 0;
+}
+static inline struct resource_table * +tee_rproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw) +{
- /* This shouldn't be possible */
- WARN_ON(1);
- return NULL;
+} +#endif /* CONFIG_REMOTEPROC_TEE */
+#endif /* REMOTEPROC_TEE_H */
2.25.1
The "st,stm32mp1-m4-tee" compatible is utilized in a system configuration where the Cortex-M4 firmware is loaded by the Trusted Execution Environment (TEE).
For instance, this compatible is used in both the Linux and OP-TEE device trees: - In OP-TEE, a node is defined in the device tree with the "st,stm32mp1-m4-tee" compatible to support signed remoteproc firmware. Based on DT properties, the OP-TEE remoteproc framework is initiated to expose a trusted application service to authenticate and load the remote processor firmware provided by the Linux remoteproc framework, as well as to start and stop the remote processor. - In Linux, when the compatibility is set, the Cortex-M resets should not be declared in the device tree. In such a configuration, the reset is managed by the OP-TEE remoteproc driver and is no longer accessible from the Linux kernel.
Associated with this new compatible, add the "st,proc-id" property to identify the remote processor. This ID is used to define a unique ID, common between Linux, U-Boot, and OP-TEE, to identify a coprocessor. This ID will be used in requests to the OP-TEE remoteproc Trusted Application to specify the remote processor.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@foss.st.com Reviewed-by: Rob Herring (Arm) robh@kernel.org --- .../bindings/remoteproc/st,stm32-rproc.yaml | 58 ++++++++++++++++--- 1 file changed, 50 insertions(+), 8 deletions(-)
diff --git a/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml index 370af61d8f28..409123cd4667 100644 --- a/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml @@ -16,7 +16,12 @@ maintainers:
properties: compatible: - const: st,stm32mp1-m4 + enum: + - st,stm32mp1-m4 + - st,stm32mp1-m4-tee + description: + Use "st,stm32mp1-m4" for the Cortex-M4 coprocessor management by non-secure context + Use "st,stm32mp1-m4-tee" for the Cortex-M4 coprocessor management by secure context
reg: description: @@ -43,6 +48,10 @@ properties: - description: The offset of the hold boot setting register - description: The field mask of the hold boot
+ st,proc-id: + description: remote processor identifier + $ref: /schemas/types.yaml#/definitions/uint32 + st,syscfg-tz: deprecated: true description: @@ -142,21 +151,43 @@ properties: required: - compatible - reg - - resets
allOf: - if: properties: - reset-names: - not: - contains: - const: hold_boot + compatible: + contains: + const: st,stm32mp1-m4 then: + if: + properties: + reset-names: + not: + contains: + const: hold_boot + then: + required: + - st,syscfg-holdboot + else: + properties: + st,syscfg-holdboot: false + required: + - reset-names required: - - st,syscfg-holdboot - else: + - resets + + - if: + properties: + compatible: + contains: + const: st,stm32mp1-m4-tee + then: properties: st,syscfg-holdboot: false + reset-names: false + resets: false + required: + - st,proc-id
additionalProperties: false
@@ -188,5 +219,16 @@ examples: st,syscfg-rsc-tbl = <&tamp 0x144 0xFFFFFFFF>; st,syscfg-m4-state = <&tamp 0x148 0xFFFFFFFF>; }; + - | + #include <dt-bindings/reset/stm32mp1-resets.h> + m4@10000000 { + compatible = "st,stm32mp1-m4-tee"; + reg = <0x10000000 0x40000>, + <0x30000000 0x40000>, + <0x38000000 0x10000>; + st,proc-id = <0>; + st,syscfg-rsc-tbl = <&tamp 0x144 0xFFFFFFFF>; + st,syscfg-m4-state = <&tamp 0x148 0xFFFFFFFF>; + };
...
To prepare for the support of TEE remoteproc, create sub-functions that can be used in both cases, with and without remoteproc TEE support.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@foss.st.com --- drivers/remoteproc/stm32_rproc.c | 84 +++++++++++++++++++------------- 1 file changed, 51 insertions(+), 33 deletions(-)
diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index 88623df7d0c3..8cd838df4e92 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -209,6 +209,54 @@ static int stm32_rproc_mbox_idx(struct rproc *rproc, const unsigned char *name) return -EINVAL; }
+static void stm32_rproc_request_shutdown(struct rproc *rproc) +{ + struct stm32_rproc *ddata = rproc->priv; + int err, dummy_data, idx; + + /* Request shutdown of the remote processor */ + if (rproc->state != RPROC_OFFLINE && rproc->state != RPROC_CRASHED) { + idx = stm32_rproc_mbox_idx(rproc, STM32_MBX_SHUTDOWN); + if (idx >= 0 && ddata->mb[idx].chan) { + /* A dummy data is sent to allow to block on transmit. */ + err = mbox_send_message(ddata->mb[idx].chan, + &dummy_data); + if (err < 0) + dev_warn(&rproc->dev, "warning: remote FW shutdown without ack\n"); + } + } +} + +static int stm32_rproc_release(struct rproc *rproc) +{ + struct stm32_rproc *ddata = rproc->priv; + unsigned int err = 0; + + /* To allow platform Standby power mode, set remote proc Deep Sleep. */ + if (ddata->pdds.map) { + err = regmap_update_bits(ddata->pdds.map, ddata->pdds.reg, + ddata->pdds.mask, 1); + if (err) { + dev_err(&rproc->dev, "failed to set pdds\n"); + return err; + } + } + + /* Update coprocessor state to OFF if available. */ + if (ddata->m4_state.map) { + err = regmap_update_bits(ddata->m4_state.map, + ddata->m4_state.reg, + ddata->m4_state.mask, + M4_STATE_OFF); + if (err) { + dev_err(&rproc->dev, "failed to set copro state\n"); + return err; + } + } + + return 0; +} + static int stm32_rproc_prepare(struct rproc *rproc) { struct device *dev = rproc->dev.parent; @@ -519,17 +567,9 @@ static int stm32_rproc_detach(struct rproc *rproc) static int stm32_rproc_stop(struct rproc *rproc) { struct stm32_rproc *ddata = rproc->priv; - int err, idx; + int err;
- /* request shutdown of the remote processor */ - if (rproc->state != RPROC_OFFLINE && rproc->state != RPROC_CRASHED) { - idx = stm32_rproc_mbox_idx(rproc, STM32_MBX_SHUTDOWN); - if (idx >= 0 && ddata->mb[idx].chan) { - err = mbox_send_message(ddata->mb[idx].chan, "detach"); - if (err < 0) - dev_warn(&rproc->dev, "warning: remote FW shutdown without ack\n"); - } - } + stm32_rproc_request_shutdown(rproc);
err = stm32_rproc_set_hold_boot(rproc, true); if (err) @@ -541,29 +581,7 @@ static int stm32_rproc_stop(struct rproc *rproc) return err; }
- /* to allow platform Standby power mode, set remote proc Deep Sleep */ - if (ddata->pdds.map) { - err = regmap_update_bits(ddata->pdds.map, ddata->pdds.reg, - ddata->pdds.mask, 1); - if (err) { - dev_err(&rproc->dev, "failed to set pdds\n"); - return err; - } - } - - /* update coprocessor state to OFF if available */ - if (ddata->m4_state.map) { - err = regmap_update_bits(ddata->m4_state.map, - ddata->m4_state.reg, - ddata->m4_state.mask, - M4_STATE_OFF); - if (err) { - dev_err(&rproc->dev, "failed to set copro state\n"); - return err; - } - } - - return 0; + return stm32_rproc_release(rproc); }
static void stm32_rproc_kick(struct rproc *rproc, int vqid)
The new TEE remoteproc device is used to manage remote firmware in a secure, trusted context. The 'st,stm32mp1-m4-tee' compatibility is introduced to delegate the loading of the firmware to the trusted execution context. In such cases, the firmware should be signed and adhere to the image format defined by the TEE.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@foss.st.com --- drivers/remoteproc/stm32_rproc.c | 63 ++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-)
diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index 8cd838df4e92..fd905b3cf206 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -18,6 +18,7 @@ #include <linux/pm_wakeirq.h> #include <linux/regmap.h> #include <linux/remoteproc.h> +#include <linux/remoteproc_tee.h> #include <linux/reset.h> #include <linux/slab.h> #include <linux/workqueue.h> @@ -257,6 +258,19 @@ static int stm32_rproc_release(struct rproc *rproc) return 0; }
+static int stm32_rproc_tee_stop(struct rproc *rproc) +{ + int err; + + stm32_rproc_request_shutdown(rproc); + + err = tee_rproc_stop(rproc); + if (err) + return err; + + return stm32_rproc_release(rproc); +} + static int stm32_rproc_prepare(struct rproc *rproc) { struct device *dev = rproc->dev.parent; @@ -693,8 +707,20 @@ static const struct rproc_ops st_rproc_ops = { .get_boot_addr = rproc_elf_get_boot_addr, };
+static const struct rproc_ops st_rproc_tee_ops = { + .prepare = stm32_rproc_prepare, + .start = tee_rproc_start, + .stop = stm32_rproc_tee_stop, + .kick = stm32_rproc_kick, + .load = tee_rproc_load_fw, + .parse_fw = tee_rproc_parse_fw, + .find_loaded_rsc_table = tee_rproc_find_loaded_rsc_table, + +}; + static const struct of_device_id stm32_rproc_match[] = { { .compatible = "st,stm32mp1-m4" }, + { .compatible = "st,stm32mp1-m4-tee" }, {}, }; MODULE_DEVICE_TABLE(of, stm32_rproc_match); @@ -853,17 +879,42 @@ static int stm32_rproc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct stm32_rproc *ddata; struct device_node *np = dev->of_node; + struct tee_rproc *trproc = NULL; struct rproc *rproc; unsigned int state; + u32 proc_id; int ret;
ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (ret) return ret;
- rproc = devm_rproc_alloc(dev, np->name, &st_rproc_ops, NULL, sizeof(*ddata)); - if (!rproc) - return -ENOMEM; + if (of_device_is_compatible(np, "st,stm32mp1-m4-tee")) { + /* + * Delegate the firmware management to the secure context. + * The firmware loaded has to be signed. + */ + ret = of_property_read_u32(np, "st,proc-id", &proc_id); + if (ret) { + dev_err(dev, "failed to read st,rproc-id property\n"); + return ret; + } + + rproc = devm_rproc_alloc(dev, np->name, &st_rproc_tee_ops, NULL, sizeof(*ddata)); + if (!rproc) + return -ENOMEM; + + trproc = tee_rproc_register(dev, rproc, proc_id); + if (IS_ERR(trproc)) { + dev_err_probe(dev, PTR_ERR(trproc), + "signed firmware not supported by TEE\n"); + return PTR_ERR(trproc); + } + } else { + rproc = devm_rproc_alloc(dev, np->name, &st_rproc_ops, NULL, sizeof(*ddata)); + if (!rproc) + return -ENOMEM; + }
ddata = rproc->priv;
@@ -915,6 +966,9 @@ static int stm32_rproc_probe(struct platform_device *pdev) dev_pm_clear_wake_irq(dev); device_init_wakeup(dev, false); } + if (trproc) + tee_rproc_unregister(trproc); + return ret; }
@@ -935,6 +989,9 @@ static void stm32_rproc_remove(struct platform_device *pdev) dev_pm_clear_wake_irq(dev); device_init_wakeup(dev, false); } + if (rproc->tee_interface) + tee_rproc_unregister(rproc->tee_interface); + }
static int stm32_rproc_suspend(struct device *dev)
On Fri, Jun 21, 2024 at 04:37:59PM +0200, Arnaud Pouliquen wrote:
The new TEE remoteproc device is used to manage remote firmware in a
Device or driver?
secure, trusted context. The 'st,stm32mp1-m4-tee' compatibility is introduced to delegate the loading of the firmware to the trusted execution context. In such cases, the firmware should be signed and adhere to the image format defined by the TEE.
Signed-off-by: Arnaud Pouliquen arnaud.pouliquen@foss.st.com
drivers/remoteproc/stm32_rproc.c | 63 ++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-)
diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index 8cd838df4e92..fd905b3cf206 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -18,6 +18,7 @@ #include <linux/pm_wakeirq.h> #include <linux/regmap.h> #include <linux/remoteproc.h> +#include <linux/remoteproc_tee.h> #include <linux/reset.h> #include <linux/slab.h> #include <linux/workqueue.h> @@ -257,6 +258,19 @@ static int stm32_rproc_release(struct rproc *rproc) return 0; } +static int stm32_rproc_tee_stop(struct rproc *rproc) +{
- int err;
- stm32_rproc_request_shutdown(rproc);
- err = tee_rproc_stop(rproc);
- if (err)
return err;
- return stm32_rproc_release(rproc);
+}
static int stm32_rproc_prepare(struct rproc *rproc) { struct device *dev = rproc->dev.parent; @@ -693,8 +707,20 @@ static const struct rproc_ops st_rproc_ops = { .get_boot_addr = rproc_elf_get_boot_addr, }; +static const struct rproc_ops st_rproc_tee_ops = {
- .prepare = stm32_rproc_prepare,
- .start = tee_rproc_start,
- .stop = stm32_rproc_tee_stop,
- .kick = stm32_rproc_kick,
- .load = tee_rproc_load_fw,
- .parse_fw = tee_rproc_parse_fw,
- .find_loaded_rsc_table = tee_rproc_find_loaded_rsc_table,
+};
static const struct of_device_id stm32_rproc_match[] = { { .compatible = "st,stm32mp1-m4" },
- { .compatible = "st,stm32mp1-m4-tee" }, {},
}; MODULE_DEVICE_TABLE(of, stm32_rproc_match); @@ -853,17 +879,42 @@ static int stm32_rproc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct stm32_rproc *ddata; struct device_node *np = dev->of_node;
- struct tee_rproc *trproc = NULL; struct rproc *rproc; unsigned int state;
- u32 proc_id; int ret;
ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (ret) return ret;
- rproc = devm_rproc_alloc(dev, np->name, &st_rproc_ops, NULL, sizeof(*ddata));
- if (!rproc)
return -ENOMEM;
- if (of_device_is_compatible(np, "st,stm32mp1-m4-tee")) {
/*
* Delegate the firmware management to the secure context.
* The firmware loaded has to be signed.
*/
ret = of_property_read_u32(np, "st,proc-id", &proc_id);
if (ret) {
dev_err(dev, "failed to read st,rproc-id property\n");
return ret;
}
rproc = devm_rproc_alloc(dev, np->name, &st_rproc_tee_ops, NULL, sizeof(*ddata));
if (!rproc)
return -ENOMEM;
trproc = tee_rproc_register(dev, rproc, proc_id);
if (IS_ERR(trproc)) {
dev_err_probe(dev, PTR_ERR(trproc),
"signed firmware not supported by TEE\n");
return PTR_ERR(trproc);
}
- } else {
rproc = devm_rproc_alloc(dev, np->name, &st_rproc_ops, NULL, sizeof(*ddata));
if (!rproc)
return -ENOMEM;
- }
ddata = rproc->priv; @@ -915,6 +966,9 @@ static int stm32_rproc_probe(struct platform_device *pdev) dev_pm_clear_wake_irq(dev); device_init_wakeup(dev, false); }
- if (trproc)
tee_rproc_unregister(trproc);
- return ret;
} @@ -935,6 +989,9 @@ static void stm32_rproc_remove(struct platform_device *pdev) dev_pm_clear_wake_irq(dev); device_init_wakeup(dev, false); }
- if (rproc->tee_interface)
tee_rproc_unregister(rproc->tee_interface);
} static int stm32_rproc_suspend(struct device *dev) -- 2.25.1
op-tee@lists.trustedfirmware.org