/**************************************************************************** * drivers/misc/optee_socket.c * * SPDX-License-Identifier: Apache-2.0 * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include "optee.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define OPTEE_SOCKET_MAX_IOVEC_NUM 7 /**************************************************************************** * Private Types ****************************************************************************/ struct optee_socket_priv_data { struct optee_priv_data base; struct socket socket; }; /**************************************************************************** * Private Data ****************************************************************************/ /**************************************************************************** * Private Functions ****************************************************************************/ static int optee_socket_recv(FAR struct socket *psock, FAR void *msg, size_t size) { while (size > 0) { ssize_t n = psock_recv(psock, msg, size, 0); if (n <= 0) { return n < 0 ? n : -EIO; } size -= n; msg = (FAR char *)msg + n; } return 0; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: optee_transport_init * * Description: * Perform any initialization actions specific to the transport used * right before the driver is registered. * * Returned Values: * 0 on success; A negated errno value is returned on any failure. * ****************************************************************************/ int optee_transport_init(void) { return 0; } /**************************************************************************** * Name: optee_transport_open * * Description: * Perform any transport-specific actions upon driver character device * open. In this case, open the socket and connect to it. * * Parameters: * priv_ - the optee_priv_data struct to allocate and return by * reference. * * Returned Values: * 0 on success; A negated errno value is returned on any failure. * ****************************************************************************/ int optee_transport_open(FAR struct optee_priv_data **priv_) { FAR struct optee_socket_priv_data *priv; #ifdef CONFIG_DEV_OPTEE_LOCAL struct sockaddr_un addr; #else struct sockaddr_rpmsg addr; #endif int ret; priv = kmm_zalloc(sizeof(struct optee_socket_priv_data)); if (priv == NULL) { return -ENOMEM; } #ifdef CONFIG_DEV_OPTEE_LOCAL ret = psock_socket(AF_UNIX, SOCK_STREAM, 0, &priv->socket); #else ret = psock_socket(AF_RPMSG, SOCK_STREAM, 0, &priv->socket); #endif if (ret < 0) { kmm_free(priv); return ret; } memset(&addr, 0, sizeof(addr)); #ifdef CONFIG_DEV_OPTEE_LOCAL addr.sun_family = AF_UNIX; strlcpy(addr.sun_path, OPTEE_SERVER_PATH, sizeof(addr.sun_path)); #else addr.rp_family = AF_RPMSG; strlcpy(addr.rp_name, OPTEE_SERVER_PATH, sizeof(addr.rp_name)); strlcpy(addr.rp_cpu, CONFIG_OPTEE_REMOTE_CPU_NAME, sizeof(addr.rp_cpu)); #endif ret = psock_connect(&priv->socket, (FAR const struct sockaddr *)&addr, sizeof(addr)); if (ret < 0) { psock_close(&priv->socket); kmm_free(priv); return ret; } priv->base.alignment = 0; *priv_ = (FAR struct optee_priv_data *)priv; return 0; } /**************************************************************************** * Name: optee_transport_close * * Description: * Perform any transport-specific actions upon driver character device * close. * * Parameters: * priv_ - the optee_priv_data struct to close and de-allocate. * * Returned Values: * None * ****************************************************************************/ void optee_transport_close(FAR struct optee_priv_data *priv_) { FAR struct optee_socket_priv_data *priv = (FAR struct optee_socket_priv_data *)priv_; psock_close(&priv->socket); kmm_free(priv); } /**************************************************************************** * Name: optee_transport_call * * Description: * Call OP-TEE OS through the RPMsg/local socket. * * Parameters: * priv_ - the optee_priv_data struct to use * * Returned Values: * 0 on success; A negated errno value is returned on any failure. * ****************************************************************************/ int optee_transport_call(FAR struct optee_priv_data *priv_, FAR struct optee_msg_arg *arg) { /* iov[0]: struct opteee_msg_arg + struct optee_msg_param[n] * iov[1 - n+1]: shm_mem * 0 <= n <= 6 */ FAR struct optee_socket_priv_data *priv = (FAR struct optee_socket_priv_data *)priv_; size_t arg_size = OPTEE_MSG_GET_ARG_SIZE(arg->num_params); size_t shm_size[OPTEE_MAX_PARAM_NUM]; size_t shm_addr[OPTEE_MAX_PARAM_NUM]; struct iovec iov[OPTEE_SOCKET_MAX_IOVEC_NUM]; struct msghdr msghdr; unsigned long iovlen = 1; unsigned long i; int ret; memset(iov, 0, sizeof(iov)); memset(shm_size, 0, sizeof(shm_size)); iov[0].iov_base = arg; iov[0].iov_len = arg_size; for (i = 0; i < arg->num_params; i++) { if (arg->params[i].attr == OPTEE_MSG_ATTR_TYPE_RMEM_INPUT || arg->params[i].attr == OPTEE_MSG_ATTR_TYPE_RMEM_INOUT) { iov[iovlen].iov_base = (FAR void *)(uintptr_t)arg->params[i].u.rmem.shm_ref; iov[iovlen].iov_len = arg->params[i].u.rmem.size; shm_size[i] = arg->params[i].u.rmem.size; shm_addr[i] = arg->params[i].u.rmem.shm_ref; iovlen++; } else if (arg->params[i].attr == OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT) { shm_size[i] = arg->params[i].u.rmem.size; shm_addr[i] = arg->params[i].u.rmem.shm_ref; } } memset(&msghdr, 0, sizeof(struct msghdr)); msghdr.msg_iov = iov; msghdr.msg_iovlen = iovlen; ret = psock_sendmsg(&priv->socket, &msghdr, 0); if (ret < 0) { return ret; } ret = optee_socket_recv(&priv->socket, arg, arg_size); if (ret < 0) { return ret; } for (i = 0; i < arg->num_params; i++) { if (arg->params[i].attr == OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT || arg->params[i].attr == OPTEE_MSG_ATTR_TYPE_RMEM_INOUT) { size_t size = MIN(arg->params[i].u.rmem.size, shm_size[i]); arg->params[i].u.rmem.shm_ref = shm_addr[i]; ret = optee_socket_recv(&priv->socket, (FAR void *)(uintptr_t) arg->params[i].u.rmem.shm_ref, size); if (ret < 0) { return ret; } } } return 0; }