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@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 */