// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ /* * GenieZone (hypervisor-based seucrity platform) enables hardware protected * and isolated security execution environment, includes * 1. GZ hypervisor * 2. Hypervisor-TEE OS (built-in Trusty OS) * 3. Drivers (ex: debug, communication and interrupt) for GZ and * hypervisor-TEE OS * 4. GZ and hypervisor-TEE and GZ framework (supporting multiple TEE * ecosystem, ex: M-TEE, Trusty, GlobalPlatform, ...) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if IS_ENABLED(CONFIG_PM_SLEEP) #include #endif #include #include #include #include #include #include #include #include #include /* FIXME: MTK_PPM_SUPPORT is disabled temporarily */ #if IS_ENABLED(CONFIG_MTK_TEE_GP_SUPPORT) #include "tee_client_api.h" #endif #if IS_ENABLED(CONFIG_MTK_ENG_BUILD) #define DBG_KREE_SYS #endif #ifdef DBG_KREE_SYS #define KREE_DEBUG(fmt...) pr_info("[KREE]" fmt) #define KREE_INFO(fmt...) pr_info("[KREE]" fmt) #define KREE_ERR(fmt...) pr_info("[KREE][ERR]" fmt) #else #define KREE_DEBUG(fmt...) #define KREE_INFO(fmt...) pr_info("[KREE]" fmt) #define KREE_ERR(fmt...) pr_info("[KREE][ERR]" fmt) #endif #if IS_ENABLED(CONFIG_TEEGRIS_TEE_SUPPORT) #define TYPE_STRUCT #else #define TYPE_STRUCT struct #endif #define DYNAMIC_TIPC_LEN #define GZ_SYS_SERVICE_NAME_TRUSTY "com.mediatek.geniezone.srv.sys" #define GZ_SYS_SERVICE_NAME_NEBULA "nebula.com.mediatek.geniezone.srv.sys" #define GZ_MEM_SERVICE_NAME_TRUSTY "com.mediatek.geniezone.srv.mem" #define GZ_MEM_SERVICE_NAME_NEBULA "nebula.com.mediatek.geniezone.srv.mem" #define KREE_SESSION_HANDLE_MAX_SIZE (512) #define KREE_SESSION_HANDLE_SIZE_MASK (KREE_SESSION_HANDLE_MAX_SIZE - 1) #define KREE_SESSION_HANDLE_TRUSTY (KREE_SESSION_HANDLE_MAX_SIZE) #define KREE_SESSION_HANDLE_NEBULA (KREE_SESSION_HANDLE_MAX_SIZE + 1) #define TIPC_RETRY_MAX_COUNT (100) #define TIPC_RETRY_WAIT_MS (10) #define IS_RESTARTSYS_ERROR(err) (err == -ERESTARTSYS) #define RETRY_REQUIRED(cnt) (cnt <= TIPC_RETRY_MAX_COUNT) DEFINE_MUTEX(fd_mutex); DEFINE_MUTEX(session_mutex); DEFINE_MUTEX(port_mutex); /* For high performance required and do not use mutiha API user, it may have * some delay when run into multi HA situation. Use servicecall_lock to ensure * the performance for such users. */ DEFINE_MUTEX(servicecall_lock); int perf_boost_cnt; struct mutex perf_boost_lock; struct platform_device *tz_system_dev; struct completion ree_dummy_event; struct task_struct *ree_dummy_task; #if IS_ENABLED(CONFIG_PM_SLEEP) struct wakeup_source *TeeServiceCall_wake_lock; /*4.19*/ #endif /* only need to open sys service once */ static int32_t _sys_service_Fd[TEE_ID_END] = { [0 ... TEE_ID_END - 1] = -1 }; static struct tipc_k_handle _sys_service_h[TEE_ID_END]; static char *gz_sys_service_name[] = { GZ_SYS_SERVICE_NAME_TRUSTY, GZ_SYS_SERVICE_NAME_NEBULA }; static struct tipc_k_handle _kree_session_handle_pool[KREE_SESSION_HANDLE_MAX_SIZE]; static uint32_t _kree_session_handle_idx; #define debugFg 0 #define TIPC_PORT_START_ID (1000) #define TIPC_PORT_END_ID (0xFFFFFFFF) #define MAX_SRV_NAME_LEN (64) #define MAX_SRV_NAME_CNT (4) /* After sending a command to GZ, GZ binds the HA on specific CPU and the HA can * only execute on that CPU. So that, it needs to ensure only one command can * sent to the HA at one time. */ struct tipc_k_port { struct mutex lock; struct kref refcount; int id; size_t srv_name_cnt; char srv_name[MAX_SRV_NAME_CNT][MAX_SRV_NAME_LEN]; }; DEFINE_IDR(port_idr); static void _free_port(struct kref *kref) { struct tipc_k_port *port; port = container_of(kref, struct tipc_k_port, refcount); mutex_lock(&port_mutex); if (kref_read(kref) < 1) { KREE_DEBUG("%s: Release tipc_k_port srv %s, id %d\n", __func__, port->srv_name[0], port->id); idr_remove(&port_idr, port->id); kfree(port); } mutex_unlock(&port_mutex); } static inline void port_lock(struct tipc_k_port *port) { if (port) { kref_get(&port->refcount); mutex_lock(&port->lock); } } static inline void port_unlock(struct tipc_k_port *port) { if (port) { mutex_unlock(&port->lock); kref_put(&port->refcount, _free_port); } } static inline struct tipc_k_port *lookup_port_by_id(int id) { return idr_find(&port_idr, id); } static struct tipc_k_port *lookup_port_by_name(const char *srv_name) { struct tipc_k_port *port, *ret = NULL; int id = 0, i; idr_for_each_entry(&port_idr, port, id) { for (i = 0; i < port->srv_name_cnt; i++) { if (strncmp(port->srv_name[i], srv_name, MAX_SRV_NAME_LEN) == 0) return port; } } return ret; } static TZ_RESULT port_append_srv(struct tipc_k_port *port, const char *srv_name) { if (port->srv_name_cnt + 1 > MAX_SRV_NAME_CNT) { KREE_ERR("%s: Failed to append srv_name %s for port %s\n", __func__, srv_name, port->srv_name[0]); return TZ_RESULT_ERROR_EXCESS_DATA; } strncpy(port->srv_name[port->srv_name_cnt], srv_name, MAX_SRV_NAME_LEN - 1); port->srv_name[port->srv_name_cnt][MAX_SRV_NAME_LEN - 1] = '\0'; port->srv_name_cnt += 1; return TZ_RESULT_SUCCESS; } static struct tipc_k_port *create_port_locked(const char *srv_name) { struct tipc_k_port *port; port = lookup_port_by_name(srv_name); if (port) { kref_get(&port->refcount); return port; } port = kzalloc(sizeof(*port), GFP_KERNEL); if (!port) { return ERR_PTR(-ENOMEM); } mutex_init(&port->lock); kref_init(&port->refcount); port->id = idr_alloc(&port_idr, port, TIPC_PORT_START_ID, TIPC_PORT_END_ID, GFP_KERNEL); port_append_srv(port, srv_name); return port; } static struct tipc_k_port *create_port(const char *srv_name) { struct tipc_k_port *port; mutex_lock(&port_mutex); port = create_port_locked(srv_name); mutex_unlock(&port_mutex); return port; } static bool _tipc_retry_check_and_wait(int err, int retry_cnt, int tag) { if (likely(!IS_RESTARTSYS_ERROR(err))) return false; if (IS_RESTARTSYS_ERROR(err) && RETRY_REQUIRED(retry_cnt)) { KREE_DEBUG("%s: wait %d ms (retry:%d times)(%d)\n", __func__, TIPC_RETRY_WAIT_MS, retry_cnt, tag); msleep(TIPC_RETRY_WAIT_MS); return true; } /* Caution: reach maximum retry count! */ KREE_ERR("%s: system error, please check(%d)!\n", __func__, tag); return false; } static ssize_t _tipc_k_read_retry(struct tipc_k_handle *h, void *buf, size_t buf_len, unsigned int flags) { ssize_t rc; int retry = 0; do { rc = tipc_k_read(h, (void *)buf, buf_len, flags); retry++; } while (_tipc_retry_check_and_wait(rc, retry, 0)); return rc; } static ssize_t _tipc_k_write_retry(struct tipc_k_handle *h, void *buf, size_t buf_len, unsigned int flags) { ssize_t rc; int retry = 0; do { rc = tipc_k_write(h, (void *)buf, buf_len, flags); retry++; } while (_tipc_retry_check_and_wait(rc, retry, 1)); return rc; } enum tipc_chan_state { TIPC_DISCONNECTED = 0, TIPC_CONNECTING, TIPC_CONNECTED, TIPC_STALE, }; static bool _is_tipc_channel_connected(struct tipc_dn_chan *dn) { bool is_chan_connected = false; mutex_lock(&dn->lock); if (dn->state == TIPC_CONNECTED) is_chan_connected = true; mutex_unlock(&dn->lock); return is_chan_connected; } static int _tipc_k_connect_retry(struct tipc_k_handle *h, const char *port_name) { int rc = 0; int retry = 0; struct tipc_k_port *port; port = create_port(port_name); if (unlikely(PTR_ERR_OR_ZERO(port))) { KREE_ERR("%s: Failed to create tipc_k_port for srv %s ret %d\n", __func__, port_name, PTR_ERR_OR_ZERO(port)); return TZ_RESULT_ERROR_OUT_OF_MEMORY; } port_lock(port); do { if (unlikely(IS_RESTARTSYS_ERROR(rc))) { struct tipc_dn_chan *dn = h->dn; if (dn && _is_tipc_channel_connected(dn)) { KREE_DEBUG( "%s: channel is connected already!\n", __func__); port_unlock(port); return 0; } KREE_DEBUG("%s: disconnect and retry!\n", __func__); tipc_k_disconnect(h); } rc = tipc_k_connect(h, port_name); retry++; } while (_tipc_retry_check_and_wait(rc, retry, 2)); port_unlock(port); if (rc == 0) h->dn->port_id = port->id; else kref_put(&port->refcount, _free_port); return rc; } int32_t _setSessionHandle(struct tipc_k_handle h) { int32_t session; int32_t i; mutex_lock(&fd_mutex); for (i = 0; i < KREE_SESSION_HANDLE_MAX_SIZE; i++) { if (_kree_session_handle_pool[_kree_session_handle_idx].dn == 0) break; _kree_session_handle_idx = (_kree_session_handle_idx + 1) % KREE_SESSION_HANDLE_MAX_SIZE; } if (i == KREE_SESSION_HANDLE_MAX_SIZE) { KREE_ERR(" %s: can not get empty slot for session!\n", __func__); return -1; } _kree_session_handle_pool[_kree_session_handle_idx].dn = h.dn; session = _kree_session_handle_idx; _kree_session_handle_idx = (_kree_session_handle_idx + 1) % KREE_SESSION_HANDLE_MAX_SIZE; mutex_unlock(&fd_mutex); return session; } void _clearSessionHandle(uint32_t session) { mutex_lock(&fd_mutex); _kree_session_handle_pool[session].dn = 0; mutex_unlock(&fd_mutex); } int _getSessionHandle(int32_t session, struct tipc_k_handle **h) { if (session == KREE_SESSION_HANDLE_MAX_SIZE + TEE_ID_TRUSTY) *h = &_sys_service_h[TEE_ID_TRUSTY]; else if (session == KREE_SESSION_HANDLE_MAX_SIZE + TEE_ID_NEBULA) *h = &_sys_service_h[TEE_ID_NEBULA]; else if (session >= 0 && session < KREE_SESSION_HANDLE_MAX_SIZE) *h = &_kree_session_handle_pool[session]; else return -1; return 0; } struct tipc_k_handle *_FdToHandle(int32_t session) { struct tipc_k_handle *h = 0; int ret; ret = _getSessionHandle(session, &h); if (ret < 0) KREE_ERR(" %s: can not get seesion handle!\n", __func__); return h; } int32_t _HandleToFd(struct tipc_k_handle h) { return _setSessionHandle(h); } static inline struct tipc_dn_chan *_HandleToChanInfo(struct tipc_k_handle *handle) { return handle ? (struct tipc_dn_chan *)(handle->dn) : NULL; } TZ_RESULT KREE_SessionToTID(KREE_SESSION_HANDLE session, enum tee_id_t *o_tid) { struct tipc_dn_chan *dn = _HandleToChanInfo(_FdToHandle(session)); if (dn) { *o_tid = dn->tee_id; return TZ_RESULT_SUCCESS; } port_lookup_tid("com.default.tee_id", o_tid); pr_info("[%s] session %d to tee id failed, return default %d\n", __func__, session, *o_tid); return TZ_RESULT_ERROR_NO_DATA; } EXPORT_SYMBOL(KREE_SessionToTID); void KREE_SESSION_LOCK(int32_t handle) { struct tipc_dn_chan *chan_p = _HandleToChanInfo(_FdToHandle(handle)); if (!chan_p) return; if (!is_tee_id(chan_p->tee_id)) return; if (handle != _sys_service_Fd[chan_p->tee_id]) mutex_lock(&chan_p->sess_lock); } void KREE_SESSION_UNLOCK(int32_t handle) { struct tipc_dn_chan *chan_p = _HandleToChanInfo(_FdToHandle(handle)); if (!chan_p) return; if (!is_tee_id(chan_p->tee_id)) return; if (handle != _sys_service_Fd[chan_p->tee_id]) mutex_unlock(&chan_p->sess_lock); } static TZ_RESULT KREE_OpenSysFd(uint32_t tee_id) { TZ_RESULT ret = TZ_RESULT_SUCCESS; ret = _tipc_k_connect_retry(&_sys_service_h[tee_id], gz_sys_service_name[tee_id]); if (ret < 0) { KREE_DEBUG("%s: Failed to connect to service, ret = %d\n", __func__, ret); return TZ_RESULT_ERROR_COMMUNICATION; } _sys_service_Fd[tee_id] = KREE_SESSION_HANDLE_MAX_SIZE + tee_id; KREE_DEBUG("===> %s: chan_p = %p, tee_id %d\n", __func__, _sys_service_h[tee_id].dn, tee_id); return ret; } static TZ_RESULT KREE_OpenFd(const char *port, int32_t *Fd) { struct tipc_k_handle h = {.dn = NULL}; TZ_RESULT ret = TZ_RESULT_SUCCESS; int32_t tmp; *Fd = -1; /* invalid fd */ if (!port) return TZ_RESULT_ERROR_BAD_PARAMETERS; KREE_DEBUG(" ===> %s: %s.\n", __func__, port); ret = _tipc_k_connect_retry(&h, port); if (ret < 0) { KREE_ERR("%s: Failed to connect to service, ret = %d\n", __func__, ret); return TZ_RESULT_ERROR_COMMUNICATION; } tmp = _HandleToFd(h); if (tmp < 0) { KREE_ERR("%s: Failed to get session\n", __func__); return TZ_RESULT_ERROR_OUT_OF_MEMORY; } *Fd = tmp; KREE_DEBUG("%s: Fd = %d, chan_p = %p\n", __func__, *Fd, h.dn); return ret; } static TZ_RESULT KREE_CloseFd(int32_t Fd) { int rc; struct tipc_k_handle *h; int ret = TZ_RESULT_SUCCESS; struct tipc_k_port *port; KREE_DEBUG(" ===> %s: Close FD %u\n", __func__, Fd); h = _FdToHandle(Fd); /* verify Fd inside */ if (!h) { KREE_ERR("%s: get tipc handle failed\n", __func__); return TZ_RESULT_ERROR_BAD_PARAMETERS; } if (!h->dn) { KREE_ERR("%s: get tipc dn channel failed\n", __func__); return TZ_RESULT_ERROR_BAD_PARAMETERS; } port = lookup_port_by_id(h->dn->port_id); if (!port) { KREE_ERR("%s: port is not found\n", __func__); return TZ_RESULT_ERROR_BAD_PARAMETERS; } port_lock(port); rc = tipc_k_disconnect(h); port_unlock(port); if (rc) { KREE_ERR("%s: tipc_k_disconnect failed\n", __func__); ret = TZ_RESULT_ERROR_COMMUNICATION; } _clearSessionHandle(Fd); kref_put(&port->refcount, _free_port); return ret; } /* GZ client issue command */ int _gz_client_cmd(int32_t Fd, int session, unsigned int cmd, void *param, int param_size) { ssize_t rc; struct tipc_k_handle *handle; handle = _FdToHandle(Fd); if (!handle) { KREE_ERR("%s: get tipc handle failed\n", __func__); return -1; } KREE_DEBUG(" ===> %s: command = %d.\n", __func__, cmd); KREE_DEBUG(" ===> %s: param_size = %d.\n", __func__, param_size); rc = _tipc_k_write_retry(handle, param, param_size, O_RDWR); KREE_DEBUG(" ===> %s: tipc_k_write rc = %d.\n", __func__, (int)rc); return rc; } /* GZ client wait command ack and return value */ int _gz_client_wait_ret(int32_t Fd, struct gz_syscall_cmd_param *data) { ssize_t rc; struct tipc_k_handle *handle; int size; handle = _FdToHandle(Fd); if (!handle) { KREE_ERR("%s: get tipc handle failed\n", __func__); return -1; } KREE_DEBUG(" ===> %s: tipc_k_read\n", __func__); rc = _tipc_k_read_retry(handle, (void *)data, sizeof(struct gz_syscall_cmd_param), O_RDWR); size = data->payload_size; KREE_DEBUG(" ===> %s: tipc_k_read(1) rc = %d.\n", __func__, (int)rc); KREE_DEBUG(" ===> %s: data payload size = %d.\n", __func__, size); #if debugFg if (size > GZ_MSG_DATA_MAX_LEN) { KREE_ERR("%s: ret payload size(%d) exceed max len(%d)\n", __func__, size, GZ_MSG_DATA_MAX_LEN); return -1; } #endif /* FIXME!!!: need to check return... */ return rc; } static void recover_64_params(union MTEEC_PARAM *dst, union MTEEC_PARAM *src, uint32_t types) { int i; uint32_t type; for (i = 0; i < 4; i++) { type = TZ_GetParamTypes(types, i); if (type == TZPT_MEM_OUTPUT || type == TZPT_MEM_INOUT || type == TZPT_MEM_INPUT) { KREE_DEBUG("RP: recover %p to %p(%u)\n", dst[i].mem.buffer, src[i].mem.buffer, src[i].mem.size); dst[i].mem.buffer = src[i].mem.buffer; dst[i].mem.size = src[i].mem.size; } } } #ifdef GP_PARAM_DEBUG static void report_param_byte(struct gz_syscall_cmd_param *param) { int i, len; char *ptr = (char *)&(param->param); len = sizeof(union MTEEC_PARAM) * 4; KREE_DEBUG("==== report param byte ====\n"); KREE_DEBUG("head of param: %p\n", param->param); KREE_DEBUG("len = %d\n", len); for (i = 0; i < len; i++) { KREE_DEBUG("byte[%d]: 0x%x\n", i, *ptr); ptr++; } } static void report_param(struct gz_syscall_cmd_param *param) { int i; union MTEEC_PARAM *param_p = (union MTEEC_PARAM *) param->param; KREE_DEBUG("session: 0x%x, command: 0x%x\n", param->handle, param->command); KREE_DEBUG("paramTypes: 0x%x\n", param->paramTypes); for (i = 0; i < 4; i++) KREE_DEBUG("param[%d]: 0x%x, 0x%x\n", i, param_p[i].value.a, param_p[i].value.b); } #endif static int GZ_CopyMemToBuffer(struct gz_syscall_cmd_param *param) { uint32_t type, size, offset = 0; int i; void *addr; void *end_buf = ¶m->data[GZ_MSG_DATA_MAX_LEN]; for (i = 0; i < 4; i++) { type = TZ_GetParamTypes(param->paramTypes, i); if (type == TZPT_MEM_INPUT || type == TZPT_MEM_INOUT) { addr = param->param[i].mem.buffer; size = param->param[i].mem.size; /* check mem addr & size */ if (!addr) { KREE_DEBUG("CMTB: empty gp mem addr\n"); continue; } if (!size) { KREE_DEBUG("CMTB: mem size=0\n"); continue; } /* NOTE: currently a single mem param can use up to GZ_MSG_DATA_MAX_LEN */ /* Users just need to make sure that total does not exceed the limit */ #if debugFg if (size > GZ_MEM_MAX_LEN) { KREE_DEBUG("CMTB: invalid gp mem size: %u\n", size); return -1; } #endif /* if (offset >= GZ_MSG_DATA_MAX_LEN) { */ if ((void *)¶m->data[offset + size] > end_buf) { KREE_ERR("CMTB: ERR: buffer overflow\n"); return -1; } KREE_DEBUG("CMTB: cp param %d from %p to %p, size %u\n", i, addr, (void *)¶m->data[offset], size); memcpy((void *)¶m->data[offset], addr, size); #ifdef FIX_MEM_DATA_BUFFER offset += GZ_MEM_MAX_LEN; #else offset += size; #endif } } KREE_DEBUG("CMTB: total %u bytes copied\n", offset); return offset; } static int GZ_CopyMemFromBuffer(struct gz_syscall_cmd_param *param) { uint32_t type, size, offset = 0; int i; void *addr; void *end_buf = ¶m->data[GZ_MSG_DATA_MAX_LEN]; KREE_DEBUG("CMTB: end of buf: %p\n", end_buf); for (i = 0; i < 4; i++) { type = TZ_GetParamTypes(param->paramTypes, i); if (type == TZPT_MEM_OUTPUT || type == TZPT_MEM_INOUT) { addr = param->param[i].mem.buffer; size = param->param[i].mem.size; /* check mem addr & size */ if (!addr) { KREE_DEBUG("CMTB: empty gp mem addr\n"); continue; } if (!size) { KREE_DEBUG("CMTB: mem size=0\n"); continue; } /* NOTE: currently a single mem param can use up to GZ_MSG_DATA_MAX_LEN */ /* Users just need to make sure that total does not exceed the limit */ #if debugFg if (size > GZ_MEM_MAX_LEN) { KREE_DEBUG("CMTB: invalid gp mem size: %u\n", size); return -1; } #endif /* if (offset >= GZ_MSG_DATA_MAX_LEN) { */ if ((void *)¶m->data[offset + size] > end_buf) { KREE_ERR("CMFB: ERR: buffer overflow\n"); break; } KREE_DEBUG( "CMFB: copy param %d from %p to %p, size %d\n", i, (void *)¶m->data[offset], addr, size); memcpy(addr, (void *)¶m->data[offset], size); #ifdef FIX_MEM_DATA_BUFFER offset += GZ_MEM_MAX_LEN; #else offset += size; #endif } } return offset; } void GZ_RewriteParamMemAddr(struct gz_syscall_cmd_param *param) { uint32_t type, size, offset = 0; int i; union MTEEC_PARAM *param_p = (union MTEEC_PARAM *) param->param; KREE_DEBUG("RPMA: head of param: %p\n", param_p); for (i = 0; i < 4; i++) { type = TZ_GetParamTypes(param->paramTypes, i); if (type == TZPT_MEM_OUTPUT || type == TZPT_MEM_INOUT) { size = param_p[i] .mem .size; /* GZ use mem rather than mem64 */ KREE_DEBUG("RPMA: param %d mem size: %u\n", i, size); param_p[i].mem.buffer = &(param->data[offset]); param_p[i].mem.size = size; KREE_DEBUG("RPMA: head of param[%d]: %p\n", i, ¶m_p[i]); KREE_DEBUG("RPMA: rewrite param %d mem addr: %p\n", i, param_p[i].mem.buffer); KREE_DEBUG("RPMA: rewrite param %d mem size: %u\n", i, param_p[i].mem.size); if (offset >= GZ_MSG_DATA_MAX_LEN) { KREE_ERR("CMTB: ERR: buffer overflow\n"); break; } #ifdef FIX_MEM_DATA_BUFFER offset += GZ_MEM_MAX_LEN; #else offset += size; #endif } } #ifdef GP_PARAM_DEBUG KREE_DEBUG("======== in RPMA =========\n"); report_param_byte(param); KREE_DEBUG("======== leave RPMA =========\n"); #endif } void make_64_params_local(union MTEEC_PARAM *dst, uint32_t types) { int i; long addr; uint32_t type, size; uint64_t tmp; uint64_t high = 0, low = 0; for (i = 0; i < 4; i++) { type = TZ_GetParamTypes(types, i); if (type == TZPT_MEM_INPUT || type == TZPT_MEM_INOUT || type == TZPT_MEM_OUTPUT) { KREE_DEBUG("make 64 params local, addr: %p/%u\n", dst[i].mem.buffer, dst[i].mem.size); size = dst[i].mem.size; addr = (long)dst[i].mem.buffer; high = addr; high >>= 32; high <<= 32; low = (addr & 0xffffffff); KREE_DEBUG( "make 64 params local, high/low: 0x%llx/0x%llx\n", high, low); tmp = (high | low); dst[i].mem64.buffer = tmp; dst[i].mem64.size = size; KREE_DEBUG( "make 64 params local, new addr: 0x%llx/%u\n", dst[i].mem64.buffer, dst[i].mem64.size); } } } /* GZ Ree service */ enum GZ_ReeServiceCommand { REE_SERVICE_CMD_BASE = 0x0, REE_SERVICE_CMD_ADD, REE_SERVICE_CMD_MUL, REE_SERVICE_CMD_NEW_THREAD, REE_SERVICE_CMD_KICK_SEM, REE_SERVICE_CMD_TEE_INIT_CTX, REE_SERVICE_CMD_TEE_FINAL_CTX, REE_SERVICE_CMD_TEE_OPEN_SE, REE_SERVICE_CMD_TEE_CLOSE_SE, REE_SERVICE_CMD_TEE_INVOK_CMD, REE_SERVICE_CMD_END }; #ifdef MTK_PPM_SUPPORT struct _cpus_cluster_freq cpus_cluster_freq[NR_PPM_CLUSTERS]; /* static atomic_t boost_cpu[NR_PPM_CLUSTERS]; */ #endif struct nop_param { uint32_t type; /* 1: new thread, 2: kick semaphore */ uint64_t value; uint32_t boost_enabled; }; struct nop_param new_param; int ree_dummy_thread(void *data) { /* int curr_cpu = get_cpu(); */ /* uint32_t boost_enabled = 0; */ struct platform_device *pdev = (struct platform_device *)data; struct device *trusty_dev = NULL; struct sched_param param = { .sched_priority = 1 }; struct trusty_nop nop; int ret; KREE_DEBUG("%s +++++\n", __func__); if (!data) { KREE_ERR("%s failed to get data %p\n", __func__, data); return 0; } trusty_dev = pdev->dev.parent; if (!pdev) { KREE_ERR("%s failed to get device %p\n", __func__, trusty_dev); return 0; } sched_setscheduler(current, SCHED_FIFO, ¶m); trusty_nop_init(&nop, 0, 0, 0); while (1) { ret = wait_for_completion_interruptible(&ree_dummy_event); if (ret != 0) KREE_DEBUG("%s: wait_for_completion failed, ret=%d %p", __func__, ret, get_current()); /* REE_SERVICE_CMD_KICK_SEM */ if (new_param.type == 2) usleep_range(100, 200); nop.args[0] = new_param.type; nop.args[1] = (uint32_t)new_param.value; nop.args[2] = (uint32_t)(new_param.value >> 32); /* get into GZ through NOP SMC call */ trusty_enqueue_nop(trusty_dev, &nop, smp_processor_id()); } KREE_ERR("%s leave(%d)\n", __func__, ret); return 0; } static int ree_service_threads(uint32_t type, uint32_t val_a, uint32_t val_b, uint32_t param1_val_b) { struct cpumask ree_cpumask; new_param.type = type; new_param.value = (((uint64_t)val_b) << 32) | val_a; new_param.boost_enabled = (param1_val_b & 0x00ff0000)>>16; cpumask_copy(&ree_cpumask, cpu_all_mask); cpumask_clear_cpu(smp_processor_id(), &ree_cpumask); if (!cpumask_empty(&ree_cpumask)) set_cpus_allowed_ptr(ree_dummy_task, &ree_cpumask); complete(&ree_dummy_event); return 0; } TZ_RESULT _Gz_KreeServiceCall_body(KREE_SESSION_HANDLE handle, uint32_t command, uint32_t paramTypes, union MTEEC_PARAM param[4]) { int ret; /* * NOTE: Only support VALUE type gp parameters */ KREE_DEBUG("=====> session %d, command %x\n", handle, command); KREE_DEBUG("=====> %s, param values %x %x %x %x\n", __func__, param[0].value.a, param[1].value.a, param[2].value.a, param[3].value.a); switch (command) { case REE_SERVICE_CMD_ADD: param[2].value.a = param[0].value.a + param[1].value.a; break; case REE_SERVICE_CMD_MUL: param[2].value.a = param[0].value.a * param[1].value.a; break; case REE_SERVICE_CMD_NEW_THREAD: KREE_DEBUG( "[REE service call] fork threads 0x%x%x, parm1ValB=0x%x\n", param[0].value.b, param[0].value.a, param[1].value.b); ret = ree_service_threads(1, (uint32_t)param[0].value.a, (uint32_t)param[0].value.b, (uint32_t)param[1].value.b); param[1].value.a = ret; break; case REE_SERVICE_CMD_KICK_SEM: KREE_DEBUG( "[REE service call] kick semaphore 0x%x%x, parm1ValB=0x%x\n", param[0].value.b, param[0].value.a, param[1].value.b); ret = ree_service_threads(2, (uint32_t)param[0].value.a, (uint32_t)param[0].value.b, (uint32_t)param[1].value.b); param[1].value.a = ret; break; #if IS_ENABLED(CONFIG_MTK_TEE_GP_SUPPORT) && !IS_ENABLED(CONFIG_TEEGRIS_TEE_SUPPORT) case REE_SERVICE_CMD_TEE_INIT_CTX: ret = TEEC_InitializeContext( (char *)param[0].mem.buffer, (TYPE_STRUCT TEEC_Context *)param[1].mem.buffer); if (ret != TEEC_SUCCESS) KREE_ERR("[ERROR] TEEC_InitializeContext failed: %x\n", ret); param[2].value.a = ret; break; case REE_SERVICE_CMD_TEE_FINAL_CTX: TEEC_FinalizeContext( (TYPE_STRUCT TEEC_Context *)param[0].mem.buffer); break; case REE_SERVICE_CMD_TEE_OPEN_SE: ret = TEEC_OpenSession( (TYPE_STRUCT TEEC_Context *)param[0].mem.buffer, (TYPE_STRUCT TEEC_Session *)param[1].mem.buffer, (TYPE_STRUCT TEEC_UUID *)param[2].mem.buffer, TEEC_LOGIN_PUBLIC, NULL, NULL, NULL); if (ret != TEEC_SUCCESS) KREE_ERR("[ERROR] TEEC_OpenSession failed: %x\n", ret); param[3].value.a = ret; break; case REE_SERVICE_CMD_TEE_CLOSE_SE: TEEC_CloseSession((TYPE_STRUCT TEEC_Session *)param[0].mem.buffer); break; case REE_SERVICE_CMD_TEE_INVOK_CMD: ret = TEEC_InvokeCommand( (TYPE_STRUCT TEEC_Session *)param[0].mem.buffer, param[1].value.a, (TYPE_STRUCT TEEC_Operation *)param[2].mem.buffer, NULL); if (ret != TEEC_SUCCESS) KREE_ERR("[ERROR] TEEC_InvokeCommand failed: %x\n", ret); param[3].value.a = ret; break; #endif default: KREE_DEBUG("[REE service call] %s: invalid command = 0x%x\n", __func__, command); break; } return 0; } TZ_RESULT _GzServiceCall_body(int32_t Fd, unsigned int cmd, struct gz_syscall_cmd_param *param, union MTEEC_PARAM origin[4]) { int ret = TZ_RESULT_SUCCESS; int rc, copied = 0; KREE_SESSION_HANDLE session; struct tipc_dn_chan *chan_p; struct gz_syscall_cmd_param ree_param = {0}; /* get session from Fd */ chan_p = _HandleToChanInfo(_FdToHandle(Fd)); if (!chan_p) { KREE_ERR("%s: NULL chan_p, invalid Fd\n", __func__); return TZ_RESULT_ERROR_BAD_PARAMETERS; } session = chan_p->session; KREE_DEBUG("%s: session = %d.\n", __func__, (int)session); /* make input */ param->handle = session; copied = GZ_CopyMemToBuffer(param); if (copied < 0) { KREE_ERR(" invalid input gp params in service call\n"); return TZ_RESULT_ERROR_BAD_PARAMETERS; } param->payload_size = copied; make_64_params_local(param->param, param->paramTypes); /* send command to establish system session in geniezone */ #ifdef DYNAMIC_TIPC_LEN rc = _gz_client_cmd(Fd, session, cmd, param, GZ_MSG_HEADER_LEN + param->payload_size); #else rc = _gz_client_cmd(Fd, session, cmd, param, sizeof(*param)); #endif if (rc < 0) { KREE_ERR("%s: gz client cmd failed\n", __func__); return TZ_RESULT_ERROR_COMMUNICATION; } /* keeps serving REE call until ends */ while (1) { rc = _gz_client_wait_ret(Fd, &ree_param); if (rc < 0) { KREE_ERR("%s: wait ret failed(%d)\n", __func__, rc); ret = TZ_RESULT_ERROR_COMMUNICATION; break; } KREE_DEBUG("=====> %s, ree service %d\n", __func__, ree_param.ree_service); /* TODO: ret = ree_param.ret */ /* check if REE service */ if (ree_param.ree_service == 0) { KREE_DEBUG("=====> %s, general return!!!!\n", __func__); memcpy(param, &ree_param, sizeof(*param)); recover_64_params(param->param, origin, param->paramTypes); copied = GZ_CopyMemFromBuffer(param); if (copied < 0) { KREE_ERR(" invalid output gp params\n"); ret = TZ_RESULT_ERROR_BAD_PARAMETERS; } break; } else if (ree_param.ree_service != 1) { KREE_ERR("invalid ree_service value\n"); break; } /* REE service main function */ GZ_RewriteParamMemAddr(&ree_param); _Gz_KreeServiceCall_body(ree_param.handle, ree_param.command, ree_param.paramTypes, ree_param.param); /* return param to GZ */ copied = GZ_CopyMemToBuffer(&ree_param); if (copied < 0) { KREE_ERR(" invalid gp params\n"); break; } ree_param.payload_size = copied; make_64_params_local(ree_param.param, ree_param.paramTypes); #ifdef DYNAMIC_TIPC_LEN rc = _gz_client_cmd(Fd, session, 0, &ree_param, GZ_MSG_HEADER_LEN + ree_param.payload_size); #else rc = _gz_client_cmd(Fd, session, 0, &ree_param, sizeof(ree_param)); #endif if (rc < 0) { KREE_ERR("%s: gz client cmd failed\n", __func__); ret = TZ_RESULT_ERROR_COMMUNICATION; break; } KREE_DEBUG("=========> %s _GzRounter done\n", __func__); } return ret; } static void kree_perf_boost(int enable) { /* struct ppm_limit_data freq_to_set[2]; */ mutex_lock(&perf_boost_lock); /* KREE_ERR("%s %s\n", __func__, enable>0?"enable":"disable"); */ if (enable) { if (perf_boost_cnt == 0) { KREE_DEBUG("%s wake_lock\n", __func__); #if IS_ENABLED(CONFIG_PM_SLEEP) __pm_stay_awake(TeeServiceCall_wake_lock); /*4.19*/ #endif } perf_boost_cnt++; } else { if (perf_boost_cnt == 1) { KREE_DEBUG("%s wake_unlock\n", __func__); #if IS_ENABLED(CONFIG_PM_SLEEP) __pm_relax(TeeServiceCall_wake_lock); /*4.19*/ #endif } if (perf_boost_cnt > 0) perf_boost_cnt--; } mutex_unlock(&perf_boost_lock); } TZ_RESULT KREE_CreateSession(const char *ta_uuid, KREE_SESSION_HANDLE *pHandle) { TZ_RESULT ret = TZ_RESULT_SUCCESS; int32_t chan_fd; struct tipc_dn_chan *chan_p; union MTEEC_PARAM p[4]; KREE_SESSION_HANDLE session; uint tee_id; KREE_DEBUG("%s: %s\n", __func__, ta_uuid); if (!pHandle || !ta_uuid) return TZ_RESULT_ERROR_BAD_PARAMETERS; mutex_lock(&session_mutex); /* connect to target service */ ret = KREE_OpenFd(ta_uuid, &chan_fd); if (ret) { KREE_ERR("%s: open fd fail\n", __func__); goto create_session_out; } chan_p = _HandleToChanInfo(_FdToHandle(chan_fd)); if (!chan_p) { KREE_ERR("%s: NULL chan_p, invalid Fd\n", __func__); ret = TZ_RESULT_ERROR_BAD_PARAMETERS; goto create_session_out; } KREE_DEBUG("===> _GzCreateSession_body: chan_p = 0x%llx\n", (uint64_t)chan_p); tee_id = chan_p->tee_id; KREE_DEBUG("%s: get tee_id %d\n", __func__, tee_id); /* connect to sys service */ if (_sys_service_Fd[tee_id] < 0) { KREE_DEBUG("%s: Open sys service fd first.\n", __func__); ret = KREE_OpenSysFd(tee_id); if (ret) { KREE_ERR("%s: open sys service fd failed, ret = 0x%x\n", __func__, ret); goto create_session_out; } } p[0].mem.buffer = (void *)ta_uuid; p[0].mem.size = (uint32_t)(strnlen(ta_uuid, MAX_UUID_LEN) + 1); ret = KREE_TeeServiceCall( _sys_service_Fd[tee_id], TZCMD_SYS_SESSION_CREATE, TZ_ParamTypes2(TZPT_MEM_INPUT, TZPT_VALUE_OUTPUT), p); if (ret != TZ_RESULT_SUCCESS) { KREE_ERR("%s: create session fail\n", __func__); goto create_session_out; } session = p[1].value.a; KREE_DEBUG(" ===> %s: Get session = %d.\n", __func__, (int)session); chan_p->session = session; mutex_init(&chan_p->sess_lock); create_session_out: *pHandle = chan_fd; mutex_unlock(&session_mutex); return ret; } EXPORT_SYMBOL(KREE_CreateSession); TZ_RESULT KREE_CloseSession(KREE_SESSION_HANDLE handle) { TZ_RESULT ret = TZ_RESULT_SUCCESS; int32_t Fd; KREE_SESSION_HANDLE session; union MTEEC_PARAM p[4]; struct tipc_dn_chan *chan_p; enum tee_id_t tee_id; KREE_DEBUG(" ===> %s: Close session ...\n", __func__); mutex_lock(&session_mutex); Fd = handle; /* get session from Fd */ chan_p = _HandleToChanInfo(_FdToHandle(Fd)); if (!chan_p) { KREE_ERR("%s: NULL chan_p, invalid Fd\n", __func__); ret = TZ_RESULT_ERROR_BAD_PARAMETERS; goto close_session_out; } tee_id = chan_p->tee_id; if (!is_tee_id(tee_id) || _sys_service_Fd[tee_id] < 0) { KREE_ERR("%s: sys service fd is not open.\n", __func__); ret = TZ_RESULT_ERROR_GENERIC; goto close_session_out; } session = chan_p->session; KREE_DEBUG(" ===> %s: session = %d.\n", __func__, (int)session); /* close session */ p[0].value.a = session; ret = KREE_TeeServiceCall(_sys_service_Fd[tee_id], TZCMD_SYS_SESSION_CLOSE, TZ_ParamTypes1(TZPT_VALUE_INPUT), p); if (ret != TZ_RESULT_SUCCESS) { KREE_ERR("%s: close session fail\n", __func__); goto close_session_out; } mutex_destroy(&chan_p->sess_lock); /* close Fd */ ret = KREE_CloseFd(Fd); if (ret != TZ_RESULT_SUCCESS) { KREE_ERR("%s: close FD fail\n", __func__); goto close_session_out; } close_session_out: mutex_unlock(&session_mutex); return ret; } EXPORT_SYMBOL(KREE_CloseSession); TZ_RESULT KREE_TeeServiceCallPlus(KREE_SESSION_HANDLE handle, uint32_t command, uint32_t paramTypes, union MTEEC_PARAM param[4], int32_t cpumask) { int iret; struct gz_syscall_cmd_param cparam; int32_t Fd; struct tipc_dn_chan *chan_p; struct tipc_k_port *port; Fd = handle; chan_p = _HandleToChanInfo(_FdToHandle(handle)); if (!chan_p) { KREE_ERR("%s: get tipc dn channel failed\n", __func__); return TZ_RESULT_ERROR_BAD_PARAMETERS; } /* pass cpumask to channel */ chan_p->cpumask = cpumask; port = lookup_port_by_id(chan_p->port_id); if (!port) { KREE_ERR("%s: port is not found\n", __func__); return TZ_RESULT_ERROR_BAD_PARAMETERS; } memset(&cparam, 0, sizeof(cparam)); cparam.command = command; cparam.paramTypes = paramTypes; memcpy(cparam.param, param, sizeof(union MTEEC_PARAM) * 4); KREE_DEBUG(" ===> KREE Tee Service Call cmd = %d / %d\n", command, cparam.command); if (cpumask == -1) mutex_lock(&servicecall_lock); KREE_SESSION_LOCK(Fd); port_lock(port); kree_perf_boost(1); iret = _GzServiceCall_body(Fd, command, &cparam, param); kree_perf_boost(0); port_unlock(port); KREE_SESSION_UNLOCK(Fd); if (cpumask == -1) mutex_unlock(&servicecall_lock); memcpy(param, cparam.param, sizeof(union MTEEC_PARAM) * 4); return iret; } EXPORT_SYMBOL(KREE_TeeServiceCallPlus); TZ_RESULT KREE_TeeServiceCall(KREE_SESSION_HANDLE handle, uint32_t command, uint32_t paramTypes, union MTEEC_PARAM param[4]) { return KREE_TeeServiceCallPlus(handle, command, paramTypes, param, -1); } EXPORT_SYMBOL(KREE_TeeServiceCall); #if debugFg #include "tz_cross/tz_error_strings.h" const char *TZ_GetErrorString(TZ_RESULT res) { return _TZ_GetErrorString(res); } EXPORT_SYMBOL(TZ_GetErrorString); #endif /***** System Hareware Counter *****/ u64 KREE_GetSystemCnt(void) { u64 cnt = 0; cnt = arch_counter_get_cntvct(); return cnt; } u32 KREE_GetSystemCntFrq(void) { u32 freq = 0; freq = arch_timer_get_cntfrq(); return freq; } static int tz_system_probe(struct platform_device *pdev) { struct tipc_k_port *port = NULL; int ret = 0; KREE_DEBUG("%s\n", __func__); init_completion(&ree_dummy_event); ree_dummy_task = kthread_create(ree_dummy_thread, pdev, "ree_dummy_task"); if (IS_ERR(ree_dummy_task)) { KREE_ERR("Unable to start kernel thread %s\n", __func__); ret = PTR_ERR(ree_dummy_task); } else { set_user_nice(ree_dummy_task, -20); wake_up_process(ree_dummy_task); } tz_system_dev = pdev; /* create gz sys port */ port = create_port(GZ_SYS_SERVICE_NAME_TRUSTY); if (!PTR_ERR_OR_ZERO(port)) port_append_srv(port, GZ_MEM_SERVICE_NAME_TRUSTY); port = create_port(GZ_SYS_SERVICE_NAME_NEBULA); if (!PTR_ERR_OR_ZERO(port)) port_append_srv(port, GZ_MEM_SERVICE_NAME_NEBULA); return ret; } static int tz_system_remove(struct platform_device *pdev) { struct tipc_k_port *port; int id = 0; KREE_DEBUG("%s\n", __func__); idr_for_each_entry(&port_idr, port, id) { _free_port(&port->refcount); } return 0; } #define MODULE_NAME "trusty_gz" static const struct of_device_id tz_system_of_match[] = { { .compatible = "mediatek,trusty-gz", }, {}, }; MODULE_DEVICE_TABLE(of, tz_system_of_match); struct platform_driver tz_system_driver = { .probe = tz_system_probe, .remove = tz_system_remove, .driver = { .name = MODULE_NAME, .owner = THIS_MODULE, .of_match_table = tz_system_of_match, }, }; int tz_system_std_call32(u32 smcnr, u32 a0, u32 a1, u32 a2) { return trusty_std_call32(tz_system_dev->dev.parent, smcnr, a0, a1, a2); }