[PATCH RFC 03/10] firmware: add RPMI TEE bus support
Amirreza Zarrabi
amirreza.zarrabi at oss.qualcomm.com
Mon Sep 28 20:16:54 PDT 2026
Add a bus for RISC-V RPMI TEE services. Each service exposed by a
TEE endpoint is registered as its own device on this bus, identified
by a UUID, so that a single physical TEE can host multiple
independently-bindable service drivers.
Signed-off-by: Amirreza Zarrabi <amirreza.zarrabi at oss.qualcomm.com>
---
drivers/firmware/Kconfig | 2 +
drivers/firmware/Makefile | 1 +
drivers/firmware/riscv_rpmi_tee/Kconfig | 8 ++
drivers/firmware/riscv_rpmi_tee/Makefile | 5 +
drivers/firmware/riscv_rpmi_tee/bus.c | 202 +++++++++++++++++++++++++++++++
include/linux/rpmi_tee.h | 83 +++++++++++++
6 files changed, 301 insertions(+)
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index c183d98c1e8f..46297332288f 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -6,6 +6,8 @@
menu "Firmware Drivers"
+source "drivers/firmware/riscv_rpmi_tee/Kconfig"
+
source "drivers/firmware/arm_scmi/Kconfig"
config ARM_SCPI_PROTOCOL
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index a855d3696173..772fe024baab 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_TURRIS_MOX_RWTM) += turris-mox-rwtm.o
obj-y += arm_ffa/
obj-y += arm_scmi/
+obj-y += riscv_rpmi_tee/
obj-y += broadcom/
obj-y += cirrus/
obj-y += meson/
diff --git a/drivers/firmware/riscv_rpmi_tee/Kconfig b/drivers/firmware/riscv_rpmi_tee/Kconfig
new file mode 100644
index 000000000000..b0396183b87d
--- /dev/null
+++ b/drivers/firmware/riscv_rpmi_tee/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config RISCV_RPMI_TEE_TRANSPORT
+ tristate "RISC-V RPMI TEE service group transport"
+ depends on RISCV && OF && RISCV_SBI_MPXY_MBOX
+ help
+ Say Y or M here to enable the transport for services provided by the
+ RISC-V RPMI TEE service group.
diff --git a/drivers/firmware/riscv_rpmi_tee/Makefile b/drivers/firmware/riscv_rpmi_tee/Makefile
new file mode 100644
index 000000000000..d80ab5bc9dc4
--- /dev/null
+++ b/drivers/firmware/riscv_rpmi_tee/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+rpmi-tee-bus-y = bus.o
+rpmi-tee-core-objs := $(rpmi-tee-bus-y)
+obj-$(CONFIG_RISCV_RPMI_TEE_TRANSPORT) = rpmi-tee-core.o
diff --git a/drivers/firmware/riscv_rpmi_tee/bus.c b/drivers/firmware/riscv_rpmi_tee/bus.c
new file mode 100644
index 000000000000..32f555c2f1f2
--- /dev/null
+++ b/drivers/firmware/riscv_rpmi_tee/bus.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * RISC-V RPMI TEE bus
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/device.h>
+#include <linux/ida.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rpmi_tee.h>
+#include <linux/slab.h>
+
+#define RPMI_TEE_UEVENT_MODALIAS_FMT "rpmi_tee:%pUb"
+
+static DEFINE_IDA(rpmi_tee_bus_id);
+
+static int rpmi_tee_device_match(struct device *dev,
+ const struct device_driver *drv)
+{
+ const struct rpmi_tee_device_id *id_table;
+ struct rpmi_tee_device *rdev = to_rpmi_tee_dev(dev);
+
+ id_table = to_rpmi_tee_drv(drv)->id_table;
+ if (!id_table)
+ return 0;
+
+ while (!uuid_is_null(&id_table->uuid)) {
+ if (uuid_equal(&rdev->uuid, &id_table->uuid))
+ return 1;
+ id_table++;
+ }
+
+ return 0;
+}
+
+static int rpmi_tee_device_probe(struct device *dev)
+{
+ struct rpmi_tee_driver *rdrv = to_rpmi_tee_drv(dev->driver);
+
+ return rdrv->probe(to_rpmi_tee_dev(dev));
+}
+
+static void rpmi_tee_device_remove(struct device *dev)
+{
+ struct rpmi_tee_driver *rdrv = to_rpmi_tee_drv(dev->driver);
+
+ if (rdrv->remove)
+ rdrv->remove(to_rpmi_tee_dev(dev));
+}
+
+static int rpmi_tee_device_uevent(const struct device *dev,
+ struct kobj_uevent_env *env)
+{
+ const struct rpmi_tee_device *rdev = to_rpmi_tee_dev(dev);
+
+ return add_uevent_var(env, "MODALIAS=" RPMI_TEE_UEVENT_MODALIAS_FMT,
+ &rdev->uuid);
+}
+
+static ssize_t endpoint_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rpmi_tee_device *rdev = to_rpmi_tee_dev(dev);
+
+ return sysfs_emit(buf, "0x%x\n", rdev->endpoint_id);
+}
+static DEVICE_ATTR_RO(endpoint_id);
+
+static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct rpmi_tee_device *rdev = to_rpmi_tee_dev(dev);
+
+ return sysfs_emit(buf, "%pUb\n", &rdev->uuid);
+}
+static DEVICE_ATTR_RO(uuid);
+
+static ssize_t modalias_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rpmi_tee_device *rdev = to_rpmi_tee_dev(dev);
+
+ return sysfs_emit(buf, RPMI_TEE_UEVENT_MODALIAS_FMT, &rdev->uuid);
+}
+static DEVICE_ATTR_RO(modalias);
+
+static struct attribute *rpmi_tee_device_attrs[] = {
+ &dev_attr_endpoint_id.attr,
+ &dev_attr_uuid.attr,
+ &dev_attr_modalias.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(rpmi_tee_device);
+
+const struct bus_type rpmi_tee_bus_type = {
+ .name = "rpmi_tee",
+ .match = rpmi_tee_device_match,
+ .probe = rpmi_tee_device_probe,
+ .remove = rpmi_tee_device_remove,
+ .uevent = rpmi_tee_device_uevent,
+ .dev_groups = rpmi_tee_device_groups,
+};
+EXPORT_SYMBOL_GPL(rpmi_tee_bus_type);
+
+int rpmi_tee_driver_register(struct rpmi_tee_driver *driver,
+ struct module *owner, const char *mod_name)
+{
+ if (!driver->probe || !driver->id_table)
+ return -EINVAL;
+
+ driver->driver.bus = &rpmi_tee_bus_type;
+ driver->driver.name = driver->name;
+ driver->driver.owner = owner;
+ driver->driver.mod_name = mod_name;
+
+ return driver_register(&driver->driver);
+}
+EXPORT_SYMBOL_GPL(rpmi_tee_driver_register);
+
+void rpmi_tee_driver_unregister(struct rpmi_tee_driver *driver)
+{
+ driver_unregister(&driver->driver);
+}
+EXPORT_SYMBOL_GPL(rpmi_tee_driver_unregister);
+
+static void rpmi_tee_device_release(struct device *dev)
+{
+ struct rpmi_tee_device *rdev = to_rpmi_tee_dev(dev);
+
+ ida_free(&rpmi_tee_bus_id, rdev->id);
+ kfree(rdev);
+}
+
+struct rpmi_tee_device *
+rpmi_tee_device_register(const uuid_t *uuid, u32 endpoint_id,
+ const struct rpmi_tee_ops *ops, struct device *parent)
+{
+ struct rpmi_tee_device *rdev;
+ int id;
+ int ret;
+
+ if (!uuid || !ops)
+ return ERR_PTR(-EINVAL);
+
+ id = ida_alloc_min(&rpmi_tee_bus_id, 1, GFP_KERNEL);
+ if (id < 0)
+ return ERR_PTR(id);
+
+ rdev = kzalloc_obj(*rdev, GFP_KERNEL);
+ if (!rdev) {
+ ida_free(&rpmi_tee_bus_id, id);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ rdev->dev.parent = parent;
+ rdev->dev.bus = &rpmi_tee_bus_type;
+ rdev->dev.release = rpmi_tee_device_release;
+ dev_set_name(&rdev->dev, "rpmi-tee-%d", id);
+
+ rdev->id = id;
+ rdev->endpoint_id = endpoint_id;
+ rdev->ops = ops;
+ uuid_copy(&rdev->uuid, uuid);
+
+ ret = device_register(&rdev->dev);
+ if (ret) {
+ put_device(&rdev->dev);
+ return ERR_PTR(ret);
+ }
+
+ return rdev;
+}
+EXPORT_SYMBOL_GPL(rpmi_tee_device_register);
+
+void rpmi_tee_device_unregister(struct rpmi_tee_device *rdev)
+{
+ if (rdev)
+ device_unregister(&rdev->dev);
+}
+EXPORT_SYMBOL_GPL(rpmi_tee_device_unregister);
+
+static int __init rpmi_tee_bus_init(void)
+{
+ return bus_register(&rpmi_tee_bus_type);
+}
+
+subsys_initcall(rpmi_tee_bus_init);
+
+static void __exit rpmi_tee_bus_exit(void)
+{
+ bus_unregister(&rpmi_tee_bus_type);
+ ida_destroy(&rpmi_tee_bus_id);
+}
+
+module_exit(rpmi_tee_bus_exit);
+
+MODULE_DESCRIPTION("RISC-V RPMI TEE bus");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/rpmi_tee.h b/include/linux/rpmi_tee.h
new file mode 100644
index 000000000000..c499f0427833
--- /dev/null
+++ b/include/linux/rpmi_tee.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * RISC-V RPMI TEE transport interface
+ */
+
+#ifndef _LINUX_RPMI_TEE_H
+#define _LINUX_RPMI_TEE_H
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/uuid.h>
+
+struct rpmi_tee_ops;
+
+struct rpmi_tee_device {
+ struct device dev;
+ u32 id;
+ u32 endpoint_id; /* RPMI endpoint identifier of the TEE. */
+ uuid_t uuid; /* UUID identifying the TEE service. */
+ const struct rpmi_tee_ops *ops;
+};
+
+#define to_rpmi_tee_dev(d) container_of(d, struct rpmi_tee_device, dev)
+
+struct rpmi_tee_device_id {
+ uuid_t uuid;
+};
+
+struct rpmi_tee_driver {
+ const char *name;
+ int (*probe)(struct rpmi_tee_device *rdev);
+ void (*remove)(struct rpmi_tee_device *rdev);
+ /* NULL-UUID-terminated list of supported service UUIDs. */
+ const struct rpmi_tee_device_id *id_table;
+ struct device_driver driver;
+};
+
+#define to_rpmi_tee_drv(d) \
+ container_of_const(d, struct rpmi_tee_driver, driver)
+
+extern const struct bus_type rpmi_tee_bus_type;
+
+#if IS_REACHABLE(CONFIG_RISCV_RPMI_TEE_TRANSPORT)
+struct rpmi_tee_device *
+rpmi_tee_device_register(const uuid_t *uuid, u32 endpoint_id,
+ const struct rpmi_tee_ops *ops, struct device *parent);
+void rpmi_tee_device_unregister(struct rpmi_tee_device *rdev);
+int rpmi_tee_driver_register(struct rpmi_tee_driver *driver,
+ struct module *owner, const char *mod_name);
+void rpmi_tee_driver_unregister(struct rpmi_tee_driver *driver);
+#else
+static inline struct rpmi_tee_device *
+rpmi_tee_device_register(const uuid_t *uuid, u32 endpoint_id,
+ const struct rpmi_tee_ops *ops, struct device *parent)
+{
+ return NULL;
+}
+
+static inline void rpmi_tee_device_unregister(struct rpmi_tee_device *rdev)
+{
+}
+
+static inline int rpmi_tee_driver_register(struct rpmi_tee_driver *driver,
+ struct module *owner,
+ const char *mod_name)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void rpmi_tee_driver_unregister(struct rpmi_tee_driver *driver)
+{
+}
+#endif
+
+#define rpmi_tee_register(driver) \
+ rpmi_tee_driver_register(driver, THIS_MODULE, KBUILD_MODNAME)
+#define rpmi_tee_unregister(driver) \
+ rpmi_tee_driver_unregister(driver)
+
+#endif /* _LINUX_RPMI_TEE_H */
--
2.34.1
More information about the linux-riscv
mailing list