/*
 * =====================================================================================
 *
 *       Filename:  bfAppMain.c
 *
 *    Description:  Entry point of Blowfish trustzone app.
 *
 *        Version:  1.0
 *        Created:  05/29/2017 03:53:52 PM
 *       Compiler:  armcc
 *
 *         Author:  Dongwook Shim (), dw.shim@samsung.com
 *        Company:  Samsung Electronics
 *
 *        Copyright (c) 2017 by Samsung Electronics, All rights reserved.
 *
 * =====================================================================================
 */

#include "commonConfig.h"
#include "cryptoEngine.h"
#include "log.h"
#include "secMemoryManager.h"
#include "taConfig.h"
#include "teeCmdExecuter.h"
#include "version.h"
#ifndef DISABLE_LOG_ENCRYPTION
#include "logEncryptor.h"
#endif  // End of DISABLE_LOG_ENCRYPTION

#include "tee_internal_api.h"
#include "teeProperties.h"

TEE_Result TA_CreateEntryPoint(void)
{
    return TEE_SUCCESS;
}

void TA_DestroyEntryPoint(void)
{
    return;
}

TEE_Result TA_OpenSessionEntryPoint(uint32_t paramTypes, TEE_Param params[4], void **sessionContext)
{
    /* print trustlet name and version */
    VERSIONIZE(TRUSTLET_TAG);
    return TEE_SUCCESS;
}

void TA_CloseSessionEntryPoint(session_ctx_ptr pSessionContext)
{
    (void) pSessionContext;
}

TEE_Result TA_InvokeCommandEntryPoint(session_ctx_ptr pSessionContext, uint32_t commandID, uint32_t param_types, TEE_Param param[4])
{
    int32_t ret = NOT_ERROR;
    CmdReq_t req;
    CmdRsp_t rsp;
    uint8_t *pData = param[0].memref.buffer;
    const size_t maxDataSize = MAX_SKM_BUF_SIZE;

    // Check shared buffer.
    if(pData == NULL)
    {
        LOGE("Invalid parameter.");
        return ERR_TA_INVALID_ARGUMENT;
    }

    // Allocator initialize.
    if((ret = secMemoryManagerInit()) != NOT_ERROR)
        return ret;

    // crypto engine init
    CRYPTO_init();

    memset(&req, 0, sizeof(req));
    memset(&rsp, 0, sizeof(rsp));

    if(TEE_PARAM_TYPE_GET(param_types, 0) != TEE_PARAM_TYPE_MEMREF_INOUT) {
        LOGE("Invalid param_types.");
        ret = ERR_TA_INVALID_ARGUMENT;
        goto cleanup;
    }

#if (TEEGRIS_SDK_VER >= 3)
    if((ret = TEES_IsREESharedMemory(TEE_MEMORY_ACCESS_READ|TEE_MEMORY_ACCESS_WRITE, (void *)pData, param[0].memref.size)) != TEE_SUCCESS)
    {
        LOGE("Faild to check shared memory with ret=0x%X ", ret);
        ret = ERR_TA_INVALID_ARGUMENT;
        goto cleanup;
    }
#endif
    if(TA_BUFFER_HEADER_LEN > param[0].memref.size)
    {
        LOGE("Request data length (%d) is too big Invalid argument.", param[0].memref.size);
        ret = ERR_TA_BUFFER_OVERFLOW;
        goto cleanup;
    }

    memcpy(&req, pData, TA_BUFFER_HEADER_LEN);

    if(((TA_BUFFER_HEADER_LEN + req.dataLen) > param[0].memref.size) || req.dataLen > maxDataSize)
    {
        LOGE("Request data length (%d) is too big Invalid argument.",req.dataLen);
        ret = ERR_TA_BUFFER_OVERFLOW;
        goto cleanup;
    }

    memcpy(req.data, (char *)pData + TA_BUFFER_HEADER_LEN, (req.dataLen < maxDataSize) ? req.dataLen : maxDataSize);
    rsp.dataLen = maxDataSize;

    LOGD("Received Cmd = 0x%x with %d bytes in request buffer.", req.cmdId, req.dataLen);

    if((rsp.status = taCmdExecute(req.cmdId, req.data, req.dataLen, rsp.data, &(rsp.dataLen))) != NOT_ERROR)
    {
        LOGE("Invoke Command data caused error! Ret = %d", rsp.status);
#ifndef DISABLE_LOG_ENCRYPTION
        if((ret = logEncrypt(rsp.data, maxDataSize)) > 0)
            rsp.dataLen = (uint32_t)ret;
        else
        {
            LOGE("Failed to log encryption with error %d.", ret);
            rsp.dataLen = 0;
            goto cleanup;
        }
#endif  // End of DISABLE_LOG_ENCRYPTION
    }

    ret = rsp.status;

    // Treat out buffer size equal to max shared buffer length.
    if(rsp.dataLen > maxDataSize || (TA_BUFFER_HEADER_LEN + rsp.dataLen) > param[0].memref.size)
    {
        LOGE("Wrapped data is too big (%d bytes)", rsp.dataLen);
        ret = ERR_TA_BUFFER_OVERFLOW;
        goto cleanup;
    }

    //  Copy out data from temp static buffer to shared buffer.
    memcpy((char *)pData, &rsp, TA_BUFFER_HEADER_LEN);
    memcpy((char *)pData + TA_BUFFER_HEADER_LEN, rsp.data, (rsp.dataLen < maxDataSize) ? rsp.dataLen : maxDataSize);

    LOGD("Respond from Cmd = 0x%x with %d bytes in Cmd Buffer", req.cmdId, rsp.dataLen);

cleanup:
    memset(&req, 0, sizeof(req));
    memset(&rsp, 0, sizeof(rsp));

    return ret;
}
