diff --git a/drivers/firmware/arm_scmi/optee.c b/drivers/firmware/arm_scmi/optee.c index b37f78d5ce9d2..1b344b986a41d 100644 --- a/drivers/firmware/arm_scmi/optee.c +++ b/drivers/firmware/arm_scmi/optee.c @@ -1,19 +1,20 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2019 Linaro Ltd. + * Copyright (C) 2019-2021 Linaro Limited */ +#include #include -#include -#include #include #include +#include #include +#include +#include +#include #include #include #include -#include -#include #include #include "common.h" @@ -21,124 +22,225 @@ #define DRIVER_NAME "optee-scmi-agent" /* - * PTA_SCMI_CMD_CHANNEL_COUNT - Get number of channels supported + * PTA_SCMI_CMD_CAPABILITIES - Get channel capabilities * - * param[0] (out value) - value.a: Number of communication channels - * param[1] unused - * param[2] unused - * param[3] unused - * - * Result: - * TEE_SUCCESS - Invoke command success - * TEE_ERROR_BAD_PARAMETERS - Incorrect input param + * [out] value[0].a: Capability bit mask (PTA_SCMI_CAPS_*) + * [out] value[0].b: Extended capabilities or 0 */ -#define PTA_SCMI_CMD_CHANNEL_COUNT 0 +#define PTA_SCMI_CMD_CAPABILITIES 0 /* - * PTA_SCMI_CMD_PROCESS_SMT_CHANNEL - Process message through a channel shmem + * PTA_SCMI_CMD_PROCESS_SMT_CHANNEL - Process SCMI message in SMT buffer * - * Channel identifier defines the shmem buffer where SCMI message is posted. + * [in] value[0].a: Channel handle * - * value[0] [in] SCMI channel identifier + * 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). */ -#define PTA_SCMI_CMD_PROCESS_SMT_CHANNEL 1 +#define PTA_SCMI_CMD_PROCESS_SMT_CHANNEL 1 /* - * PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE - Process message for a SCMI channel + * PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE - Process SMT/SCMI message * - * Command provides the channel identifier and the message payload in memref[1]. - * Output SCMI response payload is writen to memref[1]. - - * value[0] [in] SCMI channel identifier - * memref[1] [in/out] 128byte SCMI message buffer + * [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). */ #define PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE 2 -/** - * struct optee_scmi_channel - OP-TEE server assigns channel ID per shmem - * @channel_id: SCMI channel ID to provide to the PTA - * @cinfo: SCMI channel info - * @shmem: Shmem reference use to store message payload +/* + * PTA_SCMI_CMD_GET_CHANNEL_HANDLE - 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 (PTA_SCMI_CAPS_*) */ +#define PTA_SCMI_CMD_GET_CHANNEL_HANDLE 3 + +/* + * Channel capabilities + */ + +/* Channel supports shared memory using the SMT header protocol */ +#define PTA_SCMI_CAPS_SMT_HEADER BIT(0) + struct optee_scmi_channel { - uint32_t channel_id; + /* OP-TEE channel ID used in transoprt */ + u32 channel_id; + /* Channel entry protection */ + struct mutex mu; + /* Channel private data */ + u32 tee_session; + struct optee_scmi_agent *agent; struct scmi_chan_info *cinfo; struct scmi_shared_mem __iomem *shmem; + /* Channel capabilities */ + u32 caps; + /* Reference in agent's channel list */ + struct list_head link; }; /** - * struct optee_scmi_agent - OP-TEE Random Number Generator private data - * @dev: OP-TEE based SCMI server device. - * @ctx: OP-TEE context handler. - * @tee_session_id: SCMI server TA session identifier in OP-TEE - * @channel_count: Count of agent channels supported by the server + * struct optee_scmi_agent - OP-TEE transport private data */ struct optee_scmi_agent { + /* TEE context the agent operates with */ struct device *dev; - struct tee_context *ctx; - u32 session_id; - unsigned int channel_count; + struct tee_context *tee_ctx; + /* Supported channel capabilities (PTA_SCMI_CAPS_*) */ + u32 caps; + /* List all created channels */ + struct list_head channel_list; }; /* There is only 1 SCMI PTA to connect to */ -static struct optee_scmi_agent agent_private; +static struct optee_scmi_agent *agent_private; -static struct scmi_shared_mem *get_channel_shm(struct optee_scmi_channel *chan, - struct scmi_xfer *xfer) +static struct list_head optee_agent_list = LIST_HEAD_INIT(optee_agent_list); +static DEFINE_MUTEX(list_mutex); + +/* Open a session toward SCMI PTA with REE_KERNEL identity */ +static int open_session(struct optee_scmi_agent *agent, u32 *tee_session) { - if (!chan) - return NULL; + struct device *dev = agent->dev; + struct tee_client_device *scmi_pta = to_tee_client_device(dev); + struct tee_ioctl_open_session_arg sess_arg; + int ret; - return chan->shmem; + memset(&sess_arg, 0, sizeof(sess_arg)); + + memcpy(sess_arg.uuid, scmi_pta->id.uuid.b, TEE_IOCTL_UUID_LEN); + sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; + + ret = tee_client_open_session(agent->tee_ctx, &sess_arg, NULL); + if ((ret < 0) || (sess_arg.ret != 0)) { + dev_err(dev, "tee_client_open_session failed, err: %x\n", + sess_arg.ret); + return -EINVAL; + } + + *tee_session = sess_arg.session; + + return 0; +} + +static void close_session(struct optee_scmi_agent *agent, u32 tee_session) +{ + tee_client_close_session(agent->tee_ctx, tee_session); } -static int get_channel_count(void) +static int get_capabilities(struct optee_scmi_agent *agent) { int ret; struct tee_ioctl_invoke_arg inv_arg; struct tee_param param[1]; + u32 tee_session; + + ret = open_session(agent, &tee_session); + if (ret) + return ret; memset(&inv_arg, 0, sizeof(inv_arg)); memset(¶m, 0, sizeof(param)); - inv_arg.func = PTA_SCMI_CMD_CHANNEL_COUNT; - inv_arg.session = agent_private.session_id; + inv_arg.func = PTA_SCMI_CMD_CAPABILITIES; + inv_arg.session = tee_session; inv_arg.num_params = 1; param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT; - ret = tee_client_invoke_func(agent_private.ctx, &inv_arg, param); + ret = tee_client_invoke_func(agent->tee_ctx, &inv_arg, param); + + close_session(agent, tee_session); + if ((ret < 0) || (inv_arg.ret != 0)) { - dev_err(agent_private.dev, "Can't get channel count: 0x%x\n", - inv_arg.ret); + dev_err(agent->dev, "Can't get capabilities: %d / %#x\n", + ret, inv_arg.ret); + + return -ENOTSUPP; + } + + agent->caps = param[0].u.value.a; + + if (!(agent->caps & PTA_SCMI_CAPS_SMT_HEADER)) { + dev_err(agent->dev, "OP-TEE SCMI PTA doesn't support SMT\n"); + + return -ENODEV; + } + + return 0; +} + +static int get_channel(struct optee_scmi_channel *channel) +{ + struct device *dev = channel->agent->dev; + int ret; + struct tee_ioctl_invoke_arg inv_arg; + struct tee_param param[1]; + unsigned int caps; + + caps = PTA_SCMI_CAPS_SMT_HEADER; + + memset(&inv_arg, 0, sizeof(inv_arg)); + memset(¶m, 0, sizeof(param)); + + inv_arg.func = PTA_SCMI_CMD_GET_CHANNEL_HANDLE; + inv_arg.session = channel->tee_session; + inv_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(channel->agent->tee_ctx, &inv_arg, param); + + if (ret || inv_arg.ret) { + dev_err(dev, "Can't get channel with caps %#x: ret=%d, tee-res=%#x\n", + caps, ret, inv_arg.ret); + return -ENOTSUPP; } - agent_private.channel_count = param[0].u.value.a; + /* Only channel handler is used, can discard old channel ID value */ + channel->channel_id = param[0].u.value.a; + channel->caps = caps; return 0; } -static int process_event(struct optee_scmi_channel *channel, - struct scmi_xfer *xfer) +/* Invocation of the PTA through a regular command invoke */ +static int invoke_process_smt_channel(struct optee_scmi_channel *channel) { int ret; struct tee_ioctl_invoke_arg inv_arg; struct tee_param param[1]; + if (!(channel->caps & PTA_SCMI_CAPS_SMT_HEADER)) + return -EINVAL; + memset(&inv_arg, 0, sizeof(inv_arg)); memset(¶m, 0, sizeof(param)); inv_arg.func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL; - inv_arg.session = agent_private.session_id; + inv_arg.session = channel->tee_session; inv_arg.num_params = 1; param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; param[0].u.value.a = channel->channel_id; - ret = tee_client_invoke_func(agent_private.ctx, &inv_arg, param); - if ((ret < 0) || (inv_arg.ret != 0)) { - dev_err(agent_private.dev, "Failed on channel %u: 0x%x\n", + ret = tee_client_invoke_func(channel->agent->tee_ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret) { + dev_err(channel->agent->dev, "Failed on channel %u: 0x%x\n", channel->channel_id, inv_arg.ret); return -EIO; } @@ -151,7 +253,7 @@ static bool optee_chan_available(struct device *dev, int idx) u32 channel_id; struct device_node *np = of_parse_phandle(dev->of_node, "shmem", 0); - /* Currently expect a shmem, but will maybe not in the future */ + /* Currently expect a shmem, but will maybe not in the future */ if (!np) return false; @@ -206,12 +308,9 @@ static int optee_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, int ret, idx = tx ? 0 : 1; /* Shall wait for OP-TEE driver to be up and ready */ - if (!agent_private.ctx) + if (!agent_private || !agent_private->tee_ctx) return -EPROBE_DEFER; - if (!agent_private.channel_count) - return -ENOENT; - channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL); if (!channel) return -ENOMEM; @@ -230,8 +329,29 @@ static int optee_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, cinfo->transport_info = channel; channel->cinfo = cinfo; + channel->agent = agent_private; + + ret = open_session(channel->agent, &channel->tee_session); + if (ret) + return ret; + + ret = get_channel(channel); + if (ret) + goto err; + + mutex_init(&channel->mu); + + mutex_lock(&list_mutex); + list_add(&channel->link, &channel->agent->channel_list); + mutex_unlock(&list_mutex); return 0; + +err: + close_session(channel->agent, channel->tee_session); + channel->tee_session = 0; + + return ret; } static int optee_chan_free(int id, void *p, void *data) @@ -239,6 +359,10 @@ static int optee_chan_free(int id, void *p, void *data) struct scmi_chan_info *cinfo = p; struct optee_scmi_channel *channel = cinfo->transport_info; + mutex_lock(&list_mutex); + list_del(&channel->link); + mutex_unlock(&list_mutex); + cinfo->transport_info = NULL; channel->cinfo = NULL; @@ -247,26 +371,36 @@ static int optee_chan_free(int id, void *p, void *data) return 0; } +static struct scmi_shared_mem *get_channel_shm(struct optee_scmi_channel *chan, + struct scmi_xfer *xfer) +{ + if (!chan) + return NULL; + + return chan->shmem; +} + static int optee_send_message(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) { struct optee_scmi_channel *channel = cinfo->transport_info; struct scmi_shared_mem *shmem; - int ret; + int ret = 0; - if (!channel && !agent_private.ctx) - return -EINVAL; + if (!channel && !channel->agent && !channel->agent->tee_ctx) + return -ENODEV; shmem = get_channel_shm(channel, xfer); + + mutex_lock(&channel->mu); shmem_tx_prepare(shmem, xfer); - ret = process_event(channel, xfer); - if (ret) - return ret; + ret = invoke_process_smt_channel(channel); scmi_rx_callback(cinfo, shmem_read_header(shmem), NULL); + mutex_unlock(&channel->mu); - return 0; + return ret; } static void optee_fetch_response(struct scmi_chan_info *cinfo, @@ -304,63 +438,67 @@ const struct scmi_desc scmi_optee_desc = { .max_msg_size = 128, }; -static int optee_ctx_match(struct tee_ioctl_version_data *ver, - const void *data) +static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) { return ver->impl_id == TEE_IMPL_ID_OPTEE; } static int optee_scmi_probe(struct device *dev) { - struct tee_client_device *scmi_device = to_tee_client_device(dev); - int ret = 0, err = -ENODEV; - struct tee_ioctl_open_session_arg sess_arg; - - memset(&sess_arg, 0, sizeof(sess_arg)); + struct optee_scmi_agent *agent; + struct tee_context *tee_ctx; + int ret; - agent_private.ctx = tee_client_open_context(NULL, optee_ctx_match, - NULL, NULL); - if (IS_ERR(agent_private.ctx)) + tee_ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL); + if (IS_ERR(tee_ctx)) return -ENODEV; - agent_private.dev = dev; + agent = devm_kzalloc(dev, sizeof(*agent), GFP_KERNEL); + if (!agent) { + ret = -ENOMEM; + goto err; + } - /* Open session with SCMI server TA */ - memcpy(sess_arg.uuid, scmi_device->id.uuid.b, TEE_IOCTL_UUID_LEN); - sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL; + agent->dev = dev; + agent->tee_ctx = tee_ctx; - ret = tee_client_open_session(agent_private.ctx, &sess_arg, NULL); - if ((ret < 0) || (sess_arg.ret != 0)) { - dev_err(dev, "tee_client_open_session failed, err: %x\n", - sess_arg.ret); - err = -EINVAL; - goto out_ctx; + ret = get_capabilities(agent); + if (ret) + goto err; + + /* We currently support only 1 OP-TEE device */ + if (WARN_ON(agent_private)) { + ret = -EINVAL; + goto err; } - agent_private.session_id = sess_arg.session; + agent_private = agent; - err = get_channel_count(); - if (err) - goto out_sess; + INIT_LIST_HEAD(&agent->channel_list); - dev_err(dev, "OP-TEE SCMI channel probed\n"); + dev_dbg(dev, "OP-TEE SCMI channel probed\n"); return 0; -out_sess: - tee_client_close_session(agent_private.ctx, agent_private.session_id); -out_ctx: - tee_client_close_context(agent_private.ctx); - agent_private.ctx = NULL; - - return err; +err: + tee_client_close_context(tee_ctx); + return ret; } static int optee_scmi_remove(struct device *dev) { - WARN_ON(1); - tee_client_close_session(agent_private.ctx, agent_private.session_id); - tee_client_close_context(agent_private.ctx); - agent_private.ctx = NULL; + struct optee_scmi_channel *channel; + struct list_head *elt, *n; + + mutex_lock(&list_mutex); + list_for_each_safe(elt, n, &agent_private->channel_list) { + channel = list_entry(elt, struct optee_scmi_channel, link); + list_del(&channel->link); + } + mutex_unlock(&list_mutex); + + tee_client_close_context(agent_private->tee_ctx); + + agent_private = NULL; return 0; } @@ -378,10 +516,10 @@ MODULE_DEVICE_TABLE(tee, optee_scmi_id_table); static struct tee_client_driver optee_scmi_driver = { .id_table = optee_scmi_id_table, .driver = { - .name = DRIVER_NAME, - .bus = &tee_bus_type, - .probe = optee_scmi_probe, - .remove = optee_scmi_remove, + .name = DRIVER_NAME, + .bus = &tee_bus_type, + .probe = optee_scmi_probe, + .remove = optee_scmi_remove, }, }; @@ -400,4 +538,4 @@ module_exit(optee_scmi_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Etienne Carriere "); -MODULE_DESCRIPTION("OP-TEE SCMI agent driver"); +MODULE_DESCRIPTION("OP-TEE SCMI transport driver");