blob: ac7844ae67e585033dbd82c82fb4c8ae24f25129 [file] [log] [blame]
/*
* Copyright (C) 2012-2017, Samsung Electronics Co., Ltd.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/anon_inodes.h>
#include <linux/atomic.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/file.h>
#include <linux/ioctl.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <tzdev/tee_client_api.h>
#include "misc.h"
#include "sysdep.h"
#include "tz_iwsock.h"
#include "tz_kthread_pool.h"
#include "tzlog.h"
#define MAX_CONNECTION_ATTEMPTS 20
#define CONNECTION_ATTEMPT_TIMEOUT 100
int tzdev_teec_connect(struct sock_desc *socket, char *name,
uint32_t *result, uint32_t *origin)
{
int ret;
unsigned int attempt;
*origin = TEEC_ORIGIN_COMMS;
*result = TEEC_SUCCESS;
for (attempt = 0; attempt < MAX_CONNECTION_ATTEMPTS; attempt++) {
ret = tz_iwsock_connect(socket, name, 0);
if (ret == 0)
return ret;
tzdev_teec_info("Failed to connect to %s socket, error = %d, retrying...\n",
name, ret);
msleep(CONNECTION_ATTEMPT_TIMEOUT);
}
tzdev_teec_error("Failed to connect to %s socket after %d retries\n",
name, MAX_CONNECTION_ATTEMPTS);
*result = tzdev_teec_error_to_tee_error(ret);
return ret;
}
void tzdev_teec_disconnect(struct sock_desc *socket)
{
tz_iwsock_release(socket);
}
int tzdev_teec_send(struct sock_desc *socket, void *data, uint32_t size, int flags,
uint32_t *result, uint32_t *origin)
{
int ret;
*origin = TEEC_ORIGIN_COMMS;
*result = TEEC_SUCCESS;
ret = tz_iwsock_write(socket, data, size, flags);
if (ret >= 0 && ret != size)
ret = -EMSGSIZE;
if (ret < 0)
*result = tzdev_teec_error_to_tee_error(ret);
return ret;
}
int tzdev_teec_recv(struct sock_desc *socket, void *data, uint32_t size, int flags,
uint32_t *result, uint32_t *origin)
{
int ret;
*origin = TEEC_ORIGIN_COMMS;
*result = TEEC_SUCCESS;
ret = tz_iwsock_read(socket, data, size, flags);
if (ret > 0 && ret != size)
ret = -EMSGSIZE;
if (!ret)
ret = -ECONNRESET;
if (ret < 0)
*result = tzdev_teec_error_to_tee_error(ret);
return ret;
}
int tzdev_teec_send_then_recv(struct sock_desc *socket,
void *send_data, uint32_t send_size, int send_flags,
void *recv_data, uint32_t recv_size, int recv_flags,
uint32_t *result, uint32_t *origin)
{
int ret;
ret = tzdev_teec_send(socket, send_data, send_size, send_flags,
result, origin);
if (ret < 0)
return ret;
ret = tzdev_teec_recv(socket, recv_data, recv_size, recv_flags,
result, origin);
if (ret < 0)
return ret;
return 0;
}
int tzdev_teec_check_reply(struct cmd_reply *reply, uint32_t cmd, uint32_t serial,
uint32_t *result, uint32_t *origin)
{
int ret;
*origin = TEEC_ORIGIN_COMMS;
*result = TEEC_SUCCESS;
if (reply->base.cmd != cmd) {
tzdev_teec_error("Received wrong reply = %u, expected = %u\n",
reply->base.cmd, cmd);
ret = -ECOMM;
goto out;
}
if (reply->base.serial != serial) {
tzdev_teec_error("Received wrong reply serial = %u, expected = %u\n",
reply->base.serial, serial);
ret = -ECOMM;
goto out;
}
return 0;
out:
*result = tzdev_teec_error_to_tee_error(ret);
return ret;
}
uint32_t tzdev_teec_error_to_tee_error(int error)
{
switch (error) {
case -EINVAL:
return TEEC_ERROR_BAD_PARAMETERS;
case -ENOMEM:
return TEEC_ERROR_OUT_OF_MEMORY;
case -EPIPE:
case -ENOTCONN:
case -ECONNREFUSED:
case -EMSGSIZE:
case -ECOMM:
return TEEC_ERROR_COMMUNICATION;
case -ECONNRESET:
return TEEC_ERROR_TARGET_DEAD;
case -ECANCELED:
return TEEC_ERROR_CANCEL;
default:
tzdev_teec_error("Unknown error code = %d\n", error);
return TEEC_ERROR_GENERIC;
}
}
void tzdev_teec_fixup_origin(uint32_t result, uint32_t *origin)
{
if (result == TEEC_ERROR_TARGET_DEAD)
*origin = TEEC_ORIGIN_TEE;
}