Register discovered TEE services as RPMI TEE bus devices so service drivers can bind to them.
Provide TEE_CALL operations for exchanging service-defined requests and responses, and expose the transport's payload limits to clients.
Signed-off-by: Amirreza Zarrabi amirreza.zarrabi@oss.qualcomm.com --- drivers/firmware/riscv_rpmi_tee/driver.c | 278 ++++++++++++++++++++- drivers/firmware/riscv_rpmi_tee/rpmi_tee_private.h | 13 + include/linux/rpmi_tee.h | 35 +++ 3 files changed, 325 insertions(+), 1 deletion(-)
diff --git a/drivers/firmware/riscv_rpmi_tee/driver.c b/drivers/firmware/riscv_rpmi_tee/driver.c index a70b8be41bed..a683f7d28c25 100644 --- a/drivers/firmware/riscv_rpmi_tee/driver.c +++ b/drivers/firmware/riscv_rpmi_tee/driver.c @@ -5,16 +5,52 @@ * 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/cleanup.h> +#include <linux/list.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/rpmi_tee.h> +#include <linux/slab.h> +#include <linux/unaligned.h>
#include "rpmi_tee_private.h"
+/** + * struct rpmi_tee_call_req - TEE_CALL request prefix + * @sender_id: Calling REE endpoint identifier. + * @target_id: Destination TEE endpoint identifier. + * @service: UUID of the target service. + * @service_data_len: Length of @service_data in bytes. + * @service_data: Service-defined request data. + */ +struct rpmi_tee_call_req { + __le32 sender_id; + __le32 target_id; + u8 service[UUID_SIZE]; + __le32 service_data_len; + u8 service_data[]; +} __packed; + +/** + * struct rpmi_tee_call_resp - TEE_CALL response prefix + * @status: RPMI completion status. + * @service_data_len: Length of @service_data in bytes. + * @service_data: Service-defined response data. + */ +struct rpmi_tee_call_resp { + __le32 status; + __le32 service_data_len; + u8 service_data[]; +} __packed; + +struct rpmi_tee_child { + struct list_head node; + struct rpmi_tee_device *rdev; +}; + /* 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, @@ -43,6 +79,47 @@ int rpmi_tee_send_with_status(struct rpmi_tee_transport *priv, u32 service_id, return 0; }
+/** + * rpmi_tee_send() - Send an RPMI TEE service request + * @priv: RPMI TEE transport + * @service_id: RPMI TEE service identifier + * @req: Request data + * @req_len: Request data length + * @resp: Response data buffer, or %NULL for a status-only response + * @resp_len: On entry, response buffer capacity; on success, response length + * + * Pass both @resp and @resp_len as %NULL when the service has no response + * payload beyond the mandatory RPMI status word. + * + * Return: 0 on success, or a negative error code. + */ +static int rpmi_tee_send(struct rpmi_tee_transport *priv, u32 service_id, + const void *req, size_t req_len, void *resp, + size_t *resp_len) +{ + __le32 status_resp; + size_t status_resp_len = sizeof(status_resp); + s32 status; + int ret; + + if (!resp && !resp_len) { + resp = &status_resp; + resp_len = &status_resp_len; + } else if (!resp || !resp_len) { + return -EINVAL; + } + + ret = rpmi_tee_send_with_status(priv, service_id, req, req_len, resp, + resp_len, &status); + if (ret) + return ret; + + if (status == RPMI_ERR_NO_DATA) + return -ENODATA; + + return rpmi_to_linux_error(status); +} + /** * rpmi_tee_get_attr() - Get an RPMI mailbox attribute * @priv: RPMI TEE transport @@ -95,12 +172,202 @@ static int rpmi_tee_check_transport(struct rpmi_tee_transport *priv) ret = rpmi_tee_get_attr(priv, RPMI_MBOX_ATTR_MAX_MSG_DATA_SIZE, &value); if (ret) return ret; + /* The mandatory TEE_CALL request and response must fit the mailbox. */ + if (value < sizeof(struct rpmi_tee_call_req) || + value < sizeof(struct rpmi_tee_call_resp)) + return -EMSGSIZE;
priv->mbox.max_msg_data_size = value; + priv->max_call_req_size = value - sizeof(struct rpmi_tee_call_req); + priv->max_call_resp_size = value - sizeof(struct rpmi_tee_call_resp); + + return 0; +} + +/* RPMI TEE SERVICE GRP API. */ + +/* Return the transport that owns @rdev. */ +static struct rpmi_tee_transport * +rpmi_tee_device_to_transport(struct rpmi_tee_device *rdev) +{ + return dev_get_drvdata(rdev->dev.parent); +} + +static int rpmi_tee_op_msg_limits_get(struct rpmi_tee_device *rdev, + struct rpmi_tee_msg_limits *limits) +{ + struct rpmi_tee_transport *priv = rpmi_tee_device_to_transport(rdev); + + if (!limits) + return -EINVAL; + + limits->max_req_size = priv->max_call_req_size; + limits->max_resp_size = priv->max_call_resp_size; + + return 0; +} + +/** + * rpmi_tee_op_call - Invoke a service offered by a TEE endpoint + * @rdev: TEE service device. + * @req: Service-defined request data. + * @req_len: Length of @req in bytes. + * @resp: Buffer for service-defined response data. + * @resp_len: On entry, capacity of @resp; on success, response length. + * + * MPXY can return -ENOSPC after the TEE has processed the request when the + * response exceeds the supplied buffer. Callers must not blindly retry a + * non-idempotent request in that case. + * + * Return: 0 on success, or a negative error code. + */ +static int rpmi_tee_op_call(struct rpmi_tee_device *rdev, const void *req, + size_t req_len, void *resp, size_t *resp_len) +{ + struct rpmi_tee_transport *priv = rpmi_tee_device_to_transport(rdev); + size_t call_req_len, call_resp_len; + u32 service_data_len; + int ret; + + if (!resp_len || (!req && req_len) || (!resp && *resp_len)) + return -EINVAL; + + /* TEE_CALL payload must fit the mailbox. */ + if (req_len > priv->max_call_req_size || + *resp_len > priv->max_call_resp_size) + return -EMSGSIZE; + + call_req_len = sizeof(struct rpmi_tee_call_req) + req_len; + call_resp_len = sizeof(struct rpmi_tee_call_resp) + *resp_len; + + struct rpmi_tee_call_req *call_req __free(kfree) = + kzalloc(call_req_len, GFP_KERNEL); + if (!call_req) + return -ENOMEM; + + struct rpmi_tee_call_resp *call_resp __free(kfree) = + kzalloc(call_resp_len, GFP_KERNEL); + if (!call_resp) + return -ENOMEM; + + call_req->sender_id = cpu_to_le32(priv->self_id); + call_req->target_id = cpu_to_le32(rdev->endpoint_id); + export_uuid(call_req->service, &rdev->uuid); + call_req->service_data_len = cpu_to_le32(req_len); + if (req_len) + memcpy(call_req->service_data, req, req_len); + /* Make TEE CALL. */ + ret = rpmi_tee_send(priv, RPMI_TEE_SRV_CALL, call_req, call_req_len, + call_resp, &call_resp_len); + if (ret) + return ret; + + if (call_resp_len < sizeof(*call_resp)) + return -EPROTO; + service_data_len = get_unaligned_le32(&call_resp->service_data_len); + /* Verify the firmware-reported length fits the actual response. */ + if (service_data_len != call_resp_len - sizeof(*call_resp)) + return -EPROTO; + + if (service_data_len) + memcpy(resp, call_resp->service_data, service_data_len); + *resp_len = service_data_len; + + return 0; +} + +static const struct rpmi_tee_info_ops rpmi_tee_info_ops = { + .msg_limits_get = rpmi_tee_op_msg_limits_get, +}; + +static const struct rpmi_tee_msg_ops rpmi_tee_msg_ops = { + .call = rpmi_tee_op_call, +}; + +static const struct rpmi_tee_ops rpmi_tee_ops = { + .info_ops = &rpmi_tee_info_ops, + .msg_ops = &rpmi_tee_msg_ops, +}; + +static void rpmi_tee_unregister_devices(struct rpmi_tee_transport *priv) +{ + struct rpmi_tee_child *child, *tmp; + + list_for_each_entry_safe(child, tmp, &priv->devices, node) { + list_del(&child->node); + rpmi_tee_device_unregister(child->rdev); + kfree(child); + } +} + +static struct rpmi_tee_device * +rpmi_tee_find_device(struct rpmi_tee_transport *priv, const uuid_t *uuid, + u32 endpoint_id) +{ + struct rpmi_tee_child *child; + + list_for_each_entry(child, &priv->devices, node) { + if (child->rdev->endpoint_id == endpoint_id && + uuid_equal(&child->rdev->uuid, uuid)) + return child->rdev; + } + + return NULL; +} + +static int +rpmi_tee_register_devices(struct rpmi_tee_transport *priv, + const struct rpmi_tee_discovered_endpoint *ep) +{ + u32 i; + + for (i = 0; i < ep->service_count; i++) { + const uuid_t *uuid = &ep->services[i]; + + /* Discard duplicate devices in the same endpoint. */ + if (rpmi_tee_find_device(priv, uuid, ep->ep_id)) + continue; + + struct rpmi_tee_child *child __free(kfree) = + kzalloc_obj(*child, GFP_KERNEL); + if (!child) + return -ENOMEM; + + child->rdev = + rpmi_tee_device_register(uuid, ep->ep_id, + &rpmi_tee_ops, priv->dev); + if (IS_ERR(child->rdev)) + return PTR_ERR(child->rdev); + + list_add_tail(&no_free_ptr(child)->node, &priv->devices); + }
return 0; }
+static int rpmi_tee_setup_endpoints(struct rpmi_tee_transport *priv) +{ + struct rpmi_tee_discovered_endpoint *ep; + struct rpmi_tee_discovery system; + int ret; + + ret = rpmi_tee_discover_endpoints(priv, &system); + if (ret) + return ret; + + list_for_each_entry(ep, &system.eps, node) { + ret = rpmi_tee_register_devices(priv, ep); + if (ret) { + rpmi_tee_unregister_devices(priv); + break; + } + } + + rpmi_tee_free_discovery(&system); + + return ret; +} + static int rpmi_tee_transport_probe(struct platform_device *pdev) { struct rpmi_tee_transport *priv; @@ -112,6 +379,7 @@ static int rpmi_tee_transport_probe(struct platform_device *pdev)
priv->dev = &pdev->dev; platform_set_drvdata(pdev, priv); + INIT_LIST_HEAD(&priv->devices); priv->mbox.client.dev = &pdev->dev; priv->mbox.client.tx_sync = true; priv->mbox.chan = mbox_request_channel(&priv->mbox.client, 0); @@ -126,6 +394,13 @@ static int rpmi_tee_transport_probe(struct platform_device *pdev) goto out_failed; }
+ ret = rpmi_tee_setup_endpoints(priv); + if (ret) { + dev_err_probe(&pdev->dev, ret, + "failed to discover RPMI TEE services\n"); + goto out_failed; + } + return 0;
out_failed: @@ -138,6 +413,7 @@ static void rpmi_tee_transport_remove(struct platform_device *pdev) { struct rpmi_tee_transport *priv = platform_get_drvdata(pdev);
+ rpmi_tee_unregister_devices(priv); mbox_free_channel(priv->mbox.chan); }
diff --git a/drivers/firmware/riscv_rpmi_tee/rpmi_tee_private.h b/drivers/firmware/riscv_rpmi_tee/rpmi_tee_private.h index ba0e7cb59b6d..07e6b9913cb7 100644 --- a/drivers/firmware/riscv_rpmi_tee/rpmi_tee_private.h +++ b/drivers/firmware/riscv_rpmi_tee/rpmi_tee_private.h @@ -17,6 +17,7 @@ #define RPMI_TEE_SRV_PROBE_SYSTEM 0x03 #define RPMI_TEE_SRV_PROBE_DOMAIN 0x04 #define RPMI_TEE_SRV_PROBE_ENDPOINT 0x05 +#define RPMI_TEE_SRV_CALL 0x18
struct rpmi_tee_mbox { struct mbox_client client; @@ -24,10 +25,22 @@ struct rpmi_tee_mbox { u32 max_msg_data_size; };
+/** + * struct rpmi_tee_transport - State for one RPMI TEE transport instance + * @dev: Parent platform device. + * @mbox: RPMI mailbox transport state. + * @max_call_req_size: Maximum TEE_CALL request payload size in bytes. + * @max_call_resp_size: Maximum TEE_CALL response payload size in bytes. + * @self_id: Local REE physical endpoint identifier. + * @devices: List of registered TEE service devices. + */ struct rpmi_tee_transport { struct device *dev; struct rpmi_tee_mbox mbox; + size_t max_call_req_size; + size_t max_call_resp_size; u32 self_id; + struct list_head devices; };
/* Report local transport errors separately from the returned RPMI status. */ diff --git a/include/linux/rpmi_tee.h b/include/linux/rpmi_tee.h index c499f0427833..a76055149996 100644 --- a/include/linux/rpmi_tee.h +++ b/include/linux/rpmi_tee.h @@ -41,6 +41,41 @@ struct rpmi_tee_driver { #define to_rpmi_tee_drv(d) \ container_of_const(d, struct rpmi_tee_driver, driver)
+/** + * struct rpmi_tee_msg_limits - TEE_CALL service payload limits + * @max_req_size: Maximum request payload size in bytes. + * @max_resp_size: Maximum response payload size in bytes. + * + * Limits exclude the RPMI TEE_CALL request and response prefixes, but include + * any service-specific headers supplied by the caller. + */ +struct rpmi_tee_msg_limits { + size_t max_req_size; + size_t max_resp_size; +}; + +/** + * struct rpmi_tee_info_ops - RPMI TEE transport information operations + * @msg_limits_get: Return cached TEE_CALL payload limits for the transport + * serving @rdev in @limits. Limits remain fixed for the transport lifetime. + * Return 0 on success, or a negative error code on failure. + */ +struct rpmi_tee_info_ops { + int (*msg_limits_get)(struct rpmi_tee_device *rdev, + struct rpmi_tee_msg_limits *limits); +}; + +struct rpmi_tee_msg_ops { + int (*call)(struct rpmi_tee_device *rdev, const void *req, + size_t req_len, void *resp, size_t *resp_len); +}; + +/* RPMI TEE transport operation groups. */ +struct rpmi_tee_ops { + const struct rpmi_tee_info_ops *info_ops; + const struct rpmi_tee_msg_ops *msg_ops; +}; + extern const struct bus_type rpmi_tee_bus_type;
#if IS_REACHABLE(CONFIG_RISCV_RPMI_TEE_TRANSPORT)