blob: d9cf56e2f500eea258244580063635d7f8292af7 [file] [log] [blame]
/*
* Copyright (c) 2012-2015, NVIDIA CORPORATION. All rights reserved
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <debug.h>
#include <errno.h>
#include <stddef.h>
#include <list.h>
#include <malloc.h>
#include <err.h>
#include <string.h>
#include <assert.h>
#include <kernel/thread.h>
#include <kernel/event.h>
#include <platform/memmap.h>
#include <ote_intf.h>
#include <lib/ote/ote_protocol.h>
#define MAX_ARGS_PER_CMD 0x8
#define PRINT_UUID(s,u) \
dprintf(CRITICAL, "%s: uuid: 0x%x 0x%x 0x%x 0x%x%x 0x%x%x%x%x%x%x\n", \
s, (u)->time_low, (u)->time_mid, \
(u)->time_hi_and_version, \
(u)->clock_seq_and_node[0], \
(u)->clock_seq_and_node[1], \
(u)->clock_seq_and_node[2], \
(u)->clock_seq_and_node[3], \
(u)->clock_seq_and_node[4], \
(u)->clock_seq_and_node[5], \
(u)->clock_seq_and_node[6], \
(u)->clock_seq_and_node[7]);
static event_t task_event;
static unsigned int nactive_sessions;
/* global session id list */
static struct list_node session_list;
static struct list_node completion_list;
typedef struct {
struct list_node node;
bool active;
task_map_t *mptr;
} persist_map_t;
typedef struct {
struct list_node node;
struct list_node cmd_node;
unsigned int session_id;
struct list_node persist_maps;
void *context;
thread_t *thread;
} session_t;
typedef struct te_command {
struct list_node node;
te_entry_point_message_t msg;
session_t *session;
task_map_t *mptr[MAX_ARGS_PER_CMD];
u_int maps;
task_t *requesting_task;
uint32_t returning_cmd;
} te_command_t;
/* Check if client is restriced access.
* 'task_issued' is true when client is secure task.
* returns true if access is denied.
* returns false otherwise. */
static bool check_restrict_access(task_t *task, bool task_issued)
{
ASSERT(task != NULL);
u_int restricted = task->props.restrict_access;
if (task_issued && (restricted & OTE_RESTRICT_SECURE_TASKS)) {
dprintf(SPEW, "%s: Secure task client type denied access\n",
__func__);
return true;
}
if (!task_issued && (restricted & OTE_RESTRICT_NON_SECURE_APPS)) {
dprintf(SPEW, "%s: Non-secure app client type denied access\n",
__func__);
return true;
}
if (task->task_state == TASK_STATE_BLOCKED) {
dprintf(INFO, "%s: Task administratively blocked -- access denied\n",
__func__);
return true;
}
if (task->task_state != TASK_STATE_ACTIVE) {
dprintf(INFO, "%s: Task is not in active state (%d) -- access denied\n",
__func__, task->task_state);
return true;
}
return false;
}
static char *te_get_fd_str(uint32_t fd)
{
char *fd_str;
switch (fd) {
case TE_INTERFACE:
fd_str = "INTERFACE";
break;
case TE_RESULT:
fd_str = "RESULT";
break;
case TE_INFO:
fd_str = "INFO";
break;
case TE_ERR:
fd_str = "ERR";
break;
case TE_SECURE:
fd_str = "SECURE";
break;
case TE_CRITICAL:
fd_str = "CRITICAL";
break;
default:
fd_str = "BAD FD";
}
return fd_str;
}
static uint32_t new_session_id(void)
{
return platform_get_rand32();
}
static session_t *te_get_session_from_id(uint32_t id)
{
session_t *session;
list_for_every_entry(&session_list, session, session_t, node) {
if (session->session_id == id)
return session;
}
exit:
return NULL;
}
static te_error_t te_add_persist_map(task_map_t *mptr, session_t *session)
{
persist_map_t *persist_map = NULL;
persist_map = calloc(1, sizeof(persist_map_t));
if (persist_map == NULL) {
return OTE_ERROR_OUT_OF_MEMORY;
}
list_initialize(&persist_map->node);
persist_map->mptr = mptr;
persist_map->active = false;
enter_critical_section();
list_add_tail(&session->persist_maps, &persist_map->node);
exit_critical_section();
return OTE_SUCCESS;
}
static void te_release_persist_maps(session_t *session, task_t *task,
bool only_inactive)
{
persist_map_t *persist_map, *next;
list_for_every_entry_safe(&session->persist_maps,
persist_map, next, persist_map_t, node) {
/* skip active mappings if desired */
if (only_inactive && persist_map->active)
continue;
arch_task_unmap_memory(task, persist_map->mptr);
list_delete(&persist_map->node);
free(persist_map);
}
}
static void te_update_persist_maps(session_t *session, task_t *task)
{
persist_map_t *persist_map, *next;
/* activate any pending persistent mappings */
list_for_every_entry_safe(&session->persist_maps,
persist_map, next, persist_map_t, node) {
if (!persist_map->active)
persist_map->active = true;
}
}
static te_error_t te_prepare_memref_param(task_t *task, struct te_command *cmd,
te_oper_param_t *param)
{
task_map_t *mptr;
uint64_t vaddr;
uint32_t size, flags;
te_error_t result;
/* set read/write flags */
if ((param->type == TE_PARAM_TYPE_MEM_RW) ||
(param->type == TE_PARAM_TYPE_PERSIST_MEM_RW)) {
flags = (TM_UR | TM_UW);
} else {
flags = TM_UR;
}
/* make sure we have room if it's a temporary memref */
if ((param->type == TE_PARAM_TYPE_MEM_RO) ||
(param->type == TE_PARAM_TYPE_MEM_RW)) {
if (cmd->maps == MAX_ARGS_PER_CMD) {
dprintf(CRITICAL, "out of memref maps\n");
return OTE_ERROR_OUT_OF_MEMORY;
}
}
vaddr = param->u.Mem.base;
size = param->u.Mem.len;
if (!cmd->requesting_task) {
/* command (and memory) came from the NS world */
flags |= TM_NS_MEM;
} else {
task_t *ctask = cmd->requesting_task;
task_map_t *cmptr;
/*
* In mapping memory from one task to another, the pages could
* have come from either the NS world, or its own malloc-ed
* buffer. A lookup in the current task is needed to set the
* TM_NS_MEM flag accordingly in this task's mapping.
*/
ASSERT(ctask);
ASSERT(ctask != task);
flags |= TM_SEC_VA;
cmptr = task_find_mapping(ctask, vaddr, size);
if (cmptr == NULL) {
dprintf(CRITICAL, "address 0x%08x not found in current task\n",
(u_int)vaddr);
return OTE_ERROR_BAD_PARAMETERS;
}
flags |= (cmptr->flags & TM_NS_MEM);
}
mptr = arch_task_map_memory(task, vaddr, size, flags);
if (mptr == NULL) {
return OTE_ERROR_OUT_OF_MEMORY;
}
if ((param->type == TE_PARAM_TYPE_PERSIST_MEM_RO) ||
(param->type == TE_PARAM_TYPE_PERSIST_MEM_RW)) {
result = te_add_persist_map(mptr, cmd->session);
if (result != OTE_SUCCESS) {
arch_task_unmap_memory(task, mptr);
return result;
}
} else {
cmd->mptr[cmd->maps++] = mptr;
}
/* create new address w/ old page offset */
vaddr &= PAGE_MASK;
vaddr |= mptr->vaddr;
/* rewrite param with this mapping */
param->u.Mem.base = vaddr;
return OTE_SUCCESS;
}
static te_error_t te_check_operation_params(task_t *task, struct te_command *cmd)
{
te_oper_param_t *params;
uint32_t i, param_type;
params = (te_oper_param_t *)(uintptr_t)cmd->msg.params;
/* check params */
for (i = 0; i < cmd->msg.params_size; i++) {
te_error_t result;
switch (params[i].type) {
case TE_PARAM_TYPE_INT_RW:
case TE_PARAM_TYPE_INT_RO:
continue;
case TE_PARAM_TYPE_MEM_RO:
case TE_PARAM_TYPE_MEM_RW:
case TE_PARAM_TYPE_PERSIST_MEM_RO:
case TE_PARAM_TYPE_PERSIST_MEM_RW:
break;
default:
dprintf(CRITICAL, "%s: unknown param type: 0x%x\n",
__func__, params[i].type);
return OTE_ERROR_BAD_PARAMETERS;
}
result = te_prepare_memref_param(task, cmd, &params[i]);
if (result != OTE_SUCCESS) {
dprintf(CRITICAL, "%s: memref map failed: 0x%x\n",
__func__, result);
return result;
}
}
return OTE_SUCCESS;
}
static te_error_t te_copyin_ta_params(te_request_t *req, struct te_command *cmd)
{
te_oper_param_t *in_param;
te_oper_param_t *out_params;
uint32_t i;
if (req->params_size == 0)
return OTE_SUCCESS;
out_params = (te_oper_param_t *)calloc(1, req->params_size * sizeof(*out_params));
if (out_params == NULL)
return OTE_ERROR_OUT_OF_MEMORY;
dprintf(SPEW, "%s: new out_params %p in_params %p\n",
__func__, out_params, (void *)(uintptr_t)req->params);
in_param = (te_oper_param_t *)(uintptr_t)req->params;
for (i = 0; i < req->params_size; i++) {
if (!in_param) {
dprintf(CRITICAL, "%s: NULL in_param at index %d\n",
__func__, i);
free(out_params);
return OTE_ERROR_BAD_PARAMETERS;
}
memcpy(&out_params[i], in_param, sizeof(*out_params));
switch (out_params[i].type) {
case TE_PARAM_TYPE_INT_RO:
dprintf(SPEW, "%s: %d: INT_RO: val 0x%x\n",
__func__, i, out_params[i].u.Int.val);
break;
case TE_PARAM_TYPE_INT_RW:
dprintf(SPEW, "%s: %d: INT_RW: val 0x%x\n",
__func__, i, out_params[i].u.Int.val);
break;
case TE_PARAM_TYPE_MEM_RO:
dprintf(SPEW, "%s: %d: MEM_RO: len 0x%x\n",
__func__, i, out_params[i].u.Mem.len);
break;
case TE_PARAM_TYPE_MEM_RW:
dprintf(SPEW, "%s: %d: MEM_RW: len 0x%x\n",
__func__, i, out_params[i].u.Mem.len);
break;
case TE_PARAM_TYPE_PERSIST_MEM_RO:
dprintf(SPEW, "%s: %d: PERSIST_MEM_RO: len 0x%x\n",
__func__, i, out_params[i].u.Mem.len);
break;
case TE_PARAM_TYPE_PERSIST_MEM_RW:
dprintf(SPEW, "%s: %d: PERSIST_MEM_RW: len 0x%x\n",
__func__, i, out_params[i].u.Mem.len);
break;
default:
dprintf(INFO, "%s: unhandled param type 0x%x\n",
__func__, in_param->type);
break;
}
in_param = (te_oper_param_t *)(uintptr_t)in_param->next;
/* fix up next ptr */
if (in_param == NULL) {
out_params[i].next = NULL;
} else {
out_params[i].next = (uintptr_t)&out_params[i+1];
}
}
cmd->msg.params = (uintptr_t)out_params;
cmd->msg.params_size = req->params_size;
return OTE_SUCCESS;
}
static void te_copyout_ta_params(struct te_command *cmd, te_request_t *req)
{
te_oper_param_t *in_params;
te_oper_param_t *out_param;
uint32_t i;
if (cmd->msg.params_size == 0)
return;
in_params = (te_oper_param_t *)(uintptr_t)cmd->msg.params;
out_param = (te_oper_param_t *)(uintptr_t)req->params;
for (i = 0; i < req->params_size; i++) {
switch (out_param->type) {
case TE_PARAM_TYPE_INT_RO:
dprintf(SPEW, "%s: %d: INT_RO: val 0x%x\n",
__func__, i, in_params[i].u.Int.val);
break;
case TE_PARAM_TYPE_INT_RW:
dprintf(SPEW, "%s: %d: INT_RW: val 0x%x\n",
__func__, i, in_params[i].u.Int.val);
out_param->u.Int.val = in_params[i].u.Int.val;
break;
case TE_PARAM_TYPE_MEM_RO:
dprintf(SPEW, "%s: %d: MEM_RO: len 0x%x\n",
__func__, i, in_params[i].u.Mem.len);
break;
case TE_PARAM_TYPE_MEM_RW:
dprintf(SPEW, "%s: %d: MEM_RW: len 0x%x\n",
__func__, i, in_params[i].u.Mem.len);
out_param->u.Mem.len = in_params[i].u.Mem.len;
break;
case TE_PARAM_TYPE_PERSIST_MEM_RO:
dprintf(SPEW, "%s: %d: PERSIST_MEM_RO: len 0x%x\n",
__func__, i, in_params[i].u.Mem.len);
break;
case TE_PARAM_TYPE_PERSIST_MEM_RW:
dprintf(SPEW, "%s: %d: PERSIST_MEM_RW: len 0x%x\n",
__func__, i, in_params[i].u.Mem.len);
out_param->u.Mem.len = in_params[i].u.Mem.len;
break;
default:
dprintf(INFO, "%s: %d: unhandled param type 0x%x\n",
__func__, i, out_param->type);
break;
}
out_param = (te_oper_param_t *)(uintptr_t)out_param->next;
}
free((void *)(uintptr_t)cmd->msg.params);
return;
}
static session_t *te_create_thread_session(task_t *task)
{
bool duplicate_id;
session_t *session;
uint32_t i = 0;
if (task == NULL) {
return NULL;
}
/* allocate session */
session = calloc(1, sizeof(session_t));
if (session == NULL)
return NULL;
list_initialize(&session->node);
list_initialize(&session->cmd_node);
list_initialize(&session->persist_maps);
do {
session_t *temp;
duplicate_id = false;
session->session_id = new_session_id();
list_for_every_entry(&session_list, temp, session_t, node) {
if (session->session_id == temp->session_id) {
duplicate_id = true;
break;
}
}
} while (duplicate_id);
/* check if single or multi instance */
if (task->props.multi_instance) {
char name[32];
sprintf(name, "task_%d_instance", (i + 1));
session->thread = thread_create(name, (thread_start_routine)(task->entry),
0, LOW_PRIORITY, 4096);
if (session->thread == NULL) {
dprintf(CRITICAL, "allocate task instance thread failed\n");
free(session);
return NULL;
}
thread_resume(session->thread);
} else {
/* for single, use current instance */
session->thread = list_peek_head_type(&task->thread_node,
thread_t, task_node);
}
return session;
}
te_error_t te_handle_open_session(te_request_t *req, bool task_issued)
{
struct te_command *cmd = NULL;
te_service_id_t uuid;
task_t *task;
session_t *session = NULL;
te_error_t result = OTE_SUCCESS;
memcpy(&uuid, req->dest_uuid, sizeof(uuid));
task = task_find_task_by_uuid(&uuid);
if (task == NULL) {
dprintf(SPEW, "%s: task with uuid not found!\n", __func__);
PRINT_UUID(__func__, &uuid);
return OTE_ERROR_BAD_PARAMETERS;
}
if (check_restrict_access(task, task_issued)) {
return OTE_ERROR_ACCESS_DENIED;
}
session = te_create_thread_session(task);
if (session == NULL) {
return OTE_ERROR_BAD_PARAMETERS;
}
//PRINT_UUID(__func__, &uuid);
/* setup command structure for task */
cmd = (struct te_command *) calloc(1, sizeof(struct te_command));
if (cmd == NULL) {
result = OTE_ERROR_OUT_OF_MEMORY;
goto exit;
}
cmd->msg.type = OPEN_SESSION;
list_initialize(&cmd->node);
/*
* If the request is from a service (TA), then we need to
* copy the parameters into a new buffer and forward that
* buffer onto the target service (TA).
*/
if (task_issued) {
result = te_copyin_ta_params(req, cmd);
if (result != OTE_SUCCESS) {
goto exit;
}
} else {
cmd->msg.params = req->params;
cmd->msg.params_size = req->params_size;
}
cmd->returning_cmd = 0;
/* save reference if issued by another task */
if (task_issued)
cmd->requesting_task = current_thread->arch.task;
cmd->session = session;
task = session->thread->arch.task;
result = te_check_operation_params(task, cmd);
if (result != OTE_SUCCESS) {
goto exit;
}
if (task->props.multi_instance || !task->te_instances) {
/* first instance, issue create instance first */
cmd->msg.type = CREATE_INSTANCE;
}
enter_critical_section();
list_add_tail(&session->cmd_node, &cmd->node);
list_add_tail(&session_list, &session->node);
/* kickoff thread */
thread_unblock_from_wait_queue(session->thread, false, NO_ERROR);
thread_yield();
exit_critical_section();
result = OTE_SUCCESS;
exit:
if (result != OTE_SUCCESS) {
if (cmd) {
if (cmd->msg.params)
free((void *)(uintptr_t)cmd->msg.params);
free(cmd);
}
if (session)
free(session);
}
return result;
}
te_error_t te_handle_close_session(te_request_t *req, bool task_issued)
{
struct te_command *cmd;
session_t *session;
task_t *task;
session = te_get_session_from_id(req->session_id);
if (session == NULL) {
return OTE_ERROR_BAD_PARAMETERS;
}
task = session->thread->arch.task;
if (check_restrict_access(task, task_issued)) {
dprintf(CRITICAL, "Restricted client type tried to close "
"session for a task which they are not permitted!\n");
return OTE_ERROR_ACCESS_DENIED;
}
cmd = calloc(1, sizeof(struct te_command));
if (cmd == NULL) {
return OTE_ERROR_OUT_OF_MEMORY;
}
cmd->msg.type = CLOSE_SESSION;
cmd->msg.context = (uintptr_t)session->context;
cmd->session = session;
cmd->returning_cmd = 0;
/* add command to session */
enter_critical_section();
list_add_tail(&session->cmd_node, &cmd->node);
/* kickoff thread */
thread_unblock_from_wait_queue(session->thread, false, NO_ERROR);
thread_yield();
exit_critical_section();
return OTE_SUCCESS;
}
te_error_t te_handle_launch_op(te_request_t *req, bool task_issued)
{
struct te_command *cmd;
task_t *task;
session_t *session;
te_error_t result;
dprintf(SPEW, "%s: entry\n", __func__);
session = te_get_session_from_id(req->session_id);
if (session == NULL) {
return OTE_ERROR_BAD_PARAMETERS;
}
task = session->thread->arch.task;
if (check_restrict_access(task, task_issued)) {
dprintf(CRITICAL, "Restricted client type tried to perform "
"an operation with a session/task for which they are "
"not permitted!\n");
return OTE_ERROR_ACCESS_DENIED;
}
cmd = calloc(1, sizeof(struct te_command));
if (cmd == NULL) {
return OTE_ERROR_OUT_OF_MEMORY;
}
cmd->msg.type = LAUNCH_OPERATION;
cmd->msg.context = (uintptr_t)session->context;
cmd->session = session;
/*
* If the request is from a service (TA), then we need to
* copy the parameters into a new buffer and forward that
* buffer onto the target service (TA).
*/
if (task_issued) {
result = te_copyin_ta_params(req, cmd);
if (result != OTE_SUCCESS) {
goto exit;
}
} else {
cmd->msg.params = req->params;
cmd->msg.params_size = req->params_size;
}
cmd->msg.command_id = req->command_id;
cmd->returning_cmd = 0;
/* save reference if issued by another task */
if (task_issued)
cmd->requesting_task = current_thread->arch.task;
result = te_check_operation_params(task, cmd);
if (result != OTE_SUCCESS) {
goto exit;
}
/* add command to session */
enter_critical_section();
list_add_tail(&session->cmd_node, &cmd->node);
/* kickoff thread */
thread_unblock_from_wait_queue(session->thread, false, NO_ERROR);
thread_yield();
exit_critical_section();
result = OTE_SUCCESS;
exit:
if (result != OTE_SUCCESS) {
if (cmd->msg.params)
free((void *)(uintptr_t)cmd->msg.params);
if (cmd)
free(cmd);
}
return result;
}
te_error_t te_handle_ta_event(enum ta_event_id event)
{
struct te_command cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.msg.type = TA_EVENT;
cmd.msg.command_id = event;
/* Signal all tasks that have requested events. */
return task_signal_ta_event(event, &cmd);
}
void te_intf_init(void)
{
/* initialize the session id list */
list_initialize(&session_list);
/* initialize the command list */
list_initialize(&completion_list);
event_init(&task_event, false, EVENT_FLAG_AUTOUNSIGNAL);
}
void te_get_completed_cmd(te_request_t *req, bool task_issued)
{
struct te_command *cmd;
session_t *session, *next;
task_t *task;
cmd = list_remove_head_type(&completion_list, struct te_command, node);
if (cmd == NULL) {
/* command didn't complete (let NS world know) */
req->result = OTE_ERROR_NO_ANSWER;
req->result_origin = OTE_RESULT_ORIGIN_KERNEL;
return;
}
/* get session */
list_for_every_entry_safe(&session_list, session, next, session_t, node) {
if (cmd->session == session) {
break;
}
}
task = session->thread->arch.task;
dprintf(SPEW, "%s: cmd %d\n", __func__, cmd->msg.type);
switch (cmd->msg.type) {
case CREATE_INSTANCE:
case OPEN_SESSION:
/* unhook and free session on failure */
if (cmd->msg.result != OTE_SUCCESS) {
ASSERT(session && next);
/* release persistent mappings if any */
te_release_persist_maps(session, task, false);
list_delete(&session->node);
free(session);
req->session_id = 0x0;
} else {
/* return session id */
nactive_sessions++;
task->te_instances++;
req->session_id = session->session_id;
if (task_issued)
te_copyout_ta_params(cmd, req);
/* mark persistent mappings as active */
te_update_persist_maps(session, task);
}
break;
case DESTROY_INSTANCE:
case CLOSE_SESSION:
ASSERT(session && next);
/* release any persistent mappings */
te_release_persist_maps(session, task, false);
/* unhook and free session */
list_delete(&session->node);
free(session);
nactive_sessions--;
task->te_instances--;
break;
case LAUNCH_OPERATION:
if (cmd->msg.result != OTE_SUCCESS) {
/* release inactive persistent mappings if any */
te_release_persist_maps(session, task, true);
} else {
/* mark persistent mappings as active */
te_update_persist_maps(session, task);
if (task_issued)
te_copyout_ta_params(cmd, req);
}
break;
}
req->result = cmd->msg.result;
req->result_origin = OTE_RESULT_ORIGIN_TRUSTED_APP;
dprintf(SPEW, "%s: 0x%x 0x%x\n", __func__, req->result, req->result_origin);
free(cmd);
}
static struct te_command *te_thread_current_command()
{
struct te_command *cmd = NULL;
session_t *session;
/* return current command, if one is bound */
if (current_thread->arg)
return (struct te_command *) current_thread->arg;
if (list_is_empty(&session_list))
return NULL;
/* traverse session list for a command for this thread */
enter_critical_section();
list_for_every_entry(&session_list, session, session_t, node) {
if (session->thread == current_thread) {
cmd = list_remove_head_type(&session->cmd_node, struct te_command, node);
if (cmd)
break;
}
}
exit_critical_section();
return cmd;
}
static int te_handle_command_message(struct te_command *cmd, void *msg, bool read)
{
te_entry_point_message_t *ep;
if (read && !cmd) {
/* no commands for this thread, wait */
event_wait(&task_event);
cmd = te_thread_current_command();
}
if (cmd->returning_cmd) {
ep = (te_entry_point_message_t *) msg;
cmd->returning_cmd = 0;
/* copy params take two pass syscalls */
ep = (te_entry_point_message_t *) msg;
if (ep->params_size == cmd->msg.params_size && ep->params != NULL) {
memcpy((void *)(uintptr_t)ep->params,
(void *)(uintptr_t)cmd->msg.params,
sizeof(te_oper_param_t) * ep->params_size);
} else {
return -EINVAL;
}
return sizeof(*ep);
}
if (read) {
ASSERT(cmd);
/* setup message */
memset(msg, 0, sizeof(te_entry_point_message_t));
ep = (te_entry_point_message_t *) msg;
ep->type = cmd->msg.type;
switch (ep->type) {
case CREATE_INSTANCE:
case DESTROY_INSTANCE:
break;
case OPEN_SESSION:
ep->context = cmd->msg.context;
ep->params_size = cmd->msg.params_size;
if (ep->params_size)
cmd->returning_cmd = 1;
break;
case CLOSE_SESSION:
ep->context = cmd->msg.context;
break;
case LAUNCH_OPERATION:
ep->context = cmd->msg.context;
ep->command_id = cmd->msg.command_id;
ep->params_size = cmd->msg.params_size;
if (ep->params_size)
cmd->returning_cmd = 1;
break;
case TA_EVENT:
ep->command_id = cmd->msg.command_id;
/* events do not write back a result.
* clear current arg. */
current_thread->arg = NULL;
return sizeof(*ep);
default:
/* unknown command */
return -EINVAL;
}
current_thread->arg = cmd;
return sizeof(*ep);
}
/* TBD handle writes for TA -> TA commands */
return -EINVAL;
}
static int te_handle_result_message(struct te_command *cmd, void *msg, bool read)
{
te_entry_point_message_t *ep;
task_t *task;
if (read || !cmd) {
/* expect only writes and command pending */
return -EINVAL;
}
ep = (te_entry_point_message_t *) msg;
if ((ep->result == OTE_SUCCESS) && (cmd->msg.type == CREATE_INSTANCE)) {
/*
* Special handling occurs on a successful CREATE_INSTANCE.
*
* This is always followed by an OPEN_SESSION command and only
* the status of the OPEN_SESSION is returned to the caller.
* Just change type (no need to unhook it), so it's found
* when the TA queries for the next command.
*/
cmd->msg.type = OPEN_SESSION;
return sizeof(te_entry_point_message_t);
} else if (cmd->msg.type == CLOSE_SESSION) {
/*
* Same type of handling occurs on a DESTROY_INSTANCE, where
* CLOSE_SESSION is what follows (no check of status as
* DESTROY_INSTANCE doesn't return one).
*/
task = cmd->session->thread->arch.task;
if (task->props.multi_instance || (task->te_instances == 1)) {
/* last instance, issue delete instance first */
cmd->msg.type = DESTROY_INSTANCE;
return sizeof(te_entry_point_message_t);
}
}
/* copy back data to command */
switch (cmd->msg.type) {
case CREATE_INSTANCE:
case DESTROY_INSTANCE:
case CLOSE_SESSION:
/* these don't return a result, just make it look clean */
cmd->msg.result = OTE_SUCCESS;
break;
case OPEN_SESSION:
cmd->msg.result = ep->result;
if (ep->params_size && ep->params) {
memcpy((void *)(uintptr_t)cmd->msg.params,
(void *)(uintptr_t)ep->params,
sizeof(te_oper_param_t) * cmd->msg.params_size);
}
cmd->session->context = (void *)(uintptr_t)ep->context;
/* release non-persistent mappings */
task = cmd->session->thread->arch.task;
while (cmd->maps--) {
arch_task_unmap_memory(task, cmd->mptr[cmd->maps]);
}
break;
case LAUNCH_OPERATION:
cmd->msg.result = ep->result;
cmd->msg.command_id = ep->command_id;
if (ep->params_size && ep->params) {
memcpy((void *)(uintptr_t)cmd->msg.params,
(void *)(uintptr_t)ep->params,
sizeof(te_oper_param_t) * ep->params_size);
}
/* release non-persistent mappings */
task = cmd->session->thread->arch.task;
while (cmd->maps--) {
arch_task_unmap_memory(task, cmd->mptr[cmd->maps]);
}
break;
}
/* unhook command and add to completion */
current_thread->arg = NULL;
enter_critical_section();
list_add_tail(&completion_list, &cmd->node);
exit_critical_section();
return sizeof(te_entry_point_message_t);
}
/*
* This routine is called anytime the trusted app (TA) requests the next
* command (reads the interface fd) or completes a command by writing the
* result fd. A TA is also able to request a command of another TA by
* writing to the interface fd.
*
* On completion, status is copied back to the current command and
* the command is added to the completion list. When the idle thread is
* scheduled, it picks up the status and returns it.
*/
int te_handle_ta_message(uint32_t fd, void *msg, uint32_t size, bool read)
{
struct te_command *cmd;
int retval;
dprintf(SPEW, "%s: thread: 0x%p fd: %s action: %s\n",
__func__,
current_thread,
te_get_fd_str(fd),
((read) ? "READ" : "WRITE"));
/* sanity checks */
if (msg == NULL)
return -EFAULT;
if ((fd == TE_RESULT) || (fd == TE_INTERFACE)) {
if (size < sizeof(te_entry_point_message_t))
return -EINVAL;
}
if ((fd != TE_RESULT) &&
(fd != TE_INTERFACE) &&
(fd != TE_INFO) &&
(fd != TE_ERR) &&
(fd != TE_SECURE) &&
(fd != TE_CRITICAL))
return -EBADF;
cmd = te_thread_current_command();
switch (fd) {
case TE_INTERFACE:
/* get next command or issue to another TA */
retval = te_handle_command_message(cmd, msg, read);
break;
case TE_RESULT:
/* pickup result of previous command */
retval = te_handle_result_message(cmd, msg, read);
break;
case TE_CRITICAL:
case TE_ERR:
case TE_SECURE:
case TE_INFO:
dprintf(CRITICAL, "%s", (char *)msg);
retval = size;
break;
default:
return -EBADF;
}
return retval;
}
int te_get_current_ta_property(te_get_property_args_t *args)
{
task_t *taskp = current_thread->arch.task;
/* determine which client property they're after */
ASSERT(args->data_type == OTE_PROP_DATA_TYPE_UUID);
ASSERT(args->value_size == sizeof(te_service_id_t));
memcpy(&args->value.uuid,
&taskp->props.uuid,
sizeof(te_service_id_t));
args->result = OTE_SUCCESS;
return 0;
}
int te_get_current_client_property(te_get_property_args_t *args)
{
task_t *taskp;
struct te_command *cmd;
/* determine which client property they're after */
ASSERT(args->data_type == OTE_PROP_DATA_TYPE_IDENTITY);
ASSERT(args->value_size == sizeof(te_identity_t));
/* we expect to have a current command bound */
if (!current_thread->arg) {
args->result = OTE_ERROR_BAD_STATE;
return -EINVAL;
}
cmd = (struct te_command *) current_thread->arg;
/* set login type and uuid */
if (cmd->requesting_task) {
taskp = cmd->requesting_task;
memcpy(&args->value.identity.uuid, &taskp->props.uuid,
sizeof(te_service_id_t));
args->value.identity.login = TE_LOGIN_TA;
} else {
/* no uuid available */
memset(&args->value.identity.uuid, 0, sizeof(te_service_id_t));
args->value.identity.login = TE_LOGIN_PUBLIC;
}
args->result = OTE_SUCCESS;
return 0;
}
int te_get_implementation_property(te_get_property_args_t *args)
{
/* XXX to be done */
args->result = OTE_ERROR_NOT_IMPLEMENTED;
return -EINVAL;
}
void te_session_cancel_thread(thread_t *t)
{
session_t *session = NULL;
session_t *s_tmp = NULL;
enter_critical_section();
/* Delete any session referring to the thread being canceled. */
list_for_every_entry_safe(&session_list, session, s_tmp, session_t, node) {
if (session->thread == t) {
task_t *task = NULL;
struct te_command *cmd = NULL;
struct te_command *cmd_tmp = NULL;
list_delete(&session->node);
task = session->thread->arch.task;
list_for_every_entry_safe(&session->cmd_node, cmd, cmd_tmp,
te_command_t, node) {
while (cmd->maps--) {
arch_task_unmap_memory(task,
cmd->mptr[cmd->maps]);
}
list_delete(&cmd->node);
free(cmd);
cmd = NULL;
}
te_release_persist_maps(session, task, false);
free(session);
session = NULL;
}
}
exit_critical_section();
}