/*
 * =====================================================================================
 *
 *       Filename:  qseeAppMain.c
 *
 *    Description:  Entry point of QSEE trustzone app.
 *
 *        Version:  1.0
 *        Created:  04/26/2017 05:19:18 PM
 *       Compiler:  armcc
 *
 *         Author:  Dongwook Shim (), dw.shim@samsung.com
 *        Company:  Samsung Electronics
 *
 *        Copyright (c) 2017 by Samsung Electronics, All rights reserved. 
 *
 * =====================================================================================
 */

#include "qsee_core.h"

#include "commonConfig.h"
#include "cryptoEngine.h"
#include "log.h"
#include "secMemoryManager.h"
#include "taConfig.h"
#include "teeCmdExecuter.h"
#include "version.h"
#include "qseeSecureState.h"

#ifndef DISABLE_LOG_ENCRYPTION
#include "logEncryptor.h"
#endif  // End of DISABLE_LOG_ENCRYPTION

char TZ_APP_NAME[] = "prov";

void tz_app_init(void)
{
	VERSIONIZE(TRUSTLET_TAG);
}

void tz_app_shutdown(void)
{
	LOGD("Unloading %s trustlet...", TRUSTLET_TAG);
}

void tz_app_cmd_handler(void *req, uint32_t reqLen, void *rsp, uint32_t rspLen)
{
	int32_t ret;
	CmdReq_t reqBuf;
	CmdRsp_t rspBuf;
	const size_t maxDataSize = getTaMaxDataSize(TA_PROV);

	if(req == NULL || rsp == NULL)
	{
		LOGE("Buffer is invalid - %d %d.", reqLen, rspLen);
		return;
	}

	if(qsee_is_ns_range(req, reqLen) || qsee_is_ns_range(rsp, rspLen))
	{
		LOGE("Buffer is located at non-secure memory.");
		((CmdPtrRsp_t *)rsp)->status = ERR_TA_NOT_SECURE;
		return;
	}

	if(!checkBspSecureState())
	{
		((CmdPtrRsp_t *)rsp)->status = ERR_SECURE_BOOT_DISABLED;
		return;
	}

	// Allocator initialize.
	if((ret = secMemoryManagerInit()) != NOT_ERROR)
	{
		((CmdPtrRsp_t *)rsp)->status = ret;
		return;
	}

	// crypto engine init
	CRYPTO_init();
	
	memset(&reqBuf, 0, sizeof(reqBuf));
	memset(&rspBuf, 0, sizeof(rspBuf));

	if((reqBuf.data = (uint8_t *)secMemoryManagerMalloc(maxDataSize)) == NULL)
	{
		LOGE("Failed to allocate memory.");
		((CmdPtrRsp_t *)rsp)->status = ERR_TA_NOT_ENOUGH_MEMORY;
		return;
	}

	if((rspBuf.data = (uint8_t *)secMemoryManagerMalloc(maxDataSize)) == NULL)
	{
		LOGE("Failed to allocate memory.");
		secMemoryManagerFree(reqBuf.data);
		((CmdPtrRsp_t *)rsp)->status = ERR_TA_NOT_ENOUGH_MEMORY;
		return;
	}

	memset(reqBuf.data, 0, maxDataSize);
	memset(rspBuf.data, 0, maxDataSize);

	memcpy(&reqBuf, req, TA_BUFFER_HEADER_LEN);

	if(reqBuf.dataLen > maxDataSize)
	{
		LOGE("Request data length (%d) is too big Invalid argument.",reqBuf.dataLen);
		((CmdPtrRsp_t *)rsp)->status = ERR_TA_NOT_ENOUGH_MEMORY;
		goto cleanup;
	}

	memcpy(reqBuf.data, (char *)req + TA_BUFFER_HEADER_LEN,
		(reqLen - TA_BUFFER_HEADER_LEN < maxDataSize) ? reqLen - TA_BUFFER_HEADER_LEN : maxDataSize);
	rspBuf.dataLen = maxDataSize;

	LOGD("Received Cmd = 0x%x with %d bytes in request buffer.", reqBuf.cmdId, reqBuf.dataLen);

	if((rspBuf.status = taCmdExecute(reqBuf.cmdId, reqBuf.data, reqBuf.dataLen, rspBuf.data, &(rspBuf.dataLen))) != NOT_ERROR)
	{
		LOGE("Invoke Command data caused error! Ret = %d", rspBuf.status);
#ifndef DISABLE_LOG_ENCRYPTION
		if((ret = logEncrypt(rspBuf.data, maxDataSize)) > 0)
			rspBuf.dataLen = (uint32_t)ret;
		else
		{
			LOGE("Failed to log encryption with error %d.", ret);
			rspBuf.dataLen = 0;
		}
#else
		((CmdPtrRsp_t *)rsp)->status = rspBuf.status;
			goto cleanup;
#endif  // End of DISABLE_LOG_ENCRYPTION
	}

	// Treat out buffer size equal to max shared buffer length.
	if(rspBuf.dataLen > maxDataSize || (TA_BUFFER_HEADER_LEN + rspBuf.dataLen) > rspLen)
	{
		LOGE("Wrapped data is too big (%d bytes)", rspBuf.dataLen);
		((CmdPtrRsp_t *)rsp)->status = ERR_TA_BUFFER_OVERFLOW;
		goto cleanup;
	}

	//  Copy out data from temp static buffer to shared buffer.
	memcpy((char *)rsp, &rspBuf, TA_BUFFER_HEADER_LEN);
	memcpy((char *)rsp + TA_BUFFER_HEADER_LEN, rspBuf.data, (rspBuf.dataLen < maxDataSize) ? rspBuf.dataLen : maxDataSize);

	LOGD("Respond from Cmd = 0x%x with %d bytes in Cmd Buffer", reqBuf.cmdId, rspBuf.dataLen);

cleanup:
	memset(reqBuf.data, 0, maxDataSize);
	memset(rspBuf.data, 0, maxDataSize);
	secMemoryManagerFree(reqBuf.data);
	secMemoryManagerFree(rspBuf.data);
	memset(&reqBuf, 0, sizeof(reqBuf));
	memset(&rspBuf, 0, sizeof(rspBuf));
}
