| /* |
| * 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, ¶ms[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(); |
| } |