blob: 7ff119154eb3ac51339ab5c26f2a343fe65215c7 [file] [log] [blame]
/*
* Copyright (C) 2012-2018, 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/err.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <tzdev/tee_client_api.h>
#include "iw_messages.h"
#include "misc.h"
#include "types.h"
#include "tz_iwsock.h"
#include "tzlog.h"
#define CONTEXT_NAME_SAMSUNG "TEE Samsung"
static uint32_t tzdev_teec_context_init(TEEC_Context *context)
{
struct tzdev_teec_context *ctx;
uint32_t result;
tzdev_teec_debug("Enter, context = %pK\n", context);
ctx = kmalloc(sizeof(struct tzdev_teec_context), GFP_KERNEL);
if (!ctx) {
tzdev_teec_error("Failed to allocate context struct\n");
result = TEEC_ERROR_OUT_OF_MEMORY;
goto out;
}
mutex_init(&ctx->mutex);
ctx->socket = NULL;
ctx->serial = 0;
ctx->id = 0;
context->imp = ctx;
result = TEEC_SUCCESS;
out:
tzdev_teec_debug("Exit, context = %pK result = %x\n", context, result);
return result;
}
static void tzdev_teec_context_fini(TEEC_Context *context)
{
struct tzdev_teec_context *ctx = context->imp;
tzdev_teec_debug("Enter, context = %pK\n", context);
context->imp = NULL;
mutex_destroy(&ctx->mutex);
kfree(ctx);
tzdev_teec_debug("Exit, context = %pK\n", context);
}
static uint32_t tzdev_teec_initialize_context(TEEC_Context *context, uint32_t *origin)
{
struct tzdev_teec_context *ctx = context->imp;
struct cmd_initialize_context cmd;
struct cmd_reply_initialize_context ack;
uint32_t result;
int ret;
tzdev_teec_debug("Enter, context = %pK\n", context);
*origin = TEEC_ORIGIN_API;
cmd.base.cmd = CMD_INITIALIZE_CONTEXT;
cmd.base.serial = ctx->serial;
ret = tzdev_teec_send_then_recv(ctx->socket,
&cmd, sizeof(cmd), 0x0,
&ack, sizeof(ack), 0x0,
&result, origin);
if (ret < 0) {
tzdev_teec_error("Failed to xmit initialize context, ret = %d\n", ret);
goto out;
}
ret = tzdev_teec_check_reply(&ack.base, CMD_REPLY_INITIALIZE_CONTEXT,
ctx->serial, &result, origin);
if (ret) {
tzdev_teec_error("Failed to check initialize context reply, ret = %d\n", ret);
goto out;
}
result = ack.base.result;
*origin = ack.base.origin;
ctx->id = ack.ctx_id;
out:
ctx->serial++;
tzdev_teec_fixup_origin(result, origin);
tzdev_teec_debug("Exit, context = %pK\n", context);
return result;
}
static uint32_t tzdev_teec_finalize_context(TEEC_Context *context, uint32_t *origin)
{
struct tzdev_teec_context *ctx = context->imp;
struct cmd_finalize_context cmd;
struct cmd_reply_finalize_context ack;
uint32_t result;
int ret;
tzdev_teec_debug("Enter, context = %pK\n", context);
*origin = TEEC_ORIGIN_API;
cmd.base.cmd = CMD_FINALIZE_CONTEXT;
cmd.base.serial = ctx->serial;
ret = tzdev_teec_send_then_recv(ctx->socket,
&cmd, sizeof(cmd), 0x0,
&ack, sizeof(ack), 0x0,
&result, origin);
if (ret < 0) {
tzdev_teec_error("Failed to xmit finalize context, ret = %d\n", ret);
goto out;
}
ret = tzdev_teec_check_reply(&ack.base, CMD_REPLY_FINALIZE_CONTEXT,
ctx->serial, &result, origin);
if (ret) {
tzdev_teec_error("Failed to check finalize context reply, ret = %d\n", ret);
goto out;
}
result = ack.base.result;
*origin = ack.base.origin;
out:
ctx->serial++;
tzdev_teec_fixup_origin(result, origin);
tzdev_teec_debug("Exit, context = %pK\n", context);
return result;
}
static uint32_t tzdev_teec_context_check_args(const char *name, TEEC_Context *context)
{
if (!context) {
tzdev_teec_error("Null context passed\n");
return TEEC_ERROR_BAD_PARAMETERS;
}
if (name && strncmp(name, CONTEXT_NAME_SAMSUNG, sizeof(CONTEXT_NAME_SAMSUNG))) {
tzdev_teec_error("Unsupported TEE name = \"%s\"\n", name);
return TEEC_ERROR_BAD_PARAMETERS;
}
return TEEC_SUCCESS;
}
TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *context)
{
struct tzdev_teec_context *ctx;
struct sock_desc *sd;
uint32_t result;
uint32_t origin;
int ret;
tzdev_teec_debug("Enter, context = %pK\n", context);
origin = TEEC_ORIGIN_API;
result = tzdev_teec_context_check_args(name, context);
if (result != TEEC_SUCCESS)
goto out;
result = tzdev_teec_context_init(context);
if (result != TEEC_SUCCESS)
goto out;
ctx = context->imp;
sd = tz_iwsock_socket(1);
if (IS_ERR(sd))
goto out_fini_context;
ret = tzdev_teec_connect(sd, ROOT_TASK_SOCK, &result, &origin);
if (ret < 0) {
tzdev_teec_error("Failed to connect to root task, context = %pK ret = %d\n",
context, ret);
goto out_disconnect;
}
ctx->socket = sd;
result = tzdev_teec_initialize_context(context, &origin);
if (result != TEEC_SUCCESS) {
tzdev_teec_error("Failed to initialize context, context = %pK\n", context);
goto out_disconnect;
}
tzdev_teec_debug("Success, context = %pK socket = %u id = %u\n",
context, ctx->socket->id, ctx->id);
goto out;
out_disconnect:
tzdev_teec_disconnect(sd);
out_fini_context:
tzdev_teec_context_fini(context);
out:
tzdev_teec_debug("Exit, context = %pK result = %x origin = %u\n", context, result, origin);
return result;
}
void TEEC_FinalizeContext(TEEC_Context *context)
{
struct tzdev_teec_context *ctx;
uint32_t result;
uint32_t origin;
tzdev_teec_debug("Enter, context = %pK\n", context);
origin = TEEC_ORIGIN_API;
if (!context || !context->imp) {
tzdev_teec_error("Null context passed\n");
result = TEEC_ERROR_BAD_PARAMETERS;
goto out;
}
ctx = context->imp;
result = tzdev_teec_finalize_context(context, &origin);
if (result != TEEC_SUCCESS) {
tzdev_teec_error("Failed to finalize context, context = %pK socket = %u id = %u\n",
context, ctx->socket->id, ctx->id);
goto out_fini;
}
tzdev_teec_debug("Success, context = %pK socket = %u id = %u\n",
context, ctx->socket->id, ctx->id);
out_fini:
tzdev_teec_disconnect(ctx->socket);
tzdev_teec_context_fini(context);
out:
tzdev_teec_debug("Exit, context = %pK result = %x origin = %u\n", context, result, origin);
}