/*
 * Copyright (c) 2019 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Brief: Kinibi trustlet entry point.
 */
#include <tlStd.h>
#include <TlApi/TlApi.h>

#include "TigerMacros.h"
#include "TigerCore.h"
#include "TigerMbedTlsHooks.h"

#include "TzwMemory.h"
#include "TzwString.h"

#include "TigerTaConfig.h"
#include "TigerUtils.h"
#include "TigerTci.h"

#include "ta_banner_print.h"

#include <inttypes.h>

DECLARE_TRUSTED_APPLICATION_MAIN_STACK(0x80000)

#define TA_PROP_DESCRIPTION     "TIGER"

/**
 * Termination codes
 */
#define EXIT_ERROR                      ((uint32_t)(-1))


uint64_t perf_time_begin;
uint64_t perf_time_end;

static uint64_t session_counter = 0;

#ifdef __DEV_DEBUG__
static uint64_t start_time_of_entry_point = 0;
static uint64_t end_time_of_entry_point = 0;

static uint64_t start_time_of_session_entry = 0;
static uint64_t end_time_of_session_entry = 0;
#endif /* __DEV_DEBUG__ */

void __CreateEntryPoint(void) {

    /*LOG_I("VERSION: %s", taAppVersion);*/

    setupMbedTlsHooks();
#ifdef __DEV_DEBUG__
    start_time_of_entry_point = systemTime();
#endif /* __DEV_DEBUG__ */

    print_banner(TA_PROP_DESCRIPTION, taAppVersion);

    session_counter +=1;

    LOG_Raw("session counter: %" PRIu64, session_counter);

}

void __DestroyEntryPoint(void) {
    LOG_I("TA is unloaded");

#ifdef __DEV_DEBUG__
    end_time_of_entry_point = systemTime();
    LOG_DEV("elapsed time(entry point): %" PRIu64 "ms", end_time_of_entry_point - start_time_of_entry_point);
#endif /* __DEV_DEBUG__ */

    session_counter -=1;
    LOG_Raw("session counter: %" PRIu64, session_counter);
}

/**
 * Trustlet entry.
 **/
_TLAPI_ENTRY void tlMain( const addr_t tciBuffer, const uint32_t tciBufferLen ) {
    __CreateEntryPoint();

    LOG_D("trustlet is starting");
#ifdef __DEV_DEBUG__
    {
        uint32_t tlApiVersion;
        tlApiResult_t ret;
        mcVersionInfo_t versionInfo;

        ret = tlApiGetVersion(&tlApiVersion);
        if (TLAPI_OK != ret) {
            LOG_E("tlApiGetVersion failed with ret=0x%08X, exit", ret);
            tlApiExit(ret);
        }
        LOG_D("tlApi version 0x%08X, exit", tlApiVersion);

        ret = tlApiGetMobicoreVersion(&versionInfo);
        if (TLAPI_OK != ret) {
            LOG_E("tlApiGetMobicoreVersion failed with ret=0x%08X, exit", ret);
            tlApiExit(ret);
        }
        LOG_D("productId        = %s",     versionInfo.productId);
        LOG_D("versionMci       = 0x%08X", versionInfo.versionMci);
        LOG_D("versionSo        = 0x%08X", versionInfo.versionSo);
        LOG_D("versionMclf      = 0x%08X", versionInfo.versionMclf);
        LOG_D("versionContainer = 0x%08X", versionInfo.versionContainer);
        LOG_D("versionMcConfig  = 0x%08X", versionInfo.versionMcConfig);
        LOG_D("versionTlApi     = 0x%08X", versionInfo.versionTlApi);
        LOG_D("versionDrApi     = 0x%08X", versionInfo.versionDrApi);
        LOG_D("versionCmp       = 0x%08X", versionInfo.versionCmp);
    }
#endif

    /* Check if the size of the given TCI is sufficient */
    if ((NULL == tciBuffer) || ( sizeof(cmd_req_t) + sizeof(cmd_rsp_t) != tciBufferLen)) {
          LOG_E("TCI error");
          LOG_E("TCI buffer: 0x%08x", (uint32_t)tciBuffer);
          LOG_E("TCI buffer actual length:0x%04x, expected length: 0x%04x",
                              tciBufferLen, sizeof(cmd_rsp_t) + sizeof(cmd_req_t));
          tlApiExit(EXIT_ERROR);
    }

    LOG_DEV("Trusted Application is processing the command");

    TEE_Result status = TEE_SUCCESS;
    cmd_req_p request = NULL;
    cmd_rsp_p response = NULL;

    do {

        LOG_DEV("Trusted Application is waiting for a notification to arrive");
        /* Wait for a notification to arrive */
        tlApiResult_t ret;
        ret = tlApiWaitNotification(TLAPI_INFINITE_TIMEOUT);
        if(TLAPI_OK != ret){
            LOG_E("tlApiWaitNotification failed with ret=0x%08X, exit", ret);
        }
        /* Dereference commandId once for further usage */
        LOG_D("Trusted Application received command");

        /* Process command message */
        LOG_DEV("tciBufferLen:%p", tciBuffer);

        cmd_req_p request = tzwMalloc(sizeof(cmd_req_t));
        if(!request){
            LOG_E("Fail to tzwMalloc", request);
            status = TEE_ERROR_OUT_OF_MEMORY;
            break;
        }

        cmd_rsp_p response = tzwMalloc(sizeof(cmd_rsp_t));
        if(!request){
            LOG_E("Fail to tzwMalloc", request);
            tzwFree(request);
            status = TEE_ERROR_OUT_OF_MEMORY;
            break;
        }

        tzwMemMove(request, tciBuffer, sizeof(cmd_req_t));
        tzwMemMove(response, (uint8_t*)tciBuffer + sizeof(cmd_req_t), sizeof(cmd_rsp_t));

#ifdef __DEV_DEBUG__
        logRawByteArrayHex((uint8_t*)request, sizeof(cmd_req_t)/2, "request bytes");
#endif
        LOG_D("request: %p, sizeof request: 0x%04x", request->input, request->in_len);
        LOG_D("response: %p, sizeof response: 0x%04x", response->output , response->out_len);

        uint32_t expected_size = response->out_len;

        LOG_I_PERF_BEGIN
        status = processCommand(request, sizeof(cmd_req_t), (uint8_t*)(response) + sizeof(uint32_t), expected_size);
        LOG_I_PERF_END

        if(request){
            tzwFree(request);
            request = NULL;
        }

        response->ret = status;
        LOG_D("Trusted Application is exiting with status: 0x%08x ", status);

        //--------------------------------------------------------------
        //Transmit TA log to Nwd
        uint32_t total_log_size = 0;
#if defined(TRANSMIT_LOG_TO_CA)
#ifdef TA_RELEASE
        if (TEE_SUCCESS != status){
#endif /*TA_RELEASE*/
        do{
            int len = 0;
            LOG_I_AM_HERE
            cmd_rsp_p logOut = response;
            char *ptr = (char*)logOut->log_buf;
            logOut->log_len= 0;
            while(!isStackLogEmpty()) {
                char *str = popStackLog(&len);
                total_log_size += (len + 4);
                if(LOGGER_BUF_LEN < total_log_size ){
                    /*LOG_Raw("missing log:%s", str);*/
                    tzwFree(str);
                    continue;
                }

                *((uint32_t*)ptr) = len;
                ptr += 4;

                strncpy(ptr, str, len);
                ptr += len;
                logOut->log_len += (len + 4);
                tzwFree(str);
            }
        }while(0);

#ifdef __DEV_DEBUG__
        LOG_Raw("len of transmit Ta log: %u", response->log_len);
        LOG_Raw("len of all Ta log: %d", total_log_size);
#endif /* __DEV_DEBUG__ */
#ifdef TA_RELEASE
    }
#endif
#endif /*TRANSMIT_LOG_TO_CA*/

        //--------------------------------------------------------------
        // Copy Response from SWD memory back to NWD
        //--------------------------------------------------------------
        tzwMemMove((uint8_t*)tciBuffer + sizeof(cmd_req_t), response, sizeof(cmd_rsp_t));
#ifdef __DEV_DEBUG__
        LOG_Raw("Len of Response: %u", response->out_len);
#endif /* __DEV_DEBUG__ */

        if(response){
            tzwFree(response);
            response = NULL;
        }

        /* Notify back the TLC */
        tlApiNotify();
    }while(1);

    if(request){
        tzwFree(request);
        request = NULL;
    }

    if(response){
        tzwFree(response);
        response = NULL;
    }

    LOG_FUNC_END

    __DestroyEntryPoint();

    tlApiExit(TEE_SUCCESS);
}

