// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #define PR_FMT_HEADER_MUST_BE_INCLUDED_BEFORE_ALL_HDRS #include "private/tmem_pr_fmt.h" PR_FMT_HEADER_MUST_BE_INCLUDED_BEFORE_ALL_HDRS #include #include #include #include #include #include #include #include #include #include #include #include #include #include "private/mld_helper.h" #include "private/tmem_device.h" #include "private/tmem_error.h" #include "private/tmem_utils.h" #include "private/tmem_dev_desc.h" #include "tee_impl/tee_ops.h" #include "tee_impl/tee_gp_def.h" #if IS_ENABLED(CONFIG_MTK_GZ_KREE) #include "mtee_impl/mtee_invoke.h" #endif #include "tee_client_api.h" /* clang-format off */ #define SECMEM_TL_GP_UUID \ { 0x08030000, 0x0000, 0x0000, \ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } /* clang-format on */ #define SECMEM_TL_GP_UUID_STRING "08030000000000000000000000000000" struct TEE_GP_SESSION_DATA { struct TEEC_Context context; struct TEEC_Session session; struct TEEC_SharedMemory wsm; void *wsm_buffer; }; static struct TEE_GP_SESSION_DATA *g_sess_data; static bool is_sess_ready; static unsigned int sess_ref_cnt; #define TEE_SESSION_LOCK() mutex_lock(&g_sess_lock) #define TEE_SESSION_UNLOCK() mutex_unlock(&g_sess_lock) static DEFINE_MUTEX(g_sess_lock); static struct TEE_GP_SESSION_DATA *tee_gp_create_session_data(void) { struct TEE_GP_SESSION_DATA *data; data = mld_kmalloc(sizeof(struct TEE_GP_SESSION_DATA), GFP_KERNEL); if (INVALID(data)) return NULL; memset(data, 0x0, sizeof(struct TEE_GP_SESSION_DATA)); return data; } static void tee_gp_destroy_session_data(void) { if (VALID(g_sess_data)) { mld_kfree(g_sess_data); g_sess_data = NULL; } } static int tee_session_open_single_session_unlocked(void) { int ret = TMEM_OK; struct TEEC_UUID destination = SECMEM_TL_GP_UUID; if (is_sess_ready) { pr_debug("UT_SUITE:Session is already created!\n"); return TMEM_OK; } g_sess_data = tee_gp_create_session_data(); if (INVALID(g_sess_data)) { pr_err("Create session data failed: out of memory!\n"); return TMEM_TEE_CREATE_SESSION_DATA_FAILED; } g_sess_data->wsm_buffer = mld_kmalloc(sizeof(struct secmem_ta_msg_t), GFP_KERNEL); if (INVALID(g_sess_data->wsm_buffer)) { pr_err("Create wsm buffer failed: out of memory!\n"); goto err_create_wsm_buffer; } ret = TEEC_InitializeContext(SECMEM_TL_GP_UUID_STRING, &g_sess_data->context); if (ret != TEEC_SUCCESS) { pr_err("TEEC_InitializeContext failed: %x\n", ret); goto err_initialize_context; } g_sess_data->wsm.buffer = g_sess_data->wsm_buffer; g_sess_data->wsm.size = sizeof(struct secmem_ta_msg_t); g_sess_data->wsm.flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT; memset(g_sess_data->wsm.buffer, 0, g_sess_data->wsm.size); ret = TEEC_RegisterSharedMemory(&g_sess_data->context, &g_sess_data->wsm); if (ret != TEEC_SUCCESS) { pr_err("TEEC_RegisterSharedMemory failed: %x\n", ret); goto err_register_shared_memory; } ret = TEEC_OpenSession(&g_sess_data->context, &g_sess_data->session, &destination, TEEC_LOGIN_PUBLIC, NULL, NULL, NULL); if (ret != TEEC_SUCCESS) { pr_err("TEEC_OpenSession failed: %x\n", ret); goto err_open_session; } is_sess_ready = true; return TMEM_OK; err_open_session: pr_err("TEEC_ReleaseSharedMemory\n"); TEEC_ReleaseSharedMemory(&g_sess_data->wsm); err_register_shared_memory: pr_err("TEEC_FinalizeContext\n"); TEEC_FinalizeContext(&g_sess_data->context); err_initialize_context: mld_kfree(g_sess_data->wsm_buffer); err_create_wsm_buffer: tee_gp_destroy_session_data(); return TMEM_TEE_CREATE_SESSION_FAILED; } static int tee_session_close_single_session_unlocked(void) { if (!is_sess_ready) { pr_debug("Session is already closed!\n"); return TMEM_OK; } TEEC_CloseSession(&g_sess_data->session); TEEC_ReleaseSharedMemory(&g_sess_data->wsm); TEEC_FinalizeContext(&g_sess_data->context); mld_kfree(g_sess_data->wsm_buffer); tee_gp_destroy_session_data(); is_sess_ready = false; return TMEM_OK; } int tee_session_open(void **tee_data, void *dev_desc) { UNUSED(tee_data); UNUSED(dev_desc); TEE_SESSION_LOCK(); if (tee_session_open_single_session_unlocked() == TMEM_OK) sess_ref_cnt++; TEE_SESSION_UNLOCK(); return TMEM_OK; } int tee_session_close(void *tee_data, void *dev_desc) { UNUSED(tee_data); UNUSED(dev_desc); TEE_SESSION_LOCK(); if (sess_ref_cnt == 0) { pr_err("Session is already closed!\n"); TEE_SESSION_UNLOCK(); return TMEM_OK; } sess_ref_cnt--; if (sess_ref_cnt == 0) { pr_debug("Try closing session!\n"); tee_session_close_single_session_unlocked(); } TEE_SESSION_UNLOCK(); return TMEM_OK; } static int secmem_execute(u32 cmd, struct secmem_param *param) { int ret = TEEC_SUCCESS; struct TEEC_Operation op; struct secmem_ta_msg_t *msg; TEE_SESSION_LOCK(); if (!is_sess_ready) { pr_err("Session is not ready!\n"); TEE_SESSION_UNLOCK(); return TMEM_TEE_SESSION_IS_NOT_READY; } memset(g_sess_data->wsm.buffer, 0, g_sess_data->wsm.size); msg = g_sess_data->wsm.buffer; msg->sec_handle = param->sec_handle; msg->alignment = param->alignment; msg->size = param->size; msg->refcount = param->refcount; memset(&op, 0, sizeof(struct TEEC_Operation)); op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_PARTIAL_INOUT, TEEC_NONE, TEEC_NONE, TEEC_NONE); op.params[0].memref.parent = &g_sess_data->wsm; op.params[0].memref.offset = 0; op.params[0].memref.size = g_sess_data->wsm.size; ret = TEEC_InvokeCommand(&g_sess_data->session, cmd, &op, NULL); if (ret != TEEC_SUCCESS) { pr_err("TEEC_InvokeCommand failed! cmd:%d, ret:0x%x\n", cmd, ret); TEE_SESSION_UNLOCK(); return ret; } param->sec_handle = msg->sec_handle; param->refcount = msg->refcount; param->alignment = msg->alignment; param->size = msg->size; pr_debug("shndl=0x%llx refcnt=%d align=0x%llx size=0x%llx\n", (u64)param->sec_handle, param->refcount, (u64)param->alignment, (u64)param->size); TEE_SESSION_UNLOCK(); return TMEM_OK; } #define SECMEM_ERROR_OUT_OF_MEMORY (0x8) int tee_alloc(u32 alignment, u32 size, u32 *refcount, u64 *sec_handle, u8 *owner, u32 id, u32 clean, void *tee_data, void *dev_desc) { int ret; struct secmem_param param = {0}; struct tmem_device_description *tee_dev_desc = (struct tmem_device_description *)dev_desc; struct tee_peer_ops_data *ops_data = &tee_dev_desc->u_ops_data.tee; u32 tee_ta_cmd = (clean ? ops_data->tee_cmds[TEE_OP_ALLOC_ZERO] : ops_data->tee_cmds[TEE_OP_ALLOC]); UNUSED(tee_data); UNUSED(owner); UNUSED(id); UNUSED(dev_desc); *refcount = 0; *sec_handle = 0; param.alignment = alignment; param.size = size; param.refcount = 0; param.sec_handle = 0; ret = secmem_execute(tee_ta_cmd, ¶m); if (ret == SECMEM_ERROR_OUT_OF_MEMORY) { pr_err("%s:%d out of memory!\n", __func__, __LINE__); return -ENOMEM; } else if (ret) { return TMEM_TEE_ALLOC_CHUNK_FAILED; } *refcount = param.refcount; *sec_handle = param.sec_handle; pr_debug("ref cnt: 0x%x, sec_handle: 0x%llx\n", param.refcount, param.sec_handle); return TMEM_OK; } int tee_free(u64 sec_handle, u8 *owner, u32 id, void *tee_data, void *dev_desc) { struct secmem_param param = {0}; struct tmem_device_description *tee_dev_desc = (struct tmem_device_description *)dev_desc; struct tee_peer_ops_data *ops_data = &tee_dev_desc->u_ops_data.tee; u32 tee_ta_cmd = ops_data->tee_cmds[TEE_OP_FREE]; UNUSED(tee_data); UNUSED(owner); UNUSED(id); UNUSED(dev_desc); param.sec_handle = (u32)sec_handle; if (secmem_execute(tee_ta_cmd, ¶m)) return TMEM_TEE_FREE_CHUNK_FAILED; return TMEM_OK; } int tee_mem_reg_add(u64 pa, u32 size, void *tee_data, void *dev_desc) { int ret; struct secmem_param param = {0}; struct tmem_device_description *tee_dev_desc = (struct tmem_device_description *)dev_desc; struct tee_peer_ops_data *ops_data = &tee_dev_desc->u_ops_data.tee; u32 tee_ta_cmd = ops_data->tee_cmds[TEE_OP_REGION_ENABLE]; UNUSED(tee_data); UNUSED(dev_desc); param.sec_handle = pa; param.size = size; if (secmem_execute(tee_ta_cmd, ¶m)) return TMEM_TEE_APPEND_MEMORY_FAILED; if (tee_dev_desc->notify_remote && tee_dev_desc->notify_remote_fn) { ret = tee_dev_desc->notify_remote_fn( pa, size, tee_dev_desc->mtee_chunks_id); if (ret != 0) { pr_err("[%d] TEE notify reg mem add to MTEE failed:%d\n", tee_dev_desc->kern_tmem_type, ret); return TMEM_TEE_NOTIFY_MEM_ADD_CFG_TO_MTEE_FAILED; } } pr_info("[%d] TEE append reg mem PASS: PA=0x%lx, size=0x%lx\n", tee_dev_desc->kern_tmem_type, pa, size); return TMEM_OK; } int tee_mem_reg_remove(void *tee_data, void *dev_desc) { int ret; struct secmem_param param = {0}; struct tmem_device_description *tee_dev_desc = (struct tmem_device_description *)dev_desc; struct tee_peer_ops_data *ops_data = &tee_dev_desc->u_ops_data.tee; u32 tee_ta_cmd = ops_data->tee_cmds[TEE_OP_REGION_DISABLE]; UNUSED(tee_data); UNUSED(dev_desc); if (secmem_execute(tee_ta_cmd, ¶m)) return TMEM_TEE_RELEASE_MEMORY_FAILED; if (tee_dev_desc->notify_remote && tee_dev_desc->notify_remote_fn) { ret = tee_dev_desc->notify_remote_fn( 0x0ULL, 0x0, tee_dev_desc->mtee_chunks_id); if (ret != 0) { pr_err("[%d] TEE notify reg mem remove to MTEE failed:%d\n", tee_dev_desc->mtee_chunks_id, ret); return TMEM_TEE_NOTIFY_MEM_REMOVE_CFG_TO_MTEE_FAILED; } } return TMEM_OK; } #define VALID_INVOKE_COMMAND(cmd) \ ((cmd >= CMD_SEC_MEM_INVOKE_CMD_START) \ && (cmd <= CMD_SEC_MEM_INVOKE_CMD_END)) static int tee_invoke_command(struct trusted_driver_cmd_params *invoke_params, void *tee_data, void *dev_desc) { struct secmem_param param = {0}; UNUSED(tee_data); UNUSED(dev_desc); if (INVALID(invoke_params)) return TMEM_PARAMETER_ERROR; if (!VALID_INVOKE_COMMAND(invoke_params->cmd)) { pr_err("%s:%d unsupported cmd:%d!\n", __func__, __LINE__, invoke_params->cmd); return TMEM_COMMAND_NOT_SUPPORTED; } pr_debug("invoke cmd is %d (0x%llx, 0x%llx, 0x%llx, 0x%llx)\n", invoke_params->cmd, invoke_params->param0, invoke_params->param1, invoke_params->param2, invoke_params->param3); param.alignment = invoke_params->param0; param.size = invoke_params->param1; /* CAUTION: USE IT CAREFULLY! * For param2, secmem ta only supports 32-bit for backward compatibility */ param.refcount = (u32)invoke_params->param2; param.sec_handle = invoke_params->param3; if (secmem_execute(invoke_params->cmd, ¶m)) return TMEM_TEE_INVOKE_COMMAND_FAILED; invoke_params->param0 = param.alignment; invoke_params->param1 = param.size; /* CAUTION: USE IT CAREFULLY! * For param2, secmem ta only supports 32-bit for backward compatibility */ invoke_params->param2 = (u64)param.refcount; invoke_params->param3 = param.sec_handle; return TMEM_OK; } static struct trusted_driver_operations tee_gp_peer_ops = { .session_open = tee_session_open, .session_close = tee_session_close, .memory_alloc = tee_alloc, .memory_free = tee_free, .memory_grant = tee_mem_reg_add, .memory_reclaim = tee_mem_reg_remove, .invoke_cmd = tee_invoke_command, }; void get_tee_peer_ops(struct trusted_driver_operations **ops) { pr_info("TEE_OPS set\n"); *ops = &tee_gp_peer_ops; }