/**
 * @file tl_main.c
 * @brief TA main loop implementation for <t-base
 * @author Iaroslav Makarchuk (i.makarchuk@samsung.com)
 * @date Created Oct 3, 2016
 * @par In Samsung Ukraine R&D Center (SURC) under a contract between
 * @par LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) and
 * @par "Samsung Elecrtronics Co", Ltd (Seoul, Republic of Korea)
 * @par Copyright: (c) Samsung Electronics Co, Ltd 2015. All rights reserved.
 *
 * This software is proprietary of Samsung Electronics.
 * No part of this software, either material or conceptual may be copied
 * or distributed, transmitted, transcribed, stored in a retrieval system
 * or translated into any human or computer language in any form by any means,
 * electronic, mechanical, manual or otherwise, or disclosed to third parties
 * without the express written permission of Samsung Electronics.
 */

#include "tlStd.h"
#include "TlApi/TlApi.h"

#include "tl_main.h"

#include <locale.h>
#include <protocol.h>
#include <setjmp.h>
#include <tee_internal_api.h>
#include <tee_param_utils.h>
#include <tees_log.h>

#define KEEP_WORKING   0
#define MUST_UNLOAD_TA 1

#define MB_TBASE_DEFAULT_HEAP  (128 * 1024)
#ifdef BORING_SSL
#define MB_TBASE_DEFAULT_STACK (128 * 1024)
#else
#define MB_TBASE_DEFAULT_STACK (64 * 1024)
#endif

DECLARE_TRUSTLET_MAIN_STACK(MB_TBASE_DEFAULT_STACK)
#if TBASE_API_LEVEL < 5
DECLARE_TRUSTED_APPLICATION_MAIN_HEAP(MB_TBASE_DEFAULT_HEAP)
#endif

/* The below variable is necessary for scrypto2 */
char TZ_APP_NAME[] = {TBASE_APP_NAME};

void *g_session_context = NULL;
extern jmp_buf g_panic_jmp_buf;
int g_ta_created = 0;

int init_crypto(void);
void init_properties(void *start_TA_property);

PersObjectCmd *g_po_cmd_buffer = NULL;
ProtocolCmd *g_cmd_buffer = NULL;
ProtocolCmd g_tmp_tci_buffer;

static int HandleCommand(ProtocolCmd *command) {
  int retval = KEEP_WORKING;
  TEE_Param params[4];

  // Return origin: "TEE" must be set by default.
  command->return_origin = TEE_ORIGIN_TEE;

  command->cmd_ret = FillParamValues(command->param_types, params, command);
  if (TEE_SUCCESS != command->cmd_ret) {
    TEES_LOG(TEES_LOG_LEVEL_ERROR, "Failed to read command params!\n");
    goto exit;
  }

  if (PROTOCOL_COMMAND_UNLOAD == command->cmd_id) {
    retval = MUST_UNLOAD_TA;
    TA_CloseSessionEntryPoint(g_session_context);
    command->return_origin = TEE_ORIGIN_TRUSTED_APP;
    goto exit;
  }

  if (PROTOCOL_COMMAND_LOAD == command->cmd_id) {
    if (!g_ta_created) {

      /* property init */
      init_properties((void *)&_TA_property);

      TA_CreateEntryPoint();
      g_ta_created++;
    }

    command->cmd_ret = TA_OpenSessionEntryPoint(command->param_types,
                                                params,
                                                &g_session_context);
  } else {
    command->cmd_ret = TA_InvokeCommandEntryPoint(g_session_context,
                                                  command->cmd_id,
                                                  command->param_types,
                                                  params);
  }

  if (TEE_SUCCESS != FillCommandArgs(command->param_types, command, params)) {
    TEES_LOG(TEES_LOG_LEVEL_ERROR, "Failed to write command params!\n");
    command->cmd_ret = TEE_ERROR_COMMUNICATION;
    goto exit;
  }

  command->return_origin = TEE_ORIGIN_TRUSTED_APP;

exit:
  return retval;
}


_TLAPI_ENTRY void tlMainInternal(const addr_t tci_buffer,
                                 const uint32_t tci_buffer_len) {
  ProtocolCmd *command = tci_buffer;
  int must_unload = KEEP_WORKING;
  int panic_code = 0;

  setlocale(LC_ALL,"C");

  if (!command) {
    TEES_LOG(TEES_LOG_LEVEL_ERROR, "Invalid tci buffer ptr!\n");
    goto panic;
  }

  if (sizeof(ProtocolCmd) + sizeof(PersObjectCmd) != tci_buffer_len) {
    TEES_LOG(TEES_LOG_LEVEL_ERROR, "Invalid shared buffer size (%u of %u)!\n",
             tci_buffer_len, sizeof(ProtocolCmd) + sizeof(PersObjectCmd));
  } else {
    g_po_cmd_buffer = (PersObjectCmd *)((uint8_t *)tci_buffer + sizeof(ProtocolCmd));
    g_cmd_buffer = (ProtocolCmd *)tci_buffer;

    panic_code = setjmp(g_panic_jmp_buf);
    if (panic_code) {
      command->cmd_ret = TEE_ERROR_TARGET_DEAD;
      tlApiNotify();
    } else {
      /* Trusted Application main loop */
      while (1) {
        tlApiWaitNotification(TLAPI_INFINITE_TIMEOUT);

        memcpy(&g_tmp_tci_buffer, command, sizeof(g_tmp_tci_buffer));
        must_unload = HandleCommand(&g_tmp_tci_buffer);
        memcpy(command, &g_tmp_tci_buffer, sizeof(g_tmp_tci_buffer));

        tlApiNotify();
        /* Multibuild closes TA right after sending UNLOAD command. So we can
         * wait here to get rid of anoying debug message from mcClient which
         * may happen if TA is closed at this step.
         */
        if (MUST_UNLOAD_TA == must_unload) {
          TA_DestroyEntryPoint();
        }
      }
    }
  }

panic:
  // There is no return function
  tlApiExit(1);
}

