/* * tee_invoke_command - invoke TEE to invoke a GP TEE command */ static int tz_invoke(struct tee_session *sess, struct tee_cmd *cmd) { struct tee *tee; struct tee_tz *ptee; int ret = 0; struct teesmc32_arg *arg32; uintptr_t parg32; struct teesmc32_param *params32; BUG_ON(!sess->ctx->tee); BUG_ON(!sess->ctx->tee->priv); tee = sess->ctx->tee; ptee = tee->priv; dev_dbg(DEV, "> sessid %x cmd %x type %x\n", sess->sessid, cmd->cmd, cmd->param.type); if (!CAPABLE(tee)) { dev_dbg(tee->dev, "< not capable\n"); return -EBUSY; } arg32 = (typeof(arg32))alloc_tee_arg(ptee, &parg32, TEESMC32_GET_ARG_SIZE(TEEC_CONFIG_PAYLOAD_REF_COUNT)); if (!arg32) { free_tee_arg(ptee, parg32); return TEEC_ERROR_OUT_OF_MEMORY; } memset(arg32, 0, sizeof(*arg32)); arg32->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT; params32 = TEESMC32_GET_PARAMS(arg32); arg32->cmd = TEESMC_CMD_INVOKE_COMMAND; arg32->session = sess->sessid; arg32->ta_func = cmd->cmd; set_params(ptee, params32, cmd->param.type, &cmd->param); call_tee(ptee, parg32, arg32); get_params(&cmd->param, params32); if (arg32->ret != TEEC_ERROR_COMMUNICATION) { cmd->err = arg32->ret; cmd->origin = arg32->ret_origin; } else ret = -EBUSY; free_tee_arg(ptee, parg32); dev_dbg(DEV, "< %x:%d\n", arg32->ret, ret); return ret; }
/* * tee_invoke_command - invoke TEE to invoke a GP TEE command */ static TEEC_Result tee_invoke_command(struct tee_session *ts, enum t_cmd_service_id sec_cmd, uint32_t ta_cmd, uint32_t param_type, TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT], uint32_t *origin) { TEEC_Result ret_tee; struct teesmc32_arg *arg32; uintptr_t parg32; struct teesmc32_param *params32; dev_dbg(DEV, "> [%p] [%d]\n", (void *)ts->id, ta_cmd); arg32 = (typeof(arg32))alloc_tee_arg(&parg32, TEESMC32_GET_ARG_SIZE(TEEC_CONFIG_PAYLOAD_REF_COUNT)); if (!arg32) { free_tee_arg(parg32); return TEEC_ERROR_OUT_OF_MEMORY; } memset(arg32, 0, sizeof(*arg32)); arg32->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT; params32 = TEESMC32_GET_PARAMS(arg32); arg32->cmd = TEESMC_CMD_INVOKE_COMMAND; arg32->session = ts->id; arg32->ta_func = ta_cmd; set_params(params32, param_type, params); mutex_lock(&e_mutex_teez); call_tee(parg32, arg32); mutex_unlock(&e_mutex_teez); ret_tee = arg32->ret; get_params(params, params32); if (origin) *origin = arg32->ret_origin; free_tee_arg(parg32); dev_dbg(DEV, "< [%p]\n", (void *)ret_tee); return ret_tee; }
static void handle_rpc_func_cmd_mutex_wait(struct tee_tz *ptee, struct teesmc32_arg *arg32) { struct teesmc32_param *params; if (arg32->num_params != 2) goto bad; params = TEESMC32_GET_PARAMS(arg32); if ((params[0].attr & TEESMC_ATTR_TYPE_MASK) != TEESMC_ATTR_TYPE_VALUE_INPUT) goto bad; if ((params[1].attr & TEESMC_ATTR_TYPE_MASK) != TEESMC_ATTR_TYPE_VALUE_INPUT) goto bad; switch (params[0].u.value.a) { case TEE_MUTEX_WAIT_SLEEP: tee_mutex_wait_sleep(DEV, &ptee->mutex_wait, params[1].u.value.a, params[1].u.value.b); break; case TEE_MUTEX_WAIT_WAKEUP: tee_mutex_wait_wakeup(DEV, &ptee->mutex_wait, params[1].u.value.a, params[1].u.value.b); break; case TEE_MUTEX_WAIT_DELETE: tee_mutex_wait_delete(DEV, &ptee->mutex_wait, params[1].u.value.a); break; default: goto bad; } arg32->ret = TEEC_SUCCESS; return; bad: arg32->ret = TEEC_ERROR_BAD_PARAMETERS; }
static void handle_rpc_func_cmd_wait(struct teesmc32_arg *arg32) { struct teesmc32_param *params; u32 msec_to_wait; if (arg32->num_params != 1) goto bad; params = TEESMC32_GET_PARAMS(arg32); msec_to_wait = params[0].u.value.a; /* set task's state to interruptible sleep */ set_current_state(TASK_INTERRUPTIBLE); /* take a nap */ schedule_timeout(msecs_to_jiffies(msec_to_wait)); arg32->ret = TEEC_SUCCESS; return; bad: arg32->ret = TEEC_ERROR_BAD_PARAMETERS; }
/* * tee_open_session - invoke TEE to open a GP TEE session */ static int tz_open(struct tee_session *sess, struct tee_cmd *cmd) { struct tee *tee; struct tee_tz *ptee; int ret = 0; struct teesmc32_arg *arg32; struct teesmc32_param *params32; struct teesmc_meta_open_session *meta; uintptr_t parg32; uintptr_t pmeta; size_t num_meta = 1; uint8_t *ta; TEEC_UUID *uuid; BUG_ON(!sess->ctx->tee); BUG_ON(!sess->ctx->tee->priv); tee = sess->ctx->tee; ptee = tee->priv; if (cmd->uuid) uuid = cmd->uuid->kaddr; else uuid = NULL; dev_dbg(tee->dev, "> ta kaddr %p, uuid=%08x-%04x-%04x\n", (cmd->ta) ? cmd->ta->kaddr : NULL, ((uuid) ? uuid->timeLow : 0xDEAD), ((uuid) ? uuid->timeMid : 0xDEAD), ((uuid) ? uuid->timeHiAndVersion : 0xDEAD)); if (!CAPABLE(ptee->tee)) { dev_dbg(tee->dev, "< not capable\n"); return -EBUSY; } /* case ta binary is inside the open request */ ta = NULL; if (cmd->ta) ta = cmd->ta->kaddr; if (ta) num_meta++; arg32 = alloc_tee_arg(ptee, &parg32, TEESMC32_GET_ARG_SIZE( TEEC_CONFIG_PAYLOAD_REF_COUNT + num_meta)); meta = alloc_tee_arg(ptee, &pmeta, sizeof(*meta)); if ((arg32 == NULL) || (meta == NULL)) { free_tee_arg(ptee, parg32); free_tee_arg(ptee, pmeta); return TEEC_ERROR_OUT_OF_MEMORY; } memset(arg32, 0, sizeof(*arg32)); memset(meta, 0, sizeof(*meta)); arg32->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT + num_meta; params32 = TEESMC32_GET_PARAMS(arg32); arg32->cmd = TEESMC_CMD_OPEN_SESSION; params32[0].u.memref.buf_ptr = pmeta; params32[0].u.memref.size = sizeof(*meta); params32[0].attr = TEESMC_ATTR_TYPE_MEMREF_INPUT | TEESMC_ATTR_META | get_cache_attrs(ptee); if (ta) { params32[1].u.memref.buf_ptr = tee_shm_pool_v2p(DEV, ptee->shm_pool, cmd->ta->kaddr); params32[1].u.memref.size = cmd->ta->size_req; params32[1].attr = TEESMC_ATTR_TYPE_MEMREF_INPUT | TEESMC_ATTR_META | get_cache_attrs(ptee); } if (uuid != NULL) memcpy(meta->uuid, uuid, TEESMC_UUID_LEN); meta->clnt_login = 0; /* FIXME: is this reliable ? used ? */ params32 += num_meta; set_params(ptee, params32, cmd->param.type, &cmd->param); call_tee(ptee, parg32, arg32); get_params(&cmd->param, params32); if (arg32->ret != TEEC_ERROR_COMMUNICATION) { sess->sessid = arg32->session; cmd->err = arg32->ret; cmd->origin = arg32->ret_origin; } else ret = -EBUSY; free_tee_arg(ptee, parg32); free_tee_arg(ptee, pmeta); dev_dbg(DEV, "< %x:%d\n", arg32->ret, ret); return ret; }
static void handle_rpc_func_cmd_to_supplicant(struct tee_tz *ptee, struct teesmc32_arg *arg32) { struct teesmc32_param *params; struct tee_rpc_invoke inv; size_t n; uint32_t ret; if (arg32->num_params > TEE_RPC_BUFFER_NUMBER) { arg32->ret = TEEC_ERROR_GENERIC; return; } params = TEESMC32_GET_PARAMS(arg32); memset(&inv, 0, sizeof(inv)); inv.cmd = arg32->cmd; /* * Set a suitable error code in case tee-supplicant * ignores the request. */ inv.res = TEEC_ERROR_NOT_IMPLEMENTED; inv.nbr_bf = arg32->num_params; for (n = 0; n < arg32->num_params; n++) { inv.cmds[n].buffer = (void *)(uintptr_t)params[n].u.memref.buf_ptr; inv.cmds[n].size = params[n].u.memref.size; switch (params[n].attr & TEESMC_ATTR_TYPE_MASK) { case TEESMC_ATTR_TYPE_VALUE_INPUT: case TEESMC_ATTR_TYPE_VALUE_OUTPUT: case TEESMC_ATTR_TYPE_VALUE_INOUT: inv.cmds[n].type = TEE_RPC_VALUE; break; case TEESMC_ATTR_TYPE_MEMREF_INPUT: case TEESMC_ATTR_TYPE_MEMREF_OUTPUT: case TEESMC_ATTR_TYPE_MEMREF_INOUT: inv.cmds[n].type = TEE_RPC_BUFFER; break; default: arg32->ret = TEEC_ERROR_GENERIC; return; } } ret = tee_supp_cmd(ptee->tee, TEE_RPC_ICMD_INVOKE, &inv, sizeof(inv)); if (ret == TEEC_RPC_OK) arg32->ret = inv.res; for (n = 0; n < arg32->num_params; n++) { switch (params[n].attr & TEESMC_ATTR_TYPE_MASK) { case TEESMC_ATTR_TYPE_VALUE_INPUT: case TEESMC_ATTR_TYPE_VALUE_OUTPUT: case TEESMC_ATTR_TYPE_VALUE_INOUT: case TEESMC_ATTR_TYPE_MEMREF_OUTPUT: case TEESMC_ATTR_TYPE_MEMREF_INOUT: /* * Allow supplicant to assign a new pointer * to an out-buffer. Needed when the * supplicant allocates a new buffer, for * instance when loading a TA. */ params[n].u.memref.buf_ptr = (uint32_t)(uintptr_t)inv.cmds[n].buffer; params[n].u.memref.size = inv.cmds[n].size; break; default: break; } } }
static u32 handle_rpc(struct smc_param *param) { switch (TEESMC_RETURN_GET_RPC_FUNC(param->a0)) { case TEESMC_RPC_FUNC_ALLOC_ARG: param->a1 = tee_shm_pool_alloc(DEV, TZop.Allocator, param->a1, 4); case TEESMC_RPC_FUNC_ALLOC_PAYLOAD: /* Can't support payload shared memory with this interface */ param->a2 = 0; break; case TEESMC_RPC_FUNC_FREE_ARG: tee_shm_pool_free(DEV, TZop.Allocator, param->a1, 0); break; case TEESMC_RPC_FUNC_FREE_PAYLOAD: /* Can't support payload shared memory with this interface */ break; case TEESMC_ST_RPC_FUNC_ALLOC_PAYLOAD: { struct tee_shm *shm; shm = tee_shm_allocate(&TZop, 0, param->a1, 0); if (!shm) { param->a1 = 0; break; } param->a1 = shm->paddr; param->a2 = (uint32_t)shm; break; } case TEESMC_ST_RPC_FUNC_FREE_PAYLOAD: if (param->a1) tee_shm_unallocate((struct tee_shm *)param->a1); break; case TEESMC_RPC_FUNC_IRQ: break; case TEESMC_RPC_FUNC_CMD: { struct teesmc32_arg *arg32; struct teesmc32_param *params; struct tee_rpc_invoke inv; size_t n; uint32_t ret; arg32 = tee_shm_pool_p2v(DEV, TZop.Allocator, param->a1); if (arg32->num_params > TEE_RPC_BUFFER_NUMBER) { arg32->ret = TEEC_ERROR_GENERIC; goto out; } params = TEESMC32_GET_PARAMS(arg32); memset(&inv, 0, sizeof(inv)); inv.cmd = arg32->cmd; /* * Set a suitable error code in case tee-supplicant * ignores the request. */ inv.res = TEEC_ERROR_NOT_IMPLEMENTED; inv.nbr_bf = arg32->num_params; for (n = 0; n < arg32->num_params; n++) { inv.cmds[n].buffer = (void *)params[n].u.memref.buf_ptr; inv.cmds[n].size = params[n].u.memref.size; switch (params[n].attr & TEESMC_ATTR_TYPE_MASK) { case TEESMC_ATTR_TYPE_VALUE_INPUT: case TEESMC_ATTR_TYPE_VALUE_OUTPUT: case TEESMC_ATTR_TYPE_VALUE_INOUT: inv.cmds[n].type = TEE_RPC_VALUE; break; case TEESMC_ATTR_TYPE_MEMREF_INPUT: case TEESMC_ATTR_TYPE_MEMREF_OUTPUT: case TEESMC_ATTR_TYPE_MEMREF_INOUT: inv.cmds[n].type = TEE_RPC_BUFFER; break; default: arg32->ret = TEEC_ERROR_GENERIC; goto out; } } ret = tee_supp_cmd(&TZop, TEE_RPC_ICMD_INVOKE, &inv, sizeof(inv)); if (ret == TEEC_RPC_OK) arg32->ret = inv.res; for (n = 0; n < arg32->num_params; n++) { switch (params[n].attr & TEESMC_ATTR_TYPE_MASK) { case TEESMC_ATTR_TYPE_VALUE_INPUT: case TEESMC_ATTR_TYPE_VALUE_OUTPUT: case TEESMC_ATTR_TYPE_VALUE_INOUT: case TEESMC_ATTR_TYPE_MEMREF_OUTPUT: case TEESMC_ATTR_TYPE_MEMREF_INOUT: /* * Allow supplicant to assign a new pointer * to an out-buffer. Needed when the * supplicant allocates a new buffer, for * instance when loading a TA. */ params[n].u.memref.buf_ptr = (uint32_t)inv.cmds[n].buffer; params[n].u.memref.size = inv.cmds[n].size; break; default: break; } } break; } default: dev_warn(DEV, "Unknown RPC func 0x%x\n", TEESMC_RETURN_GET_RPC_FUNC(param->a0)); break; } out: if (irqs_disabled()) return TEESMC32_FASTCALL_RETURN_FROM_RPC; else return TEESMC32_CALL_RETURN_FROM_RPC; }
/* * tee_open_session - invoke TEE to open a GP TEE session */ static TEEC_Result tee_open_session(struct tee_session *ts, enum t_cmd_service_id sec_cmd, uint32_t ta_cmd, uint32_t param_type, TEEC_Value params[TEEC_CONFIG_PAYLOAD_REF_COUNT], uint32_t *origin) { TEEC_Result ret_tee; struct teesmc32_arg *arg32; uintptr_t parg32; struct teesmc32_param *params32; struct teesmc_meta_open_session *meta; uintptr_t pmeta; size_t num_meta = 1; dev_dbg(DEV, "> uuid=%08x-%04x-%04x\n", ((ts->uuid) ? ts->uuid->timeLow : 0xDEAD), ((ts->uuid) ? ts->uuid->timeMid : 0xDEAD), ((ts->uuid) ? ts->uuid-> timeHiAndVersion : 0xDEAD)); if (tee_tz_ready == false) return TEEC_ERROR_BUSY; if (ts->ta) num_meta++; arg32 = (typeof(arg32))alloc_tee_arg(&parg32, TEESMC32_GET_ARG_SIZE( TEEC_CONFIG_PAYLOAD_REF_COUNT + num_meta)); meta = (typeof(meta))alloc_tee_arg(&pmeta, sizeof(*meta)); if ((arg32 == NULL) || (meta == NULL)) { free_tee_arg(parg32); free_tee_arg(pmeta); return TEEC_ERROR_OUT_OF_MEMORY; } memset(arg32, 0, sizeof(*arg32)); memset(meta, 0, sizeof(*meta)); arg32->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT + num_meta; params32 = TEESMC32_GET_PARAMS(arg32); arg32->cmd = TEESMC_CMD_OPEN_SESSION; params32[0].u.memref.buf_ptr = pmeta; params32[0].u.memref.size = sizeof(*meta); params32[0].attr = TEESMC_ATTR_TYPE_MEMREF_INPUT | TEESMC_ATTR_META | get_cache_attrs(); if (ts->ta != NULL) { params32[1].u.memref.buf_ptr = tee_shm_pool_v2p(DEV, TZop.Allocator, ts->ta); params32[1].u.memref.size = ts->tasize; params32[1].attr = TEESMC_ATTR_TYPE_MEMREF_INPUT | TEESMC_ATTR_META | get_cache_attrs(); } if (ts->uuid != NULL) memcpy(meta->uuid, ts->uuid, TEESMC_UUID_LEN); meta->clnt_login = ts->login; set_params(params32 + num_meta, param_type, params); mutex_lock(&e_mutex_teez); call_tee(parg32, arg32); mutex_unlock(&e_mutex_teez); ts->id = arg32->session; ret_tee = arg32->ret; if (origin) *origin = arg32->ret_origin; get_params(params, params32 + num_meta); free_tee_arg(parg32); free_tee_arg(pmeta); dev_dbg(DEV, "< [%d]\n", ret_tee); return ret_tee; }