From: Cristian Marussi Date: Mon, 12 Aug 2024 17:33:38 +0000 (+0100) Subject: firmware: arm_scmi: Make OPTEE transport a standalone driver X-Git-Tag: v6.12-rc1~187^2~20^2~17 X-Git-Url: https://git.kernel.dk/?a=commitdiff_plain;h=db9cc5e677783a8a9157804f4a61bb81d83049ac;p=linux-block.git firmware: arm_scmi: Make OPTEE transport a standalone driver Make SCMI OPTEE transport a standalone driver that can be optionally loaded as a module. CC: Etienne Carriere Signed-off-by: Cristian Marussi Message-Id: <20240812173340.3912830-8-cristian.marussi@arm.com> Signed-off-by: Sudeep Holla --- diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig index 27de15ad6444..b5e3634a8399 100644 --- a/drivers/firmware/arm_scmi/Kconfig +++ b/drivers/firmware/arm_scmi/Kconfig @@ -71,19 +71,6 @@ config ARM_SCMI_DEBUG_COUNTERS source "drivers/firmware/arm_scmi/transports/Kconfig" -config ARM_SCMI_TRANSPORT_OPTEE - bool "SCMI transport based on OP-TEE service" - depends on OPTEE=y || OPTEE=ARM_SCMI_PROTOCOL - select ARM_SCMI_HAVE_TRANSPORT - select ARM_SCMI_HAVE_SHMEM - select ARM_SCMI_HAVE_MSG - default y - help - This enables the OP-TEE service based transport for SCMI. - - If you want the ARM SCMI PROTOCOL stack to include support for a - transport based on OP-TEE SCMI service, answer Y. - config ARM_SCMI_TRANSPORT_VIRTIO bool "SCMI transport based on VirtIO" depends on VIRTIO=y || VIRTIO=ARM_SCMI_PROTOCOL diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index 4c58c668ecc4..4b6dcf241ce1 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -7,7 +7,6 @@ scmi-driver-$(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT) += raw_mode.o scmi-transport-$(CONFIG_ARM_SCMI_HAVE_SHMEM) = shmem.o scmi-transport-$(CONFIG_ARM_SCMI_HAVE_MSG) += msg.o scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_VIRTIO) += virtio.o -scmi-transport-$(CONFIG_ARM_SCMI_TRANSPORT_OPTEE) += optee.o scmi-protocols-y := base.o clock.o perf.o power.o reset.o sensors.o system.o voltage.o powercap.o scmi-protocols-y += pinctrl.o scmi-module-objs := $(scmi-driver-y) $(scmi-protocols-y) $(scmi-transport-y) diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 231b061dadb9..18026d446b59 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -289,9 +289,6 @@ int scmi_xfer_raw_wait_for_message_response(struct scmi_chan_info *cinfo, #ifdef CONFIG_ARM_SCMI_TRANSPORT_VIRTIO extern const struct scmi_desc scmi_virtio_desc; #endif -#ifdef CONFIG_ARM_SCMI_TRANSPORT_OPTEE -extern const struct scmi_desc scmi_optee_desc; -#endif void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr, void *priv); diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index bdb952c385d2..084936e9575d 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -3318,9 +3318,6 @@ ATTRIBUTE_GROUPS(versions); /* Each compatible listed below must have descriptor associated with it */ static const struct of_device_id scmi_of_match[] = { -#ifdef CONFIG_ARM_SCMI_TRANSPORT_OPTEE - { .compatible = "linaro,scmi-optee", .data = &scmi_optee_desc }, -#endif #ifdef CONFIG_ARM_SCMI_TRANSPORT_VIRTIO { .compatible = "arm,scmi-virtio", .data = &scmi_virtio_desc}, #endif diff --git a/drivers/firmware/arm_scmi/optee.c b/drivers/firmware/arm_scmi/optee.c deleted file mode 100644 index dae68adf6f9e..000000000000 --- a/drivers/firmware/arm_scmi/optee.c +++ /dev/null @@ -1,629 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2019-2021 Linaro Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" - -#define SCMI_OPTEE_MAX_MSG_SIZE 128 - -enum scmi_optee_pta_cmd { - /* - * PTA_SCMI_CMD_CAPABILITIES - Get channel capabilities - * - * [out] value[0].a: Capability bit mask (enum pta_scmi_caps) - * [out] value[0].b: Extended capabilities or 0 - */ - PTA_SCMI_CMD_CAPABILITIES = 0, - - /* - * PTA_SCMI_CMD_PROCESS_SMT_CHANNEL - Process SCMI message in SMT buffer - * - * [in] value[0].a: Channel handle - * - * Shared memory used for SCMI message/response exhange is expected - * already identified and bound to channel handle in both SCMI agent - * and SCMI server (OP-TEE) parts. - * The memory uses SMT header to carry SCMI meta-data (protocol ID and - * protocol message ID). - */ - PTA_SCMI_CMD_PROCESS_SMT_CHANNEL = 1, - - /* - * PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE - Process SMT/SCMI message - * - * [in] value[0].a: Channel handle - * [in/out] memref[1]: Message/response buffer (SMT and SCMI payload) - * - * Shared memory used for SCMI message/response is a SMT buffer - * referenced by param[1]. It shall be 128 bytes large to fit response - * payload whatever message playload size. - * The memory uses SMT header to carry SCMI meta-data (protocol ID and - * protocol message ID). - */ - PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE = 2, - - /* - * PTA_SCMI_CMD_GET_CHANNEL - Get channel handle - * - * SCMI shm information are 0 if agent expects to use OP-TEE regular SHM - * - * [in] value[0].a: Channel identifier - * [out] value[0].a: Returned channel handle - * [in] value[0].b: Requested capabilities mask (enum pta_scmi_caps) - */ - PTA_SCMI_CMD_GET_CHANNEL = 3, - - /* - * PTA_SCMI_CMD_PROCESS_MSG_CHANNEL - Process SCMI message in a MSG - * buffer pointed by memref parameters - * - * [in] value[0].a: Channel handle - * [in] memref[1]: Message buffer (MSG and SCMI payload) - * [out] memref[2]: Response buffer (MSG and SCMI payload) - * - * Shared memories used for SCMI message/response are MSG buffers - * referenced by param[1] and param[2]. MSG transport protocol - * uses a 32bit header to carry SCMI meta-data (protocol ID and - * protocol message ID) followed by the effective SCMI message - * payload. - */ - PTA_SCMI_CMD_PROCESS_MSG_CHANNEL = 4, -}; - -/* - * OP-TEE SCMI service capabilities bit flags (32bit) - * - * PTA_SCMI_CAPS_SMT_HEADER - * When set, OP-TEE supports command using SMT header protocol (SCMI shmem) in - * shared memory buffers to carry SCMI protocol synchronisation information. - * - * PTA_SCMI_CAPS_MSG_HEADER - * When set, OP-TEE supports command using MSG header protocol in an OP-TEE - * shared memory to carry SCMI protocol synchronisation information and SCMI - * message payload. - */ -#define PTA_SCMI_CAPS_NONE 0 -#define PTA_SCMI_CAPS_SMT_HEADER BIT(0) -#define PTA_SCMI_CAPS_MSG_HEADER BIT(1) -#define PTA_SCMI_CAPS_MASK (PTA_SCMI_CAPS_SMT_HEADER | \ - PTA_SCMI_CAPS_MSG_HEADER) - -/** - * struct scmi_optee_channel - Description of an OP-TEE SCMI channel - * - * @channel_id: OP-TEE channel ID used for this transport - * @tee_session: TEE session identifier - * @caps: OP-TEE SCMI channel capabilities - * @rx_len: Response size - * @mu: Mutex protection on channel access - * @cinfo: SCMI channel information - * @req: union for SCMI interface - * @req.shmem: Virtual base address of the shared memory - * @req.msg: Shared memory protocol handle for SCMI request and - * synchronous response - * @tee_shm: TEE shared memory handle @req or NULL if using IOMEM shmem - * @link: Reference in agent's channel list - */ -struct scmi_optee_channel { - u32 channel_id; - u32 tee_session; - u32 caps; - u32 rx_len; - struct mutex mu; - struct scmi_chan_info *cinfo; - union { - struct scmi_shared_mem __iomem *shmem; - struct scmi_msg_payld *msg; - } req; - struct tee_shm *tee_shm; - struct list_head link; -}; - -/** - * struct scmi_optee_agent - OP-TEE transport private data - * - * @dev: Device used for communication with TEE - * @tee_ctx: TEE context used for communication - * @caps: Supported channel capabilities - * @mu: Mutex for protection of @channel_list - * @channel_list: List of all created channels for the agent - */ -struct scmi_optee_agent { - struct device *dev; - struct tee_context *tee_ctx; - u32 caps; - struct mutex mu; - struct list_head channel_list; -}; - -/* There can be only 1 SCMI service in OP-TEE we connect to */ -static struct scmi_optee_agent *scmi_optee_private; - -/* Forward reference to scmi_optee transport initialization */ -static int scmi_optee_init(void); - -/* Open a session toward SCMI OP-TEE service with REE_KERNEL identity */ -static int open_session(struct scmi_optee_agent *agent, u32 *tee_session) -{ - struct device *dev = agent->dev; - struct tee_client_device *scmi_pta = to_tee_client_device(dev); - struct tee_ioctl_open_session_arg arg = { }; - int ret; - - memcpy(arg.uuid, scmi_pta->id.uuid.b, TEE_IOCTL_UUID_LEN); - arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; - - ret = tee_client_open_session(agent->tee_ctx, &arg, NULL); - if (ret < 0 || arg.ret) { - dev_err(dev, "Can't open tee session: %d / %#x\n", ret, arg.ret); - return -EOPNOTSUPP; - } - - *tee_session = arg.session; - - return 0; -} - -static void close_session(struct scmi_optee_agent *agent, u32 tee_session) -{ - tee_client_close_session(agent->tee_ctx, tee_session); -} - -static int get_capabilities(struct scmi_optee_agent *agent) -{ - struct tee_ioctl_invoke_arg arg = { }; - struct tee_param param[1] = { }; - u32 caps; - u32 tee_session; - int ret; - - ret = open_session(agent, &tee_session); - if (ret) - return ret; - - arg.func = PTA_SCMI_CMD_CAPABILITIES; - arg.session = tee_session; - arg.num_params = 1; - - param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; - - ret = tee_client_invoke_func(agent->tee_ctx, &arg, param); - - close_session(agent, tee_session); - - if (ret < 0 || arg.ret) { - dev_err(agent->dev, "Can't get capabilities: %d / %#x\n", ret, arg.ret); - return -EOPNOTSUPP; - } - - caps = param[0].u.value.a; - - if (!(caps & (PTA_SCMI_CAPS_SMT_HEADER | PTA_SCMI_CAPS_MSG_HEADER))) { - dev_err(agent->dev, "OP-TEE SCMI PTA doesn't support SMT and MSG\n"); - return -EOPNOTSUPP; - } - - agent->caps = caps; - - return 0; -} - -static int get_channel(struct scmi_optee_channel *channel) -{ - struct device *dev = scmi_optee_private->dev; - struct tee_ioctl_invoke_arg arg = { }; - struct tee_param param[1] = { }; - unsigned int caps = 0; - int ret; - - if (channel->tee_shm) - caps = PTA_SCMI_CAPS_MSG_HEADER; - else - caps = PTA_SCMI_CAPS_SMT_HEADER; - - arg.func = PTA_SCMI_CMD_GET_CHANNEL; - arg.session = channel->tee_session; - arg.num_params = 1; - - param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT; - param[0].u.value.a = channel->channel_id; - param[0].u.value.b = caps; - - ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param); - - if (ret || arg.ret) { - dev_err(dev, "Can't get channel with caps %#x: %d / %#x\n", caps, ret, arg.ret); - return -EOPNOTSUPP; - } - - /* From now on use channel identifer provided by OP-TEE SCMI service */ - channel->channel_id = param[0].u.value.a; - channel->caps = caps; - - return 0; -} - -static int invoke_process_smt_channel(struct scmi_optee_channel *channel) -{ - struct tee_ioctl_invoke_arg arg = { - .func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL, - .session = channel->tee_session, - .num_params = 1, - }; - struct tee_param param[1] = { }; - int ret; - - param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; - param[0].u.value.a = channel->channel_id; - - ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param); - if (ret < 0 || arg.ret) { - dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n", - channel->channel_id, ret, arg.ret); - return -EIO; - } - - return 0; -} - -static int invoke_process_msg_channel(struct scmi_optee_channel *channel, size_t msg_size) -{ - struct tee_ioctl_invoke_arg arg = { - .func = PTA_SCMI_CMD_PROCESS_MSG_CHANNEL, - .session = channel->tee_session, - .num_params = 3, - }; - struct tee_param param[3] = { }; - int ret; - - param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; - param[0].u.value.a = channel->channel_id; - - param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; - param[1].u.memref.shm = channel->tee_shm; - param[1].u.memref.size = msg_size; - - param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; - param[2].u.memref.shm = channel->tee_shm; - param[2].u.memref.size = SCMI_OPTEE_MAX_MSG_SIZE; - - ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param); - if (ret < 0 || arg.ret) { - dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n", - channel->channel_id, ret, arg.ret); - return -EIO; - } - - /* Save response size */ - channel->rx_len = param[2].u.memref.size; - - return 0; -} - -static int scmi_optee_link_supplier(struct device *dev) -{ - if (!scmi_optee_private) { - if (scmi_optee_init()) - dev_dbg(dev, "Optee bus not yet ready\n"); - - /* Wait for optee bus */ - return -EPROBE_DEFER; - } - - if (!device_link_add(dev, scmi_optee_private->dev, DL_FLAG_AUTOREMOVE_CONSUMER)) { - dev_err(dev, "Adding link to supplier optee device failed\n"); - return -ECANCELED; - } - - return 0; -} - -static bool scmi_optee_chan_available(struct device_node *of_node, int idx) -{ - u32 channel_id; - - return !of_property_read_u32_index(of_node, "linaro,optee-channel-id", - idx, &channel_id); -} - -static void scmi_optee_clear_channel(struct scmi_chan_info *cinfo) -{ - struct scmi_optee_channel *channel = cinfo->transport_info; - - if (!channel->tee_shm) - scmi_shmem_ops.clear_channel(channel->req.shmem); -} - -static int setup_dynamic_shmem(struct device *dev, struct scmi_optee_channel *channel) -{ - const size_t msg_size = SCMI_OPTEE_MAX_MSG_SIZE; - void *shbuf; - - channel->tee_shm = tee_shm_alloc_kernel_buf(scmi_optee_private->tee_ctx, msg_size); - if (IS_ERR(channel->tee_shm)) { - dev_err(channel->cinfo->dev, "shmem allocation failed\n"); - return -ENOMEM; - } - - shbuf = tee_shm_get_va(channel->tee_shm, 0); - memset(shbuf, 0, msg_size); - channel->req.msg = shbuf; - channel->rx_len = msg_size; - - return 0; -} - -static int setup_static_shmem(struct device *dev, struct scmi_chan_info *cinfo, - struct scmi_optee_channel *channel) -{ - channel->req.shmem = scmi_shmem_ops.setup_iomap(cinfo, dev, true, NULL); - if (IS_ERR(channel->req.shmem)) - return PTR_ERR(channel->req.shmem); - - return 0; -} - -static int setup_shmem(struct device *dev, struct scmi_chan_info *cinfo, - struct scmi_optee_channel *channel) -{ - if (of_property_present(cinfo->dev->of_node, "shmem")) - return setup_static_shmem(dev, cinfo, channel); - else - return setup_dynamic_shmem(dev, channel); -} - -static int scmi_optee_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, bool tx) -{ - struct scmi_optee_channel *channel; - uint32_t channel_id; - int ret; - - if (!tx) - return -ENODEV; - - channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL); - if (!channel) - return -ENOMEM; - - ret = of_property_read_u32_index(cinfo->dev->of_node, "linaro,optee-channel-id", - 0, &channel_id); - if (ret) - return ret; - - cinfo->transport_info = channel; - channel->cinfo = cinfo; - channel->channel_id = channel_id; - mutex_init(&channel->mu); - - ret = setup_shmem(dev, cinfo, channel); - if (ret) - return ret; - - ret = open_session(scmi_optee_private, &channel->tee_session); - if (ret) - goto err_free_shm; - - ret = tee_client_system_session(scmi_optee_private->tee_ctx, channel->tee_session); - if (ret) - dev_warn(dev, "Could not switch to system session, do best effort\n"); - - ret = get_channel(channel); - if (ret) - goto err_close_sess; - - /* Enable polling */ - cinfo->no_completion_irq = true; - - mutex_lock(&scmi_optee_private->mu); - list_add(&channel->link, &scmi_optee_private->channel_list); - mutex_unlock(&scmi_optee_private->mu); - - return 0; - -err_close_sess: - close_session(scmi_optee_private, channel->tee_session); -err_free_shm: - if (channel->tee_shm) - tee_shm_free(channel->tee_shm); - - return ret; -} - -static int scmi_optee_chan_free(int id, void *p, void *data) -{ - struct scmi_chan_info *cinfo = p; - struct scmi_optee_channel *channel = cinfo->transport_info; - - /* - * Different protocols might share the same chan info, so a previous - * call might have already freed the structure. - */ - if (!channel) - return 0; - - mutex_lock(&scmi_optee_private->mu); - list_del(&channel->link); - mutex_unlock(&scmi_optee_private->mu); - - close_session(scmi_optee_private, channel->tee_session); - - if (channel->tee_shm) { - tee_shm_free(channel->tee_shm); - channel->tee_shm = NULL; - } - - cinfo->transport_info = NULL; - channel->cinfo = NULL; - - return 0; -} - -static int scmi_optee_send_message(struct scmi_chan_info *cinfo, - struct scmi_xfer *xfer) -{ - struct scmi_optee_channel *channel = cinfo->transport_info; - int ret; - - mutex_lock(&channel->mu); - - if (channel->tee_shm) { - scmi_msg_ops.tx_prepare(channel->req.msg, xfer); - ret = invoke_process_msg_channel(channel, scmi_msg_ops.command_size(xfer)); - } else { - scmi_shmem_ops.tx_prepare(channel->req.shmem, xfer, cinfo); - ret = invoke_process_smt_channel(channel); - } - - if (ret) - mutex_unlock(&channel->mu); - - return ret; -} - -static void scmi_optee_fetch_response(struct scmi_chan_info *cinfo, - struct scmi_xfer *xfer) -{ - struct scmi_optee_channel *channel = cinfo->transport_info; - - if (channel->tee_shm) - scmi_msg_ops.fetch_response(channel->req.msg, channel->rx_len, xfer); - else - scmi_shmem_ops.fetch_response(channel->req.shmem, xfer); -} - -static void scmi_optee_mark_txdone(struct scmi_chan_info *cinfo, int ret, - struct scmi_xfer *__unused) -{ - struct scmi_optee_channel *channel = cinfo->transport_info; - - mutex_unlock(&channel->mu); -} - -static struct scmi_transport_ops scmi_optee_ops = { - .link_supplier = scmi_optee_link_supplier, - .chan_available = scmi_optee_chan_available, - .chan_setup = scmi_optee_chan_setup, - .chan_free = scmi_optee_chan_free, - .send_message = scmi_optee_send_message, - .mark_txdone = scmi_optee_mark_txdone, - .fetch_response = scmi_optee_fetch_response, - .clear_channel = scmi_optee_clear_channel, -}; - -static int scmi_optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) -{ - return ver->impl_id == TEE_IMPL_ID_OPTEE; -} - -static int scmi_optee_service_probe(struct device *dev) -{ - struct scmi_optee_agent *agent; - struct tee_context *tee_ctx; - int ret; - - /* Only one SCMI OP-TEE device allowed */ - if (scmi_optee_private) { - dev_err(dev, "An SCMI OP-TEE device was already initialized: only one allowed\n"); - return -EBUSY; - } - - tee_ctx = tee_client_open_context(NULL, scmi_optee_ctx_match, NULL, NULL); - if (IS_ERR(tee_ctx)) - return -ENODEV; - - agent = devm_kzalloc(dev, sizeof(*agent), GFP_KERNEL); - if (!agent) { - ret = -ENOMEM; - goto err; - } - - agent->dev = dev; - agent->tee_ctx = tee_ctx; - INIT_LIST_HEAD(&agent->channel_list); - mutex_init(&agent->mu); - - ret = get_capabilities(agent); - if (ret) - goto err; - - /* Ensure agent resources are all visible before scmi_optee_private is */ - smp_mb(); - scmi_optee_private = agent; - - return 0; - -err: - tee_client_close_context(tee_ctx); - - return ret; -} - -static int scmi_optee_service_remove(struct device *dev) -{ - struct scmi_optee_agent *agent = scmi_optee_private; - - if (!scmi_optee_private) - return -EINVAL; - - if (!list_empty(&scmi_optee_private->channel_list)) - return -EBUSY; - - /* Ensure cleared reference is visible before resources are released */ - smp_store_mb(scmi_optee_private, NULL); - - tee_client_close_context(agent->tee_ctx); - - return 0; -} - -static const struct tee_client_device_id scmi_optee_service_id[] = { - { - UUID_INIT(0xa8cfe406, 0xd4f5, 0x4a2e, - 0x9f, 0x8d, 0xa2, 0x5d, 0xc7, 0x54, 0xc0, 0x99) - }, - { } -}; - -MODULE_DEVICE_TABLE(tee, scmi_optee_service_id); - -static struct tee_client_driver scmi_optee_driver = { - .id_table = scmi_optee_service_id, - .driver = { - .name = "scmi-optee", - .bus = &tee_bus_type, - .probe = scmi_optee_service_probe, - .remove = scmi_optee_service_remove, - }, -}; - -static int scmi_optee_init(void) -{ - return driver_register(&scmi_optee_driver.driver); -} - -static void scmi_optee_exit(void) -{ - if (scmi_optee_private) - driver_unregister(&scmi_optee_driver.driver); -} - -const struct scmi_desc scmi_optee_desc = { - .transport_exit = scmi_optee_exit, - .ops = &scmi_optee_ops, - .max_rx_timeout_ms = 30, - .max_msg = 20, - .max_msg_size = SCMI_OPTEE_MAX_MSG_SIZE, - .sync_cmds_completed_on_ret = true, -}; diff --git a/drivers/firmware/arm_scmi/transports/Kconfig b/drivers/firmware/arm_scmi/transports/Kconfig index 85d693811a1c..7d478a4f69df 100644 --- a/drivers/firmware/arm_scmi/transports/Kconfig +++ b/drivers/firmware/arm_scmi/transports/Kconfig @@ -62,4 +62,19 @@ config ARM_SCMI_TRANSPORT_SMC_ATOMIC_ENABLE in atomic context too, at the price of using a number of busy-waiting primitives all over instead. If unsure say N. +config ARM_SCMI_TRANSPORT_OPTEE + tristate "SCMI transport based on OP-TEE service" + depends on OPTEE + select ARM_SCMI_HAVE_TRANSPORT + select ARM_SCMI_HAVE_SHMEM + select ARM_SCMI_HAVE_MSG + default y + help + This enables the OP-TEE service based transport for SCMI. + + If you want the ARM SCMI PROTOCOL stack to include support for a + transport based on OP-TEE SCMI service, answer Y. + This driver can also be built as a module. If so, the module + will be called scmi_transport_optee. + endmenu diff --git a/drivers/firmware/arm_scmi/transports/Makefile b/drivers/firmware/arm_scmi/transports/Makefile index 080bd76d9dbd..efc002e7efcd 100644 --- a/drivers/firmware/arm_scmi/transports/Makefile +++ b/drivers/firmware/arm_scmi/transports/Makefile @@ -3,6 +3,8 @@ scmi_transport_mailbox-objs := mailbox.o obj-$(CONFIG_ARM_SCMI_TRANSPORT_MAILBOX) += scmi_transport_mailbox.o scmi_transport_smc-objs := smc.o obj-$(CONFIG_ARM_SCMI_TRANSPORT_SMC) += scmi_transport_smc.o +scmi_transport_optee-objs := optee.o +obj-$(CONFIG_ARM_SCMI_TRANSPORT_OPTEE) += scmi_transport_optee.o ifeq ($(CONFIG_THUMB2_KERNEL)$(CONFIG_CC_IS_CLANG),yy) # The use of R7 in the SMCCC conflicts with the compiler's use of R7 as a frame diff --git a/drivers/firmware/arm_scmi/transports/optee.c b/drivers/firmware/arm_scmi/transports/optee.c new file mode 100644 index 000000000000..978750aaf6b7 --- /dev/null +++ b/drivers/firmware/arm_scmi/transports/optee.c @@ -0,0 +1,632 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019-2021 Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../common.h" + +#define SCMI_OPTEE_MAX_MSG_SIZE 128 + +enum scmi_optee_pta_cmd { + /* + * PTA_SCMI_CMD_CAPABILITIES - Get channel capabilities + * + * [out] value[0].a: Capability bit mask (enum pta_scmi_caps) + * [out] value[0].b: Extended capabilities or 0 + */ + PTA_SCMI_CMD_CAPABILITIES = 0, + + /* + * PTA_SCMI_CMD_PROCESS_SMT_CHANNEL - Process SCMI message in SMT buffer + * + * [in] value[0].a: Channel handle + * + * Shared memory used for SCMI message/response exhange is expected + * already identified and bound to channel handle in both SCMI agent + * and SCMI server (OP-TEE) parts. + * The memory uses SMT header to carry SCMI meta-data (protocol ID and + * protocol message ID). + */ + PTA_SCMI_CMD_PROCESS_SMT_CHANNEL = 1, + + /* + * PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE - Process SMT/SCMI message + * + * [in] value[0].a: Channel handle + * [in/out] memref[1]: Message/response buffer (SMT and SCMI payload) + * + * Shared memory used for SCMI message/response is a SMT buffer + * referenced by param[1]. It shall be 128 bytes large to fit response + * payload whatever message playload size. + * The memory uses SMT header to carry SCMI meta-data (protocol ID and + * protocol message ID). + */ + PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE = 2, + + /* + * PTA_SCMI_CMD_GET_CHANNEL - Get channel handle + * + * SCMI shm information are 0 if agent expects to use OP-TEE regular SHM + * + * [in] value[0].a: Channel identifier + * [out] value[0].a: Returned channel handle + * [in] value[0].b: Requested capabilities mask (enum pta_scmi_caps) + */ + PTA_SCMI_CMD_GET_CHANNEL = 3, + + /* + * PTA_SCMI_CMD_PROCESS_MSG_CHANNEL - Process SCMI message in a MSG + * buffer pointed by memref parameters + * + * [in] value[0].a: Channel handle + * [in] memref[1]: Message buffer (MSG and SCMI payload) + * [out] memref[2]: Response buffer (MSG and SCMI payload) + * + * Shared memories used for SCMI message/response are MSG buffers + * referenced by param[1] and param[2]. MSG transport protocol + * uses a 32bit header to carry SCMI meta-data (protocol ID and + * protocol message ID) followed by the effective SCMI message + * payload. + */ + PTA_SCMI_CMD_PROCESS_MSG_CHANNEL = 4, +}; + +/* + * OP-TEE SCMI service capabilities bit flags (32bit) + * + * PTA_SCMI_CAPS_SMT_HEADER + * When set, OP-TEE supports command using SMT header protocol (SCMI shmem) in + * shared memory buffers to carry SCMI protocol synchronisation information. + * + * PTA_SCMI_CAPS_MSG_HEADER + * When set, OP-TEE supports command using MSG header protocol in an OP-TEE + * shared memory to carry SCMI protocol synchronisation information and SCMI + * message payload. + */ +#define PTA_SCMI_CAPS_NONE 0 +#define PTA_SCMI_CAPS_SMT_HEADER BIT(0) +#define PTA_SCMI_CAPS_MSG_HEADER BIT(1) +#define PTA_SCMI_CAPS_MASK (PTA_SCMI_CAPS_SMT_HEADER | \ + PTA_SCMI_CAPS_MSG_HEADER) + +/** + * struct scmi_optee_channel - Description of an OP-TEE SCMI channel + * + * @channel_id: OP-TEE channel ID used for this transport + * @tee_session: TEE session identifier + * @caps: OP-TEE SCMI channel capabilities + * @rx_len: Response size + * @mu: Mutex protection on channel access + * @cinfo: SCMI channel information + * @req: union for SCMI interface + * @req.shmem: Virtual base address of the shared memory + * @req.msg: Shared memory protocol handle for SCMI request and + * synchronous response + * @tee_shm: TEE shared memory handle @req or NULL if using IOMEM shmem + * @link: Reference in agent's channel list + */ +struct scmi_optee_channel { + u32 channel_id; + u32 tee_session; + u32 caps; + u32 rx_len; + struct mutex mu; + struct scmi_chan_info *cinfo; + union { + struct scmi_shared_mem __iomem *shmem; + struct scmi_msg_payld *msg; + } req; + struct tee_shm *tee_shm; + struct list_head link; +}; + +/** + * struct scmi_optee_agent - OP-TEE transport private data + * + * @dev: Device used for communication with TEE + * @tee_ctx: TEE context used for communication + * @caps: Supported channel capabilities + * @mu: Mutex for protection of @channel_list + * @channel_list: List of all created channels for the agent + */ +struct scmi_optee_agent { + struct device *dev; + struct tee_context *tee_ctx; + u32 caps; + struct mutex mu; + struct list_head channel_list; +}; + +static struct scmi_transport_core_operations *core; + +/* There can be only 1 SCMI service in OP-TEE we connect to */ +static struct scmi_optee_agent *scmi_optee_private; + +/* Open a session toward SCMI OP-TEE service with REE_KERNEL identity */ +static int open_session(struct scmi_optee_agent *agent, u32 *tee_session) +{ + struct device *dev = agent->dev; + struct tee_client_device *scmi_pta = to_tee_client_device(dev); + struct tee_ioctl_open_session_arg arg = { }; + int ret; + + memcpy(arg.uuid, scmi_pta->id.uuid.b, TEE_IOCTL_UUID_LEN); + arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; + + ret = tee_client_open_session(agent->tee_ctx, &arg, NULL); + if (ret < 0 || arg.ret) { + dev_err(dev, "Can't open tee session: %d / %#x\n", ret, arg.ret); + return -EOPNOTSUPP; + } + + *tee_session = arg.session; + + return 0; +} + +static void close_session(struct scmi_optee_agent *agent, u32 tee_session) +{ + tee_client_close_session(agent->tee_ctx, tee_session); +} + +static int get_capabilities(struct scmi_optee_agent *agent) +{ + struct tee_ioctl_invoke_arg arg = { }; + struct tee_param param[1] = { }; + u32 caps; + u32 tee_session; + int ret; + + ret = open_session(agent, &tee_session); + if (ret) + return ret; + + arg.func = PTA_SCMI_CMD_CAPABILITIES; + arg.session = tee_session; + arg.num_params = 1; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; + + ret = tee_client_invoke_func(agent->tee_ctx, &arg, param); + + close_session(agent, tee_session); + + if (ret < 0 || arg.ret) { + dev_err(agent->dev, "Can't get capabilities: %d / %#x\n", ret, arg.ret); + return -EOPNOTSUPP; + } + + caps = param[0].u.value.a; + + if (!(caps & (PTA_SCMI_CAPS_SMT_HEADER | PTA_SCMI_CAPS_MSG_HEADER))) { + dev_err(agent->dev, "OP-TEE SCMI PTA doesn't support SMT and MSG\n"); + return -EOPNOTSUPP; + } + + agent->caps = caps; + + return 0; +} + +static int get_channel(struct scmi_optee_channel *channel) +{ + struct device *dev = scmi_optee_private->dev; + struct tee_ioctl_invoke_arg arg = { }; + struct tee_param param[1] = { }; + unsigned int caps = 0; + int ret; + + if (channel->tee_shm) + caps = PTA_SCMI_CAPS_MSG_HEADER; + else + caps = PTA_SCMI_CAPS_SMT_HEADER; + + arg.func = PTA_SCMI_CMD_GET_CHANNEL; + arg.session = channel->tee_session; + arg.num_params = 1; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT; + param[0].u.value.a = channel->channel_id; + param[0].u.value.b = caps; + + ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param); + + if (ret || arg.ret) { + dev_err(dev, "Can't get channel with caps %#x: %d / %#x\n", caps, ret, arg.ret); + return -EOPNOTSUPP; + } + + /* From now on use channel identifer provided by OP-TEE SCMI service */ + channel->channel_id = param[0].u.value.a; + channel->caps = caps; + + return 0; +} + +static int invoke_process_smt_channel(struct scmi_optee_channel *channel) +{ + struct tee_ioctl_invoke_arg arg = { + .func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL, + .session = channel->tee_session, + .num_params = 1, + }; + struct tee_param param[1] = { }; + int ret; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = channel->channel_id; + + ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param); + if (ret < 0 || arg.ret) { + dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n", + channel->channel_id, ret, arg.ret); + return -EIO; + } + + return 0; +} + +static int invoke_process_msg_channel(struct scmi_optee_channel *channel, size_t msg_size) +{ + struct tee_ioctl_invoke_arg arg = { + .func = PTA_SCMI_CMD_PROCESS_MSG_CHANNEL, + .session = channel->tee_session, + .num_params = 3, + }; + struct tee_param param[3] = { }; + int ret; + + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = channel->channel_id; + + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT; + param[1].u.memref.shm = channel->tee_shm; + param[1].u.memref.size = msg_size; + + param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT; + param[2].u.memref.shm = channel->tee_shm; + param[2].u.memref.size = SCMI_OPTEE_MAX_MSG_SIZE; + + ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param); + if (ret < 0 || arg.ret) { + dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n", + channel->channel_id, ret, arg.ret); + return -EIO; + } + + /* Save response size */ + channel->rx_len = param[2].u.memref.size; + + return 0; +} + +static bool scmi_optee_chan_available(struct device_node *of_node, int idx) +{ + u32 channel_id; + + return !of_property_read_u32_index(of_node, "linaro,optee-channel-id", + idx, &channel_id); +} + +static void scmi_optee_clear_channel(struct scmi_chan_info *cinfo) +{ + struct scmi_optee_channel *channel = cinfo->transport_info; + + if (!channel->tee_shm) + core->shmem->clear_channel(channel->req.shmem); +} + +static int setup_dynamic_shmem(struct device *dev, struct scmi_optee_channel *channel) +{ + const size_t msg_size = SCMI_OPTEE_MAX_MSG_SIZE; + void *shbuf; + + channel->tee_shm = tee_shm_alloc_kernel_buf(scmi_optee_private->tee_ctx, msg_size); + if (IS_ERR(channel->tee_shm)) { + dev_err(channel->cinfo->dev, "shmem allocation failed\n"); + return -ENOMEM; + } + + shbuf = tee_shm_get_va(channel->tee_shm, 0); + memset(shbuf, 0, msg_size); + channel->req.msg = shbuf; + channel->rx_len = msg_size; + + return 0; +} + +static int setup_static_shmem(struct device *dev, struct scmi_chan_info *cinfo, + struct scmi_optee_channel *channel) +{ + channel->req.shmem = core->shmem->setup_iomap(cinfo, dev, true, NULL); + if (IS_ERR(channel->req.shmem)) + return PTR_ERR(channel->req.shmem); + + return 0; +} + +static int setup_shmem(struct device *dev, struct scmi_chan_info *cinfo, + struct scmi_optee_channel *channel) +{ + if (of_property_present(cinfo->dev->of_node, "shmem")) + return setup_static_shmem(dev, cinfo, channel); + else + return setup_dynamic_shmem(dev, channel); +} + +static int scmi_optee_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, bool tx) +{ + struct scmi_optee_channel *channel; + uint32_t channel_id; + int ret; + + if (!tx) + return -ENODEV; + + channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL); + if (!channel) + return -ENOMEM; + + ret = of_property_read_u32_index(cinfo->dev->of_node, "linaro,optee-channel-id", + 0, &channel_id); + if (ret) + return ret; + + cinfo->transport_info = channel; + channel->cinfo = cinfo; + channel->channel_id = channel_id; + mutex_init(&channel->mu); + + ret = setup_shmem(dev, cinfo, channel); + if (ret) + return ret; + + ret = open_session(scmi_optee_private, &channel->tee_session); + if (ret) + goto err_free_shm; + + ret = tee_client_system_session(scmi_optee_private->tee_ctx, channel->tee_session); + if (ret) + dev_warn(dev, "Could not switch to system session, do best effort\n"); + + ret = get_channel(channel); + if (ret) + goto err_close_sess; + + /* Enable polling */ + cinfo->no_completion_irq = true; + + mutex_lock(&scmi_optee_private->mu); + list_add(&channel->link, &scmi_optee_private->channel_list); + mutex_unlock(&scmi_optee_private->mu); + + return 0; + +err_close_sess: + close_session(scmi_optee_private, channel->tee_session); +err_free_shm: + if (channel->tee_shm) + tee_shm_free(channel->tee_shm); + + return ret; +} + +static int scmi_optee_chan_free(int id, void *p, void *data) +{ + struct scmi_chan_info *cinfo = p; + struct scmi_optee_channel *channel = cinfo->transport_info; + + /* + * Different protocols might share the same chan info, so a previous + * call might have already freed the structure. + */ + if (!channel) + return 0; + + mutex_lock(&scmi_optee_private->mu); + list_del(&channel->link); + mutex_unlock(&scmi_optee_private->mu); + + close_session(scmi_optee_private, channel->tee_session); + + if (channel->tee_shm) { + tee_shm_free(channel->tee_shm); + channel->tee_shm = NULL; + } + + cinfo->transport_info = NULL; + channel->cinfo = NULL; + + return 0; +} + +static int scmi_optee_send_message(struct scmi_chan_info *cinfo, + struct scmi_xfer *xfer) +{ + struct scmi_optee_channel *channel = cinfo->transport_info; + int ret; + + mutex_lock(&channel->mu); + + if (channel->tee_shm) { + core->msg->tx_prepare(channel->req.msg, xfer); + ret = invoke_process_msg_channel(channel, + core->msg->command_size(xfer)); + } else { + core->shmem->tx_prepare(channel->req.shmem, xfer, cinfo); + ret = invoke_process_smt_channel(channel); + } + + if (ret) + mutex_unlock(&channel->mu); + + return ret; +} + +static void scmi_optee_fetch_response(struct scmi_chan_info *cinfo, + struct scmi_xfer *xfer) +{ + struct scmi_optee_channel *channel = cinfo->transport_info; + + if (channel->tee_shm) + core->msg->fetch_response(channel->req.msg, + channel->rx_len, xfer); + else + core->shmem->fetch_response(channel->req.shmem, xfer); +} + +static void scmi_optee_mark_txdone(struct scmi_chan_info *cinfo, int ret, + struct scmi_xfer *__unused) +{ + struct scmi_optee_channel *channel = cinfo->transport_info; + + mutex_unlock(&channel->mu); +} + +static struct scmi_transport_ops scmi_optee_ops = { + .chan_available = scmi_optee_chan_available, + .chan_setup = scmi_optee_chan_setup, + .chan_free = scmi_optee_chan_free, + .send_message = scmi_optee_send_message, + .mark_txdone = scmi_optee_mark_txdone, + .fetch_response = scmi_optee_fetch_response, + .clear_channel = scmi_optee_clear_channel, +}; + +static int scmi_optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{ + return ver->impl_id == TEE_IMPL_ID_OPTEE; +} + +static const struct scmi_desc scmi_optee_desc = { + .ops = &scmi_optee_ops, + .max_rx_timeout_ms = 30, + .max_msg = 20, + .max_msg_size = SCMI_OPTEE_MAX_MSG_SIZE, + .sync_cmds_completed_on_ret = true, +}; + +static const struct of_device_id scmi_of_match[] = { + { .compatible = "linaro,scmi-optee" }, + { /* Sentinel */ }, +}; + +DEFINE_SCMI_TRANSPORT_DRIVER(scmi_optee, scmi_optee_driver, scmi_optee_desc, + scmi_of_match, core); + +static int scmi_optee_service_probe(struct device *dev) +{ + struct scmi_optee_agent *agent; + struct tee_context *tee_ctx; + int ret; + + /* Only one SCMI OP-TEE device allowed */ + if (scmi_optee_private) { + dev_err(dev, "An SCMI OP-TEE device was already initialized: only one allowed\n"); + return -EBUSY; + } + + tee_ctx = tee_client_open_context(NULL, scmi_optee_ctx_match, NULL, NULL); + if (IS_ERR(tee_ctx)) + return -ENODEV; + + agent = devm_kzalloc(dev, sizeof(*agent), GFP_KERNEL); + if (!agent) { + ret = -ENOMEM; + goto err; + } + + agent->dev = dev; + agent->tee_ctx = tee_ctx; + INIT_LIST_HEAD(&agent->channel_list); + mutex_init(&agent->mu); + + ret = get_capabilities(agent); + if (ret) + goto err; + + /* Ensure agent resources are all visible before scmi_optee_private is */ + smp_mb(); + scmi_optee_private = agent; + + ret = platform_driver_register(&scmi_optee_driver); + if (ret) { + scmi_optee_private = NULL; + goto err; + } + + return 0; + +err: + tee_client_close_context(tee_ctx); + + return ret; +} + +static int scmi_optee_service_remove(struct device *dev) +{ + struct scmi_optee_agent *agent = scmi_optee_private; + + if (!scmi_optee_private) + return -EINVAL; + + platform_driver_unregister(&scmi_optee_driver); + + if (!list_empty(&scmi_optee_private->channel_list)) + return -EBUSY; + + /* Ensure cleared reference is visible before resources are released */ + smp_store_mb(scmi_optee_private, NULL); + + tee_client_close_context(agent->tee_ctx); + + return 0; +} + +static const struct tee_client_device_id scmi_optee_service_id[] = { + { + UUID_INIT(0xa8cfe406, 0xd4f5, 0x4a2e, + 0x9f, 0x8d, 0xa2, 0x5d, 0xc7, 0x54, 0xc0, 0x99) + }, + { } +}; + +MODULE_DEVICE_TABLE(tee, scmi_optee_service_id); + +static struct tee_client_driver scmi_optee_service_driver = { + .id_table = scmi_optee_service_id, + .driver = { + .name = "scmi-optee", + .bus = &tee_bus_type, + .probe = scmi_optee_service_probe, + .remove = scmi_optee_service_remove, + }, +}; + +static int __init scmi_transport_optee_init(void) +{ + return driver_register(&scmi_optee_service_driver.driver); +} +module_init(scmi_transport_optee_init); + +static void __exit scmi_transport_optee_exit(void) +{ + driver_unregister(&scmi_optee_service_driver.driver); +} +module_exit(scmi_transport_optee_exit); + +MODULE_AUTHOR("Etienne Carriere "); +MODULE_DESCRIPTION("SCMI OPTEE Transport driver"); +MODULE_LICENSE("GPL");