2024-01-18 10:05:33

by Arnaud POULIQUEN

[permalink] [raw]
Subject: [PATCH v2 0/4] Introduction of a remoteproc tee to load signed firmware

Updates from the previous version [1]
- fix issues reported by kernel test robot,
- address Rob Herring comment on bindings.

[1] https://lore.kernel.org/linux-arm-kernel/[email protected]/T/


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_core.c#L18-L57

3) OP-TEE trusted application API:

Refer to:
https://github.com/OP-TEE/optee_os/blob/master/ta/remoteproc/include/ta_remoteproc.h

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 (4):
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 | 52 ++-
drivers/remoteproc/Kconfig | 9 +
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/stm32_rproc.c | 233 +++++++++--
drivers/remoteproc/tee_remoteproc.c | 393 ++++++++++++++++++
include/linux/tee_remoteproc.h | 99 +++++
6 files changed, 740 insertions(+), 47 deletions(-)
create mode 100644 drivers/remoteproc/tee_remoteproc.c
create mode 100644 include/linux/tee_remoteproc.h


base-commit: 0dd3ee31125508cd67f7e7172247f05b7fd1753a
--
2.25.1



2024-01-18 10:06:52

by Arnaud POULIQUEN

[permalink] [raw]
Subject: [PATCH v2 1/4] remoteproc: Add TEE support

From: Arnaud Pouliquen <[email protected]>

Add a remoteproc TEE (Trusted Execution Environment) device
that will be probed by the TEE bus. If the associated Trusted
application is supported on secure part this device offers a client
interface to load a firmware in the secure part.
This firmware could be authenticated and decrypted by the secure
trusted application.

Signed-off-by: Arnaud Pouliquen <[email protected]>
---
drivers/remoteproc/Kconfig | 9 +
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/tee_remoteproc.c | 393 ++++++++++++++++++++++++++++
include/linux/tee_remoteproc.h | 99 +++++++
4 files changed, 502 insertions(+)
create mode 100644 drivers/remoteproc/tee_remoteproc.c
create mode 100644 include/linux/tee_remoteproc.h

diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 48845dc8fa85..85299606806c 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -365,6 +365,15 @@ config XLNX_R5_REMOTEPROC

It's safe to say N if not interested in using RPU r5f cores.

+
+config TEE_REMOTEPROC
+ tristate "trusted firmware support by a TEE application"
+ depends on OPTEE
+ help
+ Support for trusted remote processors firmware. The firmware
+ authentication and/or decryption are managed by a trusted application.
+ 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..fa8daebce277 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_TEE_REMOTEPROC) += tee_remoteproc.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/tee_remoteproc.c b/drivers/remoteproc/tee_remoteproc.c
new file mode 100644
index 000000000000..49e1e0caf889
--- /dev/null
+++ b/drivers/remoteproc/tee_remoteproc.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) STMicroelectronics 2023 - All Rights Reserved
+ * Author: Arnaud Pouliquen <[email protected]>
+ */
+
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/remoteproc.h>
+#include <linux/slab.h>
+#include <linux/tee_drv.h>
+#include <linux/tee_remoteproc.h>
+
+#include "remoteproc_internal.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_mem {
+ char name[20];
+ void __iomem *cpu_addr;
+ phys_addr_t bus_addr;
+ u32 dev_addr;
+ size_t size;
+};
+
+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 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 tee_rproc *trproc, const struct firmware *fw)
+{
+ struct tee_ioctl_invoke_arg arg;
+ struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
+ struct tee_shm *fw_shm;
+ int ret;
+
+ 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);
+
+ 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);
+
+int rproc_tee_get_rsc_table(struct tee_rproc *trproc)
+{
+ struct tee_ioctl_invoke_arg arg;
+ struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
+ struct rproc *rproc = trproc->rproc;
+ size_t rsc_size;
+ int ret;
+
+ 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;
+ }
+
+ rsc_size = param[2].u.value.a;
+
+ /* If the size is null no resource table defined in the image */
+ if (!rsc_size)
+ return 0;
+
+ /* Store the resource table address that would be updated by the remote core . */
+ trproc->rsc_va = ioremap_wc(param[1].u.value.a, rsc_size);
+ if (IS_ERR_OR_NULL(trproc->rsc_va)) {
+ dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %lld+%zx\n",
+ param[1].u.value.a, rsc_size);
+ trproc->rsc_va = NULL;
+ return -ENOMEM;
+ }
+
+ /*
+ * A cached table is requested as the physical address is not mapped yet
+ * but remoteproc needs to parse the table for resources.
+ */
+ rproc->cached_table = kmemdup((__force void *)trproc->rsc_va, rsc_size, GFP_KERNEL);
+ if (!rproc->cached_table)
+ return -ENOMEM;
+
+ rproc->table_ptr = rproc->cached_table;
+ rproc->table_sz = rsc_size;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rproc_tee_get_rsc_table);
+
+struct resource_table *tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc)
+{
+ return (__force struct resource_table *)trproc->rsc_va;
+}
+EXPORT_SYMBOL_GPL(tee_rproc_get_loaded_rsc_table);
+
+int tee_rproc_start(struct tee_rproc *trproc)
+{
+ struct tee_ioctl_invoke_arg arg;
+ struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
+ int ret;
+
+ 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)
+ ret = -EIO;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tee_rproc_start);
+
+int tee_rproc_stop(struct tee_rproc *trproc)
+{
+ struct tee_ioctl_invoke_arg arg;
+ struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
+ int ret;
+
+ 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;
+ }
+ if (trproc->rsc_va)
+ iounmap(trproc->rsc_va);
+ trproc->rsc_va = NULL;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tee_rproc_stop);
+
+static const struct tee_client_device_id stm32_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, unsigned int rproc_id)
+{
+ struct tee_client_device *rproc_tee_device;
+ struct tee_ioctl_open_session_arg sess_arg;
+ struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
+ struct tee_rproc *trproc;
+ int ret;
+
+ /*
+ * The device is not probed by the TEE bus. We ignore the reason (bus could be not yet
+ * probed or service not available in the secure firmware)
+ * Assumption here is that the TEE bus is not probed.
+ */
+ if (!tee_rproc_ctx)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
+ if (!trproc)
+ return ERR_PTR(-ENOMEM);
+
+ rproc_tee_device = to_tee_client_device(tee_rproc_ctx->dev);
+ memset(&sess_arg, 0, sizeof(sess_arg));
+
+ /* Open session with rproc_tee load the OP-TEE Trusted Application */
+ memcpy(sess_arg.uuid, rproc_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);
+ return ERR_PTR(-EINVAL);
+ }
+
+ trproc->parent = dev;
+ trproc->rproc_id = rproc_id;
+ trproc->session_id = sess_arg.session;
+
+ list_add_tail(&trproc->node, &tee_rproc_ctx->sessions);
+
+ return trproc;
+}
+EXPORT_SYMBOL_GPL(tee_rproc_register);
+
+int tee_rproc_unregister(struct tee_rproc *trproc)
+{
+ int ret;
+
+ if (!tee_rproc_ctx)
+ return -ENODEV;
+
+ 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);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tee_rproc_unregister);
+
+static int tee_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;
+
+ /* Only one RPROC OP-TEE device allowed */
+ if (tee_rproc_ctx) {
+ dev_err(dev, "An RPROC OP-TEE device was already initialized: only one allowed\n");
+ return -EBUSY;
+ }
+
+ /* Open context with TEE driver */
+ tee_ctx = tee_client_open_context(NULL, tee_ctx_match, NULL, NULL);
+ if (IS_ERR(tee_ctx))
+ return PTR_ERR(tee_ctx);
+
+ tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_ctx), GFP_KERNEL);
+ if (!tee_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, stm32_tee_rproc_id_table);
+
+static struct tee_client_driver tee_rproc_fw_driver = {
+ .id_table = stm32_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(" TEE remote processor control driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/tee_remoteproc.h b/include/linux/tee_remoteproc.h
new file mode 100644
index 000000000000..537d6dc3b858
--- /dev/null
+++ b/include/linux/tee_remoteproc.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright(c) 2023 STMicroelectronics - All Rights Reserved
+ */
+
+#ifndef TEE_REMOTEPROC_H
+#define TEE_REMOTEPROC_H
+
+#include <linux/remoteproc.h>
+#include <linux/tee_drv.h>
+
+/**
+ * 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
+ * @rsc_va: Resource table virtual address.
+ */
+struct tee_rproc {
+ struct list_head node;
+ struct rproc *rproc;
+ struct device *parent;
+ u32 rproc_id;
+ u32 session_id;
+ void __iomem *rsc_va;
+};
+
+#if IS_ENABLED(CONFIG_TEE_REMOTEPROC)
+
+struct tee_rproc *tee_rproc_register(struct device *dev, unsigned int rproc_id);
+int tee_rproc_unregister(struct tee_rproc *trproc);
+
+int tee_rproc_load_fw(struct tee_rproc *trproc, const struct firmware *fw);
+int rproc_tee_get_rsc_table(struct tee_rproc *trproc);
+struct resource_table *tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc);
+int tee_rproc_start(struct tee_rproc *trproc);
+int tee_rproc_stop(struct tee_rproc *trproc);
+
+#else
+
+static inline struct tee_rproc *tee_rproc_register(struct device *dev, unsigned int rproc_id)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+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 tee_rproc *trproc,
+ const struct firmware *fw)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+
+ return 0;
+}
+
+static inline int tee_rproc_start(struct tee_rproc *trproc)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+
+ return 0;
+}
+
+static inline int tee_rproc_stop(struct tee_rproc *trproc)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+
+ return 0;
+}
+
+static inline int rproc_tee_get_rsc_table(struct tee_rproc *trproc)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+
+ return 0;
+}
+
+static inline struct resource_table *
+ tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+
+ return NULL;
+}
+
+#endif /* CONFIG_TEE_REMOTEPROC */
+#endif /* TEE_REMOTEPROC_H */
--
2.25.1


2024-01-18 10:07:46

by Arnaud POULIQUEN

[permalink] [raw]
Subject: [PATCH v2 2/4] dt-bindings: remoteproc: Add compatibility for TEE support

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-tree:
- In OP-TEE, a node is defined in the device tree with the
st,stm32mp1-m4-tee to support signed remoteproc firmware.
Based on DT properties, OP-TEE authenticates, loads, starts, and stops
the firmware.
- On Linux, when the compatibility is set, the Cortex-M resets should not
be declared in the device tree.

Signed-off-by: Arnaud Pouliquen <[email protected]>
---
V1 to V2 updates
- update "st,stm32mp1-m4" compatible description to generalize
- remove the 'reset-names' requirement in one conditional branch, as the
property is already part of the condition test.
---
.../bindings/remoteproc/st,stm32-rproc.yaml | 52 +++++++++++++++----
1 file changed, 43 insertions(+), 9 deletions(-)

diff --git a/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml
index 370af61d8f28..6af821b15736 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:
@@ -142,21 +147,40 @@ 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
+ - resets
+ else:
+ properties:
+ st,syscfg-holdboot: false
+ required:
+ - resets
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: st,stm32mp1-m4-tee
then:
- required:
- - st,syscfg-holdboot
- else:
properties:
st,syscfg-holdboot: false
+ reset-names: false
+ resets: false

additionalProperties: false

@@ -188,5 +212,15 @@ 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,syscfg-rsc-tbl = <&tamp 0x144 0xFFFFFFFF>;
+ st,syscfg-m4-state = <&tamp 0x148 0xFFFFFFFF>;
+ };

...
--
2.25.1


2024-01-18 10:08:50

by Arnaud POULIQUEN

[permalink] [raw]
Subject: [PATCH v2 3/4] remoteproc: stm32: Create sub-functions to request shutdown and release

To prepare for the support of TEE remoteproc, create sub-functions
that can be used in both cases, with and without TEE support.

Signed-off-by: Arnaud Pouliquen <[email protected]>
---
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 4f469f0bcf8b..fcc0001e2657 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 err;
+}
+
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)
--
2.25.1


2024-01-18 10:10:02

by Arnaud POULIQUEN

[permalink] [raw]
Subject: [PATCH v2 4/4] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware

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 <[email protected]>
---
V1 to V2 update:
- remove the select "TEE_REMOTEPROC" in STM32_RPROC config as detected by
the kernel test robot:
WARNING: unmet direct dependencies detected for TEE_REMOTEPROC
Depends on [n]: REMOTEPROC [=y] && OPTEE [=n]
Selected by [y]:
- STM32_RPROC [=y] && (ARCH_STM32 || COMPILE_TEST [=y]) && REMOTEPROC [=y]
- Fix initialized trproc variable in stm32_rproc_probe
---
drivers/remoteproc/stm32_rproc.c | 149 +++++++++++++++++++++++++++++--
1 file changed, 144 insertions(+), 5 deletions(-)

diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
index fcc0001e2657..cf6a21bac945 100644
--- a/drivers/remoteproc/stm32_rproc.c
+++ b/drivers/remoteproc/stm32_rproc.c
@@ -20,6 +20,7 @@
#include <linux/remoteproc.h>
#include <linux/reset.h>
#include <linux/slab.h>
+#include <linux/tee_remoteproc.h>
#include <linux/workqueue.h>

#include "remoteproc_internal.h"
@@ -49,6 +50,9 @@
#define M4_STATE_STANDBY 4
#define M4_STATE_CRASH 5

+/* Remote processor unique identifier aligned with the Trusted Execution Environment definitions */
+#define STM32_MP1_M4_PROC_ID 0
+
struct stm32_syscon {
struct regmap *map;
u32 reg;
@@ -90,6 +94,8 @@ struct stm32_rproc {
struct stm32_mbox mb[MBOX_NB_MBX];
struct workqueue_struct *workqueue;
bool hold_boot_smc;
+ bool fw_loaded;
+ struct tee_rproc *trproc;
void __iomem *rsc_va;
};

@@ -257,6 +263,91 @@ static int stm32_rproc_release(struct rproc *rproc)
return err;
}

+static int stm32_rproc_tee_elf_sanity_check(struct rproc *rproc,
+ const struct firmware *fw)
+{
+ struct stm32_rproc *ddata = rproc->priv;
+ unsigned int ret = 0;
+
+ if (rproc->state == RPROC_DETACHED)
+ return 0;
+
+ ret = tee_rproc_load_fw(ddata->trproc, fw);
+ if (!ret)
+ ddata->fw_loaded = true;
+
+ return ret;
+}
+
+static int stm32_rproc_tee_elf_load(struct rproc *rproc,
+ const struct firmware *fw)
+{
+ struct stm32_rproc *ddata = rproc->priv;
+ unsigned int ret;
+
+ /*
+ * This function can be called by remote proc for recovery
+ * without the sanity check. In this case we need to load the firmware
+ * else nothing done here as the firmware has been preloaded for the
+ * sanity check to be able to parse it for the resource table.
+ */
+ if (ddata->fw_loaded)
+ return 0;
+
+ ret = tee_rproc_load_fw(ddata->trproc, fw);
+ if (ret)
+ return ret;
+ ddata->fw_loaded = true;
+
+ /* Update the resource table parameters. */
+ if (rproc_tee_get_rsc_table(ddata->trproc)) {
+ /* No resource table: reset the related fields. */
+ rproc->cached_table = NULL;
+ rproc->table_ptr = NULL;
+ rproc->table_sz = 0;
+ }
+
+ return 0;
+}
+
+static struct resource_table *
+stm32_rproc_tee_elf_find_loaded_rsc_table(struct rproc *rproc,
+ const struct firmware *fw)
+{
+ struct stm32_rproc *ddata = rproc->priv;
+
+ return tee_rproc_get_loaded_rsc_table(ddata->trproc);
+}
+
+static int stm32_rproc_tee_start(struct rproc *rproc)
+{
+ struct stm32_rproc *ddata = rproc->priv;
+
+ return tee_rproc_start(ddata->trproc);
+}
+
+static int stm32_rproc_tee_attach(struct rproc *rproc)
+{
+ /* Nothing to do, remote proc already started by the secured context. */
+ return 0;
+}
+
+static int stm32_rproc_tee_stop(struct rproc *rproc)
+{
+ struct stm32_rproc *ddata = rproc->priv;
+ int err;
+
+ stm32_rproc_request_shutdown(rproc);
+
+ err = tee_rproc_stop(ddata->trproc);
+ if (err)
+ return err;
+
+ ddata->fw_loaded = false;
+
+ return stm32_rproc_release(rproc);
+}
+
static int stm32_rproc_prepare(struct rproc *rproc)
{
struct device *dev = rproc->dev.parent;
@@ -319,7 +410,14 @@ static int stm32_rproc_prepare(struct rproc *rproc)

static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
{
- if (rproc_elf_load_rsc_table(rproc, fw))
+ struct stm32_rproc *ddata = rproc->priv;
+ int ret;
+
+ if (ddata->trproc)
+ ret = rproc_tee_get_rsc_table(ddata->trproc);
+ else
+ ret = rproc_elf_load_rsc_table(rproc, fw);
+ if (ret)
dev_warn(&rproc->dev, "no resource table found for this firmware\n");

return 0;
@@ -693,8 +791,22 @@ 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 = stm32_rproc_tee_start,
+ .stop = stm32_rproc_tee_stop,
+ .attach = stm32_rproc_tee_attach,
+ .kick = stm32_rproc_kick,
+ .parse_fw = stm32_rproc_parse_fw,
+ .find_loaded_rsc_table = stm32_rproc_tee_elf_find_loaded_rsc_table,
+ .get_loaded_rsc_table = stm32_rproc_get_loaded_rsc_table,
+ .sanity_check = stm32_rproc_tee_elf_sanity_check,
+ .load = stm32_rproc_tee_elf_load,
+};
+
static const struct of_device_id stm32_rproc_match[] = {
- { .compatible = "st,stm32mp1-m4" },
+ {.compatible = "st,stm32mp1-m4",},
+ {.compatible = "st,stm32mp1-m4-tee",},
{},
};
MODULE_DEVICE_TABLE(of, stm32_rproc_match);
@@ -853,6 +965,7 @@ 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;
int ret;
@@ -861,11 +974,31 @@ static int stm32_rproc_probe(struct platform_device *pdev)
if (ret)
return ret;

- rproc = 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")) {
+ trproc = tee_rproc_register(dev, STM32_MP1_M4_PROC_ID);
+ if (IS_ERR(trproc)) {
+ dev_err_probe(dev, PTR_ERR(trproc),
+ "signed firmware not supported by TEE\n");
+ return PTR_ERR(trproc);
+ }
+ /*
+ * Delegate the firmware management to the secure context.
+ * The firmware loaded has to be signed.
+ */
+ dev_info(dev, "Support of signed firmware only\n");
+ }
+ rproc = rproc_alloc(dev, np->name,
+ trproc ? &st_rproc_tee_ops : &st_rproc_ops,
+ NULL, sizeof(*ddata));
+ if (!rproc) {
+ ret = -ENOMEM;
+ goto free_tee;
+ }

ddata = rproc->priv;
+ ddata->trproc = trproc;
+ if (trproc)
+ trproc->rproc = rproc;

rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);

@@ -916,6 +1049,10 @@ static int stm32_rproc_probe(struct platform_device *pdev)
device_init_wakeup(dev, false);
}
rproc_free(rproc);
+free_tee:
+ if (trproc)
+ tee_rproc_unregister(trproc);
+
return ret;
}

@@ -937,6 +1074,8 @@ static void stm32_rproc_remove(struct platform_device *pdev)
device_init_wakeup(dev, false);
}
rproc_free(rproc);
+ if (ddata->trproc)
+ tee_rproc_unregister(ddata->trproc);
}

static int stm32_rproc_suspend(struct device *dev)
--
2.25.1


2024-01-25 18:55:25

by Mathieu Poirier

[permalink] [raw]
Subject: Re: [PATCH v2 1/4] remoteproc: Add TEE support

Hi Arnaud,

On Thu, Jan 18, 2024 at 11:04:30AM +0100, Arnaud Pouliquen wrote:
> From: Arnaud Pouliquen <[email protected]>
>
> Add a remoteproc TEE (Trusted Execution Environment) device

Device or driver? Seems to be the latter...

> that will be probed by the TEE bus. If the associated Trusted
> application is supported on secure part this device offers a client
> interface to load a firmware in the secure part.
> This firmware could be authenticated and decrypted by the secure
> trusted application.
>
> Signed-off-by: Arnaud Pouliquen <[email protected]>
> ---
> drivers/remoteproc/Kconfig | 9 +
> drivers/remoteproc/Makefile | 1 +
> drivers/remoteproc/tee_remoteproc.c | 393 ++++++++++++++++++++++++++++
> include/linux/tee_remoteproc.h | 99 +++++++
> 4 files changed, 502 insertions(+)
> create mode 100644 drivers/remoteproc/tee_remoteproc.c
> create mode 100644 include/linux/tee_remoteproc.h
>
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> index 48845dc8fa85..85299606806c 100644
> --- a/drivers/remoteproc/Kconfig
> +++ b/drivers/remoteproc/Kconfig
> @@ -365,6 +365,15 @@ config XLNX_R5_REMOTEPROC
>
> It's safe to say N if not interested in using RPU r5f cores.
>
> +
> +config TEE_REMOTEPROC
> + tristate "trusted firmware support by a TEE application"
> + depends on OPTEE
> + help
> + Support for trusted remote processors firmware. The firmware
> + authentication and/or decryption are managed by a trusted application.
> + 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..fa8daebce277 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_TEE_REMOTEPROC) += tee_remoteproc.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/tee_remoteproc.c b/drivers/remoteproc/tee_remoteproc.c
> new file mode 100644
> index 000000000000..49e1e0caf889
> --- /dev/null
> +++ b/drivers/remoteproc/tee_remoteproc.c
> @@ -0,0 +1,393 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) STMicroelectronics 2023 - All Rights Reserved
> + * Author: Arnaud Pouliquen <[email protected]>
> + */
> +
> +#include <linux/firmware.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/remoteproc.h>
> +#include <linux/slab.h>
> +#include <linux/tee_drv.h>
> +#include <linux/tee_remoteproc.h>
> +
> +#include "remoteproc_internal.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_mem {
> + char name[20];
> + void __iomem *cpu_addr;
> + phys_addr_t bus_addr;
> + u32 dev_addr;
> + size_t size;
> +};
> +
> +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 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 tee_rproc *trproc, const struct firmware *fw)
> +{
> + struct tee_ioctl_invoke_arg arg;
> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> + struct tee_shm *fw_shm;
> + int ret;
> +
> + 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);
> +
> + 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);
> +
> +int rproc_tee_get_rsc_table(struct tee_rproc *trproc)
> +{
> + struct tee_ioctl_invoke_arg arg;
> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> + struct rproc *rproc = trproc->rproc;
> + size_t rsc_size;
> + int ret;
> +
> + 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;
> + }
> +
> + rsc_size = param[2].u.value.a;
> +
> + /* If the size is null no resource table defined in the image */
> + if (!rsc_size)
> + return 0;
> +
> + /* Store the resource table address that would be updated by the remote core . */
> + trproc->rsc_va = ioremap_wc(param[1].u.value.a, rsc_size);
> + if (IS_ERR_OR_NULL(trproc->rsc_va)) {
> + dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %lld+%zx\n",
> + param[1].u.value.a, rsc_size);
> + trproc->rsc_va = NULL;
> + return -ENOMEM;
> + }
> +
> + /*
> + * A cached table is requested as the physical address is not mapped yet
> + * but remoteproc needs to parse the table for resources.
> + */
> + rproc->cached_table = kmemdup((__force void *)trproc->rsc_va, rsc_size, GFP_KERNEL);
> + if (!rproc->cached_table)
> + return -ENOMEM;
> +
> + rproc->table_ptr = rproc->cached_table;
> + rproc->table_sz = rsc_size;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_get_rsc_table);
> +
> +struct resource_table *tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc)
> +{
> + return (__force struct resource_table *)trproc->rsc_va;
> +}
> +EXPORT_SYMBOL_GPL(tee_rproc_get_loaded_rsc_table);
> +
> +int tee_rproc_start(struct tee_rproc *trproc)
> +{
> + struct tee_ioctl_invoke_arg arg;
> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> + int ret;
> +
> + 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)
> + ret = -EIO;
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(tee_rproc_start);
> +
> +int tee_rproc_stop(struct tee_rproc *trproc)
> +{
> + struct tee_ioctl_invoke_arg arg;
> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> + int ret;
> +
> + 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;
> + }
> + if (trproc->rsc_va)
> + iounmap(trproc->rsc_va);
> + trproc->rsc_va = NULL;
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(tee_rproc_stop);
> +
> +static const struct tee_client_device_id stm32_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, unsigned int rproc_id)
> +{
> + struct tee_client_device *rproc_tee_device;
> + struct tee_ioctl_open_session_arg sess_arg;
> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> + struct tee_rproc *trproc;
> + int ret;
> +
> + /*
> + * The device is not probed by the TEE bus. We ignore the reason (bus could be not yet
> + * probed or service not available in the secure firmware)
> + * Assumption here is that the TEE bus is not probed.
> + */
> + if (!tee_rproc_ctx)
> + return ERR_PTR(-EPROBE_DEFER);
> +
> + trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
> + if (!trproc)
> + return ERR_PTR(-ENOMEM);
> +
> + rproc_tee_device = to_tee_client_device(tee_rproc_ctx->dev);
> + memset(&sess_arg, 0, sizeof(sess_arg));
> +
> + /* Open session with rproc_tee load the OP-TEE Trusted Application */
> + memcpy(sess_arg.uuid, rproc_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);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + trproc->parent = dev;
> + trproc->rproc_id = rproc_id;
> + trproc->session_id = sess_arg.session;
> +
> + list_add_tail(&trproc->node, &tee_rproc_ctx->sessions);
> +
> + return trproc;
> +}
> +EXPORT_SYMBOL_GPL(tee_rproc_register);
> +
> +int tee_rproc_unregister(struct tee_rproc *trproc)
> +{
> + int ret;
> +
> + if (!tee_rproc_ctx)
> + return -ENODEV;
> +
> + 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);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(tee_rproc_unregister);
> +
> +static int tee_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;
> +
> + /* Only one RPROC OP-TEE device allowed */
> + if (tee_rproc_ctx) {
> + dev_err(dev, "An RPROC OP-TEE device was already initialized: only one allowed\n");
> + return -EBUSY;
> + }
> +
> + /* Open context with TEE driver */
> + tee_ctx = tee_client_open_context(NULL, tee_ctx_match, NULL, NULL);
> + if (IS_ERR(tee_ctx))
> + return PTR_ERR(tee_ctx);
> +
> + tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_ctx), GFP_KERNEL);
> + if (!tee_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, stm32_tee_rproc_id_table);
> +
> +static struct tee_client_driver tee_rproc_fw_driver = {
> + .id_table = stm32_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(" TEE remote processor control driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/tee_remoteproc.h b/include/linux/tee_remoteproc.h
> new file mode 100644
> index 000000000000..537d6dc3b858
> --- /dev/null
> +++ b/include/linux/tee_remoteproc.h
> @@ -0,0 +1,99 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright(c) 2023 STMicroelectronics - All Rights Reserved
> + */
> +
> +#ifndef TEE_REMOTEPROC_H
> +#define TEE_REMOTEPROC_H
> +
> +#include <linux/remoteproc.h>
> +#include <linux/tee_drv.h>
> +
> +/**
> + * 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
> + * @rsc_va: Resource table virtual address.
> + */
> +struct tee_rproc {
> + struct list_head node;
> + struct rproc *rproc;
> + struct device *parent;
> + u32 rproc_id;
> + u32 session_id;
> + void __iomem *rsc_va;
> +};
> +
> +#if IS_ENABLED(CONFIG_TEE_REMOTEPROC)
> +
> +struct tee_rproc *tee_rproc_register(struct device *dev, unsigned int rproc_id);
> +int tee_rproc_unregister(struct tee_rproc *trproc);
> +
> +int tee_rproc_load_fw(struct tee_rproc *trproc, const struct firmware *fw);
> +int rproc_tee_get_rsc_table(struct tee_rproc *trproc);

Shouldn't this be tee_rproc_get_rsc_table()? Why the exception?

> +struct resource_table *tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc);
> +int tee_rproc_start(struct tee_rproc *trproc);
> +int tee_rproc_stop(struct tee_rproc *trproc);
> +
> +#else
> +
> +static inline struct tee_rproc *tee_rproc_register(struct device *dev, unsigned int rproc_id)
> +{
> + return ERR_PTR(-ENODEV);
> +}
> +
> +static inline int tee_rproc_unregister(struct tee_rproc *trproc)
> +{
> + /* This shouldn't be possible */

Why can't this be possible? Looking at the Kconfig file it seems entirely
plausible to compile stm32_rproc without TEE_REMOTEPROC...

More comments to come tomorrow or on Monday.

Thanks,
Mathieu

> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int tee_rproc_load_fw(struct tee_rproc *trproc,
> + const struct firmware *fw)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int tee_rproc_start(struct tee_rproc *trproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int tee_rproc_stop(struct tee_rproc *trproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int rproc_tee_get_rsc_table(struct tee_rproc *trproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline struct resource_table *
> + tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return NULL;
> +}
> +
> +#endif /* CONFIG_TEE_REMOTEPROC */
> +#endif /* TEE_REMOTEPROC_H */
> --
> 2.25.1
>

2024-01-26 11:04:40

by Krzysztof Kozlowski

[permalink] [raw]
Subject: Re: [PATCH v2 2/4] dt-bindings: remoteproc: Add compatibility for TEE support

On 18/01/2024 11:04, Arnaud Pouliquen wrote:
> 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-tree:
> - In OP-TEE, a node is defined in the device tree with the
> st,stm32mp1-m4-tee to support signed remoteproc firmware.
> Based on DT properties, OP-TEE authenticates, loads, starts, and stops
> the firmware.
> - On Linux, when the compatibility is set, the Cortex-M resets should not
> be declared in the device tree.
>
> Signed-off-by: Arnaud Pouliquen <[email protected]>
> ---
> V1 to V2 updates
> - update "st,stm32mp1-m4" compatible description to generalize
> - remove the 'reset-names' requirement in one conditional branch, as the
> property is already part of the condition test.
> ---
> .../bindings/remoteproc/st,stm32-rproc.yaml | 52 +++++++++++++++----
> 1 file changed, 43 insertions(+), 9 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml
> index 370af61d8f28..6af821b15736 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

The patch looks good to me, but I wonder about this choice of two
compatibles.

Basically this is the same hardware with the same interface, but two
compatibles to differentiate a bit different firmware setup. We have
already such cases for Qualcomm [1] [2] and new ones will be coming. [3]

I wonder whether this should be rather the same compatible with
additional property, e.g. "st,tee-control" or "remote-control".

[1]
https://elixir.bootlin.com/linux/v6.7.1/source/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml#L54

[2]
https://elixir.bootlin.com/linux/v6.7.1/source/Documentation/devicetree/bindings/net/qcom,ipa.yaml#L129
(that's a bit different)

[3] https://lore.kernel.org/linux-devicetree/20240124103623.GJ4906@thinkpad/

@Rob,
Any general guidance for this and Qualcomm?

Best regards,
Krzysztof


2024-01-26 13:40:36

by Arnaud POULIQUEN

[permalink] [raw]
Subject: Re: [PATCH v2 1/4] remoteproc: Add TEE support

Hi Mathieu,

On 1/25/24 19:55, Mathieu Poirier wrote:
> Hi Arnaud,
>
> On Thu, Jan 18, 2024 at 11:04:30AM +0100, Arnaud Pouliquen wrote:
>> From: Arnaud Pouliquen <[email protected]>
>>
>> Add a remoteproc TEE (Trusted Execution Environment) device
>
> Device or driver? Seems to be the latter...

Right, driver is better

>
>> that will be probed by the TEE bus. If the associated Trusted
>> application is supported on secure part this device offers a client
>> interface to load a firmware in the secure part.
>> This firmware could be authenticated and decrypted by the secure
>> trusted application.
>>
>> Signed-off-by: Arnaud Pouliquen <[email protected]>
>> ---
>> drivers/remoteproc/Kconfig | 9 +
>> drivers/remoteproc/Makefile | 1 +
>> drivers/remoteproc/tee_remoteproc.c | 393 ++++++++++++++++++++++++++++
>> include/linux/tee_remoteproc.h | 99 +++++++
>> 4 files changed, 502 insertions(+)
>> create mode 100644 drivers/remoteproc/tee_remoteproc.c
>> create mode 100644 include/linux/tee_remoteproc.h
>>
>> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
>> index 48845dc8fa85..85299606806c 100644
>> --- a/drivers/remoteproc/Kconfig
>> +++ b/drivers/remoteproc/Kconfig
>> @@ -365,6 +365,15 @@ config XLNX_R5_REMOTEPROC
>>
>> It's safe to say N if not interested in using RPU r5f cores.
>>
>> +
>> +config TEE_REMOTEPROC
>> + tristate "trusted firmware support by a TEE application"
>> + depends on OPTEE
>> + help
>> + Support for trusted remote processors firmware. The firmware
>> + authentication and/or decryption are managed by a trusted application.
>> + 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..fa8daebce277 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_TEE_REMOTEPROC) += tee_remoteproc.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/tee_remoteproc.c b/drivers/remoteproc/tee_remoteproc.c
>> new file mode 100644
>> index 000000000000..49e1e0caf889
>> --- /dev/null
>> +++ b/drivers/remoteproc/tee_remoteproc.c
>> @@ -0,0 +1,393 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * Copyright (C) STMicroelectronics 2023 - All Rights Reserved
>> + * Author: Arnaud Pouliquen <[email protected]>
>> + */
>> +
>> +#include <linux/firmware.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_device.h>
>> +#include <linux/of_reserved_mem.h>
>> +#include <linux/remoteproc.h>
>> +#include <linux/slab.h>
>> +#include <linux/tee_drv.h>
>> +#include <linux/tee_remoteproc.h>
>> +
>> +#include "remoteproc_internal.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_mem {
>> + char name[20];
>> + void __iomem *cpu_addr;
>> + phys_addr_t bus_addr;
>> + u32 dev_addr;
>> + size_t size;
>> +};
>> +
>> +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 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 tee_rproc *trproc, const struct firmware *fw)
>> +{
>> + struct tee_ioctl_invoke_arg arg;
>> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
>> + struct tee_shm *fw_shm;
>> + int ret;
>> +
>> + 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);
>> +
>> + 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);
>> +
>> +int rproc_tee_get_rsc_table(struct tee_rproc *trproc)
>> +{
>> + struct tee_ioctl_invoke_arg arg;
>> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
>> + struct rproc *rproc = trproc->rproc;
>> + size_t rsc_size;
>> + int ret;
>> +
>> + 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;
>> + }
>> +
>> + rsc_size = param[2].u.value.a;
>> +
>> + /* If the size is null no resource table defined in the image */
>> + if (!rsc_size)
>> + return 0;
>> +
>> + /* Store the resource table address that would be updated by the remote core . */
>> + trproc->rsc_va = ioremap_wc(param[1].u.value.a, rsc_size);
>> + if (IS_ERR_OR_NULL(trproc->rsc_va)) {
>> + dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %lld+%zx\n",
>> + param[1].u.value.a, rsc_size);
>> + trproc->rsc_va = NULL;
>> + return -ENOMEM;
>> + }
>> +
>> + /*
>> + * A cached table is requested as the physical address is not mapped yet
>> + * but remoteproc needs to parse the table for resources.
>> + */
>> + rproc->cached_table = kmemdup((__force void *)trproc->rsc_va, rsc_size, GFP_KERNEL);
>> + if (!rproc->cached_table)
>> + return -ENOMEM;
>> +
>> + rproc->table_ptr = rproc->cached_table;
>> + rproc->table_sz = rsc_size;
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(rproc_tee_get_rsc_table);
>> +
>> +struct resource_table *tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc)
>> +{
>> + return (__force struct resource_table *)trproc->rsc_va;
>> +}
>> +EXPORT_SYMBOL_GPL(tee_rproc_get_loaded_rsc_table);
>> +
>> +int tee_rproc_start(struct tee_rproc *trproc)
>> +{
>> + struct tee_ioctl_invoke_arg arg;
>> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
>> + int ret;
>> +
>> + 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)
>> + ret = -EIO;
>> + }
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(tee_rproc_start);
>> +
>> +int tee_rproc_stop(struct tee_rproc *trproc)
>> +{
>> + struct tee_ioctl_invoke_arg arg;
>> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
>> + int ret;
>> +
>> + 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;
>> + }
>> + if (trproc->rsc_va)
>> + iounmap(trproc->rsc_va);
>> + trproc->rsc_va = NULL;
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(tee_rproc_stop);
>> +
>> +static const struct tee_client_device_id stm32_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, unsigned int rproc_id)
>> +{
>> + struct tee_client_device *rproc_tee_device;
>> + struct tee_ioctl_open_session_arg sess_arg;
>> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
>> + struct tee_rproc *trproc;
>> + int ret;
>> +
>> + /*
>> + * The device is not probed by the TEE bus. We ignore the reason (bus could be not yet
>> + * probed or service not available in the secure firmware)
>> + * Assumption here is that the TEE bus is not probed.
>> + */
>> + if (!tee_rproc_ctx)
>> + return ERR_PTR(-EPROBE_DEFER);
>> +
>> + trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
>> + if (!trproc)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + rproc_tee_device = to_tee_client_device(tee_rproc_ctx->dev);
>> + memset(&sess_arg, 0, sizeof(sess_arg));
>> +
>> + /* Open session with rproc_tee load the OP-TEE Trusted Application */
>> + memcpy(sess_arg.uuid, rproc_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);
>> + return ERR_PTR(-EINVAL);
>> + }
>> +
>> + trproc->parent = dev;
>> + trproc->rproc_id = rproc_id;
>> + trproc->session_id = sess_arg.session;
>> +
>> + list_add_tail(&trproc->node, &tee_rproc_ctx->sessions);
>> +
>> + return trproc;
>> +}
>> +EXPORT_SYMBOL_GPL(tee_rproc_register);
>> +
>> +int tee_rproc_unregister(struct tee_rproc *trproc)
>> +{
>> + int ret;
>> +
>> + if (!tee_rproc_ctx)
>> + return -ENODEV;
>> +
>> + 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);
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(tee_rproc_unregister);
>> +
>> +static int tee_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;
>> +
>> + /* Only one RPROC OP-TEE device allowed */
>> + if (tee_rproc_ctx) {
>> + dev_err(dev, "An RPROC OP-TEE device was already initialized: only one allowed\n");
>> + return -EBUSY;
>> + }
>> +
>> + /* Open context with TEE driver */
>> + tee_ctx = tee_client_open_context(NULL, tee_ctx_match, NULL, NULL);
>> + if (IS_ERR(tee_ctx))
>> + return PTR_ERR(tee_ctx);
>> +
>> + tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_ctx), GFP_KERNEL);
>> + if (!tee_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, stm32_tee_rproc_id_table);
>> +
>> +static struct tee_client_driver tee_rproc_fw_driver = {
>> + .id_table = stm32_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(" TEE remote processor control driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/include/linux/tee_remoteproc.h b/include/linux/tee_remoteproc.h
>> new file mode 100644
>> index 000000000000..537d6dc3b858
>> --- /dev/null
>> +++ b/include/linux/tee_remoteproc.h
>> @@ -0,0 +1,99 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + * Copyright(c) 2023 STMicroelectronics - All Rights Reserved
>> + */
>> +
>> +#ifndef TEE_REMOTEPROC_H
>> +#define TEE_REMOTEPROC_H
>> +
>> +#include <linux/remoteproc.h>
>> +#include <linux/tee_drv.h>
>> +
>> +/**
>> + * 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
>> + * @rsc_va: Resource table virtual address.
>> + */
>> +struct tee_rproc {
>> + struct list_head node;
>> + struct rproc *rproc;
>> + struct device *parent;
>> + u32 rproc_id;
>> + u32 session_id;
>> + void __iomem *rsc_va;
>> +};
>> +
>> +#if IS_ENABLED(CONFIG_TEE_REMOTEPROC)
>> +
>> +struct tee_rproc *tee_rproc_register(struct device *dev, unsigned int rproc_id);
>> +int tee_rproc_unregister(struct tee_rproc *trproc);
>> +
>> +int tee_rproc_load_fw(struct tee_rproc *trproc, const struct firmware *fw);
>> +int rproc_tee_get_rsc_table(struct tee_rproc *trproc);
>
> Shouldn't this be tee_rproc_get_rsc_table()? Why the exception?

No reason, just a miss during concatenation of patches to prepare the series.
I will change this.

>
>> +struct resource_table *tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc);
>> +int tee_rproc_start(struct tee_rproc *trproc);
>> +int tee_rproc_stop(struct tee_rproc *trproc);
>> +
>> +#else
>> +
>> +static inline struct tee_rproc *tee_rproc_register(struct device *dev, unsigned int rproc_id)
>> +{
>> + return ERR_PTR(-ENODEV);
>> +}
>> +
>> +static inline int tee_rproc_unregister(struct tee_rproc *trproc)
>> +{
>> + /* This shouldn't be possible */
>
> Why can't this be possible? Looking at the Kconfig file it seems entirely
> plausible to compile stm32_rproc without TEE_REMOTEPROC...

This sentence is copied from rpmsg.h [1]. It is not possible to call the API if
the TEE_REMOTEPROC is not set, as an error is returned by tee_rproc_register.
Therefore, we should not fall into a state where we call this API or another one.

[1]https://elixir.bootlin.com/linux/v6.7.1/source/include/linux/rpmsg.h#L218

Thanks,
Arnaud

>
> More comments to come tomorrow or on Monday.
>
> Thanks,
> Mathieu
>
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline int tee_rproc_load_fw(struct tee_rproc *trproc,
>> + const struct firmware *fw)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline int tee_rproc_start(struct tee_rproc *trproc)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline int tee_rproc_stop(struct tee_rproc *trproc)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline int rproc_tee_get_rsc_table(struct tee_rproc *trproc)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline struct resource_table *
>> + tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return NULL;
>> +}
>> +
>> +#endif /* CONFIG_TEE_REMOTEPROC */
>> +#endif /* TEE_REMOTEPROC_H */
>> --
>> 2.25.1
>>

2024-01-26 13:59:27

by Arnaud POULIQUEN

[permalink] [raw]
Subject: Re: [PATCH v2 2/4] dt-bindings: remoteproc: Add compatibility for TEE support

Hello Krzysztof,

On 1/26/24 12:03, Krzysztof Kozlowski wrote:
> On 18/01/2024 11:04, Arnaud Pouliquen wrote:
>> 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-tree:
>> - In OP-TEE, a node is defined in the device tree with the
>> st,stm32mp1-m4-tee to support signed remoteproc firmware.
>> Based on DT properties, OP-TEE authenticates, loads, starts, and stops
>> the firmware.
>> - On Linux, when the compatibility is set, the Cortex-M resets should not
>> be declared in the device tree.
>>
>> Signed-off-by: Arnaud Pouliquen <[email protected]>
>> ---
>> V1 to V2 updates
>> - update "st,stm32mp1-m4" compatible description to generalize
>> - remove the 'reset-names' requirement in one conditional branch, as the
>> property is already part of the condition test.
>> ---
>> .../bindings/remoteproc/st,stm32-rproc.yaml | 52 +++++++++++++++----
>> 1 file changed, 43 insertions(+), 9 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml
>> index 370af61d8f28..6af821b15736 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
>
> The patch looks good to me, but I wonder about this choice of two
> compatibles.
>
> Basically this is the same hardware with the same interface, but two
> compatibles to differentiate a bit different firmware setup. We have
> already such cases for Qualcomm [1] [2] and new ones will be coming. [3]
>
> I wonder whether this should be rather the same compatible with
> additional property, e.g. "st,tee-control" or "remote-control".

Yes the point is valid, I asked myself the question.

I proposed a compatibility solution for one main reason. On the STM32MP15, if
the firmware is loaded by Linux, no driver is probed in OP-TEE. But if the
firmware is authenticated and loaded by OP-TEE, a Op-TEE driver is probed to
manage memory access rights.

The drawback of a property is that we would need to probe the OP-TEE driver for
the STM32MP1 platform even if it is not used, just to check this property.

Thanks,
Arnaud

>
> [1]
> https://elixir.bootlin.com/linux/v6.7.1/source/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml#L54
>
> [2]
> https://elixir.bootlin.com/linux/v6.7.1/source/Documentation/devicetree/bindings/net/qcom,ipa.yaml#L129
> (that's a bit different)
>
> [3] https://lore.kernel.org/linux-devicetree/20240124103623.GJ4906@thinkpad/
>
> @Rob,
> Any general guidance for this and Qualcomm?
>
> Best regards,
> Krzysztof
>

2024-01-26 16:04:50

by Mathieu Poirier

[permalink] [raw]
Subject: Re: [PATCH v2 1/4] remoteproc: Add TEE support

On Fri, Jan 26, 2024 at 02:39:33PM +0100, Arnaud POULIQUEN wrote:
> Hi Mathieu,
>
> On 1/25/24 19:55, Mathieu Poirier wrote:
> > Hi Arnaud,
> >
> > On Thu, Jan 18, 2024 at 11:04:30AM +0100, Arnaud Pouliquen wrote:
> >> From: Arnaud Pouliquen <[email protected]>
> >>
> >> Add a remoteproc TEE (Trusted Execution Environment) device
> >
> > Device or driver? Seems to be the latter...
>
> Right, driver is better
>
> >
> >> that will be probed by the TEE bus. If the associated Trusted
> >> application is supported on secure part this device offers a client
> >> interface to load a firmware in the secure part.
> >> This firmware could be authenticated and decrypted by the secure
> >> trusted application.
> >>
> >> Signed-off-by: Arnaud Pouliquen <[email protected]>
> >> ---
> >> drivers/remoteproc/Kconfig | 9 +
> >> drivers/remoteproc/Makefile | 1 +
> >> drivers/remoteproc/tee_remoteproc.c | 393 ++++++++++++++++++++++++++++
> >> include/linux/tee_remoteproc.h | 99 +++++++
> >> 4 files changed, 502 insertions(+)
> >> create mode 100644 drivers/remoteproc/tee_remoteproc.c
> >> create mode 100644 include/linux/tee_remoteproc.h
> >>
> >> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> >> index 48845dc8fa85..85299606806c 100644
> >> --- a/drivers/remoteproc/Kconfig
> >> +++ b/drivers/remoteproc/Kconfig
> >> @@ -365,6 +365,15 @@ config XLNX_R5_REMOTEPROC
> >>
> >> It's safe to say N if not interested in using RPU r5f cores.
> >>
> >> +
> >> +config TEE_REMOTEPROC
> >> + tristate "trusted firmware support by a TEE application"
> >> + depends on OPTEE
> >> + help
> >> + Support for trusted remote processors firmware. The firmware
> >> + authentication and/or decryption are managed by a trusted application.
> >> + 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..fa8daebce277 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_TEE_REMOTEPROC) += tee_remoteproc.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/tee_remoteproc.c b/drivers/remoteproc/tee_remoteproc.c
> >> new file mode 100644
> >> index 000000000000..49e1e0caf889
> >> --- /dev/null
> >> +++ b/drivers/remoteproc/tee_remoteproc.c
> >> @@ -0,0 +1,393 @@
> >> +// SPDX-License-Identifier: GPL-2.0-or-later
> >> +/*
> >> + * Copyright (C) STMicroelectronics 2023 - All Rights Reserved
> >> + * Author: Arnaud Pouliquen <[email protected]>
> >> + */
> >> +
> >> +#include <linux/firmware.h>
> >> +#include <linux/interrupt.h>
> >> +#include <linux/io.h>
> >> +#include <linux/module.h>
> >> +#include <linux/of_address.h>
> >> +#include <linux/of_device.h>
> >> +#include <linux/of_reserved_mem.h>
> >> +#include <linux/remoteproc.h>
> >> +#include <linux/slab.h>
> >> +#include <linux/tee_drv.h>
> >> +#include <linux/tee_remoteproc.h>
> >> +
> >> +#include "remoteproc_internal.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_mem {
> >> + char name[20];
> >> + void __iomem *cpu_addr;
> >> + phys_addr_t bus_addr;
> >> + u32 dev_addr;
> >> + size_t size;
> >> +};
> >> +
> >> +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 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 tee_rproc *trproc, const struct firmware *fw)
> >> +{
> >> + struct tee_ioctl_invoke_arg arg;
> >> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> >> + struct tee_shm *fw_shm;
> >> + int ret;
> >> +
> >> + 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);
> >> +
> >> + 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);
> >> +
> >> +int rproc_tee_get_rsc_table(struct tee_rproc *trproc)
> >> +{
> >> + struct tee_ioctl_invoke_arg arg;
> >> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> >> + struct rproc *rproc = trproc->rproc;
> >> + size_t rsc_size;
> >> + int ret;
> >> +
> >> + 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;
> >> + }
> >> +
> >> + rsc_size = param[2].u.value.a;
> >> +
> >> + /* If the size is null no resource table defined in the image */
> >> + if (!rsc_size)
> >> + return 0;
> >> +
> >> + /* Store the resource table address that would be updated by the remote core . */
> >> + trproc->rsc_va = ioremap_wc(param[1].u.value.a, rsc_size);
> >> + if (IS_ERR_OR_NULL(trproc->rsc_va)) {
> >> + dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %lld+%zx\n",
> >> + param[1].u.value.a, rsc_size);
> >> + trproc->rsc_va = NULL;
> >> + return -ENOMEM;
> >> + }
> >> +
> >> + /*
> >> + * A cached table is requested as the physical address is not mapped yet
> >> + * but remoteproc needs to parse the table for resources.
> >> + */
> >> + rproc->cached_table = kmemdup((__force void *)trproc->rsc_va, rsc_size, GFP_KERNEL);
> >> + if (!rproc->cached_table)
> >> + return -ENOMEM;
> >> +
> >> + rproc->table_ptr = rproc->cached_table;
> >> + rproc->table_sz = rsc_size;
> >> +
> >> + return 0;
> >> +}
> >> +EXPORT_SYMBOL_GPL(rproc_tee_get_rsc_table);
> >> +
> >> +struct resource_table *tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc)
> >> +{
> >> + return (__force struct resource_table *)trproc->rsc_va;
> >> +}
> >> +EXPORT_SYMBOL_GPL(tee_rproc_get_loaded_rsc_table);
> >> +
> >> +int tee_rproc_start(struct tee_rproc *trproc)
> >> +{
> >> + struct tee_ioctl_invoke_arg arg;
> >> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> >> + int ret;
> >> +
> >> + 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)
> >> + ret = -EIO;
> >> + }
> >> +
> >> + return ret;
> >> +}
> >> +EXPORT_SYMBOL_GPL(tee_rproc_start);
> >> +
> >> +int tee_rproc_stop(struct tee_rproc *trproc)
> >> +{
> >> + struct tee_ioctl_invoke_arg arg;
> >> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> >> + int ret;
> >> +
> >> + 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;
> >> + }
> >> + if (trproc->rsc_va)
> >> + iounmap(trproc->rsc_va);
> >> + trproc->rsc_va = NULL;
> >> +
> >> + return ret;
> >> +}
> >> +EXPORT_SYMBOL_GPL(tee_rproc_stop);
> >> +
> >> +static const struct tee_client_device_id stm32_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, unsigned int rproc_id)
> >> +{
> >> + struct tee_client_device *rproc_tee_device;
> >> + struct tee_ioctl_open_session_arg sess_arg;
> >> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> >> + struct tee_rproc *trproc;
> >> + int ret;
> >> +
> >> + /*
> >> + * The device is not probed by the TEE bus. We ignore the reason (bus could be not yet
> >> + * probed or service not available in the secure firmware)
> >> + * Assumption here is that the TEE bus is not probed.
> >> + */
> >> + if (!tee_rproc_ctx)
> >> + return ERR_PTR(-EPROBE_DEFER);
> >> +
> >> + trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
> >> + if (!trproc)
> >> + return ERR_PTR(-ENOMEM);
> >> +
> >> + rproc_tee_device = to_tee_client_device(tee_rproc_ctx->dev);
> >> + memset(&sess_arg, 0, sizeof(sess_arg));
> >> +
> >> + /* Open session with rproc_tee load the OP-TEE Trusted Application */
> >> + memcpy(sess_arg.uuid, rproc_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);
> >> + return ERR_PTR(-EINVAL);
> >> + }
> >> +
> >> + trproc->parent = dev;
> >> + trproc->rproc_id = rproc_id;
> >> + trproc->session_id = sess_arg.session;
> >> +
> >> + list_add_tail(&trproc->node, &tee_rproc_ctx->sessions);
> >> +
> >> + return trproc;
> >> +}
> >> +EXPORT_SYMBOL_GPL(tee_rproc_register);
> >> +
> >> +int tee_rproc_unregister(struct tee_rproc *trproc)
> >> +{
> >> + int ret;
> >> +
> >> + if (!tee_rproc_ctx)
> >> + return -ENODEV;
> >> +
> >> + 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);
> >> +
> >> + return ret;
> >> +}
> >> +EXPORT_SYMBOL_GPL(tee_rproc_unregister);
> >> +
> >> +static int tee_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;
> >> +
> >> + /* Only one RPROC OP-TEE device allowed */
> >> + if (tee_rproc_ctx) {
> >> + dev_err(dev, "An RPROC OP-TEE device was already initialized: only one allowed\n");
> >> + return -EBUSY;
> >> + }
> >> +
> >> + /* Open context with TEE driver */
> >> + tee_ctx = tee_client_open_context(NULL, tee_ctx_match, NULL, NULL);
> >> + if (IS_ERR(tee_ctx))
> >> + return PTR_ERR(tee_ctx);
> >> +
> >> + tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_ctx), GFP_KERNEL);
> >> + if (!tee_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, stm32_tee_rproc_id_table);
> >> +
> >> +static struct tee_client_driver tee_rproc_fw_driver = {
> >> + .id_table = stm32_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(" TEE remote processor control driver");
> >> +MODULE_LICENSE("GPL");
> >> diff --git a/include/linux/tee_remoteproc.h b/include/linux/tee_remoteproc.h
> >> new file mode 100644
> >> index 000000000000..537d6dc3b858
> >> --- /dev/null
> >> +++ b/include/linux/tee_remoteproc.h
> >> @@ -0,0 +1,99 @@
> >> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> >> +/*
> >> + * Copyright(c) 2023 STMicroelectronics - All Rights Reserved
> >> + */
> >> +
> >> +#ifndef TEE_REMOTEPROC_H
> >> +#define TEE_REMOTEPROC_H
> >> +
> >> +#include <linux/remoteproc.h>
> >> +#include <linux/tee_drv.h>
> >> +
> >> +/**
> >> + * 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
> >> + * @rsc_va: Resource table virtual address.
> >> + */
> >> +struct tee_rproc {
> >> + struct list_head node;
> >> + struct rproc *rproc;
> >> + struct device *parent;
> >> + u32 rproc_id;
> >> + u32 session_id;
> >> + void __iomem *rsc_va;
> >> +};
> >> +
> >> +#if IS_ENABLED(CONFIG_TEE_REMOTEPROC)
> >> +
> >> +struct tee_rproc *tee_rproc_register(struct device *dev, unsigned int rproc_id);
> >> +int tee_rproc_unregister(struct tee_rproc *trproc);
> >> +
> >> +int tee_rproc_load_fw(struct tee_rproc *trproc, const struct firmware *fw);
> >> +int rproc_tee_get_rsc_table(struct tee_rproc *trproc);
> >
> > Shouldn't this be tee_rproc_get_rsc_table()? Why the exception?
>
> No reason, just a miss during concatenation of patches to prepare the series.
> I will change this.
>
> >
> >> +struct resource_table *tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc);
> >> +int tee_rproc_start(struct tee_rproc *trproc);
> >> +int tee_rproc_stop(struct tee_rproc *trproc);
> >> +
> >> +#else
> >> +
> >> +static inline struct tee_rproc *tee_rproc_register(struct device *dev, unsigned int rproc_id)
> >> +{
> >> + return ERR_PTR(-ENODEV);
> >> +}
> >> +
> >> +static inline int tee_rproc_unregister(struct tee_rproc *trproc)
> >> +{
> >> + /* This shouldn't be possible */
> >
> > Why can't this be possible? Looking at the Kconfig file it seems entirely
> > plausible to compile stm32_rproc without TEE_REMOTEPROC...
>
> This sentence is copied from rpmsg.h [1]. It is not possible to call the API if
> the TEE_REMOTEPROC is not set, as an error is returned by tee_rproc_register.

Yes, I see. I agree with your assessment.

> Therefore, we should not fall into a state where we call this API or another one.
>
> [1]https://elixir.bootlin.com/linux/v6.7.1/source/include/linux/rpmsg.h#L218
>
> Thanks,
> Arnaud
>
> >
> > More comments to come tomorrow or on Monday.
> >
> > Thanks,
> > Mathieu
> >
> >> + WARN_ON(1);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static inline int tee_rproc_load_fw(struct tee_rproc *trproc,
> >> + const struct firmware *fw)
> >> +{
> >> + /* This shouldn't be possible */
> >> + WARN_ON(1);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static inline int tee_rproc_start(struct tee_rproc *trproc)
> >> +{
> >> + /* This shouldn't be possible */
> >> + WARN_ON(1);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static inline int tee_rproc_stop(struct tee_rproc *trproc)
> >> +{
> >> + /* This shouldn't be possible */
> >> + WARN_ON(1);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static inline int rproc_tee_get_rsc_table(struct tee_rproc *trproc)
> >> +{
> >> + /* This shouldn't be possible */
> >> + WARN_ON(1);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static inline struct resource_table *
> >> + tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc)
> >> +{
> >> + /* This shouldn't be possible */
> >> + WARN_ON(1);
> >> +
> >> + return NULL;
> >> +}
> >> +
> >> +#endif /* CONFIG_TEE_REMOTEPROC */
> >> +#endif /* TEE_REMOTEPROC_H */
> >> --
> >> 2.25.1
> >>

2024-01-26 17:11:29

by Mathieu Poirier

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware

On Thu, Jan 18, 2024 at 11:04:33AM +0100, Arnaud Pouliquen wrote:
> 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 <[email protected]>
> ---
> V1 to V2 update:
> - remove the select "TEE_REMOTEPROC" in STM32_RPROC config as detected by
> the kernel test robot:
> WARNING: unmet direct dependencies detected for TEE_REMOTEPROC
> Depends on [n]: REMOTEPROC [=y] && OPTEE [=n]
> Selected by [y]:
> - STM32_RPROC [=y] && (ARCH_STM32 || COMPILE_TEST [=y]) && REMOTEPROC [=y]
> - Fix initialized trproc variable in stm32_rproc_probe
> ---
> drivers/remoteproc/stm32_rproc.c | 149 +++++++++++++++++++++++++++++--
> 1 file changed, 144 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
> index fcc0001e2657..cf6a21bac945 100644
> --- a/drivers/remoteproc/stm32_rproc.c
> +++ b/drivers/remoteproc/stm32_rproc.c
> @@ -20,6 +20,7 @@
> #include <linux/remoteproc.h>
> #include <linux/reset.h>
> #include <linux/slab.h>
> +#include <linux/tee_remoteproc.h>
> #include <linux/workqueue.h>
>
> #include "remoteproc_internal.h"
> @@ -49,6 +50,9 @@
> #define M4_STATE_STANDBY 4
> #define M4_STATE_CRASH 5
>
> +/* Remote processor unique identifier aligned with the Trusted Execution Environment definitions */
> +#define STM32_MP1_M4_PROC_ID 0
> +
> struct stm32_syscon {
> struct regmap *map;
> u32 reg;
> @@ -90,6 +94,8 @@ struct stm32_rproc {
> struct stm32_mbox mb[MBOX_NB_MBX];
> struct workqueue_struct *workqueue;
> bool hold_boot_smc;
> + bool fw_loaded;
> + struct tee_rproc *trproc;
> void __iomem *rsc_va;
> };
>
> @@ -257,6 +263,91 @@ static int stm32_rproc_release(struct rproc *rproc)
> return err;
> }
>
> +static int stm32_rproc_tee_elf_sanity_check(struct rproc *rproc,
> + const struct firmware *fw)
> +{
> + struct stm32_rproc *ddata = rproc->priv;
> + unsigned int ret = 0;
> +
> + if (rproc->state == RPROC_DETACHED)
> + return 0;
> +
> + ret = tee_rproc_load_fw(ddata->trproc, fw);
> + if (!ret)
> + ddata->fw_loaded = true;
> +
> + return ret;
> +}
> +
> +static int stm32_rproc_tee_elf_load(struct rproc *rproc,
> + const struct firmware *fw)
> +{
> + struct stm32_rproc *ddata = rproc->priv;
> + unsigned int ret;
> +
> + /*
> + * This function can be called by remote proc for recovery
> + * without the sanity check. In this case we need to load the firmware
> + * else nothing done here as the firmware has been preloaded for the
> + * sanity check to be able to parse it for the resource table.
> + */

This comment is very confusing - please consider refactoring.

> + if (ddata->fw_loaded)
> + return 0;
> +

I'm not sure about keeping a flag to indicate the status of the loaded firmware.
It is not done for the non-secure method, I don't see why it would be needed for
the secure one.

> + ret = tee_rproc_load_fw(ddata->trproc, fw);
> + if (ret)
> + return ret;
> + ddata->fw_loaded = true;
> +
> + /* Update the resource table parameters. */
> + if (rproc_tee_get_rsc_table(ddata->trproc)) {
> + /* No resource table: reset the related fields. */
> + rproc->cached_table = NULL;
> + rproc->table_ptr = NULL;
> + rproc->table_sz = 0;
> + }
> +
> + return 0;
> +}
> +
> +static struct resource_table *
> +stm32_rproc_tee_elf_find_loaded_rsc_table(struct rproc *rproc,
> + const struct firmware *fw)
> +{
> + struct stm32_rproc *ddata = rproc->priv;
> +
> + return tee_rproc_get_loaded_rsc_table(ddata->trproc);
> +}
> +
> +static int stm32_rproc_tee_start(struct rproc *rproc)
> +{
> + struct stm32_rproc *ddata = rproc->priv;
> +
> + return tee_rproc_start(ddata->trproc);
> +}
> +
> +static int stm32_rproc_tee_attach(struct rproc *rproc)
> +{
> + /* Nothing to do, remote proc already started by the secured context. */
> + return 0;
> +}
> +
> +static int stm32_rproc_tee_stop(struct rproc *rproc)
> +{
> + struct stm32_rproc *ddata = rproc->priv;
> + int err;
> +
> + stm32_rproc_request_shutdown(rproc);
> +
> + err = tee_rproc_stop(ddata->trproc);
> + if (err)
> + return err;
> +
> + ddata->fw_loaded = false;
> +
> + return stm32_rproc_release(rproc);
> +}
> +
> static int stm32_rproc_prepare(struct rproc *rproc)
> {
> struct device *dev = rproc->dev.parent;
> @@ -319,7 +410,14 @@ static int stm32_rproc_prepare(struct rproc *rproc)
>
> static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
> {
> - if (rproc_elf_load_rsc_table(rproc, fw))
> + struct stm32_rproc *ddata = rproc->priv;
> + int ret;
> +
> + if (ddata->trproc)
> + ret = rproc_tee_get_rsc_table(ddata->trproc);
> + else
> + ret = rproc_elf_load_rsc_table(rproc, fw);
> + if (ret)
> dev_warn(&rproc->dev, "no resource table found for this firmware\n");
>
> return 0;
> @@ -693,8 +791,22 @@ 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 = stm32_rproc_tee_start,
> + .stop = stm32_rproc_tee_stop,
> + .attach = stm32_rproc_tee_attach,
> + .kick = stm32_rproc_kick,
> + .parse_fw = stm32_rproc_parse_fw,
> + .find_loaded_rsc_table = stm32_rproc_tee_elf_find_loaded_rsc_table,
> + .get_loaded_rsc_table = stm32_rproc_get_loaded_rsc_table,
> + .sanity_check = stm32_rproc_tee_elf_sanity_check,
> + .load = stm32_rproc_tee_elf_load,
> +};
> +
> static const struct of_device_id stm32_rproc_match[] = {
> - { .compatible = "st,stm32mp1-m4" },
> + {.compatible = "st,stm32mp1-m4",},
> + {.compatible = "st,stm32mp1-m4-tee",},
> {},
> };
> MODULE_DEVICE_TABLE(of, stm32_rproc_match);
> @@ -853,6 +965,7 @@ 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;
> int ret;
> @@ -861,11 +974,31 @@ static int stm32_rproc_probe(struct platform_device *pdev)
> if (ret)
> return ret;
>
> - rproc = 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")) {
> + trproc = tee_rproc_register(dev, STM32_MP1_M4_PROC_ID);
> + if (IS_ERR(trproc)) {
> + dev_err_probe(dev, PTR_ERR(trproc),
> + "signed firmware not supported by TEE\n");
> + return PTR_ERR(trproc);
> + }
> + /*
> + * Delegate the firmware management to the secure context.
> + * The firmware loaded has to be signed.
> + */
> + dev_info(dev, "Support of signed firmware only\n");

Not sure what this adds. Please remove.

> + }
> + rproc = rproc_alloc(dev, np->name,
> + trproc ? &st_rproc_tee_ops : &st_rproc_ops,
> + NULL, sizeof(*ddata));
> + if (!rproc) {
> + ret = -ENOMEM;
> + goto free_tee;
> + }
>
> ddata = rproc->priv;
> + ddata->trproc = trproc;
> + if (trproc)
> + trproc->rproc = rproc;
>
> rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
>
> @@ -916,6 +1049,10 @@ static int stm32_rproc_probe(struct platform_device *pdev)
> device_init_wakeup(dev, false);
> }
> rproc_free(rproc);
> +free_tee:
> + if (trproc)
> + tee_rproc_unregister(trproc);
> +
> return ret;
> }
>
> @@ -937,6 +1074,8 @@ static void stm32_rproc_remove(struct platform_device *pdev)
> device_init_wakeup(dev, false);
> }
> rproc_free(rproc);
> + if (ddata->trproc)
> + tee_rproc_unregister(ddata->trproc);
> }
>
> static int stm32_rproc_suspend(struct device *dev)
> --
> 2.25.1
>

2024-01-26 18:03:45

by Mathieu Poirier

[permalink] [raw]
Subject: Re: [PATCH v2 1/4] remoteproc: Add TEE support

On Thu, Jan 18, 2024 at 11:04:30AM +0100, Arnaud Pouliquen wrote:
> From: Arnaud Pouliquen <[email protected]>
>
> Add a remoteproc TEE (Trusted Execution Environment) device
> that will be probed by the TEE bus. If the associated Trusted
> application is supported on secure part this device offers a client
> interface to load a firmware in the secure part.
> This firmware could be authenticated and decrypted by the secure
> trusted application.
>
> Signed-off-by: Arnaud Pouliquen <[email protected]>
> ---
> drivers/remoteproc/Kconfig | 9 +
> drivers/remoteproc/Makefile | 1 +
> drivers/remoteproc/tee_remoteproc.c | 393 ++++++++++++++++++++++++++++
> include/linux/tee_remoteproc.h | 99 +++++++
> 4 files changed, 502 insertions(+)
> create mode 100644 drivers/remoteproc/tee_remoteproc.c
> create mode 100644 include/linux/tee_remoteproc.h
>
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> index 48845dc8fa85..85299606806c 100644
> --- a/drivers/remoteproc/Kconfig
> +++ b/drivers/remoteproc/Kconfig
> @@ -365,6 +365,15 @@ config XLNX_R5_REMOTEPROC
>
> It's safe to say N if not interested in using RPU r5f cores.
>
> +
> +config TEE_REMOTEPROC
> + tristate "trusted firmware support by a TEE application"
> + depends on OPTEE
> + help
> + Support for trusted remote processors firmware. The firmware
> + authentication and/or decryption are managed by a trusted application.
> + 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..fa8daebce277 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_TEE_REMOTEPROC) += tee_remoteproc.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/tee_remoteproc.c b/drivers/remoteproc/tee_remoteproc.c
> new file mode 100644
> index 000000000000..49e1e0caf889
> --- /dev/null
> +++ b/drivers/remoteproc/tee_remoteproc.c
> @@ -0,0 +1,393 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) STMicroelectronics 2023 - All Rights Reserved
> + * Author: Arnaud Pouliquen <[email protected]>
> + */
> +
> +#include <linux/firmware.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/remoteproc.h>
> +#include <linux/slab.h>
> +#include <linux/tee_drv.h>
> +#include <linux/tee_remoteproc.h>
> +
> +#include "remoteproc_internal.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_mem {
> + char name[20];
> + void __iomem *cpu_addr;
> + phys_addr_t bus_addr;
> + u32 dev_addr;
> + size_t size;
> +};
> +
> +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 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 tee_rproc *trproc, const struct firmware *fw)
> +{
> + struct tee_ioctl_invoke_arg arg;
> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> + struct tee_shm *fw_shm;
> + int ret;
> +
> + 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);
> +
> + 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);
> +
> +int rproc_tee_get_rsc_table(struct tee_rproc *trproc)
> +{
> + struct tee_ioctl_invoke_arg arg;
> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> + struct rproc *rproc = trproc->rproc;
> + size_t rsc_size;
> + int ret;
> +
> + 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;
> + }
> +
> + rsc_size = param[2].u.value.a;
> +
> + /* If the size is null no resource table defined in the image */
> + if (!rsc_size)
> + return 0;
> +
> + /* Store the resource table address that would be updated by the remote core . */
> + trproc->rsc_va = ioremap_wc(param[1].u.value.a, rsc_size);
> + if (IS_ERR_OR_NULL(trproc->rsc_va)) {
> + dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %lld+%zx\n",
> + param[1].u.value.a, rsc_size);
> + trproc->rsc_va = NULL;
> + return -ENOMEM;
> + }
> +
> + /*
> + * A cached table is requested as the physical address is not mapped yet
> + * but remoteproc needs to parse the table for resources.
> + */
> + rproc->cached_table = kmemdup((__force void *)trproc->rsc_va, rsc_size, GFP_KERNEL);
> + if (!rproc->cached_table)
> + return -ENOMEM;
> +
> + rproc->table_ptr = rproc->cached_table;
> + rproc->table_sz = rsc_size;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_get_rsc_table);
> +
> +struct resource_table *tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc)
> +{
> + return (__force struct resource_table *)trproc->rsc_va;
> +}
> +EXPORT_SYMBOL_GPL(tee_rproc_get_loaded_rsc_table);
> +
> +int tee_rproc_start(struct tee_rproc *trproc)
> +{
> + struct tee_ioctl_invoke_arg arg;
> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> + int ret;
> +
> + 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)
> + ret = -EIO;
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(tee_rproc_start);
> +
> +int tee_rproc_stop(struct tee_rproc *trproc)
> +{
> + struct tee_ioctl_invoke_arg arg;
> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> + int ret;
> +
> + 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;
> + }
> + if (trproc->rsc_va)
> + iounmap(trproc->rsc_va);
> + trproc->rsc_va = NULL;
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(tee_rproc_stop);
> +
> +static const struct tee_client_device_id stm32_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, unsigned int rproc_id)
> +{
> + struct tee_client_device *rproc_tee_device;

This belongs to the TEE subsystem and as such would call it tee_device.

> + struct tee_ioctl_open_session_arg sess_arg;
> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> + struct tee_rproc *trproc;
> + int ret;
> +
> + /*
> + * The device is not probed by the TEE bus. We ignore the reason (bus could be not yet
> + * probed or service not available in the secure firmware)
> + * Assumption here is that the TEE bus is not probed.
> + */
> + if (!tee_rproc_ctx)
> + return ERR_PTR(-EPROBE_DEFER);
> +
> + trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
> + if (!trproc)
> + return ERR_PTR(-ENOMEM);
> +
> + rproc_tee_device = to_tee_client_device(tee_rproc_ctx->dev);
> + memset(&sess_arg, 0, sizeof(sess_arg));
> +
> + /* Open session with rproc_tee load the OP-TEE Trusted Application */
> + memcpy(sess_arg.uuid, rproc_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);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + trproc->parent = dev;
> + trproc->rproc_id = rproc_id;
> + trproc->session_id = sess_arg.session;
> +
> + list_add_tail(&trproc->node, &tee_rproc_ctx->sessions);
> +
> + return trproc;
> +}
> +EXPORT_SYMBOL_GPL(tee_rproc_register);
> +
> +int tee_rproc_unregister(struct tee_rproc *trproc)
> +{
> + int ret;
> +
> + if (!tee_rproc_ctx)
> + return -ENODEV;
> +
> + 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);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(tee_rproc_unregister);
> +
> +static int tee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)

tee_rproc_ctx_match()

> +{
> + /* 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;
> +
> + /* Only one RPROC OP-TEE device allowed */
> + if (tee_rproc_ctx) {
> + dev_err(dev, "An RPROC OP-TEE device was already initialized: only one allowed\n");
> + return -EBUSY;
> + }
> +
> + /* Open context with TEE driver */
> + tee_ctx = tee_client_open_context(NULL, tee_ctx_match, NULL, NULL);
> + if (IS_ERR(tee_ctx))
> + return PTR_ERR(tee_ctx);
> +
> + tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_ctx), GFP_KERNEL);
> + if (!tee_ctx) {

There is a big problem here...

More comments on Monday.

Thanks,
Mathieu

> + 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, stm32_tee_rproc_id_table);
> +
> +static struct tee_client_driver tee_rproc_fw_driver = {
> + .id_table = stm32_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(" TEE remote processor control driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/tee_remoteproc.h b/include/linux/tee_remoteproc.h
> new file mode 100644
> index 000000000000..537d6dc3b858
> --- /dev/null
> +++ b/include/linux/tee_remoteproc.h
> @@ -0,0 +1,99 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright(c) 2023 STMicroelectronics - All Rights Reserved
> + */
> +
> +#ifndef TEE_REMOTEPROC_H
> +#define TEE_REMOTEPROC_H
> +
> +#include <linux/remoteproc.h>
> +#include <linux/tee_drv.h>
> +
> +/**
> + * 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
> + * @rsc_va: Resource table virtual address.
> + */
> +struct tee_rproc {
> + struct list_head node;
> + struct rproc *rproc;
> + struct device *parent;
> + u32 rproc_id;
> + u32 session_id;
> + void __iomem *rsc_va;
> +};
> +
> +#if IS_ENABLED(CONFIG_TEE_REMOTEPROC)
> +
> +struct tee_rproc *tee_rproc_register(struct device *dev, unsigned int rproc_id);
> +int tee_rproc_unregister(struct tee_rproc *trproc);
> +
> +int tee_rproc_load_fw(struct tee_rproc *trproc, const struct firmware *fw);
> +int rproc_tee_get_rsc_table(struct tee_rproc *trproc);
> +struct resource_table *tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc);
> +int tee_rproc_start(struct tee_rproc *trproc);
> +int tee_rproc_stop(struct tee_rproc *trproc);
> +
> +#else
> +
> +static inline struct tee_rproc *tee_rproc_register(struct device *dev, unsigned int rproc_id)
> +{
> + return ERR_PTR(-ENODEV);
> +}
> +
> +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 tee_rproc *trproc,
> + const struct firmware *fw)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int tee_rproc_start(struct tee_rproc *trproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int tee_rproc_stop(struct tee_rproc *trproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int rproc_tee_get_rsc_table(struct tee_rproc *trproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline struct resource_table *
> + tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return NULL;
> +}
> +
> +#endif /* CONFIG_TEE_REMOTEPROC */
> +#endif /* TEE_REMOTEPROC_H */
> --
> 2.25.1
>

2024-01-29 18:55:21

by Mathieu Poirier

[permalink] [raw]
Subject: Re: [PATCH v2 1/4] remoteproc: Add TEE support

On Thu, Jan 18, 2024 at 11:04:30AM +0100, Arnaud Pouliquen wrote:
> From: Arnaud Pouliquen <[email protected]>
>
> Add a remoteproc TEE (Trusted Execution Environment) device
> that will be probed by the TEE bus. If the associated Trusted
> application is supported on secure part this device offers a client
> interface to load a firmware in the secure part.
> This firmware could be authenticated and decrypted by the secure
> trusted application.
>
> Signed-off-by: Arnaud Pouliquen <[email protected]>
> ---
> drivers/remoteproc/Kconfig | 9 +
> drivers/remoteproc/Makefile | 1 +
> drivers/remoteproc/tee_remoteproc.c | 393 ++++++++++++++++++++++++++++
> include/linux/tee_remoteproc.h | 99 +++++++
> 4 files changed, 502 insertions(+)
> create mode 100644 drivers/remoteproc/tee_remoteproc.c
> create mode 100644 include/linux/tee_remoteproc.h
>
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> index 48845dc8fa85..85299606806c 100644
> --- a/drivers/remoteproc/Kconfig
> +++ b/drivers/remoteproc/Kconfig
> @@ -365,6 +365,15 @@ config XLNX_R5_REMOTEPROC
>
> It's safe to say N if not interested in using RPU r5f cores.
>
> +
> +config TEE_REMOTEPROC
> + tristate "trusted firmware support by a TEE application"
> + depends on OPTEE
> + help
> + Support for trusted remote processors firmware. The firmware
> + authentication and/or decryption are managed by a trusted application.
> + 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..fa8daebce277 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_TEE_REMOTEPROC) += tee_remoteproc.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/tee_remoteproc.c b/drivers/remoteproc/tee_remoteproc.c
> new file mode 100644
> index 000000000000..49e1e0caf889
> --- /dev/null
> +++ b/drivers/remoteproc/tee_remoteproc.c
> @@ -0,0 +1,393 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) STMicroelectronics 2023 - All Rights Reserved
> + * Author: Arnaud Pouliquen <[email protected]>
> + */
> +
> +#include <linux/firmware.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/remoteproc.h>
> +#include <linux/slab.h>
> +#include <linux/tee_drv.h>
> +#include <linux/tee_remoteproc.h>
> +
> +#include "remoteproc_internal.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_mem {
> + char name[20];
> + void __iomem *cpu_addr;
> + phys_addr_t bus_addr;
> + u32 dev_addr;
> + size_t size;
> +};
> +
> +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 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 tee_rproc *trproc, const struct firmware *fw)
> +{
> + struct tee_ioctl_invoke_arg arg;
> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> + struct tee_shm *fw_shm;
> + int ret;
> +
> + 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);
> +
> + 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);
> +
> +int rproc_tee_get_rsc_table(struct tee_rproc *trproc)
> +{
> + struct tee_ioctl_invoke_arg arg;
> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> + struct rproc *rproc = trproc->rproc;
> + size_t rsc_size;
> + int ret;
> +
> + 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;
> + }
> +
> + rsc_size = param[2].u.value.a;
> +
> + /* If the size is null no resource table defined in the image */
> + if (!rsc_size)
> + return 0;
> +
> + /* Store the resource table address that would be updated by the remote core . */
> + trproc->rsc_va = ioremap_wc(param[1].u.value.a, rsc_size);
> + if (IS_ERR_OR_NULL(trproc->rsc_va)) {
> + dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %lld+%zx\n",
> + param[1].u.value.a, rsc_size);
> + trproc->rsc_va = NULL;
> + return -ENOMEM;
> + }
> +
> + /*
> + * A cached table is requested as the physical address is not mapped yet
> + * but remoteproc needs to parse the table for resources.
> + */
> + rproc->cached_table = kmemdup((__force void *)trproc->rsc_va, rsc_size, GFP_KERNEL);
> + if (!rproc->cached_table)
> + return -ENOMEM;
> +
> + rproc->table_ptr = rproc->cached_table;
> + rproc->table_sz = rsc_size;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_get_rsc_table);
> +
> +struct resource_table *tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc)

Please change this to tee_rproc_find_loaded_rsc_table(). That way we know it
should be correlated with ops::find_loaded_rsc_table() rather than
ops::get_loaded_rsc_table().

> +{
> + return (__force struct resource_table *)trproc->rsc_va;
> +}
> +EXPORT_SYMBOL_GPL(tee_rproc_get_loaded_rsc_table);
> +
> +int tee_rproc_start(struct tee_rproc *trproc)
> +{
> + struct tee_ioctl_invoke_arg arg;
> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> + int ret;
> +
> + 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) {

Please split the conditions of this if() statement to make it easier to understand
the correlation with the other if() below. Same for tee_rproc_stop().

> + 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)
> + ret = -EIO;
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(tee_rproc_start);
> +
> +int tee_rproc_stop(struct tee_rproc *trproc)
> +{
> + struct tee_ioctl_invoke_arg arg;
> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> + int ret;
> +
> + 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;
> + }
> + if (trproc->rsc_va)
> + iounmap(trproc->rsc_va);
> + trproc->rsc_va = NULL;
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(tee_rproc_stop);
> +
> +static const struct tee_client_device_id stm32_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, unsigned int rproc_id)
> +{
> + struct tee_client_device *rproc_tee_device;
> + struct tee_ioctl_open_session_arg sess_arg;
> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
> + struct tee_rproc *trproc;
> + int ret;
> +
> + /*
> + * The device is not probed by the TEE bus. We ignore the reason (bus could be not yet
> + * probed or service not available in the secure firmware)
> + * Assumption here is that the TEE bus is not probed.
> + */
> + if (!tee_rproc_ctx)
> + return ERR_PTR(-EPROBE_DEFER);
> +
> + trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
> + if (!trproc)
> + return ERR_PTR(-ENOMEM);
> +
> + rproc_tee_device = to_tee_client_device(tee_rproc_ctx->dev);
> + memset(&sess_arg, 0, sizeof(sess_arg));
> +
> + /* Open session with rproc_tee load the OP-TEE Trusted Application */
> + memcpy(sess_arg.uuid, rproc_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);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + trproc->parent = dev;
> + trproc->rproc_id = rproc_id;
> + trproc->session_id = sess_arg.session;
> +
> + list_add_tail(&trproc->node, &tee_rproc_ctx->sessions);
> +
> + return trproc;
> +}
> +EXPORT_SYMBOL_GPL(tee_rproc_register);
> +
> +int tee_rproc_unregister(struct tee_rproc *trproc)
> +{
> + int ret;
> +
> + if (!tee_rproc_ctx)
> + return -ENODEV;

Not sure this check is needed since we can't be here if memory allocation for
@tee_rproc_ctx failed in tee_rproc_probe().

> +
> + 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);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(tee_rproc_unregister);
> +
> +static int tee_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;
> +
> + /* Only one RPROC OP-TEE device allowed */
> + if (tee_rproc_ctx) {
> + dev_err(dev, "An RPROC OP-TEE device was already initialized: only one allowed\n");
> + return -EBUSY;
> + }

Is this check needed? How can @tee_rproc_ctx be initialized twice?

More comments tomorrow.

Thanks,
Mathieu

> +
> + /* Open context with TEE driver */
> + tee_ctx = tee_client_open_context(NULL, tee_ctx_match, NULL, NULL);
> + if (IS_ERR(tee_ctx))
> + return PTR_ERR(tee_ctx);
> +
> + tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_ctx), GFP_KERNEL);
> + if (!tee_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, stm32_tee_rproc_id_table);
> +
> +static struct tee_client_driver tee_rproc_fw_driver = {
> + .id_table = stm32_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(" TEE remote processor control driver");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/tee_remoteproc.h b/include/linux/tee_remoteproc.h
> new file mode 100644
> index 000000000000..537d6dc3b858
> --- /dev/null
> +++ b/include/linux/tee_remoteproc.h
> @@ -0,0 +1,99 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright(c) 2023 STMicroelectronics - All Rights Reserved
> + */
> +
> +#ifndef TEE_REMOTEPROC_H
> +#define TEE_REMOTEPROC_H
> +
> +#include <linux/remoteproc.h>
> +#include <linux/tee_drv.h>
> +
> +/**
> + * 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
> + * @rsc_va: Resource table virtual address.
> + */
> +struct tee_rproc {
> + struct list_head node;
> + struct rproc *rproc;
> + struct device *parent;
> + u32 rproc_id;
> + u32 session_id;
> + void __iomem *rsc_va;
> +};
> +
> +#if IS_ENABLED(CONFIG_TEE_REMOTEPROC)
> +
> +struct tee_rproc *tee_rproc_register(struct device *dev, unsigned int rproc_id);
> +int tee_rproc_unregister(struct tee_rproc *trproc);
> +
> +int tee_rproc_load_fw(struct tee_rproc *trproc, const struct firmware *fw);
> +int rproc_tee_get_rsc_table(struct tee_rproc *trproc);
> +struct resource_table *tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc);
> +int tee_rproc_start(struct tee_rproc *trproc);
> +int tee_rproc_stop(struct tee_rproc *trproc);
> +
> +#else
> +
> +static inline struct tee_rproc *tee_rproc_register(struct device *dev, unsigned int rproc_id)
> +{
> + return ERR_PTR(-ENODEV);
> +}
> +
> +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 tee_rproc *trproc,
> + const struct firmware *fw)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int tee_rproc_start(struct tee_rproc *trproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int tee_rproc_stop(struct tee_rproc *trproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int rproc_tee_get_rsc_table(struct tee_rproc *trproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline struct resource_table *
> + tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return NULL;
> +}
> +
> +#endif /* CONFIG_TEE_REMOTEPROC */
> +#endif /* TEE_REMOTEPROC_H */
> --
> 2.25.1
>

2024-01-30 08:23:59

by Arnaud POULIQUEN

[permalink] [raw]
Subject: Re: [PATCH v2 1/4] remoteproc: Add TEE support



On 1/29/24 19:55, Mathieu Poirier wrote:
> On Thu, Jan 18, 2024 at 11:04:30AM +0100, Arnaud Pouliquen wrote:
>> From: Arnaud Pouliquen <[email protected]>
>>
>> Add a remoteproc TEE (Trusted Execution Environment) device
>> that will be probed by the TEE bus. If the associated Trusted
>> application is supported on secure part this device offers a client
>> interface to load a firmware in the secure part.
>> This firmware could be authenticated and decrypted by the secure
>> trusted application.
>>
>> Signed-off-by: Arnaud Pouliquen <[email protected]>
>> ---
>> drivers/remoteproc/Kconfig | 9 +
>> drivers/remoteproc/Makefile | 1 +
>> drivers/remoteproc/tee_remoteproc.c | 393 ++++++++++++++++++++++++++++
>> include/linux/tee_remoteproc.h | 99 +++++++
>> 4 files changed, 502 insertions(+)
>> create mode 100644 drivers/remoteproc/tee_remoteproc.c
>> create mode 100644 include/linux/tee_remoteproc.h
>>
>> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
>> index 48845dc8fa85..85299606806c 100644
>> --- a/drivers/remoteproc/Kconfig
>> +++ b/drivers/remoteproc/Kconfig
>> @@ -365,6 +365,15 @@ config XLNX_R5_REMOTEPROC
>>
>> It's safe to say N if not interested in using RPU r5f cores.
>>
>> +
>> +config TEE_REMOTEPROC
>> + tristate "trusted firmware support by a TEE application"
>> + depends on OPTEE
>> + help
>> + Support for trusted remote processors firmware. The firmware
>> + authentication and/or decryption are managed by a trusted application.
>> + 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..fa8daebce277 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_TEE_REMOTEPROC) += tee_remoteproc.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/tee_remoteproc.c b/drivers/remoteproc/tee_remoteproc.c
>> new file mode 100644
>> index 000000000000..49e1e0caf889
>> --- /dev/null
>> +++ b/drivers/remoteproc/tee_remoteproc.c
>> @@ -0,0 +1,393 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * Copyright (C) STMicroelectronics 2023 - All Rights Reserved
>> + * Author: Arnaud Pouliquen <[email protected]>
>> + */
>> +
>> +#include <linux/firmware.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_device.h>
>> +#include <linux/of_reserved_mem.h>
>> +#include <linux/remoteproc.h>
>> +#include <linux/slab.h>
>> +#include <linux/tee_drv.h>
>> +#include <linux/tee_remoteproc.h>
>> +
>> +#include "remoteproc_internal.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_mem {
>> + char name[20];
>> + void __iomem *cpu_addr;
>> + phys_addr_t bus_addr;
>> + u32 dev_addr;
>> + size_t size;
>> +};
>> +
>> +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 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 tee_rproc *trproc, const struct firmware *fw)
>> +{
>> + struct tee_ioctl_invoke_arg arg;
>> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
>> + struct tee_shm *fw_shm;
>> + int ret;
>> +
>> + 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);
>> +
>> + 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);
>> +
>> +int rproc_tee_get_rsc_table(struct tee_rproc *trproc)
>> +{
>> + struct tee_ioctl_invoke_arg arg;
>> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
>> + struct rproc *rproc = trproc->rproc;
>> + size_t rsc_size;
>> + int ret;
>> +
>> + 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;
>> + }
>> +
>> + rsc_size = param[2].u.value.a;
>> +
>> + /* If the size is null no resource table defined in the image */
>> + if (!rsc_size)
>> + return 0;
>> +
>> + /* Store the resource table address that would be updated by the remote core . */
>> + trproc->rsc_va = ioremap_wc(param[1].u.value.a, rsc_size);
>> + if (IS_ERR_OR_NULL(trproc->rsc_va)) {
>> + dev_err(tee_rproc_ctx->dev, "Unable to map memory region: %lld+%zx\n",
>> + param[1].u.value.a, rsc_size);
>> + trproc->rsc_va = NULL;
>> + return -ENOMEM;
>> + }
>> +
>> + /*
>> + * A cached table is requested as the physical address is not mapped yet
>> + * but remoteproc needs to parse the table for resources.
>> + */
>> + rproc->cached_table = kmemdup((__force void *)trproc->rsc_va, rsc_size, GFP_KERNEL);
>> + if (!rproc->cached_table)
>> + return -ENOMEM;
>> +
>> + rproc->table_ptr = rproc->cached_table;
>> + rproc->table_sz = rsc_size;
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(rproc_tee_get_rsc_table);
>> +
>> +struct resource_table *tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc)
>
> Please change this to tee_rproc_find_loaded_rsc_table(). That way we know it
> should be correlated with ops::find_loaded_rsc_table() rather than
> ops::get_loaded_rsc_table().
>
>> +{
>> + return (__force struct resource_table *)trproc->rsc_va;
>> +}
>> +EXPORT_SYMBOL_GPL(tee_rproc_get_loaded_rsc_table);
>> +
>> +int tee_rproc_start(struct tee_rproc *trproc)
>> +{
>> + struct tee_ioctl_invoke_arg arg;
>> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
>> + int ret;
>> +
>> + 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) {
>
> Please split the conditions of this if() statement to make it easier to understand
> the correlation with the other if() below. Same for tee_rproc_stop().

Regarding other references to tee_client_invoke_func(), it is the common way to
test the returns.

If it is okay for you I would prefer to keep this implementation


>
>> + 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)
>> + ret = -EIO;
>> + }
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(tee_rproc_start);
>> +
>> +int tee_rproc_stop(struct tee_rproc *trproc)
>> +{
>> + struct tee_ioctl_invoke_arg arg;
>> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
>> + int ret;
>> +
>> + 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;
>> + }
>> + if (trproc->rsc_va)
>> + iounmap(trproc->rsc_va);
>> + trproc->rsc_va = NULL;
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(tee_rproc_stop);
>> +
>> +static const struct tee_client_device_id stm32_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, unsigned int rproc_id)
>> +{
>> + struct tee_client_device *rproc_tee_device;
>> + struct tee_ioctl_open_session_arg sess_arg;
>> + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMBER];
>> + struct tee_rproc *trproc;
>> + int ret;
>> +
>> + /*
>> + * The device is not probed by the TEE bus. We ignore the reason (bus could be not yet
>> + * probed or service not available in the secure firmware)
>> + * Assumption here is that the TEE bus is not probed.
>> + */
>> + if (!tee_rproc_ctx)
>> + return ERR_PTR(-EPROBE_DEFER);
>> +
>> + trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
>> + if (!trproc)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + rproc_tee_device = to_tee_client_device(tee_rproc_ctx->dev);
>> + memset(&sess_arg, 0, sizeof(sess_arg));
>> +
>> + /* Open session with rproc_tee load the OP-TEE Trusted Application */
>> + memcpy(sess_arg.uuid, rproc_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);
>> + return ERR_PTR(-EINVAL);
>> + }
>> +
>> + trproc->parent = dev;
>> + trproc->rproc_id = rproc_id;
>> + trproc->session_id = sess_arg.session;
>> +
>> + list_add_tail(&trproc->node, &tee_rproc_ctx->sessions);
>> +
>> + return trproc;
>> +}
>> +EXPORT_SYMBOL_GPL(tee_rproc_register);
>> +
>> +int tee_rproc_unregister(struct tee_rproc *trproc)
>> +{
>> + int ret;
>> +
>> + if (!tee_rproc_ctx)
>> + return -ENODEV;
>
> Not sure this check is needed since we can't be here if memory allocation for
> @tee_rproc_ctx failed in tee_rproc_probe().
>
>> +
>> + 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);
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(tee_rproc_unregister);
>> +
>> +static int tee_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;
>> +
>> + /* Only one RPROC OP-TEE device allowed */
>> + if (tee_rproc_ctx) {
>> + dev_err(dev, "An RPROC OP-TEE device was already initialized: only one allowed\n");
>> + return -EBUSY;
>> + }
>
> Is this check needed? How can @tee_rproc_ctx be initialized twice?


Right, this kind of check makes sense in OP-TEE, here it is probably over check.
I will clean it.


Thanks,
Arnaud

>
> More comments tomorrow.
>
> Thanks,
> Mathieu
>
>> +
>> + /* Open context with TEE driver */
>> + tee_ctx = tee_client_open_context(NULL, tee_ctx_match, NULL, NULL);
>> + if (IS_ERR(tee_ctx))
>> + return PTR_ERR(tee_ctx);
>> +
>> + tee_rproc_ctx = devm_kzalloc(dev, sizeof(*tee_ctx), GFP_KERNEL);
>> + if (!tee_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, stm32_tee_rproc_id_table);
>> +
>> +static struct tee_client_driver tee_rproc_fw_driver = {
>> + .id_table = stm32_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(" TEE remote processor control driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/include/linux/tee_remoteproc.h b/include/linux/tee_remoteproc.h
>> new file mode 100644
>> index 000000000000..537d6dc3b858
>> --- /dev/null
>> +++ b/include/linux/tee_remoteproc.h
>> @@ -0,0 +1,99 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + * Copyright(c) 2023 STMicroelectronics - All Rights Reserved
>> + */
>> +
>> +#ifndef TEE_REMOTEPROC_H
>> +#define TEE_REMOTEPROC_H
>> +
>> +#include <linux/remoteproc.h>
>> +#include <linux/tee_drv.h>
>> +
>> +/**
>> + * 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
>> + * @rsc_va: Resource table virtual address.
>> + */
>> +struct tee_rproc {
>> + struct list_head node;
>> + struct rproc *rproc;
>> + struct device *parent;
>> + u32 rproc_id;
>> + u32 session_id;
>> + void __iomem *rsc_va;
>> +};
>> +
>> +#if IS_ENABLED(CONFIG_TEE_REMOTEPROC)
>> +
>> +struct tee_rproc *tee_rproc_register(struct device *dev, unsigned int rproc_id);
>> +int tee_rproc_unregister(struct tee_rproc *trproc);
>> +
>> +int tee_rproc_load_fw(struct tee_rproc *trproc, const struct firmware *fw);
>> +int rproc_tee_get_rsc_table(struct tee_rproc *trproc);
>> +struct resource_table *tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc);
>> +int tee_rproc_start(struct tee_rproc *trproc);
>> +int tee_rproc_stop(struct tee_rproc *trproc);
>> +
>> +#else
>> +
>> +static inline struct tee_rproc *tee_rproc_register(struct device *dev, unsigned int rproc_id)
>> +{
>> + return ERR_PTR(-ENODEV);
>> +}
>> +
>> +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 tee_rproc *trproc,
>> + const struct firmware *fw)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline int tee_rproc_start(struct tee_rproc *trproc)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline int tee_rproc_stop(struct tee_rproc *trproc)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline int rproc_tee_get_rsc_table(struct tee_rproc *trproc)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline struct resource_table *
>> + tee_rproc_get_loaded_rsc_table(struct tee_rproc *trproc)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return NULL;
>> +}
>> +
>> +#endif /* CONFIG_TEE_REMOTEPROC */
>> +#endif /* TEE_REMOTEPROC_H */
>> --
>> 2.25.1
>>

2024-01-30 09:31:11

by Arnaud POULIQUEN

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware



On 1/26/24 18:11, Mathieu Poirier wrote:
> On Thu, Jan 18, 2024 at 11:04:33AM +0100, Arnaud Pouliquen wrote:
>> 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 <[email protected]>
>> ---
>> V1 to V2 update:
>> - remove the select "TEE_REMOTEPROC" in STM32_RPROC config as detected by
>> the kernel test robot:
>> WARNING: unmet direct dependencies detected for TEE_REMOTEPROC
>> Depends on [n]: REMOTEPROC [=y] && OPTEE [=n]
>> Selected by [y]:
>> - STM32_RPROC [=y] && (ARCH_STM32 || COMPILE_TEST [=y]) && REMOTEPROC [=y]
>> - Fix initialized trproc variable in stm32_rproc_probe
>> ---
>> drivers/remoteproc/stm32_rproc.c | 149 +++++++++++++++++++++++++++++--
>> 1 file changed, 144 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
>> index fcc0001e2657..cf6a21bac945 100644
>> --- a/drivers/remoteproc/stm32_rproc.c
>> +++ b/drivers/remoteproc/stm32_rproc.c
>> @@ -20,6 +20,7 @@
>> #include <linux/remoteproc.h>
>> #include <linux/reset.h>
>> #include <linux/slab.h>
>> +#include <linux/tee_remoteproc.h>
>> #include <linux/workqueue.h>
>>
>> #include "remoteproc_internal.h"
>> @@ -49,6 +50,9 @@
>> #define M4_STATE_STANDBY 4
>> #define M4_STATE_CRASH 5
>>
>> +/* Remote processor unique identifier aligned with the Trusted Execution Environment definitions */
>> +#define STM32_MP1_M4_PROC_ID 0
>> +
>> struct stm32_syscon {
>> struct regmap *map;
>> u32 reg;
>> @@ -90,6 +94,8 @@ struct stm32_rproc {
>> struct stm32_mbox mb[MBOX_NB_MBX];
>> struct workqueue_struct *workqueue;
>> bool hold_boot_smc;
>> + bool fw_loaded;
>> + struct tee_rproc *trproc;
>> void __iomem *rsc_va;
>> };
>>
>> @@ -257,6 +263,91 @@ static int stm32_rproc_release(struct rproc *rproc)
>> return err;
>> }
>>
>> +static int stm32_rproc_tee_elf_sanity_check(struct rproc *rproc,
>> + const struct firmware *fw)
>> +{
>> + struct stm32_rproc *ddata = rproc->priv;
>> + unsigned int ret = 0;
>> +
>> + if (rproc->state == RPROC_DETACHED)
>> + return 0;
>> +
>> + ret = tee_rproc_load_fw(ddata->trproc, fw);
>> + if (!ret)
>> + ddata->fw_loaded = true;
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_rproc_tee_elf_load(struct rproc *rproc,
>> + const struct firmware *fw)
>> +{
>> + struct stm32_rproc *ddata = rproc->priv;
>> + unsigned int ret;
>> +
>> + /*
>> + * This function can be called by remote proc for recovery
>> + * without the sanity check. In this case we need to load the firmware
>> + * else nothing done here as the firmware has been preloaded for the
>> + * sanity check to be able to parse it for the resource table.
>> + */
>
> This comment is very confusing - please consider refactoring.
>
>> + if (ddata->fw_loaded)
>> + return 0;
>> +
>
> I'm not sure about keeping a flag to indicate the status of the loaded firmware.
> It is not done for the non-secure method, I don't see why it would be needed for
> the secure one.
>

The difference is on the sanity check.
- in rproc_elf_sanity_check we parse the elf file to verify that it is
valid.
- in stm32_rproc_tee_elf_sanity_check we have to do the same, that means to
authenticate it. the authentication is done during the load.

So this flag is used to avoid to reload it twice time.
refactoring the comment should help to understand this flag


An alternative would be to bypass the sanity check. But this lead to same
limitation.
Before loading the firmware in remoteproc_core, we call rproc_parse_fw() that is
used to get the resource table address. To get it from tee we need to
authenticate the firmware so load it...


>> + ret = tee_rproc_load_fw(ddata->trproc, fw);
>> + if (ret)
>> + return ret;
>> + ddata->fw_loaded = true;
>> +
>> + /* Update the resource table parameters. */
>> + if (rproc_tee_get_rsc_table(ddata->trproc)) {
>> + /* No resource table: reset the related fields. */
>> + rproc->cached_table = NULL;
>> + rproc->table_ptr = NULL;
>> + rproc->table_sz = 0;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static struct resource_table *
>> +stm32_rproc_tee_elf_find_loaded_rsc_table(struct rproc *rproc,
>> + const struct firmware *fw)
>> +{
>> + struct stm32_rproc *ddata = rproc->priv;
>> +
>> + return tee_rproc_get_loaded_rsc_table(ddata->trproc);
>> +}
>> +
>> +static int stm32_rproc_tee_start(struct rproc *rproc)
>> +{
>> + struct stm32_rproc *ddata = rproc->priv;
>> +
>> + return tee_rproc_start(ddata->trproc);
>> +}
>> +
>> +static int stm32_rproc_tee_attach(struct rproc *rproc)
>> +{
>> + /* Nothing to do, remote proc already started by the secured context. */
>> + return 0;
>> +}
>> +
>> +static int stm32_rproc_tee_stop(struct rproc *rproc)
>> +{
>> + struct stm32_rproc *ddata = rproc->priv;
>> + int err;
>> +
>> + stm32_rproc_request_shutdown(rproc);
>> +
>> + err = tee_rproc_stop(ddata->trproc);
>> + if (err)
>> + return err;
>> +
>> + ddata->fw_loaded = false;
>> +
>> + return stm32_rproc_release(rproc);
>> +}
>> +
>> static int stm32_rproc_prepare(struct rproc *rproc)
>> {
>> struct device *dev = rproc->dev.parent;
>> @@ -319,7 +410,14 @@ static int stm32_rproc_prepare(struct rproc *rproc)
>>
>> static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
>> {
>> - if (rproc_elf_load_rsc_table(rproc, fw))
>> + struct stm32_rproc *ddata = rproc->priv;
>> + int ret;
>> +
>> + if (ddata->trproc)
>> + ret = rproc_tee_get_rsc_table(ddata->trproc);
>> + else
>> + ret = rproc_elf_load_rsc_table(rproc, fw);
>> + if (ret)
>> dev_warn(&rproc->dev, "no resource table found for this firmware\n");
>>
>> return 0;
>> @@ -693,8 +791,22 @@ 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 = stm32_rproc_tee_start,
>> + .stop = stm32_rproc_tee_stop,
>> + .attach = stm32_rproc_tee_attach,
>> + .kick = stm32_rproc_kick,
>> + .parse_fw = stm32_rproc_parse_fw,
>> + .find_loaded_rsc_table = stm32_rproc_tee_elf_find_loaded_rsc_table,
>> + .get_loaded_rsc_table = stm32_rproc_get_loaded_rsc_table,
>> + .sanity_check = stm32_rproc_tee_elf_sanity_check,
>> + .load = stm32_rproc_tee_elf_load,
>> +};
>> +
>> static const struct of_device_id stm32_rproc_match[] = {
>> - { .compatible = "st,stm32mp1-m4" },
>> + {.compatible = "st,stm32mp1-m4",},
>> + {.compatible = "st,stm32mp1-m4-tee",},
>> {},
>> };
>> MODULE_DEVICE_TABLE(of, stm32_rproc_match);
>> @@ -853,6 +965,7 @@ 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;
>> int ret;
>> @@ -861,11 +974,31 @@ static int stm32_rproc_probe(struct platform_device *pdev)
>> if (ret)
>> return ret;
>>
>> - rproc = 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")) {
>> + trproc = tee_rproc_register(dev, STM32_MP1_M4_PROC_ID);
>> + if (IS_ERR(trproc)) {
>> + dev_err_probe(dev, PTR_ERR(trproc),
>> + "signed firmware not supported by TEE\n");
>> + return PTR_ERR(trproc);
>> + }
>> + /*
>> + * Delegate the firmware management to the secure context.
>> + * The firmware loaded has to be signed.
>> + */
>> + dev_info(dev, "Support of signed firmware only\n");
>
> Not sure what this adds. Please remove.

This is used to inform the user that only a signed firmware can be loaded, not
an ELF file.
I have a patch in my pipe to provide the supported format in the debugfs. In a
first step, I can suppress this message and we can revisit the issue when I push
the debugfs proposal.

Thanks,
Arnaud

>
>> + }
>> + rproc = rproc_alloc(dev, np->name,
>> + trproc ? &st_rproc_tee_ops : &st_rproc_ops,
>> + NULL, sizeof(*ddata));
>> + if (!rproc) {
>> + ret = -ENOMEM;
>> + goto free_tee;
>> + }
>>
>> ddata = rproc->priv;
>> + ddata->trproc = trproc;
>> + if (trproc)
>> + trproc->rproc = rproc;
>>
>> rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
>>
>> @@ -916,6 +1049,10 @@ static int stm32_rproc_probe(struct platform_device *pdev)
>> device_init_wakeup(dev, false);
>> }
>> rproc_free(rproc);
>> +free_tee:
>> + if (trproc)
>> + tee_rproc_unregister(trproc);
>> +
>> return ret;
>> }
>>
>> @@ -937,6 +1074,8 @@ static void stm32_rproc_remove(struct platform_device *pdev)
>> device_init_wakeup(dev, false);
>> }
>> rproc_free(rproc);
>> + if (ddata->trproc)
>> + tee_rproc_unregister(ddata->trproc);
>> }
>>
>> static int stm32_rproc_suspend(struct device *dev)
>> --
>> 2.25.1
>>

2024-01-30 17:42:24

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v2 2/4] dt-bindings: remoteproc: Add compatibility for TEE support

On Fri, Jan 26, 2024 at 12:03:25PM +0100, Krzysztof Kozlowski wrote:
> On 18/01/2024 11:04, Arnaud Pouliquen wrote:
> > 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-tree:
> > - In OP-TEE, a node is defined in the device tree with the
> > st,stm32mp1-m4-tee to support signed remoteproc firmware.
> > Based on DT properties, OP-TEE authenticates, loads, starts, and stops
> > the firmware.
> > - On Linux, when the compatibility is set, the Cortex-M resets should not
> > be declared in the device tree.
> >
> > Signed-off-by: Arnaud Pouliquen <[email protected]>
> > ---
> > V1 to V2 updates
> > - update "st,stm32mp1-m4" compatible description to generalize
> > - remove the 'reset-names' requirement in one conditional branch, as the
> > property is already part of the condition test.
> > ---
> > .../bindings/remoteproc/st,stm32-rproc.yaml | 52 +++++++++++++++----
> > 1 file changed, 43 insertions(+), 9 deletions(-)
> >
> > diff --git a/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml
> > index 370af61d8f28..6af821b15736 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
>
> The patch looks good to me, but I wonder about this choice of two
> compatibles.
>
> Basically this is the same hardware with the same interface, but two
> compatibles to differentiate a bit different firmware setup. We have
> already such cases for Qualcomm [1] [2] and new ones will be coming. [3]
>
> I wonder whether this should be rather the same compatible with
> additional property, e.g. "st,tee-control" or "remote-control".
>
> [1]
> https://elixir.bootlin.com/linux/v6.7.1/source/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml#L54
>
> [2]
> https://elixir.bootlin.com/linux/v6.7.1/source/Documentation/devicetree/bindings/net/qcom,ipa.yaml#L129
> (that's a bit different)
>
> [3] https://lore.kernel.org/linux-devicetree/20240124103623.GJ4906@thinkpad/
>
> @Rob,
> Any general guidance for this and Qualcomm?

I think we have cases using compatible already as well. Either way is
fine with me.

Rob

2024-01-30 17:51:21

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v2 2/4] dt-bindings: remoteproc: Add compatibility for TEE support

On Thu, Jan 18, 2024 at 11:04:31AM +0100, Arnaud Pouliquen wrote:
> 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-tree:
> - In OP-TEE, a node is defined in the device tree with the
> st,stm32mp1-m4-tee to support signed remoteproc firmware.
> Based on DT properties, OP-TEE authenticates, loads, starts, and stops
> the firmware.
> - On Linux, when the compatibility is set, the Cortex-M resets should not
> be declared in the device tree.
>
> Signed-off-by: Arnaud Pouliquen <[email protected]>
> ---
> V1 to V2 updates
> - update "st,stm32mp1-m4" compatible description to generalize
> - remove the 'reset-names' requirement in one conditional branch, as the
> property is already part of the condition test.
> ---
> .../bindings/remoteproc/st,stm32-rproc.yaml | 52 +++++++++++++++----
> 1 file changed, 43 insertions(+), 9 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml
> index 370af61d8f28..6af821b15736 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:
> @@ -142,21 +147,40 @@ 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

Note that this is true when 'reset-names' is not present. If that is not
desired, then you need 'required: [reset-names]'. Not really a new issue
though.

> + then:
> + required:
> + - st,syscfg-holdboot
> + - resets
> + else:
> + properties:
> + st,syscfg-holdboot: false
> + required:
> + - resets

'resets' is always required within the outer 'then' schema, so you can
move this up a level.

> +
> + - if:
> + properties:
> + compatible:
> + contains:
> + const: st,stm32mp1-m4-tee
> then:
> - required:
> - - st,syscfg-holdboot
> - else:
> properties:
> st,syscfg-holdboot: false
> + reset-names: false
> + resets: false
>
> additionalProperties: false
>
> @@ -188,5 +212,15 @@ 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,syscfg-rsc-tbl = <&tamp 0x144 0xFFFFFFFF>;
> + st,syscfg-m4-state = <&tamp 0x148 0xFFFFFFFF>;
> + };
>
> ...
> --
> 2.25.1
>

2024-01-31 18:54:25

by Mathieu Poirier

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware

On Tue, Jan 30, 2024 at 10:13:48AM +0100, Arnaud POULIQUEN wrote:
>
>
> On 1/26/24 18:11, Mathieu Poirier wrote:
> > On Thu, Jan 18, 2024 at 11:04:33AM +0100, Arnaud Pouliquen wrote:
> >> 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 <[email protected]>
> >> ---
> >> V1 to V2 update:
> >> - remove the select "TEE_REMOTEPROC" in STM32_RPROC config as detected by
> >> the kernel test robot:
> >> WARNING: unmet direct dependencies detected for TEE_REMOTEPROC
> >> Depends on [n]: REMOTEPROC [=y] && OPTEE [=n]
> >> Selected by [y]:
> >> - STM32_RPROC [=y] && (ARCH_STM32 || COMPILE_TEST [=y]) && REMOTEPROC [=y]
> >> - Fix initialized trproc variable in stm32_rproc_probe
> >> ---
> >> drivers/remoteproc/stm32_rproc.c | 149 +++++++++++++++++++++++++++++--
> >> 1 file changed, 144 insertions(+), 5 deletions(-)
> >>
> >> diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
> >> index fcc0001e2657..cf6a21bac945 100644
> >> --- a/drivers/remoteproc/stm32_rproc.c
> >> +++ b/drivers/remoteproc/stm32_rproc.c
> >> @@ -20,6 +20,7 @@
> >> #include <linux/remoteproc.h>
> >> #include <linux/reset.h>
> >> #include <linux/slab.h>
> >> +#include <linux/tee_remoteproc.h>
> >> #include <linux/workqueue.h>
> >>
> >> #include "remoteproc_internal.h"
> >> @@ -49,6 +50,9 @@
> >> #define M4_STATE_STANDBY 4
> >> #define M4_STATE_CRASH 5
> >>
> >> +/* Remote processor unique identifier aligned with the Trusted Execution Environment definitions */
> >> +#define STM32_MP1_M4_PROC_ID 0
> >> +
> >> struct stm32_syscon {
> >> struct regmap *map;
> >> u32 reg;
> >> @@ -90,6 +94,8 @@ struct stm32_rproc {
> >> struct stm32_mbox mb[MBOX_NB_MBX];
> >> struct workqueue_struct *workqueue;
> >> bool hold_boot_smc;
> >> + bool fw_loaded;
> >> + struct tee_rproc *trproc;
> >> void __iomem *rsc_va;
> >> };
> >>
> >> @@ -257,6 +263,91 @@ static int stm32_rproc_release(struct rproc *rproc)
> >> return err;
> >> }
> >>
> >> +static int stm32_rproc_tee_elf_sanity_check(struct rproc *rproc,
> >> + const struct firmware *fw)
> >> +{
> >> + struct stm32_rproc *ddata = rproc->priv;
> >> + unsigned int ret = 0;
> >> +
> >> + if (rproc->state == RPROC_DETACHED)
> >> + return 0;
> >> +
> >> + ret = tee_rproc_load_fw(ddata->trproc, fw);
> >> + if (!ret)
> >> + ddata->fw_loaded = true;
> >> +
> >> + return ret;
> >> +}
> >> +
> >> +static int stm32_rproc_tee_elf_load(struct rproc *rproc,
> >> + const struct firmware *fw)
> >> +{
> >> + struct stm32_rproc *ddata = rproc->priv;
> >> + unsigned int ret;
> >> +
> >> + /*
> >> + * This function can be called by remote proc for recovery
> >> + * without the sanity check. In this case we need to load the firmware
> >> + * else nothing done here as the firmware has been preloaded for the
> >> + * sanity check to be able to parse it for the resource table.
> >> + */
> >
> > This comment is very confusing - please consider refactoring.
> >
> >> + if (ddata->fw_loaded)
> >> + return 0;
> >> +
> >
> > I'm not sure about keeping a flag to indicate the status of the loaded firmware.
> > It is not done for the non-secure method, I don't see why it would be needed for
> > the secure one.
> >
>
> The difference is on the sanity check.
> - in rproc_elf_sanity_check we parse the elf file to verify that it is
> valid.
> - in stm32_rproc_tee_elf_sanity_check we have to do the same, that means to
> authenticate it. the authentication is done during the load.
>
> So this flag is used to avoid to reload it twice time.
> refactoring the comment should help to understand this flag
>
>
> An alternative would be to bypass the sanity check. But this lead to same
> limitation.
> Before loading the firmware in remoteproc_core, we call rproc_parse_fw() that is
> used to get the resource table address. To get it from tee we need to
> authenticate the firmware so load it...
>

I spent a long time thinking about this patchset. Looking at the code as it
is now, request_firmware() in rproc_boot() is called even when the TEE is
responsible for loading the firmware. There should be some conditional code
that calls either request_firmware() or tee_rproc_load_fw(). The latter should
also be renamed to tee_rproc_request_firmware() to avoid confusion.

I touched on that before but please rename rproc_tee_get_rsc_table() to
rproc_tee_elf_load_rsc_table(). I also suggest to introduce a new function,
rproc_tee_get_loaded_rsc_table() that would be called from
rproc_tee_elf_load_rsc_table(). That way we don't need trproc->rsc_va.

I also think tee_rproc should be renamed to "rproc_tee_interface" and folded
under struct rproc.

With the above most of the problems with the current implementation should
naturally go away.

Thanks,
Mathieu

>
> >> + ret = tee_rproc_load_fw(ddata->trproc, fw);
> >> + if (ret)
> >> + return ret;
> >> + ddata->fw_loaded = true;
> >> +
> >> + /* Update the resource table parameters. */
> >> + if (rproc_tee_get_rsc_table(ddata->trproc)) {
> >> + /* No resource table: reset the related fields. */
> >> + rproc->cached_table = NULL;
> >> + rproc->table_ptr = NULL;
> >> + rproc->table_sz = 0;
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static struct resource_table *
> >> +stm32_rproc_tee_elf_find_loaded_rsc_table(struct rproc *rproc,
> >> + const struct firmware *fw)
> >> +{
> >> + struct stm32_rproc *ddata = rproc->priv;
> >> +
> >> + return tee_rproc_get_loaded_rsc_table(ddata->trproc);
> >> +}
> >> +
> >> +static int stm32_rproc_tee_start(struct rproc *rproc)
> >> +{
> >> + struct stm32_rproc *ddata = rproc->priv;
> >> +
> >> + return tee_rproc_start(ddata->trproc);
> >> +}
> >> +
> >> +static int stm32_rproc_tee_attach(struct rproc *rproc)
> >> +{
> >> + /* Nothing to do, remote proc already started by the secured context. */
> >> + return 0;
> >> +}
> >> +
> >> +static int stm32_rproc_tee_stop(struct rproc *rproc)
> >> +{
> >> + struct stm32_rproc *ddata = rproc->priv;
> >> + int err;
> >> +
> >> + stm32_rproc_request_shutdown(rproc);
> >> +
> >> + err = tee_rproc_stop(ddata->trproc);
> >> + if (err)
> >> + return err;
> >> +
> >> + ddata->fw_loaded = false;
> >> +
> >> + return stm32_rproc_release(rproc);
> >> +}
> >> +
> >> static int stm32_rproc_prepare(struct rproc *rproc)
> >> {
> >> struct device *dev = rproc->dev.parent;
> >> @@ -319,7 +410,14 @@ static int stm32_rproc_prepare(struct rproc *rproc)
> >>
> >> static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
> >> {
> >> - if (rproc_elf_load_rsc_table(rproc, fw))
> >> + struct stm32_rproc *ddata = rproc->priv;
> >> + int ret;
> >> +
> >> + if (ddata->trproc)
> >> + ret = rproc_tee_get_rsc_table(ddata->trproc);
> >> + else
> >> + ret = rproc_elf_load_rsc_table(rproc, fw);
> >> + if (ret)
> >> dev_warn(&rproc->dev, "no resource table found for this firmware\n");
> >>
> >> return 0;
> >> @@ -693,8 +791,22 @@ 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 = stm32_rproc_tee_start,
> >> + .stop = stm32_rproc_tee_stop,
> >> + .attach = stm32_rproc_tee_attach,
> >> + .kick = stm32_rproc_kick,
> >> + .parse_fw = stm32_rproc_parse_fw,
> >> + .find_loaded_rsc_table = stm32_rproc_tee_elf_find_loaded_rsc_table,
> >> + .get_loaded_rsc_table = stm32_rproc_get_loaded_rsc_table,
> >> + .sanity_check = stm32_rproc_tee_elf_sanity_check,
> >> + .load = stm32_rproc_tee_elf_load,
> >> +};
> >> +
> >> static const struct of_device_id stm32_rproc_match[] = {
> >> - { .compatible = "st,stm32mp1-m4" },
> >> + {.compatible = "st,stm32mp1-m4",},
> >> + {.compatible = "st,stm32mp1-m4-tee",},
> >> {},
> >> };
> >> MODULE_DEVICE_TABLE(of, stm32_rproc_match);
> >> @@ -853,6 +965,7 @@ 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;
> >> int ret;
> >> @@ -861,11 +974,31 @@ static int stm32_rproc_probe(struct platform_device *pdev)
> >> if (ret)
> >> return ret;
> >>
> >> - rproc = 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")) {
> >> + trproc = tee_rproc_register(dev, STM32_MP1_M4_PROC_ID);
> >> + if (IS_ERR(trproc)) {
> >> + dev_err_probe(dev, PTR_ERR(trproc),
> >> + "signed firmware not supported by TEE\n");
> >> + return PTR_ERR(trproc);
> >> + }
> >> + /*
> >> + * Delegate the firmware management to the secure context.
> >> + * The firmware loaded has to be signed.
> >> + */
> >> + dev_info(dev, "Support of signed firmware only\n");
> >
> > Not sure what this adds. Please remove.
>
> This is used to inform the user that only a signed firmware can be loaded, not
> an ELF file.
> I have a patch in my pipe to provide the supported format in the debugfs. In a
> first step, I can suppress this message and we can revisit the issue when I push
> the debugfs proposal.
>
> Thanks,
> Arnaud
>
> >
> >> + }
> >> + rproc = rproc_alloc(dev, np->name,
> >> + trproc ? &st_rproc_tee_ops : &st_rproc_ops,
> >> + NULL, sizeof(*ddata));
> >> + if (!rproc) {
> >> + ret = -ENOMEM;
> >> + goto free_tee;
> >> + }
> >>
> >> ddata = rproc->priv;
> >> + ddata->trproc = trproc;
> >> + if (trproc)
> >> + trproc->rproc = rproc;
> >>
> >> rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
> >>
> >> @@ -916,6 +1049,10 @@ static int stm32_rproc_probe(struct platform_device *pdev)
> >> device_init_wakeup(dev, false);
> >> }
> >> rproc_free(rproc);
> >> +free_tee:
> >> + if (trproc)
> >> + tee_rproc_unregister(trproc);
> >> +
> >> return ret;
> >> }
> >>
> >> @@ -937,6 +1074,8 @@ static void stm32_rproc_remove(struct platform_device *pdev)
> >> device_init_wakeup(dev, false);
> >> }
> >> rproc_free(rproc);
> >> + if (ddata->trproc)
> >> + tee_rproc_unregister(ddata->trproc);
> >> }
> >>
> >> static int stm32_rproc_suspend(struct device *dev)
> >> --
> >> 2.25.1
> >>

2024-02-01 15:09:03

by Arnaud POULIQUEN

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware

hello Mathieu,

On 1/31/24 19:52, Mathieu Poirier wrote:
> On Tue, Jan 30, 2024 at 10:13:48AM +0100, Arnaud POULIQUEN wrote:
>>
>>
>> On 1/26/24 18:11, Mathieu Poirier wrote:
>>> On Thu, Jan 18, 2024 at 11:04:33AM +0100, Arnaud Pouliquen wrote:
>>>> 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 <[email protected]>
>>>> ---
>>>> V1 to V2 update:
>>>> - remove the select "TEE_REMOTEPROC" in STM32_RPROC config as detected by
>>>> the kernel test robot:
>>>> WARNING: unmet direct dependencies detected for TEE_REMOTEPROC
>>>> Depends on [n]: REMOTEPROC [=y] && OPTEE [=n]
>>>> Selected by [y]:
>>>> - STM32_RPROC [=y] && (ARCH_STM32 || COMPILE_TEST [=y]) && REMOTEPROC [=y]
>>>> - Fix initialized trproc variable in stm32_rproc_probe
>>>> ---
>>>> drivers/remoteproc/stm32_rproc.c | 149 +++++++++++++++++++++++++++++--
>>>> 1 file changed, 144 insertions(+), 5 deletions(-)
>>>>
>>>> diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
>>>> index fcc0001e2657..cf6a21bac945 100644
>>>> --- a/drivers/remoteproc/stm32_rproc.c
>>>> +++ b/drivers/remoteproc/stm32_rproc.c
>>>> @@ -20,6 +20,7 @@
>>>> #include <linux/remoteproc.h>
>>>> #include <linux/reset.h>
>>>> #include <linux/slab.h>
>>>> +#include <linux/tee_remoteproc.h>
>>>> #include <linux/workqueue.h>
>>>>
>>>> #include "remoteproc_internal.h"
>>>> @@ -49,6 +50,9 @@
>>>> #define M4_STATE_STANDBY 4
>>>> #define M4_STATE_CRASH 5
>>>>
>>>> +/* Remote processor unique identifier aligned with the Trusted Execution Environment definitions */
>>>> +#define STM32_MP1_M4_PROC_ID 0
>>>> +
>>>> struct stm32_syscon {
>>>> struct regmap *map;
>>>> u32 reg;
>>>> @@ -90,6 +94,8 @@ struct stm32_rproc {
>>>> struct stm32_mbox mb[MBOX_NB_MBX];
>>>> struct workqueue_struct *workqueue;
>>>> bool hold_boot_smc;
>>>> + bool fw_loaded;
>>>> + struct tee_rproc *trproc;
>>>> void __iomem *rsc_va;
>>>> };
>>>>
>>>> @@ -257,6 +263,91 @@ static int stm32_rproc_release(struct rproc *rproc)
>>>> return err;
>>>> }
>>>>
>>>> +static int stm32_rproc_tee_elf_sanity_check(struct rproc *rproc,
>>>> + const struct firmware *fw)
>>>> +{
>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>> + unsigned int ret = 0;
>>>> +
>>>> + if (rproc->state == RPROC_DETACHED)
>>>> + return 0;
>>>> +
>>>> + ret = tee_rproc_load_fw(ddata->trproc, fw);
>>>> + if (!ret)
>>>> + ddata->fw_loaded = true;
>>>> +
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int stm32_rproc_tee_elf_load(struct rproc *rproc,
>>>> + const struct firmware *fw)
>>>> +{
>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>> + unsigned int ret;
>>>> +
>>>> + /*
>>>> + * This function can be called by remote proc for recovery
>>>> + * without the sanity check. In this case we need to load the firmware
>>>> + * else nothing done here as the firmware has been preloaded for the
>>>> + * sanity check to be able to parse it for the resource table.
>>>> + */
>>>
>>> This comment is very confusing - please consider refactoring.
>>>
>>>> + if (ddata->fw_loaded)
>>>> + return 0;
>>>> +
>>>
>>> I'm not sure about keeping a flag to indicate the status of the loaded firmware.
>>> It is not done for the non-secure method, I don't see why it would be needed for
>>> the secure one.
>>>
>>
>> The difference is on the sanity check.
>> - in rproc_elf_sanity_check we parse the elf file to verify that it is
>> valid.
>> - in stm32_rproc_tee_elf_sanity_check we have to do the same, that means to
>> authenticate it. the authentication is done during the load.
>>
>> So this flag is used to avoid to reload it twice time.
>> refactoring the comment should help to understand this flag
>>
>>
>> An alternative would be to bypass the sanity check. But this lead to same
>> limitation.
>> Before loading the firmware in remoteproc_core, we call rproc_parse_fw() that is
>> used to get the resource table address. To get it from tee we need to
>> authenticate the firmware so load it...
>>
>
> I spent a long time thinking about this patchset. Looking at the code as it
> is now, request_firmware() in rproc_boot() is called even when the TEE is
> responsible for loading the firmware. There should be some conditional code
> that calls either request_firmware() or tee_rproc_load_fw(). The latter should
> also be renamed to tee_rproc_request_firmware() to avoid confusion.


The request_firmware() call is needed in both cases to get the image from the
filesystem. The tee_rproc_load_fw() gets, as input, the struct firmware provided
by request_firmware().

If we want to integrate in remoteproc_core the solution could probably have to
create the equivalent of the rproc_fw_boot() to load the firmware with an
external method. Here is an example based on a new rproc_ops ( not tested)

+ static int rproc_fw_ext_boot(struct rproc *rproc, const struct firmware *fw)
+ {
+ struct device *dev = &rproc->dev;
+ const char *name = rproc->firmware;
+ int ret;
+
+
+ dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size);
+
+ /* ops to load and start the remoteprocessor */
+ ret = rproc->ops->boot(rproc, fw);
+ if (ret)
+ return ret;
+
+ /*
+ * if enabling an IOMMU isn't relevant for this rproc, this is
+ * just a nop
+ */
+ ret = rproc_enable_iommu(rproc);
+ if (ret) {
+ dev_err(dev, "can't enable iommu: %d\n", ret);
+ return ret;
+ }
+
+ /* Prepare rproc for firmware loading if needed */
+ ret = rproc_prepare_device(rproc);
+ if (ret) {
+ dev_err(dev, "can't prepare rproc %s: %d\n", rproc->name, ret);
+ goto disable_iommu;
+ }
+
+ ret = rproc_set_rsc_table(rproc);
+ if (ret) {
+ dev_err(dev, "can't load resource table: %d\n", ret);
+ goto unprepare_device;
+ }
+
+
+ /* reset max_notifyid */
+ rproc->max_notifyid = -1;
+
+ /* reset handled vdev */
+ rproc->nb_vdev = 0;
+
+ /* handle fw resources which are required to boot rproc */
+ ret = rproc_handle_resources(rproc, rproc_loading_handlers);
+ if (ret) {
+ dev_err(dev, "Failed to process resources: %d\n", ret);
+ goto clean_up_resources;
+ }
+
+ /* Allocate carveout resources associated to rproc */
+ ret = rproc_alloc_registered_carveouts(rproc);
+ if (ret) {
+ dev_err(dev, "Failed to allocate associated carveouts: %d\n",
+ ret);
+ goto clean_up_resources;
+ }
+
+ return 0;
+
+ clean_up_resources:
+ rproc_resource_cleanup(rproc);
+ unprepare_rproc:
+ /* release HW resources if needed */
+ rproc_unprepare_device(rproc);
+ disable_iommu:
+ rproc_disable_iommu(rproc);
+ return ret;
+ }


int rproc_boot(struct rproc *rproc)
{
[...]

- ret = rproc_fw_boot(rproc, firmware_p);
+ if(rproc->ops->boot)
+ ret = rproc_fw_ext_boot(rproc, firmware_p);
+ else
+ ret = rproc_fw_boot(rproc, firmware_p);

Another advantage of this solution is that it opens the framework to other
formats. For instance it could be a way to support dtb format requested in [RFC]
Passing device-tree to remoteproc [1].

[1]
https://lore.kernel.org/linux-remoteproc/[email protected]/T/#t

Thanks,
Arnaud



>
> I touched on that before but please rename rproc_tee_get_rsc_table() to
> rproc_tee_elf_load_rsc_table(). I also suggest to introduce a new function,
> rproc_tee_get_loaded_rsc_table() that would be called from
> rproc_tee_elf_load_rsc_table(). That way we don't need trproc->rsc_va.
>
> I also think tee_rproc should be renamed to "rproc_tee_interface" and folded
> under struct rproc.
>
> With the above most of the problems with the current implementation should
> naturally go away.
>
> Thanks,
> Mathieu
>
>>
>>>> + ret = tee_rproc_load_fw(ddata->trproc, fw);
>>>> + if (ret)
>>>> + return ret;
>>>> + ddata->fw_loaded = true;
>>>> +
>>>> + /* Update the resource table parameters. */
>>>> + if (rproc_tee_get_rsc_table(ddata->trproc)) {
>>>> + /* No resource table: reset the related fields. */
>>>> + rproc->cached_table = NULL;
>>>> + rproc->table_ptr = NULL;
>>>> + rproc->table_sz = 0;
>>>> + }
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static struct resource_table *
>>>> +stm32_rproc_tee_elf_find_loaded_rsc_table(struct rproc *rproc,
>>>> + const struct firmware *fw)
>>>> +{
>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>> +
>>>> + return tee_rproc_get_loaded_rsc_table(ddata->trproc);
>>>> +}
>>>> +
>>>> +static int stm32_rproc_tee_start(struct rproc *rproc)
>>>> +{
>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>> +
>>>> + return tee_rproc_start(ddata->trproc);
>>>> +}
>>>> +
>>>> +static int stm32_rproc_tee_attach(struct rproc *rproc)
>>>> +{
>>>> + /* Nothing to do, remote proc already started by the secured context. */
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static int stm32_rproc_tee_stop(struct rproc *rproc)
>>>> +{
>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>> + int err;
>>>> +
>>>> + stm32_rproc_request_shutdown(rproc);
>>>> +
>>>> + err = tee_rproc_stop(ddata->trproc);
>>>> + if (err)
>>>> + return err;
>>>> +
>>>> + ddata->fw_loaded = false;
>>>> +
>>>> + return stm32_rproc_release(rproc);
>>>> +}
>>>> +
>>>> static int stm32_rproc_prepare(struct rproc *rproc)
>>>> {
>>>> struct device *dev = rproc->dev.parent;
>>>> @@ -319,7 +410,14 @@ static int stm32_rproc_prepare(struct rproc *rproc)
>>>>
>>>> static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
>>>> {
>>>> - if (rproc_elf_load_rsc_table(rproc, fw))
>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>> + int ret;
>>>> +
>>>> + if (ddata->trproc)
>>>> + ret = rproc_tee_get_rsc_table(ddata->trproc);
>>>> + else
>>>> + ret = rproc_elf_load_rsc_table(rproc, fw);
>>>> + if (ret)
>>>> dev_warn(&rproc->dev, "no resource table found for this firmware\n");
>>>>
>>>> return 0;
>>>> @@ -693,8 +791,22 @@ 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 = stm32_rproc_tee_start,
>>>> + .stop = stm32_rproc_tee_stop,
>>>> + .attach = stm32_rproc_tee_attach,
>>>> + .kick = stm32_rproc_kick,
>>>> + .parse_fw = stm32_rproc_parse_fw,
>>>> + .find_loaded_rsc_table = stm32_rproc_tee_elf_find_loaded_rsc_table,
>>>> + .get_loaded_rsc_table = stm32_rproc_get_loaded_rsc_table,
>>>> + .sanity_check = stm32_rproc_tee_elf_sanity_check,
>>>> + .load = stm32_rproc_tee_elf_load,
>>>> +};
>>>> +
>>>> static const struct of_device_id stm32_rproc_match[] = {
>>>> - { .compatible = "st,stm32mp1-m4" },
>>>> + {.compatible = "st,stm32mp1-m4",},
>>>> + {.compatible = "st,stm32mp1-m4-tee",},
>>>> {},
>>>> };
>>>> MODULE_DEVICE_TABLE(of, stm32_rproc_match);
>>>> @@ -853,6 +965,7 @@ 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;
>>>> int ret;
>>>> @@ -861,11 +974,31 @@ static int stm32_rproc_probe(struct platform_device *pdev)
>>>> if (ret)
>>>> return ret;
>>>>
>>>> - rproc = 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")) {
>>>> + trproc = tee_rproc_register(dev, STM32_MP1_M4_PROC_ID);
>>>> + if (IS_ERR(trproc)) {
>>>> + dev_err_probe(dev, PTR_ERR(trproc),
>>>> + "signed firmware not supported by TEE\n");
>>>> + return PTR_ERR(trproc);
>>>> + }
>>>> + /*
>>>> + * Delegate the firmware management to the secure context.
>>>> + * The firmware loaded has to be signed.
>>>> + */
>>>> + dev_info(dev, "Support of signed firmware only\n");
>>>
>>> Not sure what this adds. Please remove.
>>
>> This is used to inform the user that only a signed firmware can be loaded, not
>> an ELF file.
>> I have a patch in my pipe to provide the supported format in the debugfs. In a
>> first step, I can suppress this message and we can revisit the issue when I push
>> the debugfs proposal.
>>
>> Thanks,
>> Arnaud
>>
>>>
>>>> + }
>>>> + rproc = rproc_alloc(dev, np->name,
>>>> + trproc ? &st_rproc_tee_ops : &st_rproc_ops,
>>>> + NULL, sizeof(*ddata));
>>>> + if (!rproc) {
>>>> + ret = -ENOMEM;
>>>> + goto free_tee;
>>>> + }
>>>>
>>>> ddata = rproc->priv;
>>>> + ddata->trproc = trproc;
>>>> + if (trproc)
>>>> + trproc->rproc = rproc;
>>>>
>>>> rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
>>>>
>>>> @@ -916,6 +1049,10 @@ static int stm32_rproc_probe(struct platform_device *pdev)
>>>> device_init_wakeup(dev, false);
>>>> }
>>>> rproc_free(rproc);
>>>> +free_tee:
>>>> + if (trproc)
>>>> + tee_rproc_unregister(trproc);
>>>> +
>>>> return ret;
>>>> }
>>>>
>>>> @@ -937,6 +1074,8 @@ static void stm32_rproc_remove(struct platform_device *pdev)
>>>> device_init_wakeup(dev, false);
>>>> }
>>>> rproc_free(rproc);
>>>> + if (ddata->trproc)
>>>> + tee_rproc_unregister(ddata->trproc);
>>>> }
>>>>
>>>> static int stm32_rproc_suspend(struct device *dev)
>>>> --
>>>> 2.25.1
>>>>

2024-02-01 16:28:32

by Mathieu Poirier

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware

On Thu, Feb 01, 2024 at 04:06:37PM +0100, Arnaud POULIQUEN wrote:
> hello Mathieu,
>
> On 1/31/24 19:52, Mathieu Poirier wrote:
> > On Tue, Jan 30, 2024 at 10:13:48AM +0100, Arnaud POULIQUEN wrote:
> >>
> >>
> >> On 1/26/24 18:11, Mathieu Poirier wrote:
> >>> On Thu, Jan 18, 2024 at 11:04:33AM +0100, Arnaud Pouliquen wrote:
> >>>> 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 <[email protected]>
> >>>> ---
> >>>> V1 to V2 update:
> >>>> - remove the select "TEE_REMOTEPROC" in STM32_RPROC config as detected by
> >>>> the kernel test robot:
> >>>> WARNING: unmet direct dependencies detected for TEE_REMOTEPROC
> >>>> Depends on [n]: REMOTEPROC [=y] && OPTEE [=n]
> >>>> Selected by [y]:
> >>>> - STM32_RPROC [=y] && (ARCH_STM32 || COMPILE_TEST [=y]) && REMOTEPROC [=y]
> >>>> - Fix initialized trproc variable in stm32_rproc_probe
> >>>> ---
> >>>> drivers/remoteproc/stm32_rproc.c | 149 +++++++++++++++++++++++++++++--
> >>>> 1 file changed, 144 insertions(+), 5 deletions(-)
> >>>>
> >>>> diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
> >>>> index fcc0001e2657..cf6a21bac945 100644
> >>>> --- a/drivers/remoteproc/stm32_rproc.c
> >>>> +++ b/drivers/remoteproc/stm32_rproc.c
> >>>> @@ -20,6 +20,7 @@
> >>>> #include <linux/remoteproc.h>
> >>>> #include <linux/reset.h>
> >>>> #include <linux/slab.h>
> >>>> +#include <linux/tee_remoteproc.h>
> >>>> #include <linux/workqueue.h>
> >>>>
> >>>> #include "remoteproc_internal.h"
> >>>> @@ -49,6 +50,9 @@
> >>>> #define M4_STATE_STANDBY 4
> >>>> #define M4_STATE_CRASH 5
> >>>>
> >>>> +/* Remote processor unique identifier aligned with the Trusted Execution Environment definitions */
> >>>> +#define STM32_MP1_M4_PROC_ID 0
> >>>> +
> >>>> struct stm32_syscon {
> >>>> struct regmap *map;
> >>>> u32 reg;
> >>>> @@ -90,6 +94,8 @@ struct stm32_rproc {
> >>>> struct stm32_mbox mb[MBOX_NB_MBX];
> >>>> struct workqueue_struct *workqueue;
> >>>> bool hold_boot_smc;
> >>>> + bool fw_loaded;
> >>>> + struct tee_rproc *trproc;
> >>>> void __iomem *rsc_va;
> >>>> };
> >>>>
> >>>> @@ -257,6 +263,91 @@ static int stm32_rproc_release(struct rproc *rproc)
> >>>> return err;
> >>>> }
> >>>>
> >>>> +static int stm32_rproc_tee_elf_sanity_check(struct rproc *rproc,
> >>>> + const struct firmware *fw)
> >>>> +{
> >>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>> + unsigned int ret = 0;
> >>>> +
> >>>> + if (rproc->state == RPROC_DETACHED)
> >>>> + return 0;
> >>>> +
> >>>> + ret = tee_rproc_load_fw(ddata->trproc, fw);
> >>>> + if (!ret)
> >>>> + ddata->fw_loaded = true;
> >>>> +
> >>>> + return ret;
> >>>> +}
> >>>> +
> >>>> +static int stm32_rproc_tee_elf_load(struct rproc *rproc,
> >>>> + const struct firmware *fw)
> >>>> +{
> >>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>> + unsigned int ret;
> >>>> +
> >>>> + /*
> >>>> + * This function can be called by remote proc for recovery
> >>>> + * without the sanity check. In this case we need to load the firmware
> >>>> + * else nothing done here as the firmware has been preloaded for the
> >>>> + * sanity check to be able to parse it for the resource table.
> >>>> + */
> >>>
> >>> This comment is very confusing - please consider refactoring.
> >>>
> >>>> + if (ddata->fw_loaded)
> >>>> + return 0;
> >>>> +
> >>>
> >>> I'm not sure about keeping a flag to indicate the status of the loaded firmware.
> >>> It is not done for the non-secure method, I don't see why it would be needed for
> >>> the secure one.
> >>>
> >>
> >> The difference is on the sanity check.
> >> - in rproc_elf_sanity_check we parse the elf file to verify that it is
> >> valid.
> >> - in stm32_rproc_tee_elf_sanity_check we have to do the same, that means to
> >> authenticate it. the authentication is done during the load.
> >>
> >> So this flag is used to avoid to reload it twice time.
> >> refactoring the comment should help to understand this flag
> >>
> >>
> >> An alternative would be to bypass the sanity check. But this lead to same
> >> limitation.
> >> Before loading the firmware in remoteproc_core, we call rproc_parse_fw() that is
> >> used to get the resource table address. To get it from tee we need to
> >> authenticate the firmware so load it...
> >>
> >
> > I spent a long time thinking about this patchset. Looking at the code as it
> > is now, request_firmware() in rproc_boot() is called even when the TEE is
> > responsible for loading the firmware. There should be some conditional code
> > that calls either request_firmware() or tee_rproc_load_fw(). The latter should
> > also be renamed to tee_rproc_request_firmware() to avoid confusion.
>
>
> The request_firmware() call is needed in both cases to get the image from the
> filesystem. The tee_rproc_load_fw() gets, as input, the struct firmware provided
> by request_firmware().

The cover letter clearly state the secure side is responsible for loading the
firmware image but here you're telling me it has to be loaded twice. This is
very confusing.

I'm also confused as to why stm32_rproc_tee_elf_sanity_check() is calling
tee_rproc_load_fw(). There should be one call to load the firmware and another
to perform a sanity check on it. If the sanity check is done at load time by
the secure world then ops::sanity_check() is NULL.

Most of what this patchset does makes sense, but some of it needs to be moved
around.

Thanks,
Mathieu

>
> If we want to integrate in remoteproc_core the solution could probably have to
> create the equivalent of the rproc_fw_boot() to load the firmware with an
> external method. Here is an example based on a new rproc_ops ( not tested)
>
> + static int rproc_fw_ext_boot(struct rproc *rproc, const struct firmware *fw)
> + {
> + struct device *dev = &rproc->dev;
> + const char *name = rproc->firmware;
> + int ret;
> +
> +
> + dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size);
> +
> + /* ops to load and start the remoteprocessor */
> + ret = rproc->ops->boot(rproc, fw);
> + if (ret)
> + return ret;
> +
> + /*
> + * if enabling an IOMMU isn't relevant for this rproc, this is
> + * just a nop
> + */
> + ret = rproc_enable_iommu(rproc);
> + if (ret) {
> + dev_err(dev, "can't enable iommu: %d\n", ret);
> + return ret;
> + }
> +
> + /* Prepare rproc for firmware loading if needed */
> + ret = rproc_prepare_device(rproc);
> + if (ret) {
> + dev_err(dev, "can't prepare rproc %s: %d\n", rproc->name, ret);
> + goto disable_iommu;
> + }
> +
> + ret = rproc_set_rsc_table(rproc);
> + if (ret) {
> + dev_err(dev, "can't load resource table: %d\n", ret);
> + goto unprepare_device;
> + }
> +
> +
> + /* reset max_notifyid */
> + rproc->max_notifyid = -1;
> +
> + /* reset handled vdev */
> + rproc->nb_vdev = 0;
> +
> + /* handle fw resources which are required to boot rproc */
> + ret = rproc_handle_resources(rproc, rproc_loading_handlers);
> + if (ret) {
> + dev_err(dev, "Failed to process resources: %d\n", ret);
> + goto clean_up_resources;
> + }
> +
> + /* Allocate carveout resources associated to rproc */
> + ret = rproc_alloc_registered_carveouts(rproc);
> + if (ret) {
> + dev_err(dev, "Failed to allocate associated carveouts: %d\n",
> + ret);
> + goto clean_up_resources;
> + }
> +
> + return 0;
> +
> + clean_up_resources:
> + rproc_resource_cleanup(rproc);
> + unprepare_rproc:
> + /* release HW resources if needed */
> + rproc_unprepare_device(rproc);
> + disable_iommu:
> + rproc_disable_iommu(rproc);
> + return ret;
> + }
>
>
> int rproc_boot(struct rproc *rproc)
> {
> [...]
>
> - ret = rproc_fw_boot(rproc, firmware_p);
> + if(rproc->ops->boot)
> + ret = rproc_fw_ext_boot(rproc, firmware_p);
> + else
> + ret = rproc_fw_boot(rproc, firmware_p);
>
> Another advantage of this solution is that it opens the framework to other
> formats. For instance it could be a way to support dtb format requested in [RFC]
> Passing device-tree to remoteproc [1].
>
> [1]
> https://lore.kernel.org/linux-remoteproc/[email protected]/T/#t
>
> Thanks,
> Arnaud
>
>
>
> >
> > I touched on that before but please rename rproc_tee_get_rsc_table() to
> > rproc_tee_elf_load_rsc_table(). I also suggest to introduce a new function,
> > rproc_tee_get_loaded_rsc_table() that would be called from
> > rproc_tee_elf_load_rsc_table(). That way we don't need trproc->rsc_va.
> >
> > I also think tee_rproc should be renamed to "rproc_tee_interface" and folded
> > under struct rproc.
> >
> > With the above most of the problems with the current implementation should
> > naturally go away.
> >
> > Thanks,
> > Mathieu
> >
> >>
> >>>> + ret = tee_rproc_load_fw(ddata->trproc, fw);
> >>>> + if (ret)
> >>>> + return ret;
> >>>> + ddata->fw_loaded = true;
> >>>> +
> >>>> + /* Update the resource table parameters. */
> >>>> + if (rproc_tee_get_rsc_table(ddata->trproc)) {
> >>>> + /* No resource table: reset the related fields. */
> >>>> + rproc->cached_table = NULL;
> >>>> + rproc->table_ptr = NULL;
> >>>> + rproc->table_sz = 0;
> >>>> + }
> >>>> +
> >>>> + return 0;
> >>>> +}
> >>>> +
> >>>> +static struct resource_table *
> >>>> +stm32_rproc_tee_elf_find_loaded_rsc_table(struct rproc *rproc,
> >>>> + const struct firmware *fw)
> >>>> +{
> >>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>> +
> >>>> + return tee_rproc_get_loaded_rsc_table(ddata->trproc);
> >>>> +}
> >>>> +
> >>>> +static int stm32_rproc_tee_start(struct rproc *rproc)
> >>>> +{
> >>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>> +
> >>>> + return tee_rproc_start(ddata->trproc);
> >>>> +}
> >>>> +
> >>>> +static int stm32_rproc_tee_attach(struct rproc *rproc)
> >>>> +{
> >>>> + /* Nothing to do, remote proc already started by the secured context. */
> >>>> + return 0;
> >>>> +}
> >>>> +
> >>>> +static int stm32_rproc_tee_stop(struct rproc *rproc)
> >>>> +{
> >>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>> + int err;
> >>>> +
> >>>> + stm32_rproc_request_shutdown(rproc);
> >>>> +
> >>>> + err = tee_rproc_stop(ddata->trproc);
> >>>> + if (err)
> >>>> + return err;
> >>>> +
> >>>> + ddata->fw_loaded = false;
> >>>> +
> >>>> + return stm32_rproc_release(rproc);
> >>>> +}
> >>>> +
> >>>> static int stm32_rproc_prepare(struct rproc *rproc)
> >>>> {
> >>>> struct device *dev = rproc->dev.parent;
> >>>> @@ -319,7 +410,14 @@ static int stm32_rproc_prepare(struct rproc *rproc)
> >>>>
> >>>> static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
> >>>> {
> >>>> - if (rproc_elf_load_rsc_table(rproc, fw))
> >>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>> + int ret;
> >>>> +
> >>>> + if (ddata->trproc)
> >>>> + ret = rproc_tee_get_rsc_table(ddata->trproc);
> >>>> + else
> >>>> + ret = rproc_elf_load_rsc_table(rproc, fw);
> >>>> + if (ret)
> >>>> dev_warn(&rproc->dev, "no resource table found for this firmware\n");
> >>>>
> >>>> return 0;
> >>>> @@ -693,8 +791,22 @@ 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 = stm32_rproc_tee_start,
> >>>> + .stop = stm32_rproc_tee_stop,
> >>>> + .attach = stm32_rproc_tee_attach,
> >>>> + .kick = stm32_rproc_kick,
> >>>> + .parse_fw = stm32_rproc_parse_fw,
> >>>> + .find_loaded_rsc_table = stm32_rproc_tee_elf_find_loaded_rsc_table,
> >>>> + .get_loaded_rsc_table = stm32_rproc_get_loaded_rsc_table,
> >>>> + .sanity_check = stm32_rproc_tee_elf_sanity_check,
> >>>> + .load = stm32_rproc_tee_elf_load,
> >>>> +};
> >>>> +
> >>>> static const struct of_device_id stm32_rproc_match[] = {
> >>>> - { .compatible = "st,stm32mp1-m4" },
> >>>> + {.compatible = "st,stm32mp1-m4",},
> >>>> + {.compatible = "st,stm32mp1-m4-tee",},
> >>>> {},
> >>>> };
> >>>> MODULE_DEVICE_TABLE(of, stm32_rproc_match);
> >>>> @@ -853,6 +965,7 @@ 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;
> >>>> int ret;
> >>>> @@ -861,11 +974,31 @@ static int stm32_rproc_probe(struct platform_device *pdev)
> >>>> if (ret)
> >>>> return ret;
> >>>>
> >>>> - rproc = 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")) {
> >>>> + trproc = tee_rproc_register(dev, STM32_MP1_M4_PROC_ID);
> >>>> + if (IS_ERR(trproc)) {
> >>>> + dev_err_probe(dev, PTR_ERR(trproc),
> >>>> + "signed firmware not supported by TEE\n");
> >>>> + return PTR_ERR(trproc);
> >>>> + }
> >>>> + /*
> >>>> + * Delegate the firmware management to the secure context.
> >>>> + * The firmware loaded has to be signed.
> >>>> + */
> >>>> + dev_info(dev, "Support of signed firmware only\n");
> >>>
> >>> Not sure what this adds. Please remove.
> >>
> >> This is used to inform the user that only a signed firmware can be loaded, not
> >> an ELF file.
> >> I have a patch in my pipe to provide the supported format in the debugfs. In a
> >> first step, I can suppress this message and we can revisit the issue when I push
> >> the debugfs proposal.
> >>
> >> Thanks,
> >> Arnaud
> >>
> >>>
> >>>> + }
> >>>> + rproc = rproc_alloc(dev, np->name,
> >>>> + trproc ? &st_rproc_tee_ops : &st_rproc_ops,
> >>>> + NULL, sizeof(*ddata));
> >>>> + if (!rproc) {
> >>>> + ret = -ENOMEM;
> >>>> + goto free_tee;
> >>>> + }
> >>>>
> >>>> ddata = rproc->priv;
> >>>> + ddata->trproc = trproc;
> >>>> + if (trproc)
> >>>> + trproc->rproc = rproc;
> >>>>
> >>>> rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
> >>>>
> >>>> @@ -916,6 +1049,10 @@ static int stm32_rproc_probe(struct platform_device *pdev)
> >>>> device_init_wakeup(dev, false);
> >>>> }
> >>>> rproc_free(rproc);
> >>>> +free_tee:
> >>>> + if (trproc)
> >>>> + tee_rproc_unregister(trproc);
> >>>> +
> >>>> return ret;
> >>>> }
> >>>>
> >>>> @@ -937,6 +1074,8 @@ static void stm32_rproc_remove(struct platform_device *pdev)
> >>>> device_init_wakeup(dev, false);
> >>>> }
> >>>> rproc_free(rproc);
> >>>> + if (ddata->trproc)
> >>>> + tee_rproc_unregister(ddata->trproc);
> >>>> }
> >>>>
> >>>> static int stm32_rproc_suspend(struct device *dev)
> >>>> --
> >>>> 2.25.1
> >>>>

2024-02-01 18:59:40

by Arnaud POULIQUEN

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware



On 2/1/24 17:02, Mathieu Poirier wrote:
> On Thu, Feb 01, 2024 at 04:06:37PM +0100, Arnaud POULIQUEN wrote:
>> hello Mathieu,
>>
>> On 1/31/24 19:52, Mathieu Poirier wrote:
>>> On Tue, Jan 30, 2024 at 10:13:48AM +0100, Arnaud POULIQUEN wrote:
>>>>
>>>>
>>>> On 1/26/24 18:11, Mathieu Poirier wrote:
>>>>> On Thu, Jan 18, 2024 at 11:04:33AM +0100, Arnaud Pouliquen wrote:
>>>>>> 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 <[email protected]>
>>>>>> ---
>>>>>> V1 to V2 update:
>>>>>> - remove the select "TEE_REMOTEPROC" in STM32_RPROC config as detected by
>>>>>> the kernel test robot:
>>>>>> WARNING: unmet direct dependencies detected for TEE_REMOTEPROC
>>>>>> Depends on [n]: REMOTEPROC [=y] && OPTEE [=n]
>>>>>> Selected by [y]:
>>>>>> - STM32_RPROC [=y] && (ARCH_STM32 || COMPILE_TEST [=y]) && REMOTEPROC [=y]
>>>>>> - Fix initialized trproc variable in stm32_rproc_probe
>>>>>> ---
>>>>>> drivers/remoteproc/stm32_rproc.c | 149 +++++++++++++++++++++++++++++--
>>>>>> 1 file changed, 144 insertions(+), 5 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
>>>>>> index fcc0001e2657..cf6a21bac945 100644
>>>>>> --- a/drivers/remoteproc/stm32_rproc.c
>>>>>> +++ b/drivers/remoteproc/stm32_rproc.c
>>>>>> @@ -20,6 +20,7 @@
>>>>>> #include <linux/remoteproc.h>
>>>>>> #include <linux/reset.h>
>>>>>> #include <linux/slab.h>
>>>>>> +#include <linux/tee_remoteproc.h>
>>>>>> #include <linux/workqueue.h>
>>>>>>
>>>>>> #include "remoteproc_internal.h"
>>>>>> @@ -49,6 +50,9 @@
>>>>>> #define M4_STATE_STANDBY 4
>>>>>> #define M4_STATE_CRASH 5
>>>>>>
>>>>>> +/* Remote processor unique identifier aligned with the Trusted Execution Environment definitions */
>>>>>> +#define STM32_MP1_M4_PROC_ID 0
>>>>>> +
>>>>>> struct stm32_syscon {
>>>>>> struct regmap *map;
>>>>>> u32 reg;
>>>>>> @@ -90,6 +94,8 @@ struct stm32_rproc {
>>>>>> struct stm32_mbox mb[MBOX_NB_MBX];
>>>>>> struct workqueue_struct *workqueue;
>>>>>> bool hold_boot_smc;
>>>>>> + bool fw_loaded;
>>>>>> + struct tee_rproc *trproc;
>>>>>> void __iomem *rsc_va;
>>>>>> };
>>>>>>
>>>>>> @@ -257,6 +263,91 @@ static int stm32_rproc_release(struct rproc *rproc)
>>>>>> return err;
>>>>>> }
>>>>>>
>>>>>> +static int stm32_rproc_tee_elf_sanity_check(struct rproc *rproc,
>>>>>> + const struct firmware *fw)
>>>>>> +{
>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>> + unsigned int ret = 0;
>>>>>> +
>>>>>> + if (rproc->state == RPROC_DETACHED)
>>>>>> + return 0;
>>>>>> +
>>>>>> + ret = tee_rproc_load_fw(ddata->trproc, fw);
>>>>>> + if (!ret)
>>>>>> + ddata->fw_loaded = true;
>>>>>> +
>>>>>> + return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static int stm32_rproc_tee_elf_load(struct rproc *rproc,
>>>>>> + const struct firmware *fw)
>>>>>> +{
>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>> + unsigned int ret;
>>>>>> +
>>>>>> + /*
>>>>>> + * This function can be called by remote proc for recovery
>>>>>> + * without the sanity check. In this case we need to load the firmware
>>>>>> + * else nothing done here as the firmware has been preloaded for the
>>>>>> + * sanity check to be able to parse it for the resource table.
>>>>>> + */
>>>>>
>>>>> This comment is very confusing - please consider refactoring.
>>>>>
>>>>>> + if (ddata->fw_loaded)
>>>>>> + return 0;
>>>>>> +
>>>>>
>>>>> I'm not sure about keeping a flag to indicate the status of the loaded firmware.
>>>>> It is not done for the non-secure method, I don't see why it would be needed for
>>>>> the secure one.
>>>>>
>>>>
>>>> The difference is on the sanity check.
>>>> - in rproc_elf_sanity_check we parse the elf file to verify that it is
>>>> valid.
>>>> - in stm32_rproc_tee_elf_sanity_check we have to do the same, that means to
>>>> authenticate it. the authentication is done during the load.
>>>>
>>>> So this flag is used to avoid to reload it twice time.
>>>> refactoring the comment should help to understand this flag
>>>>
>>>>
>>>> An alternative would be to bypass the sanity check. But this lead to same
>>>> limitation.
>>>> Before loading the firmware in remoteproc_core, we call rproc_parse_fw() that is
>>>> used to get the resource table address. To get it from tee we need to
>>>> authenticate the firmware so load it...
>>>>
>>>
>>> I spent a long time thinking about this patchset. Looking at the code as it
>>> is now, request_firmware() in rproc_boot() is called even when the TEE is
>>> responsible for loading the firmware. There should be some conditional code
>>> that calls either request_firmware() or tee_rproc_load_fw(). The latter should
>>> also be renamed to tee_rproc_request_firmware() to avoid confusion.
>>
>>
>> The request_firmware() call is needed in both cases to get the image from the
>> filesystem. The tee_rproc_load_fw() gets, as input, the struct firmware provided
>> by request_firmware().
>
> The cover letter clearly state the secure side is responsible for loading the
> firmware image but here you're telling me it has to be loaded twice. This is
> very confusing.

Concerning the call of request_firmware()

By "both cases" I would say that the call of request_firmware() is needed in
both modes:
- the ELF firmware is parsed and loaded by linux (legacy)
- the binary firmware is parsed and loaded by OP-TEE.

The Op-TEE is not able to get the firmware image from the file system.


Concerning the call of tee_rproc_load_fw twice time

There are 2 use cases:

- First boot of the remote processor:

1) The Linux rproc gets the binary firmware image from the file system by
calling request_firmware(). A copy is stored in memory.
2) the linux performs a sanity check on the firmware calling
rproc_fw_sanity_check()
=> from OP-TEE point of view this means to autenticate the firmware
=> let consider in this exemple that we bypass this step
(ops->sanity_check = NULL)

3) the linux rproc call rproc_parse_fw() to get the resource table
=> From OP-TEE point of view the resource table is available only when
the firmware is loaded
=> We need to call tee_rproc_load_fw() to be able then to get the
address of the resource table.
4) The Linux rproc calls rproc_handle_resources() to parse the resource table.
5) The linux rproc calls rproc_start()
- load the firrmware calling rproc_load_segments()
=> we don't want to call tee_rproc_load_fw() it a second time
- start the firmware calling ops->start()

- Reboot on crash recovery using rproc_boot_recovery()

1) The Linux rproc gets the binary firmware image from the file system by
calling request_firmware(). A copy is stored in memory.
5) The linux rproc calls rproc_start()
- load the firrmware calling rproc_load_segments()
=> we have to call tee_rproc_load_fw() to reload the firmware
- start the firmware calling ops->start()

In first use case we have to load the firmware on rproc_parse_fw(), in second
usecase on rproc_load_segments().

This is the point I have tried to solve with the ddata->fw_loaded variable.

>
> I'm also confused as to why stm32_rproc_tee_elf_sanity_check() is calling
> tee_rproc_load_fw(). There should be one call to load the firmware and another
> to perform a sanity check on it. If the sanity check is done at load time by
> the secure world then ops::sanity_check() is NULL.

Sure, make sense to remove the sanity_check ops

Thanks,
Arnaud

>
> Most of what this patchset does makes sense, but some of it needs to be moved
> around.
>
> Thanks,
> Mathieu
>
>>
>> If we want to integrate in remoteproc_core the solution could probably have to
>> create the equivalent of the rproc_fw_boot() to load the firmware with an
>> external method. Here is an example based on a new rproc_ops ( not tested)
>>
>> + static int rproc_fw_ext_boot(struct rproc *rproc, const struct firmware *fw)
>> + {
>> + struct device *dev = &rproc->dev;
>> + const char *name = rproc->firmware;
>> + int ret;
>> +
>> +
>> + dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size);
>> +
>> + /* ops to load and start the remoteprocessor */
>> + ret = rproc->ops->boot(rproc, fw);
>> + if (ret)
>> + return ret;
>> +
>> + /*
>> + * if enabling an IOMMU isn't relevant for this rproc, this is
>> + * just a nop
>> + */
>> + ret = rproc_enable_iommu(rproc);
>> + if (ret) {
>> + dev_err(dev, "can't enable iommu: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + /* Prepare rproc for firmware loading if needed */
>> + ret = rproc_prepare_device(rproc);
>> + if (ret) {
>> + dev_err(dev, "can't prepare rproc %s: %d\n", rproc->name, ret);
>> + goto disable_iommu;
>> + }
>> +
>> + ret = rproc_set_rsc_table(rproc);
>> + if (ret) {
>> + dev_err(dev, "can't load resource table: %d\n", ret);
>> + goto unprepare_device;
>> + }
>> +
>> +
>> + /* reset max_notifyid */
>> + rproc->max_notifyid = -1;
>> +
>> + /* reset handled vdev */
>> + rproc->nb_vdev = 0;
>> +
>> + /* handle fw resources which are required to boot rproc */
>> + ret = rproc_handle_resources(rproc, rproc_loading_handlers);
>> + if (ret) {
>> + dev_err(dev, "Failed to process resources: %d\n", ret);
>> + goto clean_up_resources;
>> + }
>> +
>> + /* Allocate carveout resources associated to rproc */
>> + ret = rproc_alloc_registered_carveouts(rproc);
>> + if (ret) {
>> + dev_err(dev, "Failed to allocate associated carveouts: %d\n",
>> + ret);
>> + goto clean_up_resources;
>> + }
>> +
>> + return 0;
>> +
>> + clean_up_resources:
>> + rproc_resource_cleanup(rproc);
>> + unprepare_rproc:
>> + /* release HW resources if needed */
>> + rproc_unprepare_device(rproc);
>> + disable_iommu:
>> + rproc_disable_iommu(rproc);
>> + return ret;
>> + }
>>
>>
>> int rproc_boot(struct rproc *rproc)
>> {
>> [...]
>>
>> - ret = rproc_fw_boot(rproc, firmware_p);
>> + if(rproc->ops->boot)
>> + ret = rproc_fw_ext_boot(rproc, firmware_p);
>> + else
>> + ret = rproc_fw_boot(rproc, firmware_p);
>>
>> Another advantage of this solution is that it opens the framework to other
>> formats. For instance it could be a way to support dtb format requested in [RFC]
>> Passing device-tree to remoteproc [1].
>>
>> [1]
>> https://lore.kernel.org/linux-remoteproc/[email protected]/T/#t
>>
>> Thanks,
>> Arnaud
>>
>>
>>
>>>
>>> I touched on that before but please rename rproc_tee_get_rsc_table() to
>>> rproc_tee_elf_load_rsc_table(). I also suggest to introduce a new function,
>>> rproc_tee_get_loaded_rsc_table() that would be called from
>>> rproc_tee_elf_load_rsc_table(). That way we don't need trproc->rsc_va.
>>>
>>> I also think tee_rproc should be renamed to "rproc_tee_interface" and folded
>>> under struct rproc.
>>>
>>> With the above most of the problems with the current implementation should
>>> naturally go away.
>>>
>>> Thanks,
>>> Mathieu
>>>
>>>>
>>>>>> + ret = tee_rproc_load_fw(ddata->trproc, fw);
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> + ddata->fw_loaded = true;
>>>>>> +
>>>>>> + /* Update the resource table parameters. */
>>>>>> + if (rproc_tee_get_rsc_table(ddata->trproc)) {
>>>>>> + /* No resource table: reset the related fields. */
>>>>>> + rproc->cached_table = NULL;
>>>>>> + rproc->table_ptr = NULL;
>>>>>> + rproc->table_sz = 0;
>>>>>> + }
>>>>>> +
>>>>>> + return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static struct resource_table *
>>>>>> +stm32_rproc_tee_elf_find_loaded_rsc_table(struct rproc *rproc,
>>>>>> + const struct firmware *fw)
>>>>>> +{
>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>> +
>>>>>> + return tee_rproc_get_loaded_rsc_table(ddata->trproc);
>>>>>> +}
>>>>>> +
>>>>>> +static int stm32_rproc_tee_start(struct rproc *rproc)
>>>>>> +{
>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>> +
>>>>>> + return tee_rproc_start(ddata->trproc);
>>>>>> +}
>>>>>> +
>>>>>> +static int stm32_rproc_tee_attach(struct rproc *rproc)
>>>>>> +{
>>>>>> + /* Nothing to do, remote proc already started by the secured context. */
>>>>>> + return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static int stm32_rproc_tee_stop(struct rproc *rproc)
>>>>>> +{
>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>> + int err;
>>>>>> +
>>>>>> + stm32_rproc_request_shutdown(rproc);
>>>>>> +
>>>>>> + err = tee_rproc_stop(ddata->trproc);
>>>>>> + if (err)
>>>>>> + return err;
>>>>>> +
>>>>>> + ddata->fw_loaded = false;
>>>>>> +
>>>>>> + return stm32_rproc_release(rproc);
>>>>>> +}
>>>>>> +
>>>>>> static int stm32_rproc_prepare(struct rproc *rproc)
>>>>>> {
>>>>>> struct device *dev = rproc->dev.parent;
>>>>>> @@ -319,7 +410,14 @@ static int stm32_rproc_prepare(struct rproc *rproc)
>>>>>>
>>>>>> static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
>>>>>> {
>>>>>> - if (rproc_elf_load_rsc_table(rproc, fw))
>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>> + int ret;
>>>>>> +
>>>>>> + if (ddata->trproc)
>>>>>> + ret = rproc_tee_get_rsc_table(ddata->trproc);
>>>>>> + else
>>>>>> + ret = rproc_elf_load_rsc_table(rproc, fw);
>>>>>> + if (ret)
>>>>>> dev_warn(&rproc->dev, "no resource table found for this firmware\n");
>>>>>>
>>>>>> return 0;
>>>>>> @@ -693,8 +791,22 @@ 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 = stm32_rproc_tee_start,
>>>>>> + .stop = stm32_rproc_tee_stop,
>>>>>> + .attach = stm32_rproc_tee_attach,
>>>>>> + .kick = stm32_rproc_kick,
>>>>>> + .parse_fw = stm32_rproc_parse_fw,
>>>>>> + .find_loaded_rsc_table = stm32_rproc_tee_elf_find_loaded_rsc_table,
>>>>>> + .get_loaded_rsc_table = stm32_rproc_get_loaded_rsc_table,
>>>>>> + .sanity_check = stm32_rproc_tee_elf_sanity_check,
>>>>>> + .load = stm32_rproc_tee_elf_load,
>>>>>> +};
>>>>>> +
>>>>>> static const struct of_device_id stm32_rproc_match[] = {
>>>>>> - { .compatible = "st,stm32mp1-m4" },
>>>>>> + {.compatible = "st,stm32mp1-m4",},
>>>>>> + {.compatible = "st,stm32mp1-m4-tee",},
>>>>>> {},
>>>>>> };
>>>>>> MODULE_DEVICE_TABLE(of, stm32_rproc_match);
>>>>>> @@ -853,6 +965,7 @@ 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;
>>>>>> int ret;
>>>>>> @@ -861,11 +974,31 @@ static int stm32_rproc_probe(struct platform_device *pdev)
>>>>>> if (ret)
>>>>>> return ret;
>>>>>>
>>>>>> - rproc = 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")) {
>>>>>> + trproc = tee_rproc_register(dev, STM32_MP1_M4_PROC_ID);
>>>>>> + if (IS_ERR(trproc)) {
>>>>>> + dev_err_probe(dev, PTR_ERR(trproc),
>>>>>> + "signed firmware not supported by TEE\n");
>>>>>> + return PTR_ERR(trproc);
>>>>>> + }
>>>>>> + /*
>>>>>> + * Delegate the firmware management to the secure context.
>>>>>> + * The firmware loaded has to be signed.
>>>>>> + */
>>>>>> + dev_info(dev, "Support of signed firmware only\n");
>>>>>
>>>>> Not sure what this adds. Please remove.
>>>>
>>>> This is used to inform the user that only a signed firmware can be loaded, not
>>>> an ELF file.
>>>> I have a patch in my pipe to provide the supported format in the debugfs. In a
>>>> first step, I can suppress this message and we can revisit the issue when I push
>>>> the debugfs proposal.
>>>>
>>>> Thanks,
>>>> Arnaud
>>>>
>>>>>
>>>>>> + }
>>>>>> + rproc = rproc_alloc(dev, np->name,
>>>>>> + trproc ? &st_rproc_tee_ops : &st_rproc_ops,
>>>>>> + NULL, sizeof(*ddata));
>>>>>> + if (!rproc) {
>>>>>> + ret = -ENOMEM;
>>>>>> + goto free_tee;
>>>>>> + }
>>>>>>
>>>>>> ddata = rproc->priv;
>>>>>> + ddata->trproc = trproc;
>>>>>> + if (trproc)
>>>>>> + trproc->rproc = rproc;
>>>>>>
>>>>>> rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
>>>>>>
>>>>>> @@ -916,6 +1049,10 @@ static int stm32_rproc_probe(struct platform_device *pdev)
>>>>>> device_init_wakeup(dev, false);
>>>>>> }
>>>>>> rproc_free(rproc);
>>>>>> +free_tee:
>>>>>> + if (trproc)
>>>>>> + tee_rproc_unregister(trproc);
>>>>>> +
>>>>>> return ret;
>>>>>> }
>>>>>>
>>>>>> @@ -937,6 +1074,8 @@ static void stm32_rproc_remove(struct platform_device *pdev)
>>>>>> device_init_wakeup(dev, false);
>>>>>> }
>>>>>> rproc_free(rproc);
>>>>>> + if (ddata->trproc)
>>>>>> + tee_rproc_unregister(ddata->trproc);
>>>>>> }
>>>>>>
>>>>>> static int stm32_rproc_suspend(struct device *dev)
>>>>>> --
>>>>>> 2.25.1
>>>>>>

2024-02-02 19:54:05

by Mathieu Poirier

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware

On Thu, Feb 01, 2024 at 07:33:35PM +0100, Arnaud POULIQUEN wrote:
>
>
> On 2/1/24 17:02, Mathieu Poirier wrote:
> > On Thu, Feb 01, 2024 at 04:06:37PM +0100, Arnaud POULIQUEN wrote:
> >> hello Mathieu,
> >>
> >> On 1/31/24 19:52, Mathieu Poirier wrote:
> >>> On Tue, Jan 30, 2024 at 10:13:48AM +0100, Arnaud POULIQUEN wrote:
> >>>>
> >>>>
> >>>> On 1/26/24 18:11, Mathieu Poirier wrote:
> >>>>> On Thu, Jan 18, 2024 at 11:04:33AM +0100, Arnaud Pouliquen wrote:
> >>>>>> 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 <[email protected]>
> >>>>>> ---
> >>>>>> V1 to V2 update:
> >>>>>> - remove the select "TEE_REMOTEPROC" in STM32_RPROC config as detected by
> >>>>>> the kernel test robot:
> >>>>>> WARNING: unmet direct dependencies detected for TEE_REMOTEPROC
> >>>>>> Depends on [n]: REMOTEPROC [=y] && OPTEE [=n]
> >>>>>> Selected by [y]:
> >>>>>> - STM32_RPROC [=y] && (ARCH_STM32 || COMPILE_TEST [=y]) && REMOTEPROC [=y]
> >>>>>> - Fix initialized trproc variable in stm32_rproc_probe
> >>>>>> ---
> >>>>>> drivers/remoteproc/stm32_rproc.c | 149 +++++++++++++++++++++++++++++--
> >>>>>> 1 file changed, 144 insertions(+), 5 deletions(-)
> >>>>>>
> >>>>>> diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
> >>>>>> index fcc0001e2657..cf6a21bac945 100644
> >>>>>> --- a/drivers/remoteproc/stm32_rproc.c
> >>>>>> +++ b/drivers/remoteproc/stm32_rproc.c
> >>>>>> @@ -20,6 +20,7 @@
> >>>>>> #include <linux/remoteproc.h>
> >>>>>> #include <linux/reset.h>
> >>>>>> #include <linux/slab.h>
> >>>>>> +#include <linux/tee_remoteproc.h>
> >>>>>> #include <linux/workqueue.h>
> >>>>>>
> >>>>>> #include "remoteproc_internal.h"
> >>>>>> @@ -49,6 +50,9 @@
> >>>>>> #define M4_STATE_STANDBY 4
> >>>>>> #define M4_STATE_CRASH 5
> >>>>>>
> >>>>>> +/* Remote processor unique identifier aligned with the Trusted Execution Environment definitions */
> >>>>>> +#define STM32_MP1_M4_PROC_ID 0
> >>>>>> +
> >>>>>> struct stm32_syscon {
> >>>>>> struct regmap *map;
> >>>>>> u32 reg;
> >>>>>> @@ -90,6 +94,8 @@ struct stm32_rproc {
> >>>>>> struct stm32_mbox mb[MBOX_NB_MBX];
> >>>>>> struct workqueue_struct *workqueue;
> >>>>>> bool hold_boot_smc;
> >>>>>> + bool fw_loaded;
> >>>>>> + struct tee_rproc *trproc;
> >>>>>> void __iomem *rsc_va;
> >>>>>> };
> >>>>>>
> >>>>>> @@ -257,6 +263,91 @@ static int stm32_rproc_release(struct rproc *rproc)
> >>>>>> return err;
> >>>>>> }
> >>>>>>
> >>>>>> +static int stm32_rproc_tee_elf_sanity_check(struct rproc *rproc,
> >>>>>> + const struct firmware *fw)
> >>>>>> +{
> >>>>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>>>> + unsigned int ret = 0;
> >>>>>> +
> >>>>>> + if (rproc->state == RPROC_DETACHED)
> >>>>>> + return 0;
> >>>>>> +
> >>>>>> + ret = tee_rproc_load_fw(ddata->trproc, fw);
> >>>>>> + if (!ret)
> >>>>>> + ddata->fw_loaded = true;
> >>>>>> +
> >>>>>> + return ret;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int stm32_rproc_tee_elf_load(struct rproc *rproc,
> >>>>>> + const struct firmware *fw)
> >>>>>> +{
> >>>>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>>>> + unsigned int ret;
> >>>>>> +
> >>>>>> + /*
> >>>>>> + * This function can be called by remote proc for recovery
> >>>>>> + * without the sanity check. In this case we need to load the firmware
> >>>>>> + * else nothing done here as the firmware has been preloaded for the
> >>>>>> + * sanity check to be able to parse it for the resource table.
> >>>>>> + */
> >>>>>
> >>>>> This comment is very confusing - please consider refactoring.
> >>>>>
> >>>>>> + if (ddata->fw_loaded)
> >>>>>> + return 0;
> >>>>>> +
> >>>>>
> >>>>> I'm not sure about keeping a flag to indicate the status of the loaded firmware.
> >>>>> It is not done for the non-secure method, I don't see why it would be needed for
> >>>>> the secure one.
> >>>>>
> >>>>
> >>>> The difference is on the sanity check.
> >>>> - in rproc_elf_sanity_check we parse the elf file to verify that it is
> >>>> valid.
> >>>> - in stm32_rproc_tee_elf_sanity_check we have to do the same, that means to
> >>>> authenticate it. the authentication is done during the load.
> >>>>
> >>>> So this flag is used to avoid to reload it twice time.
> >>>> refactoring the comment should help to understand this flag
> >>>>
> >>>>
> >>>> An alternative would be to bypass the sanity check. But this lead to same
> >>>> limitation.
> >>>> Before loading the firmware in remoteproc_core, we call rproc_parse_fw() that is
> >>>> used to get the resource table address. To get it from tee we need to
> >>>> authenticate the firmware so load it...
> >>>>
> >>>
> >>> I spent a long time thinking about this patchset. Looking at the code as it
> >>> is now, request_firmware() in rproc_boot() is called even when the TEE is
> >>> responsible for loading the firmware. There should be some conditional code
> >>> that calls either request_firmware() or tee_rproc_load_fw(). The latter should
> >>> also be renamed to tee_rproc_request_firmware() to avoid confusion.
> >>
> >>
> >> The request_firmware() call is needed in both cases to get the image from the
> >> filesystem. The tee_rproc_load_fw() gets, as input, the struct firmware provided
> >> by request_firmware().
> >
> > The cover letter clearly state the secure side is responsible for loading the
> > firmware image but here you're telling me it has to be loaded twice. This is
> > very confusing.
>
> Concerning the call of request_firmware()
>
> By "both cases" I would say that the call of request_firmware() is needed in
> both modes:
> - the ELF firmware is parsed and loaded by linux (legacy)
> - the binary firmware is parsed and loaded by OP-TEE.
>
> The Op-TEE is not able to get the firmware image from the file system.
>
>
> Concerning the call of tee_rproc_load_fw twice time
>
> There are 2 use cases:
>
> - First boot of the remote processor:
>
> 1) The Linux rproc gets the binary firmware image from the file system by
> calling request_firmware(). A copy is stored in memory.

Right. And I think tee_rproc_load_fw() should be called right after
request_firmware() if rproc::tee_rproc_interface is valid. At that point the TEE
app may or may not do the firmware authentication, that is application specific.

> 2) the linux performs a sanity check on the firmware calling
> rproc_fw_sanity_check()
> => from OP-TEE point of view this means to autenticate the firmware
> => let consider in this exemple that we bypass this step
> (ops->sanity_check = NULL)

Ok

>
> 3) the linux rproc call rproc_parse_fw() to get the resource table
> => From OP-TEE point of view the resource table is available only when
> the firmware is loaded

Right, and it should have been loaded already. If it is not then the TEE should
return an error.

> => We need to call tee_rproc_load_fw() to be able then to get the
> address of the resource table.

See my comment above - at this point the TEE should already have the firmware.
As such the only thing left is to get the address of the resource table, which
you already do in rproc_tee_get_rsc_table(). The upper part of that function
should be spun off in a new static function to deal with the TEE API, something
like _rproc_tee_get_rsc_table(). The new function should also be called in
tee_rproc_get_loaded_rsc_table() rather than keeping a cache value in
trproc->rsc_va.

> 4) The Linux rproc calls rproc_handle_resources() to parse the resource table.
> 5) The linux rproc calls rproc_start()
> - load the firrmware calling rproc_load_segments()
> => we don't want to call tee_rproc_load_fw() it a second time

And that is fine if the TEE app has already placed the program segments in
memory.

> - start the firmware calling ops->start()
>
> - Reboot on crash recovery using rproc_boot_recovery()
>
> 1) The Linux rproc gets the binary firmware image from the file system by
> calling request_firmware(). A copy is stored in memory.
> 5) The linux rproc calls rproc_start()
> - load the firrmware calling rproc_load_segments()
> => we have to call tee_rproc_load_fw() to reload the firmware

Loading the firmware in the TEE should be done right after request_firmware()
has been called, the same way it is done in the boot path. If there isn't a
need to reload the TEE firmware than the TEE application should ignore the
request.

> - start the firmware calling ops->start()
>
> In first use case we have to load the firmware on rproc_parse_fw(), in second
> usecase on rproc_load_segments().
>
> This is the point I have tried to solve with the ddata->fw_loaded variable.
>
> >
> > I'm also confused as to why stm32_rproc_tee_elf_sanity_check() is calling
> > tee_rproc_load_fw(). There should be one call to load the firmware and another
> > to perform a sanity check on it. If the sanity check is done at load time by
> > the secure world then ops::sanity_check() is NULL.
>
> Sure, make sense to remove the sanity_check ops
>
> Thanks,
> Arnaud
>
> >
> > Most of what this patchset does makes sense, but some of it needs to be moved
> > around.
> >
> > Thanks,
> > Mathieu
> >
> >>
> >> If we want to integrate in remoteproc_core the solution could probably have to
> >> create the equivalent of the rproc_fw_boot() to load the firmware with an
> >> external method. Here is an example based on a new rproc_ops ( not tested)
> >>
> >> + static int rproc_fw_ext_boot(struct rproc *rproc, const struct firmware *fw)
> >> + {
> >> + struct device *dev = &rproc->dev;
> >> + const char *name = rproc->firmware;
> >> + int ret;
> >> +
> >> +
> >> + dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size);
> >> +
> >> + /* ops to load and start the remoteprocessor */
> >> + ret = rproc->ops->boot(rproc, fw);
> >> + if (ret)
> >> + return ret;
> >> +
> >> + /*
> >> + * if enabling an IOMMU isn't relevant for this rproc, this is
> >> + * just a nop
> >> + */
> >> + ret = rproc_enable_iommu(rproc);
> >> + if (ret) {
> >> + dev_err(dev, "can't enable iommu: %d\n", ret);
> >> + return ret;
> >> + }
> >> +
> >> + /* Prepare rproc for firmware loading if needed */
> >> + ret = rproc_prepare_device(rproc);
> >> + if (ret) {
> >> + dev_err(dev, "can't prepare rproc %s: %d\n", rproc->name, ret);
> >> + goto disable_iommu;
> >> + }
> >> +
> >> + ret = rproc_set_rsc_table(rproc);
> >> + if (ret) {
> >> + dev_err(dev, "can't load resource table: %d\n", ret);
> >> + goto unprepare_device;
> >> + }
> >> +
> >> +
> >> + /* reset max_notifyid */
> >> + rproc->max_notifyid = -1;
> >> +
> >> + /* reset handled vdev */
> >> + rproc->nb_vdev = 0;
> >> +
> >> + /* handle fw resources which are required to boot rproc */
> >> + ret = rproc_handle_resources(rproc, rproc_loading_handlers);
> >> + if (ret) {
> >> + dev_err(dev, "Failed to process resources: %d\n", ret);
> >> + goto clean_up_resources;
> >> + }
> >> +
> >> + /* Allocate carveout resources associated to rproc */
> >> + ret = rproc_alloc_registered_carveouts(rproc);
> >> + if (ret) {
> >> + dev_err(dev, "Failed to allocate associated carveouts: %d\n",
> >> + ret);
> >> + goto clean_up_resources;
> >> + }
> >> +
> >> + return 0;
> >> +
> >> + clean_up_resources:
> >> + rproc_resource_cleanup(rproc);
> >> + unprepare_rproc:
> >> + /* release HW resources if needed */
> >> + rproc_unprepare_device(rproc);
> >> + disable_iommu:
> >> + rproc_disable_iommu(rproc);
> >> + return ret;
> >> + }
> >>
> >>
> >> int rproc_boot(struct rproc *rproc)
> >> {
> >> [...]
> >>
> >> - ret = rproc_fw_boot(rproc, firmware_p);
> >> + if(rproc->ops->boot)
> >> + ret = rproc_fw_ext_boot(rproc, firmware_p);
> >> + else
> >> + ret = rproc_fw_boot(rproc, firmware_p);
> >>
> >> Another advantage of this solution is that it opens the framework to other
> >> formats. For instance it could be a way to support dtb format requested in [RFC]
> >> Passing device-tree to remoteproc [1].
> >>
> >> [1]
> >> https://lore.kernel.org/linux-remoteproc/[email protected]/T/#t
> >>
> >> Thanks,
> >> Arnaud
> >>
> >>
> >>
> >>>
> >>> I touched on that before but please rename rproc_tee_get_rsc_table() to
> >>> rproc_tee_elf_load_rsc_table(). I also suggest to introduce a new function,
> >>> rproc_tee_get_loaded_rsc_table() that would be called from
> >>> rproc_tee_elf_load_rsc_table(). That way we don't need trproc->rsc_va.
> >>>
> >>> I also think tee_rproc should be renamed to "rproc_tee_interface" and folded
> >>> under struct rproc.
> >>>
> >>> With the above most of the problems with the current implementation should
> >>> naturally go away.
> >>>
> >>> Thanks,
> >>> Mathieu
> >>>
> >>>>
> >>>>>> + ret = tee_rproc_load_fw(ddata->trproc, fw);
> >>>>>> + if (ret)
> >>>>>> + return ret;
> >>>>>> + ddata->fw_loaded = true;
> >>>>>> +
> >>>>>> + /* Update the resource table parameters. */
> >>>>>> + if (rproc_tee_get_rsc_table(ddata->trproc)) {
> >>>>>> + /* No resource table: reset the related fields. */
> >>>>>> + rproc->cached_table = NULL;
> >>>>>> + rproc->table_ptr = NULL;
> >>>>>> + rproc->table_sz = 0;
> >>>>>> + }
> >>>>>> +
> >>>>>> + return 0;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static struct resource_table *
> >>>>>> +stm32_rproc_tee_elf_find_loaded_rsc_table(struct rproc *rproc,
> >>>>>> + const struct firmware *fw)
> >>>>>> +{
> >>>>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>>>> +
> >>>>>> + return tee_rproc_get_loaded_rsc_table(ddata->trproc);
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int stm32_rproc_tee_start(struct rproc *rproc)
> >>>>>> +{
> >>>>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>>>> +
> >>>>>> + return tee_rproc_start(ddata->trproc);
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int stm32_rproc_tee_attach(struct rproc *rproc)
> >>>>>> +{
> >>>>>> + /* Nothing to do, remote proc already started by the secured context. */
> >>>>>> + return 0;
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int stm32_rproc_tee_stop(struct rproc *rproc)
> >>>>>> +{
> >>>>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>>>> + int err;
> >>>>>> +
> >>>>>> + stm32_rproc_request_shutdown(rproc);
> >>>>>> +
> >>>>>> + err = tee_rproc_stop(ddata->trproc);
> >>>>>> + if (err)
> >>>>>> + return err;
> >>>>>> +
> >>>>>> + ddata->fw_loaded = false;
> >>>>>> +
> >>>>>> + return stm32_rproc_release(rproc);
> >>>>>> +}
> >>>>>> +
> >>>>>> static int stm32_rproc_prepare(struct rproc *rproc)
> >>>>>> {
> >>>>>> struct device *dev = rproc->dev.parent;
> >>>>>> @@ -319,7 +410,14 @@ static int stm32_rproc_prepare(struct rproc *rproc)
> >>>>>>
> >>>>>> static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
> >>>>>> {
> >>>>>> - if (rproc_elf_load_rsc_table(rproc, fw))
> >>>>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>>>> + int ret;
> >>>>>> +
> >>>>>> + if (ddata->trproc)
> >>>>>> + ret = rproc_tee_get_rsc_table(ddata->trproc);
> >>>>>> + else
> >>>>>> + ret = rproc_elf_load_rsc_table(rproc, fw);
> >>>>>> + if (ret)
> >>>>>> dev_warn(&rproc->dev, "no resource table found for this firmware\n");
> >>>>>>
> >>>>>> return 0;
> >>>>>> @@ -693,8 +791,22 @@ 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 = stm32_rproc_tee_start,
> >>>>>> + .stop = stm32_rproc_tee_stop,
> >>>>>> + .attach = stm32_rproc_tee_attach,
> >>>>>> + .kick = stm32_rproc_kick,
> >>>>>> + .parse_fw = stm32_rproc_parse_fw,
> >>>>>> + .find_loaded_rsc_table = stm32_rproc_tee_elf_find_loaded_rsc_table,
> >>>>>> + .get_loaded_rsc_table = stm32_rproc_get_loaded_rsc_table,
> >>>>>> + .sanity_check = stm32_rproc_tee_elf_sanity_check,
> >>>>>> + .load = stm32_rproc_tee_elf_load,
> >>>>>> +};
> >>>>>> +
> >>>>>> static const struct of_device_id stm32_rproc_match[] = {
> >>>>>> - { .compatible = "st,stm32mp1-m4" },
> >>>>>> + {.compatible = "st,stm32mp1-m4",},
> >>>>>> + {.compatible = "st,stm32mp1-m4-tee",},
> >>>>>> {},
> >>>>>> };
> >>>>>> MODULE_DEVICE_TABLE(of, stm32_rproc_match);
> >>>>>> @@ -853,6 +965,7 @@ 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;
> >>>>>> int ret;
> >>>>>> @@ -861,11 +974,31 @@ static int stm32_rproc_probe(struct platform_device *pdev)
> >>>>>> if (ret)
> >>>>>> return ret;
> >>>>>>
> >>>>>> - rproc = 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")) {
> >>>>>> + trproc = tee_rproc_register(dev, STM32_MP1_M4_PROC_ID);
> >>>>>> + if (IS_ERR(trproc)) {
> >>>>>> + dev_err_probe(dev, PTR_ERR(trproc),
> >>>>>> + "signed firmware not supported by TEE\n");
> >>>>>> + return PTR_ERR(trproc);
> >>>>>> + }
> >>>>>> + /*
> >>>>>> + * Delegate the firmware management to the secure context.
> >>>>>> + * The firmware loaded has to be signed.
> >>>>>> + */
> >>>>>> + dev_info(dev, "Support of signed firmware only\n");
> >>>>>
> >>>>> Not sure what this adds. Please remove.
> >>>>
> >>>> This is used to inform the user that only a signed firmware can be loaded, not
> >>>> an ELF file.
> >>>> I have a patch in my pipe to provide the supported format in the debugfs. In a
> >>>> first step, I can suppress this message and we can revisit the issue when I push
> >>>> the debugfs proposal.
> >>>>
> >>>> Thanks,
> >>>> Arnaud
> >>>>
> >>>>>
> >>>>>> + }
> >>>>>> + rproc = rproc_alloc(dev, np->name,
> >>>>>> + trproc ? &st_rproc_tee_ops : &st_rproc_ops,
> >>>>>> + NULL, sizeof(*ddata));
> >>>>>> + if (!rproc) {
> >>>>>> + ret = -ENOMEM;
> >>>>>> + goto free_tee;
> >>>>>> + }
> >>>>>>
> >>>>>> ddata = rproc->priv;
> >>>>>> + ddata->trproc = trproc;
> >>>>>> + if (trproc)
> >>>>>> + trproc->rproc = rproc;
> >>>>>>
> >>>>>> rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
> >>>>>>
> >>>>>> @@ -916,6 +1049,10 @@ static int stm32_rproc_probe(struct platform_device *pdev)
> >>>>>> device_init_wakeup(dev, false);
> >>>>>> }
> >>>>>> rproc_free(rproc);
> >>>>>> +free_tee:
> >>>>>> + if (trproc)
> >>>>>> + tee_rproc_unregister(trproc);
> >>>>>> +
> >>>>>> return ret;
> >>>>>> }
> >>>>>>
> >>>>>> @@ -937,6 +1074,8 @@ static void stm32_rproc_remove(struct platform_device *pdev)
> >>>>>> device_init_wakeup(dev, false);
> >>>>>> }
> >>>>>> rproc_free(rproc);
> >>>>>> + if (ddata->trproc)
> >>>>>> + tee_rproc_unregister(ddata->trproc);
> >>>>>> }
> >>>>>>
> >>>>>> static int stm32_rproc_suspend(struct device *dev)
> >>>>>> --
> >>>>>> 2.25.1
> >>>>>>

2024-02-05 09:22:00

by Arnaud POULIQUEN

[permalink] [raw]
Subject: Re: [PATCH v2 4/4] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware



On 2/2/24 20:53, Mathieu Poirier wrote:
> On Thu, Feb 01, 2024 at 07:33:35PM +0100, Arnaud POULIQUEN wrote:
>>
>>
>> On 2/1/24 17:02, Mathieu Poirier wrote:
>>> On Thu, Feb 01, 2024 at 04:06:37PM +0100, Arnaud POULIQUEN wrote:
>>>> hello Mathieu,
>>>>
>>>> On 1/31/24 19:52, Mathieu Poirier wrote:
>>>>> On Tue, Jan 30, 2024 at 10:13:48AM +0100, Arnaud POULIQUEN wrote:
>>>>>>
>>>>>>
>>>>>> On 1/26/24 18:11, Mathieu Poirier wrote:
>>>>>>> On Thu, Jan 18, 2024 at 11:04:33AM +0100, Arnaud Pouliquen wrote:
>>>>>>>> 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 <[email protected]>
>>>>>>>> ---
>>>>>>>> V1 to V2 update:
>>>>>>>> - remove the select "TEE_REMOTEPROC" in STM32_RPROC config as detected by
>>>>>>>> the kernel test robot:
>>>>>>>> WARNING: unmet direct dependencies detected for TEE_REMOTEPROC
>>>>>>>> Depends on [n]: REMOTEPROC [=y] && OPTEE [=n]
>>>>>>>> Selected by [y]:
>>>>>>>> - STM32_RPROC [=y] && (ARCH_STM32 || COMPILE_TEST [=y]) && REMOTEPROC [=y]
>>>>>>>> - Fix initialized trproc variable in stm32_rproc_probe
>>>>>>>> ---
>>>>>>>> drivers/remoteproc/stm32_rproc.c | 149 +++++++++++++++++++++++++++++--
>>>>>>>> 1 file changed, 144 insertions(+), 5 deletions(-)
>>>>>>>>
>>>>>>>> diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
>>>>>>>> index fcc0001e2657..cf6a21bac945 100644
>>>>>>>> --- a/drivers/remoteproc/stm32_rproc.c
>>>>>>>> +++ b/drivers/remoteproc/stm32_rproc.c
>>>>>>>> @@ -20,6 +20,7 @@
>>>>>>>> #include <linux/remoteproc.h>
>>>>>>>> #include <linux/reset.h>
>>>>>>>> #include <linux/slab.h>
>>>>>>>> +#include <linux/tee_remoteproc.h>
>>>>>>>> #include <linux/workqueue.h>
>>>>>>>>
>>>>>>>> #include "remoteproc_internal.h"
>>>>>>>> @@ -49,6 +50,9 @@
>>>>>>>> #define M4_STATE_STANDBY 4
>>>>>>>> #define M4_STATE_CRASH 5
>>>>>>>>
>>>>>>>> +/* Remote processor unique identifier aligned with the Trusted Execution Environment definitions */
>>>>>>>> +#define STM32_MP1_M4_PROC_ID 0
>>>>>>>> +
>>>>>>>> struct stm32_syscon {
>>>>>>>> struct regmap *map;
>>>>>>>> u32 reg;
>>>>>>>> @@ -90,6 +94,8 @@ struct stm32_rproc {
>>>>>>>> struct stm32_mbox mb[MBOX_NB_MBX];
>>>>>>>> struct workqueue_struct *workqueue;
>>>>>>>> bool hold_boot_smc;
>>>>>>>> + bool fw_loaded;
>>>>>>>> + struct tee_rproc *trproc;
>>>>>>>> void __iomem *rsc_va;
>>>>>>>> };
>>>>>>>>
>>>>>>>> @@ -257,6 +263,91 @@ static int stm32_rproc_release(struct rproc *rproc)
>>>>>>>> return err;
>>>>>>>> }
>>>>>>>>
>>>>>>>> +static int stm32_rproc_tee_elf_sanity_check(struct rproc *rproc,
>>>>>>>> + const struct firmware *fw)
>>>>>>>> +{
>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>>>> + unsigned int ret = 0;
>>>>>>>> +
>>>>>>>> + if (rproc->state == RPROC_DETACHED)
>>>>>>>> + return 0;
>>>>>>>> +
>>>>>>>> + ret = tee_rproc_load_fw(ddata->trproc, fw);
>>>>>>>> + if (!ret)
>>>>>>>> + ddata->fw_loaded = true;
>>>>>>>> +
>>>>>>>> + return ret;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static int stm32_rproc_tee_elf_load(struct rproc *rproc,
>>>>>>>> + const struct firmware *fw)
>>>>>>>> +{
>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>>>> + unsigned int ret;
>>>>>>>> +
>>>>>>>> + /*
>>>>>>>> + * This function can be called by remote proc for recovery
>>>>>>>> + * without the sanity check. In this case we need to load the firmware
>>>>>>>> + * else nothing done here as the firmware has been preloaded for the
>>>>>>>> + * sanity check to be able to parse it for the resource table.
>>>>>>>> + */
>>>>>>>
>>>>>>> This comment is very confusing - please consider refactoring.
>>>>>>>
>>>>>>>> + if (ddata->fw_loaded)
>>>>>>>> + return 0;
>>>>>>>> +
>>>>>>>
>>>>>>> I'm not sure about keeping a flag to indicate the status of the loaded firmware.
>>>>>>> It is not done for the non-secure method, I don't see why it would be needed for
>>>>>>> the secure one.
>>>>>>>
>>>>>>
>>>>>> The difference is on the sanity check.
>>>>>> - in rproc_elf_sanity_check we parse the elf file to verify that it is
>>>>>> valid.
>>>>>> - in stm32_rproc_tee_elf_sanity_check we have to do the same, that means to
>>>>>> authenticate it. the authentication is done during the load.
>>>>>>
>>>>>> So this flag is used to avoid to reload it twice time.
>>>>>> refactoring the comment should help to understand this flag
>>>>>>
>>>>>>
>>>>>> An alternative would be to bypass the sanity check. But this lead to same
>>>>>> limitation.
>>>>>> Before loading the firmware in remoteproc_core, we call rproc_parse_fw() that is
>>>>>> used to get the resource table address. To get it from tee we need to
>>>>>> authenticate the firmware so load it...
>>>>>>
>>>>>
>>>>> I spent a long time thinking about this patchset. Looking at the code as it
>>>>> is now, request_firmware() in rproc_boot() is called even when the TEE is
>>>>> responsible for loading the firmware. There should be some conditional code
>>>>> that calls either request_firmware() or tee_rproc_load_fw(). The latter should
>>>>> also be renamed to tee_rproc_request_firmware() to avoid confusion.
>>>>
>>>>
>>>> The request_firmware() call is needed in both cases to get the image from the
>>>> filesystem. The tee_rproc_load_fw() gets, as input, the struct firmware provided
>>>> by request_firmware().
>>>
>>> The cover letter clearly state the secure side is responsible for loading the
>>> firmware image but here you're telling me it has to be loaded twice. This is
>>> very confusing.
>>
>> Concerning the call of request_firmware()
>>
>> By "both cases" I would say that the call of request_firmware() is needed in
>> both modes:
>> - the ELF firmware is parsed and loaded by linux (legacy)
>> - the binary firmware is parsed and loaded by OP-TEE.
>>
>> The Op-TEE is not able to get the firmware image from the file system.
>>
>>
>> Concerning the call of tee_rproc_load_fw twice time
>>
>> There are 2 use cases:
>>
>> - First boot of the remote processor:
>>
>> 1) The Linux rproc gets the binary firmware image from the file system by
>> calling request_firmware(). A copy is stored in memory.
>
> Right. And I think tee_rproc_load_fw() should be called right after
> request_firmware() if rproc::tee_rproc_interface is valid. At that point the TEE
> app may or may not do the firmware authentication, that is application specific.
>
>> 2) the linux performs a sanity check on the firmware calling
>> rproc_fw_sanity_check()
>> => from OP-TEE point of view this means to autenticate the firmware
>> => let consider in this exemple that we bypass this step
>> (ops->sanity_check = NULL)
>
> Ok
>
>>
>> 3) the linux rproc call rproc_parse_fw() to get the resource table
>> => From OP-TEE point of view the resource table is available only when
>> the firmware is loaded
>
> Right, and it should have been loaded already. If it is not then the TEE should
> return an error.
>
>> => We need to call tee_rproc_load_fw() to be able then to get the
>> address of the resource table.
>
> See my comment above - at this point the TEE should already have the firmware.
> As such the only thing left is to get the address of the resource table, which
> you already do in rproc_tee_get_rsc_table(). The upper part of that function
> should be spun off in a new static function to deal with the TEE API, something
> like _rproc_tee_get_rsc_table(). The new function should also be called in
> tee_rproc_get_loaded_rsc_table() rather than keeping a cache value in
> trproc->rsc_va.
>
>> 4) The Linux rproc calls rproc_handle_resources() to parse the resource table.
>> 5) The linux rproc calls rproc_start()
>> - load the firrmware calling rproc_load_segments()
>> => we don't want to call tee_rproc_load_fw() it a second time
>
> And that is fine if the TEE app has already placed the program segments in
> memory.
>
>> - start the firmware calling ops->start()
>>
>> - Reboot on crash recovery using rproc_boot_recovery()
>>
>> 1) The Linux rproc gets the binary firmware image from the file system by
>> calling request_firmware(). A copy is stored in memory.
>> 5) The linux rproc calls rproc_start()
>> - load the firrmware calling rproc_load_segments()
>> => we have to call tee_rproc_load_fw() to reload the firmware
>
> Loading the firmware in the TEE should be done right after request_firmware()
> has been called, the same way it is done in the boot path. If there isn't a
> need to reload the TEE firmware than the TEE application should ignore the
> request.

I need to prototype to verify this proposal.
I will come back with a V3.

Thank you for the advice and review!

Regard,
Arnaud

>
>> - start the firmware calling ops->start()
>>
>> In first use case we have to load the firmware on rproc_parse_fw(), in second
>> usecase on rproc_load_segments().
>>
>> This is the point I have tried to solve with the ddata->fw_loaded variable.
>>
>>>
>>> I'm also confused as to why stm32_rproc_tee_elf_sanity_check() is calling
>>> tee_rproc_load_fw(). There should be one call to load the firmware and another
>>> to perform a sanity check on it. If the sanity check is done at load time by
>>> the secure world then ops::sanity_check() is NULL.
>>
>> Sure, make sense to remove the sanity_check ops
>>
>> Thanks,
>> Arnaud
>>
>>>
>>> Most of what this patchset does makes sense, but some of it needs to be moved
>>> around.
>>>
>>> Thanks,
>>> Mathieu
>>>
>>>>
>>>> If we want to integrate in remoteproc_core the solution could probably have to
>>>> create the equivalent of the rproc_fw_boot() to load the firmware with an
>>>> external method. Here is an example based on a new rproc_ops ( not tested)
>>>>
>>>> + static int rproc_fw_ext_boot(struct rproc *rproc, const struct firmware *fw)
>>>> + {
>>>> + struct device *dev = &rproc->dev;
>>>> + const char *name = rproc->firmware;
>>>> + int ret;
>>>> +
>>>> +
>>>> + dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size);
>>>> +
>>>> + /* ops to load and start the remoteprocessor */
>>>> + ret = rproc->ops->boot(rproc, fw);
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> + /*
>>>> + * if enabling an IOMMU isn't relevant for this rproc, this is
>>>> + * just a nop
>>>> + */
>>>> + ret = rproc_enable_iommu(rproc);
>>>> + if (ret) {
>>>> + dev_err(dev, "can't enable iommu: %d\n", ret);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + /* Prepare rproc for firmware loading if needed */
>>>> + ret = rproc_prepare_device(rproc);
>>>> + if (ret) {
>>>> + dev_err(dev, "can't prepare rproc %s: %d\n", rproc->name, ret);
>>>> + goto disable_iommu;
>>>> + }
>>>> +
>>>> + ret = rproc_set_rsc_table(rproc);
>>>> + if (ret) {
>>>> + dev_err(dev, "can't load resource table: %d\n", ret);
>>>> + goto unprepare_device;
>>>> + }
>>>> +
>>>> +
>>>> + /* reset max_notifyid */
>>>> + rproc->max_notifyid = -1;
>>>> +
>>>> + /* reset handled vdev */
>>>> + rproc->nb_vdev = 0;
>>>> +
>>>> + /* handle fw resources which are required to boot rproc */
>>>> + ret = rproc_handle_resources(rproc, rproc_loading_handlers);
>>>> + if (ret) {
>>>> + dev_err(dev, "Failed to process resources: %d\n", ret);
>>>> + goto clean_up_resources;
>>>> + }
>>>> +
>>>> + /* Allocate carveout resources associated to rproc */
>>>> + ret = rproc_alloc_registered_carveouts(rproc);
>>>> + if (ret) {
>>>> + dev_err(dev, "Failed to allocate associated carveouts: %d\n",
>>>> + ret);
>>>> + goto clean_up_resources;
>>>> + }
>>>> +
>>>> + return 0;
>>>> +
>>>> + clean_up_resources:
>>>> + rproc_resource_cleanup(rproc);
>>>> + unprepare_rproc:
>>>> + /* release HW resources if needed */
>>>> + rproc_unprepare_device(rproc);
>>>> + disable_iommu:
>>>> + rproc_disable_iommu(rproc);
>>>> + return ret;
>>>> + }
>>>>
>>>>
>>>> int rproc_boot(struct rproc *rproc)
>>>> {
>>>> [...]
>>>>
>>>> - ret = rproc_fw_boot(rproc, firmware_p);
>>>> + if(rproc->ops->boot)
>>>> + ret = rproc_fw_ext_boot(rproc, firmware_p);
>>>> + else
>>>> + ret = rproc_fw_boot(rproc, firmware_p);
>>>>
>>>> Another advantage of this solution is that it opens the framework to other
>>>> formats. For instance it could be a way to support dtb format requested in [RFC]
>>>> Passing device-tree to remoteproc [1].
>>>>
>>>> [1]
>>>> https://lore.kernel.org/linux-remoteproc/[email protected]/T/#t
>>>>
>>>> Thanks,
>>>> Arnaud
>>>>
>>>>
>>>>
>>>>>
>>>>> I touched on that before but please rename rproc_tee_get_rsc_table() to
>>>>> rproc_tee_elf_load_rsc_table(). I also suggest to introduce a new function,
>>>>> rproc_tee_get_loaded_rsc_table() that would be called from
>>>>> rproc_tee_elf_load_rsc_table(). That way we don't need trproc->rsc_va.
>>>>>
>>>>> I also think tee_rproc should be renamed to "rproc_tee_interface" and folded
>>>>> under struct rproc.
>>>>>
>>>>> With the above most of the problems with the current implementation should
>>>>> naturally go away.
>>>>>
>>>>> Thanks,
>>>>> Mathieu
>>>>>
>>>>>>
>>>>>>>> + ret = tee_rproc_load_fw(ddata->trproc, fw);
>>>>>>>> + if (ret)
>>>>>>>> + return ret;
>>>>>>>> + ddata->fw_loaded = true;
>>>>>>>> +
>>>>>>>> + /* Update the resource table parameters. */
>>>>>>>> + if (rproc_tee_get_rsc_table(ddata->trproc)) {
>>>>>>>> + /* No resource table: reset the related fields. */
>>>>>>>> + rproc->cached_table = NULL;
>>>>>>>> + rproc->table_ptr = NULL;
>>>>>>>> + rproc->table_sz = 0;
>>>>>>>> + }
>>>>>>>> +
>>>>>>>> + return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static struct resource_table *
>>>>>>>> +stm32_rproc_tee_elf_find_loaded_rsc_table(struct rproc *rproc,
>>>>>>>> + const struct firmware *fw)
>>>>>>>> +{
>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>>>> +
>>>>>>>> + return tee_rproc_get_loaded_rsc_table(ddata->trproc);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static int stm32_rproc_tee_start(struct rproc *rproc)
>>>>>>>> +{
>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>>>> +
>>>>>>>> + return tee_rproc_start(ddata->trproc);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static int stm32_rproc_tee_attach(struct rproc *rproc)
>>>>>>>> +{
>>>>>>>> + /* Nothing to do, remote proc already started by the secured context. */
>>>>>>>> + return 0;
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static int stm32_rproc_tee_stop(struct rproc *rproc)
>>>>>>>> +{
>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>>>> + int err;
>>>>>>>> +
>>>>>>>> + stm32_rproc_request_shutdown(rproc);
>>>>>>>> +
>>>>>>>> + err = tee_rproc_stop(ddata->trproc);
>>>>>>>> + if (err)
>>>>>>>> + return err;
>>>>>>>> +
>>>>>>>> + ddata->fw_loaded = false;
>>>>>>>> +
>>>>>>>> + return stm32_rproc_release(rproc);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> static int stm32_rproc_prepare(struct rproc *rproc)
>>>>>>>> {
>>>>>>>> struct device *dev = rproc->dev.parent;
>>>>>>>> @@ -319,7 +410,14 @@ static int stm32_rproc_prepare(struct rproc *rproc)
>>>>>>>>
>>>>>>>> static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
>>>>>>>> {
>>>>>>>> - if (rproc_elf_load_rsc_table(rproc, fw))
>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>>>> + int ret;
>>>>>>>> +
>>>>>>>> + if (ddata->trproc)
>>>>>>>> + ret = rproc_tee_get_rsc_table(ddata->trproc);
>>>>>>>> + else
>>>>>>>> + ret = rproc_elf_load_rsc_table(rproc, fw);
>>>>>>>> + if (ret)
>>>>>>>> dev_warn(&rproc->dev, "no resource table found for this firmware\n");
>>>>>>>>
>>>>>>>> return 0;
>>>>>>>> @@ -693,8 +791,22 @@ 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 = stm32_rproc_tee_start,
>>>>>>>> + .stop = stm32_rproc_tee_stop,
>>>>>>>> + .attach = stm32_rproc_tee_attach,
>>>>>>>> + .kick = stm32_rproc_kick,
>>>>>>>> + .parse_fw = stm32_rproc_parse_fw,
>>>>>>>> + .find_loaded_rsc_table = stm32_rproc_tee_elf_find_loaded_rsc_table,
>>>>>>>> + .get_loaded_rsc_table = stm32_rproc_get_loaded_rsc_table,
>>>>>>>> + .sanity_check = stm32_rproc_tee_elf_sanity_check,
>>>>>>>> + .load = stm32_rproc_tee_elf_load,
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> static const struct of_device_id stm32_rproc_match[] = {
>>>>>>>> - { .compatible = "st,stm32mp1-m4" },
>>>>>>>> + {.compatible = "st,stm32mp1-m4",},
>>>>>>>> + {.compatible = "st,stm32mp1-m4-tee",},
>>>>>>>> {},
>>>>>>>> };
>>>>>>>> MODULE_DEVICE_TABLE(of, stm32_rproc_match);
>>>>>>>> @@ -853,6 +965,7 @@ 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;
>>>>>>>> int ret;
>>>>>>>> @@ -861,11 +974,31 @@ static int stm32_rproc_probe(struct platform_device *pdev)
>>>>>>>> if (ret)
>>>>>>>> return ret;
>>>>>>>>
>>>>>>>> - rproc = 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")) {
>>>>>>>> + trproc = tee_rproc_register(dev, STM32_MP1_M4_PROC_ID);
>>>>>>>> + if (IS_ERR(trproc)) {
>>>>>>>> + dev_err_probe(dev, PTR_ERR(trproc),
>>>>>>>> + "signed firmware not supported by TEE\n");
>>>>>>>> + return PTR_ERR(trproc);
>>>>>>>> + }
>>>>>>>> + /*
>>>>>>>> + * Delegate the firmware management to the secure context.
>>>>>>>> + * The firmware loaded has to be signed.
>>>>>>>> + */
>>>>>>>> + dev_info(dev, "Support of signed firmware only\n");
>>>>>>>
>>>>>>> Not sure what this adds. Please remove.
>>>>>>
>>>>>> This is used to inform the user that only a signed firmware can be loaded, not
>>>>>> an ELF file.
>>>>>> I have a patch in my pipe to provide the supported format in the debugfs. In a
>>>>>> first step, I can suppress this message and we can revisit the issue when I push
>>>>>> the debugfs proposal.
>>>>>>
>>>>>> Thanks,
>>>>>> Arnaud
>>>>>>
>>>>>>>
>>>>>>>> + }
>>>>>>>> + rproc = rproc_alloc(dev, np->name,
>>>>>>>> + trproc ? &st_rproc_tee_ops : &st_rproc_ops,
>>>>>>>> + NULL, sizeof(*ddata));
>>>>>>>> + if (!rproc) {
>>>>>>>> + ret = -ENOMEM;
>>>>>>>> + goto free_tee;
>>>>>>>> + }
>>>>>>>>
>>>>>>>> ddata = rproc->priv;
>>>>>>>> + ddata->trproc = trproc;
>>>>>>>> + if (trproc)
>>>>>>>> + trproc->rproc = rproc;
>>>>>>>>
>>>>>>>> rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
>>>>>>>>
>>>>>>>> @@ -916,6 +1049,10 @@ static int stm32_rproc_probe(struct platform_device *pdev)
>>>>>>>> device_init_wakeup(dev, false);
>>>>>>>> }
>>>>>>>> rproc_free(rproc);
>>>>>>>> +free_tee:
>>>>>>>> + if (trproc)
>>>>>>>> + tee_rproc_unregister(trproc);
>>>>>>>> +
>>>>>>>> return ret;
>>>>>>>> }
>>>>>>>>
>>>>>>>> @@ -937,6 +1074,8 @@ static void stm32_rproc_remove(struct platform_device *pdev)
>>>>>>>> device_init_wakeup(dev, false);
>>>>>>>> }
>>>>>>>> rproc_free(rproc);
>>>>>>>> + if (ddata->trproc)
>>>>>>>> + tee_rproc_unregister(ddata->trproc);
>>>>>>>> }
>>>>>>>>
>>>>>>>> static int stm32_rproc_suspend(struct device *dev)
>>>>>>>> --
>>>>>>>> 2.25.1
>>>>>>>>

2024-02-13 15:40:45

by Arnaud POULIQUEN

[permalink] [raw]
Subject: Re: [PATCH v2 2/4] dt-bindings: remoteproc: Add compatibility for TEE support

Hello Rob,

On 1/30/24 18:51, Rob Herring wrote:
> On Thu, Jan 18, 2024 at 11:04:31AM +0100, Arnaud Pouliquen wrote:
>> 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-tree:
>> - In OP-TEE, a node is defined in the device tree with the
>> st,stm32mp1-m4-tee to support signed remoteproc firmware.
>> Based on DT properties, OP-TEE authenticates, loads, starts, and stops
>> the firmware.
>> - On Linux, when the compatibility is set, the Cortex-M resets should not
>> be declared in the device tree.
>>
>> Signed-off-by: Arnaud Pouliquen <[email protected]>
>> ---
>> V1 to V2 updates
>> - update "st,stm32mp1-m4" compatible description to generalize
>> - remove the 'reset-names' requirement in one conditional branch, as the
>> property is already part of the condition test.
>> ---
>> .../bindings/remoteproc/st,stm32-rproc.yaml | 52 +++++++++++++++----
>> 1 file changed, 43 insertions(+), 9 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml
>> index 370af61d8f28..6af821b15736 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:
>> @@ -142,21 +147,40 @@ 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
>
> Note that this is true when 'reset-names' is not present. If that is not
> desired, then you need 'required: [reset-names]'. Not really a new issue
> though.
>

Yes that corresponds to my expectation, for compatibility with legacy DT.
If the hold_boot reset was not used, reset-names was not mandatory
I will add the 'required: [reset-names]' in the else

Thanks,
Arnaud

>> + then:
>> + required:
>> + - st,syscfg-holdboot
>> + - resets
>> + else:
>> + properties:
>> + st,syscfg-holdboot: false
>> + required:
>> + - resets
>
> 'resets' is always required within the outer 'then' schema, so you can
> move this up a level.
>
>> +
>> + - if:
>> + properties:
>> + compatible:
>> + contains:
>> + const: st,stm32mp1-m4-tee
>> then:
>> - required:
>> - - st,syscfg-holdboot
>> - else:
>> properties:
>> st,syscfg-holdboot: false
>> + reset-names: false
>> + resets: false
>>
>> additionalProperties: false
>>
>> @@ -188,5 +212,15 @@ 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,syscfg-rsc-tbl = <&tamp 0x144 0xFFFFFFFF>;
>> + st,syscfg-m4-state = <&tamp 0x148 0xFFFFFFFF>;
>> + };
>>
>> ...
>> --
>> 2.25.1
>>

2024-02-13 15:48:51

by Arnaud POULIQUEN

[permalink] [raw]
Subject: Re: [Linux-stm32] [PATCH v2 4/4] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware

Hello Mathieu,

On 2/5/24 10:13, Arnaud POULIQUEN wrote:
>
>
> On 2/2/24 20:53, Mathieu Poirier wrote:
>> On Thu, Feb 01, 2024 at 07:33:35PM +0100, Arnaud POULIQUEN wrote:
>>>
>>>
>>> On 2/1/24 17:02, Mathieu Poirier wrote:
>>>> On Thu, Feb 01, 2024 at 04:06:37PM +0100, Arnaud POULIQUEN wrote:
>>>>> hello Mathieu,
>>>>>
>>>>> On 1/31/24 19:52, Mathieu Poirier wrote:
>>>>>> On Tue, Jan 30, 2024 at 10:13:48AM +0100, Arnaud POULIQUEN wrote:
>>>>>>>
>>>>>>>
>>>>>>> On 1/26/24 18:11, Mathieu Poirier wrote:
>>>>>>>> On Thu, Jan 18, 2024 at 11:04:33AM +0100, Arnaud Pouliquen wrote:
>>>>>>>>> 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 <[email protected]>
>>>>>>>>> ---
>>>>>>>>> V1 to V2 update:
>>>>>>>>> - remove the select "TEE_REMOTEPROC" in STM32_RPROC config as detected by
>>>>>>>>> the kernel test robot:
>>>>>>>>> WARNING: unmet direct dependencies detected for TEE_REMOTEPROC
>>>>>>>>> Depends on [n]: REMOTEPROC [=y] && OPTEE [=n]
>>>>>>>>> Selected by [y]:
>>>>>>>>> - STM32_RPROC [=y] && (ARCH_STM32 || COMPILE_TEST [=y]) && REMOTEPROC [=y]
>>>>>>>>> - Fix initialized trproc variable in stm32_rproc_probe
>>>>>>>>> ---
>>>>>>>>> drivers/remoteproc/stm32_rproc.c | 149 +++++++++++++++++++++++++++++--
>>>>>>>>> 1 file changed, 144 insertions(+), 5 deletions(-)
>>>>>>>>>
>>>>>>>>> diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
>>>>>>>>> index fcc0001e2657..cf6a21bac945 100644
>>>>>>>>> --- a/drivers/remoteproc/stm32_rproc.c
>>>>>>>>> +++ b/drivers/remoteproc/stm32_rproc.c
>>>>>>>>> @@ -20,6 +20,7 @@
>>>>>>>>> #include <linux/remoteproc.h>
>>>>>>>>> #include <linux/reset.h>
>>>>>>>>> #include <linux/slab.h>
>>>>>>>>> +#include <linux/tee_remoteproc.h>
>>>>>>>>> #include <linux/workqueue.h>
>>>>>>>>>
>>>>>>>>> #include "remoteproc_internal.h"
>>>>>>>>> @@ -49,6 +50,9 @@
>>>>>>>>> #define M4_STATE_STANDBY 4
>>>>>>>>> #define M4_STATE_CRASH 5
>>>>>>>>>
>>>>>>>>> +/* Remote processor unique identifier aligned with the Trusted Execution Environment definitions */
>>>>>>>>> +#define STM32_MP1_M4_PROC_ID 0
>>>>>>>>> +
>>>>>>>>> struct stm32_syscon {
>>>>>>>>> struct regmap *map;
>>>>>>>>> u32 reg;
>>>>>>>>> @@ -90,6 +94,8 @@ struct stm32_rproc {
>>>>>>>>> struct stm32_mbox mb[MBOX_NB_MBX];
>>>>>>>>> struct workqueue_struct *workqueue;
>>>>>>>>> bool hold_boot_smc;
>>>>>>>>> + bool fw_loaded;
>>>>>>>>> + struct tee_rproc *trproc;
>>>>>>>>> void __iomem *rsc_va;
>>>>>>>>> };
>>>>>>>>>
>>>>>>>>> @@ -257,6 +263,91 @@ static int stm32_rproc_release(struct rproc *rproc)
>>>>>>>>> return err;
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>> +static int stm32_rproc_tee_elf_sanity_check(struct rproc *rproc,
>>>>>>>>> + const struct firmware *fw)
>>>>>>>>> +{
>>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>>>>> + unsigned int ret = 0;
>>>>>>>>> +
>>>>>>>>> + if (rproc->state == RPROC_DETACHED)
>>>>>>>>> + return 0;
>>>>>>>>> +
>>>>>>>>> + ret = tee_rproc_load_fw(ddata->trproc, fw);
>>>>>>>>> + if (!ret)
>>>>>>>>> + ddata->fw_loaded = true;
>>>>>>>>> +
>>>>>>>>> + return ret;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static int stm32_rproc_tee_elf_load(struct rproc *rproc,
>>>>>>>>> + const struct firmware *fw)
>>>>>>>>> +{
>>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>>>>> + unsigned int ret;
>>>>>>>>> +
>>>>>>>>> + /*
>>>>>>>>> + * This function can be called by remote proc for recovery
>>>>>>>>> + * without the sanity check. In this case we need to load the firmware
>>>>>>>>> + * else nothing done here as the firmware has been preloaded for the
>>>>>>>>> + * sanity check to be able to parse it for the resource table.
>>>>>>>>> + */
>>>>>>>>
>>>>>>>> This comment is very confusing - please consider refactoring.
>>>>>>>>
>>>>>>>>> + if (ddata->fw_loaded)
>>>>>>>>> + return 0;
>>>>>>>>> +
>>>>>>>>
>>>>>>>> I'm not sure about keeping a flag to indicate the status of the loaded firmware.
>>>>>>>> It is not done for the non-secure method, I don't see why it would be needed for
>>>>>>>> the secure one.
>>>>>>>>
>>>>>>>
>>>>>>> The difference is on the sanity check.
>>>>>>> - in rproc_elf_sanity_check we parse the elf file to verify that it is
>>>>>>> valid.
>>>>>>> - in stm32_rproc_tee_elf_sanity_check we have to do the same, that means to
>>>>>>> authenticate it. the authentication is done during the load.
>>>>>>>
>>>>>>> So this flag is used to avoid to reload it twice time.
>>>>>>> refactoring the comment should help to understand this flag
>>>>>>>
>>>>>>>
>>>>>>> An alternative would be to bypass the sanity check. But this lead to same
>>>>>>> limitation.
>>>>>>> Before loading the firmware in remoteproc_core, we call rproc_parse_fw() that is
>>>>>>> used to get the resource table address. To get it from tee we need to
>>>>>>> authenticate the firmware so load it...
>>>>>>>
>>>>>>
>>>>>> I spent a long time thinking about this patchset. Looking at the code as it
>>>>>> is now, request_firmware() in rproc_boot() is called even when the TEE is
>>>>>> responsible for loading the firmware. There should be some conditional code
>>>>>> that calls either request_firmware() or tee_rproc_load_fw(). The latter should
>>>>>> also be renamed to tee_rproc_request_firmware() to avoid confusion.
>>>>>
>>>>>
>>>>> The request_firmware() call is needed in both cases to get the image from the
>>>>> filesystem. The tee_rproc_load_fw() gets, as input, the struct firmware provided
>>>>> by request_firmware().
>>>>
>>>> The cover letter clearly state the secure side is responsible for loading the
>>>> firmware image but here you're telling me it has to be loaded twice. This is
>>>> very confusing.
>>>
>>> Concerning the call of request_firmware()
>>>
>>> By "both cases" I would say that the call of request_firmware() is needed in
>>> both modes:
>>> - the ELF firmware is parsed and loaded by linux (legacy)
>>> - the binary firmware is parsed and loaded by OP-TEE.
>>>
>>> The Op-TEE is not able to get the firmware image from the file system.
>>>
>>>
>>> Concerning the call of tee_rproc_load_fw twice time
>>>
>>> There are 2 use cases:
>>>
>>> - First boot of the remote processor:
>>>
>>> 1) The Linux rproc gets the binary firmware image from the file system by
>>> calling request_firmware(). A copy is stored in memory.
>>
>> Right. And I think tee_rproc_load_fw() should be called right after
>> request_firmware() if rproc::tee_rproc_interface is valid. At that point the TEE
>> app may or may not do the firmware authentication, that is application specific.

FYI, I am close to completing V3 for my series. However, I am facing an issue
with rproc_load_segments() that requires the implementation of ops->load on start.
Therefore, just inserting a tee_rproc_load_fw() call is not possible.

Due to this constraint, I did not find a solution that matches your
recommendations. Nevertheless, I will propose another solution in my V3, trying
to take into account as many of your comments/requests as possible, including
updating of the remoteproc_core.c to simplify the sequence.


Regards,
Arnaud


>>
>>> 2) the linux performs a sanity check on the firmware calling
>>> rproc_fw_sanity_check()
>>> => from OP-TEE point of view this means to autenticate the firmware
>>> => let consider in this exemple that we bypass this step
>>> (ops->sanity_check = NULL)
>>
>> Ok
>>
>>>
>>> 3) the linux rproc call rproc_parse_fw() to get the resource table
>>> => From OP-TEE point of view the resource table is available only when
>>> the firmware is loaded
>>
>> Right, and it should have been loaded already. If it is not then the TEE should
>> return an error.
>>
>>> => We need to call tee_rproc_load_fw() to be able then to get the
>>> address of the resource table.
>>
>> See my comment above - at this point the TEE should already have the firmware.
>> As such the only thing left is to get the address of the resource table, which
>> you already do in rproc_tee_get_rsc_table(). The upper part of that function
>> should be spun off in a new static function to deal with the TEE API, something
>> like _rproc_tee_get_rsc_table(). The new function should also be called in
>> tee_rproc_get_loaded_rsc_table() rather than keeping a cache value in
>> trproc->rsc_va.
>>
>>> 4) The Linux rproc calls rproc_handle_resources() to parse the resource table.
>>> 5) The linux rproc calls rproc_start()
>>> - load the firrmware calling rproc_load_segments()
>>> => we don't want to call tee_rproc_load_fw() it a second time
>>
>> And that is fine if the TEE app has already placed the program segments in
>> memory.
>>
>>> - start the firmware calling ops->start()
>>>
>>> - Reboot on crash recovery using rproc_boot_recovery()
>>>
>>> 1) The Linux rproc gets the binary firmware image from the file system by
>>> calling request_firmware(). A copy is stored in memory.
>>> 5) The linux rproc calls rproc_start()
>>> - load the firrmware calling rproc_load_segments()
>>> => we have to call tee_rproc_load_fw() to reload the firmware
>>
>> Loading the firmware in the TEE should be done right after request_firmware()
>> has been called, the same way it is done in the boot path. If there isn't a
>> need to reload the TEE firmware than the TEE application should ignore the
>> request.
>
> I need to prototype to verify this proposal.
> I will come back with a V3.

>
> Thank you for the advice and review!
>
> Regard,
> Arnaud
>
>>
>>> - start the firmware calling ops->start()
>>>
>>> In first use case we have to load the firmware on rproc_parse_fw(), in second
>>> usecase on rproc_load_segments().
>>>
>>> This is the point I have tried to solve with the ddata->fw_loaded variable.
>>>
>>>>
>>>> I'm also confused as to why stm32_rproc_tee_elf_sanity_check() is calling
>>>> tee_rproc_load_fw(). There should be one call to load the firmware and another
>>>> to perform a sanity check on it. If the sanity check is done at load time by
>>>> the secure world then ops::sanity_check() is NULL.
>>>
>>> Sure, make sense to remove the sanity_check ops
>>>
>>> Thanks,
>>> Arnaud
>>>
>>>>
>>>> Most of what this patchset does makes sense, but some of it needs to be moved
>>>> around.
>>>>
>>>> Thanks,
>>>> Mathieu
>>>>
>>>>>
>>>>> If we want to integrate in remoteproc_core the solution could probably have to
>>>>> create the equivalent of the rproc_fw_boot() to load the firmware with an
>>>>> external method. Here is an example based on a new rproc_ops ( not tested)
>>>>>
>>>>> + static int rproc_fw_ext_boot(struct rproc *rproc, const struct firmware *fw)
>>>>> + {
>>>>> + struct device *dev = &rproc->dev;
>>>>> + const char *name = rproc->firmware;
>>>>> + int ret;
>>>>> +
>>>>> +
>>>>> + dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size);
>>>>> +
>>>>> + /* ops to load and start the remoteprocessor */
>>>>> + ret = rproc->ops->boot(rproc, fw);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + /*
>>>>> + * if enabling an IOMMU isn't relevant for this rproc, this is
>>>>> + * just a nop
>>>>> + */
>>>>> + ret = rproc_enable_iommu(rproc);
>>>>> + if (ret) {
>>>>> + dev_err(dev, "can't enable iommu: %d\n", ret);
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + /* Prepare rproc for firmware loading if needed */
>>>>> + ret = rproc_prepare_device(rproc);
>>>>> + if (ret) {
>>>>> + dev_err(dev, "can't prepare rproc %s: %d\n", rproc->name, ret);
>>>>> + goto disable_iommu;
>>>>> + }
>>>>> +
>>>>> + ret = rproc_set_rsc_table(rproc);
>>>>> + if (ret) {
>>>>> + dev_err(dev, "can't load resource table: %d\n", ret);
>>>>> + goto unprepare_device;
>>>>> + }
>>>>> +
>>>>> +
>>>>> + /* reset max_notifyid */
>>>>> + rproc->max_notifyid = -1;
>>>>> +
>>>>> + /* reset handled vdev */
>>>>> + rproc->nb_vdev = 0;
>>>>> +
>>>>> + /* handle fw resources which are required to boot rproc */
>>>>> + ret = rproc_handle_resources(rproc, rproc_loading_handlers);
>>>>> + if (ret) {
>>>>> + dev_err(dev, "Failed to process resources: %d\n", ret);
>>>>> + goto clean_up_resources;
>>>>> + }
>>>>> +
>>>>> + /* Allocate carveout resources associated to rproc */
>>>>> + ret = rproc_alloc_registered_carveouts(rproc);
>>>>> + if (ret) {
>>>>> + dev_err(dev, "Failed to allocate associated carveouts: %d\n",
>>>>> + ret);
>>>>> + goto clean_up_resources;
>>>>> + }
>>>>> +
>>>>> + return 0;
>>>>> +
>>>>> + clean_up_resources:
>>>>> + rproc_resource_cleanup(rproc);
>>>>> + unprepare_rproc:
>>>>> + /* release HW resources if needed */
>>>>> + rproc_unprepare_device(rproc);
>>>>> + disable_iommu:
>>>>> + rproc_disable_iommu(rproc);
>>>>> + return ret;
>>>>> + }
>>>>>
>>>>>
>>>>> int rproc_boot(struct rproc *rproc)
>>>>> {
>>>>> [...]
>>>>>
>>>>> - ret = rproc_fw_boot(rproc, firmware_p);
>>>>> + if(rproc->ops->boot)
>>>>> + ret = rproc_fw_ext_boot(rproc, firmware_p);
>>>>> + else
>>>>> + ret = rproc_fw_boot(rproc, firmware_p);
>>>>>
>>>>> Another advantage of this solution is that it opens the framework to other
>>>>> formats. For instance it could be a way to support dtb format requested in [RFC]
>>>>> Passing device-tree to remoteproc [1].
>>>>>
>>>>> [1]
>>>>> https://lore.kernel.org/linux-remoteproc/[email protected]/T/#t
>>>>>
>>>>> Thanks,
>>>>> Arnaud
>>>>>
>>>>>
>>>>>
>>>>>>
>>>>>> I touched on that before but please rename rproc_tee_get_rsc_table() to
>>>>>> rproc_tee_elf_load_rsc_table(). I also suggest to introduce a new function,
>>>>>> rproc_tee_get_loaded_rsc_table() that would be called from
>>>>>> rproc_tee_elf_load_rsc_table(). That way we don't need trproc->rsc_va.
>>>>>>
>>>>>> I also think tee_rproc should be renamed to "rproc_tee_interface" and folded
>>>>>> under struct rproc.
>>>>>>
>>>>>> With the above most of the problems with the current implementation should
>>>>>> naturally go away.
>>>>>>
>>>>>> Thanks,
>>>>>> Mathieu
>>>>>>
>>>>>>>
>>>>>>>>> + ret = tee_rproc_load_fw(ddata->trproc, fw);
>>>>>>>>> + if (ret)
>>>>>>>>> + return ret;
>>>>>>>>> + ddata->fw_loaded = true;
>>>>>>>>> +
>>>>>>>>> + /* Update the resource table parameters. */
>>>>>>>>> + if (rproc_tee_get_rsc_table(ddata->trproc)) {
>>>>>>>>> + /* No resource table: reset the related fields. */
>>>>>>>>> + rproc->cached_table = NULL;
>>>>>>>>> + rproc->table_ptr = NULL;
>>>>>>>>> + rproc->table_sz = 0;
>>>>>>>>> + }
>>>>>>>>> +
>>>>>>>>> + return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static struct resource_table *
>>>>>>>>> +stm32_rproc_tee_elf_find_loaded_rsc_table(struct rproc *rproc,
>>>>>>>>> + const struct firmware *fw)
>>>>>>>>> +{
>>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>>>>> +
>>>>>>>>> + return tee_rproc_get_loaded_rsc_table(ddata->trproc);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static int stm32_rproc_tee_start(struct rproc *rproc)
>>>>>>>>> +{
>>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>>>>> +
>>>>>>>>> + return tee_rproc_start(ddata->trproc);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static int stm32_rproc_tee_attach(struct rproc *rproc)
>>>>>>>>> +{
>>>>>>>>> + /* Nothing to do, remote proc already started by the secured context. */
>>>>>>>>> + return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static int stm32_rproc_tee_stop(struct rproc *rproc)
>>>>>>>>> +{
>>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>>>>> + int err;
>>>>>>>>> +
>>>>>>>>> + stm32_rproc_request_shutdown(rproc);
>>>>>>>>> +
>>>>>>>>> + err = tee_rproc_stop(ddata->trproc);
>>>>>>>>> + if (err)
>>>>>>>>> + return err;
>>>>>>>>> +
>>>>>>>>> + ddata->fw_loaded = false;
>>>>>>>>> +
>>>>>>>>> + return stm32_rproc_release(rproc);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> static int stm32_rproc_prepare(struct rproc *rproc)
>>>>>>>>> {
>>>>>>>>> struct device *dev = rproc->dev.parent;
>>>>>>>>> @@ -319,7 +410,14 @@ static int stm32_rproc_prepare(struct rproc *rproc)
>>>>>>>>>
>>>>>>>>> static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
>>>>>>>>> {
>>>>>>>>> - if (rproc_elf_load_rsc_table(rproc, fw))
>>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
>>>>>>>>> + int ret;
>>>>>>>>> +
>>>>>>>>> + if (ddata->trproc)
>>>>>>>>> + ret = rproc_tee_get_rsc_table(ddata->trproc);
>>>>>>>>> + else
>>>>>>>>> + ret = rproc_elf_load_rsc_table(rproc, fw);
>>>>>>>>> + if (ret)
>>>>>>>>> dev_warn(&rproc->dev, "no resource table found for this firmware\n");
>>>>>>>>>
>>>>>>>>> return 0;
>>>>>>>>> @@ -693,8 +791,22 @@ 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 = stm32_rproc_tee_start,
>>>>>>>>> + .stop = stm32_rproc_tee_stop,
>>>>>>>>> + .attach = stm32_rproc_tee_attach,
>>>>>>>>> + .kick = stm32_rproc_kick,
>>>>>>>>> + .parse_fw = stm32_rproc_parse_fw,
>>>>>>>>> + .find_loaded_rsc_table = stm32_rproc_tee_elf_find_loaded_rsc_table,
>>>>>>>>> + .get_loaded_rsc_table = stm32_rproc_get_loaded_rsc_table,
>>>>>>>>> + .sanity_check = stm32_rproc_tee_elf_sanity_check,
>>>>>>>>> + .load = stm32_rproc_tee_elf_load,
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> static const struct of_device_id stm32_rproc_match[] = {
>>>>>>>>> - { .compatible = "st,stm32mp1-m4" },
>>>>>>>>> + {.compatible = "st,stm32mp1-m4",},
>>>>>>>>> + {.compatible = "st,stm32mp1-m4-tee",},
>>>>>>>>> {},
>>>>>>>>> };
>>>>>>>>> MODULE_DEVICE_TABLE(of, stm32_rproc_match);
>>>>>>>>> @@ -853,6 +965,7 @@ 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;
>>>>>>>>> int ret;
>>>>>>>>> @@ -861,11 +974,31 @@ static int stm32_rproc_probe(struct platform_device *pdev)
>>>>>>>>> if (ret)
>>>>>>>>> return ret;
>>>>>>>>>
>>>>>>>>> - rproc = 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")) {
>>>>>>>>> + trproc = tee_rproc_register(dev, STM32_MP1_M4_PROC_ID);
>>>>>>>>> + if (IS_ERR(trproc)) {
>>>>>>>>> + dev_err_probe(dev, PTR_ERR(trproc),
>>>>>>>>> + "signed firmware not supported by TEE\n");
>>>>>>>>> + return PTR_ERR(trproc);
>>>>>>>>> + }
>>>>>>>>> + /*
>>>>>>>>> + * Delegate the firmware management to the secure context.
>>>>>>>>> + * The firmware loaded has to be signed.
>>>>>>>>> + */
>>>>>>>>> + dev_info(dev, "Support of signed firmware only\n");
>>>>>>>>
>>>>>>>> Not sure what this adds. Please remove.
>>>>>>>
>>>>>>> This is used to inform the user that only a signed firmware can be loaded, not
>>>>>>> an ELF file.
>>>>>>> I have a patch in my pipe to provide the supported format in the debugfs. In a
>>>>>>> first step, I can suppress this message and we can revisit the issue when I push
>>>>>>> the debugfs proposal.
>>>>>>>
>>>>>>> Thanks,
>>>>>>> Arnaud
>>>>>>>
>>>>>>>>
>>>>>>>>> + }
>>>>>>>>> + rproc = rproc_alloc(dev, np->name,
>>>>>>>>> + trproc ? &st_rproc_tee_ops : &st_rproc_ops,
>>>>>>>>> + NULL, sizeof(*ddata));
>>>>>>>>> + if (!rproc) {
>>>>>>>>> + ret = -ENOMEM;
>>>>>>>>> + goto free_tee;
>>>>>>>>> + }
>>>>>>>>>
>>>>>>>>> ddata = rproc->priv;
>>>>>>>>> + ddata->trproc = trproc;
>>>>>>>>> + if (trproc)
>>>>>>>>> + trproc->rproc = rproc;
>>>>>>>>>
>>>>>>>>> rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
>>>>>>>>>
>>>>>>>>> @@ -916,6 +1049,10 @@ static int stm32_rproc_probe(struct platform_device *pdev)
>>>>>>>>> device_init_wakeup(dev, false);
>>>>>>>>> }
>>>>>>>>> rproc_free(rproc);
>>>>>>>>> +free_tee:
>>>>>>>>> + if (trproc)
>>>>>>>>> + tee_rproc_unregister(trproc);
>>>>>>>>> +
>>>>>>>>> return ret;
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>> @@ -937,6 +1074,8 @@ static void stm32_rproc_remove(struct platform_device *pdev)
>>>>>>>>> device_init_wakeup(dev, false);
>>>>>>>>> }
>>>>>>>>> rproc_free(rproc);
>>>>>>>>> + if (ddata->trproc)
>>>>>>>>> + tee_rproc_unregister(ddata->trproc);
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>> static int stm32_rproc_suspend(struct device *dev)
>>>>>>>>> --
>>>>>>>>> 2.25.1
>>>>>>>>>
> _______________________________________________
> Linux-stm32 mailing list
> [email protected]
> https://st-md-mailman.stormreply.com/mailman/listinfo/linux-stm32

2024-02-13 18:40:31

by Mathieu Poirier

[permalink] [raw]
Subject: Re: [Linux-stm32] [PATCH v2 4/4] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware

On Tue, 13 Feb 2024 at 08:48, Arnaud POULIQUEN
<[email protected]> wrote:
>
> Hello Mathieu,
>
> On 2/5/24 10:13, Arnaud POULIQUEN wrote:
> >
> >
> > On 2/2/24 20:53, Mathieu Poirier wrote:
> >> On Thu, Feb 01, 2024 at 07:33:35PM +0100, Arnaud POULIQUEN wrote:
> >>>
> >>>
> >>> On 2/1/24 17:02, Mathieu Poirier wrote:
> >>>> On Thu, Feb 01, 2024 at 04:06:37PM +0100, Arnaud POULIQUEN wrote:
> >>>>> hello Mathieu,
> >>>>>
> >>>>> On 1/31/24 19:52, Mathieu Poirier wrote:
> >>>>>> On Tue, Jan 30, 2024 at 10:13:48AM +0100, Arnaud POULIQUEN wrote:
> >>>>>>>
> >>>>>>>
> >>>>>>> On 1/26/24 18:11, Mathieu Poirier wrote:
> >>>>>>>> On Thu, Jan 18, 2024 at 11:04:33AM +0100, Arnaud Pouliquen wrote:
> >>>>>>>>> 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 <[email protected]>
> >>>>>>>>> ---
> >>>>>>>>> V1 to V2 update:
> >>>>>>>>> - remove the select "TEE_REMOTEPROC" in STM32_RPROC config as detected by
> >>>>>>>>> the kernel test robot:
> >>>>>>>>> WARNING: unmet direct dependencies detected for TEE_REMOTEPROC
> >>>>>>>>> Depends on [n]: REMOTEPROC [=y] && OPTEE [=n]
> >>>>>>>>> Selected by [y]:
> >>>>>>>>> - STM32_RPROC [=y] && (ARCH_STM32 || COMPILE_TEST [=y]) && REMOTEPROC [=y]
> >>>>>>>>> - Fix initialized trproc variable in stm32_rproc_probe
> >>>>>>>>> ---
> >>>>>>>>> drivers/remoteproc/stm32_rproc.c | 149 +++++++++++++++++++++++++++++--
> >>>>>>>>> 1 file changed, 144 insertions(+), 5 deletions(-)
> >>>>>>>>>
> >>>>>>>>> diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
> >>>>>>>>> index fcc0001e2657..cf6a21bac945 100644
> >>>>>>>>> --- a/drivers/remoteproc/stm32_rproc.c
> >>>>>>>>> +++ b/drivers/remoteproc/stm32_rproc.c
> >>>>>>>>> @@ -20,6 +20,7 @@
> >>>>>>>>> #include <linux/remoteproc.h>
> >>>>>>>>> #include <linux/reset.h>
> >>>>>>>>> #include <linux/slab.h>
> >>>>>>>>> +#include <linux/tee_remoteproc.h>
> >>>>>>>>> #include <linux/workqueue.h>
> >>>>>>>>>
> >>>>>>>>> #include "remoteproc_internal.h"
> >>>>>>>>> @@ -49,6 +50,9 @@
> >>>>>>>>> #define M4_STATE_STANDBY 4
> >>>>>>>>> #define M4_STATE_CRASH 5
> >>>>>>>>>
> >>>>>>>>> +/* Remote processor unique identifier aligned with the Trusted Execution Environment definitions */
> >>>>>>>>> +#define STM32_MP1_M4_PROC_ID 0
> >>>>>>>>> +
> >>>>>>>>> struct stm32_syscon {
> >>>>>>>>> struct regmap *map;
> >>>>>>>>> u32 reg;
> >>>>>>>>> @@ -90,6 +94,8 @@ struct stm32_rproc {
> >>>>>>>>> struct stm32_mbox mb[MBOX_NB_MBX];
> >>>>>>>>> struct workqueue_struct *workqueue;
> >>>>>>>>> bool hold_boot_smc;
> >>>>>>>>> + bool fw_loaded;
> >>>>>>>>> + struct tee_rproc *trproc;
> >>>>>>>>> void __iomem *rsc_va;
> >>>>>>>>> };
> >>>>>>>>>
> >>>>>>>>> @@ -257,6 +263,91 @@ static int stm32_rproc_release(struct rproc *rproc)
> >>>>>>>>> return err;
> >>>>>>>>> }
> >>>>>>>>>
> >>>>>>>>> +static int stm32_rproc_tee_elf_sanity_check(struct rproc *rproc,
> >>>>>>>>> + const struct firmware *fw)
> >>>>>>>>> +{
> >>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>>>>>>> + unsigned int ret = 0;
> >>>>>>>>> +
> >>>>>>>>> + if (rproc->state == RPROC_DETACHED)
> >>>>>>>>> + return 0;
> >>>>>>>>> +
> >>>>>>>>> + ret = tee_rproc_load_fw(ddata->trproc, fw);
> >>>>>>>>> + if (!ret)
> >>>>>>>>> + ddata->fw_loaded = true;
> >>>>>>>>> +
> >>>>>>>>> + return ret;
> >>>>>>>>> +}
> >>>>>>>>> +
> >>>>>>>>> +static int stm32_rproc_tee_elf_load(struct rproc *rproc,
> >>>>>>>>> + const struct firmware *fw)
> >>>>>>>>> +{
> >>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>>>>>>> + unsigned int ret;
> >>>>>>>>> +
> >>>>>>>>> + /*
> >>>>>>>>> + * This function can be called by remote proc for recovery
> >>>>>>>>> + * without the sanity check. In this case we need to load the firmware
> >>>>>>>>> + * else nothing done here as the firmware has been preloaded for the
> >>>>>>>>> + * sanity check to be able to parse it for the resource table.
> >>>>>>>>> + */
> >>>>>>>>
> >>>>>>>> This comment is very confusing - please consider refactoring.
> >>>>>>>>
> >>>>>>>>> + if (ddata->fw_loaded)
> >>>>>>>>> + return 0;
> >>>>>>>>> +
> >>>>>>>>
> >>>>>>>> I'm not sure about keeping a flag to indicate the status of the loaded firmware.
> >>>>>>>> It is not done for the non-secure method, I don't see why it would be needed for
> >>>>>>>> the secure one.
> >>>>>>>>
> >>>>>>>
> >>>>>>> The difference is on the sanity check.
> >>>>>>> - in rproc_elf_sanity_check we parse the elf file to verify that it is
> >>>>>>> valid.
> >>>>>>> - in stm32_rproc_tee_elf_sanity_check we have to do the same, that means to
> >>>>>>> authenticate it. the authentication is done during the load.
> >>>>>>>
> >>>>>>> So this flag is used to avoid to reload it twice time.
> >>>>>>> refactoring the comment should help to understand this flag
> >>>>>>>
> >>>>>>>
> >>>>>>> An alternative would be to bypass the sanity check. But this lead to same
> >>>>>>> limitation.
> >>>>>>> Before loading the firmware in remoteproc_core, we call rproc_parse_fw() that is
> >>>>>>> used to get the resource table address. To get it from tee we need to
> >>>>>>> authenticate the firmware so load it...
> >>>>>>>
> >>>>>>
> >>>>>> I spent a long time thinking about this patchset. Looking at the code as it
> >>>>>> is now, request_firmware() in rproc_boot() is called even when the TEE is
> >>>>>> responsible for loading the firmware. There should be some conditional code
> >>>>>> that calls either request_firmware() or tee_rproc_load_fw(). The latter should
> >>>>>> also be renamed to tee_rproc_request_firmware() to avoid confusion.
> >>>>>
> >>>>>
> >>>>> The request_firmware() call is needed in both cases to get the image from the
> >>>>> filesystem. The tee_rproc_load_fw() gets, as input, the struct firmware provided
> >>>>> by request_firmware().
> >>>>
> >>>> The cover letter clearly state the secure side is responsible for loading the
> >>>> firmware image but here you're telling me it has to be loaded twice. This is
> >>>> very confusing.
> >>>
> >>> Concerning the call of request_firmware()
> >>>
> >>> By "both cases" I would say that the call of request_firmware() is needed in
> >>> both modes:
> >>> - the ELF firmware is parsed and loaded by linux (legacy)
> >>> - the binary firmware is parsed and loaded by OP-TEE.
> >>>
> >>> The Op-TEE is not able to get the firmware image from the file system.
> >>>
> >>>
> >>> Concerning the call of tee_rproc_load_fw twice time
> >>>
> >>> There are 2 use cases:
> >>>
> >>> - First boot of the remote processor:
> >>>
> >>> 1) The Linux rproc gets the binary firmware image from the file system by
> >>> calling request_firmware(). A copy is stored in memory.
> >>
> >> Right. And I think tee_rproc_load_fw() should be called right after
> >> request_firmware() if rproc::tee_rproc_interface is valid. At that point the TEE
> >> app may or may not do the firmware authentication, that is application specific.
>
> FYI, I am close to completing V3 for my series. However, I am facing an issue
> with rproc_load_segments() that requires the implementation of ops->load on start.
> Therefore, just inserting a tee_rproc_load_fw() call is not possible.
>
> Due to this constraint, I did not find a solution that matches your
> recommendations. Nevertheless, I will propose another solution in my V3, trying
> to take into account as many of your comments/requests as possible, including
> updating of the remoteproc_core.c to simplify the sequence.
>
>

Thanks for the heads-up, let's see what you come up with. That said,
please provide as much information as possible on the constraints you
are facing.

> Regards,
> Arnaud
>
>
> >>
> >>> 2) the linux performs a sanity check on the firmware calling
> >>> rproc_fw_sanity_check()
> >>> => from OP-TEE point of view this means to autenticate the firmware
> >>> => let consider in this exemple that we bypass this step
> >>> (ops->sanity_check = NULL)
> >>
> >> Ok
> >>
> >>>
> >>> 3) the linux rproc call rproc_parse_fw() to get the resource table
> >>> => From OP-TEE point of view the resource table is available only when
> >>> the firmware is loaded
> >>
> >> Right, and it should have been loaded already. If it is not then the TEE should
> >> return an error.
> >>
> >>> => We need to call tee_rproc_load_fw() to be able then to get the
> >>> address of the resource table.
> >>
> >> See my comment above - at this point the TEE should already have the firmware.
> >> As such the only thing left is to get the address of the resource table, which
> >> you already do in rproc_tee_get_rsc_table(). The upper part of that function
> >> should be spun off in a new static function to deal with the TEE API, something
> >> like _rproc_tee_get_rsc_table(). The new function should also be called in
> >> tee_rproc_get_loaded_rsc_table() rather than keeping a cache value in
> >> trproc->rsc_va.
> >>
> >>> 4) The Linux rproc calls rproc_handle_resources() to parse the resource table.
> >>> 5) The linux rproc calls rproc_start()
> >>> - load the firrmware calling rproc_load_segments()
> >>> => we don't want to call tee_rproc_load_fw() it a second time
> >>
> >> And that is fine if the TEE app has already placed the program segments in
> >> memory.
> >>
> >>> - start the firmware calling ops->start()
> >>>
> >>> - Reboot on crash recovery using rproc_boot_recovery()
> >>>
> >>> 1) The Linux rproc gets the binary firmware image from the file system by
> >>> calling request_firmware(). A copy is stored in memory.
> >>> 5) The linux rproc calls rproc_start()
> >>> - load the firrmware calling rproc_load_segments()
> >>> => we have to call tee_rproc_load_fw() to reload the firmware
> >>
> >> Loading the firmware in the TEE should be done right after request_firmware()
> >> has been called, the same way it is done in the boot path. If there isn't a
> >> need to reload the TEE firmware than the TEE application should ignore the
> >> request.
> >
> > I need to prototype to verify this proposal.
> > I will come back with a V3.
>
> >
> > Thank you for the advice and review!
> >
> > Regard,
> > Arnaud
> >
> >>
> >>> - start the firmware calling ops->start()
> >>>
> >>> In first use case we have to load the firmware on rproc_parse_fw(), in second
> >>> usecase on rproc_load_segments().
> >>>
> >>> This is the point I have tried to solve with the ddata->fw_loaded variable.
> >>>
> >>>>
> >>>> I'm also confused as to why stm32_rproc_tee_elf_sanity_check() is calling
> >>>> tee_rproc_load_fw(). There should be one call to load the firmware and another
> >>>> to perform a sanity check on it. If the sanity check is done at load time by
> >>>> the secure world then ops::sanity_check() is NULL.
> >>>
> >>> Sure, make sense to remove the sanity_check ops
> >>>
> >>> Thanks,
> >>> Arnaud
> >>>
> >>>>
> >>>> Most of what this patchset does makes sense, but some of it needs to be moved
> >>>> around.
> >>>>
> >>>> Thanks,
> >>>> Mathieu
> >>>>
> >>>>>
> >>>>> If we want to integrate in remoteproc_core the solution could probably have to
> >>>>> create the equivalent of the rproc_fw_boot() to load the firmware with an
> >>>>> external method. Here is an example based on a new rproc_ops ( not tested)
> >>>>>
> >>>>> + static int rproc_fw_ext_boot(struct rproc *rproc, const struct firmware *fw)
> >>>>> + {
> >>>>> + struct device *dev = &rproc->dev;
> >>>>> + const char *name = rproc->firmware;
> >>>>> + int ret;
> >>>>> +
> >>>>> +
> >>>>> + dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size);
> >>>>> +
> >>>>> + /* ops to load and start the remoteprocessor */
> >>>>> + ret = rproc->ops->boot(rproc, fw);
> >>>>> + if (ret)
> >>>>> + return ret;
> >>>>> +
> >>>>> + /*
> >>>>> + * if enabling an IOMMU isn't relevant for this rproc, this is
> >>>>> + * just a nop
> >>>>> + */
> >>>>> + ret = rproc_enable_iommu(rproc);
> >>>>> + if (ret) {
> >>>>> + dev_err(dev, "can't enable iommu: %d\n", ret);
> >>>>> + return ret;
> >>>>> + }
> >>>>> +
> >>>>> + /* Prepare rproc for firmware loading if needed */
> >>>>> + ret = rproc_prepare_device(rproc);
> >>>>> + if (ret) {
> >>>>> + dev_err(dev, "can't prepare rproc %s: %d\n", rproc->name, ret);
> >>>>> + goto disable_iommu;
> >>>>> + }
> >>>>> +
> >>>>> + ret = rproc_set_rsc_table(rproc);
> >>>>> + if (ret) {
> >>>>> + dev_err(dev, "can't load resource table: %d\n", ret);
> >>>>> + goto unprepare_device;
> >>>>> + }
> >>>>> +
> >>>>> +
> >>>>> + /* reset max_notifyid */
> >>>>> + rproc->max_notifyid = -1;
> >>>>> +
> >>>>> + /* reset handled vdev */
> >>>>> + rproc->nb_vdev = 0;
> >>>>> +
> >>>>> + /* handle fw resources which are required to boot rproc */
> >>>>> + ret = rproc_handle_resources(rproc, rproc_loading_handlers);
> >>>>> + if (ret) {
> >>>>> + dev_err(dev, "Failed to process resources: %d\n", ret);
> >>>>> + goto clean_up_resources;
> >>>>> + }
> >>>>> +
> >>>>> + /* Allocate carveout resources associated to rproc */
> >>>>> + ret = rproc_alloc_registered_carveouts(rproc);
> >>>>> + if (ret) {
> >>>>> + dev_err(dev, "Failed to allocate associated carveouts: %d\n",
> >>>>> + ret);
> >>>>> + goto clean_up_resources;
> >>>>> + }
> >>>>> +
> >>>>> + return 0;
> >>>>> +
> >>>>> + clean_up_resources:
> >>>>> + rproc_resource_cleanup(rproc);
> >>>>> + unprepare_rproc:
> >>>>> + /* release HW resources if needed */
> >>>>> + rproc_unprepare_device(rproc);
> >>>>> + disable_iommu:
> >>>>> + rproc_disable_iommu(rproc);
> >>>>> + return ret;
> >>>>> + }
> >>>>>
> >>>>>
> >>>>> int rproc_boot(struct rproc *rproc)
> >>>>> {
> >>>>> [...]
> >>>>>
> >>>>> - ret = rproc_fw_boot(rproc, firmware_p);
> >>>>> + if(rproc->ops->boot)
> >>>>> + ret = rproc_fw_ext_boot(rproc, firmware_p);
> >>>>> + else
> >>>>> + ret = rproc_fw_boot(rproc, firmware_p);
> >>>>>
> >>>>> Another advantage of this solution is that it opens the framework to other
> >>>>> formats. For instance it could be a way to support dtb format requested in [RFC]
> >>>>> Passing device-tree to remoteproc [1].
> >>>>>
> >>>>> [1]
> >>>>> https://lore.kernel.org/linux-remoteproc/[email protected]/T/#t
> >>>>>
> >>>>> Thanks,
> >>>>> Arnaud
> >>>>>
> >>>>>
> >>>>>
> >>>>>>
> >>>>>> I touched on that before but please rename rproc_tee_get_rsc_table() to
> >>>>>> rproc_tee_elf_load_rsc_table(). I also suggest to introduce a new function,
> >>>>>> rproc_tee_get_loaded_rsc_table() that would be called from
> >>>>>> rproc_tee_elf_load_rsc_table(). That way we don't need trproc->rsc_va.
> >>>>>>
> >>>>>> I also think tee_rproc should be renamed to "rproc_tee_interface" and folded
> >>>>>> under struct rproc.
> >>>>>>
> >>>>>> With the above most of the problems with the current implementation should
> >>>>>> naturally go away.
> >>>>>>
> >>>>>> Thanks,
> >>>>>> Mathieu
> >>>>>>
> >>>>>>>
> >>>>>>>>> + ret = tee_rproc_load_fw(ddata->trproc, fw);
> >>>>>>>>> + if (ret)
> >>>>>>>>> + return ret;
> >>>>>>>>> + ddata->fw_loaded = true;
> >>>>>>>>> +
> >>>>>>>>> + /* Update the resource table parameters. */
> >>>>>>>>> + if (rproc_tee_get_rsc_table(ddata->trproc)) {
> >>>>>>>>> + /* No resource table: reset the related fields. */
> >>>>>>>>> + rproc->cached_table = NULL;
> >>>>>>>>> + rproc->table_ptr = NULL;
> >>>>>>>>> + rproc->table_sz = 0;
> >>>>>>>>> + }
> >>>>>>>>> +
> >>>>>>>>> + return 0;
> >>>>>>>>> +}
> >>>>>>>>> +
> >>>>>>>>> +static struct resource_table *
> >>>>>>>>> +stm32_rproc_tee_elf_find_loaded_rsc_table(struct rproc *rproc,
> >>>>>>>>> + const struct firmware *fw)
> >>>>>>>>> +{
> >>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>>>>>>> +
> >>>>>>>>> + return tee_rproc_get_loaded_rsc_table(ddata->trproc);
> >>>>>>>>> +}
> >>>>>>>>> +
> >>>>>>>>> +static int stm32_rproc_tee_start(struct rproc *rproc)
> >>>>>>>>> +{
> >>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>>>>>>> +
> >>>>>>>>> + return tee_rproc_start(ddata->trproc);
> >>>>>>>>> +}
> >>>>>>>>> +
> >>>>>>>>> +static int stm32_rproc_tee_attach(struct rproc *rproc)
> >>>>>>>>> +{
> >>>>>>>>> + /* Nothing to do, remote proc already started by the secured context. */
> >>>>>>>>> + return 0;
> >>>>>>>>> +}
> >>>>>>>>> +
> >>>>>>>>> +static int stm32_rproc_tee_stop(struct rproc *rproc)
> >>>>>>>>> +{
> >>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>>>>>>> + int err;
> >>>>>>>>> +
> >>>>>>>>> + stm32_rproc_request_shutdown(rproc);
> >>>>>>>>> +
> >>>>>>>>> + err = tee_rproc_stop(ddata->trproc);
> >>>>>>>>> + if (err)
> >>>>>>>>> + return err;
> >>>>>>>>> +
> >>>>>>>>> + ddata->fw_loaded = false;
> >>>>>>>>> +
> >>>>>>>>> + return stm32_rproc_release(rproc);
> >>>>>>>>> +}
> >>>>>>>>> +
> >>>>>>>>> static int stm32_rproc_prepare(struct rproc *rproc)
> >>>>>>>>> {
> >>>>>>>>> struct device *dev = rproc->dev.parent;
> >>>>>>>>> @@ -319,7 +410,14 @@ static int stm32_rproc_prepare(struct rproc *rproc)
> >>>>>>>>>
> >>>>>>>>> static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
> >>>>>>>>> {
> >>>>>>>>> - if (rproc_elf_load_rsc_table(rproc, fw))
> >>>>>>>>> + struct stm32_rproc *ddata = rproc->priv;
> >>>>>>>>> + int ret;
> >>>>>>>>> +
> >>>>>>>>> + if (ddata->trproc)
> >>>>>>>>> + ret = rproc_tee_get_rsc_table(ddata->trproc);
> >>>>>>>>> + else
> >>>>>>>>> + ret = rproc_elf_load_rsc_table(rproc, fw);
> >>>>>>>>> + if (ret)
> >>>>>>>>> dev_warn(&rproc->dev, "no resource table found for this firmware\n");
> >>>>>>>>>
> >>>>>>>>> return 0;
> >>>>>>>>> @@ -693,8 +791,22 @@ 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 = stm32_rproc_tee_start,
> >>>>>>>>> + .stop = stm32_rproc_tee_stop,
> >>>>>>>>> + .attach = stm32_rproc_tee_attach,
> >>>>>>>>> + .kick = stm32_rproc_kick,
> >>>>>>>>> + .parse_fw = stm32_rproc_parse_fw,
> >>>>>>>>> + .find_loaded_rsc_table = stm32_rproc_tee_elf_find_loaded_rsc_table,
> >>>>>>>>> + .get_loaded_rsc_table = stm32_rproc_get_loaded_rsc_table,
> >>>>>>>>> + .sanity_check = stm32_rproc_tee_elf_sanity_check,
> >>>>>>>>> + .load = stm32_rproc_tee_elf_load,
> >>>>>>>>> +};
> >>>>>>>>> +
> >>>>>>>>> static const struct of_device_id stm32_rproc_match[] = {
> >>>>>>>>> - { .compatible = "st,stm32mp1-m4" },
> >>>>>>>>> + {.compatible = "st,stm32mp1-m4",},
> >>>>>>>>> + {.compatible = "st,stm32mp1-m4-tee",},
> >>>>>>>>> {},
> >>>>>>>>> };
> >>>>>>>>> MODULE_DEVICE_TABLE(of, stm32_rproc_match);
> >>>>>>>>> @@ -853,6 +965,7 @@ 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;
> >>>>>>>>> int ret;
> >>>>>>>>> @@ -861,11 +974,31 @@ static int stm32_rproc_probe(struct platform_device *pdev)
> >>>>>>>>> if (ret)
> >>>>>>>>> return ret;
> >>>>>>>>>
> >>>>>>>>> - rproc = 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")) {
> >>>>>>>>> + trproc = tee_rproc_register(dev, STM32_MP1_M4_PROC_ID);
> >>>>>>>>> + if (IS_ERR(trproc)) {
> >>>>>>>>> + dev_err_probe(dev, PTR_ERR(trproc),
> >>>>>>>>> + "signed firmware not supported by TEE\n");
> >>>>>>>>> + return PTR_ERR(trproc);
> >>>>>>>>> + }
> >>>>>>>>> + /*
> >>>>>>>>> + * Delegate the firmware management to the secure context.
> >>>>>>>>> + * The firmware loaded has to be signed.
> >>>>>>>>> + */
> >>>>>>>>> + dev_info(dev, "Support of signed firmware only\n");
> >>>>>>>>
> >>>>>>>> Not sure what this adds. Please remove.
> >>>>>>>
> >>>>>>> This is used to inform the user that only a signed firmware can be loaded, not
> >>>>>>> an ELF file.
> >>>>>>> I have a patch in my pipe to provide the supported format in the debugfs. In a
> >>>>>>> first step, I can suppress this message and we can revisit the issue when I push
> >>>>>>> the debugfs proposal.
> >>>>>>>
> >>>>>>> Thanks,
> >>>>>>> Arnaud
> >>>>>>>
> >>>>>>>>
> >>>>>>>>> + }
> >>>>>>>>> + rproc = rproc_alloc(dev, np->name,
> >>>>>>>>> + trproc ? &st_rproc_tee_ops : &st_rproc_ops,
> >>>>>>>>> + NULL, sizeof(*ddata));
> >>>>>>>>> + if (!rproc) {
> >>>>>>>>> + ret = -ENOMEM;
> >>>>>>>>> + goto free_tee;
> >>>>>>>>> + }
> >>>>>>>>>
> >>>>>>>>> ddata = rproc->priv;
> >>>>>>>>> + ddata->trproc = trproc;
> >>>>>>>>> + if (trproc)
> >>>>>>>>> + trproc->rproc = rproc;
> >>>>>>>>>
> >>>>>>>>> rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
> >>>>>>>>>
> >>>>>>>>> @@ -916,6 +1049,10 @@ static int stm32_rproc_probe(struct platform_device *pdev)
> >>>>>>>>> device_init_wakeup(dev, false);
> >>>>>>>>> }
> >>>>>>>>> rproc_free(rproc);
> >>>>>>>>> +free_tee:
> >>>>>>>>> + if (trproc)
> >>>>>>>>> + tee_rproc_unregister(trproc);
> >>>>>>>>> +
> >>>>>>>>> return ret;
> >>>>>>>>> }
> >>>>>>>>>
> >>>>>>>>> @@ -937,6 +1074,8 @@ static void stm32_rproc_remove(struct platform_device *pdev)
> >>>>>>>>> device_init_wakeup(dev, false);
> >>>>>>>>> }
> >>>>>>>>> rproc_free(rproc);
> >>>>>>>>> + if (ddata->trproc)
> >>>>>>>>> + tee_rproc_unregister(ddata->trproc);
> >>>>>>>>> }
> >>>>>>>>>
> >>>>>>>>> static int stm32_rproc_suspend(struct device *dev)
> >>>>>>>>> --
> >>>>>>>>> 2.25.1
> >>>>>>>>>
> > _______________________________________________
> > Linux-stm32 mailing list
> > [email protected]
> > https://st-md-mailman.stormreply.com/mailman/listinfo/linux-stm32