Path: blob/master/drivers/firmware/arm_scmi/transports/optee.c
26483 views
// SPDX-License-Identifier: GPL-2.01/*2* Copyright (C) 2019-2021 Linaro Ltd.3*/45#include <linux/io.h>6#include <linux/of.h>7#include <linux/of_address.h>8#include <linux/kernel.h>9#include <linux/module.h>10#include <linux/mutex.h>11#include <linux/platform_device.h>12#include <linux/slab.h>13#include <linux/tee_drv.h>14#include <linux/uuid.h>15#include <uapi/linux/tee.h>1617#include "../common.h"1819enum scmi_optee_pta_cmd {20/*21* PTA_SCMI_CMD_CAPABILITIES - Get channel capabilities22*23* [out] value[0].a: Capability bit mask (enum pta_scmi_caps)24* [out] value[0].b: Extended capabilities or 025*/26PTA_SCMI_CMD_CAPABILITIES = 0,2728/*29* PTA_SCMI_CMD_PROCESS_SMT_CHANNEL - Process SCMI message in SMT buffer30*31* [in] value[0].a: Channel handle32*33* Shared memory used for SCMI message/response exhange is expected34* already identified and bound to channel handle in both SCMI agent35* and SCMI server (OP-TEE) parts.36* The memory uses SMT header to carry SCMI meta-data (protocol ID and37* protocol message ID).38*/39PTA_SCMI_CMD_PROCESS_SMT_CHANNEL = 1,4041/*42* PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE - Process SMT/SCMI message43*44* [in] value[0].a: Channel handle45* [in/out] memref[1]: Message/response buffer (SMT and SCMI payload)46*47* Shared memory used for SCMI message/response is a SMT buffer48* referenced by param[1]. It shall be 128 bytes large to fit response49* payload whatever message playload size.50* The memory uses SMT header to carry SCMI meta-data (protocol ID and51* protocol message ID).52*/53PTA_SCMI_CMD_PROCESS_SMT_CHANNEL_MESSAGE = 2,5455/*56* PTA_SCMI_CMD_GET_CHANNEL - Get channel handle57*58* SCMI shm information are 0 if agent expects to use OP-TEE regular SHM59*60* [in] value[0].a: Channel identifier61* [out] value[0].a: Returned channel handle62* [in] value[0].b: Requested capabilities mask (enum pta_scmi_caps)63*/64PTA_SCMI_CMD_GET_CHANNEL = 3,6566/*67* PTA_SCMI_CMD_PROCESS_MSG_CHANNEL - Process SCMI message in a MSG68* buffer pointed by memref parameters69*70* [in] value[0].a: Channel handle71* [in] memref[1]: Message buffer (MSG and SCMI payload)72* [out] memref[2]: Response buffer (MSG and SCMI payload)73*74* Shared memories used for SCMI message/response are MSG buffers75* referenced by param[1] and param[2]. MSG transport protocol76* uses a 32bit header to carry SCMI meta-data (protocol ID and77* protocol message ID) followed by the effective SCMI message78* payload.79*/80PTA_SCMI_CMD_PROCESS_MSG_CHANNEL = 4,81};8283/*84* OP-TEE SCMI service capabilities bit flags (32bit)85*86* PTA_SCMI_CAPS_SMT_HEADER87* When set, OP-TEE supports command using SMT header protocol (SCMI shmem) in88* shared memory buffers to carry SCMI protocol synchronisation information.89*90* PTA_SCMI_CAPS_MSG_HEADER91* When set, OP-TEE supports command using MSG header protocol in an OP-TEE92* shared memory to carry SCMI protocol synchronisation information and SCMI93* message payload.94*/95#define PTA_SCMI_CAPS_NONE 096#define PTA_SCMI_CAPS_SMT_HEADER BIT(0)97#define PTA_SCMI_CAPS_MSG_HEADER BIT(1)98#define PTA_SCMI_CAPS_MASK (PTA_SCMI_CAPS_SMT_HEADER | \99PTA_SCMI_CAPS_MSG_HEADER)100101/**102* struct scmi_optee_channel - Description of an OP-TEE SCMI channel103*104* @channel_id: OP-TEE channel ID used for this transport105* @tee_session: TEE session identifier106* @caps: OP-TEE SCMI channel capabilities107* @rx_len: Response size108* @mu: Mutex protection on channel access109* @cinfo: SCMI channel information110* @req: union for SCMI interface111* @req.shmem: Virtual base address of the shared memory112* @req.msg: Shared memory protocol handle for SCMI request and113* synchronous response114* @io_ops: Transport specific I/O operations115* @tee_shm: TEE shared memory handle @req or NULL if using IOMEM shmem116* @link: Reference in agent's channel list117*/118struct scmi_optee_channel {119u32 channel_id;120u32 tee_session;121u32 caps;122u32 rx_len;123struct mutex mu;124struct scmi_chan_info *cinfo;125union {126struct scmi_shared_mem __iomem *shmem;127struct scmi_msg_payld *msg;128} req;129struct scmi_shmem_io_ops *io_ops;130struct tee_shm *tee_shm;131struct list_head link;132};133134/**135* struct scmi_optee_agent - OP-TEE transport private data136*137* @dev: Device used for communication with TEE138* @tee_ctx: TEE context used for communication139* @caps: Supported channel capabilities140* @mu: Mutex for protection of @channel_list141* @channel_list: List of all created channels for the agent142*/143struct scmi_optee_agent {144struct device *dev;145struct tee_context *tee_ctx;146u32 caps;147struct mutex mu;148struct list_head channel_list;149};150151static struct scmi_transport_core_operations *core;152153/* There can be only 1 SCMI service in OP-TEE we connect to */154static struct scmi_optee_agent *scmi_optee_private;155156/* Open a session toward SCMI OP-TEE service with REE_KERNEL identity */157static int open_session(struct scmi_optee_agent *agent, u32 *tee_session)158{159struct device *dev = agent->dev;160struct tee_client_device *scmi_pta = to_tee_client_device(dev);161struct tee_ioctl_open_session_arg arg = { };162int ret;163164memcpy(arg.uuid, scmi_pta->id.uuid.b, TEE_IOCTL_UUID_LEN);165arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;166167ret = tee_client_open_session(agent->tee_ctx, &arg, NULL);168if (ret < 0 || arg.ret) {169dev_err(dev, "Can't open tee session: %d / %#x\n", ret, arg.ret);170return -EOPNOTSUPP;171}172173*tee_session = arg.session;174175return 0;176}177178static void close_session(struct scmi_optee_agent *agent, u32 tee_session)179{180tee_client_close_session(agent->tee_ctx, tee_session);181}182183static int get_capabilities(struct scmi_optee_agent *agent)184{185struct tee_ioctl_invoke_arg arg = { };186struct tee_param param[1] = { };187u32 caps;188u32 tee_session;189int ret;190191ret = open_session(agent, &tee_session);192if (ret)193return ret;194195arg.func = PTA_SCMI_CMD_CAPABILITIES;196arg.session = tee_session;197arg.num_params = 1;198199param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;200201ret = tee_client_invoke_func(agent->tee_ctx, &arg, param);202203close_session(agent, tee_session);204205if (ret < 0 || arg.ret) {206dev_err(agent->dev, "Can't get capabilities: %d / %#x\n", ret, arg.ret);207return -EOPNOTSUPP;208}209210caps = param[0].u.value.a;211212if (!(caps & (PTA_SCMI_CAPS_SMT_HEADER | PTA_SCMI_CAPS_MSG_HEADER))) {213dev_err(agent->dev, "OP-TEE SCMI PTA doesn't support SMT and MSG\n");214return -EOPNOTSUPP;215}216217agent->caps = caps;218219return 0;220}221222static int get_channel(struct scmi_optee_channel *channel)223{224struct device *dev = scmi_optee_private->dev;225struct tee_ioctl_invoke_arg arg = { };226struct tee_param param[1] = { };227unsigned int caps = 0;228int ret;229230if (channel->tee_shm)231caps = PTA_SCMI_CAPS_MSG_HEADER;232else233caps = PTA_SCMI_CAPS_SMT_HEADER;234235arg.func = PTA_SCMI_CMD_GET_CHANNEL;236arg.session = channel->tee_session;237arg.num_params = 1;238239param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT;240param[0].u.value.a = channel->channel_id;241param[0].u.value.b = caps;242243ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param);244245if (ret || arg.ret) {246dev_err(dev, "Can't get channel with caps %#x: %d / %#x\n", caps, ret, arg.ret);247return -EOPNOTSUPP;248}249250/* From now on use channel identifer provided by OP-TEE SCMI service */251channel->channel_id = param[0].u.value.a;252channel->caps = caps;253254return 0;255}256257static int invoke_process_smt_channel(struct scmi_optee_channel *channel)258{259struct tee_ioctl_invoke_arg arg = {260.func = PTA_SCMI_CMD_PROCESS_SMT_CHANNEL,261.session = channel->tee_session,262.num_params = 1,263};264struct tee_param param[1] = { };265int ret;266267param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;268param[0].u.value.a = channel->channel_id;269270ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param);271if (ret < 0 || arg.ret) {272dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n",273channel->channel_id, ret, arg.ret);274return -EIO;275}276277return 0;278}279280static int invoke_process_msg_channel(struct scmi_optee_channel *channel, size_t msg_size)281{282struct tee_ioctl_invoke_arg arg = {283.func = PTA_SCMI_CMD_PROCESS_MSG_CHANNEL,284.session = channel->tee_session,285.num_params = 3,286};287struct tee_param param[3] = { };288int ret;289290param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;291param[0].u.value.a = channel->channel_id;292293param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;294param[1].u.memref.shm = channel->tee_shm;295param[1].u.memref.size = msg_size;296297param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;298param[2].u.memref.shm = channel->tee_shm;299param[2].u.memref.size = SCMI_SHMEM_MAX_PAYLOAD_SIZE;300301ret = tee_client_invoke_func(scmi_optee_private->tee_ctx, &arg, param);302if (ret < 0 || arg.ret) {303dev_err(scmi_optee_private->dev, "Can't invoke channel %u: %d / %#x\n",304channel->channel_id, ret, arg.ret);305return -EIO;306}307308/* Save response size */309channel->rx_len = param[2].u.memref.size;310311return 0;312}313314static bool scmi_optee_chan_available(struct device_node *of_node, int idx)315{316u32 channel_id;317318return !of_property_read_u32_index(of_node, "linaro,optee-channel-id",319idx, &channel_id);320}321322static void scmi_optee_clear_channel(struct scmi_chan_info *cinfo)323{324struct scmi_optee_channel *channel = cinfo->transport_info;325326if (!channel->tee_shm)327core->shmem->clear_channel(channel->req.shmem);328}329330static int setup_dynamic_shmem(struct device *dev, struct scmi_optee_channel *channel)331{332const size_t msg_size = SCMI_SHMEM_MAX_PAYLOAD_SIZE;333void *shbuf;334335channel->tee_shm = tee_shm_alloc_kernel_buf(scmi_optee_private->tee_ctx, msg_size);336if (IS_ERR(channel->tee_shm)) {337dev_err(channel->cinfo->dev, "shmem allocation failed\n");338return -ENOMEM;339}340341shbuf = tee_shm_get_va(channel->tee_shm, 0);342memset(shbuf, 0, msg_size);343channel->req.msg = shbuf;344channel->rx_len = msg_size;345346return 0;347}348349static int setup_static_shmem(struct device *dev, struct scmi_chan_info *cinfo,350struct scmi_optee_channel *channel)351{352channel->req.shmem = core->shmem->setup_iomap(cinfo, dev, true, NULL,353&channel->io_ops);354if (IS_ERR(channel->req.shmem))355return PTR_ERR(channel->req.shmem);356357return 0;358}359360static int setup_shmem(struct device *dev, struct scmi_chan_info *cinfo,361struct scmi_optee_channel *channel)362{363if (of_property_present(cinfo->dev->of_node, "shmem"))364return setup_static_shmem(dev, cinfo, channel);365else366return setup_dynamic_shmem(dev, channel);367}368369static int scmi_optee_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, bool tx)370{371struct scmi_optee_channel *channel;372uint32_t channel_id;373int ret;374375if (!tx)376return -ENODEV;377378channel = devm_kzalloc(dev, sizeof(*channel), GFP_KERNEL);379if (!channel)380return -ENOMEM;381382ret = of_property_read_u32_index(cinfo->dev->of_node, "linaro,optee-channel-id",3830, &channel_id);384if (ret)385return ret;386387cinfo->transport_info = channel;388channel->cinfo = cinfo;389channel->channel_id = channel_id;390mutex_init(&channel->mu);391392ret = setup_shmem(dev, cinfo, channel);393if (ret)394return ret;395396ret = open_session(scmi_optee_private, &channel->tee_session);397if (ret)398goto err_free_shm;399400ret = tee_client_system_session(scmi_optee_private->tee_ctx, channel->tee_session);401if (ret)402dev_warn(dev, "Could not switch to system session, do best effort\n");403404ret = get_channel(channel);405if (ret)406goto err_close_sess;407408/* Enable polling */409cinfo->no_completion_irq = true;410411mutex_lock(&scmi_optee_private->mu);412list_add(&channel->link, &scmi_optee_private->channel_list);413mutex_unlock(&scmi_optee_private->mu);414415return 0;416417err_close_sess:418close_session(scmi_optee_private, channel->tee_session);419err_free_shm:420if (channel->tee_shm)421tee_shm_free(channel->tee_shm);422423return ret;424}425426static int scmi_optee_chan_free(int id, void *p, void *data)427{428struct scmi_chan_info *cinfo = p;429struct scmi_optee_channel *channel = cinfo->transport_info;430431/*432* Different protocols might share the same chan info, so a previous433* call might have already freed the structure.434*/435if (!channel)436return 0;437438mutex_lock(&scmi_optee_private->mu);439list_del(&channel->link);440mutex_unlock(&scmi_optee_private->mu);441442close_session(scmi_optee_private, channel->tee_session);443444if (channel->tee_shm) {445tee_shm_free(channel->tee_shm);446channel->tee_shm = NULL;447}448449cinfo->transport_info = NULL;450channel->cinfo = NULL;451452return 0;453}454455static int scmi_optee_send_message(struct scmi_chan_info *cinfo,456struct scmi_xfer *xfer)457{458struct scmi_optee_channel *channel = cinfo->transport_info;459int ret;460461mutex_lock(&channel->mu);462463if (channel->tee_shm) {464core->msg->tx_prepare(channel->req.msg, xfer);465ret = invoke_process_msg_channel(channel,466core->msg->command_size(xfer));467} else {468core->shmem->tx_prepare(channel->req.shmem, xfer, cinfo,469channel->io_ops->toio);470ret = invoke_process_smt_channel(channel);471}472473if (ret)474mutex_unlock(&channel->mu);475476return ret;477}478479static void scmi_optee_fetch_response(struct scmi_chan_info *cinfo,480struct scmi_xfer *xfer)481{482struct scmi_optee_channel *channel = cinfo->transport_info;483484if (channel->tee_shm)485core->msg->fetch_response(channel->req.msg,486channel->rx_len, xfer);487else488core->shmem->fetch_response(channel->req.shmem, xfer,489channel->io_ops->fromio);490}491492static void scmi_optee_mark_txdone(struct scmi_chan_info *cinfo, int ret,493struct scmi_xfer *__unused)494{495struct scmi_optee_channel *channel = cinfo->transport_info;496497mutex_unlock(&channel->mu);498}499500static struct scmi_transport_ops scmi_optee_ops = {501.chan_available = scmi_optee_chan_available,502.chan_setup = scmi_optee_chan_setup,503.chan_free = scmi_optee_chan_free,504.send_message = scmi_optee_send_message,505.mark_txdone = scmi_optee_mark_txdone,506.fetch_response = scmi_optee_fetch_response,507.clear_channel = scmi_optee_clear_channel,508};509510static int scmi_optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)511{512return ver->impl_id == TEE_IMPL_ID_OPTEE;513}514515static struct scmi_desc scmi_optee_desc = {516.ops = &scmi_optee_ops,517.max_rx_timeout_ms = 30,518.max_msg = 20,519.max_msg_size = SCMI_SHMEM_MAX_PAYLOAD_SIZE,520.sync_cmds_completed_on_ret = true,521};522523static const struct of_device_id scmi_of_match[] = {524{ .compatible = "linaro,scmi-optee" },525{ /* Sentinel */ },526};527528DEFINE_SCMI_TRANSPORT_DRIVER(scmi_optee, scmi_optee_driver, scmi_optee_desc,529scmi_of_match, core);530531static int scmi_optee_service_probe(struct device *dev)532{533struct scmi_optee_agent *agent;534struct tee_context *tee_ctx;535int ret;536537/* Only one SCMI OP-TEE device allowed */538if (scmi_optee_private) {539dev_err(dev, "An SCMI OP-TEE device was already initialized: only one allowed\n");540return -EBUSY;541}542543tee_ctx = tee_client_open_context(NULL, scmi_optee_ctx_match, NULL, NULL);544if (IS_ERR(tee_ctx))545return -ENODEV;546547agent = devm_kzalloc(dev, sizeof(*agent), GFP_KERNEL);548if (!agent) {549ret = -ENOMEM;550goto err;551}552553agent->dev = dev;554agent->tee_ctx = tee_ctx;555INIT_LIST_HEAD(&agent->channel_list);556mutex_init(&agent->mu);557558ret = get_capabilities(agent);559if (ret)560goto err;561562/* Ensure agent resources are all visible before scmi_optee_private is */563smp_mb();564scmi_optee_private = agent;565566ret = platform_driver_register(&scmi_optee_driver);567if (ret) {568scmi_optee_private = NULL;569goto err;570}571572return 0;573574err:575tee_client_close_context(tee_ctx);576577return ret;578}579580static int scmi_optee_service_remove(struct device *dev)581{582struct scmi_optee_agent *agent = scmi_optee_private;583584if (!scmi_optee_private)585return -EINVAL;586587platform_driver_unregister(&scmi_optee_driver);588589if (!list_empty(&scmi_optee_private->channel_list))590return -EBUSY;591592/* Ensure cleared reference is visible before resources are released */593smp_store_mb(scmi_optee_private, NULL);594595tee_client_close_context(agent->tee_ctx);596597return 0;598}599600static const struct tee_client_device_id scmi_optee_service_id[] = {601{602UUID_INIT(0xa8cfe406, 0xd4f5, 0x4a2e,6030x9f, 0x8d, 0xa2, 0x5d, 0xc7, 0x54, 0xc0, 0x99)604},605{ }606};607608MODULE_DEVICE_TABLE(tee, scmi_optee_service_id);609610static struct tee_client_driver scmi_optee_service_driver = {611.id_table = scmi_optee_service_id,612.driver = {613.name = "scmi-optee",614.bus = &tee_bus_type,615.probe = scmi_optee_service_probe,616.remove = scmi_optee_service_remove,617},618};619620static int __init scmi_transport_optee_init(void)621{622return driver_register(&scmi_optee_service_driver.driver);623}624module_init(scmi_transport_optee_init);625626static void __exit scmi_transport_optee_exit(void)627{628driver_unregister(&scmi_optee_service_driver.driver);629}630module_exit(scmi_transport_optee_exit);631632MODULE_AUTHOR("Etienne Carriere <[email protected]>");633MODULE_DESCRIPTION("SCMI OPTEE Transport driver");634MODULE_LICENSE("GPL");635636637