* [PATCH v15 0/8] Introduction of a remoteproc tee to load signed firmware
@ 2024-11-28 8:42 Arnaud Pouliquen
2024-11-28 8:42 ` [PATCH v15 1/8] remoteproc: core: Introduce rproc_pa_to_va helper Arnaud Pouliquen
` (7 more replies)
0 siblings, 8 replies; 32+ messages in thread
From: Arnaud Pouliquen @ 2024-11-28 8:42 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin,
Alexandre Torgue, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Neil Armstrong,
Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
Matthias Brugger, AngeloGioacchino Del Regno, Patrice Chotard,
Fabien Dessenne, Arnaud Pouliquen
Cc: linux-remoteproc, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, imx, linux-amlogic, linux-mediatek, linux-arm-msm
Main updates from version V14[1]:
Fix Rename missing load() to load_segments() in pru_rproc.c.
Main updates from version V13[2]:
- Introduce new rproc_ops operation: load_fw() and release_fw(),
- Rename load() operation to load_segments() in rproc_ops structure and
drivers.
More details are available in each patch commit message.
[1] https://lore.kernel.org/linux-arm-kernel/20241126091042.918144-1-arnaud.pouliquen@foss.st.com/T/
[2] https://lore.kernel.org/linux-arm-kernel/20241104133515.256497-1-arnaud.pouliquen@foss.st.com/T/
Tested-on: commit adc218676eef ("Linux 6.12")
Description of the feature:
--------------------------
This series proposes the implementation of a remoteproc tee driver to
communicate with a TEE trusted application responsible for authenticating
and loading the remoteproc firmware image in an Arm secure context.
1) Principle:
The remoteproc tee driver provides services to communicate with the OP-TEE
trusted application running on the Trusted Execution Context (TEE).
The trusted application in TEE manages the remote processor lifecycle:
- authenticating and loading firmware images,
- isolating and securing the remote processor memories,
- supporting multi-firmware (e.g., TF-M + Zephyr on a Cortex-M33),
- managing the start and stop of the firmware by the TEE.
2) Format of the signed image:
Refer to:
https://github.com/OP-TEE/optee_os/blob/master/ta/remoteproc/src/remoteproc_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 (8):
remoteproc: core: Introduce rproc_pa_to_va helper
remoteproc: Add TEE support
remoteproc: Introduce load_fw and release_fw optional operation
remoteproc: Rename load() operation to load_segments() in rproc_ops
struct
remoteproc: Make load_segments and load_fw ops exclusive and optional
dt-bindings: remoteproc: Add compatibility for TEE support
remoteproc: stm32: Create sub-functions to request shutdown and
release
remoteproc: stm32: Add support of an OP-TEE TA to load the firmware
.../bindings/remoteproc/st,stm32-rproc.yaml | 58 +-
drivers/remoteproc/Kconfig | 10 +
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/imx_dsp_rproc.c | 2 +-
drivers/remoteproc/imx_rproc.c | 2 +-
drivers/remoteproc/meson_mx_ao_arc.c | 2 +-
drivers/remoteproc/mtk_scp.c | 2 +-
drivers/remoteproc/pru_rproc.c | 2 +-
drivers/remoteproc/qcom_q6v5_adsp.c | 2 +-
drivers/remoteproc/qcom_q6v5_mss.c | 2 +-
drivers/remoteproc/qcom_q6v5_pas.c | 4 +-
drivers/remoteproc/qcom_q6v5_wcss.c | 4 +-
drivers/remoteproc/qcom_wcnss.c | 2 +-
drivers/remoteproc/rcar_rproc.c | 2 +-
drivers/remoteproc/remoteproc_core.c | 68 ++-
drivers/remoteproc/remoteproc_internal.h | 20 +-
drivers/remoteproc/remoteproc_tee.c | 508 ++++++++++++++++++
drivers/remoteproc/st_remoteproc.c | 2 +-
drivers/remoteproc/st_slim_rproc.c | 2 +-
drivers/remoteproc/stm32_rproc.c | 141 +++--
drivers/remoteproc/xlnx_r5_remoteproc.c | 2 +-
include/linux/remoteproc.h | 20 +-
include/linux/remoteproc_tee.h | 105 ++++
23 files changed, 894 insertions(+), 69 deletions(-)
create mode 100644 drivers/remoteproc/remoteproc_tee.c
create mode 100644 include/linux/remoteproc_tee.h
base-commit: adc218676eef25575469234709c2d87185ca223a
--
2.25.1
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH v15 1/8] remoteproc: core: Introduce rproc_pa_to_va helper
2024-11-28 8:42 [PATCH v15 0/8] Introduction of a remoteproc tee to load signed firmware Arnaud Pouliquen
@ 2024-11-28 8:42 ` Arnaud Pouliquen
2024-11-28 8:42 ` [PATCH v15 2/8] remoteproc: Add TEE support Arnaud Pouliquen
` (6 subsequent siblings)
7 siblings, 0 replies; 32+ messages in thread
From: Arnaud Pouliquen @ 2024-11-28 8:42 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier
Cc: Arnaud Pouliquen, linux-remoteproc, linux-kernel
When a resource table is loaded by an external entity such as U-boot or
OP-TEE, we do not necessarily get the device address(da) but the physical
address(pa).
This helper performs similar translation than the rproc_da_to_va()
but based on a physical address.
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
---
drivers/remoteproc/remoteproc_core.c | 46 ++++++++++++++++++++++++++++
include/linux/remoteproc.h | 1 +
2 files changed, 47 insertions(+)
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index f276956f2c5c..ace11ea17097 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -230,6 +230,52 @@ void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
}
EXPORT_SYMBOL(rproc_da_to_va);
+/**
+ * rproc_pa_to_va() - lookup the kernel virtual address for a physical address of a remoteproc
+ * memory
+ *
+ * @rproc: handle of a remote processor
+ * @pa: remoteproc physical address
+ * @len: length of the memory region @pa is pointing to
+ * @is_iomem: optional pointer filled in to indicate if @da is iomapped memory
+ *
+ * This function is a helper function similar to rproc_da_to_va() but it deals with physical
+ * addresses instead of device addresses.
+ *
+ * Return: a valid kernel address on success or NULL on failure
+ */
+void *rproc_pa_to_va(struct rproc *rproc, phys_addr_t pa, size_t len, bool *is_iomem)
+{
+ struct rproc_mem_entry *carveout;
+ void *ptr = NULL;
+
+ list_for_each_entry(carveout, &rproc->carveouts, node) {
+ int offset = pa - carveout->dma;
+
+ /* Verify that carveout is allocated */
+ if (!carveout->va)
+ continue;
+
+ /* try next carveout if da is too small */
+ if (offset < 0)
+ continue;
+
+ /* try next carveout if da is too large */
+ if (offset + len > carveout->len)
+ continue;
+
+ ptr = carveout->va + offset;
+
+ if (is_iomem)
+ *is_iomem = carveout->is_iomem;
+
+ break;
+ }
+
+ return ptr;
+}
+EXPORT_SYMBOL(rproc_pa_to_va);
+
/**
* rproc_find_carveout_by_name() - lookup the carveout region by a name
* @rproc: handle of a remote processor
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index b4795698d8c2..8fd0d7f63c8e 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -690,6 +690,7 @@ int rproc_detach(struct rproc *rproc);
int rproc_set_firmware(struct rproc *rproc, const char *fw_name);
void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type);
void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem);
+void *rproc_pa_to_va(struct rproc *rproc, phys_addr_t pa, size_t len, bool *is_iomem);
/* from remoteproc_coredump.c */
void rproc_coredump_cleanup(struct rproc *rproc);
--
2.25.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v15 2/8] remoteproc: Add TEE support
2024-11-28 8:42 [PATCH v15 0/8] Introduction of a remoteproc tee to load signed firmware Arnaud Pouliquen
2024-11-28 8:42 ` [PATCH v15 1/8] remoteproc: core: Introduce rproc_pa_to_va helper Arnaud Pouliquen
@ 2024-11-28 8:42 ` Arnaud Pouliquen
2024-12-03 17:04 ` Mathieu Poirier
2024-12-06 22:07 ` Bjorn Andersson
2024-11-28 8:42 ` [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation Arnaud Pouliquen
` (5 subsequent siblings)
7 siblings, 2 replies; 32+ messages in thread
From: Arnaud Pouliquen @ 2024-11-28 8:42 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier
Cc: Arnaud Pouliquen, linux-kernel, linux-remoteproc
Add a remoteproc TEE (Trusted Execution Environment) driver
that will be probed by the TEE bus. If the associated Trusted
application is supported on secure part this driver offers a client
interface to load a firmware by the secure part.
This firmware could be authenticated by the secure trusted application.
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
---
Updates vs version v13:
- define REMOTEPROC_TEE as bool instead of tristate,
- remove the load of the firmware in rproc_tee_parse_fw as we will ensure
that the firmware is loaded using the load_fw() operation.
---
drivers/remoteproc/Kconfig | 10 +
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/remoteproc_tee.c | 508 ++++++++++++++++++++++++++++
include/linux/remoteproc.h | 4 +
include/linux/remoteproc_tee.h | 105 ++++++
5 files changed, 628 insertions(+)
create mode 100644 drivers/remoteproc/remoteproc_tee.c
create mode 100644 include/linux/remoteproc_tee.h
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 955e4e38477e..f6335321d540 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -23,6 +23,16 @@ config REMOTEPROC_CDEV
It's safe to say N if you don't want to use this interface.
+config REMOTEPROC_TEE
+ bool "Remoteproc support by a TEE application"
+ depends on OPTEE
+ help
+ Support a remote processor with a TEE application. The Trusted
+ Execution Context is responsible for loading the trusted firmware
+ image and managing the remote processor's lifecycle.
+
+ It's safe to say N if you don't want to use remoteproc TEE.
+
config IMX_REMOTEPROC
tristate "i.MX remoteproc support"
depends on ARCH_MXC
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 5ff4e2fee4ab..f77e0abe8349 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -11,6 +11,7 @@ remoteproc-y += remoteproc_sysfs.o
remoteproc-y += remoteproc_virtio.o
remoteproc-y += remoteproc_elf_loader.o
obj-$(CONFIG_REMOTEPROC_CDEV) += remoteproc_cdev.o
+obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o
obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o
obj-$(CONFIG_IMX_DSP_REMOTEPROC) += imx_dsp_rproc.o
obj-$(CONFIG_INGENIC_VPU_RPROC) += ingenic_rproc.o
diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c
new file mode 100644
index 000000000000..3fe3f31068f2
--- /dev/null
+++ b/drivers/remoteproc/remoteproc_tee.c
@@ -0,0 +1,508 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) STMicroelectronics 2024
+ * Author: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
+ */
+
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/remoteproc.h>
+#include <linux/remoteproc_tee.h>
+#include <linux/slab.h>
+#include <linux/tee_drv.h>
+
+#define MAX_TEE_PARAM_ARRAY_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
+
+/*
+ * Release remote processor firmware images and associated resources.
+ * This command should be used in case an error occurs between the loading of
+ * the firmware images (TA_RPROC_CMD_LOAD_FW) and the starting of the remote
+ * processor (TA_RPROC_CMD_START_FW) or after stopping the remote processor
+ * to release associated resources (TA_RPROC_CMD_STOP_FW).
+ *
+ * [in] params[0].value.a: Unique 32-bit remote processor identifier
+ */
+#define TA_RPROC_CMD_RELEASE_FW 6
+
+struct rproc_tee_context {
+ struct list_head sessions;
+ struct tee_context *tee_ctx;
+ struct device *dev;
+};
+
+static struct rproc_tee_context *rproc_tee_ctx;
+
+static void rproc_tee_prepare_args(struct rproc_tee *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_ARRAY_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,
+ };
+}
+
+void rproc_tee_release_fw(struct rproc *rproc)
+{
+ struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
+ struct rproc_tee *trproc = rproc->rproc_tee_itf;
+ struct tee_ioctl_invoke_arg arg;
+ int ret;
+
+ if (!rproc) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * If the remote processor state is RPROC_DETACHED, just ignore the
+ * request, as the remote processor is still running.
+ */
+ if (rproc->state == RPROC_DETACHED)
+ return;
+
+ if (rproc->state != RPROC_OFFLINE) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ rproc_tee_prepare_args(trproc, TA_RPROC_CMD_RELEASE_FW, &arg, param, 0);
+
+ ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
+ if (ret < 0 || arg.ret != 0) {
+ dev_err(rproc_tee_ctx->dev,
+ "TA_RPROC_CMD_RELEASE_FW invoke failed TEE err: %x, ret:%x\n",
+ arg.ret, ret);
+ ret = -EIO;
+ }
+
+out:
+ if (ret)
+ /* Unexpected state without solution to come back in a stable state */
+ dev_err(rproc_tee_ctx->dev, "Failed to release TEE remoteproc firmware: %d\n", ret);
+}
+EXPORT_SYMBOL_GPL(rproc_tee_release_fw);
+
+int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw)
+{
+ struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
+ struct rproc_tee *trproc = rproc->rproc_tee_itf;
+ struct tee_ioctl_invoke_arg arg;
+ struct tee_shm *fw_shm;
+ int ret;
+
+ if (!trproc)
+ return -EINVAL;
+
+ fw_shm = tee_shm_register_kernel_buf(rproc_tee_ctx->tee_ctx, (void *)fw->data, fw->size);
+ if (IS_ERR(fw_shm))
+ return PTR_ERR(fw_shm);
+
+ rproc_tee_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(rproc_tee_ctx->tee_ctx, &arg, param);
+ if (ret < 0 || arg.ret != 0) {
+ dev_err(rproc_tee_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(rproc_tee_load_fw);
+
+static int rproc_tee_get_loaded_rsc_table(struct rproc *rproc, phys_addr_t *rsc_pa,
+ size_t *table_sz)
+{
+ struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
+ struct rproc_tee *trproc = rproc->rproc_tee_itf;
+ struct tee_ioctl_invoke_arg arg;
+ int ret;
+
+ if (!trproc)
+ return -EINVAL;
+
+ rproc_tee_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(rproc_tee_ctx->tee_ctx, &arg, param);
+ if (ret < 0 || arg.ret != 0) {
+ dev_err(rproc_tee_ctx->dev,
+ "TA_RPROC_FW_CMD_GET_RSC_TABLE invoke failed TEE err: %x, ret:%x\n",
+ arg.ret, ret);
+ return -EIO;
+ }
+
+ *table_sz = param[2].u.value.a;
+
+ if (*table_sz)
+ *rsc_pa = param[1].u.value.a;
+ else
+ *rsc_pa = 0;
+
+ return 0;
+}
+
+int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw)
+{
+ phys_addr_t rsc_table;
+ void __iomem *rsc_va;
+ size_t table_sz;
+ int ret;
+
+ if (!rproc)
+ return -EINVAL;
+
+ /* At this point, the firmware has to be loaded to be able to parse the resource table. */
+
+ ret = rproc_tee_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
+ if (ret)
+ goto release_fw;
+
+ /*
+ * We assume here that the memory mapping is the same between the TEE and Linux kernel
+ * contexts. Else a new TEE remoteproc service could be needed to get a copy of the
+ * resource table
+ */
+ rsc_va = ioremap_wc(rsc_table, table_sz);
+ if (IS_ERR_OR_NULL(rsc_va)) {
+ dev_err(rproc_tee_ctx->dev, "Unable to map memory region: %pa+%zx\n",
+ &rsc_table, table_sz);
+ ret = -ENOMEM;
+ goto release_fw;
+ }
+
+ /*
+ * Create a copy of the resource table to have the same behavior as the ELF loader.
+ * This cached table will be used by the remoteproc core after the remoteproc stops
+ * to free resources and for crash recovery to reapply the settings.
+ * The cached table will be freed by the remoteproc core.
+ */
+ rproc->cached_table = kmemdup((__force void *)rsc_va, table_sz, GFP_KERNEL);
+ iounmap(rsc_va);
+
+ if (!rproc->cached_table) {
+ ret = -ENOMEM;
+ goto release_fw;
+ }
+
+ rproc->table_ptr = rproc->cached_table;
+ rproc->table_sz = table_sz;
+
+ return 0;
+
+release_fw:
+ rproc_tee_release_fw(rproc);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rproc_tee_parse_fw);
+
+struct resource_table *rproc_tee_find_loaded_rsc_table(struct rproc *rproc,
+ const struct firmware *fw)
+{
+ phys_addr_t rsc_table;
+ size_t table_sz;
+ int ret;
+
+ ret = rproc_tee_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
+ if (ret)
+ return NULL;
+
+ rproc->table_sz = table_sz;
+ if (!table_sz)
+ return NULL;
+
+ /*
+ * At this step the memory area that contains the resource table should have been registered
+ * by the remote proc platform driver and allocated by rproc_alloc_registered_carveouts().
+ */
+ return (struct resource_table *)rproc_pa_to_va(rproc, rsc_table, table_sz, NULL);
+}
+EXPORT_SYMBOL_GPL(rproc_tee_find_loaded_rsc_table);
+
+int rproc_tee_start(struct rproc *rproc)
+{
+ struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
+ struct rproc_tee *trproc = rproc->rproc_tee_itf;
+ struct tee_ioctl_invoke_arg arg;
+ int ret = 0;
+
+ if (!trproc)
+ return -EINVAL;
+
+ rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_START_FW, &arg, param, 0);
+
+ ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
+ if (ret < 0 || arg.ret != 0) {
+ dev_err(rproc_tee_ctx->dev,
+ "TA_RPROC_FW_CMD_START_FW invoke failed TEE err: %x, ret:%x\n",
+ arg.ret, ret);
+ if (!ret)
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rproc_tee_start);
+
+int rproc_tee_stop(struct rproc *rproc)
+{
+ struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
+ struct rproc_tee *trproc = rproc->rproc_tee_itf;
+ struct tee_ioctl_invoke_arg arg;
+ int ret;
+
+ if (!trproc)
+ return -EINVAL;
+
+ rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_STOP_FW, &arg, param, 0);
+
+ ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
+ if (ret < 0 || arg.ret != 0) {
+ dev_err(rproc_tee_ctx->dev,
+ "TA_RPROC_FW_CMD_STOP_FW invoke failed TEE err: %x, ret:%x\n",
+ arg.ret, ret);
+ if (!ret)
+ ret = -EIO;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rproc_tee_stop);
+
+static const struct tee_client_device_id rproc_tee_id_table[] = {
+ {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)},
+ {}
+};
+
+int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id)
+{
+ struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
+ struct tee_ioctl_open_session_arg sess_arg;
+ struct tee_client_device *tee_device;
+ struct rproc_tee *trproc;
+ int ret;
+
+ /*
+ * Test if the device has been probed by the TEE bus. In case of failure, we ignore the
+ * reason. The bus could be not yet probed or the service not available in the secure
+ * firmware.The assumption in such a case is that the TEE remoteproc is not probed.
+ */
+ if (!rproc_tee_ctx)
+ return -EPROBE_DEFER;
+
+ /* Prevent rproc tee module from being removed */
+ if (!try_module_get(THIS_MODULE)) {
+ dev_err(rproc_tee_ctx->dev, "can't get owner\n");
+ return -ENODEV;
+ }
+
+ trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
+ if (!trproc) {
+ ret = -ENOMEM;
+ goto module_put;
+ }
+
+ tee_device = to_tee_client_device(rproc_tee_ctx->dev);
+ memset(&sess_arg, 0, sizeof(sess_arg));
+
+ memcpy(sess_arg.uuid, tee_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
+
+ sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
+ sess_arg.num_params = 1;
+
+ param[0] = (struct tee_param) {
+ .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
+ .u.value.a = rproc_id,
+ };
+
+ ret = tee_client_open_session(rproc_tee_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);
+ ret = -EINVAL;
+ goto module_put;
+ }
+
+ trproc->parent = dev;
+ trproc->rproc_id = rproc_id;
+ trproc->session_id = sess_arg.session;
+
+ trproc->rproc = rproc;
+ rproc->rproc_tee_itf = trproc;
+
+ list_add_tail(&trproc->node, &rproc_tee_ctx->sessions);
+
+ return 0;
+
+module_put:
+ module_put(THIS_MODULE);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rproc_tee_register);
+
+int rproc_tee_unregister(struct rproc *rproc)
+{
+ struct rproc_tee *trproc = rproc->rproc_tee_itf;
+ int ret;
+
+ if (!rproc->rproc_tee_itf)
+ return -ENODEV;
+
+ ret = tee_client_close_session(rproc_tee_ctx->tee_ctx, trproc->session_id);
+ if (ret < 0)
+ dev_err(trproc->parent, "tee_client_close_session failed, err: %x\n", ret);
+
+ list_del(&trproc->node);
+ rproc->rproc_tee_itf = NULL;
+
+ module_put(THIS_MODULE);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rproc_tee_unregister);
+
+static int rproc_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 rproc_tee_probe(struct device *dev)
+{
+ struct tee_context *tee_ctx;
+ int ret;
+
+ /* Open context with TEE driver */
+ tee_ctx = tee_client_open_context(NULL, rproc_tee_ctx_match, NULL, NULL);
+ if (IS_ERR(tee_ctx))
+ return PTR_ERR(tee_ctx);
+
+ rproc_tee_ctx = devm_kzalloc(dev, sizeof(*rproc_tee_ctx), GFP_KERNEL);
+ if (!rproc_tee_ctx) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ rproc_tee_ctx->dev = dev;
+ rproc_tee_ctx->tee_ctx = tee_ctx;
+ INIT_LIST_HEAD(&rproc_tee_ctx->sessions);
+
+ return 0;
+err:
+ tee_client_close_context(tee_ctx);
+
+ return ret;
+}
+
+static int rproc_tee_remove(struct device *dev)
+{
+ struct rproc_tee *entry, *tmp;
+
+ list_for_each_entry_safe(entry, tmp, &rproc_tee_ctx->sessions, node) {
+ tee_client_close_session(rproc_tee_ctx->tee_ctx, entry->session_id);
+ list_del(&entry->node);
+ kfree(entry);
+ }
+
+ tee_client_close_context(rproc_tee_ctx->tee_ctx);
+
+ return 0;
+}
+
+MODULE_DEVICE_TABLE(tee, rproc_tee_id_table);
+
+static struct tee_client_driver rproc_tee_fw_driver = {
+ .id_table = rproc_tee_id_table,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .bus = &tee_bus_type,
+ .probe = rproc_tee_probe,
+ .remove = rproc_tee_remove,
+ },
+};
+
+static int __init rproc_tee_fw_mod_init(void)
+{
+ return driver_register(&rproc_tee_fw_driver.driver);
+}
+
+static void __exit rproc_tee_fw_mod_exit(void)
+{
+ driver_unregister(&rproc_tee_fw_driver.driver);
+}
+
+module_init(rproc_tee_fw_mod_init);
+module_exit(rproc_tee_fw_mod_exit);
+
+MODULE_DESCRIPTION(" remote processor TEE module");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index 8fd0d7f63c8e..2e0ddcb2d792 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -503,6 +503,8 @@ enum rproc_features {
RPROC_MAX_FEATURES,
};
+struct rproc_tee;
+
/**
* struct rproc - represents a physical remote processor device
* @node: list node of this rproc object
@@ -545,6 +547,7 @@ enum rproc_features {
* @cdev: character device of the rproc
* @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release
* @features: indicate remoteproc features
+ * @rproc_tee_itf: pointer to the remoteproc tee context
*/
struct rproc {
struct list_head node;
@@ -586,6 +589,7 @@ struct rproc {
struct cdev cdev;
bool cdev_put_on_release;
DECLARE_BITMAP(features, RPROC_MAX_FEATURES);
+ struct rproc_tee *rproc_tee_itf;
};
/**
diff --git a/include/linux/remoteproc_tee.h b/include/linux/remoteproc_tee.h
new file mode 100644
index 000000000000..9b498a8eff4d
--- /dev/null
+++ b/include/linux/remoteproc_tee.h
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright(c) 2024 STMicroelectronics
+ */
+
+#ifndef REMOTEPROC_TEE_H
+#define REMOTEPROC_TEE_H
+
+#include <linux/tee_drv.h>
+#include <linux/firmware.h>
+#include <linux/remoteproc.h>
+
+struct rproc;
+
+/**
+ * struct rproc_tee - TEE remoteproc structure
+ * @node: Reference in list
+ * @rproc: Remoteproc reference
+ * @parent: Parent device
+ * @rproc_id: Identifier of the target firmware
+ * @session_id: TEE session identifier
+ */
+struct rproc_tee {
+ struct list_head node;
+ struct rproc *rproc;
+ struct device *parent;
+ u32 rproc_id;
+ u32 session_id;
+};
+
+#if IS_REACHABLE(CONFIG_REMOTEPROC_TEE)
+
+int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id);
+int rproc_tee_unregister(struct rproc *rproc);
+int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw);
+int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw);
+void rproc_tee_release_fw(struct rproc *rproc);
+struct resource_table *rproc_tee_find_loaded_rsc_table(struct rproc *rproc,
+ const struct firmware *fw);
+int rproc_tee_start(struct rproc *rproc);
+int rproc_tee_stop(struct rproc *rproc);
+
+#else
+
+static inline int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id)
+{
+ return -ENODEV;
+}
+
+static inline int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+
+ return 0;
+}
+
+static inline int rproc_tee_unregister(struct rproc *rproc)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+
+ return 0;
+}
+
+static inline int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+
+ return 0;
+}
+
+static inline int rproc_tee_start(struct rproc *rproc)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+
+ return 0;
+}
+
+static inline int rproc_tee_stop(struct rproc *rproc)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+
+ return 0;
+}
+
+static inline void rproc_tee_release_fw(struct rproc *rproc)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+}
+
+static inline struct resource_table *
+rproc_tee_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
+{
+ /* This shouldn't be possible */
+ WARN_ON(1);
+
+ return NULL;
+}
+#endif /* CONFIG_REMOTEPROC_TEE */
+#endif /* REMOTEPROC_TEE_H */
--
2.25.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation
2024-11-28 8:42 [PATCH v15 0/8] Introduction of a remoteproc tee to load signed firmware Arnaud Pouliquen
2024-11-28 8:42 ` [PATCH v15 1/8] remoteproc: core: Introduce rproc_pa_to_va helper Arnaud Pouliquen
2024-11-28 8:42 ` [PATCH v15 2/8] remoteproc: Add TEE support Arnaud Pouliquen
@ 2024-11-28 8:42 ` Arnaud Pouliquen
2024-12-03 17:22 ` Mathieu Poirier
` (2 more replies)
2024-11-28 8:42 ` [PATCH v15 4/8] remoteproc: Rename load() operation to load_segments() in rproc_ops struct Arnaud Pouliquen
` (4 subsequent siblings)
7 siblings, 3 replies; 32+ messages in thread
From: Arnaud Pouliquen @ 2024-11-28 8:42 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier
Cc: Arnaud Pouliquen, linux-remoteproc, linux-kernel
This patch updates the rproc_ops structures to include two new optional
operations.
- The load_fw() op is responsible for loading the remote processor
non-ELF firmware image before starting the boot sequence. This ops will
be used, for instance, to call OP-TEE to authenticate an load the firmware
image before accessing to its resources (a.e the resource table)
- The release_fw op is responsible for releasing the remote processor
firmware image. For instance to clean memories.
The ops is called in the following cases:
- An error occurs between the loading of the firmware image and the
start of the remote processor.
- after stopping the remote processor.
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
---
Update vs version V13:
- Rework the commit to introduce load_fw() op.
- remove rproc_release_fw() call from rproc_start() as called in
rproc_boot() and rproc_boot_recovery() in case of error.
- create rproc_load_fw() and rproc_release_fw() internal functions.
---
drivers/remoteproc/remoteproc_core.c | 16 +++++++++++++++-
drivers/remoteproc/remoteproc_internal.h | 14 ++++++++++++++
include/linux/remoteproc.h | 6 ++++++
3 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index ace11ea17097..8df4b2c59bb6 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -1488,6 +1488,7 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
kfree(rproc->cached_table);
rproc->cached_table = NULL;
rproc->table_ptr = NULL;
+ rproc_release_fw(rproc);
unprepare_rproc:
/* release HW resources if needed */
rproc_unprepare_device(rproc);
@@ -1855,8 +1856,14 @@ static int rproc_boot_recovery(struct rproc *rproc)
return ret;
}
+ ret = rproc_load_fw(rproc, firmware_p);
+ if (ret)
+ return ret;
+
/* boot the remote processor up again */
ret = rproc_start(rproc, firmware_p);
+ if (ret)
+ rproc_release_fw(rproc);
release_firmware(firmware_p);
@@ -1997,7 +2004,13 @@ int rproc_boot(struct rproc *rproc)
goto downref_rproc;
}
+ ret = rproc_load_fw(rproc, firmware_p);
+ if (ret)
+ goto downref_rproc;
+
ret = rproc_fw_boot(rproc, firmware_p);
+ if (ret)
+ rproc_release_fw(rproc);
release_firmware(firmware_p);
}
@@ -2071,6 +2084,7 @@ int rproc_shutdown(struct rproc *rproc)
kfree(rproc->cached_table);
rproc->cached_table = NULL;
rproc->table_ptr = NULL;
+ rproc_release_fw(rproc);
out:
mutex_unlock(&rproc->lock);
return ret;
@@ -2471,7 +2485,7 @@ static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
if (!rproc->ops->coredump)
rproc->ops->coredump = rproc_coredump;
- if (rproc->ops->load)
+ if (rproc->ops->load || rproc->ops->load_fw)
return 0;
/* Default to ELF loader if no load function is specified */
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
index 0cd09e67ac14..2104ca449178 100644
--- a/drivers/remoteproc/remoteproc_internal.h
+++ b/drivers/remoteproc/remoteproc_internal.h
@@ -221,4 +221,18 @@ bool rproc_u64_fit_in_size_t(u64 val)
return (val <= (size_t) -1);
}
+static inline void rproc_release_fw(struct rproc *rproc)
+{
+ if (rproc->ops->release_fw)
+ rproc->ops->release_fw(rproc);
+}
+
+static inline int rproc_load_fw(struct rproc *rproc, const struct firmware *fw)
+{
+ if (rproc->ops->load_fw)
+ return rproc->ops->load_fw(rproc, fw);
+
+ return 0;
+}
+
#endif /* REMOTEPROC_INTERNAL_H */
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index 2e0ddcb2d792..ba6fd560f7ba 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -381,6 +381,10 @@ enum rsc_handling_status {
* @panic: optional callback to react to system panic, core will delay
* panic at least the returned number of milliseconds
* @coredump: collect firmware dump after the subsystem is shutdown
+ * @load_fw: optional function to load non-ELF firmware image to memory, where the remote
+ * processor expects to find it.
+ * @release_fw: optional function to release the firmware image from memories.
+ * This function is called after stopping the remote processor or in case of error
*/
struct rproc_ops {
int (*prepare)(struct rproc *rproc);
@@ -403,6 +407,8 @@ struct rproc_ops {
u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
unsigned long (*panic)(struct rproc *rproc);
void (*coredump)(struct rproc *rproc);
+ int (*load_fw)(struct rproc *rproc, const struct firmware *fw);
+ void (*release_fw)(struct rproc *rproc);
};
/**
--
2.25.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v15 4/8] remoteproc: Rename load() operation to load_segments() in rproc_ops struct
2024-11-28 8:42 [PATCH v15 0/8] Introduction of a remoteproc tee to load signed firmware Arnaud Pouliquen
` (2 preceding siblings ...)
2024-11-28 8:42 ` [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation Arnaud Pouliquen
@ 2024-11-28 8:42 ` Arnaud Pouliquen
2024-12-04 18:02 ` Mathieu Poirier
2024-11-28 8:42 ` [PATCH v15 5/8] remoteproc: Make load_segments and load_fw ops exclusive and optional Arnaud Pouliquen
` (3 subsequent siblings)
7 siblings, 1 reply; 32+ messages in thread
From: Arnaud Pouliquen @ 2024-11-28 8:42 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier, Shawn Guo, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Neil Armstrong,
Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
Matthias Brugger, AngeloGioacchino Del Regno, Patrice Chotard,
Maxime Coquelin, Alexandre Torgue
Cc: Arnaud Pouliquen, kernel test robot, linux-remoteproc, imx,
linux-arm-kernel, linux-kernel, linux-amlogic, linux-mediatek,
linux-arm-msm, linux-stm32
With the introduction of the load_fw() operation in the rproc_ops
structure, we need to clarify the difference in the use of the load()
and load_fw() ops.
The legacy load() is dedicated to loading the ELF segments into memory.
Rename this to a more explicit name: load_segments().
Suggested-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
---
Update vs version V14:
Fix: Rename missing load() to load_segments() in drivers/remoteproc/pru_rproc.c.
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202411281332.Ra70nJAW-lkp@intel.com/
---
drivers/remoteproc/imx_dsp_rproc.c | 2 +-
drivers/remoteproc/imx_rproc.c | 2 +-
drivers/remoteproc/meson_mx_ao_arc.c | 2 +-
drivers/remoteproc/mtk_scp.c | 2 +-
drivers/remoteproc/pru_rproc.c | 2 +-
drivers/remoteproc/qcom_q6v5_adsp.c | 2 +-
drivers/remoteproc/qcom_q6v5_mss.c | 2 +-
drivers/remoteproc/qcom_q6v5_pas.c | 4 ++--
drivers/remoteproc/qcom_q6v5_wcss.c | 4 ++--
drivers/remoteproc/qcom_wcnss.c | 2 +-
drivers/remoteproc/rcar_rproc.c | 2 +-
drivers/remoteproc/remoteproc_core.c | 4 ++--
drivers/remoteproc/remoteproc_internal.h | 4 ++--
drivers/remoteproc/st_remoteproc.c | 2 +-
drivers/remoteproc/st_slim_rproc.c | 2 +-
drivers/remoteproc/stm32_rproc.c | 2 +-
drivers/remoteproc/xlnx_r5_remoteproc.c | 2 +-
include/linux/remoteproc.h | 4 ++--
18 files changed, 23 insertions(+), 23 deletions(-)
diff --git a/drivers/remoteproc/imx_dsp_rproc.c b/drivers/remoteproc/imx_dsp_rproc.c
index 376187ad5754..a4a85fbce907 100644
--- a/drivers/remoteproc/imx_dsp_rproc.c
+++ b/drivers/remoteproc/imx_dsp_rproc.c
@@ -934,7 +934,7 @@ static const struct rproc_ops imx_dsp_rproc_ops = {
.start = imx_dsp_rproc_start,
.stop = imx_dsp_rproc_stop,
.kick = imx_dsp_rproc_kick,
- .load = imx_dsp_rproc_elf_load_segments,
+ .load_segments = imx_dsp_rproc_elf_load_segments,
.parse_fw = imx_dsp_rproc_parse_fw,
.find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
.sanity_check = rproc_elf_sanity_check,
diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c
index 800015ff7ff9..f45b3207f7e9 100644
--- a/drivers/remoteproc/imx_rproc.c
+++ b/drivers/remoteproc/imx_rproc.c
@@ -681,7 +681,7 @@ static const struct rproc_ops imx_rproc_ops = {
.stop = imx_rproc_stop,
.kick = imx_rproc_kick,
.da_to_va = imx_rproc_da_to_va,
- .load = rproc_elf_load_segments,
+ .load_segments = rproc_elf_load_segments,
.parse_fw = imx_rproc_parse_fw,
.find_loaded_rsc_table = imx_rproc_elf_find_loaded_rsc_table,
.get_loaded_rsc_table = imx_rproc_get_loaded_rsc_table,
diff --git a/drivers/remoteproc/meson_mx_ao_arc.c b/drivers/remoteproc/meson_mx_ao_arc.c
index f6744b538323..a1c8c0929ce3 100644
--- a/drivers/remoteproc/meson_mx_ao_arc.c
+++ b/drivers/remoteproc/meson_mx_ao_arc.c
@@ -137,7 +137,7 @@ static struct rproc_ops meson_mx_ao_arc_rproc_ops = {
.stop = meson_mx_ao_arc_rproc_stop,
.da_to_va = meson_mx_ao_arc_rproc_da_to_va,
.get_boot_addr = rproc_elf_get_boot_addr,
- .load = rproc_elf_load_segments,
+ .load_segments = rproc_elf_load_segments,
.sanity_check = rproc_elf_sanity_check,
};
diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c
index e744c07507ee..4e9a8bf3bc6e 100644
--- a/drivers/remoteproc/mtk_scp.c
+++ b/drivers/remoteproc/mtk_scp.c
@@ -924,7 +924,7 @@ static int scp_stop(struct rproc *rproc)
static const struct rproc_ops scp_ops = {
.start = scp_start,
.stop = scp_stop,
- .load = scp_load,
+ .load_segments = scp_load,
.da_to_va = scp_da_to_va,
.parse_fw = scp_parse_fw,
.sanity_check = rproc_elf_sanity_check,
diff --git a/drivers/remoteproc/pru_rproc.c b/drivers/remoteproc/pru_rproc.c
index 327f0c7ee3d6..0b2bf2574f74 100644
--- a/drivers/remoteproc/pru_rproc.c
+++ b/drivers/remoteproc/pru_rproc.c
@@ -1015,7 +1015,7 @@ static int pru_rproc_probe(struct platform_device *pdev)
return -ENOMEM;
}
/* use a custom load function to deal with PRU-specific quirks */
- rproc->ops->load = pru_rproc_load_elf_segments;
+ rproc->ops->load_segments = pru_rproc_load_elf_segments;
/* use a custom parse function to deal with PRU-specific resources */
rproc->ops->parse_fw = pru_rproc_parse_fw;
diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c
index 572dcb0f055b..aa9896930bcf 100644
--- a/drivers/remoteproc/qcom_q6v5_adsp.c
+++ b/drivers/remoteproc/qcom_q6v5_adsp.c
@@ -527,7 +527,7 @@ static const struct rproc_ops adsp_ops = {
.stop = adsp_stop,
.da_to_va = adsp_da_to_va,
.parse_fw = adsp_parse_firmware,
- .load = adsp_load,
+ .load_segments = adsp_load,
.panic = adsp_panic,
};
diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c
index 2a42215ce8e0..a8beac1deabe 100644
--- a/drivers/remoteproc/qcom_q6v5_mss.c
+++ b/drivers/remoteproc/qcom_q6v5_mss.c
@@ -1687,7 +1687,7 @@ static const struct rproc_ops q6v5_ops = {
.start = q6v5_start,
.stop = q6v5_stop,
.parse_fw = qcom_q6v5_register_dump_segments,
- .load = q6v5_load,
+ .load_segments = q6v5_load,
.panic = q6v5_panic,
};
diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
index ef82835e98a4..9b269ce390c1 100644
--- a/drivers/remoteproc/qcom_q6v5_pas.c
+++ b/drivers/remoteproc/qcom_q6v5_pas.c
@@ -436,7 +436,7 @@ static const struct rproc_ops adsp_ops = {
.stop = adsp_stop,
.da_to_va = adsp_da_to_va,
.parse_fw = qcom_register_dump_segments,
- .load = adsp_load,
+ .load_segments = adsp_load,
.panic = adsp_panic,
};
@@ -446,7 +446,7 @@ static const struct rproc_ops adsp_minidump_ops = {
.stop = adsp_stop,
.da_to_va = adsp_da_to_va,
.parse_fw = qcom_register_dump_segments,
- .load = adsp_load,
+ .load_segments = adsp_load,
.panic = adsp_panic,
.coredump = adsp_minidump,
};
diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c
index e913dabae992..44b5736dc8b9 100644
--- a/drivers/remoteproc/qcom_q6v5_wcss.c
+++ b/drivers/remoteproc/qcom_q6v5_wcss.c
@@ -771,7 +771,7 @@ static const struct rproc_ops q6v5_wcss_ipq8074_ops = {
.start = q6v5_wcss_start,
.stop = q6v5_wcss_stop,
.da_to_va = q6v5_wcss_da_to_va,
- .load = q6v5_wcss_load,
+ .load_segments = q6v5_wcss_load,
.get_boot_addr = rproc_elf_get_boot_addr,
};
@@ -779,7 +779,7 @@ static const struct rproc_ops q6v5_wcss_qcs404_ops = {
.start = q6v5_qcs404_wcss_start,
.stop = q6v5_wcss_stop,
.da_to_va = q6v5_wcss_da_to_va,
- .load = q6v5_wcss_load,
+ .load_segments = q6v5_wcss_load,
.get_boot_addr = rproc_elf_get_boot_addr,
.parse_fw = qcom_register_dump_segments,
};
diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c
index a7bb9da27029..42102bc4c458 100644
--- a/drivers/remoteproc/qcom_wcnss.c
+++ b/drivers/remoteproc/qcom_wcnss.c
@@ -335,7 +335,7 @@ static const struct rproc_ops wcnss_ops = {
.stop = wcnss_stop,
.da_to_va = wcnss_da_to_va,
.parse_fw = qcom_register_dump_segments,
- .load = wcnss_load,
+ .load_segments = wcnss_load,
};
static irqreturn_t wcnss_wdog_interrupt(int irq, void *dev)
diff --git a/drivers/remoteproc/rcar_rproc.c b/drivers/remoteproc/rcar_rproc.c
index cc17e8421f65..e36778fec072 100644
--- a/drivers/remoteproc/rcar_rproc.c
+++ b/drivers/remoteproc/rcar_rproc.c
@@ -142,7 +142,7 @@ static struct rproc_ops rcar_rproc_ops = {
.prepare = rcar_rproc_prepare,
.start = rcar_rproc_start,
.stop = rcar_rproc_stop,
- .load = rproc_elf_load_segments,
+ .load_segments = rproc_elf_load_segments,
.parse_fw = rcar_rproc_parse_fw,
.find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
.sanity_check = rproc_elf_sanity_check,
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 8df4b2c59bb6..e4ad024efcda 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -2485,11 +2485,11 @@ static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
if (!rproc->ops->coredump)
rproc->ops->coredump = rproc_coredump;
- if (rproc->ops->load || rproc->ops->load_fw)
+ if (rproc->ops->load_segments || rproc->ops->load_fw)
return 0;
/* Default to ELF loader if no load function is specified */
- rproc->ops->load = rproc_elf_load_segments;
+ rproc->ops->load_segments = rproc_elf_load_segments;
rproc->ops->parse_fw = rproc_elf_load_rsc_table;
rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table;
rproc->ops->sanity_check = rproc_elf_sanity_check;
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
index 2104ca449178..b898494600cf 100644
--- a/drivers/remoteproc/remoteproc_internal.h
+++ b/drivers/remoteproc/remoteproc_internal.h
@@ -167,8 +167,8 @@ u64 rproc_get_boot_addr(struct rproc *rproc, const struct firmware *fw)
static inline
int rproc_load_segments(struct rproc *rproc, const struct firmware *fw)
{
- if (rproc->ops->load)
- return rproc->ops->load(rproc, fw);
+ if (rproc->ops->load_segments)
+ return rproc->ops->load_segments(rproc, fw);
return -EINVAL;
}
diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c
index 1340be9d0110..8d6b75e91531 100644
--- a/drivers/remoteproc/st_remoteproc.c
+++ b/drivers/remoteproc/st_remoteproc.c
@@ -233,7 +233,7 @@ static const struct rproc_ops st_rproc_ops = {
.start = st_rproc_start,
.stop = st_rproc_stop,
.parse_fw = st_rproc_parse_fw,
- .load = rproc_elf_load_segments,
+ .load_segments = rproc_elf_load_segments,
.find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
.sanity_check = rproc_elf_sanity_check,
.get_boot_addr = rproc_elf_get_boot_addr,
diff --git a/drivers/remoteproc/st_slim_rproc.c b/drivers/remoteproc/st_slim_rproc.c
index 5412beb0a692..0f91d8f1e7c7 100644
--- a/drivers/remoteproc/st_slim_rproc.c
+++ b/drivers/remoteproc/st_slim_rproc.c
@@ -201,7 +201,7 @@ static const struct rproc_ops slim_rproc_ops = {
.stop = slim_rproc_stop,
.da_to_va = slim_rproc_da_to_va,
.get_boot_addr = rproc_elf_get_boot_addr,
- .load = rproc_elf_load_segments,
+ .load_segments = rproc_elf_load_segments,
.sanity_check = rproc_elf_sanity_check,
};
diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
index 8c7f7950b80e..7e8ffd9fcc57 100644
--- a/drivers/remoteproc/stm32_rproc.c
+++ b/drivers/remoteproc/stm32_rproc.c
@@ -667,7 +667,7 @@ static const struct rproc_ops st_rproc_ops = {
.attach = stm32_rproc_attach,
.detach = stm32_rproc_detach,
.kick = stm32_rproc_kick,
- .load = rproc_elf_load_segments,
+ .load_segments = rproc_elf_load_segments,
.parse_fw = stm32_rproc_parse_fw,
.find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
.get_loaded_rsc_table = stm32_rproc_get_loaded_rsc_table,
diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c
index 5aeedeaf3c41..59cfba0a02e7 100644
--- a/drivers/remoteproc/xlnx_r5_remoteproc.c
+++ b/drivers/remoteproc/xlnx_r5_remoteproc.c
@@ -864,7 +864,7 @@ static const struct rproc_ops zynqmp_r5_rproc_ops = {
.unprepare = zynqmp_r5_rproc_unprepare,
.start = zynqmp_r5_rproc_start,
.stop = zynqmp_r5_rproc_stop,
- .load = rproc_elf_load_segments,
+ .load_segments = rproc_elf_load_segments,
.parse_fw = zynqmp_r5_parse_fw,
.find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
.sanity_check = rproc_elf_sanity_check,
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index ba6fd560f7ba..55c20424a99f 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -374,7 +374,7 @@ enum rsc_handling_status {
* @find_loaded_rsc_table: find the loaded resource table from firmware image
* @get_loaded_rsc_table: get resource table installed in memory
* by external entity
- * @load: load firmware to memory, where the remote processor
+ * @load_segments: load firmware ELF segment to memory, where the remote processor
* expects to find it
* @sanity_check: sanity check the fw image
* @get_boot_addr: get boot address to entry point specified in firmware
@@ -402,7 +402,7 @@ struct rproc_ops {
struct rproc *rproc, const struct firmware *fw);
struct resource_table *(*get_loaded_rsc_table)(
struct rproc *rproc, size_t *size);
- int (*load)(struct rproc *rproc, const struct firmware *fw);
+ int (*load_segments)(struct rproc *rproc, const struct firmware *fw);
int (*sanity_check)(struct rproc *rproc, const struct firmware *fw);
u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
unsigned long (*panic)(struct rproc *rproc);
--
2.25.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v15 5/8] remoteproc: Make load_segments and load_fw ops exclusive and optional
2024-11-28 8:42 [PATCH v15 0/8] Introduction of a remoteproc tee to load signed firmware Arnaud Pouliquen
` (3 preceding siblings ...)
2024-11-28 8:42 ` [PATCH v15 4/8] remoteproc: Rename load() operation to load_segments() in rproc_ops struct Arnaud Pouliquen
@ 2024-11-28 8:42 ` Arnaud Pouliquen
2024-12-04 17:53 ` Mathieu Poirier
2024-11-28 8:42 ` [PATCH v15 6/8] dt-bindings: remoteproc: Add compatibility for TEE support Arnaud Pouliquen
` (2 subsequent siblings)
7 siblings, 1 reply; 32+ messages in thread
From: Arnaud Pouliquen @ 2024-11-28 8:42 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier
Cc: Arnaud Pouliquen, linux-remoteproc, linux-kernel
The two methods to load the firmware to memory should be exclusive.
- make load_segments optional returning 0 if not set in
rproc_load_segments(),
- ensure that load_segments() and load_fw() are not both set,
- do not set default rproc::ops fields if load_fw() is set.
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
---
drivers/remoteproc/remoteproc_core.c | 4 ++++
drivers/remoteproc/remoteproc_internal.h | 2 +-
include/linux/remoteproc.h | 7 +++++--
3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index e4ad024efcda..deadec0f3474 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -2477,6 +2477,10 @@ static int rproc_alloc_firmware(struct rproc *rproc,
static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
{
+ /* A processor with a load_segments() and a load_fw() functions makes no sense. */
+ if (ops->load_segments && ops->load_fw)
+ return -EINVAL;
+
rproc->ops = kmemdup(ops, sizeof(*ops), GFP_KERNEL);
if (!rproc->ops)
return -ENOMEM;
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
index b898494600cf..3a4161eaf291 100644
--- a/drivers/remoteproc/remoteproc_internal.h
+++ b/drivers/remoteproc/remoteproc_internal.h
@@ -170,7 +170,7 @@ int rproc_load_segments(struct rproc *rproc, const struct firmware *fw)
if (rproc->ops->load_segments)
return rproc->ops->load_segments(rproc, fw);
- return -EINVAL;
+ return 0;
}
static inline int rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index 55c20424a99f..4f4c65ce74af 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -374,8 +374,9 @@ enum rsc_handling_status {
* @find_loaded_rsc_table: find the loaded resource table from firmware image
* @get_loaded_rsc_table: get resource table installed in memory
* by external entity
- * @load_segments: load firmware ELF segment to memory, where the remote processor
- * expects to find it
+ * @load_segments: optional load firmware ELF segments to memory, where the remote processor
+ * expects to find it.
+ * This operation is exclusive with the load_fw()
* @sanity_check: sanity check the fw image
* @get_boot_addr: get boot address to entry point specified in firmware
* @panic: optional callback to react to system panic, core will delay
@@ -383,8 +384,10 @@ enum rsc_handling_status {
* @coredump: collect firmware dump after the subsystem is shutdown
* @load_fw: optional function to load non-ELF firmware image to memory, where the remote
* processor expects to find it.
+ * This operation is exclusive with the load_segments()
* @release_fw: optional function to release the firmware image from memories.
* This function is called after stopping the remote processor or in case of error
+ *
*/
struct rproc_ops {
int (*prepare)(struct rproc *rproc);
--
2.25.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v15 6/8] dt-bindings: remoteproc: Add compatibility for TEE support
2024-11-28 8:42 [PATCH v15 0/8] Introduction of a remoteproc tee to load signed firmware Arnaud Pouliquen
` (4 preceding siblings ...)
2024-11-28 8:42 ` [PATCH v15 5/8] remoteproc: Make load_segments and load_fw ops exclusive and optional Arnaud Pouliquen
@ 2024-11-28 8:42 ` Arnaud Pouliquen
2024-11-28 8:42 ` [PATCH v15 7/8] remoteproc: stm32: Create sub-functions to request shutdown and release Arnaud Pouliquen
2024-11-28 8:42 ` [PATCH v15 8/8] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware Arnaud Pouliquen
7 siblings, 0 replies; 32+ messages in thread
From: Arnaud Pouliquen @ 2024-11-28 8:42 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Maxime Coquelin,
Alexandre Torgue, Fabien Dessenne, Arnaud Pouliquen
Cc: linux-remoteproc, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel
The "st,stm32mp1-m4-tee" compatible is utilized in a system configuration
where the Cortex-M4 firmware is loaded by the Trusted Execution Environment
(TEE).
For instance, this compatible is used in both the Linux and OP-TEE device
trees:
- In OP-TEE, a node is defined in the device tree with the
"st,stm32mp1-m4-tee" compatible to support signed remoteproc firmware.
Based on DT properties, the OP-TEE remoteproc framework is initiated to
expose a trusted application service to authenticate and load the remote
processor firmware provided by the Linux remoteproc framework, as well
as to start and stop the remote processor.
- In Linux, when the compatibility is set, the Cortex-M resets should not
be declared in the device tree. In such a configuration, the reset is
managed by the OP-TEE remoteproc driver and is no longer accessible from
the Linux kernel.
Associated with this new compatible, add the "st,proc-id" property to
identify the remote processor. This ID is used to define a unique ID,
common between Linux, U-Boot, and OP-TEE, to identify a coprocessor.
This ID will be used in requests to the OP-TEE remoteproc Trusted
Application to specify the remote processor.
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
---
.../bindings/remoteproc/st,stm32-rproc.yaml | 58 ++++++++++++++++---
1 file changed, 50 insertions(+), 8 deletions(-)
diff --git a/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml
index 370af61d8f28..409123cd4667 100644
--- a/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml
@@ -16,7 +16,12 @@ maintainers:
properties:
compatible:
- const: st,stm32mp1-m4
+ enum:
+ - st,stm32mp1-m4
+ - st,stm32mp1-m4-tee
+ description:
+ Use "st,stm32mp1-m4" for the Cortex-M4 coprocessor management by non-secure context
+ Use "st,stm32mp1-m4-tee" for the Cortex-M4 coprocessor management by secure context
reg:
description:
@@ -43,6 +48,10 @@ properties:
- description: The offset of the hold boot setting register
- description: The field mask of the hold boot
+ st,proc-id:
+ description: remote processor identifier
+ $ref: /schemas/types.yaml#/definitions/uint32
+
st,syscfg-tz:
deprecated: true
description:
@@ -142,21 +151,43 @@ properties:
required:
- compatible
- reg
- - resets
allOf:
- if:
properties:
- reset-names:
- not:
- contains:
- const: hold_boot
+ compatible:
+ contains:
+ const: st,stm32mp1-m4
then:
+ if:
+ properties:
+ reset-names:
+ not:
+ contains:
+ const: hold_boot
+ then:
+ required:
+ - st,syscfg-holdboot
+ else:
+ properties:
+ st,syscfg-holdboot: false
+ required:
+ - reset-names
required:
- - st,syscfg-holdboot
- else:
+ - resets
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: st,stm32mp1-m4-tee
+ then:
properties:
st,syscfg-holdboot: false
+ reset-names: false
+ resets: false
+ required:
+ - st,proc-id
additionalProperties: false
@@ -188,5 +219,16 @@ examples:
st,syscfg-rsc-tbl = <&tamp 0x144 0xFFFFFFFF>;
st,syscfg-m4-state = <&tamp 0x148 0xFFFFFFFF>;
};
+ - |
+ #include <dt-bindings/reset/stm32mp1-resets.h>
+ m4@10000000 {
+ compatible = "st,stm32mp1-m4-tee";
+ reg = <0x10000000 0x40000>,
+ <0x30000000 0x40000>,
+ <0x38000000 0x10000>;
+ st,proc-id = <0>;
+ st,syscfg-rsc-tbl = <&tamp 0x144 0xFFFFFFFF>;
+ st,syscfg-m4-state = <&tamp 0x148 0xFFFFFFFF>;
+ };
...
--
2.25.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v15 7/8] remoteproc: stm32: Create sub-functions to request shutdown and release
2024-11-28 8:42 [PATCH v15 0/8] Introduction of a remoteproc tee to load signed firmware Arnaud Pouliquen
` (5 preceding siblings ...)
2024-11-28 8:42 ` [PATCH v15 6/8] dt-bindings: remoteproc: Add compatibility for TEE support Arnaud Pouliquen
@ 2024-11-28 8:42 ` Arnaud Pouliquen
2024-11-28 8:42 ` [PATCH v15 8/8] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware Arnaud Pouliquen
7 siblings, 0 replies; 32+ messages in thread
From: Arnaud Pouliquen @ 2024-11-28 8:42 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier, Maxime Coquelin,
Alexandre Torgue
Cc: Arnaud Pouliquen, linux-remoteproc, linux-stm32, linux-arm-kernel,
linux-kernel
To prepare for the support of TEE remoteproc, create sub-functions
that can be used in both cases, with and without remoteproc TEE support.
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
---
drivers/remoteproc/stm32_rproc.c | 82 +++++++++++++++++++-------------
1 file changed, 49 insertions(+), 33 deletions(-)
diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
index 7e8ffd9fcc57..1586978c5757 100644
--- a/drivers/remoteproc/stm32_rproc.c
+++ b/drivers/remoteproc/stm32_rproc.c
@@ -209,6 +209,52 @@ 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, 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) {
+ err = mbox_send_message(ddata->mb[idx].chan, "detach");
+ if (err < 0)
+ dev_warn(&rproc->dev, "warning: remote FW shutdown without ack\n");
+ }
+ }
+}
+
+static int stm32_rproc_release(struct rproc *rproc)
+{
+ struct stm32_rproc *ddata = rproc->priv;
+ unsigned int err = 0;
+
+ /* To allow platform Standby power mode, set remote proc Deep Sleep */
+ if (ddata->pdds.map) {
+ err = regmap_update_bits(ddata->pdds.map, ddata->pdds.reg,
+ ddata->pdds.mask, 1);
+ if (err) {
+ dev_err(&rproc->dev, "failed to set pdds\n");
+ return err;
+ }
+ }
+
+ /* Update coprocessor state to OFF if available */
+ if (ddata->m4_state.map) {
+ err = regmap_update_bits(ddata->m4_state.map,
+ ddata->m4_state.reg,
+ ddata->m4_state.mask,
+ M4_STATE_OFF);
+ if (err) {
+ dev_err(&rproc->dev, "failed to set copro state\n");
+ return err;
+ }
+ }
+
+ return 0;
+}
+
static int stm32_rproc_prepare(struct rproc *rproc)
{
struct device *dev = rproc->dev.parent;
@@ -519,17 +565,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 +579,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
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH v15 8/8] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware
2024-11-28 8:42 [PATCH v15 0/8] Introduction of a remoteproc tee to load signed firmware Arnaud Pouliquen
` (6 preceding siblings ...)
2024-11-28 8:42 ` [PATCH v15 7/8] remoteproc: stm32: Create sub-functions to request shutdown and release Arnaud Pouliquen
@ 2024-11-28 8:42 ` Arnaud Pouliquen
7 siblings, 0 replies; 32+ messages in thread
From: Arnaud Pouliquen @ 2024-11-28 8:42 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier, Maxime Coquelin,
Alexandre Torgue
Cc: Arnaud Pouliquen, linux-remoteproc, linux-stm32, linux-arm-kernel,
linux-kernel
The new TEE remoteproc driver is used to manage remote firmware in a
secure, trusted context. The 'st,stm32mp1-m4-tee' compatibility is
introduced to delegate the loading of the firmware to the trusted
execution context. In such cases, the firmware should be signed and
adhere to the image format defined by the TEE.
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
---
updates vs version V13:
- register load_fw() ops instead of load() to call rproc_tee_load_fw()
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
---
drivers/remoteproc/stm32_rproc.c | 57 ++++++++++++++++++++++++++++++--
1 file changed, 54 insertions(+), 3 deletions(-)
diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
index 1586978c5757..c155822324d8 100644
--- a/drivers/remoteproc/stm32_rproc.c
+++ b/drivers/remoteproc/stm32_rproc.c
@@ -18,6 +18,7 @@
#include <linux/pm_wakeirq.h>
#include <linux/regmap.h>
#include <linux/remoteproc.h>
+#include <linux/remoteproc_tee.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
@@ -255,6 +256,19 @@ static int stm32_rproc_release(struct rproc *rproc)
return 0;
}
+static int stm32_rproc_tee_stop(struct rproc *rproc)
+{
+ int err;
+
+ stm32_rproc_request_shutdown(rproc);
+
+ err = rproc_tee_stop(rproc);
+ if (err)
+ return err;
+
+ return stm32_rproc_release(rproc);
+}
+
static int stm32_rproc_prepare(struct rproc *rproc)
{
struct device *dev = rproc->dev.parent;
@@ -691,8 +705,20 @@ static const struct rproc_ops st_rproc_ops = {
.get_boot_addr = rproc_elf_get_boot_addr,
};
+static const struct rproc_ops st_rproc_tee_ops = {
+ .prepare = stm32_rproc_prepare,
+ .start = rproc_tee_start,
+ .stop = stm32_rproc_tee_stop,
+ .kick = stm32_rproc_kick,
+ .load_fw = rproc_tee_load_fw,
+ .parse_fw = rproc_tee_parse_fw,
+ .find_loaded_rsc_table = rproc_tee_find_loaded_rsc_table,
+ .release_fw = rproc_tee_release_fw,
+};
+
static const struct of_device_id stm32_rproc_match[] = {
{ .compatible = "st,stm32mp1-m4" },
+ { .compatible = "st,stm32mp1-m4-tee" },
{},
};
MODULE_DEVICE_TABLE(of, stm32_rproc_match);
@@ -853,15 +879,36 @@ static int stm32_rproc_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
struct rproc *rproc;
unsigned int state;
+ u32 proc_id;
int ret;
ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret)
return ret;
- rproc = devm_rproc_alloc(dev, np->name, &st_rproc_ops, NULL, sizeof(*ddata));
- if (!rproc)
- return -ENOMEM;
+ if (of_device_is_compatible(np, "st,stm32mp1-m4-tee")) {
+ /*
+ * Delegate the firmware management to the secure context.
+ * The firmware loaded has to be signed.
+ */
+ ret = of_property_read_u32(np, "st,proc-id", &proc_id);
+ if (ret) {
+ dev_err(dev, "failed to read st,rproc-id property\n");
+ return ret;
+ }
+
+ rproc = devm_rproc_alloc(dev, np->name, &st_rproc_tee_ops, NULL, sizeof(*ddata));
+ if (!rproc)
+ return -ENOMEM;
+
+ ret = rproc_tee_register(dev, rproc, proc_id);
+ if (ret)
+ return dev_err_probe(dev, ret, "signed firmware not supported by TEE\n");
+ } else {
+ rproc = devm_rproc_alloc(dev, np->name, &st_rproc_ops, NULL, sizeof(*ddata));
+ if (!rproc)
+ return -ENOMEM;
+ }
ddata = rproc->priv;
@@ -913,6 +960,8 @@ static int stm32_rproc_probe(struct platform_device *pdev)
dev_pm_clear_wake_irq(dev);
device_init_wakeup(dev, false);
}
+ rproc_tee_unregister(rproc);
+
return ret;
}
@@ -933,6 +982,8 @@ static void stm32_rproc_remove(struct platform_device *pdev)
dev_pm_clear_wake_irq(dev);
device_init_wakeup(dev, false);
}
+
+ rproc_tee_unregister(rproc);
}
static int stm32_rproc_suspend(struct device *dev)
--
2.25.1
^ permalink raw reply related [flat|nested] 32+ messages in thread
* Re: [PATCH v15 2/8] remoteproc: Add TEE support
2024-11-28 8:42 ` [PATCH v15 2/8] remoteproc: Add TEE support Arnaud Pouliquen
@ 2024-12-03 17:04 ` Mathieu Poirier
2024-12-06 22:07 ` Bjorn Andersson
1 sibling, 0 replies; 32+ messages in thread
From: Mathieu Poirier @ 2024-12-03 17:04 UTC (permalink / raw)
To: Arnaud Pouliquen; +Cc: Bjorn Andersson, linux-kernel, linux-remoteproc
Good morning,
On Thu, Nov 28, 2024 at 09:42:09AM +0100, Arnaud Pouliquen wrote:
> Add a remoteproc TEE (Trusted Execution Environment) driver
> that will be probed by the TEE bus. If the associated Trusted
> application is supported on secure part this driver offers a client
> interface to load a firmware by the secure part.
> This firmware could be authenticated by the secure trusted application.
>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> ---
> Updates vs version v13:
> - define REMOTEPROC_TEE as bool instead of tristate,
> - remove the load of the firmware in rproc_tee_parse_fw as we will ensure
> that the firmware is loaded using the load_fw() operation.
> ---
> drivers/remoteproc/Kconfig | 10 +
> drivers/remoteproc/Makefile | 1 +
> drivers/remoteproc/remoteproc_tee.c | 508 ++++++++++++++++++++++++++++
> include/linux/remoteproc.h | 4 +
> include/linux/remoteproc_tee.h | 105 ++++++
> 5 files changed, 628 insertions(+)
> create mode 100644 drivers/remoteproc/remoteproc_tee.c
> create mode 100644 include/linux/remoteproc_tee.h
>
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> index 955e4e38477e..f6335321d540 100644
> --- a/drivers/remoteproc/Kconfig
> +++ b/drivers/remoteproc/Kconfig
> @@ -23,6 +23,16 @@ config REMOTEPROC_CDEV
>
> It's safe to say N if you don't want to use this interface.
>
> +config REMOTEPROC_TEE
> + bool "Remoteproc support by a TEE application"
> + depends on OPTEE
> + help
> + Support a remote processor with a TEE application. The Trusted
> + Execution Context is responsible for loading the trusted firmware
> + image and managing the remote processor's lifecycle.
> +
> + It's safe to say N if you don't want to use remoteproc TEE.
> +
> config IMX_REMOTEPROC
> tristate "i.MX remoteproc support"
> depends on ARCH_MXC
> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
> index 5ff4e2fee4ab..f77e0abe8349 100644
> --- a/drivers/remoteproc/Makefile
> +++ b/drivers/remoteproc/Makefile
> @@ -11,6 +11,7 @@ remoteproc-y += remoteproc_sysfs.o
> remoteproc-y += remoteproc_virtio.o
> remoteproc-y += remoteproc_elf_loader.o
> obj-$(CONFIG_REMOTEPROC_CDEV) += remoteproc_cdev.o
> +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o
> obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o
> obj-$(CONFIG_IMX_DSP_REMOTEPROC) += imx_dsp_rproc.o
> obj-$(CONFIG_INGENIC_VPU_RPROC) += ingenic_rproc.o
> diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c
> new file mode 100644
> index 000000000000..3fe3f31068f2
> --- /dev/null
> +++ b/drivers/remoteproc/remoteproc_tee.c
> @@ -0,0 +1,508 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) STMicroelectronics 2024
> + * Author: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> + */
> +
> +#include <linux/firmware.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/remoteproc.h>
> +#include <linux/remoteproc_tee.h>
> +#include <linux/slab.h>
> +#include <linux/tee_drv.h>
> +
> +#define MAX_TEE_PARAM_ARRAY_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
> +
> +/*
> + * Release remote processor firmware images and associated resources.
> + * This command should be used in case an error occurs between the loading of
> + * the firmware images (TA_RPROC_CMD_LOAD_FW) and the starting of the remote
> + * processor (TA_RPROC_CMD_START_FW) or after stopping the remote processor
> + * to release associated resources (TA_RPROC_CMD_STOP_FW).
> + *
> + * [in] params[0].value.a: Unique 32-bit remote processor identifier
> + */
> +#define TA_RPROC_CMD_RELEASE_FW 6
> +
> +struct rproc_tee_context {
> + struct list_head sessions;
> + struct tee_context *tee_ctx;
> + struct device *dev;
> +};
> +
> +static struct rproc_tee_context *rproc_tee_ctx;
> +
> +static void rproc_tee_prepare_args(struct rproc_tee *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_ARRAY_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,
> + };
> +}
> +
> +void rproc_tee_release_fw(struct rproc *rproc)
> +{
> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
> + struct tee_ioctl_invoke_arg arg;
> + int ret;
> +
> + if (!rproc) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + /*
> + * If the remote processor state is RPROC_DETACHED, just ignore the
> + * request, as the remote processor is still running.
> + */
> + if (rproc->state == RPROC_DETACHED)
> + return;
> +
> + if (rproc->state != RPROC_OFFLINE) {
> + ret = -EBUSY;
> + goto out;
> + }
> +
> + rproc_tee_prepare_args(trproc, TA_RPROC_CMD_RELEASE_FW, &arg, param, 0);
> +
> + ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
> + if (ret < 0 || arg.ret != 0) {
> + dev_err(rproc_tee_ctx->dev,
> + "TA_RPROC_CMD_RELEASE_FW invoke failed TEE err: %x, ret:%x\n",
> + arg.ret, ret);
> + ret = -EIO;
> + }
> +
> +out:
> + if (ret)
> + /* Unexpected state without solution to come back in a stable state */
> + dev_err(rproc_tee_ctx->dev, "Failed to release TEE remoteproc firmware: %d\n", ret);
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_release_fw);
> +
> +int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw)
> +{
> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
> + struct tee_ioctl_invoke_arg arg;
> + struct tee_shm *fw_shm;
> + int ret;
> +
> + if (!trproc)
> + return -EINVAL;
> +
> + fw_shm = tee_shm_register_kernel_buf(rproc_tee_ctx->tee_ctx, (void *)fw->data, fw->size);
> + if (IS_ERR(fw_shm))
> + return PTR_ERR(fw_shm);
> +
> + rproc_tee_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(rproc_tee_ctx->tee_ctx, &arg, param);
> + if (ret < 0 || arg.ret != 0) {
> + dev_err(rproc_tee_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(rproc_tee_load_fw);
> +
> +static int rproc_tee_get_loaded_rsc_table(struct rproc *rproc, phys_addr_t *rsc_pa,
> + size_t *table_sz)
> +{
> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
> + struct tee_ioctl_invoke_arg arg;
> + int ret;
> +
> + if (!trproc)
> + return -EINVAL;
> +
> + rproc_tee_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(rproc_tee_ctx->tee_ctx, &arg, param);
> + if (ret < 0 || arg.ret != 0) {
> + dev_err(rproc_tee_ctx->dev,
> + "TA_RPROC_FW_CMD_GET_RSC_TABLE invoke failed TEE err: %x, ret:%x\n",
> + arg.ret, ret);
> + return -EIO;
> + }
> +
> + *table_sz = param[2].u.value.a;
> +
> + if (*table_sz)
> + *rsc_pa = param[1].u.value.a;
> + else
> + *rsc_pa = 0;
> +
> + return 0;
> +}
> +
> +int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw)
> +{
> + phys_addr_t rsc_table;
> + void __iomem *rsc_va;
> + size_t table_sz;
> + int ret;
> +
> + if (!rproc)
> + return -EINVAL;
> +
> + /* At this point, the firmware has to be loaded to be able to parse the resource table. */
> +
> + ret = rproc_tee_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
> + if (ret)
> + goto release_fw;
> +
> + /*
> + * We assume here that the memory mapping is the same between the TEE and Linux kernel
> + * contexts. Else a new TEE remoteproc service could be needed to get a copy of the
> + * resource table
> + */
> + rsc_va = ioremap_wc(rsc_table, table_sz);
> + if (IS_ERR_OR_NULL(rsc_va)) {
> + dev_err(rproc_tee_ctx->dev, "Unable to map memory region: %pa+%zx\n",
> + &rsc_table, table_sz);
> + ret = -ENOMEM;
> + goto release_fw;
> + }
> +
> + /*
> + * Create a copy of the resource table to have the same behavior as the ELF loader.
> + * This cached table will be used by the remoteproc core after the remoteproc stops
> + * to free resources and for crash recovery to reapply the settings.
> + * The cached table will be freed by the remoteproc core.
> + */
> + rproc->cached_table = kmemdup((__force void *)rsc_va, table_sz, GFP_KERNEL);
> + iounmap(rsc_va);
> +
> + if (!rproc->cached_table) {
> + ret = -ENOMEM;
> + goto release_fw;
> + }
> +
> + rproc->table_ptr = rproc->cached_table;
> + rproc->table_sz = table_sz;
> +
> + return 0;
> +
> +release_fw:
> + rproc_tee_release_fw(rproc);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_parse_fw);
> +
> +struct resource_table *rproc_tee_find_loaded_rsc_table(struct rproc *rproc,
> + const struct firmware *fw)
> +{
> + phys_addr_t rsc_table;
> + size_t table_sz;
> + int ret;
> +
> + ret = rproc_tee_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
> + if (ret)
> + return NULL;
> +
> + rproc->table_sz = table_sz;
> + if (!table_sz)
> + return NULL;
> +
> + /*
> + * At this step the memory area that contains the resource table should have been registered
> + * by the remote proc platform driver and allocated by rproc_alloc_registered_carveouts().
> + */
> + return (struct resource_table *)rproc_pa_to_va(rproc, rsc_table, table_sz, NULL);
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_find_loaded_rsc_table);
> +
> +int rproc_tee_start(struct rproc *rproc)
> +{
> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
> + struct tee_ioctl_invoke_arg arg;
> + int ret = 0;
> +
> + if (!trproc)
> + return -EINVAL;
> +
> + rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_START_FW, &arg, param, 0);
> +
> + ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
> + if (ret < 0 || arg.ret != 0) {
> + dev_err(rproc_tee_ctx->dev,
> + "TA_RPROC_FW_CMD_START_FW invoke failed TEE err: %x, ret:%x\n",
> + arg.ret, ret);
> + if (!ret)
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_start);
> +
> +int rproc_tee_stop(struct rproc *rproc)
> +{
> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
> + struct tee_ioctl_invoke_arg arg;
> + int ret;
> +
> + if (!trproc)
> + return -EINVAL;
> +
> + rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_STOP_FW, &arg, param, 0);
> +
> + ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
> + if (ret < 0 || arg.ret != 0) {
> + dev_err(rproc_tee_ctx->dev,
> + "TA_RPROC_FW_CMD_STOP_FW invoke failed TEE err: %x, ret:%x\n",
> + arg.ret, ret);
> + if (!ret)
> + ret = -EIO;
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_stop);
> +
> +static const struct tee_client_device_id rproc_tee_id_table[] = {
> + {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)},
> + {}
> +};
> +
> +int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id)
> +{
> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
> + struct tee_ioctl_open_session_arg sess_arg;
> + struct tee_client_device *tee_device;
> + struct rproc_tee *trproc;
> + int ret;
> +
> + /*
> + * Test if the device has been probed by the TEE bus. In case of failure, we ignore the
> + * reason. The bus could be not yet probed or the service not available in the secure
> + * firmware.The assumption in such a case is that the TEE remoteproc is not probed.
> + */
> + if (!rproc_tee_ctx)
> + return -EPROBE_DEFER;
> +
> + /* Prevent rproc tee module from being removed */
> + if (!try_module_get(THIS_MODULE)) {
> + dev_err(rproc_tee_ctx->dev, "can't get owner\n");
> + return -ENODEV;
> + }
> +
> + trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
> + if (!trproc) {
> + ret = -ENOMEM;
> + goto module_put;
> + }
> +
> + tee_device = to_tee_client_device(rproc_tee_ctx->dev);
> + memset(&sess_arg, 0, sizeof(sess_arg));
> +
> + memcpy(sess_arg.uuid, tee_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
> +
> + sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
> + sess_arg.num_params = 1;
> +
> + param[0] = (struct tee_param) {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
> + .u.value.a = rproc_id,
> + };
> +
> + ret = tee_client_open_session(rproc_tee_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);
> + ret = -EINVAL;
> + goto module_put;
> + }
> +
> + trproc->parent = dev;
> + trproc->rproc_id = rproc_id;
> + trproc->session_id = sess_arg.session;
> +
> + trproc->rproc = rproc;
> + rproc->rproc_tee_itf = trproc;
> +
> + list_add_tail(&trproc->node, &rproc_tee_ctx->sessions);
> +
> + return 0;
> +
> +module_put:
> + module_put(THIS_MODULE);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_register);
> +
> +int rproc_tee_unregister(struct rproc *rproc)
> +{
> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
> + int ret;
> +
> + if (!rproc->rproc_tee_itf)
> + return -ENODEV;
> +
> + ret = tee_client_close_session(rproc_tee_ctx->tee_ctx, trproc->session_id);
> + if (ret < 0)
> + dev_err(trproc->parent, "tee_client_close_session failed, err: %x\n", ret);
> +
> + list_del(&trproc->node);
> + rproc->rproc_tee_itf = NULL;
> +
> + module_put(THIS_MODULE);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_unregister);
> +
> +static int rproc_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 rproc_tee_probe(struct device *dev)
> +{
> + struct tee_context *tee_ctx;
> + int ret;
> +
> + /* Open context with TEE driver */
> + tee_ctx = tee_client_open_context(NULL, rproc_tee_ctx_match, NULL, NULL);
> + if (IS_ERR(tee_ctx))
> + return PTR_ERR(tee_ctx);
> +
> + rproc_tee_ctx = devm_kzalloc(dev, sizeof(*rproc_tee_ctx), GFP_KERNEL);
> + if (!rproc_tee_ctx) {
> + ret = -ENOMEM;
> + goto err;
> + }
> +
> + rproc_tee_ctx->dev = dev;
> + rproc_tee_ctx->tee_ctx = tee_ctx;
> + INIT_LIST_HEAD(&rproc_tee_ctx->sessions);
> +
> + return 0;
> +err:
> + tee_client_close_context(tee_ctx);
> +
> + return ret;
> +}
> +
> +static int rproc_tee_remove(struct device *dev)
> +{
> + struct rproc_tee *entry, *tmp;
> +
> + list_for_each_entry_safe(entry, tmp, &rproc_tee_ctx->sessions, node) {
> + tee_client_close_session(rproc_tee_ctx->tee_ctx, entry->session_id);
> + list_del(&entry->node);
> + kfree(entry);
> + }
> +
> + tee_client_close_context(rproc_tee_ctx->tee_ctx);
> +
> + return 0;
> +}
> +
> +MODULE_DEVICE_TABLE(tee, rproc_tee_id_table);
> +
> +static struct tee_client_driver rproc_tee_fw_driver = {
> + .id_table = rproc_tee_id_table,
> + .driver = {
> + .name = KBUILD_MODNAME,
> + .bus = &tee_bus_type,
> + .probe = rproc_tee_probe,
> + .remove = rproc_tee_remove,
> + },
> +};
> +
> +static int __init rproc_tee_fw_mod_init(void)
> +{
> + return driver_register(&rproc_tee_fw_driver.driver);
> +}
> +
> +static void __exit rproc_tee_fw_mod_exit(void)
> +{
> + driver_unregister(&rproc_tee_fw_driver.driver);
> +}
> +
> +module_init(rproc_tee_fw_mod_init);
> +module_exit(rproc_tee_fw_mod_exit);
> +
> +MODULE_DESCRIPTION(" remote processor TEE module");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
> index 8fd0d7f63c8e..2e0ddcb2d792 100644
> --- a/include/linux/remoteproc.h
> +++ b/include/linux/remoteproc.h
> @@ -503,6 +503,8 @@ enum rproc_features {
> RPROC_MAX_FEATURES,
> };
>
> +struct rproc_tee;
> +
> /**
> * struct rproc - represents a physical remote processor device
> * @node: list node of this rproc object
> @@ -545,6 +547,7 @@ enum rproc_features {
> * @cdev: character device of the rproc
> * @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release
> * @features: indicate remoteproc features
> + * @rproc_tee_itf: pointer to the remoteproc tee context
> */
> struct rproc {
> struct list_head node;
> @@ -586,6 +589,7 @@ struct rproc {
> struct cdev cdev;
> bool cdev_put_on_release;
> DECLARE_BITMAP(features, RPROC_MAX_FEATURES);
> + struct rproc_tee *rproc_tee_itf;
> };
>
> /**
> diff --git a/include/linux/remoteproc_tee.h b/include/linux/remoteproc_tee.h
> new file mode 100644
> index 000000000000..9b498a8eff4d
> --- /dev/null
> +++ b/include/linux/remoteproc_tee.h
> @@ -0,0 +1,105 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright(c) 2024 STMicroelectronics
> + */
> +
> +#ifndef REMOTEPROC_TEE_H
> +#define REMOTEPROC_TEE_H
> +
> +#include <linux/tee_drv.h>
> +#include <linux/firmware.h>
> +#include <linux/remoteproc.h>
> +
> +struct rproc;
> +
> +/**
> + * struct rproc_tee - TEE remoteproc structure
> + * @node: Reference in list
> + * @rproc: Remoteproc reference
> + * @parent: Parent device
> + * @rproc_id: Identifier of the target firmware
> + * @session_id: TEE session identifier
> + */
> +struct rproc_tee {
> + struct list_head node;
> + struct rproc *rproc;
> + struct device *parent;
> + u32 rproc_id;
> + u32 session_id;
> +};
> +
> +#if IS_REACHABLE(CONFIG_REMOTEPROC_TEE)
Now that you have switched to a "bool" in the Kconfig file, this should be
IS_ENABLED().
> +
> +int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id);
> +int rproc_tee_unregister(struct rproc *rproc);
> +int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw);
> +int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw);
> +void rproc_tee_release_fw(struct rproc *rproc);
> +struct resource_table *rproc_tee_find_loaded_rsc_table(struct rproc *rproc,
> + const struct firmware *fw);
> +int rproc_tee_start(struct rproc *rproc);
> +int rproc_tee_stop(struct rproc *rproc);
> +
> +#else
> +
> +static inline int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id)
> +{
> + return -ENODEV;
> +}
> +
> +static inline int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int rproc_tee_unregister(struct rproc *rproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int rproc_tee_start(struct rproc *rproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int rproc_tee_stop(struct rproc *rproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline void rproc_tee_release_fw(struct rproc *rproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +}
> +
> +static inline struct resource_table *
> +rproc_tee_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return NULL;
> +}
> +#endif /* CONFIG_REMOTEPROC_TEE */
> +#endif /* REMOTEPROC_TEE_H */
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation
2024-11-28 8:42 ` [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation Arnaud Pouliquen
@ 2024-12-03 17:22 ` Mathieu Poirier
2024-12-05 18:20 ` Arnaud POULIQUEN
2024-12-04 17:39 ` Mathieu Poirier
2024-12-09 23:14 ` Bjorn Andersson
2 siblings, 1 reply; 32+ messages in thread
From: Mathieu Poirier @ 2024-12-03 17:22 UTC (permalink / raw)
To: Arnaud Pouliquen; +Cc: Bjorn Andersson, linux-remoteproc, linux-kernel
On Thu, Nov 28, 2024 at 09:42:10AM +0100, Arnaud Pouliquen wrote:
> This patch updates the rproc_ops structures to include two new optional
> operations.
>
> - The load_fw() op is responsible for loading the remote processor
> non-ELF firmware image before starting the boot sequence. This ops will
> be used, for instance, to call OP-TEE to authenticate an load the firmware
> image before accessing to its resources (a.e the resource table)
>
> - The release_fw op is responsible for releasing the remote processor
> firmware image. For instance to clean memories.
> The ops is called in the following cases:
> - An error occurs between the loading of the firmware image and the
> start of the remote processor.
> - after stopping the remote processor.
>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> ---
> Update vs version V13:
> - Rework the commit to introduce load_fw() op.
> - remove rproc_release_fw() call from rproc_start() as called in
> rproc_boot() and rproc_boot_recovery() in case of error.
> - create rproc_load_fw() and rproc_release_fw() internal functions.
> ---
> drivers/remoteproc/remoteproc_core.c | 16 +++++++++++++++-
> drivers/remoteproc/remoteproc_internal.h | 14 ++++++++++++++
> include/linux/remoteproc.h | 6 ++++++
> 3 files changed, 35 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
> index ace11ea17097..8df4b2c59bb6 100644
> --- a/drivers/remoteproc/remoteproc_core.c
> +++ b/drivers/remoteproc/remoteproc_core.c
> @@ -1488,6 +1488,7 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
> kfree(rproc->cached_table);
> rproc->cached_table = NULL;
> rproc->table_ptr = NULL;
> + rproc_release_fw(rproc);
This is not needed since rproc_release_fw() is called in rproc_boot().
> unprepare_rproc:
> /* release HW resources if needed */
> rproc_unprepare_device(rproc);
> @@ -1855,8 +1856,14 @@ static int rproc_boot_recovery(struct rproc *rproc)
> return ret;
> }
>
> + ret = rproc_load_fw(rproc, firmware_p);
> + if (ret)
> + return ret;
> +
> /* boot the remote processor up again */
> ret = rproc_start(rproc, firmware_p);
> + if (ret)
> + rproc_release_fw(rproc);
>
> release_firmware(firmware_p);
>
> @@ -1997,7 +2004,13 @@ int rproc_boot(struct rproc *rproc)
> goto downref_rproc;
> }
>
> + ret = rproc_load_fw(rproc, firmware_p);
> + if (ret)
> + goto downref_rproc;
In case of error the firmware is not released.
> +
> ret = rproc_fw_boot(rproc, firmware_p);
> + if (ret)
> + rproc_release_fw(rproc);
>
> release_firmware(firmware_p);
> }
> @@ -2071,6 +2084,7 @@ int rproc_shutdown(struct rproc *rproc)
> kfree(rproc->cached_table);
> rproc->cached_table = NULL;
> rproc->table_ptr = NULL;
> + rproc_release_fw(rproc);
Please move this after rproc_disable_iommu().
> out:
> mutex_unlock(&rproc->lock);
> return ret;
> @@ -2471,7 +2485,7 @@ static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
> if (!rproc->ops->coredump)
> rproc->ops->coredump = rproc_coredump;
>
> - if (rproc->ops->load)
> + if (rproc->ops->load || rproc->ops->load_fw)
> return 0;
>
> /* Default to ELF loader if no load function is specified */
> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
> index 0cd09e67ac14..2104ca449178 100644
> --- a/drivers/remoteproc/remoteproc_internal.h
> +++ b/drivers/remoteproc/remoteproc_internal.h
> @@ -221,4 +221,18 @@ bool rproc_u64_fit_in_size_t(u64 val)
> return (val <= (size_t) -1);
> }
>
> +static inline void rproc_release_fw(struct rproc *rproc)
> +{
> + if (rproc->ops->release_fw)
> + rproc->ops->release_fw(rproc);
> +}
> +
> +static inline int rproc_load_fw(struct rproc *rproc, const struct firmware *fw)
> +{
> + if (rproc->ops->load_fw)
> + return rproc->ops->load_fw(rproc, fw);
> +
> + return 0;
> +}
> +
> #endif /* REMOTEPROC_INTERNAL_H */
> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
> index 2e0ddcb2d792..ba6fd560f7ba 100644
> --- a/include/linux/remoteproc.h
> +++ b/include/linux/remoteproc.h
> @@ -381,6 +381,10 @@ enum rsc_handling_status {
> * @panic: optional callback to react to system panic, core will delay
> * panic at least the returned number of milliseconds
> * @coredump: collect firmware dump after the subsystem is shutdown
> + * @load_fw: optional function to load non-ELF firmware image to memory, where the remote
Round this down to 80 characters please. Here having a longer line doesn't
improve readability.
> + * processor expects to find it.
> + * @release_fw: optional function to release the firmware image from memories.
> + * This function is called after stopping the remote processor or in case of error
Same.
More comments tomorrow or later during the week.
Thanks,
Mathieu
> */
> struct rproc_ops {
> int (*prepare)(struct rproc *rproc);
> @@ -403,6 +407,8 @@ struct rproc_ops {
> u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
> unsigned long (*panic)(struct rproc *rproc);
> void (*coredump)(struct rproc *rproc);
> + int (*load_fw)(struct rproc *rproc, const struct firmware *fw);
> + void (*release_fw)(struct rproc *rproc);
> };
>
> /**
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation
2024-11-28 8:42 ` [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation Arnaud Pouliquen
2024-12-03 17:22 ` Mathieu Poirier
@ 2024-12-04 17:39 ` Mathieu Poirier
2024-12-09 23:14 ` Bjorn Andersson
2 siblings, 0 replies; 32+ messages in thread
From: Mathieu Poirier @ 2024-12-04 17:39 UTC (permalink / raw)
To: Arnaud Pouliquen; +Cc: Bjorn Andersson, linux-remoteproc, linux-kernel
On Thu, Nov 28, 2024 at 09:42:10AM +0100, Arnaud Pouliquen wrote:
> This patch updates the rproc_ops structures to include two new optional
> operations.
>
> - The load_fw() op is responsible for loading the remote processor
> non-ELF firmware image before starting the boot sequence. This ops will
> be used, for instance, to call OP-TEE to authenticate an load the firmware
1) two space between "to" and "authenticate"
2) s/"an load"/"and load"
> image before accessing to its resources (a.e the resource table)
>
> - The release_fw op is responsible for releasing the remote processor
> firmware image. For instance to clean memories.
> The ops is called in the following cases:
> - An error occurs between the loading of the firmware image and the
> start of the remote processor.
> - after stopping the remote processor.
>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> ---
> Update vs version V13:
> - Rework the commit to introduce load_fw() op.
> - remove rproc_release_fw() call from rproc_start() as called in
> rproc_boot() and rproc_boot_recovery() in case of error.
> - create rproc_load_fw() and rproc_release_fw() internal functions.
> ---
> drivers/remoteproc/remoteproc_core.c | 16 +++++++++++++++-
> drivers/remoteproc/remoteproc_internal.h | 14 ++++++++++++++
> include/linux/remoteproc.h | 6 ++++++
> 3 files changed, 35 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
> index ace11ea17097..8df4b2c59bb6 100644
> --- a/drivers/remoteproc/remoteproc_core.c
> +++ b/drivers/remoteproc/remoteproc_core.c
> @@ -1488,6 +1488,7 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
> kfree(rproc->cached_table);
> rproc->cached_table = NULL;
> rproc->table_ptr = NULL;
> + rproc_release_fw(rproc);
> unprepare_rproc:
> /* release HW resources if needed */
> rproc_unprepare_device(rproc);
> @@ -1855,8 +1856,14 @@ static int rproc_boot_recovery(struct rproc *rproc)
> return ret;
> }
>
> + ret = rproc_load_fw(rproc, firmware_p);
> + if (ret)
> + return ret;
> +
> /* boot the remote processor up again */
> ret = rproc_start(rproc, firmware_p);
> + if (ret)
> + rproc_release_fw(rproc);
>
> release_firmware(firmware_p);
>
> @@ -1997,7 +2004,13 @@ int rproc_boot(struct rproc *rproc)
> goto downref_rproc;
> }
>
> + ret = rproc_load_fw(rproc, firmware_p);
> + if (ret)
> + goto downref_rproc;
> +
> ret = rproc_fw_boot(rproc, firmware_p);
> + if (ret)
> + rproc_release_fw(rproc);
>
> release_firmware(firmware_p);
> }
> @@ -2071,6 +2084,7 @@ int rproc_shutdown(struct rproc *rproc)
> kfree(rproc->cached_table);
> rproc->cached_table = NULL;
> rproc->table_ptr = NULL;
> + rproc_release_fw(rproc);
> out:
> mutex_unlock(&rproc->lock);
> return ret;
> @@ -2471,7 +2485,7 @@ static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
> if (!rproc->ops->coredump)
> rproc->ops->coredump = rproc_coredump;
>
> - if (rproc->ops->load)
> + if (rproc->ops->load || rproc->ops->load_fw)
> return 0;
>
> /* Default to ELF loader if no load function is specified */
> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
> index 0cd09e67ac14..2104ca449178 100644
> --- a/drivers/remoteproc/remoteproc_internal.h
> +++ b/drivers/remoteproc/remoteproc_internal.h
> @@ -221,4 +221,18 @@ bool rproc_u64_fit_in_size_t(u64 val)
> return (val <= (size_t) -1);
> }
>
> +static inline void rproc_release_fw(struct rproc *rproc)
> +{
> + if (rproc->ops->release_fw)
> + rproc->ops->release_fw(rproc);
> +}
> +
> +static inline int rproc_load_fw(struct rproc *rproc, const struct firmware *fw)
> +{
> + if (rproc->ops->load_fw)
> + return rproc->ops->load_fw(rproc, fw);
> +
> + return 0;
> +}
> +
> #endif /* REMOTEPROC_INTERNAL_H */
> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
> index 2e0ddcb2d792..ba6fd560f7ba 100644
> --- a/include/linux/remoteproc.h
> +++ b/include/linux/remoteproc.h
> @@ -381,6 +381,10 @@ enum rsc_handling_status {
> * @panic: optional callback to react to system panic, core will delay
> * panic at least the returned number of milliseconds
> * @coredump: collect firmware dump after the subsystem is shutdown
> + * @load_fw: optional function to load non-ELF firmware image to memory, where the remote
> + * processor expects to find it.
> + * @release_fw: optional function to release the firmware image from memories.
> + * This function is called after stopping the remote processor or in case of error
> */
> struct rproc_ops {
> int (*prepare)(struct rproc *rproc);
> @@ -403,6 +407,8 @@ struct rproc_ops {
> u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
> unsigned long (*panic)(struct rproc *rproc);
> void (*coredump)(struct rproc *rproc);
> + int (*load_fw)(struct rproc *rproc, const struct firmware *fw);
> + void (*release_fw)(struct rproc *rproc);
> };
>
> /**
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 5/8] remoteproc: Make load_segments and load_fw ops exclusive and optional
2024-11-28 8:42 ` [PATCH v15 5/8] remoteproc: Make load_segments and load_fw ops exclusive and optional Arnaud Pouliquen
@ 2024-12-04 17:53 ` Mathieu Poirier
0 siblings, 0 replies; 32+ messages in thread
From: Mathieu Poirier @ 2024-12-04 17:53 UTC (permalink / raw)
To: Arnaud Pouliquen; +Cc: Bjorn Andersson, linux-remoteproc, linux-kernel
On Thu, Nov 28, 2024 at 09:42:12AM +0100, Arnaud Pouliquen wrote:
> The two methods to load the firmware to memory should be exclusive.
>
> - make load_segments optional returning 0 if not set in
> rproc_load_segments(),
> - ensure that load_segments() and load_fw() are not both set,
> - do not set default rproc::ops fields if load_fw() is set.
This changelog is describing "what" the patch does rather than "why". I have
commented on that before.
>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> ---
> drivers/remoteproc/remoteproc_core.c | 4 ++++
> drivers/remoteproc/remoteproc_internal.h | 2 +-
> include/linux/remoteproc.h | 7 +++++--
> 3 files changed, 10 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
> index e4ad024efcda..deadec0f3474 100644
> --- a/drivers/remoteproc/remoteproc_core.c
> +++ b/drivers/remoteproc/remoteproc_core.c
> @@ -2477,6 +2477,10 @@ static int rproc_alloc_firmware(struct rproc *rproc,
>
> static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
> {
> + /* A processor with a load_segments() and a load_fw() functions makes no sense. */
> + if (ops->load_segments && ops->load_fw)
> + return -EINVAL;
> +
It is only a matter of time before someone needs both ->load_segments() and
->load_fw().
> rproc->ops = kmemdup(ops, sizeof(*ops), GFP_KERNEL);
> if (!rproc->ops)
> return -ENOMEM;
> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
> index b898494600cf..3a4161eaf291 100644
> --- a/drivers/remoteproc/remoteproc_internal.h
> +++ b/drivers/remoteproc/remoteproc_internal.h
> @@ -170,7 +170,7 @@ int rproc_load_segments(struct rproc *rproc, const struct firmware *fw)
> if (rproc->ops->load_segments)
> return rproc->ops->load_segments(rproc, fw);
>
> - return -EINVAL;
> + return 0;
Other than this hunk I would drop this patch completely.
> }
>
> static inline int rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
> index 55c20424a99f..4f4c65ce74af 100644
> --- a/include/linux/remoteproc.h
> +++ b/include/linux/remoteproc.h
> @@ -374,8 +374,9 @@ enum rsc_handling_status {
> * @find_loaded_rsc_table: find the loaded resource table from firmware image
> * @get_loaded_rsc_table: get resource table installed in memory
> * by external entity
> - * @load_segments: load firmware ELF segment to memory, where the remote processor
> - * expects to find it
> + * @load_segments: optional load firmware ELF segments to memory, where the remote processor
> + * expects to find it.
> + * This operation is exclusive with the load_fw()
> * @sanity_check: sanity check the fw image
> * @get_boot_addr: get boot address to entry point specified in firmware
> * @panic: optional callback to react to system panic, core will delay
> @@ -383,8 +384,10 @@ enum rsc_handling_status {
> * @coredump: collect firmware dump after the subsystem is shutdown
> * @load_fw: optional function to load non-ELF firmware image to memory, where the remote
> * processor expects to find it.
> + * This operation is exclusive with the load_segments()
> * @release_fw: optional function to release the firmware image from memories.
> * This function is called after stopping the remote processor or in case of error
> + *
> */
> struct rproc_ops {
> int (*prepare)(struct rproc *rproc);
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 4/8] remoteproc: Rename load() operation to load_segments() in rproc_ops struct
2024-11-28 8:42 ` [PATCH v15 4/8] remoteproc: Rename load() operation to load_segments() in rproc_ops struct Arnaud Pouliquen
@ 2024-12-04 18:02 ` Mathieu Poirier
0 siblings, 0 replies; 32+ messages in thread
From: Mathieu Poirier @ 2024-12-04 18:02 UTC (permalink / raw)
To: Arnaud Pouliquen
Cc: Bjorn Andersson, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Neil Armstrong, Kevin Hilman, Jerome Brunet,
Martin Blumenstingl, Matthias Brugger, AngeloGioacchino Del Regno,
Patrice Chotard, Maxime Coquelin, Alexandre Torgue,
kernel test robot, linux-remoteproc, imx, linux-arm-kernel,
linux-kernel, linux-amlogic, linux-mediatek, linux-arm-msm,
linux-stm32
On Thu, Nov 28, 2024 at 09:42:11AM +0100, Arnaud Pouliquen wrote:
> With the introduction of the load_fw() operation in the rproc_ops
> structure, we need to clarify the difference in the use of the load()
> and load_fw() ops.
>
> The legacy load() is dedicated to loading the ELF segments into memory.
> Rename this to a more explicit name: load_segments().
This is introducing more code churn than is worth it. Please enhance the usage
comment for ->load() as part of the previous patch and drop this one.
I am done reviewing this set.
Thanks,
Mathieu
>
> Suggested-by: Mathieu Poirier <mathieu.poirier@linaro.org>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> ---
> Update vs version V14:
> Fix: Rename missing load() to load_segments() in drivers/remoteproc/pru_rproc.c.
>
> Reported-by: kernel test robot <lkp@intel.com>
> Closes: https://lore.kernel.org/oe-kbuild-all/202411281332.Ra70nJAW-lkp@intel.com/
>
> ---
> drivers/remoteproc/imx_dsp_rproc.c | 2 +-
> drivers/remoteproc/imx_rproc.c | 2 +-
> drivers/remoteproc/meson_mx_ao_arc.c | 2 +-
> drivers/remoteproc/mtk_scp.c | 2 +-
> drivers/remoteproc/pru_rproc.c | 2 +-
> drivers/remoteproc/qcom_q6v5_adsp.c | 2 +-
> drivers/remoteproc/qcom_q6v5_mss.c | 2 +-
> drivers/remoteproc/qcom_q6v5_pas.c | 4 ++--
> drivers/remoteproc/qcom_q6v5_wcss.c | 4 ++--
> drivers/remoteproc/qcom_wcnss.c | 2 +-
> drivers/remoteproc/rcar_rproc.c | 2 +-
> drivers/remoteproc/remoteproc_core.c | 4 ++--
> drivers/remoteproc/remoteproc_internal.h | 4 ++--
> drivers/remoteproc/st_remoteproc.c | 2 +-
> drivers/remoteproc/st_slim_rproc.c | 2 +-
> drivers/remoteproc/stm32_rproc.c | 2 +-
> drivers/remoteproc/xlnx_r5_remoteproc.c | 2 +-
> include/linux/remoteproc.h | 4 ++--
> 18 files changed, 23 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/remoteproc/imx_dsp_rproc.c b/drivers/remoteproc/imx_dsp_rproc.c
> index 376187ad5754..a4a85fbce907 100644
> --- a/drivers/remoteproc/imx_dsp_rproc.c
> +++ b/drivers/remoteproc/imx_dsp_rproc.c
> @@ -934,7 +934,7 @@ static const struct rproc_ops imx_dsp_rproc_ops = {
> .start = imx_dsp_rproc_start,
> .stop = imx_dsp_rproc_stop,
> .kick = imx_dsp_rproc_kick,
> - .load = imx_dsp_rproc_elf_load_segments,
> + .load_segments = imx_dsp_rproc_elf_load_segments,
> .parse_fw = imx_dsp_rproc_parse_fw,
> .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
> .sanity_check = rproc_elf_sanity_check,
> diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c
> index 800015ff7ff9..f45b3207f7e9 100644
> --- a/drivers/remoteproc/imx_rproc.c
> +++ b/drivers/remoteproc/imx_rproc.c
> @@ -681,7 +681,7 @@ static const struct rproc_ops imx_rproc_ops = {
> .stop = imx_rproc_stop,
> .kick = imx_rproc_kick,
> .da_to_va = imx_rproc_da_to_va,
> - .load = rproc_elf_load_segments,
> + .load_segments = rproc_elf_load_segments,
> .parse_fw = imx_rproc_parse_fw,
> .find_loaded_rsc_table = imx_rproc_elf_find_loaded_rsc_table,
> .get_loaded_rsc_table = imx_rproc_get_loaded_rsc_table,
> diff --git a/drivers/remoteproc/meson_mx_ao_arc.c b/drivers/remoteproc/meson_mx_ao_arc.c
> index f6744b538323..a1c8c0929ce3 100644
> --- a/drivers/remoteproc/meson_mx_ao_arc.c
> +++ b/drivers/remoteproc/meson_mx_ao_arc.c
> @@ -137,7 +137,7 @@ static struct rproc_ops meson_mx_ao_arc_rproc_ops = {
> .stop = meson_mx_ao_arc_rproc_stop,
> .da_to_va = meson_mx_ao_arc_rproc_da_to_va,
> .get_boot_addr = rproc_elf_get_boot_addr,
> - .load = rproc_elf_load_segments,
> + .load_segments = rproc_elf_load_segments,
> .sanity_check = rproc_elf_sanity_check,
> };
>
> diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c
> index e744c07507ee..4e9a8bf3bc6e 100644
> --- a/drivers/remoteproc/mtk_scp.c
> +++ b/drivers/remoteproc/mtk_scp.c
> @@ -924,7 +924,7 @@ static int scp_stop(struct rproc *rproc)
> static const struct rproc_ops scp_ops = {
> .start = scp_start,
> .stop = scp_stop,
> - .load = scp_load,
> + .load_segments = scp_load,
> .da_to_va = scp_da_to_va,
> .parse_fw = scp_parse_fw,
> .sanity_check = rproc_elf_sanity_check,
> diff --git a/drivers/remoteproc/pru_rproc.c b/drivers/remoteproc/pru_rproc.c
> index 327f0c7ee3d6..0b2bf2574f74 100644
> --- a/drivers/remoteproc/pru_rproc.c
> +++ b/drivers/remoteproc/pru_rproc.c
> @@ -1015,7 +1015,7 @@ static int pru_rproc_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
> /* use a custom load function to deal with PRU-specific quirks */
> - rproc->ops->load = pru_rproc_load_elf_segments;
> + rproc->ops->load_segments = pru_rproc_load_elf_segments;
>
> /* use a custom parse function to deal with PRU-specific resources */
> rproc->ops->parse_fw = pru_rproc_parse_fw;
> diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c
> index 572dcb0f055b..aa9896930bcf 100644
> --- a/drivers/remoteproc/qcom_q6v5_adsp.c
> +++ b/drivers/remoteproc/qcom_q6v5_adsp.c
> @@ -527,7 +527,7 @@ static const struct rproc_ops adsp_ops = {
> .stop = adsp_stop,
> .da_to_va = adsp_da_to_va,
> .parse_fw = adsp_parse_firmware,
> - .load = adsp_load,
> + .load_segments = adsp_load,
> .panic = adsp_panic,
> };
>
> diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c
> index 2a42215ce8e0..a8beac1deabe 100644
> --- a/drivers/remoteproc/qcom_q6v5_mss.c
> +++ b/drivers/remoteproc/qcom_q6v5_mss.c
> @@ -1687,7 +1687,7 @@ static const struct rproc_ops q6v5_ops = {
> .start = q6v5_start,
> .stop = q6v5_stop,
> .parse_fw = qcom_q6v5_register_dump_segments,
> - .load = q6v5_load,
> + .load_segments = q6v5_load,
> .panic = q6v5_panic,
> };
>
> diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
> index ef82835e98a4..9b269ce390c1 100644
> --- a/drivers/remoteproc/qcom_q6v5_pas.c
> +++ b/drivers/remoteproc/qcom_q6v5_pas.c
> @@ -436,7 +436,7 @@ static const struct rproc_ops adsp_ops = {
> .stop = adsp_stop,
> .da_to_va = adsp_da_to_va,
> .parse_fw = qcom_register_dump_segments,
> - .load = adsp_load,
> + .load_segments = adsp_load,
> .panic = adsp_panic,
> };
>
> @@ -446,7 +446,7 @@ static const struct rproc_ops adsp_minidump_ops = {
> .stop = adsp_stop,
> .da_to_va = adsp_da_to_va,
> .parse_fw = qcom_register_dump_segments,
> - .load = adsp_load,
> + .load_segments = adsp_load,
> .panic = adsp_panic,
> .coredump = adsp_minidump,
> };
> diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c
> index e913dabae992..44b5736dc8b9 100644
> --- a/drivers/remoteproc/qcom_q6v5_wcss.c
> +++ b/drivers/remoteproc/qcom_q6v5_wcss.c
> @@ -771,7 +771,7 @@ static const struct rproc_ops q6v5_wcss_ipq8074_ops = {
> .start = q6v5_wcss_start,
> .stop = q6v5_wcss_stop,
> .da_to_va = q6v5_wcss_da_to_va,
> - .load = q6v5_wcss_load,
> + .load_segments = q6v5_wcss_load,
> .get_boot_addr = rproc_elf_get_boot_addr,
> };
>
> @@ -779,7 +779,7 @@ static const struct rproc_ops q6v5_wcss_qcs404_ops = {
> .start = q6v5_qcs404_wcss_start,
> .stop = q6v5_wcss_stop,
> .da_to_va = q6v5_wcss_da_to_va,
> - .load = q6v5_wcss_load,
> + .load_segments = q6v5_wcss_load,
> .get_boot_addr = rproc_elf_get_boot_addr,
> .parse_fw = qcom_register_dump_segments,
> };
> diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c
> index a7bb9da27029..42102bc4c458 100644
> --- a/drivers/remoteproc/qcom_wcnss.c
> +++ b/drivers/remoteproc/qcom_wcnss.c
> @@ -335,7 +335,7 @@ static const struct rproc_ops wcnss_ops = {
> .stop = wcnss_stop,
> .da_to_va = wcnss_da_to_va,
> .parse_fw = qcom_register_dump_segments,
> - .load = wcnss_load,
> + .load_segments = wcnss_load,
> };
>
> static irqreturn_t wcnss_wdog_interrupt(int irq, void *dev)
> diff --git a/drivers/remoteproc/rcar_rproc.c b/drivers/remoteproc/rcar_rproc.c
> index cc17e8421f65..e36778fec072 100644
> --- a/drivers/remoteproc/rcar_rproc.c
> +++ b/drivers/remoteproc/rcar_rproc.c
> @@ -142,7 +142,7 @@ static struct rproc_ops rcar_rproc_ops = {
> .prepare = rcar_rproc_prepare,
> .start = rcar_rproc_start,
> .stop = rcar_rproc_stop,
> - .load = rproc_elf_load_segments,
> + .load_segments = rproc_elf_load_segments,
> .parse_fw = rcar_rproc_parse_fw,
> .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
> .sanity_check = rproc_elf_sanity_check,
> diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
> index 8df4b2c59bb6..e4ad024efcda 100644
> --- a/drivers/remoteproc/remoteproc_core.c
> +++ b/drivers/remoteproc/remoteproc_core.c
> @@ -2485,11 +2485,11 @@ static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
> if (!rproc->ops->coredump)
> rproc->ops->coredump = rproc_coredump;
>
> - if (rproc->ops->load || rproc->ops->load_fw)
> + if (rproc->ops->load_segments || rproc->ops->load_fw)
> return 0;
>
> /* Default to ELF loader if no load function is specified */
> - rproc->ops->load = rproc_elf_load_segments;
> + rproc->ops->load_segments = rproc_elf_load_segments;
> rproc->ops->parse_fw = rproc_elf_load_rsc_table;
> rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table;
> rproc->ops->sanity_check = rproc_elf_sanity_check;
> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
> index 2104ca449178..b898494600cf 100644
> --- a/drivers/remoteproc/remoteproc_internal.h
> +++ b/drivers/remoteproc/remoteproc_internal.h
> @@ -167,8 +167,8 @@ u64 rproc_get_boot_addr(struct rproc *rproc, const struct firmware *fw)
> static inline
> int rproc_load_segments(struct rproc *rproc, const struct firmware *fw)
> {
> - if (rproc->ops->load)
> - return rproc->ops->load(rproc, fw);
> + if (rproc->ops->load_segments)
> + return rproc->ops->load_segments(rproc, fw);
>
> return -EINVAL;
> }
> diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c
> index 1340be9d0110..8d6b75e91531 100644
> --- a/drivers/remoteproc/st_remoteproc.c
> +++ b/drivers/remoteproc/st_remoteproc.c
> @@ -233,7 +233,7 @@ static const struct rproc_ops st_rproc_ops = {
> .start = st_rproc_start,
> .stop = st_rproc_stop,
> .parse_fw = st_rproc_parse_fw,
> - .load = rproc_elf_load_segments,
> + .load_segments = rproc_elf_load_segments,
> .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
> .sanity_check = rproc_elf_sanity_check,
> .get_boot_addr = rproc_elf_get_boot_addr,
> diff --git a/drivers/remoteproc/st_slim_rproc.c b/drivers/remoteproc/st_slim_rproc.c
> index 5412beb0a692..0f91d8f1e7c7 100644
> --- a/drivers/remoteproc/st_slim_rproc.c
> +++ b/drivers/remoteproc/st_slim_rproc.c
> @@ -201,7 +201,7 @@ static const struct rproc_ops slim_rproc_ops = {
> .stop = slim_rproc_stop,
> .da_to_va = slim_rproc_da_to_va,
> .get_boot_addr = rproc_elf_get_boot_addr,
> - .load = rproc_elf_load_segments,
> + .load_segments = rproc_elf_load_segments,
> .sanity_check = rproc_elf_sanity_check,
> };
>
> diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c
> index 8c7f7950b80e..7e8ffd9fcc57 100644
> --- a/drivers/remoteproc/stm32_rproc.c
> +++ b/drivers/remoteproc/stm32_rproc.c
> @@ -667,7 +667,7 @@ static const struct rproc_ops st_rproc_ops = {
> .attach = stm32_rproc_attach,
> .detach = stm32_rproc_detach,
> .kick = stm32_rproc_kick,
> - .load = rproc_elf_load_segments,
> + .load_segments = rproc_elf_load_segments,
> .parse_fw = stm32_rproc_parse_fw,
> .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
> .get_loaded_rsc_table = stm32_rproc_get_loaded_rsc_table,
> diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c
> index 5aeedeaf3c41..59cfba0a02e7 100644
> --- a/drivers/remoteproc/xlnx_r5_remoteproc.c
> +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c
> @@ -864,7 +864,7 @@ static const struct rproc_ops zynqmp_r5_rproc_ops = {
> .unprepare = zynqmp_r5_rproc_unprepare,
> .start = zynqmp_r5_rproc_start,
> .stop = zynqmp_r5_rproc_stop,
> - .load = rproc_elf_load_segments,
> + .load_segments = rproc_elf_load_segments,
> .parse_fw = zynqmp_r5_parse_fw,
> .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
> .sanity_check = rproc_elf_sanity_check,
> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
> index ba6fd560f7ba..55c20424a99f 100644
> --- a/include/linux/remoteproc.h
> +++ b/include/linux/remoteproc.h
> @@ -374,7 +374,7 @@ enum rsc_handling_status {
> * @find_loaded_rsc_table: find the loaded resource table from firmware image
> * @get_loaded_rsc_table: get resource table installed in memory
> * by external entity
> - * @load: load firmware to memory, where the remote processor
> + * @load_segments: load firmware ELF segment to memory, where the remote processor
> * expects to find it
> * @sanity_check: sanity check the fw image
> * @get_boot_addr: get boot address to entry point specified in firmware
> @@ -402,7 +402,7 @@ struct rproc_ops {
> struct rproc *rproc, const struct firmware *fw);
> struct resource_table *(*get_loaded_rsc_table)(
> struct rproc *rproc, size_t *size);
> - int (*load)(struct rproc *rproc, const struct firmware *fw);
> + int (*load_segments)(struct rproc *rproc, const struct firmware *fw);
> int (*sanity_check)(struct rproc *rproc, const struct firmware *fw);
> u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
> unsigned long (*panic)(struct rproc *rproc);
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation
2024-12-03 17:22 ` Mathieu Poirier
@ 2024-12-05 18:20 ` Arnaud POULIQUEN
2024-12-06 17:05 ` Mathieu Poirier
0 siblings, 1 reply; 32+ messages in thread
From: Arnaud POULIQUEN @ 2024-12-05 18:20 UTC (permalink / raw)
To: Mathieu Poirier; +Cc: Bjorn Andersson, linux-remoteproc, linux-kernel
Hello Mathieu,
Thanks for the review!
I just need to clarify a point below before preparing the next revision.
On 12/3/24 18:22, Mathieu Poirier wrote:
> On Thu, Nov 28, 2024 at 09:42:10AM +0100, Arnaud Pouliquen wrote:
>> This patch updates the rproc_ops structures to include two new optional
>> operations.
>>
>> - The load_fw() op is responsible for loading the remote processor
>> non-ELF firmware image before starting the boot sequence. This ops will
>> be used, for instance, to call OP-TEE to authenticate an load the firmware
>> image before accessing to its resources (a.e the resource table)
>>
>> - The release_fw op is responsible for releasing the remote processor
>> firmware image. For instance to clean memories.
>> The ops is called in the following cases:
>> - An error occurs between the loading of the firmware image and the
>> start of the remote processor.
>> - after stopping the remote processor.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>> ---
>> Update vs version V13:
>> - Rework the commit to introduce load_fw() op.
>> - remove rproc_release_fw() call from rproc_start() as called in
>> rproc_boot() and rproc_boot_recovery() in case of error.
>> - create rproc_load_fw() and rproc_release_fw() internal functions.
>> ---
>> drivers/remoteproc/remoteproc_core.c | 16 +++++++++++++++-
>> drivers/remoteproc/remoteproc_internal.h | 14 ++++++++++++++
>> include/linux/remoteproc.h | 6 ++++++
>> 3 files changed, 35 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
>> index ace11ea17097..8df4b2c59bb6 100644
>> --- a/drivers/remoteproc/remoteproc_core.c
>> +++ b/drivers/remoteproc/remoteproc_core.c
>> @@ -1488,6 +1488,7 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
>> kfree(rproc->cached_table);
>> rproc->cached_table = NULL;
>> rproc->table_ptr = NULL;
>> + rproc_release_fw(rproc);
>
> This is not needed since rproc_release_fw() is called in rproc_boot().
>
>> unprepare_rproc:
>> /* release HW resources if needed */
>> rproc_unprepare_device(rproc);
>> @@ -1855,8 +1856,14 @@ static int rproc_boot_recovery(struct rproc *rproc)
>> return ret;
>> }
>>
>> + ret = rproc_load_fw(rproc, firmware_p);
>> + if (ret)
>> + return ret;
>> +
>> /* boot the remote processor up again */
>> ret = rproc_start(rproc, firmware_p);
>> + if (ret)
>> + rproc_release_fw(rproc);
>>
>> release_firmware(firmware_p);
>>
>> @@ -1997,7 +2004,13 @@ int rproc_boot(struct rproc *rproc)
>> goto downref_rproc;
>> }
>>
>> + ret = rproc_load_fw(rproc, firmware_p);
>> + if (ret)
>> + goto downref_rproc;
>
> In case of error the firmware is not released.
I considered that if the load fails, the firmware is not loaded
and therefore does not need to be released.
In other words, in case of a load error in OP-TEE, OP-TEE should
perform the cleanup to return to its initial state before the load.
Do you see a use case where we should force the release in Linux?
Otherwise, I would propose to implement this behavior later if needed.
Thanks,
Arnaud
>
>> +
>> ret = rproc_fw_boot(rproc, firmware_p);
>> + if (ret)
>> + rproc_release_fw(rproc);
>>
>> release_firmware(firmware_p);
>> }
>> @@ -2071,6 +2084,7 @@ int rproc_shutdown(struct rproc *rproc)
>> kfree(rproc->cached_table);
>> rproc->cached_table = NULL;
>> rproc->table_ptr = NULL;
>> + rproc_release_fw(rproc);
>
> Please move this after rproc_disable_iommu().
>
>
>> out:
>> mutex_unlock(&rproc->lock);
>> return ret;
>> @@ -2471,7 +2485,7 @@ static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
>> if (!rproc->ops->coredump)
>> rproc->ops->coredump = rproc_coredump;
>>
>> - if (rproc->ops->load)
>> + if (rproc->ops->load || rproc->ops->load_fw)
>> return 0;
>>
>> /* Default to ELF loader if no load function is specified */
>> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
>> index 0cd09e67ac14..2104ca449178 100644
>> --- a/drivers/remoteproc/remoteproc_internal.h
>> +++ b/drivers/remoteproc/remoteproc_internal.h
>> @@ -221,4 +221,18 @@ bool rproc_u64_fit_in_size_t(u64 val)
>> return (val <= (size_t) -1);
>> }
>>
>> +static inline void rproc_release_fw(struct rproc *rproc)
>> +{
>> + if (rproc->ops->release_fw)
>> + rproc->ops->release_fw(rproc);
>> +}
>> +
>> +static inline int rproc_load_fw(struct rproc *rproc, const struct firmware *fw)
>> +{
>> + if (rproc->ops->load_fw)
>> + return rproc->ops->load_fw(rproc, fw);
>> +
>> + return 0;
>> +}
>> +
>> #endif /* REMOTEPROC_INTERNAL_H */
>> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
>> index 2e0ddcb2d792..ba6fd560f7ba 100644
>> --- a/include/linux/remoteproc.h
>> +++ b/include/linux/remoteproc.h
>> @@ -381,6 +381,10 @@ enum rsc_handling_status {
>> * @panic: optional callback to react to system panic, core will delay
>> * panic at least the returned number of milliseconds
>> * @coredump: collect firmware dump after the subsystem is shutdown
>> + * @load_fw: optional function to load non-ELF firmware image to memory, where the remote
>
> Round this down to 80 characters please. Here having a longer line doesn't
> improve readability.
>
>> + * processor expects to find it.
>> + * @release_fw: optional function to release the firmware image from memories.
>> + * This function is called after stopping the remote processor or in case of error
>
> Same.
>
> More comments tomorrow or later during the week.
>
> Thanks,
> Mathieu
>
>> */
>> struct rproc_ops {
>> int (*prepare)(struct rproc *rproc);
>> @@ -403,6 +407,8 @@ struct rproc_ops {
>> u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
>> unsigned long (*panic)(struct rproc *rproc);
>> void (*coredump)(struct rproc *rproc);
>> + int (*load_fw)(struct rproc *rproc, const struct firmware *fw);
>> + void (*release_fw)(struct rproc *rproc);
>> };
>>
>> /**
>> --
>> 2.25.1
>>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation
2024-12-05 18:20 ` Arnaud POULIQUEN
@ 2024-12-06 17:05 ` Mathieu Poirier
2024-12-06 17:07 ` Mathieu Poirier
0 siblings, 1 reply; 32+ messages in thread
From: Mathieu Poirier @ 2024-12-06 17:05 UTC (permalink / raw)
To: Arnaud POULIQUEN; +Cc: Bjorn Andersson, linux-remoteproc, linux-kernel
On Thu, 5 Dec 2024 at 11:22, Arnaud POULIQUEN
<arnaud.pouliquen@foss.st.com> wrote:
>
> Hello Mathieu,
>
> Thanks for the review!
> I just need to clarify a point below before preparing the next revision.
>
> On 12/3/24 18:22, Mathieu Poirier wrote:
> > On Thu, Nov 28, 2024 at 09:42:10AM +0100, Arnaud Pouliquen wrote:
> >> This patch updates the rproc_ops structures to include two new optional
> >> operations.
> >>
> >> - The load_fw() op is responsible for loading the remote processor
> >> non-ELF firmware image before starting the boot sequence. This ops will
> >> be used, for instance, to call OP-TEE to authenticate an load the firmware
> >> image before accessing to its resources (a.e the resource table)
> >>
> >> - The release_fw op is responsible for releasing the remote processor
> >> firmware image. For instance to clean memories.
> >> The ops is called in the following cases:
> >> - An error occurs between the loading of the firmware image and the
> >> start of the remote processor.
> >> - after stopping the remote processor.
> >>
> >> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> >> ---
> >> Update vs version V13:
> >> - Rework the commit to introduce load_fw() op.
> >> - remove rproc_release_fw() call from rproc_start() as called in
> >> rproc_boot() and rproc_boot_recovery() in case of error.
> >> - create rproc_load_fw() and rproc_release_fw() internal functions.
> >> ---
> >> drivers/remoteproc/remoteproc_core.c | 16 +++++++++++++++-
> >> drivers/remoteproc/remoteproc_internal.h | 14 ++++++++++++++
> >> include/linux/remoteproc.h | 6 ++++++
> >> 3 files changed, 35 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
> >> index ace11ea17097..8df4b2c59bb6 100644
> >> --- a/drivers/remoteproc/remoteproc_core.c
> >> +++ b/drivers/remoteproc/remoteproc_core.c
> >> @@ -1488,6 +1488,7 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
> >> kfree(rproc->cached_table);
> >> rproc->cached_table = NULL;
> >> rproc->table_ptr = NULL;
> >> + rproc_release_fw(rproc);
> >
> > This is not needed since rproc_release_fw() is called in rproc_boot().
> >
> >> unprepare_rproc:
> >> /* release HW resources if needed */
> >> rproc_unprepare_device(rproc);
> >> @@ -1855,8 +1856,14 @@ static int rproc_boot_recovery(struct rproc *rproc)
> >> return ret;
> >> }
> >>
> >> + ret = rproc_load_fw(rproc, firmware_p);
> >> + if (ret)
> >> + return ret;
> >> +
> >> /* boot the remote processor up again */
> >> ret = rproc_start(rproc, firmware_p);
> >> + if (ret)
> >> + rproc_release_fw(rproc);
> >>
> >> release_firmware(firmware_p);
> >>
> >> @@ -1997,7 +2004,13 @@ int rproc_boot(struct rproc *rproc)
> >> goto downref_rproc;
> >> }
> >>
> >> + ret = rproc_load_fw(rproc, firmware_p);
> >> + if (ret)
> >> + goto downref_rproc;
> >
> > In case of error the firmware is not released.
>
> I considered that if the load fails, the firmware is not loaded
> and therefore does not need to be released.
> In other words, in case of a load error in OP-TEE, OP-TEE should
> perform the cleanup to return to its initial state before the load.
>
> Do you see a use case where we should force the release in Linux?
> Otherwise, I would propose to implement this behavior later if needed.
>
I'm talking about release_firmware() - it is not called if
rproc_load_fw(), and it needs to.
> Thanks,
> Arnaud
>
> >
> >> +
> >> ret = rproc_fw_boot(rproc, firmware_p);
> >> + if (ret)
> >> + rproc_release_fw(rproc);
> >>
> >> release_firmware(firmware_p);
> >> }
> >> @@ -2071,6 +2084,7 @@ int rproc_shutdown(struct rproc *rproc)
> >> kfree(rproc->cached_table);
> >> rproc->cached_table = NULL;
> >> rproc->table_ptr = NULL;
> >> + rproc_release_fw(rproc);
> >
> > Please move this after rproc_disable_iommu().
> >
> >
> >> out:
> >> mutex_unlock(&rproc->lock);
> >> return ret;
> >> @@ -2471,7 +2485,7 @@ static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
> >> if (!rproc->ops->coredump)
> >> rproc->ops->coredump = rproc_coredump;
> >>
> >> - if (rproc->ops->load)
> >> + if (rproc->ops->load || rproc->ops->load_fw)
> >> return 0;
> >>
> >> /* Default to ELF loader if no load function is specified */
> >> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
> >> index 0cd09e67ac14..2104ca449178 100644
> >> --- a/drivers/remoteproc/remoteproc_internal.h
> >> +++ b/drivers/remoteproc/remoteproc_internal.h
> >> @@ -221,4 +221,18 @@ bool rproc_u64_fit_in_size_t(u64 val)
> >> return (val <= (size_t) -1);
> >> }
> >>
> >> +static inline void rproc_release_fw(struct rproc *rproc)
> >> +{
> >> + if (rproc->ops->release_fw)
> >> + rproc->ops->release_fw(rproc);
> >> +}
> >> +
> >> +static inline int rproc_load_fw(struct rproc *rproc, const struct firmware *fw)
> >> +{
> >> + if (rproc->ops->load_fw)
> >> + return rproc->ops->load_fw(rproc, fw);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> #endif /* REMOTEPROC_INTERNAL_H */
> >> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
> >> index 2e0ddcb2d792..ba6fd560f7ba 100644
> >> --- a/include/linux/remoteproc.h
> >> +++ b/include/linux/remoteproc.h
> >> @@ -381,6 +381,10 @@ enum rsc_handling_status {
> >> * @panic: optional callback to react to system panic, core will delay
> >> * panic at least the returned number of milliseconds
> >> * @coredump: collect firmware dump after the subsystem is shutdown
> >> + * @load_fw: optional function to load non-ELF firmware image to memory, where the remote
> >
> > Round this down to 80 characters please. Here having a longer line doesn't
> > improve readability.
> >
> >> + * processor expects to find it.
> >> + * @release_fw: optional function to release the firmware image from memories.
> >> + * This function is called after stopping the remote processor or in case of error
> >
> > Same.
> >
> > More comments tomorrow or later during the week.
> >
> > Thanks,
> > Mathieu
> >
> >> */
> >> struct rproc_ops {
> >> int (*prepare)(struct rproc *rproc);
> >> @@ -403,6 +407,8 @@ struct rproc_ops {
> >> u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
> >> unsigned long (*panic)(struct rproc *rproc);
> >> void (*coredump)(struct rproc *rproc);
> >> + int (*load_fw)(struct rproc *rproc, const struct firmware *fw);
> >> + void (*release_fw)(struct rproc *rproc);
> >> };
> >>
> >> /**
> >> --
> >> 2.25.1
> >>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation
2024-12-06 17:05 ` Mathieu Poirier
@ 2024-12-06 17:07 ` Mathieu Poirier
2024-12-06 18:09 ` Arnaud POULIQUEN
0 siblings, 1 reply; 32+ messages in thread
From: Mathieu Poirier @ 2024-12-06 17:07 UTC (permalink / raw)
To: Arnaud POULIQUEN; +Cc: Bjorn Andersson, linux-remoteproc, linux-kernel
On Fri, 6 Dec 2024 at 10:05, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>
> On Thu, 5 Dec 2024 at 11:22, Arnaud POULIQUEN
> <arnaud.pouliquen@foss.st.com> wrote:
> >
> > Hello Mathieu,
> >
> > Thanks for the review!
> > I just need to clarify a point below before preparing the next revision.
> >
> > On 12/3/24 18:22, Mathieu Poirier wrote:
> > > On Thu, Nov 28, 2024 at 09:42:10AM +0100, Arnaud Pouliquen wrote:
> > >> This patch updates the rproc_ops structures to include two new optional
> > >> operations.
> > >>
> > >> - The load_fw() op is responsible for loading the remote processor
> > >> non-ELF firmware image before starting the boot sequence. This ops will
> > >> be used, for instance, to call OP-TEE to authenticate an load the firmware
> > >> image before accessing to its resources (a.e the resource table)
> > >>
> > >> - The release_fw op is responsible for releasing the remote processor
> > >> firmware image. For instance to clean memories.
> > >> The ops is called in the following cases:
> > >> - An error occurs between the loading of the firmware image and the
> > >> start of the remote processor.
> > >> - after stopping the remote processor.
> > >>
> > >> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> > >> ---
> > >> Update vs version V13:
> > >> - Rework the commit to introduce load_fw() op.
> > >> - remove rproc_release_fw() call from rproc_start() as called in
> > >> rproc_boot() and rproc_boot_recovery() in case of error.
> > >> - create rproc_load_fw() and rproc_release_fw() internal functions.
> > >> ---
> > >> drivers/remoteproc/remoteproc_core.c | 16 +++++++++++++++-
> > >> drivers/remoteproc/remoteproc_internal.h | 14 ++++++++++++++
> > >> include/linux/remoteproc.h | 6 ++++++
> > >> 3 files changed, 35 insertions(+), 1 deletion(-)
> > >>
> > >> diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
> > >> index ace11ea17097..8df4b2c59bb6 100644
> > >> --- a/drivers/remoteproc/remoteproc_core.c
> > >> +++ b/drivers/remoteproc/remoteproc_core.c
> > >> @@ -1488,6 +1488,7 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
> > >> kfree(rproc->cached_table);
> > >> rproc->cached_table = NULL;
> > >> rproc->table_ptr = NULL;
> > >> + rproc_release_fw(rproc);
> > >
> > > This is not needed since rproc_release_fw() is called in rproc_boot().
> > >
> > >> unprepare_rproc:
> > >> /* release HW resources if needed */
> > >> rproc_unprepare_device(rproc);
> > >> @@ -1855,8 +1856,14 @@ static int rproc_boot_recovery(struct rproc *rproc)
> > >> return ret;
> > >> }
> > >>
> > >> + ret = rproc_load_fw(rproc, firmware_p);
> > >> + if (ret)
> > >> + return ret;
> > >> +
> > >> /* boot the remote processor up again */
> > >> ret = rproc_start(rproc, firmware_p);
> > >> + if (ret)
> > >> + rproc_release_fw(rproc);
> > >>
> > >> release_firmware(firmware_p);
> > >>
> > >> @@ -1997,7 +2004,13 @@ int rproc_boot(struct rproc *rproc)
> > >> goto downref_rproc;
> > >> }
> > >>
> > >> + ret = rproc_load_fw(rproc, firmware_p);
> > >> + if (ret)
> > >> + goto downref_rproc;
> > >
> > > In case of error the firmware is not released.
> >
> > I considered that if the load fails, the firmware is not loaded
> > and therefore does not need to be released.
> > In other words, in case of a load error in OP-TEE, OP-TEE should
> > perform the cleanup to return to its initial state before the load.
> >
> > Do you see a use case where we should force the release in Linux?
> > Otherwise, I would propose to implement this behavior later if needed.
> >
>
> I'm talking about release_firmware() - it is not called if
> rproc_load_fw(), and it needs to.
Take 2: I'm talking about release_firwware() - it is not called if
rproc_load_fw() fails and it needs to.
>
> > Thanks,
> > Arnaud
> >
> > >
> > >> +
> > >> ret = rproc_fw_boot(rproc, firmware_p);
> > >> + if (ret)
> > >> + rproc_release_fw(rproc);
> > >>
> > >> release_firmware(firmware_p);
> > >> }
> > >> @@ -2071,6 +2084,7 @@ int rproc_shutdown(struct rproc *rproc)
> > >> kfree(rproc->cached_table);
> > >> rproc->cached_table = NULL;
> > >> rproc->table_ptr = NULL;
> > >> + rproc_release_fw(rproc);
> > >
> > > Please move this after rproc_disable_iommu().
> > >
> > >
> > >> out:
> > >> mutex_unlock(&rproc->lock);
> > >> return ret;
> > >> @@ -2471,7 +2485,7 @@ static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
> > >> if (!rproc->ops->coredump)
> > >> rproc->ops->coredump = rproc_coredump;
> > >>
> > >> - if (rproc->ops->load)
> > >> + if (rproc->ops->load || rproc->ops->load_fw)
> > >> return 0;
> > >>
> > >> /* Default to ELF loader if no load function is specified */
> > >> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
> > >> index 0cd09e67ac14..2104ca449178 100644
> > >> --- a/drivers/remoteproc/remoteproc_internal.h
> > >> +++ b/drivers/remoteproc/remoteproc_internal.h
> > >> @@ -221,4 +221,18 @@ bool rproc_u64_fit_in_size_t(u64 val)
> > >> return (val <= (size_t) -1);
> > >> }
> > >>
> > >> +static inline void rproc_release_fw(struct rproc *rproc)
> > >> +{
> > >> + if (rproc->ops->release_fw)
> > >> + rproc->ops->release_fw(rproc);
> > >> +}
> > >> +
> > >> +static inline int rproc_load_fw(struct rproc *rproc, const struct firmware *fw)
> > >> +{
> > >> + if (rproc->ops->load_fw)
> > >> + return rproc->ops->load_fw(rproc, fw);
> > >> +
> > >> + return 0;
> > >> +}
> > >> +
> > >> #endif /* REMOTEPROC_INTERNAL_H */
> > >> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
> > >> index 2e0ddcb2d792..ba6fd560f7ba 100644
> > >> --- a/include/linux/remoteproc.h
> > >> +++ b/include/linux/remoteproc.h
> > >> @@ -381,6 +381,10 @@ enum rsc_handling_status {
> > >> * @panic: optional callback to react to system panic, core will delay
> > >> * panic at least the returned number of milliseconds
> > >> * @coredump: collect firmware dump after the subsystem is shutdown
> > >> + * @load_fw: optional function to load non-ELF firmware image to memory, where the remote
> > >
> > > Round this down to 80 characters please. Here having a longer line doesn't
> > > improve readability.
> > >
> > >> + * processor expects to find it.
> > >> + * @release_fw: optional function to release the firmware image from memories.
> > >> + * This function is called after stopping the remote processor or in case of error
> > >
> > > Same.
> > >
> > > More comments tomorrow or later during the week.
> > >
> > > Thanks,
> > > Mathieu
> > >
> > >> */
> > >> struct rproc_ops {
> > >> int (*prepare)(struct rproc *rproc);
> > >> @@ -403,6 +407,8 @@ struct rproc_ops {
> > >> u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
> > >> unsigned long (*panic)(struct rproc *rproc);
> > >> void (*coredump)(struct rproc *rproc);
> > >> + int (*load_fw)(struct rproc *rproc, const struct firmware *fw);
> > >> + void (*release_fw)(struct rproc *rproc);
> > >> };
> > >>
> > >> /**
> > >> --
> > >> 2.25.1
> > >>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation
2024-12-06 17:07 ` Mathieu Poirier
@ 2024-12-06 18:09 ` Arnaud POULIQUEN
0 siblings, 0 replies; 32+ messages in thread
From: Arnaud POULIQUEN @ 2024-12-06 18:09 UTC (permalink / raw)
To: Mathieu Poirier; +Cc: Bjorn Andersson, linux-remoteproc, linux-kernel
On 12/6/24 18:07, Mathieu Poirier wrote:
> On Fri, 6 Dec 2024 at 10:05, Mathieu Poirier <mathieu.poirier@linaro.org> wrote:
>>
>> On Thu, 5 Dec 2024 at 11:22, Arnaud POULIQUEN
>> <arnaud.pouliquen@foss.st.com> wrote:
>>>
>>> Hello Mathieu,
>>>
>>> Thanks for the review!
>>> I just need to clarify a point below before preparing the next revision.
>>>
>>> On 12/3/24 18:22, Mathieu Poirier wrote:
>>>> On Thu, Nov 28, 2024 at 09:42:10AM +0100, Arnaud Pouliquen wrote:
>>>>> This patch updates the rproc_ops structures to include two new optional
>>>>> operations.
>>>>>
>>>>> - The load_fw() op is responsible for loading the remote processor
>>>>> non-ELF firmware image before starting the boot sequence. This ops will
>>>>> be used, for instance, to call OP-TEE to authenticate an load the firmware
>>>>> image before accessing to its resources (a.e the resource table)
>>>>>
>>>>> - The release_fw op is responsible for releasing the remote processor
>>>>> firmware image. For instance to clean memories.
>>>>> The ops is called in the following cases:
>>>>> - An error occurs between the loading of the firmware image and the
>>>>> start of the remote processor.
>>>>> - after stopping the remote processor.
>>>>>
>>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>>>>> ---
>>>>> Update vs version V13:
>>>>> - Rework the commit to introduce load_fw() op.
>>>>> - remove rproc_release_fw() call from rproc_start() as called in
>>>>> rproc_boot() and rproc_boot_recovery() in case of error.
>>>>> - create rproc_load_fw() and rproc_release_fw() internal functions.
>>>>> ---
>>>>> drivers/remoteproc/remoteproc_core.c | 16 +++++++++++++++-
>>>>> drivers/remoteproc/remoteproc_internal.h | 14 ++++++++++++++
>>>>> include/linux/remoteproc.h | 6 ++++++
>>>>> 3 files changed, 35 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
>>>>> index ace11ea17097..8df4b2c59bb6 100644
>>>>> --- a/drivers/remoteproc/remoteproc_core.c
>>>>> +++ b/drivers/remoteproc/remoteproc_core.c
>>>>> @@ -1488,6 +1488,7 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
>>>>> kfree(rproc->cached_table);
>>>>> rproc->cached_table = NULL;
>>>>> rproc->table_ptr = NULL;
>>>>> + rproc_release_fw(rproc);
>>>>
>>>> This is not needed since rproc_release_fw() is called in rproc_boot().
>>>>
>>>>> unprepare_rproc:
>>>>> /* release HW resources if needed */
>>>>> rproc_unprepare_device(rproc);
>>>>> @@ -1855,8 +1856,14 @@ static int rproc_boot_recovery(struct rproc *rproc)
>>>>> return ret;
>>>>> }
>>>>>
>>>>> + ret = rproc_load_fw(rproc, firmware_p);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> /* boot the remote processor up again */
>>>>> ret = rproc_start(rproc, firmware_p);
>>>>> + if (ret)
>>>>> + rproc_release_fw(rproc);
>>>>>
>>>>> release_firmware(firmware_p);
>>>>>
>>>>> @@ -1997,7 +2004,13 @@ int rproc_boot(struct rproc *rproc)
>>>>> goto downref_rproc;
>>>>> }
>>>>>
>>>>> + ret = rproc_load_fw(rproc, firmware_p);
>>>>> + if (ret)
>>>>> + goto downref_rproc;
>>>>
>>>> In case of error the firmware is not released.
>>>
>>> I considered that if the load fails, the firmware is not loaded
>>> and therefore does not need to be released.
>>> In other words, in case of a load error in OP-TEE, OP-TEE should
>>> perform the cleanup to return to its initial state before the load.
>>>
>>> Do you see a use case where we should force the release in Linux?
>>> Otherwise, I would propose to implement this behavior later if needed.
>>>
>>
>> I'm talking about release_firmware() - it is not called if
>> rproc_load_fw(), and it needs to.
>
> Take 2: I'm talking about release_firwware() - it is not called if
> rproc_load_fw() fails and it needs to.
I completely missed that, and moreover, there is the same error in
trproc_boot_recovery() :(
Thanks!
Arnaud
>
>>
>>> Thanks,
>>> Arnaud
>>>
>>>>
>>>>> +
>>>>> ret = rproc_fw_boot(rproc, firmware_p);
>>>>> + if (ret)
>>>>> + rproc_release_fw(rproc);
>>>>>
>>>>> release_firmware(firmware_p);
>>>>> }
>>>>> @@ -2071,6 +2084,7 @@ int rproc_shutdown(struct rproc *rproc)
>>>>> kfree(rproc->cached_table);
>>>>> rproc->cached_table = NULL;
>>>>> rproc->table_ptr = NULL;
>>>>> + rproc_release_fw(rproc);
>>>>
>>>> Please move this after rproc_disable_iommu().
>>>>
>>>>
>>>>> out:
>>>>> mutex_unlock(&rproc->lock);
>>>>> return ret;
>>>>> @@ -2471,7 +2485,7 @@ static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
>>>>> if (!rproc->ops->coredump)
>>>>> rproc->ops->coredump = rproc_coredump;
>>>>>
>>>>> - if (rproc->ops->load)
>>>>> + if (rproc->ops->load || rproc->ops->load_fw)
>>>>> return 0;
>>>>>
>>>>> /* Default to ELF loader if no load function is specified */
>>>>> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
>>>>> index 0cd09e67ac14..2104ca449178 100644
>>>>> --- a/drivers/remoteproc/remoteproc_internal.h
>>>>> +++ b/drivers/remoteproc/remoteproc_internal.h
>>>>> @@ -221,4 +221,18 @@ bool rproc_u64_fit_in_size_t(u64 val)
>>>>> return (val <= (size_t) -1);
>>>>> }
>>>>>
>>>>> +static inline void rproc_release_fw(struct rproc *rproc)
>>>>> +{
>>>>> + if (rproc->ops->release_fw)
>>>>> + rproc->ops->release_fw(rproc);
>>>>> +}
>>>>> +
>>>>> +static inline int rproc_load_fw(struct rproc *rproc, const struct firmware *fw)
>>>>> +{
>>>>> + if (rproc->ops->load_fw)
>>>>> + return rproc->ops->load_fw(rproc, fw);
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>> +
>>>>> #endif /* REMOTEPROC_INTERNAL_H */
>>>>> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
>>>>> index 2e0ddcb2d792..ba6fd560f7ba 100644
>>>>> --- a/include/linux/remoteproc.h
>>>>> +++ b/include/linux/remoteproc.h
>>>>> @@ -381,6 +381,10 @@ enum rsc_handling_status {
>>>>> * @panic: optional callback to react to system panic, core will delay
>>>>> * panic at least the returned number of milliseconds
>>>>> * @coredump: collect firmware dump after the subsystem is shutdown
>>>>> + * @load_fw: optional function to load non-ELF firmware image to memory, where the remote
>>>>
>>>> Round this down to 80 characters please. Here having a longer line doesn't
>>>> improve readability.
>>>>
>>>>> + * processor expects to find it.
>>>>> + * @release_fw: optional function to release the firmware image from memories.
>>>>> + * This function is called after stopping the remote processor or in case of error
>>>>
>>>> Same.
>>>>
>>>> More comments tomorrow or later during the week.
>>>>
>>>> Thanks,
>>>> Mathieu
>>>>
>>>>> */
>>>>> struct rproc_ops {
>>>>> int (*prepare)(struct rproc *rproc);
>>>>> @@ -403,6 +407,8 @@ struct rproc_ops {
>>>>> u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
>>>>> unsigned long (*panic)(struct rproc *rproc);
>>>>> void (*coredump)(struct rproc *rproc);
>>>>> + int (*load_fw)(struct rproc *rproc, const struct firmware *fw);
>>>>> + void (*release_fw)(struct rproc *rproc);
>>>>> };
>>>>>
>>>>> /**
>>>>> --
>>>>> 2.25.1
>>>>>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 2/8] remoteproc: Add TEE support
2024-11-28 8:42 ` [PATCH v15 2/8] remoteproc: Add TEE support Arnaud Pouliquen
2024-12-03 17:04 ` Mathieu Poirier
@ 2024-12-06 22:07 ` Bjorn Andersson
2024-12-10 8:57 ` Arnaud POULIQUEN
2025-03-25 11:05 ` Arnaud POULIQUEN
1 sibling, 2 replies; 32+ messages in thread
From: Bjorn Andersson @ 2024-12-06 22:07 UTC (permalink / raw)
To: Arnaud Pouliquen; +Cc: Mathieu Poirier, linux-kernel, linux-remoteproc
On Thu, Nov 28, 2024 at 09:42:09AM GMT, Arnaud Pouliquen wrote:
> Add a remoteproc TEE (Trusted Execution Environment) driver
> that will be probed by the TEE bus. If the associated Trusted
> application is supported on secure part this driver offers a client
> interface to load a firmware by the secure part.
If...else?
> This firmware could be authenticated by the secure trusted application.
>
I would like for this to fully describe how this fits with the bus and
how it is expected to be used by a specific remoteproc driver.
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> ---
> Updates vs version v13:
> - define REMOTEPROC_TEE as bool instead of tristate,
> - remove the load of the firmware in rproc_tee_parse_fw as we will ensure
> that the firmware is loaded using the load_fw() operation.
> ---
> drivers/remoteproc/Kconfig | 10 +
> drivers/remoteproc/Makefile | 1 +
> drivers/remoteproc/remoteproc_tee.c | 508 ++++++++++++++++++++++++++++
> include/linux/remoteproc.h | 4 +
> include/linux/remoteproc_tee.h | 105 ++++++
> 5 files changed, 628 insertions(+)
> create mode 100644 drivers/remoteproc/remoteproc_tee.c
> create mode 100644 include/linux/remoteproc_tee.h
>
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> index 955e4e38477e..f6335321d540 100644
> --- a/drivers/remoteproc/Kconfig
> +++ b/drivers/remoteproc/Kconfig
> @@ -23,6 +23,16 @@ config REMOTEPROC_CDEV
>
> It's safe to say N if you don't want to use this interface.
>
> +config REMOTEPROC_TEE
> + bool "Remoteproc support by a TEE application"
> + depends on OPTEE
> + help
> + Support a remote processor with a TEE application.
Does the remote processor run TEE applications? (Rethorical question...)
> The Trusted
> + Execution Context is responsible for loading the trusted firmware
> + image and managing the remote processor's lifecycle.
> +
> + It's safe to say N if you don't want to use remoteproc TEE.
It's not really about "wanting to use", it's a question whether your
device implements/provides the remoteproc TEE.
> +
> config IMX_REMOTEPROC
> tristate "i.MX remoteproc support"
> depends on ARCH_MXC
> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
> index 5ff4e2fee4ab..f77e0abe8349 100644
> --- a/drivers/remoteproc/Makefile
> +++ b/drivers/remoteproc/Makefile
> @@ -11,6 +11,7 @@ remoteproc-y += remoteproc_sysfs.o
> remoteproc-y += remoteproc_virtio.o
> remoteproc-y += remoteproc_elf_loader.o
> obj-$(CONFIG_REMOTEPROC_CDEV) += remoteproc_cdev.o
> +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o
> obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o
> obj-$(CONFIG_IMX_DSP_REMOTEPROC) += imx_dsp_rproc.o
> obj-$(CONFIG_INGENIC_VPU_RPROC) += ingenic_rproc.o
> diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c
> new file mode 100644
> index 000000000000..3fe3f31068f2
> --- /dev/null
> +++ b/drivers/remoteproc/remoteproc_tee.c
> @@ -0,0 +1,508 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) STMicroelectronics 2024
> + * Author: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> + */
> +
> +#include <linux/firmware.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/remoteproc.h>
> +#include <linux/remoteproc_tee.h>
> +#include <linux/slab.h>
> +#include <linux/tee_drv.h>
> +
> +#define MAX_TEE_PARAM_ARRAY_MEMBER 4
> +
> +/*
> + * Authentication of the firmware and load in the remote processor memory
Exactly what does this imply? Will the content of @memref be copied into
some other memory?
> + *
> + * [in] params[0].value.a: unique 32bit identifier of the remote processor
Why not just "remote processor identifier"?
> + * [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
Why is there two "FW" in this constant? Why isn't it just
"TA_RPROC_FW_CMD_START"?
And why is it not TEE_PROC_FW_CMD_START?
> +
> +/*
> + * 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.
These three lines describe a scenario that doesn't make any sense to me.
But if that's the case, you should be able to describe that the API
might give you a inaccessible pointer, by design.
> + *
> + * [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
What does this mean? What will I find at @memref after this call?
> + *
> + * [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
s/else return Null/or NULL/
> + */
> +#define TA_RPROC_FW_CMD_GET_COREDUMP 5
> +
> +/*
> + * Release remote processor firmware images and associated resources.
Exactly what does this mean for the caller?
> + * This command should be used in case an error occurs between the loading of
> + * the firmware images (TA_RPROC_CMD_LOAD_FW) and the starting of the remote
> + * processor (TA_RPROC_CMD_START_FW) or after stopping the remote processor
> + * to release associated resources (TA_RPROC_CMD_STOP_FW).
This description belongs adjacent to LOAD_FW, and describe it in terms
of what state LOAD_FW leaves the buffers and remote processor in.
> + *
> + * [in] params[0].value.a: Unique 32-bit remote processor identifier
> + */
> +#define TA_RPROC_CMD_RELEASE_FW 6
> +
> +struct rproc_tee_context {
> + struct list_head sessions;
> + struct tee_context *tee_ctx;
> + struct device *dev;
> +};
> +
> +static struct rproc_tee_context *rproc_tee_ctx;
> +
> +static void rproc_tee_prepare_args(struct rproc_tee *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_ARRAY_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,
> + };
> +}
> +
Provide kernel-doc for EXPORT_SYMBOL*() functions.
> +void rproc_tee_release_fw(struct rproc *rproc)
> +{
> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
> + struct tee_ioctl_invoke_arg arg;
> + int ret;
> +
> + if (!rproc) {
How can this happen?
This error will happen in two cases:
1) on your desk while you develop the client and you have to hunt
through the kernel log to figure out that the reason you can't start
your remoteproc is because 5 minutes ago there was a error log saying
that we didn't stop it last time.
2) in the customer device because of some obscure bug, where no one will
read the logs and the software will happily continue to execute with a
broken state.
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + /*
> + * If the remote processor state is RPROC_DETACHED, just ignore the
> + * request, as the remote processor is still running.
> + */
> + if (rproc->state == RPROC_DETACHED)
> + return;
> +
> + if (rproc->state != RPROC_OFFLINE) {
> + ret = -EBUSY;
The function is void... Defensive coding is only useful when it saves
you from future mistakes, not when it hides problems from you.
> + goto out;
> + }
> +
> + rproc_tee_prepare_args(trproc, TA_RPROC_CMD_RELEASE_FW, &arg, param, 0);
> +
> + ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
> + if (ret < 0 || arg.ret != 0) {
> + dev_err(rproc_tee_ctx->dev,
> + "TA_RPROC_CMD_RELEASE_FW invoke failed TEE err: %x, ret:%x\n",
> + arg.ret, ret);
At least @ret will be base 10, so don't print that in base 16 without
indication that it's not base 10.
Also, this will result in two dev_err() prints, printing out two
different error codes.
> + ret = -EIO;
> + }
> +
> +out:
> + if (ret)
> + /* Unexpected state without solution to come back in a stable state */
> + dev_err(rproc_tee_ctx->dev, "Failed to release TEE remoteproc firmware: %d\n", ret);
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_release_fw);
> +
kernel-doc.
> +int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw)
> +{
> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
> + struct tee_ioctl_invoke_arg arg;
> + struct tee_shm *fw_shm;
> + int ret;
> +
> + if (!trproc)
> + return -EINVAL;
> +
> + fw_shm = tee_shm_register_kernel_buf(rproc_tee_ctx->tee_ctx, (void *)fw->data, fw->size);
> + if (IS_ERR(fw_shm))
> + return PTR_ERR(fw_shm);
> +
> + rproc_tee_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(rproc_tee_ctx->tee_ctx, &arg, param);
> + if (ret < 0 || arg.ret != 0) {
> + dev_err(rproc_tee_ctx->dev,
> + "TA_RPROC_FW_CMD_LOAD_FW invoke failed TEE err: %x, ret:%x\n",
More confused bases
> + arg.ret, ret);
> + if (!ret)
> + ret = -EIO;
> + }
> +
> + tee_shm_free(fw_shm);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_load_fw);
> +
> +static int rproc_tee_get_loaded_rsc_table(struct rproc *rproc, phys_addr_t *rsc_pa,
> + size_t *table_sz)
> +{
> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
> + struct tee_ioctl_invoke_arg arg;
> + int ret;
> +
> + if (!trproc)
> + return -EINVAL;
> +
> + rproc_tee_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(rproc_tee_ctx->tee_ctx, &arg, param);
> + if (ret < 0 || arg.ret != 0) {
> + dev_err(rproc_tee_ctx->dev,
> + "TA_RPROC_FW_CMD_GET_RSC_TABLE invoke failed TEE err: %x, ret:%x\n",
> + arg.ret, ret);
> + return -EIO;
> + }
> +
> + *table_sz = param[2].u.value.a;
> +
> + if (*table_sz)
> + *rsc_pa = param[1].u.value.a;
> + else
> + *rsc_pa = 0;
> +
> + return 0;
> +}
> +
> +int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw)
> +{
> + phys_addr_t rsc_table;
> + void __iomem *rsc_va;
> + size_t table_sz;
> + int ret;
> +
> + if (!rproc)
> + return -EINVAL;
> +
> + /* At this point, the firmware has to be loaded to be able to parse the resource table. */
There's two ways to read this, either you're saying "we now need to load
the firmware, so that we can parse the resource table", or "let's hope
the firmware is loaded because I will parse it now!"
> +
> + ret = rproc_tee_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
> + if (ret)
> + goto release_fw;
> +
> + /*
> + * We assume here that the memory mapping is the same between the TEE and Linux kernel
> + * contexts. Else a new TEE remoteproc service could be needed to get a copy of the
> + * resource table
> + */
> + rsc_va = ioremap_wc(rsc_table, table_sz);
> + if (IS_ERR_OR_NULL(rsc_va)) {
When does ioremap_wc() return IS_ERR()?
> + dev_err(rproc_tee_ctx->dev, "Unable to map memory region: %pa+%zx\n",
> + &rsc_table, table_sz);
> + ret = -ENOMEM;
> + goto release_fw;
> + }
> +
> + /*
> + * Create a copy of the resource table to have the same behavior as the ELF loader.
> + * This cached table will be used by the remoteproc core after the remoteproc stops
> + * to free resources and for crash recovery to reapply the settings.
> + * The cached table will be freed by the remoteproc core.
> + */
> + rproc->cached_table = kmemdup((__force void *)rsc_va, table_sz, GFP_KERNEL);
> + iounmap(rsc_va);
> +
> + if (!rproc->cached_table) {
> + ret = -ENOMEM;
> + goto release_fw;
> + }
> +
> + rproc->table_ptr = rproc->cached_table;
> + rproc->table_sz = table_sz;
> +
> + return 0;
> +
> +release_fw:
> + rproc_tee_release_fw(rproc);
This unrolls state that this function didn't establish. This will at
best confuse the caller.
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_parse_fw);
> +
> +struct resource_table *rproc_tee_find_loaded_rsc_table(struct rproc *rproc,
> + const struct firmware *fw)
> +{
> + phys_addr_t rsc_table;
> + size_t table_sz;
> + int ret;
> +
> + ret = rproc_tee_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
> + if (ret)
> + return NULL;
> +
> + rproc->table_sz = table_sz;
> + if (!table_sz)
> + return NULL;
> +
> + /*
> + * At this step the memory area that contains the resource table should have been registered
> + * by the remote proc platform driver and allocated by rproc_alloc_registered_carveouts().
> + */
> + return (struct resource_table *)rproc_pa_to_va(rproc, rsc_table, table_sz, NULL);
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_find_loaded_rsc_table);
> +
> +int rproc_tee_start(struct rproc *rproc)
> +{
> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
> + struct tee_ioctl_invoke_arg arg;
> + int ret = 0;
> +
> + if (!trproc)
> + return -EINVAL;
> +
> + rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_START_FW, &arg, param, 0);
> +
> + ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
> + if (ret < 0 || arg.ret != 0) {
> + dev_err(rproc_tee_ctx->dev,
> + "TA_RPROC_FW_CMD_START_FW invoke failed TEE err: %x, ret:%x\n",
> + arg.ret, ret);
> + if (!ret)
> + return -EIO;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_start);
> +
> +int rproc_tee_stop(struct rproc *rproc)
> +{
> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
> + struct tee_ioctl_invoke_arg arg;
> + int ret;
> +
> + if (!trproc)
> + return -EINVAL;
> +
> + rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_STOP_FW, &arg, param, 0);
> +
> + ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
> + if (ret < 0 || arg.ret != 0) {
> + dev_err(rproc_tee_ctx->dev,
> + "TA_RPROC_FW_CMD_STOP_FW invoke failed TEE err: %x, ret:%x\n",
> + arg.ret, ret);
> + if (!ret)
> + ret = -EIO;
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_stop);
> +
> +static const struct tee_client_device_id rproc_tee_id_table[] = {
> + {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)},
> + {}
> +};
> +
> +int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id)
> +{
> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
> + struct tee_ioctl_open_session_arg sess_arg;
> + struct tee_client_device *tee_device;
> + struct rproc_tee *trproc;
> + int ret;
> +
> + /*
> + * Test if the device has been probed by the TEE bus. In case of failure, we ignore the
> + * reason. The bus could be not yet probed or the service not available in the secure
> + * firmware.The assumption in such a case is that the TEE remoteproc is not probed.
> + */
> + if (!rproc_tee_ctx)
> + return -EPROBE_DEFER;
> +
> + /* Prevent rproc tee module from being removed */
> + if (!try_module_get(THIS_MODULE)) {
We're doing this in the core because there's no direct dependency on the
remoteproc driver which we will jump to through rproc_ops.
In contrast, your remoteproc driver will be prevented from being
unloaded by the core's refcount and this module can't be unloaded
because your driver is referencing it directly.
That said, none of this matter now that you made the tee driver bool.
I.e. drop this.
> + dev_err(rproc_tee_ctx->dev, "can't get owner\n");
> + return -ENODEV;
> + }
> +
> + trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
> + if (!trproc) {
> + ret = -ENOMEM;
> + goto module_put;
> + }
> +
> + tee_device = to_tee_client_device(rproc_tee_ctx->dev);
> + memset(&sess_arg, 0, sizeof(sess_arg));
> +
> + memcpy(sess_arg.uuid, tee_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
> +
> + sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
> + sess_arg.num_params = 1;
> +
> + param[0] = (struct tee_param) {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
> + .u.value.a = rproc_id,
> + };
> +
> + ret = tee_client_open_session(rproc_tee_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);
> + ret = -EINVAL;
> + goto module_put;
> + }
> +
> + trproc->parent = dev;
> + trproc->rproc_id = rproc_id;
> + trproc->session_id = sess_arg.session;
> +
> + trproc->rproc = rproc;
> + rproc->rproc_tee_itf = trproc;
> +
> + list_add_tail(&trproc->node, &rproc_tee_ctx->sessions);
> +
> + return 0;
> +
> +module_put:
> + module_put(THIS_MODULE);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_register);
> +
> +int rproc_tee_unregister(struct rproc *rproc)
> +{
> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
> + int ret;
> +
> + if (!rproc->rproc_tee_itf)
How can this happen?
> + return -ENODEV;
> +
> + ret = tee_client_close_session(rproc_tee_ctx->tee_ctx, trproc->session_id);
> + if (ret < 0)
> + dev_err(trproc->parent, "tee_client_close_session failed, err: %x\n", ret);
> +
> + list_del(&trproc->node);
> + rproc->rproc_tee_itf = NULL;
> +
> + module_put(THIS_MODULE);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_unregister);
> +
> +static int rproc_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 rproc_tee_probe(struct device *dev)
> +{
> + struct tee_context *tee_ctx;
> + int ret;
> +
> + /* Open context with TEE driver */
> + tee_ctx = tee_client_open_context(NULL, rproc_tee_ctx_match, NULL, NULL);
> + if (IS_ERR(tee_ctx))
> + return PTR_ERR(tee_ctx);
> +
> + rproc_tee_ctx = devm_kzalloc(dev, sizeof(*rproc_tee_ctx), GFP_KERNEL);
> + if (!rproc_tee_ctx) {
> + ret = -ENOMEM;
> + goto err;
> + }
rproc_tee_register() checks if rproc_tee_ctx is non-NULL before
continuing, which means that if you have a client calling that at the
same time as you're here...you will have a NULL dereference - and/or
other exciting issues.
> +
> + rproc_tee_ctx->dev = dev;
> + rproc_tee_ctx->tee_ctx = tee_ctx;
> + INIT_LIST_HEAD(&rproc_tee_ctx->sessions);
> +
> + return 0;
> +err:
> + tee_client_close_context(tee_ctx);
devm_kzalloc() seems like a simpler function, and it's definitely easier
to unroll than the tee context, so flip the open/alloc and your cleanup
will be easier.
> +
> + return ret;
> +}
> +
> +static int rproc_tee_remove(struct device *dev)
> +{
> + struct rproc_tee *entry, *tmp;
> +
> + list_for_each_entry_safe(entry, tmp, &rproc_tee_ctx->sessions, node) {
> + tee_client_close_session(rproc_tee_ctx->tee_ctx, entry->session_id);
> + list_del(&entry->node);
> + kfree(entry);
So each remoteproc driver have a rproc_tee context on
&rproc_tee_ctx-->sessions and without telling them, you just destroy
and free their context?
Perhaps I'm misinterpreting what you're doing here, but did you test
this?
> + }
> +
> + tee_client_close_context(rproc_tee_ctx->tee_ctx);
As you return here, the devres-allocated rproc_tee_ctx memory will be
freed, and you have a seemingly valid looking pointer and whole bunch of
use-after-free cases.
> +
> + return 0;
> +}
> +
> +MODULE_DEVICE_TABLE(tee, rproc_tee_id_table);
> +
> +static struct tee_client_driver rproc_tee_fw_driver = {
> + .id_table = rproc_tee_id_table,
> + .driver = {
> + .name = KBUILD_MODNAME,
> + .bus = &tee_bus_type,
> + .probe = rproc_tee_probe,
> + .remove = rproc_tee_remove,
> + },
> +};
> +
> +static int __init rproc_tee_fw_mod_init(void)
> +{
> + return driver_register(&rproc_tee_fw_driver.driver);
> +}
> +
> +static void __exit rproc_tee_fw_mod_exit(void)
> +{
> + driver_unregister(&rproc_tee_fw_driver.driver);
> +}
> +
> +module_init(rproc_tee_fw_mod_init);
> +module_exit(rproc_tee_fw_mod_exit);
Please add an equivalent of the module_platform_driver() macro to tee
framework instead of open-coding this.
> +
> +MODULE_DESCRIPTION(" remote processor TEE module");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
> index 8fd0d7f63c8e..2e0ddcb2d792 100644
> --- a/include/linux/remoteproc.h
> +++ b/include/linux/remoteproc.h
> @@ -503,6 +503,8 @@ enum rproc_features {
> RPROC_MAX_FEATURES,
> };
>
> +struct rproc_tee;
> +
> /**
> * struct rproc - represents a physical remote processor device
> * @node: list node of this rproc object
> @@ -545,6 +547,7 @@ enum rproc_features {
> * @cdev: character device of the rproc
> * @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release
> * @features: indicate remoteproc features
> + * @rproc_tee_itf: pointer to the remoteproc tee context
> */
> struct rproc {
> struct list_head node;
> @@ -586,6 +589,7 @@ struct rproc {
> struct cdev cdev;
> bool cdev_put_on_release;
> DECLARE_BITMAP(features, RPROC_MAX_FEATURES);
> + struct rproc_tee *rproc_tee_itf;
TEE is just one specific remoteproc implementation, why does it need to
infest the core data structure? Do you want a stm32_rproc here as well?
> };
>
> /**
> diff --git a/include/linux/remoteproc_tee.h b/include/linux/remoteproc_tee.h
> new file mode 100644
> index 000000000000..9b498a8eff4d
> --- /dev/null
> +++ b/include/linux/remoteproc_tee.h
> @@ -0,0 +1,105 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright(c) 2024 STMicroelectronics
> + */
> +
> +#ifndef REMOTEPROC_TEE_H
> +#define REMOTEPROC_TEE_H
> +
> +#include <linux/tee_drv.h>
> +#include <linux/firmware.h>
> +#include <linux/remoteproc.h>
> +
> +struct rproc;
> +
> +/**
> + * struct rproc_tee - TEE remoteproc structure
> + * @node: Reference in list
> + * @rproc: Remoteproc reference
> + * @parent: Parent device
Isn't that rproc->dev->parent?
> + * @rproc_id: Identifier of the target firmware
> + * @session_id: TEE session identifier
> + */
> +struct rproc_tee {
As far as I can tell this isn't dereferenced outside remoteproc_tee.c,
can we hide it therein?
> + struct list_head node;
> + struct rproc *rproc;
> + struct device *parent;
> + u32 rproc_id;
> + u32 session_id;
> +};
> +
> +#if IS_REACHABLE(CONFIG_REMOTEPROC_TEE)
> +
> +int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id);
> +int rproc_tee_unregister(struct rproc *rproc);
> +int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw);
> +int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw);
> +void rproc_tee_release_fw(struct rproc *rproc);
> +struct resource_table *rproc_tee_find_loaded_rsc_table(struct rproc *rproc,
> + const struct firmware *fw);
> +int rproc_tee_start(struct rproc *rproc);
> +int rproc_tee_stop(struct rproc *rproc);
> +
> +#else
> +
Do we really need yet another bunch of stubs? Can't we just leave
CONFIG_REMOTEPROC_TEE non-user-selectable and have the drivers that rely
on it do "select REMOTEPROC_TEE"?
If my measurements are correct, it's 3.1kB of code...
Regards,
Bjorn
> +static inline int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id)
> +{
> + return -ENODEV;
> +}
> +
> +static inline int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int rproc_tee_unregister(struct rproc *rproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int rproc_tee_start(struct rproc *rproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline int rproc_tee_stop(struct rproc *rproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return 0;
> +}
> +
> +static inline void rproc_tee_release_fw(struct rproc *rproc)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +}
> +
> +static inline struct resource_table *
> +rproc_tee_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
> +{
> + /* This shouldn't be possible */
> + WARN_ON(1);
> +
> + return NULL;
> +}
> +#endif /* CONFIG_REMOTEPROC_TEE */
> +#endif /* REMOTEPROC_TEE_H */
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation
2024-11-28 8:42 ` [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation Arnaud Pouliquen
2024-12-03 17:22 ` Mathieu Poirier
2024-12-04 17:39 ` Mathieu Poirier
@ 2024-12-09 23:14 ` Bjorn Andersson
2024-12-10 10:33 ` Arnaud POULIQUEN
2 siblings, 1 reply; 32+ messages in thread
From: Bjorn Andersson @ 2024-12-09 23:14 UTC (permalink / raw)
To: Arnaud Pouliquen; +Cc: Mathieu Poirier, linux-remoteproc, linux-kernel
On Thu, Nov 28, 2024 at 09:42:10AM GMT, Arnaud Pouliquen wrote:
> This patch updates the rproc_ops structures to include two new optional
> operations.
>
> - The load_fw() op is responsible for loading the remote processor
> non-ELF firmware image before starting the boot sequence. This ops will
> be used, for instance, to call OP-TEE to authenticate an load the firmware
> image before accessing to its resources (a.e the resource table)
>
> - The release_fw op is responsible for releasing the remote processor
> firmware image. For instance to clean memories.
> The ops is called in the following cases:
> - An error occurs between the loading of the firmware image and the
> start of the remote processor.
> - after stopping the remote processor.
>
Why does this difference need to be encoded in rproc_ops? I think we
should strive for having a single, simple high level flow of operations
through the remoteproc core for which the specifics of each remoteproc
instance can be encoded in that driver.
Perhaps there's a good reason for this, but if so please read and follow
https://docs.kernel.org/process/submitting-patches.html#describe-your-changes
to make that reasoning clear in the commit message.
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> ---
> Update vs version V13:
> - Rework the commit to introduce load_fw() op.
> - remove rproc_release_fw() call from rproc_start() as called in
> rproc_boot() and rproc_boot_recovery() in case of error.
> - create rproc_load_fw() and rproc_release_fw() internal functions.
> ---
> drivers/remoteproc/remoteproc_core.c | 16 +++++++++++++++-
> drivers/remoteproc/remoteproc_internal.h | 14 ++++++++++++++
> include/linux/remoteproc.h | 6 ++++++
> 3 files changed, 35 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
> index ace11ea17097..8df4b2c59bb6 100644
> --- a/drivers/remoteproc/remoteproc_core.c
> +++ b/drivers/remoteproc/remoteproc_core.c
> @@ -1488,6 +1488,7 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
> kfree(rproc->cached_table);
> rproc->cached_table = NULL;
> rproc->table_ptr = NULL;
> + rproc_release_fw(rproc);
> unprepare_rproc:
> /* release HW resources if needed */
> rproc_unprepare_device(rproc);
> @@ -1855,8 +1856,14 @@ static int rproc_boot_recovery(struct rproc *rproc)
> return ret;
> }
>
> + ret = rproc_load_fw(rproc, firmware_p);
It is not clear to me why in the case of OP-TEE we need to invoke the
"load operation" here, and in the case of "legacy" ELF loading we do it
first thing in rproc_start() (i.e. on the very next line of code being
executed).
Should we start by renaming rproc_load_segments() rproc_load() and move
it out of rproc_start()? (I.e. here?)
Perhaps define that rproc_load() is responsible for "loading firmware"
(whatever that means) and establishing rproc->cached_table, and
rproc->table_ptr?
(Note that this seems like a good cleanup of the spaghetti regardless)
> + if (ret)
> + return ret;
> +
> /* boot the remote processor up again */
> ret = rproc_start(rproc, firmware_p);
> + if (ret)
> + rproc_release_fw(rproc);
The fact that you rproc_release_fw() in the error path here, right
before we unconditionally release_firmware() the actual firmware means
that you have 2 different life cycles with very very similar names.
This will contain bugs, sooner or later.
>
> release_firmware(firmware_p);
>
> @@ -1997,7 +2004,13 @@ int rproc_boot(struct rproc *rproc)
> goto downref_rproc;
> }
>
> + ret = rproc_load_fw(rproc, firmware_p);
> + if (ret)
> + goto downref_rproc;
> +
> ret = rproc_fw_boot(rproc, firmware_p);
> + if (ret)
> + rproc_release_fw(rproc);
>
> release_firmware(firmware_p);
> }
> @@ -2071,6 +2084,7 @@ int rproc_shutdown(struct rproc *rproc)
> kfree(rproc->cached_table);
> rproc->cached_table = NULL;
> rproc->table_ptr = NULL;
> + rproc_release_fw(rproc);
> out:
> mutex_unlock(&rproc->lock);
> return ret;
> @@ -2471,7 +2485,7 @@ static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
> if (!rproc->ops->coredump)
> rproc->ops->coredump = rproc_coredump;
>
> - if (rproc->ops->load)
> + if (rproc->ops->load || rproc->ops->load_fw)
> return 0;
>
> /* Default to ELF loader if no load function is specified */
> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
> index 0cd09e67ac14..2104ca449178 100644
> --- a/drivers/remoteproc/remoteproc_internal.h
> +++ b/drivers/remoteproc/remoteproc_internal.h
> @@ -221,4 +221,18 @@ bool rproc_u64_fit_in_size_t(u64 val)
> return (val <= (size_t) -1);
> }
>
> +static inline void rproc_release_fw(struct rproc *rproc)
> +{
> + if (rproc->ops->release_fw)
> + rproc->ops->release_fw(rproc);
> +}
> +
> +static inline int rproc_load_fw(struct rproc *rproc, const struct firmware *fw)
> +{
> + if (rproc->ops->load_fw)
> + return rproc->ops->load_fw(rproc, fw);
> +
> + return 0;
> +}
> +
> #endif /* REMOTEPROC_INTERNAL_H */
> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
> index 2e0ddcb2d792..ba6fd560f7ba 100644
> --- a/include/linux/remoteproc.h
> +++ b/include/linux/remoteproc.h
> @@ -381,6 +381,10 @@ enum rsc_handling_status {
> * @panic: optional callback to react to system panic, core will delay
> * panic at least the returned number of milliseconds
> * @coredump: collect firmware dump after the subsystem is shutdown
> + * @load_fw: optional function to load non-ELF firmware image to memory, where the remote
> + * processor expects to find it.
Why does it matter if it's an ELF or not?
In the Qualcomm case, firmware comes in ELF format, Linux loads the
LOAD segments and the trusted world then authenticates the content and
start the remote processor.
I think the difference in your case is that you have memory reserved
elsewhere, and you want the "load" operation to pass the firmware to the
TEE - which means that you need rproc_release_fw() to eventually clean
up the state if rproc_start() fails - and upon shutdown.
If we improve the definition of rproc_load_segments() to mean
"remoteproc (or remoteproc driver) is loading segments", then in your
case there's no "loading" operation in Linux. Instead you make that a
nop and invoke LOAD_FW and START_FW within your start callback, then you
can clean up the remnant state within your driver's start and stop
callbacks - without complicating the core framework.
Regards,
Bjorn
> + * @release_fw: optional function to release the firmware image from memories.
> + * This function is called after stopping the remote processor or in case of error
> */
> struct rproc_ops {
> int (*prepare)(struct rproc *rproc);
> @@ -403,6 +407,8 @@ struct rproc_ops {
> u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
> unsigned long (*panic)(struct rproc *rproc);
> void (*coredump)(struct rproc *rproc);
> + int (*load_fw)(struct rproc *rproc, const struct firmware *fw);
> + void (*release_fw)(struct rproc *rproc);
> };
>
> /**
> --
> 2.25.1
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 2/8] remoteproc: Add TEE support
2024-12-06 22:07 ` Bjorn Andersson
@ 2024-12-10 8:57 ` Arnaud POULIQUEN
2025-01-10 8:51 ` Arnaud POULIQUEN
2025-02-12 3:18 ` Bjorn Andersson
2025-03-25 11:05 ` Arnaud POULIQUEN
1 sibling, 2 replies; 32+ messages in thread
From: Arnaud POULIQUEN @ 2024-12-10 8:57 UTC (permalink / raw)
To: Bjorn Andersson; +Cc: Mathieu Poirier, linux-kernel, linux-remoteproc
Hello Bjorn,
On 12/6/24 23:07, Bjorn Andersson wrote:
> On Thu, Nov 28, 2024 at 09:42:09AM GMT, Arnaud Pouliquen wrote:
>> Add a remoteproc TEE (Trusted Execution Environment) driver
>> that will be probed by the TEE bus. If the associated Trusted
>> application is supported on secure part this driver offers a client
>> interface to load a firmware by the secure part.
>
> If...else?
>
>> This firmware could be authenticated by the secure trusted application.
>>
>
> I would like for this to fully describe how this fits with the bus and
Are you speaking about the OP-TEE bus?
I assume that your attempt is that I provide more details on the live cycle
sequence, right?
> how it is expected to be used by a specific remoteproc driver.
>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>> ---
>> Updates vs version v13:
>> - define REMOTEPROC_TEE as bool instead of tristate,
>> - remove the load of the firmware in rproc_tee_parse_fw as we will ensure
>> that the firmware is loaded using the load_fw() operation.
>> ---
>> drivers/remoteproc/Kconfig | 10 +
>> drivers/remoteproc/Makefile | 1 +
>> drivers/remoteproc/remoteproc_tee.c | 508 ++++++++++++++++++++++++++++
>> include/linux/remoteproc.h | 4 +
>> include/linux/remoteproc_tee.h | 105 ++++++
>> 5 files changed, 628 insertions(+)
>> create mode 100644 drivers/remoteproc/remoteproc_tee.c
>> create mode 100644 include/linux/remoteproc_tee.h
>>
>> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
>> index 955e4e38477e..f6335321d540 100644
>> --- a/drivers/remoteproc/Kconfig
>> +++ b/drivers/remoteproc/Kconfig
>> @@ -23,6 +23,16 @@ config REMOTEPROC_CDEV
>>
>> It's safe to say N if you don't want to use this interface.
>>
>> +config REMOTEPROC_TEE
>> + bool "Remoteproc support by a TEE application"
>> + depends on OPTEE
>> + help
>> + Support a remote processor with a TEE application.
>
> Does the remote processor run TEE applications? (Rethorical question...)
>
>> The Trusted
>> + Execution Context is responsible for loading the trusted firmware
>> + image and managing the remote processor's lifecycle.
>> +
>> + It's safe to say N if you don't want to use remoteproc TEE.
>
> It's not really about "wanting to use", it's a question whether your
> device implements/provides the remoteproc TEE.
>
>> +
>> config IMX_REMOTEPROC
>> tristate "i.MX remoteproc support"
>> depends on ARCH_MXC
>> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
>> index 5ff4e2fee4ab..f77e0abe8349 100644
>> --- a/drivers/remoteproc/Makefile
>> +++ b/drivers/remoteproc/Makefile
>> @@ -11,6 +11,7 @@ remoteproc-y += remoteproc_sysfs.o
>> remoteproc-y += remoteproc_virtio.o
>> remoteproc-y += remoteproc_elf_loader.o
>> obj-$(CONFIG_REMOTEPROC_CDEV) += remoteproc_cdev.o
>> +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o
>> obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o
>> obj-$(CONFIG_IMX_DSP_REMOTEPROC) += imx_dsp_rproc.o
>> obj-$(CONFIG_INGENIC_VPU_RPROC) += ingenic_rproc.o
>> diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c
>> new file mode 100644
>> index 000000000000..3fe3f31068f2
>> --- /dev/null
>> +++ b/drivers/remoteproc/remoteproc_tee.c
>> @@ -0,0 +1,508 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * Copyright (C) STMicroelectronics 2024
>> + * Author: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>> + */
>> +
>> +#include <linux/firmware.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/remoteproc.h>
>> +#include <linux/remoteproc_tee.h>
>> +#include <linux/slab.h>
>> +#include <linux/tee_drv.h>
>> +
>> +#define MAX_TEE_PARAM_ARRAY_MEMBER 4
>> +
>> +/*
>> + * Authentication of the firmware and load in the remote processor memory
>
> Exactly what does this imply? Will the content of @memref be copied into
> some other memory?
The objective is to authenticate and load in one step. So, yes, the image is
loaded into the remoteproc destination memory.
On stm32mp1 we can not store the elf file in a temporary secure memory as
the memory is encrypted by software (this would take to much time).
For your information, in OP-TEE, the application code is split into a generic
part and a platform adaptation layer. The generic application is mainly
responsible for:
- Copying the binary header and metadata into secure memory and authenticating them.
- Parsing the ELF images and providing segments to load with associated
authenticated hashes to the platform application.
In the future, someone can add their own format if needed.
But the generic part could be enhance to authenticate and load a non ELF binary.
So I'm trying to be generic as possible here.
>
>> + *
>> + * [in] params[0].value.a: unique 32bit identifier of the remote processor
>
> Why not just "remote processor identifier"?
>
>> + * [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
>
> Why is there two "FW" in this constant? Why isn't it just
> "TA_RPROC_FW_CMD_START"?
>
> And why is it not TEE_PROC_FW_CMD_START?
>
>> +
>> +/*
>> + * 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.
>
> These three lines describe a scenario that doesn't make any sense to me.
> But if that's the case, you should be able to describe that the API
> might give you a inaccessible pointer, by design.
On STM32MP, we have a kind of firewall in OP-TEE that sets memory access rights
from the device tree. So if the firmware image is not linked according to the
firewall configuration, the pointer may not be accessible.
I will update the comment as you propose.
>
>> + *
>> + * [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
>
> What does this mean? What will I find at @memref after this call?
I do not have a simple answer here as it depends on the OP-TEE strategy.
It could be an obscure core dump with possible encryption.
I will remove this as it is not yet implemented in OP-TEE.
https://elixir.bootlin.com/op-tee/4.4.0/source/ta/remoteproc/src/remoteproc_core.c#L1131
>
>> + *
>> + * [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
>
> s/else return Null/or NULL/
>
>> + */
>> +#define TA_RPROC_FW_CMD_GET_COREDUMP 5
>> +
>> +/*
>> + * Release remote processor firmware images and associated resources.
>
> Exactly what does this mean for the caller?
It is platform dependent. It can consist in cleaning the memory, but
can be also something else such as firewall configuration.
On stm323mp we clean all the memories region reserved for the remote processor.
>
>> + * This command should be used in case an error occurs between the loading of
>> + * the firmware images (TA_RPROC_CMD_LOAD_FW) and the starting of the remote
>> + * processor (TA_RPROC_CMD_START_FW) or after stopping the remote processor
>> + * to release associated resources (TA_RPROC_CMD_STOP_FW).
>
> This description belongs adjacent to LOAD_FW, and describe it in terms
> of what state LOAD_FW leaves the buffers and remote processor in.
Sorry, it is not clear to me what you are expecting here.
>
>> + *
>> + * [in] params[0].value.a: Unique 32-bit remote processor identifier
>> + */
>> +#define TA_RPROC_CMD_RELEASE_FW 6
>> +
>> +struct rproc_tee_context {
>> + struct list_head sessions;
>> + struct tee_context *tee_ctx;
>> + struct device *dev;
>> +};
>> +
>> +static struct rproc_tee_context *rproc_tee_ctx;
>> +
>> +static void rproc_tee_prepare_args(struct rproc_tee *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_ARRAY_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,
>> + };
>> +}
>> +
>
> Provide kernel-doc for EXPORT_SYMBOL*() functions.
Should it be in the remoteproc kernel doc or in a new doc file that
provide an overview of the remoteproc_tee usage?
>
>> +void rproc_tee_release_fw(struct rproc *rproc)
>> +{
>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
>> + struct tee_ioctl_invoke_arg arg;
>> + int ret;
>> +
>> + if (!rproc) {
>
> How can this happen?
>
> This error will happen in two cases:
>
> 1) on your desk while you develop the client and you have to hunt
> through the kernel log to figure out that the reason you can't start
> your remoteproc is because 5 minutes ago there was a error log saying
> that we didn't stop it last time.
>
> 2) in the customer device because of some obscure bug, where no one will
> read the logs and the software will happily continue to execute with a
> broken state.
>
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + /*
>> + * If the remote processor state is RPROC_DETACHED, just ignore the
>> + * request, as the remote processor is still running.
>> + */
>> + if (rproc->state == RPROC_DETACHED)
>> + return;
>> +
>> + if (rproc->state != RPROC_OFFLINE) {
>> + ret = -EBUSY;
>
> The function is void... Defensive coding is only useful when it saves
> you from future mistakes, not when it hides problems from you.
>
>> + goto out;
>> + }
>> +
>> + rproc_tee_prepare_args(trproc, TA_RPROC_CMD_RELEASE_FW, &arg, param, 0);
>> +
>> + ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
>> + if (ret < 0 || arg.ret != 0) {
>> + dev_err(rproc_tee_ctx->dev,
>> + "TA_RPROC_CMD_RELEASE_FW invoke failed TEE err: %x, ret:%x\n",
>> + arg.ret, ret);
>
> At least @ret will be base 10, so don't print that in base 16 without
> indication that it's not base 10.
>
>
> Also, this will result in two dev_err() prints, printing out two
> different error codes.
Here i copy /past error managing from other drivers [1][2].
Should I make it different and use 2 dev_err?
[1]
https://elixir.bootlin.com/linux/v6.12.1/source/drivers/firmware/arm_scmi/transports/optee.c#L244
[2]
https://elixir.bootlin.com/linux/v6.12.1/source/drivers/firmware/arm_scmi/transports/optee.c#L244
>
>> + ret = -EIO;
>> + }
>> +
>> +out:
>> + if (ret)
>> + /* Unexpected state without solution to come back in a stable state */
>> + dev_err(rproc_tee_ctx->dev, "Failed to release TEE remoteproc firmware: %d\n", ret);
>> +}
>> +EXPORT_SYMBOL_GPL(rproc_tee_release_fw);
>> +
>
> kernel-doc.
>
>> +int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw)
>> +{
>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
>> + struct tee_ioctl_invoke_arg arg;
>> + struct tee_shm *fw_shm;
>> + int ret;
>> +
>> + if (!trproc)
>> + return -EINVAL;
>> +
>> + fw_shm = tee_shm_register_kernel_buf(rproc_tee_ctx->tee_ctx, (void *)fw->data, fw->size);
>> + if (IS_ERR(fw_shm))
>> + return PTR_ERR(fw_shm);
>> +
>> + rproc_tee_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(rproc_tee_ctx->tee_ctx, &arg, param);
>> + if (ret < 0 || arg.ret != 0) {
>> + dev_err(rproc_tee_ctx->dev,
>> + "TA_RPROC_FW_CMD_LOAD_FW invoke failed TEE err: %x, ret:%x\n",
>
> More confused bases
>
>> + arg.ret, ret);
>> + if (!ret)
>> + ret = -EIO;
>> + }
>> +
>> + tee_shm_free(fw_shm);
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(rproc_tee_load_fw);
>> +
>> +static int rproc_tee_get_loaded_rsc_table(struct rproc *rproc, phys_addr_t *rsc_pa,
>> + size_t *table_sz)
>> +{
>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
>> + struct tee_ioctl_invoke_arg arg;
>> + int ret;
>> +
>> + if (!trproc)
>> + return -EINVAL;
>> +
>> + rproc_tee_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(rproc_tee_ctx->tee_ctx, &arg, param);
>> + if (ret < 0 || arg.ret != 0) {
>> + dev_err(rproc_tee_ctx->dev,
>> + "TA_RPROC_FW_CMD_GET_RSC_TABLE invoke failed TEE err: %x, ret:%x\n",
>> + arg.ret, ret);
>> + return -EIO;
>> + }
>> +
>> + *table_sz = param[2].u.value.a;
>> +
>> + if (*table_sz)
>> + *rsc_pa = param[1].u.value.a;
>> + else
>> + *rsc_pa = 0;
>> +
>> + return 0;
>> +}
>> +
>> +int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw)
>> +{
>> + phys_addr_t rsc_table;
>> + void __iomem *rsc_va;
>> + size_t table_sz;
>> + int ret;
>> +
>> + if (!rproc)
>> + return -EINVAL;
>> +
>> + /* At this point, the firmware has to be loaded to be able to parse the resource table. */
>
> There's two ways to read this, either you're saying "we now need to load
> the firmware, so that we can parse the resource table", or "let's hope
> the firmware is loaded because I will parse it now!"
>
>> +
>> + ret = rproc_tee_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
>> + if (ret)
>> + goto release_fw;
>> +
>> + /*
>> + * We assume here that the memory mapping is the same between the TEE and Linux kernel
>> + * contexts. Else a new TEE remoteproc service could be needed to get a copy of the
>> + * resource table
>> + */
>> + rsc_va = ioremap_wc(rsc_table, table_sz);
>> + if (IS_ERR_OR_NULL(rsc_va)) {
>
> When does ioremap_wc() return IS_ERR()?
>
>> + dev_err(rproc_tee_ctx->dev, "Unable to map memory region: %pa+%zx\n",
>> + &rsc_table, table_sz);
>> + ret = -ENOMEM;
>> + goto release_fw;
>> + }
>> +
>> + /*
>> + * Create a copy of the resource table to have the same behavior as the ELF loader.
>> + * This cached table will be used by the remoteproc core after the remoteproc stops
>> + * to free resources and for crash recovery to reapply the settings.
>> + * The cached table will be freed by the remoteproc core.
>> + */
>> + rproc->cached_table = kmemdup((__force void *)rsc_va, table_sz, GFP_KERNEL);
>> + iounmap(rsc_va);
>> +
>> + if (!rproc->cached_table) {
>> + ret = -ENOMEM;
>> + goto release_fw;
>> + }
>> +
>> + rproc->table_ptr = rproc->cached_table;
>> + rproc->table_sz = table_sz;
>> +
>> + return 0;
>> +
>> +release_fw:
>> + rproc_tee_release_fw(rproc);
>
> This unrolls state that this function didn't establish. This will at
> best confuse the caller.
>
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(rproc_tee_parse_fw);
>> +
>> +struct resource_table *rproc_tee_find_loaded_rsc_table(struct rproc *rproc,
>> + const struct firmware *fw)
>> +{
>> + phys_addr_t rsc_table;
>> + size_t table_sz;
>> + int ret;
>> +
>> + ret = rproc_tee_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
>> + if (ret)
>> + return NULL;
>> +
>> + rproc->table_sz = table_sz;
>> + if (!table_sz)
>> + return NULL;
>> +
>> + /*
>> + * At this step the memory area that contains the resource table should have been registered
>> + * by the remote proc platform driver and allocated by rproc_alloc_registered_carveouts().
>> + */
>> + return (struct resource_table *)rproc_pa_to_va(rproc, rsc_table, table_sz, NULL);
>> +}
>> +EXPORT_SYMBOL_GPL(rproc_tee_find_loaded_rsc_table);
>> +
>> +int rproc_tee_start(struct rproc *rproc)
>> +{
>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
>> + struct tee_ioctl_invoke_arg arg;
>> + int ret = 0;
>> +
>> + if (!trproc)
>> + return -EINVAL;
>> +
>> + rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_START_FW, &arg, param, 0);
>> +
>> + ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
>> + if (ret < 0 || arg.ret != 0) {
>> + dev_err(rproc_tee_ctx->dev,
>> + "TA_RPROC_FW_CMD_START_FW invoke failed TEE err: %x, ret:%x\n",
>> + arg.ret, ret);
>> + if (!ret)
>> + return -EIO;
>> + }
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(rproc_tee_start);
>> +
>> +int rproc_tee_stop(struct rproc *rproc)
>> +{
>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
>> + struct tee_ioctl_invoke_arg arg;
>> + int ret;
>> +
>> + if (!trproc)
>> + return -EINVAL;
>> +
>> + rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_STOP_FW, &arg, param, 0);
>> +
>> + ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
>> + if (ret < 0 || arg.ret != 0) {
>> + dev_err(rproc_tee_ctx->dev,
>> + "TA_RPROC_FW_CMD_STOP_FW invoke failed TEE err: %x, ret:%x\n",
>> + arg.ret, ret);
>> + if (!ret)
>> + ret = -EIO;
>> + }
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(rproc_tee_stop);
>> +
>> +static const struct tee_client_device_id rproc_tee_id_table[] = {
>> + {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)},
>> + {}
>> +};
>> +
>> +int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id)
>> +{
>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>> + struct tee_ioctl_open_session_arg sess_arg;
>> + struct tee_client_device *tee_device;
>> + struct rproc_tee *trproc;
>> + int ret;
>> +
>> + /*
>> + * Test if the device has been probed by the TEE bus. In case of failure, we ignore the
>> + * reason. The bus could be not yet probed or the service not available in the secure
>> + * firmware.The assumption in such a case is that the TEE remoteproc is not probed.
>> + */
>> + if (!rproc_tee_ctx)
>> + return -EPROBE_DEFER;
>> +
>> + /* Prevent rproc tee module from being removed */
>> + if (!try_module_get(THIS_MODULE)) {
>
> We're doing this in the core because there's no direct dependency on the
> remoteproc driver which we will jump to through rproc_ops.
>
> In contrast, your remoteproc driver will be prevented from being
> unloaded by the core's refcount and this module can't be unloaded
> because your driver is referencing it directly.
>
>
> That said, none of this matter now that you made the tee driver bool.
>
> I.e. drop this.
>
>> + dev_err(rproc_tee_ctx->dev, "can't get owner\n");
>> + return -ENODEV;
>> + }
>> +
>> + trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
>> + if (!trproc) {
>> + ret = -ENOMEM;
>> + goto module_put;
>> + }
>> +
>> + tee_device = to_tee_client_device(rproc_tee_ctx->dev);
>> + memset(&sess_arg, 0, sizeof(sess_arg));
>> +
>> + memcpy(sess_arg.uuid, tee_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
>> +
>> + sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
>> + sess_arg.num_params = 1;
>> +
>> + param[0] = (struct tee_param) {
>> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
>> + .u.value.a = rproc_id,
>> + };
>> +
>> + ret = tee_client_open_session(rproc_tee_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);
>> + ret = -EINVAL;
>> + goto module_put;
>> + }
>> +
>> + trproc->parent = dev;
>> + trproc->rproc_id = rproc_id;
>> + trproc->session_id = sess_arg.session;
>> +
>> + trproc->rproc = rproc;
>> + rproc->rproc_tee_itf = trproc;
>> +
>> + list_add_tail(&trproc->node, &rproc_tee_ctx->sessions);
>> +
>> + return 0;
>> +
>> +module_put:
>> + module_put(THIS_MODULE);
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(rproc_tee_register);
>> +
>> +int rproc_tee_unregister(struct rproc *rproc)
>> +{
>> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
>> + int ret;
>> +
>> + if (!rproc->rproc_tee_itf)
>
> How can this happen?
>
>> + return -ENODEV;
>> +
>> + ret = tee_client_close_session(rproc_tee_ctx->tee_ctx, trproc->session_id);
>> + if (ret < 0)
>> + dev_err(trproc->parent, "tee_client_close_session failed, err: %x\n", ret);
>> +
>> + list_del(&trproc->node);
>> + rproc->rproc_tee_itf = NULL;
>> +
>> + module_put(THIS_MODULE);
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(rproc_tee_unregister);
>> +
>> +static int rproc_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 rproc_tee_probe(struct device *dev)
>> +{
>> + struct tee_context *tee_ctx;
>> + int ret;
>> +
>> + /* Open context with TEE driver */
>> + tee_ctx = tee_client_open_context(NULL, rproc_tee_ctx_match, NULL, NULL);
>> + if (IS_ERR(tee_ctx))
>> + return PTR_ERR(tee_ctx);
>> +
>> + rproc_tee_ctx = devm_kzalloc(dev, sizeof(*rproc_tee_ctx), GFP_KERNEL);
>> + if (!rproc_tee_ctx) {
>> + ret = -ENOMEM;
>> + goto err;
>> + }
>
> rproc_tee_register() checks if rproc_tee_ctx is non-NULL before
> continuing, which means that if you have a client calling that at the
> same time as you're here...you will have a NULL dereference - and/or
> other exciting issues.
>
>> +
>> + rproc_tee_ctx->dev = dev;
>> + rproc_tee_ctx->tee_ctx = tee_ctx;
>> + INIT_LIST_HEAD(&rproc_tee_ctx->sessions);
>> +
>> + return 0;
>> +err:
>> + tee_client_close_context(tee_ctx);
>
> devm_kzalloc() seems like a simpler function, and it's definitely easier
> to unroll than the tee context, so flip the open/alloc and your cleanup
> will be easier.
>
>> +
>> + return ret;
>> +}
>> +
>> +static int rproc_tee_remove(struct device *dev)
>> +{
>> + struct rproc_tee *entry, *tmp;
>> +
>> + list_for_each_entry_safe(entry, tmp, &rproc_tee_ctx->sessions, node) {
>> + tee_client_close_session(rproc_tee_ctx->tee_ctx, entry->session_id);
>> + list_del(&entry->node);
>> + kfree(entry);
>
> So each remoteproc driver have a rproc_tee context on
> &rproc_tee_ctx-->sessions and without telling them, you just destroy
> and free their context?
>
> Perhaps I'm misinterpreting what you're doing here, but did you test
> this?
>
>> + }
>> +
>> + tee_client_close_context(rproc_tee_ctx->tee_ctx);
>
> As you return here, the devres-allocated rproc_tee_ctx memory will be
> freed, and you have a seemingly valid looking pointer and whole bunch of
> use-after-free cases.
>
>> +
>> + return 0;
>> +}
>> +
>> +MODULE_DEVICE_TABLE(tee, rproc_tee_id_table);
>> +
>> +static struct tee_client_driver rproc_tee_fw_driver = {
>> + .id_table = rproc_tee_id_table,
>> + .driver = {
>> + .name = KBUILD_MODNAME,
>> + .bus = &tee_bus_type,
>> + .probe = rproc_tee_probe,
>> + .remove = rproc_tee_remove,
>> + },
>> +};
>> +
>> +static int __init rproc_tee_fw_mod_init(void)
>> +{
>> + return driver_register(&rproc_tee_fw_driver.driver);
>> +}
>> +
>> +static void __exit rproc_tee_fw_mod_exit(void)
>> +{
>> + driver_unregister(&rproc_tee_fw_driver.driver);
>> +}
>> +
>> +module_init(rproc_tee_fw_mod_init);
>> +module_exit(rproc_tee_fw_mod_exit);
>
> Please add an equivalent of the module_platform_driver() macro to tee
> framework instead of open-coding this.
>
>> +
>> +MODULE_DESCRIPTION(" remote processor TEE module");
>> +MODULE_LICENSE("GPL");
>> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
>> index 8fd0d7f63c8e..2e0ddcb2d792 100644
>> --- a/include/linux/remoteproc.h
>> +++ b/include/linux/remoteproc.h
>> @@ -503,6 +503,8 @@ enum rproc_features {
>> RPROC_MAX_FEATURES,
>> };
>>
>> +struct rproc_tee;
>> +
>> /**
>> * struct rproc - represents a physical remote processor device
>> * @node: list node of this rproc object
>> @@ -545,6 +547,7 @@ enum rproc_features {
>> * @cdev: character device of the rproc
>> * @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release
>> * @features: indicate remoteproc features
>> + * @rproc_tee_itf: pointer to the remoteproc tee context
>> */
>> struct rproc {
>> struct list_head node;
>> @@ -586,6 +589,7 @@ struct rproc {
>> struct cdev cdev;
>> bool cdev_put_on_release;
>> DECLARE_BITMAP(features, RPROC_MAX_FEATURES);
>> + struct rproc_tee *rproc_tee_itf;
>
> TEE is just one specific remoteproc implementation, why does it need to
> infest the core data structure? Do you want a stm32_rproc here as well?
Right, this variable is resulting from discussions on some previous revisions.
As we removed the dependency between the remoteproc core and remoteproc TEE, I
should be ableto remove it in the next revision.
>
>> };
>>
>> /**
>> diff --git a/include/linux/remoteproc_tee.h b/include/linux/remoteproc_tee.h
>> new file mode 100644
>> index 000000000000..9b498a8eff4d
>> --- /dev/null
>> +++ b/include/linux/remoteproc_tee.h
>> @@ -0,0 +1,105 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + * Copyright(c) 2024 STMicroelectronics
>> + */
>> +
>> +#ifndef REMOTEPROC_TEE_H
>> +#define REMOTEPROC_TEE_H
>> +
>> +#include <linux/tee_drv.h>
>> +#include <linux/firmware.h>
>> +#include <linux/remoteproc.h>
>> +
>> +struct rproc;
>> +
>> +/**
>> + * struct rproc_tee - TEE remoteproc structure
>> + * @node: Reference in list
>> + * @rproc: Remoteproc reference
>> + * @parent: Parent device
>
> Isn't that rproc->dev->parent?
>
>> + * @rproc_id: Identifier of the target firmware
>> + * @session_id: TEE session identifier
>> + */
>> +struct rproc_tee {
>
> As far as I can tell this isn't dereferenced outside remoteproc_tee.c,
> can we hide it therein?
The only reference is for rproc_tee_itf, so yes can become internal
>
>> + struct list_head node;
>> + struct rproc *rproc;
>> + struct device *parent;
>> + u32 rproc_id;
>> + u32 session_id;
>> +};
>> +
>> +#if IS_REACHABLE(CONFIG_REMOTEPROC_TEE)
>> +
>> +int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id);
>> +int rproc_tee_unregister(struct rproc *rproc);
>> +int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw);
>> +int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw);
>> +void rproc_tee_release_fw(struct rproc *rproc);
>> +struct resource_table *rproc_tee_find_loaded_rsc_table(struct rproc *rproc,
>> + const struct firmware *fw);
>> +int rproc_tee_start(struct rproc *rproc);
>> +int rproc_tee_stop(struct rproc *rproc);
>> +
>> +#else
>> +
>
> Do we really need yet another bunch of stubs? Can't we just leave
> CONFIG_REMOTEPROC_TEE non-user-selectable and have the drivers that rely
> on it do "select REMOTEPROC_TEE"?
>
> If my measurements are correct, it's 3.1kB of code...
REMOTEPROC_TEE config depends on OPTEE that can be not enabled
( we have some customer that not use OPTEE)
So for build we need them
Thanks for your review,
Arnaud
>
> Regards,
> Bjorn
>
>> +static inline int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id)
>> +{
>> + return -ENODEV;
>> +}
>> +
>> +static inline int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline int rproc_tee_unregister(struct rproc *rproc)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline int rproc_tee_start(struct rproc *rproc)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline int rproc_tee_stop(struct rproc *rproc)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline void rproc_tee_release_fw(struct rproc *rproc)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +}
>> +
>> +static inline struct resource_table *
>> +rproc_tee_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return NULL;
>> +}
>> +#endif /* CONFIG_REMOTEPROC_TEE */
>> +#endif /* REMOTEPROC_TEE_H */
>> --
>> 2.25.1
>>
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation
2024-12-09 23:14 ` Bjorn Andersson
@ 2024-12-10 10:33 ` Arnaud POULIQUEN
2025-02-12 3:54 ` Bjorn Andersson
0 siblings, 1 reply; 32+ messages in thread
From: Arnaud POULIQUEN @ 2024-12-10 10:33 UTC (permalink / raw)
To: Bjorn Andersson; +Cc: Mathieu Poirier, linux-remoteproc, linux-kernel
On 12/10/24 00:14, Bjorn Andersson wrote:
> On Thu, Nov 28, 2024 at 09:42:10AM GMT, Arnaud Pouliquen wrote:
>> This patch updates the rproc_ops structures to include two new optional
>> operations.
>>
>> - The load_fw() op is responsible for loading the remote processor
>> non-ELF firmware image before starting the boot sequence. This ops will
>> be used, for instance, to call OP-TEE to authenticate an load the firmware
>> image before accessing to its resources (a.e the resource table)
>>
>> - The release_fw op is responsible for releasing the remote processor
>> firmware image. For instance to clean memories.
>> The ops is called in the following cases:
>> - An error occurs between the loading of the firmware image and the
>> start of the remote processor.
>> - after stopping the remote processor.
>>
>
> Why does this difference need to be encoded in rproc_ops? I think we
> should strive for having a single, simple high level flow of operations
> through the remoteproc core for which the specifics of each remoteproc
> instance can be encoded in that driver.
>
>
> Perhaps there's a good reason for this, but if so please read and follow
> https://docs.kernel.org/process/submitting-patches.html#describe-your-changes
> to make that reasoning clear in the commit message.
>
The actual sequence to load a remoteproc firmware is
- get firmware from file system and store the firmware image in Linux kernel memory
- get resource table from the firmware image and make a copy(
- parse the resource table and handle the resources
- load the firmware
- start the firmware
In OP-TEE we support not only one ELF image but n images (for instance a TF-M +
a zephyr), the segments can be encrypted the OP-TEE load sequence is
- copy header and meta data of the signed image in a secure memory
- verify it
- copy segments in remote processor memory and authenticate segments in place.
- optionally decrypt the segments
Only at this step the resource table as been authenticated (and decrypted)
So the point is that we need to load the firmware before getting the resource table
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>> ---
>> Update vs version V13:
>> - Rework the commit to introduce load_fw() op.
>> - remove rproc_release_fw() call from rproc_start() as called in
>> rproc_boot() and rproc_boot_recovery() in case of error.
>> - create rproc_load_fw() and rproc_release_fw() internal functions.
>> ---
>> drivers/remoteproc/remoteproc_core.c | 16 +++++++++++++++-
>> drivers/remoteproc/remoteproc_internal.h | 14 ++++++++++++++
>> include/linux/remoteproc.h | 6 ++++++
>> 3 files changed, 35 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
>> index ace11ea17097..8df4b2c59bb6 100644
>> --- a/drivers/remoteproc/remoteproc_core.c
>> +++ b/drivers/remoteproc/remoteproc_core.c
>> @@ -1488,6 +1488,7 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
>> kfree(rproc->cached_table);
>> rproc->cached_table = NULL;
>> rproc->table_ptr = NULL;
>> + rproc_release_fw(rproc);
>> unprepare_rproc:
>> /* release HW resources if needed */
>> rproc_unprepare_device(rproc);
>> @@ -1855,8 +1856,14 @@ static int rproc_boot_recovery(struct rproc *rproc)
>> return ret;
>> }
>>
>> + ret = rproc_load_fw(rproc, firmware_p);
>
> It is not clear to me why in the case of OP-TEE we need to invoke the
> "load operation" here, and in the case of "legacy" ELF loading we do it
> first thing in rproc_start() (i.e. on the very next line of code being
> executed).
For the OP-TEE, please refer to my comment above.
The only reason I can see for the legacy ELF is that the resource table could
contain information to be able to configure some resources to load the firmware.
In case of OP-TEE this would be managed in OP-TEE.
>
>
> Should we start by renaming rproc_load_segments() rproc_load() and move
> it out of rproc_start()? (I.e. here?)
>
> Perhaps define that rproc_load() is responsible for "loading firmware"
> (whatever that means) and establishing rproc->cached_table, and
> rproc->table_ptr?
>
> (Note that this seems like a good cleanup of the spaghetti regardless)
>
It's something that crossed my mind, but I don't know the legacy well enough to
guarantee that it will work in all drivers.
If you want to go in this direction, perhaps this is something that could be
addressed in a dedicated pull request? In this case, the ops could become
load_fw and load_fw_new, similar to how it is done for platform_driver::remove.
>> + if (ret)
>> + return ret;
>> +
>> /* boot the remote processor up again */
>> ret = rproc_start(rproc, firmware_p);
>> + if (ret)
>> + rproc_release_fw(rproc);
>
> The fact that you rproc_release_fw() in the error path here, right
> before we unconditionally release_firmware() the actual firmware means
> that you have 2 different life cycles with very very similar names.
>
> This will contain bugs, sooner or later.
So we need to find a better way for the ops if we continue in this direction.
What about introducing rproc_load_new and rproc_release?
>
>>
>> release_firmware(firmware_p);
>>
>> @@ -1997,7 +2004,13 @@ int rproc_boot(struct rproc *rproc)
>> goto downref_rproc;
>> }
>>
>> + ret = rproc_load_fw(rproc, firmware_p);
>> + if (ret)
>> + goto downref_rproc;
>> +
>> ret = rproc_fw_boot(rproc, firmware_p);
>> + if (ret)
>> + rproc_release_fw(rproc);
>>
>> release_firmware(firmware_p);
>> }
>> @@ -2071,6 +2084,7 @@ int rproc_shutdown(struct rproc *rproc)
>> kfree(rproc->cached_table);
>> rproc->cached_table = NULL;
>> rproc->table_ptr = NULL;
>> + rproc_release_fw(rproc);
>> out:
>> mutex_unlock(&rproc->lock);
>> return ret;
>> @@ -2471,7 +2485,7 @@ static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
>> if (!rproc->ops->coredump)
>> rproc->ops->coredump = rproc_coredump;
>>
>> - if (rproc->ops->load)
>> + if (rproc->ops->load || rproc->ops->load_fw)
>> return 0;
>>
>> /* Default to ELF loader if no load function is specified */
>> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
>> index 0cd09e67ac14..2104ca449178 100644
>> --- a/drivers/remoteproc/remoteproc_internal.h
>> +++ b/drivers/remoteproc/remoteproc_internal.h
>> @@ -221,4 +221,18 @@ bool rproc_u64_fit_in_size_t(u64 val)
>> return (val <= (size_t) -1);
>> }
>>
>> +static inline void rproc_release_fw(struct rproc *rproc)
>> +{
>> + if (rproc->ops->release_fw)
>> + rproc->ops->release_fw(rproc);
>> +}
>> +
>> +static inline int rproc_load_fw(struct rproc *rproc, const struct firmware *fw)
>> +{
>> + if (rproc->ops->load_fw)
>> + return rproc->ops->load_fw(rproc, fw);
>> +
>> + return 0;
>> +}
>> +
>> #endif /* REMOTEPROC_INTERNAL_H */
>> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
>> index 2e0ddcb2d792..ba6fd560f7ba 100644
>> --- a/include/linux/remoteproc.h
>> +++ b/include/linux/remoteproc.h
>> @@ -381,6 +381,10 @@ enum rsc_handling_status {
>> * @panic: optional callback to react to system panic, core will delay
>> * panic at least the returned number of milliseconds
>> * @coredump: collect firmware dump after the subsystem is shutdown
>> + * @load_fw: optional function to load non-ELF firmware image to memory, where the remote
>> + * processor expects to find it.
>
> Why does it matter if it's an ELF or not?
No matter. It was more to differentiate from the legacy one, but it does not
make sense and adds to the argument that the ops naming is not accurate.
>
> In the Qualcomm case, firmware comes in ELF format, Linux loads the
> LOAD segments and the trusted world then authenticates the content and
> start the remote processor.
>
>
> I think the difference in your case is that you have memory reserved
> elsewhere, and you want the "load" operation to pass the firmware to the
> TEE - which means that you need rproc_release_fw() to eventually clean
> up the state if rproc_start() fails - and upon shutdown.
Yes the OP-TEE is make more stuff:
- authenticate several firmware images
- decrypt images if encrypted
- ensure that the load is done in granted memories
- manage the memory access rights to enure that the code and data memory
is never accessible by the Linux.
>
> If we improve the definition of rproc_load_segments() to mean
> "remoteproc (or remoteproc driver) is loading segments", then in your
> case there's no "loading" operation in Linux. Instead you make that a
> nop and invoke LOAD_FW and START_FW within your start callback, then you
> can clean up the remnant state within your driver's start and stop
> callbacks - without complicating the core framework.
This would not work as I need to load the firmware before calling
rproc_handle_resources().
I can not use rproc_prepare_device() as it is not called on recovery
Thanks,
Arnaud
>
> Regards,
> Bjorn
>
>> + * @release_fw: optional function to release the firmware image from memories.
>> + * This function is called after stopping the remote processor or in case of error
>> */
>> struct rproc_ops {
>> int (*prepare)(struct rproc *rproc);
>> @@ -403,6 +407,8 @@ struct rproc_ops {
>> u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
>> unsigned long (*panic)(struct rproc *rproc);
>> void (*coredump)(struct rproc *rproc);
>> + int (*load_fw)(struct rproc *rproc, const struct firmware *fw);
>> + void (*release_fw)(struct rproc *rproc);
>> };
>>
>> /**
>> --
>> 2.25.1
>>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 2/8] remoteproc: Add TEE support
2024-12-10 8:57 ` Arnaud POULIQUEN
@ 2025-01-10 8:51 ` Arnaud POULIQUEN
2025-02-12 3:18 ` Bjorn Andersson
1 sibling, 0 replies; 32+ messages in thread
From: Arnaud POULIQUEN @ 2025-01-10 8:51 UTC (permalink / raw)
To: Bjorn Andersson; +Cc: Mathieu Poirier, linux-kernel, linux-remoteproc
Hello Bjorn,
Gentle reminder.
I would need that we clarify pending points with this commit and [3/8] to move
in the right direction for the next revision.
Thanks in advance,
Arnaud
On 12/10/24 09:57, Arnaud POULIQUEN wrote:
> Hello Bjorn,
>
> On 12/6/24 23:07, Bjorn Andersson wrote:
>> On Thu, Nov 28, 2024 at 09:42:09AM GMT, Arnaud Pouliquen wrote:
>>> Add a remoteproc TEE (Trusted Execution Environment) driver
>>> that will be probed by the TEE bus. If the associated Trusted
>>> application is supported on secure part this driver offers a client
>>> interface to load a firmware by the secure part.
>>
>> If...else?
>>
>>> This firmware could be authenticated by the secure trusted application.
>>>
>>
>> I would like for this to fully describe how this fits with the bus and
> Are you speaking about the OP-TEE bus?
>
> I assume that your attempt is that I provide more details on the live cycle
> sequence, right?
>
>> how it is expected to be used by a specific remoteproc driver.
>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>>> ---
>>> Updates vs version v13:
>>> - define REMOTEPROC_TEE as bool instead of tristate,
>>> - remove the load of the firmware in rproc_tee_parse_fw as we will ensure
>>> that the firmware is loaded using the load_fw() operation.
>>> ---
>>> drivers/remoteproc/Kconfig | 10 +
>>> drivers/remoteproc/Makefile | 1 +
>>> drivers/remoteproc/remoteproc_tee.c | 508 ++++++++++++++++++++++++++++
>>> include/linux/remoteproc.h | 4 +
>>> include/linux/remoteproc_tee.h | 105 ++++++
>>> 5 files changed, 628 insertions(+)
>>> create mode 100644 drivers/remoteproc/remoteproc_tee.c
>>> create mode 100644 include/linux/remoteproc_tee.h
>>>
>>> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
>>> index 955e4e38477e..f6335321d540 100644
>>> --- a/drivers/remoteproc/Kconfig
>>> +++ b/drivers/remoteproc/Kconfig
>>> @@ -23,6 +23,16 @@ config REMOTEPROC_CDEV
>>>
>>> It's safe to say N if you don't want to use this interface.
>>>
>>> +config REMOTEPROC_TEE
>>> + bool "Remoteproc support by a TEE application"
>>> + depends on OPTEE
>>> + help
>>> + Support a remote processor with a TEE application.
>>
>> Does the remote processor run TEE applications? (Rethorical question...)
>>
>>> The Trusted
>>> + Execution Context is responsible for loading the trusted firmware
>>> + image and managing the remote processor's lifecycle.
>>> +
>>> + It's safe to say N if you don't want to use remoteproc TEE.
>>
>> It's not really about "wanting to use", it's a question whether your
>> device implements/provides the remoteproc TEE.
>>
>>> +
>>> config IMX_REMOTEPROC
>>> tristate "i.MX remoteproc support"
>>> depends on ARCH_MXC
>>> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
>>> index 5ff4e2fee4ab..f77e0abe8349 100644
>>> --- a/drivers/remoteproc/Makefile
>>> +++ b/drivers/remoteproc/Makefile
>>> @@ -11,6 +11,7 @@ remoteproc-y += remoteproc_sysfs.o
>>> remoteproc-y += remoteproc_virtio.o
>>> remoteproc-y += remoteproc_elf_loader.o
>>> obj-$(CONFIG_REMOTEPROC_CDEV) += remoteproc_cdev.o
>>> +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o
>>> obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o
>>> obj-$(CONFIG_IMX_DSP_REMOTEPROC) += imx_dsp_rproc.o
>>> obj-$(CONFIG_INGENIC_VPU_RPROC) += ingenic_rproc.o
>>> diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c
>>> new file mode 100644
>>> index 000000000000..3fe3f31068f2
>>> --- /dev/null
>>> +++ b/drivers/remoteproc/remoteproc_tee.c
>>> @@ -0,0 +1,508 @@
>>> +// SPDX-License-Identifier: GPL-2.0-or-later
>>> +/*
>>> + * Copyright (C) STMicroelectronics 2024
>>> + * Author: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>>> + */
>>> +
>>> +#include <linux/firmware.h>
>>> +#include <linux/io.h>
>>> +#include <linux/module.h>
>>> +#include <linux/remoteproc.h>
>>> +#include <linux/remoteproc_tee.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/tee_drv.h>
>>> +
>>> +#define MAX_TEE_PARAM_ARRAY_MEMBER 4
>>> +
>>> +/*
>>> + * Authentication of the firmware and load in the remote processor memory
>>
>> Exactly what does this imply? Will the content of @memref be copied into
>> some other memory?
>
> The objective is to authenticate and load in one step. So, yes, the image is
> loaded into the remoteproc destination memory.
>
> On stm32mp1 we can not store the elf file in a temporary secure memory as
> the memory is encrypted by software (this would take to much time).
>
> For your information, in OP-TEE, the application code is split into a generic
> part and a platform adaptation layer. The generic application is mainly
> responsible for:
>
> - Copying the binary header and metadata into secure memory and authenticating them.
> - Parsing the ELF images and providing segments to load with associated
> authenticated hashes to the platform application.
> In the future, someone can add their own format if needed.
>
> But the generic part could be enhance to authenticate and load a non ELF binary.
> So I'm trying to be generic as possible here.
>
>
>>
>>> + *
>>> + * [in] params[0].value.a: unique 32bit identifier of the remote processor
>>
>> Why not just "remote processor identifier"?
>>
>>> + * [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
>>
>> Why is there two "FW" in this constant? Why isn't it just
>> "TA_RPROC_FW_CMD_START"?
>>
>> And why is it not TEE_PROC_FW_CMD_START?
>>
>>> +
>>> +/*
>>> + * 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.
>>
>> These three lines describe a scenario that doesn't make any sense to me.
>> But if that's the case, you should be able to describe that the API
>> might give you a inaccessible pointer, by design.
>
> On STM32MP, we have a kind of firewall in OP-TEE that sets memory access rights
> from the device tree. So if the firmware image is not linked according to the
> firewall configuration, the pointer may not be accessible.
>
> I will update the comment as you propose.
>
>>
>>> + *
>>> + * [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
>>
>> What does this mean? What will I find at @memref after this call?
>
> I do not have a simple answer here as it depends on the OP-TEE strategy.
> It could be an obscure core dump with possible encryption.
>
> I will remove this as it is not yet implemented in OP-TEE.
>
> https://elixir.bootlin.com/op-tee/4.4.0/source/ta/remoteproc/src/remoteproc_core.c#L1131
>
>>
>>> + *
>>> + * [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
>>
>> s/else return Null/or NULL/
>>
>>> + */
>>> +#define TA_RPROC_FW_CMD_GET_COREDUMP 5
>>> +
>>> +/*
>>> + * Release remote processor firmware images and associated resources.
>>
>> Exactly what does this mean for the caller?
>
> It is platform dependent. It can consist in cleaning the memory, but
> can be also something else such as firewall configuration.
> On stm323mp we clean all the memories region reserved for the remote processor.
>
>>
>>> + * This command should be used in case an error occurs between the loading of
>>> + * the firmware images (TA_RPROC_CMD_LOAD_FW) and the starting of the remote
>>> + * processor (TA_RPROC_CMD_START_FW) or after stopping the remote processor
>>> + * to release associated resources (TA_RPROC_CMD_STOP_FW).
>>
>> This description belongs adjacent to LOAD_FW, and describe it in terms
>> of what state LOAD_FW leaves the buffers and remote processor in.
>
> Sorry, it is not clear to me what you are expecting here.
>
>>
>>> + *
>>> + * [in] params[0].value.a: Unique 32-bit remote processor identifier
>>> + */
>>> +#define TA_RPROC_CMD_RELEASE_FW 6
>>> +
>>> +struct rproc_tee_context {
>>> + struct list_head sessions;
>>> + struct tee_context *tee_ctx;
>>> + struct device *dev;
>>> +};
>>> +
>>> +static struct rproc_tee_context *rproc_tee_ctx;
>>> +
>>> +static void rproc_tee_prepare_args(struct rproc_tee *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_ARRAY_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,
>>> + };
>>> +}
>>> +
>>
>> Provide kernel-doc for EXPORT_SYMBOL*() functions.
>
> Should it be in the remoteproc kernel doc or in a new doc file that
> provide an overview of the remoteproc_tee usage?
>
>
>>
>>> +void rproc_tee_release_fw(struct rproc *rproc)
>>> +{
>>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>>> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
>>> + struct tee_ioctl_invoke_arg arg;
>>> + int ret;
>>> +
>>> + if (!rproc) {
>>
>> How can this happen?
>>
>> This error will happen in two cases:
>>
>> 1) on your desk while you develop the client and you have to hunt
>> through the kernel log to figure out that the reason you can't start
>> your remoteproc is because 5 minutes ago there was a error log saying
>> that we didn't stop it last time.
>>
>> 2) in the customer device because of some obscure bug, where no one will
>> read the logs and the software will happily continue to execute with a
>> broken state.
>>
>>> + ret = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + /*
>>> + * If the remote processor state is RPROC_DETACHED, just ignore the
>>> + * request, as the remote processor is still running.
>>> + */
>>> + if (rproc->state == RPROC_DETACHED)
>>> + return;
>>> +
>>> + if (rproc->state != RPROC_OFFLINE) {
>>> + ret = -EBUSY;
>>
>> The function is void... Defensive coding is only useful when it saves
>> you from future mistakes, not when it hides problems from you.
>>
>>> + goto out;
>>> + }
>>> +
>>> + rproc_tee_prepare_args(trproc, TA_RPROC_CMD_RELEASE_FW, &arg, param, 0);
>>> +
>>> + ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
>>> + if (ret < 0 || arg.ret != 0) {
>>> + dev_err(rproc_tee_ctx->dev,
>>> + "TA_RPROC_CMD_RELEASE_FW invoke failed TEE err: %x, ret:%x\n",
>>> + arg.ret, ret);
>>
>> At least @ret will be base 10, so don't print that in base 16 without
>> indication that it's not base 10.
>>
>>
>> Also, this will result in two dev_err() prints, printing out two
>> different error codes.
>
> Here i copy /past error managing from other drivers [1][2].
> Should I make it different and use 2 dev_err?
>
>
> [1]
> https://elixir.bootlin.com/linux/v6.12.1/source/drivers/firmware/arm_scmi/transports/optee.c#L244
>
> [2]
> https://elixir.bootlin.com/linux/v6.12.1/source/drivers/firmware/arm_scmi/transports/optee.c#L244
>
>>
>>> + ret = -EIO;
>>> + }
>>> +
>>> +out:
>>> + if (ret)
>>> + /* Unexpected state without solution to come back in a stable state */
>>> + dev_err(rproc_tee_ctx->dev, "Failed to release TEE remoteproc firmware: %d\n", ret);
>>> +}
>>> +EXPORT_SYMBOL_GPL(rproc_tee_release_fw);
>>> +
>>
>> kernel-doc.
>>
>>> +int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw)
>>> +{
>>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>>> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
>>> + struct tee_ioctl_invoke_arg arg;
>>> + struct tee_shm *fw_shm;
>>> + int ret;
>>> +
>>> + if (!trproc)
>>> + return -EINVAL;
>>> +
>>> + fw_shm = tee_shm_register_kernel_buf(rproc_tee_ctx->tee_ctx, (void *)fw->data, fw->size);
>>> + if (IS_ERR(fw_shm))
>>> + return PTR_ERR(fw_shm);
>>> +
>>> + rproc_tee_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(rproc_tee_ctx->tee_ctx, &arg, param);
>>> + if (ret < 0 || arg.ret != 0) {
>>> + dev_err(rproc_tee_ctx->dev,
>>> + "TA_RPROC_FW_CMD_LOAD_FW invoke failed TEE err: %x, ret:%x\n",
>>
>> More confused bases
>>
>>> + arg.ret, ret);
>>> + if (!ret)
>>> + ret = -EIO;
>>> + }
>>> +
>>> + tee_shm_free(fw_shm);
>>> +
>>> + return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(rproc_tee_load_fw);
>>> +
>>> +static int rproc_tee_get_loaded_rsc_table(struct rproc *rproc, phys_addr_t *rsc_pa,
>>> + size_t *table_sz)
>>> +{
>>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>>> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
>>> + struct tee_ioctl_invoke_arg arg;
>>> + int ret;
>>> +
>>> + if (!trproc)
>>> + return -EINVAL;
>>> +
>>> + rproc_tee_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(rproc_tee_ctx->tee_ctx, &arg, param);
>>> + if (ret < 0 || arg.ret != 0) {
>>> + dev_err(rproc_tee_ctx->dev,
>>> + "TA_RPROC_FW_CMD_GET_RSC_TABLE invoke failed TEE err: %x, ret:%x\n",
>>> + arg.ret, ret);
>>> + return -EIO;
>>> + }
>>> +
>>> + *table_sz = param[2].u.value.a;
>>> +
>>> + if (*table_sz)
>>> + *rsc_pa = param[1].u.value.a;
>>> + else
>>> + *rsc_pa = 0;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw)
>>> +{
>>> + phys_addr_t rsc_table;
>>> + void __iomem *rsc_va;
>>> + size_t table_sz;
>>> + int ret;
>>> +
>>> + if (!rproc)
>>> + return -EINVAL;
>>> +
>>> + /* At this point, the firmware has to be loaded to be able to parse the resource table. */
>>
>> There's two ways to read this, either you're saying "we now need to load
>> the firmware, so that we can parse the resource table", or "let's hope
>> the firmware is loaded because I will parse it now!"
>>
>>> +
>>> + ret = rproc_tee_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
>>> + if (ret)
>>> + goto release_fw;
>>> +
>>> + /*
>>> + * We assume here that the memory mapping is the same between the TEE and Linux kernel
>>> + * contexts. Else a new TEE remoteproc service could be needed to get a copy of the
>>> + * resource table
>>> + */
>>> + rsc_va = ioremap_wc(rsc_table, table_sz);
>>> + if (IS_ERR_OR_NULL(rsc_va)) {
>>
>> When does ioremap_wc() return IS_ERR()?
>>
>>> + dev_err(rproc_tee_ctx->dev, "Unable to map memory region: %pa+%zx\n",
>>> + &rsc_table, table_sz);
>>> + ret = -ENOMEM;
>>> + goto release_fw;
>>> + }
>>> +
>>> + /*
>>> + * Create a copy of the resource table to have the same behavior as the ELF loader.
>>> + * This cached table will be used by the remoteproc core after the remoteproc stops
>>> + * to free resources and for crash recovery to reapply the settings.
>>> + * The cached table will be freed by the remoteproc core.
>>> + */
>>> + rproc->cached_table = kmemdup((__force void *)rsc_va, table_sz, GFP_KERNEL);
>>> + iounmap(rsc_va);
>>> +
>>> + if (!rproc->cached_table) {
>>> + ret = -ENOMEM;
>>> + goto release_fw;
>>> + }
>>> +
>>> + rproc->table_ptr = rproc->cached_table;
>>> + rproc->table_sz = table_sz;
>>> +
>>> + return 0;
>>> +
>>> +release_fw:
>>> + rproc_tee_release_fw(rproc);
>>
>> This unrolls state that this function didn't establish. This will at
>> best confuse the caller.
>>
>>> + return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(rproc_tee_parse_fw);
>>> +
>>> +struct resource_table *rproc_tee_find_loaded_rsc_table(struct rproc *rproc,
>>> + const struct firmware *fw)
>>> +{
>>> + phys_addr_t rsc_table;
>>> + size_t table_sz;
>>> + int ret;
>>> +
>>> + ret = rproc_tee_get_loaded_rsc_table(rproc, &rsc_table, &table_sz);
>>> + if (ret)
>>> + return NULL;
>>> +
>>> + rproc->table_sz = table_sz;
>>> + if (!table_sz)
>>> + return NULL;
>>> +
>>> + /*
>>> + * At this step the memory area that contains the resource table should have been registered
>>> + * by the remote proc platform driver and allocated by rproc_alloc_registered_carveouts().
>>> + */
>>> + return (struct resource_table *)rproc_pa_to_va(rproc, rsc_table, table_sz, NULL);
>>> +}
>>> +EXPORT_SYMBOL_GPL(rproc_tee_find_loaded_rsc_table);
>>> +
>>> +int rproc_tee_start(struct rproc *rproc)
>>> +{
>>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>>> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
>>> + struct tee_ioctl_invoke_arg arg;
>>> + int ret = 0;
>>> +
>>> + if (!trproc)
>>> + return -EINVAL;
>>> +
>>> + rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_START_FW, &arg, param, 0);
>>> +
>>> + ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
>>> + if (ret < 0 || arg.ret != 0) {
>>> + dev_err(rproc_tee_ctx->dev,
>>> + "TA_RPROC_FW_CMD_START_FW invoke failed TEE err: %x, ret:%x\n",
>>> + arg.ret, ret);
>>> + if (!ret)
>>> + return -EIO;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(rproc_tee_start);
>>> +
>>> +int rproc_tee_stop(struct rproc *rproc)
>>> +{
>>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>>> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
>>> + struct tee_ioctl_invoke_arg arg;
>>> + int ret;
>>> +
>>> + if (!trproc)
>>> + return -EINVAL;
>>> +
>>> + rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_STOP_FW, &arg, param, 0);
>>> +
>>> + ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
>>> + if (ret < 0 || arg.ret != 0) {
>>> + dev_err(rproc_tee_ctx->dev,
>>> + "TA_RPROC_FW_CMD_STOP_FW invoke failed TEE err: %x, ret:%x\n",
>>> + arg.ret, ret);
>>> + if (!ret)
>>> + ret = -EIO;
>>> + }
>>> +
>>> + return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(rproc_tee_stop);
>>> +
>>> +static const struct tee_client_device_id rproc_tee_id_table[] = {
>>> + {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)},
>>> + {}
>>> +};
>>> +
>>> +int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id)
>>> +{
>>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>>> + struct tee_ioctl_open_session_arg sess_arg;
>>> + struct tee_client_device *tee_device;
>>> + struct rproc_tee *trproc;
>>> + int ret;
>>> +
>>> + /*
>>> + * Test if the device has been probed by the TEE bus. In case of failure, we ignore the
>>> + * reason. The bus could be not yet probed or the service not available in the secure
>>> + * firmware.The assumption in such a case is that the TEE remoteproc is not probed.
>>> + */
>>> + if (!rproc_tee_ctx)
>>> + return -EPROBE_DEFER;
>>> +
>>> + /* Prevent rproc tee module from being removed */
>>> + if (!try_module_get(THIS_MODULE)) {
>>
>> We're doing this in the core because there's no direct dependency on the
>> remoteproc driver which we will jump to through rproc_ops.
>>
>> In contrast, your remoteproc driver will be prevented from being
>> unloaded by the core's refcount and this module can't be unloaded
>> because your driver is referencing it directly.
>>
>>
>> That said, none of this matter now that you made the tee driver bool.
>>
>> I.e. drop this.
>>
>>> + dev_err(rproc_tee_ctx->dev, "can't get owner\n");
>>> + return -ENODEV;
>>> + }
>>> +
>>> + trproc = devm_kzalloc(dev, sizeof(*trproc), GFP_KERNEL);
>>> + if (!trproc) {
>>> + ret = -ENOMEM;
>>> + goto module_put;
>>> + }
>>> +
>>> + tee_device = to_tee_client_device(rproc_tee_ctx->dev);
>>> + memset(&sess_arg, 0, sizeof(sess_arg));
>>> +
>>> + memcpy(sess_arg.uuid, tee_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
>>> +
>>> + sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
>>> + sess_arg.num_params = 1;
>>> +
>>> + param[0] = (struct tee_param) {
>>> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
>>> + .u.value.a = rproc_id,
>>> + };
>>> +
>>> + ret = tee_client_open_session(rproc_tee_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);
>>> + ret = -EINVAL;
>>> + goto module_put;
>>> + }
>>> +
>>> + trproc->parent = dev;
>>> + trproc->rproc_id = rproc_id;
>>> + trproc->session_id = sess_arg.session;
>>> +
>>> + trproc->rproc = rproc;
>>> + rproc->rproc_tee_itf = trproc;
>>> +
>>> + list_add_tail(&trproc->node, &rproc_tee_ctx->sessions);
>>> +
>>> + return 0;
>>> +
>>> +module_put:
>>> + module_put(THIS_MODULE);
>>> + return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(rproc_tee_register);
>>> +
>>> +int rproc_tee_unregister(struct rproc *rproc)
>>> +{
>>> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
>>> + int ret;
>>> +
>>> + if (!rproc->rproc_tee_itf)
>>
>> How can this happen?
>>
>>> + return -ENODEV;
>>> +
>>> + ret = tee_client_close_session(rproc_tee_ctx->tee_ctx, trproc->session_id);
>>> + if (ret < 0)
>>> + dev_err(trproc->parent, "tee_client_close_session failed, err: %x\n", ret);
>>> +
>>> + list_del(&trproc->node);
>>> + rproc->rproc_tee_itf = NULL;
>>> +
>>> + module_put(THIS_MODULE);
>>> +
>>> + return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(rproc_tee_unregister);
>>> +
>>> +static int rproc_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 rproc_tee_probe(struct device *dev)
>>> +{
>>> + struct tee_context *tee_ctx;
>>> + int ret;
>>> +
>>> + /* Open context with TEE driver */
>>> + tee_ctx = tee_client_open_context(NULL, rproc_tee_ctx_match, NULL, NULL);
>>> + if (IS_ERR(tee_ctx))
>>> + return PTR_ERR(tee_ctx);
>>> +
>>> + rproc_tee_ctx = devm_kzalloc(dev, sizeof(*rproc_tee_ctx), GFP_KERNEL);
>>> + if (!rproc_tee_ctx) {
>>> + ret = -ENOMEM;
>>> + goto err;
>>> + }
>>
>> rproc_tee_register() checks if rproc_tee_ctx is non-NULL before
>> continuing, which means that if you have a client calling that at the
>> same time as you're here...you will have a NULL dereference - and/or
>> other exciting issues.
>>
>>> +
>>> + rproc_tee_ctx->dev = dev;
>>> + rproc_tee_ctx->tee_ctx = tee_ctx;
>>> + INIT_LIST_HEAD(&rproc_tee_ctx->sessions);
>>> +
>>> + return 0;
>>> +err:
>>> + tee_client_close_context(tee_ctx);
>>
>> devm_kzalloc() seems like a simpler function, and it's definitely easier
>> to unroll than the tee context, so flip the open/alloc and your cleanup
>> will be easier.
>>
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int rproc_tee_remove(struct device *dev)
>>> +{
>>> + struct rproc_tee *entry, *tmp;
>>> +
>>> + list_for_each_entry_safe(entry, tmp, &rproc_tee_ctx->sessions, node) {
>>> + tee_client_close_session(rproc_tee_ctx->tee_ctx, entry->session_id);
>>> + list_del(&entry->node);
>>> + kfree(entry);
>>
>> So each remoteproc driver have a rproc_tee context on
>> &rproc_tee_ctx-->sessions and without telling them, you just destroy
>> and free their context?
>>
>> Perhaps I'm misinterpreting what you're doing here, but did you test
>> this?
>>
>>> + }
>>> +
>>> + tee_client_close_context(rproc_tee_ctx->tee_ctx);
>>
>> As you return here, the devres-allocated rproc_tee_ctx memory will be
>> freed, and you have a seemingly valid looking pointer and whole bunch of
>> use-after-free cases.
>>
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +MODULE_DEVICE_TABLE(tee, rproc_tee_id_table);
>>> +
>>> +static struct tee_client_driver rproc_tee_fw_driver = {
>>> + .id_table = rproc_tee_id_table,
>>> + .driver = {
>>> + .name = KBUILD_MODNAME,
>>> + .bus = &tee_bus_type,
>>> + .probe = rproc_tee_probe,
>>> + .remove = rproc_tee_remove,
>>> + },
>>> +};
>>> +
>>> +static int __init rproc_tee_fw_mod_init(void)
>>> +{
>>> + return driver_register(&rproc_tee_fw_driver.driver);
>>> +}
>>> +
>>> +static void __exit rproc_tee_fw_mod_exit(void)
>>> +{
>>> + driver_unregister(&rproc_tee_fw_driver.driver);
>>> +}
>>> +
>>> +module_init(rproc_tee_fw_mod_init);
>>> +module_exit(rproc_tee_fw_mod_exit);
>>
>> Please add an equivalent of the module_platform_driver() macro to tee
>> framework instead of open-coding this.
>>
>>> +
>>> +MODULE_DESCRIPTION(" remote processor TEE module");
>>> +MODULE_LICENSE("GPL");
>>> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
>>> index 8fd0d7f63c8e..2e0ddcb2d792 100644
>>> --- a/include/linux/remoteproc.h
>>> +++ b/include/linux/remoteproc.h
>>> @@ -503,6 +503,8 @@ enum rproc_features {
>>> RPROC_MAX_FEATURES,
>>> };
>>>
>>> +struct rproc_tee;
>>> +
>>> /**
>>> * struct rproc - represents a physical remote processor device
>>> * @node: list node of this rproc object
>>> @@ -545,6 +547,7 @@ enum rproc_features {
>>> * @cdev: character device of the rproc
>>> * @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release
>>> * @features: indicate remoteproc features
>>> + * @rproc_tee_itf: pointer to the remoteproc tee context
>>> */
>>> struct rproc {
>>> struct list_head node;
>>> @@ -586,6 +589,7 @@ struct rproc {
>>> struct cdev cdev;
>>> bool cdev_put_on_release;
>>> DECLARE_BITMAP(features, RPROC_MAX_FEATURES);
>>> + struct rproc_tee *rproc_tee_itf;
>>
>> TEE is just one specific remoteproc implementation, why does it need to
>> infest the core data structure? Do you want a stm32_rproc here as well?
>
> Right, this variable is resulting from discussions on some previous revisions.
> As we removed the dependency between the remoteproc core and remoteproc TEE, I
> should be ableto remove it in the next revision.
>
>>
>>> };
>>>
>>> /**
>>> diff --git a/include/linux/remoteproc_tee.h b/include/linux/remoteproc_tee.h
>>> new file mode 100644
>>> index 000000000000..9b498a8eff4d
>>> --- /dev/null
>>> +++ b/include/linux/remoteproc_tee.h
>>> @@ -0,0 +1,105 @@
>>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>>> +/*
>>> + * Copyright(c) 2024 STMicroelectronics
>>> + */
>>> +
>>> +#ifndef REMOTEPROC_TEE_H
>>> +#define REMOTEPROC_TEE_H
>>> +
>>> +#include <linux/tee_drv.h>
>>> +#include <linux/firmware.h>
>>> +#include <linux/remoteproc.h>
>>> +
>>> +struct rproc;
>>> +
>>> +/**
>>> + * struct rproc_tee - TEE remoteproc structure
>>> + * @node: Reference in list
>>> + * @rproc: Remoteproc reference
>>> + * @parent: Parent device
>>
>> Isn't that rproc->dev->parent?
>>
>>> + * @rproc_id: Identifier of the target firmware
>>> + * @session_id: TEE session identifier
>>> + */
>>> +struct rproc_tee {
>>
>> As far as I can tell this isn't dereferenced outside remoteproc_tee.c,
>> can we hide it therein?
>
> The only reference is for rproc_tee_itf, so yes can become internal
>
>>
>>> + struct list_head node;
>>> + struct rproc *rproc;
>>> + struct device *parent;
>>> + u32 rproc_id;
>>> + u32 session_id;
>>> +};
>>> +
>>> +#if IS_REACHABLE(CONFIG_REMOTEPROC_TEE)
>>> +
>>> +int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id);
>>> +int rproc_tee_unregister(struct rproc *rproc);
>>> +int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw);
>>> +int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw);
>>> +void rproc_tee_release_fw(struct rproc *rproc);
>>> +struct resource_table *rproc_tee_find_loaded_rsc_table(struct rproc *rproc,
>>> + const struct firmware *fw);
>>> +int rproc_tee_start(struct rproc *rproc);
>>> +int rproc_tee_stop(struct rproc *rproc);
>>> +
>>> +#else
>>> +
>>
>> Do we really need yet another bunch of stubs? Can't we just leave
>> CONFIG_REMOTEPROC_TEE non-user-selectable and have the drivers that rely
>> on it do "select REMOTEPROC_TEE"?
>>
>> If my measurements are correct, it's 3.1kB of code...
>
> REMOTEPROC_TEE config depends on OPTEE that can be not enabled
> ( we have some customer that not use OPTEE)
> So for build we need them
>
> Thanks for your review,
> Arnaud
>
>>
>> Regards,
>> Bjorn
>>
>>> +static inline int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id)
>>> +{
>>> + return -ENODEV;
>>> +}
>>> +
>>> +static inline int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw)
>>> +{
>>> + /* This shouldn't be possible */
>>> + WARN_ON(1);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static inline int rproc_tee_unregister(struct rproc *rproc)
>>> +{
>>> + /* This shouldn't be possible */
>>> + WARN_ON(1);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static inline int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw)
>>> +{
>>> + /* This shouldn't be possible */
>>> + WARN_ON(1);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static inline int rproc_tee_start(struct rproc *rproc)
>>> +{
>>> + /* This shouldn't be possible */
>>> + WARN_ON(1);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static inline int rproc_tee_stop(struct rproc *rproc)
>>> +{
>>> + /* This shouldn't be possible */
>>> + WARN_ON(1);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static inline void rproc_tee_release_fw(struct rproc *rproc)
>>> +{
>>> + /* This shouldn't be possible */
>>> + WARN_ON(1);
>>> +}
>>> +
>>> +static inline struct resource_table *
>>> +rproc_tee_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
>>> +{
>>> + /* This shouldn't be possible */
>>> + WARN_ON(1);
>>> +
>>> + return NULL;
>>> +}
>>> +#endif /* CONFIG_REMOTEPROC_TEE */
>>> +#endif /* REMOTEPROC_TEE_H */
>>> --
>>> 2.25.1
>>>
>>
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 2/8] remoteproc: Add TEE support
2024-12-10 8:57 ` Arnaud POULIQUEN
2025-01-10 8:51 ` Arnaud POULIQUEN
@ 2025-02-12 3:18 ` Bjorn Andersson
2025-02-12 13:42 ` Arnaud POULIQUEN
1 sibling, 1 reply; 32+ messages in thread
From: Bjorn Andersson @ 2025-02-12 3:18 UTC (permalink / raw)
To: Arnaud POULIQUEN; +Cc: Mathieu Poirier, linux-kernel, linux-remoteproc
On Tue, Dec 10, 2024 at 09:57:40AM +0100, Arnaud POULIQUEN wrote:
> Hello Bjorn,
>
> On 12/6/24 23:07, Bjorn Andersson wrote:
> > On Thu, Nov 28, 2024 at 09:42:09AM GMT, Arnaud Pouliquen wrote:
> >> Add a remoteproc TEE (Trusted Execution Environment) driver
> >> that will be probed by the TEE bus. If the associated Trusted
> >> application is supported on secure part this driver offers a client
> >> interface to load a firmware by the secure part.
> >
> > If...else?
> >
> >> This firmware could be authenticated by the secure trusted application.
> >>
> >
> > I would like for this to fully describe how this fits with the bus and
> Are you speaking about the OP-TEE bus?
>
> I assume that your attempt is that I provide more details on the live cycle
> sequence, right?
>
Right, there's a tee_client_driver and there's a remoteproc driver.
Let's document clearly how these interact.
> > how it is expected to be used by a specific remoteproc driver.
> >
> >> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> >> ---
> >> Updates vs version v13:
> >> - define REMOTEPROC_TEE as bool instead of tristate,
> >> - remove the load of the firmware in rproc_tee_parse_fw as we will ensure
> >> that the firmware is loaded using the load_fw() operation.
> >> ---
> >> drivers/remoteproc/Kconfig | 10 +
> >> drivers/remoteproc/Makefile | 1 +
> >> drivers/remoteproc/remoteproc_tee.c | 508 ++++++++++++++++++++++++++++
> >> include/linux/remoteproc.h | 4 +
> >> include/linux/remoteproc_tee.h | 105 ++++++
> >> 5 files changed, 628 insertions(+)
> >> create mode 100644 drivers/remoteproc/remoteproc_tee.c
> >> create mode 100644 include/linux/remoteproc_tee.h
> >>
> >> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> >> index 955e4e38477e..f6335321d540 100644
> >> --- a/drivers/remoteproc/Kconfig
> >> +++ b/drivers/remoteproc/Kconfig
> >> @@ -23,6 +23,16 @@ config REMOTEPROC_CDEV
> >>
> >> It's safe to say N if you don't want to use this interface.
> >>
> >> +config REMOTEPROC_TEE
> >> + bool "Remoteproc support by a TEE application"
> >> + depends on OPTEE
> >> + help
> >> + Support a remote processor with a TEE application.
> >
> > Does the remote processor run TEE applications? (Rethorical question...)
> >
> >> The Trusted
> >> + Execution Context is responsible for loading the trusted firmware
> >> + image and managing the remote processor's lifecycle.
> >> +
> >> + It's safe to say N if you don't want to use remoteproc TEE.
> >
> > It's not really about "wanting to use", it's a question whether your
> > device implements/provides the remoteproc TEE.
> >
> >> +
> >> config IMX_REMOTEPROC
> >> tristate "i.MX remoteproc support"
> >> depends on ARCH_MXC
> >> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
> >> index 5ff4e2fee4ab..f77e0abe8349 100644
> >> --- a/drivers/remoteproc/Makefile
> >> +++ b/drivers/remoteproc/Makefile
> >> @@ -11,6 +11,7 @@ remoteproc-y += remoteproc_sysfs.o
> >> remoteproc-y += remoteproc_virtio.o
> >> remoteproc-y += remoteproc_elf_loader.o
> >> obj-$(CONFIG_REMOTEPROC_CDEV) += remoteproc_cdev.o
> >> +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o
> >> obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o
> >> obj-$(CONFIG_IMX_DSP_REMOTEPROC) += imx_dsp_rproc.o
> >> obj-$(CONFIG_INGENIC_VPU_RPROC) += ingenic_rproc.o
> >> diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c
> >> new file mode 100644
> >> index 000000000000..3fe3f31068f2
> >> --- /dev/null
> >> +++ b/drivers/remoteproc/remoteproc_tee.c
> >> @@ -0,0 +1,508 @@
> >> +// SPDX-License-Identifier: GPL-2.0-or-later
> >> +/*
> >> + * Copyright (C) STMicroelectronics 2024
> >> + * Author: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> >> + */
> >> +
> >> +#include <linux/firmware.h>
> >> +#include <linux/io.h>
> >> +#include <linux/module.h>
> >> +#include <linux/remoteproc.h>
> >> +#include <linux/remoteproc_tee.h>
> >> +#include <linux/slab.h>
> >> +#include <linux/tee_drv.h>
> >> +
> >> +#define MAX_TEE_PARAM_ARRAY_MEMBER 4
> >> +
> >> +/*
> >> + * Authentication of the firmware and load in the remote processor memory
> >
> > Exactly what does this imply? Will the content of @memref be copied into
> > some other memory?
>
> The objective is to authenticate and load in one step. So, yes, the image is
> loaded into the remoteproc destination memory.
>
So, some separate device-memory, or some preallocated carveout which is
only accessible from secure world?
Does the OS need to retain @memref past this point?
> On stm32mp1 we can not store the elf file in a temporary secure memory as
> the memory is encrypted by software (this would take to much time).
>
> For your information, in OP-TEE, the application code is split into a generic
> part and a platform adaptation layer. The generic application is mainly
> responsible for:
>
> - Copying the binary header and metadata into secure memory and authenticating them.
> - Parsing the ELF images and providing segments to load with associated
> authenticated hashes to the platform application.
> In the future, someone can add their own format if needed.
>
> But the generic part could be enhance to authenticate and load a non ELF binary.
> So I'm trying to be generic as possible here.
>
Generic might be okay, but I'd prefer this to be less vague.
Also worth noting is the Qualcomm implementation of TZ-backed
remoteproc, which is already in the tree. There the firmware is loaded
into carveouts, the certificates and hashes are validated. Lastly
the operation "authenticate and start" is invoked, which does that, and
locks the OS out of the given memory region - until "shutdown" is
invoked.
>
> >
> >> + *
> >> + * [in] params[0].value.a: unique 32bit identifier of the remote processor
> >
> > Why not just "remote processor identifier"?
> >
> >> + * [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
> >
> > Why is there two "FW" in this constant? Why isn't it just
> > "TA_RPROC_FW_CMD_START"?
> >
> > And why is it not TEE_PROC_FW_CMD_START?
> >
> >> +
> >> +/*
> >> + * 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.
> >
> > These three lines describe a scenario that doesn't make any sense to me.
> > But if that's the case, you should be able to describe that the API
> > might give you a inaccessible pointer, by design.
>
> On STM32MP, we have a kind of firewall in OP-TEE that sets memory access rights
> from the device tree. So if the firmware image is not linked according to the
> firewall configuration, the pointer may not be accessible.
>
> I will update the comment as you propose.
>
> >
> >> + *
> >> + * [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
> >
> > What does this mean? What will I find at @memref after this call?
>
> I do not have a simple answer here as it depends on the OP-TEE strategy.
> It could be an obscure core dump with possible encryption.
>
> I will remove this as it is not yet implemented in OP-TEE.
>
Okay. But I would prefer that we define the semantics before it's
implemented...
> https://elixir.bootlin.com/op-tee/4.4.0/source/ta/remoteproc/src/remoteproc_core.c#L1131
>
> >
> >> + *
> >> + * [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
> >
> > s/else return Null/or NULL/
> >
> >> + */
> >> +#define TA_RPROC_FW_CMD_GET_COREDUMP 5
> >> +
> >> +/*
> >> + * Release remote processor firmware images and associated resources.
> >
> > Exactly what does this mean for the caller?
>
> It is platform dependent. It can consist in cleaning the memory, but
> can be also something else such as firewall configuration.
> On stm323mp we clean all the memories region reserved for the remote processor.
>
We can't have an ABI which isn't well defined in intent. Your examples
would easily fall in the realm of a well defined interface, but this
ties into the question above - what does is actually mean in terms of
the memory carveouts and such.
> >
> >> + * This command should be used in case an error occurs between the loading of
> >> + * the firmware images (TA_RPROC_CMD_LOAD_FW) and the starting of the remote
> >> + * processor (TA_RPROC_CMD_START_FW) or after stopping the remote processor
> >> + * to release associated resources (TA_RPROC_CMD_STOP_FW).
> >
> > This description belongs adjacent to LOAD_FW, and describe it in terms
> > of what state LOAD_FW leaves the buffers and remote processor in.
>
> Sorry, it is not clear to me what you are expecting here.
>
This describes the state LOAD_FW leaves things in and the action you
expect the caller to take. Right now I need to read all the
documentation in order to understand how LOAD_FW works.
Document this behavior in relation to LOAD_FW.
> >
> >> + *
> >> + * [in] params[0].value.a: Unique 32-bit remote processor identifier
> >> + */
> >> +#define TA_RPROC_CMD_RELEASE_FW 6
> >> +
> >> +struct rproc_tee_context {
> >> + struct list_head sessions;
> >> + struct tee_context *tee_ctx;
> >> + struct device *dev;
> >> +};
> >> +
> >> +static struct rproc_tee_context *rproc_tee_ctx;
> >> +
> >> +static void rproc_tee_prepare_args(struct rproc_tee *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_ARRAY_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,
> >> + };
> >> +}
> >> +
> >
> > Provide kernel-doc for EXPORT_SYMBOL*() functions.
>
> Should it be in the remoteproc kernel doc or in a new doc file that
> provide an overview of the remoteproc_tee usage?
>
I'm referring to
https://docs.kernel.org/doc-guide/kernel-doc.html#function-documentation,
i.e. a /** func() ... */ comment above each such function.
This allow the caller to easily reach the documentation by using
mechanism such as "jump-to-definition" in their IDE.
>
> >
> >> +void rproc_tee_release_fw(struct rproc *rproc)
> >> +{
> >> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
> >> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
> >> + struct tee_ioctl_invoke_arg arg;
> >> + int ret;
> >> +
> >> + if (!rproc) {
> >
> > How can this happen?
> >
> > This error will happen in two cases:
> >
> > 1) on your desk while you develop the client and you have to hunt
> > through the kernel log to figure out that the reason you can't start
> > your remoteproc is because 5 minutes ago there was a error log saying
> > that we didn't stop it last time.
> >
> > 2) in the customer device because of some obscure bug, where no one will
> > read the logs and the software will happily continue to execute with a
> > broken state.
> >
> >> + ret = -EINVAL;
> >> + goto out;
> >> + }
> >> +
> >> + /*
> >> + * If the remote processor state is RPROC_DETACHED, just ignore the
> >> + * request, as the remote processor is still running.
> >> + */
> >> + if (rproc->state == RPROC_DETACHED)
> >> + return;
> >> +
> >> + if (rproc->state != RPROC_OFFLINE) {
> >> + ret = -EBUSY;
> >
> > The function is void... Defensive coding is only useful when it saves
> > you from future mistakes, not when it hides problems from you.
> >
> >> + goto out;
> >> + }
> >> +
> >> + rproc_tee_prepare_args(trproc, TA_RPROC_CMD_RELEASE_FW, &arg, param, 0);
> >> +
> >> + ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
> >> + if (ret < 0 || arg.ret != 0) {
> >> + dev_err(rproc_tee_ctx->dev,
> >> + "TA_RPROC_CMD_RELEASE_FW invoke failed TEE err: %x, ret:%x\n",
> >> + arg.ret, ret);
> >
> > At least @ret will be base 10, so don't print that in base 16 without
> > indication that it's not base 10.
> >
> >
> > Also, this will result in two dev_err() prints, printing out two
> > different error codes.
>
> Here i copy /past error managing from other drivers [1][2].
No, both of these examples has a '#' between '%' and 'x', which will
make the number be prefixed with 0x - making the base clear to the
reader.
> Should I make it different and use 2 dev_err?
>
What I mean is that here you're telling the user that there was an
error, with one value of "ret". Then you continue below, will find ret
with -EIO and then you will print another message saying that it failed
and this time with -EIO.
Why two prints?
>
> [1]
> https://elixir.bootlin.com/linux/v6.12.1/source/drivers/firmware/arm_scmi/transports/optee.c#L244
>
> [2]
> https://elixir.bootlin.com/linux/v6.12.1/source/drivers/firmware/arm_scmi/transports/optee.c#L244
>
> >
> >> + ret = -EIO;
> >> + }
> >> +
> >> +out:
> >> + if (ret)
> >> + /* Unexpected state without solution to come back in a stable state */
> >> + dev_err(rproc_tee_ctx->dev, "Failed to release TEE remoteproc firmware: %d\n", ret);
> >> +}
> >> +EXPORT_SYMBOL_GPL(rproc_tee_release_fw);
> >> +
Regards,
Bjorn
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation
2024-12-10 10:33 ` Arnaud POULIQUEN
@ 2025-02-12 3:54 ` Bjorn Andersson
2025-02-12 13:48 ` Arnaud POULIQUEN
0 siblings, 1 reply; 32+ messages in thread
From: Bjorn Andersson @ 2025-02-12 3:54 UTC (permalink / raw)
To: Arnaud POULIQUEN; +Cc: Mathieu Poirier, linux-remoteproc, linux-kernel
On Tue, Dec 10, 2024 at 11:33:31AM +0100, Arnaud POULIQUEN wrote:
>
>
> On 12/10/24 00:14, Bjorn Andersson wrote:
> > On Thu, Nov 28, 2024 at 09:42:10AM GMT, Arnaud Pouliquen wrote:
> >> This patch updates the rproc_ops structures to include two new optional
> >> operations.
> >>
> >> - The load_fw() op is responsible for loading the remote processor
> >> non-ELF firmware image before starting the boot sequence. This ops will
> >> be used, for instance, to call OP-TEE to authenticate an load the firmware
> >> image before accessing to its resources (a.e the resource table)
> >>
> >> - The release_fw op is responsible for releasing the remote processor
> >> firmware image. For instance to clean memories.
> >> The ops is called in the following cases:
> >> - An error occurs between the loading of the firmware image and the
> >> start of the remote processor.
> >> - after stopping the remote processor.
> >>
> >
> > Why does this difference need to be encoded in rproc_ops? I think we
> > should strive for having a single, simple high level flow of operations
> > through the remoteproc core for which the specifics of each remoteproc
> > instance can be encoded in that driver.
> >
> >
> > Perhaps there's a good reason for this, but if so please read and follow
> > https://docs.kernel.org/process/submitting-patches.html#describe-your-changes
> > to make that reasoning clear in the commit message.
> >
>
> The actual sequence to load a remoteproc firmware is
> - get firmware from file system and store the firmware image in Linux kernel memory
This sounds like "load" in remoteproc terminology.
> - get resource table from the firmware image and make a copy(
> - parse the resource table and handle the resources
> - load the firmware
> - start the firmware
And these two are "start".
>
>
> In OP-TEE we support not only one ELF image but n images (for instance a TF-M +
> a zephyr), the segments can be encrypted the OP-TEE load sequence is
> - copy header and meta data of the signed image in a secure memory
> - verify it
> - copy segments in remote processor memory and authenticate segments in place.
> - optionally decrypt the segments
>
> Only at this step the resource table as been authenticated (and decrypted)
"this step" meaning TA_RPROC_FW_CMD_LOAD_FW? Above you say that happens
after you parse the resource table.
>
> So the point is that we need to load the firmware before getting the resource table
>
>
> >> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> >> ---
> >> Update vs version V13:
> >> - Rework the commit to introduce load_fw() op.
> >> - remove rproc_release_fw() call from rproc_start() as called in
> >> rproc_boot() and rproc_boot_recovery() in case of error.
> >> - create rproc_load_fw() and rproc_release_fw() internal functions.
> >> ---
> >> drivers/remoteproc/remoteproc_core.c | 16 +++++++++++++++-
> >> drivers/remoteproc/remoteproc_internal.h | 14 ++++++++++++++
> >> include/linux/remoteproc.h | 6 ++++++
> >> 3 files changed, 35 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
> >> index ace11ea17097..8df4b2c59bb6 100644
> >> --- a/drivers/remoteproc/remoteproc_core.c
> >> +++ b/drivers/remoteproc/remoteproc_core.c
> >> @@ -1488,6 +1488,7 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
> >> kfree(rproc->cached_table);
> >> rproc->cached_table = NULL;
> >> rproc->table_ptr = NULL;
> >> + rproc_release_fw(rproc);
> >> unprepare_rproc:
> >> /* release HW resources if needed */
> >> rproc_unprepare_device(rproc);
> >> @@ -1855,8 +1856,14 @@ static int rproc_boot_recovery(struct rproc *rproc)
> >> return ret;
> >> }
> >>
> >> + ret = rproc_load_fw(rproc, firmware_p);
> >
> > It is not clear to me why in the case of OP-TEE we need to invoke the
> > "load operation" here, and in the case of "legacy" ELF loading we do it
> > first thing in rproc_start() (i.e. on the very next line of code being
> > executed).
>
> For the OP-TEE, please refer to my comment above.
>
> The only reason I can see for the legacy ELF is that the resource table could
> contain information to be able to configure some resources to load the firmware.
> In case of OP-TEE this would be managed in OP-TEE.
>
Sure, but as I say...inline rproc_start() here and the very next
operation we perform is something we call "load".
Why do we need to differentiate "load" and "load firmware", when we call
them at the same step in the sequence?
> >
> >
> > Should we start by renaming rproc_load_segments() rproc_load() and move
> > it out of rproc_start()? (I.e. here?)
> >
> > Perhaps define that rproc_load() is responsible for "loading firmware"
> > (whatever that means) and establishing rproc->cached_table, and
> > rproc->table_ptr?
> >
> > (Note that this seems like a good cleanup of the spaghetti regardless)
> >
>
> It's something that crossed my mind, but I don't know the legacy well enough to
> guarantee that it will work in all drivers.
>
Looking at this again, if we move it, we need to duplicate it across a
few different call sites. So might need to take another look at that.
Still flatten the code and you seem to do:
if (load_fw)
load_fw();
if (load)
load();
Why do we need two different things named "load".
> If you want to go in this direction, perhaps this is something that could be
> addressed in a dedicated pull request? In this case, the ops could become
> load_fw and load_fw_new, similar to how it is done for platform_driver::remove.
>
No need to make it more complicated than it is, change the symbol name
and fix the 3 places that calls the function.
>
> >> + if (ret)
> >> + return ret;
> >> +
> >> /* boot the remote processor up again */
> >> ret = rproc_start(rproc, firmware_p);
> >> + if (ret)
> >> + rproc_release_fw(rproc);
> >
> > The fact that you rproc_release_fw() in the error path here, right
> > before we unconditionally release_firmware() the actual firmware means
> > that you have 2 different life cycles with very very similar names.
> >
> > This will contain bugs, sooner or later.
>
> So we need to find a better way for the ops if we continue in this direction.
> What about introducing rproc_load_new and rproc_release?
>
You need to help me understand why load and load_new are both needed.
And no, "load_new" is not an ok name.
> >
> >>
> >> release_firmware(firmware_p);
> >>
> >> @@ -1997,7 +2004,13 @@ int rproc_boot(struct rproc *rproc)
> >> goto downref_rproc;
> >> }
> >>
> >> + ret = rproc_load_fw(rproc, firmware_p);
> >> + if (ret)
> >> + goto downref_rproc;
> >> +
> >> ret = rproc_fw_boot(rproc, firmware_p);
> >> + if (ret)
> >> + rproc_release_fw(rproc);
> >>
> >> release_firmware(firmware_p);
> >> }
> >> @@ -2071,6 +2084,7 @@ int rproc_shutdown(struct rproc *rproc)
> >> kfree(rproc->cached_table);
> >> rproc->cached_table = NULL;
> >> rproc->table_ptr = NULL;
> >> + rproc_release_fw(rproc);
> >> out:
> >> mutex_unlock(&rproc->lock);
> >> return ret;
> >> @@ -2471,7 +2485,7 @@ static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
> >> if (!rproc->ops->coredump)
> >> rproc->ops->coredump = rproc_coredump;
> >>
> >> - if (rproc->ops->load)
> >> + if (rproc->ops->load || rproc->ops->load_fw)
> >> return 0;
> >>
> >> /* Default to ELF loader if no load function is specified */
> >> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
> >> index 0cd09e67ac14..2104ca449178 100644
> >> --- a/drivers/remoteproc/remoteproc_internal.h
> >> +++ b/drivers/remoteproc/remoteproc_internal.h
> >> @@ -221,4 +221,18 @@ bool rproc_u64_fit_in_size_t(u64 val)
> >> return (val <= (size_t) -1);
> >> }
> >>
> >> +static inline void rproc_release_fw(struct rproc *rproc)
> >> +{
> >> + if (rproc->ops->release_fw)
> >> + rproc->ops->release_fw(rproc);
> >> +}
> >> +
> >> +static inline int rproc_load_fw(struct rproc *rproc, const struct firmware *fw)
> >> +{
> >> + if (rproc->ops->load_fw)
> >> + return rproc->ops->load_fw(rproc, fw);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> #endif /* REMOTEPROC_INTERNAL_H */
> >> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
> >> index 2e0ddcb2d792..ba6fd560f7ba 100644
> >> --- a/include/linux/remoteproc.h
> >> +++ b/include/linux/remoteproc.h
> >> @@ -381,6 +381,10 @@ enum rsc_handling_status {
> >> * @panic: optional callback to react to system panic, core will delay
> >> * panic at least the returned number of milliseconds
> >> * @coredump: collect firmware dump after the subsystem is shutdown
> >> + * @load_fw: optional function to load non-ELF firmware image to memory, where the remote
> >> + * processor expects to find it.
> >
> > Why does it matter if it's an ELF or not?
>
> No matter. It was more to differentiate from the legacy one, but it does not
> make sense and adds to the argument that the ops naming is not accurate.
>
I'm probably misunderstanding your intention here, but I can't help
feeling that you add this code path to avoid understanding or fixing the
existing code.
> >
> > In the Qualcomm case, firmware comes in ELF format, Linux loads the
> > LOAD segments and the trusted world then authenticates the content and
> > start the remote processor.
> >
> >
> > I think the difference in your case is that you have memory reserved
> > elsewhere, and you want the "load" operation to pass the firmware to the
> > TEE - which means that you need rproc_release_fw() to eventually clean
> > up the state if rproc_start() fails - and upon shutdown.
>
> Yes the OP-TEE is make more stuff:
> - authenticate several firmware images
Please tell me how that work. I'm not able to see the multiple calls to
request_firmware()...
> - decrypt images if encrypted
This is done for some Qualcomm remoteprocs as well.
> - ensure that the load is done in granted memories
At the top of your reply you say that you're loading the firmware into
Linux memory, then you invoke TA_RPROC_FW_CMD_LOAD_FW to "load" it once
more - presumably copying into some other memory.
It sounds like this is an optimization to fail early in the case that
something is wrong?
> - manage the memory access rights to enure that the code and data memory
> is never accessible by the Linux.
>
Right, after authentication Linux must be locked out. That's done in the
Qualcomm "authenticate and start" phase, at the very end of the loading
and setting things up.
> >
> > If we improve the definition of rproc_load_segments() to mean
> > "remoteproc (or remoteproc driver) is loading segments", then in your
> > case there's no "loading" operation in Linux. Instead you make that a
> > nop and invoke LOAD_FW and START_FW within your start callback, then you
> > can clean up the remnant state within your driver's start and stop
> > callbacks - without complicating the core framework.
>
> This would not work as I need to load the firmware before calling
> rproc_handle_resources().
>
Ohh, now I see what you're saying. This is why you should follow
https://docs.kernel.org/process/submitting-patches.html#describe-your-changes
when you write a commit message. Then I would have understood your
problem in the very first paragraph of the patch.
This very much sounds like what's intended to happen in
rproc_parse_fw(), per the comment right next to the call:
/* Load resource table, core dump segment list etc from the firmware */
But I'm guessing that would require loading the segments etc into the
destination memory, perform the authentication and then you can get hold
of the resource table?
Which..per your patch you do before calling rproc_fw_boot(), so you're
already diverging completely from the expected flow.
> I can not use rproc_prepare_device() as it is not called on recovery
>
The purpose of rproc_prepare_device() is to ensure that any clocks etc
for the memory where we're going to load the firmware is enabled, so
that doesn't sounds like the right place.
Regards,
Bjorn
> Thanks,
> Arnaud
>
> >
> > Regards,
> > Bjorn
> >
> >> + * @release_fw: optional function to release the firmware image from memories.
> >> + * This function is called after stopping the remote processor or in case of error
> >> */
> >> struct rproc_ops {
> >> int (*prepare)(struct rproc *rproc);
> >> @@ -403,6 +407,8 @@ struct rproc_ops {
> >> u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
> >> unsigned long (*panic)(struct rproc *rproc);
> >> void (*coredump)(struct rproc *rproc);
> >> + int (*load_fw)(struct rproc *rproc, const struct firmware *fw);
> >> + void (*release_fw)(struct rproc *rproc);
> >> };
> >>
> >> /**
> >> --
> >> 2.25.1
> >>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 2/8] remoteproc: Add TEE support
2025-02-12 3:18 ` Bjorn Andersson
@ 2025-02-12 13:42 ` Arnaud POULIQUEN
2025-03-04 15:58 ` Bjorn Andersson
0 siblings, 1 reply; 32+ messages in thread
From: Arnaud POULIQUEN @ 2025-02-12 13:42 UTC (permalink / raw)
To: Bjorn Andersson; +Cc: Mathieu Poirier, linux-kernel, linux-remoteproc
Hello,
On 2/12/25 04:18, Bjorn Andersson wrote:
> On Tue, Dec 10, 2024 at 09:57:40AM +0100, Arnaud POULIQUEN wrote:
>> Hello Bjorn,
>>
>> On 12/6/24 23:07, Bjorn Andersson wrote:
>>> On Thu, Nov 28, 2024 at 09:42:09AM GMT, Arnaud Pouliquen wrote:
>>>> Add a remoteproc TEE (Trusted Execution Environment) driver
>>>> that will be probed by the TEE bus. If the associated Trusted
>>>> application is supported on secure part this driver offers a client
>>>> interface to load a firmware by the secure part.
>>>
>>> If...else?
>>>
>>>> This firmware could be authenticated by the secure trusted application.
>>>>
>>>
>>> I would like for this to fully describe how this fits with the bus and
>> Are you speaking about the OP-TEE bus?
>>
>> I assume that your attempt is that I provide more details on the live cycle
>> sequence, right?
>>
>
> Right, there's a tee_client_driver and there's a remoteproc driver.
> Let's document clearly how these interact.
>
>>> how it is expected to be used by a specific remoteproc driver.
>>>
>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>>>> ---
>>>> Updates vs version v13:
>>>> - define REMOTEPROC_TEE as bool instead of tristate,
>>>> - remove the load of the firmware in rproc_tee_parse_fw as we will ensure
>>>> that the firmware is loaded using the load_fw() operation.
>>>> ---
>>>> drivers/remoteproc/Kconfig | 10 +
>>>> drivers/remoteproc/Makefile | 1 +
>>>> drivers/remoteproc/remoteproc_tee.c | 508 ++++++++++++++++++++++++++++
>>>> include/linux/remoteproc.h | 4 +
>>>> include/linux/remoteproc_tee.h | 105 ++++++
>>>> 5 files changed, 628 insertions(+)
>>>> create mode 100644 drivers/remoteproc/remoteproc_tee.c
>>>> create mode 100644 include/linux/remoteproc_tee.h
>>>>
>>>> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
>>>> index 955e4e38477e..f6335321d540 100644
>>>> --- a/drivers/remoteproc/Kconfig
>>>> +++ b/drivers/remoteproc/Kconfig
>>>> @@ -23,6 +23,16 @@ config REMOTEPROC_CDEV
>>>>
>>>> It's safe to say N if you don't want to use this interface.
>>>>
>>>> +config REMOTEPROC_TEE
>>>> + bool "Remoteproc support by a TEE application"
>>>> + depends on OPTEE
>>>> + help
>>>> + Support a remote processor with a TEE application.
>>>
>>> Does the remote processor run TEE applications? (Rethorical question...)
>>>
>>>> The Trusted
>>>> + Execution Context is responsible for loading the trusted firmware
>>>> + image and managing the remote processor's lifecycle.
>>>> +
>>>> + It's safe to say N if you don't want to use remoteproc TEE.
>>>
>>> It's not really about "wanting to use", it's a question whether your
>>> device implements/provides the remoteproc TEE.
>>>
>>>> +
>>>> config IMX_REMOTEPROC
>>>> tristate "i.MX remoteproc support"
>>>> depends on ARCH_MXC
>>>> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
>>>> index 5ff4e2fee4ab..f77e0abe8349 100644
>>>> --- a/drivers/remoteproc/Makefile
>>>> +++ b/drivers/remoteproc/Makefile
>>>> @@ -11,6 +11,7 @@ remoteproc-y += remoteproc_sysfs.o
>>>> remoteproc-y += remoteproc_virtio.o
>>>> remoteproc-y += remoteproc_elf_loader.o
>>>> obj-$(CONFIG_REMOTEPROC_CDEV) += remoteproc_cdev.o
>>>> +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o
>>>> obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o
>>>> obj-$(CONFIG_IMX_DSP_REMOTEPROC) += imx_dsp_rproc.o
>>>> obj-$(CONFIG_INGENIC_VPU_RPROC) += ingenic_rproc.o
>>>> diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c
>>>> new file mode 100644
>>>> index 000000000000..3fe3f31068f2
>>>> --- /dev/null
>>>> +++ b/drivers/remoteproc/remoteproc_tee.c
>>>> @@ -0,0 +1,508 @@
>>>> +// SPDX-License-Identifier: GPL-2.0-or-later
>>>> +/*
>>>> + * Copyright (C) STMicroelectronics 2024
>>>> + * Author: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>>>> + */
>>>> +
>>>> +#include <linux/firmware.h>
>>>> +#include <linux/io.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/remoteproc.h>
>>>> +#include <linux/remoteproc_tee.h>
>>>> +#include <linux/slab.h>
>>>> +#include <linux/tee_drv.h>
>>>> +
>>>> +#define MAX_TEE_PARAM_ARRAY_MEMBER 4
>>>> +
>>>> +/*
>>>> + * Authentication of the firmware and load in the remote processor memory
>>>
>>> Exactly what does this imply? Will the content of @memref be copied into
>>> some other memory?
>>
>> The objective is to authenticate and load in one step. So, yes, the image is
>> loaded into the remoteproc destination memory.
>>
>
> So, some separate device-memory, or some preallocated carveout which is
> only accessible from secure world?
No sure to understand the difference you do between eparate device-memory and
preallocated carveout.
In OP-TEE, we use the same principle as in Linux. OP-TEE uses memory regions
declared in its device tree to list memories usable for the coprocessor (with
associated access rights). On load, it checks that the segments to load are
included in these memory regions.
Linux only declares the shared memory-regions in the device tree, for IPC.
>
> Does the OS need to retain @memref past this point?
No need,and as the area contains the reult of request_firmware() that can be
corrupted by Linux, OP-TEE considered this as a temporaray unsafe memory. After
the load + authentication step this buffer is no more used.
For detail, OPTEE make a copy of the header and TLV (metadata) in a secure
memory. and load the firmware images in destination memories All these memories
are not accessible from the Linux.
>
>> On stm32mp1 we can not store the elf file in a temporary secure memory as
>> the memory is encrypted by software (this would take to much time).
>>
>> For your information, in OP-TEE, the application code is split into a generic
>> part and a platform adaptation layer. The generic application is mainly
>> responsible for:
>>
>> - Copying the binary header and metadata into secure memory and authenticating them.
>> - Parsing the ELF images and providing segments to load with associated
>> authenticated hashes to the platform application.
>> In the future, someone can add their own format if needed.
>>
>> But the generic part could be enhance to authenticate and load a non ELF binary.
>> So I'm trying to be generic as possible here.
>>
>
> Generic might be okay, but I'd prefer this to be less vague.
> Also worth noting is the Qualcomm implementation of TZ-backed
> remoteproc, which is already in the tree.
Could you point me the code in Linux and your TEE, please?
> There the firmware is loaded
> into carveouts, the certificates and hashes are validated.
Seems to me that there is also a partial Authentication done during the load step.
> Lastly
> the operation "authenticate and start" is invoked, which does that, and
> locks the OS out of the given memory region - until "shutdown" is
> invoked.
The differnce between the 2 implementations is the authentication method done in
2 steps for Qualcomm implementation , in one step for OP-TEE.
So here if I just remove the term 'authentication' in the command description
does it ok for you?
>
>>
>>>
>>>> + *
>>>> + * [in] params[0].value.a: unique 32bit identifier of the remote processor
>>>
>>> Why not just "remote processor identifier"?
>>>
>>>> + * [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
>>>
>>> Why is there two "FW" in this constant? Why isn't it just
>>> "TA_RPROC_FW_CMD_START"?
>>>
>>> And why is it not TEE_PROC_FW_CMD_START?
>>>
>>>> +
>>>> +/*
>>>> + * 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.
>>>
>>> These three lines describe a scenario that doesn't make any sense to me.
>>> But if that's the case, you should be able to describe that the API
>>> might give you a inaccessible pointer, by design.
>>
>> On STM32MP, we have a kind of firewall in OP-TEE that sets memory access rights
>> from the device tree. So if the firmware image is not linked according to the
>> firewall configuration, the pointer may not be accessible.
>>
>> I will update the comment as you propose.
>>
>>>
>>>> + *
>>>> + * [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
>>>
>>> What does this mean? What will I find at @memref after this call?
>>
>> I do not have a simple answer here as it depends on the OP-TEE strategy.
>> It could be an obscure core dump with possible encryption.
>>
>> I will remove this as it is not yet implemented in OP-TEE.
>>
>
> Okay. But I would prefer that we define the semantics before it's
> implemented...
that seems fair, I notice that we will have to address this in a separate thread
strating with a series in Linux.
>
>> https://elixir.bootlin.com/op-tee/4.4.0/source/ta/remoteproc/src/remoteproc_core.c#L1131
>>
>>>
>>>> + *
>>>> + * [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
>>>
>>> s/else return Null/or NULL/
>>>
>>>> + */
>>>> +#define TA_RPROC_FW_CMD_GET_COREDUMP 5
>>>> +
>>>> +/*
>>>> + * Release remote processor firmware images and associated resources.
>>>
>>> Exactly what does this mean for the caller?
>>
>> It is platform dependent. It can consist in cleaning the memory, but
>> can be also something else such as firewall configuration.
>> On stm323mp we clean all the memories region reserved for the remote processor.
>>
>
> We can't have an ABI which isn't well defined in intent. Your examples
> would easily fall in the realm of a well defined interface, but this
> ties into the question above - what does is actually mean in terms of
> the memory carveouts and such.
>
Regarding this comment and the one below, does following description would
respond to your expectations? else do you have a suggestion?
/*
* This command should be used in case an error occurs between the loading of
* the firmware images (TA_RPROC_CMD_LOAD_FW) and the starting of the remote
* processor (TA_RPROC_CMD_START_FW), or after stopping the remote processor
* (TA_RPROC_CMD_STOP_FW).
*
* This command is used to inform the TEE (Trusted Execution Environment) that
* resources associated with the remote processor can be released. After this
* command, the firmware is no longer present in the remote processor's memories
* and must be reloaded (TA_RPROC_FW_CMD_LOAD_FW) to restart the remote
* processor.
*/
Thanks;
Arnaud
>>>
>>>> + * This command should be used in case an error occurs between the loading of
>>>> + * the firmware images (TA_RPROC_CMD_LOAD_FW) and the starting of the remote
>>>> + * processor (TA_RPROC_CMD_START_FW) or after stopping the remote processor
>>>> + * to release associated resources (TA_RPROC_CMD_STOP_FW).
>>>
>>> This description belongs adjacent to LOAD_FW, and describe it in terms
>>> of what state LOAD_FW leaves the buffers and remote processor in.
>>
>> Sorry, it is not clear to me what you are expecting here.
>>
>
> This describes the state LOAD_FW leaves things in and the action you
> expect the caller to take. Right now I need to read all the
> documentation in order to understand how LOAD_FW works.
>
> Document this behavior in relation to LOAD_FW.
>
>>>
>>>> + *
>>>> + * [in] params[0].value.a: Unique 32-bit remote processor identifier
>>>> + */
>>>> +#define TA_RPROC_CMD_RELEASE_FW 6
>>>> +
>>>> +struct rproc_tee_context {
>>>> + struct list_head sessions;
>>>> + struct tee_context *tee_ctx;
>>>> + struct device *dev;
>>>> +};
>>>> +
>>>> +static struct rproc_tee_context *rproc_tee_ctx;
>>>> +
>>>> +static void rproc_tee_prepare_args(struct rproc_tee *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_ARRAY_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,
>>>> + };
>>>> +}
>>>> +
>>>
>>> Provide kernel-doc for EXPORT_SYMBOL*() functions.
>>
>> Should it be in the remoteproc kernel doc or in a new doc file that
>> provide an overview of the remoteproc_tee usage?
>>
>
> I'm referring to
> https://docs.kernel.org/doc-guide/kernel-doc.html#function-documentation,
> i.e. a /** func() ... */ comment above each such function.
>
> This allow the caller to easily reach the documentation by using
> mechanism such as "jump-to-definition" in their IDE.
>
>>
>>>
>>>> +void rproc_tee_release_fw(struct rproc *rproc)
>>>> +{
>>>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>>>> + struct rproc_tee *trproc = rproc->rproc_tee_itf;
>>>> + struct tee_ioctl_invoke_arg arg;
>>>> + int ret;
>>>> +
>>>> + if (!rproc) {
>>>
>>> How can this happen?
>>>
>>> This error will happen in two cases:
>>>
>>> 1) on your desk while you develop the client and you have to hunt
>>> through the kernel log to figure out that the reason you can't start
>>> your remoteproc is because 5 minutes ago there was a error log saying
>>> that we didn't stop it last time.
>>>
>>> 2) in the customer device because of some obscure bug, where no one will
>>> read the logs and the software will happily continue to execute with a
>>> broken state.
>>>
>>>> + ret = -EINVAL;
>>>> + goto out;
>>>> + }
>>>> +
>>>> + /*
>>>> + * If the remote processor state is RPROC_DETACHED, just ignore the
>>>> + * request, as the remote processor is still running.
>>>> + */
>>>> + if (rproc->state == RPROC_DETACHED)
>>>> + return;
>>>> +
>>>> + if (rproc->state != RPROC_OFFLINE) {
>>>> + ret = -EBUSY;
>>>
>>> The function is void... Defensive coding is only useful when it saves
>>> you from future mistakes, not when it hides problems from you.
>>>
>>>> + goto out;
>>>> + }
>>>> +
>>>> + rproc_tee_prepare_args(trproc, TA_RPROC_CMD_RELEASE_FW, &arg, param, 0);
>>>> +
>>>> + ret = tee_client_invoke_func(rproc_tee_ctx->tee_ctx, &arg, param);
>>>> + if (ret < 0 || arg.ret != 0) {
>>>> + dev_err(rproc_tee_ctx->dev,
>>>> + "TA_RPROC_CMD_RELEASE_FW invoke failed TEE err: %x, ret:%x\n",
>>>> + arg.ret, ret);
>>>
>>> At least @ret will be base 10, so don't print that in base 16 without
>>> indication that it's not base 10.
>>>
>>>
>>> Also, this will result in two dev_err() prints, printing out two
>>> different error codes.
>>
>> Here i copy /past error managing from other drivers [1][2].
>
> No, both of these examples has a '#' between '%' and 'x', which will
> make the number be prefixed with 0x - making the base clear to the
> reader.
>
>> Should I make it different and use 2 dev_err?
>>
>
> What I mean is that here you're telling the user that there was an
> error, with one value of "ret". Then you continue below, will find ret
> with -EIO and then you will print another message saying that it failed
> and this time with -EIO.
>
> Why two prints?
>
>>
>> [1]
>> https://elixir.bootlin.com/linux/v6.12.1/source/drivers/firmware/arm_scmi/transports/optee.c#L244
>>
>> [2]
>> https://elixir.bootlin.com/linux/v6.12.1/source/drivers/firmware/arm_scmi/transports/optee.c#L244
>>
>>>
>>>> + ret = -EIO;
>>>> + }
>>>> +
>>>> +out:
>>>> + if (ret)
>>>> + /* Unexpected state without solution to come back in a stable state */
>>>> + dev_err(rproc_tee_ctx->dev, "Failed to release TEE remoteproc firmware: %d\n", ret);
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(rproc_tee_release_fw);
>>>> +
>
> Regards,
> Bjorn
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation
2025-02-12 3:54 ` Bjorn Andersson
@ 2025-02-12 13:48 ` Arnaud POULIQUEN
2025-03-04 15:23 ` Bjorn Andersson
0 siblings, 1 reply; 32+ messages in thread
From: Arnaud POULIQUEN @ 2025-02-12 13:48 UTC (permalink / raw)
To: Bjorn Andersson; +Cc: Mathieu Poirier, linux-remoteproc, linux-kernel
On 2/12/25 04:54, Bjorn Andersson wrote:
> On Tue, Dec 10, 2024 at 11:33:31AM +0100, Arnaud POULIQUEN wrote:
>>
>>
>> On 12/10/24 00:14, Bjorn Andersson wrote:
>>> On Thu, Nov 28, 2024 at 09:42:10AM GMT, Arnaud Pouliquen wrote:
>>>> This patch updates the rproc_ops structures to include two new optional
>>>> operations.
>>>>
>>>> - The load_fw() op is responsible for loading the remote processor
>>>> non-ELF firmware image before starting the boot sequence. This ops will
>>>> be used, for instance, to call OP-TEE to authenticate an load the firmware
>>>> image before accessing to its resources (a.e the resource table)
>>>>
>>>> - The release_fw op is responsible for releasing the remote processor
>>>> firmware image. For instance to clean memories.
>>>> The ops is called in the following cases:
>>>> - An error occurs between the loading of the firmware image and the
>>>> start of the remote processor.
>>>> - after stopping the remote processor.
>>>>
>>>
>>> Why does this difference need to be encoded in rproc_ops? I think we
>>> should strive for having a single, simple high level flow of operations
>>> through the remoteproc core for which the specifics of each remoteproc
>>> instance can be encoded in that driver.
>>>
>>>
>>> Perhaps there's a good reason for this, but if so please read and follow
>>> https://docs.kernel.org/process/submitting-patches.html#describe-your-changes
>>> to make that reasoning clear in the commit message.
>>>
>>
>> The actual sequence to load a remoteproc firmware is
>> - get firmware from file system and store the firmware image in Linux kernel memory
>
> This sounds like "load" in remoteproc terminology.
it is the request_firmware()
>
>> - get resource table from the firmware image and make a copy(
>> - parse the resource table and handle the resources
>
>> - load the firmware
>> - start the firmware
>
> And these two are "start".
yes done in rproc_start()
>
>>
>>
>> In OP-TEE we support not only one ELF image but n images (for instance a TF-M +
>> a zephyr), the segments can be encrypted the OP-TEE load sequence is
>> - copy header and meta data of the signed image in a secure memory
>> - verify it
>> - copy segments in remote processor memory and authenticate segments in place.
>> - optionally decrypt the segments
>>
>> Only at this step the resource table as been authenticated (and decrypted)
>
> "this step" meaning TA_RPROC_FW_CMD_LOAD_FW?
yes
>Above you say that happens
> after you parse the resource table.
The sequence above staring by "the actual sequence to load a remoteproc firmware
is" describes the existing legacy Linux kernel sequence without my series
>
>>
>> So the point is that we need to load the firmware before getting the resource table
>>
>>
>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>>>> ---
>>>> Update vs version V13:
>>>> - Rework the commit to introduce load_fw() op.
>>>> - remove rproc_release_fw() call from rproc_start() as called in
>>>> rproc_boot() and rproc_boot_recovery() in case of error.
>>>> - create rproc_load_fw() and rproc_release_fw() internal functions.
>>>> ---
>>>> drivers/remoteproc/remoteproc_core.c | 16 +++++++++++++++-
>>>> drivers/remoteproc/remoteproc_internal.h | 14 ++++++++++++++
>>>> include/linux/remoteproc.h | 6 ++++++
>>>> 3 files changed, 35 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
>>>> index ace11ea17097..8df4b2c59bb6 100644
>>>> --- a/drivers/remoteproc/remoteproc_core.c
>>>> +++ b/drivers/remoteproc/remoteproc_core.c
>>>> @@ -1488,6 +1488,7 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
>>>> kfree(rproc->cached_table);
>>>> rproc->cached_table = NULL;
>>>> rproc->table_ptr = NULL;
>>>> + rproc_release_fw(rproc);
>>>> unprepare_rproc:
>>>> /* release HW resources if needed */
>>>> rproc_unprepare_device(rproc);
>>>> @@ -1855,8 +1856,14 @@ static int rproc_boot_recovery(struct rproc *rproc)
>>>> return ret;
>>>> }
>>>>
>>>> + ret = rproc_load_fw(rproc, firmware_p);
>>>
>>> It is not clear to me why in the case of OP-TEE we need to invoke the
>>> "load operation" here, and in the case of "legacy" ELF loading we do it
>>> first thing in rproc_start() (i.e. on the very next line of code being
>>> executed).
>>
>> For the OP-TEE, please refer to my comment above.
>>
>> The only reason I can see for the legacy ELF is that the resource table could
>> contain information to be able to configure some resources to load the firmware.
>> In case of OP-TEE this would be managed in OP-TEE.
>>
>
> Sure, but as I say...inline rproc_start() here and the very next
> operation we perform is something we call "load".
>
> Why do we need to differentiate "load" and "load firmware", when we call
> them at the same step in the sequence?
A simplified sequence showing the implementation after this commit would be
1) request_firmware()
2) rproc_load_fw() (optional)
3) rproc_parse_fw()
4) rproc_handle_resources()
5) rproc_load_segments() (optional)
6) rproc->ops->start()
Here we introduce rproc_load_fw() because we need to load the firmware and
authenticate it before parsing it for the resource table calling rproc_parse_fw().
legacy Elf format sequence:
1) request_firmware()
3) rproc_parse_fw()
4) rproc_handle_resources()
5) rproc_load_segments()
6) rproc->ops->start()
Requested remoteproc TEE sequence
1) request_firmware()
2) rproc_load_fw()
3) rproc_parse_fw()
4) rproc_handle_resources()
6) rproc->ops->start()
>
>>>
>>>
>>> Should we start by renaming rproc_load_segments() rproc_load() and move
>>> it out of rproc_start()? (I.e. here?)
>>>
>>> Perhaps define that rproc_load() is responsible for "loading firmware"
>>> (whatever that means) and establishing rproc->cached_table, and
>>> rproc->table_ptr?
>>>
>>> (Note that this seems like a good cleanup of the spaghetti regardless)
>>>
>>
>> It's something that crossed my mind, but I don't know the legacy well enough to
>> guarantee that it will work in all drivers.
>>
>
> Looking at this again, if we move it, we need to duplicate it across a
> few different call sites. So might need to take another look at that.
>
> Still flatten the code and you seem to do:
>
> if (load_fw)
> load_fw();
> if (load)
> load();
>
> Why do we need two different things named "load".
Right, the terminology can be confusing, but both perform a load. We arrived at
this terminology with Mathieu, but if you have a suggestion, don't hesitate to
share.
>
>> If you want to go in this direction, perhaps this is something that could be
>> addressed in a dedicated pull request? In this case, the ops could become
>> load_fw and load_fw_new, similar to how it is done for platform_driver::remove.
>>
>
> No need to make it more complicated than it is, change the symbol name
> and fix the 3 places that calls the function.
>
>>
>>>> + if (ret)
>>>> + return ret;
>>>> +
>>>> /* boot the remote processor up again */
>>>> ret = rproc_start(rproc, firmware_p);
>>>> + if (ret)
>>>> + rproc_release_fw(rproc);
>>>
>>> The fact that you rproc_release_fw() in the error path here, right
>>> before we unconditionally release_firmware() the actual firmware means
>>> that you have 2 different life cycles with very very similar names.
>>>
>>> This will contain bugs, sooner or later.
>>
>> So we need to find a better way for the ops if we continue in this direction.
>> What about introducing rproc_load_new and rproc_release?
>>
>
> You need to help me understand why load and load_new are both needed.
>
> And no, "load_new" is not an ok name.
>
>>>
>>>>
>>>> release_firmware(firmware_p);
>>>>
>>>> @@ -1997,7 +2004,13 @@ int rproc_boot(struct rproc *rproc)
>>>> goto downref_rproc;
>>>> }
>>>>
>>>> + ret = rproc_load_fw(rproc, firmware_p);
>>>> + if (ret)
>>>> + goto downref_rproc;
>>>> +
>>>> ret = rproc_fw_boot(rproc, firmware_p);
>>>> + if (ret)
>>>> + rproc_release_fw(rproc);
>>>>
>>>> release_firmware(firmware_p);
>>>> }
>>>> @@ -2071,6 +2084,7 @@ int rproc_shutdown(struct rproc *rproc)
>>>> kfree(rproc->cached_table);
>>>> rproc->cached_table = NULL;
>>>> rproc->table_ptr = NULL;
>>>> + rproc_release_fw(rproc);
>>>> out:
>>>> mutex_unlock(&rproc->lock);
>>>> return ret;
>>>> @@ -2471,7 +2485,7 @@ static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
>>>> if (!rproc->ops->coredump)
>>>> rproc->ops->coredump = rproc_coredump;
>>>>
>>>> - if (rproc->ops->load)
>>>> + if (rproc->ops->load || rproc->ops->load_fw)
>>>> return 0;
>>>>
>>>> /* Default to ELF loader if no load function is specified */
>>>> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
>>>> index 0cd09e67ac14..2104ca449178 100644
>>>> --- a/drivers/remoteproc/remoteproc_internal.h
>>>> +++ b/drivers/remoteproc/remoteproc_internal.h
>>>> @@ -221,4 +221,18 @@ bool rproc_u64_fit_in_size_t(u64 val)
>>>> return (val <= (size_t) -1);
>>>> }
>>>>
>>>> +static inline void rproc_release_fw(struct rproc *rproc)
>>>> +{
>>>> + if (rproc->ops->release_fw)
>>>> + rproc->ops->release_fw(rproc);
>>>> +}
>>>> +
>>>> +static inline int rproc_load_fw(struct rproc *rproc, const struct firmware *fw)
>>>> +{
>>>> + if (rproc->ops->load_fw)
>>>> + return rproc->ops->load_fw(rproc, fw);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> #endif /* REMOTEPROC_INTERNAL_H */
>>>> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
>>>> index 2e0ddcb2d792..ba6fd560f7ba 100644
>>>> --- a/include/linux/remoteproc.h
>>>> +++ b/include/linux/remoteproc.h
>>>> @@ -381,6 +381,10 @@ enum rsc_handling_status {
>>>> * @panic: optional callback to react to system panic, core will delay
>>>> * panic at least the returned number of milliseconds
>>>> * @coredump: collect firmware dump after the subsystem is shutdown
>>>> + * @load_fw: optional function to load non-ELF firmware image to memory, where the remote
>>>> + * processor expects to find it.
>>>
>>> Why does it matter if it's an ELF or not?
>>
>> No matter. It was more to differentiate from the legacy one, but it does not
>> make sense and adds to the argument that the ops naming is not accurate.
>>
>
> I'm probably misunderstanding your intention here, but I can't help
> feeling that you add this code path to avoid understanding or fixing the
> existing code.
>
>>>
>>> In the Qualcomm case, firmware comes in ELF format, Linux loads the
>>> LOAD segments and the trusted world then authenticates the content and
>>> start the remote processor.
>>>
>>>
>>> I think the difference in your case is that you have memory reserved
>>> elsewhere, and you want the "load" operation to pass the firmware to the
>>> TEE - which means that you need rproc_release_fw() to eventually clean
>>> up the state if rproc_start() fails - and upon shutdown.
>>
>> Yes the OP-TEE is make more stuff:
>> - authenticate several firmware images
>
> Please tell me how that work. I'm not able to see the multiple calls to
> request_firmware()...
From a Linux perspective, it is only one firmware image to load.
The format defined in OP-TEE, available here [1], allows concatenation of
several firmware images into one signed binary image. For instance, for the
stm32mp2 platform, we can run a TF-M and a Zephyr firmware on the Cortex-M33.
[1]
https://github.com/OP-TEE/optee_os/blob/master/ta/remoteproc/src/remoteproc_core.c#L18
>
>> - decrypt images if encrypte
>
> This is done for some Qualcomm remoteprocs as well.
Do you decrypt before authenticating? On the stm32mp1, due to memory
constraints, we encrypt only the ELF segment to load. We compute the signature
based on the encrypted version of the ELF. So, we authenticate before decrypting."
>
>> - ensure that the load is done in granted memories
>
> At the top of your reply you say that you're loading the firmware into
> Linux memory, then you invoke TA_RPROC_FW_CMD_LOAD_FW to "load" it once
> more - presumably copying into some other memory.
Sorry if it was not clear. By 'loading the firmware into Linux memory' I mean
calling request_firmware() to get the image from the filesystem and copy it into
Linux-allocated memory. Then, the address of this memory is registered in OP-TEE
memory space and provided as a parameter of the TA_RPROC_FW_CMD_LOAD_FW command.
>
> It sounds like this is an optimization to fail early in the case that
> something is wrong?
Not only that, it is to ensure that everything is valid and decrypted before
enabling the parsing of the resource table (rproc_parse_fw()).
>
>> - manage the memory access rights to enure that the code and data memory
>> is never accessible by the Linux.
>>
>
> Right, after authentication Linux must be locked out. That's done in the
> Qualcomm "authenticate and start" phase, at the very end of the loading
> and setting things up.
Make sense, but not possible with the resource table handling.
>
>>>
>>> If we improve the definition of rproc_load_segments() to mean
>>> "remoteproc (or remoteproc driver) is loading segments", then in your
>>> case there's no "loading" operation in Linux. Instead you make that a
>>> nop and invoke LOAD_FW and START_FW within your start callback, then you
>>> can clean up the remnant state within your driver's start and stop
>>> callbacks - without complicating the core framework.
>>
>> This would not work as I need to load the firmware before calling
>> rproc_handle_resources().
Yes this is the blocking point. I suppose that you don't have this constraint in
Qualcomm implementations?
>>
>
> Ohh, now I see what you're saying. This is why you should follow
> https://docs.kernel.org/process/submitting-patches.html#describe-your-changes
> when you write a commit message. Then I would have understood your
> problem in the very first paragraph of the patch.
My apologize with the succession of versions and associated discussions. I
forgot that a series version must be self-sufficient in terms of explanation.
>
> This very much sounds like what's intended to happen in
> rproc_parse_fw(), per the comment right next to the call:
>
> /* Load resource table, core dump segment list etc from the firmware */
Yes, the main difference is that for ELF files, we get the resource table from
the ELF image and put it in rproc->cached_table. Then, the cached_table is
updated in rproc_handle_resources() and finally written to the destination
memory in rproc_start().
For the TEE implementation, we directly provide the destination memory address,
which is stored in rproc->cached_table.
Notice that the rproc->cached_table is mandatory for the recovery.
>
>
> But I'm guessing that would require loading the segments etc into the
> destination memory, perform the authentication and then you can get hold
> of the resource table?
>
> Which..per your patch you do before calling rproc_fw_boot(), so you're
> already diverging completely from the expected flow.
The call of rproc_load_fw() should be moved in rproc_fw_boot(), I guess
What would you suggest as the next step for these commits? Should I just rename
load_fw to release_fw? If so, do you have a naming suggestion?
Or do we need to find another way to sequence the boot?
Thanks,
Arnaud
>
>> I can not use rproc_prepare_device() as it is not called on recovery
>>
>
> The purpose of rproc_prepare_device() is to ensure that any clocks etc
> for the memory where we're going to load the firmware is enabled, so
> that doesn't sounds like the right place.
>
> Regards,
> Bjorn
>
>> Thanks,
>> Arnaud
>>
>>>
>>> Regards,
>>> Bjorn
>>>
>>>> + * @release_fw: optional function to release the firmware image from memories.
>>>> + * This function is called after stopping the remote processor or in case of error
>>>> */
>>>> struct rproc_ops {
>>>> int (*prepare)(struct rproc *rproc);
>>>> @@ -403,6 +407,8 @@ struct rproc_ops {
>>>> u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
>>>> unsigned long (*panic)(struct rproc *rproc);
>>>> void (*coredump)(struct rproc *rproc);
>>>> + int (*load_fw)(struct rproc *rproc, const struct firmware *fw);
>>>> + void (*release_fw)(struct rproc *rproc);
>>>> };
>>>>
>>>> /**
>>>> --
>>>> 2.25.1
>>>>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation
2025-02-12 13:48 ` Arnaud POULIQUEN
@ 2025-03-04 15:23 ` Bjorn Andersson
2025-03-05 12:50 ` Arnaud POULIQUEN
0 siblings, 1 reply; 32+ messages in thread
From: Bjorn Andersson @ 2025-03-04 15:23 UTC (permalink / raw)
To: Arnaud POULIQUEN; +Cc: Mathieu Poirier, linux-remoteproc, linux-kernel
On Wed, Feb 12, 2025 at 02:48:30PM +0100, Arnaud POULIQUEN wrote:
>
>
> On 2/12/25 04:54, Bjorn Andersson wrote:
> > On Tue, Dec 10, 2024 at 11:33:31AM +0100, Arnaud POULIQUEN wrote:
> >>
> >>
> >> On 12/10/24 00:14, Bjorn Andersson wrote:
> >>> On Thu, Nov 28, 2024 at 09:42:10AM GMT, Arnaud Pouliquen wrote:
> >>>> This patch updates the rproc_ops structures to include two new optional
> >>>> operations.
> >>>>
> >>>> - The load_fw() op is responsible for loading the remote processor
> >>>> non-ELF firmware image before starting the boot sequence. This ops will
> >>>> be used, for instance, to call OP-TEE to authenticate an load the firmware
> >>>> image before accessing to its resources (a.e the resource table)
> >>>>
> >>>> - The release_fw op is responsible for releasing the remote processor
> >>>> firmware image. For instance to clean memories.
> >>>> The ops is called in the following cases:
> >>>> - An error occurs between the loading of the firmware image and the
> >>>> start of the remote processor.
> >>>> - after stopping the remote processor.
> >>>>
> >>>
> >>> Why does this difference need to be encoded in rproc_ops? I think we
> >>> should strive for having a single, simple high level flow of operations
> >>> through the remoteproc core for which the specifics of each remoteproc
> >>> instance can be encoded in that driver.
> >>>
> >>>
> >>> Perhaps there's a good reason for this, but if so please read and follow
> >>> https://docs.kernel.org/process/submitting-patches.html#describe-your-changes
> >>> to make that reasoning clear in the commit message.
> >>>
> >>
> >> The actual sequence to load a remoteproc firmware is
> >> - get firmware from file system and store the firmware image in Linux kernel memory
> >
> > This sounds like "load" in remoteproc terminology.
>
> it is the request_firmware()
>
> >
> >> - get resource table from the firmware image and make a copy(
> >> - parse the resource table and handle the resources
> >
> >> - load the firmware
> >> - start the firmware
> >
> > And these two are "start".
>
> yes done in rproc_start()
>
> >
> >>
> >>
> >> In OP-TEE we support not only one ELF image but n images (for instance a TF-M +
> >> a zephyr), the segments can be encrypted the OP-TEE load sequence is
> >> - copy header and meta data of the signed image in a secure memory
> >> - verify it
> >> - copy segments in remote processor memory and authenticate segments in place.
> >> - optionally decrypt the segments
> >>
> >> Only at this step the resource table as been authenticated (and decrypted)
> >
> > "this step" meaning TA_RPROC_FW_CMD_LOAD_FW?
>
> yes
>
> >Above you say that happens
> > after you parse the resource table.
>
> The sequence above staring by "the actual sequence to load a remoteproc firmware
> is" describes the existing legacy Linux kernel sequence without my series
>
It's useful not to call this "the legacy sequence", because it's a
sequence that all other cases will continue to use. The task at hand is
to determine a sequence that (at some abstraction level) captures the
different sequences that we need to support.
> >
> >>
> >> So the point is that we need to load the firmware before getting the resource table
> >>
> >>
> >>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> >>>> ---
> >>>> Update vs version V13:
> >>>> - Rework the commit to introduce load_fw() op.
> >>>> - remove rproc_release_fw() call from rproc_start() as called in
> >>>> rproc_boot() and rproc_boot_recovery() in case of error.
> >>>> - create rproc_load_fw() and rproc_release_fw() internal functions.
> >>>> ---
> >>>> drivers/remoteproc/remoteproc_core.c | 16 +++++++++++++++-
> >>>> drivers/remoteproc/remoteproc_internal.h | 14 ++++++++++++++
> >>>> include/linux/remoteproc.h | 6 ++++++
> >>>> 3 files changed, 35 insertions(+), 1 deletion(-)
> >>>>
> >>>> diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
> >>>> index ace11ea17097..8df4b2c59bb6 100644
> >>>> --- a/drivers/remoteproc/remoteproc_core.c
> >>>> +++ b/drivers/remoteproc/remoteproc_core.c
> >>>> @@ -1488,6 +1488,7 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
> >>>> kfree(rproc->cached_table);
> >>>> rproc->cached_table = NULL;
> >>>> rproc->table_ptr = NULL;
> >>>> + rproc_release_fw(rproc);
> >>>> unprepare_rproc:
> >>>> /* release HW resources if needed */
> >>>> rproc_unprepare_device(rproc);
> >>>> @@ -1855,8 +1856,14 @@ static int rproc_boot_recovery(struct rproc *rproc)
> >>>> return ret;
> >>>> }
> >>>>
> >>>> + ret = rproc_load_fw(rproc, firmware_p);
> >>>
> >>> It is not clear to me why in the case of OP-TEE we need to invoke the
> >>> "load operation" here, and in the case of "legacy" ELF loading we do it
> >>> first thing in rproc_start() (i.e. on the very next line of code being
> >>> executed).
> >>
> >> For the OP-TEE, please refer to my comment above.
> >>
> >> The only reason I can see for the legacy ELF is that the resource table could
> >> contain information to be able to configure some resources to load the firmware.
> >> In case of OP-TEE this would be managed in OP-TEE.
> >>
> >
> > Sure, but as I say...inline rproc_start() here and the very next
> > operation we perform is something we call "load".
> >
> > Why do we need to differentiate "load" and "load firmware", when we call
> > them at the same step in the sequence?
>
> A simplified sequence showing the implementation after this commit would be
>
> 1) request_firmware()
> 2) rproc_load_fw() (optional)
> 3) rproc_parse_fw()
> 4) rproc_handle_resources()
> 5) rproc_load_segments() (optional)
> 6) rproc->ops->start()
>
> Here we introduce rproc_load_fw() because we need to load the firmware and
> authenticate it before parsing it for the resource table calling rproc_parse_fw().
>
Per your explanation, I think what you're saying is that before
rproc_handle_resources() we must load and authenticate the resource
table. The loading of ELF segments is more a side effect of the firmware
being processed by OP-TEE at this point (I'm not saying that you must
implement rproc_load_segments() here)
> legacy Elf format sequence:
> 1) request_firmware()
> 3) rproc_parse_fw()
> 4) rproc_handle_resources()
> 5) rproc_load_segments()
> 6) rproc->ops->start()
>
>
> Requested remoteproc TEE sequence
> 1) request_firmware()
> 2) rproc_load_fw()
> 3) rproc_parse_fw()
> 4) rproc_handle_resources()
> 6) rproc->ops->start()
>
>
> >
> >>>
> >>>
> >>> Should we start by renaming rproc_load_segments() rproc_load() and move
> >>> it out of rproc_start()? (I.e. here?)
> >>>
> >>> Perhaps define that rproc_load() is responsible for "loading firmware"
> >>> (whatever that means) and establishing rproc->cached_table, and
> >>> rproc->table_ptr?
> >>>
> >>> (Note that this seems like a good cleanup of the spaghetti regardless)
> >>>
> >>
> >> It's something that crossed my mind, but I don't know the legacy well enough to
> >> guarantee that it will work in all drivers.
> >>
> >
> > Looking at this again, if we move it, we need to duplicate it across a
> > few different call sites. So might need to take another look at that.
> >
> > Still flatten the code and you seem to do:
> >
> > if (load_fw)
> > load_fw();
> > if (load)
> > load();
> >
> > Why do we need two different things named "load".
>
> Right, the terminology can be confusing, but both perform a load. We arrived at
> this terminology with Mathieu, but if you have a suggestion, don't hesitate to
> share.
>
If the terminology is confusing, then it's a bad API.
If you clearly define that resource table needs to be
decrypted/authenticated before rproc_handle_resources() then it's not so
confusing.
> >
> >> If you want to go in this direction, perhaps this is something that could be
> >> addressed in a dedicated pull request? In this case, the ops could become
> >> load_fw and load_fw_new, similar to how it is done for platform_driver::remove.
> >>
> >
> > No need to make it more complicated than it is, change the symbol name
> > and fix the 3 places that calls the function.
> >
> >>
> >>>> + if (ret)
> >>>> + return ret;
> >>>> +
> >>>> /* boot the remote processor up again */
> >>>> ret = rproc_start(rproc, firmware_p);
> >>>> + if (ret)
> >>>> + rproc_release_fw(rproc);
> >>>
> >>> The fact that you rproc_release_fw() in the error path here, right
> >>> before we unconditionally release_firmware() the actual firmware means
> >>> that you have 2 different life cycles with very very similar names.
> >>>
> >>> This will contain bugs, sooner or later.
> >>
> >> So we need to find a better way for the ops if we continue in this direction.
> >> What about introducing rproc_load_new and rproc_release?
> >>
> >
> > You need to help me understand why load and load_new are both needed.
> >
> > And no, "load_new" is not an ok name.
> >
> >>>
> >>>>
> >>>> release_firmware(firmware_p);
> >>>>
> >>>> @@ -1997,7 +2004,13 @@ int rproc_boot(struct rproc *rproc)
> >>>> goto downref_rproc;
> >>>> }
> >>>>
> >>>> + ret = rproc_load_fw(rproc, firmware_p);
> >>>> + if (ret)
> >>>> + goto downref_rproc;
> >>>> +
> >>>> ret = rproc_fw_boot(rproc, firmware_p);
> >>>> + if (ret)
> >>>> + rproc_release_fw(rproc);
> >>>>
> >>>> release_firmware(firmware_p);
> >>>> }
> >>>> @@ -2071,6 +2084,7 @@ int rproc_shutdown(struct rproc *rproc)
> >>>> kfree(rproc->cached_table);
> >>>> rproc->cached_table = NULL;
> >>>> rproc->table_ptr = NULL;
> >>>> + rproc_release_fw(rproc);
> >>>> out:
> >>>> mutex_unlock(&rproc->lock);
> >>>> return ret;
> >>>> @@ -2471,7 +2485,7 @@ static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
> >>>> if (!rproc->ops->coredump)
> >>>> rproc->ops->coredump = rproc_coredump;
> >>>>
> >>>> - if (rproc->ops->load)
> >>>> + if (rproc->ops->load || rproc->ops->load_fw)
> >>>> return 0;
> >>>>
> >>>> /* Default to ELF loader if no load function is specified */
> >>>> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
> >>>> index 0cd09e67ac14..2104ca449178 100644
> >>>> --- a/drivers/remoteproc/remoteproc_internal.h
> >>>> +++ b/drivers/remoteproc/remoteproc_internal.h
> >>>> @@ -221,4 +221,18 @@ bool rproc_u64_fit_in_size_t(u64 val)
> >>>> return (val <= (size_t) -1);
> >>>> }
> >>>>
> >>>> +static inline void rproc_release_fw(struct rproc *rproc)
> >>>> +{
> >>>> + if (rproc->ops->release_fw)
> >>>> + rproc->ops->release_fw(rproc);
> >>>> +}
> >>>> +
> >>>> +static inline int rproc_load_fw(struct rproc *rproc, const struct firmware *fw)
> >>>> +{
> >>>> + if (rproc->ops->load_fw)
> >>>> + return rproc->ops->load_fw(rproc, fw);
> >>>> +
> >>>> + return 0;
> >>>> +}
> >>>> +
> >>>> #endif /* REMOTEPROC_INTERNAL_H */
> >>>> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
> >>>> index 2e0ddcb2d792..ba6fd560f7ba 100644
> >>>> --- a/include/linux/remoteproc.h
> >>>> +++ b/include/linux/remoteproc.h
> >>>> @@ -381,6 +381,10 @@ enum rsc_handling_status {
> >>>> * @panic: optional callback to react to system panic, core will delay
> >>>> * panic at least the returned number of milliseconds
> >>>> * @coredump: collect firmware dump after the subsystem is shutdown
> >>>> + * @load_fw: optional function to load non-ELF firmware image to memory, where the remote
> >>>> + * processor expects to find it.
> >>>
> >>> Why does it matter if it's an ELF or not?
> >>
> >> No matter. It was more to differentiate from the legacy one, but it does not
> >> make sense and adds to the argument that the ops naming is not accurate.
> >>
> >
> > I'm probably misunderstanding your intention here, but I can't help
> > feeling that you add this code path to avoid understanding or fixing the
> > existing code.
> >
> >>>
> >>> In the Qualcomm case, firmware comes in ELF format, Linux loads the
> >>> LOAD segments and the trusted world then authenticates the content and
> >>> start the remote processor.
> >>>
> >>>
> >>> I think the difference in your case is that you have memory reserved
> >>> elsewhere, and you want the "load" operation to pass the firmware to the
> >>> TEE - which means that you need rproc_release_fw() to eventually clean
> >>> up the state if rproc_start() fails - and upon shutdown.
> >>
> >> Yes the OP-TEE is make more stuff:
> >> - authenticate several firmware images
> >
> > Please tell me how that work. I'm not able to see the multiple calls to
> > request_firmware()...
>
> From a Linux perspective, it is only one firmware image to load.
>
> The format defined in OP-TEE, available here [1], allows concatenation of
> several firmware images into one signed binary image. For instance, for the
> stm32mp2 platform, we can run a TF-M and a Zephyr firmware on the Cortex-M33.
>
Okay, that makes sense
E.g. the ELF files loaded by the Qualcomm remoteproc drivers contain any
number of ELF segments. Each remoteproc as one entity in the view of
Linux, but Linux has no awareness of the internal hardware
configuration.
But there are also cases in the Qualcomm space where multiple firmware
files are loaded for a single remoteproc - something which is rather
messy.
> [1]
> https://github.com/OP-TEE/optee_os/blob/master/ta/remoteproc/src/remoteproc_core.c#L18
>
>
> >
> >> - decrypt images if encrypte
> >
> > This is done for some Qualcomm remoteprocs as well.
>
> Do you decrypt before authenticating? On the stm32mp1, due to memory
> constraints, we encrypt only the ELF segment to load. We compute the signature
> based on the encrypted version of the ELF. So, we authenticate before decrypting."
>
I actually don't know, both operations are performed by the secure
world.
But authenticating the encrypted data makes sense to me.
> >
> >> - ensure that the load is done in granted memories
> >
> > At the top of your reply you say that you're loading the firmware into
> > Linux memory, then you invoke TA_RPROC_FW_CMD_LOAD_FW to "load" it once
> > more - presumably copying into some other memory.
>
> Sorry if it was not clear. By 'loading the firmware into Linux memory' I mean
> calling request_firmware() to get the image from the filesystem and copy it into
> Linux-allocated memory. Then, the address of this memory is registered in OP-TEE
> memory space and provided as a parameter of the TA_RPROC_FW_CMD_LOAD_FW command.
>
Okay, this is an important difference. In the Qualcomm case Linux will
load the ELF segments and then at "authenticate and start" memory
management tricks will be played to lock Linux out of the memory while
the remoteproc is running.
In your case you effectively have "external" pre-allocated memory for
your remoteproc instance - and TA_RPROC_FW_CMD_LOAD_FW is the mechanism
used to populate it _and_ decode the resource table.
> >
> > It sounds like this is an optimization to fail early in the case that
> > something is wrong?
>
> Not only that, it is to ensure that everything is valid and decrypted before
> enabling the parsing of the resource table (rproc_parse_fw()).
>
But correct me if I'm wrong, there's no strong reason for why the
non-resource-table segments needs to be decrypted at this point, right?
(I acknowledge that it makes sense to do it in one go, just trying to
understand)
> >
> >> - manage the memory access rights to enure that the code and data memory
> >> is never accessible by the Linux.
> >>
> >
> > Right, after authentication Linux must be locked out. That's done in the
> > Qualcomm "authenticate and start" phase, at the very end of the loading
> > and setting things up.
>
> Make sense, but not possible with the resource table handling.
>
Correct, if the resource table needs to be authenticated and/or
decrypted this needs to happen at an earlier point.
> >
> >>>
> >>> If we improve the definition of rproc_load_segments() to mean
> >>> "remoteproc (or remoteproc driver) is loading segments", then in your
> >>> case there's no "loading" operation in Linux. Instead you make that a
> >>> nop and invoke LOAD_FW and START_FW within your start callback, then you
> >>> can clean up the remnant state within your driver's start and stop
> >>> callbacks - without complicating the core framework.
> >>
> >> This would not work as I need to load the firmware before calling
> >> rproc_handle_resources().
>
> Yes this is the blocking point. I suppose that you don't have this constraint in
> Qualcomm implementations?
>
Correct, as Linux is being locked out of the memory while the remoteproc
is running the "carveout"-style resources are defined using no-map
reserved-memory regions, and IPC is described in Devicetree (as there's
both consumers and providers for other Devicetree entries there).
But there's been some prototyping in the past related to utilizing the
resource table, at which point this would become a "problem".
> >>
> >
> > Ohh, now I see what you're saying. This is why you should follow
> > https://docs.kernel.org/process/submitting-patches.html#describe-your-changes
> > when you write a commit message. Then I would have understood your
> > problem in the very first paragraph of the patch.
>
> My apologize with the succession of versions and associated discussions. I
> forgot that a series version must be self-sufficient in terms of explanation.
>
> >
> > This very much sounds like what's intended to happen in
> > rproc_parse_fw(), per the comment right next to the call:
> >
> > /* Load resource table, core dump segment list etc from the firmware */
>
> Yes, the main difference is that for ELF files, we get the resource table from
> the ELF image and put it in rproc->cached_table. Then, the cached_table is
> updated in rproc_handle_resources() and finally written to the destination
> memory in rproc_start().
>
> For the TEE implementation, we directly provide the destination memory address,
> which is stored in rproc->cached_table.
>
I presume you mean, you call TA_RPROC_FW_CMD_LOAD_FW which will decode
the resource table and you then request the address of the loaded and
decoded resource table from TA_RPROC?
> Notice that the rproc->cached_table is mandatory for the recovery.
>
> >
> >
> > But I'm guessing that would require loading the segments etc into the
> > destination memory, perform the authentication and then you can get hold
> > of the resource table?
> >
> > Which..per your patch you do before calling rproc_fw_boot(), so you're
> > already diverging completely from the expected flow.
>
> The call of rproc_load_fw() should be moved in rproc_fw_boot(), I guess
>
> What would you suggest as the next step for these commits? Should I just rename
> load_fw to release_fw? If so, do you have a naming suggestion?
>
> Or do we need to find another way to sequence the boot?
>
I think it's clear from this discussion that you need a way to load,
authenticate and decrypt the resource table before
rproc_handle_resources().
It's also clear that "freeing" the resource table can no longer be done
with just a call to kfree().
I also find it conceivable that someone would have the desire to
authenticate the resource table before rproc_handle_resources() and then
use rproc_load_segments() to load the firmware content (followed by
another round of authentication in start()).
So if we've established that what you're looking for is a
driver-specific way to get hold of the resource table and that's the
reason for your modifications, then that should fit pretty well into the
concept of rproc_parse_fw().
You have a non-standard optimization in that in that same operation you
store the rest of the firmware in the non-Linux side, to avoid having to
call load_segment separately (but there doesn't seem to be any clear
reason for the segments to be loaded at this time (perhaps you encrypt
the whole image and don't have segments?)).
rproc_load_segments() is already optional, so no change is needed there.
We need to abstract out the kfree(rproc->cached_table) so that this can
also release the resource table resources and secure state that you
established during rproc_parse_fw().
Regards,
Bjorn
> Thanks,
> Arnaud
>
> >
> >> I can not use rproc_prepare_device() as it is not called on recovery
> >>
> >
> > The purpose of rproc_prepare_device() is to ensure that any clocks etc
> > for the memory where we're going to load the firmware is enabled, so
> > that doesn't sounds like the right place.
> >
> > Regards,
> > Bjorn
> >
> >> Thanks,
> >> Arnaud
> >>
> >>>
> >>> Regards,
> >>> Bjorn
> >>>
> >>>> + * @release_fw: optional function to release the firmware image from memories.
> >>>> + * This function is called after stopping the remote processor or in case of error
> >>>> */
> >>>> struct rproc_ops {
> >>>> int (*prepare)(struct rproc *rproc);
> >>>> @@ -403,6 +407,8 @@ struct rproc_ops {
> >>>> u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
> >>>> unsigned long (*panic)(struct rproc *rproc);
> >>>> void (*coredump)(struct rproc *rproc);
> >>>> + int (*load_fw)(struct rproc *rproc, const struct firmware *fw);
> >>>> + void (*release_fw)(struct rproc *rproc);
> >>>> };
> >>>>
> >>>> /**
> >>>> --
> >>>> 2.25.1
> >>>>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 2/8] remoteproc: Add TEE support
2025-02-12 13:42 ` Arnaud POULIQUEN
@ 2025-03-04 15:58 ` Bjorn Andersson
2025-03-05 12:48 ` Arnaud POULIQUEN
0 siblings, 1 reply; 32+ messages in thread
From: Bjorn Andersson @ 2025-03-04 15:58 UTC (permalink / raw)
To: Arnaud POULIQUEN; +Cc: Mathieu Poirier, linux-kernel, linux-remoteproc
On Wed, Feb 12, 2025 at 02:42:28PM +0100, Arnaud POULIQUEN wrote:
> Hello,
>
> On 2/12/25 04:18, Bjorn Andersson wrote:
> > On Tue, Dec 10, 2024 at 09:57:40AM +0100, Arnaud POULIQUEN wrote:
> >> Hello Bjorn,
> >>
> >> On 12/6/24 23:07, Bjorn Andersson wrote:
> >>> On Thu, Nov 28, 2024 at 09:42:09AM GMT, Arnaud Pouliquen wrote:
> >>>> Add a remoteproc TEE (Trusted Execution Environment) driver
> >>>> that will be probed by the TEE bus. If the associated Trusted
> >>>> application is supported on secure part this driver offers a client
> >>>> interface to load a firmware by the secure part.
> >>>
> >>> If...else?
> >>>
> >>>> This firmware could be authenticated by the secure trusted application.
> >>>>
> >>>
> >>> I would like for this to fully describe how this fits with the bus and
> >> Are you speaking about the OP-TEE bus?
> >>
> >> I assume that your attempt is that I provide more details on the live cycle
> >> sequence, right?
> >>
> >
> > Right, there's a tee_client_driver and there's a remoteproc driver.
> > Let's document clearly how these interact.
> >
> >>> how it is expected to be used by a specific remoteproc driver.
> >>>
> >>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> >>>> ---
> >>>> Updates vs version v13:
> >>>> - define REMOTEPROC_TEE as bool instead of tristate,
> >>>> - remove the load of the firmware in rproc_tee_parse_fw as we will ensure
> >>>> that the firmware is loaded using the load_fw() operation.
> >>>> ---
> >>>> drivers/remoteproc/Kconfig | 10 +
> >>>> drivers/remoteproc/Makefile | 1 +
> >>>> drivers/remoteproc/remoteproc_tee.c | 508 ++++++++++++++++++++++++++++
> >>>> include/linux/remoteproc.h | 4 +
> >>>> include/linux/remoteproc_tee.h | 105 ++++++
> >>>> 5 files changed, 628 insertions(+)
> >>>> create mode 100644 drivers/remoteproc/remoteproc_tee.c
> >>>> create mode 100644 include/linux/remoteproc_tee.h
> >>>>
> >>>> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> >>>> index 955e4e38477e..f6335321d540 100644
> >>>> --- a/drivers/remoteproc/Kconfig
> >>>> +++ b/drivers/remoteproc/Kconfig
> >>>> @@ -23,6 +23,16 @@ config REMOTEPROC_CDEV
> >>>>
> >>>> It's safe to say N if you don't want to use this interface.
> >>>>
> >>>> +config REMOTEPROC_TEE
> >>>> + bool "Remoteproc support by a TEE application"
> >>>> + depends on OPTEE
> >>>> + help
> >>>> + Support a remote processor with a TEE application.
> >>>
> >>> Does the remote processor run TEE applications? (Rethorical question...)
> >>>
> >>>> The Trusted
> >>>> + Execution Context is responsible for loading the trusted firmware
> >>>> + image and managing the remote processor's lifecycle.
> >>>> +
> >>>> + It's safe to say N if you don't want to use remoteproc TEE.
> >>>
> >>> It's not really about "wanting to use", it's a question whether your
> >>> device implements/provides the remoteproc TEE.
> >>>
> >>>> +
> >>>> config IMX_REMOTEPROC
> >>>> tristate "i.MX remoteproc support"
> >>>> depends on ARCH_MXC
> >>>> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
> >>>> index 5ff4e2fee4ab..f77e0abe8349 100644
> >>>> --- a/drivers/remoteproc/Makefile
> >>>> +++ b/drivers/remoteproc/Makefile
> >>>> @@ -11,6 +11,7 @@ remoteproc-y += remoteproc_sysfs.o
> >>>> remoteproc-y += remoteproc_virtio.o
> >>>> remoteproc-y += remoteproc_elf_loader.o
> >>>> obj-$(CONFIG_REMOTEPROC_CDEV) += remoteproc_cdev.o
> >>>> +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o
> >>>> obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o
> >>>> obj-$(CONFIG_IMX_DSP_REMOTEPROC) += imx_dsp_rproc.o
> >>>> obj-$(CONFIG_INGENIC_VPU_RPROC) += ingenic_rproc.o
> >>>> diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c
> >>>> new file mode 100644
> >>>> index 000000000000..3fe3f31068f2
> >>>> --- /dev/null
> >>>> +++ b/drivers/remoteproc/remoteproc_tee.c
> >>>> @@ -0,0 +1,508 @@
> >>>> +// SPDX-License-Identifier: GPL-2.0-or-later
> >>>> +/*
> >>>> + * Copyright (C) STMicroelectronics 2024
> >>>> + * Author: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> >>>> + */
> >>>> +
> >>>> +#include <linux/firmware.h>
> >>>> +#include <linux/io.h>
> >>>> +#include <linux/module.h>
> >>>> +#include <linux/remoteproc.h>
> >>>> +#include <linux/remoteproc_tee.h>
> >>>> +#include <linux/slab.h>
> >>>> +#include <linux/tee_drv.h>
> >>>> +
> >>>> +#define MAX_TEE_PARAM_ARRAY_MEMBER 4
> >>>> +
> >>>> +/*
> >>>> + * Authentication of the firmware and load in the remote processor memory
> >>>
> >>> Exactly what does this imply? Will the content of @memref be copied into
> >>> some other memory?
> >>
> >> The objective is to authenticate and load in one step. So, yes, the image is
> >> loaded into the remoteproc destination memory.
> >>
> >
> > So, some separate device-memory, or some preallocated carveout which is
> > only accessible from secure world?
>
> No sure to understand the difference you do between eparate device-memory and
> preallocated carveout.
>
The main clarification I was looking for was that in your design you
don't use any resources on the Linux side for when your remoteproc
instance is running - i.e. no carveouts etc on the Linux side.
> In OP-TEE, we use the same principle as in Linux. OP-TEE uses memory regions
> declared in its device tree to list memories usable for the coprocessor (with
> associated access rights). On load, it checks that the segments to load are
> included in these memory regions.
>
> Linux only declares the shared memory-regions in the device tree, for IPC.
>
> >
> > Does the OS need to retain @memref past this point?
>
> No need,and as the area contains the reult of request_firmware() that can be
> corrupted by Linux, OP-TEE considered this as a temporaray unsafe memory. After
> the load + authentication step this buffer is no more used.
> For detail, OPTEE make a copy of the header and TLV (metadata) in a secure
> memory. and load the firmware images in destination memories All these memories
> are not accessible from the Linux.
>
No concerns with this, but these semantics should be clearly documented
here.
> >
> >> On stm32mp1 we can not store the elf file in a temporary secure memory as
> >> the memory is encrypted by software (this would take to much time).
> >>
> >> For your information, in OP-TEE, the application code is split into a generic
> >> part and a platform adaptation layer. The generic application is mainly
> >> responsible for:
> >>
> >> - Copying the binary header and metadata into secure memory and authenticating them.
> >> - Parsing the ELF images and providing segments to load with associated
> >> authenticated hashes to the platform application.
> >> In the future, someone can add their own format if needed.
> >>
> >> But the generic part could be enhance to authenticate and load a non ELF binary.
> >> So I'm trying to be generic as possible here.
> >>
> >
> > Generic might be okay, but I'd prefer this to be less vague.
> > Also worth noting is the Qualcomm implementation of TZ-backed
> > remoteproc, which is already in the tree.
>
> Could you point me the code in Linux and your TEE, please?
>
One example is drivers/remoteproc/qcom_q6v5_pas.c where this is captured
in adsp_start().
qcom_mdt_pas_init() parses out the ELF header and signature information
and passes this to the secure world, it then loads the segments of the
ELF into the carvouts (qcom_mdt_load_no_init()) and finally it jumps to
secure world with qcom_scm_pas_auth_and_reset(), which will lock down
Linux's access to the carveouts, then based on previously established
data will authenticate the loaded firmware and finally start execution
on the remote processor.
The difference in this model though is that we don't need the resource
table for rproc_handle_resources() - so this doesn't meet your needs.
> > There the firmware is loaded
> > into carveouts, the certificates and hashes are validated.
>
> Seems to me that there is also a partial Authentication done during the load step.
>
Given that the ELF header and signature information is vetted before the
actually copy the segments into carveouts, it's conceivable that the ELF
header could be sanity checked...
> > Lastly
> > the operation "authenticate and start" is invoked, which does that, and
> > locks the OS out of the given memory region - until "shutdown" is
> > invoked.
>
> The differnce between the 2 implementations is the authentication method done in
> 2 steps for Qualcomm implementation , in one step for OP-TEE.
>
Yes, but it needs to be pointed out that this is because you want the
resource table to be authenticated.
> So here if I just remove the term 'authentication' in the command description
> does it ok for you?
>
No, perhaps I'm misinterpreting you here; but the goal isn't to play
word games until it's good enough - the goal is to have a clean design
that will cover the various cases, and for that we need to establish
what your actual requirements on the host OS side is (typically while
considering the "other side" to be a black box).
> >
> >>
> >>>
> >>>> + *
> >>>> + * [in] params[0].value.a: unique 32bit identifier of the remote processor
> >>>
> >>> Why not just "remote processor identifier"?
> >>>
> >>>> + * [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
> >>>
> >>> Why is there two "FW" in this constant? Why isn't it just
> >>> "TA_RPROC_FW_CMD_START"?
> >>>
> >>> And why is it not TEE_PROC_FW_CMD_START?
> >>>
> >>>> +
> >>>> +/*
> >>>> + * 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.
> >>>
> >>> These three lines describe a scenario that doesn't make any sense to me.
> >>> But if that's the case, you should be able to describe that the API
> >>> might give you a inaccessible pointer, by design.
> >>
> >> On STM32MP, we have a kind of firewall in OP-TEE that sets memory access rights
> >> from the device tree. So if the firmware image is not linked according to the
> >> firewall configuration, the pointer may not be accessible.
> >>
> >> I will update the comment as you propose.
> >>
> >>>
> >>>> + *
> >>>> + * [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
> >>>
> >>> What does this mean? What will I find at @memref after this call?
> >>
> >> I do not have a simple answer here as it depends on the OP-TEE strategy.
> >> It could be an obscure core dump with possible encryption.
> >>
> >> I will remove this as it is not yet implemented in OP-TEE.
> >>
> >
> > Okay. But I would prefer that we define the semantics before it's
> > implemented...
>
> that seems fair, I notice that we will have to address this in a separate thread
> strating with a series in Linux.
>
>
> >
> >> https://elixir.bootlin.com/op-tee/4.4.0/source/ta/remoteproc/src/remoteproc_core.c#L1131
> >>
> >>>
> >>>> + *
> >>>> + * [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
> >>>
> >>> s/else return Null/or NULL/
> >>>
> >>>> + */
> >>>> +#define TA_RPROC_FW_CMD_GET_COREDUMP 5
> >>>> +
> >>>> +/*
> >>>> + * Release remote processor firmware images and associated resources.
> >>>
> >>> Exactly what does this mean for the caller?
> >>
> >> It is platform dependent. It can consist in cleaning the memory, but
> >> can be also something else such as firewall configuration.
> >> On stm323mp we clean all the memories region reserved for the remote processor.
> >>
> >
> > We can't have an ABI which isn't well defined in intent. Your examples
> > would easily fall in the realm of a well defined interface, but this
> > ties into the question above - what does is actually mean in terms of
> > the memory carveouts and such.
> >
>
> Regarding this comment and the one below, does following description would
> respond to your expectations? else do you have a suggestion?
>
> /*
> * This command should be used in case an error occurs between the loading of
> * the firmware images (TA_RPROC_CMD_LOAD_FW) and the starting of the remote
> * processor (TA_RPROC_CMD_START_FW),
This is valuable information related to TA_RPROC_CMD_LOAD_FW and
TA_RPROC_CMD_START_FW, so let's document this there instead.
> * or after stopping the remote processor
> * (TA_RPROC_CMD_STOP_FW).
> *
> * This command is used to inform the TEE (Trusted Execution Environment) that
> * resources associated with the remote processor can be released. After this
> * command, the firmware is no longer present in the remote processor's memories
> * and must be reloaded (TA_RPROC_FW_CMD_LOAD_FW) to restart the remote
> * processor.
I guess it's fine to define it like this on this level, but in the
remoteproc core I'd like us to express the related logic as "release
resources allocated durign rproc_parse_fw(). (And I don't think these
two interpretations are in conflict).
What's unexpected to me then is that you're not actually reloading your
firmware across a recovery/restart?
Regards,
Bjorn
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 2/8] remoteproc: Add TEE support
2025-03-04 15:58 ` Bjorn Andersson
@ 2025-03-05 12:48 ` Arnaud POULIQUEN
0 siblings, 0 replies; 32+ messages in thread
From: Arnaud POULIQUEN @ 2025-03-05 12:48 UTC (permalink / raw)
To: Bjorn Andersson; +Cc: Mathieu Poirier, linux-kernel, linux-remoteproc
On 3/4/25 16:58, Bjorn Andersson wrote:
> On Wed, Feb 12, 2025 at 02:42:28PM +0100, Arnaud POULIQUEN wrote:
>> Hello,
>>
>> On 2/12/25 04:18, Bjorn Andersson wrote:
>>> On Tue, Dec 10, 2024 at 09:57:40AM +0100, Arnaud POULIQUEN wrote:
>>>> Hello Bjorn,
>>>>
>>>> On 12/6/24 23:07, Bjorn Andersson wrote:
>>>>> On Thu, Nov 28, 2024 at 09:42:09AM GMT, Arnaud Pouliquen wrote:
>>>>>> Add a remoteproc TEE (Trusted Execution Environment) driver
>>>>>> that will be probed by the TEE bus. If the associated Trusted
>>>>>> application is supported on secure part this driver offers a client
>>>>>> interface to load a firmware by the secure part.
>>>>>
>>>>> If...else?
>>>>>
>>>>>> This firmware could be authenticated by the secure trusted application.
>>>>>>
>>>>>
>>>>> I would like for this to fully describe how this fits with the bus and
>>>> Are you speaking about the OP-TEE bus?
>>>>
>>>> I assume that your attempt is that I provide more details on the live cycle
>>>> sequence, right?
>>>>
>>>
>>> Right, there's a tee_client_driver and there's a remoteproc driver.
>>> Let's document clearly how these interact.
>>>
>>>>> how it is expected to be used by a specific remoteproc driver.
>>>>>
>>>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>>>>>> ---
>>>>>> Updates vs version v13:
>>>>>> - define REMOTEPROC_TEE as bool instead of tristate,
>>>>>> - remove the load of the firmware in rproc_tee_parse_fw as we will ensure
>>>>>> that the firmware is loaded using the load_fw() operation.
>>>>>> ---
>>>>>> drivers/remoteproc/Kconfig | 10 +
>>>>>> drivers/remoteproc/Makefile | 1 +
>>>>>> drivers/remoteproc/remoteproc_tee.c | 508 ++++++++++++++++++++++++++++
>>>>>> include/linux/remoteproc.h | 4 +
>>>>>> include/linux/remoteproc_tee.h | 105 ++++++
>>>>>> 5 files changed, 628 insertions(+)
>>>>>> create mode 100644 drivers/remoteproc/remoteproc_tee.c
>>>>>> create mode 100644 include/linux/remoteproc_tee.h
>>>>>>
>>>>>> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
>>>>>> index 955e4e38477e..f6335321d540 100644
>>>>>> --- a/drivers/remoteproc/Kconfig
>>>>>> +++ b/drivers/remoteproc/Kconfig
>>>>>> @@ -23,6 +23,16 @@ config REMOTEPROC_CDEV
>>>>>>
>>>>>> It's safe to say N if you don't want to use this interface.
>>>>>>
>>>>>> +config REMOTEPROC_TEE
>>>>>> + bool "Remoteproc support by a TEE application"
>>>>>> + depends on OPTEE
>>>>>> + help
>>>>>> + Support a remote processor with a TEE application.
>>>>>
>>>>> Does the remote processor run TEE applications? (Rethorical question...)
>>>>>
>>>>>> The Trusted
>>>>>> + Execution Context is responsible for loading the trusted firmware
>>>>>> + image and managing the remote processor's lifecycle.
>>>>>> +
>>>>>> + It's safe to say N if you don't want to use remoteproc TEE.
>>>>>
>>>>> It's not really about "wanting to use", it's a question whether your
>>>>> device implements/provides the remoteproc TEE.
>>>>>
>>>>>> +
>>>>>> config IMX_REMOTEPROC
>>>>>> tristate "i.MX remoteproc support"
>>>>>> depends on ARCH_MXC
>>>>>> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
>>>>>> index 5ff4e2fee4ab..f77e0abe8349 100644
>>>>>> --- a/drivers/remoteproc/Makefile
>>>>>> +++ b/drivers/remoteproc/Makefile
>>>>>> @@ -11,6 +11,7 @@ remoteproc-y += remoteproc_sysfs.o
>>>>>> remoteproc-y += remoteproc_virtio.o
>>>>>> remoteproc-y += remoteproc_elf_loader.o
>>>>>> obj-$(CONFIG_REMOTEPROC_CDEV) += remoteproc_cdev.o
>>>>>> +obj-$(CONFIG_REMOTEPROC_TEE) += remoteproc_tee.o
>>>>>> obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o
>>>>>> obj-$(CONFIG_IMX_DSP_REMOTEPROC) += imx_dsp_rproc.o
>>>>>> obj-$(CONFIG_INGENIC_VPU_RPROC) += ingenic_rproc.o
>>>>>> diff --git a/drivers/remoteproc/remoteproc_tee.c b/drivers/remoteproc/remoteproc_tee.c
>>>>>> new file mode 100644
>>>>>> index 000000000000..3fe3f31068f2
>>>>>> --- /dev/null
>>>>>> +++ b/drivers/remoteproc/remoteproc_tee.c
>>>>>> @@ -0,0 +1,508 @@
>>>>>> +// SPDX-License-Identifier: GPL-2.0-or-later
>>>>>> +/*
>>>>>> + * Copyright (C) STMicroelectronics 2024
>>>>>> + * Author: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>>>>>> + */
>>>>>> +
>>>>>> +#include <linux/firmware.h>
>>>>>> +#include <linux/io.h>
>>>>>> +#include <linux/module.h>
>>>>>> +#include <linux/remoteproc.h>
>>>>>> +#include <linux/remoteproc_tee.h>
>>>>>> +#include <linux/slab.h>
>>>>>> +#include <linux/tee_drv.h>
>>>>>> +
>>>>>> +#define MAX_TEE_PARAM_ARRAY_MEMBER 4
>>>>>> +
>>>>>> +/*
>>>>>> + * Authentication of the firmware and load in the remote processor memory
>>>>>
>>>>> Exactly what does this imply? Will the content of @memref be copied into
>>>>> some other memory?
>>>>
>>>> The objective is to authenticate and load in one step. So, yes, the image is
>>>> loaded into the remoteproc destination memory.
>>>>
>>>
>>> So, some separate device-memory, or some preallocated carveout which is
>>> only accessible from secure world?
>>
>> No sure to understand the difference you do between eparate device-memory and
>> preallocated carveout.
>>
>
> The main clarification I was looking for was that in your design you
> don't use any resources on the Linux side for when your remoteproc
> instance is running - i.e. no carveouts etc on the Linux side.
>
>> In OP-TEE, we use the same principle as in Linux. OP-TEE uses memory regions
>> declared in its device tree to list memories usable for the coprocessor (with
>> associated access rights). On load, it checks that the segments to load are
>> included in these memory regions.
>>
>> Linux only declares the shared memory-regions in the device tree, for IPC.
>>
>>>
>>> Does the OS need to retain @memref past this point?
>>
>> No need,and as the area contains the reult of request_firmware() that can be
>> corrupted by Linux, OP-TEE considered this as a temporaray unsafe memory. After
>> the load + authentication step this buffer is no more used.
>> For detail, OPTEE make a copy of the header and TLV (metadata) in a secure
>> memory. and load the firmware images in destination memories All these memories
>> are not accessible from the Linux.
>>
>
> No concerns with this, but these semantics should be clearly documented
> here.
>
>>>
>>>> On stm32mp1 we can not store the elf file in a temporary secure memory as
>>>> the memory is encrypted by software (this would take to much time).
>>>>
>>>> For your information, in OP-TEE, the application code is split into a generic
>>>> part and a platform adaptation layer. The generic application is mainly
>>>> responsible for:
>>>>
>>>> - Copying the binary header and metadata into secure memory and authenticating them.
>>>> - Parsing the ELF images and providing segments to load with associated
>>>> authenticated hashes to the platform application.
>>>> In the future, someone can add their own format if needed.
>>>>
>>>> But the generic part could be enhance to authenticate and load a non ELF binary.
>>>> So I'm trying to be generic as possible here.
>>>>
>>>
>>> Generic might be okay, but I'd prefer this to be less vague.
>>> Also worth noting is the Qualcomm implementation of TZ-backed
>>> remoteproc, which is already in the tree.
>>
>> Could you point me the code in Linux and your TEE, please?
>>
>
> One example is drivers/remoteproc/qcom_q6v5_pas.c where this is captured
> in adsp_start().
>
> qcom_mdt_pas_init() parses out the ELF header and signature information
> and passes this to the secure world, it then loads the segments of the
> ELF into the carvouts (qcom_mdt_load_no_init()) and finally it jumps to
> secure world with qcom_scm_pas_auth_and_reset(), which will lock down
> Linux's access to the carveouts, then based on previously established
> data will authenticate the loaded firmware and finally start execution
> on the remote processor.
>
> The difference in this model though is that we don't need the resource
> table for rproc_handle_resources() - so this doesn't meet your needs.
>
>>> There the firmware is loaded
>>> into carveouts, the certificates and hashes are validated.
>>
>> Seems to me that there is also a partial Authentication done during the load step.
>>
>
> Given that the ELF header and signature information is vetted before the
> actually copy the segments into carveouts, it's conceivable that the ELF
> header could be sanity checked...
>
>>> Lastly
>>> the operation "authenticate and start" is invoked, which does that, and
>>> locks the OS out of the given memory region - until "shutdown" is
>>> invoked.
>>
>> The differnce between the 2 implementations is the authentication method done in
>> 2 steps for Qualcomm implementation , in one step for OP-TEE.
>>
>
> Yes, but it needs to be pointed out that this is because you want the
> resource table to be authenticated.
>
>> So here if I just remove the term 'authentication' in the command description
>> does it ok for you?
>>
>
> No, perhaps I'm misinterpreting you here; but the goal isn't to play
> word games until it's good enough - the goal is to have a clean design
> that will cover the various cases, and for that we need to establish
> what your actual requirements on the host OS side is (typically while
> considering the "other side" to be a black box).
>
>>>
>>>>
>>>>>
>>>>>> + *
>>>>>> + * [in] params[0].value.a: unique 32bit identifier of the remote processor
>>>>>
>>>>> Why not just "remote processor identifier"?
>>>>>
>>>>>> + * [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
>>>>>
>>>>> Why is there two "FW" in this constant? Why isn't it just
>>>>> "TA_RPROC_FW_CMD_START"?
>>>>>
>>>>> And why is it not TEE_PROC_FW_CMD_START?
>>>>>
>>>>>> +
>>>>>> +/*
>>>>>> + * 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.
>>>>>
>>>>> These three lines describe a scenario that doesn't make any sense to me.
>>>>> But if that's the case, you should be able to describe that the API
>>>>> might give you a inaccessible pointer, by design.
>>>>
>>>> On STM32MP, we have a kind of firewall in OP-TEE that sets memory access rights
>>>> from the device tree. So if the firmware image is not linked according to the
>>>> firewall configuration, the pointer may not be accessible.
>>>>
>>>> I will update the comment as you propose.
>>>>
>>>>>
>>>>>> + *
>>>>>> + * [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
>>>>>
>>>>> What does this mean? What will I find at @memref after this call?
>>>>
>>>> I do not have a simple answer here as it depends on the OP-TEE strategy.
>>>> It could be an obscure core dump with possible encryption.
>>>>
>>>> I will remove this as it is not yet implemented in OP-TEE.
>>>>
>>>
>>> Okay. But I would prefer that we define the semantics before it's
>>> implemented...
>>
>> that seems fair, I notice that we will have to address this in a separate thread
>> strating with a series in Linux.
>>
>>
>>>
>>>> https://elixir.bootlin.com/op-tee/4.4.0/source/ta/remoteproc/src/remoteproc_core.c#L1131
>>>>
>>>>>
>>>>>> + *
>>>>>> + * [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
>>>>>
>>>>> s/else return Null/or NULL/
>>>>>
>>>>>> + */
>>>>>> +#define TA_RPROC_FW_CMD_GET_COREDUMP 5
>>>>>> +
>>>>>> +/*
>>>>>> + * Release remote processor firmware images and associated resources.
>>>>>
>>>>> Exactly what does this mean for the caller?
>>>>
>>>> It is platform dependent. It can consist in cleaning the memory, but
>>>> can be also something else such as firewall configuration.
>>>> On stm323mp we clean all the memories region reserved for the remote processor.
>>>>
>>>
>>> We can't have an ABI which isn't well defined in intent. Your examples
>>> would easily fall in the realm of a well defined interface, but this
>>> ties into the question above - what does is actually mean in terms of
>>> the memory carveouts and such.
>>>
>>
>> Regarding this comment and the one below, does following description would
>> respond to your expectations? else do you have a suggestion?
>>
>> /*
>> * This command should be used in case an error occurs between the loading of
>> * the firmware images (TA_RPROC_CMD_LOAD_FW) and the starting of the remote
>> * processor (TA_RPROC_CMD_START_FW),
>
> This is valuable information related to TA_RPROC_CMD_LOAD_FW and
> TA_RPROC_CMD_START_FW, so let's document this there instead.
>
>> * or after stopping the remote processor
>> * (TA_RPROC_CMD_STOP_FW).
>> *
>> * This command is used to inform the TEE (Trusted Execution Environment) that
>> * resources associated with the remote processor can be released. After this
>> * command, the firmware is no longer present in the remote processor's memories
>> * and must be reloaded (TA_RPROC_FW_CMD_LOAD_FW) to restart the remote
>> * processor.
>
> I guess it's fine to define it like this on this level, but in the
> remoteproc core I'd like us to express the related logic as "release
> resources allocated durign rproc_parse_fw(). (And I don't think these
> two interpretations are in conflict).
>
> What's unexpected to me then is that you're not actually reloading your
> firmware across a recovery/restart?
I do it. in rproc_boot_recovery()
- we call on rproc_stop()
rproc_reset_rsc_table_on_stop() copy the resource table in
rproc->cached_table
- we call rproc_load_fw() added in patch 3/8
- we call rproc_start() which overwrite the resource table with values in
proc->cached_table
The proc->cached_table avoid to release and reallocate all the carveout on recovery.
This management is one of the points that complexity the sequence in the
remoteproc_tee case.
Thanks,
Arnaud
>
> Regards,
> Bjorn
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation
2025-03-04 15:23 ` Bjorn Andersson
@ 2025-03-05 12:50 ` Arnaud POULIQUEN
0 siblings, 0 replies; 32+ messages in thread
From: Arnaud POULIQUEN @ 2025-03-05 12:50 UTC (permalink / raw)
To: Bjorn Andersson; +Cc: Mathieu Poirier, linux-remoteproc, linux-kernel
On 3/4/25 16:23, Bjorn Andersson wrote:
> On Wed, Feb 12, 2025 at 02:48:30PM +0100, Arnaud POULIQUEN wrote:
>>
>>
>> On 2/12/25 04:54, Bjorn Andersson wrote:
>>> On Tue, Dec 10, 2024 at 11:33:31AM +0100, Arnaud POULIQUEN wrote:
>>>>
>>>>
>>>> On 12/10/24 00:14, Bjorn Andersson wrote:
>>>>> On Thu, Nov 28, 2024 at 09:42:10AM GMT, Arnaud Pouliquen wrote:
>>>>>> This patch updates the rproc_ops structures to include two new optional
>>>>>> operations.
>>>>>>
>>>>>> - The load_fw() op is responsible for loading the remote processor
>>>>>> non-ELF firmware image before starting the boot sequence. This ops will
>>>>>> be used, for instance, to call OP-TEE to authenticate an load the firmware
>>>>>> image before accessing to its resources (a.e the resource table)
>>>>>>
>>>>>> - The release_fw op is responsible for releasing the remote processor
>>>>>> firmware image. For instance to clean memories.
>>>>>> The ops is called in the following cases:
>>>>>> - An error occurs between the loading of the firmware image and the
>>>>>> start of the remote processor.
>>>>>> - after stopping the remote processor.
>>>>>>
>>>>>
>>>>> Why does this difference need to be encoded in rproc_ops? I think we
>>>>> should strive for having a single, simple high level flow of operations
>>>>> through the remoteproc core for which the specifics of each remoteproc
>>>>> instance can be encoded in that driver.
>>>>>
>>>>>
>>>>> Perhaps there's a good reason for this, but if so please read and follow
>>>>> https://docs.kernel.org/process/submitting-patches.html#describe-your-changes
>>>>> to make that reasoning clear in the commit message.
>>>>>
>>>>
>>>> The actual sequence to load a remoteproc firmware is
>>>> - get firmware from file system and store the firmware image in Linux kernel memory
>>>
>>> This sounds like "load" in remoteproc terminology.
>>
>> it is the request_firmware()
>>
>>>
>>>> - get resource table from the firmware image and make a copy(
>>>> - parse the resource table and handle the resources
>>>
>>>> - load the firmware
>>>> - start the firmware
>>>
>>> And these two are "start".
>>
>> yes done in rproc_start()
>>
>>>
>>>>
>>>>
>>>> In OP-TEE we support not only one ELF image but n images (for instance a TF-M +
>>>> a zephyr), the segments can be encrypted the OP-TEE load sequence is
>>>> - copy header and meta data of the signed image in a secure memory
>>>> - verify it
>>>> - copy segments in remote processor memory and authenticate segments in place.
>>>> - optionally decrypt the segments
>>>>
>>>> Only at this step the resource table as been authenticated (and decrypted)
>>>
>>> "this step" meaning TA_RPROC_FW_CMD_LOAD_FW?
>>
>> yes
>>
>>> Above you say that happens
>>> after you parse the resource table.
>>
>> The sequence above staring by "the actual sequence to load a remoteproc firmware
>> is" describes the existing legacy Linux kernel sequence without my series
>>
>
> It's useful not to call this "the legacy sequence", because it's a
> sequence that all other cases will continue to use. The task at hand is
> to determine a sequence that (at some abstraction level) captures the
> different sequences that we need to support.
>
>>>
>>>>
>>>> So the point is that we need to load the firmware before getting the resource table
>>>>
>>>>
>>>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>>>>>> ---
>>>>>> Update vs version V13:
>>>>>> - Rework the commit to introduce load_fw() op.
>>>>>> - remove rproc_release_fw() call from rproc_start() as called in
>>>>>> rproc_boot() and rproc_boot_recovery() in case of error.
>>>>>> - create rproc_load_fw() and rproc_release_fw() internal functions.
>>>>>> ---
>>>>>> drivers/remoteproc/remoteproc_core.c | 16 +++++++++++++++-
>>>>>> drivers/remoteproc/remoteproc_internal.h | 14 ++++++++++++++
>>>>>> include/linux/remoteproc.h | 6 ++++++
>>>>>> 3 files changed, 35 insertions(+), 1 deletion(-)
>>>>>>
>>>>>> diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
>>>>>> index ace11ea17097..8df4b2c59bb6 100644
>>>>>> --- a/drivers/remoteproc/remoteproc_core.c
>>>>>> +++ b/drivers/remoteproc/remoteproc_core.c
>>>>>> @@ -1488,6 +1488,7 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
>>>>>> kfree(rproc->cached_table);
>>>>>> rproc->cached_table = NULL;
>>>>>> rproc->table_ptr = NULL;
>>>>>> + rproc_release_fw(rproc);
>>>>>> unprepare_rproc:
>>>>>> /* release HW resources if needed */
>>>>>> rproc_unprepare_device(rproc);
>>>>>> @@ -1855,8 +1856,14 @@ static int rproc_boot_recovery(struct rproc *rproc)
>>>>>> return ret;
>>>>>> }
>>>>>>
>>>>>> + ret = rproc_load_fw(rproc, firmware_p);
>>>>>
>>>>> It is not clear to me why in the case of OP-TEE we need to invoke the
>>>>> "load operation" here, and in the case of "legacy" ELF loading we do it
>>>>> first thing in rproc_start() (i.e. on the very next line of code being
>>>>> executed).
>>>>
>>>> For the OP-TEE, please refer to my comment above.
>>>>
>>>> The only reason I can see for the legacy ELF is that the resource table could
>>>> contain information to be able to configure some resources to load the firmware.
>>>> In case of OP-TEE this would be managed in OP-TEE.
>>>>
>>>
>>> Sure, but as I say...inline rproc_start() here and the very next
>>> operation we perform is something we call "load".
>>>
>>> Why do we need to differentiate "load" and "load firmware", when we call
>>> them at the same step in the sequence?
>>
>> A simplified sequence showing the implementation after this commit would be
>>
>> 1) request_firmware()
>> 2) rproc_load_fw() (optional)
>> 3) rproc_parse_fw()
>> 4) rproc_handle_resources()
>> 5) rproc_load_segments() (optional)
>> 6) rproc->ops->start()
>>
>> Here we introduce rproc_load_fw() because we need to load the firmware and
>> authenticate it before parsing it for the resource table calling rproc_parse_fw().
>>
>
> Per your explanation, I think what you're saying is that before
> rproc_handle_resources() we must load and authenticate the resource
> table. The loading of ELF segments is more a side effect of the firmware
> being processed by OP-TEE at this point (I'm not saying that you must
> implement rproc_load_segments() here)
>
>> legacy Elf format sequence:
>> 1) request_firmware()
>> 3) rproc_parse_fw()
>> 4) rproc_handle_resources()
>> 5) rproc_load_segments()
>> 6) rproc->ops->start()
>>
>>
>> Requested remoteproc TEE sequence
>> 1) request_firmware()
>> 2) rproc_load_fw()
>> 3) rproc_parse_fw()
>> 4) rproc_handle_resources()
>> 6) rproc->ops->start()
>>
>>
>>>
>>>>>
>>>>>
>>>>> Should we start by renaming rproc_load_segments() rproc_load() and move
>>>>> it out of rproc_start()? (I.e. here?)
>>>>>
>>>>> Perhaps define that rproc_load() is responsible for "loading firmware"
>>>>> (whatever that means) and establishing rproc->cached_table, and
>>>>> rproc->table_ptr?
>>>>>
>>>>> (Note that this seems like a good cleanup of the spaghetti regardless)
>>>>>
>>>>
>>>> It's something that crossed my mind, but I don't know the legacy well enough to
>>>> guarantee that it will work in all drivers.
>>>>
>>>
>>> Looking at this again, if we move it, we need to duplicate it across a
>>> few different call sites. So might need to take another look at that.
>>>
>>> Still flatten the code and you seem to do:
>>>
>>> if (load_fw)
>>> load_fw();
>>> if (load)
>>> load();
>>>
>>> Why do we need two different things named "load".
>>
>> Right, the terminology can be confusing, but both perform a load. We arrived at
>> this terminology with Mathieu, but if you have a suggestion, don't hesitate to
>> share.
>>
>
> If the terminology is confusing, then it's a bad API.
>
> If you clearly define that resource table needs to be
> decrypted/authenticated before rproc_handle_resources() then it's not so
> confusing.
>
>>>
>>>> If you want to go in this direction, perhaps this is something that could be
>>>> addressed in a dedicated pull request? In this case, the ops could become
>>>> load_fw and load_fw_new, similar to how it is done for platform_driver::remove.
>>>>
>>>
>>> No need to make it more complicated than it is, change the symbol name
>>> and fix the 3 places that calls the function.
>>>
>>>>
>>>>>> + if (ret)
>>>>>> + return ret;
>>>>>> +
>>>>>> /* boot the remote processor up again */
>>>>>> ret = rproc_start(rproc, firmware_p);
>>>>>> + if (ret)
>>>>>> + rproc_release_fw(rproc);
>>>>>
>>>>> The fact that you rproc_release_fw() in the error path here, right
>>>>> before we unconditionally release_firmware() the actual firmware means
>>>>> that you have 2 different life cycles with very very similar names.
>>>>>
>>>>> This will contain bugs, sooner or later.
>>>>
>>>> So we need to find a better way for the ops if we continue in this direction.
>>>> What about introducing rproc_load_new and rproc_release?
>>>>
>>>
>>> You need to help me understand why load and load_new are both needed.
>>>
>>> And no, "load_new" is not an ok name.
>>>
>>>>>
>>>>>>
>>>>>> release_firmware(firmware_p);
>>>>>>
>>>>>> @@ -1997,7 +2004,13 @@ int rproc_boot(struct rproc *rproc)
>>>>>> goto downref_rproc;
>>>>>> }
>>>>>>
>>>>>> + ret = rproc_load_fw(rproc, firmware_p);
>>>>>> + if (ret)
>>>>>> + goto downref_rproc;
>>>>>> +
>>>>>> ret = rproc_fw_boot(rproc, firmware_p);
>>>>>> + if (ret)
>>>>>> + rproc_release_fw(rproc);
>>>>>>
>>>>>> release_firmware(firmware_p);
>>>>>> }
>>>>>> @@ -2071,6 +2084,7 @@ int rproc_shutdown(struct rproc *rproc)
>>>>>> kfree(rproc->cached_table);
>>>>>> rproc->cached_table = NULL;
>>>>>> rproc->table_ptr = NULL;
>>>>>> + rproc_release_fw(rproc);
>>>>>> out:
>>>>>> mutex_unlock(&rproc->lock);
>>>>>> return ret;
>>>>>> @@ -2471,7 +2485,7 @@ static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops)
>>>>>> if (!rproc->ops->coredump)
>>>>>> rproc->ops->coredump = rproc_coredump;
>>>>>>
>>>>>> - if (rproc->ops->load)
>>>>>> + if (rproc->ops->load || rproc->ops->load_fw)
>>>>>> return 0;
>>>>>>
>>>>>> /* Default to ELF loader if no load function is specified */
>>>>>> diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
>>>>>> index 0cd09e67ac14..2104ca449178 100644
>>>>>> --- a/drivers/remoteproc/remoteproc_internal.h
>>>>>> +++ b/drivers/remoteproc/remoteproc_internal.h
>>>>>> @@ -221,4 +221,18 @@ bool rproc_u64_fit_in_size_t(u64 val)
>>>>>> return (val <= (size_t) -1);
>>>>>> }
>>>>>>
>>>>>> +static inline void rproc_release_fw(struct rproc *rproc)
>>>>>> +{
>>>>>> + if (rproc->ops->release_fw)
>>>>>> + rproc->ops->release_fw(rproc);
>>>>>> +}
>>>>>> +
>>>>>> +static inline int rproc_load_fw(struct rproc *rproc, const struct firmware *fw)
>>>>>> +{
>>>>>> + if (rproc->ops->load_fw)
>>>>>> + return rproc->ops->load_fw(rproc, fw);
>>>>>> +
>>>>>> + return 0;
>>>>>> +}
>>>>>> +
>>>>>> #endif /* REMOTEPROC_INTERNAL_H */
>>>>>> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
>>>>>> index 2e0ddcb2d792..ba6fd560f7ba 100644
>>>>>> --- a/include/linux/remoteproc.h
>>>>>> +++ b/include/linux/remoteproc.h
>>>>>> @@ -381,6 +381,10 @@ enum rsc_handling_status {
>>>>>> * @panic: optional callback to react to system panic, core will delay
>>>>>> * panic at least the returned number of milliseconds
>>>>>> * @coredump: collect firmware dump after the subsystem is shutdown
>>>>>> + * @load_fw: optional function to load non-ELF firmware image to memory, where the remote
>>>>>> + * processor expects to find it.
>>>>>
>>>>> Why does it matter if it's an ELF or not?
>>>>
>>>> No matter. It was more to differentiate from the legacy one, but it does not
>>>> make sense and adds to the argument that the ops naming is not accurate.
>>>>
>>>
>>> I'm probably misunderstanding your intention here, but I can't help
>>> feeling that you add this code path to avoid understanding or fixing the
>>> existing code.
>>>
>>>>>
>>>>> In the Qualcomm case, firmware comes in ELF format, Linux loads the
>>>>> LOAD segments and the trusted world then authenticates the content and
>>>>> start the remote processor.
>>>>>
>>>>>
>>>>> I think the difference in your case is that you have memory reserved
>>>>> elsewhere, and you want the "load" operation to pass the firmware to the
>>>>> TEE - which means that you need rproc_release_fw() to eventually clean
>>>>> up the state if rproc_start() fails - and upon shutdown.
>>>>
>>>> Yes the OP-TEE is make more stuff:
>>>> - authenticate several firmware images
>>>
>>> Please tell me how that work. I'm not able to see the multiple calls to
>>> request_firmware()...
>>
>> From a Linux perspective, it is only one firmware image to load.
>>
>> The format defined in OP-TEE, available here [1], allows concatenation of
>> several firmware images into one signed binary image. For instance, for the
>> stm32mp2 platform, we can run a TF-M and a Zephyr firmware on the Cortex-M33.
>>
>
> Okay, that makes sense
>
> E.g. the ELF files loaded by the Qualcomm remoteproc drivers contain any
> number of ELF segments. Each remoteproc as one entity in the view of
> Linux, but Linux has no awareness of the internal hardware
> configuration.
>
> But there are also cases in the Qualcomm space where multiple firmware
> files are loaded for a single remoteproc - something which is rather
> messy.
>
>> [1]
>> https://github.com/OP-TEE/optee_os/blob/master/ta/remoteproc/src/remoteproc_core.c#L18
>>
>>
>>>
>>>> - decrypt images if encrypte
>>>
>>> This is done for some Qualcomm remoteprocs as well.
>>
>> Do you decrypt before authenticating? On the stm32mp1, due to memory
>> constraints, we encrypt only the ELF segment to load. We compute the signature
>> based on the encrypted version of the ELF. So, we authenticate before decrypting."
>>
>
> I actually don't know, both operations are performed by the secure
> world.
>
> But authenticating the encrypted data makes sense to me.
>
>>>
>>>> - ensure that the load is done in granted memories
>>>
>>> At the top of your reply you say that you're loading the firmware into
>>> Linux memory, then you invoke TA_RPROC_FW_CMD_LOAD_FW to "load" it once
>>> more - presumably copying into some other memory.
>>
>> Sorry if it was not clear. By 'loading the firmware into Linux memory' I mean
>> calling request_firmware() to get the image from the filesystem and copy it into
>> Linux-allocated memory. Then, the address of this memory is registered in OP-TEE
>> memory space and provided as a parameter of the TA_RPROC_FW_CMD_LOAD_FW command.
>>
>
> Okay, this is an important difference. In the Qualcomm case Linux will
> load the ELF segments and then at "authenticate and start" memory
> management tricks will be played to lock Linux out of the memory while
> the remoteproc is running.
>
> In your case you effectively have "external" pre-allocated memory for
> your remoteproc instance - and TA_RPROC_FW_CMD_LOAD_FW is the mechanism
> used to populate it _and_ decode the resource table.
>
>>>
>>> It sounds like this is an optimization to fail early in the case that
>>> something is wrong?
>>
>> Not only that, it is to ensure that everything is valid and decrypted before
>> enabling the parsing of the resource table (rproc_parse_fw()).
>>
>
> But correct me if I'm wrong, there's no strong reason for why the
> non-resource-table segments needs to be decrypted at this point, right?
>
> (I acknowledge that it makes sense to do it in one go, just trying to
> understand)
In OP-TEE, we does not store firmware segments informationa. When we come
back fromTA_RPROC_FW_CMD_LOAD_FW request we have lost load information.
only the address of the resource table is stored.
>
>>>
>>>> - manage the memory access rights to enure that the code and data memory
>>>> is never accessible by the Linux.
>>>>
>>>
>>> Right, after authentication Linux must be locked out. That's done in the
>>> Qualcomm "authenticate and start" phase, at the very end of the loading
>>> and setting things up.
>>
>> Make sense, but not possible with the resource table handling.
>>
>
> Correct, if the resource table needs to be authenticated and/or
> decrypted this needs to happen at an earlier point.
>
>>>
>>>>>
>>>>> If we improve the definition of rproc_load_segments() to mean
>>>>> "remoteproc (or remoteproc driver) is loading segments", then in your
>>>>> case there's no "loading" operation in Linux. Instead you make that a
>>>>> nop and invoke LOAD_FW and START_FW within your start callback, then you
>>>>> can clean up the remnant state within your driver's start and stop
>>>>> callbacks - without complicating the core framework.
>>>>
>>>> This would not work as I need to load the firmware before calling
>>>> rproc_handle_resources().
>>
>> Yes this is the blocking point. I suppose that you don't have this constraint in
>> Qualcomm implementations?
>>
>
> Correct, as Linux is being locked out of the memory while the remoteproc
> is running the "carveout"-style resources are defined using no-map
> reserved-memory regions, and IPC is described in Devicetree (as there's
> both consumers and providers for other Devicetree entries there).
>
> But there's been some prototyping in the past related to utilizing the
> resource table, at which point this would become a "problem".
>
>>>>
>>>
>>> Ohh, now I see what you're saying. This is why you should follow
>>> https://docs.kernel.org/process/submitting-patches.html#describe-your-changes
>>> when you write a commit message. Then I would have understood your
>>> problem in the very first paragraph of the patch.
>>
>> My apologize with the succession of versions and associated discussions. I
>> forgot that a series version must be self-sufficient in terms of explanation.
>>
>>>
>>> This very much sounds like what's intended to happen in
>>> rproc_parse_fw(), per the comment right next to the call:
>>>
>>> /* Load resource table, core dump segment list etc from the firmware */
>>
>> Yes, the main difference is that for ELF files, we get the resource table from
>> the ELF image and put it in rproc->cached_table. Then, the cached_table is
>> updated in rproc_handle_resources() and finally written to the destination
>> memory in rproc_start().
>>
>> For the TEE implementation, we directly provide the destination memory address,
>> which is stored in rproc->cached_table.
>>
>
> I presume you mean, you call TA_RPROC_FW_CMD_LOAD_FW which will decode
> the resource table and you then request the address of the loaded and
> decoded resource table from TA_RPROC?
that's correct.
>
>> Notice that the rproc->cached_table is mandatory for the recovery.
>>
>>>
>>>
>>> But I'm guessing that would require loading the segments etc into the
>>> destination memory, perform the authentication and then you can get hold
>>> of the resource table?
>>>
>>> Which..per your patch you do before calling rproc_fw_boot(), so you're
>>> already diverging completely from the expected flow.
>>
>> The call of rproc_load_fw() should be moved in rproc_fw_boot(), I guess
>>
>> What would you suggest as the next step for these commits? Should I just rename
>> load_fw to release_fw? If so, do you have a naming suggestion?
>>
>> Or do we need to find another way to sequence the boot?
>>
>
> I think it's clear from this discussion that you need a way to load,
> authenticate and decrypt the resource table before
> rproc_handle_resources().
>
> It's also clear that "freeing" the resource table can no longer be done
> with just a call to kfree().
>
>
> I also find it conceivable that someone would have the desire to
> authenticate the resource table before rproc_handle_resources() and then
> use rproc_load_segments() to load the firmware content (followed by
> another round of authentication in start()).
>
>
> So if we've established that what you're looking for is a
> driver-specific way to get hold of the resource table and that's the
> reason for your modifications, then that should fit pretty well into the
> concept of rproc_parse_fw().
>
> You have a non-standard optimization in that in that same operation you
> store the rest of the firmware in the non-Linux side, to avoid having to
> call load_segment separately (but there doesn't seem to be any clear
> reason for the segments to be loaded at this time (perhaps you encrypt
> the whole image and don't have segments?)).
>
> rproc_load_segments() is already optional, so no change is needed there.
>
> We need to abstract out the kfree(rproc->cached_table) so that this can
> also release the resource table resources and secure state that you
> established during rproc_parse_fw().
Please tell me if I misunderstood, your proposal seems similar to the version 13
[1][2] of my series.
In the version v13 patch 3/7:
- the rproc_tee_parse_fw() that implements the rproc:ops:parse_fw loads
the firmware and gets the resource table.
- the rproc_load_segment() is not optional in remoteproc-tee. We need to
reload the firmware on recovery. For that we send twice the
TA_RPROC_FW_CMD_LOAD_FW command to OP-TEE (on rproc_parse_fw and on
then rproc_load_segment)
OP-TEE just ignors the command if the firmware is already loaded.
In the v13 the resource release was something that trig Mathieu's concerns[3]
resulting in the V15 approach with 2 loads ops.
Could you confirm that the proposal in V13 is what you have in mind?
If Yes, and Mathieu is also ok to come back on v13 approach, I will use the V13
as base, for my next version integrating other comments and improving the
release part.
[1]
https://patchwork.kernel.org/project/linux-arm-kernel/cover/20241104133515.256497-1-arnaud.pouliquen@foss.st.com/
[2]
https://patchwork.kernel.org/project/linux-arm-kernel/list/?series=906081&archive=both
[3] https://lore.kernel.org/lkml/ZzZcITZq%2FU9SOqnP@p14s/
Thanks,
Arnaud
>
> Regards,
> Bjorn
>
>> Thanks,
>> Arnaud
>>
>>>
>>>> I can not use rproc_prepare_device() as it is not called on recovery
>>>>
>>>
>>> The purpose of rproc_prepare_device() is to ensure that any clocks etc
>>> for the memory where we're going to load the firmware is enabled, so
>>> that doesn't sounds like the right place.
>>>
>>> Regards,
>>> Bjorn
>>>
>>>> Thanks,
>>>> Arnaud
>>>>
>>>>>
>>>>> Regards,
>>>>> Bjorn
>>>>>
>>>>>> + * @release_fw: optional function to release the firmware image from memories.
>>>>>> + * This function is called after stopping the remote processor or in case of error
>>>>>> */
>>>>>> struct rproc_ops {
>>>>>> int (*prepare)(struct rproc *rproc);
>>>>>> @@ -403,6 +407,8 @@ struct rproc_ops {
>>>>>> u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
>>>>>> unsigned long (*panic)(struct rproc *rproc);
>>>>>> void (*coredump)(struct rproc *rproc);
>>>>>> + int (*load_fw)(struct rproc *rproc, const struct firmware *fw);
>>>>>> + void (*release_fw)(struct rproc *rproc);
>>>>>> };
>>>>>>
>>>>>> /**
>>>>>> --
>>>>>> 2.25.1
>>>>>>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH v15 2/8] remoteproc: Add TEE support
2024-12-06 22:07 ` Bjorn Andersson
2024-12-10 8:57 ` Arnaud POULIQUEN
@ 2025-03-25 11:05 ` Arnaud POULIQUEN
1 sibling, 0 replies; 32+ messages in thread
From: Arnaud POULIQUEN @ 2025-03-25 11:05 UTC (permalink / raw)
To: Bjorn Andersson; +Cc: Mathieu Poirier, linux-kernel, linux-remoteproc
On 12/6/24 23:07, Bjorn Andersson wrote:
> On Thu, Nov 28, 2024 at 09:42:09AM GMT, Arnaud Pouliquen wrote:
>> Add a remoteproc TEE (Trusted Execution Environment) driver
>> that will be probed by the TEE bus. If the associated Trusted
>> application is supported on secure part this driver offers a client
>> interface to load a firmware by the secure part.
>
> If...else?
>
>> This firmware could be authenticated by the secure trusted application.
>>
>
> I would like for this to fully describe how this fits with the bus and
> how it is expected to be used by a specific remoteproc driver.
>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>> ---
>> Updates vs version v13:
>> - define REMOTEPROC_TEE as bool instead of tristate,
>> - remove the load of the firmware in rproc_tee_parse_fw as we will ensure
>> that the firmware is loaded using the load_fw() operation.
>> ---
>> drivers/remoteproc/Kconfig | 10 +
>> drivers/remoteproc/Makefile | 1 +
>> drivers/remoteproc/remoteproc_tee.c | 508 ++++++++++++++++++++++++++++
>> include/linux/remoteproc.h | 4 +
>> include/linux/remoteproc_tee.h | 105 ++++++
>> 5 files changed, 628 insertions(+)
>> create mode 100644 drivers/remoteproc/remoteproc_tee.c
>> create mode 100644 include/linux/remoteproc_tee.h
[...]
>> +
>> +MODULE_DEVICE_TABLE(tee, rproc_tee_id_table);
>> +
>> +static struct tee_client_driver rproc_tee_fw_driver = {
>> + .id_table = rproc_tee_id_table,
>> + .driver = {
>> + .name = KBUILD_MODNAME,
>> + .bus = &tee_bus_type,
>> + .probe = rproc_tee_probe,
>> + .remove = rproc_tee_remove,
>> + },
>> +};
>> +
>> +static int __init rproc_tee_fw_mod_init(void)
>> +{
>> + return driver_register(&rproc_tee_fw_driver.driver);
>> +}
>> +
>> +static void __exit rproc_tee_fw_mod_exit(void)
>> +{
>> + driver_unregister(&rproc_tee_fw_driver.driver);
>> +}
>> +
>> +module_init(rproc_tee_fw_mod_init);
>> +module_exit(rproc_tee_fw_mod_exit);
>
> Please add an equivalent of the module_platform_driver() macro to tee
> framework instead of open-coding this.
>
It is not possible to use equivalent of the module_platform_driver() macro
as the device is on the TEE bus.
I followed recommendation provided in Documentation/driver-api/tee.rst[1]
Or do you have an alternative in mind?
Thanks,
Arnaud
[1]https://elixir.bootlin.com/linux/v6.14-rc6/source/Documentation/driver-api/tee.rst
>> +
>> +MODULE_DESCRIPTION(" remote processor TEE module");
>> +MODULE_LICENSE("GPL");
>> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
>> index 8fd0d7f63c8e..2e0ddcb2d792 100644
>> --- a/include/linux/remoteproc.h
>> +++ b/include/linux/remoteproc.h
>> @@ -503,6 +503,8 @@ enum rproc_features {
>> RPROC_MAX_FEATURES,
>> };
>>
>> +struct rproc_tee;
>> +
>> /**
>> * struct rproc - represents a physical remote processor device
>> * @node: list node of this rproc object
>> @@ -545,6 +547,7 @@ enum rproc_features {
>> * @cdev: character device of the rproc
>> * @cdev_put_on_release: flag to indicate if remoteproc should be shutdown on @char_dev release
>> * @features: indicate remoteproc features
>> + * @rproc_tee_itf: pointer to the remoteproc tee context
>> */
>> struct rproc {
>> struct list_head node;
>> @@ -586,6 +589,7 @@ struct rproc {
>> struct cdev cdev;
>> bool cdev_put_on_release;
>> DECLARE_BITMAP(features, RPROC_MAX_FEATURES);
>> + struct rproc_tee *rproc_tee_itf;
>
> TEE is just one specific remoteproc implementation, why does it need to
> infest the core data structure? Do you want a stm32_rproc here as well?
>
>> };
>>
>> /**
>> diff --git a/include/linux/remoteproc_tee.h b/include/linux/remoteproc_tee.h
>> new file mode 100644
>> index 000000000000..9b498a8eff4d
>> --- /dev/null
>> +++ b/include/linux/remoteproc_tee.h
>> @@ -0,0 +1,105 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + * Copyright(c) 2024 STMicroelectronics
>> + */
>> +
>> +#ifndef REMOTEPROC_TEE_H
>> +#define REMOTEPROC_TEE_H
>> +
>> +#include <linux/tee_drv.h>
>> +#include <linux/firmware.h>
>> +#include <linux/remoteproc.h>
>> +
>> +struct rproc;
>> +
>> +/**
>> + * struct rproc_tee - TEE remoteproc structure
>> + * @node: Reference in list
>> + * @rproc: Remoteproc reference
>> + * @parent: Parent device
>
> Isn't that rproc->dev->parent?
>
>> + * @rproc_id: Identifier of the target firmware
>> + * @session_id: TEE session identifier
>> + */
>> +struct rproc_tee {
>
> As far as I can tell this isn't dereferenced outside remoteproc_tee.c,
> can we hide it therein?
>
>> + struct list_head node;
>> + struct rproc *rproc;
>> + struct device *parent;
>> + u32 rproc_id;
>> + u32 session_id;
>> +};
>> +
>> +#if IS_REACHABLE(CONFIG_REMOTEPROC_TEE)
>> +
>> +int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id);
>> +int rproc_tee_unregister(struct rproc *rproc);
>> +int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw);
>> +int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw);
>> +void rproc_tee_release_fw(struct rproc *rproc);
>> +struct resource_table *rproc_tee_find_loaded_rsc_table(struct rproc *rproc,
>> + const struct firmware *fw);
>> +int rproc_tee_start(struct rproc *rproc);
>> +int rproc_tee_stop(struct rproc *rproc);
>> +
>> +#else
>> +
>
> Do we really need yet another bunch of stubs? Can't we just leave
> CONFIG_REMOTEPROC_TEE non-user-selectable and have the drivers that rely
> on it do "select REMOTEPROC_TEE"?
>
> If my measurements are correct, it's 3.1kB of code...
>
> Regards,
> Bjorn
>
>> +static inline int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id)
>> +{
>> + return -ENODEV;
>> +}
>> +
>> +static inline int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline int rproc_tee_unregister(struct rproc *rproc)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline int rproc_tee_load_fw(struct rproc *rproc, const struct firmware *fw)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline int rproc_tee_start(struct rproc *rproc)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline int rproc_tee_stop(struct rproc *rproc)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return 0;
>> +}
>> +
>> +static inline void rproc_tee_release_fw(struct rproc *rproc)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +}
>> +
>> +static inline struct resource_table *
>> +rproc_tee_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
>> +{
>> + /* This shouldn't be possible */
>> + WARN_ON(1);
>> +
>> + return NULL;
>> +}
>> +#endif /* CONFIG_REMOTEPROC_TEE */
>> +#endif /* REMOTEPROC_TEE_H */
>> --
>> 2.25.1
>>
>
^ permalink raw reply [flat|nested] 32+ messages in thread
end of thread, other threads:[~2025-03-25 11:07 UTC | newest]
Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-28 8:42 [PATCH v15 0/8] Introduction of a remoteproc tee to load signed firmware Arnaud Pouliquen
2024-11-28 8:42 ` [PATCH v15 1/8] remoteproc: core: Introduce rproc_pa_to_va helper Arnaud Pouliquen
2024-11-28 8:42 ` [PATCH v15 2/8] remoteproc: Add TEE support Arnaud Pouliquen
2024-12-03 17:04 ` Mathieu Poirier
2024-12-06 22:07 ` Bjorn Andersson
2024-12-10 8:57 ` Arnaud POULIQUEN
2025-01-10 8:51 ` Arnaud POULIQUEN
2025-02-12 3:18 ` Bjorn Andersson
2025-02-12 13:42 ` Arnaud POULIQUEN
2025-03-04 15:58 ` Bjorn Andersson
2025-03-05 12:48 ` Arnaud POULIQUEN
2025-03-25 11:05 ` Arnaud POULIQUEN
2024-11-28 8:42 ` [PATCH v15 3/8] remoteproc: Introduce load_fw and release_fw optional operation Arnaud Pouliquen
2024-12-03 17:22 ` Mathieu Poirier
2024-12-05 18:20 ` Arnaud POULIQUEN
2024-12-06 17:05 ` Mathieu Poirier
2024-12-06 17:07 ` Mathieu Poirier
2024-12-06 18:09 ` Arnaud POULIQUEN
2024-12-04 17:39 ` Mathieu Poirier
2024-12-09 23:14 ` Bjorn Andersson
2024-12-10 10:33 ` Arnaud POULIQUEN
2025-02-12 3:54 ` Bjorn Andersson
2025-02-12 13:48 ` Arnaud POULIQUEN
2025-03-04 15:23 ` Bjorn Andersson
2025-03-05 12:50 ` Arnaud POULIQUEN
2024-11-28 8:42 ` [PATCH v15 4/8] remoteproc: Rename load() operation to load_segments() in rproc_ops struct Arnaud Pouliquen
2024-12-04 18:02 ` Mathieu Poirier
2024-11-28 8:42 ` [PATCH v15 5/8] remoteproc: Make load_segments and load_fw ops exclusive and optional Arnaud Pouliquen
2024-12-04 17:53 ` Mathieu Poirier
2024-11-28 8:42 ` [PATCH v15 6/8] dt-bindings: remoteproc: Add compatibility for TEE support Arnaud Pouliquen
2024-11-28 8:42 ` [PATCH v15 7/8] remoteproc: stm32: Create sub-functions to request shutdown and release Arnaud Pouliquen
2024-11-28 8:42 ` [PATCH v15 8/8] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware Arnaud Pouliquen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).