/* * Copyright (c) Hisilicon Technologies Co., Ltd. 2018-2019. All rights reserved. * Description: hidl for tee * Author: NameMagic * Create: 2018-03-10 */ #define LOG_TAG "LibteecGlobal@3.0" #include "LibteecGlobal.h" #include <cstring> #include <cerrno> #include <csignal> #include <pthread.h> #include <time.h> #include <log/log.h> #include <cutils/list.h> #include <sys/types.h> #include <sys/syscall.h> #include <securec.h> #include "tee_client_api.h" #include "tc_ns_client.h" #include "tee_ca_daemon.h" #include "tee_client_msg.h" using android::hidl::memory::V1_0::IMemory; extern "C" int getpidcon(pid_t pid, char **context); extern "C" void freecon(char *con); namespace vendor { namespace huanglong { namespace hardware { namespace libteec { namespace V3_0 { namespace implementation { using::android::hardware::Void; #if defined(__LP64__) const static char * const DLOPEN_LIBTEEC_PATH = "/vendor/lib64/libteec_vendor.so"; #else const static char * const DLOPEN_LIBTEEC_PATH = "/vendor/lib/libteec_vendor.so"; #endif const static int DIR_NAME_LENS = 64; /* DeathNote */ static list_declare(g_teecHidlProcDataList); static list_declare(g_teecTidList); static pthread_mutex_t g_mutexTidList = PTHREAD_MUTEX_INITIALIZER; static sp<ILibteecGlobalNotify> g_teecNotify; static int32_t TidMutexLock(void) { int lockRet = pthread_mutex_lock(&g_mutexTidList); return lockRet; } static void TidMutexUnlock(int lockRet) { int unlockRet; if (lockRet != 0) { ALOGE("%s: not exe, mutex not in lock state. lock_ret = %d\n", __func__, lockRet); return; } unlockRet = pthread_mutex_unlock(&g_mutexTidList); if (unlockRet != 0) { ALOGE("%s: exe mutexUnlock error, ret = %d\n", __func__, unlockRet); } } static DaemonProcdata *GetProcdataByPid(int pid) { /* found server procdata */ DaemonProcdata *procDataInList = nullptr; struct listnode *ptr = nullptr; /* Paramters right, start execution */ if (!list_empty(&g_teecHidlProcDataList)) { list_for_each(ptr, &g_teecHidlProcDataList) { DaemonProcdata *tmp = node_to_item(ptr, DaemonProcdata, procdataHead); if (tmp->callingPid == pid) { procDataInList = tmp; break; } } } return procDataInList; } static bool CheckProcDataFdFull(DaemonProcdata *procData) { int i; DaemonProcdata *tmpProcData = procData; for (i = 0; i < MAX_CXTCNT_ONECA; i++) { if (tmpProcData->cxtFd[i] == -1) { return false; } } return true; } static bool CheckProcDataFdEmpty(DaemonProcdata *procData) { int i; for (i = 0; i < MAX_CXTCNT_ONECA; i++) { if (procData->cxtFd[i] != -1) { return false; } } return true; } static TEEC_Result SetContextToProcData(DaemonProcdata *outProcData, TEEC_ContextHidl *outContext) { int i; for (i = 0; i < MAX_CXTCNT_ONECA; i++) { if (outProcData->cxtFd[i] == -1) { outProcData->cxtFd[i] = outContext->fd; return TEEC_SUCCESS; } } ALOGE("%s: the cnt of contexts in outProcData is already %d, please finalize some of them\n", __func__, i); return TEEC_FAIL; } static void RemoveContextFromProcData(DaemonProcdata *outProcData, int32_t outContextFd) { int i; for (i = 0; i < MAX_CXTCNT_ONECA; i++) { if (outContextFd == outProcData->cxtFd[i]) { outProcData->cxtFd[i] = -1; return; } } ALOGE("%s: can not find context in outProcdata\n", __func__); } /* just mkdir sec_storage_data, mount sec_storage partition in init.chip.rc */ static int MakeSecStorageDir(const char *storageDir, int strSize) { struct stat statBuf; int ret; if (strSize < 0) { return TEE_HIDL_FAILURE; } /* mkdir for sec_storage_data partition */ if (stat(storageDir, &statBuf)) { ALOGE("stat fail, erron=%d\n", errno); if (errno == ENOENT) { /* folder not exist */ ALOGE("stat folder not exist\n"); ret = mkdir(storageDir, S_IRUSR | S_IWUSR | S_IXUSR); if (ret == -1) { ALOGE("make dir fail, err %d\n", errno); return TEE_HIDL_FAILURE; } ret = chown(storageDir, AID_SYSTEM, AID_SYSTEM); if (ret < 0) { ALOGE("chown error"); } } else { ALOGE("stat fail else\n"); if (S_ISDIR(statBuf.st_mode)) { ALOGE("ROOT DATA DIR is exist, but not a dir\n"); /* shell we rm it and mkdir? */ return TEE_HIDL_FAILURE; } } } return TEE_HIDL_SUCCESS; } /* symlink user0 when teecd init */ static void SymlinkUser0(const char *oldPath, const char *newPath) { int ret; int retChMod; int retChOwn; ret = symlink(oldPath, newPath); if (ret < 0) { ALOGE("symlink user0 erro, errno=%d\n", errno); return; } retChMod = chmod(newPath, S_IRUSR | S_IWUSR | S_IXUSR); retChOwn = chown(newPath, AID_SYSTEM, AID_SYSTEM); int tmpCheckStatus = ((retChMod < 0) || (retChOwn < 0)); if (tmpCheckStatus) { ALOGE("chmod error %d, or chown error %d", retChMod, retChOwn); } return; } /* return 0 when open dir success, others failure */ static int CheckStatAndOpenDir(const char *name) { struct stat st; /* is it a file or directory? */ if (lstat(name, &st) < 0) { ALOGE("lstat %s failed, errno is %x\n", name, errno); return TEE_HIDL_FAILURE; } /* a file, so unlink it */ if (S_ISDIR(st.st_mode) != 1) { if (unlink(name) != 0) { ALOGE("unlink failed, errno is %d\n", errno); return TEE_HIDL_FAILURE; } return TEE_HIDL_FAILURE; } return TEE_HIDL_SUCCESS; } static int ClearOpenStat(const char *name, DIR *dir) { /* close directory handle */ if (closedir(dir) < 0) { ALOGE("closedir %s failed, errno is %d\n", name, errno); return TEE_HIDL_FAILURE; } /* delete target directory */ if (rmdir(name) < 0) { /* no perto handle other dirs */ ALOGE("rmdir %s failed, errno is %d\n", name, errno); return TEE_HIDL_FAILURE; } return TEE_HIDL_SUCCESS; } /* return -1 on failure, with errno set to the first error */ static int UnlinkRecursive(const char *name) { DIR *dir = nullptr; struct dirent *de = nullptr; int fail = 0; char dn[PATH_MAX] = {0}; errno_t rc = EOK; int32_t ret; int32_t tmpCheckStatus; ret = CheckStatAndOpenDir(name); if (ret != TEE_HIDL_SUCCESS) { return ret; } /* a directory, so open handle */ dir = opendir(name); /* teecd has no permission to handle other directorys */ if (dir == nullptr) { ALOGE("dir %s open failed\n", name); return TEE_HIDL_FAILURE; } /* recurse over components */ errno = 0; de = readdir(dir); while (de != nullptr) { tmpCheckStatus = ((!strncmp(de->d_name, "..", sizeof(".."))) || (!strncmp(de->d_name, ".", sizeof(".")))); if (tmpCheckStatus) { de = readdir(dir); continue; } rc = snprintf_s(dn, sizeof(dn), sizeof(dn) - 1, "%s/%s", name, de->d_name); if (rc == -1) { ALOGE("snprintf_s failed %d\n", rc); fail = 1; break; } if (UnlinkRecursive(dn) < 0) { /* no per to handle other dirs */ ALOGE("loop UnlinkRecursive() failed, there are read-only file\n"); fail = 1; break; } errno = 0; de = readdir(dir); } /* in case readdir or UnlinkRecursive failed */ tmpCheckStatus = (fail || errno < 0); if (tmpCheckStatus) { int save = errno; closedir(dir); errno = save; ALOGE("fail is %d, errno is %d\n", fail, errno); return TEE_HIDL_FAILURE; } return ClearOpenStat(name, dir); } static void SigUsr1Handler(int sign) { sign = 0; return; } static void RemoveTidFromList(TidData *tidData) { int retMutexLock = TidMutexLock(); if (retMutexLock) ALOGE("tid mutex lock failed\n"); list_remove(&tidData->tidHead); TidMutexUnlock(retMutexLock); free(tidData); return; } static int AddTidData(TidData **tidData, int pid) { int ret = TEE_HIDL_SUCCESS; int mutexRet; *tidData = reinterpret_cast<TidData *>(malloc(sizeof(TidData))); if (*tidData == nullptr) { ALOGE("%s: tid_data malloc failed\n", __func__); return TEE_HIDL_FAILURE; } (*tidData)->tid = syscall(SYS_gettid); (*tidData)->callingPid = pid; list_init(&(*tidData)->tidHead); mutexRet = TidMutexLock(); if (mutexRet) { ALOGE("tid mutex lock failed\n"); free(*tidData); *tidData = nullptr; return TEE_HIDL_FAILURE; } list_add_tail(&g_teecTidList, &(*tidData)->tidHead); TidMutexUnlock(mutexRet); ALOGD("%s: tid %d is sending command to TEE\n", __func__, (*tidData)->tid); return ret; } static void SendSigToTzdriver(int pid) { int ret; int mutexRet; struct listnode *ptr = nullptr; signal(SIGUSR1, SigUsr1Handler); ALOGD("%s: ignore signal SIGUSR1!\n", __func__); mutexRet = TidMutexLock(); if (mutexRet) { ALOGE("tid mutex lock failed\n"); return; } if (!list_empty(&g_teecTidList)) { list_for_each(ptr, &g_teecTidList) { TidData *tmp = node_to_item(ptr, TidData, tidHead); if (tmp->callingPid == pid) { ret = tgkill(getpid(), tmp->tid, SIGUSR1); ALOGD("%s: send signal SIGUSR1 to tid: %d! ret = %d\n", __func__, tmp->tid, ret); } } } TidMutexUnlock(mutexRet); return; } static int MakeDirAndLinkUser(int userid, uint32_t userStatus, bool status) { int ret; char username[DIR_NAME_LENS] = {0}; /* create user sec storage dir */ ret = MakeSecStorageDir(reinterpret_cast<const char *>(SEC_STORAGE_DATA_USERS), strlen(SEC_STORAGE_DATA_USERS)); if (ret < 0) { ALOGE("mkdir fail ,ret=%d\n", ret); return ret; } /* symlink, no matter if error */ SymlinkUser0(SEC_STORAGE_DATA_DIR, SEC_STORAGE_DATA_USER_0); ret = snprintf_s(username, sizeof(username), sizeof(username) - 1, "%s%d", SEC_STORAGE_DATA_USERS, userid); if (ret < 0) { ALOGE("snprintf ret error\n"); return ret; } if (!status) { ALOGE("userid %u is not in the status %x range\n", userStatus, userid); return TEE_HIDL_FAILURE; } ret = MakeSecStorageDir(username, sizeof(username)); if (ret < 0) { ALOGE("mkdir fail ,ret=%d\n", ret); return ret; } return TEE_HIDL_SUCCESS; } static int UnlinkUser(int userid, uint32_t userStatus, bool status) { int ret; char username[DIR_NAME_LENS] = {0}; /* delete user */ if (!status) { ALOGE("userid %u is not in the status %x range\n", userStatus, userid); return TEE_HIDL_FAILURE; } ret = snprintf_s(username, sizeof(username), sizeof(username) - 1, "%s%d", SEC_STORAGE_DATA_USERS, userid); if (ret < 0) { ALOGE("snprintf2 ret error\n"); return ret; } ret = UnlinkRecursive(username); if (ret < 0) { ALOGE("rmdir fail ,ret=%d\n", ret); return ret; } return TEE_HIDL_SUCCESS; } static void CopyToShareMemory(TEEC_SharedMemory *shareMemBuf, uint8_t *data, uint32_t shmInfoOffset, uint32_t *shmOffset) { shareMemBuf->is_allocated = *reinterpret_cast<bool *>(data + shmInfoOffset); shmInfoOffset += sizeof(bool); shareMemBuf->flags = *reinterpret_cast<uint32_t *>(data + shmInfoOffset); shmInfoOffset += sizeof(uint32_t); shareMemBuf->ops_cnt = *reinterpret_cast<uint32_t *>(data + shmInfoOffset); shmInfoOffset += sizeof(uint32_t); *shmOffset = *reinterpret_cast<uint32_t *>(data + shmInfoOffset); shmInfoOffset += sizeof(uint32_t); shareMemBuf->size = *reinterpret_cast<uint32_t *>(data + shmInfoOffset); shmInfoOffset += sizeof(uint32_t); } LibteecGlobal::~LibteecGlobal() { if (mHandle != nullptr) { dlclose(mHandle); mHandle = nullptr; } ALOGD("Deinit LibteecGlobal!\n"); } bool LibteecGlobal::IsValidContextWithoutLock(const TEEC_Context *context, int pid) { int i; int tmpCheckStatus; DaemonProcdata *outProcData = GetProcdataByPid(pid); tmpCheckStatus = (outProcData == nullptr || context == nullptr); if (tmpCheckStatus) { return false; } if (context->fd < 0) { return false; } for (i = 0; i < MAX_CXTCNT_ONECA; i++) { if (context->fd == outProcData->cxtFd[i]) { return true; } } return false; } bool LibteecGlobal::IsValidContext(const TEEC_Context *context, int pid) { Mutex::Autolock _l(mProcDataLock); return IsValidContextWithoutLock(context, pid); } Return<int32_t> LibteecGlobal::CheckAndOpenHandle() { Mutex::Autolock _l(mHandleLock); if (mHandle == nullptr) { mHandle = dlopen(DLOPEN_LIBTEEC_PATH, RTLD_LAZY); if (mHandle == nullptr) { return TEE_HIDL_FAILURE; } initializeContextProxy = reinterpret_cast<InitializeContextFunc>(dlsym(mHandle, "TEEC_InitializeContextWithType")); openSessionProxy = reinterpret_cast<OpenSessionFunc>(dlsym(mHandle, "TEEC_OpenSessionHidl")); invokeCommandProxy = reinterpret_cast<InvokeCommandFunc>(dlsym(mHandle, "TEEC_InvokeCommandHidl")); closeSessionProxy = reinterpret_cast<CloseSessionFunc>(dlsym(mHandle, "TEEC_CloseSessionHidl")); registerSharedMemoryProxy = reinterpret_cast<RegisterSharedMemoryFunc>(dlsym(mHandle, "TEEC_RegisterSharedMemoryHidl")); allocateSharedMemoryProxy = reinterpret_cast<AllocateSharedMemoryFunc>(dlsym(mHandle, "TEEC_AllocateSharedMemoryHidl")); releaseSharedMemoryProxy = reinterpret_cast<ReleaseSharedMemoryFunc>(dlsym(mHandle, "TEEC_ReleaseSharedMemoryHidl")); extTuiSendEventProxy = reinterpret_cast<ExtTuiSendEventFunc>(dlsym(mHandle, "TEEC_EXT_TuiSendEvent")); getTEEVersionProxy = reinterpret_cast<GetTEEVersionFunc>(dlsym(mHandle, "TEEC_GetTEEVersionHidl")); extSendSysHashXmlProxy = reinterpret_cast<ExtSendSysHashXmlFunc>(dlsym(mHandle, "SendSysHashXml")); getBnContextProxy = reinterpret_cast<GetBnContextFunc>(dlsym(mHandle, "GetBnContext")); putBnContextProxy = reinterpret_cast<PutBnContextFunc>(dlsym(mHandle, "PutBnContext")); findAndRemoveBnContextProxy = reinterpret_cast<FindAndRemoveBnContextFunc>(dlsym(mHandle, "FindAndRemoveBnContext")); getBnSessionProxy = reinterpret_cast<GetBnSessionFunc>(dlsym(mHandle, "GetBnSession")); putBnSessionProxy = reinterpret_cast<PutBnSessionFunc>(dlsym(mHandle, "PutBnSession")); findAndRemoveBnSessionProxy = reinterpret_cast<FindAndRemoveBnSessionFunc>(dlsym(mHandle, "FindAndRemoveSession")); getBnShmByOffsetProxy = reinterpret_cast<GetBnShmByOffsetFunc>(dlsym(mHandle, "GetBnShmByOffset")); putBnShmProxy = reinterpret_cast<PutBnShmFunc>(dlsym(mHandle, "PutBnShrMem")); sendSecfileProxy = reinterpret_cast<SendSecfileFunc>(dlsym(mHandle, "TEEC_SendSecfileHidl")); } return TEE_HIDL_SUCCESS; } /**************************************************************************/ /* function implements */ /**************************************************************************/ Return<void> LibteecGlobal::initializeContext(const hidl_string &name, const hidl_vec<uint8_t> &authInfo, initializeContext_cb hidlCallBackPtr) { int32_t ret = (int32_t)TEEC_FAIL; hidl_vec<uint8_t> contextOutPtr; TEEC_ContextHidl *outContext = nullptr; if (hidlCallBackPtr == nullptr) { ALOGE("%s: hidlCallBackPtr is nullptr\n", __func__); return Void(); } (void)CheckAndOpenHandle(); if (putBnContextProxy == nullptr) { ALOGE("no putBnContext in this handle!\n"); hidlCallBackPtr(ret, contextOutPtr); return Void(); } if ((authInfo.data() == nullptr) || (authInfo.size() != sizeof(CaAuthInfo))) { ALOGE("%s: authInfo is nullptr or size is 0.\n", __func__); hidlCallBackPtr(ret, contextOutPtr); return Void(); } CaAuthInfo *caAuth = reinterpret_cast<CaAuthInfo *>(malloc(sizeof(CaAuthInfo))); if (caAuth == nullptr) { ALOGE("%s: malloc ca auth failed\n", __func__); hidlCallBackPtr(ret, contextOutPtr); return Void(); } (void)memcpy_s(caAuth, sizeof(CaAuthInfo), authInfo.data(), sizeof(CaAuthInfo)); ALOGD("%s: getCallingPid=%d", __func__, caAuth->pid); Mutex::Autolock _l(mProcDataLock); DaemonProcdata *outProcData = CallGetProcDataPtr(caAuth->pid); if (outProcData == nullptr) { goto INITEND; } ret = CallInitializeContextProxy(name, caAuth, &outContext); if (ret == (int32_t)TEEC_SUCCESS) { if (SetContextToProcData(outProcData, outContext)) { ret = TEEC_FAIL; putBnContextProxy(outContext); /* pair with ops_cnt++ when add to list */ putBnContextProxy(outContext); /* pair with initial value 1 */ goto INITEND; } contextOutPtr.setToExternal(reinterpret_cast<uint8_t *>(outContext), sizeof(TEEC_ContextHidl)); putBnContextProxy(outContext); /* pair with ops_cnt++ when add to list */ } INITEND: hidlCallBackPtr(ret, contextOutPtr); free(caAuth); return Void(); } Return<DaemonProcdata *> LibteecGlobal::CallGetProcDataPtr(int pid) { DaemonProcdata *outProcData = GetProcdataByPid(pid); if (outProcData != nullptr) { if (CheckProcDataFdFull(outProcData)) { ALOGE("%s: pid[%d] can not get more context, please finalize some of them\n", __func__, pid); return nullptr; } } else { DaemonProcdata *procData = reinterpret_cast<DaemonProcdata *>(malloc(sizeof(DaemonProcdata))); if (procData == nullptr) { ALOGE("%s: procdata malloc failed\n", __func__); return nullptr; } (void)memset_s(procData, sizeof(DaemonProcdata), 0, sizeof(DaemonProcdata)); for (int i = 0; i < MAX_CXTCNT_ONECA; i++) { procData->cxtFd[i] = -1; } procData->callingPid = pid; list_init(&(procData->procdataHead)); list_add_tail(&g_teecHidlProcDataList, &procData->procdataHead); outProcData = procData; } return outProcData; } Return<int32_t> LibteecGlobal::CallInitializeContextProxy(const hidl_string &name, CaAuthInfo *authInfo, TEEC_ContextHidl **outHidlContext) { int ret; const int32_t type = TEECD_CONNECT; TEEC_ContextHidl *outContext = nullptr; if (outHidlContext == nullptr) { ALOGE("intialize context proxy: out hidl context is nullptr\n"); return TEEC_FAIL; } if (initializeContextProxy == nullptr) { ALOGE("no initializeContext in this handle!\n"); return TEEC_FAIL; } outContext = reinterpret_cast<TEEC_ContextHidl *>(malloc(sizeof(TEEC_ContextHidl))); if (outContext == nullptr) { ALOGE("%s: outContext malloc failed\n", __func__); return TEEC_FAIL; } (void)memset_s(outContext, sizeof(TEEC_ContextHidl), 0x00, sizeof(TEEC_ContextHidl)); ret = initializeContextProxy(reinterpret_cast<const char *>(name.c_str()), reinterpret_cast<TEEC_ContextHidl *>(outContext), type, true, authInfo); if (ret) { ALOGE("%s: TEEC_InitializeContextWithType failed(0x%x)\n", __func__, (TEEC_Result)ret); free(outContext); outContext = nullptr; } *outHidlContext = outContext; return ret; } Return<void> LibteecGlobal::finalizeContext(int32_t pid, const hidl_vec<uint8_t> &halCxtPtr) { ALOGD("%s: getCallingPid=%d", __func__, pid); int tmpCheckStatus = ((halCxtPtr.data() == nullptr) || (halCxtPtr.size() != sizeof(TEEC_Context))); if (tmpCheckStatus) { ALOGE("%s: invalid context!\n", __func__); return Void(); } Mutex::Autolock _l(mProcDataLock); if (!IsValidContextWithoutLock(reinterpret_cast<const TEEC_Context *>(halCxtPtr.data()), pid)) { ALOGE("context and procdata have been released by service_died!\n"); return Void(); } int32_t tempContextFd = CallFinalizeContextProxy(reinterpret_cast<const TEEC_Context *>(halCxtPtr.data())); if (tempContextFd < 0) { ALOGE("%s: CallFinalizeContextProxy failed!\n", __func__); return Void(); } DaemonProcdata *outProcData = GetProcdataByPid(pid); if (outProcData == nullptr) { ALOGE("%s: outProcdata is nullptr\n", __func__); return Void(); } RemoveContextFromProcData(outProcData, tempContextFd); if (CheckProcDataFdEmpty(outProcData)) { list_remove(&outProcData->procdataHead); free(outProcData); outProcData = nullptr; } else { ALOGD("%s: still have context not finalize in pid[%d]\n", __func__, pid); } return Void(); } Return<int32_t> LibteecGlobal::CallFinalizeContextProxy(const TEEC_Context *contextPtr) { TEEC_ContextHidl *outContext = nullptr; int32_t contextFd; if ((findAndRemoveBnContextProxy == nullptr) || (putBnContextProxy == nullptr)) { ALOGE("call finalizeContext proxy:missing proxy in this handle!\n"); return TEE_HIDL_FAILURE; } outContext = findAndRemoveBnContextProxy(contextPtr); if (outContext == nullptr) { ALOGE("%s: no context found in hidl service!\n", __func__); return TEE_HIDL_FAILURE; } contextFd = outContext->fd; putBnContextProxy(outContext); /* pair with initialize context */ return contextFd; } Return<void> LibteecGlobal::openSession(int32_t pid, const hidl_vec<uint8_t> &halCxtPtr, const android::hardware::hidl_handle &handle, const hidl_string &taPath, const hidl_vec<uint8_t> &halUuidPtr, uint32_t halConnMth, const hidl_vec<uint8_t> &haConnData, const hidl_vec<uint8_t> &halOptPtr, const android::hardware::hidl_memory &opMem, openSession_cb hidlCallBackPtr) { int32_t ret = (int32_t)TEEC_FAIL; hidl_vec<uint8_t> contextOutPtr; hidl_vec<uint8_t> sessionOutPtr; hidl_vec<uint8_t> optOutPtr; int32_t originRet = TEEC_ORIGIN_API; if (hidlCallBackPtr == nullptr) { ALOGE("%s: hidlCallBackPtr is nullptr\n", __func__); return Void(); } (void)CheckAndOpenHandle(); if (putBnSessionProxy == nullptr || putBnContextProxy == nullptr) { ALOGE("open session: missing proxy in this handle!\n"); hidlCallBackPtr(ret, contextOutPtr, sessionOutPtr, optOutPtr, originRet); return Void(); } InOutPara paraInOut; /* just for transfer params */ paraInOut.cmdId = (int32_t)halConnMth; paraInOut.pid = pid; ALOGD("%s: getCallingPid=%d ", __func__, paraInOut.pid); TEEC_Session *outSession = nullptr; TEEC_ContextHidl *outContext = nullptr; TEEC_Session retSession; (void)memset_s((void *)(&retSession), sizeof(TEEC_Session), 0, sizeof(TEEC_Session)); int getBnRet = CallGetBnContextProxy(halCxtPtr, paraInOut.pid, &outSession, &outContext); if (getBnRet) { ret = getBnRet; ALOGE("%s: callGetBnProxy failed! ret is %x.\n", __func__, getBnRet); hidlCallBackPtr(ret, contextOutPtr, sessionOutPtr, optOutPtr, originRet); return Void(); } TaFileInfo taFile = { .taPath = nullptr, .taFp = nullptr }; taFile.taPath = reinterpret_cast<const uint8_t *>(taPath.c_str()); native_handle_t *tempHandle = nullptr; taFile.taFp = GetFpFromHandle(handle, tempHandle); TEEC_Operation operation; (void)memset_s(&operation, sizeof(TEEC_Operation), 0, sizeof(TEEC_Operation)); paraInOut.operationPtr = &operation; paraInOut.outContextPtr = outContext; paraInOut.outSessionPtr = outSession; if (SubOpenSession(&taFile, halOptPtr, opMem, halUuidPtr, haConnData, ¶InOut)) { free(outSession); goto OPENSESSIONEND; } retSession.session_id = outSession->session_id; retSession.service_id = outSession->service_id; retSession.ops_cnt = outSession->ops_cnt; contextOutPtr.setToExternal(reinterpret_cast<uint8_t *>(outContext), sizeof(TEEC_ContextHidl)); sessionOutPtr.setToExternal(reinterpret_cast<uint8_t *>(&retSession), sizeof(TEEC_Session)); optOutPtr.setToExternal(reinterpret_cast<uint8_t *>(&operation), sizeof(TEEC_Operation)); putBnSessionProxy(outSession); /* pair with ops_cnt++ when add to list */ OPENSESSIONEND: ret = paraInOut.ret; originRet = paraInOut.originRet; putBnContextProxy(outContext); if (taFile.taFp != nullptr) { fclose(taFile.taFp); } native_handle_delete(tempHandle); hidlCallBackPtr(ret, contextOutPtr, sessionOutPtr, optOutPtr, originRet); return Void(); } Return<int32_t> LibteecGlobal::CallGetBnContextProxy(const hidl_vec<uint8_t> &halCxtPtr, int pid, TEEC_Session **outHidlSession, TEEC_ContextHidl **outHidlContext) { TEEC_ContextHidl *outContext = nullptr; int tmpCheckStatus = ((halCxtPtr.data() == nullptr) || (halCxtPtr.size() != sizeof(TEEC_Context)) || (!IsValidContext(reinterpret_cast<const TEEC_Context *>(halCxtPtr.data()), pid))); if (tmpCheckStatus) { ALOGE("%s: invalid context!\n", __func__); return TEEC_FAIL; } if (getBnContextProxy == nullptr || putBnContextProxy == nullptr) { ALOGE("call get bn context proxy: missing proxy in this handle!\n"); return TEEC_FAIL; } outContext = getBnContextProxy(reinterpret_cast<const TEEC_Context *>(halCxtPtr.data())); if (outContext == nullptr) { ALOGE("%s: no context found in hidl service.\n", __func__); return TEEC_ERROR_BAD_PARAMETERS; } TEEC_Session *outSession = reinterpret_cast<TEEC_Session *>(malloc(sizeof(TEEC_Session))); if (outSession == nullptr) { ALOGE("%s: outSession malloc failed!\n", __func__); putBnContextProxy(outContext); return TEEC_FAIL; } (void)memset_s(outSession, sizeof(TEEC_Session), 0x00, sizeof(TEEC_Session)); *outHidlSession = outSession; *outHidlContext = outContext; return TEEC_SUCCESS; } Return<int32_t> LibteecGlobal::SubOpenSession(const TaFileInfo *taFile, const hidl_vec<uint8_t> &halOptPtr, const android::hardware::hidl_memory &opMem, const hidl_vec<uint8_t> &halUuidPtr, const hidl_vec<uint8_t> &haConnData, InOutPara *paraInOut) { int tempRet; sp<IMemory> memory; TEEC_ContextHidl *outContext = paraInOut->outContextPtr; TEEC_Session *outSession = paraInOut->outSessionPtr; paraInOut->ret = TEEC_FAIL; paraInOut->originRet = TEEC_ORIGIN_API; TEEC_Operation *operation = paraInOut->operationPtr; operation->started = 1; TEEC_SharedMemory shm[PARAM_NUM]; TEEC_SharedMemoryHidl *shmHidl[PARAM_NUM]; (void)memset_s(&shm, sizeof(shm), 0, sizeof(shm)); (void)memset_s(&shmHidl, sizeof(shmHidl), 0x00, sizeof(shmHidl)); int tmpCheckStatus = ((halOptPtr.data() != nullptr) && (halOptPtr.size() == sizeof(TEEC_Operation))); int tmpCheckStatus2 = ((opMem.size() != 0) && (opMem.handle() != nullptr)); if (tmpCheckStatus) { tempRet = GetOperationFromHidlVec(halOptPtr, operation); if (tempRet != TEE_HIDL_SUCCESS) { ALOGE("get oper from hidl vec failed.\n"); return TEE_HIDL_FAILURE; } if (tmpCheckStatus2) { memory = mapMemory(opMem); if (memory == nullptr) { ALOGD("%s: memory is nullptr.\n", __func__); return TEE_HIDL_FAILURE; } uint8_t *data = static_cast<uint8_t *>(static_cast<void *>(memory->getPointer())); memory->update(); size_t memSize = (size_t)memory->getSize(); TEEC_Result ret = DecodeHidlMemory(outContext, operation, shm, shmHidl, PARAM_NUM, data, memSize); if (ret) { paraInOut->ret = ret; ALOGE("open session: decode hidl memory failed\n"); PutAllocShrMem(shmHidl, PARAM_NUM); return TEE_HIDL_FAILURE; } } else { ALOGD("%s: opMem is nullptr or size is 0\n", __func__); } } tempRet = CallOpenSessionProxy(halUuidPtr, taFile, outContext, outSession, paraInOut, haConnData); PutAllocShrMem(shmHidl, PARAM_NUM); if (tempRet != TEE_HIDL_SUCCESS) { ALOGE("%s: callInvokeCommandProxy failed, ret is %x.\n", __func__, tempRet); return TEE_HIDL_FAILURE; } int tmpCheckStatus3 = (memory != nullptr); tmpCheckStatus = (tmpCheckStatus && tmpCheckStatus2 && tmpCheckStatus3); if (tmpCheckStatus) { memory->update(); memory->commit(); } else { ALOGD("%s: memory maybe nullptr, status is %x...\n", __func__, tmpCheckStatus3); ALOGD("%s: opMem maybe nullptr or size maybe 0, status is %x...\n", __func__, tmpCheckStatus2); } return 0; } Return<int32_t> LibteecGlobal::CallOpenSessionProxy(const hidl_vec<uint8_t> &halUuidPtr, const TaFileInfo *taFile, TEEC_ContextHidl *outContext, TEEC_Session *outSession, InOutPara *paraInOut, const hidl_vec<uint8_t> &haConnData) { int ret; int32_t originRet = 0; TidData *tidData = nullptr; uint32_t halConnMth = (uint32_t)paraInOut->cmdId; int pid = paraInOut->pid; TEEC_Operation *operation = (paraInOut->operationPtr); if (openSessionProxy == nullptr) { ALOGE("no openSession in this handle!\n"); return TEE_HIDL_FAILURE; } ret = AddTidData(&tidData, pid); if (ret) { ALOGE("%s: AddTidData failed\n", __func__); return TEE_HIDL_FAILURE; } ret = openSessionProxy(pid, taFile, reinterpret_cast<TEEC_ContextHidl *>(outContext), reinterpret_cast<TEEC_Session *>(outSession), reinterpret_cast<const TEEC_UUID *>(halUuidPtr.data()), halConnMth, reinterpret_cast<const void *>(haConnData.data()), reinterpret_cast<TEEC_Operation *>(operation), reinterpret_cast<uint32_t *>(&originRet)); ALOGD("%s: tid %d return from TEE.\n", __func__, tidData->tid); RemoveTidFromList(tidData); tidData = nullptr; paraInOut->ret = ret; /* return code from teeos or tzdriver, such as TEEC_FAIL */ paraInOut->originRet = originRet; /* return origin code from teeos or tzdriver, such as TEE_ORIGIN_TEE */ if (ret != TEEC_SUCCESS) { ALOGD("open session proxy return failed from TEE: 0x%x \n", ret); return TEE_HIDL_FAILURE; } return TEE_HIDL_SUCCESS; } Return<void> LibteecGlobal::closeSession(int32_t pid, const hidl_vec<uint8_t> &halCxtPtr, const hidl_vec<uint8_t> &halSessPtr) { ALOGD("%s: get Calling Pid=%d", __func__, pid); int tmpCheckStatus = ((halCxtPtr.data() == nullptr) || (halCxtPtr.size() != sizeof(TEEC_Context)) || (!IsValidContext(reinterpret_cast<const TEEC_Context *>(halCxtPtr.data()), pid))); if (tmpCheckStatus) { ALOGE("%s: invalid context!\n", __func__); return Void(); } (void)CheckAndOpenHandle(); if (getBnContextProxy == nullptr || putBnContextProxy == nullptr || putBnSessionProxy == nullptr || findAndRemoveBnSessionProxy == nullptr) { ALOGE("close session: missing proxy in this handle!\n"); return Void(); } TEEC_ContextHidl *outContext = nullptr; outContext = getBnContextProxy(reinterpret_cast<const TEEC_Context *>(halCxtPtr.data())); if (outContext == nullptr) { ALOGE("%s: no context found in hidl service!\n", __func__); return Void(); } TEEC_Session *outSession = nullptr; tmpCheckStatus = ((halSessPtr.data() != nullptr) && (halSessPtr.size() == sizeof(TEEC_Session))); if (!tmpCheckStatus) { ALOGE("receive session struct failed!\n"); putBnContextProxy(outContext); return Void(); } const TEEC_Session *inSession = reinterpret_cast<const TEEC_Session *>(halSessPtr.data()); outSession = findAndRemoveBnSessionProxy(inSession, outContext); if (outSession == nullptr) { ALOGE("%s: no session found in hidl service!\n", __func__); putBnContextProxy(outContext); return Void(); } CallCloseSessionProxy(outSession, outContext, pid); putBnSessionProxy(outSession); /* pair with open session */ putBnContextProxy(outContext); return Void(); } Return<void> LibteecGlobal::CallCloseSessionProxy(TEEC_Session *outSession, TEEC_ContextHidl *outContext, int pid) { TidData *tidData = nullptr; if (closeSessionProxy == nullptr) { ALOGE("no closeSession in this handle!\n"); return Void(); } int ret = AddTidData(&tidData, pid); if (ret) { ALOGE("%s: add_tid_data failed\n", __func__); return Void(); } closeSessionProxy(reinterpret_cast<TEEC_Session *>(outSession), reinterpret_cast<TEEC_ContextHidl *>(outContext)); ALOGD("%s: tid %d return from TEE\n", __func__, tidData->tid); RemoveTidFromList(tidData); tidData = nullptr; return Void(); } Return<void> LibteecGlobal::invokeCommandHidl(int32_t pid, const hidl_vec<uint8_t> &halCxtPtr, const hidl_vec<uint8_t> &halSessPtr, uint32_t cmdId, const hidl_vec<uint8_t> &halOptPtr, const android::hardware::hidl_memory &opMem, invokeCommandHidl_cb hidlCallBackPtr) { int32_t ret = (int32_t)TEEC_FAIL; hidl_vec<uint8_t> sessionOutPtr; hidl_vec<uint8_t> optOutPtr; int32_t originRet = TEEC_ORIGIN_API; TEEC_Operation operation; TEEC_ContextHidl *outContext = nullptr; TEEC_Session *outSession = nullptr; InOutPara paraInOut; /* just for transfer params */ int32_t tempRet; if (hidlCallBackPtr == nullptr) { ALOGE("%s: hidlCallBackPtr is nullptr\n", __func__); return Void(); } (void)CheckAndOpenHandle(); if (putBnSessionProxy == nullptr || putBnContextProxy == nullptr) { ALOGE("invoke command: missing proxy in this handle!\n"); goto INVOKEEND; } ALOGD("%s: getCallingPid=%d", __func__, pid); (void)memset_s(&operation, sizeof(TEEC_Operation), 0x00, sizeof(TEEC_Operation)); /* clear operation */ operation.started = 1; tempRet = CallGetBnProxy(pid, halCxtPtr, halSessPtr, &outContext, &outSession); if (tempRet != 0) { ret = tempRet; goto INVOKEEND; } paraInOut.cmdId = (int32_t)cmdId; paraInOut.pid = pid; paraInOut.operationPtr = &operation; paraInOut.outContextPtr = outContext; paraInOut.outSessionPtr = outSession; tempRet = SubInvokeCommand(halOptPtr, opMem, ¶InOut); /* tempRet=0, meas TEEC_SUCCESS */ if (tempRet == 0) { ret = paraInOut.ret; originRet = paraInOut.originRet; sessionOutPtr.setToExternal(reinterpret_cast<uint8_t *>(outSession), sizeof(TEEC_Session)); optOutPtr.setToExternal(reinterpret_cast<uint8_t *>(&operation), sizeof(TEEC_Operation)); } else { ALOGE("%s: call SubInvokeCommand return, ret is %x.\n", __func__, tempRet); } putBnSessionProxy(outSession); putBnContextProxy(outContext); INVOKEEND: hidlCallBackPtr(ret, sessionOutPtr, optOutPtr, originRet); return Void(); } Return<int32_t> LibteecGlobal::CallGetBnProxy(int pid, const hidl_vec<uint8_t> &halCxtPtr, const hidl_vec<uint8_t> &halSessPtr, TEEC_ContextHidl **outHidlContext, TEEC_Session **outHidlSession) { TEEC_ContextHidl *outContext = nullptr; TEEC_Session *outSession = nullptr; int tmpCheckStatus = ((halCxtPtr.data() == nullptr) || (halCxtPtr.size() != sizeof(TEEC_Context)) || (!IsValidContext(reinterpret_cast<const TEEC_Context *>(halCxtPtr.data()), pid))); if (tmpCheckStatus) { ALOGE("%s: invalid context!\n", __func__); return TEEC_FAIL; } if (getBnContextProxy == nullptr || putBnContextProxy == nullptr || getBnSessionProxy == nullptr) { ALOGE("call get bn proxy: missing proxy in this handle!\n"); return TEEC_FAIL; } outContext = getBnContextProxy(reinterpret_cast<const TEEC_Context *>(halCxtPtr.data())); if (outContext == nullptr) { ALOGE("%s: no context found in hidl service!\n", __func__); return TEEC_ERROR_BAD_PARAMETERS; } tmpCheckStatus = ((halSessPtr.data() != nullptr) && (halSessPtr.size() == sizeof(TEEC_Session))); if (!tmpCheckStatus) { ALOGE("receive session struct failed!\n"); putBnContextProxy(outContext); return TEEC_FAIL; } outSession = getBnSessionProxy(reinterpret_cast<const TEEC_Session *>(halSessPtr.data()), outContext); if (outSession == nullptr) { ALOGE("%s: no session found in hidl service!\n", __func__); putBnContextProxy(outContext); return TEEC_ERROR_BAD_PARAMETERS; } *outHidlContext = outContext; *outHidlSession = outSession; return TEEC_SUCCESS; } Return<int32_t> LibteecGlobal::SubInvokeCommand(const hidl_vec<uint8_t> &halOptPtr, const android::hardware::hidl_memory &opMem, InOutPara *paraInOut) { int tempRet; sp<IMemory> memory; TEEC_Operation *operation = (paraInOut->operationPtr); TEEC_ContextHidl *outContext = paraInOut->outContextPtr; TEEC_Session *outSession = paraInOut->outSessionPtr; TEEC_SharedMemory shm[PARAM_NUM]; TEEC_SharedMemoryHidl *shmHidl[PARAM_NUM]; (void)memset_s(&shm, sizeof(shm), 0x00, sizeof(shm)); (void)memset_s(&shmHidl, sizeof(shmHidl), 0x00, sizeof(shmHidl)); int tmpCheckStatus = ((halOptPtr.data() != nullptr) && (halOptPtr.size() == sizeof(TEEC_Operation))); int tmpCheckStatus2 = ((opMem.size() != 0) && (opMem.handle() != nullptr)); if (tmpCheckStatus) { tempRet = GetOperationFromHidlVec(halOptPtr, operation); if (tempRet != TEE_HIDL_SUCCESS) { ALOGE("get oper from hidl vec failed.\n"); return TEE_HIDL_FAILURE; } if (tmpCheckStatus2) { memory = mapMemory(opMem); if (memory == nullptr) { ALOGD("%s: memory is nullptr\n", __func__); return TEE_HIDL_FAILURE; } uint8_t *dataPtr = static_cast<uint8_t *>(static_cast<void *>(memory->getPointer())); memory->update(); size_t memSize = (size_t)memory->getSize(); TEEC_Result ret = DecodeHidlMemory(outContext, operation, shm, shmHidl, PARAM_NUM, dataPtr, memSize); if (ret) { ALOGE("invoke command: decode hidl memory failed\n"); PutAllocShrMem(shmHidl, PARAM_NUM); return TEE_HIDL_FAILURE; } } else { ALOGD("%s: opMem is nullptr or size is 0\n", __func__); } } tempRet = CallInvokeCommandProxy(outContext, outSession, operation, paraInOut); PutAllocShrMem(shmHidl, PARAM_NUM); if (tempRet != TEE_HIDL_SUCCESS) { ALOGE("%s: callInvokeCommandProxy failed, ret is %x.\n", __func__, tempRet); return TEE_HIDL_FAILURE; } int tmpCheckStatus3 = (memory != nullptr); tmpCheckStatus = (tmpCheckStatus2 && tmpCheckStatus3 && tmpCheckStatus); if (tmpCheckStatus) { memory->update(); memory->commit(); } else { ALOGD("%s: memory maybe nullptr, status is %x.\n", __func__, tmpCheckStatus3); ALOGD("%s: opMem maybe nullptr or size maybe 0, status is %x.\n", __func__, tmpCheckStatus2); } return TEE_HIDL_SUCCESS; } Return<int32_t> LibteecGlobal::CallInvokeCommandProxy(TEEC_ContextHidl *outContext, TEEC_Session *outSession, TEEC_Operation *operation, InOutPara *paraInOut) { int ret; int32_t originRet = 0; TidData *tidData = nullptr; if (invokeCommandProxy == nullptr) { ALOGE("no invoke command in this handle!\n"); return TEE_HIDL_FAILURE; } ret = AddTidData(&tidData, paraInOut->pid); if (ret) { ALOGE("%s: AddTidData failed\n", __func__); return TEE_HIDL_FAILURE; } ret = invokeCommandProxy(reinterpret_cast<TEEC_ContextHidl *>(outContext), reinterpret_cast<TEEC_Session *>(outSession), paraInOut->cmdId, reinterpret_cast<TEEC_Operation *>(operation), reinterpret_cast<uint32_t *>(&originRet)); ALOGD("%s: tid %d return from TEE\n", __func__, tidData->tid); RemoveTidFromList(tidData); tidData = nullptr; paraInOut->ret = ret; /* return code from teeos or tzdriver, such as TEEC_FAIL */ paraInOut->originRet = originRet; /* return origin code from teeos or tzdriver, such as TEE_ORIGIN_TEE */ return TEE_HIDL_SUCCESS; } Return<void> LibteecGlobal::registerSharedMemory(int32_t pid, const hidl_vec<uint8_t> &halCxtPtr, const hidl_vec<uint8_t> &halMemPtr, registerSharedMemory_cb hidlCallBackPtr) { int32_t ret = (int32_t)TEEC_FAIL; hidl_vec<uint8_t> outShmPtr; if (hidlCallBackPtr == nullptr) { ALOGE("%s: hidlCallBackPtr is nullptr\n", __func__); return Void(); } int tmpCheckStatus = ((halCxtPtr.data() == nullptr) || (halCxtPtr.size() != sizeof(TEEC_Context)) || (!IsValidContext(reinterpret_cast<const TEEC_Context *>(halCxtPtr.data()), pid))); if (tmpCheckStatus) { ALOGE("%s: invalid context!\n", __func__); hidlCallBackPtr(ret, outShmPtr); return Void(); } (void)CheckAndOpenHandle(); if (getBnContextProxy == nullptr || putBnContextProxy == nullptr) { ALOGE("missing proxy in this handle!\n"); hidlCallBackPtr(ret, outShmPtr); return Void(); } TEEC_ContextHidl *outContext = nullptr; outContext = getBnContextProxy(reinterpret_cast<const TEEC_Context *>(halCxtPtr.data())); if (outContext == nullptr) { ALOGE("%s: no context found in hidl service!\n", __func__); ret = TEEC_ERROR_BAD_PARAMETERS; hidlCallBackPtr(ret, outShmPtr); return Void(); } TEEC_SharedMemoryHidl outShm; (void)memset_s((void *)(&outShm), sizeof(TEEC_SharedMemoryHidl), 0, sizeof(TEEC_SharedMemoryHidl)); ret = CallRegisterSharedMemoryProxy(halMemPtr, outContext, &outShm); if (ret) { ALOGE("%s: callRegisterSharedMemoryProxy failed, ret is %x.\n", __func__, ret); putBnContextProxy(outContext); hidlCallBackPtr(ret, outShmPtr); return Void(); } putBnContextProxy(outContext); outShmPtr.setToExternal(reinterpret_cast<uint8_t *>(&outShm), sizeof(TEEC_SharedMemoryHidl)); hidlCallBackPtr(ret, outShmPtr); return Void(); } Return<int32_t> LibteecGlobal::CallRegisterSharedMemoryProxy(const hidl_vec<uint8_t> &halMemPtr, TEEC_ContextHidl *outContext, TEEC_SharedMemoryHidl *outRegShm) { int32_t ret; TEEC_SharedMemoryHidl *outShm = nullptr; if (registerSharedMemoryProxy == nullptr || putBnShmProxy == nullptr) { ALOGE("call register shrmem proxy: missing proxy in this handle!\n"); return TEEC_FAIL; } outShm = reinterpret_cast<TEEC_SharedMemoryHidl *>(malloc(sizeof(TEEC_SharedMemoryHidl))); if (outShm == nullptr) { ALOGE("%s: outShm malloc failed.\n", __func__); return TEEC_FAIL; } (void)memset_s(outShm, sizeof(TEEC_SharedMemoryHidl), 0x00, sizeof(TEEC_SharedMemoryHidl)); int tmpCheckStatus = ((halMemPtr.data() != nullptr) && (halMemPtr.size() == sizeof(TEEC_SharedMemory))); if (tmpCheckStatus) { if (memcpy_s(outShm, sizeof(TEEC_SharedMemoryHidl), halMemPtr.data(), sizeof(TEEC_SharedMemory))) { ALOGE("%s: memcpy failed when copy data to shm[]!\n", __func__); free(outShm); return TEEC_FAIL; } } ret = registerSharedMemoryProxy(reinterpret_cast<TEEC_ContextHidl *>(outContext), reinterpret_cast<TEEC_SharedMemoryHidl *>(outShm)); if (ret) { free(outShm); return ret; } outRegShm->ops_cnt = outShm->ops_cnt; outRegShm->is_allocated = outShm->is_allocated; outRegShm->offset = outShm->offset; putBnShmProxy(outShm); /* pair with ops_cnt++ when add to list */ return ret; /* maybe ok, maybe failed */ } Return<void> LibteecGlobal::allocateSharedMemory(int32_t pid, const hidl_vec<uint8_t> &halCxtPtr, const hidl_vec<uint8_t> &halMemPtr, allocateSharedMemory_cb hidlCallBackPtr) { int32_t ret = (int32_t)TEEC_ERROR_BAD_PARAMETERS; hidl_vec<uint8_t> outShmPtr; android::hardware::hidl_handle handle; int tempFd = -1; TEEC_ContextHidl *outContext = nullptr; if (hidlCallBackPtr == nullptr) { ALOGE("%s: hidlCallBackPtr is nullptr\n", __func__); return Void(); } (void)CheckAndOpenHandle(); if (putBnContextProxy == nullptr) { ALOGE("allocate sharemem : missing proxy in this handle!!!\n"); ret = TEEC_FAIL; hidlCallBackPtr(ret, outShmPtr, handle); return Void(); } ret = GetSharedMemoryContextAndFd(halCxtPtr, pid, &outContext, &tempFd); if (ret != TEEC_SUCCESS) { ALOGE("get shm contex failed , or get fd failed when Ashm\n"); hidlCallBackPtr(ret, outShmPtr, handle); return Void(); } native_handle_t *nativeHandle = native_handle_create(1, 0); /* 2params: means: nFds & nInts */ if (nativeHandle == nullptr) { ALOGE("create nativehandle failed when Ashm\n"); ret = TEEC_FAIL; close(tempFd); goto ALLOCEND; } nativeHandle->data[0] = tempFd; TEEC_SharedMemoryHidl outShm; (void)memset_s((void *)(&outShm), sizeof(TEEC_SharedMemoryHidl), 0, sizeof(TEEC_SharedMemoryHidl)); ret = CallAllocateSharedMemoryProxy(halMemPtr, outContext, &outShm); if (ret) { ALOGE("%s: callAllocateSharedMemoryProxy failed, ret is %x.\n", __func__, ret); native_handle_close(nativeHandle); native_handle_delete(nativeHandle); goto ALLOCEND; } /* 2nd param: true: means take ownership */ handle.setTo(nativeHandle, true); outShmPtr.setToExternal(reinterpret_cast<uint8_t *>(&outShm), sizeof(TEEC_SharedMemoryHidl)); ALLOCEND: hidlCallBackPtr(ret, outShmPtr, handle); putBnContextProxy(outContext); return Void(); } Return<int32_t> LibteecGlobal::GetSharedMemoryContextAndFd(const hidl_vec<uint8_t> &halCxtPtr, int32_t pid, TEEC_ContextHidl **outContext, int *fd) { TEEC_ContextHidl *shmContext = nullptr; int tmpCheckStatus = (getBnContextProxy == nullptr || putBnContextProxy == nullptr); if (tmpCheckStatus) { ALOGE("allocate sharemem : missing proxy in this handle!!!\n"); return TEEC_FAIL; } tmpCheckStatus = ((halCxtPtr.data() == nullptr) || (halCxtPtr.size() != sizeof(TEEC_Context)) || (!IsValidContext(reinterpret_cast<const TEEC_Context *>(halCxtPtr.data()), pid))); if (tmpCheckStatus) { ALOGE("%s: invalid context!\n", __func__); return TEEC_FAIL; } shmContext = getBnContextProxy(reinterpret_cast<const TEEC_Context *>(halCxtPtr.data())); if (shmContext == nullptr) { ALOGE("%s: no context found in hidl service!!!\n", __func__); return TEEC_ERROR_BAD_PARAMETERS; } *fd = dup(shmContext->fd); if (*fd < 0) { ALOGE("get fd failed when Ashm \n"); putBnContextProxy(shmContext); return TEEC_FAIL; } *outContext = shmContext; return TEEC_SUCCESS; } Return<int32_t> LibteecGlobal::CallAllocateSharedMemoryProxy(const hidl_vec<uint8_t> &halMemPtr, TEEC_ContextHidl *outContext, TEEC_SharedMemoryHidl *outAllocShm) { TEEC_SharedMemoryHidl *outShm = nullptr; int checkStat = (allocateSharedMemoryProxy == nullptr || putBnShmProxy == nullptr); if (checkStat) { ALOGE("call allocate sharemem proxy:missing proxy in this handle!!!\n"); return TEEC_FAIL; } outShm = reinterpret_cast<TEEC_SharedMemoryHidl *>(malloc(sizeof(TEEC_SharedMemoryHidl))); if (outShm == nullptr) { ALOGE("%s: outShm malloc failed\n", __func__); return TEEC_FAIL; } (void)memset_s(outShm, sizeof(TEEC_SharedMemoryHidl), 0x00, sizeof(TEEC_SharedMemoryHidl)); int tmpCheckStatus = ((halMemPtr.data() != nullptr) && (halMemPtr.size() == sizeof(TEEC_SharedMemory))); if (tmpCheckStatus) { if (memcpy_s(outShm, sizeof(TEEC_SharedMemoryHidl), halMemPtr.data(), sizeof(TEEC_SharedMemory))) { ALOGE("%s: memcpy failed when copy data to shm[]\n", __func__); free(outShm); return TEEC_FAIL; } } int32_t ret = allocateSharedMemoryProxy(reinterpret_cast<TEEC_ContextHidl *>(outContext), reinterpret_cast<TEEC_SharedMemoryHidl *>(outShm)); if (ret) { ALOGE("%s: allocate shared memory failed\n", __func__); free(outShm); return ret; } outAllocShm->ops_cnt = outShm->ops_cnt; outAllocShm->is_allocated = outShm->is_allocated; outAllocShm->offset = outShm->offset; putBnShmProxy(outShm); /* pair with ops_cnt++ when add to list */ return ret; /* maybe ok, maybe failed */ } Return<void> LibteecGlobal::releaseSharedMemory(int32_t pid, const hidl_vec<uint8_t> &halCxtPtr, const hidl_vec<uint8_t> &halMemPtr, uint32_t shmOffset) { int tmpCheckStatus = ((halCxtPtr.data() == nullptr) || (halCxtPtr.size() != sizeof(TEEC_Context)) || (!IsValidContext(reinterpret_cast<const TEEC_Context *>(halCxtPtr.data()), pid))); if (tmpCheckStatus) { ALOGE("%s: invalid context!\n", __func__); return Void(); } (void)CheckAndOpenHandle(); TEEC_SharedMemoryHidl outShm; tmpCheckStatus = ((halMemPtr.data() != nullptr) && (halMemPtr.size() == sizeof(TEEC_SharedMemory))); if (tmpCheckStatus) { if (memcpy_s(&outShm, sizeof(TEEC_SharedMemoryHidl), halMemPtr.data(), sizeof(TEEC_SharedMemory))) { ALOGE("%s: memcpy failed when copy data to outShm \n", __func__); return Void(); } outShm.offset = shmOffset; } CallReleaseSharedMemoryProxy(halCxtPtr, &outShm); return Void(); } Return<void> LibteecGlobal::CallReleaseSharedMemoryProxy(const hidl_vec<uint8_t> &halCxtPtr, TEEC_SharedMemoryHidl *outShm) { if (getBnContextProxy == nullptr || putBnContextProxy == nullptr || releaseSharedMemoryProxy == nullptr) { ALOGE("call release sharemem proxy:missing proxy in this handle!!!\n"); return Void(); } TEEC_ContextHidl *outContext = nullptr; outContext = getBnContextProxy(reinterpret_cast<const TEEC_Context *>(halCxtPtr.data())); if (outContext == nullptr) { ALOGE("%s: no context found in hidl service!\n", __func__); return Void(); } outShm->context = outContext; releaseSharedMemoryProxy(outShm); putBnContextProxy(outContext); return Void(); } Return<void> LibteecGlobal::requestCancellation(const hidl_vec<uint8_t> &halOptPtr) { ALOGI("%s: hidl requestCancellation not support\n", __func__); (void)halOptPtr; return Void(); } Return<int32_t> LibteecGlobal::extTuiSendEvent(int32_t pid, int32_t uid, const hidl_vec<uint8_t> &halTuiPtr) { int32_t ret = (int32_t)TEEC_ERROR_BAD_PARAMETERS; (void)CheckAndOpenHandle(); if (extTuiSendEventProxy == nullptr) { ALOGE("no TEEC_EXT_TuiSendEvent in this handle!\n"); return TEEC_FAIL; } CaAuthInfo *caAuth = reinterpret_cast<CaAuthInfo *>(malloc(sizeof(CaAuthInfo))); if (caAuth == nullptr) { ALOGE("%s: malloc ca auth failed\n", __func__); return TEEC_FAIL; } caAuth->uid = (uid_t)uid; caAuth->pid = pid; caAuth->type = SYSTEM_CA; caAuth->fromHidlSide = HIDL_SIDE; int tmpCheckStatus = ((halTuiPtr.data() != nullptr) && (halTuiPtr.size() == sizeof(TEEC_TUI_Parameter))); if (!tmpCheckStatus) { ALOGE("no TEEC_EXT_TuiSendEvent for this handle!\n"); goto freeBuffer; } ret = extTuiSendEventProxy(reinterpret_cast<const TEEC_TUI_Parameter *>(halTuiPtr.data()), caAuth); freeBuffer: free(caAuth); return ret; } Return<void> LibteecGlobal::setCallBack(const ::android::sp<ILibteecGlobalNotify> ¬ify) { if (notify == nullptr) { ALOGE("%s: Notify is NULL\n", __func__); return Void(); } g_teecNotify = notify; int pid = android::hardware::IPCThreadState::self()->getCallingPid(); int result = g_teecNotify->linkToDeath(this, pid); if (!result) { ALOGE("%s: linkToDeath failed %d\n", __func__, result); } ALOGW("%s: receive notify from teec service pid=%d\n", __func__, pid); return Void(); } Return<int32_t> LibteecGlobal::extSendSystemHashXml(const hidl_vec<uint8_t> &halXmlPtr, const hidl_vec<uint8_t> &authInfo) { int32_t ret = (int32_t)TEEC_ERROR_BAD_PARAMETERS; (void)CheckAndOpenHandle(); if (extSendSysHashXmlProxy == nullptr) { ALOGE("no SendSysHashXml for this handle!\n"); return TEEC_FAIL; } int tmpCheckStatus = ((authInfo.data() != nullptr) && (authInfo.size() == sizeof(CaAuthInfo))); if (!tmpCheckStatus) { ALOGE("%s: authInfo is nullptr or size is 0.\n", __func__); return ret; } CaAuthInfo *caAuth = reinterpret_cast<CaAuthInfo *>(malloc(sizeof(CaAuthInfo))); if (caAuth == nullptr) { ALOGE("%s: malloc ca auth failed\n", __func__); return TEEC_ERROR_OUT_OF_MEMORY; } if (memcpy_s(caAuth, sizeof(CaAuthInfo), authInfo.data(), sizeof(CaAuthInfo))) { ALOGE("%s: memcpy failed.\n", __func__); free(caAuth); return ret; } if ((halXmlPtr.data() == nullptr) || (halXmlPtr.size() != sizeof(TEEC_XmlParameter))) { ALOGE("no TEEC_EXT_Set_Native_CAHash for this handle!\n"); free(caAuth); return ret; } ret = (int32_t)extSendSysHashXmlProxy(reinterpret_cast<const TEEC_XmlParameter *>(halXmlPtr.data()), caAuth); free(caAuth); return ret; } Return<int32_t> LibteecGlobal::secfileSendEvent(int32_t pid, const android::hardware::hidl_handle &handle, const hidl_string &libPath, const hidl_vec<uint8_t> &halCxtPtr, const hidl_vec<uint8_t> &halSessPtr) { TEEC_ContextHidl *outContext = nullptr; FILE *fp = nullptr; (void)halSessPtr; (void)CheckAndOpenHandle(); if (sendSecfileProxy == nullptr || getBnContextProxy == nullptr) { ALOGE("missing proxy in this handle!\n"); return TEEC_FAIL; } int tmpCheckStatus = ((halCxtPtr.data() == nullptr) || (halCxtPtr.size() != sizeof(TEEC_Context)) || (!IsValidContext(reinterpret_cast<const TEEC_Context *>(halCxtPtr.data()), pid))); if (tmpCheckStatus) { ALOGE("invalid context!\n"); return TEEC_FAIL; } outContext = getBnContextProxy(reinterpret_cast<const TEEC_Context *>(halCxtPtr.data())); if (outContext == nullptr) { ALOGE("no context found in hidl service!\n"); return TEEC_FAIL; } native_handle_t *tempHandle = nullptr; fp = GetFpFromHandle(handle, tempHandle); int32_t ret = sendSecfileProxy(reinterpret_cast<const char *>(libPath.c_str()), outContext->fd, fp); putBnContextProxy(outContext); if (fp != nullptr) { fclose(fp); fp = nullptr; } native_handle_delete(tempHandle); return ret; } void LibteecGlobal::serviceDied(uint64_t cookie, const ::android::wp<::android::hidl::base::V1_0::IBase> &who) { (void)who; ALOGW("%s: teec service died pid=%d\n", __func__, static_cast<int>(cookie)); Mutex::Autolock _l(mProcDataLock); if (!list_empty(&g_teecHidlProcDataList)) { struct listnode *handNode = nullptr; struct listnode *tempNode = nullptr; list_for_each_safe(handNode, tempNode, &g_teecHidlProcDataList) { DaemonProcdata *handProcdata = node_to_item(handNode, DaemonProcdata, procdataHead); ALOGW("%s: cleaning pid=%d", __func__, handProcdata->callingPid); SendSigToTzdriver(handProcdata->callingPid); CleanProcDataForOneCa(handProcdata); list_remove(&handProcdata->procdataHead); free(handProcdata); handProcdata = nullptr; } } ALOGW("%s: clean done", __func__); return; } Return<uint32_t> LibteecGlobal::iGetTEEVersionHidl() { uint32_t version; (void)CheckAndOpenHandle(); if (getTEEVersionProxy == nullptr) { ALOGE("no TEEC_GetTEEVersionHidl for this handle!\n"); return 0; } version = getTEEVersionProxy(); if (version == 0) { ALOGE("fail to get tee version\n"); } return version; } Return<void> LibteecGlobal::processCaDied(int32_t pid) { ALOGW("%s: getCallingPid=%d\n", __func__, pid); DaemonProcdata *outProcData = NULL; { Mutex::Autolock _l(mProcDataLock); outProcData = GetProcdataByPid(pid); if (outProcData == nullptr) { ALOGW("%s: outProcdata[%d] not in the list\n", __func__, pid); return Void(); } list_remove(&outProcData->procdataHead); } SendSigToTzdriver(pid); CleanProcDataForOneCa(outProcData); free(outProcData); ALOGW("%s: clean done", __func__); return Void(); } Return<int32_t> LibteecGlobal::CheckInputPidUid(int pid, uid_t uid) { char *context = nullptr; if (uid != AID_ROOT) { ALOGE("invalid access client(uid = %u pid = %d) will be rejected\n", uid, pid); return TEEC_FAIL; } if (::getpidcon(pid, &context) < 0) { ALOGE("SELinux: getpidcon(pid=%d) failed to retrieve pid context.\n", pid); return TEEC_FAIL; } if (strcmp(context, "u:r:system_teecd:s0")) { ALOGE("invalid access client(uid = %u pid = %d SELinux %s) will be rejected\n", uid, pid, context); ::freecon(context); return TEEC_FAIL; } ::freecon(context); return TEEC_SUCCESS; } Return<int32_t> LibteecGlobal::handleMultiUserMsg(const hidl_vec<uint8_t> &multiUserRecvMsg) { int pid = android::hardware::IPCThreadState::self()->getCallingPid(); uid_t uid = android::hardware::IPCThreadState::self()->getCallingUid(); if (CheckInputPidUid(pid, uid)) { ALOGE("%s: checkInputPidUid failed!!\n", __func__); return TEE_HIDL_FAILURE; } int tmpCheckStatus = ((multiUserRecvMsg.data() == nullptr) || (multiUserRecvMsg.size() != sizeof(RecvMsg))); if (tmpCheckStatus) { return TEE_HIDL_FAILURE; } const RecvMsg *multiUserMsg = reinterpret_cast<const RecvMsg *>(multiUserRecvMsg.data()); uint32_t magic = (unsigned int)multiUserMsg->magic; uint32_t status = (unsigned int)multiUserMsg->status; int userid = (int)multiUserMsg->userid; tmpCheckStatus = (userid > MU_MSG_USERID_OWNER && userid < MU_MSG_USERID_MAX); /* check magic, and then status, and userid validate value */ if (magic != MU_MSG_MAGIC) { /* do nothing */ ALOGE("cmd no define to handle\n\n"); return TEE_HIDL_FAILURE; } if (status == MU_MSG_STAT_NEW_USER) { return MakeDirAndLinkUser(userid, status, tmpCheckStatus); } else if (status == MU_MSG_STAT_RM_USER) { return UnlinkUser(userid, status, tmpCheckStatus); } else if (status == MU_MSG_STAT_SWITCH_USER) { /* switch user */ ALOGE("switch user cmd do not handle now, status %x.\n", status); return TEE_HIDL_SUCCESS; } /* not in all conditions we can handle, do nothing */ ALOGE("cmd no define to handle, status is %x.\n", status); return TEE_HIDL_FAILURE; } static bool CheckSizeStatus(uint32_t shmInfoOffset, uint32_t refSize, uint32_t totalSize, uint32_t memSize) { return ((shmInfoOffset + refSize < shmInfoOffset) || (shmInfoOffset + refSize < refSize) || (shmInfoOffset + refSize > totalSize) || (refSize > memSize)); } Return<TEEC_Result> LibteecGlobal::DecodeHidlMemory(TEEC_ContextHidl *outContext, TEEC_Operation *operation, TEEC_SharedMemory *shm, TEEC_SharedMemoryHidl *shmHidl[], uint32_t shmNum, uint8_t *data, size_t memSize) { uint32_t paramType[PARAM_NUM]; uint32_t shmInfoOffset = 0; uint32_t totalHidlMemSize = memSize; TEEC_Operation *tmpOperation = operation; if (shmNum != PARAM_NUM) { return TEEC_FAIL; } for (uint32_t paramCnt = 0; paramCnt < PARAM_NUM; paramCnt++) { paramType[paramCnt] = TEEC_PARAM_TYPE_GET(tmpOperation->paramTypes, paramCnt); bool tmpCheckStatus1 = ((paramType[paramCnt] == TEEC_MEMREF_TEMP_INPUT) || (paramType[paramCnt] == TEEC_MEMREF_TEMP_OUTPUT) || (paramType[paramCnt] == TEEC_MEMREF_TEMP_INOUT)); bool tmpCheckStatus2 = ((paramType[paramCnt] == TEEC_MEMREF_WHOLE) || (paramType[paramCnt] == TEEC_MEMREF_PARTIAL_INPUT) || (paramType[paramCnt] == TEEC_MEMREF_PARTIAL_OUTPUT) || (paramType[paramCnt] == TEEC_MEMREF_PARTIAL_INOUT)); if (tmpCheckStatus1) { uint32_t refSize = tmpOperation->params[paramCnt].tmpref.size; tmpCheckStatus1 = CheckSizeStatus(shmInfoOffset, refSize, totalHidlMemSize, memSize); if (tmpCheckStatus1) { ALOGE("%s: temp mem:%x greater than hidl mem:%x:%x:%x\n", __func__, refSize, shmInfoOffset, (uint32_t)memSize, totalHidlMemSize); return TEEC_FAIL; } tmpOperation->params[paramCnt].tmpref.buffer = data + shmInfoOffset; memSize -= refSize; shmInfoOffset += refSize; } else if (tmpCheckStatus2) { InputPara tmpInputPara; tmpInputPara.paraType = paramType[paramCnt]; tmpInputPara.offset = shmInfoOffset; tmpInputPara.memSize = memSize; tmpInputPara.totalSize = totalHidlMemSize; TEEC_Result retCode = FillShareMemoryBuffer(&(shm[paramCnt]), &(shmHidl[paramCnt]), data, &tmpInputPara, outContext, &(tmpOperation->params[paramCnt])); if (retCode) { return retCode; } shmInfoOffset = tmpInputPara.offset; memSize = tmpInputPara.memSize; } } return TEEC_SUCCESS; } #define STRUCT_SIZE (4 * (sizeof(uint32_t)) + 1 * (sizeof(bool))) Return<TEEC_Result> LibteecGlobal::FillShareMemoryBuffer(TEEC_SharedMemory *shareMemBuf, TEEC_SharedMemoryHidl **shmHidl, uint8_t *data, InputPara *inputPara, TEEC_ContextHidl *outContext, TEEC_Parameter *params) { uint32_t shmInfoOffset = inputPara->offset; uint32_t memSize = inputPara->memSize; uint32_t shmOffset = 0; uint32_t refSize = STRUCT_SIZE; if (CheckSizeStatus(shmInfoOffset, refSize, inputPara->totalSize, memSize)) { goto FILLBUFFEREND; } CopyToShareMemory(shareMemBuf, data, shmInfoOffset, &shmOffset); memSize -= refSize; shmInfoOffset += refSize; refSize = params->memref.size; if (inputPara->paraType == TEEC_MEMREF_WHOLE) refSize = shareMemBuf->size; if (CheckSizeStatus(shmInfoOffset, refSize, inputPara->totalSize, memSize)) { goto FILLBUFFEREND; } if (!shareMemBuf->is_allocated) { shareMemBuf->buffer = data + shmInfoOffset; params->memref.offset = 0; } else { if (getBnShmByOffsetProxy == nullptr) { ALOGE("no GetBnShmAddr in this handle!\n"); return TEEC_FAIL; } TEEC_SharedMemoryHidl *shmTemp = getBnShmByOffsetProxy(shmOffset, outContext); if (shmTemp == nullptr || shmTemp->buffer == nullptr) { ALOGE("%s: no shm buffer found in hidl service!\n", __func__); return TEEC_ERROR_BAD_PARAMETERS; } shareMemBuf->buffer = shmTemp->buffer; *shmHidl = shmTemp; } params->memref.parent = shareMemBuf; memSize -= refSize; shmInfoOffset += refSize; inputPara->offset = shmInfoOffset; inputPara->memSize = memSize; return TEEC_SUCCESS; FILLBUFFEREND: ALOGE("%s: partial mem:%x greater than hidl mem:%x:%x:%x\n", __func__, refSize, shmInfoOffset, memSize, inputPara->totalSize); return TEEC_FAIL; } void LibteecGlobal::PutAllocShrMem(TEEC_SharedMemoryHidl *shmHidl[], uint32_t shmNum) { if (putBnShmProxy == nullptr) { ALOGE("no PutBnShm in this handle!\n"); return; } uint32_t paramCnt; for (paramCnt = 0; paramCnt < shmNum; paramCnt++) { if (shmHidl[paramCnt] != nullptr) { putBnShmProxy(shmHidl[paramCnt]); } } return; } Return<FILE *> LibteecGlobal::GetFpFromHandle(const android::hardware::hidl_handle &handle, native_handle_t *&tempHandle) const { FILE *fp = nullptr; if (handle == nullptr || handle.getNativeHandle() == nullptr) { return fp; } tempHandle = native_handle_clone(handle); if (tempHandle == nullptr) { ALOGE("get fp failed caused by native handle clone failed!\n"); return fp; } int flag = tempHandle->data[1]; if (flag == 1) { int fd = tempHandle->data[0]; fp = fdopen(fd, "r"); } return fp; } Return<void> LibteecGlobal::CleanProcDataForOneCa(DaemonProcdata *procData) { if ((findAndRemoveBnContextProxy == nullptr) || (putBnContextProxy == nullptr)) { ALOGE("missing proxy in this handle!\n"); return Void(); } for (int i = 0; i < MAX_CXTCNT_ONECA; i++) { if (procData->cxtFd[i] == -1) { continue; } TEEC_Context context; context.fd = procData->cxtFd[i]; procData->cxtFd[i] = -1; TEEC_ContextHidl *outContext = findAndRemoveBnContextProxy(&context); if (outContext == nullptr) { ALOGE("%s: no context found in hidl service!\n", __func__); continue; } putBnContextProxy(outContext); /* pair with initialize context */ } return Void(); } Return<int32_t> LibteecGlobal::GetOperationFromHidlVec(const hidl_vec<uint8_t> &halOptPtr, TEEC_Operation *operation) { uint32_t paramCnt; uint32_t paramType; bool tempType = false; bool partialType = false; if (memcpy_s(operation, sizeof(TEEC_Operation), halOptPtr.data(), sizeof(TEEC_Operation))) { ALOGE("memcpy failed...\n"); return TEE_HIDL_FAILURE; } /* clear the pointer from ca to avoid access to invalid address */ operation->session = nullptr; for (paramCnt = 0; paramCnt < PARAM_NUM; paramCnt++) { paramType = TEEC_PARAM_TYPE_GET(operation->paramTypes, paramCnt); tempType = ((paramType == TEEC_MEMREF_TEMP_INPUT) || (paramType == TEEC_MEMREF_TEMP_OUTPUT) || (paramType == TEEC_MEMREF_TEMP_INOUT)); partialType = ((paramType == TEEC_MEMREF_WHOLE) || (paramType == TEEC_MEMREF_PARTIAL_INPUT) || (paramType == TEEC_MEMREF_PARTIAL_OUTPUT) || (paramType == TEEC_MEMREF_PARTIAL_INOUT)); if (tempType) { operation->params[paramCnt].tmpref.buffer = nullptr; } else if (partialType) { operation->params[paramCnt].memref.parent = nullptr; } } return TEE_HIDL_SUCCESS; } /**************************************************************************/ /* fetch method */ /**************************************************************************/ using::vendor::huanglong::hardware::libteec::V3_0::ILibteecGlobal; ILibteecGlobal *HIDL_FETCH_ILibteecGlobal(const char *) { ALOGI("------------------ ILibteecGlobal ------------------\n"); LibteecGlobal *libteecGp = new (std::nothrow) LibteecGlobal(); if (libteecGp == nullptr) { ALOGE("cannot allocate for libteecGp! \n"); } /* ignore SIGPIPE(when teecd is killed, libteec hidl will not restart). */ signal(SIGPIPE, SIG_IGN); return libteecGp; } } // namespace implementation } // namespace V3_0 } // namespace libteec } // namespace hardware } // namespace huanglong } // namespace vendor