[PATCH RFC v2 06/10] firmware: riscv: rpmi-tee: discover TEE endpoints

Amirreza Zarrabi amirreza.zarrabi at oss.qualcomm.com
Wed Oct 7 20:27:12 PDT 2026


Add discovery through the PROBE_SYSTEM, PROBE_DOMAIN and PROBE_ENDPOINT
services. Obtain the caller's endpoint ID and enumerate reachable
physical TEE endpoints and their service UUIDs.

Signed-off-by: Amirreza Zarrabi <amirreza.zarrabi at oss.qualcomm.com>
---
 drivers/firmware/riscv_rpmi_tee/Makefile           |   2 +-
 drivers/firmware/riscv_rpmi_tee/discovery.c        | 383 +++++++++++++++++++++
 drivers/firmware/riscv_rpmi_tee/driver.c           |  29 ++
 drivers/firmware/riscv_rpmi_tee/rpmi_tee_private.h |  42 +++
 4 files changed, 455 insertions(+), 1 deletion(-)

diff --git a/drivers/firmware/riscv_rpmi_tee/Makefile b/drivers/firmware/riscv_rpmi_tee/Makefile
index 8984127535ed..fc6edfb21dbc 100644
--- a/drivers/firmware/riscv_rpmi_tee/Makefile
+++ b/drivers/firmware/riscv_rpmi_tee/Makefile
@@ -3,6 +3,6 @@
 rpmi-tee-bus-y = bus.o
 rpmi-tee-core-objs := $(rpmi-tee-bus-y)
 obj-$(CONFIG_RISCV_RPMI_TEE_TRANSPORT) = rpmi-tee-core.o
-rpmi-tee-driver-y = driver.o
+rpmi-tee-driver-y = driver.o discovery.o
 rpmi-tee-module-objs := $(rpmi-tee-driver-y)
 obj-$(CONFIG_RISCV_RPMI_TEE_TRANSPORT) += rpmi-tee-module.o
diff --git a/drivers/firmware/riscv_rpmi_tee/discovery.c b/drivers/firmware/riscv_rpmi_tee/discovery.c
new file mode 100644
index 000000000000..fedd68bba639
--- /dev/null
+++ b/drivers/firmware/riscv_rpmi_tee/discovery.c
@@ -0,0 +1,383 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/bits.h>
+#include <linux/errno.h>
+#include <linux/cleanup.h>
+#include <linux/mailbox/riscv-rpmi-message.h>
+#include <linux/slab.h>
+#include <linux/unaligned.h>
+
+#include "rpmi_tee_private.h"
+
+/* Role flags in PROBE_DOMAIN and PROBE_ENDPOINT responses. */
+#define RPMI_TEE_DOMAIN_TEE		BIT(31)
+#define RPMI_TEE_ENDPOINT_TEE		BIT(31)
+#define RPMI_TEE_ENDPOINT_PHYSICAL	BIT(30)
+
+/**
+ * struct rpmi_tee_probe_system_resp - PROBE_SYSTEM response prefix
+ * @status: RPMI completion status.
+ * @caller_domain: Calling endpoint's domain ID.
+ * @caller_endpoint: Calling physical endpoint ID.
+ * @domain_count: Number of entries in @domains.
+ * @domains: Reachable domain identifiers.
+ */
+struct rpmi_tee_probe_system_resp {
+	__le32 status;
+	__le32 caller_domain;
+	__le32 caller_endpoint;
+	__le32 domain_count;
+	__le32 domains[];
+} __packed;
+
+/**
+ * struct rpmi_tee_probe_domain_req - PROBE_DOMAIN request
+ * @domain_id: Domain identifier from PROBE_SYSTEM.
+ */
+struct rpmi_tee_probe_domain_req {
+	__le32 domain_id;
+} __packed;
+
+/**
+ * struct rpmi_tee_probe_domain_resp - PROBE_DOMAIN response prefix
+ * @status: RPMI completion status.
+ * @flags: Domain role flags.
+ * @endpoint_count: Number of entries in @endpoints.
+ * @endpoints: Physical and proxied endpoint IDs.
+ */
+struct rpmi_tee_probe_domain_resp {
+	__le32 status;
+	__le32 flags;
+	__le32 endpoint_count;
+	__le32 endpoints[];
+} __packed;
+
+/**
+ * struct rpmi_tee_probe_endpoint_req - PROBE_ENDPOINT request
+ * @endpoint_id: Endpoint identifier from PROBE_DOMAIN.
+ */
+struct rpmi_tee_probe_endpoint_req {
+	__le32 endpoint_id;
+} __packed;
+
+/**
+ * struct rpmi_tee_probe_endpoint_resp - PROBE_ENDPOINT response prefix
+ * @status: RPMI completion status.
+ * @flags: Endpoint role and lifetime flags.
+ * @name: Debug name, not a matching identity.
+ * @proxied_count: Number of proxied endpoint IDs in @data.
+ * @parcel_count: Number of parcel IDs in @data.
+ * @service_count: Number of 16-byte service UUIDs in @data.
+ * @metadata_len: Metadata length in bytes.
+ * @data: Proxied endpoint IDs, parcel IDs, service UUIDs, then metadata.
+ */
+struct rpmi_tee_probe_endpoint_resp {
+	__le32 status;
+	__le32 flags;
+	u8 name[32];
+	__le32 proxied_count;
+	__le32 parcel_count;
+	__le32 service_count;
+	__le32 metadata_len;
+	u8 data[];
+} __packed;
+
+struct rpmi_tee_system_info {
+	u32 caller_domain;
+	u32 caller_endpoint;
+	u32 domain_count;
+	const u8 *domains;
+};
+
+/* Return the domain ID at a validated index in the system response. */
+static inline u32
+rpmi_tee_domain_id_at(const struct rpmi_tee_system_info *info, u32 index)
+{
+	return get_unaligned_le32(info->domains +
+				  (size_t)index * sizeof(__le32));
+}
+
+struct rpmi_tee_domain_info {
+	u32 flags;
+	u32 endpoint_count;
+	const u8 *endpoints;
+};
+
+/* Return the endpoint ID at a validated index in the domain response. */
+static inline u32
+rpmi_tee_endpoint_id_at(const struct rpmi_tee_domain_info *info, u32 index)
+{
+	return get_unaligned_le32(info->endpoints +
+				  (size_t)index * sizeof(__le32));
+}
+
+struct rpmi_tee_endpoint_info {
+	u32 flags;
+	u32 service_count;
+	const u8 *services;
+};
+
+/* Copy the service UUID at a validated index in the endpoint response. */
+static inline void
+rpmi_tee_service_uuid_at(const struct rpmi_tee_endpoint_info *info,
+			 u32 index, uuid_t *uuid)
+{
+	import_uuid(uuid, info->services + (size_t)index * UUID_SIZE);
+}
+
+/* rpmi_tee_parse_*_response parse and validate the response. */
+
+static int rpmi_tee_parse_system_response(const void *data, size_t len,
+					  struct rpmi_tee_system_info *info)
+{
+	const struct rpmi_tee_probe_system_resp *resp = data;
+	u32 count;
+
+	if (len < sizeof(*resp))
+		return -EPROTO;
+
+	count = get_unaligned_le32(&resp->domain_count);
+	if (len != sizeof(*resp) + (u64)count * sizeof(__le32))
+		return -EPROTO;
+
+	info->caller_domain = get_unaligned_le32(&resp->caller_domain);
+	info->caller_endpoint = get_unaligned_le32(&resp->caller_endpoint);
+	info->domain_count = count;
+	/* Borrowed pointer, use rpmi_tee_domain_id_at() to access. */
+	info->domains = (const u8 *)data + sizeof(*resp);
+
+	return 0;
+}
+
+static int rpmi_tee_parse_domain_response(const void *data, size_t len,
+					  struct rpmi_tee_domain_info *info)
+{
+	const struct rpmi_tee_probe_domain_resp *resp = data;
+	u32 count;
+
+	if (len < sizeof(*resp))
+		return -EPROTO;
+
+	count = get_unaligned_le32(&resp->endpoint_count);
+	if (len != sizeof(*resp) + (u64)count * sizeof(__le32))
+		return -EPROTO;
+
+	info->flags = get_unaligned_le32(&resp->flags);
+	info->endpoint_count = count;
+	/* Borrowed pointer, use rpmi_tee_endpoint_id_at() to access. */
+	info->endpoints = (const u8 *)data + sizeof(*resp);
+
+	return 0;
+}
+
+static int rpmi_tee_parse_endpoint_response(const void *data, size_t len,
+					    struct rpmi_tee_endpoint_info *info)
+{
+	const struct rpmi_tee_probe_endpoint_resp *resp = data;
+	size_t remaining, service_offset;
+	u32 proxied, parcels, services;
+
+	if (len < sizeof(*resp))
+		return -EPROTO;
+
+	remaining = len - sizeof(*resp);
+	proxied = get_unaligned_le32(&resp->proxied_count);
+	parcels = get_unaligned_le32(&resp->parcel_count);
+	services = get_unaligned_le32(&resp->service_count);
+
+	/* Check both ID arrays before adding their counts. */
+	if (proxied > remaining / sizeof(__le32) ||
+	    parcels > remaining / sizeof(__le32) - proxied)
+		return -EPROTO;
+
+	service_offset = ((size_t)proxied + parcels) * sizeof(__le32);
+	remaining -= service_offset;
+	if (services > remaining / UUID_SIZE)
+		return -EPROTO;
+
+	/* Ignore metadata following the service UUID array. */
+	info->flags = get_unaligned_le32(&resp->flags);
+	info->service_count = services;
+	/* Borrowed pointer, use rpmi_tee_service_uuid_at() to get UUID. */
+	info->services = resp->data + service_offset;
+
+	return 0;
+}
+
+/* Return an owned probe response; vanished domain/endpoint IDs yield -ENOENT. */
+static int rpmi_tee_probe_info(struct rpmi_tee_transport *priv, u32 service,
+			       const void *req, size_t req_len, void **data,
+			       size_t *data_len)
+{
+	size_t resp_len = priv->mbox.max_msg_data_size;
+	s32 status;
+	int ret;
+
+	void *resp __free(kfree) = kzalloc(resp_len, GFP_KERNEL);
+	if (!resp)
+		return -ENOMEM;
+
+	ret = rpmi_tee_send_with_status(priv, service, req, req_len, resp,
+					&resp_len, &status);
+	if (ret)
+		return ret;
+
+	if (status == RPMI_ERR_INVALID_PARAM &&
+	    (service == RPMI_TEE_SRV_PROBE_DOMAIN ||
+	     service == RPMI_TEE_SRV_PROBE_ENDPOINT))
+		return -ENOENT;
+
+	ret = rpmi_to_linux_error(status);
+	if (ret)
+		return ret;
+
+	*data_len = resp_len;
+	*data = no_free_ptr(resp);
+
+	return 0;
+}
+
+static int rpmi_tee_add_endpoint(struct rpmi_tee_discovery *system, u32 ep_id,
+				 const struct rpmi_tee_endpoint_info *info)
+{
+	struct rpmi_tee_discovered_endpoint *ep;
+	u32 i;
+
+	/* Skip endpoints already discovered. */
+	list_for_each_entry(ep, &system->eps, node)
+		if (ep->ep_id == ep_id)
+			return 0;
+
+	ep = kzalloc(struct_size(ep, services, info->service_count), GFP_KERNEL);
+	if (!ep)
+		return -ENOMEM;
+
+	ep->ep_id = ep_id;
+	ep->service_count = info->service_count;
+	for (i = 0; i < ep->service_count; i++)
+		rpmi_tee_service_uuid_at(info, i, &ep->services[i]);
+
+	list_add_tail(&ep->node, &system->eps);
+
+	return 0;
+}
+
+/* Discover physical TEE endpoints in one domain, skipping vanished IDs. */
+static int rpmi_tee_discover_domain(struct rpmi_tee_transport *priv, u32 id,
+				    struct rpmi_tee_discovery *system)
+{
+	struct rpmi_tee_domain_info domain;
+	struct rpmi_tee_probe_domain_req req = {
+		.domain_id = cpu_to_le32(id),
+	};
+	size_t len;
+	int ret;
+	u32 i;
+
+	void *data __free(kfree) = NULL;
+
+	ret = rpmi_tee_probe_info(priv, RPMI_TEE_SRV_PROBE_DOMAIN, &req,
+				  sizeof(req), &data, &len);
+	if (ret)
+		return ret == -ENOENT ? 0 : ret;
+
+	ret = rpmi_tee_parse_domain_response(data, len, &domain);
+	/* Only TEE domains are processed. */
+	if (ret || !(domain.flags & RPMI_TEE_DOMAIN_TEE))
+		return ret;
+
+	for (i = 0; i < domain.endpoint_count; i++) {
+		u32 ep_id = rpmi_tee_endpoint_id_at(&domain, i);
+		struct rpmi_tee_endpoint_info endpoint;
+		struct rpmi_tee_probe_endpoint_req ep_req = {
+			.endpoint_id = cpu_to_le32(ep_id),
+		};
+
+		void *ep_data __free(kfree) = NULL;
+
+		ret = rpmi_tee_probe_info(priv, RPMI_TEE_SRV_PROBE_ENDPOINT,
+					  &ep_req, sizeof(ep_req), &ep_data,
+					  &len);
+		if (ret == -ENOENT)
+			continue;
+
+		if (ret)
+			return ret;
+
+		ret = rpmi_tee_parse_endpoint_response(ep_data, len, &endpoint);
+		if (ret)
+			return ret;
+
+		/* Only physical TEE endpoints are processed. */
+		if (ep_id == priv->self_id ||
+		    !(endpoint.flags & RPMI_TEE_ENDPOINT_TEE) ||
+		    !(endpoint.flags & RPMI_TEE_ENDPOINT_PHYSICAL))
+			continue;
+
+		ret = rpmi_tee_add_endpoint(system, ep_id, &endpoint);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/* Free all discovered endpoints and their copied service UUIDs. */
+void rpmi_tee_free_discovery(struct rpmi_tee_discovery *system)
+{
+	struct rpmi_tee_discovered_endpoint *ep, *next;
+
+	list_for_each_entry_safe(ep, next, &system->eps, node) {
+		list_del(&ep->node);
+		kfree(ep);
+	}
+}
+
+/**
+ * rpmi_tee_discover_endpoints() - Discover physical TEE endpoints and services
+ * @priv: RPMI TEE transport
+ * @system: Returned list of endpoints and their service UUIDs
+ *
+ * Obtain caller identity and walk SYSTEM, DOMAIN and ENDPOINT probes. The
+ * caller releases the entries with rpmi_tee_free_discovery() on success.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+int rpmi_tee_discover_endpoints(struct rpmi_tee_transport *priv,
+				struct rpmi_tee_discovery *system)
+{
+	struct rpmi_tee_system_info info;
+	size_t len;
+	int ret;
+	u32 i;
+
+	void *data __free(kfree) = NULL;
+
+	INIT_LIST_HEAD(&system->eps);
+	/* PROBE_SYSTEM has no request data and returns the caller's identity. */
+	ret = rpmi_tee_probe_info(priv, RPMI_TEE_SRV_PROBE_SYSTEM, NULL, 0,
+				  &data, &len);
+	if (ret)
+		return ret;
+
+	ret = rpmi_tee_parse_system_response(data, len, &info);
+	if (ret)
+		return ret;
+
+	priv->self_id = info.caller_endpoint;
+	for (i = 0; i < info.domain_count; i++) {
+		u32 domain_id = rpmi_tee_domain_id_at(&info, i);
+
+		ret = rpmi_tee_discover_domain(priv, domain_id, system);
+		if (ret) {
+			rpmi_tee_free_discovery(system);
+
+			return ret;
+		}
+	}
+
+	return 0;
+}
diff --git a/drivers/firmware/riscv_rpmi_tee/driver.c b/drivers/firmware/riscv_rpmi_tee/driver.c
index d9ae161d241b..a70b8be41bed 100644
--- a/drivers/firmware/riscv_rpmi_tee/driver.c
+++ b/drivers/firmware/riscv_rpmi_tee/driver.c
@@ -5,6 +5,7 @@
  * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  */
 
+#include <linux/unaligned.h>
 #include <linux/mailbox_client.h>
 #include <linux/mailbox/riscv-rpmi-message.h>
 #include <linux/module.h>
@@ -14,6 +15,34 @@
 
 #include "rpmi_tee_private.h"
 
+/* rpmi_tee_send_with_status() - Send an RPMI TEE service request. */
+int rpmi_tee_send_with_status(struct rpmi_tee_transport *priv, u32 service_id,
+			      const void *req, size_t req_len, void *resp,
+			      size_t *resp_len, s32 *status)
+{
+	size_t max_resp_len = *resp_len;
+	struct rpmi_mbox_message msg;
+	int ret;
+
+	if (req_len > priv->mbox.max_msg_data_size ||
+	    max_resp_len > priv->mbox.max_msg_data_size)
+		return -EMSGSIZE;
+
+	rpmi_mbox_init_send_with_response(&msg, service_id, (void *)req,
+					  req_len, resp, max_resp_len);
+	ret = rpmi_mbox_send_message_sync(priv->mbox.chan, &msg);
+	if (ret)
+		return ret;
+	/* At least STATUS word should be present. */
+	if (msg.data.out_response_len < sizeof(__le32))
+		return -EPROTO;
+
+	*resp_len = msg.data.out_response_len;
+	*status = (s32)get_unaligned_le32(resp);
+
+	return 0;
+}
+
 /**
  * rpmi_tee_get_attr() - Get an RPMI mailbox attribute
  * @priv: RPMI TEE transport
diff --git a/drivers/firmware/riscv_rpmi_tee/rpmi_tee_private.h b/drivers/firmware/riscv_rpmi_tee/rpmi_tee_private.h
index b25192c5df00..ba0e7cb59b6d 100644
--- a/drivers/firmware/riscv_rpmi_tee/rpmi_tee_private.h
+++ b/drivers/firmware/riscv_rpmi_tee/rpmi_tee_private.h
@@ -6,12 +6,18 @@
 #ifndef _RPMI_TEE_PRIVATE_H
 #define _RPMI_TEE_PRIVATE_H
 
+#include <linux/list.h>
 #include <linux/mailbox_client.h>
 #include <linux/types.h>
+#include <linux/uuid.h>
 
 /* TEE service group and the services used by this module. */
 #define RPMI_SRVGRP_TEE		0x10
 
+#define RPMI_TEE_SRV_PROBE_SYSTEM	0x03
+#define RPMI_TEE_SRV_PROBE_DOMAIN	0x04
+#define RPMI_TEE_SRV_PROBE_ENDPOINT	0x05
+
 struct rpmi_tee_mbox {
 	struct mbox_client client;
 	struct mbox_chan *chan;
@@ -21,6 +27,42 @@ struct rpmi_tee_mbox {
 struct rpmi_tee_transport {
 	struct device *dev;
 	struct rpmi_tee_mbox mbox;
+	u32 self_id;
+};
+
+/* Report local transport errors separately from the returned RPMI status. */
+int rpmi_tee_send_with_status(struct rpmi_tee_transport *priv, u32 service_id,
+			      const void *req, size_t req_len, void *resp,
+			      size_t *resp_len, s32 *status);
+
+/**
+ * struct rpmi_tee_discovered_endpoint - Physical TEE endpoint and its services
+ * @node: Entry in &struct rpmi_tee_discovery.eps.
+ * @ep_id: Physical TEE endpoint ID.
+ * @service_count: Number of UUIDs in @services.
+ * @services: Service UUIDs for the endpoint.
+ */
+struct rpmi_tee_discovered_endpoint {
+	struct list_head node;
+	u32 ep_id;
+	u32 service_count;
+	uuid_t services[] __counted_by(service_count);
 };
 
+/**
+ * struct rpmi_tee_discovery - Discovered physical TEE endpoints and services
+ * @eps: List of &struct rpmi_tee_discovered_endpoint entries, excluding the
+ *	 caller endpoint.
+ *
+ * On successful discovery, the caller owns the entries and releases them with
+ * rpmi_tee_free_discovery(). Endpoints without services are also included.
+ */
+struct rpmi_tee_discovery {
+	struct list_head eps;
+};
+
+void rpmi_tee_free_discovery(struct rpmi_tee_discovery *system);
+int rpmi_tee_discover_endpoints(struct rpmi_tee_transport *priv,
+				struct rpmi_tee_discovery *system);
+
 #endif /* _RPMI_TEE_PRIVATE_H */

-- 
2.34.1




More information about the linux-riscv mailing list