/*
 * =====================================================================================
 *
 *       Filename:  mcAppMain.c
 *
 *    Description:  Entry point of Tbase trustzone app.
 *
 *        Version:  1.0
 *        Created:  03/28/2017 03:44:55 PM
 *       Compiler:  armcc
 *
 *         Author:  Dongwook Shim (), dw.shim@samsung.com
 *        Company:  Samsung Electronics
 *
 *        Copyright (c) 2017 by Samsung Electronics, All rights reserved. 
 *
 * =====================================================================================
 */

#include "tlStd.h"
#include "TlApi/TlApi.h"
#include "TlApi/TlApiMcSystem.h"

#include "commonConfig.h"
#include "cryptoEngine.h"
#include "log.h"
#include "mcStackProtection.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

#define TRUSTLET_MAIN_STACK_SIZE 0x10000
DECLARE_TRUSTLET_MAIN_STACK(TRUSTLET_MAIN_STACK_SIZE)

_TLAPI_ENTRY void tlMain(const addr_t tciBuffer, const uint32_t tciBufferLen)
{
	int32_t ret;
	CmdReq_t req;
	CmdRsp_t rsp;
	const size_t maxDataSize = getTaMaxDataSize(TA_PROV);

	// Print trustzone app's name and version.
	VERSIONIZE(TRUSTLET_TAG);

	// Apply stack protection - requested by yj0729.kim at 17.12.05.
	initStackProtection();

	// Check shared buffer.
	if(tciBuffer == NULL)
	{
		LOGE("Invalid TCI data - %d.", (int)tciBufferLen);
		ret = ERR_TA_INVALID_ARGUMENT;
		tlApiExit((uint32_t)ret);
	}

	if(!tlApiIsNwdBufferValid(tciBuffer, tciBufferLen))
	{
		LOGE("Invalid TCI buffer range (got 0x%08p, %d)", tciBuffer, (int)tciBufferLen);
		ret = ERR_TA_INVALID_ARGUMENT;
		tlApiExit((uint32_t)ret);
	}

	// Allocator initialize.
	if((ret = secMemoryManagerInit()) != NOT_ERROR)
		tlApiExit(ret);

	// crypto engine init
	CRYPTO_init();

	// Infinite loop waiting for MC TEE driver notification.
	while(1)
	{
		// Wait fo "ready to go" event from MC driver.
		tlApiWaitNotification(TLAPI_INFINITE_TIMEOUT);

		// Copy data to secure world. - Riscure request.
		memset(&req, 0, sizeof(req));
		memset(&rsp, 0, sizeof(rsp));

		if((req.data = (uint8_t *)secMemoryManagerMalloc(maxDataSize)) == NULL)
		{
			LOGE("Failed to allocate memory.");
			ret = ERR_TA_NOT_ENOUGH_MEMORY;
			tlApiExit((uint32_t)ret);
		}

		if((rsp.data = (uint8_t *)secMemoryManagerMalloc(maxDataSize)) == NULL)
		{
			LOGE("Failed to allocate memory.");
			secMemoryManagerFree(req.data);
			ret = ERR_TA_NOT_ENOUGH_MEMORY;
			tlApiExit((uint32_t)ret);
		}

		memset(req.data, 0, maxDataSize);
		memset(rsp.data, 0, maxDataSize);

		memcpy(&req, tciBuffer, TA_BUFFER_HEADER_LEN);

		if(req.dataLen > maxDataSize)
		{
			LOGE("Request data length (%d) is too big Invalid argument.",req.dataLen);
			secMemoryManagerFree(req.data);
			secMemoryManagerFree(rsp.data);
			ret = ERR_TA_BUFFER_OVERFLOW;
			tlApiExit((uint32_t)ret);
		}

		memcpy(req.data, (char *)tciBuffer + TA_BUFFER_HEADER_LEN, 
			(tciBufferLen - TA_BUFFER_HEADER_LEN < maxDataSize) ?  tciBufferLen - TA_BUFFER_HEADER_LEN : 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;
			}
#else
			((CmdPtrRsp_t *)tciBuffer)->status = rsp.status;
			goto cleanup;
#endif  // End of DISABLE_LOG_ENCRYPTION
		}

		// Treat out buffer size equal to max shared buffer length.
		if(rsp.dataLen > maxDataSize || (TA_BUFFER_HEADER_LEN + rsp.dataLen) > tciBufferLen)
		{
			LOGE("Wrapped data is too big (%d bytes)", rsp.dataLen);
			((CmdPtrRsp_t *)tciBuffer)->status = ERR_TA_BUFFER_OVERFLOW;
			goto cleanup;
		}

		//  Copy out data from temp static buffer to shared buffer.
		memcpy((char *)tciBuffer, &rsp, TA_BUFFER_HEADER_LEN);
		memcpy((char *)tciBuffer + 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.data, 0, maxDataSize);
		memset(rsp.data, 0, maxDataSize);
		secMemoryManagerFree(req.data);
		secMemoryManagerFree(rsp.data);
		memset(&req, 0, sizeof(req));
		memset(&rsp, 0, sizeof(rsp));

		tlApiNotify();
	}
}
