| /** |
| * Copyright(c) 2011 Trusted Logic. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name Trusted Logic nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "tee_client_api.h" |
| #include "schannel6_protocol.h" |
| #include "s_error.h" |
| #include "s_version.h" |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <ctype.h> |
| #include <string.h> |
| #include <stdarg.h> |
| #include <assert.h> |
| |
| #include <sys/ioctl.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/mman.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <linux/limits.h> |
| #include <time.h> |
| #include <sys/time.h> |
| |
| /* |
| * SCX_VERSION_INFORMATION_BUFFER structure description |
| * Description of the sVersionBuffer handed over from user space to kernel space |
| * This field is filled after an IOCTL call and handed back to user space |
| */ |
| typedef struct |
| { |
| uint8_t sDriverDescription[65]; |
| uint8_t sSecureWorldDescription[65]; |
| } SCX_VERSION_INFORMATION_BUFFER; |
| |
| |
| /* The IOCTLs to the driver */ |
| #define IOCTL_SCX_GET_VERSION \ |
| _IO('z', 0) |
| |
| #define IOCTL_SCX_EXCHANGE \ |
| _IOWR('z', 1, SCHANNEL6_COMMAND) |
| |
| #define IOCTL_SCX_GET_DESCRIPTION \ |
| _IOR('z', 2, SCX_VERSION_INFORMATION_BUFFER) |
| |
| |
| /* Expected driver interface version. */ |
| #define SM_DRIVER_VERSION 0x04000000 |
| |
| #define SCX_DEFAULT_DEVICE_NAME "tf_driver" |
| |
| #define SCX_PARAM_TYPE_GET(nParamTypes, i) (((nParamTypes) >> (4*i)) & 0xF) |
| |
| #define VAR_NOT_USED(variable) do{(void)(variable);}while(0); |
| |
| #define SIZE_4KB 0x1000 |
| #define SIZE_1MB 0x100000 |
| |
| /* ------------------------------------------------------------------------ */ |
| /* UTILS */ |
| /* ------------------------------------------------------------------------ */ |
| #ifdef NDEBUG |
| /* Compile-out the traces */ |
| #define TRACE_ERROR(...) |
| #define TRACE_WARNING(...) |
| #define TRACE_INFO(...) |
| #else |
| #if defined ANDROID |
| #define LOG_TAG "TEE" |
| #include <android/log.h> |
| #define TRACE_INFO(format, ...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, format, __VA_ARGS__) |
| #define TRACE_ERROR(format, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, format, __VA_ARGS__) |
| #define TRACE_WARNING(format, ...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, format, __VA_ARGS__) |
| #else |
| static void TRACE_ERROR(const char* format, ...) |
| { |
| va_list ap; |
| va_start(ap, format); |
| fprintf(stderr, "TRACE: ERROR: "); |
| vfprintf(stderr, format, ap); |
| fprintf(stderr, "\n"); |
| va_end(ap); |
| } |
| |
| static void TRACE_WARNING(const char* format, ...) |
| { |
| va_list ap; |
| va_start(ap, format); |
| fprintf(stderr, "TRACE: WARNING: "); |
| vfprintf(stderr, format, ap); |
| fprintf(stderr, "\n"); |
| va_end(ap); |
| } |
| |
| static void TRACE_INFO(const char* format, ...) |
| { |
| va_list ap; |
| va_start(ap, format); |
| fprintf(stderr, "TRACE: "); |
| vfprintf(stderr, format, ap); |
| fprintf(stderr, "\n"); |
| va_end(ap); |
| } |
| #endif /* ANDROID */ |
| #endif /* NDEBUG */ |
| |
| |
| /* |
| * ==================================================== |
| * Internal functions |
| * ===================================================== |
| */ |
| |
| static void scxYield(void) |
| { |
| sleep(0); |
| } |
| |
| /* ------------------------------------------------------------------------ */ |
| |
| |
| /* |
| * Exchange a message with the Secure World |
| * by calling the ioctl command of the linux driver |
| * |
| * @param pContext |
| * @param pCommand a SChannel command message that must have been filled except for the operation parameters |
| * @param pAnswer a placeholder for the SChannel answer |
| * @param pOperation a TEEC_Operation structure that contains the operation parameters (and types) |
| * and is updated with the SChannel answer data as appropriate. This parameter is |
| * used only for the open and invoke operations |
| */ |
| static TEEC_Result scxExchangeMessage( |
| IN TEEC_Context* pContext, |
| IN SCHANNEL6_COMMAND* pCommand, |
| OUT SCHANNEL6_ANSWER* pAnswer, |
| IN TEEC_Operation* pOperation) |
| { |
| TEEC_Result nResult = TEEC_SUCCESS; |
| |
| TRACE_INFO("scxExchangeMessage[0x%X]\n",pContext); |
| |
| if (pOperation != NULL) |
| { |
| /* Determine message parameters from operation parameters */ |
| uint32_t i; |
| SCHANNEL6_COMMAND_PARAM* pSCXParams; |
| |
| /* Note that nParamType is at the same position in an open and an invoke message */ |
| pCommand->sHeader.nMessageInfo = pOperation->paramTypes; |
| |
| if (pCommand->sHeader.nMessageType == SCX_OPEN_CLIENT_SESSION) |
| { |
| pSCXParams = pCommand->sOpenClientSession.sParams; |
| } |
| else |
| { |
| /* An invoke-command */ |
| pSCXParams = pCommand->sInvokeClientCommand.sParams; |
| } |
| |
| for (i = 0; i < 4; i++) |
| { |
| uint32_t nTEECParamType = SCX_PARAM_TYPE_GET(pOperation->paramTypes, i); |
| TEEC_Parameter* pTEECParam = &pOperation->params[i]; |
| SCHANNEL6_COMMAND_PARAM* pSCXParam = &pSCXParams[i]; |
| |
| if (nTEECParamType & SCX_PARAM_TYPE_MEMREF_FLAG) |
| { |
| if (nTEECParamType & SCX_PARAM_TYPE_REGISTERED_MEMREF_FLAG) |
| { |
| /* A registered memref */ |
| pSCXParam->sMemref.hBlock = pTEECParam->memref.parent->imp._hBlock; |
| if (nTEECParamType == TEEC_MEMREF_WHOLE) |
| { |
| /* A memref on the whole shared memory */ |
| /* Set the direction from the shared memory flags */ |
| pCommand->sInvokeClientCommand.nParamTypes |= |
| (pTEECParam->memref.parent->flags & (SCX_PARAM_TYPE_INPUT_FLAG | SCX_PARAM_TYPE_OUTPUT_FLAG)) |
| << (4*i); |
| pSCXParam->sMemref.nSize = pTEECParam->memref.parent->size; |
| pSCXParam->sMemref.nOffset = 0; |
| } |
| else |
| { |
| /* A partial memref */ |
| pSCXParam->sMemref.nSize = pTEECParam->memref.size; |
| pSCXParam->sMemref.nOffset = pTEECParam->memref.offset; |
| } |
| } |
| else |
| { |
| /* A temporary memref */ |
| /* Set nOffset to the address in the client. This allows the server |
| to allocate a block with the same alignment and also to |
| detect a NULL tmpref. |
| */ |
| pSCXParam->sTempMemref.nOffset = (uint32_t)pTEECParam->tmpref.buffer; |
| pSCXParam->sTempMemref.nDescriptor = (uint32_t)pTEECParam->tmpref.buffer; |
| pSCXParam->sTempMemref.nSize = pTEECParam->tmpref.size; |
| } |
| } |
| else if (nTEECParamType & SCX_PARAM_TYPE_INPUT_FLAG) |
| { |
| /* An input value */ |
| pSCXParam->sValue.a = pTEECParam->value.a; |
| pSCXParam->sValue.b = pTEECParam->value.b; |
| } |
| } |
| } |
| |
| pCommand->sHeader.nOperationID = (uint32_t)pAnswer; |
| |
| nResult = ioctl((S_HANDLE)pContext->imp._hConnection, IOCTL_SCX_EXCHANGE, pCommand); |
| if (nResult != S_SUCCESS) |
| { |
| TRACE_INFO("scxExchangeMessage[0x%X]: Ioctl returned error: 0x%x (0x%x - %d)\n",pContext,nResult,errno,errno); |
| switch(errno) |
| { |
| case ENOMEM: |
| nResult=TEEC_ERROR_OUT_OF_MEMORY; |
| break; |
| case EACCES: |
| nResult=TEEC_ERROR_ACCESS_DENIED; |
| break; |
| default: |
| nResult=TEEC_ERROR_COMMUNICATION; |
| break; |
| } |
| } |
| |
| if (pOperation != NULL) |
| { |
| /* Update the operation parameters from the answer message */ |
| uint32_t i; |
| SCHANNEL6_ANSWER_PARAM * pSCXAnswers; |
| if (pAnswer->sHeader.nMessageType == SCX_OPEN_CLIENT_SESSION) |
| { |
| /* Open session */ |
| pSCXAnswers = pAnswer->sOpenClientSession.sAnswers; |
| } |
| else |
| { |
| /* Invoke case */ |
| pSCXAnswers = pAnswer->sInvokeClientCommand.sAnswers; |
| } |
| |
| for (i = 0; i < 4; i++) |
| { |
| uint32_t nSCXParamType; |
| nSCXParamType = SCX_GET_PARAM_TYPE(pCommand->sHeader.nMessageInfo, i); |
| if (nSCXParamType & SCX_PARAM_TYPE_OUTPUT_FLAG) |
| { |
| if (nSCXParamType & SCX_PARAM_TYPE_MEMREF_FLAG) |
| { |
| /* Trick: the size field is at the same position in a memref or a tmpref */ |
| pOperation->params[i].memref.size = pSCXAnswers[i].sSize.nSize; |
| } |
| else |
| { |
| /* An output value */ |
| pOperation->params[i].value.a = pSCXAnswers[i].sValue.a; |
| pOperation->params[i].value.b = pSCXAnswers[i].sValue.b; |
| } |
| } |
| } |
| } |
| |
| return nResult; |
| } |
| |
| /* ------------------------------------------------------------------------ */ |
| |
| static void* scxAllocateSharedMemory( |
| IN uint32_t nLength) |
| { |
| if (nLength == 0) |
| { |
| /* This is valid, although we don't want to call mmap. |
| Just return a dummy non-NULL pointer */ |
| return (void*)0x10; |
| } |
| else |
| { |
| return mmap( |
| 0,nLength, |
| PROT_READ | PROT_WRITE, |
| MAP_SHARED | MAP_ANONYMOUS, |
| 0,0); |
| } |
| } |
| |
| /* ------------------------------------------------------------------------ */ |
| |
| static void scxReleaseSharedMemory(IN void* pBuffer, |
| IN uint32_t nLength) |
| { |
| if (nLength == 0) |
| { |
| return; |
| } |
| if (munmap(pBuffer, nLength)!= 0) |
| { |
| TRACE_WARNING("scxReleaseSharedMemory returned 0x%x \n",errno); |
| } |
| } |
| /* ------------------------------------------------------------------------ */ |
| |
| uint64_t scxGetCurrentTime(void) |
| { |
| uint64_t currentTime = 0; |
| struct timeval now; |
| |
| gettimeofday(&now,NULL); |
| currentTime = now.tv_sec; |
| currentTime = (currentTime * 1000) + (now.tv_usec / 1000); |
| |
| return currentTime; |
| } |
| /* ------------------------------------------------------------------------ */ |
| |
| /* |
| * ==================================================== |
| * TEE Client API |
| * ===================================================== |
| */ |
| |
| /** |
| * Get a time-limit equal to now + relative timeout expressed in milliseconds. |
| **/ |
| void TEEC_GetTimeLimit( |
| TEEC_Context* sContext, |
| uint32_t nTimeout, |
| TEEC_TimeLimit* sTimeLimit) |
| { |
| uint64_t nTimeLimit = 0; |
| VAR_NOT_USED(sContext); |
| |
| TRACE_INFO("TEEC_GetTimeLimit(0x%X, %u ms)", sContext, nTimeout); |
| |
| if (nTimeout == 0xFFFFFFFF ) |
| { |
| /* Infinite timeout */ |
| nTimeLimit = SCTIME_INFINITE; |
| } |
| else |
| { |
| nTimeLimit = scxGetCurrentTime() + nTimeout; |
| } |
| TRACE_INFO("GetTimeLimit %ld\n",nTimeLimit); |
| memcpy(sTimeLimit, &nTimeLimit, sizeof(TEEC_TimeLimit)); |
| } |
| |
| //----------------------------------------------------------------------------------------------------- |
| TEEC_Result TEEC_InitializeContext( |
| const char* pDeviceName, |
| TEEC_Context* pContext) |
| { |
| |
| TEEC_Result nError = TEEC_SUCCESS; |
| S_HANDLE hDriver = S_HANDLE_NULL; |
| char sFullDeviceName[PATH_MAX]; |
| uint32_t nVersion; |
| |
| if(pDeviceName == NULL) |
| { |
| pDeviceName = SCX_DEFAULT_DEVICE_NAME; |
| } |
| strcpy(sFullDeviceName, "/dev/"); |
| strcat(sFullDeviceName, pDeviceName); |
| |
| hDriver = open(sFullDeviceName, O_RDWR, 0); |
| |
| if (hDriver == (uint32_t)-1) |
| { |
| TRACE_ERROR("scxOpen: open() failed 0x%x\n", errno); |
| switch(errno) |
| { |
| case ENOMEM: |
| nError = TEEC_ERROR_OUT_OF_MEMORY; |
| goto error; |
| case EINTR: |
| break; |
| default: |
| nError = TEEC_ERROR_COMMUNICATION; |
| goto error; |
| } |
| } |
| fcntl(hDriver, F_SETFD, FD_CLOEXEC); |
| nVersion = ioctl(hDriver, IOCTL_SCX_GET_VERSION); |
| if (nVersion != SM_DRIVER_VERSION) |
| { |
| TRACE_ERROR("scxOpen: Not expected driver version: 0x%x instead of 0x%x\n", nVersion,SM_DRIVER_VERSION); |
| switch(errno) |
| { |
| case ENOMEM: |
| nError=TEEC_ERROR_OUT_OF_MEMORY; |
| break; |
| default: |
| nError=TEEC_ERROR_COMMUNICATION; |
| break; |
| } |
| close(hDriver); |
| } |
| error: |
| if(nError == TEEC_SUCCESS) |
| { |
| pContext->imp._hConnection = hDriver; |
| } |
| else |
| { |
| TRACE_ERROR("scxOpen failed 0x%x\n", nError); |
| pContext->imp._hConnection = 0; |
| } |
| |
| return nError; |
| } |
| |
| //----------------------------------------------------------------------------------------------------- |
| void TEEC_FinalizeContext(TEEC_Context* pContext) |
| { |
| TRACE_INFO("TEEC_FinalizeContext[0x%X]", pContext); |
| |
| if (pContext == NULL) return; |
| |
| close(pContext->imp._hConnection); |
| pContext->imp._hConnection = 0; |
| } |
| |
| //----------------------------------------------------------------------------------------------------- |
| TEEC_Result TEEC_OpenSession ( |
| TEEC_Context* context, |
| TEEC_Session* session, /* OUT */ |
| const TEEC_UUID* destination, /* The trusted application UUID we want to open the session with */ |
| uint32_t connectionMethod, /* LoginType*/ |
| void* connectionData, /* LoginData */ |
| TEEC_Operation* operation, /* payload. If operation is NULL then no data buffers are exchanged with the Trusted Application, and the operation cannot be cancelled by the Client Application */ |
| uint32_t* errorOrigin) |
| { |
| return TEEC_OpenSessionEx(context, |
| session, |
| NULL, |
| destination, |
| connectionMethod, |
| connectionData, |
| operation, |
| errorOrigin); |
| } |
| |
| //----------------------------------------------------------------------------------------------------- |
| void TEEC_CloseSession (TEEC_Session* session) |
| { |
| TEEC_Context* context; |
| SCHANNEL6_ANSWER sAnswer; |
| SCHANNEL6_COMMAND sCommand; |
| if (session == NULL) return; |
| context = session->imp._pContext; |
| memset(&sCommand,0,sizeof(sCommand)); |
| sCommand.sHeader.nMessageType = SCX_CLOSE_CLIENT_SESSION; |
| sCommand.sHeader.nMessageSize = (sizeof(SCHANNEL6_CLOSE_CLIENT_SESSION_COMMAND) |
| - sizeof(SCHANNEL6_COMMAND_HEADER))/sizeof(uint32_t); |
| sCommand.sCloseClientSession.hClientSession = session->imp._hClientSession; |
| scxExchangeMessage(context, &sCommand, &sAnswer, NULL); |
| /* we ignore the error code of scxExchangeMessage */ |
| session->imp._hClientSession = S_HANDLE_NULL; |
| session->imp._pContext = NULL; |
| } |
| |
| //----------------------------------------------------------------------------------------------------- |
| TEEC_Result TEEC_InvokeCommand( |
| TEEC_Session* session, |
| uint32_t commandID, |
| TEEC_Operation* operation, |
| uint32_t* errorOrigin) |
| { |
| return TEEC_InvokeCommandEx(session, |
| NULL, |
| commandID, |
| operation, |
| errorOrigin); |
| } |
| |
| |
| //----------------------------------------------------------------------------------------------------- |
| /* Used to implement both register and allocate */ |
| static TEEC_Result TEEC_RegisterSharedMemory0( |
| TEEC_Context* context, |
| TEEC_SharedMemory* sharedMem) |
| { |
| TEEC_Result nResult; |
| SCHANNEL6_COMMAND sCommand; |
| SCHANNEL6_ANSWER sAnswer; |
| |
| TRACE_INFO("TEEC_RegisterSharedMemory0 (%p, %p)",context, sharedMem); |
| memset(&sCommand, 0, sizeof(sCommand)); |
| |
| sCommand.sRegisterSharedMemory.nMessageSize = (sizeof(SCHANNEL6_REGISTER_SHARED_MEMORY_COMMAND) - sizeof(SCHANNEL6_COMMAND_HEADER))/4; |
| sCommand.sRegisterSharedMemory.nMessageType = SCX_REGISTER_SHARED_MEMORY; |
| sCommand.sRegisterSharedMemory.nMemoryFlags = sharedMem->flags; |
| sCommand.sRegisterSharedMemory.nSharedMemSize = sharedMem->size; |
| sCommand.sRegisterSharedMemory.nSharedMemStartOffset = 0; |
| sCommand.sRegisterSharedMemory.nSharedMemDescriptors[0] = (uint32_t)sharedMem->buffer; |
| nResult = scxExchangeMessage(context, |
| &sCommand, |
| &sAnswer, |
| NULL); |
| if (nResult == TEEC_SUCCESS) |
| { |
| nResult = sAnswer.sRegisterSharedMemory.nErrorCode; |
| } |
| if (nResult == TEEC_SUCCESS) |
| { |
| sharedMem->imp._pContext = context; |
| sharedMem->imp._hBlock = sAnswer.sRegisterSharedMemory.hBlock; |
| } |
| return nResult; |
| } |
| |
| |
| //----------------------------------------------------------------------------------------------------- |
| TEEC_Result TEEC_RegisterSharedMemory( |
| TEEC_Context* context, |
| TEEC_SharedMemory* sharedMem) |
| { |
| TRACE_INFO("TEEC_RegisterSharedMemory (%p)",context); |
| sharedMem->imp._pContext = NULL; |
| sharedMem->imp._hBlock = S_HANDLE_NULL; |
| sharedMem->imp._bAllocated = false; |
| return TEEC_RegisterSharedMemory0(context, sharedMem); |
| } |
| |
| //----------------------------------------------------------------------------------------------------- |
| TEEC_Result TEEC_AllocateSharedMemory( |
| TEEC_Context* context, |
| TEEC_SharedMemory* sharedMem) |
| { |
| TEEC_Result nResult; |
| TRACE_INFO("TEEC_AllocateSharedMemory (%p)",context); |
| |
| sharedMem->imp._pContext = NULL; |
| sharedMem->imp._hBlock = S_HANDLE_NULL; |
| sharedMem->buffer = scxAllocateSharedMemory(sharedMem->size); |
| if (sharedMem->buffer == NULL) |
| { |
| return TEEC_ERROR_OUT_OF_MEMORY; |
| } |
| sharedMem->imp._bAllocated = true; |
| nResult = TEEC_RegisterSharedMemory0(context, sharedMem); |
| if (nResult != TEEC_SUCCESS) |
| { |
| scxReleaseSharedMemory(sharedMem->buffer,sharedMem->size); |
| sharedMem->buffer = NULL; |
| } |
| return nResult; |
| } |
| |
| //----------------------------------------------------------------------------------------------------- |
| void TEEC_ReleaseSharedMemory ( |
| TEEC_SharedMemory* sharedMem) |
| { |
| SCHANNEL6_ANSWER sAnswer; |
| SCHANNEL6_COMMAND sMessage; |
| TEEC_Context* context; |
| |
| context = (TEEC_Context *)sharedMem->imp._pContext; |
| memset(&sMessage, 0, sizeof(SCHANNEL6_COMMAND)); |
| sMessage.sReleaseSharedMemory.nMessageType = SCX_RELEASE_SHARED_MEMORY; |
| sMessage.sReleaseSharedMemory.nMessageSize = (sizeof(SCHANNEL6_RELEASE_SHARED_MEMORY_COMMAND) |
| - sizeof(SCHANNEL6_COMMAND_HEADER))/sizeof(uint32_t); |
| sMessage.sReleaseSharedMemory.hBlock = sharedMem->imp._hBlock; |
| scxExchangeMessage(context,&sMessage, &sAnswer, NULL); |
| if (sharedMem->imp._bAllocated) |
| { |
| scxReleaseSharedMemory(sharedMem->buffer,sharedMem->size); |
| /* Update parameters: |
| * In this case the Implementation MUST set the buffer and size fields of the sharedMem structure |
| * to NULL and 0 respectively before returning. |
| */ |
| sharedMem->buffer = NULL; |
| sharedMem->size = 0; |
| } |
| sharedMem->imp._pContext = NULL; |
| sharedMem->imp._hBlock = S_HANDLE_NULL; |
| } |
| |
| //----------------------------------------------------------------------------------------------------- |
| void TEEC_RequestCancellation(TEEC_Operation* operation) |
| { |
| uint32_t nOperationState; |
| TEEC_Result nResult; |
| |
| if (operation == NULL) return; |
| |
| retry: |
| nOperationState = operation->started; |
| if (nOperationState == 2) |
| { |
| /* Operation already finished. Return immediately */ |
| return; |
| } |
| else if (nOperationState == 1) |
| { |
| /* Operation is in progress */ |
| TEEC_Context* context; |
| SCHANNEL6_ANSWER sAnswer; |
| SCHANNEL6_COMMAND sMessage; |
| |
| context = operation->imp._pContext; |
| |
| memset(&sMessage,0,sizeof(sMessage)); |
| sMessage.sHeader.nMessageType = SCX_CANCEL_CLIENT_OPERATION; |
| sMessage.sHeader.nMessageSize = (sizeof(SCHANNEL6_CANCEL_CLIENT_OPERATION_COMMAND) - sizeof(SCHANNEL6_COMMAND_HEADER))/4; |
| sMessage.sCancelClientOperation.hClientSession = operation->imp._hSession; |
| sMessage.sCancelClientOperation.nCancellationID = (uint32_t)operation; |
| nResult = scxExchangeMessage(context,&sMessage, &sAnswer, NULL); |
| |
| if (nResult != TEEC_SUCCESS) |
| { |
| /* Communication failure. Ignore the error: the operation is already cancelled anyway */ |
| return; |
| } |
| if (sAnswer.sCancelClientOperation.nErrorCode == S_SUCCESS) |
| { |
| /* Command was successfully cancelled */ |
| return; |
| } |
| /* Otherwise, the command has not yet reached the secure world or has already finished and we must retry */ |
| } |
| /* This applies as well when nOperationState == 0. In this case, the operation has not yet |
| started yet and we don't even have a pointer to the context */ |
| scxYield(); |
| goto retry; |
| } |
| |
| |
| //----------------------------------------------------------------------------------------------------- |
| TEEC_Result TEEC_ReadSignatureFile( |
| void** ppSignatureFile, |
| uint32_t* pnSignatureFileLength) |
| { |
| TEEC_Result nErrorCode = TEEC_SUCCESS; |
| |
| uint32_t nBytesRead; |
| uint32_t nSignatureSize = 0; |
| uint8_t* pSignature = NULL; |
| FILE* pSignatureFile = NULL; |
| char sFileName[PATH_MAX + 1 + 5]; /* Allocate room for the signature extension */ |
| long nFileSize; |
| |
| *pnSignatureFileLength = 0; |
| *ppSignatureFile = NULL; |
| |
| if (realpath("/proc/self/exe", sFileName) == NULL) |
| { |
| TRACE_ERROR("TEEC_ReadSignatureFile: realpath failed [%d]", errno); |
| return TEEC_ERROR_OS; |
| } |
| |
| /* Work out the signature file name */ |
| strcat(sFileName, ".ssig"); |
| |
| pSignatureFile = fopen(sFileName, "rb"); |
| if (pSignatureFile == NULL) |
| { |
| /* Signature doesn't exist */ |
| return TEEC_ERROR_ITEM_NOT_FOUND; |
| } |
| |
| if (fseek(pSignatureFile, 0, SEEK_END) != 0) |
| { |
| TRACE_ERROR("TEEC_ReadSignatureFile: fseek(%s) failed [%d]", |
| sFileName, errno); |
| nErrorCode = TEEC_ERROR_OS; |
| goto error; |
| } |
| |
| nFileSize = ftell(pSignatureFile); |
| if (nFileSize < 0) |
| { |
| TRACE_ERROR("TEEC_ReadSignatureFile: ftell(%s) failed [%d]", |
| sFileName, errno); |
| nErrorCode = TEEC_ERROR_OS; |
| goto error; |
| } |
| |
| nSignatureSize = (uint32_t)nFileSize; |
| |
| if (nSignatureSize != 0) |
| { |
| pSignature = malloc(nSignatureSize); |
| if (pSignature == NULL) |
| { |
| TRACE_ERROR("TEEC_ReadSignatureFile: Heap - Out of memory for %u bytes", |
| nSignatureSize); |
| nErrorCode = TEEC_ERROR_OUT_OF_MEMORY; |
| goto error; |
| } |
| |
| rewind(pSignatureFile); |
| |
| nBytesRead = fread(pSignature, 1, nSignatureSize, pSignatureFile); |
| if (nBytesRead < nSignatureSize) |
| { |
| TRACE_ERROR("TEEC_ReadSignatureFile: fread failed [%d]", errno); |
| nErrorCode = TEEC_ERROR_OS; |
| goto error; |
| } |
| } |
| |
| fclose(pSignatureFile); |
| |
| *pnSignatureFileLength = nSignatureSize; |
| *ppSignatureFile = pSignature; |
| |
| return S_SUCCESS; |
| |
| error: |
| fclose(pSignatureFile); |
| free(pSignature); |
| |
| return nErrorCode; |
| } |
| |
| //----------------------------------------------------------------------------------------------------- |
| TEEC_Result TEEC_OpenSessionEx ( |
| TEEC_Context* context, |
| TEEC_Session* session, /* OUT */ |
| const TEEC_TimeLimit* timeLimit, |
| const TEEC_UUID* destination, /* The trusted application UUID we want to open the session with */ |
| uint32_t connectionMethod, /* LoginType*/ |
| void* connectionData, /* LoginData */ |
| TEEC_Operation* operation, /* payload. If operation is NULL then no data buffers are exchanged with the Trusted Application, and the operation cannot be cancelled by the Client Application */ |
| uint32_t* returnOrigin) |
| { |
| TEEC_Result nError; |
| uint32_t nReturnOrigin; |
| SCHANNEL6_ANSWER sAnswer; |
| SCHANNEL6_COMMAND sCommand; |
| |
| memset(&sCommand, 0, sizeof(SCHANNEL6_COMMAND)); |
| |
| sCommand.sHeader.nMessageType = SCX_OPEN_CLIENT_SESSION; |
| sCommand.sHeader.nMessageSize = (sizeof(SCHANNEL6_OPEN_CLIENT_SESSION_COMMAND) - 20 -sizeof(SCHANNEL6_COMMAND_HEADER))/sizeof(uint32_t); |
| if (timeLimit == NULL) |
| { |
| sCommand.sOpenClientSession.sTimeout = SCTIME_INFINITE; |
| } |
| else |
| { |
| sCommand.sOpenClientSession.sTimeout = *(uint64_t*)timeLimit; |
| } |
| sCommand.sOpenClientSession.sDestinationUUID = *((S_UUID*)destination); |
| sCommand.sOpenClientSession.nLoginType = connectionMethod; |
| if ((connectionMethod == TEEC_LOGIN_GROUP)||(connectionMethod == TEEC_LOGIN_GROUP_APPLICATION)) |
| { |
| /* connectionData MUST point to a uint32_t which contains the group |
| * which this Client Application wants to connect as. The Linux Driver |
| * is responsible for securely ensuring that the Client Application |
| * instance is actually a member of this group. |
| */ |
| if (connectionData != NULL) |
| { |
| *(uint32_t*)sCommand.sOpenClientSession.sLoginData = *(uint32_t*)connectionData; |
| sCommand.sHeader.nMessageSize += 1; |
| } |
| } |
| sCommand.sOpenClientSession.nCancellationID = (uint32_t)operation; // used for TEEC_RequestCancellation |
| |
| if (operation != NULL) |
| { |
| operation->imp._pContext = context; |
| operation->imp._hSession = S_HANDLE_NULL; |
| operation->started = 1; |
| } |
| |
| nError = scxExchangeMessage(context, &sCommand, &sAnswer, operation); |
| |
| if (operation != NULL) operation->started = 2; |
| |
| if (nError != TEEC_SUCCESS) |
| { |
| nReturnOrigin = TEEC_ORIGIN_COMMS; |
| } |
| else |
| { |
| nError = sAnswer.sOpenClientSession.nErrorCode; |
| nReturnOrigin = sAnswer.sOpenClientSession.nReturnOrigin; |
| } |
| |
| if (returnOrigin != NULL) *returnOrigin = nReturnOrigin; |
| |
| if (nError == S_SUCCESS) |
| { |
| session->imp._hClientSession = sAnswer.sOpenClientSession.hClientSession; |
| session->imp._pContext = context; |
| } |
| |
| return nError; |
| } |
| |
| //----------------------------------------------------------------------------------------------------- |
| TEEC_Result TEEC_InvokeCommandEx( |
| TEEC_Session* session, |
| const TEEC_TimeLimit* timeLimit, |
| uint32_t commandID, |
| TEEC_Operation* operation, |
| uint32_t* returnOrigin) |
| { |
| TEEC_Result nError; |
| SCHANNEL6_ANSWER sAnswer; |
| SCHANNEL6_COMMAND sCommand; |
| uint32_t nReturnOrigin; |
| TEEC_Context * context; |
| |
| context = (TEEC_Context *)session->imp._pContext; |
| memset(&sCommand, 0, sizeof(SCHANNEL6_COMMAND)); |
| |
| sCommand.sHeader.nMessageType = SCX_INVOKE_CLIENT_COMMAND; |
| sCommand.sHeader.nMessageSize = (sizeof(SCHANNEL6_INVOKE_CLIENT_COMMAND_COMMAND) - sizeof(SCHANNEL6_COMMAND_HEADER))/sizeof(uint32_t); |
| sCommand.sInvokeClientCommand.nClientCommandIdentifier = commandID; |
| if (timeLimit == NULL) |
| { |
| sCommand.sInvokeClientCommand.sTimeout = SCTIME_INFINITE; |
| } |
| else |
| { |
| sCommand.sInvokeClientCommand.sTimeout = *(uint64_t*)timeLimit; |
| } |
| sCommand.sInvokeClientCommand.hClientSession = session->imp._hClientSession; |
| sCommand.sInvokeClientCommand.nCancellationID = (uint32_t)operation; // used for TEEC_RequestCancellation |
| |
| if (operation != NULL) |
| { |
| operation->imp._pContext = session->imp._pContext; |
| operation->imp._hSession = session->imp._hClientSession; |
| operation->started = 1; |
| } |
| |
| nError = scxExchangeMessage(context, &sCommand, &sAnswer, operation); |
| |
| if (operation != NULL) |
| { |
| operation->started = 2; |
| operation->imp._hSession = S_HANDLE_NULL; |
| operation->imp._pContext = NULL; |
| } |
| |
| if (nError != TEEC_SUCCESS) |
| { |
| nReturnOrigin = TEEC_ORIGIN_COMMS; |
| } |
| else |
| { |
| nError = sAnswer.sInvokeClientCommand.nErrorCode; |
| nReturnOrigin = sAnswer.sInvokeClientCommand.nReturnOrigin; |
| } |
| |
| if (returnOrigin != NULL) *returnOrigin = nReturnOrigin; |
| |
| return nError; |
| |
| } |
| |
| //----------------------------------------------------------------------------------------------------- |
| /* |
| * Retrieves information about the implementation |
| */ |
| void TEEC_GetImplementationInfo( |
| TEEC_Context* context, |
| TEEC_ImplementationInfo* description) |
| { |
| TRACE_INFO("TEEC_GetImplementationInfo", NULL); |
| |
| memset(description, 0, sizeof(TEEC_ImplementationInfo)); |
| |
| strcpy(description->apiDescription, S_VERSION_STRING); |
| |
| if (context != NULL) |
| { |
| SCX_VERSION_INFORMATION_BUFFER sInfoBuffer; |
| uint32_t nResult; |
| |
| nResult = ioctl((S_HANDLE)context->imp._hConnection, IOCTL_SCX_GET_DESCRIPTION, &sInfoBuffer); |
| if (nResult != S_SUCCESS) |
| { |
| TRACE_ERROR("TEEC_GetImplementationInfo[0x%X]: ioctl returned error: 0x%x ( %d)\n",context, nResult, errno); |
| return; |
| } |
| |
| memcpy(description->commsDescription, sInfoBuffer.sDriverDescription, 64); |
| description->commsDescription[64] = 0; |
| memcpy(description->TEEDescription, sInfoBuffer.sSecureWorldDescription, 64); |
| description->TEEDescription[64] = 0; |
| } |
| } |
| |
| void TEEC_GetImplementationLimits( |
| TEEC_ImplementationLimits* limits) |
| { |
| memset(limits, 0, sizeof(TEEC_ImplementationLimits)); |
| |
| /* A temp mem ref can not be mapped on more than 1Mb */ |
| limits->pageSize = SIZE_4KB; |
| limits->tmprefMaxSize = SIZE_1MB; |
| limits->sharedMemMaxSize = SIZE_1MB * 8; |
| } |