* [PATCH v3 1/1] tee: optee: expose OS revision via sysfs
@ 2025-12-26 13:19 ` Aristo Chen
0 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2025-12-26 13:19 UTC (permalink / raw)
To: linux-kernel
Cc: jens.wiklander, sumit.garg, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
Today the only way to read the OP-TEE OS version is from dmesg/journal
logs, which can be lost as buffers roll over. Capture the OS revision
(major/minor/build_id) from secure world for both SMC and FF-A ABIs, store
it in the OP-TEE driver, and expose a stable userspace readout via
/sys/class/tee/tee*/revision.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
---
Documentation/ABI/testing/sysfs-class-tee | 10 +++++
drivers/tee/optee/core.c | 22 ++++++++++
drivers/tee/optee/ffa_abi.c | 25 ++++++++++-
drivers/tee/optee/optee_private.h | 19 +++++++++
drivers/tee/optee/smc_abi.c | 25 ++++++++++-
drivers/tee/tee_core.c | 52 ++++++++++++++++++++++-
include/linux/tee_core.h | 5 +++
include/linux/tee_drv.h | 3 ++
8 files changed, 156 insertions(+), 5 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
index c9144d16003e..6e783210104e 100644
--- a/Documentation/ABI/testing/sysfs-class-tee
+++ b/Documentation/ABI/testing/sysfs-class-tee
@@ -13,3 +13,13 @@ Description:
space if the variable is absent. The primary purpose
of this variable is to let systemd know whether
tee-supplicant is needed in the early boot with initramfs.
+
+What: /sys/class/tee/tee{,priv}X/revision
+Date: Dec 2025
+KernelVersion: 6.18
+Contact: op-tee@lists.trustedfirmware.org
+Description:
+ Read-only revision string reported by the TEE driver. This is
+ for diagnostics only and must not be used to infer feature
+ support. Use TEE_IOC_VERSION for capability and compatibility
+ checks.
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 5b62139714ce..58ee9ac0e467 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -63,6 +63,28 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
}
+int optee_get_revision(struct optee *optee, char *buf, size_t len)
+{
+ u64 build_id;
+
+ if (!optee)
+ return -ENODEV;
+ if (!buf || !len)
+ return -EINVAL;
+
+ build_id = optee->revision.os_build_id;
+ if (build_id)
+ scnprintf(buf, len, "%u.%u (%016llx)",
+ optee->revision.os_major,
+ optee->revision.os_minor,
+ (unsigned long long)build_id);
+ else
+ scnprintf(buf, len, "%u.%u", optee->revision.os_major,
+ optee->revision.os_minor);
+
+ return 0;
+}
+
static void optee_bus_scan(struct work_struct *work)
{
WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
index bf8390789ecf..0a2990c2491e 100644
--- a/drivers/tee/optee/ffa_abi.c
+++ b/drivers/tee/optee/ffa_abi.c
@@ -776,7 +776,8 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
*/
static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
- const struct ffa_ops *ops)
+ const struct ffa_ops *ops,
+ struct optee_revision *revision)
{
const struct ffa_msg_ops *msg_ops = ops->msg_ops;
struct ffa_send_direct_data data = {
@@ -806,6 +807,12 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
pr_err("Unexpected error %d\n", rc);
return false;
}
+ if (revision) {
+ revision->os_major = data.data0;
+ revision->os_minor = data.data1;
+ revision->os_build_id = data.data2;
+ }
+
if (data.data2)
pr_info("revision %lu.%lu (%08lx)",
data.data0, data.data1, data.data2);
@@ -898,8 +905,12 @@ static int optee_ffa_open(struct tee_context *ctx)
return optee_open(ctx, true);
}
+static int optee_ffa_get_tee_revision(struct tee_device *teedev,
+ char *buf, size_t len);
+
static const struct tee_driver_ops optee_ffa_clnt_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_ffa_get_tee_revision,
.open = optee_ffa_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -918,6 +929,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
static const struct tee_driver_ops optee_ffa_supp_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_ffa_get_tee_revision,
.open = optee_ffa_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1034,6 +1046,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
{
const struct ffa_notifier_ops *notif_ops;
const struct ffa_ops *ffa_ops;
+ struct optee_revision revision = { };
unsigned int max_notif_value;
unsigned int rpc_param_count;
struct tee_shm_pool *pool;
@@ -1047,7 +1060,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
ffa_ops = ffa_dev->ops;
notif_ops = ffa_ops->notifier_ops;
- if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops))
+ if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &revision))
return -EINVAL;
if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &sec_caps,
@@ -1059,6 +1072,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
optee = kzalloc(sizeof(*optee), GFP_KERNEL);
if (!optee)
return -ENOMEM;
+ optee->revision = revision;
pool = optee_ffa_shm_pool_alloc_pages();
if (IS_ERR(pool)) {
@@ -1194,3 +1208,10 @@ void optee_ffa_abi_unregister(void)
if (IS_REACHABLE(CONFIG_ARM_FFA_TRANSPORT))
ffa_unregister(&optee_ffa_driver);
}
+static int optee_ffa_get_tee_revision(struct tee_device *teedev,
+ char *buf, size_t len)
+{
+ struct optee *optee = tee_get_drvdata(teedev);
+
+ return optee_get_revision(optee, buf, len);
+}
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index db9ea673fbca..8d65f8b16b4a 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -171,6 +171,24 @@ struct optee_ffa {
struct optee;
+/**
+ * struct optee_revision - OP-TEE OS revision reported by secure world
+ * @os_major: OP-TEE OS major version
+ * @os_minor: OP-TEE OS minor version
+ * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
+ *
+ * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
+ * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
+ * FF-A ABI version.
+ */
+struct optee_revision {
+ u32 os_major;
+ u32 os_minor;
+ u64 os_build_id;
+};
+
+int optee_get_revision(struct optee *optee, char *buf, size_t len);
+
/**
* struct optee_ops - OP-TEE driver internal operations
* @do_call_with_arg: enters OP-TEE in secure world
@@ -249,6 +267,7 @@ struct optee {
bool in_kernel_rpmb_routing;
struct work_struct scan_bus_work;
struct work_struct rpmb_scan_bus_work;
+ struct optee_revision revision;
};
struct optee_session {
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
index 0be663fcd52b..4dc2c16147ee 100644
--- a/drivers/tee/optee/smc_abi.c
+++ b/drivers/tee/optee/smc_abi.c
@@ -1240,8 +1240,12 @@ static int optee_smc_open(struct tee_context *ctx)
return optee_open(ctx, sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL);
}
+static int optee_get_tee_revision(struct tee_device *teedev,
+ char *buf, size_t len);
+
static const struct tee_driver_ops optee_clnt_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_tee_revision,
.open = optee_smc_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -1261,6 +1265,7 @@ static const struct tee_desc optee_clnt_desc = {
static const struct tee_driver_ops optee_supp_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_tee_revision,
.open = optee_smc_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1323,7 +1328,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
}
#endif
-static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
+static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
+ struct optee_revision *revision)
{
union {
struct arm_smccc_res smccc;
@@ -1337,6 +1343,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
&res.smccc);
+ if (revision) {
+ revision->os_major = res.result.major;
+ revision->os_minor = res.result.minor;
+ revision->os_build_id = res.result.build_id;
+ }
+
if (res.result.build_id)
pr_info("revision %lu.%lu (%0*lx)", res.result.major,
res.result.minor, (int)sizeof(res.result.build_id) * 2,
@@ -1727,6 +1739,7 @@ static int optee_probe(struct platform_device *pdev)
unsigned int thread_count;
struct tee_device *teedev;
struct tee_context *ctx;
+ struct optee_revision revision = { };
u32 max_notif_value;
u32 arg_cache_flags;
u32 sec_caps;
@@ -1745,7 +1758,7 @@ static int optee_probe(struct platform_device *pdev)
return -EINVAL;
}
- optee_msg_get_os_revision(invoke_fn);
+ optee_msg_get_os_revision(invoke_fn, &revision);
if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
pr_warn("api revision mismatch\n");
@@ -1814,6 +1827,7 @@ static int optee_probe(struct platform_device *pdev)
rc = -ENOMEM;
goto err_free_shm_pool;
}
+ optee->revision = revision;
optee->ops = &optee_ops;
optee->smc.invoke_fn = invoke_fn;
@@ -1971,3 +1985,10 @@ void optee_smc_abi_unregister(void)
{
platform_driver_unregister(&optee_driver);
}
+static int optee_get_tee_revision(struct tee_device *teedev,
+ char *buf, size_t len)
+{
+ struct optee *optee = tee_get_drvdata(teedev);
+
+ return optee_get_revision(optee, buf, len);
+}
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index d65d47cc154e..0ba0e16462b9 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -13,6 +13,7 @@
#include <linux/overflow.h>
#include <linux/slab.h>
#include <linux/tee_core.h>
+#include <linux/tee_drv.h>
#include <linux/uaccess.h>
#include <crypto/sha1.h>
#include "tee_private.h"
@@ -1146,7 +1147,56 @@ static struct attribute *tee_dev_attrs[] = {
NULL
};
-ATTRIBUTE_GROUPS(tee_dev);
+static const struct attribute_group tee_dev_group = {
+ .attrs = tee_dev_attrs,
+};
+
+static ssize_t revision_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+ char version[TEE_REVISION_STR_SIZE];
+ int ret;
+
+ if (!teedev->desc->ops->get_tee_revision)
+ return -ENODEV;
+
+ ret = teedev->desc->ops->get_tee_revision(teedev, version,
+ sizeof(version));
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%s\n", version);
+}
+static DEVICE_ATTR_RO(revision);
+
+static struct attribute *tee_revision_attrs[] = {
+ &dev_attr_revision.attr,
+ NULL
+};
+
+static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+
+ if (teedev->desc->ops->get_tee_revision)
+ return attr->mode;
+
+ return 0;
+}
+
+static const struct attribute_group tee_revision_group = {
+ .attrs = tee_revision_attrs,
+ .is_visible = tee_revision_attr_is_visible,
+};
+
+static const struct attribute_group *tee_dev_groups[] = {
+ &tee_dev_group,
+ &tee_revision_group,
+ NULL
+};
static const struct class tee_class = {
.name = "tee",
diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
index 1f3e5dad6d0d..de11612afa62 100644
--- a/include/linux/tee_core.h
+++ b/include/linux/tee_core.h
@@ -76,6 +76,9 @@ struct tee_device {
/**
* struct tee_driver_ops - driver operations vtable
* @get_version: returns version of driver
+ * @get_tee_revision: returns revision string (diagnostic only);
+ * do not infer feature support from this, use
+ * TEE_IOC_VERSION instead
* @open: called for a context when the device file is opened
* @close_context: called when the device file is closed
* @release: called to release the context
@@ -98,6 +101,8 @@ struct tee_device {
struct tee_driver_ops {
void (*get_version)(struct tee_device *teedev,
struct tee_ioctl_version_data *vers);
+ int (*get_tee_revision)(struct tee_device *teedev,
+ char *buf, size_t len);
int (*open)(struct tee_context *ctx);
void (*close_context)(struct tee_context *ctx);
void (*release)(struct tee_context *ctx);
diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index 88a6f9697c89..e76238d435ce 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -322,4 +322,7 @@ struct tee_client_driver {
#define to_tee_client_driver(d) \
container_of_const(d, struct tee_client_driver, driver)
+/* Size for TEE revision string buffer used by get_tee_revision(). */
+#define TEE_REVISION_STR_SIZE 128
+
#endif /*__TEE_DRV_H*/
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread* Re: [PATCH v3 1/1] tee: optee: expose OS revision via sysfs
2025-12-26 13:19 ` Aristo Chen
@ 2025-12-29 4:59 ` Sumit Garg
-1 siblings, 0 replies; 74+ messages in thread
From: Sumit Garg via OP-TEE @ 2025-12-29 4:59 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
Hi Aristo,
On Fri, Dec 26, 2025 at 09:19:13PM +0800, Aristo Chen wrote:
> Today the only way to read the OP-TEE OS version is from dmesg/journal
> logs, which can be lost as buffers roll over. Capture the OS revision
> (major/minor/build_id) from secure world for both SMC and FF-A ABIs, store
> it in the OP-TEE driver, and expose a stable userspace readout via
> /sys/class/tee/tee*/revision.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> Documentation/ABI/testing/sysfs-class-tee | 10 +++++
> drivers/tee/optee/core.c | 22 ++++++++++
> drivers/tee/optee/ffa_abi.c | 25 ++++++++++-
> drivers/tee/optee/optee_private.h | 19 +++++++++
> drivers/tee/optee/smc_abi.c | 25 ++++++++++-
> drivers/tee/tee_core.c | 52 ++++++++++++++++++++++-
> include/linux/tee_core.h | 5 +++
> include/linux/tee_drv.h | 3 ++
> 8 files changed, 156 insertions(+), 5 deletions(-)
Please split up this patch properly as to what goes into the generic TEE
driver vs the OP-TEE changes.
>
> diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
> index c9144d16003e..6e783210104e 100644
> --- a/Documentation/ABI/testing/sysfs-class-tee
> +++ b/Documentation/ABI/testing/sysfs-class-tee
> @@ -13,3 +13,13 @@ Description:
> space if the variable is absent. The primary purpose
> of this variable is to let systemd know whether
> tee-supplicant is needed in the early boot with initramfs.
> +
> +What: /sys/class/tee/tee{,priv}X/revision
> +Date: Dec 2025
> +KernelVersion: 6.18
> +Contact: op-tee@lists.trustedfirmware.org
> +Description:
> + Read-only revision string reported by the TEE driver. This is
> + for diagnostics only and must not be used to infer feature
> + support. Use TEE_IOC_VERSION for capability and compatibility
> + checks.
> diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> index 5b62139714ce..58ee9ac0e467 100644
> --- a/drivers/tee/optee/core.c
> +++ b/drivers/tee/optee/core.c
> @@ -63,6 +63,28 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> }
>
> +int optee_get_revision(struct optee *optee, char *buf, size_t len)
> +{
> + u64 build_id;
> +
> + if (!optee)
> + return -ENODEV;
> + if (!buf || !len)
> + return -EINVAL;
> +
> + build_id = optee->revision.os_build_id;
> + if (build_id)
> + scnprintf(buf, len, "%u.%u (%016llx)",
> + optee->revision.os_major,
> + optee->revision.os_minor,
> + (unsigned long long)build_id);
> + else
> + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> + optee->revision.os_minor);
> +
> + return 0;
> +}
> +
> static void optee_bus_scan(struct work_struct *work)
> {
> WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> index bf8390789ecf..0a2990c2491e 100644
> --- a/drivers/tee/optee/ffa_abi.c
> +++ b/drivers/tee/optee/ffa_abi.c
> @@ -776,7 +776,8 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> */
>
> static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> - const struct ffa_ops *ops)
> + const struct ffa_ops *ops,
> + struct optee_revision *revision)
> {
> const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> struct ffa_send_direct_data data = {
> @@ -806,6 +807,12 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> pr_err("Unexpected error %d\n", rc);
> return false;
> }
> + if (revision) {
> + revision->os_major = data.data0;
> + revision->os_minor = data.data1;
> + revision->os_build_id = data.data2;
> + }
> +
> if (data.data2)
> pr_info("revision %lu.%lu (%08lx)",
> data.data0, data.data1, data.data2);
> @@ -898,8 +905,12 @@ static int optee_ffa_open(struct tee_context *ctx)
> return optee_open(ctx, true);
> }
>
> +static int optee_ffa_get_tee_revision(struct tee_device *teedev,
> + char *buf, size_t len);
I don't think you need a wrapper here but insted you can just use the
generic optee_get_revision() callback.
> +
> static const struct tee_driver_ops optee_ffa_clnt_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_ffa_get_tee_revision,
This will then become:
.get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -918,6 +929,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
>
> static const struct tee_driver_ops optee_ffa_supp_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_ffa_get_tee_revision,
ditto.
> .open = optee_ffa_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1034,6 +1046,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> {
> const struct ffa_notifier_ops *notif_ops;
> const struct ffa_ops *ffa_ops;
> + struct optee_revision revision = { };
> unsigned int max_notif_value;
> unsigned int rpc_param_count;
> struct tee_shm_pool *pool;
> @@ -1047,7 +1060,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> ffa_ops = ffa_dev->ops;
> notif_ops = ffa_ops->notifier_ops;
>
> - if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops))
> + if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &revision))
> return -EINVAL;
>
> if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &sec_caps,
> @@ -1059,6 +1072,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> optee = kzalloc(sizeof(*optee), GFP_KERNEL);
> if (!optee)
> return -ENOMEM;
> + optee->revision = revision;
>
> pool = optee_ffa_shm_pool_alloc_pages();
> if (IS_ERR(pool)) {
> @@ -1194,3 +1208,10 @@ void optee_ffa_abi_unregister(void)
> if (IS_REACHABLE(CONFIG_ARM_FFA_TRANSPORT))
> ffa_unregister(&optee_ffa_driver);
> }
> +static int optee_ffa_get_tee_revision(struct tee_device *teedev,
> + char *buf, size_t len)
> +{
> + struct optee *optee = tee_get_drvdata(teedev);
> +
> + return optee_get_revision(optee, buf, len);
> +}
> diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> index db9ea673fbca..8d65f8b16b4a 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -171,6 +171,24 @@ struct optee_ffa {
>
> struct optee;
>
> +/**
> + * struct optee_revision - OP-TEE OS revision reported by secure world
> + * @os_major: OP-TEE OS major version
> + * @os_minor: OP-TEE OS minor version
> + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> + *
> + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> + * FF-A ABI version.
> + */
> +struct optee_revision {
> + u32 os_major;
> + u32 os_minor;
> + u64 os_build_id;
> +};
> +
> +int optee_get_revision(struct optee *optee, char *buf, size_t len);
> +
> /**
> * struct optee_ops - OP-TEE driver internal operations
> * @do_call_with_arg: enters OP-TEE in secure world
> @@ -249,6 +267,7 @@ struct optee {
> bool in_kernel_rpmb_routing;
> struct work_struct scan_bus_work;
> struct work_struct rpmb_scan_bus_work;
> + struct optee_revision revision;
> };
>
> struct optee_session {
> diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> index 0be663fcd52b..4dc2c16147ee 100644
> --- a/drivers/tee/optee/smc_abi.c
> +++ b/drivers/tee/optee/smc_abi.c
> @@ -1240,8 +1240,12 @@ static int optee_smc_open(struct tee_context *ctx)
> return optee_open(ctx, sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL);
> }
>
> +static int optee_get_tee_revision(struct tee_device *teedev,
> + char *buf, size_t len);
> +
> static const struct tee_driver_ops optee_clnt_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_tee_revision,
> .open = optee_smc_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -1261,6 +1265,7 @@ static const struct tee_desc optee_clnt_desc = {
>
> static const struct tee_driver_ops optee_supp_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_tee_revision,
> .open = optee_smc_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1323,7 +1328,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> }
> #endif
>
> -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> + struct optee_revision *revision)
> {
> union {
> struct arm_smccc_res smccc;
> @@ -1337,6 +1343,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> &res.smccc);
>
> + if (revision) {
> + revision->os_major = res.result.major;
> + revision->os_minor = res.result.minor;
> + revision->os_build_id = res.result.build_id;
> + }
> +
> if (res.result.build_id)
> pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> res.result.minor, (int)sizeof(res.result.build_id) * 2,
> @@ -1727,6 +1739,7 @@ static int optee_probe(struct platform_device *pdev)
> unsigned int thread_count;
> struct tee_device *teedev;
> struct tee_context *ctx;
> + struct optee_revision revision = { };
> u32 max_notif_value;
> u32 arg_cache_flags;
> u32 sec_caps;
> @@ -1745,7 +1758,7 @@ static int optee_probe(struct platform_device *pdev)
> return -EINVAL;
> }
>
> - optee_msg_get_os_revision(invoke_fn);
> + optee_msg_get_os_revision(invoke_fn, &revision);
>
> if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> pr_warn("api revision mismatch\n");
> @@ -1814,6 +1827,7 @@ static int optee_probe(struct platform_device *pdev)
> rc = -ENOMEM;
> goto err_free_shm_pool;
> }
> + optee->revision = revision;
>
> optee->ops = &optee_ops;
> optee->smc.invoke_fn = invoke_fn;
> @@ -1971,3 +1985,10 @@ void optee_smc_abi_unregister(void)
> {
> platform_driver_unregister(&optee_driver);
> }
> +static int optee_get_tee_revision(struct tee_device *teedev,
> + char *buf, size_t len)
Ditto here, you don't need this extra wrapper here.
> +{
> + struct optee *optee = tee_get_drvdata(teedev);
> +
> + return optee_get_revision(optee, buf, len);
> +}
> diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> index d65d47cc154e..0ba0e16462b9 100644
> --- a/drivers/tee/tee_core.c
> +++ b/drivers/tee/tee_core.c
> @@ -13,6 +13,7 @@
> #include <linux/overflow.h>
> #include <linux/slab.h>
> #include <linux/tee_core.h>
> +#include <linux/tee_drv.h>
This include is rather meant for TEE client drivers, don't include it
here. Instead use linux/tee_core.h.
> #include <linux/uaccess.h>
> #include <crypto/sha1.h>
> #include "tee_private.h"
> @@ -1146,7 +1147,56 @@ static struct attribute *tee_dev_attrs[] = {
> NULL
> };
>
> -ATTRIBUTE_GROUPS(tee_dev);
> +static const struct attribute_group tee_dev_group = {
> + .attrs = tee_dev_attrs,
> +};
> +
> +static ssize_t revision_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> + char version[TEE_REVISION_STR_SIZE];
> + int ret;
> +
> + if (!teedev->desc->ops->get_tee_revision)
> + return -ENODEV;
> +
> + ret = teedev->desc->ops->get_tee_revision(teedev, version,
> + sizeof(version));
> + if (ret)
> + return ret;
> +
> + return sysfs_emit(buf, "%s\n", version);
> +}
> +static DEVICE_ATTR_RO(revision);
> +
> +static struct attribute *tee_revision_attrs[] = {
> + &dev_attr_revision.attr,
> + NULL
> +};
> +
> +static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
> + struct attribute *attr, int n)
> +{
> + struct device *dev = kobj_to_dev(kobj);
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> +
> + if (teedev->desc->ops->get_tee_revision)
> + return attr->mode;
> +
> + return 0;
> +}
> +
> +static const struct attribute_group tee_revision_group = {
> + .attrs = tee_revision_attrs,
> + .is_visible = tee_revision_attr_is_visible,
> +};
> +
> +static const struct attribute_group *tee_dev_groups[] = {
> + &tee_dev_group,
> + &tee_revision_group,
> + NULL
> +};
>
> static const struct class tee_class = {
> .name = "tee",
> diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
> index 1f3e5dad6d0d..de11612afa62 100644
> --- a/include/linux/tee_core.h
> +++ b/include/linux/tee_core.h
> @@ -76,6 +76,9 @@ struct tee_device {
> /**
> * struct tee_driver_ops - driver operations vtable
> * @get_version: returns version of driver
> + * @get_tee_revision: returns revision string (diagnostic only);
> + * do not infer feature support from this, use
> + * TEE_IOC_VERSION instead
> * @open: called for a context when the device file is opened
> * @close_context: called when the device file is closed
> * @release: called to release the context
> @@ -98,6 +101,8 @@ struct tee_device {
> struct tee_driver_ops {
> void (*get_version)(struct tee_device *teedev,
> struct tee_ioctl_version_data *vers);
> + int (*get_tee_revision)(struct tee_device *teedev,
> + char *buf, size_t len);
> int (*open)(struct tee_context *ctx);
> void (*close_context)(struct tee_context *ctx);
> void (*release)(struct tee_context *ctx);
> diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
> index 88a6f9697c89..e76238d435ce 100644
> --- a/include/linux/tee_drv.h
> +++ b/include/linux/tee_drv.h
> @@ -322,4 +322,7 @@ struct tee_client_driver {
> #define to_tee_client_driver(d) \
> container_of_const(d, struct tee_client_driver, driver)
>
> +/* Size for TEE revision string buffer used by get_tee_revision(). */
> +#define TEE_REVISION_STR_SIZE 128
This should rather go to linux/tee_core.h.
-Sumit
> +
> #endif /*__TEE_DRV_H*/
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v3 1/1] tee: optee: expose OS revision via sysfs
@ 2025-12-29 4:59 ` Sumit Garg
0 siblings, 0 replies; 74+ messages in thread
From: Sumit Garg @ 2025-12-29 4:59 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, jens.wiklander, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
Hi Aristo,
On Fri, Dec 26, 2025 at 09:19:13PM +0800, Aristo Chen wrote:
> Today the only way to read the OP-TEE OS version is from dmesg/journal
> logs, which can be lost as buffers roll over. Capture the OS revision
> (major/minor/build_id) from secure world for both SMC and FF-A ABIs, store
> it in the OP-TEE driver, and expose a stable userspace readout via
> /sys/class/tee/tee*/revision.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> Documentation/ABI/testing/sysfs-class-tee | 10 +++++
> drivers/tee/optee/core.c | 22 ++++++++++
> drivers/tee/optee/ffa_abi.c | 25 ++++++++++-
> drivers/tee/optee/optee_private.h | 19 +++++++++
> drivers/tee/optee/smc_abi.c | 25 ++++++++++-
> drivers/tee/tee_core.c | 52 ++++++++++++++++++++++-
> include/linux/tee_core.h | 5 +++
> include/linux/tee_drv.h | 3 ++
> 8 files changed, 156 insertions(+), 5 deletions(-)
Please split up this patch properly as to what goes into the generic TEE
driver vs the OP-TEE changes.
>
> diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
> index c9144d16003e..6e783210104e 100644
> --- a/Documentation/ABI/testing/sysfs-class-tee
> +++ b/Documentation/ABI/testing/sysfs-class-tee
> @@ -13,3 +13,13 @@ Description:
> space if the variable is absent. The primary purpose
> of this variable is to let systemd know whether
> tee-supplicant is needed in the early boot with initramfs.
> +
> +What: /sys/class/tee/tee{,priv}X/revision
> +Date: Dec 2025
> +KernelVersion: 6.18
> +Contact: op-tee@lists.trustedfirmware.org
> +Description:
> + Read-only revision string reported by the TEE driver. This is
> + for diagnostics only and must not be used to infer feature
> + support. Use TEE_IOC_VERSION for capability and compatibility
> + checks.
> diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> index 5b62139714ce..58ee9ac0e467 100644
> --- a/drivers/tee/optee/core.c
> +++ b/drivers/tee/optee/core.c
> @@ -63,6 +63,28 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> }
>
> +int optee_get_revision(struct optee *optee, char *buf, size_t len)
> +{
> + u64 build_id;
> +
> + if (!optee)
> + return -ENODEV;
> + if (!buf || !len)
> + return -EINVAL;
> +
> + build_id = optee->revision.os_build_id;
> + if (build_id)
> + scnprintf(buf, len, "%u.%u (%016llx)",
> + optee->revision.os_major,
> + optee->revision.os_minor,
> + (unsigned long long)build_id);
> + else
> + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> + optee->revision.os_minor);
> +
> + return 0;
> +}
> +
> static void optee_bus_scan(struct work_struct *work)
> {
> WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> index bf8390789ecf..0a2990c2491e 100644
> --- a/drivers/tee/optee/ffa_abi.c
> +++ b/drivers/tee/optee/ffa_abi.c
> @@ -776,7 +776,8 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> */
>
> static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> - const struct ffa_ops *ops)
> + const struct ffa_ops *ops,
> + struct optee_revision *revision)
> {
> const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> struct ffa_send_direct_data data = {
> @@ -806,6 +807,12 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> pr_err("Unexpected error %d\n", rc);
> return false;
> }
> + if (revision) {
> + revision->os_major = data.data0;
> + revision->os_minor = data.data1;
> + revision->os_build_id = data.data2;
> + }
> +
> if (data.data2)
> pr_info("revision %lu.%lu (%08lx)",
> data.data0, data.data1, data.data2);
> @@ -898,8 +905,12 @@ static int optee_ffa_open(struct tee_context *ctx)
> return optee_open(ctx, true);
> }
>
> +static int optee_ffa_get_tee_revision(struct tee_device *teedev,
> + char *buf, size_t len);
I don't think you need a wrapper here but insted you can just use the
generic optee_get_revision() callback.
> +
> static const struct tee_driver_ops optee_ffa_clnt_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_ffa_get_tee_revision,
This will then become:
.get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -918,6 +929,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
>
> static const struct tee_driver_ops optee_ffa_supp_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_ffa_get_tee_revision,
ditto.
> .open = optee_ffa_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1034,6 +1046,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> {
> const struct ffa_notifier_ops *notif_ops;
> const struct ffa_ops *ffa_ops;
> + struct optee_revision revision = { };
> unsigned int max_notif_value;
> unsigned int rpc_param_count;
> struct tee_shm_pool *pool;
> @@ -1047,7 +1060,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> ffa_ops = ffa_dev->ops;
> notif_ops = ffa_ops->notifier_ops;
>
> - if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops))
> + if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &revision))
> return -EINVAL;
>
> if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &sec_caps,
> @@ -1059,6 +1072,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> optee = kzalloc(sizeof(*optee), GFP_KERNEL);
> if (!optee)
> return -ENOMEM;
> + optee->revision = revision;
>
> pool = optee_ffa_shm_pool_alloc_pages();
> if (IS_ERR(pool)) {
> @@ -1194,3 +1208,10 @@ void optee_ffa_abi_unregister(void)
> if (IS_REACHABLE(CONFIG_ARM_FFA_TRANSPORT))
> ffa_unregister(&optee_ffa_driver);
> }
> +static int optee_ffa_get_tee_revision(struct tee_device *teedev,
> + char *buf, size_t len)
> +{
> + struct optee *optee = tee_get_drvdata(teedev);
> +
> + return optee_get_revision(optee, buf, len);
> +}
> diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> index db9ea673fbca..8d65f8b16b4a 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -171,6 +171,24 @@ struct optee_ffa {
>
> struct optee;
>
> +/**
> + * struct optee_revision - OP-TEE OS revision reported by secure world
> + * @os_major: OP-TEE OS major version
> + * @os_minor: OP-TEE OS minor version
> + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> + *
> + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> + * FF-A ABI version.
> + */
> +struct optee_revision {
> + u32 os_major;
> + u32 os_minor;
> + u64 os_build_id;
> +};
> +
> +int optee_get_revision(struct optee *optee, char *buf, size_t len);
> +
> /**
> * struct optee_ops - OP-TEE driver internal operations
> * @do_call_with_arg: enters OP-TEE in secure world
> @@ -249,6 +267,7 @@ struct optee {
> bool in_kernel_rpmb_routing;
> struct work_struct scan_bus_work;
> struct work_struct rpmb_scan_bus_work;
> + struct optee_revision revision;
> };
>
> struct optee_session {
> diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> index 0be663fcd52b..4dc2c16147ee 100644
> --- a/drivers/tee/optee/smc_abi.c
> +++ b/drivers/tee/optee/smc_abi.c
> @@ -1240,8 +1240,12 @@ static int optee_smc_open(struct tee_context *ctx)
> return optee_open(ctx, sec_caps & OPTEE_SMC_SEC_CAP_MEMREF_NULL);
> }
>
> +static int optee_get_tee_revision(struct tee_device *teedev,
> + char *buf, size_t len);
> +
> static const struct tee_driver_ops optee_clnt_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_tee_revision,
> .open = optee_smc_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -1261,6 +1265,7 @@ static const struct tee_desc optee_clnt_desc = {
>
> static const struct tee_driver_ops optee_supp_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_tee_revision,
> .open = optee_smc_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1323,7 +1328,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> }
> #endif
>
> -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> + struct optee_revision *revision)
> {
> union {
> struct arm_smccc_res smccc;
> @@ -1337,6 +1343,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> &res.smccc);
>
> + if (revision) {
> + revision->os_major = res.result.major;
> + revision->os_minor = res.result.minor;
> + revision->os_build_id = res.result.build_id;
> + }
> +
> if (res.result.build_id)
> pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> res.result.minor, (int)sizeof(res.result.build_id) * 2,
> @@ -1727,6 +1739,7 @@ static int optee_probe(struct platform_device *pdev)
> unsigned int thread_count;
> struct tee_device *teedev;
> struct tee_context *ctx;
> + struct optee_revision revision = { };
> u32 max_notif_value;
> u32 arg_cache_flags;
> u32 sec_caps;
> @@ -1745,7 +1758,7 @@ static int optee_probe(struct platform_device *pdev)
> return -EINVAL;
> }
>
> - optee_msg_get_os_revision(invoke_fn);
> + optee_msg_get_os_revision(invoke_fn, &revision);
>
> if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> pr_warn("api revision mismatch\n");
> @@ -1814,6 +1827,7 @@ static int optee_probe(struct platform_device *pdev)
> rc = -ENOMEM;
> goto err_free_shm_pool;
> }
> + optee->revision = revision;
>
> optee->ops = &optee_ops;
> optee->smc.invoke_fn = invoke_fn;
> @@ -1971,3 +1985,10 @@ void optee_smc_abi_unregister(void)
> {
> platform_driver_unregister(&optee_driver);
> }
> +static int optee_get_tee_revision(struct tee_device *teedev,
> + char *buf, size_t len)
Ditto here, you don't need this extra wrapper here.
> +{
> + struct optee *optee = tee_get_drvdata(teedev);
> +
> + return optee_get_revision(optee, buf, len);
> +}
> diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> index d65d47cc154e..0ba0e16462b9 100644
> --- a/drivers/tee/tee_core.c
> +++ b/drivers/tee/tee_core.c
> @@ -13,6 +13,7 @@
> #include <linux/overflow.h>
> #include <linux/slab.h>
> #include <linux/tee_core.h>
> +#include <linux/tee_drv.h>
This include is rather meant for TEE client drivers, don't include it
here. Instead use linux/tee_core.h.
> #include <linux/uaccess.h>
> #include <crypto/sha1.h>
> #include "tee_private.h"
> @@ -1146,7 +1147,56 @@ static struct attribute *tee_dev_attrs[] = {
> NULL
> };
>
> -ATTRIBUTE_GROUPS(tee_dev);
> +static const struct attribute_group tee_dev_group = {
> + .attrs = tee_dev_attrs,
> +};
> +
> +static ssize_t revision_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> + char version[TEE_REVISION_STR_SIZE];
> + int ret;
> +
> + if (!teedev->desc->ops->get_tee_revision)
> + return -ENODEV;
> +
> + ret = teedev->desc->ops->get_tee_revision(teedev, version,
> + sizeof(version));
> + if (ret)
> + return ret;
> +
> + return sysfs_emit(buf, "%s\n", version);
> +}
> +static DEVICE_ATTR_RO(revision);
> +
> +static struct attribute *tee_revision_attrs[] = {
> + &dev_attr_revision.attr,
> + NULL
> +};
> +
> +static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
> + struct attribute *attr, int n)
> +{
> + struct device *dev = kobj_to_dev(kobj);
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> +
> + if (teedev->desc->ops->get_tee_revision)
> + return attr->mode;
> +
> + return 0;
> +}
> +
> +static const struct attribute_group tee_revision_group = {
> + .attrs = tee_revision_attrs,
> + .is_visible = tee_revision_attr_is_visible,
> +};
> +
> +static const struct attribute_group *tee_dev_groups[] = {
> + &tee_dev_group,
> + &tee_revision_group,
> + NULL
> +};
>
> static const struct class tee_class = {
> .name = "tee",
> diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
> index 1f3e5dad6d0d..de11612afa62 100644
> --- a/include/linux/tee_core.h
> +++ b/include/linux/tee_core.h
> @@ -76,6 +76,9 @@ struct tee_device {
> /**
> * struct tee_driver_ops - driver operations vtable
> * @get_version: returns version of driver
> + * @get_tee_revision: returns revision string (diagnostic only);
> + * do not infer feature support from this, use
> + * TEE_IOC_VERSION instead
> * @open: called for a context when the device file is opened
> * @close_context: called when the device file is closed
> * @release: called to release the context
> @@ -98,6 +101,8 @@ struct tee_device {
> struct tee_driver_ops {
> void (*get_version)(struct tee_device *teedev,
> struct tee_ioctl_version_data *vers);
> + int (*get_tee_revision)(struct tee_device *teedev,
> + char *buf, size_t len);
> int (*open)(struct tee_context *ctx);
> void (*close_context)(struct tee_context *ctx);
> void (*release)(struct tee_context *ctx);
> diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
> index 88a6f9697c89..e76238d435ce 100644
> --- a/include/linux/tee_drv.h
> +++ b/include/linux/tee_drv.h
> @@ -322,4 +322,7 @@ struct tee_client_driver {
> #define to_tee_client_driver(d) \
> container_of_const(d, struct tee_client_driver, driver)
>
> +/* Size for TEE revision string buffer used by get_tee_revision(). */
> +#define TEE_REVISION_STR_SIZE 128
This should rather go to linux/tee_core.h.
-Sumit
> +
> #endif /*__TEE_DRV_H*/
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH v4 1/2] tee: add revision sysfs attribute
2025-12-26 13:19 ` Aristo Chen
@ 2025-12-30 5:17 ` Aristo Chen
-1 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2025-12-30 5:17 UTC (permalink / raw)
To: linux-kernel
Cc: sumit.garg, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
Add a generic TEE revision sysfs attribute backed by a new optional
get_tee_revision() callback. The revision string is diagnostic-only and
must not be used to infer feature support.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
---
Documentation/ABI/testing/sysfs-class-tee | 10 +++++
drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
include/linux/tee_core.h | 8 ++++
3 files changed, 68 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
index c9144d16003e..6e783210104e 100644
--- a/Documentation/ABI/testing/sysfs-class-tee
+++ b/Documentation/ABI/testing/sysfs-class-tee
@@ -13,3 +13,13 @@ Description:
space if the variable is absent. The primary purpose
of this variable is to let systemd know whether
tee-supplicant is needed in the early boot with initramfs.
+
+What: /sys/class/tee/tee{,priv}X/revision
+Date: Dec 2025
+KernelVersion: 6.18
+Contact: op-tee@lists.trustedfirmware.org
+Description:
+ Read-only revision string reported by the TEE driver. This is
+ for diagnostics only and must not be used to infer feature
+ support. Use TEE_IOC_VERSION for capability and compatibility
+ checks.
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index d65d47cc154e..0a00499811c1 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
NULL
};
-ATTRIBUTE_GROUPS(tee_dev);
+static const struct attribute_group tee_dev_group = {
+ .attrs = tee_dev_attrs,
+};
+
+static ssize_t revision_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+ char version[TEE_REVISION_STR_SIZE];
+ int ret;
+
+ if (!teedev->desc->ops->get_tee_revision)
+ return -ENODEV;
+
+ ret = teedev->desc->ops->get_tee_revision(teedev, version,
+ sizeof(version));
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%s\n", version);
+}
+static DEVICE_ATTR_RO(revision);
+
+static struct attribute *tee_revision_attrs[] = {
+ &dev_attr_revision.attr,
+ NULL
+};
+
+static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+
+ if (teedev->desc->ops->get_tee_revision)
+ return attr->mode;
+
+ return 0;
+}
+
+static const struct attribute_group tee_revision_group = {
+ .attrs = tee_revision_attrs,
+ .is_visible = tee_revision_attr_is_visible,
+};
+
+static const struct attribute_group *tee_dev_groups[] = {
+ &tee_dev_group,
+ &tee_revision_group,
+ NULL
+};
static const struct class tee_class = {
.name = "tee",
diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
index 1f3e5dad6d0d..c214c38898ca 100644
--- a/include/linux/tee_core.h
+++ b/include/linux/tee_core.h
@@ -76,6 +76,9 @@ struct tee_device {
/**
* struct tee_driver_ops - driver operations vtable
* @get_version: returns version of driver
+ * @get_tee_revision: returns revision string (diagnostic only);
+ * do not infer feature support from this, use
+ * TEE_IOC_VERSION instead
* @open: called for a context when the device file is opened
* @close_context: called when the device file is closed
* @release: called to release the context
@@ -98,6 +101,8 @@ struct tee_device {
struct tee_driver_ops {
void (*get_version)(struct tee_device *teedev,
struct tee_ioctl_version_data *vers);
+ int (*get_tee_revision)(struct tee_device *teedev,
+ char *buf, size_t len);
int (*open)(struct tee_context *ctx);
void (*close_context)(struct tee_context *ctx);
void (*release)(struct tee_context *ctx);
@@ -292,6 +297,9 @@ struct tee_shm_pool *tee_shm_pool_alloc_res_mem(unsigned long vaddr,
phys_addr_t paddr, size_t size,
int min_alloc_order);
+/* Size for TEE revision string buffer used by get_tee_revision(). */
+#define TEE_REVISION_STR_SIZE 128
+
/**
* tee_shm_pool_free() - Free a shared memory pool
* @pool: The shared memory pool to free
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread* [PATCH v4 1/2] tee: add revision sysfs attribute
@ 2025-12-30 5:17 ` Aristo Chen
0 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2025-12-30 5:17 UTC (permalink / raw)
To: linux-kernel
Cc: jens.wiklander, sumit.garg, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
Add a generic TEE revision sysfs attribute backed by a new optional
get_tee_revision() callback. The revision string is diagnostic-only and
must not be used to infer feature support.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
---
Documentation/ABI/testing/sysfs-class-tee | 10 +++++
drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
include/linux/tee_core.h | 8 ++++
3 files changed, 68 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
index c9144d16003e..6e783210104e 100644
--- a/Documentation/ABI/testing/sysfs-class-tee
+++ b/Documentation/ABI/testing/sysfs-class-tee
@@ -13,3 +13,13 @@ Description:
space if the variable is absent. The primary purpose
of this variable is to let systemd know whether
tee-supplicant is needed in the early boot with initramfs.
+
+What: /sys/class/tee/tee{,priv}X/revision
+Date: Dec 2025
+KernelVersion: 6.18
+Contact: op-tee@lists.trustedfirmware.org
+Description:
+ Read-only revision string reported by the TEE driver. This is
+ for diagnostics only and must not be used to infer feature
+ support. Use TEE_IOC_VERSION for capability and compatibility
+ checks.
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index d65d47cc154e..0a00499811c1 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
NULL
};
-ATTRIBUTE_GROUPS(tee_dev);
+static const struct attribute_group tee_dev_group = {
+ .attrs = tee_dev_attrs,
+};
+
+static ssize_t revision_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+ char version[TEE_REVISION_STR_SIZE];
+ int ret;
+
+ if (!teedev->desc->ops->get_tee_revision)
+ return -ENODEV;
+
+ ret = teedev->desc->ops->get_tee_revision(teedev, version,
+ sizeof(version));
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%s\n", version);
+}
+static DEVICE_ATTR_RO(revision);
+
+static struct attribute *tee_revision_attrs[] = {
+ &dev_attr_revision.attr,
+ NULL
+};
+
+static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+
+ if (teedev->desc->ops->get_tee_revision)
+ return attr->mode;
+
+ return 0;
+}
+
+static const struct attribute_group tee_revision_group = {
+ .attrs = tee_revision_attrs,
+ .is_visible = tee_revision_attr_is_visible,
+};
+
+static const struct attribute_group *tee_dev_groups[] = {
+ &tee_dev_group,
+ &tee_revision_group,
+ NULL
+};
static const struct class tee_class = {
.name = "tee",
diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
index 1f3e5dad6d0d..c214c38898ca 100644
--- a/include/linux/tee_core.h
+++ b/include/linux/tee_core.h
@@ -76,6 +76,9 @@ struct tee_device {
/**
* struct tee_driver_ops - driver operations vtable
* @get_version: returns version of driver
+ * @get_tee_revision: returns revision string (diagnostic only);
+ * do not infer feature support from this, use
+ * TEE_IOC_VERSION instead
* @open: called for a context when the device file is opened
* @close_context: called when the device file is closed
* @release: called to release the context
@@ -98,6 +101,8 @@ struct tee_device {
struct tee_driver_ops {
void (*get_version)(struct tee_device *teedev,
struct tee_ioctl_version_data *vers);
+ int (*get_tee_revision)(struct tee_device *teedev,
+ char *buf, size_t len);
int (*open)(struct tee_context *ctx);
void (*close_context)(struct tee_context *ctx);
void (*release)(struct tee_context *ctx);
@@ -292,6 +297,9 @@ struct tee_shm_pool *tee_shm_pool_alloc_res_mem(unsigned long vaddr,
phys_addr_t paddr, size_t size,
int min_alloc_order);
+/* Size for TEE revision string buffer used by get_tee_revision(). */
+#define TEE_REVISION_STR_SIZE 128
+
/**
* tee_shm_pool_free() - Free a shared memory pool
* @pool: The shared memory pool to free
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread* [PATCH v4 2/2] tee: optee: store OS revision for TEE core
2025-12-30 5:17 ` Aristo Chen
@ 2025-12-30 5:17 ` Aristo Chen
-1 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2025-12-30 5:17 UTC (permalink / raw)
To: linux-kernel
Cc: sumit.garg, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
Collect OP-TEE OS revision from secure world for both SMC and FF-A ABIs,
store it in the OP-TEE driver, and expose it through the generic
get_tee_revision() callback.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
---
drivers/tee/optee/core.c | 23 +++++++++++++++++++++++
drivers/tee/optee/ffa_abi.c | 14 ++++++++++++--
drivers/tee/optee/optee_private.h | 19 +++++++++++++++++++
drivers/tee/optee/smc_abi.c | 15 +++++++++++++--
4 files changed, 67 insertions(+), 4 deletions(-)
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 5b62139714ce..2d807bc748bc 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
}
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
+{
+ struct optee *optee = tee_get_drvdata(teedev);
+ u64 build_id;
+
+ if (!optee)
+ return -ENODEV;
+ if (!buf || !len)
+ return -EINVAL;
+
+ build_id = optee->revision.os_build_id;
+ if (build_id)
+ scnprintf(buf, len, "%u.%u (%016llx)",
+ optee->revision.os_major,
+ optee->revision.os_minor,
+ (unsigned long long)build_id);
+ else
+ scnprintf(buf, len, "%u.%u", optee->revision.os_major,
+ optee->revision.os_minor);
+
+ return 0;
+}
+
static void optee_bus_scan(struct work_struct *work)
{
WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
index bf8390789ecf..d67ca1ec9506 100644
--- a/drivers/tee/optee/ffa_abi.c
+++ b/drivers/tee/optee/ffa_abi.c
@@ -776,7 +776,8 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
*/
static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
- const struct ffa_ops *ops)
+ const struct ffa_ops *ops,
+ struct optee_revision *revision)
{
const struct ffa_msg_ops *msg_ops = ops->msg_ops;
struct ffa_send_direct_data data = {
@@ -806,6 +807,11 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
pr_err("Unexpected error %d\n", rc);
return false;
}
+ if (revision) {
+ revision->os_major = data.data0;
+ revision->os_minor = data.data1;
+ revision->os_build_id = data.data2;
+ }
if (data.data2)
pr_info("revision %lu.%lu (%08lx)",
data.data0, data.data1, data.data2);
@@ -900,6 +906,7 @@ static int optee_ffa_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_ffa_clnt_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -918,6 +925,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
static const struct tee_driver_ops optee_ffa_supp_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1034,6 +1042,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
{
const struct ffa_notifier_ops *notif_ops;
const struct ffa_ops *ffa_ops;
+ struct optee_revision revision = { };
unsigned int max_notif_value;
unsigned int rpc_param_count;
struct tee_shm_pool *pool;
@@ -1047,7 +1056,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
ffa_ops = ffa_dev->ops;
notif_ops = ffa_ops->notifier_ops;
- if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops))
+ if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &revision))
return -EINVAL;
if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &sec_caps,
@@ -1059,6 +1068,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
optee = kzalloc(sizeof(*optee), GFP_KERNEL);
if (!optee)
return -ENOMEM;
+ optee->revision = revision;
pool = optee_ffa_shm_pool_alloc_pages();
if (IS_ERR(pool)) {
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index db9ea673fbca..acd3051c4879 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -171,6 +171,24 @@ struct optee_ffa {
struct optee;
+/**
+ * struct optee_revision - OP-TEE OS revision reported by secure world
+ * @os_major: OP-TEE OS major version
+ * @os_minor: OP-TEE OS minor version
+ * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
+ *
+ * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
+ * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
+ * FF-A ABI version.
+ */
+struct optee_revision {
+ u32 os_major;
+ u32 os_minor;
+ u64 os_build_id;
+};
+
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
+
/**
* struct optee_ops - OP-TEE driver internal operations
* @do_call_with_arg: enters OP-TEE in secure world
@@ -249,6 +267,7 @@ struct optee {
bool in_kernel_rpmb_routing;
struct work_struct scan_bus_work;
struct work_struct rpmb_scan_bus_work;
+ struct optee_revision revision;
};
struct optee_session {
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
index 0be663fcd52b..0baaf7986a35 100644
--- a/drivers/tee/optee/smc_abi.c
+++ b/drivers/tee/optee/smc_abi.c
@@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_clnt_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
static const struct tee_driver_ops optee_supp_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
}
#endif
-static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
+static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
+ struct optee_revision *revision)
{
union {
struct arm_smccc_res smccc;
@@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
&res.smccc);
+ if (revision) {
+ revision->os_major = res.result.major;
+ revision->os_minor = res.result.minor;
+ revision->os_build_id = res.result.build_id;
+ }
+
if (res.result.build_id)
pr_info("revision %lu.%lu (%0*lx)", res.result.major,
res.result.minor, (int)sizeof(res.result.build_id) * 2,
@@ -1727,6 +1736,7 @@ static int optee_probe(struct platform_device *pdev)
unsigned int thread_count;
struct tee_device *teedev;
struct tee_context *ctx;
+ struct optee_revision revision = { };
u32 max_notif_value;
u32 arg_cache_flags;
u32 sec_caps;
@@ -1745,7 +1755,7 @@ static int optee_probe(struct platform_device *pdev)
return -EINVAL;
}
- optee_msg_get_os_revision(invoke_fn);
+ optee_msg_get_os_revision(invoke_fn, &revision);
if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
pr_warn("api revision mismatch\n");
@@ -1837,6 +1847,7 @@ static int optee_probe(struct platform_device *pdev)
goto err_unreg_teedev;
}
optee->supp_teedev = teedev;
+ optee->revision = revision;
optee_set_dev_group(optee);
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread* [PATCH v4 2/2] tee: optee: store OS revision for TEE core
@ 2025-12-30 5:17 ` Aristo Chen
0 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2025-12-30 5:17 UTC (permalink / raw)
To: linux-kernel
Cc: jens.wiklander, sumit.garg, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
Collect OP-TEE OS revision from secure world for both SMC and FF-A ABIs,
store it in the OP-TEE driver, and expose it through the generic
get_tee_revision() callback.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
---
drivers/tee/optee/core.c | 23 +++++++++++++++++++++++
drivers/tee/optee/ffa_abi.c | 14 ++++++++++++--
drivers/tee/optee/optee_private.h | 19 +++++++++++++++++++
drivers/tee/optee/smc_abi.c | 15 +++++++++++++--
4 files changed, 67 insertions(+), 4 deletions(-)
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 5b62139714ce..2d807bc748bc 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
}
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
+{
+ struct optee *optee = tee_get_drvdata(teedev);
+ u64 build_id;
+
+ if (!optee)
+ return -ENODEV;
+ if (!buf || !len)
+ return -EINVAL;
+
+ build_id = optee->revision.os_build_id;
+ if (build_id)
+ scnprintf(buf, len, "%u.%u (%016llx)",
+ optee->revision.os_major,
+ optee->revision.os_minor,
+ (unsigned long long)build_id);
+ else
+ scnprintf(buf, len, "%u.%u", optee->revision.os_major,
+ optee->revision.os_minor);
+
+ return 0;
+}
+
static void optee_bus_scan(struct work_struct *work)
{
WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
index bf8390789ecf..d67ca1ec9506 100644
--- a/drivers/tee/optee/ffa_abi.c
+++ b/drivers/tee/optee/ffa_abi.c
@@ -776,7 +776,8 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
*/
static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
- const struct ffa_ops *ops)
+ const struct ffa_ops *ops,
+ struct optee_revision *revision)
{
const struct ffa_msg_ops *msg_ops = ops->msg_ops;
struct ffa_send_direct_data data = {
@@ -806,6 +807,11 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
pr_err("Unexpected error %d\n", rc);
return false;
}
+ if (revision) {
+ revision->os_major = data.data0;
+ revision->os_minor = data.data1;
+ revision->os_build_id = data.data2;
+ }
if (data.data2)
pr_info("revision %lu.%lu (%08lx)",
data.data0, data.data1, data.data2);
@@ -900,6 +906,7 @@ static int optee_ffa_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_ffa_clnt_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -918,6 +925,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
static const struct tee_driver_ops optee_ffa_supp_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1034,6 +1042,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
{
const struct ffa_notifier_ops *notif_ops;
const struct ffa_ops *ffa_ops;
+ struct optee_revision revision = { };
unsigned int max_notif_value;
unsigned int rpc_param_count;
struct tee_shm_pool *pool;
@@ -1047,7 +1056,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
ffa_ops = ffa_dev->ops;
notif_ops = ffa_ops->notifier_ops;
- if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops))
+ if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &revision))
return -EINVAL;
if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &sec_caps,
@@ -1059,6 +1068,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
optee = kzalloc(sizeof(*optee), GFP_KERNEL);
if (!optee)
return -ENOMEM;
+ optee->revision = revision;
pool = optee_ffa_shm_pool_alloc_pages();
if (IS_ERR(pool)) {
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index db9ea673fbca..acd3051c4879 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -171,6 +171,24 @@ struct optee_ffa {
struct optee;
+/**
+ * struct optee_revision - OP-TEE OS revision reported by secure world
+ * @os_major: OP-TEE OS major version
+ * @os_minor: OP-TEE OS minor version
+ * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
+ *
+ * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
+ * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
+ * FF-A ABI version.
+ */
+struct optee_revision {
+ u32 os_major;
+ u32 os_minor;
+ u64 os_build_id;
+};
+
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
+
/**
* struct optee_ops - OP-TEE driver internal operations
* @do_call_with_arg: enters OP-TEE in secure world
@@ -249,6 +267,7 @@ struct optee {
bool in_kernel_rpmb_routing;
struct work_struct scan_bus_work;
struct work_struct rpmb_scan_bus_work;
+ struct optee_revision revision;
};
struct optee_session {
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
index 0be663fcd52b..0baaf7986a35 100644
--- a/drivers/tee/optee/smc_abi.c
+++ b/drivers/tee/optee/smc_abi.c
@@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_clnt_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
static const struct tee_driver_ops optee_supp_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
}
#endif
-static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
+static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
+ struct optee_revision *revision)
{
union {
struct arm_smccc_res smccc;
@@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
&res.smccc);
+ if (revision) {
+ revision->os_major = res.result.major;
+ revision->os_minor = res.result.minor;
+ revision->os_build_id = res.result.build_id;
+ }
+
if (res.result.build_id)
pr_info("revision %lu.%lu (%0*lx)", res.result.major,
res.result.minor, (int)sizeof(res.result.build_id) * 2,
@@ -1727,6 +1736,7 @@ static int optee_probe(struct platform_device *pdev)
unsigned int thread_count;
struct tee_device *teedev;
struct tee_context *ctx;
+ struct optee_revision revision = { };
u32 max_notif_value;
u32 arg_cache_flags;
u32 sec_caps;
@@ -1745,7 +1755,7 @@ static int optee_probe(struct platform_device *pdev)
return -EINVAL;
}
- optee_msg_get_os_revision(invoke_fn);
+ optee_msg_get_os_revision(invoke_fn, &revision);
if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
pr_warn("api revision mismatch\n");
@@ -1837,6 +1847,7 @@ static int optee_probe(struct platform_device *pdev)
goto err_unreg_teedev;
}
optee->supp_teedev = teedev;
+ optee->revision = revision;
optee_set_dev_group(optee);
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread* Re: [PATCH v4 2/2] tee: optee: store OS revision for TEE core
2025-12-30 5:17 ` Aristo Chen
@ 2026-01-05 5:20 ` Sumit Garg
-1 siblings, 0 replies; 74+ messages in thread
From: Sumit Garg via OP-TEE @ 2026-01-05 5:20 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
On Tue, Dec 30, 2025 at 01:17:59PM +0800, Aristo Chen wrote:
> Collect OP-TEE OS revision from secure world for both SMC and FF-A ABIs,
> store it in the OP-TEE driver, and expose it through the generic
> get_tee_revision() callback.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> drivers/tee/optee/core.c | 23 +++++++++++++++++++++++
> drivers/tee/optee/ffa_abi.c | 14 ++++++++++++--
> drivers/tee/optee/optee_private.h | 19 +++++++++++++++++++
> drivers/tee/optee/smc_abi.c | 15 +++++++++++++--
> 4 files changed, 67 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> index 5b62139714ce..2d807bc748bc 100644
> --- a/drivers/tee/optee/core.c
> +++ b/drivers/tee/optee/core.c
> @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> }
>
> +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> +{
> + struct optee *optee = tee_get_drvdata(teedev);
> + u64 build_id;
> +
> + if (!optee)
> + return -ENODEV;
> + if (!buf || !len)
> + return -EINVAL;
> +
> + build_id = optee->revision.os_build_id;
> + if (build_id)
> + scnprintf(buf, len, "%u.%u (%016llx)",
> + optee->revision.os_major,
> + optee->revision.os_minor,
> + (unsigned long long)build_id);
> + else
> + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> + optee->revision.os_minor);
> +
> + return 0;
> +}
> +
> static void optee_bus_scan(struct work_struct *work)
> {
> WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> index bf8390789ecf..d67ca1ec9506 100644
> --- a/drivers/tee/optee/ffa_abi.c
> +++ b/drivers/tee/optee/ffa_abi.c
> @@ -776,7 +776,8 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> */
>
> static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> - const struct ffa_ops *ops)
> + const struct ffa_ops *ops,
> + struct optee_revision *revision)
> {
> const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> struct ffa_send_direct_data data = {
> @@ -806,6 +807,11 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> pr_err("Unexpected error %d\n", rc);
> return false;
> }
> + if (revision) {
> + revision->os_major = data.data0;
> + revision->os_minor = data.data1;
> + revision->os_build_id = data.data2;
> + }
> if (data.data2)
> pr_info("revision %lu.%lu (%08lx)",
> data.data0, data.data1, data.data2);
> @@ -900,6 +906,7 @@ static int optee_ffa_open(struct tee_context *ctx)
>
> static const struct tee_driver_ops optee_ffa_clnt_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -918,6 +925,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
>
> static const struct tee_driver_ops optee_ffa_supp_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1034,6 +1042,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> {
> const struct ffa_notifier_ops *notif_ops;
> const struct ffa_ops *ffa_ops;
> + struct optee_revision revision = { };
No need for this redundant variable on stack when...
> unsigned int max_notif_value;
> unsigned int rpc_param_count;
> struct tee_shm_pool *pool;
> @@ -1047,7 +1056,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> ffa_ops = ffa_dev->ops;
> notif_ops = ffa_ops->notifier_ops;
>
> - if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops))
> + if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &revision))
...you can just invoke this API as rather:
if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &optee->revision))
> return -EINVAL;
>
> if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &sec_caps,
> @@ -1059,6 +1068,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> optee = kzalloc(sizeof(*optee), GFP_KERNEL);
> if (!optee)
> return -ENOMEM;
> + optee->revision = revision;
>
> pool = optee_ffa_shm_pool_alloc_pages();
> if (IS_ERR(pool)) {
> diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> index db9ea673fbca..acd3051c4879 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -171,6 +171,24 @@ struct optee_ffa {
>
> struct optee;
>
> +/**
> + * struct optee_revision - OP-TEE OS revision reported by secure world
> + * @os_major: OP-TEE OS major version
> + * @os_minor: OP-TEE OS minor version
> + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> + *
> + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> + * FF-A ABI version.
> + */
> +struct optee_revision {
> + u32 os_major;
> + u32 os_minor;
> + u64 os_build_id;
> +};
> +
> +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
> +
> /**
> * struct optee_ops - OP-TEE driver internal operations
> * @do_call_with_arg: enters OP-TEE in secure world
> @@ -249,6 +267,7 @@ struct optee {
> bool in_kernel_rpmb_routing;
> struct work_struct scan_bus_work;
> struct work_struct rpmb_scan_bus_work;
> + struct optee_revision revision;
> };
>
> struct optee_session {
> diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> index 0be663fcd52b..0baaf7986a35 100644
> --- a/drivers/tee/optee/smc_abi.c
> +++ b/drivers/tee/optee/smc_abi.c
> @@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
>
> static const struct tee_driver_ops optee_clnt_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_smc_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
>
> static const struct tee_driver_ops optee_supp_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_smc_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> }
> #endif
>
> -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> + struct optee_revision *revision)
> {
> union {
> struct arm_smccc_res smccc;
> @@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> &res.smccc);
>
> + if (revision) {
> + revision->os_major = res.result.major;
> + revision->os_minor = res.result.minor;
> + revision->os_build_id = res.result.build_id;
> + }
> +
> if (res.result.build_id)
> pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> res.result.minor, (int)sizeof(res.result.build_id) * 2,
> @@ -1727,6 +1736,7 @@ static int optee_probe(struct platform_device *pdev)
> unsigned int thread_count;
> struct tee_device *teedev;
> struct tee_context *ctx;
> + struct optee_revision revision = { };
Ditto here.
-Sumit
> u32 max_notif_value;
> u32 arg_cache_flags;
> u32 sec_caps;
> @@ -1745,7 +1755,7 @@ static int optee_probe(struct platform_device *pdev)
> return -EINVAL;
> }
>
> - optee_msg_get_os_revision(invoke_fn);
> + optee_msg_get_os_revision(invoke_fn, &revision);
>
> if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> pr_warn("api revision mismatch\n");
> @@ -1837,6 +1847,7 @@ static int optee_probe(struct platform_device *pdev)
> goto err_unreg_teedev;
> }
> optee->supp_teedev = teedev;
> + optee->revision = revision;
>
> optee_set_dev_group(optee);
>
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v4 2/2] tee: optee: store OS revision for TEE core
@ 2026-01-05 5:20 ` Sumit Garg
0 siblings, 0 replies; 74+ messages in thread
From: Sumit Garg @ 2026-01-05 5:20 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, jens.wiklander, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
On Tue, Dec 30, 2025 at 01:17:59PM +0800, Aristo Chen wrote:
> Collect OP-TEE OS revision from secure world for both SMC and FF-A ABIs,
> store it in the OP-TEE driver, and expose it through the generic
> get_tee_revision() callback.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> drivers/tee/optee/core.c | 23 +++++++++++++++++++++++
> drivers/tee/optee/ffa_abi.c | 14 ++++++++++++--
> drivers/tee/optee/optee_private.h | 19 +++++++++++++++++++
> drivers/tee/optee/smc_abi.c | 15 +++++++++++++--
> 4 files changed, 67 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> index 5b62139714ce..2d807bc748bc 100644
> --- a/drivers/tee/optee/core.c
> +++ b/drivers/tee/optee/core.c
> @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> }
>
> +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> +{
> + struct optee *optee = tee_get_drvdata(teedev);
> + u64 build_id;
> +
> + if (!optee)
> + return -ENODEV;
> + if (!buf || !len)
> + return -EINVAL;
> +
> + build_id = optee->revision.os_build_id;
> + if (build_id)
> + scnprintf(buf, len, "%u.%u (%016llx)",
> + optee->revision.os_major,
> + optee->revision.os_minor,
> + (unsigned long long)build_id);
> + else
> + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> + optee->revision.os_minor);
> +
> + return 0;
> +}
> +
> static void optee_bus_scan(struct work_struct *work)
> {
> WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> index bf8390789ecf..d67ca1ec9506 100644
> --- a/drivers/tee/optee/ffa_abi.c
> +++ b/drivers/tee/optee/ffa_abi.c
> @@ -776,7 +776,8 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> */
>
> static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> - const struct ffa_ops *ops)
> + const struct ffa_ops *ops,
> + struct optee_revision *revision)
> {
> const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> struct ffa_send_direct_data data = {
> @@ -806,6 +807,11 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> pr_err("Unexpected error %d\n", rc);
> return false;
> }
> + if (revision) {
> + revision->os_major = data.data0;
> + revision->os_minor = data.data1;
> + revision->os_build_id = data.data2;
> + }
> if (data.data2)
> pr_info("revision %lu.%lu (%08lx)",
> data.data0, data.data1, data.data2);
> @@ -900,6 +906,7 @@ static int optee_ffa_open(struct tee_context *ctx)
>
> static const struct tee_driver_ops optee_ffa_clnt_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -918,6 +925,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
>
> static const struct tee_driver_ops optee_ffa_supp_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1034,6 +1042,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> {
> const struct ffa_notifier_ops *notif_ops;
> const struct ffa_ops *ffa_ops;
> + struct optee_revision revision = { };
No need for this redundant variable on stack when...
> unsigned int max_notif_value;
> unsigned int rpc_param_count;
> struct tee_shm_pool *pool;
> @@ -1047,7 +1056,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> ffa_ops = ffa_dev->ops;
> notif_ops = ffa_ops->notifier_ops;
>
> - if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops))
> + if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &revision))
...you can just invoke this API as rather:
if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &optee->revision))
> return -EINVAL;
>
> if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &sec_caps,
> @@ -1059,6 +1068,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> optee = kzalloc(sizeof(*optee), GFP_KERNEL);
> if (!optee)
> return -ENOMEM;
> + optee->revision = revision;
>
> pool = optee_ffa_shm_pool_alloc_pages();
> if (IS_ERR(pool)) {
> diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> index db9ea673fbca..acd3051c4879 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -171,6 +171,24 @@ struct optee_ffa {
>
> struct optee;
>
> +/**
> + * struct optee_revision - OP-TEE OS revision reported by secure world
> + * @os_major: OP-TEE OS major version
> + * @os_minor: OP-TEE OS minor version
> + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> + *
> + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> + * FF-A ABI version.
> + */
> +struct optee_revision {
> + u32 os_major;
> + u32 os_minor;
> + u64 os_build_id;
> +};
> +
> +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
> +
> /**
> * struct optee_ops - OP-TEE driver internal operations
> * @do_call_with_arg: enters OP-TEE in secure world
> @@ -249,6 +267,7 @@ struct optee {
> bool in_kernel_rpmb_routing;
> struct work_struct scan_bus_work;
> struct work_struct rpmb_scan_bus_work;
> + struct optee_revision revision;
> };
>
> struct optee_session {
> diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> index 0be663fcd52b..0baaf7986a35 100644
> --- a/drivers/tee/optee/smc_abi.c
> +++ b/drivers/tee/optee/smc_abi.c
> @@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
>
> static const struct tee_driver_ops optee_clnt_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_smc_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
>
> static const struct tee_driver_ops optee_supp_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_smc_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> }
> #endif
>
> -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> + struct optee_revision *revision)
> {
> union {
> struct arm_smccc_res smccc;
> @@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> &res.smccc);
>
> + if (revision) {
> + revision->os_major = res.result.major;
> + revision->os_minor = res.result.minor;
> + revision->os_build_id = res.result.build_id;
> + }
> +
> if (res.result.build_id)
> pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> res.result.minor, (int)sizeof(res.result.build_id) * 2,
> @@ -1727,6 +1736,7 @@ static int optee_probe(struct platform_device *pdev)
> unsigned int thread_count;
> struct tee_device *teedev;
> struct tee_context *ctx;
> + struct optee_revision revision = { };
Ditto here.
-Sumit
> u32 max_notif_value;
> u32 arg_cache_flags;
> u32 sec_caps;
> @@ -1745,7 +1755,7 @@ static int optee_probe(struct platform_device *pdev)
> return -EINVAL;
> }
>
> - optee_msg_get_os_revision(invoke_fn);
> + optee_msg_get_os_revision(invoke_fn, &revision);
>
> if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> pr_warn("api revision mismatch\n");
> @@ -1837,6 +1847,7 @@ static int optee_probe(struct platform_device *pdev)
> goto err_unreg_teedev;
> }
> optee->supp_teedev = teedev;
> + optee->revision = revision;
>
> optee_set_dev_group(optee);
>
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v4 2/2] tee: optee: store OS revision for TEE core
2026-01-05 5:20 ` Sumit Garg
@ 2026-01-05 8:13 ` Aristo Chen
-1 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-05 8:13 UTC (permalink / raw)
To: Sumit Garg
Cc: linux-kernel, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
Hi Sumit,
Sumit Garg <sumit.garg@kernel.org> 於 2026年1月5日週一 下午1:20寫道:
>
> On Tue, Dec 30, 2025 at 01:17:59PM +0800, Aristo Chen wrote:
> > Collect OP-TEE OS revision from secure world for both SMC and FF-A ABIs,
> > store it in the OP-TEE driver, and expose it through the generic
> > get_tee_revision() callback.
> >
> > Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> > ---
> > drivers/tee/optee/core.c | 23 +++++++++++++++++++++++
> > drivers/tee/optee/ffa_abi.c | 14 ++++++++++++--
> > drivers/tee/optee/optee_private.h | 19 +++++++++++++++++++
> > drivers/tee/optee/smc_abi.c | 15 +++++++++++++--
> > 4 files changed, 67 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> > index 5b62139714ce..2d807bc748bc 100644
> > --- a/drivers/tee/optee/core.c
> > +++ b/drivers/tee/optee/core.c
> > @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> > return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> > }
> >
> > +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> > +{
> > + struct optee *optee = tee_get_drvdata(teedev);
> > + u64 build_id;
> > +
> > + if (!optee)
> > + return -ENODEV;
> > + if (!buf || !len)
> > + return -EINVAL;
> > +
> > + build_id = optee->revision.os_build_id;
> > + if (build_id)
> > + scnprintf(buf, len, "%u.%u (%016llx)",
> > + optee->revision.os_major,
> > + optee->revision.os_minor,
> > + (unsigned long long)build_id);
> > + else
> > + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> > + optee->revision.os_minor);
> > +
> > + return 0;
> > +}
> > +
> > static void optee_bus_scan(struct work_struct *work)
> > {
> > WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> > diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> > index bf8390789ecf..d67ca1ec9506 100644
> > --- a/drivers/tee/optee/ffa_abi.c
> > +++ b/drivers/tee/optee/ffa_abi.c
> > @@ -776,7 +776,8 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> > */
> >
> > static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > - const struct ffa_ops *ops)
> > + const struct ffa_ops *ops,
> > + struct optee_revision *revision)
> > {
> > const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> > struct ffa_send_direct_data data = {
> > @@ -806,6 +807,11 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > pr_err("Unexpected error %d\n", rc);
> > return false;
> > }
> > + if (revision) {
> > + revision->os_major = data.data0;
> > + revision->os_minor = data.data1;
> > + revision->os_build_id = data.data2;
> > + }
> > if (data.data2)
> > pr_info("revision %lu.%lu (%08lx)",
> > data.data0, data.data1, data.data2);
> > @@ -900,6 +906,7 @@ static int optee_ffa_open(struct tee_context *ctx)
> >
> > static const struct tee_driver_ops optee_ffa_clnt_ops = {
> > .get_version = optee_ffa_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_ffa_open,
> > .release = optee_release,
> > .open_session = optee_open_session,
> > @@ -918,6 +925,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
> >
> > static const struct tee_driver_ops optee_ffa_supp_ops = {
> > .get_version = optee_ffa_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_ffa_open,
> > .release = optee_release_supp,
> > .supp_recv = optee_supp_recv,
> > @@ -1034,6 +1042,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> > {
> > const struct ffa_notifier_ops *notif_ops;
> > const struct ffa_ops *ffa_ops;
> > + struct optee_revision revision = { };
>
> No need for this redundant variable on stack when...
>
> > unsigned int max_notif_value;
> > unsigned int rpc_param_count;
> > struct tee_shm_pool *pool;
> > @@ -1047,7 +1056,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> > ffa_ops = ffa_dev->ops;
> > notif_ops = ffa_ops->notifier_ops;
> >
> > - if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops))
> > + if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &revision))
>
> ...you can just invoke this API as rather:
>
> if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &optee->revision))
>
Thanks for the review. To avoid the temporary struct optee_revision,
I need to move the optee allocation earlier so we can pass
&optee->revision into optee_ffa_api_is_compatible().
AFAICT, there aren’t kernel selftests covering this, the only test that
I am aware of is the xtest, so I’m slightly cautious about touching
probe ordering. If you prefer to avoid the early allocation, I can keep
the original order and use a local struct optee_revision instead.
Please let me know which you’d prefer
> > return -EINVAL;
> >
> > if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &sec_caps,
> > @@ -1059,6 +1068,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> > optee = kzalloc(sizeof(*optee), GFP_KERNEL);
> > if (!optee)
> > return -ENOMEM;
> > + optee->revision = revision;
> >
> > pool = optee_ffa_shm_pool_alloc_pages();
> > if (IS_ERR(pool)) {
> > diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> > index db9ea673fbca..acd3051c4879 100644
> > --- a/drivers/tee/optee/optee_private.h
> > +++ b/drivers/tee/optee/optee_private.h
> > @@ -171,6 +171,24 @@ struct optee_ffa {
> >
> > struct optee;
> >
> > +/**
> > + * struct optee_revision - OP-TEE OS revision reported by secure world
> > + * @os_major: OP-TEE OS major version
> > + * @os_minor: OP-TEE OS minor version
> > + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> > + *
> > + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> > + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> > + * FF-A ABI version.
> > + */
> > +struct optee_revision {
> > + u32 os_major;
> > + u32 os_minor;
> > + u64 os_build_id;
> > +};
> > +
> > +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
> > +
> > /**
> > * struct optee_ops - OP-TEE driver internal operations
> > * @do_call_with_arg: enters OP-TEE in secure world
> > @@ -249,6 +267,7 @@ struct optee {
> > bool in_kernel_rpmb_routing;
> > struct work_struct scan_bus_work;
> > struct work_struct rpmb_scan_bus_work;
> > + struct optee_revision revision;
> > };
> >
> > struct optee_session {
> > diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> > index 0be663fcd52b..0baaf7986a35 100644
> > --- a/drivers/tee/optee/smc_abi.c
> > +++ b/drivers/tee/optee/smc_abi.c
> > @@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
> >
> > static const struct tee_driver_ops optee_clnt_ops = {
> > .get_version = optee_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_smc_open,
> > .release = optee_release,
> > .open_session = optee_open_session,
> > @@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
> >
> > static const struct tee_driver_ops optee_supp_ops = {
> > .get_version = optee_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_smc_open,
> > .release = optee_release_supp,
> > .supp_recv = optee_supp_recv,
> > @@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> > }
> > #endif
> >
> > -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> > +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> > + struct optee_revision *revision)
> > {
> > union {
> > struct arm_smccc_res smccc;
> > @@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> > invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> > &res.smccc);
> >
> > + if (revision) {
> > + revision->os_major = res.result.major;
> > + revision->os_minor = res.result.minor;
> > + revision->os_build_id = res.result.build_id;
> > + }
> > +
> > if (res.result.build_id)
> > pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> > res.result.minor, (int)sizeof(res.result.build_id) * 2,
> > @@ -1727,6 +1736,7 @@ static int optee_probe(struct platform_device *pdev)
> > unsigned int thread_count;
> > struct tee_device *teedev;
> > struct tee_context *ctx;
> > + struct optee_revision revision = { };
>
> Ditto here.
>
> -Sumit
>
> > u32 max_notif_value;
> > u32 arg_cache_flags;
> > u32 sec_caps;
> > @@ -1745,7 +1755,7 @@ static int optee_probe(struct platform_device *pdev)
> > return -EINVAL;
> > }
> >
> > - optee_msg_get_os_revision(invoke_fn);
> > + optee_msg_get_os_revision(invoke_fn, &revision);
> >
> > if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> > pr_warn("api revision mismatch\n");
> > @@ -1837,6 +1847,7 @@ static int optee_probe(struct platform_device *pdev)
> > goto err_unreg_teedev;
> > }
> > optee->supp_teedev = teedev;
> > + optee->revision = revision;
> >
> > optee_set_dev_group(optee);
> >
> > --
> > 2.43.0
> >
Best regards,
Aristo
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v4 2/2] tee: optee: store OS revision for TEE core
@ 2026-01-05 8:13 ` Aristo Chen
0 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-05 8:13 UTC (permalink / raw)
To: Sumit Garg
Cc: linux-kernel, jens.wiklander, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
Hi Sumit,
Sumit Garg <sumit.garg@kernel.org> 於 2026年1月5日週一 下午1:20寫道:
>
> On Tue, Dec 30, 2025 at 01:17:59PM +0800, Aristo Chen wrote:
> > Collect OP-TEE OS revision from secure world for both SMC and FF-A ABIs,
> > store it in the OP-TEE driver, and expose it through the generic
> > get_tee_revision() callback.
> >
> > Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> > ---
> > drivers/tee/optee/core.c | 23 +++++++++++++++++++++++
> > drivers/tee/optee/ffa_abi.c | 14 ++++++++++++--
> > drivers/tee/optee/optee_private.h | 19 +++++++++++++++++++
> > drivers/tee/optee/smc_abi.c | 15 +++++++++++++--
> > 4 files changed, 67 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> > index 5b62139714ce..2d807bc748bc 100644
> > --- a/drivers/tee/optee/core.c
> > +++ b/drivers/tee/optee/core.c
> > @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> > return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> > }
> >
> > +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> > +{
> > + struct optee *optee = tee_get_drvdata(teedev);
> > + u64 build_id;
> > +
> > + if (!optee)
> > + return -ENODEV;
> > + if (!buf || !len)
> > + return -EINVAL;
> > +
> > + build_id = optee->revision.os_build_id;
> > + if (build_id)
> > + scnprintf(buf, len, "%u.%u (%016llx)",
> > + optee->revision.os_major,
> > + optee->revision.os_minor,
> > + (unsigned long long)build_id);
> > + else
> > + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> > + optee->revision.os_minor);
> > +
> > + return 0;
> > +}
> > +
> > static void optee_bus_scan(struct work_struct *work)
> > {
> > WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> > diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> > index bf8390789ecf..d67ca1ec9506 100644
> > --- a/drivers/tee/optee/ffa_abi.c
> > +++ b/drivers/tee/optee/ffa_abi.c
> > @@ -776,7 +776,8 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> > */
> >
> > static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > - const struct ffa_ops *ops)
> > + const struct ffa_ops *ops,
> > + struct optee_revision *revision)
> > {
> > const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> > struct ffa_send_direct_data data = {
> > @@ -806,6 +807,11 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > pr_err("Unexpected error %d\n", rc);
> > return false;
> > }
> > + if (revision) {
> > + revision->os_major = data.data0;
> > + revision->os_minor = data.data1;
> > + revision->os_build_id = data.data2;
> > + }
> > if (data.data2)
> > pr_info("revision %lu.%lu (%08lx)",
> > data.data0, data.data1, data.data2);
> > @@ -900,6 +906,7 @@ static int optee_ffa_open(struct tee_context *ctx)
> >
> > static const struct tee_driver_ops optee_ffa_clnt_ops = {
> > .get_version = optee_ffa_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_ffa_open,
> > .release = optee_release,
> > .open_session = optee_open_session,
> > @@ -918,6 +925,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
> >
> > static const struct tee_driver_ops optee_ffa_supp_ops = {
> > .get_version = optee_ffa_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_ffa_open,
> > .release = optee_release_supp,
> > .supp_recv = optee_supp_recv,
> > @@ -1034,6 +1042,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> > {
> > const struct ffa_notifier_ops *notif_ops;
> > const struct ffa_ops *ffa_ops;
> > + struct optee_revision revision = { };
>
> No need for this redundant variable on stack when...
>
> > unsigned int max_notif_value;
> > unsigned int rpc_param_count;
> > struct tee_shm_pool *pool;
> > @@ -1047,7 +1056,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> > ffa_ops = ffa_dev->ops;
> > notif_ops = ffa_ops->notifier_ops;
> >
> > - if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops))
> > + if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &revision))
>
> ...you can just invoke this API as rather:
>
> if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &optee->revision))
>
Thanks for the review. To avoid the temporary struct optee_revision,
I need to move the optee allocation earlier so we can pass
&optee->revision into optee_ffa_api_is_compatible().
AFAICT, there aren’t kernel selftests covering this, the only test that
I am aware of is the xtest, so I’m slightly cautious about touching
probe ordering. If you prefer to avoid the early allocation, I can keep
the original order and use a local struct optee_revision instead.
Please let me know which you’d prefer
> > return -EINVAL;
> >
> > if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &sec_caps,
> > @@ -1059,6 +1068,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> > optee = kzalloc(sizeof(*optee), GFP_KERNEL);
> > if (!optee)
> > return -ENOMEM;
> > + optee->revision = revision;
> >
> > pool = optee_ffa_shm_pool_alloc_pages();
> > if (IS_ERR(pool)) {
> > diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> > index db9ea673fbca..acd3051c4879 100644
> > --- a/drivers/tee/optee/optee_private.h
> > +++ b/drivers/tee/optee/optee_private.h
> > @@ -171,6 +171,24 @@ struct optee_ffa {
> >
> > struct optee;
> >
> > +/**
> > + * struct optee_revision - OP-TEE OS revision reported by secure world
> > + * @os_major: OP-TEE OS major version
> > + * @os_minor: OP-TEE OS minor version
> > + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> > + *
> > + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> > + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> > + * FF-A ABI version.
> > + */
> > +struct optee_revision {
> > + u32 os_major;
> > + u32 os_minor;
> > + u64 os_build_id;
> > +};
> > +
> > +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
> > +
> > /**
> > * struct optee_ops - OP-TEE driver internal operations
> > * @do_call_with_arg: enters OP-TEE in secure world
> > @@ -249,6 +267,7 @@ struct optee {
> > bool in_kernel_rpmb_routing;
> > struct work_struct scan_bus_work;
> > struct work_struct rpmb_scan_bus_work;
> > + struct optee_revision revision;
> > };
> >
> > struct optee_session {
> > diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> > index 0be663fcd52b..0baaf7986a35 100644
> > --- a/drivers/tee/optee/smc_abi.c
> > +++ b/drivers/tee/optee/smc_abi.c
> > @@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
> >
> > static const struct tee_driver_ops optee_clnt_ops = {
> > .get_version = optee_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_smc_open,
> > .release = optee_release,
> > .open_session = optee_open_session,
> > @@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
> >
> > static const struct tee_driver_ops optee_supp_ops = {
> > .get_version = optee_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_smc_open,
> > .release = optee_release_supp,
> > .supp_recv = optee_supp_recv,
> > @@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> > }
> > #endif
> >
> > -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> > +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> > + struct optee_revision *revision)
> > {
> > union {
> > struct arm_smccc_res smccc;
> > @@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> > invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> > &res.smccc);
> >
> > + if (revision) {
> > + revision->os_major = res.result.major;
> > + revision->os_minor = res.result.minor;
> > + revision->os_build_id = res.result.build_id;
> > + }
> > +
> > if (res.result.build_id)
> > pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> > res.result.minor, (int)sizeof(res.result.build_id) * 2,
> > @@ -1727,6 +1736,7 @@ static int optee_probe(struct platform_device *pdev)
> > unsigned int thread_count;
> > struct tee_device *teedev;
> > struct tee_context *ctx;
> > + struct optee_revision revision = { };
>
> Ditto here.
>
> -Sumit
>
> > u32 max_notif_value;
> > u32 arg_cache_flags;
> > u32 sec_caps;
> > @@ -1745,7 +1755,7 @@ static int optee_probe(struct platform_device *pdev)
> > return -EINVAL;
> > }
> >
> > - optee_msg_get_os_revision(invoke_fn);
> > + optee_msg_get_os_revision(invoke_fn, &revision);
> >
> > if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> > pr_warn("api revision mismatch\n");
> > @@ -1837,6 +1847,7 @@ static int optee_probe(struct platform_device *pdev)
> > goto err_unreg_teedev;
> > }
> > optee->supp_teedev = teedev;
> > + optee->revision = revision;
> >
> > optee_set_dev_group(optee);
> >
> > --
> > 2.43.0
> >
Best regards,
Aristo
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v4 2/2] tee: optee: store OS revision for TEE core
2026-01-05 8:13 ` Aristo Chen
@ 2026-01-05 8:48 ` Sumit Garg
-1 siblings, 0 replies; 74+ messages in thread
From: Sumit Garg via OP-TEE @ 2026-01-05 8:48 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
On Mon, Jan 05, 2026 at 04:13:52PM +0800, Aristo Chen wrote:
> Hi Sumit,
>
> Sumit Garg <sumit.garg@kernel.org> 於 2026年1月5日週一 下午1:20寫道:
> >
> > On Tue, Dec 30, 2025 at 01:17:59PM +0800, Aristo Chen wrote:
> > > Collect OP-TEE OS revision from secure world for both SMC and FF-A ABIs,
> > > store it in the OP-TEE driver, and expose it through the generic
> > > get_tee_revision() callback.
> > >
> > > Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> > > ---
> > > drivers/tee/optee/core.c | 23 +++++++++++++++++++++++
> > > drivers/tee/optee/ffa_abi.c | 14 ++++++++++++--
> > > drivers/tee/optee/optee_private.h | 19 +++++++++++++++++++
> > > drivers/tee/optee/smc_abi.c | 15 +++++++++++++--
> > > 4 files changed, 67 insertions(+), 4 deletions(-)
> > >
> > > diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> > > index 5b62139714ce..2d807bc748bc 100644
> > > --- a/drivers/tee/optee/core.c
> > > +++ b/drivers/tee/optee/core.c
> > > @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> > > return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> > > }
> > >
> > > +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> > > +{
> > > + struct optee *optee = tee_get_drvdata(teedev);
> > > + u64 build_id;
> > > +
> > > + if (!optee)
> > > + return -ENODEV;
> > > + if (!buf || !len)
> > > + return -EINVAL;
> > > +
> > > + build_id = optee->revision.os_build_id;
> > > + if (build_id)
> > > + scnprintf(buf, len, "%u.%u (%016llx)",
> > > + optee->revision.os_major,
> > > + optee->revision.os_minor,
> > > + (unsigned long long)build_id);
> > > + else
> > > + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> > > + optee->revision.os_minor);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > static void optee_bus_scan(struct work_struct *work)
> > > {
> > > WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> > > diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> > > index bf8390789ecf..d67ca1ec9506 100644
> > > --- a/drivers/tee/optee/ffa_abi.c
> > > +++ b/drivers/tee/optee/ffa_abi.c
> > > @@ -776,7 +776,8 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> > > */
> > >
> > > static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > > - const struct ffa_ops *ops)
> > > + const struct ffa_ops *ops,
> > > + struct optee_revision *revision)
> > > {
> > > const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> > > struct ffa_send_direct_data data = {
> > > @@ -806,6 +807,11 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > > pr_err("Unexpected error %d\n", rc);
> > > return false;
> > > }
> > > + if (revision) {
> > > + revision->os_major = data.data0;
> > > + revision->os_minor = data.data1;
> > > + revision->os_build_id = data.data2;
> > > + }
> > > if (data.data2)
> > > pr_info("revision %lu.%lu (%08lx)",
> > > data.data0, data.data1, data.data2);
> > > @@ -900,6 +906,7 @@ static int optee_ffa_open(struct tee_context *ctx)
> > >
> > > static const struct tee_driver_ops optee_ffa_clnt_ops = {
> > > .get_version = optee_ffa_get_version,
> > > + .get_tee_revision = optee_get_revision,
> > > .open = optee_ffa_open,
> > > .release = optee_release,
> > > .open_session = optee_open_session,
> > > @@ -918,6 +925,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
> > >
> > > static const struct tee_driver_ops optee_ffa_supp_ops = {
> > > .get_version = optee_ffa_get_version,
> > > + .get_tee_revision = optee_get_revision,
> > > .open = optee_ffa_open,
> > > .release = optee_release_supp,
> > > .supp_recv = optee_supp_recv,
> > > @@ -1034,6 +1042,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> > > {
> > > const struct ffa_notifier_ops *notif_ops;
> > > const struct ffa_ops *ffa_ops;
> > > + struct optee_revision revision = { };
> >
> > No need for this redundant variable on stack when...
> >
> > > unsigned int max_notif_value;
> > > unsigned int rpc_param_count;
> > > struct tee_shm_pool *pool;
> > > @@ -1047,7 +1056,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> > > ffa_ops = ffa_dev->ops;
> > > notif_ops = ffa_ops->notifier_ops;
> > >
> > > - if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops))
> > > + if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &revision))
> >
> > ...you can just invoke this API as rather:
> >
> > if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &optee->revision))
> >
> Thanks for the review. To avoid the temporary struct optee_revision,
> I need to move the optee allocation earlier so we can pass
> &optee->revision into optee_ffa_api_is_compatible().
I would rather suggest to split this API to add a new one like
optee_ffa_get_os_revision() and then move it's invocation once optee
struct has been allocated.
>
> AFAICT, there aren’t kernel selftests covering this, the only test that
> I am aware of is the xtest, so I’m slightly cautious about touching
> probe ordering. If you prefer to avoid the early allocation, I can keep
> the original order and use a local struct optee_revision instead.
With above suggestion, the probe ordering won't change much but rather
the point changes when you ask OP-TEE for it's revision.
-Sumit
>
> Please let me know which you’d prefer
>
> > > return -EINVAL;
> > >
> > > if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &sec_caps,
> > > @@ -1059,6 +1068,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> > > optee = kzalloc(sizeof(*optee), GFP_KERNEL);
> > > if (!optee)
> > > return -ENOMEM;
> > > + optee->revision = revision;
> > >
> > > pool = optee_ffa_shm_pool_alloc_pages();
> > > if (IS_ERR(pool)) {
> > > diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> > > index db9ea673fbca..acd3051c4879 100644
> > > --- a/drivers/tee/optee/optee_private.h
> > > +++ b/drivers/tee/optee/optee_private.h
> > > @@ -171,6 +171,24 @@ struct optee_ffa {
> > >
> > > struct optee;
> > >
> > > +/**
> > > + * struct optee_revision - OP-TEE OS revision reported by secure world
> > > + * @os_major: OP-TEE OS major version
> > > + * @os_minor: OP-TEE OS minor version
> > > + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> > > + *
> > > + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> > > + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> > > + * FF-A ABI version.
> > > + */
> > > +struct optee_revision {
> > > + u32 os_major;
> > > + u32 os_minor;
> > > + u64 os_build_id;
> > > +};
> > > +
> > > +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
> > > +
> > > /**
> > > * struct optee_ops - OP-TEE driver internal operations
> > > * @do_call_with_arg: enters OP-TEE in secure world
> > > @@ -249,6 +267,7 @@ struct optee {
> > > bool in_kernel_rpmb_routing;
> > > struct work_struct scan_bus_work;
> > > struct work_struct rpmb_scan_bus_work;
> > > + struct optee_revision revision;
> > > };
> > >
> > > struct optee_session {
> > > diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> > > index 0be663fcd52b..0baaf7986a35 100644
> > > --- a/drivers/tee/optee/smc_abi.c
> > > +++ b/drivers/tee/optee/smc_abi.c
> > > @@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
> > >
> > > static const struct tee_driver_ops optee_clnt_ops = {
> > > .get_version = optee_get_version,
> > > + .get_tee_revision = optee_get_revision,
> > > .open = optee_smc_open,
> > > .release = optee_release,
> > > .open_session = optee_open_session,
> > > @@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
> > >
> > > static const struct tee_driver_ops optee_supp_ops = {
> > > .get_version = optee_get_version,
> > > + .get_tee_revision = optee_get_revision,
> > > .open = optee_smc_open,
> > > .release = optee_release_supp,
> > > .supp_recv = optee_supp_recv,
> > > @@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> > > }
> > > #endif
> > >
> > > -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> > > +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> > > + struct optee_revision *revision)
> > > {
> > > union {
> > > struct arm_smccc_res smccc;
> > > @@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> > > invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> > > &res.smccc);
> > >
> > > + if (revision) {
> > > + revision->os_major = res.result.major;
> > > + revision->os_minor = res.result.minor;
> > > + revision->os_build_id = res.result.build_id;
> > > + }
> > > +
> > > if (res.result.build_id)
> > > pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> > > res.result.minor, (int)sizeof(res.result.build_id) * 2,
> > > @@ -1727,6 +1736,7 @@ static int optee_probe(struct platform_device *pdev)
> > > unsigned int thread_count;
> > > struct tee_device *teedev;
> > > struct tee_context *ctx;
> > > + struct optee_revision revision = { };
> >
> > Ditto here.
> >
> > -Sumit
> >
> > > u32 max_notif_value;
> > > u32 arg_cache_flags;
> > > u32 sec_caps;
> > > @@ -1745,7 +1755,7 @@ static int optee_probe(struct platform_device *pdev)
> > > return -EINVAL;
> > > }
> > >
> > > - optee_msg_get_os_revision(invoke_fn);
> > > + optee_msg_get_os_revision(invoke_fn, &revision);
> > >
> > > if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> > > pr_warn("api revision mismatch\n");
> > > @@ -1837,6 +1847,7 @@ static int optee_probe(struct platform_device *pdev)
> > > goto err_unreg_teedev;
> > > }
> > > optee->supp_teedev = teedev;
> > > + optee->revision = revision;
> > >
> > > optee_set_dev_group(optee);
> > >
> > > --
> > > 2.43.0
> > >
>
> Best regards,
> Aristo
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v4 2/2] tee: optee: store OS revision for TEE core
@ 2026-01-05 8:48 ` Sumit Garg
0 siblings, 0 replies; 74+ messages in thread
From: Sumit Garg @ 2026-01-05 8:48 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, jens.wiklander, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
On Mon, Jan 05, 2026 at 04:13:52PM +0800, Aristo Chen wrote:
> Hi Sumit,
>
> Sumit Garg <sumit.garg@kernel.org> 於 2026年1月5日週一 下午1:20寫道:
> >
> > On Tue, Dec 30, 2025 at 01:17:59PM +0800, Aristo Chen wrote:
> > > Collect OP-TEE OS revision from secure world for both SMC and FF-A ABIs,
> > > store it in the OP-TEE driver, and expose it through the generic
> > > get_tee_revision() callback.
> > >
> > > Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> > > ---
> > > drivers/tee/optee/core.c | 23 +++++++++++++++++++++++
> > > drivers/tee/optee/ffa_abi.c | 14 ++++++++++++--
> > > drivers/tee/optee/optee_private.h | 19 +++++++++++++++++++
> > > drivers/tee/optee/smc_abi.c | 15 +++++++++++++--
> > > 4 files changed, 67 insertions(+), 4 deletions(-)
> > >
> > > diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> > > index 5b62139714ce..2d807bc748bc 100644
> > > --- a/drivers/tee/optee/core.c
> > > +++ b/drivers/tee/optee/core.c
> > > @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> > > return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> > > }
> > >
> > > +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> > > +{
> > > + struct optee *optee = tee_get_drvdata(teedev);
> > > + u64 build_id;
> > > +
> > > + if (!optee)
> > > + return -ENODEV;
> > > + if (!buf || !len)
> > > + return -EINVAL;
> > > +
> > > + build_id = optee->revision.os_build_id;
> > > + if (build_id)
> > > + scnprintf(buf, len, "%u.%u (%016llx)",
> > > + optee->revision.os_major,
> > > + optee->revision.os_minor,
> > > + (unsigned long long)build_id);
> > > + else
> > > + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> > > + optee->revision.os_minor);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > static void optee_bus_scan(struct work_struct *work)
> > > {
> > > WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> > > diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> > > index bf8390789ecf..d67ca1ec9506 100644
> > > --- a/drivers/tee/optee/ffa_abi.c
> > > +++ b/drivers/tee/optee/ffa_abi.c
> > > @@ -776,7 +776,8 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> > > */
> > >
> > > static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > > - const struct ffa_ops *ops)
> > > + const struct ffa_ops *ops,
> > > + struct optee_revision *revision)
> > > {
> > > const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> > > struct ffa_send_direct_data data = {
> > > @@ -806,6 +807,11 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > > pr_err("Unexpected error %d\n", rc);
> > > return false;
> > > }
> > > + if (revision) {
> > > + revision->os_major = data.data0;
> > > + revision->os_minor = data.data1;
> > > + revision->os_build_id = data.data2;
> > > + }
> > > if (data.data2)
> > > pr_info("revision %lu.%lu (%08lx)",
> > > data.data0, data.data1, data.data2);
> > > @@ -900,6 +906,7 @@ static int optee_ffa_open(struct tee_context *ctx)
> > >
> > > static const struct tee_driver_ops optee_ffa_clnt_ops = {
> > > .get_version = optee_ffa_get_version,
> > > + .get_tee_revision = optee_get_revision,
> > > .open = optee_ffa_open,
> > > .release = optee_release,
> > > .open_session = optee_open_session,
> > > @@ -918,6 +925,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
> > >
> > > static const struct tee_driver_ops optee_ffa_supp_ops = {
> > > .get_version = optee_ffa_get_version,
> > > + .get_tee_revision = optee_get_revision,
> > > .open = optee_ffa_open,
> > > .release = optee_release_supp,
> > > .supp_recv = optee_supp_recv,
> > > @@ -1034,6 +1042,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> > > {
> > > const struct ffa_notifier_ops *notif_ops;
> > > const struct ffa_ops *ffa_ops;
> > > + struct optee_revision revision = { };
> >
> > No need for this redundant variable on stack when...
> >
> > > unsigned int max_notif_value;
> > > unsigned int rpc_param_count;
> > > struct tee_shm_pool *pool;
> > > @@ -1047,7 +1056,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> > > ffa_ops = ffa_dev->ops;
> > > notif_ops = ffa_ops->notifier_ops;
> > >
> > > - if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops))
> > > + if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &revision))
> >
> > ...you can just invoke this API as rather:
> >
> > if (!optee_ffa_api_is_compatible(ffa_dev, ffa_ops, &optee->revision))
> >
> Thanks for the review. To avoid the temporary struct optee_revision,
> I need to move the optee allocation earlier so we can pass
> &optee->revision into optee_ffa_api_is_compatible().
I would rather suggest to split this API to add a new one like
optee_ffa_get_os_revision() and then move it's invocation once optee
struct has been allocated.
>
> AFAICT, there aren’t kernel selftests covering this, the only test that
> I am aware of is the xtest, so I’m slightly cautious about touching
> probe ordering. If you prefer to avoid the early allocation, I can keep
> the original order and use a local struct optee_revision instead.
With above suggestion, the probe ordering won't change much but rather
the point changes when you ask OP-TEE for it's revision.
-Sumit
>
> Please let me know which you’d prefer
>
> > > return -EINVAL;
> > >
> > > if (!optee_ffa_exchange_caps(ffa_dev, ffa_ops, &sec_caps,
> > > @@ -1059,6 +1068,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> > > optee = kzalloc(sizeof(*optee), GFP_KERNEL);
> > > if (!optee)
> > > return -ENOMEM;
> > > + optee->revision = revision;
> > >
> > > pool = optee_ffa_shm_pool_alloc_pages();
> > > if (IS_ERR(pool)) {
> > > diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> > > index db9ea673fbca..acd3051c4879 100644
> > > --- a/drivers/tee/optee/optee_private.h
> > > +++ b/drivers/tee/optee/optee_private.h
> > > @@ -171,6 +171,24 @@ struct optee_ffa {
> > >
> > > struct optee;
> > >
> > > +/**
> > > + * struct optee_revision - OP-TEE OS revision reported by secure world
> > > + * @os_major: OP-TEE OS major version
> > > + * @os_minor: OP-TEE OS minor version
> > > + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> > > + *
> > > + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> > > + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> > > + * FF-A ABI version.
> > > + */
> > > +struct optee_revision {
> > > + u32 os_major;
> > > + u32 os_minor;
> > > + u64 os_build_id;
> > > +};
> > > +
> > > +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
> > > +
> > > /**
> > > * struct optee_ops - OP-TEE driver internal operations
> > > * @do_call_with_arg: enters OP-TEE in secure world
> > > @@ -249,6 +267,7 @@ struct optee {
> > > bool in_kernel_rpmb_routing;
> > > struct work_struct scan_bus_work;
> > > struct work_struct rpmb_scan_bus_work;
> > > + struct optee_revision revision;
> > > };
> > >
> > > struct optee_session {
> > > diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> > > index 0be663fcd52b..0baaf7986a35 100644
> > > --- a/drivers/tee/optee/smc_abi.c
> > > +++ b/drivers/tee/optee/smc_abi.c
> > > @@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
> > >
> > > static const struct tee_driver_ops optee_clnt_ops = {
> > > .get_version = optee_get_version,
> > > + .get_tee_revision = optee_get_revision,
> > > .open = optee_smc_open,
> > > .release = optee_release,
> > > .open_session = optee_open_session,
> > > @@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
> > >
> > > static const struct tee_driver_ops optee_supp_ops = {
> > > .get_version = optee_get_version,
> > > + .get_tee_revision = optee_get_revision,
> > > .open = optee_smc_open,
> > > .release = optee_release_supp,
> > > .supp_recv = optee_supp_recv,
> > > @@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> > > }
> > > #endif
> > >
> > > -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> > > +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> > > + struct optee_revision *revision)
> > > {
> > > union {
> > > struct arm_smccc_res smccc;
> > > @@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> > > invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> > > &res.smccc);
> > >
> > > + if (revision) {
> > > + revision->os_major = res.result.major;
> > > + revision->os_minor = res.result.minor;
> > > + revision->os_build_id = res.result.build_id;
> > > + }
> > > +
> > > if (res.result.build_id)
> > > pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> > > res.result.minor, (int)sizeof(res.result.build_id) * 2,
> > > @@ -1727,6 +1736,7 @@ static int optee_probe(struct platform_device *pdev)
> > > unsigned int thread_count;
> > > struct tee_device *teedev;
> > > struct tee_context *ctx;
> > > + struct optee_revision revision = { };
> >
> > Ditto here.
> >
> > -Sumit
> >
> > > u32 max_notif_value;
> > > u32 arg_cache_flags;
> > > u32 sec_caps;
> > > @@ -1745,7 +1755,7 @@ static int optee_probe(struct platform_device *pdev)
> > > return -EINVAL;
> > > }
> > >
> > > - optee_msg_get_os_revision(invoke_fn);
> > > + optee_msg_get_os_revision(invoke_fn, &revision);
> > >
> > > if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> > > pr_warn("api revision mismatch\n");
> > > @@ -1837,6 +1847,7 @@ static int optee_probe(struct platform_device *pdev)
> > > goto err_unreg_teedev;
> > > }
> > > optee->supp_teedev = teedev;
> > > + optee->revision = revision;
> > >
> > > optee_set_dev_group(optee);
> > >
> > > --
> > > 2.43.0
> > >
>
> Best regards,
> Aristo
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v4 1/2] tee: add revision sysfs attribute
2025-12-30 5:17 ` Aristo Chen
@ 2026-01-05 4:53 ` Sumit Garg
-1 siblings, 0 replies; 74+ messages in thread
From: Sumit Garg via OP-TEE @ 2026-01-05 4:53 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
On Tue, Dec 30, 2025 at 01:17:58PM +0800, Aristo Chen wrote:
> Add a generic TEE revision sysfs attribute backed by a new optional
> get_tee_revision() callback. The revision string is diagnostic-only and
> must not be used to infer feature support.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> Documentation/ABI/testing/sysfs-class-tee | 10 +++++
> drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
> include/linux/tee_core.h | 8 ++++
> 3 files changed, 68 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
> index c9144d16003e..6e783210104e 100644
> --- a/Documentation/ABI/testing/sysfs-class-tee
> +++ b/Documentation/ABI/testing/sysfs-class-tee
> @@ -13,3 +13,13 @@ Description:
> space if the variable is absent. The primary purpose
> of this variable is to let systemd know whether
> tee-supplicant is needed in the early boot with initramfs.
> +
> +What: /sys/class/tee/tee{,priv}X/revision
> +Date: Dec 2025
> +KernelVersion: 6.18
> +Contact: op-tee@lists.trustedfirmware.org
> +Description:
> + Read-only revision string reported by the TEE driver. This is
> + for diagnostics only and must not be used to infer feature
> + support. Use TEE_IOC_VERSION for capability and compatibility
> + checks.
> diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> index d65d47cc154e..0a00499811c1 100644
> --- a/drivers/tee/tee_core.c
> +++ b/drivers/tee/tee_core.c
> @@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
> NULL
> };
>
> -ATTRIBUTE_GROUPS(tee_dev);
> +static const struct attribute_group tee_dev_group = {
> + .attrs = tee_dev_attrs,
> +};
> +
> +static ssize_t revision_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> + char version[TEE_REVISION_STR_SIZE];
> + int ret;
> +
> + if (!teedev->desc->ops->get_tee_revision)
> + return -ENODEV;
> +
> + ret = teedev->desc->ops->get_tee_revision(teedev, version,
> + sizeof(version));
> + if (ret)
> + return ret;
> +
> + return sysfs_emit(buf, "%s\n", version);
> +}
> +static DEVICE_ATTR_RO(revision);
> +
> +static struct attribute *tee_revision_attrs[] = {
> + &dev_attr_revision.attr,
> + NULL
> +};
> +
> +static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
> + struct attribute *attr, int n)
> +{
> + struct device *dev = kobj_to_dev(kobj);
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> +
> + if (teedev->desc->ops->get_tee_revision)
> + return attr->mode;
> +
> + return 0;
> +}
> +
> +static const struct attribute_group tee_revision_group = {
> + .attrs = tee_revision_attrs,
> + .is_visible = tee_revision_attr_is_visible,
> +};
> +
> +static const struct attribute_group *tee_dev_groups[] = {
> + &tee_dev_group,
> + &tee_revision_group,
> + NULL
> +};
>
> static const struct class tee_class = {
> .name = "tee",
> diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
> index 1f3e5dad6d0d..c214c38898ca 100644
> --- a/include/linux/tee_core.h
> +++ b/include/linux/tee_core.h
> @@ -76,6 +76,9 @@ struct tee_device {
> /**
> * struct tee_driver_ops - driver operations vtable
> * @get_version: returns version of driver
> + * @get_tee_revision: returns revision string (diagnostic only);
> + * do not infer feature support from this, use
> + * TEE_IOC_VERSION instead
> * @open: called for a context when the device file is opened
> * @close_context: called when the device file is closed
> * @release: called to release the context
> @@ -98,6 +101,8 @@ struct tee_device {
> struct tee_driver_ops {
> void (*get_version)(struct tee_device *teedev,
> struct tee_ioctl_version_data *vers);
> + int (*get_tee_revision)(struct tee_device *teedev,
> + char *buf, size_t len);
> int (*open)(struct tee_context *ctx);
> void (*close_context)(struct tee_context *ctx);
> void (*release)(struct tee_context *ctx);
> @@ -292,6 +297,9 @@ struct tee_shm_pool *tee_shm_pool_alloc_res_mem(unsigned long vaddr,
> phys_addr_t paddr, size_t size,
> int min_alloc_order);
>
> +/* Size for TEE revision string buffer used by get_tee_revision(). */
> +#define TEE_REVISION_STR_SIZE 128
> +
Move this macro alongside get_tee_revision() callback. With that feel
free to add:
Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
-Sumit
> /**
> * tee_shm_pool_free() - Free a shared memory pool
> * @pool: The shared memory pool to free
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v4 1/2] tee: add revision sysfs attribute
@ 2026-01-05 4:53 ` Sumit Garg
0 siblings, 0 replies; 74+ messages in thread
From: Sumit Garg @ 2026-01-05 4:53 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, jens.wiklander, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
On Tue, Dec 30, 2025 at 01:17:58PM +0800, Aristo Chen wrote:
> Add a generic TEE revision sysfs attribute backed by a new optional
> get_tee_revision() callback. The revision string is diagnostic-only and
> must not be used to infer feature support.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> Documentation/ABI/testing/sysfs-class-tee | 10 +++++
> drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
> include/linux/tee_core.h | 8 ++++
> 3 files changed, 68 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
> index c9144d16003e..6e783210104e 100644
> --- a/Documentation/ABI/testing/sysfs-class-tee
> +++ b/Documentation/ABI/testing/sysfs-class-tee
> @@ -13,3 +13,13 @@ Description:
> space if the variable is absent. The primary purpose
> of this variable is to let systemd know whether
> tee-supplicant is needed in the early boot with initramfs.
> +
> +What: /sys/class/tee/tee{,priv}X/revision
> +Date: Dec 2025
> +KernelVersion: 6.18
> +Contact: op-tee@lists.trustedfirmware.org
> +Description:
> + Read-only revision string reported by the TEE driver. This is
> + for diagnostics only and must not be used to infer feature
> + support. Use TEE_IOC_VERSION for capability and compatibility
> + checks.
> diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> index d65d47cc154e..0a00499811c1 100644
> --- a/drivers/tee/tee_core.c
> +++ b/drivers/tee/tee_core.c
> @@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
> NULL
> };
>
> -ATTRIBUTE_GROUPS(tee_dev);
> +static const struct attribute_group tee_dev_group = {
> + .attrs = tee_dev_attrs,
> +};
> +
> +static ssize_t revision_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> + char version[TEE_REVISION_STR_SIZE];
> + int ret;
> +
> + if (!teedev->desc->ops->get_tee_revision)
> + return -ENODEV;
> +
> + ret = teedev->desc->ops->get_tee_revision(teedev, version,
> + sizeof(version));
> + if (ret)
> + return ret;
> +
> + return sysfs_emit(buf, "%s\n", version);
> +}
> +static DEVICE_ATTR_RO(revision);
> +
> +static struct attribute *tee_revision_attrs[] = {
> + &dev_attr_revision.attr,
> + NULL
> +};
> +
> +static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
> + struct attribute *attr, int n)
> +{
> + struct device *dev = kobj_to_dev(kobj);
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> +
> + if (teedev->desc->ops->get_tee_revision)
> + return attr->mode;
> +
> + return 0;
> +}
> +
> +static const struct attribute_group tee_revision_group = {
> + .attrs = tee_revision_attrs,
> + .is_visible = tee_revision_attr_is_visible,
> +};
> +
> +static const struct attribute_group *tee_dev_groups[] = {
> + &tee_dev_group,
> + &tee_revision_group,
> + NULL
> +};
>
> static const struct class tee_class = {
> .name = "tee",
> diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
> index 1f3e5dad6d0d..c214c38898ca 100644
> --- a/include/linux/tee_core.h
> +++ b/include/linux/tee_core.h
> @@ -76,6 +76,9 @@ struct tee_device {
> /**
> * struct tee_driver_ops - driver operations vtable
> * @get_version: returns version of driver
> + * @get_tee_revision: returns revision string (diagnostic only);
> + * do not infer feature support from this, use
> + * TEE_IOC_VERSION instead
> * @open: called for a context when the device file is opened
> * @close_context: called when the device file is closed
> * @release: called to release the context
> @@ -98,6 +101,8 @@ struct tee_device {
> struct tee_driver_ops {
> void (*get_version)(struct tee_device *teedev,
> struct tee_ioctl_version_data *vers);
> + int (*get_tee_revision)(struct tee_device *teedev,
> + char *buf, size_t len);
> int (*open)(struct tee_context *ctx);
> void (*close_context)(struct tee_context *ctx);
> void (*release)(struct tee_context *ctx);
> @@ -292,6 +297,9 @@ struct tee_shm_pool *tee_shm_pool_alloc_res_mem(unsigned long vaddr,
> phys_addr_t paddr, size_t size,
> int min_alloc_order);
>
> +/* Size for TEE revision string buffer used by get_tee_revision(). */
> +#define TEE_REVISION_STR_SIZE 128
> +
Move this macro alongside get_tee_revision() callback. With that feel
free to add:
Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
-Sumit
> /**
> * tee_shm_pool_free() - Free a shared memory pool
> * @pool: The shared memory pool to free
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH v5 1/2] tee: add revision sysfs attribute
2025-12-30 5:17 ` Aristo Chen
@ 2026-01-07 15:26 ` Aristo Chen
-1 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-07 15:26 UTC (permalink / raw)
To: linux-kernel
Cc: sumit.garg, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
Add a generic TEE revision sysfs attribute backed by a new
optional get_tee_revision() callback. The revision string is
diagnostic-only and must not be used to infer feature support.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
---
Documentation/ABI/testing/sysfs-class-tee | 10 +++++
drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
include/linux/tee_core.h | 9 ++++
3 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
index c9144d16003e..6e783210104e 100644
--- a/Documentation/ABI/testing/sysfs-class-tee
+++ b/Documentation/ABI/testing/sysfs-class-tee
@@ -13,3 +13,13 @@ Description:
space if the variable is absent. The primary purpose
of this variable is to let systemd know whether
tee-supplicant is needed in the early boot with initramfs.
+
+What: /sys/class/tee/tee{,priv}X/revision
+Date: Dec 2025
+KernelVersion: 6.18
+Contact: op-tee@lists.trustedfirmware.org
+Description:
+ Read-only revision string reported by the TEE driver. This is
+ for diagnostics only and must not be used to infer feature
+ support. Use TEE_IOC_VERSION for capability and compatibility
+ checks.
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index d65d47cc154e..0a00499811c1 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
NULL
};
-ATTRIBUTE_GROUPS(tee_dev);
+static const struct attribute_group tee_dev_group = {
+ .attrs = tee_dev_attrs,
+};
+
+static ssize_t revision_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+ char version[TEE_REVISION_STR_SIZE];
+ int ret;
+
+ if (!teedev->desc->ops->get_tee_revision)
+ return -ENODEV;
+
+ ret = teedev->desc->ops->get_tee_revision(teedev, version,
+ sizeof(version));
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%s\n", version);
+}
+static DEVICE_ATTR_RO(revision);
+
+static struct attribute *tee_revision_attrs[] = {
+ &dev_attr_revision.attr,
+ NULL
+};
+
+static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+
+ if (teedev->desc->ops->get_tee_revision)
+ return attr->mode;
+
+ return 0;
+}
+
+static const struct attribute_group tee_revision_group = {
+ .attrs = tee_revision_attrs,
+ .is_visible = tee_revision_attr_is_visible,
+};
+
+static const struct attribute_group *tee_dev_groups[] = {
+ &tee_dev_group,
+ &tee_revision_group,
+ NULL
+};
static const struct class tee_class = {
.name = "tee",
diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
index 1f3e5dad6d0d..ee5f0bd41f43 100644
--- a/include/linux/tee_core.h
+++ b/include/linux/tee_core.h
@@ -76,6 +76,9 @@ struct tee_device {
/**
* struct tee_driver_ops - driver operations vtable
* @get_version: returns version of driver
+ * @get_tee_revision: returns revision string (diagnostic only);
+ * do not infer feature support from this, use
+ * TEE_IOC_VERSION instead
* @open: called for a context when the device file is opened
* @close_context: called when the device file is closed
* @release: called to release the context
@@ -95,9 +98,12 @@ struct tee_device {
* client closes the device file, even if there are existing references to the
* context. The TEE driver can use @close_context to start cleaning up.
*/
+
struct tee_driver_ops {
void (*get_version)(struct tee_device *teedev,
struct tee_ioctl_version_data *vers);
+ int (*get_tee_revision)(struct tee_device *teedev,
+ char *buf, size_t len);
int (*open)(struct tee_context *ctx);
void (*close_context)(struct tee_context *ctx);
void (*release)(struct tee_context *ctx);
@@ -123,6 +129,9 @@ struct tee_driver_ops {
int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
};
+/* Size for TEE revision string buffer used by get_tee_revision(). */
+#define TEE_REVISION_STR_SIZE 128
+
/**
* struct tee_desc - Describes the TEE driver to the subsystem
* @name: name of driver
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread* [PATCH v5 1/2] tee: add revision sysfs attribute
@ 2026-01-07 15:26 ` Aristo Chen
0 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-07 15:26 UTC (permalink / raw)
To: linux-kernel
Cc: jens.wiklander, sumit.garg, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
Add a generic TEE revision sysfs attribute backed by a new
optional get_tee_revision() callback. The revision string is
diagnostic-only and must not be used to infer feature support.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
---
Documentation/ABI/testing/sysfs-class-tee | 10 +++++
drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
include/linux/tee_core.h | 9 ++++
3 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
index c9144d16003e..6e783210104e 100644
--- a/Documentation/ABI/testing/sysfs-class-tee
+++ b/Documentation/ABI/testing/sysfs-class-tee
@@ -13,3 +13,13 @@ Description:
space if the variable is absent. The primary purpose
of this variable is to let systemd know whether
tee-supplicant is needed in the early boot with initramfs.
+
+What: /sys/class/tee/tee{,priv}X/revision
+Date: Dec 2025
+KernelVersion: 6.18
+Contact: op-tee@lists.trustedfirmware.org
+Description:
+ Read-only revision string reported by the TEE driver. This is
+ for diagnostics only and must not be used to infer feature
+ support. Use TEE_IOC_VERSION for capability and compatibility
+ checks.
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index d65d47cc154e..0a00499811c1 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
NULL
};
-ATTRIBUTE_GROUPS(tee_dev);
+static const struct attribute_group tee_dev_group = {
+ .attrs = tee_dev_attrs,
+};
+
+static ssize_t revision_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+ char version[TEE_REVISION_STR_SIZE];
+ int ret;
+
+ if (!teedev->desc->ops->get_tee_revision)
+ return -ENODEV;
+
+ ret = teedev->desc->ops->get_tee_revision(teedev, version,
+ sizeof(version));
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%s\n", version);
+}
+static DEVICE_ATTR_RO(revision);
+
+static struct attribute *tee_revision_attrs[] = {
+ &dev_attr_revision.attr,
+ NULL
+};
+
+static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+
+ if (teedev->desc->ops->get_tee_revision)
+ return attr->mode;
+
+ return 0;
+}
+
+static const struct attribute_group tee_revision_group = {
+ .attrs = tee_revision_attrs,
+ .is_visible = tee_revision_attr_is_visible,
+};
+
+static const struct attribute_group *tee_dev_groups[] = {
+ &tee_dev_group,
+ &tee_revision_group,
+ NULL
+};
static const struct class tee_class = {
.name = "tee",
diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
index 1f3e5dad6d0d..ee5f0bd41f43 100644
--- a/include/linux/tee_core.h
+++ b/include/linux/tee_core.h
@@ -76,6 +76,9 @@ struct tee_device {
/**
* struct tee_driver_ops - driver operations vtable
* @get_version: returns version of driver
+ * @get_tee_revision: returns revision string (diagnostic only);
+ * do not infer feature support from this, use
+ * TEE_IOC_VERSION instead
* @open: called for a context when the device file is opened
* @close_context: called when the device file is closed
* @release: called to release the context
@@ -95,9 +98,12 @@ struct tee_device {
* client closes the device file, even if there are existing references to the
* context. The TEE driver can use @close_context to start cleaning up.
*/
+
struct tee_driver_ops {
void (*get_version)(struct tee_device *teedev,
struct tee_ioctl_version_data *vers);
+ int (*get_tee_revision)(struct tee_device *teedev,
+ char *buf, size_t len);
int (*open)(struct tee_context *ctx);
void (*close_context)(struct tee_context *ctx);
void (*release)(struct tee_context *ctx);
@@ -123,6 +129,9 @@ struct tee_driver_ops {
int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
};
+/* Size for TEE revision string buffer used by get_tee_revision(). */
+#define TEE_REVISION_STR_SIZE 128
+
/**
* struct tee_desc - Describes the TEE driver to the subsystem
* @name: name of driver
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread* [PATCH v5 2/2] tee: optee: store OS revision for TEE core
2026-01-07 15:26 ` Aristo Chen
@ 2026-01-07 15:26 ` Aristo Chen
-1 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-07 15:26 UTC (permalink / raw)
To: linux-kernel
Cc: sumit.garg, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
Collect OP-TEE OS revision from secure world for both SMC and FF-A
ABIs, store it in the OP-TEE driver, and expose it through the
generic get_tee_revision() callback.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
---
drivers/tee/optee/core.c | 23 +++++++++++++
drivers/tee/optee/ffa_abi.c | 57 ++++++++++++++++++++++++-------
drivers/tee/optee/optee_private.h | 19 +++++++++++
drivers/tee/optee/smc_abi.c | 15 ++++++--
4 files changed, 99 insertions(+), 15 deletions(-)
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 5b62139714ce..2d807bc748bc 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
}
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
+{
+ struct optee *optee = tee_get_drvdata(teedev);
+ u64 build_id;
+
+ if (!optee)
+ return -ENODEV;
+ if (!buf || !len)
+ return -EINVAL;
+
+ build_id = optee->revision.os_build_id;
+ if (build_id)
+ scnprintf(buf, len, "%u.%u (%016llx)",
+ optee->revision.os_major,
+ optee->revision.os_minor,
+ (unsigned long long)build_id);
+ else
+ scnprintf(buf, len, "%u.%u", optee->revision.os_major,
+ optee->revision.os_minor);
+
+ return 0;
+}
+
static void optee_bus_scan(struct work_struct *work)
{
WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
index bf8390789ecf..82dbed1c87e5 100644
--- a/drivers/tee/optee/ffa_abi.c
+++ b/drivers/tee/optee/ffa_abi.c
@@ -775,6 +775,42 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
* with a matching configuration.
*/
+static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
+ const struct ffa_ops *ops,
+ struct optee_revision *revision,
+ bool log)
+{
+ const struct ffa_msg_ops *msg_ops = ops->msg_ops;
+ struct ffa_send_direct_data data = {
+ .data0 = OPTEE_FFA_GET_OS_VERSION,
+ };
+ int rc;
+
+ msg_ops->mode_32bit_set(ffa_dev);
+
+ rc = msg_ops->sync_send_receive(ffa_dev, &data);
+ if (rc) {
+ pr_err("Unexpected error %d\n", rc);
+ return false;
+ }
+
+ if (revision) {
+ revision->os_major = data.data0;
+ revision->os_minor = data.data1;
+ revision->os_build_id = data.data2;
+ }
+
+ if (log) {
+ if (data.data2)
+ pr_info("revision %lu.%lu (%08lx)",
+ data.data0, data.data1, data.data2);
+ else
+ pr_info("revision %lu.%lu", data.data0, data.data1);
+ }
+
+ return true;
+}
+
static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
const struct ffa_ops *ops)
{
@@ -798,19 +834,8 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
return false;
}
- data = (struct ffa_send_direct_data){
- .data0 = OPTEE_FFA_GET_OS_VERSION,
- };
- rc = msg_ops->sync_send_receive(ffa_dev, &data);
- if (rc) {
- pr_err("Unexpected error %d\n", rc);
+ if (!optee_ffa_get_os_revision(ffa_dev, ops, NULL, true))
return false;
- }
- if (data.data2)
- pr_info("revision %lu.%lu (%08lx)",
- data.data0, data.data1, data.data2);
- else
- pr_info("revision %lu.%lu", data.data0, data.data1);
return true;
}
@@ -900,6 +925,7 @@ static int optee_ffa_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_ffa_clnt_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -918,6 +944,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
static const struct tee_driver_ops optee_ffa_supp_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1060,6 +1087,12 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
if (!optee)
return -ENOMEM;
+ if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision,
+ false)) {
+ rc = -EINVAL;
+ goto err_free_optee;
+ }
+
pool = optee_ffa_shm_pool_alloc_pages();
if (IS_ERR(pool)) {
rc = PTR_ERR(pool);
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index db9ea673fbca..acd3051c4879 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -171,6 +171,24 @@ struct optee_ffa {
struct optee;
+/**
+ * struct optee_revision - OP-TEE OS revision reported by secure world
+ * @os_major: OP-TEE OS major version
+ * @os_minor: OP-TEE OS minor version
+ * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
+ *
+ * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
+ * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
+ * FF-A ABI version.
+ */
+struct optee_revision {
+ u32 os_major;
+ u32 os_minor;
+ u64 os_build_id;
+};
+
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
+
/**
* struct optee_ops - OP-TEE driver internal operations
* @do_call_with_arg: enters OP-TEE in secure world
@@ -249,6 +267,7 @@ struct optee {
bool in_kernel_rpmb_routing;
struct work_struct scan_bus_work;
struct work_struct rpmb_scan_bus_work;
+ struct optee_revision revision;
};
struct optee_session {
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
index 0be663fcd52b..51fae1ab8ef8 100644
--- a/drivers/tee/optee/smc_abi.c
+++ b/drivers/tee/optee/smc_abi.c
@@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_clnt_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
static const struct tee_driver_ops optee_supp_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
}
#endif
-static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
+static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
+ struct optee_revision *revision)
{
union {
struct arm_smccc_res smccc;
@@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
&res.smccc);
+ if (revision) {
+ revision->os_major = res.result.major;
+ revision->os_minor = res.result.minor;
+ revision->os_build_id = res.result.build_id;
+ }
+
if (res.result.build_id)
pr_info("revision %lu.%lu (%0*lx)", res.result.major,
res.result.minor, (int)sizeof(res.result.build_id) * 2,
@@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev)
return -EINVAL;
}
- optee_msg_get_os_revision(invoke_fn);
-
if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
pr_warn("api revision mismatch\n");
return -EINVAL;
@@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev)
goto err_free_shm_pool;
}
+ optee_msg_get_os_revision(invoke_fn, &optee->revision);
+
optee->ops = &optee_ops;
optee->smc.invoke_fn = invoke_fn;
optee->smc.sec_caps = sec_caps;
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread* [PATCH v5 2/2] tee: optee: store OS revision for TEE core
@ 2026-01-07 15:26 ` Aristo Chen
0 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-07 15:26 UTC (permalink / raw)
To: linux-kernel
Cc: jens.wiklander, sumit.garg, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
Collect OP-TEE OS revision from secure world for both SMC and FF-A
ABIs, store it in the OP-TEE driver, and expose it through the
generic get_tee_revision() callback.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
---
drivers/tee/optee/core.c | 23 +++++++++++++
drivers/tee/optee/ffa_abi.c | 57 ++++++++++++++++++++++++-------
drivers/tee/optee/optee_private.h | 19 +++++++++++
drivers/tee/optee/smc_abi.c | 15 ++++++--
4 files changed, 99 insertions(+), 15 deletions(-)
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 5b62139714ce..2d807bc748bc 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
}
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
+{
+ struct optee *optee = tee_get_drvdata(teedev);
+ u64 build_id;
+
+ if (!optee)
+ return -ENODEV;
+ if (!buf || !len)
+ return -EINVAL;
+
+ build_id = optee->revision.os_build_id;
+ if (build_id)
+ scnprintf(buf, len, "%u.%u (%016llx)",
+ optee->revision.os_major,
+ optee->revision.os_minor,
+ (unsigned long long)build_id);
+ else
+ scnprintf(buf, len, "%u.%u", optee->revision.os_major,
+ optee->revision.os_minor);
+
+ return 0;
+}
+
static void optee_bus_scan(struct work_struct *work)
{
WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
index bf8390789ecf..82dbed1c87e5 100644
--- a/drivers/tee/optee/ffa_abi.c
+++ b/drivers/tee/optee/ffa_abi.c
@@ -775,6 +775,42 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
* with a matching configuration.
*/
+static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
+ const struct ffa_ops *ops,
+ struct optee_revision *revision,
+ bool log)
+{
+ const struct ffa_msg_ops *msg_ops = ops->msg_ops;
+ struct ffa_send_direct_data data = {
+ .data0 = OPTEE_FFA_GET_OS_VERSION,
+ };
+ int rc;
+
+ msg_ops->mode_32bit_set(ffa_dev);
+
+ rc = msg_ops->sync_send_receive(ffa_dev, &data);
+ if (rc) {
+ pr_err("Unexpected error %d\n", rc);
+ return false;
+ }
+
+ if (revision) {
+ revision->os_major = data.data0;
+ revision->os_minor = data.data1;
+ revision->os_build_id = data.data2;
+ }
+
+ if (log) {
+ if (data.data2)
+ pr_info("revision %lu.%lu (%08lx)",
+ data.data0, data.data1, data.data2);
+ else
+ pr_info("revision %lu.%lu", data.data0, data.data1);
+ }
+
+ return true;
+}
+
static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
const struct ffa_ops *ops)
{
@@ -798,19 +834,8 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
return false;
}
- data = (struct ffa_send_direct_data){
- .data0 = OPTEE_FFA_GET_OS_VERSION,
- };
- rc = msg_ops->sync_send_receive(ffa_dev, &data);
- if (rc) {
- pr_err("Unexpected error %d\n", rc);
+ if (!optee_ffa_get_os_revision(ffa_dev, ops, NULL, true))
return false;
- }
- if (data.data2)
- pr_info("revision %lu.%lu (%08lx)",
- data.data0, data.data1, data.data2);
- else
- pr_info("revision %lu.%lu", data.data0, data.data1);
return true;
}
@@ -900,6 +925,7 @@ static int optee_ffa_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_ffa_clnt_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -918,6 +944,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
static const struct tee_driver_ops optee_ffa_supp_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1060,6 +1087,12 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
if (!optee)
return -ENOMEM;
+ if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision,
+ false)) {
+ rc = -EINVAL;
+ goto err_free_optee;
+ }
+
pool = optee_ffa_shm_pool_alloc_pages();
if (IS_ERR(pool)) {
rc = PTR_ERR(pool);
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index db9ea673fbca..acd3051c4879 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -171,6 +171,24 @@ struct optee_ffa {
struct optee;
+/**
+ * struct optee_revision - OP-TEE OS revision reported by secure world
+ * @os_major: OP-TEE OS major version
+ * @os_minor: OP-TEE OS minor version
+ * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
+ *
+ * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
+ * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
+ * FF-A ABI version.
+ */
+struct optee_revision {
+ u32 os_major;
+ u32 os_minor;
+ u64 os_build_id;
+};
+
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
+
/**
* struct optee_ops - OP-TEE driver internal operations
* @do_call_with_arg: enters OP-TEE in secure world
@@ -249,6 +267,7 @@ struct optee {
bool in_kernel_rpmb_routing;
struct work_struct scan_bus_work;
struct work_struct rpmb_scan_bus_work;
+ struct optee_revision revision;
};
struct optee_session {
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
index 0be663fcd52b..51fae1ab8ef8 100644
--- a/drivers/tee/optee/smc_abi.c
+++ b/drivers/tee/optee/smc_abi.c
@@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_clnt_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
static const struct tee_driver_ops optee_supp_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
}
#endif
-static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
+static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
+ struct optee_revision *revision)
{
union {
struct arm_smccc_res smccc;
@@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
&res.smccc);
+ if (revision) {
+ revision->os_major = res.result.major;
+ revision->os_minor = res.result.minor;
+ revision->os_build_id = res.result.build_id;
+ }
+
if (res.result.build_id)
pr_info("revision %lu.%lu (%0*lx)", res.result.major,
res.result.minor, (int)sizeof(res.result.build_id) * 2,
@@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev)
return -EINVAL;
}
- optee_msg_get_os_revision(invoke_fn);
-
if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
pr_warn("api revision mismatch\n");
return -EINVAL;
@@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev)
goto err_free_shm_pool;
}
+ optee_msg_get_os_revision(invoke_fn, &optee->revision);
+
optee->ops = &optee_ops;
optee->smc.invoke_fn = invoke_fn;
optee->smc.sec_caps = sec_caps;
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread
* Re: [PATCH v5 1/2] tee: add revision sysfs attribute
2026-01-07 15:26 ` Aristo Chen
@ 2026-01-07 15:28 ` Mario Limonciello
-1 siblings, 0 replies; 74+ messages in thread
From: Mario Limonciello via OP-TEE @ 2026-01-07 15:28 UTC (permalink / raw)
To: Aristo Chen, linux-kernel
Cc: sumit.garg, op-tee, harshal.dev, Rijo-john.Thomas,
amirreza.zarrabi, Aristo Chen
On 1/7/26 9:26 AM, Aristo Chen wrote:
> Add a generic TEE revision sysfs attribute backed by a new
> optional get_tee_revision() callback. The revision string is
> diagnostic-only and must not be used to infer feature support.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> Documentation/ABI/testing/sysfs-class-tee | 10 +++++
> drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
> include/linux/tee_core.h | 9 ++++
> 3 files changed, 69 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
> index c9144d16003e..6e783210104e 100644
> --- a/Documentation/ABI/testing/sysfs-class-tee
> +++ b/Documentation/ABI/testing/sysfs-class-tee
> @@ -13,3 +13,13 @@ Description:
> space if the variable is absent. The primary purpose
> of this variable is to let systemd know whether
> tee-supplicant is needed in the early boot with initramfs.
> +
> +What: /sys/class/tee/tee{,priv}X/revision
> +Date: Dec 2025
> +KernelVersion: 6.18
This needs to be bumped up and dates pushed out.
> +Contact: op-tee@lists.trustedfirmware.org
> +Description:
> + Read-only revision string reported by the TEE driver. This is
> + for diagnostics only and must not be used to infer feature
> + support. Use TEE_IOC_VERSION for capability and compatibility
> + checks.
> diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> index d65d47cc154e..0a00499811c1 100644
> --- a/drivers/tee/tee_core.c
> +++ b/drivers/tee/tee_core.c
> @@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
> NULL
> };
>
> -ATTRIBUTE_GROUPS(tee_dev);
> +static const struct attribute_group tee_dev_group = {
> + .attrs = tee_dev_attrs,
> +};
> +
> +static ssize_t revision_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> + char version[TEE_REVISION_STR_SIZE];
> + int ret;
> +
> + if (!teedev->desc->ops->get_tee_revision)
> + return -ENODEV;
> +
> + ret = teedev->desc->ops->get_tee_revision(teedev, version,
> + sizeof(version));
> + if (ret)
> + return ret;
> +
> + return sysfs_emit(buf, "%s\n", version);
> +}
> +static DEVICE_ATTR_RO(revision);
> +
> +static struct attribute *tee_revision_attrs[] = {
> + &dev_attr_revision.attr,
> + NULL
> +};
> +
> +static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
> + struct attribute *attr, int n)
> +{
> + struct device *dev = kobj_to_dev(kobj);
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> +
> + if (teedev->desc->ops->get_tee_revision)
> + return attr->mode;
> +
> + return 0;
> +}
> +
> +static const struct attribute_group tee_revision_group = {
> + .attrs = tee_revision_attrs,
> + .is_visible = tee_revision_attr_is_visible,
> +};
> +
> +static const struct attribute_group *tee_dev_groups[] = {
> + &tee_dev_group,
> + &tee_revision_group,
> + NULL
> +};
>
> static const struct class tee_class = {
> .name = "tee",
> diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
> index 1f3e5dad6d0d..ee5f0bd41f43 100644
> --- a/include/linux/tee_core.h
> +++ b/include/linux/tee_core.h
> @@ -76,6 +76,9 @@ struct tee_device {
> /**
> * struct tee_driver_ops - driver operations vtable
> * @get_version: returns version of driver
> + * @get_tee_revision: returns revision string (diagnostic only);
Why is this comment here about it being for diagnostics only? I feel
it's up to the implementation how it would be used.
> + * do not infer feature support from this, use
> + * TEE_IOC_VERSION instead
> * @open: called for a context when the device file is opened
> * @close_context: called when the device file is closed
> * @release: called to release the context
> @@ -95,9 +98,12 @@ struct tee_device {
> * client closes the device file, even if there are existing references to the
> * context. The TEE driver can use @close_context to start cleaning up.
> */
> +
> struct tee_driver_ops {
> void (*get_version)(struct tee_device *teedev,
> struct tee_ioctl_version_data *vers);
> + int (*get_tee_revision)(struct tee_device *teedev,
> + char *buf, size_t len);
> int (*open)(struct tee_context *ctx);
> void (*close_context)(struct tee_context *ctx);
> void (*release)(struct tee_context *ctx);
> @@ -123,6 +129,9 @@ struct tee_driver_ops {
> int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
> };
>
> +/* Size for TEE revision string buffer used by get_tee_revision(). */
> +#define TEE_REVISION_STR_SIZE 128
> +
> /**
> * struct tee_desc - Describes the TEE driver to the subsystem
> * @name: name of driver
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v5 1/2] tee: add revision sysfs attribute
@ 2026-01-07 15:28 ` Mario Limonciello
0 siblings, 0 replies; 74+ messages in thread
From: Mario Limonciello @ 2026-01-07 15:28 UTC (permalink / raw)
To: Aristo Chen, linux-kernel
Cc: jens.wiklander, sumit.garg, op-tee, harshal.dev, Rijo-john.Thomas,
amirreza.zarrabi, Aristo Chen
On 1/7/26 9:26 AM, Aristo Chen wrote:
> Add a generic TEE revision sysfs attribute backed by a new
> optional get_tee_revision() callback. The revision string is
> diagnostic-only and must not be used to infer feature support.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> Documentation/ABI/testing/sysfs-class-tee | 10 +++++
> drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
> include/linux/tee_core.h | 9 ++++
> 3 files changed, 69 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
> index c9144d16003e..6e783210104e 100644
> --- a/Documentation/ABI/testing/sysfs-class-tee
> +++ b/Documentation/ABI/testing/sysfs-class-tee
> @@ -13,3 +13,13 @@ Description:
> space if the variable is absent. The primary purpose
> of this variable is to let systemd know whether
> tee-supplicant is needed in the early boot with initramfs.
> +
> +What: /sys/class/tee/tee{,priv}X/revision
> +Date: Dec 2025
> +KernelVersion: 6.18
This needs to be bumped up and dates pushed out.
> +Contact: op-tee@lists.trustedfirmware.org
> +Description:
> + Read-only revision string reported by the TEE driver. This is
> + for diagnostics only and must not be used to infer feature
> + support. Use TEE_IOC_VERSION for capability and compatibility
> + checks.
> diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> index d65d47cc154e..0a00499811c1 100644
> --- a/drivers/tee/tee_core.c
> +++ b/drivers/tee/tee_core.c
> @@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
> NULL
> };
>
> -ATTRIBUTE_GROUPS(tee_dev);
> +static const struct attribute_group tee_dev_group = {
> + .attrs = tee_dev_attrs,
> +};
> +
> +static ssize_t revision_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> + char version[TEE_REVISION_STR_SIZE];
> + int ret;
> +
> + if (!teedev->desc->ops->get_tee_revision)
> + return -ENODEV;
> +
> + ret = teedev->desc->ops->get_tee_revision(teedev, version,
> + sizeof(version));
> + if (ret)
> + return ret;
> +
> + return sysfs_emit(buf, "%s\n", version);
> +}
> +static DEVICE_ATTR_RO(revision);
> +
> +static struct attribute *tee_revision_attrs[] = {
> + &dev_attr_revision.attr,
> + NULL
> +};
> +
> +static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
> + struct attribute *attr, int n)
> +{
> + struct device *dev = kobj_to_dev(kobj);
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> +
> + if (teedev->desc->ops->get_tee_revision)
> + return attr->mode;
> +
> + return 0;
> +}
> +
> +static const struct attribute_group tee_revision_group = {
> + .attrs = tee_revision_attrs,
> + .is_visible = tee_revision_attr_is_visible,
> +};
> +
> +static const struct attribute_group *tee_dev_groups[] = {
> + &tee_dev_group,
> + &tee_revision_group,
> + NULL
> +};
>
> static const struct class tee_class = {
> .name = "tee",
> diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
> index 1f3e5dad6d0d..ee5f0bd41f43 100644
> --- a/include/linux/tee_core.h
> +++ b/include/linux/tee_core.h
> @@ -76,6 +76,9 @@ struct tee_device {
> /**
> * struct tee_driver_ops - driver operations vtable
> * @get_version: returns version of driver
> + * @get_tee_revision: returns revision string (diagnostic only);
Why is this comment here about it being for diagnostics only? I feel
it's up to the implementation how it would be used.
> + * do not infer feature support from this, use
> + * TEE_IOC_VERSION instead
> * @open: called for a context when the device file is opened
> * @close_context: called when the device file is closed
> * @release: called to release the context
> @@ -95,9 +98,12 @@ struct tee_device {
> * client closes the device file, even if there are existing references to the
> * context. The TEE driver can use @close_context to start cleaning up.
> */
> +
> struct tee_driver_ops {
> void (*get_version)(struct tee_device *teedev,
> struct tee_ioctl_version_data *vers);
> + int (*get_tee_revision)(struct tee_device *teedev,
> + char *buf, size_t len);
> int (*open)(struct tee_context *ctx);
> void (*close_context)(struct tee_context *ctx);
> void (*release)(struct tee_context *ctx);
> @@ -123,6 +129,9 @@ struct tee_driver_ops {
> int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
> };
>
> +/* Size for TEE revision string buffer used by get_tee_revision(). */
> +#define TEE_REVISION_STR_SIZE 128
> +
> /**
> * struct tee_desc - Describes the TEE driver to the subsystem
> * @name: name of driver
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v5 1/2] tee: add revision sysfs attribute
2026-01-07 15:28 ` Mario Limonciello
@ 2026-01-08 2:55 ` Aristo Chen
-1 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-08 2:55 UTC (permalink / raw)
To: Mario Limonciello
Cc: linux-kernel, sumit.garg, op-tee, harshal.dev, Rijo-john.Thomas,
amirreza.zarrabi, Aristo Chen
Hi Mario,
Mario Limonciello <superm1@kernel.org> 於 2026年1月7日週三 下午11:28寫道:
>
> On 1/7/26 9:26 AM, Aristo Chen wrote:
> > Add a generic TEE revision sysfs attribute backed by a new
> > optional get_tee_revision() callback. The revision string is
> > diagnostic-only and must not be used to infer feature support.
> >
> > Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> > ---
> > Documentation/ABI/testing/sysfs-class-tee | 10 +++++
> > drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
> > include/linux/tee_core.h | 9 ++++
> > 3 files changed, 69 insertions(+), 1 deletion(-)
> >
> > diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
> > index c9144d16003e..6e783210104e 100644
> > --- a/Documentation/ABI/testing/sysfs-class-tee
> > +++ b/Documentation/ABI/testing/sysfs-class-tee
> > @@ -13,3 +13,13 @@ Description:
> > space if the variable is absent. The primary purpose
> > of this variable is to let systemd know whether
> > tee-supplicant is needed in the early boot with initramfs.
> > +
> > +What: /sys/class/tee/tee{,priv}X/revision
> > +Date: Dec 2025
> > +KernelVersion: 6.18
>
> This needs to be bumped up and dates pushed out.
I will fix this in the v6 patch, thanks!
>
> > +Contact: op-tee@lists.trustedfirmware.org
> > +Description:
> > + Read-only revision string reported by the TEE driver. This is
> > + for diagnostics only and must not be used to infer feature
> > + support. Use TEE_IOC_VERSION for capability and compatibility
> > + checks.
> > diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> > index d65d47cc154e..0a00499811c1 100644
> > --- a/drivers/tee/tee_core.c
> > +++ b/drivers/tee/tee_core.c
> > @@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
> > NULL
> > };
> >
> > -ATTRIBUTE_GROUPS(tee_dev);
> > +static const struct attribute_group tee_dev_group = {
> > + .attrs = tee_dev_attrs,
> > +};
> > +
> > +static ssize_t revision_show(struct device *dev,
> > + struct device_attribute *attr, char *buf)
> > +{
> > + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> > + char version[TEE_REVISION_STR_SIZE];
> > + int ret;
> > +
> > + if (!teedev->desc->ops->get_tee_revision)
> > + return -ENODEV;
> > +
> > + ret = teedev->desc->ops->get_tee_revision(teedev, version,
> > + sizeof(version));
> > + if (ret)
> > + return ret;
> > +
> > + return sysfs_emit(buf, "%s\n", version);
> > +}
> > +static DEVICE_ATTR_RO(revision);
> > +
> > +static struct attribute *tee_revision_attrs[] = {
> > + &dev_attr_revision.attr,
> > + NULL
> > +};
> > +
> > +static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
> > + struct attribute *attr, int n)
> > +{
> > + struct device *dev = kobj_to_dev(kobj);
> > + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> > +
> > + if (teedev->desc->ops->get_tee_revision)
> > + return attr->mode;
> > +
> > + return 0;
> > +}
> > +
> > +static const struct attribute_group tee_revision_group = {
> > + .attrs = tee_revision_attrs,
> > + .is_visible = tee_revision_attr_is_visible,
> > +};
> > +
> > +static const struct attribute_group *tee_dev_groups[] = {
> > + &tee_dev_group,
> > + &tee_revision_group,
> > + NULL
> > +};
> >
> > static const struct class tee_class = {
> > .name = "tee",
> > diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
> > index 1f3e5dad6d0d..ee5f0bd41f43 100644
> > --- a/include/linux/tee_core.h
> > +++ b/include/linux/tee_core.h
> > @@ -76,6 +76,9 @@ struct tee_device {
> > /**
> > * struct tee_driver_ops - driver operations vtable
> > * @get_version: returns version of driver
> > + * @get_tee_revision: returns revision string (diagnostic only);
>
> Why is this comment here about it being for diagnostics only? I feel
> it's up to the implementation how it would be used.
According to the previous discussion, we would like to prevent user
thinking about optee os version x.y means z feature, and we should
always use TEE_IOC_VERSION for capability and compatibility
check.
Is there any other specific use case that makes you think removing
the wording is required?
>
> > + * do not infer feature support from this, use
> > + * TEE_IOC_VERSION instead
> > * @open: called for a context when the device file is opened
> > * @close_context: called when the device file is closed
> > * @release: called to release the context
> > @@ -95,9 +98,12 @@ struct tee_device {
> > * client closes the device file, even if there are existing references to the
> > * context. The TEE driver can use @close_context to start cleaning up.
> > */
> > +
> > struct tee_driver_ops {
> > void (*get_version)(struct tee_device *teedev,
> > struct tee_ioctl_version_data *vers);
> > + int (*get_tee_revision)(struct tee_device *teedev,
> > + char *buf, size_t len);
> > int (*open)(struct tee_context *ctx);
> > void (*close_context)(struct tee_context *ctx);
> > void (*release)(struct tee_context *ctx);
> > @@ -123,6 +129,9 @@ struct tee_driver_ops {
> > int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
> > };
> >
> > +/* Size for TEE revision string buffer used by get_tee_revision(). */
> > +#define TEE_REVISION_STR_SIZE 128
> > +
> > /**
> > * struct tee_desc - Describes the TEE driver to the subsystem
> > * @name: name of driver
>
Best regards,
Aristo
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v5 1/2] tee: add revision sysfs attribute
@ 2026-01-08 2:55 ` Aristo Chen
0 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-08 2:55 UTC (permalink / raw)
To: Mario Limonciello
Cc: linux-kernel, jens.wiklander, sumit.garg, op-tee, harshal.dev,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
Hi Mario,
Mario Limonciello <superm1@kernel.org> 於 2026年1月7日週三 下午11:28寫道:
>
> On 1/7/26 9:26 AM, Aristo Chen wrote:
> > Add a generic TEE revision sysfs attribute backed by a new
> > optional get_tee_revision() callback. The revision string is
> > diagnostic-only and must not be used to infer feature support.
> >
> > Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> > ---
> > Documentation/ABI/testing/sysfs-class-tee | 10 +++++
> > drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
> > include/linux/tee_core.h | 9 ++++
> > 3 files changed, 69 insertions(+), 1 deletion(-)
> >
> > diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
> > index c9144d16003e..6e783210104e 100644
> > --- a/Documentation/ABI/testing/sysfs-class-tee
> > +++ b/Documentation/ABI/testing/sysfs-class-tee
> > @@ -13,3 +13,13 @@ Description:
> > space if the variable is absent. The primary purpose
> > of this variable is to let systemd know whether
> > tee-supplicant is needed in the early boot with initramfs.
> > +
> > +What: /sys/class/tee/tee{,priv}X/revision
> > +Date: Dec 2025
> > +KernelVersion: 6.18
>
> This needs to be bumped up and dates pushed out.
I will fix this in the v6 patch, thanks!
>
> > +Contact: op-tee@lists.trustedfirmware.org
> > +Description:
> > + Read-only revision string reported by the TEE driver. This is
> > + for diagnostics only and must not be used to infer feature
> > + support. Use TEE_IOC_VERSION for capability and compatibility
> > + checks.
> > diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> > index d65d47cc154e..0a00499811c1 100644
> > --- a/drivers/tee/tee_core.c
> > +++ b/drivers/tee/tee_core.c
> > @@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
> > NULL
> > };
> >
> > -ATTRIBUTE_GROUPS(tee_dev);
> > +static const struct attribute_group tee_dev_group = {
> > + .attrs = tee_dev_attrs,
> > +};
> > +
> > +static ssize_t revision_show(struct device *dev,
> > + struct device_attribute *attr, char *buf)
> > +{
> > + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> > + char version[TEE_REVISION_STR_SIZE];
> > + int ret;
> > +
> > + if (!teedev->desc->ops->get_tee_revision)
> > + return -ENODEV;
> > +
> > + ret = teedev->desc->ops->get_tee_revision(teedev, version,
> > + sizeof(version));
> > + if (ret)
> > + return ret;
> > +
> > + return sysfs_emit(buf, "%s\n", version);
> > +}
> > +static DEVICE_ATTR_RO(revision);
> > +
> > +static struct attribute *tee_revision_attrs[] = {
> > + &dev_attr_revision.attr,
> > + NULL
> > +};
> > +
> > +static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
> > + struct attribute *attr, int n)
> > +{
> > + struct device *dev = kobj_to_dev(kobj);
> > + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> > +
> > + if (teedev->desc->ops->get_tee_revision)
> > + return attr->mode;
> > +
> > + return 0;
> > +}
> > +
> > +static const struct attribute_group tee_revision_group = {
> > + .attrs = tee_revision_attrs,
> > + .is_visible = tee_revision_attr_is_visible,
> > +};
> > +
> > +static const struct attribute_group *tee_dev_groups[] = {
> > + &tee_dev_group,
> > + &tee_revision_group,
> > + NULL
> > +};
> >
> > static const struct class tee_class = {
> > .name = "tee",
> > diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
> > index 1f3e5dad6d0d..ee5f0bd41f43 100644
> > --- a/include/linux/tee_core.h
> > +++ b/include/linux/tee_core.h
> > @@ -76,6 +76,9 @@ struct tee_device {
> > /**
> > * struct tee_driver_ops - driver operations vtable
> > * @get_version: returns version of driver
> > + * @get_tee_revision: returns revision string (diagnostic only);
>
> Why is this comment here about it being for diagnostics only? I feel
> it's up to the implementation how it would be used.
According to the previous discussion, we would like to prevent user
thinking about optee os version x.y means z feature, and we should
always use TEE_IOC_VERSION for capability and compatibility
check.
Is there any other specific use case that makes you think removing
the wording is required?
>
> > + * do not infer feature support from this, use
> > + * TEE_IOC_VERSION instead
> > * @open: called for a context when the device file is opened
> > * @close_context: called when the device file is closed
> > * @release: called to release the context
> > @@ -95,9 +98,12 @@ struct tee_device {
> > * client closes the device file, even if there are existing references to the
> > * context. The TEE driver can use @close_context to start cleaning up.
> > */
> > +
> > struct tee_driver_ops {
> > void (*get_version)(struct tee_device *teedev,
> > struct tee_ioctl_version_data *vers);
> > + int (*get_tee_revision)(struct tee_device *teedev,
> > + char *buf, size_t len);
> > int (*open)(struct tee_context *ctx);
> > void (*close_context)(struct tee_context *ctx);
> > void (*release)(struct tee_context *ctx);
> > @@ -123,6 +129,9 @@ struct tee_driver_ops {
> > int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
> > };
> >
> > +/* Size for TEE revision string buffer used by get_tee_revision(). */
> > +#define TEE_REVISION_STR_SIZE 128
> > +
> > /**
> > * struct tee_desc - Describes the TEE driver to the subsystem
> > * @name: name of driver
>
Best regards,
Aristo
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v5 1/2] tee: add revision sysfs attribute
2026-01-08 2:55 ` Aristo Chen
@ 2026-01-08 3:01 ` Mario Limonciello (AMD) (kernel.org)
-1 siblings, 0 replies; 74+ messages in thread
From: Mario Limonciello (AMD) (kernel.org) via OP-TEE @ 2026-01-08 3:01 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, sumit.garg, op-tee, harshal.dev, Rijo-john.Thomas,
amirreza.zarrabi, Aristo Chen
On 1/7/2026 8:55 PM, Aristo Chen wrote:
> Hi Mario,
>
> Mario Limonciello <superm1@kernel.org> 於 2026年1月7日週三 下午11:28寫道:
>>
>> On 1/7/26 9:26 AM, Aristo Chen wrote:
>>> Add a generic TEE revision sysfs attribute backed by a new
>>> optional get_tee_revision() callback. The revision string is
>>> diagnostic-only and must not be used to infer feature support.
>>>
>>> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
>>> ---
>>> Documentation/ABI/testing/sysfs-class-tee | 10 +++++
>>> drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
>>> include/linux/tee_core.h | 9 ++++
>>> 3 files changed, 69 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
>>> index c9144d16003e..6e783210104e 100644
>>> --- a/Documentation/ABI/testing/sysfs-class-tee
>>> +++ b/Documentation/ABI/testing/sysfs-class-tee
>>> @@ -13,3 +13,13 @@ Description:
>>> space if the variable is absent. The primary purpose
>>> of this variable is to let systemd know whether
>>> tee-supplicant is needed in the early boot with initramfs.
>>> +
>>> +What: /sys/class/tee/tee{,priv}X/revision
>>> +Date: Dec 2025
>>> +KernelVersion: 6.18
>>
>> This needs to be bumped up and dates pushed out.
>
> I will fix this in the v6 patch, thanks!
>
>>
>>> +Contact: op-tee@lists.trustedfirmware.org
>>> +Description:
>>> + Read-only revision string reported by the TEE driver. This is
>>> + for diagnostics only and must not be used to infer feature
>>> + support. Use TEE_IOC_VERSION for capability and compatibility
>>> + checks.
>>> diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
>>> index d65d47cc154e..0a00499811c1 100644
>>> --- a/drivers/tee/tee_core.c
>>> +++ b/drivers/tee/tee_core.c
>>> @@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
>>> NULL
>>> };
>>>
>>> -ATTRIBUTE_GROUPS(tee_dev);
>>> +static const struct attribute_group tee_dev_group = {
>>> + .attrs = tee_dev_attrs,
>>> +};
>>> +
>>> +static ssize_t revision_show(struct device *dev,
>>> + struct device_attribute *attr, char *buf)
>>> +{
>>> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
>>> + char version[TEE_REVISION_STR_SIZE];
>>> + int ret;
>>> +
>>> + if (!teedev->desc->ops->get_tee_revision)
>>> + return -ENODEV;
>>> +
>>> + ret = teedev->desc->ops->get_tee_revision(teedev, version,
>>> + sizeof(version));
>>> + if (ret)
>>> + return ret;
>>> +
>>> + return sysfs_emit(buf, "%s\n", version);
>>> +}
>>> +static DEVICE_ATTR_RO(revision);
>>> +
>>> +static struct attribute *tee_revision_attrs[] = {
>>> + &dev_attr_revision.attr,
>>> + NULL
>>> +};
>>> +
>>> +static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
>>> + struct attribute *attr, int n)
>>> +{
>>> + struct device *dev = kobj_to_dev(kobj);
>>> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
>>> +
>>> + if (teedev->desc->ops->get_tee_revision)
>>> + return attr->mode;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct attribute_group tee_revision_group = {
>>> + .attrs = tee_revision_attrs,
>>> + .is_visible = tee_revision_attr_is_visible,
>>> +};
>>> +
>>> +static const struct attribute_group *tee_dev_groups[] = {
>>> + &tee_dev_group,
>>> + &tee_revision_group,
>>> + NULL
>>> +};
>>>
>>> static const struct class tee_class = {
>>> .name = "tee",
>>> diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
>>> index 1f3e5dad6d0d..ee5f0bd41f43 100644
>>> --- a/include/linux/tee_core.h
>>> +++ b/include/linux/tee_core.h
>>> @@ -76,6 +76,9 @@ struct tee_device {
>>> /**
>>> * struct tee_driver_ops - driver operations vtable
>>> * @get_version: returns version of driver
>>> + * @get_tee_revision: returns revision string (diagnostic only);
>>
>> Why is this comment here about it being for diagnostics only? I feel
>> it's up to the implementation how it would be used.
>
> According to the previous discussion, we would like to prevent user
> thinking about optee os version x.y means z feature, and we should
> always use TEE_IOC_VERSION for capability and compatibility
> check.
>
> Is there any other specific use case that makes you think removing
> the wording is required?
Ah I didn't realize there was previous discussion that lead to this, I
saw some earlier versions in my holiday mailbox glut but ignored them
when I saw the new one.
Leave it as is then.
>
>>
>>> + * do not infer feature support from this, use
>>> + * TEE_IOC_VERSION instead
>>> * @open: called for a context when the device file is opened
>>> * @close_context: called when the device file is closed
>>> * @release: called to release the context
>>> @@ -95,9 +98,12 @@ struct tee_device {
>>> * client closes the device file, even if there are existing references to the
>>> * context. The TEE driver can use @close_context to start cleaning up.
>>> */
>>> +
>>> struct tee_driver_ops {
>>> void (*get_version)(struct tee_device *teedev,
>>> struct tee_ioctl_version_data *vers);
>>> + int (*get_tee_revision)(struct tee_device *teedev,
>>> + char *buf, size_t len);
>>> int (*open)(struct tee_context *ctx);
>>> void (*close_context)(struct tee_context *ctx);
>>> void (*release)(struct tee_context *ctx);
>>> @@ -123,6 +129,9 @@ struct tee_driver_ops {
>>> int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
>>> };
>>>
>>> +/* Size for TEE revision string buffer used by get_tee_revision(). */
>>> +#define TEE_REVISION_STR_SIZE 128
>>> +
>>> /**
>>> * struct tee_desc - Describes the TEE driver to the subsystem
>>> * @name: name of driver
>>
>
> Best regards,
> Aristo
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v5 1/2] tee: add revision sysfs attribute
@ 2026-01-08 3:01 ` Mario Limonciello (AMD) (kernel.org)
0 siblings, 0 replies; 74+ messages in thread
From: Mario Limonciello (AMD) (kernel.org) @ 2026-01-08 3:01 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, jens.wiklander, sumit.garg, op-tee, harshal.dev,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
On 1/7/2026 8:55 PM, Aristo Chen wrote:
> Hi Mario,
>
> Mario Limonciello <superm1@kernel.org> 於 2026年1月7日週三 下午11:28寫道:
>>
>> On 1/7/26 9:26 AM, Aristo Chen wrote:
>>> Add a generic TEE revision sysfs attribute backed by a new
>>> optional get_tee_revision() callback. The revision string is
>>> diagnostic-only and must not be used to infer feature support.
>>>
>>> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
>>> ---
>>> Documentation/ABI/testing/sysfs-class-tee | 10 +++++
>>> drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
>>> include/linux/tee_core.h | 9 ++++
>>> 3 files changed, 69 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
>>> index c9144d16003e..6e783210104e 100644
>>> --- a/Documentation/ABI/testing/sysfs-class-tee
>>> +++ b/Documentation/ABI/testing/sysfs-class-tee
>>> @@ -13,3 +13,13 @@ Description:
>>> space if the variable is absent. The primary purpose
>>> of this variable is to let systemd know whether
>>> tee-supplicant is needed in the early boot with initramfs.
>>> +
>>> +What: /sys/class/tee/tee{,priv}X/revision
>>> +Date: Dec 2025
>>> +KernelVersion: 6.18
>>
>> This needs to be bumped up and dates pushed out.
>
> I will fix this in the v6 patch, thanks!
>
>>
>>> +Contact: op-tee@lists.trustedfirmware.org
>>> +Description:
>>> + Read-only revision string reported by the TEE driver. This is
>>> + for diagnostics only and must not be used to infer feature
>>> + support. Use TEE_IOC_VERSION for capability and compatibility
>>> + checks.
>>> diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
>>> index d65d47cc154e..0a00499811c1 100644
>>> --- a/drivers/tee/tee_core.c
>>> +++ b/drivers/tee/tee_core.c
>>> @@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
>>> NULL
>>> };
>>>
>>> -ATTRIBUTE_GROUPS(tee_dev);
>>> +static const struct attribute_group tee_dev_group = {
>>> + .attrs = tee_dev_attrs,
>>> +};
>>> +
>>> +static ssize_t revision_show(struct device *dev,
>>> + struct device_attribute *attr, char *buf)
>>> +{
>>> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
>>> + char version[TEE_REVISION_STR_SIZE];
>>> + int ret;
>>> +
>>> + if (!teedev->desc->ops->get_tee_revision)
>>> + return -ENODEV;
>>> +
>>> + ret = teedev->desc->ops->get_tee_revision(teedev, version,
>>> + sizeof(version));
>>> + if (ret)
>>> + return ret;
>>> +
>>> + return sysfs_emit(buf, "%s\n", version);
>>> +}
>>> +static DEVICE_ATTR_RO(revision);
>>> +
>>> +static struct attribute *tee_revision_attrs[] = {
>>> + &dev_attr_revision.attr,
>>> + NULL
>>> +};
>>> +
>>> +static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
>>> + struct attribute *attr, int n)
>>> +{
>>> + struct device *dev = kobj_to_dev(kobj);
>>> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
>>> +
>>> + if (teedev->desc->ops->get_tee_revision)
>>> + return attr->mode;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct attribute_group tee_revision_group = {
>>> + .attrs = tee_revision_attrs,
>>> + .is_visible = tee_revision_attr_is_visible,
>>> +};
>>> +
>>> +static const struct attribute_group *tee_dev_groups[] = {
>>> + &tee_dev_group,
>>> + &tee_revision_group,
>>> + NULL
>>> +};
>>>
>>> static const struct class tee_class = {
>>> .name = "tee",
>>> diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
>>> index 1f3e5dad6d0d..ee5f0bd41f43 100644
>>> --- a/include/linux/tee_core.h
>>> +++ b/include/linux/tee_core.h
>>> @@ -76,6 +76,9 @@ struct tee_device {
>>> /**
>>> * struct tee_driver_ops - driver operations vtable
>>> * @get_version: returns version of driver
>>> + * @get_tee_revision: returns revision string (diagnostic only);
>>
>> Why is this comment here about it being for diagnostics only? I feel
>> it's up to the implementation how it would be used.
>
> According to the previous discussion, we would like to prevent user
> thinking about optee os version x.y means z feature, and we should
> always use TEE_IOC_VERSION for capability and compatibility
> check.
>
> Is there any other specific use case that makes you think removing
> the wording is required?
Ah I didn't realize there was previous discussion that lead to this, I
saw some earlier versions in my holiday mailbox glut but ignored them
when I saw the new one.
Leave it as is then.
>
>>
>>> + * do not infer feature support from this, use
>>> + * TEE_IOC_VERSION instead
>>> * @open: called for a context when the device file is opened
>>> * @close_context: called when the device file is closed
>>> * @release: called to release the context
>>> @@ -95,9 +98,12 @@ struct tee_device {
>>> * client closes the device file, even if there are existing references to the
>>> * context. The TEE driver can use @close_context to start cleaning up.
>>> */
>>> +
>>> struct tee_driver_ops {
>>> void (*get_version)(struct tee_device *teedev,
>>> struct tee_ioctl_version_data *vers);
>>> + int (*get_tee_revision)(struct tee_device *teedev,
>>> + char *buf, size_t len);
>>> int (*open)(struct tee_context *ctx);
>>> void (*close_context)(struct tee_context *ctx);
>>> void (*release)(struct tee_context *ctx);
>>> @@ -123,6 +129,9 @@ struct tee_driver_ops {
>>> int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
>>> };
>>>
>>> +/* Size for TEE revision string buffer used by get_tee_revision(). */
>>> +#define TEE_REVISION_STR_SIZE 128
>>> +
>>> /**
>>> * struct tee_desc - Describes the TEE driver to the subsystem
>>> * @name: name of driver
>>
>
> Best regards,
> Aristo
^ permalink raw reply [flat|nested] 74+ messages in thread
* [PATCH v6 1/2] tee: add revision sysfs attribute
2026-01-07 15:26 ` Aristo Chen
@ 2026-01-08 6:45 ` Aristo Chen
-1 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-08 6:45 UTC (permalink / raw)
To: linux-kernel
Cc: sumit.garg, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
Add a generic TEE revision sysfs attribute backed by a new
optional get_tee_revision() callback. The revision string is
diagnostic-only and must not be used to infer feature support.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
---
Documentation/ABI/testing/sysfs-class-tee | 10 +++++
drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
include/linux/tee_core.h | 9 ++++
3 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
index c9144d16003e..1a0a3050aaa9 100644
--- a/Documentation/ABI/testing/sysfs-class-tee
+++ b/Documentation/ABI/testing/sysfs-class-tee
@@ -13,3 +13,13 @@ Description:
space if the variable is absent. The primary purpose
of this variable is to let systemd know whether
tee-supplicant is needed in the early boot with initramfs.
+
+What: /sys/class/tee/tee{,priv}X/revision
+Date: Jan 2026
+KernelVersion: 6.19
+Contact: op-tee@lists.trustedfirmware.org
+Description:
+ Read-only revision string reported by the TEE driver. This is
+ for diagnostics only and must not be used to infer feature
+ support. Use TEE_IOC_VERSION for capability and compatibility
+ checks.
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index d65d47cc154e..0a00499811c1 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
NULL
};
-ATTRIBUTE_GROUPS(tee_dev);
+static const struct attribute_group tee_dev_group = {
+ .attrs = tee_dev_attrs,
+};
+
+static ssize_t revision_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+ char version[TEE_REVISION_STR_SIZE];
+ int ret;
+
+ if (!teedev->desc->ops->get_tee_revision)
+ return -ENODEV;
+
+ ret = teedev->desc->ops->get_tee_revision(teedev, version,
+ sizeof(version));
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%s\n", version);
+}
+static DEVICE_ATTR_RO(revision);
+
+static struct attribute *tee_revision_attrs[] = {
+ &dev_attr_revision.attr,
+ NULL
+};
+
+static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+
+ if (teedev->desc->ops->get_tee_revision)
+ return attr->mode;
+
+ return 0;
+}
+
+static const struct attribute_group tee_revision_group = {
+ .attrs = tee_revision_attrs,
+ .is_visible = tee_revision_attr_is_visible,
+};
+
+static const struct attribute_group *tee_dev_groups[] = {
+ &tee_dev_group,
+ &tee_revision_group,
+ NULL
+};
static const struct class tee_class = {
.name = "tee",
diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
index 1f3e5dad6d0d..ee5f0bd41f43 100644
--- a/include/linux/tee_core.h
+++ b/include/linux/tee_core.h
@@ -76,6 +76,9 @@ struct tee_device {
/**
* struct tee_driver_ops - driver operations vtable
* @get_version: returns version of driver
+ * @get_tee_revision: returns revision string (diagnostic only);
+ * do not infer feature support from this, use
+ * TEE_IOC_VERSION instead
* @open: called for a context when the device file is opened
* @close_context: called when the device file is closed
* @release: called to release the context
@@ -95,9 +98,12 @@ struct tee_device {
* client closes the device file, even if there are existing references to the
* context. The TEE driver can use @close_context to start cleaning up.
*/
+
struct tee_driver_ops {
void (*get_version)(struct tee_device *teedev,
struct tee_ioctl_version_data *vers);
+ int (*get_tee_revision)(struct tee_device *teedev,
+ char *buf, size_t len);
int (*open)(struct tee_context *ctx);
void (*close_context)(struct tee_context *ctx);
void (*release)(struct tee_context *ctx);
@@ -123,6 +129,9 @@ struct tee_driver_ops {
int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
};
+/* Size for TEE revision string buffer used by get_tee_revision(). */
+#define TEE_REVISION_STR_SIZE 128
+
/**
* struct tee_desc - Describes the TEE driver to the subsystem
* @name: name of driver
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread* [PATCH v6 1/2] tee: add revision sysfs attribute
@ 2026-01-08 6:45 ` Aristo Chen
0 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-08 6:45 UTC (permalink / raw)
To: linux-kernel
Cc: jens.wiklander, sumit.garg, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
Add a generic TEE revision sysfs attribute backed by a new
optional get_tee_revision() callback. The revision string is
diagnostic-only and must not be used to infer feature support.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
---
Documentation/ABI/testing/sysfs-class-tee | 10 +++++
drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
include/linux/tee_core.h | 9 ++++
3 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
index c9144d16003e..1a0a3050aaa9 100644
--- a/Documentation/ABI/testing/sysfs-class-tee
+++ b/Documentation/ABI/testing/sysfs-class-tee
@@ -13,3 +13,13 @@ Description:
space if the variable is absent. The primary purpose
of this variable is to let systemd know whether
tee-supplicant is needed in the early boot with initramfs.
+
+What: /sys/class/tee/tee{,priv}X/revision
+Date: Jan 2026
+KernelVersion: 6.19
+Contact: op-tee@lists.trustedfirmware.org
+Description:
+ Read-only revision string reported by the TEE driver. This is
+ for diagnostics only and must not be used to infer feature
+ support. Use TEE_IOC_VERSION for capability and compatibility
+ checks.
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index d65d47cc154e..0a00499811c1 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
NULL
};
-ATTRIBUTE_GROUPS(tee_dev);
+static const struct attribute_group tee_dev_group = {
+ .attrs = tee_dev_attrs,
+};
+
+static ssize_t revision_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+ char version[TEE_REVISION_STR_SIZE];
+ int ret;
+
+ if (!teedev->desc->ops->get_tee_revision)
+ return -ENODEV;
+
+ ret = teedev->desc->ops->get_tee_revision(teedev, version,
+ sizeof(version));
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%s\n", version);
+}
+static DEVICE_ATTR_RO(revision);
+
+static struct attribute *tee_revision_attrs[] = {
+ &dev_attr_revision.attr,
+ NULL
+};
+
+static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+
+ if (teedev->desc->ops->get_tee_revision)
+ return attr->mode;
+
+ return 0;
+}
+
+static const struct attribute_group tee_revision_group = {
+ .attrs = tee_revision_attrs,
+ .is_visible = tee_revision_attr_is_visible,
+};
+
+static const struct attribute_group *tee_dev_groups[] = {
+ &tee_dev_group,
+ &tee_revision_group,
+ NULL
+};
static const struct class tee_class = {
.name = "tee",
diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
index 1f3e5dad6d0d..ee5f0bd41f43 100644
--- a/include/linux/tee_core.h
+++ b/include/linux/tee_core.h
@@ -76,6 +76,9 @@ struct tee_device {
/**
* struct tee_driver_ops - driver operations vtable
* @get_version: returns version of driver
+ * @get_tee_revision: returns revision string (diagnostic only);
+ * do not infer feature support from this, use
+ * TEE_IOC_VERSION instead
* @open: called for a context when the device file is opened
* @close_context: called when the device file is closed
* @release: called to release the context
@@ -95,9 +98,12 @@ struct tee_device {
* client closes the device file, even if there are existing references to the
* context. The TEE driver can use @close_context to start cleaning up.
*/
+
struct tee_driver_ops {
void (*get_version)(struct tee_device *teedev,
struct tee_ioctl_version_data *vers);
+ int (*get_tee_revision)(struct tee_device *teedev,
+ char *buf, size_t len);
int (*open)(struct tee_context *ctx);
void (*close_context)(struct tee_context *ctx);
void (*release)(struct tee_context *ctx);
@@ -123,6 +129,9 @@ struct tee_driver_ops {
int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
};
+/* Size for TEE revision string buffer used by get_tee_revision(). */
+#define TEE_REVISION_STR_SIZE 128
+
/**
* struct tee_desc - Describes the TEE driver to the subsystem
* @name: name of driver
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread* [PATCH v6 2/2] tee: optee: store OS revision for TEE core
2026-01-08 6:45 ` Aristo Chen
@ 2026-01-08 6:45 ` Aristo Chen
-1 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-08 6:45 UTC (permalink / raw)
To: linux-kernel
Cc: sumit.garg, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
Collect OP-TEE OS revision from secure world for both SMC and FF-A
ABIs, store it in the OP-TEE driver, and expose it through the
generic get_tee_revision() callback.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
---
drivers/tee/optee/core.c | 23 +++++++++++++
drivers/tee/optee/ffa_abi.c | 57 ++++++++++++++++++++++++-------
drivers/tee/optee/optee_private.h | 19 +++++++++++
drivers/tee/optee/smc_abi.c | 15 ++++++--
4 files changed, 99 insertions(+), 15 deletions(-)
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 5b62139714ce..2d807bc748bc 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
}
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
+{
+ struct optee *optee = tee_get_drvdata(teedev);
+ u64 build_id;
+
+ if (!optee)
+ return -ENODEV;
+ if (!buf || !len)
+ return -EINVAL;
+
+ build_id = optee->revision.os_build_id;
+ if (build_id)
+ scnprintf(buf, len, "%u.%u (%016llx)",
+ optee->revision.os_major,
+ optee->revision.os_minor,
+ (unsigned long long)build_id);
+ else
+ scnprintf(buf, len, "%u.%u", optee->revision.os_major,
+ optee->revision.os_minor);
+
+ return 0;
+}
+
static void optee_bus_scan(struct work_struct *work)
{
WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
index bf8390789ecf..82dbed1c87e5 100644
--- a/drivers/tee/optee/ffa_abi.c
+++ b/drivers/tee/optee/ffa_abi.c
@@ -775,6 +775,42 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
* with a matching configuration.
*/
+static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
+ const struct ffa_ops *ops,
+ struct optee_revision *revision,
+ bool log)
+{
+ const struct ffa_msg_ops *msg_ops = ops->msg_ops;
+ struct ffa_send_direct_data data = {
+ .data0 = OPTEE_FFA_GET_OS_VERSION,
+ };
+ int rc;
+
+ msg_ops->mode_32bit_set(ffa_dev);
+
+ rc = msg_ops->sync_send_receive(ffa_dev, &data);
+ if (rc) {
+ pr_err("Unexpected error %d\n", rc);
+ return false;
+ }
+
+ if (revision) {
+ revision->os_major = data.data0;
+ revision->os_minor = data.data1;
+ revision->os_build_id = data.data2;
+ }
+
+ if (log) {
+ if (data.data2)
+ pr_info("revision %lu.%lu (%08lx)",
+ data.data0, data.data1, data.data2);
+ else
+ pr_info("revision %lu.%lu", data.data0, data.data1);
+ }
+
+ return true;
+}
+
static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
const struct ffa_ops *ops)
{
@@ -798,19 +834,8 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
return false;
}
- data = (struct ffa_send_direct_data){
- .data0 = OPTEE_FFA_GET_OS_VERSION,
- };
- rc = msg_ops->sync_send_receive(ffa_dev, &data);
- if (rc) {
- pr_err("Unexpected error %d\n", rc);
+ if (!optee_ffa_get_os_revision(ffa_dev, ops, NULL, true))
return false;
- }
- if (data.data2)
- pr_info("revision %lu.%lu (%08lx)",
- data.data0, data.data1, data.data2);
- else
- pr_info("revision %lu.%lu", data.data0, data.data1);
return true;
}
@@ -900,6 +925,7 @@ static int optee_ffa_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_ffa_clnt_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -918,6 +944,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
static const struct tee_driver_ops optee_ffa_supp_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1060,6 +1087,12 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
if (!optee)
return -ENOMEM;
+ if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision,
+ false)) {
+ rc = -EINVAL;
+ goto err_free_optee;
+ }
+
pool = optee_ffa_shm_pool_alloc_pages();
if (IS_ERR(pool)) {
rc = PTR_ERR(pool);
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index db9ea673fbca..acd3051c4879 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -171,6 +171,24 @@ struct optee_ffa {
struct optee;
+/**
+ * struct optee_revision - OP-TEE OS revision reported by secure world
+ * @os_major: OP-TEE OS major version
+ * @os_minor: OP-TEE OS minor version
+ * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
+ *
+ * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
+ * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
+ * FF-A ABI version.
+ */
+struct optee_revision {
+ u32 os_major;
+ u32 os_minor;
+ u64 os_build_id;
+};
+
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
+
/**
* struct optee_ops - OP-TEE driver internal operations
* @do_call_with_arg: enters OP-TEE in secure world
@@ -249,6 +267,7 @@ struct optee {
bool in_kernel_rpmb_routing;
struct work_struct scan_bus_work;
struct work_struct rpmb_scan_bus_work;
+ struct optee_revision revision;
};
struct optee_session {
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
index 0be663fcd52b..51fae1ab8ef8 100644
--- a/drivers/tee/optee/smc_abi.c
+++ b/drivers/tee/optee/smc_abi.c
@@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_clnt_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
static const struct tee_driver_ops optee_supp_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
}
#endif
-static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
+static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
+ struct optee_revision *revision)
{
union {
struct arm_smccc_res smccc;
@@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
&res.smccc);
+ if (revision) {
+ revision->os_major = res.result.major;
+ revision->os_minor = res.result.minor;
+ revision->os_build_id = res.result.build_id;
+ }
+
if (res.result.build_id)
pr_info("revision %lu.%lu (%0*lx)", res.result.major,
res.result.minor, (int)sizeof(res.result.build_id) * 2,
@@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev)
return -EINVAL;
}
- optee_msg_get_os_revision(invoke_fn);
-
if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
pr_warn("api revision mismatch\n");
return -EINVAL;
@@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev)
goto err_free_shm_pool;
}
+ optee_msg_get_os_revision(invoke_fn, &optee->revision);
+
optee->ops = &optee_ops;
optee->smc.invoke_fn = invoke_fn;
optee->smc.sec_caps = sec_caps;
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread* [PATCH v6 2/2] tee: optee: store OS revision for TEE core
@ 2026-01-08 6:45 ` Aristo Chen
0 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-08 6:45 UTC (permalink / raw)
To: linux-kernel
Cc: jens.wiklander, sumit.garg, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
Collect OP-TEE OS revision from secure world for both SMC and FF-A
ABIs, store it in the OP-TEE driver, and expose it through the
generic get_tee_revision() callback.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
---
drivers/tee/optee/core.c | 23 +++++++++++++
drivers/tee/optee/ffa_abi.c | 57 ++++++++++++++++++++++++-------
drivers/tee/optee/optee_private.h | 19 +++++++++++
drivers/tee/optee/smc_abi.c | 15 ++++++--
4 files changed, 99 insertions(+), 15 deletions(-)
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 5b62139714ce..2d807bc748bc 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
}
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
+{
+ struct optee *optee = tee_get_drvdata(teedev);
+ u64 build_id;
+
+ if (!optee)
+ return -ENODEV;
+ if (!buf || !len)
+ return -EINVAL;
+
+ build_id = optee->revision.os_build_id;
+ if (build_id)
+ scnprintf(buf, len, "%u.%u (%016llx)",
+ optee->revision.os_major,
+ optee->revision.os_minor,
+ (unsigned long long)build_id);
+ else
+ scnprintf(buf, len, "%u.%u", optee->revision.os_major,
+ optee->revision.os_minor);
+
+ return 0;
+}
+
static void optee_bus_scan(struct work_struct *work)
{
WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
index bf8390789ecf..82dbed1c87e5 100644
--- a/drivers/tee/optee/ffa_abi.c
+++ b/drivers/tee/optee/ffa_abi.c
@@ -775,6 +775,42 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
* with a matching configuration.
*/
+static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
+ const struct ffa_ops *ops,
+ struct optee_revision *revision,
+ bool log)
+{
+ const struct ffa_msg_ops *msg_ops = ops->msg_ops;
+ struct ffa_send_direct_data data = {
+ .data0 = OPTEE_FFA_GET_OS_VERSION,
+ };
+ int rc;
+
+ msg_ops->mode_32bit_set(ffa_dev);
+
+ rc = msg_ops->sync_send_receive(ffa_dev, &data);
+ if (rc) {
+ pr_err("Unexpected error %d\n", rc);
+ return false;
+ }
+
+ if (revision) {
+ revision->os_major = data.data0;
+ revision->os_minor = data.data1;
+ revision->os_build_id = data.data2;
+ }
+
+ if (log) {
+ if (data.data2)
+ pr_info("revision %lu.%lu (%08lx)",
+ data.data0, data.data1, data.data2);
+ else
+ pr_info("revision %lu.%lu", data.data0, data.data1);
+ }
+
+ return true;
+}
+
static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
const struct ffa_ops *ops)
{
@@ -798,19 +834,8 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
return false;
}
- data = (struct ffa_send_direct_data){
- .data0 = OPTEE_FFA_GET_OS_VERSION,
- };
- rc = msg_ops->sync_send_receive(ffa_dev, &data);
- if (rc) {
- pr_err("Unexpected error %d\n", rc);
+ if (!optee_ffa_get_os_revision(ffa_dev, ops, NULL, true))
return false;
- }
- if (data.data2)
- pr_info("revision %lu.%lu (%08lx)",
- data.data0, data.data1, data.data2);
- else
- pr_info("revision %lu.%lu", data.data0, data.data1);
return true;
}
@@ -900,6 +925,7 @@ static int optee_ffa_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_ffa_clnt_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -918,6 +944,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
static const struct tee_driver_ops optee_ffa_supp_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1060,6 +1087,12 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
if (!optee)
return -ENOMEM;
+ if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision,
+ false)) {
+ rc = -EINVAL;
+ goto err_free_optee;
+ }
+
pool = optee_ffa_shm_pool_alloc_pages();
if (IS_ERR(pool)) {
rc = PTR_ERR(pool);
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index db9ea673fbca..acd3051c4879 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -171,6 +171,24 @@ struct optee_ffa {
struct optee;
+/**
+ * struct optee_revision - OP-TEE OS revision reported by secure world
+ * @os_major: OP-TEE OS major version
+ * @os_minor: OP-TEE OS minor version
+ * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
+ *
+ * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
+ * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
+ * FF-A ABI version.
+ */
+struct optee_revision {
+ u32 os_major;
+ u32 os_minor;
+ u64 os_build_id;
+};
+
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
+
/**
* struct optee_ops - OP-TEE driver internal operations
* @do_call_with_arg: enters OP-TEE in secure world
@@ -249,6 +267,7 @@ struct optee {
bool in_kernel_rpmb_routing;
struct work_struct scan_bus_work;
struct work_struct rpmb_scan_bus_work;
+ struct optee_revision revision;
};
struct optee_session {
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
index 0be663fcd52b..51fae1ab8ef8 100644
--- a/drivers/tee/optee/smc_abi.c
+++ b/drivers/tee/optee/smc_abi.c
@@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_clnt_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
static const struct tee_driver_ops optee_supp_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
}
#endif
-static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
+static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
+ struct optee_revision *revision)
{
union {
struct arm_smccc_res smccc;
@@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
&res.smccc);
+ if (revision) {
+ revision->os_major = res.result.major;
+ revision->os_minor = res.result.minor;
+ revision->os_build_id = res.result.build_id;
+ }
+
if (res.result.build_id)
pr_info("revision %lu.%lu (%0*lx)", res.result.major,
res.result.minor, (int)sizeof(res.result.build_id) * 2,
@@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev)
return -EINVAL;
}
- optee_msg_get_os_revision(invoke_fn);
-
if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
pr_warn("api revision mismatch\n");
return -EINVAL;
@@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev)
goto err_free_shm_pool;
}
+ optee_msg_get_os_revision(invoke_fn, &optee->revision);
+
optee->ops = &optee_ops;
optee->smc.invoke_fn = invoke_fn;
optee->smc.sec_caps = sec_caps;
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread* Re: [PATCH v6 2/2] tee: optee: store OS revision for TEE core
2026-01-08 6:45 ` Aristo Chen
@ 2026-01-09 11:50 ` Sumit Garg
-1 siblings, 0 replies; 74+ messages in thread
From: Sumit Garg via OP-TEE @ 2026-01-09 11:50 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
On Thu, Jan 08, 2026 at 02:45:09PM +0800, Aristo Chen wrote:
> Collect OP-TEE OS revision from secure world for both SMC and FF-A
> ABIs, store it in the OP-TEE driver, and expose it through the
> generic get_tee_revision() callback.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> drivers/tee/optee/core.c | 23 +++++++++++++
> drivers/tee/optee/ffa_abi.c | 57 ++++++++++++++++++++++++-------
> drivers/tee/optee/optee_private.h | 19 +++++++++++
> drivers/tee/optee/smc_abi.c | 15 ++++++--
> 4 files changed, 99 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> index 5b62139714ce..2d807bc748bc 100644
> --- a/drivers/tee/optee/core.c
> +++ b/drivers/tee/optee/core.c
> @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> }
>
> +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> +{
> + struct optee *optee = tee_get_drvdata(teedev);
> + u64 build_id;
> +
> + if (!optee)
> + return -ENODEV;
> + if (!buf || !len)
> + return -EINVAL;
> +
> + build_id = optee->revision.os_build_id;
> + if (build_id)
> + scnprintf(buf, len, "%u.%u (%016llx)",
> + optee->revision.os_major,
> + optee->revision.os_minor,
> + (unsigned long long)build_id);
> + else
> + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> + optee->revision.os_minor);
> +
> + return 0;
> +}
> +
> static void optee_bus_scan(struct work_struct *work)
> {
> WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> index bf8390789ecf..82dbed1c87e5 100644
> --- a/drivers/tee/optee/ffa_abi.c
> +++ b/drivers/tee/optee/ffa_abi.c
> @@ -775,6 +775,42 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> * with a matching configuration.
> */
>
> +static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
> + const struct ffa_ops *ops,
> + struct optee_revision *revision,
> + bool log)
> +{
> + const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> + struct ffa_send_direct_data data = {
> + .data0 = OPTEE_FFA_GET_OS_VERSION,
> + };
> + int rc;
> +
> + msg_ops->mode_32bit_set(ffa_dev);
> +
> + rc = msg_ops->sync_send_receive(ffa_dev, &data);
> + if (rc) {
> + pr_err("Unexpected error %d\n", rc);
> + return false;
> + }
> +
> + if (revision) {
> + revision->os_major = data.data0;
> + revision->os_minor = data.data1;
> + revision->os_build_id = data.data2;
> + }
> +
> + if (log) {
> + if (data.data2)
> + pr_info("revision %lu.%lu (%08lx)",
> + data.data0, data.data1, data.data2);
> + else
> + pr_info("revision %lu.%lu", data.data0, data.data1);
> + }
> +
> + return true;
> +}
> +
> static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> const struct ffa_ops *ops)
> {
> @@ -798,19 +834,8 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> return false;
> }
>
> - data = (struct ffa_send_direct_data){
> - .data0 = OPTEE_FFA_GET_OS_VERSION,
> - };
> - rc = msg_ops->sync_send_receive(ffa_dev, &data);
> - if (rc) {
> - pr_err("Unexpected error %d\n", rc);
> + if (!optee_ffa_get_os_revision(ffa_dev, ops, NULL, true))
Why do you need to invoke optee_ffa_get_os_revision() here?
-Sumit
> return false;
> - }
> - if (data.data2)
> - pr_info("revision %lu.%lu (%08lx)",
> - data.data0, data.data1, data.data2);
> - else
> - pr_info("revision %lu.%lu", data.data0, data.data1);
>
> return true;
> }
> @@ -900,6 +925,7 @@ static int optee_ffa_open(struct tee_context *ctx)
>
> static const struct tee_driver_ops optee_ffa_clnt_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -918,6 +944,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
>
> static const struct tee_driver_ops optee_ffa_supp_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1060,6 +1087,12 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> if (!optee)
> return -ENOMEM;
>
> + if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision,
> + false)) {
> + rc = -EINVAL;
> + goto err_free_optee;
> + }
> +
> pool = optee_ffa_shm_pool_alloc_pages();
> if (IS_ERR(pool)) {
> rc = PTR_ERR(pool);
> diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> index db9ea673fbca..acd3051c4879 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -171,6 +171,24 @@ struct optee_ffa {
>
> struct optee;
>
> +/**
> + * struct optee_revision - OP-TEE OS revision reported by secure world
> + * @os_major: OP-TEE OS major version
> + * @os_minor: OP-TEE OS minor version
> + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> + *
> + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> + * FF-A ABI version.
> + */
> +struct optee_revision {
> + u32 os_major;
> + u32 os_minor;
> + u64 os_build_id;
> +};
> +
> +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
> +
> /**
> * struct optee_ops - OP-TEE driver internal operations
> * @do_call_with_arg: enters OP-TEE in secure world
> @@ -249,6 +267,7 @@ struct optee {
> bool in_kernel_rpmb_routing;
> struct work_struct scan_bus_work;
> struct work_struct rpmb_scan_bus_work;
> + struct optee_revision revision;
> };
>
> struct optee_session {
> diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> index 0be663fcd52b..51fae1ab8ef8 100644
> --- a/drivers/tee/optee/smc_abi.c
> +++ b/drivers/tee/optee/smc_abi.c
> @@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
>
> static const struct tee_driver_ops optee_clnt_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_smc_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
>
> static const struct tee_driver_ops optee_supp_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_smc_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> }
> #endif
>
> -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> + struct optee_revision *revision)
> {
> union {
> struct arm_smccc_res smccc;
> @@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> &res.smccc);
>
> + if (revision) {
> + revision->os_major = res.result.major;
> + revision->os_minor = res.result.minor;
> + revision->os_build_id = res.result.build_id;
> + }
> +
> if (res.result.build_id)
> pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> res.result.minor, (int)sizeof(res.result.build_id) * 2,
> @@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev)
> return -EINVAL;
> }
>
> - optee_msg_get_os_revision(invoke_fn);
> -
> if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> pr_warn("api revision mismatch\n");
> return -EINVAL;
> @@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev)
> goto err_free_shm_pool;
> }
>
> + optee_msg_get_os_revision(invoke_fn, &optee->revision);
> +
> optee->ops = &optee_ops;
> optee->smc.invoke_fn = invoke_fn;
> optee->smc.sec_caps = sec_caps;
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v6 2/2] tee: optee: store OS revision for TEE core
@ 2026-01-09 11:50 ` Sumit Garg
0 siblings, 0 replies; 74+ messages in thread
From: Sumit Garg @ 2026-01-09 11:50 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, jens.wiklander, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
On Thu, Jan 08, 2026 at 02:45:09PM +0800, Aristo Chen wrote:
> Collect OP-TEE OS revision from secure world for both SMC and FF-A
> ABIs, store it in the OP-TEE driver, and expose it through the
> generic get_tee_revision() callback.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> drivers/tee/optee/core.c | 23 +++++++++++++
> drivers/tee/optee/ffa_abi.c | 57 ++++++++++++++++++++++++-------
> drivers/tee/optee/optee_private.h | 19 +++++++++++
> drivers/tee/optee/smc_abi.c | 15 ++++++--
> 4 files changed, 99 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> index 5b62139714ce..2d807bc748bc 100644
> --- a/drivers/tee/optee/core.c
> +++ b/drivers/tee/optee/core.c
> @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> }
>
> +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> +{
> + struct optee *optee = tee_get_drvdata(teedev);
> + u64 build_id;
> +
> + if (!optee)
> + return -ENODEV;
> + if (!buf || !len)
> + return -EINVAL;
> +
> + build_id = optee->revision.os_build_id;
> + if (build_id)
> + scnprintf(buf, len, "%u.%u (%016llx)",
> + optee->revision.os_major,
> + optee->revision.os_minor,
> + (unsigned long long)build_id);
> + else
> + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> + optee->revision.os_minor);
> +
> + return 0;
> +}
> +
> static void optee_bus_scan(struct work_struct *work)
> {
> WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> index bf8390789ecf..82dbed1c87e5 100644
> --- a/drivers/tee/optee/ffa_abi.c
> +++ b/drivers/tee/optee/ffa_abi.c
> @@ -775,6 +775,42 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> * with a matching configuration.
> */
>
> +static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
> + const struct ffa_ops *ops,
> + struct optee_revision *revision,
> + bool log)
> +{
> + const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> + struct ffa_send_direct_data data = {
> + .data0 = OPTEE_FFA_GET_OS_VERSION,
> + };
> + int rc;
> +
> + msg_ops->mode_32bit_set(ffa_dev);
> +
> + rc = msg_ops->sync_send_receive(ffa_dev, &data);
> + if (rc) {
> + pr_err("Unexpected error %d\n", rc);
> + return false;
> + }
> +
> + if (revision) {
> + revision->os_major = data.data0;
> + revision->os_minor = data.data1;
> + revision->os_build_id = data.data2;
> + }
> +
> + if (log) {
> + if (data.data2)
> + pr_info("revision %lu.%lu (%08lx)",
> + data.data0, data.data1, data.data2);
> + else
> + pr_info("revision %lu.%lu", data.data0, data.data1);
> + }
> +
> + return true;
> +}
> +
> static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> const struct ffa_ops *ops)
> {
> @@ -798,19 +834,8 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> return false;
> }
>
> - data = (struct ffa_send_direct_data){
> - .data0 = OPTEE_FFA_GET_OS_VERSION,
> - };
> - rc = msg_ops->sync_send_receive(ffa_dev, &data);
> - if (rc) {
> - pr_err("Unexpected error %d\n", rc);
> + if (!optee_ffa_get_os_revision(ffa_dev, ops, NULL, true))
Why do you need to invoke optee_ffa_get_os_revision() here?
-Sumit
> return false;
> - }
> - if (data.data2)
> - pr_info("revision %lu.%lu (%08lx)",
> - data.data0, data.data1, data.data2);
> - else
> - pr_info("revision %lu.%lu", data.data0, data.data1);
>
> return true;
> }
> @@ -900,6 +925,7 @@ static int optee_ffa_open(struct tee_context *ctx)
>
> static const struct tee_driver_ops optee_ffa_clnt_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -918,6 +944,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
>
> static const struct tee_driver_ops optee_ffa_supp_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1060,6 +1087,12 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> if (!optee)
> return -ENOMEM;
>
> + if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision,
> + false)) {
> + rc = -EINVAL;
> + goto err_free_optee;
> + }
> +
> pool = optee_ffa_shm_pool_alloc_pages();
> if (IS_ERR(pool)) {
> rc = PTR_ERR(pool);
> diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> index db9ea673fbca..acd3051c4879 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -171,6 +171,24 @@ struct optee_ffa {
>
> struct optee;
>
> +/**
> + * struct optee_revision - OP-TEE OS revision reported by secure world
> + * @os_major: OP-TEE OS major version
> + * @os_minor: OP-TEE OS minor version
> + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> + *
> + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> + * FF-A ABI version.
> + */
> +struct optee_revision {
> + u32 os_major;
> + u32 os_minor;
> + u64 os_build_id;
> +};
> +
> +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
> +
> /**
> * struct optee_ops - OP-TEE driver internal operations
> * @do_call_with_arg: enters OP-TEE in secure world
> @@ -249,6 +267,7 @@ struct optee {
> bool in_kernel_rpmb_routing;
> struct work_struct scan_bus_work;
> struct work_struct rpmb_scan_bus_work;
> + struct optee_revision revision;
> };
>
> struct optee_session {
> diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> index 0be663fcd52b..51fae1ab8ef8 100644
> --- a/drivers/tee/optee/smc_abi.c
> +++ b/drivers/tee/optee/smc_abi.c
> @@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
>
> static const struct tee_driver_ops optee_clnt_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_smc_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
>
> static const struct tee_driver_ops optee_supp_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_smc_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> }
> #endif
>
> -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> + struct optee_revision *revision)
> {
> union {
> struct arm_smccc_res smccc;
> @@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> &res.smccc);
>
> + if (revision) {
> + revision->os_major = res.result.major;
> + revision->os_minor = res.result.minor;
> + revision->os_build_id = res.result.build_id;
> + }
> +
> if (res.result.build_id)
> pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> res.result.minor, (int)sizeof(res.result.build_id) * 2,
> @@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev)
> return -EINVAL;
> }
>
> - optee_msg_get_os_revision(invoke_fn);
> -
> if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> pr_warn("api revision mismatch\n");
> return -EINVAL;
> @@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev)
> goto err_free_shm_pool;
> }
>
> + optee_msg_get_os_revision(invoke_fn, &optee->revision);
> +
> optee->ops = &optee_ops;
> optee->smc.invoke_fn = invoke_fn;
> optee->smc.sec_caps = sec_caps;
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v6 2/2] tee: optee: store OS revision for TEE core
2026-01-09 11:50 ` Sumit Garg
@ 2026-01-09 15:07 ` Aristo Chen
-1 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-09 15:07 UTC (permalink / raw)
To: Sumit Garg
Cc: linux-kernel, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
Hi Sumit
Sumit Garg <sumit.garg@kernel.org> 於 2026年1月9日週五 下午7:50寫道:
>
> On Thu, Jan 08, 2026 at 02:45:09PM +0800, Aristo Chen wrote:
> > Collect OP-TEE OS revision from secure world for both SMC and FF-A
> > ABIs, store it in the OP-TEE driver, and expose it through the
> > generic get_tee_revision() callback.
> >
> > Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> > ---
> > drivers/tee/optee/core.c | 23 +++++++++++++
> > drivers/tee/optee/ffa_abi.c | 57 ++++++++++++++++++++++++-------
> > drivers/tee/optee/optee_private.h | 19 +++++++++++
> > drivers/tee/optee/smc_abi.c | 15 ++++++--
> > 4 files changed, 99 insertions(+), 15 deletions(-)
> >
> > diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> > index 5b62139714ce..2d807bc748bc 100644
> > --- a/drivers/tee/optee/core.c
> > +++ b/drivers/tee/optee/core.c
> > @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> > return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> > }
> >
> > +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> > +{
> > + struct optee *optee = tee_get_drvdata(teedev);
> > + u64 build_id;
> > +
> > + if (!optee)
> > + return -ENODEV;
> > + if (!buf || !len)
> > + return -EINVAL;
> > +
> > + build_id = optee->revision.os_build_id;
> > + if (build_id)
> > + scnprintf(buf, len, "%u.%u (%016llx)",
> > + optee->revision.os_major,
> > + optee->revision.os_minor,
> > + (unsigned long long)build_id);
> > + else
> > + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> > + optee->revision.os_minor);
> > +
> > + return 0;
> > +}
> > +
> > static void optee_bus_scan(struct work_struct *work)
> > {
> > WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> > diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> > index bf8390789ecf..82dbed1c87e5 100644
> > --- a/drivers/tee/optee/ffa_abi.c
> > +++ b/drivers/tee/optee/ffa_abi.c
> > @@ -775,6 +775,42 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> > * with a matching configuration.
> > */
> >
> > +static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
> > + const struct ffa_ops *ops,
> > + struct optee_revision *revision,
> > + bool log)
> > +{
> > + const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> > + struct ffa_send_direct_data data = {
> > + .data0 = OPTEE_FFA_GET_OS_VERSION,
> > + };
> > + int rc;
> > +
> > + msg_ops->mode_32bit_set(ffa_dev);
> > +
> > + rc = msg_ops->sync_send_receive(ffa_dev, &data);
> > + if (rc) {
> > + pr_err("Unexpected error %d\n", rc);
> > + return false;
> > + }
> > +
> > + if (revision) {
> > + revision->os_major = data.data0;
> > + revision->os_minor = data.data1;
> > + revision->os_build_id = data.data2;
> > + }
> > +
> > + if (log) {
> > + if (data.data2)
> > + pr_info("revision %lu.%lu (%08lx)",
> > + data.data0, data.data1, data.data2);
> > + else
> > + pr_info("revision %lu.%lu", data.data0, data.data1);
> > + }
> > +
> > + return true;
> > +}
> > +
> > static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > const struct ffa_ops *ops)
> > {
> > @@ -798,19 +834,8 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > return false;
> > }
> >
> > - data = (struct ffa_send_direct_data){
> > - .data0 = OPTEE_FFA_GET_OS_VERSION,
> > - };
> > - rc = msg_ops->sync_send_receive(ffa_dev, &data);
> > - if (rc) {
> > - pr_err("Unexpected error %d\n", rc);
> > + if (!optee_ffa_get_os_revision(ffa_dev, ops, NULL, true))
>
> Why do you need to invoke optee_ffa_get_os_revision() here?
I’m calling optee_ffa_get_os_revision() here to avoid duplicating the
GET_OS_VERSION call/printing logic. The original code in
optee_ffa_api_is_compatible() already did the OS version query and
printed it; after factoring it out, the helper keeps that behavior in one
place. If we don’t call it there, we’d either lose the existing dmesg log
or re‑implement the same OS‑version query/print block again, which I’m
trying to avoid.
If you’d rather avoid the extra call at this stage, maybe we can always
print the log in the optee_ffa_get_os_revision() and remove the call
from optee_ffa_api_is_compatible() entirely.
>
> -Sumit
>
> > return false;
> > - }
> > - if (data.data2)
> > - pr_info("revision %lu.%lu (%08lx)",
> > - data.data0, data.data1, data.data2);
> > - else
> > - pr_info("revision %lu.%lu", data.data0, data.data1);
> >
> > return true;
> > }
> > @@ -900,6 +925,7 @@ static int optee_ffa_open(struct tee_context *ctx)
> >
> > static const struct tee_driver_ops optee_ffa_clnt_ops = {
> > .get_version = optee_ffa_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_ffa_open,
> > .release = optee_release,
> > .open_session = optee_open_session,
> > @@ -918,6 +944,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
> >
> > static const struct tee_driver_ops optee_ffa_supp_ops = {
> > .get_version = optee_ffa_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_ffa_open,
> > .release = optee_release_supp,
> > .supp_recv = optee_supp_recv,
> > @@ -1060,6 +1087,12 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> > if (!optee)
> > return -ENOMEM;
> >
> > + if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision,
> > + false)) {
> > + rc = -EINVAL;
> > + goto err_free_optee;
> > + }
> > +
> > pool = optee_ffa_shm_pool_alloc_pages();
> > if (IS_ERR(pool)) {
> > rc = PTR_ERR(pool);
> > diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> > index db9ea673fbca..acd3051c4879 100644
> > --- a/drivers/tee/optee/optee_private.h
> > +++ b/drivers/tee/optee/optee_private.h
> > @@ -171,6 +171,24 @@ struct optee_ffa {
> >
> > struct optee;
> >
> > +/**
> > + * struct optee_revision - OP-TEE OS revision reported by secure world
> > + * @os_major: OP-TEE OS major version
> > + * @os_minor: OP-TEE OS minor version
> > + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> > + *
> > + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> > + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> > + * FF-A ABI version.
> > + */
> > +struct optee_revision {
> > + u32 os_major;
> > + u32 os_minor;
> > + u64 os_build_id;
> > +};
> > +
> > +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
> > +
> > /**
> > * struct optee_ops - OP-TEE driver internal operations
> > * @do_call_with_arg: enters OP-TEE in secure world
> > @@ -249,6 +267,7 @@ struct optee {
> > bool in_kernel_rpmb_routing;
> > struct work_struct scan_bus_work;
> > struct work_struct rpmb_scan_bus_work;
> > + struct optee_revision revision;
> > };
> >
> > struct optee_session {
> > diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> > index 0be663fcd52b..51fae1ab8ef8 100644
> > --- a/drivers/tee/optee/smc_abi.c
> > +++ b/drivers/tee/optee/smc_abi.c
> > @@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
> >
> > static const struct tee_driver_ops optee_clnt_ops = {
> > .get_version = optee_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_smc_open,
> > .release = optee_release,
> > .open_session = optee_open_session,
> > @@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
> >
> > static const struct tee_driver_ops optee_supp_ops = {
> > .get_version = optee_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_smc_open,
> > .release = optee_release_supp,
> > .supp_recv = optee_supp_recv,
> > @@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> > }
> > #endif
> >
> > -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> > +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> > + struct optee_revision *revision)
> > {
> > union {
> > struct arm_smccc_res smccc;
> > @@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> > invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> > &res.smccc);
> >
> > + if (revision) {
> > + revision->os_major = res.result.major;
> > + revision->os_minor = res.result.minor;
> > + revision->os_build_id = res.result.build_id;
> > + }
> > +
> > if (res.result.build_id)
> > pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> > res.result.minor, (int)sizeof(res.result.build_id) * 2,
> > @@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev)
> > return -EINVAL;
> > }
> >
> > - optee_msg_get_os_revision(invoke_fn);
> > -
> > if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> > pr_warn("api revision mismatch\n");
> > return -EINVAL;
> > @@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev)
> > goto err_free_shm_pool;
> > }
> >
> > + optee_msg_get_os_revision(invoke_fn, &optee->revision);
> > +
> > optee->ops = &optee_ops;
> > optee->smc.invoke_fn = invoke_fn;
> > optee->smc.sec_caps = sec_caps;
> > --
> > 2.43.0
> >
Best regards,
Aristo
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v6 2/2] tee: optee: store OS revision for TEE core
@ 2026-01-09 15:07 ` Aristo Chen
0 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-09 15:07 UTC (permalink / raw)
To: Sumit Garg
Cc: linux-kernel, jens.wiklander, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
Hi Sumit
Sumit Garg <sumit.garg@kernel.org> 於 2026年1月9日週五 下午7:50寫道:
>
> On Thu, Jan 08, 2026 at 02:45:09PM +0800, Aristo Chen wrote:
> > Collect OP-TEE OS revision from secure world for both SMC and FF-A
> > ABIs, store it in the OP-TEE driver, and expose it through the
> > generic get_tee_revision() callback.
> >
> > Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> > ---
> > drivers/tee/optee/core.c | 23 +++++++++++++
> > drivers/tee/optee/ffa_abi.c | 57 ++++++++++++++++++++++++-------
> > drivers/tee/optee/optee_private.h | 19 +++++++++++
> > drivers/tee/optee/smc_abi.c | 15 ++++++--
> > 4 files changed, 99 insertions(+), 15 deletions(-)
> >
> > diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> > index 5b62139714ce..2d807bc748bc 100644
> > --- a/drivers/tee/optee/core.c
> > +++ b/drivers/tee/optee/core.c
> > @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> > return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> > }
> >
> > +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> > +{
> > + struct optee *optee = tee_get_drvdata(teedev);
> > + u64 build_id;
> > +
> > + if (!optee)
> > + return -ENODEV;
> > + if (!buf || !len)
> > + return -EINVAL;
> > +
> > + build_id = optee->revision.os_build_id;
> > + if (build_id)
> > + scnprintf(buf, len, "%u.%u (%016llx)",
> > + optee->revision.os_major,
> > + optee->revision.os_minor,
> > + (unsigned long long)build_id);
> > + else
> > + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> > + optee->revision.os_minor);
> > +
> > + return 0;
> > +}
> > +
> > static void optee_bus_scan(struct work_struct *work)
> > {
> > WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> > diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> > index bf8390789ecf..82dbed1c87e5 100644
> > --- a/drivers/tee/optee/ffa_abi.c
> > +++ b/drivers/tee/optee/ffa_abi.c
> > @@ -775,6 +775,42 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> > * with a matching configuration.
> > */
> >
> > +static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
> > + const struct ffa_ops *ops,
> > + struct optee_revision *revision,
> > + bool log)
> > +{
> > + const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> > + struct ffa_send_direct_data data = {
> > + .data0 = OPTEE_FFA_GET_OS_VERSION,
> > + };
> > + int rc;
> > +
> > + msg_ops->mode_32bit_set(ffa_dev);
> > +
> > + rc = msg_ops->sync_send_receive(ffa_dev, &data);
> > + if (rc) {
> > + pr_err("Unexpected error %d\n", rc);
> > + return false;
> > + }
> > +
> > + if (revision) {
> > + revision->os_major = data.data0;
> > + revision->os_minor = data.data1;
> > + revision->os_build_id = data.data2;
> > + }
> > +
> > + if (log) {
> > + if (data.data2)
> > + pr_info("revision %lu.%lu (%08lx)",
> > + data.data0, data.data1, data.data2);
> > + else
> > + pr_info("revision %lu.%lu", data.data0, data.data1);
> > + }
> > +
> > + return true;
> > +}
> > +
> > static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > const struct ffa_ops *ops)
> > {
> > @@ -798,19 +834,8 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > return false;
> > }
> >
> > - data = (struct ffa_send_direct_data){
> > - .data0 = OPTEE_FFA_GET_OS_VERSION,
> > - };
> > - rc = msg_ops->sync_send_receive(ffa_dev, &data);
> > - if (rc) {
> > - pr_err("Unexpected error %d\n", rc);
> > + if (!optee_ffa_get_os_revision(ffa_dev, ops, NULL, true))
>
> Why do you need to invoke optee_ffa_get_os_revision() here?
I’m calling optee_ffa_get_os_revision() here to avoid duplicating the
GET_OS_VERSION call/printing logic. The original code in
optee_ffa_api_is_compatible() already did the OS version query and
printed it; after factoring it out, the helper keeps that behavior in one
place. If we don’t call it there, we’d either lose the existing dmesg log
or re‑implement the same OS‑version query/print block again, which I’m
trying to avoid.
If you’d rather avoid the extra call at this stage, maybe we can always
print the log in the optee_ffa_get_os_revision() and remove the call
from optee_ffa_api_is_compatible() entirely.
>
> -Sumit
>
> > return false;
> > - }
> > - if (data.data2)
> > - pr_info("revision %lu.%lu (%08lx)",
> > - data.data0, data.data1, data.data2);
> > - else
> > - pr_info("revision %lu.%lu", data.data0, data.data1);
> >
> > return true;
> > }
> > @@ -900,6 +925,7 @@ static int optee_ffa_open(struct tee_context *ctx)
> >
> > static const struct tee_driver_ops optee_ffa_clnt_ops = {
> > .get_version = optee_ffa_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_ffa_open,
> > .release = optee_release,
> > .open_session = optee_open_session,
> > @@ -918,6 +944,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
> >
> > static const struct tee_driver_ops optee_ffa_supp_ops = {
> > .get_version = optee_ffa_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_ffa_open,
> > .release = optee_release_supp,
> > .supp_recv = optee_supp_recv,
> > @@ -1060,6 +1087,12 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> > if (!optee)
> > return -ENOMEM;
> >
> > + if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision,
> > + false)) {
> > + rc = -EINVAL;
> > + goto err_free_optee;
> > + }
> > +
> > pool = optee_ffa_shm_pool_alloc_pages();
> > if (IS_ERR(pool)) {
> > rc = PTR_ERR(pool);
> > diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> > index db9ea673fbca..acd3051c4879 100644
> > --- a/drivers/tee/optee/optee_private.h
> > +++ b/drivers/tee/optee/optee_private.h
> > @@ -171,6 +171,24 @@ struct optee_ffa {
> >
> > struct optee;
> >
> > +/**
> > + * struct optee_revision - OP-TEE OS revision reported by secure world
> > + * @os_major: OP-TEE OS major version
> > + * @os_minor: OP-TEE OS minor version
> > + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> > + *
> > + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> > + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> > + * FF-A ABI version.
> > + */
> > +struct optee_revision {
> > + u32 os_major;
> > + u32 os_minor;
> > + u64 os_build_id;
> > +};
> > +
> > +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
> > +
> > /**
> > * struct optee_ops - OP-TEE driver internal operations
> > * @do_call_with_arg: enters OP-TEE in secure world
> > @@ -249,6 +267,7 @@ struct optee {
> > bool in_kernel_rpmb_routing;
> > struct work_struct scan_bus_work;
> > struct work_struct rpmb_scan_bus_work;
> > + struct optee_revision revision;
> > };
> >
> > struct optee_session {
> > diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> > index 0be663fcd52b..51fae1ab8ef8 100644
> > --- a/drivers/tee/optee/smc_abi.c
> > +++ b/drivers/tee/optee/smc_abi.c
> > @@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
> >
> > static const struct tee_driver_ops optee_clnt_ops = {
> > .get_version = optee_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_smc_open,
> > .release = optee_release,
> > .open_session = optee_open_session,
> > @@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
> >
> > static const struct tee_driver_ops optee_supp_ops = {
> > .get_version = optee_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_smc_open,
> > .release = optee_release_supp,
> > .supp_recv = optee_supp_recv,
> > @@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> > }
> > #endif
> >
> > -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> > +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> > + struct optee_revision *revision)
> > {
> > union {
> > struct arm_smccc_res smccc;
> > @@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> > invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> > &res.smccc);
> >
> > + if (revision) {
> > + revision->os_major = res.result.major;
> > + revision->os_minor = res.result.minor;
> > + revision->os_build_id = res.result.build_id;
> > + }
> > +
> > if (res.result.build_id)
> > pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> > res.result.minor, (int)sizeof(res.result.build_id) * 2,
> > @@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev)
> > return -EINVAL;
> > }
> >
> > - optee_msg_get_os_revision(invoke_fn);
> > -
> > if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> > pr_warn("api revision mismatch\n");
> > return -EINVAL;
> > @@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev)
> > goto err_free_shm_pool;
> > }
> >
> > + optee_msg_get_os_revision(invoke_fn, &optee->revision);
> > +
> > optee->ops = &optee_ops;
> > optee->smc.invoke_fn = invoke_fn;
> > optee->smc.sec_caps = sec_caps;
> > --
> > 2.43.0
> >
Best regards,
Aristo
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v6 2/2] tee: optee: store OS revision for TEE core
2026-01-09 15:07 ` Aristo Chen
@ 2026-01-12 11:22 ` Sumit Garg
-1 siblings, 0 replies; 74+ messages in thread
From: Sumit Garg via OP-TEE @ 2026-01-12 11:22 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
On Fri, Jan 09, 2026 at 11:07:40PM +0800, Aristo Chen wrote:
> Hi Sumit
>
> Sumit Garg <sumit.garg@kernel.org> 於 2026年1月9日週五 下午7:50寫道:
> >
> > On Thu, Jan 08, 2026 at 02:45:09PM +0800, Aristo Chen wrote:
> > > Collect OP-TEE OS revision from secure world for both SMC and FF-A
> > > ABIs, store it in the OP-TEE driver, and expose it through the
> > > generic get_tee_revision() callback.
> > >
> > > Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> > > ---
> > > drivers/tee/optee/core.c | 23 +++++++++++++
> > > drivers/tee/optee/ffa_abi.c | 57 ++++++++++++++++++++++++-------
> > > drivers/tee/optee/optee_private.h | 19 +++++++++++
> > > drivers/tee/optee/smc_abi.c | 15 ++++++--
> > > 4 files changed, 99 insertions(+), 15 deletions(-)
> > >
> > > diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> > > index 5b62139714ce..2d807bc748bc 100644
> > > --- a/drivers/tee/optee/core.c
> > > +++ b/drivers/tee/optee/core.c
> > > @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> > > return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> > > }
> > >
> > > +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> > > +{
> > > + struct optee *optee = tee_get_drvdata(teedev);
> > > + u64 build_id;
> > > +
> > > + if (!optee)
> > > + return -ENODEV;
> > > + if (!buf || !len)
> > > + return -EINVAL;
> > > +
> > > + build_id = optee->revision.os_build_id;
> > > + if (build_id)
> > > + scnprintf(buf, len, "%u.%u (%016llx)",
> > > + optee->revision.os_major,
> > > + optee->revision.os_minor,
> > > + (unsigned long long)build_id);
> > > + else
> > > + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> > > + optee->revision.os_minor);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > static void optee_bus_scan(struct work_struct *work)
> > > {
> > > WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> > > diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> > > index bf8390789ecf..82dbed1c87e5 100644
> > > --- a/drivers/tee/optee/ffa_abi.c
> > > +++ b/drivers/tee/optee/ffa_abi.c
> > > @@ -775,6 +775,42 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> > > * with a matching configuration.
> > > */
> > >
> > > +static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
> > > + const struct ffa_ops *ops,
> > > + struct optee_revision *revision,
> > > + bool log)
> > > +{
> > > + const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> > > + struct ffa_send_direct_data data = {
> > > + .data0 = OPTEE_FFA_GET_OS_VERSION,
> > > + };
> > > + int rc;
> > > +
> > > + msg_ops->mode_32bit_set(ffa_dev);
> > > +
> > > + rc = msg_ops->sync_send_receive(ffa_dev, &data);
> > > + if (rc) {
> > > + pr_err("Unexpected error %d\n", rc);
> > > + return false;
> > > + }
> > > +
> > > + if (revision) {
> > > + revision->os_major = data.data0;
> > > + revision->os_minor = data.data1;
> > > + revision->os_build_id = data.data2;
> > > + }
> > > +
> > > + if (log) {
> > > + if (data.data2)
> > > + pr_info("revision %lu.%lu (%08lx)",
> > > + data.data0, data.data1, data.data2);
> > > + else
> > > + pr_info("revision %lu.%lu", data.data0, data.data1);
> > > + }
> > > +
> > > + return true;
> > > +}
> > > +
> > > static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > > const struct ffa_ops *ops)
> > > {
> > > @@ -798,19 +834,8 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > > return false;
> > > }
> > >
> > > - data = (struct ffa_send_direct_data){
> > > - .data0 = OPTEE_FFA_GET_OS_VERSION,
> > > - };
> > > - rc = msg_ops->sync_send_receive(ffa_dev, &data);
> > > - if (rc) {
> > > - pr_err("Unexpected error %d\n", rc);
> > > + if (!optee_ffa_get_os_revision(ffa_dev, ops, NULL, true))
> >
> > Why do you need to invoke optee_ffa_get_os_revision() here?
>
> I’m calling optee_ffa_get_os_revision() here to avoid duplicating the
> GET_OS_VERSION call/printing logic. The original code in
> optee_ffa_api_is_compatible() already did the OS version query and
> printed it; after factoring it out, the helper keeps that behavior in one
> place. If we don’t call it there, we’d either lose the existing dmesg log
> or re‑implement the same OS‑version query/print block again, which I’m
> trying to avoid.
>
> If you’d rather avoid the extra call at this stage, maybe we can always
> print the log in the optee_ffa_get_os_revision() and remove the call
> from optee_ffa_api_is_compatible() entirely.
Yeah move the print to optee_ffa_get_os_revision() and drop the extra
call.
-Sumit
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v6 2/2] tee: optee: store OS revision for TEE core
@ 2026-01-12 11:22 ` Sumit Garg
0 siblings, 0 replies; 74+ messages in thread
From: Sumit Garg @ 2026-01-12 11:22 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, jens.wiklander, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
On Fri, Jan 09, 2026 at 11:07:40PM +0800, Aristo Chen wrote:
> Hi Sumit
>
> Sumit Garg <sumit.garg@kernel.org> 於 2026年1月9日週五 下午7:50寫道:
> >
> > On Thu, Jan 08, 2026 at 02:45:09PM +0800, Aristo Chen wrote:
> > > Collect OP-TEE OS revision from secure world for both SMC and FF-A
> > > ABIs, store it in the OP-TEE driver, and expose it through the
> > > generic get_tee_revision() callback.
> > >
> > > Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> > > ---
> > > drivers/tee/optee/core.c | 23 +++++++++++++
> > > drivers/tee/optee/ffa_abi.c | 57 ++++++++++++++++++++++++-------
> > > drivers/tee/optee/optee_private.h | 19 +++++++++++
> > > drivers/tee/optee/smc_abi.c | 15 ++++++--
> > > 4 files changed, 99 insertions(+), 15 deletions(-)
> > >
> > > diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> > > index 5b62139714ce..2d807bc748bc 100644
> > > --- a/drivers/tee/optee/core.c
> > > +++ b/drivers/tee/optee/core.c
> > > @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> > > return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> > > }
> > >
> > > +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> > > +{
> > > + struct optee *optee = tee_get_drvdata(teedev);
> > > + u64 build_id;
> > > +
> > > + if (!optee)
> > > + return -ENODEV;
> > > + if (!buf || !len)
> > > + return -EINVAL;
> > > +
> > > + build_id = optee->revision.os_build_id;
> > > + if (build_id)
> > > + scnprintf(buf, len, "%u.%u (%016llx)",
> > > + optee->revision.os_major,
> > > + optee->revision.os_minor,
> > > + (unsigned long long)build_id);
> > > + else
> > > + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> > > + optee->revision.os_minor);
> > > +
> > > + return 0;
> > > +}
> > > +
> > > static void optee_bus_scan(struct work_struct *work)
> > > {
> > > WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> > > diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> > > index bf8390789ecf..82dbed1c87e5 100644
> > > --- a/drivers/tee/optee/ffa_abi.c
> > > +++ b/drivers/tee/optee/ffa_abi.c
> > > @@ -775,6 +775,42 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> > > * with a matching configuration.
> > > */
> > >
> > > +static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
> > > + const struct ffa_ops *ops,
> > > + struct optee_revision *revision,
> > > + bool log)
> > > +{
> > > + const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> > > + struct ffa_send_direct_data data = {
> > > + .data0 = OPTEE_FFA_GET_OS_VERSION,
> > > + };
> > > + int rc;
> > > +
> > > + msg_ops->mode_32bit_set(ffa_dev);
> > > +
> > > + rc = msg_ops->sync_send_receive(ffa_dev, &data);
> > > + if (rc) {
> > > + pr_err("Unexpected error %d\n", rc);
> > > + return false;
> > > + }
> > > +
> > > + if (revision) {
> > > + revision->os_major = data.data0;
> > > + revision->os_minor = data.data1;
> > > + revision->os_build_id = data.data2;
> > > + }
> > > +
> > > + if (log) {
> > > + if (data.data2)
> > > + pr_info("revision %lu.%lu (%08lx)",
> > > + data.data0, data.data1, data.data2);
> > > + else
> > > + pr_info("revision %lu.%lu", data.data0, data.data1);
> > > + }
> > > +
> > > + return true;
> > > +}
> > > +
> > > static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > > const struct ffa_ops *ops)
> > > {
> > > @@ -798,19 +834,8 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > > return false;
> > > }
> > >
> > > - data = (struct ffa_send_direct_data){
> > > - .data0 = OPTEE_FFA_GET_OS_VERSION,
> > > - };
> > > - rc = msg_ops->sync_send_receive(ffa_dev, &data);
> > > - if (rc) {
> > > - pr_err("Unexpected error %d\n", rc);
> > > + if (!optee_ffa_get_os_revision(ffa_dev, ops, NULL, true))
> >
> > Why do you need to invoke optee_ffa_get_os_revision() here?
>
> I’m calling optee_ffa_get_os_revision() here to avoid duplicating the
> GET_OS_VERSION call/printing logic. The original code in
> optee_ffa_api_is_compatible() already did the OS version query and
> printed it; after factoring it out, the helper keeps that behavior in one
> place. If we don’t call it there, we’d either lose the existing dmesg log
> or re‑implement the same OS‑version query/print block again, which I’m
> trying to avoid.
>
> If you’d rather avoid the extra call at this stage, maybe we can always
> print the log in the optee_ffa_get_os_revision() and remove the call
> from optee_ffa_api_is_compatible() entirely.
Yeah move the print to optee_ffa_get_os_revision() and drop the extra
call.
-Sumit
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v6 2/2] tee: optee: store OS revision for TEE core
2026-01-08 6:45 ` Aristo Chen
(?)
(?)
@ 2026-01-12 9:55 ` Jens Wiklander
2026-01-12 10:43 ` Aristo Chen
-1 siblings, 1 reply; 74+ messages in thread
From: Jens Wiklander @ 2026-01-12 9:55 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, sumit.garg, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
On Thu, Jan 08, 2026 at 02:45:09PM +0800, Aristo Chen wrote:
> Collect OP-TEE OS revision from secure world for both SMC and FF-A
> ABIs, store it in the OP-TEE driver, and expose it through the
> generic get_tee_revision() callback.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> drivers/tee/optee/core.c | 23 +++++++++++++
> drivers/tee/optee/ffa_abi.c | 57 ++++++++++++++++++++++++-------
> drivers/tee/optee/optee_private.h | 19 +++++++++++
> drivers/tee/optee/smc_abi.c | 15 ++++++--
> 4 files changed, 99 insertions(+), 15 deletions(-)
Checkpatch complains with:
CHECK: Alignment should match open parenthesis
#62: FILE: drivers/tee/optee/ffa_abi.c:779:
+static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
+ const struct ffa_ops *ops,
CHECK: Alignment should match open parenthesis
#142: FILE: drivers/tee/optee/ffa_abi.c:1091:
+ if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision,
+ false)) {
WARNING: From:/Signed-off-by: email address mismatch: 'From: Aristo Chen <jj251510319013@gmail.com>' != 'Signed-off-by: Aristo Chen <aristo.chen@canonical.com>'
total: 0 errors, 1 warnings, 2 checks, 199 lines checked
Cheers,
Jens
>
> diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> index 5b62139714ce..2d807bc748bc 100644
> --- a/drivers/tee/optee/core.c
> +++ b/drivers/tee/optee/core.c
> @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> }
>
> +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> +{
> + struct optee *optee = tee_get_drvdata(teedev);
> + u64 build_id;
> +
> + if (!optee)
> + return -ENODEV;
> + if (!buf || !len)
> + return -EINVAL;
> +
> + build_id = optee->revision.os_build_id;
> + if (build_id)
> + scnprintf(buf, len, "%u.%u (%016llx)",
> + optee->revision.os_major,
> + optee->revision.os_minor,
> + (unsigned long long)build_id);
> + else
> + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> + optee->revision.os_minor);
> +
> + return 0;
> +}
> +
> static void optee_bus_scan(struct work_struct *work)
> {
> WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> index bf8390789ecf..82dbed1c87e5 100644
> --- a/drivers/tee/optee/ffa_abi.c
> +++ b/drivers/tee/optee/ffa_abi.c
> @@ -775,6 +775,42 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> * with a matching configuration.
> */
>
> +static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
> + const struct ffa_ops *ops,
> + struct optee_revision *revision,
> + bool log)
> +{
> + const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> + struct ffa_send_direct_data data = {
> + .data0 = OPTEE_FFA_GET_OS_VERSION,
> + };
> + int rc;
> +
> + msg_ops->mode_32bit_set(ffa_dev);
> +
> + rc = msg_ops->sync_send_receive(ffa_dev, &data);
> + if (rc) {
> + pr_err("Unexpected error %d\n", rc);
> + return false;
> + }
> +
> + if (revision) {
> + revision->os_major = data.data0;
> + revision->os_minor = data.data1;
> + revision->os_build_id = data.data2;
> + }
> +
> + if (log) {
> + if (data.data2)
> + pr_info("revision %lu.%lu (%08lx)",
> + data.data0, data.data1, data.data2);
> + else
> + pr_info("revision %lu.%lu", data.data0, data.data1);
> + }
> +
> + return true;
> +}
> +
> static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> const struct ffa_ops *ops)
> {
> @@ -798,19 +834,8 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> return false;
> }
>
> - data = (struct ffa_send_direct_data){
> - .data0 = OPTEE_FFA_GET_OS_VERSION,
> - };
> - rc = msg_ops->sync_send_receive(ffa_dev, &data);
> - if (rc) {
> - pr_err("Unexpected error %d\n", rc);
> + if (!optee_ffa_get_os_revision(ffa_dev, ops, NULL, true))
> return false;
> - }
> - if (data.data2)
> - pr_info("revision %lu.%lu (%08lx)",
> - data.data0, data.data1, data.data2);
> - else
> - pr_info("revision %lu.%lu", data.data0, data.data1);
>
> return true;
> }
> @@ -900,6 +925,7 @@ static int optee_ffa_open(struct tee_context *ctx)
>
> static const struct tee_driver_ops optee_ffa_clnt_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -918,6 +944,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
>
> static const struct tee_driver_ops optee_ffa_supp_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1060,6 +1087,12 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> if (!optee)
> return -ENOMEM;
>
> + if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision,
> + false)) {
> + rc = -EINVAL;
> + goto err_free_optee;
> + }
> +
> pool = optee_ffa_shm_pool_alloc_pages();
> if (IS_ERR(pool)) {
> rc = PTR_ERR(pool);
> diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> index db9ea673fbca..acd3051c4879 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -171,6 +171,24 @@ struct optee_ffa {
>
> struct optee;
>
> +/**
> + * struct optee_revision - OP-TEE OS revision reported by secure world
> + * @os_major: OP-TEE OS major version
> + * @os_minor: OP-TEE OS minor version
> + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> + *
> + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> + * FF-A ABI version.
> + */
> +struct optee_revision {
> + u32 os_major;
> + u32 os_minor;
> + u64 os_build_id;
> +};
> +
> +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
> +
> /**
> * struct optee_ops - OP-TEE driver internal operations
> * @do_call_with_arg: enters OP-TEE in secure world
> @@ -249,6 +267,7 @@ struct optee {
> bool in_kernel_rpmb_routing;
> struct work_struct scan_bus_work;
> struct work_struct rpmb_scan_bus_work;
> + struct optee_revision revision;
> };
>
> struct optee_session {
> diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> index 0be663fcd52b..51fae1ab8ef8 100644
> --- a/drivers/tee/optee/smc_abi.c
> +++ b/drivers/tee/optee/smc_abi.c
> @@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
>
> static const struct tee_driver_ops optee_clnt_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_smc_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
>
> static const struct tee_driver_ops optee_supp_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_smc_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> }
> #endif
>
> -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> + struct optee_revision *revision)
> {
> union {
> struct arm_smccc_res smccc;
> @@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> &res.smccc);
>
> + if (revision) {
> + revision->os_major = res.result.major;
> + revision->os_minor = res.result.minor;
> + revision->os_build_id = res.result.build_id;
> + }
> +
> if (res.result.build_id)
> pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> res.result.minor, (int)sizeof(res.result.build_id) * 2,
> @@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev)
> return -EINVAL;
> }
>
> - optee_msg_get_os_revision(invoke_fn);
> -
> if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> pr_warn("api revision mismatch\n");
> return -EINVAL;
> @@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev)
> goto err_free_shm_pool;
> }
>
> + optee_msg_get_os_revision(invoke_fn, &optee->revision);
> +
> optee->ops = &optee_ops;
> optee->smc.invoke_fn = invoke_fn;
> optee->smc.sec_caps = sec_caps;
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v6 2/2] tee: optee: store OS revision for TEE core
2026-01-12 9:55 ` Jens Wiklander
@ 2026-01-12 10:43 ` Aristo Chen
0 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-12 10:43 UTC (permalink / raw)
To: Jens Wiklander
Cc: linux-kernel, sumit.garg, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
Hi Jens,
Jens Wiklander <jens.wiklander@linaro.org> 於 2026年1月12日週一 下午5:55寫道:
>
> On Thu, Jan 08, 2026 at 02:45:09PM +0800, Aristo Chen wrote:
> > Collect OP-TEE OS revision from secure world for both SMC and FF-A
> > ABIs, store it in the OP-TEE driver, and expose it through the
> > generic get_tee_revision() callback.
> >
> > Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> > ---
> > drivers/tee/optee/core.c | 23 +++++++++++++
> > drivers/tee/optee/ffa_abi.c | 57 ++++++++++++++++++++++++-------
> > drivers/tee/optee/optee_private.h | 19 +++++++++++
> > drivers/tee/optee/smc_abi.c | 15 ++++++--
> > 4 files changed, 99 insertions(+), 15 deletions(-)
>
> Checkpatch complains with:
> CHECK: Alignment should match open parenthesis
> #62: FILE: drivers/tee/optee/ffa_abi.c:779:
> +static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
> + const struct ffa_ops *ops,
>
> CHECK: Alignment should match open parenthesis
> #142: FILE: drivers/tee/optee/ffa_abi.c:1091:
> + if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision,
> + false)) {
>
> WARNING: From:/Signed-off-by: email address mismatch: 'From: Aristo Chen <jj251510319013@gmail.com>' != 'Signed-off-by: Aristo Chen <aristo.chen@canonical.com>'
>
> total: 0 errors, 1 warnings, 2 checks, 199 lines checked
Sorry for causing the inconvenience, somehow I did not get complained
by the checkpatch.pl
and I will fix the warnings in v7
just out of curiosity and trying to prevent from making the same
mistake again, are you using
command like this?
```
$ ./scripts/checkpatch.pl v6_patch/*
-------------------------------------------------------
v6_patch/v6-0001-tee-add-revision-sysfs-attribute.patch
-------------------------------------------------------
total: 0 errors, 0 warnings, 100 lines checked
v6_patch/v6-0001-tee-add-revision-sysfs-attribute.patch has no obvious
style problems and is ready for submission.
---------------------------------------------------------------
v6_patch/v6-0002-tee-optee-store-OS-revision-for-TEE-core.patch
---------------------------------------------------------------
total: 0 errors, 0 warnings, 199 lines checked
v6_patch/v6-0002-tee-optee-store-OS-revision-for-TEE-core.patch has no
obvious style problems and is ready for submission.
```
As you can see, I did not see any complain from the output, so I was
assuming they are
in a good shape.
>
> Cheers,
> Jens
>
> >
> > diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> > index 5b62139714ce..2d807bc748bc 100644
> > --- a/drivers/tee/optee/core.c
> > +++ b/drivers/tee/optee/core.c
> > @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> > return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> > }
> >
> > +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> > +{
> > + struct optee *optee = tee_get_drvdata(teedev);
> > + u64 build_id;
> > +
> > + if (!optee)
> > + return -ENODEV;
> > + if (!buf || !len)
> > + return -EINVAL;
> > +
> > + build_id = optee->revision.os_build_id;
> > + if (build_id)
> > + scnprintf(buf, len, "%u.%u (%016llx)",
> > + optee->revision.os_major,
> > + optee->revision.os_minor,
> > + (unsigned long long)build_id);
> > + else
> > + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> > + optee->revision.os_minor);
> > +
> > + return 0;
> > +}
> > +
> > static void optee_bus_scan(struct work_struct *work)
> > {
> > WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> > diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> > index bf8390789ecf..82dbed1c87e5 100644
> > --- a/drivers/tee/optee/ffa_abi.c
> > +++ b/drivers/tee/optee/ffa_abi.c
> > @@ -775,6 +775,42 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> > * with a matching configuration.
> > */
> >
> > +static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
> > + const struct ffa_ops *ops,
> > + struct optee_revision *revision,
> > + bool log)
> > +{
> > + const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> > + struct ffa_send_direct_data data = {
> > + .data0 = OPTEE_FFA_GET_OS_VERSION,
> > + };
> > + int rc;
> > +
> > + msg_ops->mode_32bit_set(ffa_dev);
> > +
> > + rc = msg_ops->sync_send_receive(ffa_dev, &data);
> > + if (rc) {
> > + pr_err("Unexpected error %d\n", rc);
> > + return false;
> > + }
> > +
> > + if (revision) {
> > + revision->os_major = data.data0;
> > + revision->os_minor = data.data1;
> > + revision->os_build_id = data.data2;
> > + }
> > +
> > + if (log) {
> > + if (data.data2)
> > + pr_info("revision %lu.%lu (%08lx)",
> > + data.data0, data.data1, data.data2);
> > + else
> > + pr_info("revision %lu.%lu", data.data0, data.data1);
> > + }
> > +
> > + return true;
> > +}
> > +
> > static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > const struct ffa_ops *ops)
> > {
> > @@ -798,19 +834,8 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> > return false;
> > }
> >
> > - data = (struct ffa_send_direct_data){
> > - .data0 = OPTEE_FFA_GET_OS_VERSION,
> > - };
> > - rc = msg_ops->sync_send_receive(ffa_dev, &data);
> > - if (rc) {
> > - pr_err("Unexpected error %d\n", rc);
> > + if (!optee_ffa_get_os_revision(ffa_dev, ops, NULL, true))
> > return false;
> > - }
> > - if (data.data2)
> > - pr_info("revision %lu.%lu (%08lx)",
> > - data.data0, data.data1, data.data2);
> > - else
> > - pr_info("revision %lu.%lu", data.data0, data.data1);
> >
> > return true;
> > }
> > @@ -900,6 +925,7 @@ static int optee_ffa_open(struct tee_context *ctx)
> >
> > static const struct tee_driver_ops optee_ffa_clnt_ops = {
> > .get_version = optee_ffa_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_ffa_open,
> > .release = optee_release,
> > .open_session = optee_open_session,
> > @@ -918,6 +944,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
> >
> > static const struct tee_driver_ops optee_ffa_supp_ops = {
> > .get_version = optee_ffa_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_ffa_open,
> > .release = optee_release_supp,
> > .supp_recv = optee_supp_recv,
> > @@ -1060,6 +1087,12 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> > if (!optee)
> > return -ENOMEM;
> >
> > + if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision,
> > + false)) {
> > + rc = -EINVAL;
> > + goto err_free_optee;
> > + }
> > +
> > pool = optee_ffa_shm_pool_alloc_pages();
> > if (IS_ERR(pool)) {
> > rc = PTR_ERR(pool);
> > diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> > index db9ea673fbca..acd3051c4879 100644
> > --- a/drivers/tee/optee/optee_private.h
> > +++ b/drivers/tee/optee/optee_private.h
> > @@ -171,6 +171,24 @@ struct optee_ffa {
> >
> > struct optee;
> >
> > +/**
> > + * struct optee_revision - OP-TEE OS revision reported by secure world
> > + * @os_major: OP-TEE OS major version
> > + * @os_minor: OP-TEE OS minor version
> > + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> > + *
> > + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> > + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> > + * FF-A ABI version.
> > + */
> > +struct optee_revision {
> > + u32 os_major;
> > + u32 os_minor;
> > + u64 os_build_id;
> > +};
> > +
> > +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
> > +
> > /**
> > * struct optee_ops - OP-TEE driver internal operations
> > * @do_call_with_arg: enters OP-TEE in secure world
> > @@ -249,6 +267,7 @@ struct optee {
> > bool in_kernel_rpmb_routing;
> > struct work_struct scan_bus_work;
> > struct work_struct rpmb_scan_bus_work;
> > + struct optee_revision revision;
> > };
> >
> > struct optee_session {
> > diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> > index 0be663fcd52b..51fae1ab8ef8 100644
> > --- a/drivers/tee/optee/smc_abi.c
> > +++ b/drivers/tee/optee/smc_abi.c
> > @@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
> >
> > static const struct tee_driver_ops optee_clnt_ops = {
> > .get_version = optee_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_smc_open,
> > .release = optee_release,
> > .open_session = optee_open_session,
> > @@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
> >
> > static const struct tee_driver_ops optee_supp_ops = {
> > .get_version = optee_get_version,
> > + .get_tee_revision = optee_get_revision,
> > .open = optee_smc_open,
> > .release = optee_release_supp,
> > .supp_recv = optee_supp_recv,
> > @@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> > }
> > #endif
> >
> > -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> > +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> > + struct optee_revision *revision)
> > {
> > union {
> > struct arm_smccc_res smccc;
> > @@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> > invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> > &res.smccc);
> >
> > + if (revision) {
> > + revision->os_major = res.result.major;
> > + revision->os_minor = res.result.minor;
> > + revision->os_build_id = res.result.build_id;
> > + }
> > +
> > if (res.result.build_id)
> > pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> > res.result.minor, (int)sizeof(res.result.build_id) * 2,
> > @@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev)
> > return -EINVAL;
> > }
> >
> > - optee_msg_get_os_revision(invoke_fn);
> > -
> > if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> > pr_warn("api revision mismatch\n");
> > return -EINVAL;
> > @@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev)
> > goto err_free_shm_pool;
> > }
> >
> > + optee_msg_get_os_revision(invoke_fn, &optee->revision);
> > +
> > optee->ops = &optee_ops;
> > optee->smc.invoke_fn = invoke_fn;
> > optee->smc.sec_caps = sec_caps;
> > --
> > 2.43.0
> >
Best regards,
Aristo
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v6 1/2] tee: add revision sysfs attribute
2026-01-08 6:45 ` Aristo Chen
@ 2026-01-09 11:48 ` Sumit Garg
-1 siblings, 0 replies; 74+ messages in thread
From: Sumit Garg via OP-TEE @ 2026-01-09 11:48 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
On Thu, Jan 08, 2026 at 02:45:08PM +0800, Aristo Chen wrote:
> Add a generic TEE revision sysfs attribute backed by a new
> optional get_tee_revision() callback. The revision string is
> diagnostic-only and must not be used to infer feature support.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> Documentation/ABI/testing/sysfs-class-tee | 10 +++++
> drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
> include/linux/tee_core.h | 9 ++++
> 3 files changed, 69 insertions(+), 1 deletion(-)
Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
-Sumit
>
> diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
> index c9144d16003e..1a0a3050aaa9 100644
> --- a/Documentation/ABI/testing/sysfs-class-tee
> +++ b/Documentation/ABI/testing/sysfs-class-tee
> @@ -13,3 +13,13 @@ Description:
> space if the variable is absent. The primary purpose
> of this variable is to let systemd know whether
> tee-supplicant is needed in the early boot with initramfs.
> +
> +What: /sys/class/tee/tee{,priv}X/revision
> +Date: Jan 2026
> +KernelVersion: 6.19
> +Contact: op-tee@lists.trustedfirmware.org
> +Description:
> + Read-only revision string reported by the TEE driver. This is
> + for diagnostics only and must not be used to infer feature
> + support. Use TEE_IOC_VERSION for capability and compatibility
> + checks.
> diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> index d65d47cc154e..0a00499811c1 100644
> --- a/drivers/tee/tee_core.c
> +++ b/drivers/tee/tee_core.c
> @@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
> NULL
> };
>
> -ATTRIBUTE_GROUPS(tee_dev);
> +static const struct attribute_group tee_dev_group = {
> + .attrs = tee_dev_attrs,
> +};
> +
> +static ssize_t revision_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> + char version[TEE_REVISION_STR_SIZE];
> + int ret;
> +
> + if (!teedev->desc->ops->get_tee_revision)
> + return -ENODEV;
> +
> + ret = teedev->desc->ops->get_tee_revision(teedev, version,
> + sizeof(version));
> + if (ret)
> + return ret;
> +
> + return sysfs_emit(buf, "%s\n", version);
> +}
> +static DEVICE_ATTR_RO(revision);
> +
> +static struct attribute *tee_revision_attrs[] = {
> + &dev_attr_revision.attr,
> + NULL
> +};
> +
> +static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
> + struct attribute *attr, int n)
> +{
> + struct device *dev = kobj_to_dev(kobj);
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> +
> + if (teedev->desc->ops->get_tee_revision)
> + return attr->mode;
> +
> + return 0;
> +}
> +
> +static const struct attribute_group tee_revision_group = {
> + .attrs = tee_revision_attrs,
> + .is_visible = tee_revision_attr_is_visible,
> +};
> +
> +static const struct attribute_group *tee_dev_groups[] = {
> + &tee_dev_group,
> + &tee_revision_group,
> + NULL
> +};
>
> static const struct class tee_class = {
> .name = "tee",
> diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
> index 1f3e5dad6d0d..ee5f0bd41f43 100644
> --- a/include/linux/tee_core.h
> +++ b/include/linux/tee_core.h
> @@ -76,6 +76,9 @@ struct tee_device {
> /**
> * struct tee_driver_ops - driver operations vtable
> * @get_version: returns version of driver
> + * @get_tee_revision: returns revision string (diagnostic only);
> + * do not infer feature support from this, use
> + * TEE_IOC_VERSION instead
> * @open: called for a context when the device file is opened
> * @close_context: called when the device file is closed
> * @release: called to release the context
> @@ -95,9 +98,12 @@ struct tee_device {
> * client closes the device file, even if there are existing references to the
> * context. The TEE driver can use @close_context to start cleaning up.
> */
> +
> struct tee_driver_ops {
> void (*get_version)(struct tee_device *teedev,
> struct tee_ioctl_version_data *vers);
> + int (*get_tee_revision)(struct tee_device *teedev,
> + char *buf, size_t len);
> int (*open)(struct tee_context *ctx);
> void (*close_context)(struct tee_context *ctx);
> void (*release)(struct tee_context *ctx);
> @@ -123,6 +129,9 @@ struct tee_driver_ops {
> int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
> };
>
> +/* Size for TEE revision string buffer used by get_tee_revision(). */
> +#define TEE_REVISION_STR_SIZE 128
> +
> /**
> * struct tee_desc - Describes the TEE driver to the subsystem
> * @name: name of driver
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v6 1/2] tee: add revision sysfs attribute
@ 2026-01-09 11:48 ` Sumit Garg
0 siblings, 0 replies; 74+ messages in thread
From: Sumit Garg @ 2026-01-09 11:48 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, jens.wiklander, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
On Thu, Jan 08, 2026 at 02:45:08PM +0800, Aristo Chen wrote:
> Add a generic TEE revision sysfs attribute backed by a new
> optional get_tee_revision() callback. The revision string is
> diagnostic-only and must not be used to infer feature support.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> Documentation/ABI/testing/sysfs-class-tee | 10 +++++
> drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
> include/linux/tee_core.h | 9 ++++
> 3 files changed, 69 insertions(+), 1 deletion(-)
Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
-Sumit
>
> diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
> index c9144d16003e..1a0a3050aaa9 100644
> --- a/Documentation/ABI/testing/sysfs-class-tee
> +++ b/Documentation/ABI/testing/sysfs-class-tee
> @@ -13,3 +13,13 @@ Description:
> space if the variable is absent. The primary purpose
> of this variable is to let systemd know whether
> tee-supplicant is needed in the early boot with initramfs.
> +
> +What: /sys/class/tee/tee{,priv}X/revision
> +Date: Jan 2026
> +KernelVersion: 6.19
> +Contact: op-tee@lists.trustedfirmware.org
> +Description:
> + Read-only revision string reported by the TEE driver. This is
> + for diagnostics only and must not be used to infer feature
> + support. Use TEE_IOC_VERSION for capability and compatibility
> + checks.
> diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> index d65d47cc154e..0a00499811c1 100644
> --- a/drivers/tee/tee_core.c
> +++ b/drivers/tee/tee_core.c
> @@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
> NULL
> };
>
> -ATTRIBUTE_GROUPS(tee_dev);
> +static const struct attribute_group tee_dev_group = {
> + .attrs = tee_dev_attrs,
> +};
> +
> +static ssize_t revision_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> + char version[TEE_REVISION_STR_SIZE];
> + int ret;
> +
> + if (!teedev->desc->ops->get_tee_revision)
> + return -ENODEV;
> +
> + ret = teedev->desc->ops->get_tee_revision(teedev, version,
> + sizeof(version));
> + if (ret)
> + return ret;
> +
> + return sysfs_emit(buf, "%s\n", version);
> +}
> +static DEVICE_ATTR_RO(revision);
> +
> +static struct attribute *tee_revision_attrs[] = {
> + &dev_attr_revision.attr,
> + NULL
> +};
> +
> +static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
> + struct attribute *attr, int n)
> +{
> + struct device *dev = kobj_to_dev(kobj);
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> +
> + if (teedev->desc->ops->get_tee_revision)
> + return attr->mode;
> +
> + return 0;
> +}
> +
> +static const struct attribute_group tee_revision_group = {
> + .attrs = tee_revision_attrs,
> + .is_visible = tee_revision_attr_is_visible,
> +};
> +
> +static const struct attribute_group *tee_dev_groups[] = {
> + &tee_dev_group,
> + &tee_revision_group,
> + NULL
> +};
>
> static const struct class tee_class = {
> .name = "tee",
> diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
> index 1f3e5dad6d0d..ee5f0bd41f43 100644
> --- a/include/linux/tee_core.h
> +++ b/include/linux/tee_core.h
> @@ -76,6 +76,9 @@ struct tee_device {
> /**
> * struct tee_driver_ops - driver operations vtable
> * @get_version: returns version of driver
> + * @get_tee_revision: returns revision string (diagnostic only);
> + * do not infer feature support from this, use
> + * TEE_IOC_VERSION instead
> * @open: called for a context when the device file is opened
> * @close_context: called when the device file is closed
> * @release: called to release the context
> @@ -95,9 +98,12 @@ struct tee_device {
> * client closes the device file, even if there are existing references to the
> * context. The TEE driver can use @close_context to start cleaning up.
> */
> +
> struct tee_driver_ops {
> void (*get_version)(struct tee_device *teedev,
> struct tee_ioctl_version_data *vers);
> + int (*get_tee_revision)(struct tee_device *teedev,
> + char *buf, size_t len);
> int (*open)(struct tee_context *ctx);
> void (*close_context)(struct tee_context *ctx);
> void (*release)(struct tee_context *ctx);
> @@ -123,6 +129,9 @@ struct tee_driver_ops {
> int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
> };
>
> +/* Size for TEE revision string buffer used by get_tee_revision(). */
> +#define TEE_REVISION_STR_SIZE 128
> +
> /**
> * struct tee_desc - Describes the TEE driver to the subsystem
> * @name: name of driver
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v6 1/2] tee: add revision sysfs attribute
2026-01-08 6:45 ` Aristo Chen
` (2 preceding siblings ...)
(?)
@ 2026-01-12 9:50 ` Jens Wiklander
-1 siblings, 0 replies; 74+ messages in thread
From: Jens Wiklander @ 2026-01-12 9:50 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, sumit.garg, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
On Thu, Jan 8, 2026 at 7:45 AM Aristo Chen <jj251510319013@gmail.com> wrote:
>
> Add a generic TEE revision sysfs attribute backed by a new
> optional get_tee_revision() callback. The revision string is
> diagnostic-only and must not be used to infer feature support.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> Documentation/ABI/testing/sysfs-class-tee | 10 +++++
> drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
> include/linux/tee_core.h | 9 ++++
> 3 files changed, 69 insertions(+), 1 deletion(-)
checkpatch complains with:
WARNING: From:/Signed-off-by: email address mismatch: 'From: Aristo
Chen <jj251510319013@gmail.com>' != 'Signed-off-by: Aristo Chen
<aristo.chen@canonical.com>'
total: 0 errors, 1 warnings, 0 checks, 100 lines checked
Cheers,
Jens
>
> diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
> index c9144d16003e..1a0a3050aaa9 100644
> --- a/Documentation/ABI/testing/sysfs-class-tee
> +++ b/Documentation/ABI/testing/sysfs-class-tee
> @@ -13,3 +13,13 @@ Description:
> space if the variable is absent. The primary purpose
> of this variable is to let systemd know whether
> tee-supplicant is needed in the early boot with initramfs.
> +
> +What: /sys/class/tee/tee{,priv}X/revision
> +Date: Jan 2026
> +KernelVersion: 6.19
> +Contact: op-tee@lists.trustedfirmware.org
> +Description:
> + Read-only revision string reported by the TEE driver. This is
> + for diagnostics only and must not be used to infer feature
> + support. Use TEE_IOC_VERSION for capability and compatibility
> + checks.
> diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> index d65d47cc154e..0a00499811c1 100644
> --- a/drivers/tee/tee_core.c
> +++ b/drivers/tee/tee_core.c
> @@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
> NULL
> };
>
> -ATTRIBUTE_GROUPS(tee_dev);
> +static const struct attribute_group tee_dev_group = {
> + .attrs = tee_dev_attrs,
> +};
> +
> +static ssize_t revision_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> + char version[TEE_REVISION_STR_SIZE];
> + int ret;
> +
> + if (!teedev->desc->ops->get_tee_revision)
> + return -ENODEV;
> +
> + ret = teedev->desc->ops->get_tee_revision(teedev, version,
> + sizeof(version));
> + if (ret)
> + return ret;
> +
> + return sysfs_emit(buf, "%s\n", version);
> +}
> +static DEVICE_ATTR_RO(revision);
> +
> +static struct attribute *tee_revision_attrs[] = {
> + &dev_attr_revision.attr,
> + NULL
> +};
> +
> +static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
> + struct attribute *attr, int n)
> +{
> + struct device *dev = kobj_to_dev(kobj);
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> +
> + if (teedev->desc->ops->get_tee_revision)
> + return attr->mode;
> +
> + return 0;
> +}
> +
> +static const struct attribute_group tee_revision_group = {
> + .attrs = tee_revision_attrs,
> + .is_visible = tee_revision_attr_is_visible,
> +};
> +
> +static const struct attribute_group *tee_dev_groups[] = {
> + &tee_dev_group,
> + &tee_revision_group,
> + NULL
> +};
>
> static const struct class tee_class = {
> .name = "tee",
> diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
> index 1f3e5dad6d0d..ee5f0bd41f43 100644
> --- a/include/linux/tee_core.h
> +++ b/include/linux/tee_core.h
> @@ -76,6 +76,9 @@ struct tee_device {
> /**
> * struct tee_driver_ops - driver operations vtable
> * @get_version: returns version of driver
> + * @get_tee_revision: returns revision string (diagnostic only);
> + * do not infer feature support from this, use
> + * TEE_IOC_VERSION instead
> * @open: called for a context when the device file is opened
> * @close_context: called when the device file is closed
> * @release: called to release the context
> @@ -95,9 +98,12 @@ struct tee_device {
> * client closes the device file, even if there are existing references to the
> * context. The TEE driver can use @close_context to start cleaning up.
> */
> +
> struct tee_driver_ops {
> void (*get_version)(struct tee_device *teedev,
> struct tee_ioctl_version_data *vers);
> + int (*get_tee_revision)(struct tee_device *teedev,
> + char *buf, size_t len);
> int (*open)(struct tee_context *ctx);
> void (*close_context)(struct tee_context *ctx);
> void (*release)(struct tee_context *ctx);
> @@ -123,6 +129,9 @@ struct tee_driver_ops {
> int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
> };
>
> +/* Size for TEE revision string buffer used by get_tee_revision(). */
> +#define TEE_REVISION_STR_SIZE 128
> +
> /**
> * struct tee_desc - Describes the TEE driver to the subsystem
> * @name: name of driver
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 74+ messages in thread* [PATCH v7 1/2] tee: add revision sysfs attribute
2026-01-08 6:45 ` Aristo Chen
@ 2026-01-12 15:48 ` Aristo Chen
-1 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen via OP-TEE @ 2026-01-12 15:48 UTC (permalink / raw)
To: linux-kernel
Cc: sumit.garg, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen, Sumit Garg
Add a generic TEE revision sysfs attribute backed by a new
optional get_tee_revision() callback. The revision string is
diagnostic-only and must not be used to infer feature support.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
Documentation/ABI/testing/sysfs-class-tee | 10 +++++
drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
include/linux/tee_core.h | 9 ++++
3 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
index c9144d16003e..1a0a3050aaa9 100644
--- a/Documentation/ABI/testing/sysfs-class-tee
+++ b/Documentation/ABI/testing/sysfs-class-tee
@@ -13,3 +13,13 @@ Description:
space if the variable is absent. The primary purpose
of this variable is to let systemd know whether
tee-supplicant is needed in the early boot with initramfs.
+
+What: /sys/class/tee/tee{,priv}X/revision
+Date: Jan 2026
+KernelVersion: 6.19
+Contact: op-tee@lists.trustedfirmware.org
+Description:
+ Read-only revision string reported by the TEE driver. This is
+ for diagnostics only and must not be used to infer feature
+ support. Use TEE_IOC_VERSION for capability and compatibility
+ checks.
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index d65d47cc154e..0a00499811c1 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
NULL
};
-ATTRIBUTE_GROUPS(tee_dev);
+static const struct attribute_group tee_dev_group = {
+ .attrs = tee_dev_attrs,
+};
+
+static ssize_t revision_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+ char version[TEE_REVISION_STR_SIZE];
+ int ret;
+
+ if (!teedev->desc->ops->get_tee_revision)
+ return -ENODEV;
+
+ ret = teedev->desc->ops->get_tee_revision(teedev, version,
+ sizeof(version));
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%s\n", version);
+}
+static DEVICE_ATTR_RO(revision);
+
+static struct attribute *tee_revision_attrs[] = {
+ &dev_attr_revision.attr,
+ NULL
+};
+
+static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+
+ if (teedev->desc->ops->get_tee_revision)
+ return attr->mode;
+
+ return 0;
+}
+
+static const struct attribute_group tee_revision_group = {
+ .attrs = tee_revision_attrs,
+ .is_visible = tee_revision_attr_is_visible,
+};
+
+static const struct attribute_group *tee_dev_groups[] = {
+ &tee_dev_group,
+ &tee_revision_group,
+ NULL
+};
static const struct class tee_class = {
.name = "tee",
diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
index 1f3e5dad6d0d..ee5f0bd41f43 100644
--- a/include/linux/tee_core.h
+++ b/include/linux/tee_core.h
@@ -76,6 +76,9 @@ struct tee_device {
/**
* struct tee_driver_ops - driver operations vtable
* @get_version: returns version of driver
+ * @get_tee_revision: returns revision string (diagnostic only);
+ * do not infer feature support from this, use
+ * TEE_IOC_VERSION instead
* @open: called for a context when the device file is opened
* @close_context: called when the device file is closed
* @release: called to release the context
@@ -95,9 +98,12 @@ struct tee_device {
* client closes the device file, even if there are existing references to the
* context. The TEE driver can use @close_context to start cleaning up.
*/
+
struct tee_driver_ops {
void (*get_version)(struct tee_device *teedev,
struct tee_ioctl_version_data *vers);
+ int (*get_tee_revision)(struct tee_device *teedev,
+ char *buf, size_t len);
int (*open)(struct tee_context *ctx);
void (*close_context)(struct tee_context *ctx);
void (*release)(struct tee_context *ctx);
@@ -123,6 +129,9 @@ struct tee_driver_ops {
int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
};
+/* Size for TEE revision string buffer used by get_tee_revision(). */
+#define TEE_REVISION_STR_SIZE 128
+
/**
* struct tee_desc - Describes the TEE driver to the subsystem
* @name: name of driver
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread* [PATCH v7 1/2] tee: add revision sysfs attribute
@ 2026-01-12 15:48 ` Aristo Chen
0 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-12 15:48 UTC (permalink / raw)
To: linux-kernel
Cc: jens.wiklander, sumit.garg, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen, Sumit Garg
Add a generic TEE revision sysfs attribute backed by a new
optional get_tee_revision() callback. The revision string is
diagnostic-only and must not be used to infer feature support.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
---
Documentation/ABI/testing/sysfs-class-tee | 10 +++++
drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
include/linux/tee_core.h | 9 ++++
3 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
index c9144d16003e..1a0a3050aaa9 100644
--- a/Documentation/ABI/testing/sysfs-class-tee
+++ b/Documentation/ABI/testing/sysfs-class-tee
@@ -13,3 +13,13 @@ Description:
space if the variable is absent. The primary purpose
of this variable is to let systemd know whether
tee-supplicant is needed in the early boot with initramfs.
+
+What: /sys/class/tee/tee{,priv}X/revision
+Date: Jan 2026
+KernelVersion: 6.19
+Contact: op-tee@lists.trustedfirmware.org
+Description:
+ Read-only revision string reported by the TEE driver. This is
+ for diagnostics only and must not be used to infer feature
+ support. Use TEE_IOC_VERSION for capability and compatibility
+ checks.
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index d65d47cc154e..0a00499811c1 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
NULL
};
-ATTRIBUTE_GROUPS(tee_dev);
+static const struct attribute_group tee_dev_group = {
+ .attrs = tee_dev_attrs,
+};
+
+static ssize_t revision_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+ char version[TEE_REVISION_STR_SIZE];
+ int ret;
+
+ if (!teedev->desc->ops->get_tee_revision)
+ return -ENODEV;
+
+ ret = teedev->desc->ops->get_tee_revision(teedev, version,
+ sizeof(version));
+ if (ret)
+ return ret;
+
+ return sysfs_emit(buf, "%s\n", version);
+}
+static DEVICE_ATTR_RO(revision);
+
+static struct attribute *tee_revision_attrs[] = {
+ &dev_attr_revision.attr,
+ NULL
+};
+
+static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct tee_device *teedev = container_of(dev, struct tee_device, dev);
+
+ if (teedev->desc->ops->get_tee_revision)
+ return attr->mode;
+
+ return 0;
+}
+
+static const struct attribute_group tee_revision_group = {
+ .attrs = tee_revision_attrs,
+ .is_visible = tee_revision_attr_is_visible,
+};
+
+static const struct attribute_group *tee_dev_groups[] = {
+ &tee_dev_group,
+ &tee_revision_group,
+ NULL
+};
static const struct class tee_class = {
.name = "tee",
diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
index 1f3e5dad6d0d..ee5f0bd41f43 100644
--- a/include/linux/tee_core.h
+++ b/include/linux/tee_core.h
@@ -76,6 +76,9 @@ struct tee_device {
/**
* struct tee_driver_ops - driver operations vtable
* @get_version: returns version of driver
+ * @get_tee_revision: returns revision string (diagnostic only);
+ * do not infer feature support from this, use
+ * TEE_IOC_VERSION instead
* @open: called for a context when the device file is opened
* @close_context: called when the device file is closed
* @release: called to release the context
@@ -95,9 +98,12 @@ struct tee_device {
* client closes the device file, even if there are existing references to the
* context. The TEE driver can use @close_context to start cleaning up.
*/
+
struct tee_driver_ops {
void (*get_version)(struct tee_device *teedev,
struct tee_ioctl_version_data *vers);
+ int (*get_tee_revision)(struct tee_device *teedev,
+ char *buf, size_t len);
int (*open)(struct tee_context *ctx);
void (*close_context)(struct tee_context *ctx);
void (*release)(struct tee_context *ctx);
@@ -123,6 +129,9 @@ struct tee_driver_ops {
int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
};
+/* Size for TEE revision string buffer used by get_tee_revision(). */
+#define TEE_REVISION_STR_SIZE 128
+
/**
* struct tee_desc - Describes the TEE driver to the subsystem
* @name: name of driver
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread* [PATCH v7 2/2] tee: optee: store OS revision for TEE core
2026-01-12 15:48 ` Aristo Chen
@ 2026-01-12 15:48 ` Aristo Chen
-1 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen via OP-TEE @ 2026-01-12 15:48 UTC (permalink / raw)
To: linux-kernel
Cc: sumit.garg, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Aristo Chen
Collect OP-TEE OS revision from secure world for both SMC and FF-A
ABIs, store it in the OP-TEE driver, and expose it through the
generic get_tee_revision() callback.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
---
drivers/tee/optee/core.c | 23 +++++++++++++
drivers/tee/optee/ffa_abi.c | 54 +++++++++++++++++++++++--------
drivers/tee/optee/optee_private.h | 19 +++++++++++
drivers/tee/optee/smc_abi.c | 15 +++++++--
4 files changed, 94 insertions(+), 17 deletions(-)
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 5b62139714ce..2d807bc748bc 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
}
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
+{
+ struct optee *optee = tee_get_drvdata(teedev);
+ u64 build_id;
+
+ if (!optee)
+ return -ENODEV;
+ if (!buf || !len)
+ return -EINVAL;
+
+ build_id = optee->revision.os_build_id;
+ if (build_id)
+ scnprintf(buf, len, "%u.%u (%016llx)",
+ optee->revision.os_major,
+ optee->revision.os_minor,
+ (unsigned long long)build_id);
+ else
+ scnprintf(buf, len, "%u.%u", optee->revision.os_major,
+ optee->revision.os_minor);
+
+ return 0;
+}
+
static void optee_bus_scan(struct work_struct *work)
{
WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
index bf8390789ecf..8fc72aa95722 100644
--- a/drivers/tee/optee/ffa_abi.c
+++ b/drivers/tee/optee/ffa_abi.c
@@ -775,6 +775,39 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
* with a matching configuration.
*/
+static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
+ const struct ffa_ops *ops,
+ struct optee_revision *revision)
+{
+ const struct ffa_msg_ops *msg_ops = ops->msg_ops;
+ struct ffa_send_direct_data data = {
+ .data0 = OPTEE_FFA_GET_OS_VERSION,
+ };
+ int rc;
+
+ msg_ops->mode_32bit_set(ffa_dev);
+
+ rc = msg_ops->sync_send_receive(ffa_dev, &data);
+ if (rc) {
+ pr_err("Unexpected error %d\n", rc);
+ return false;
+ }
+
+ if (revision) {
+ revision->os_major = data.data0;
+ revision->os_minor = data.data1;
+ revision->os_build_id = data.data2;
+ }
+
+ if (data.data2)
+ pr_info("revision %lu.%lu (%08lx)",
+ data.data0, data.data1, data.data2);
+ else
+ pr_info("revision %lu.%lu", data.data0, data.data1);
+
+ return true;
+}
+
static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
const struct ffa_ops *ops)
{
@@ -798,20 +831,6 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
return false;
}
- data = (struct ffa_send_direct_data){
- .data0 = OPTEE_FFA_GET_OS_VERSION,
- };
- rc = msg_ops->sync_send_receive(ffa_dev, &data);
- if (rc) {
- pr_err("Unexpected error %d\n", rc);
- return false;
- }
- if (data.data2)
- pr_info("revision %lu.%lu (%08lx)",
- data.data0, data.data1, data.data2);
- else
- pr_info("revision %lu.%lu", data.data0, data.data1);
-
return true;
}
@@ -900,6 +919,7 @@ static int optee_ffa_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_ffa_clnt_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -918,6 +938,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
static const struct tee_driver_ops optee_ffa_supp_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1060,6 +1081,11 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
if (!optee)
return -ENOMEM;
+ if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision)) {
+ rc = -EINVAL;
+ goto err_free_optee;
+ }
+
pool = optee_ffa_shm_pool_alloc_pages();
if (IS_ERR(pool)) {
rc = PTR_ERR(pool);
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index db9ea673fbca..acd3051c4879 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -171,6 +171,24 @@ struct optee_ffa {
struct optee;
+/**
+ * struct optee_revision - OP-TEE OS revision reported by secure world
+ * @os_major: OP-TEE OS major version
+ * @os_minor: OP-TEE OS minor version
+ * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
+ *
+ * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
+ * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
+ * FF-A ABI version.
+ */
+struct optee_revision {
+ u32 os_major;
+ u32 os_minor;
+ u64 os_build_id;
+};
+
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
+
/**
* struct optee_ops - OP-TEE driver internal operations
* @do_call_with_arg: enters OP-TEE in secure world
@@ -249,6 +267,7 @@ struct optee {
bool in_kernel_rpmb_routing;
struct work_struct scan_bus_work;
struct work_struct rpmb_scan_bus_work;
+ struct optee_revision revision;
};
struct optee_session {
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
index 0be663fcd52b..51fae1ab8ef8 100644
--- a/drivers/tee/optee/smc_abi.c
+++ b/drivers/tee/optee/smc_abi.c
@@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_clnt_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
static const struct tee_driver_ops optee_supp_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
}
#endif
-static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
+static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
+ struct optee_revision *revision)
{
union {
struct arm_smccc_res smccc;
@@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
&res.smccc);
+ if (revision) {
+ revision->os_major = res.result.major;
+ revision->os_minor = res.result.minor;
+ revision->os_build_id = res.result.build_id;
+ }
+
if (res.result.build_id)
pr_info("revision %lu.%lu (%0*lx)", res.result.major,
res.result.minor, (int)sizeof(res.result.build_id) * 2,
@@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev)
return -EINVAL;
}
- optee_msg_get_os_revision(invoke_fn);
-
if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
pr_warn("api revision mismatch\n");
return -EINVAL;
@@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev)
goto err_free_shm_pool;
}
+ optee_msg_get_os_revision(invoke_fn, &optee->revision);
+
optee->ops = &optee_ops;
optee->smc.invoke_fn = invoke_fn;
optee->smc.sec_caps = sec_caps;
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread* [PATCH v7 2/2] tee: optee: store OS revision for TEE core
@ 2026-01-12 15:48 ` Aristo Chen
0 siblings, 0 replies; 74+ messages in thread
From: Aristo Chen @ 2026-01-12 15:48 UTC (permalink / raw)
To: linux-kernel
Cc: jens.wiklander, sumit.garg, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi,
Aristo Chen
Collect OP-TEE OS revision from secure world for both SMC and FF-A
ABIs, store it in the OP-TEE driver, and expose it through the
generic get_tee_revision() callback.
Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
---
drivers/tee/optee/core.c | 23 +++++++++++++
drivers/tee/optee/ffa_abi.c | 54 +++++++++++++++++++++++--------
drivers/tee/optee/optee_private.h | 19 +++++++++++
drivers/tee/optee/smc_abi.c | 15 +++++++--
4 files changed, 94 insertions(+), 17 deletions(-)
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 5b62139714ce..2d807bc748bc 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
}
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
+{
+ struct optee *optee = tee_get_drvdata(teedev);
+ u64 build_id;
+
+ if (!optee)
+ return -ENODEV;
+ if (!buf || !len)
+ return -EINVAL;
+
+ build_id = optee->revision.os_build_id;
+ if (build_id)
+ scnprintf(buf, len, "%u.%u (%016llx)",
+ optee->revision.os_major,
+ optee->revision.os_minor,
+ (unsigned long long)build_id);
+ else
+ scnprintf(buf, len, "%u.%u", optee->revision.os_major,
+ optee->revision.os_minor);
+
+ return 0;
+}
+
static void optee_bus_scan(struct work_struct *work)
{
WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
index bf8390789ecf..8fc72aa95722 100644
--- a/drivers/tee/optee/ffa_abi.c
+++ b/drivers/tee/optee/ffa_abi.c
@@ -775,6 +775,39 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
* with a matching configuration.
*/
+static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
+ const struct ffa_ops *ops,
+ struct optee_revision *revision)
+{
+ const struct ffa_msg_ops *msg_ops = ops->msg_ops;
+ struct ffa_send_direct_data data = {
+ .data0 = OPTEE_FFA_GET_OS_VERSION,
+ };
+ int rc;
+
+ msg_ops->mode_32bit_set(ffa_dev);
+
+ rc = msg_ops->sync_send_receive(ffa_dev, &data);
+ if (rc) {
+ pr_err("Unexpected error %d\n", rc);
+ return false;
+ }
+
+ if (revision) {
+ revision->os_major = data.data0;
+ revision->os_minor = data.data1;
+ revision->os_build_id = data.data2;
+ }
+
+ if (data.data2)
+ pr_info("revision %lu.%lu (%08lx)",
+ data.data0, data.data1, data.data2);
+ else
+ pr_info("revision %lu.%lu", data.data0, data.data1);
+
+ return true;
+}
+
static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
const struct ffa_ops *ops)
{
@@ -798,20 +831,6 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
return false;
}
- data = (struct ffa_send_direct_data){
- .data0 = OPTEE_FFA_GET_OS_VERSION,
- };
- rc = msg_ops->sync_send_receive(ffa_dev, &data);
- if (rc) {
- pr_err("Unexpected error %d\n", rc);
- return false;
- }
- if (data.data2)
- pr_info("revision %lu.%lu (%08lx)",
- data.data0, data.data1, data.data2);
- else
- pr_info("revision %lu.%lu", data.data0, data.data1);
-
return true;
}
@@ -900,6 +919,7 @@ static int optee_ffa_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_ffa_clnt_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -918,6 +938,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
static const struct tee_driver_ops optee_ffa_supp_ops = {
.get_version = optee_ffa_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_ffa_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1060,6 +1081,11 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
if (!optee)
return -ENOMEM;
+ if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision)) {
+ rc = -EINVAL;
+ goto err_free_optee;
+ }
+
pool = optee_ffa_shm_pool_alloc_pages();
if (IS_ERR(pool)) {
rc = PTR_ERR(pool);
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index db9ea673fbca..acd3051c4879 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -171,6 +171,24 @@ struct optee_ffa {
struct optee;
+/**
+ * struct optee_revision - OP-TEE OS revision reported by secure world
+ * @os_major: OP-TEE OS major version
+ * @os_minor: OP-TEE OS minor version
+ * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
+ *
+ * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
+ * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
+ * FF-A ABI version.
+ */
+struct optee_revision {
+ u32 os_major;
+ u32 os_minor;
+ u64 os_build_id;
+};
+
+int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
+
/**
* struct optee_ops - OP-TEE driver internal operations
* @do_call_with_arg: enters OP-TEE in secure world
@@ -249,6 +267,7 @@ struct optee {
bool in_kernel_rpmb_routing;
struct work_struct scan_bus_work;
struct work_struct rpmb_scan_bus_work;
+ struct optee_revision revision;
};
struct optee_session {
diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
index 0be663fcd52b..51fae1ab8ef8 100644
--- a/drivers/tee/optee/smc_abi.c
+++ b/drivers/tee/optee/smc_abi.c
@@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
static const struct tee_driver_ops optee_clnt_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release,
.open_session = optee_open_session,
@@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
static const struct tee_driver_ops optee_supp_ops = {
.get_version = optee_get_version,
+ .get_tee_revision = optee_get_revision,
.open = optee_smc_open,
.release = optee_release_supp,
.supp_recv = optee_supp_recv,
@@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
}
#endif
-static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
+static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
+ struct optee_revision *revision)
{
union {
struct arm_smccc_res smccc;
@@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
&res.smccc);
+ if (revision) {
+ revision->os_major = res.result.major;
+ revision->os_minor = res.result.minor;
+ revision->os_build_id = res.result.build_id;
+ }
+
if (res.result.build_id)
pr_info("revision %lu.%lu (%0*lx)", res.result.major,
res.result.minor, (int)sizeof(res.result.build_id) * 2,
@@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev)
return -EINVAL;
}
- optee_msg_get_os_revision(invoke_fn);
-
if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
pr_warn("api revision mismatch\n");
return -EINVAL;
@@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev)
goto err_free_shm_pool;
}
+ optee_msg_get_os_revision(invoke_fn, &optee->revision);
+
optee->ops = &optee_ops;
optee->smc.invoke_fn = invoke_fn;
optee->smc.sec_caps = sec_caps;
--
2.43.0
^ permalink raw reply related [flat|nested] 74+ messages in thread* Re: [PATCH v7 2/2] tee: optee: store OS revision for TEE core
2026-01-12 15:48 ` Aristo Chen
(?)
@ 2026-01-14 15:43 ` Jens Wiklander
-1 siblings, 0 replies; 74+ messages in thread
From: Jens Wiklander @ 2026-01-14 15:43 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, sumit.garg, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi
On Mon, Jan 12, 2026 at 4:48 PM Aristo Chen <aristo.chen@canonical.com> wrote:
>
> Collect OP-TEE OS revision from secure world for both SMC and FF-A
> ABIs, store it in the OP-TEE driver, and expose it through the
> generic get_tee_revision() callback.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> drivers/tee/optee/core.c | 23 +++++++++++++
> drivers/tee/optee/ffa_abi.c | 54 +++++++++++++++++++++++--------
> drivers/tee/optee/optee_private.h | 19 +++++++++++
> drivers/tee/optee/smc_abi.c | 15 +++++++--
> 4 files changed, 94 insertions(+), 17 deletions(-)
Looks good, I'm picking up this.
Thanks,
Jens
>
> diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> index 5b62139714ce..2d807bc748bc 100644
> --- a/drivers/tee/optee/core.c
> +++ b/drivers/tee/optee/core.c
> @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> }
>
> +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> +{
> + struct optee *optee = tee_get_drvdata(teedev);
> + u64 build_id;
> +
> + if (!optee)
> + return -ENODEV;
> + if (!buf || !len)
> + return -EINVAL;
> +
> + build_id = optee->revision.os_build_id;
> + if (build_id)
> + scnprintf(buf, len, "%u.%u (%016llx)",
> + optee->revision.os_major,
> + optee->revision.os_minor,
> + (unsigned long long)build_id);
> + else
> + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> + optee->revision.os_minor);
> +
> + return 0;
> +}
> +
> static void optee_bus_scan(struct work_struct *work)
> {
> WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> index bf8390789ecf..8fc72aa95722 100644
> --- a/drivers/tee/optee/ffa_abi.c
> +++ b/drivers/tee/optee/ffa_abi.c
> @@ -775,6 +775,39 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> * with a matching configuration.
> */
>
> +static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
> + const struct ffa_ops *ops,
> + struct optee_revision *revision)
> +{
> + const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> + struct ffa_send_direct_data data = {
> + .data0 = OPTEE_FFA_GET_OS_VERSION,
> + };
> + int rc;
> +
> + msg_ops->mode_32bit_set(ffa_dev);
> +
> + rc = msg_ops->sync_send_receive(ffa_dev, &data);
> + if (rc) {
> + pr_err("Unexpected error %d\n", rc);
> + return false;
> + }
> +
> + if (revision) {
> + revision->os_major = data.data0;
> + revision->os_minor = data.data1;
> + revision->os_build_id = data.data2;
> + }
> +
> + if (data.data2)
> + pr_info("revision %lu.%lu (%08lx)",
> + data.data0, data.data1, data.data2);
> + else
> + pr_info("revision %lu.%lu", data.data0, data.data1);
> +
> + return true;
> +}
> +
> static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> const struct ffa_ops *ops)
> {
> @@ -798,20 +831,6 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> return false;
> }
>
> - data = (struct ffa_send_direct_data){
> - .data0 = OPTEE_FFA_GET_OS_VERSION,
> - };
> - rc = msg_ops->sync_send_receive(ffa_dev, &data);
> - if (rc) {
> - pr_err("Unexpected error %d\n", rc);
> - return false;
> - }
> - if (data.data2)
> - pr_info("revision %lu.%lu (%08lx)",
> - data.data0, data.data1, data.data2);
> - else
> - pr_info("revision %lu.%lu", data.data0, data.data1);
> -
> return true;
> }
>
> @@ -900,6 +919,7 @@ static int optee_ffa_open(struct tee_context *ctx)
>
> static const struct tee_driver_ops optee_ffa_clnt_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -918,6 +938,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
>
> static const struct tee_driver_ops optee_ffa_supp_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1060,6 +1081,11 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> if (!optee)
> return -ENOMEM;
>
> + if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision)) {
> + rc = -EINVAL;
> + goto err_free_optee;
> + }
> +
> pool = optee_ffa_shm_pool_alloc_pages();
> if (IS_ERR(pool)) {
> rc = PTR_ERR(pool);
> diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> index db9ea673fbca..acd3051c4879 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -171,6 +171,24 @@ struct optee_ffa {
>
> struct optee;
>
> +/**
> + * struct optee_revision - OP-TEE OS revision reported by secure world
> + * @os_major: OP-TEE OS major version
> + * @os_minor: OP-TEE OS minor version
> + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> + *
> + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> + * FF-A ABI version.
> + */
> +struct optee_revision {
> + u32 os_major;
> + u32 os_minor;
> + u64 os_build_id;
> +};
> +
> +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
> +
> /**
> * struct optee_ops - OP-TEE driver internal operations
> * @do_call_with_arg: enters OP-TEE in secure world
> @@ -249,6 +267,7 @@ struct optee {
> bool in_kernel_rpmb_routing;
> struct work_struct scan_bus_work;
> struct work_struct rpmb_scan_bus_work;
> + struct optee_revision revision;
> };
>
> struct optee_session {
> diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> index 0be663fcd52b..51fae1ab8ef8 100644
> --- a/drivers/tee/optee/smc_abi.c
> +++ b/drivers/tee/optee/smc_abi.c
> @@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
>
> static const struct tee_driver_ops optee_clnt_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_smc_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
>
> static const struct tee_driver_ops optee_supp_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_smc_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> }
> #endif
>
> -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> + struct optee_revision *revision)
> {
> union {
> struct arm_smccc_res smccc;
> @@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> &res.smccc);
>
> + if (revision) {
> + revision->os_major = res.result.major;
> + revision->os_minor = res.result.minor;
> + revision->os_build_id = res.result.build_id;
> + }
> +
> if (res.result.build_id)
> pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> res.result.minor, (int)sizeof(res.result.build_id) * 2,
> @@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev)
> return -EINVAL;
> }
>
> - optee_msg_get_os_revision(invoke_fn);
> -
> if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> pr_warn("api revision mismatch\n");
> return -EINVAL;
> @@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev)
> goto err_free_shm_pool;
> }
>
> + optee_msg_get_os_revision(invoke_fn, &optee->revision);
> +
> optee->ops = &optee_ops;
> optee->smc.invoke_fn = invoke_fn;
> optee->smc.sec_caps = sec_caps;
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v7 2/2] tee: optee: store OS revision for TEE core
2026-01-12 15:48 ` Aristo Chen
@ 2026-01-15 6:18 ` Sumit Garg
-1 siblings, 0 replies; 74+ messages in thread
From: Sumit Garg via OP-TEE @ 2026-01-15 6:18 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi
On Mon, Jan 12, 2026 at 11:48:30PM +0800, Aristo Chen wrote:
> Collect OP-TEE OS revision from secure world for both SMC and FF-A
> ABIs, store it in the OP-TEE driver, and expose it through the
> generic get_tee_revision() callback.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> drivers/tee/optee/core.c | 23 +++++++++++++
> drivers/tee/optee/ffa_abi.c | 54 +++++++++++++++++++++++--------
> drivers/tee/optee/optee_private.h | 19 +++++++++++
> drivers/tee/optee/smc_abi.c | 15 +++++++--
> 4 files changed, 94 insertions(+), 17 deletions(-)
Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
-Sumit
>
> diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> index 5b62139714ce..2d807bc748bc 100644
> --- a/drivers/tee/optee/core.c
> +++ b/drivers/tee/optee/core.c
> @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> }
>
> +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> +{
> + struct optee *optee = tee_get_drvdata(teedev);
> + u64 build_id;
> +
> + if (!optee)
> + return -ENODEV;
> + if (!buf || !len)
> + return -EINVAL;
> +
> + build_id = optee->revision.os_build_id;
> + if (build_id)
> + scnprintf(buf, len, "%u.%u (%016llx)",
> + optee->revision.os_major,
> + optee->revision.os_minor,
> + (unsigned long long)build_id);
> + else
> + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> + optee->revision.os_minor);
> +
> + return 0;
> +}
> +
> static void optee_bus_scan(struct work_struct *work)
> {
> WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> index bf8390789ecf..8fc72aa95722 100644
> --- a/drivers/tee/optee/ffa_abi.c
> +++ b/drivers/tee/optee/ffa_abi.c
> @@ -775,6 +775,39 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> * with a matching configuration.
> */
>
> +static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
> + const struct ffa_ops *ops,
> + struct optee_revision *revision)
> +{
> + const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> + struct ffa_send_direct_data data = {
> + .data0 = OPTEE_FFA_GET_OS_VERSION,
> + };
> + int rc;
> +
> + msg_ops->mode_32bit_set(ffa_dev);
> +
> + rc = msg_ops->sync_send_receive(ffa_dev, &data);
> + if (rc) {
> + pr_err("Unexpected error %d\n", rc);
> + return false;
> + }
> +
> + if (revision) {
> + revision->os_major = data.data0;
> + revision->os_minor = data.data1;
> + revision->os_build_id = data.data2;
> + }
> +
> + if (data.data2)
> + pr_info("revision %lu.%lu (%08lx)",
> + data.data0, data.data1, data.data2);
> + else
> + pr_info("revision %lu.%lu", data.data0, data.data1);
> +
> + return true;
> +}
> +
> static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> const struct ffa_ops *ops)
> {
> @@ -798,20 +831,6 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> return false;
> }
>
> - data = (struct ffa_send_direct_data){
> - .data0 = OPTEE_FFA_GET_OS_VERSION,
> - };
> - rc = msg_ops->sync_send_receive(ffa_dev, &data);
> - if (rc) {
> - pr_err("Unexpected error %d\n", rc);
> - return false;
> - }
> - if (data.data2)
> - pr_info("revision %lu.%lu (%08lx)",
> - data.data0, data.data1, data.data2);
> - else
> - pr_info("revision %lu.%lu", data.data0, data.data1);
> -
> return true;
> }
>
> @@ -900,6 +919,7 @@ static int optee_ffa_open(struct tee_context *ctx)
>
> static const struct tee_driver_ops optee_ffa_clnt_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -918,6 +938,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
>
> static const struct tee_driver_ops optee_ffa_supp_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1060,6 +1081,11 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> if (!optee)
> return -ENOMEM;
>
> + if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision)) {
> + rc = -EINVAL;
> + goto err_free_optee;
> + }
> +
> pool = optee_ffa_shm_pool_alloc_pages();
> if (IS_ERR(pool)) {
> rc = PTR_ERR(pool);
> diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> index db9ea673fbca..acd3051c4879 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -171,6 +171,24 @@ struct optee_ffa {
>
> struct optee;
>
> +/**
> + * struct optee_revision - OP-TEE OS revision reported by secure world
> + * @os_major: OP-TEE OS major version
> + * @os_minor: OP-TEE OS minor version
> + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> + *
> + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> + * FF-A ABI version.
> + */
> +struct optee_revision {
> + u32 os_major;
> + u32 os_minor;
> + u64 os_build_id;
> +};
> +
> +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
> +
> /**
> * struct optee_ops - OP-TEE driver internal operations
> * @do_call_with_arg: enters OP-TEE in secure world
> @@ -249,6 +267,7 @@ struct optee {
> bool in_kernel_rpmb_routing;
> struct work_struct scan_bus_work;
> struct work_struct rpmb_scan_bus_work;
> + struct optee_revision revision;
> };
>
> struct optee_session {
> diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> index 0be663fcd52b..51fae1ab8ef8 100644
> --- a/drivers/tee/optee/smc_abi.c
> +++ b/drivers/tee/optee/smc_abi.c
> @@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
>
> static const struct tee_driver_ops optee_clnt_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_smc_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
>
> static const struct tee_driver_ops optee_supp_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_smc_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> }
> #endif
>
> -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> + struct optee_revision *revision)
> {
> union {
> struct arm_smccc_res smccc;
> @@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> &res.smccc);
>
> + if (revision) {
> + revision->os_major = res.result.major;
> + revision->os_minor = res.result.minor;
> + revision->os_build_id = res.result.build_id;
> + }
> +
> if (res.result.build_id)
> pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> res.result.minor, (int)sizeof(res.result.build_id) * 2,
> @@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev)
> return -EINVAL;
> }
>
> - optee_msg_get_os_revision(invoke_fn);
> -
> if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> pr_warn("api revision mismatch\n");
> return -EINVAL;
> @@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev)
> goto err_free_shm_pool;
> }
>
> + optee_msg_get_os_revision(invoke_fn, &optee->revision);
> +
> optee->ops = &optee_ops;
> optee->smc.invoke_fn = invoke_fn;
> optee->smc.sec_caps = sec_caps;
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 74+ messages in thread* Re: [PATCH v7 2/2] tee: optee: store OS revision for TEE core
@ 2026-01-15 6:18 ` Sumit Garg
0 siblings, 0 replies; 74+ messages in thread
From: Sumit Garg @ 2026-01-15 6:18 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, jens.wiklander, op-tee, harshal.dev,
mario.limonciello, Rijo-john.Thomas, amirreza.zarrabi
On Mon, Jan 12, 2026 at 11:48:30PM +0800, Aristo Chen wrote:
> Collect OP-TEE OS revision from secure world for both SMC and FF-A
> ABIs, store it in the OP-TEE driver, and expose it through the
> generic get_tee_revision() callback.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> ---
> drivers/tee/optee/core.c | 23 +++++++++++++
> drivers/tee/optee/ffa_abi.c | 54 +++++++++++++++++++++++--------
> drivers/tee/optee/optee_private.h | 19 +++++++++++
> drivers/tee/optee/smc_abi.c | 15 +++++++--
> 4 files changed, 94 insertions(+), 17 deletions(-)
Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
-Sumit
>
> diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> index 5b62139714ce..2d807bc748bc 100644
> --- a/drivers/tee/optee/core.c
> +++ b/drivers/tee/optee/core.c
> @@ -63,6 +63,29 @@ int optee_set_dma_mask(struct optee *optee, u_int pa_width)
> return dma_coerce_mask_and_coherent(&optee->teedev->dev, mask);
> }
>
> +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len)
> +{
> + struct optee *optee = tee_get_drvdata(teedev);
> + u64 build_id;
> +
> + if (!optee)
> + return -ENODEV;
> + if (!buf || !len)
> + return -EINVAL;
> +
> + build_id = optee->revision.os_build_id;
> + if (build_id)
> + scnprintf(buf, len, "%u.%u (%016llx)",
> + optee->revision.os_major,
> + optee->revision.os_minor,
> + (unsigned long long)build_id);
> + else
> + scnprintf(buf, len, "%u.%u", optee->revision.os_major,
> + optee->revision.os_minor);
> +
> + return 0;
> +}
> +
> static void optee_bus_scan(struct work_struct *work)
> {
> WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP));
> diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c
> index bf8390789ecf..8fc72aa95722 100644
> --- a/drivers/tee/optee/ffa_abi.c
> +++ b/drivers/tee/optee/ffa_abi.c
> @@ -775,6 +775,39 @@ static int optee_ffa_reclaim_protmem(struct optee *optee,
> * with a matching configuration.
> */
>
> +static bool optee_ffa_get_os_revision(struct ffa_device *ffa_dev,
> + const struct ffa_ops *ops,
> + struct optee_revision *revision)
> +{
> + const struct ffa_msg_ops *msg_ops = ops->msg_ops;
> + struct ffa_send_direct_data data = {
> + .data0 = OPTEE_FFA_GET_OS_VERSION,
> + };
> + int rc;
> +
> + msg_ops->mode_32bit_set(ffa_dev);
> +
> + rc = msg_ops->sync_send_receive(ffa_dev, &data);
> + if (rc) {
> + pr_err("Unexpected error %d\n", rc);
> + return false;
> + }
> +
> + if (revision) {
> + revision->os_major = data.data0;
> + revision->os_minor = data.data1;
> + revision->os_build_id = data.data2;
> + }
> +
> + if (data.data2)
> + pr_info("revision %lu.%lu (%08lx)",
> + data.data0, data.data1, data.data2);
> + else
> + pr_info("revision %lu.%lu", data.data0, data.data1);
> +
> + return true;
> +}
> +
> static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> const struct ffa_ops *ops)
> {
> @@ -798,20 +831,6 @@ static bool optee_ffa_api_is_compatible(struct ffa_device *ffa_dev,
> return false;
> }
>
> - data = (struct ffa_send_direct_data){
> - .data0 = OPTEE_FFA_GET_OS_VERSION,
> - };
> - rc = msg_ops->sync_send_receive(ffa_dev, &data);
> - if (rc) {
> - pr_err("Unexpected error %d\n", rc);
> - return false;
> - }
> - if (data.data2)
> - pr_info("revision %lu.%lu (%08lx)",
> - data.data0, data.data1, data.data2);
> - else
> - pr_info("revision %lu.%lu", data.data0, data.data1);
> -
> return true;
> }
>
> @@ -900,6 +919,7 @@ static int optee_ffa_open(struct tee_context *ctx)
>
> static const struct tee_driver_ops optee_ffa_clnt_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -918,6 +938,7 @@ static const struct tee_desc optee_ffa_clnt_desc = {
>
> static const struct tee_driver_ops optee_ffa_supp_ops = {
> .get_version = optee_ffa_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_ffa_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1060,6 +1081,11 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev)
> if (!optee)
> return -ENOMEM;
>
> + if (!optee_ffa_get_os_revision(ffa_dev, ffa_ops, &optee->revision)) {
> + rc = -EINVAL;
> + goto err_free_optee;
> + }
> +
> pool = optee_ffa_shm_pool_alloc_pages();
> if (IS_ERR(pool)) {
> rc = PTR_ERR(pool);
> diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> index db9ea673fbca..acd3051c4879 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -171,6 +171,24 @@ struct optee_ffa {
>
> struct optee;
>
> +/**
> + * struct optee_revision - OP-TEE OS revision reported by secure world
> + * @os_major: OP-TEE OS major version
> + * @os_minor: OP-TEE OS minor version
> + * @os_build_id: OP-TEE OS build identifier (0 if unspecified)
> + *
> + * Values come from OPTEE_SMC_CALL_GET_OS_REVISION (SMC ABI) or
> + * OPTEE_FFA_GET_OS_VERSION (FF-A ABI); this is the trusted OS revision, not an
> + * FF-A ABI version.
> + */
> +struct optee_revision {
> + u32 os_major;
> + u32 os_minor;
> + u64 os_build_id;
> +};
> +
> +int optee_get_revision(struct tee_device *teedev, char *buf, size_t len);
> +
> /**
> * struct optee_ops - OP-TEE driver internal operations
> * @do_call_with_arg: enters OP-TEE in secure world
> @@ -249,6 +267,7 @@ struct optee {
> bool in_kernel_rpmb_routing;
> struct work_struct scan_bus_work;
> struct work_struct rpmb_scan_bus_work;
> + struct optee_revision revision;
> };
>
> struct optee_session {
> diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c
> index 0be663fcd52b..51fae1ab8ef8 100644
> --- a/drivers/tee/optee/smc_abi.c
> +++ b/drivers/tee/optee/smc_abi.c
> @@ -1242,6 +1242,7 @@ static int optee_smc_open(struct tee_context *ctx)
>
> static const struct tee_driver_ops optee_clnt_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_smc_open,
> .release = optee_release,
> .open_session = optee_open_session,
> @@ -1261,6 +1262,7 @@ static const struct tee_desc optee_clnt_desc = {
>
> static const struct tee_driver_ops optee_supp_ops = {
> .get_version = optee_get_version,
> + .get_tee_revision = optee_get_revision,
> .open = optee_smc_open,
> .release = optee_release_supp,
> .supp_recv = optee_supp_recv,
> @@ -1323,7 +1325,8 @@ static bool optee_msg_api_uid_is_optee_image_load(optee_invoke_fn *invoke_fn)
> }
> #endif
>
> -static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> +static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn,
> + struct optee_revision *revision)
> {
> union {
> struct arm_smccc_res smccc;
> @@ -1337,6 +1340,12 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn)
> invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0,
> &res.smccc);
>
> + if (revision) {
> + revision->os_major = res.result.major;
> + revision->os_minor = res.result.minor;
> + revision->os_build_id = res.result.build_id;
> + }
> +
> if (res.result.build_id)
> pr_info("revision %lu.%lu (%0*lx)", res.result.major,
> res.result.minor, (int)sizeof(res.result.build_id) * 2,
> @@ -1745,8 +1754,6 @@ static int optee_probe(struct platform_device *pdev)
> return -EINVAL;
> }
>
> - optee_msg_get_os_revision(invoke_fn);
> -
> if (!optee_msg_api_revision_is_compatible(invoke_fn)) {
> pr_warn("api revision mismatch\n");
> return -EINVAL;
> @@ -1815,6 +1822,8 @@ static int optee_probe(struct platform_device *pdev)
> goto err_free_shm_pool;
> }
>
> + optee_msg_get_os_revision(invoke_fn, &optee->revision);
> +
> optee->ops = &optee_ops;
> optee->smc.invoke_fn = invoke_fn;
> optee->smc.sec_caps = sec_caps;
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 74+ messages in thread
* Re: [PATCH v7 1/2] tee: add revision sysfs attribute
2026-01-12 15:48 ` Aristo Chen
(?)
(?)
@ 2026-01-14 15:42 ` Jens Wiklander
-1 siblings, 0 replies; 74+ messages in thread
From: Jens Wiklander @ 2026-01-14 15:42 UTC (permalink / raw)
To: Aristo Chen
Cc: linux-kernel, sumit.garg, op-tee, harshal.dev, mario.limonciello,
Rijo-john.Thomas, amirreza.zarrabi, Sumit Garg
On Mon, Jan 12, 2026 at 4:48 PM Aristo Chen <aristo.chen@canonical.com> wrote:
>
> Add a generic TEE revision sysfs attribute backed by a new
> optional get_tee_revision() callback. The revision string is
> diagnostic-only and must not be used to infer feature support.
>
> Signed-off-by: Aristo Chen <aristo.chen@canonical.com>
> Reviewed-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
> ---
> Documentation/ABI/testing/sysfs-class-tee | 10 +++++
> drivers/tee/tee_core.c | 51 ++++++++++++++++++++++-
> include/linux/tee_core.h | 9 ++++
> 3 files changed, 69 insertions(+), 1 deletion(-)
Looks good, I'm picking up this.
Thanks,
Jens
>
> diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee
> index c9144d16003e..1a0a3050aaa9 100644
> --- a/Documentation/ABI/testing/sysfs-class-tee
> +++ b/Documentation/ABI/testing/sysfs-class-tee
> @@ -13,3 +13,13 @@ Description:
> space if the variable is absent. The primary purpose
> of this variable is to let systemd know whether
> tee-supplicant is needed in the early boot with initramfs.
> +
> +What: /sys/class/tee/tee{,priv}X/revision
> +Date: Jan 2026
> +KernelVersion: 6.19
> +Contact: op-tee@lists.trustedfirmware.org
> +Description:
> + Read-only revision string reported by the TEE driver. This is
> + for diagnostics only and must not be used to infer feature
> + support. Use TEE_IOC_VERSION for capability and compatibility
> + checks.
> diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> index d65d47cc154e..0a00499811c1 100644
> --- a/drivers/tee/tee_core.c
> +++ b/drivers/tee/tee_core.c
> @@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = {
> NULL
> };
>
> -ATTRIBUTE_GROUPS(tee_dev);
> +static const struct attribute_group tee_dev_group = {
> + .attrs = tee_dev_attrs,
> +};
> +
> +static ssize_t revision_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> + char version[TEE_REVISION_STR_SIZE];
> + int ret;
> +
> + if (!teedev->desc->ops->get_tee_revision)
> + return -ENODEV;
> +
> + ret = teedev->desc->ops->get_tee_revision(teedev, version,
> + sizeof(version));
> + if (ret)
> + return ret;
> +
> + return sysfs_emit(buf, "%s\n", version);
> +}
> +static DEVICE_ATTR_RO(revision);
> +
> +static struct attribute *tee_revision_attrs[] = {
> + &dev_attr_revision.attr,
> + NULL
> +};
> +
> +static umode_t tee_revision_attr_is_visible(struct kobject *kobj,
> + struct attribute *attr, int n)
> +{
> + struct device *dev = kobj_to_dev(kobj);
> + struct tee_device *teedev = container_of(dev, struct tee_device, dev);
> +
> + if (teedev->desc->ops->get_tee_revision)
> + return attr->mode;
> +
> + return 0;
> +}
> +
> +static const struct attribute_group tee_revision_group = {
> + .attrs = tee_revision_attrs,
> + .is_visible = tee_revision_attr_is_visible,
> +};
> +
> +static const struct attribute_group *tee_dev_groups[] = {
> + &tee_dev_group,
> + &tee_revision_group,
> + NULL
> +};
>
> static const struct class tee_class = {
> .name = "tee",
> diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h
> index 1f3e5dad6d0d..ee5f0bd41f43 100644
> --- a/include/linux/tee_core.h
> +++ b/include/linux/tee_core.h
> @@ -76,6 +76,9 @@ struct tee_device {
> /**
> * struct tee_driver_ops - driver operations vtable
> * @get_version: returns version of driver
> + * @get_tee_revision: returns revision string (diagnostic only);
> + * do not infer feature support from this, use
> + * TEE_IOC_VERSION instead
> * @open: called for a context when the device file is opened
> * @close_context: called when the device file is closed
> * @release: called to release the context
> @@ -95,9 +98,12 @@ struct tee_device {
> * client closes the device file, even if there are existing references to the
> * context. The TEE driver can use @close_context to start cleaning up.
> */
> +
> struct tee_driver_ops {
> void (*get_version)(struct tee_device *teedev,
> struct tee_ioctl_version_data *vers);
> + int (*get_tee_revision)(struct tee_device *teedev,
> + char *buf, size_t len);
> int (*open)(struct tee_context *ctx);
> void (*close_context)(struct tee_context *ctx);
> void (*release)(struct tee_context *ctx);
> @@ -123,6 +129,9 @@ struct tee_driver_ops {
> int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
> };
>
> +/* Size for TEE revision string buffer used by get_tee_revision(). */
> +#define TEE_REVISION_STR_SIZE 128
> +
> /**
> * struct tee_desc - Describes the TEE driver to the subsystem
> * @name: name of driver
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 74+ messages in thread