* [PATCH v19 0/6] Introduction of a remoteproc tee to load signed firmware
@ 2025-06-25 9:40 Arnaud Pouliquen
2025-06-25 9:40 ` [PATCH v19 1/6] remoteproc: core: Introduce rproc_pa_to_va helper Arnaud Pouliquen
` (5 more replies)
0 siblings, 6 replies; 15+ messages in thread
From: Arnaud Pouliquen @ 2025-06-25 9:40 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier, Jens Wiklander, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-stm32, linux-arm-kernel, linux-remoteproc, linux-kernel,
op-tee, devicetree, Arnaud Pouliquen
Main updates from version V18[2]:
- rework documentation for the release_fw ops
- rework function documentation in remoteproc_tee.c
- replace spinlock by mutex and generalize usage in remoteproc_tee.c
Main updates from version V17[1]:
- Fix: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h>
is missing
More details are available in each patch commit message.
[1] https://lore.kernel.org/linux-remoteproc/20250613091650.2337411-1-arnaud.pouliquen@foss.st.com/
[2] https://lore.kernel.org/linux-remoteproc/20250616075530.4106090-1-arnaud.pouliquen@foss.st.com/
Tested-on: commit 19272b37aa4f ("Linux 6.16-rc1")
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 (6):
remoteproc: core: Introduce rproc_pa_to_va helper
remoteproc: Add TEE support
remoteproc: Introduce optional release_fw operation
dt-bindings: remoteproc: Add compatibility for TEE support
remoteproc: stm32: Create sub-functions to request shutdown and
release
remoteproc: stm32: Add support of an OP-TEE TA to load the firmware
.../bindings/remoteproc/st,stm32-rproc.yaml | 58 +-
drivers/remoteproc/Kconfig | 10 +
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/remoteproc_core.c | 52 ++
drivers/remoteproc/remoteproc_internal.h | 6 +
drivers/remoteproc/remoteproc_tee.c | 708 ++++++++++++++++++
drivers/remoteproc/stm32_rproc.c | 139 +++-
include/linux/remoteproc.h | 6 +
include/linux/remoteproc_tee.h | 87 +++
9 files changed, 1023 insertions(+), 44 deletions(-)
create mode 100644 drivers/remoteproc/remoteproc_tee.c
create mode 100644 include/linux/remoteproc_tee.h
base-commit: 19272b37aa4f83ca52bdf9c16d5d81bdd1354494
--
2.25.1
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v19 1/6] remoteproc: core: Introduce rproc_pa_to_va helper
2025-06-25 9:40 [PATCH v19 0/6] Introduction of a remoteproc tee to load signed firmware Arnaud Pouliquen
@ 2025-06-25 9:40 ` Arnaud Pouliquen
2025-06-25 9:40 ` [PATCH v19 2/6] remoteproc: Add TEE support Arnaud Pouliquen
` (4 subsequent siblings)
5 siblings, 0 replies; 15+ messages in thread
From: Arnaud Pouliquen @ 2025-06-25 9:40 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier, Jens Wiklander, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-stm32, linux-arm-kernel, linux-remoteproc, linux-kernel,
op-tee, devicetree, Arnaud Pouliquen
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 81b2ccf988e8..d06eef1fa424 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] 15+ messages in thread
* [PATCH v19 2/6] remoteproc: Add TEE support
2025-06-25 9:40 [PATCH v19 0/6] Introduction of a remoteproc tee to load signed firmware Arnaud Pouliquen
2025-06-25 9:40 ` [PATCH v19 1/6] remoteproc: core: Introduce rproc_pa_to_va helper Arnaud Pouliquen
@ 2025-06-25 9:40 ` Arnaud Pouliquen
2025-07-31 10:25 ` Harshal Dev
2025-08-13 16:42 ` Abdellatif El Khlifi
2025-06-25 9:40 ` [PATCH v19 3/6] remoteproc: Introduce optional release_fw operation Arnaud Pouliquen
` (3 subsequent siblings)
5 siblings, 2 replies; 15+ messages in thread
From: Arnaud Pouliquen @ 2025-06-25 9:40 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier, Jens Wiklander, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-stm32, linux-arm-kernel, linux-remoteproc, linux-kernel,
op-tee, devicetree, Arnaud Pouliquen
Add a remoteproc TEE (Trusted Execution Environment) driver that will be
probed by the TEE bus. If the associated Trusted application is supported
on the secure part, this driver offers a client interface to load firmware
by the secure part.
This firmware could be authenticated by the secure trusted application.
A specificity of the implementation is that the firmware has to be
authenticated and optionally decrypted to access the resource table.
Consequently, the boot sequence is:
1) rproc_parse_fw --> rproc_tee_parse_fw
remoteproc TEE:
- Requests the TEE application to authenticate and load the firmware
in the remote processor memories.
- Requests the TEE application for the address of the resource table.
- Creates a copy of the resource table stored in rproc->cached_table.
2) rproc_load_segments --> rproc_tee_load_fw
remoteproc TEE:
- Requests the TEE application to load the firmware. Nothing is done
at the TEE application as the firmware is already loaded.
- In case of recovery, the TEE application has to reload the firmware.
3) rproc_tee_get_loaded_rsc_table
remoteproc TEE requests the TEE application for the address of the
resource table.
4) rproc_start --> rproc_tee_start
- Requests the TEE application to start the remote processor.
The shutdown sequence is:
5) rproc_stop --> rproc_tee_stop
- Requests the TEE application to stop the remote processor.
6) rproc_tee_release_fw
This function is used to request the TEE application to perform actions
to return to the initial state on stop or on error during the boot
sequence.
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
---
Updates vs version [18]:
- rework/fix function headers
- use memremap instead of ioremap for the resource table.
- realign comments to 80 chars limit, with few exceptions for readability
- replace spinlock by mutex and and protect APIs from concurrent access
- add support of 64-bit address in rproc_tee_get_loaded_rsc_table()
- Generalize teston rproc_tee_ctx.dev to prevent an unbind
- update copyright year
Updates vs version [17]:
Fix warning:
warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
---
drivers/remoteproc/Kconfig | 10 +
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/remoteproc_tee.c | 708 ++++++++++++++++++++++++++++
include/linux/remoteproc_tee.h | 87 ++++
4 files changed, 806 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 83962a114dc9..e39265d249d9 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 that is managed by an application running in a Trusted
+ Execution Environment (TEE). This application is responsible for loading the remote
+ processor firmware image and managing its lifecycle.
+
+ It's safe to say N if the remote processor is not managed by a TEE.
+
config IMX_REMOTEPROC
tristate "i.MX remoteproc support"
depends on ARCH_MXC
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 1c7598b8475d..a1a5201982d4 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..b9da911b8a83
--- /dev/null
+++ b/drivers/remoteproc/remoteproc_tee.c
@@ -0,0 +1,708 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) STMicroelectronics 2025
+ * Author: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
+ */
+
+#include <linux/export.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.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 and load of the firmware image in the remote processor
+ * memories by the TEE. After this step the firmware is installed in destination
+ * memories, which can then be locked to prevent access by Linux.
+ *
+ * [in] params[0].value.a: remote processor identifier
+ * [in] params[1].memref: buffer containing a temporary copy of the signed
+ * image to load.
+ */
+#define TA_RPROC_FW_CMD_LOAD_FW 1
+
+/*
+ * Start the remote processor by the TEE
+ *
+ * [in] params[0].value.a: remote processor identifier
+ */
+#define TA_RPROC_FW_CMD_START 2
+
+/*
+ * Stop the remote processor by the TEE
+ *
+ * [in] params[0].value.a: remote processor identifier
+ */
+#define TA_RPROC_FW_CMD_STOP 3
+
+/*
+ * Return the address of the resource table, or 0 if not found.
+ *
+ * [in] params[0].value.a: remote processor identifier
+ * [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
+
+/*
+ * 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: 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;
+};
+
+/**
+ * struct rproc_tee - TEE remoteproc structure
+ * @node: Reference in list
+ * @rproc: Remoteproc reference
+ * @rproc_id: remote processor identifier
+ * @session_id: TEE session identifier
+ */
+struct rproc_tee {
+ struct list_head node;
+ struct rproc *rproc;
+ u32 rproc_id;
+ u32 session_id;
+};
+
+static struct rproc_tee_context rproc_tee_ctx;
+static struct mutex (ctx_lock); /* Protects concurrent manipulations of the rproc_tee_ctx*/
+
+static struct rproc_tee *rproc_to_trproc(struct rproc *rproc)
+{
+ struct rproc_tee *trproc;
+
+ list_for_each_entry(trproc, &rproc_tee_ctx.sessions, node)
+ if (trproc->rproc == rproc)
+ return trproc;
+
+ return NULL;
+}
+
+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,
+ };
+}
+
+/**
+ * rproc_tee_release_fw() - Release the firmware for a TEE-based remote processor
+ * @rproc: Pointer to the struct rproc representing the remote processor
+ *
+ * This function invokes the TA_RPROC_CMD_RELEASE_FW TEE client function to
+ * release the firmware. It should only be called when the remoteproc state is
+ * RPROC_OFFLINE or RPROC_DETACHED. The function requests the TEE remoteproc
+ * application to release the firmware loaded by rproc_tee_load_fw().
+ * The request is ignored if the rproc state is RPROC_DETACHED as the remote
+ * processor is still running.
+ */
+void rproc_tee_release_fw(struct rproc *rproc)
+{
+ struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
+ struct rproc_tee *trproc;
+ struct tee_ioctl_invoke_arg arg;
+ int ret;
+
+ ret = mutex_lock_interruptible(&ctx_lock);
+ if (ret)
+ return;
+
+ if (!rproc_tee_ctx.dev)
+ goto out;
+
+ trproc = rproc_to_trproc(rproc);
+ if (!trproc)
+ 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)
+ goto out;
+
+ if (rproc->state != RPROC_OFFLINE) {
+ dev_err(rproc_tee_ctx.dev, "unexpected rproc state: %d\n", rproc->state);
+ 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:%d\n",
+ arg.ret, ret);
+ }
+
+out:
+ mutex_unlock(&ctx_lock);
+}
+EXPORT_SYMBOL_GPL(rproc_tee_release_fw);
+
+/**
+ * rproc_tee_load_fw() - Load firmware from TEE application
+ * @rproc: Pointer to the struct rproc representing the remote processor
+ * @fw: Pointer to the firmware structure containing the firmware data and size
+ *
+ * This function invokes the TA_RPROC_FW_CMD_LOAD_FW TEE client function to load
+ * the firmware. It registers the fw->data as a shared memory region with the
+ * TEE, and request the TEE to load the firmware. This function can be called
+ * twice during the remote processor boot, a first by rproc_tee_parse_fw() to
+ * parse the resource table , and a second time by rproc_tee_load_fw().
+ * The TEE application should ignores the command if the firmware
+ * is already loaded by rproc_tee_parse_fw().
+ *
+ * Return: 0 on success, or an error code on failure
+ */
+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;
+ struct tee_ioctl_invoke_arg arg;
+ struct tee_shm *fw_shm;
+ int ret;
+
+ ret = mutex_lock_interruptible(&ctx_lock);
+ if (ret)
+ return ret;
+
+ if (!rproc_tee_ctx.dev) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ trproc = rproc_to_trproc(rproc);
+ if (!trproc) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ fw_shm = tee_shm_register_kernel_buf(rproc_tee_ctx.tee_ctx, (void *)fw->data, fw->size);
+ if (IS_ERR(fw_shm)) {
+ ret = PTR_ERR(fw_shm);
+ goto out;
+ }
+
+ 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:%d\n",
+ arg.ret, ret);
+ if (!ret)
+ ret = -EIO;
+ }
+
+ tee_shm_free(fw_shm);
+
+out:
+ mutex_unlock(&ctx_lock);
+ 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;
+ struct tee_ioctl_invoke_arg arg;
+ int ret;
+
+ ret = mutex_lock_interruptible(&ctx_lock);
+ if (ret)
+ return ret;
+
+ if (!rproc_tee_ctx.dev) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ trproc = rproc_to_trproc(rproc);
+ if (!trproc) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ 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:%d\n",
+ arg.ret, ret);
+ ret = -EIO;
+ goto out;
+ }
+
+ *table_sz = param[2].u.value.a;
+ if (sizeof(phys_addr_t) == sizeof(u64))
+ *table_sz |= param[2].u.value.b << 32;
+
+ if (*table_sz) {
+ *rsc_pa = param[1].u.value.a;
+ if (sizeof(phys_addr_t) == sizeof(u64))
+ *rsc_pa |= param[1].u.value.b << 32;
+ } else {
+ *rsc_pa = 0;
+ }
+
+out:
+ mutex_unlock(&ctx_lock);
+ return ret;
+}
+
+/**
+ * rproc_tee_parse_fw() - Get the resource table from TEE application
+ * @rproc: Pointer to the struct rproc representing the remote processor
+ * @fw: Pointer to the firmware structure containing the firmware data and size
+ *
+ * This function retrieves the loaded resource table and creates a cached_table
+ * copy. Since the firmware image is signed and potentially encrypted, the
+ * firmware must be loaded first to access the loaded resource table.
+ *
+ * Return: 0 on success, or an error code on failure
+ */
+int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw)
+{
+ phys_addr_t rsc_table;
+ void *rsc_va;
+ size_t table_sz;
+ int ret;
+
+ if (!rproc)
+ return -EINVAL;
+
+ /* We need first to Load the firmware, to be able to get the resource table. */
+ ret = rproc_tee_load_fw(rproc, fw);
+ if (ret)
+ return ret;
+
+ 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 = memremap(rsc_table, table_sz, MEMREMAP_WC);
+ if (!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(rsc_va, table_sz, GFP_KERNEL);
+ memunmap(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);
+
+/**
+ * rproc_tee_find_loaded_rsc_table() - Find the loaded resource table loaded by
+ * the TEE application
+ * @rproc: Pointer to the struct rproc representing the remote processor
+ * @fw: Pointer to the firmware structure containing the firmware data and size
+ *
+ * This function retrieves the physical address and size of the resource table
+ * loaded by the TEE application.
+ *
+ * Return: pointer to the resource table if found, or NULL if not found or size
+ * is 0
+ */
+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 rproc_pa_to_va(rproc, rsc_table, table_sz, NULL);
+}
+EXPORT_SYMBOL_GPL(rproc_tee_find_loaded_rsc_table);
+
+/**
+ * rproc_tee_start() - Request the TEE application to start the remote processor
+ * @rproc: Pointer to the struct rproc representing the remote processor
+ *
+ * This function invokes the TA_RPROC_FW_CMD_START command to start the remote
+ * processor.
+ *
+ * Return: Returns 0 on success, -EINVAL or -EIO on failure
+ */
+int rproc_tee_start(struct rproc *rproc)
+{
+ struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
+ struct rproc_tee *trproc;
+ struct tee_ioctl_invoke_arg arg;
+ int ret;
+
+ ret = mutex_lock_interruptible(&ctx_lock);
+ if (ret)
+ return ret;
+
+ if (!rproc_tee_ctx.dev) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ trproc = rproc_to_trproc(rproc);
+ if (!trproc) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_START, &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 invoke failed TEE err: %#x, ret:%d\n", arg.ret, ret);
+ if (!ret)
+ ret = -EIO;
+ }
+
+out:
+ mutex_unlock(&ctx_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rproc_tee_start);
+
+/**
+ * rproc_tee_stop() - Request the TEE application to start the remote processor
+ * @rproc: Pointer to the struct rproc representing the remote processor
+ *
+ * This function invokes the TA_RPROC_FW_CMD_STOP command to stop the remote
+ * processor.
+ *
+ * Return: Returns 0 on success, -EINVAL or -EIO on failure
+ */
+int rproc_tee_stop(struct rproc *rproc)
+{
+ struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
+ struct rproc_tee *trproc;
+ struct tee_ioctl_invoke_arg arg;
+ int ret;
+
+ ret = mutex_lock_interruptible(&ctx_lock);
+ if (ret)
+ return ret;
+
+ if (!rproc_tee_ctx.dev) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ trproc = rproc_to_trproc(rproc);
+ if (!trproc) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_STOP, &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 invoke failed TEE err: %#x, ret:%d\n", arg.ret, ret);
+ if (!ret)
+ ret = -EIO;
+ }
+
+out:
+ mutex_unlock(&ctx_lock);
+ 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)},
+ {}
+};
+
+/**
+ * rproc_tee_register() - Register a remote processor controlled by the TEE application.
+ * @dev: Pointer to client rproc device
+ * @rproc: Pointer to the struct rproc representing the remote processor
+ * @rproc_id: ID of the remote processor
+ *
+ * This function registers a remote processor that will be managed by a TEE
+ * application,by opening a session with the TEE client.
+ *
+ * Return: Returns 0 on success, or an error code on failure
+ */
+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;
+ struct device_link *link;
+ int ret;
+
+ ret = mutex_lock_interruptible(&ctx_lock);
+ if (ret)
+ return 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.dev) {
+ ret = -EPROBE_DEFER;
+ goto out;
+ }
+
+ trproc = kzalloc(sizeof(*trproc), GFP_KERNEL);
+ if (!trproc) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ 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 free_tproc;
+ }
+
+ trproc->rproc_id = rproc_id;
+ trproc->session_id = sess_arg.session;
+
+ trproc->rproc = rproc;
+
+ /*
+ * Create device link between the rproc device and the TEE device to
+ * ensure that the rproc device is removed accordingly to a remove of
+ * the remoteproc tee device.
+ */
+ link = device_link_add(dev, rproc_tee_ctx.dev, DL_FLAG_AUTOREMOVE_CONSUMER);
+ if (!link) {
+ ret = -ENOMEM;
+ goto close_tee;
+ }
+ list_add_tail(&trproc->node, &rproc_tee_ctx.sessions);
+
+ mutex_unlock(&ctx_lock);
+
+ return 0;
+
+close_tee:
+ if (tee_client_close_session(rproc_tee_ctx.tee_ctx, trproc->session_id))
+ dev_err(rproc_tee_ctx.dev, "tee_client_close_session failed\n");
+free_tproc:
+ kfree(trproc);
+out:
+ mutex_unlock(&ctx_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rproc_tee_register);
+
+/**
+ * rproc_tee_unregister - Register a remote processor controlled by the TEE application.
+ * @dev: Pointer to client rproc device
+ * @rproc: Pointer to the struct rproc representing the remote processor
+ *
+ * This function unregisters a remote processor previously registered by the
+ * rproc_tee_register() function.
+ *
+ * Return: Returns 0 on success, or an error code on failure
+ */
+int rproc_tee_unregister(struct device *dev, struct rproc *rproc)
+{
+ struct rproc_tee *trproc;
+ int ret;
+
+ ret = mutex_lock_interruptible(&ctx_lock);
+ if (ret)
+ return ret;
+
+ if (!rproc_tee_ctx.dev) {
+ ret = -EPROBE_DEFER;
+ goto out;
+ }
+
+ trproc = rproc_to_trproc(rproc);
+ if (!trproc) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = tee_client_close_session(rproc_tee_ctx.tee_ctx, trproc->session_id);
+ if (ret < 0)
+ dev_err(rproc_tee_ctx.dev, "tee_client_close_session failed, err: %#x\n", ret);
+
+ list_del(&trproc->node);
+ kfree(trproc);
+
+out:
+ mutex_unlock(&ctx_lock);
+ 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);
+
+ ret = mutex_lock_interruptible(&ctx_lock);
+ if (ret)
+ return ret;
+
+ rproc_tee_ctx.dev = dev;
+ rproc_tee_ctx.tee_ctx = tee_ctx;
+ INIT_LIST_HEAD(&rproc_tee_ctx.sessions);
+ mutex_unlock(&ctx_lock);
+
+ return 0;
+}
+
+static int rproc_tee_remove(struct device *dev)
+{
+ mutex_lock(&ctx_lock);
+ tee_client_close_context(rproc_tee_ctx.tee_ctx);
+ rproc_tee_ctx.dev = NULL;
+ rproc_tee_ctx.tee_ctx = NULL;
+ mutex_unlock(&ctx_lock);
+
+ 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)
+{
+ mutex_init(&ctx_lock);
+
+ 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_tee.h b/include/linux/remoteproc_tee.h
new file mode 100644
index 000000000000..a5fdc1627774
--- /dev/null
+++ b/include/linux/remoteproc_tee.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright(c) 2025 STMicroelectronics
+ */
+
+#ifndef REMOTEPROC_TEE_H
+#define REMOTEPROC_TEE_H
+
+#include <linux/tee_drv.h>
+#include <linux/firmware.h>
+#include <linux/remoteproc.h>
+
+#if IS_ENABLED(CONFIG_REMOTEPROC_TEE)
+
+int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id);
+int rproc_tee_unregister(struct device *dev, 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 device *dev, 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] 15+ messages in thread
* [PATCH v19 3/6] remoteproc: Introduce optional release_fw operation
2025-06-25 9:40 [PATCH v19 0/6] Introduction of a remoteproc tee to load signed firmware Arnaud Pouliquen
2025-06-25 9:40 ` [PATCH v19 1/6] remoteproc: core: Introduce rproc_pa_to_va helper Arnaud Pouliquen
2025-06-25 9:40 ` [PATCH v19 2/6] remoteproc: Add TEE support Arnaud Pouliquen
@ 2025-06-25 9:40 ` Arnaud Pouliquen
2025-06-25 9:40 ` [PATCH v19 4/6] dt-bindings: remoteproc: Add compatibility for TEE support Arnaud Pouliquen
` (2 subsequent siblings)
5 siblings, 0 replies; 15+ messages in thread
From: Arnaud Pouliquen @ 2025-06-25 9:40 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier, Jens Wiklander, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-stm32, linux-arm-kernel, linux-remoteproc, linux-kernel,
op-tee, devicetree, Arnaud Pouliquen
The release_fw operation allows releasing remote processor resources
configured during rproc_parse_fw() and rproc_load_segments().
For example, it can release carveouts mapped during these operations.
This operation is invoked in the following cases:
- When an error occurs during remote processor boot.
- When an error occurs during remote processor recovery start.
- After stopping the remote processor.
This operation is required for the remoteproc_tee implementation following
a stop or upon encountering an error. Since the remoteproc image is loaded
during resource table parsing, multiple failure scenarios may occur prior
to remote processor startup, including issues with resource handling and
carveout allocation.
Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
---
Updates from version 18:
- Rewrite rproc_ops::release_fw documentation
- Improve commit message
Updates from version 16:
- Removed the rproc:load_fw() ops introduced in previous version.
- Removed duplicate calls to rproc_release_fw in rproc_fw_boot and rproc_boot.
---
drivers/remoteproc/remoteproc_core.c | 6 ++++++
drivers/remoteproc/remoteproc_internal.h | 6 ++++++
include/linux/remoteproc.h | 5 +++++
3 files changed, 17 insertions(+)
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index d06eef1fa424..4c1a4bc9e7b7 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -1857,6 +1857,8 @@ static int rproc_boot_recovery(struct rproc *rproc)
/* boot the remote processor up again */
ret = rproc_start(rproc, firmware_p);
+ if (ret)
+ rproc_release_fw(rproc);
release_firmware(firmware_p);
@@ -1998,6 +2000,8 @@ int rproc_boot(struct rproc *rproc)
}
ret = rproc_fw_boot(rproc, firmware_p);
+ if (ret)
+ rproc_release_fw(rproc);
release_firmware(firmware_p);
}
@@ -2067,6 +2071,8 @@ int rproc_shutdown(struct rproc *rproc)
rproc_disable_iommu(rproc);
+ rproc_release_fw(rproc);
+
/* Free the copy of the resource table */
kfree(rproc->cached_table);
rproc->cached_table = NULL;
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
index 0cd09e67ac14..c7fb908f8652 100644
--- a/drivers/remoteproc/remoteproc_internal.h
+++ b/drivers/remoteproc/remoteproc_internal.h
@@ -221,4 +221,10 @@ 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);
+}
+
#endif /* REMOTEPROC_INTERNAL_H */
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index 8fd0d7f63c8e..a2bb51a113b1 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
+ * @release_fw: Optional function to release resources allocated during
+ * parse_fw() or load() operations. This function is called after
+ * stopping the remote processor or in case of an error during the
+ * boot or recovery sequence.
*/
struct rproc_ops {
int (*prepare)(struct rproc *rproc);
@@ -403,6 +407,7 @@ 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);
+ void (*release_fw)(struct rproc *rproc);
};
/**
--
2.25.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v19 4/6] dt-bindings: remoteproc: Add compatibility for TEE support
2025-06-25 9:40 [PATCH v19 0/6] Introduction of a remoteproc tee to load signed firmware Arnaud Pouliquen
` (2 preceding siblings ...)
2025-06-25 9:40 ` [PATCH v19 3/6] remoteproc: Introduce optional release_fw operation Arnaud Pouliquen
@ 2025-06-25 9:40 ` Arnaud Pouliquen
2025-06-25 9:40 ` [PATCH v19 5/6] remoteproc: stm32: Create sub-functions to request shutdown and release Arnaud Pouliquen
2025-06-25 9:40 ` [PATCH v19 6/6] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware Arnaud Pouliquen
5 siblings, 0 replies; 15+ messages in thread
From: Arnaud Pouliquen @ 2025-06-25 9:40 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier, Jens Wiklander, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-stm32, linux-arm-kernel, linux-remoteproc, linux-kernel,
op-tee, devicetree, Arnaud Pouliquen
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 843679c557e7..58da07e536fc 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:
@@ -146,21 +155,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
@@ -192,5 +223,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] 15+ messages in thread
* [PATCH v19 5/6] remoteproc: stm32: Create sub-functions to request shutdown and release
2025-06-25 9:40 [PATCH v19 0/6] Introduction of a remoteproc tee to load signed firmware Arnaud Pouliquen
` (3 preceding siblings ...)
2025-06-25 9:40 ` [PATCH v19 4/6] dt-bindings: remoteproc: Add compatibility for TEE support Arnaud Pouliquen
@ 2025-06-25 9:40 ` Arnaud Pouliquen
2025-06-25 9:40 ` [PATCH v19 6/6] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware Arnaud Pouliquen
5 siblings, 0 replies; 15+ messages in thread
From: Arnaud Pouliquen @ 2025-06-25 9:40 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier, Jens Wiklander, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-stm32, linux-arm-kernel, linux-remoteproc, linux-kernel,
op-tee, devicetree, Arnaud Pouliquen
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 431648607d53..db82d4308376 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] 15+ messages in thread
* [PATCH v19 6/6] remoteproc: stm32: Add support of an OP-TEE TA to load the firmware
2025-06-25 9:40 [PATCH v19 0/6] Introduction of a remoteproc tee to load signed firmware Arnaud Pouliquen
` (4 preceding siblings ...)
2025-06-25 9:40 ` [PATCH v19 5/6] remoteproc: stm32: Create sub-functions to request shutdown and release Arnaud Pouliquen
@ 2025-06-25 9:40 ` Arnaud Pouliquen
5 siblings, 0 replies; 15+ messages in thread
From: Arnaud Pouliquen @ 2025-06-25 9:40 UTC (permalink / raw)
To: Bjorn Andersson, Mathieu Poirier, Jens Wiklander, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-stm32, linux-arm-kernel, linux-remoteproc, linux-kernel,
op-tee, devicetree, Arnaud Pouliquen
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>
---
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 db82d4308376..a3613c337264 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 = 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);
@@ -854,6 +880,7 @@ static int stm32_rproc_probe(struct platform_device *pdev)
const char *fw_name;
struct rproc *rproc;
unsigned int state;
+ u32 proc_id;
int ret;
ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
@@ -865,9 +892,29 @@ static int stm32_rproc_probe(struct platform_device *pdev)
if (ret < 0 && ret != -EINVAL)
return ret;
- rproc = devm_rproc_alloc(dev, np->name, &st_rproc_ops, fw_name, 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, fw_name, 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, fw_name, sizeof(*ddata));
+ if (!rproc)
+ return -ENOMEM;
+ }
ddata = rproc->priv;
@@ -919,6 +966,8 @@ static int stm32_rproc_probe(struct platform_device *pdev)
dev_pm_clear_wake_irq(dev);
device_init_wakeup(dev, false);
}
+ rproc_tee_unregister(dev, rproc);
+
return ret;
}
@@ -939,6 +988,8 @@ static void stm32_rproc_remove(struct platform_device *pdev)
dev_pm_clear_wake_irq(dev);
device_init_wakeup(dev, false);
}
+
+ rproc_tee_unregister(dev, rproc);
}
static int stm32_rproc_suspend(struct device *dev)
--
2.25.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH v19 2/6] remoteproc: Add TEE support
2025-06-25 9:40 ` [PATCH v19 2/6] remoteproc: Add TEE support Arnaud Pouliquen
@ 2025-07-31 10:25 ` Harshal Dev
2025-08-01 7:23 ` Arnaud POULIQUEN
2025-08-13 16:42 ` Abdellatif El Khlifi
1 sibling, 1 reply; 15+ messages in thread
From: Harshal Dev @ 2025-07-31 10:25 UTC (permalink / raw)
To: Arnaud Pouliquen, Bjorn Andersson, Mathieu Poirier,
Jens Wiklander, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-stm32, linux-arm-kernel, linux-remoteproc, linux-kernel,
op-tee, devicetree
Hello Arnaud,
On 6/25/2025 3:10 PM, 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 the secure part, this driver offers a client interface to load firmware
> by the secure part.
> This firmware could be authenticated by the secure trusted application.
>
> A specificity of the implementation is that the firmware has to be
> authenticated and optionally decrypted to access the resource table.
>
> Consequently, the boot sequence is:
>
> 1) rproc_parse_fw --> rproc_tee_parse_fw
> remoteproc TEE:
> - Requests the TEE application to authenticate and load the firmware
> in the remote processor memories.
> - Requests the TEE application for the address of the resource table.
> - Creates a copy of the resource table stored in rproc->cached_table.
>
> 2) rproc_load_segments --> rproc_tee_load_fw
> remoteproc TEE:
> - Requests the TEE application to load the firmware. Nothing is done
> at the TEE application as the firmware is already loaded.
> - In case of recovery, the TEE application has to reload the firmware.
>
> 3) rproc_tee_get_loaded_rsc_table
> remoteproc TEE requests the TEE application for the address of the
> resource table.
>
> 4) rproc_start --> rproc_tee_start
> - Requests the TEE application to start the remote processor.
>
> The shutdown sequence is:
>
> 5) rproc_stop --> rproc_tee_stop
> - Requests the TEE application to stop the remote processor.
>
> 6) rproc_tee_release_fw
> This function is used to request the TEE application to perform actions
> to return to the initial state on stop or on error during the boot
> sequence.
>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> ---
> Updates vs version [18]:
> - rework/fix function headers
> - use memremap instead of ioremap for the resource table.
> - realign comments to 80 chars limit, with few exceptions for readability
> - replace spinlock by mutex and and protect APIs from concurrent access
> - add support of 64-bit address in rproc_tee_get_loaded_rsc_table()
> - Generalize teston rproc_tee_ctx.dev to prevent an unbind
> - update copyright year
>
> Updates vs version [17]:
> Fix warning:
> warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
> ---
> drivers/remoteproc/Kconfig | 10 +
> drivers/remoteproc/Makefile | 1 +
> drivers/remoteproc/remoteproc_tee.c | 708 ++++++++++++++++++++++++++++
> include/linux/remoteproc_tee.h | 87 ++++
> 4 files changed, 806 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 83962a114dc9..e39265d249d9 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 that is managed by an application running in a Trusted
> + Execution Environment (TEE). This application is responsible for loading the remote
> + processor firmware image and managing its lifecycle.
> +
> + It's safe to say N if the remote processor is not managed by a TEE.
> +
> config IMX_REMOTEPROC
> tristate "i.MX remoteproc support"
> depends on ARCH_MXC
> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
> index 1c7598b8475d..a1a5201982d4 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..b9da911b8a83
> --- /dev/null
> +++ b/drivers/remoteproc/remoteproc_tee.c
> @@ -0,0 +1,708 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) STMicroelectronics 2025
> + * Author: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> + */
> +
> +#include <linux/export.h>
> +#include <linux/firmware.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.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 and load of the firmware image in the remote processor
> + * memories by the TEE. After this step the firmware is installed in destination
> + * memories, which can then be locked to prevent access by Linux.
> + *
> + * [in] params[0].value.a: remote processor identifier
> + * [in] params[1].memref: buffer containing a temporary copy of the signed
> + * image to load.
> + */
> +#define TA_RPROC_FW_CMD_LOAD_FW 1
> +
> +/*
> + * Start the remote processor by the TEE
> + *
> + * [in] params[0].value.a: remote processor identifier
> + */
> +#define TA_RPROC_FW_CMD_START 2
> +
> +/*
> + * Stop the remote processor by the TEE
> + *
> + * [in] params[0].value.a: remote processor identifier
> + */
> +#define TA_RPROC_FW_CMD_STOP 3
> +
> +/*
> + * Return the address of the resource table, or 0 if not found.
> + *
> + * [in] params[0].value.a: remote processor identifier
> + * [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
> +
> +/*
> + * 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: 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;
> +};
> +
> +/**
> + * struct rproc_tee - TEE remoteproc structure
> + * @node: Reference in list
> + * @rproc: Remoteproc reference
> + * @rproc_id: remote processor identifier
> + * @session_id: TEE session identifier
> + */
> +struct rproc_tee {
> + struct list_head node;
> + struct rproc *rproc;
> + u32 rproc_id;
> + u32 session_id;
> +};
> +
> +static struct rproc_tee_context rproc_tee_ctx;
> +static struct mutex (ctx_lock); /* Protects concurrent manipulations of the rproc_tee_ctx*/
> +
Since ctx_lock is protecting struct rproc_tee_ctx, can we place it within the struct?
> +static struct rproc_tee *rproc_to_trproc(struct rproc *rproc)
> +{
> + struct rproc_tee *trproc;
> +
> + list_for_each_entry(trproc, &rproc_tee_ctx.sessions, node)
> + if (trproc->rproc == rproc)
> + return trproc;
> +
> + return NULL;
> +}
> +
> +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,
> + };
> +}
> +
> +/**
> + * rproc_tee_release_fw() - Release the firmware for a TEE-based remote processor
> + * @rproc: Pointer to the struct rproc representing the remote processor
> + *
> + * This function invokes the TA_RPROC_CMD_RELEASE_FW TEE client function to
> + * release the firmware. It should only be called when the remoteproc state is
> + * RPROC_OFFLINE or RPROC_DETACHED. The function requests the TEE remoteproc
> + * application to release the firmware loaded by rproc_tee_load_fw().
> + * The request is ignored if the rproc state is RPROC_DETACHED as the remote
> + * processor is still running.
> + */
> +void rproc_tee_release_fw(struct rproc *rproc)
> +{
> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
> + struct rproc_tee *trproc;
> + struct tee_ioctl_invoke_arg arg;
> + int ret;
> +
> + ret = mutex_lock_interruptible(&ctx_lock);
> + if (ret)
> + return;
> +
> + if (!rproc_tee_ctx.dev)
> + goto out;
> +
> + trproc = rproc_to_trproc(rproc);
> + if (!trproc)
> + 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)
> + goto out;
> +
> + if (rproc->state != RPROC_OFFLINE) {
> + dev_err(rproc_tee_ctx.dev, "unexpected rproc state: %d\n", rproc->state);
> + 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:%d\n",
> + arg.ret, ret);
> + }
> +
> +out:
> + mutex_unlock(&ctx_lock);
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_release_fw);
> +
> +/**
> + * rproc_tee_load_fw() - Load firmware from TEE application
> + * @rproc: Pointer to the struct rproc representing the remote processor
> + * @fw: Pointer to the firmware structure containing the firmware data and size
> + *
> + * This function invokes the TA_RPROC_FW_CMD_LOAD_FW TEE client function to load
> + * the firmware. It registers the fw->data as a shared memory region with the
> + * TEE, and request the TEE to load the firmware. This function can be called
> + * twice during the remote processor boot, a first by rproc_tee_parse_fw() to
> + * parse the resource table , and a second time by rproc_tee_load_fw().
> + * The TEE application should ignores the command if the firmware
> + * is already loaded by rproc_tee_parse_fw().
> + *
> + * Return: 0 on success, or an error code on failure
> + */
> +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;
> + struct tee_ioctl_invoke_arg arg;
> + struct tee_shm *fw_shm;
> + int ret;
> +
> + ret = mutex_lock_interruptible(&ctx_lock);
> + if (ret)
> + return ret;
> +
> + if (!rproc_tee_ctx.dev) {
> + ret = -ENODEV;
> + goto out;
> + }
> +
> + trproc = rproc_to_trproc(rproc);
> + if (!trproc) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + fw_shm = tee_shm_register_kernel_buf(rproc_tee_ctx.tee_ctx, (void *)fw->data, fw->size);
> + if (IS_ERR(fw_shm)) {
> + ret = PTR_ERR(fw_shm);
> + goto out;
> + }
> +
> + 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:%d\n",
> + arg.ret, ret);
> + if (!ret)
> + ret = -EIO;
> + }
> +
> + tee_shm_free(fw_shm);
> +
> +out:
> + mutex_unlock(&ctx_lock);
> + 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;
> + struct tee_ioctl_invoke_arg arg;
> + int ret;
> +
> + ret = mutex_lock_interruptible(&ctx_lock);
> + if (ret)
> + return ret;
> +
> + if (!rproc_tee_ctx.dev) {
> + ret = -ENODEV;
> + goto out;
> + }
> +
> + trproc = rproc_to_trproc(rproc);
> + if (!trproc) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + 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:%d\n",
> + arg.ret, ret);
> + ret = -EIO;
> + goto out;
> + }
> +
> + *table_sz = param[2].u.value.a;
> + if (sizeof(phys_addr_t) == sizeof(u64))
> + *table_sz |= param[2].u.value.b << 32;
> +
> + if (*table_sz) {
> + *rsc_pa = param[1].u.value.a;
> + if (sizeof(phys_addr_t) == sizeof(u64))
> + *rsc_pa |= param[1].u.value.b << 32;
> + } else {
> + *rsc_pa = 0;
> + }
> +
> +out:
> + mutex_unlock(&ctx_lock);
> + return ret;
> +}
> +
> +/**
> + * rproc_tee_parse_fw() - Get the resource table from TEE application
> + * @rproc: Pointer to the struct rproc representing the remote processor
> + * @fw: Pointer to the firmware structure containing the firmware data and size
> + *
> + * This function retrieves the loaded resource table and creates a cached_table
> + * copy. Since the firmware image is signed and potentially encrypted, the
> + * firmware must be loaded first to access the loaded resource table.
> + *
> + * Return: 0 on success, or an error code on failure
> + */
> +int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw)
> +{
> + phys_addr_t rsc_table;
> + void *rsc_va;
> + size_t table_sz;
> + int ret;
> +
> + if (!rproc)
> + return -EINVAL;
> +
> + /* We need first to Load the firmware, to be able to get the resource table. */
> + ret = rproc_tee_load_fw(rproc, fw);
> + if (ret)
> + return ret;
> +
> + 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 = memremap(rsc_table, table_sz, MEMREMAP_WC);
> + if (!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(rsc_va, table_sz, GFP_KERNEL);
> + memunmap(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);
> +
> +/**
> + * rproc_tee_find_loaded_rsc_table() - Find the loaded resource table loaded by
> + * the TEE application
> + * @rproc: Pointer to the struct rproc representing the remote processor
> + * @fw: Pointer to the firmware structure containing the firmware data and size
> + *
> + * This function retrieves the physical address and size of the resource table
> + * loaded by the TEE application.
> + *
> + * Return: pointer to the resource table if found, or NULL if not found or size
> + * is 0
> + */
> +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 rproc_pa_to_va(rproc, rsc_table, table_sz, NULL);
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_find_loaded_rsc_table);
> +
> +/**
> + * rproc_tee_start() - Request the TEE application to start the remote processor
> + * @rproc: Pointer to the struct rproc representing the remote processor
> + *
> + * This function invokes the TA_RPROC_FW_CMD_START command to start the remote
> + * processor.
> + *
> + * Return: Returns 0 on success, -EINVAL or -EIO on failure
> + */
> +int rproc_tee_start(struct rproc *rproc)
> +{
> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
> + struct rproc_tee *trproc;
> + struct tee_ioctl_invoke_arg arg;
> + int ret;
> +
> + ret = mutex_lock_interruptible(&ctx_lock);
> + if (ret)
> + return ret;
> +
> + if (!rproc_tee_ctx.dev) {
> + ret = -ENODEV;
> + goto out;
> + }
> +
> + trproc = rproc_to_trproc(rproc);
> + if (!trproc) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_START, &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 invoke failed TEE err: %#x, ret:%d\n", arg.ret, ret);
> + if (!ret)
> + ret = -EIO;
> + }
> +
> +out:
> + mutex_unlock(&ctx_lock);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_start);
> +
> +/**
> + * rproc_tee_stop() - Request the TEE application to start the remote processor
> + * @rproc: Pointer to the struct rproc representing the remote processor
> + *
> + * This function invokes the TA_RPROC_FW_CMD_STOP command to stop the remote
> + * processor.
> + *
> + * Return: Returns 0 on success, -EINVAL or -EIO on failure
> + */
> +int rproc_tee_stop(struct rproc *rproc)
> +{
> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
> + struct rproc_tee *trproc;
> + struct tee_ioctl_invoke_arg arg;
> + int ret;
> +
> + ret = mutex_lock_interruptible(&ctx_lock);
> + if (ret)
> + return ret;
> +
> + if (!rproc_tee_ctx.dev) {
> + ret = -ENODEV;
> + goto out;
> + }
> +
> + trproc = rproc_to_trproc(rproc);
> + if (!trproc) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_STOP, &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 invoke failed TEE err: %#x, ret:%d\n", arg.ret, ret);
> + if (!ret)
> + ret = -EIO;
> + }
> +
> +out:
> + mutex_unlock(&ctx_lock);
> + 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)},
> + {}
> +};
> +
> +/**
> + * rproc_tee_register() - Register a remote processor controlled by the TEE application.
> + * @dev: Pointer to client rproc device
> + * @rproc: Pointer to the struct rproc representing the remote processor
> + * @rproc_id: ID of the remote processor
> + *
> + * This function registers a remote processor that will be managed by a TEE
> + * application,by opening a session with the TEE client.
> + *
> + * Return: Returns 0 on success, or an error code on failure
> + */
> +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;
> + struct device_link *link;
> + int ret;
> +
> + ret = mutex_lock_interruptible(&ctx_lock);
> + if (ret)
> + return 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.dev) {
> + ret = -EPROBE_DEFER;
> + goto out;
> + }
> +
> + trproc = kzalloc(sizeof(*trproc), GFP_KERNEL);
> + if (!trproc) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + 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 free_tproc;
> + }
> +
> + trproc->rproc_id = rproc_id;
> + trproc->session_id = sess_arg.session;
> +
> + trproc->rproc = rproc;
> +
> + /*
> + * Create device link between the rproc device and the TEE device to
> + * ensure that the rproc device is removed accordingly to a remove of
> + * the remoteproc tee device.
> + */
> + link = device_link_add(dev, rproc_tee_ctx.dev, DL_FLAG_AUTOREMOVE_CONSUMER);
> + if (!link) {
> + ret = -ENOMEM;
> + goto close_tee;
> + }
> + list_add_tail(&trproc->node, &rproc_tee_ctx.sessions);
> +
> + mutex_unlock(&ctx_lock);
> +
> + return 0;
> +
> +close_tee:
> + if (tee_client_close_session(rproc_tee_ctx.tee_ctx, trproc->session_id))
> + dev_err(rproc_tee_ctx.dev, "tee_client_close_session failed\n");
> +free_tproc:
> + kfree(trproc);
> +out:
> + mutex_unlock(&ctx_lock);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(rproc_tee_register);
> +
> +/**
> + * rproc_tee_unregister - Register a remote processor controlled by the TEE application.
> + * @dev: Pointer to client rproc device
> + * @rproc: Pointer to the struct rproc representing the remote processor
> + *
> + * This function unregisters a remote processor previously registered by the
> + * rproc_tee_register() function.
> + *
> + * Return: Returns 0 on success, or an error code on failure
> + */
> +int rproc_tee_unregister(struct device *dev, struct rproc *rproc)
> +{
> + struct rproc_tee *trproc;
> + int ret;
> +
> + ret = mutex_lock_interruptible(&ctx_lock);
> + if (ret)
> + return ret;
> +
> + if (!rproc_tee_ctx.dev) {
> + ret = -EPROBE_DEFER;
> + goto out;
> + }
> +
> + trproc = rproc_to_trproc(rproc);
> + if (!trproc) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + ret = tee_client_close_session(rproc_tee_ctx.tee_ctx, trproc->session_id);
> + if (ret < 0)
> + dev_err(rproc_tee_ctx.dev, "tee_client_close_session failed, err: %#x\n", ret);
> +
> + list_del(&trproc->node);
> + kfree(trproc);
> +
> +out:
> + mutex_unlock(&ctx_lock);
> + 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);
> +
> + ret = mutex_lock_interruptible(&ctx_lock);
> + if (ret)
> + return ret;
> +
> + rproc_tee_ctx.dev = dev;
> + rproc_tee_ctx.tee_ctx = tee_ctx;
> + INIT_LIST_HEAD(&rproc_tee_ctx.sessions);
> + mutex_unlock(&ctx_lock);
> +
> + return 0;
> +}
As you mentioned above, this could be extended to other TEEs. If so, is it possible for probe
to be called multiple times if we we have other TEE devices exposing the firmware authentication
service? In that case, I think rproc_tee_ctx should be dynamically initializated instead of being
static. And since we are creating a link between the Rproc device and TEE device, a call to a
function like rproc_tee_start() could retreive the associated TEE device, and then the associated
rproc_tee? :)
Thanks,
Harshal
> +
> +static int rproc_tee_remove(struct device *dev)
> +{
> + mutex_lock(&ctx_lock);
> + tee_client_close_context(rproc_tee_ctx.tee_ctx);
> + rproc_tee_ctx.dev = NULL;
> + rproc_tee_ctx.tee_ctx = NULL;
> + mutex_unlock(&ctx_lock);
> +
> + 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)
> +{
> + mutex_init(&ctx_lock);
> +
> + 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_tee.h b/include/linux/remoteproc_tee.h
> new file mode 100644
> index 000000000000..a5fdc1627774
> --- /dev/null
> +++ b/include/linux/remoteproc_tee.h
> @@ -0,0 +1,87 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright(c) 2025 STMicroelectronics
> + */
> +
> +#ifndef REMOTEPROC_TEE_H
> +#define REMOTEPROC_TEE_H
> +
> +#include <linux/tee_drv.h>
> +#include <linux/firmware.h>
> +#include <linux/remoteproc.h>
> +
> +#if IS_ENABLED(CONFIG_REMOTEPROC_TEE)
> +
> +int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id);
> +int rproc_tee_unregister(struct device *dev, 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 device *dev, 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 */
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v19 2/6] remoteproc: Add TEE support
2025-07-31 10:25 ` Harshal Dev
@ 2025-08-01 7:23 ` Arnaud POULIQUEN
2025-08-04 9:26 ` Harshal Dev
0 siblings, 1 reply; 15+ messages in thread
From: Arnaud POULIQUEN @ 2025-08-01 7:23 UTC (permalink / raw)
To: Harshal Dev, Bjorn Andersson, Mathieu Poirier, Jens Wiklander,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-stm32, linux-arm-kernel, linux-remoteproc, linux-kernel,
op-tee, devicetree
Hello Harshal,
On 7/31/25 12:25, Harshal Dev wrote:
> Hello Arnaud,
>
> On 6/25/2025 3:10 PM, 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 the secure part, this driver offers a client interface to load firmware
>> by the secure part.
>> This firmware could be authenticated by the secure trusted application.
>>
>> A specificity of the implementation is that the firmware has to be
>> authenticated and optionally decrypted to access the resource table.
>>
>> Consequently, the boot sequence is:
>>
>> 1) rproc_parse_fw --> rproc_tee_parse_fw
>> remoteproc TEE:
>> - Requests the TEE application to authenticate and load the firmware
>> in the remote processor memories.
>> - Requests the TEE application for the address of the resource table.
>> - Creates a copy of the resource table stored in rproc->cached_table.
>>
>> 2) rproc_load_segments --> rproc_tee_load_fw
>> remoteproc TEE:
>> - Requests the TEE application to load the firmware. Nothing is done
>> at the TEE application as the firmware is already loaded.
>> - In case of recovery, the TEE application has to reload the firmware.
>>
>> 3) rproc_tee_get_loaded_rsc_table
>> remoteproc TEE requests the TEE application for the address of the
>> resource table.
>>
>> 4) rproc_start --> rproc_tee_start
>> - Requests the TEE application to start the remote processor.
>>
>> The shutdown sequence is:
>>
>> 5) rproc_stop --> rproc_tee_stop
>> - Requests the TEE application to stop the remote processor.
>>
>> 6) rproc_tee_release_fw
>> This function is used to request the TEE application to perform actions
>> to return to the initial state on stop or on error during the boot
>> sequence.
>>
>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>> ---
>> Updates vs version [18]:
>> - rework/fix function headers
>> - use memremap instead of ioremap for the resource table.
>> - realign comments to 80 chars limit, with few exceptions for readability
>> - replace spinlock by mutex and and protect APIs from concurrent access
>> - add support of 64-bit address in rproc_tee_get_loaded_rsc_table()
>> - Generalize teston rproc_tee_ctx.dev to prevent an unbind
>> - update copyright year
>>
>> Updates vs version [17]:
>> Fix warning:
>> warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
>> ---
>> drivers/remoteproc/Kconfig | 10 +
>> drivers/remoteproc/Makefile | 1 +
>> drivers/remoteproc/remoteproc_tee.c | 708 ++++++++++++++++++++++++++++
>> include/linux/remoteproc_tee.h | 87 ++++
>> 4 files changed, 806 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 83962a114dc9..e39265d249d9 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 that is managed by an application running in a Trusted
>> + Execution Environment (TEE). This application is responsible for loading the remote
>> + processor firmware image and managing its lifecycle.
>> +
>> + It's safe to say N if the remote processor is not managed by a TEE.
>> +
>> config IMX_REMOTEPROC
>> tristate "i.MX remoteproc support"
>> depends on ARCH_MXC
>> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
>> index 1c7598b8475d..a1a5201982d4 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..b9da911b8a83
>> --- /dev/null
>> +++ b/drivers/remoteproc/remoteproc_tee.c
>> @@ -0,0 +1,708 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * Copyright (C) STMicroelectronics 2025
>> + * Author: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>> + */
>> +
>> +#include <linux/export.h>
>> +#include <linux/firmware.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.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 and load of the firmware image in the remote processor
>> + * memories by the TEE. After this step the firmware is installed in destination
>> + * memories, which can then be locked to prevent access by Linux.
>> + *
>> + * [in] params[0].value.a: remote processor identifier
>> + * [in] params[1].memref: buffer containing a temporary copy of the signed
>> + * image to load.
>> + */
>> +#define TA_RPROC_FW_CMD_LOAD_FW 1
>> +
>> +/*
>> + * Start the remote processor by the TEE
>> + *
>> + * [in] params[0].value.a: remote processor identifier
>> + */
>> +#define TA_RPROC_FW_CMD_START 2
>> +
>> +/*
>> + * Stop the remote processor by the TEE
>> + *
>> + * [in] params[0].value.a: remote processor identifier
>> + */
>> +#define TA_RPROC_FW_CMD_STOP 3
>> +
>> +/*
>> + * Return the address of the resource table, or 0 if not found.
>> + *
>> + * [in] params[0].value.a: remote processor identifier
>> + * [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
>> +
>> +/*
>> + * 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: 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;
>> +};
>> +
>> +/**
>> + * struct rproc_tee - TEE remoteproc structure
>> + * @node: Reference in list
>> + * @rproc: Remoteproc reference
>> + * @rproc_id: remote processor identifier
>> + * @session_id: TEE session identifier
>> + */
>> +struct rproc_tee {
>> + struct list_head node;
>> + struct rproc *rproc;
>> + u32 rproc_id;
>> + u32 session_id;
>> +};
>> +
>> +static struct rproc_tee_context rproc_tee_ctx;
>> +static struct mutex (ctx_lock); /* Protects concurrent manipulations of the rproc_tee_ctx*/
>> +
>
> Since ctx_lock is protecting struct rproc_tee_ctx, can we place it within the struct?
>
>> +static struct rproc_tee *rproc_to_trproc(struct rproc *rproc)
>> +{
>> + struct rproc_tee *trproc;
>> +
>> + list_for_each_entry(trproc, &rproc_tee_ctx.sessions, node)
>> + if (trproc->rproc == rproc)
>> + return trproc;
>> +
>> + return NULL;
>> +}
>> +
>> +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,
>> + };
>> +}
>> +
>> +/**
>> + * rproc_tee_release_fw() - Release the firmware for a TEE-based remote processor
>> + * @rproc: Pointer to the struct rproc representing the remote processor
>> + *
>> + * This function invokes the TA_RPROC_CMD_RELEASE_FW TEE client function to
>> + * release the firmware. It should only be called when the remoteproc state is
>> + * RPROC_OFFLINE or RPROC_DETACHED. The function requests the TEE remoteproc
>> + * application to release the firmware loaded by rproc_tee_load_fw().
>> + * The request is ignored if the rproc state is RPROC_DETACHED as the remote
>> + * processor is still running.
>> + */
>> +void rproc_tee_release_fw(struct rproc *rproc)
>> +{
>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>> + struct rproc_tee *trproc;
>> + struct tee_ioctl_invoke_arg arg;
>> + int ret;
>> +
>> + ret = mutex_lock_interruptible(&ctx_lock);
>> + if (ret)
>> + return;
>> +
>> + if (!rproc_tee_ctx.dev)
>> + goto out;
>> +
>> + trproc = rproc_to_trproc(rproc);
>> + if (!trproc)
>> + 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)
>> + goto out;
>> +
>> + if (rproc->state != RPROC_OFFLINE) {
>> + dev_err(rproc_tee_ctx.dev, "unexpected rproc state: %d\n", rproc->state);
>> + 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:%d\n",
>> + arg.ret, ret);
>> + }
>> +
>> +out:
>> + mutex_unlock(&ctx_lock);
>> +}
>> +EXPORT_SYMBOL_GPL(rproc_tee_release_fw);
>> +
>> +/**
>> + * rproc_tee_load_fw() - Load firmware from TEE application
>> + * @rproc: Pointer to the struct rproc representing the remote processor
>> + * @fw: Pointer to the firmware structure containing the firmware data and size
>> + *
>> + * This function invokes the TA_RPROC_FW_CMD_LOAD_FW TEE client function to load
>> + * the firmware. It registers the fw->data as a shared memory region with the
>> + * TEE, and request the TEE to load the firmware. This function can be called
>> + * twice during the remote processor boot, a first by rproc_tee_parse_fw() to
>> + * parse the resource table , and a second time by rproc_tee_load_fw().
>> + * The TEE application should ignores the command if the firmware
>> + * is already loaded by rproc_tee_parse_fw().
>> + *
>> + * Return: 0 on success, or an error code on failure
>> + */
>> +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;
>> + struct tee_ioctl_invoke_arg arg;
>> + struct tee_shm *fw_shm;
>> + int ret;
>> +
>> + ret = mutex_lock_interruptible(&ctx_lock);
>> + if (ret)
>> + return ret;
>> +
>> + if (!rproc_tee_ctx.dev) {
>> + ret = -ENODEV;
>> + goto out;
>> + }
>> +
>> + trproc = rproc_to_trproc(rproc);
>> + if (!trproc) {
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + fw_shm = tee_shm_register_kernel_buf(rproc_tee_ctx.tee_ctx, (void *)fw->data, fw->size);
>> + if (IS_ERR(fw_shm)) {
>> + ret = PTR_ERR(fw_shm);
>> + goto out;
>> + }
>> +
>> + 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:%d\n",
>> + arg.ret, ret);
>> + if (!ret)
>> + ret = -EIO;
>> + }
>> +
>> + tee_shm_free(fw_shm);
>> +
>> +out:
>> + mutex_unlock(&ctx_lock);
>> + 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;
>> + struct tee_ioctl_invoke_arg arg;
>> + int ret;
>> +
>> + ret = mutex_lock_interruptible(&ctx_lock);
>> + if (ret)
>> + return ret;
>> +
>> + if (!rproc_tee_ctx.dev) {
>> + ret = -ENODEV;
>> + goto out;
>> + }
>> +
>> + trproc = rproc_to_trproc(rproc);
>> + if (!trproc) {
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + 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:%d\n",
>> + arg.ret, ret);
>> + ret = -EIO;
>> + goto out;
>> + }
>> +
>> + *table_sz = param[2].u.value.a;
>> + if (sizeof(phys_addr_t) == sizeof(u64))
>> + *table_sz |= param[2].u.value.b << 32;
>> +
>> + if (*table_sz) {
>> + *rsc_pa = param[1].u.value.a;
>> + if (sizeof(phys_addr_t) == sizeof(u64))
>> + *rsc_pa |= param[1].u.value.b << 32;
>> + } else {
>> + *rsc_pa = 0;
>> + }
>> +
>> +out:
>> + mutex_unlock(&ctx_lock);
>> + return ret;
>> +}
>> +
>> +/**
>> + * rproc_tee_parse_fw() - Get the resource table from TEE application
>> + * @rproc: Pointer to the struct rproc representing the remote processor
>> + * @fw: Pointer to the firmware structure containing the firmware data and size
>> + *
>> + * This function retrieves the loaded resource table and creates a cached_table
>> + * copy. Since the firmware image is signed and potentially encrypted, the
>> + * firmware must be loaded first to access the loaded resource table.
>> + *
>> + * Return: 0 on success, or an error code on failure
>> + */
>> +int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw)
>> +{
>> + phys_addr_t rsc_table;
>> + void *rsc_va;
>> + size_t table_sz;
>> + int ret;
>> +
>> + if (!rproc)
>> + return -EINVAL;
>> +
>> + /* We need first to Load the firmware, to be able to get the resource table. */
>> + ret = rproc_tee_load_fw(rproc, fw);
>> + if (ret)
>> + return ret;
>> +
>> + 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 = memremap(rsc_table, table_sz, MEMREMAP_WC);
>> + if (!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(rsc_va, table_sz, GFP_KERNEL);
>> + memunmap(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);
>> +
>> +/**
>> + * rproc_tee_find_loaded_rsc_table() - Find the loaded resource table loaded by
>> + * the TEE application
>> + * @rproc: Pointer to the struct rproc representing the remote processor
>> + * @fw: Pointer to the firmware structure containing the firmware data and size
>> + *
>> + * This function retrieves the physical address and size of the resource table
>> + * loaded by the TEE application.
>> + *
>> + * Return: pointer to the resource table if found, or NULL if not found or size
>> + * is 0
>> + */
>> +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 rproc_pa_to_va(rproc, rsc_table, table_sz, NULL);
>> +}
>> +EXPORT_SYMBOL_GPL(rproc_tee_find_loaded_rsc_table);
>> +
>> +/**
>> + * rproc_tee_start() - Request the TEE application to start the remote processor
>> + * @rproc: Pointer to the struct rproc representing the remote processor
>> + *
>> + * This function invokes the TA_RPROC_FW_CMD_START command to start the remote
>> + * processor.
>> + *
>> + * Return: Returns 0 on success, -EINVAL or -EIO on failure
>> + */
>> +int rproc_tee_start(struct rproc *rproc)
>> +{
>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>> + struct rproc_tee *trproc;
>> + struct tee_ioctl_invoke_arg arg;
>> + int ret;
>> +
>> + ret = mutex_lock_interruptible(&ctx_lock);
>> + if (ret)
>> + return ret;
>> +
>> + if (!rproc_tee_ctx.dev) {
>> + ret = -ENODEV;
>> + goto out;
>> + }
>> +
>> + trproc = rproc_to_trproc(rproc);
>> + if (!trproc) {
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_START, &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 invoke failed TEE err: %#x, ret:%d\n", arg.ret, ret);
>> + if (!ret)
>> + ret = -EIO;
>> + }
>> +
>> +out:
>> + mutex_unlock(&ctx_lock);
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(rproc_tee_start);
>> +
>> +/**
>> + * rproc_tee_stop() - Request the TEE application to start the remote processor
>> + * @rproc: Pointer to the struct rproc representing the remote processor
>> + *
>> + * This function invokes the TA_RPROC_FW_CMD_STOP command to stop the remote
>> + * processor.
>> + *
>> + * Return: Returns 0 on success, -EINVAL or -EIO on failure
>> + */
>> +int rproc_tee_stop(struct rproc *rproc)
>> +{
>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>> + struct rproc_tee *trproc;
>> + struct tee_ioctl_invoke_arg arg;
>> + int ret;
>> +
>> + ret = mutex_lock_interruptible(&ctx_lock);
>> + if (ret)
>> + return ret;
>> +
>> + if (!rproc_tee_ctx.dev) {
>> + ret = -ENODEV;
>> + goto out;
>> + }
>> +
>> + trproc = rproc_to_trproc(rproc);
>> + if (!trproc) {
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_STOP, &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 invoke failed TEE err: %#x, ret:%d\n", arg.ret, ret);
>> + if (!ret)
>> + ret = -EIO;
>> + }
>> +
>> +out:
>> + mutex_unlock(&ctx_lock);
>> + 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)},
>> + {}
>> +};
>> +
>> +/**
>> + * rproc_tee_register() - Register a remote processor controlled by the TEE application.
>> + * @dev: Pointer to client rproc device
>> + * @rproc: Pointer to the struct rproc representing the remote processor
>> + * @rproc_id: ID of the remote processor
>> + *
>> + * This function registers a remote processor that will be managed by a TEE
>> + * application,by opening a session with the TEE client.
>> + *
>> + * Return: Returns 0 on success, or an error code on failure
>> + */
>> +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;
>> + struct device_link *link;
>> + int ret;
>> +
>> + ret = mutex_lock_interruptible(&ctx_lock);
>> + if (ret)
>> + return 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.dev) {
>> + ret = -EPROBE_DEFER;
>> + goto out;
>> + }
>> +
>> + trproc = kzalloc(sizeof(*trproc), GFP_KERNEL);
>> + if (!trproc) {
>> + ret = -ENOMEM;
>> + goto out;
>> + }
>> +
>> + 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 free_tproc;
>> + }
>> +
>> + trproc->rproc_id = rproc_id;
>> + trproc->session_id = sess_arg.session;
>> +
>> + trproc->rproc = rproc;
>> +
>> + /*
>> + * Create device link between the rproc device and the TEE device to
>> + * ensure that the rproc device is removed accordingly to a remove of
>> + * the remoteproc tee device.
>> + */
>> + link = device_link_add(dev, rproc_tee_ctx.dev, DL_FLAG_AUTOREMOVE_CONSUMER);
>> + if (!link) {
>> + ret = -ENOMEM;
>> + goto close_tee;
>> + }
>> + list_add_tail(&trproc->node, &rproc_tee_ctx.sessions);
>> +
>> + mutex_unlock(&ctx_lock);
>> +
>> + return 0;
>> +
>> +close_tee:
>> + if (tee_client_close_session(rproc_tee_ctx.tee_ctx, trproc->session_id))
>> + dev_err(rproc_tee_ctx.dev, "tee_client_close_session failed\n");
>> +free_tproc:
>> + kfree(trproc);
>> +out:
>> + mutex_unlock(&ctx_lock);
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(rproc_tee_register);
>> +
>> +/**
>> + * rproc_tee_unregister - Register a remote processor controlled by the TEE application.
>> + * @dev: Pointer to client rproc device
>> + * @rproc: Pointer to the struct rproc representing the remote processor
>> + *
>> + * This function unregisters a remote processor previously registered by the
>> + * rproc_tee_register() function.
>> + *
>> + * Return: Returns 0 on success, or an error code on failure
>> + */
>> +int rproc_tee_unregister(struct device *dev, struct rproc *rproc)
>> +{
>> + struct rproc_tee *trproc;
>> + int ret;
>> +
>> + ret = mutex_lock_interruptible(&ctx_lock);
>> + if (ret)
>> + return ret;
>> +
>> + if (!rproc_tee_ctx.dev) {
>> + ret = -EPROBE_DEFER;
>> + goto out;
>> + }
>> +
>> + trproc = rproc_to_trproc(rproc);
>> + if (!trproc) {
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + ret = tee_client_close_session(rproc_tee_ctx.tee_ctx, trproc->session_id);
>> + if (ret < 0)
>> + dev_err(rproc_tee_ctx.dev, "tee_client_close_session failed, err: %#x\n", ret);
>> +
>> + list_del(&trproc->node);
>> + kfree(trproc);
>> +
>> +out:
>> + mutex_unlock(&ctx_lock);
>> + 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);
>> +
>> + ret = mutex_lock_interruptible(&ctx_lock);
>> + if (ret)
>> + return ret;
>> +
>> + rproc_tee_ctx.dev = dev;
>> + rproc_tee_ctx.tee_ctx = tee_ctx;
>> + INIT_LIST_HEAD(&rproc_tee_ctx.sessions);
>> + mutex_unlock(&ctx_lock);
>> +
>> + return 0;
>> +}
>
> As you mentioned above, this could be extended to other TEEs. If so, is it possible for probe
> to be called multiple times if we we have other TEE devices exposing the firmware authentication
> service? In that case, I think rproc_tee_ctx should be dynamically initializated instead of being
> static. And since we are creating a link between the Rproc device and TEE device, a call to a
> function like rproc_tee_start() could retreive the associated TEE device, and then the associated
> rproc_tee? :)
I have never seen a use case that requires multiple instances, but perhaps you
have some?
We can expect only one TEE, which could be OP-TEE, Trusty, or another.
The device is associated with a unique UUID, so only one instance is expected.
That said, making this driver support multiple instances seems like a valid
future enhancement. However, I would suggest implementing it as a second step
when there is a concrete need.
Thanks,
Arnaud
>
> Thanks,
> Harshal
>> +
>> +static int rproc_tee_remove(struct device *dev)
>> +{
>> + mutex_lock(&ctx_lock);
>> + tee_client_close_context(rproc_tee_ctx.tee_ctx);
>> + rproc_tee_ctx.dev = NULL;
>> + rproc_tee_ctx.tee_ctx = NULL;
>> + mutex_unlock(&ctx_lock);
>> +
>> + 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)
>> +{
>> + mutex_init(&ctx_lock);
>> +
>> + 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_tee.h b/include/linux/remoteproc_tee.h
>> new file mode 100644
>> index 000000000000..a5fdc1627774
>> --- /dev/null
>> +++ b/include/linux/remoteproc_tee.h
>> @@ -0,0 +1,87 @@
>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>> +/*
>> + * Copyright(c) 2025 STMicroelectronics
>> + */
>> +
>> +#ifndef REMOTEPROC_TEE_H
>> +#define REMOTEPROC_TEE_H
>> +
>> +#include <linux/tee_drv.h>
>> +#include <linux/firmware.h>
>> +#include <linux/remoteproc.h>
>> +
>> +#if IS_ENABLED(CONFIG_REMOTEPROC_TEE)
>> +
>> +int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id);
>> +int rproc_tee_unregister(struct device *dev, 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 device *dev, 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 */
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v19 2/6] remoteproc: Add TEE support
2025-08-01 7:23 ` Arnaud POULIQUEN
@ 2025-08-04 9:26 ` Harshal Dev
2025-08-11 14:11 ` Sumit Garg
0 siblings, 1 reply; 15+ messages in thread
From: Harshal Dev @ 2025-08-04 9:26 UTC (permalink / raw)
To: Arnaud POULIQUEN, Bjorn Andersson, Mathieu Poirier,
Jens Wiklander, Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: linux-stm32, linux-arm-kernel, linux-remoteproc, linux-kernel,
op-tee, devicetree
Hi Arnaud,
On 8/1/2025 12:53 PM, Arnaud POULIQUEN wrote:
> Hello Harshal,
>
>
> On 7/31/25 12:25, Harshal Dev wrote:
>> Hello Arnaud,
>>
>> On 6/25/2025 3:10 PM, 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 the secure part, this driver offers a client interface to load firmware
>>> by the secure part.
>>> This firmware could be authenticated by the secure trusted application.
>>>
>>> A specificity of the implementation is that the firmware has to be
>>> authenticated and optionally decrypted to access the resource table.
>>>
>>> Consequently, the boot sequence is:
>>>
>>> 1) rproc_parse_fw --> rproc_tee_parse_fw
>>> remoteproc TEE:
>>> - Requests the TEE application to authenticate and load the firmware
>>> in the remote processor memories.
>>> - Requests the TEE application for the address of the resource table.
>>> - Creates a copy of the resource table stored in rproc->cached_table.
>>>
>>> 2) rproc_load_segments --> rproc_tee_load_fw
>>> remoteproc TEE:
>>> - Requests the TEE application to load the firmware. Nothing is done
>>> at the TEE application as the firmware is already loaded.
>>> - In case of recovery, the TEE application has to reload the firmware.
>>>
>>> 3) rproc_tee_get_loaded_rsc_table
>>> remoteproc TEE requests the TEE application for the address of the
>>> resource table.
>>>
>>> 4) rproc_start --> rproc_tee_start
>>> - Requests the TEE application to start the remote processor.
>>>
>>> The shutdown sequence is:
>>>
>>> 5) rproc_stop --> rproc_tee_stop
>>> - Requests the TEE application to stop the remote processor.
>>>
>>> 6) rproc_tee_release_fw
>>> This function is used to request the TEE application to perform actions
>>> to return to the initial state on stop or on error during the boot
>>> sequence.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>>> ---
>>> Updates vs version [18]:
>>> - rework/fix function headers
>>> - use memremap instead of ioremap for the resource table.
>>> - realign comments to 80 chars limit, with few exceptions for readability
>>> - replace spinlock by mutex and and protect APIs from concurrent access
>>> - add support of 64-bit address in rproc_tee_get_loaded_rsc_table()
>>> - Generalize teston rproc_tee_ctx.dev to prevent an unbind
>>> - update copyright year
>>>
>>> Updates vs version [17]:
>>> Fix warning:
>>> warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
>>> ---
>>> drivers/remoteproc/Kconfig | 10 +
>>> drivers/remoteproc/Makefile | 1 +
>>> drivers/remoteproc/remoteproc_tee.c | 708 ++++++++++++++++++++++++++++
>>> include/linux/remoteproc_tee.h | 87 ++++
>>> 4 files changed, 806 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 83962a114dc9..e39265d249d9 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 that is managed by an application running in a Trusted
>>> + Execution Environment (TEE). This application is responsible for loading the remote
>>> + processor firmware image and managing its lifecycle.
>>> +
>>> + It's safe to say N if the remote processor is not managed by a TEE.
>>> +
>>> config IMX_REMOTEPROC
>>> tristate "i.MX remoteproc support"
>>> depends on ARCH_MXC
>>> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
>>> index 1c7598b8475d..a1a5201982d4 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..b9da911b8a83
>>> --- /dev/null
>>> +++ b/drivers/remoteproc/remoteproc_tee.c
>>> @@ -0,0 +1,708 @@
>>> +// SPDX-License-Identifier: GPL-2.0-or-later
>>> +/*
>>> + * Copyright (C) STMicroelectronics 2025
>>> + * Author: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>>> + */
>>> +
>>> +#include <linux/export.h>
>>> +#include <linux/firmware.h>
>>> +#include <linux/io.h>
>>> +#include <linux/module.h>
>>> +#include <linux/platform_device.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 and load of the firmware image in the remote processor
>>> + * memories by the TEE. After this step the firmware is installed in destination
>>> + * memories, which can then be locked to prevent access by Linux.
>>> + *
>>> + * [in] params[0].value.a: remote processor identifier
>>> + * [in] params[1].memref: buffer containing a temporary copy of the signed
>>> + * image to load.
>>> + */
>>> +#define TA_RPROC_FW_CMD_LOAD_FW 1
>>> +
>>> +/*
>>> + * Start the remote processor by the TEE
>>> + *
>>> + * [in] params[0].value.a: remote processor identifier
>>> + */
>>> +#define TA_RPROC_FW_CMD_START 2
>>> +
>>> +/*
>>> + * Stop the remote processor by the TEE
>>> + *
>>> + * [in] params[0].value.a: remote processor identifier
>>> + */
>>> +#define TA_RPROC_FW_CMD_STOP 3
>>> +
>>> +/*
>>> + * Return the address of the resource table, or 0 if not found.
>>> + *
>>> + * [in] params[0].value.a: remote processor identifier
>>> + * [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
>>> +
>>> +/*
>>> + * 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: 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;
>>> +};
>>> +
>>> +/**
>>> + * struct rproc_tee - TEE remoteproc structure
>>> + * @node: Reference in list
>>> + * @rproc: Remoteproc reference
>>> + * @rproc_id: remote processor identifier
>>> + * @session_id: TEE session identifier
>>> + */
>>> +struct rproc_tee {
>>> + struct list_head node;
>>> + struct rproc *rproc;
>>> + u32 rproc_id;
>>> + u32 session_id;
>>> +};
>>> +
>>> +static struct rproc_tee_context rproc_tee_ctx;
>>> +static struct mutex (ctx_lock); /* Protects concurrent manipulations of the rproc_tee_ctx*/
>>> +
>>
>> Since ctx_lock is protecting struct rproc_tee_ctx, can we place it within the struct?
>>
>>> +static struct rproc_tee *rproc_to_trproc(struct rproc *rproc)
>>> +{
>>> + struct rproc_tee *trproc;
>>> +
>>> + list_for_each_entry(trproc, &rproc_tee_ctx.sessions, node)
>>> + if (trproc->rproc == rproc)
>>> + return trproc;
>>> +
>>> + return NULL;
>>> +}
>>> +
>>> +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,
>>> + };
>>> +}
>>> +
>>> +/**
>>> + * rproc_tee_release_fw() - Release the firmware for a TEE-based remote processor
>>> + * @rproc: Pointer to the struct rproc representing the remote processor
>>> + *
>>> + * This function invokes the TA_RPROC_CMD_RELEASE_FW TEE client function to
>>> + * release the firmware. It should only be called when the remoteproc state is
>>> + * RPROC_OFFLINE or RPROC_DETACHED. The function requests the TEE remoteproc
>>> + * application to release the firmware loaded by rproc_tee_load_fw().
>>> + * The request is ignored if the rproc state is RPROC_DETACHED as the remote
>>> + * processor is still running.
>>> + */
>>> +void rproc_tee_release_fw(struct rproc *rproc)
>>> +{
>>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>>> + struct rproc_tee *trproc;
>>> + struct tee_ioctl_invoke_arg arg;
>>> + int ret;
>>> +
>>> + ret = mutex_lock_interruptible(&ctx_lock);
>>> + if (ret)
>>> + return;
>>> +
>>> + if (!rproc_tee_ctx.dev)
>>> + goto out;
>>> +
>>> + trproc = rproc_to_trproc(rproc);
>>> + if (!trproc)
>>> + 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)
>>> + goto out;
>>> +
>>> + if (rproc->state != RPROC_OFFLINE) {
>>> + dev_err(rproc_tee_ctx.dev, "unexpected rproc state: %d\n", rproc->state);
>>> + 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:%d\n",
>>> + arg.ret, ret);
>>> + }
>>> +
>>> +out:
>>> + mutex_unlock(&ctx_lock);
>>> +}
>>> +EXPORT_SYMBOL_GPL(rproc_tee_release_fw);
>>> +
>>> +/**
>>> + * rproc_tee_load_fw() - Load firmware from TEE application
>>> + * @rproc: Pointer to the struct rproc representing the remote processor
>>> + * @fw: Pointer to the firmware structure containing the firmware data and size
>>> + *
>>> + * This function invokes the TA_RPROC_FW_CMD_LOAD_FW TEE client function to load
>>> + * the firmware. It registers the fw->data as a shared memory region with the
>>> + * TEE, and request the TEE to load the firmware. This function can be called
>>> + * twice during the remote processor boot, a first by rproc_tee_parse_fw() to
>>> + * parse the resource table , and a second time by rproc_tee_load_fw().
>>> + * The TEE application should ignores the command if the firmware
>>> + * is already loaded by rproc_tee_parse_fw().
>>> + *
>>> + * Return: 0 on success, or an error code on failure
>>> + */
>>> +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;
>>> + struct tee_ioctl_invoke_arg arg;
>>> + struct tee_shm *fw_shm;
>>> + int ret;
>>> +
>>> + ret = mutex_lock_interruptible(&ctx_lock);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + if (!rproc_tee_ctx.dev) {
>>> + ret = -ENODEV;
>>> + goto out;
>>> + }
>>> +
>>> + trproc = rproc_to_trproc(rproc);
>>> + if (!trproc) {
>>> + ret = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + fw_shm = tee_shm_register_kernel_buf(rproc_tee_ctx.tee_ctx, (void *)fw->data, fw->size);
>>> + if (IS_ERR(fw_shm)) {
>>> + ret = PTR_ERR(fw_shm);
>>> + goto out;
>>> + }
>>> +
>>> + 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:%d\n",
>>> + arg.ret, ret);
>>> + if (!ret)
>>> + ret = -EIO;
>>> + }
>>> +
>>> + tee_shm_free(fw_shm);
>>> +
>>> +out:
>>> + mutex_unlock(&ctx_lock);
>>> + 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;
>>> + struct tee_ioctl_invoke_arg arg;
>>> + int ret;
>>> +
>>> + ret = mutex_lock_interruptible(&ctx_lock);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + if (!rproc_tee_ctx.dev) {
>>> + ret = -ENODEV;
>>> + goto out;
>>> + }
>>> +
>>> + trproc = rproc_to_trproc(rproc);
>>> + if (!trproc) {
>>> + ret = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + 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:%d\n",
>>> + arg.ret, ret);
>>> + ret = -EIO;
>>> + goto out;
>>> + }
>>> +
>>> + *table_sz = param[2].u.value.a;
>>> + if (sizeof(phys_addr_t) == sizeof(u64))
>>> + *table_sz |= param[2].u.value.b << 32;
>>> +
>>> + if (*table_sz) {
>>> + *rsc_pa = param[1].u.value.a;
>>> + if (sizeof(phys_addr_t) == sizeof(u64))
>>> + *rsc_pa |= param[1].u.value.b << 32;
>>> + } else {
>>> + *rsc_pa = 0;
>>> + }
>>> +
>>> +out:
>>> + mutex_unlock(&ctx_lock);
>>> + return ret;
>>> +}
>>> +
>>> +/**
>>> + * rproc_tee_parse_fw() - Get the resource table from TEE application
>>> + * @rproc: Pointer to the struct rproc representing the remote processor
>>> + * @fw: Pointer to the firmware structure containing the firmware data and size
>>> + *
>>> + * This function retrieves the loaded resource table and creates a cached_table
>>> + * copy. Since the firmware image is signed and potentially encrypted, the
>>> + * firmware must be loaded first to access the loaded resource table.
>>> + *
>>> + * Return: 0 on success, or an error code on failure
>>> + */
>>> +int rproc_tee_parse_fw(struct rproc *rproc, const struct firmware *fw)
>>> +{
>>> + phys_addr_t rsc_table;
>>> + void *rsc_va;
>>> + size_t table_sz;
>>> + int ret;
>>> +
>>> + if (!rproc)
>>> + return -EINVAL;
>>> +
>>> + /* We need first to Load the firmware, to be able to get the resource table. */
>>> + ret = rproc_tee_load_fw(rproc, fw);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + 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 = memremap(rsc_table, table_sz, MEMREMAP_WC);
>>> + if (!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(rsc_va, table_sz, GFP_KERNEL);
>>> + memunmap(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);
>>> +
>>> +/**
>>> + * rproc_tee_find_loaded_rsc_table() - Find the loaded resource table loaded by
>>> + * the TEE application
>>> + * @rproc: Pointer to the struct rproc representing the remote processor
>>> + * @fw: Pointer to the firmware structure containing the firmware data and size
>>> + *
>>> + * This function retrieves the physical address and size of the resource table
>>> + * loaded by the TEE application.
>>> + *
>>> + * Return: pointer to the resource table if found, or NULL if not found or size
>>> + * is 0
>>> + */
>>> +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 rproc_pa_to_va(rproc, rsc_table, table_sz, NULL);
>>> +}
>>> +EXPORT_SYMBOL_GPL(rproc_tee_find_loaded_rsc_table);
>>> +
>>> +/**
>>> + * rproc_tee_start() - Request the TEE application to start the remote processor
>>> + * @rproc: Pointer to the struct rproc representing the remote processor
>>> + *
>>> + * This function invokes the TA_RPROC_FW_CMD_START command to start the remote
>>> + * processor.
>>> + *
>>> + * Return: Returns 0 on success, -EINVAL or -EIO on failure
>>> + */
>>> +int rproc_tee_start(struct rproc *rproc)
>>> +{
>>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>>> + struct rproc_tee *trproc;
>>> + struct tee_ioctl_invoke_arg arg;
>>> + int ret;
>>> +
>>> + ret = mutex_lock_interruptible(&ctx_lock);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + if (!rproc_tee_ctx.dev) {
>>> + ret = -ENODEV;
>>> + goto out;
>>> + }
>>> +
>>> + trproc = rproc_to_trproc(rproc);
>>> + if (!trproc) {
>>> + ret = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_START, &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 invoke failed TEE err: %#x, ret:%d\n", arg.ret, ret);
>>> + if (!ret)
>>> + ret = -EIO;
>>> + }
>>> +
>>> +out:
>>> + mutex_unlock(&ctx_lock);
>>> + return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(rproc_tee_start);
>>> +
>>> +/**
>>> + * rproc_tee_stop() - Request the TEE application to start the remote processor
>>> + * @rproc: Pointer to the struct rproc representing the remote processor
>>> + *
>>> + * This function invokes the TA_RPROC_FW_CMD_STOP command to stop the remote
>>> + * processor.
>>> + *
>>> + * Return: Returns 0 on success, -EINVAL or -EIO on failure
>>> + */
>>> +int rproc_tee_stop(struct rproc *rproc)
>>> +{
>>> + struct tee_param param[MAX_TEE_PARAM_ARRAY_MEMBER];
>>> + struct rproc_tee *trproc;
>>> + struct tee_ioctl_invoke_arg arg;
>>> + int ret;
>>> +
>>> + ret = mutex_lock_interruptible(&ctx_lock);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + if (!rproc_tee_ctx.dev) {
>>> + ret = -ENODEV;
>>> + goto out;
>>> + }
>>> +
>>> + trproc = rproc_to_trproc(rproc);
>>> + if (!trproc) {
>>> + ret = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + rproc_tee_prepare_args(trproc, TA_RPROC_FW_CMD_STOP, &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 invoke failed TEE err: %#x, ret:%d\n", arg.ret, ret);
>>> + if (!ret)
>>> + ret = -EIO;
>>> + }
>>> +
>>> +out:
>>> + mutex_unlock(&ctx_lock);
>>> + 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)},
>>> + {}
>>> +};
>>> +
>>> +/**
>>> + * rproc_tee_register() - Register a remote processor controlled by the TEE application.
>>> + * @dev: Pointer to client rproc device
>>> + * @rproc: Pointer to the struct rproc representing the remote processor
>>> + * @rproc_id: ID of the remote processor
>>> + *
>>> + * This function registers a remote processor that will be managed by a TEE
>>> + * application,by opening a session with the TEE client.
>>> + *
>>> + * Return: Returns 0 on success, or an error code on failure
>>> + */
>>> +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;
>>> + struct device_link *link;
>>> + int ret;
>>> +
>>> + ret = mutex_lock_interruptible(&ctx_lock);
>>> + if (ret)
>>> + return 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.dev) {
>>> + ret = -EPROBE_DEFER;
>>> + goto out;
>>> + }
>>> +
>>> + trproc = kzalloc(sizeof(*trproc), GFP_KERNEL);
>>> + if (!trproc) {
>>> + ret = -ENOMEM;
>>> + goto out;
>>> + }
>>> +
>>> + 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 free_tproc;
>>> + }
>>> +
>>> + trproc->rproc_id = rproc_id;
>>> + trproc->session_id = sess_arg.session;
>>> +
>>> + trproc->rproc = rproc;
>>> +
>>> + /*
>>> + * Create device link between the rproc device and the TEE device to
>>> + * ensure that the rproc device is removed accordingly to a remove of
>>> + * the remoteproc tee device.
>>> + */
>>> + link = device_link_add(dev, rproc_tee_ctx.dev, DL_FLAG_AUTOREMOVE_CONSUMER);
>>> + if (!link) {
>>> + ret = -ENOMEM;
>>> + goto close_tee;
>>> + }
>>> + list_add_tail(&trproc->node, &rproc_tee_ctx.sessions);
>>> +
>>> + mutex_unlock(&ctx_lock);
>>> +
>>> + return 0;
>>> +
>>> +close_tee:
>>> + if (tee_client_close_session(rproc_tee_ctx.tee_ctx, trproc->session_id))
>>> + dev_err(rproc_tee_ctx.dev, "tee_client_close_session failed\n");
>>> +free_tproc:
>>> + kfree(trproc);
>>> +out:
>>> + mutex_unlock(&ctx_lock);
>>> +
>>> + return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(rproc_tee_register);
>>> +
>>> +/**
>>> + * rproc_tee_unregister - Register a remote processor controlled by the TEE application.
>>> + * @dev: Pointer to client rproc device
>>> + * @rproc: Pointer to the struct rproc representing the remote processor
>>> + *
>>> + * This function unregisters a remote processor previously registered by the
>>> + * rproc_tee_register() function.
>>> + *
>>> + * Return: Returns 0 on success, or an error code on failure
>>> + */
>>> +int rproc_tee_unregister(struct device *dev, struct rproc *rproc)
>>> +{
>>> + struct rproc_tee *trproc;
>>> + int ret;
>>> +
>>> + ret = mutex_lock_interruptible(&ctx_lock);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + if (!rproc_tee_ctx.dev) {
>>> + ret = -EPROBE_DEFER;
>>> + goto out;
>>> + }
>>> +
>>> + trproc = rproc_to_trproc(rproc);
>>> + if (!trproc) {
>>> + ret = -EINVAL;
>>> + goto out;
>>> + }
>>> +
>>> + ret = tee_client_close_session(rproc_tee_ctx.tee_ctx, trproc->session_id);
>>> + if (ret < 0)
>>> + dev_err(rproc_tee_ctx.dev, "tee_client_close_session failed, err: %#x\n", ret);
>>> +
>>> + list_del(&trproc->node);
>>> + kfree(trproc);
>>> +
>>> +out:
>>> + mutex_unlock(&ctx_lock);
>>> + 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);
>>> +
>>> + ret = mutex_lock_interruptible(&ctx_lock);
>>> + if (ret)
>>> + return ret;
>>> +
>>> + rproc_tee_ctx.dev = dev;
>>> + rproc_tee_ctx.tee_ctx = tee_ctx;
>>> + INIT_LIST_HEAD(&rproc_tee_ctx.sessions);
>>> + mutex_unlock(&ctx_lock);
>>> +
>>> + return 0;
>>> +}
>>
>> As you mentioned above, this could be extended to other TEEs. If so, is it possible for probe
>> to be called multiple times if we we have other TEE devices exposing the firmware authentication
>> service? In that case, I think rproc_tee_ctx should be dynamically initializated instead of being
>> static. And since we are creating a link between the Rproc device and TEE device, a call to a
>> function like rproc_tee_start() could retreive the associated TEE device, and then the associated
>> rproc_tee? :)
>
> I have never seen a use case that requires multiple instances, but perhaps you
> have some?
>
> We can expect only one TEE, which could be OP-TEE, Trusty, or another.
> The device is associated with a unique UUID, so only one instance is expected.
>
> That said, making this driver support multiple instances seems like a valid
> future enhancement. However, I would suggest implementing it as a second step
> when there is a concrete need.
>
My thought process on this stems from 1) the recent ARM FF-A developments and 2) from the current
implementation of the TEE subsystem which allows multiple back-end drivers to register themselves
via the tee_device_register() API. This means, that it's possible to have a configuration
where a platform supports multiple TEEs running as Secure Partitions via FF-A, and each of those
TEEs register their services as PTA devices on the TEE bus.
However, I do not really know if it's possible to have a UUID collision in such a case, which
would lead to rproc_tee_probe() being called twice above, which is why I raised this question. :)
All of this aside, I realize now that other TEE client drivers are also implemented with a static
private data similar to how you are doing it. So perhaps we can think of this as a later
enhancement if we believe that the scenario I am describing is not possible in the near future..
Thanks,
Harshal
> Thanks,
> Arnaud
>
>>
>> Thanks,
>> Harshal
>>> +
>>> +static int rproc_tee_remove(struct device *dev)
>>> +{
>>> + mutex_lock(&ctx_lock);
>>> + tee_client_close_context(rproc_tee_ctx.tee_ctx);
>>> + rproc_tee_ctx.dev = NULL;
>>> + rproc_tee_ctx.tee_ctx = NULL;
>>> + mutex_unlock(&ctx_lock);
>>> +
>>> + 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)
>>> +{
>>> + mutex_init(&ctx_lock);
>>> +
>>> + 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_tee.h b/include/linux/remoteproc_tee.h
>>> new file mode 100644
>>> index 000000000000..a5fdc1627774
>>> --- /dev/null
>>> +++ b/include/linux/remoteproc_tee.h
>>> @@ -0,0 +1,87 @@
>>> +/* SPDX-License-Identifier: GPL-2.0-or-later */
>>> +/*
>>> + * Copyright(c) 2025 STMicroelectronics
>>> + */
>>> +
>>> +#ifndef REMOTEPROC_TEE_H
>>> +#define REMOTEPROC_TEE_H
>>> +
>>> +#include <linux/tee_drv.h>
>>> +#include <linux/firmware.h>
>>> +#include <linux/remoteproc.h>
>>> +
>>> +#if IS_ENABLED(CONFIG_REMOTEPROC_TEE)
>>> +
>>> +int rproc_tee_register(struct device *dev, struct rproc *rproc, unsigned int rproc_id);
>>> +int rproc_tee_unregister(struct device *dev, 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 device *dev, 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 */
>>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v19 2/6] remoteproc: Add TEE support
2025-08-04 9:26 ` Harshal Dev
@ 2025-08-11 14:11 ` Sumit Garg
2025-08-14 10:47 ` Harshal Dev
0 siblings, 1 reply; 15+ messages in thread
From: Sumit Garg @ 2025-08-11 14:11 UTC (permalink / raw)
To: Harshal Dev
Cc: Arnaud POULIQUEN, Bjorn Andersson, Mathieu Poirier,
Jens Wiklander, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-stm32, linux-arm-kernel, linux-remoteproc, linux-kernel,
op-tee, devicetree
Hi Harshal,
On Mon, Aug 04, 2025 at 02:56:18PM +0530, Harshal Dev wrote:
> Hi Arnaud,
>
> On 8/1/2025 12:53 PM, Arnaud POULIQUEN wrote:
> > Hello Harshal,
> >
> >
> > On 7/31/25 12:25, Harshal Dev wrote:
> >> Hello Arnaud,
> >>
> >> On 6/25/2025 3:10 PM, 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 the secure part, this driver offers a client interface to load firmware
> >>> by the secure part.
> >>> This firmware could be authenticated by the secure trusted application.
> >>>
> >>> A specificity of the implementation is that the firmware has to be
> >>> authenticated and optionally decrypted to access the resource table.
> >>>
> >>> Consequently, the boot sequence is:
> >>>
> >>> 1) rproc_parse_fw --> rproc_tee_parse_fw
> >>> remoteproc TEE:
> >>> - Requests the TEE application to authenticate and load the firmware
> >>> in the remote processor memories.
> >>> - Requests the TEE application for the address of the resource table.
> >>> - Creates a copy of the resource table stored in rproc->cached_table.
> >>>
> >>> 2) rproc_load_segments --> rproc_tee_load_fw
> >>> remoteproc TEE:
> >>> - Requests the TEE application to load the firmware. Nothing is done
> >>> at the TEE application as the firmware is already loaded.
> >>> - In case of recovery, the TEE application has to reload the firmware.
> >>>
> >>> 3) rproc_tee_get_loaded_rsc_table
> >>> remoteproc TEE requests the TEE application for the address of the
> >>> resource table.
> >>>
> >>> 4) rproc_start --> rproc_tee_start
> >>> - Requests the TEE application to start the remote processor.
> >>>
> >>> The shutdown sequence is:
> >>>
> >>> 5) rproc_stop --> rproc_tee_stop
> >>> - Requests the TEE application to stop the remote processor.
> >>>
> >>> 6) rproc_tee_release_fw
> >>> This function is used to request the TEE application to perform actions
> >>> to return to the initial state on stop or on error during the boot
> >>> sequence.
> >>>
> >>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> >>> ---
> >>> Updates vs version [18]:
> >>> - rework/fix function headers
> >>> - use memremap instead of ioremap for the resource table.
> >>> - realign comments to 80 chars limit, with few exceptions for readability
> >>> - replace spinlock by mutex and and protect APIs from concurrent access
> >>> - add support of 64-bit address in rproc_tee_get_loaded_rsc_table()
> >>> - Generalize teston rproc_tee_ctx.dev to prevent an unbind
> >>> - update copyright year
> >>>
> >>> Updates vs version [17]:
> >>> Fix warning:
> >>> warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
> >>> ---
> >>> drivers/remoteproc/Kconfig | 10 +
> >>> drivers/remoteproc/Makefile | 1 +
> >>> drivers/remoteproc/remoteproc_tee.c | 708 ++++++++++++++++++++++++++++
> >>> include/linux/remoteproc_tee.h | 87 ++++
> >>> 4 files changed, 806 insertions(+)
> >>> create mode 100644 drivers/remoteproc/remoteproc_tee.c
> >>> create mode 100644 include/linux/remoteproc_tee.h
> >>>
<snip>
> >>> +
> >>> +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);
> >>> +
> >>> + ret = mutex_lock_interruptible(&ctx_lock);
> >>> + if (ret)
> >>> + return ret;
> >>> +
> >>> + rproc_tee_ctx.dev = dev;
> >>> + rproc_tee_ctx.tee_ctx = tee_ctx;
> >>> + INIT_LIST_HEAD(&rproc_tee_ctx.sessions);
> >>> + mutex_unlock(&ctx_lock);
> >>> +
> >>> + return 0;
> >>> +}
> >>
> >> As you mentioned above, this could be extended to other TEEs. If so, is it possible for probe
> >> to be called multiple times if we we have other TEE devices exposing the firmware authentication
> >> service? In that case, I think rproc_tee_ctx should be dynamically initializated instead of being
> >> static. And since we are creating a link between the Rproc device and TEE device, a call to a
> >> function like rproc_tee_start() could retreive the associated TEE device, and then the associated
> >> rproc_tee? :)
> >
> > I have never seen a use case that requires multiple instances, but perhaps you
> > have some?
> >
> > We can expect only one TEE, which could be OP-TEE, Trusty, or another.
> > The device is associated with a unique UUID, so only one instance is expected.
> >
> > That said, making this driver support multiple instances seems like a valid
> > future enhancement. However, I would suggest implementing it as a second step
> > when there is a concrete need.
> >
>
> My thought process on this stems from 1) the recent ARM FF-A developments and 2) from the current
> implementation of the TEE subsystem which allows multiple back-end drivers to register themselves
> via the tee_device_register() API. This means, that it's possible to have a configuration
> where a platform supports multiple TEEs running as Secure Partitions via FF-A, and each of those
> TEEs register their services as PTA devices on the TEE bus.
>
> However, I do not really know if it's possible to have a UUID collision in such a case, which
> would lead to rproc_tee_probe() being called twice above, which is why I raised this question. :)
>
> All of this aside, I realize now that other TEE client drivers are also implemented with a static
> private data similar to how you are doing it. So perhaps we can think of this as a later
> enhancement if we believe that the scenario I am describing is not possible in the near future..
>
Theoretically it is possible for multiple TEE services to be there but
why should a platform/silicon vendor require 2 redundant remoteproc firmware
loading services to be supported? It should either be a service hosted
by the trusted OS or can rather be an independent platform service
running as a FF-A secure partition.
-Sumit
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v19 2/6] remoteproc: Add TEE support
2025-06-25 9:40 ` [PATCH v19 2/6] remoteproc: Add TEE support Arnaud Pouliquen
2025-07-31 10:25 ` Harshal Dev
@ 2025-08-13 16:42 ` Abdellatif El Khlifi
2025-08-14 7:17 ` Sumit Garg
1 sibling, 1 reply; 15+ messages in thread
From: Abdellatif El Khlifi @ 2025-08-13 16:42 UTC (permalink / raw)
To: Arnaud Pouliquen
Cc: Bjorn Andersson, Mathieu Poirier, Jens Wiklander, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-stm32, linux-arm-kernel,
linux-remoteproc, linux-kernel, op-tee, devicetree,
Abdellatif El Khlifi, Srinivas Kalaga
Hi Arnaud,
> Add a remoteproc TEE (Trusted Execution Environment) driver that will be
> probed by the TEE bus. If the associated Trusted application is supported
> on the secure part, this driver offers a client interface to load firmware
> by the secure part.
> This firmware could be authenticated by the secure trusted application.
>
> A specificity of the implementation is that the firmware has to be
> authenticated and optionally decrypted to access the resource table.
>
> Consequently, the boot sequence is:
>
> 1) rproc_parse_fw --> rproc_tee_parse_fw
> remoteproc TEE:
> - Requests the TEE application to authenticate and load the firmware
> in the remote processor memories.
> - Requests the TEE application for the address of the resource table.
> - Creates a copy of the resource table stored in rproc->cached_table.
>
> 2) rproc_load_segments --> rproc_tee_load_fw
> remoteproc TEE:
> - Requests the TEE application to load the firmware. Nothing is done
> at the TEE application as the firmware is already loaded.
> - In case of recovery, the TEE application has to reload the firmware.
>
> 3) rproc_tee_get_loaded_rsc_table
> remoteproc TEE requests the TEE application for the address of the
> resource table.
>
> 4) rproc_start --> rproc_tee_start
> - Requests the TEE application to start the remote processor.
>
> The shutdown sequence is:
>
> 5) rproc_stop --> rproc_tee_stop
> - Requests the TEE application to stop the remote processor.
>
> 6) rproc_tee_release_fw
> This function is used to request the TEE application to perform actions
> to return to the initial state on stop or on error during the boot
> sequence.
>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
...
> +
> +static const struct tee_client_device_id rproc_tee_id_table[] = {
> + {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)},
> + {}
> +};
Other implementations may use different UUIDs.
What about adding a kernel configuration option which, when enabled, allows
alternative implementations to override this table?
> +/**
> + * rproc_tee_register() - Register a remote processor controlled by the TEE application.
...
> +
> +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);
> +}
Could we make ver->impl_id user-configurable please ? for example, by reading
it from the device tree since it isn’t discoverable at runtime? In our setup, we’d set
it to TEE_IMPL_ID_TSTEE.
> +
> +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);
> +
> + ret = mutex_lock_interruptible(&ctx_lock);
> + if (ret)
> + return ret;
In some TEEs, the client driver might need to perform extra work during probing.
For example, when using TS TEE, calling tee_shm_alloc_kernel_buf() is required.
Could we introduce an rproc_tee_ops and add a TEE probe operation called by the
remoteproc driver for performing custom TEE setup ?
Kind regards,
Abdellatif
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v19 2/6] remoteproc: Add TEE support
2025-08-13 16:42 ` Abdellatif El Khlifi
@ 2025-08-14 7:17 ` Sumit Garg
0 siblings, 0 replies; 15+ messages in thread
From: Sumit Garg @ 2025-08-14 7:17 UTC (permalink / raw)
To: Abdellatif El Khlifi
Cc: Arnaud Pouliquen, Bjorn Andersson, Mathieu Poirier, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-stm32, linux-arm-kernel,
linux-remoteproc, linux-kernel, op-tee, devicetree,
Srinivas Kalaga
Hi Abdellatif,
On Wed, Aug 13, 2025 at 05:42:58PM +0100, Abdellatif El Khlifi wrote:
> Hi Arnaud,
>
> > Add a remoteproc TEE (Trusted Execution Environment) driver that will be
> > probed by the TEE bus. If the associated Trusted application is supported
> > on the secure part, this driver offers a client interface to load firmware
> > by the secure part.
> > This firmware could be authenticated by the secure trusted application.
> >
> > A specificity of the implementation is that the firmware has to be
> > authenticated and optionally decrypted to access the resource table.
> >
> > Consequently, the boot sequence is:
> >
> > 1) rproc_parse_fw --> rproc_tee_parse_fw
> > remoteproc TEE:
> > - Requests the TEE application to authenticate and load the firmware
> > in the remote processor memories.
> > - Requests the TEE application for the address of the resource table.
> > - Creates a copy of the resource table stored in rproc->cached_table.
> >
> > 2) rproc_load_segments --> rproc_tee_load_fw
> > remoteproc TEE:
> > - Requests the TEE application to load the firmware. Nothing is done
> > at the TEE application as the firmware is already loaded.
> > - In case of recovery, the TEE application has to reload the firmware.
> >
> > 3) rproc_tee_get_loaded_rsc_table
> > remoteproc TEE requests the TEE application for the address of the
> > resource table.
> >
> > 4) rproc_start --> rproc_tee_start
> > - Requests the TEE application to start the remote processor.
> >
> > The shutdown sequence is:
> >
> > 5) rproc_stop --> rproc_tee_stop
> > - Requests the TEE application to stop the remote processor.
> >
> > 6) rproc_tee_release_fw
> > This function is used to request the TEE application to perform actions
> > to return to the initial state on stop or on error during the boot
> > sequence.
> >
> > Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> ...
> > +
> > +static const struct tee_client_device_id rproc_tee_id_table[] = {
> > + {UUID_INIT(0x80a4c275, 0x0a47, 0x4905, 0x82, 0x85, 0x14, 0x86, 0xa9, 0x77, 0x1a, 0x08)},
> > + {}
> > +};
>
> Other implementations may use different UUIDs.
> What about adding a kernel configuration option which, when enabled, allows
> alternative implementations to override this table?
You don't need any other kernel configuration option for table override
but rather you extend this table with UUID for service provided by
TS-TEE.
>
> > +/**
> > + * rproc_tee_register() - Register a remote processor controlled by the TEE application.
> ...
> > +
> > +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);
> > +}
>
> Could we make ver->impl_id user-configurable please ? for example, by reading
> it from the device tree since it isn’t discoverable at runtime? In our setup, we’d set
> it to TEE_IMPL_ID_TSTEE.
In case the TS-TEE service gets enumerated on TEE bus then the
ver->impl_id will get automatically configured to TEE_IMPL_ID_TSTEE. It
is how the driver will get to know if it is communicating with an OP-TEE
based service of TS-TEE based service.
>
> > +
> > +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);
> > +
> > + ret = mutex_lock_interruptible(&ctx_lock);
> > + if (ret)
> > + return ret;
>
> In some TEEs, the client driver might need to perform extra work during probing.
> For example, when using TS TEE, calling tee_shm_alloc_kernel_buf() is required.
> Could we introduce an rproc_tee_ops and add a TEE probe operation called by the
> remoteproc driver for performing custom TEE setup ?
Sure, as I mentioned above the driver will be able to know if it's
communicating with TS-TEE then the additional functionality needed can
be conditionally implemented during probe.
I think it is really the next step after this patch-set lands where we
have to support the remoteproc service hosted under different TEE
implementations like OP-TEE, TS-TEE or QTEE etc.
-Sumit
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v19 2/6] remoteproc: Add TEE support
2025-08-11 14:11 ` Sumit Garg
@ 2025-08-14 10:47 ` Harshal Dev
2025-08-18 5:07 ` Sumit Garg
0 siblings, 1 reply; 15+ messages in thread
From: Harshal Dev @ 2025-08-14 10:47 UTC (permalink / raw)
To: Sumit Garg
Cc: Arnaud POULIQUEN, Bjorn Andersson, Mathieu Poirier,
Jens Wiklander, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-stm32, linux-arm-kernel, linux-remoteproc, linux-kernel,
op-tee, devicetree
Hi Sumit,
On 8/11/2025 7:41 PM, Sumit Garg wrote:
> Hi Harshal,
>
> On Mon, Aug 04, 2025 at 02:56:18PM +0530, Harshal Dev wrote:
>> Hi Arnaud,
>>
>> On 8/1/2025 12:53 PM, Arnaud POULIQUEN wrote:
>>> Hello Harshal,
>>>
>>>
>>> On 7/31/25 12:25, Harshal Dev wrote:
>>>> Hello Arnaud,
>>>>
>>>> On 6/25/2025 3:10 PM, 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 the secure part, this driver offers a client interface to load firmware
>>>>> by the secure part.
>>>>> This firmware could be authenticated by the secure trusted application.
>>>>>
>>>>> A specificity of the implementation is that the firmware has to be
>>>>> authenticated and optionally decrypted to access the resource table.
>>>>>
>>>>> Consequently, the boot sequence is:
>>>>>
>>>>> 1) rproc_parse_fw --> rproc_tee_parse_fw
>>>>> remoteproc TEE:
>>>>> - Requests the TEE application to authenticate and load the firmware
>>>>> in the remote processor memories.
>>>>> - Requests the TEE application for the address of the resource table.
>>>>> - Creates a copy of the resource table stored in rproc->cached_table.
>>>>>
>>>>> 2) rproc_load_segments --> rproc_tee_load_fw
>>>>> remoteproc TEE:
>>>>> - Requests the TEE application to load the firmware. Nothing is done
>>>>> at the TEE application as the firmware is already loaded.
>>>>> - In case of recovery, the TEE application has to reload the firmware.
>>>>>
>>>>> 3) rproc_tee_get_loaded_rsc_table
>>>>> remoteproc TEE requests the TEE application for the address of the
>>>>> resource table.
>>>>>
>>>>> 4) rproc_start --> rproc_tee_start
>>>>> - Requests the TEE application to start the remote processor.
>>>>>
>>>>> The shutdown sequence is:
>>>>>
>>>>> 5) rproc_stop --> rproc_tee_stop
>>>>> - Requests the TEE application to stop the remote processor.
>>>>>
>>>>> 6) rproc_tee_release_fw
>>>>> This function is used to request the TEE application to perform actions
>>>>> to return to the initial state on stop or on error during the boot
>>>>> sequence.
>>>>>
>>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
>>>>> ---
>>>>> Updates vs version [18]:
>>>>> - rework/fix function headers
>>>>> - use memremap instead of ioremap for the resource table.
>>>>> - realign comments to 80 chars limit, with few exceptions for readability
>>>>> - replace spinlock by mutex and and protect APIs from concurrent access
>>>>> - add support of 64-bit address in rproc_tee_get_loaded_rsc_table()
>>>>> - Generalize teston rproc_tee_ctx.dev to prevent an unbind
>>>>> - update copyright year
>>>>>
>>>>> Updates vs version [17]:
>>>>> Fix warning:
>>>>> warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
>>>>> ---
>>>>> drivers/remoteproc/Kconfig | 10 +
>>>>> drivers/remoteproc/Makefile | 1 +
>>>>> drivers/remoteproc/remoteproc_tee.c | 708 ++++++++++++++++++++++++++++
>>>>> include/linux/remoteproc_tee.h | 87 ++++
>>>>> 4 files changed, 806 insertions(+)
>>>>> create mode 100644 drivers/remoteproc/remoteproc_tee.c
>>>>> create mode 100644 include/linux/remoteproc_tee.h
>>>>>
>
> <snip>
>
>>>>> +
>>>>> +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);
>>>>> +
>>>>> + ret = mutex_lock_interruptible(&ctx_lock);
>>>>> + if (ret)
>>>>> + return ret;
>>>>> +
>>>>> + rproc_tee_ctx.dev = dev;
>>>>> + rproc_tee_ctx.tee_ctx = tee_ctx;
>>>>> + INIT_LIST_HEAD(&rproc_tee_ctx.sessions);
>>>>> + mutex_unlock(&ctx_lock);
>>>>> +
>>>>> + return 0;
>>>>> +}
>>>>
>>>> As you mentioned above, this could be extended to other TEEs. If so, is it possible for probe
>>>> to be called multiple times if we we have other TEE devices exposing the firmware authentication
>>>> service? In that case, I think rproc_tee_ctx should be dynamically initializated instead of being
>>>> static. And since we are creating a link between the Rproc device and TEE device, a call to a
>>>> function like rproc_tee_start() could retreive the associated TEE device, and then the associated
>>>> rproc_tee? :)
>>>
>>> I have never seen a use case that requires multiple instances, but perhaps you
>>> have some?
>>>
>>> We can expect only one TEE, which could be OP-TEE, Trusty, or another.
>>> The device is associated with a unique UUID, so only one instance is expected.
>>>
>>> That said, making this driver support multiple instances seems like a valid
>>> future enhancement. However, I would suggest implementing it as a second step
>>> when there is a concrete need.
>>>
>>
>> My thought process on this stems from 1) the recent ARM FF-A developments and 2) from the current
>> implementation of the TEE subsystem which allows multiple back-end drivers to register themselves
>> via the tee_device_register() API. This means, that it's possible to have a configuration
>> where a platform supports multiple TEEs running as Secure Partitions via FF-A, and each of those
>> TEEs register their services as PTA devices on the TEE bus.
>>
>> However, I do not really know if it's possible to have a UUID collision in such a case, which
>> would lead to rproc_tee_probe() being called twice above, which is why I raised this question. :)
>>
>> All of this aside, I realize now that other TEE client drivers are also implemented with a static
>> private data similar to how you are doing it. So perhaps we can think of this as a later
>> enhancement if we believe that the scenario I am describing is not possible in the near future..
>>
>
> Theoretically it is possible for multiple TEE services to be there but
> why should a platform/silicon vendor require 2 redundant remoteproc firmware
> loading services to be supported? It should either be a service hosted
> by the trusted OS or can rather be an independent platform service
> running as a FF-A secure partition.
>
I agree that it doesn't make sense for a system integrator to have two remoteproc firmware
loading services supported from two different TEEs running as Secure Partitions.
After all, one service exposed by one TEE is good enough for fulfilling any use-case.
My concern is that ARM FF-A makes its possible to have a platform running two TEEs, which
each have their own remoteproc firmware authentication service implemented (as usually TEEs do).
In such a scenario, when both TEEs enumerate their services on the TEE bus, and find a match
because the rproc_tee_id_table has a UUID for say, both the TS-TEE remoteproc service and
OP-TEE remoteproc service, rproc_tee_probe() will be called twice, and the current implementation
will break because it uses a single static rproc_tee_ctx, whose contents would be overwritten
leading to unexpected scenarios.
And so, should TEE subsystem clients (like this one) be prepared to handle such as scenario?
Thanks,
Harshal
> -Sumit
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v19 2/6] remoteproc: Add TEE support
2025-08-14 10:47 ` Harshal Dev
@ 2025-08-18 5:07 ` Sumit Garg
0 siblings, 0 replies; 15+ messages in thread
From: Sumit Garg @ 2025-08-18 5:07 UTC (permalink / raw)
To: Harshal Dev
Cc: Arnaud POULIQUEN, Bjorn Andersson, Mathieu Poirier,
Jens Wiklander, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Abdellatif El Khlifi, Srinivas Kalaga, linux-stm32,
linux-arm-kernel, linux-remoteproc, linux-kernel, op-tee,
devicetree
On Thu, Aug 14, 2025 at 04:17:55PM +0530, Harshal Dev wrote:
> Hi Sumit,
>
> On 8/11/2025 7:41 PM, Sumit Garg wrote:
> > Hi Harshal,
> >
> > On Mon, Aug 04, 2025 at 02:56:18PM +0530, Harshal Dev wrote:
> >> Hi Arnaud,
> >>
> >> On 8/1/2025 12:53 PM, Arnaud POULIQUEN wrote:
> >>> Hello Harshal,
> >>>
> >>>
> >>> On 7/31/25 12:25, Harshal Dev wrote:
> >>>> Hello Arnaud,
> >>>>
> >>>> On 6/25/2025 3:10 PM, 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 the secure part, this driver offers a client interface to load firmware
> >>>>> by the secure part.
> >>>>> This firmware could be authenticated by the secure trusted application.
> >>>>>
> >>>>> A specificity of the implementation is that the firmware has to be
> >>>>> authenticated and optionally decrypted to access the resource table.
> >>>>>
> >>>>> Consequently, the boot sequence is:
> >>>>>
> >>>>> 1) rproc_parse_fw --> rproc_tee_parse_fw
> >>>>> remoteproc TEE:
> >>>>> - Requests the TEE application to authenticate and load the firmware
> >>>>> in the remote processor memories.
> >>>>> - Requests the TEE application for the address of the resource table.
> >>>>> - Creates a copy of the resource table stored in rproc->cached_table.
> >>>>>
> >>>>> 2) rproc_load_segments --> rproc_tee_load_fw
> >>>>> remoteproc TEE:
> >>>>> - Requests the TEE application to load the firmware. Nothing is done
> >>>>> at the TEE application as the firmware is already loaded.
> >>>>> - In case of recovery, the TEE application has to reload the firmware.
> >>>>>
> >>>>> 3) rproc_tee_get_loaded_rsc_table
> >>>>> remoteproc TEE requests the TEE application for the address of the
> >>>>> resource table.
> >>>>>
> >>>>> 4) rproc_start --> rproc_tee_start
> >>>>> - Requests the TEE application to start the remote processor.
> >>>>>
> >>>>> The shutdown sequence is:
> >>>>>
> >>>>> 5) rproc_stop --> rproc_tee_stop
> >>>>> - Requests the TEE application to stop the remote processor.
> >>>>>
> >>>>> 6) rproc_tee_release_fw
> >>>>> This function is used to request the TEE application to perform actions
> >>>>> to return to the initial state on stop or on error during the boot
> >>>>> sequence.
> >>>>>
> >>>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@foss.st.com>
> >>>>> ---
> >>>>> Updates vs version [18]:
> >>>>> - rework/fix function headers
> >>>>> - use memremap instead of ioremap for the resource table.
> >>>>> - realign comments to 80 chars limit, with few exceptions for readability
> >>>>> - replace spinlock by mutex and and protect APIs from concurrent access
> >>>>> - add support of 64-bit address in rproc_tee_get_loaded_rsc_table()
> >>>>> - Generalize teston rproc_tee_ctx.dev to prevent an unbind
> >>>>> - update copyright year
> >>>>>
> >>>>> Updates vs version [17]:
> >>>>> Fix warning:
> >>>>> warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing
> >>>>> ---
> >>>>> drivers/remoteproc/Kconfig | 10 +
> >>>>> drivers/remoteproc/Makefile | 1 +
> >>>>> drivers/remoteproc/remoteproc_tee.c | 708 ++++++++++++++++++++++++++++
> >>>>> include/linux/remoteproc_tee.h | 87 ++++
> >>>>> 4 files changed, 806 insertions(+)
> >>>>> create mode 100644 drivers/remoteproc/remoteproc_tee.c
> >>>>> create mode 100644 include/linux/remoteproc_tee.h
> >>>>>
> >
> > <snip>
> >
> >>>>> +
> >>>>> +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);
> >>>>> +
> >>>>> + ret = mutex_lock_interruptible(&ctx_lock);
> >>>>> + if (ret)
> >>>>> + return ret;
> >>>>> +
> >>>>> + rproc_tee_ctx.dev = dev;
> >>>>> + rproc_tee_ctx.tee_ctx = tee_ctx;
> >>>>> + INIT_LIST_HEAD(&rproc_tee_ctx.sessions);
> >>>>> + mutex_unlock(&ctx_lock);
> >>>>> +
> >>>>> + return 0;
> >>>>> +}
> >>>>
> >>>> As you mentioned above, this could be extended to other TEEs. If so, is it possible for probe
> >>>> to be called multiple times if we we have other TEE devices exposing the firmware authentication
> >>>> service? In that case, I think rproc_tee_ctx should be dynamically initializated instead of being
> >>>> static. And since we are creating a link between the Rproc device and TEE device, a call to a
> >>>> function like rproc_tee_start() could retreive the associated TEE device, and then the associated
> >>>> rproc_tee? :)
> >>>
> >>> I have never seen a use case that requires multiple instances, but perhaps you
> >>> have some?
> >>>
> >>> We can expect only one TEE, which could be OP-TEE, Trusty, or another.
> >>> The device is associated with a unique UUID, so only one instance is expected.
> >>>
> >>> That said, making this driver support multiple instances seems like a valid
> >>> future enhancement. However, I would suggest implementing it as a second step
> >>> when there is a concrete need.
> >>>
> >>
> >> My thought process on this stems from 1) the recent ARM FF-A developments and 2) from the current
> >> implementation of the TEE subsystem which allows multiple back-end drivers to register themselves
> >> via the tee_device_register() API. This means, that it's possible to have a configuration
> >> where a platform supports multiple TEEs running as Secure Partitions via FF-A, and each of those
> >> TEEs register their services as PTA devices on the TEE bus.
> >>
> >> However, I do not really know if it's possible to have a UUID collision in such a case, which
> >> would lead to rproc_tee_probe() being called twice above, which is why I raised this question. :)
> >>
> >> All of this aside, I realize now that other TEE client drivers are also implemented with a static
> >> private data similar to how you are doing it. So perhaps we can think of this as a later
> >> enhancement if we believe that the scenario I am describing is not possible in the near future..
> >>
> >
> > Theoretically it is possible for multiple TEE services to be there but
> > why should a platform/silicon vendor require 2 redundant remoteproc firmware
> > loading services to be supported? It should either be a service hosted
> > by the trusted OS or can rather be an independent platform service
> > running as a FF-A secure partition.
> >
> I agree that it doesn't make sense for a system integrator to have two remoteproc firmware
> loading services supported from two different TEEs running as Secure Partitions.
> After all, one service exposed by one TEE is good enough for fulfilling any use-case.
>
> My concern is that ARM FF-A makes its possible to have a platform running two TEEs, which
> each have their own remoteproc firmware authentication service implemented (as usually TEEs do).
> In such a scenario, when both TEEs enumerate their services on the TEE bus, and find a match
> because the rproc_tee_id_table has a UUID for say, both the TS-TEE remoteproc service and
> OP-TEE remoteproc service, rproc_tee_probe() will be called twice, and the current implementation
> will break because it uses a single static rproc_tee_ctx, whose contents would be overwritten
> leading to unexpected scenarios.
>
> And so, should TEE subsystem clients (like this one) be prepared to handle such as scenario?
With Arm FF-A and platforms supporting S-EL2, yes it is possible to run
multiple trusted OS in parallel. However, the recommendation is to then
host the platform specific services as independent FF-A based Secure
Partitions (SPs) which aren't tied to any particular trusted OS. I have
CCed some Arm folks who on the other thread were interested to extend
this driver to support remoteproc service hosted as a FF-A based secure
partition.
-Sumit
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2025-08-18 5:07 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-25 9:40 [PATCH v19 0/6] Introduction of a remoteproc tee to load signed firmware Arnaud Pouliquen
2025-06-25 9:40 ` [PATCH v19 1/6] remoteproc: core: Introduce rproc_pa_to_va helper Arnaud Pouliquen
2025-06-25 9:40 ` [PATCH v19 2/6] remoteproc: Add TEE support Arnaud Pouliquen
2025-07-31 10:25 ` Harshal Dev
2025-08-01 7:23 ` Arnaud POULIQUEN
2025-08-04 9:26 ` Harshal Dev
2025-08-11 14:11 ` Sumit Garg
2025-08-14 10:47 ` Harshal Dev
2025-08-18 5:07 ` Sumit Garg
2025-08-13 16:42 ` Abdellatif El Khlifi
2025-08-14 7:17 ` Sumit Garg
2025-06-25 9:40 ` [PATCH v19 3/6] remoteproc: Introduce optional release_fw operation Arnaud Pouliquen
2025-06-25 9:40 ` [PATCH v19 4/6] dt-bindings: remoteproc: Add compatibility for TEE support Arnaud Pouliquen
2025-06-25 9:40 ` [PATCH v19 5/6] remoteproc: stm32: Create sub-functions to request shutdown and release Arnaud Pouliquen
2025-06-25 9:40 ` [PATCH v19 6/6] 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 NNTP newsgroup(s).