/**
 * Copyright (C) 2011 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Mobile Communication Division,
 * Digital Media & Communications Business, Samsung Electronics Co., Ltd.
 *
 * This software and its documentation are confidential and proprietary
 * information of Samsung Electronics Co., Ltd.  No part of the software and
 * documents may be copied, reproduced, transmitted, translated, or reduced to
 * any electronic medium or machine-readable form without the prior written
 * consent of Samsung Electronics.
 *
 * Samsung Electronics makes no representations with respect to the contents,
 * and assumes no responsibility for any errors that might appear in the
 * software and documents. This publication and the contents hereof are subject
 * to change without notice.
 *
 */

/**
 * @file tz_hdcp2.c
 * @author
 * @date
 * @brief This file contains all the function calls which are defined on SWD side.
 */

#include "tlStd.h"
#include "TlApi/TlApi.h"
#include "tz_hdcp2.h"

/**
 * @def TRUSTLET_MAIN_STACK_SIZE
 * This macro is assigned a value of 2048 to be maintained everywhere.
 */
#define TRUSTLET_MAIN_STACK_SIZE	2048
// Declare 4kb of stack
DECLARE_TRUSTLET_MAIN_STACK(TRUSTLET_MAIN_STACK_SIZE);

/**
 * @def TRUSTLET_MAIN_HEAP_SIZE
 * This macro is assigned a value of 100*2048 to be maintained everywhere.
 */
//#define TRUSTLET_MAIN_HEAP_SIZE	100*1024
// Declare 100kb of stack
#ifndef USE_MC500
#define TRUSTLET_MAIN_HEAP_SIZE	100*1024
DECLARE_TRUSTED_APPLICATION_MAIN_HEAP(TRUSTLET_MAIN_HEAP_SIZE);
#endif
static bool TZ_Initialize = false;

/**
 * @fn int TZ_COMMAND(const uint8_t command, uint8_t *request,const u32 req_size, uint8_t *response, uint32_t *res_size);
 * @brief The control comes to this function when secure world is entered and the command to be executed next is decided here only.
 * @param command - The command to be executed, sent from NWD
 * @param request- pointer to request
 * @param req_size - size of request
 * @param response- pointer to response
 * @param req_size - size of response
 * @return int - HDCP2_OK in case of success, else error code corresponding to the error
 */
int TZ_COMMAND(const uint8_t command, uint8_t *request, const u32 req_size,
			uint8_t *response, uint32_t *res_size)
{
	int ret = HDCP2_ERR_NOT_SUPPORTED_COMMAND;

	TZ_HDCP_DEBUG("[TZ_COMMAND] command : %d\n", command);

	switch (command) {
		case CMD_NULL_MESSAGE + CMD_RECEIVER:
			break;
		case CMD_HDCP2_WRAPKEY + CMD_RECEIVER:
			ret = TZ_HDCP2_WRAPKEY(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_HDCP2_LOADKEY + CMD_RECEIVER:
			ret = TZ_HDCP2_LOADKEY_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_AKE_INIT + CMD_RECEIVER:
			ret = TZ_AKE_Init_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_AKE_SEND_CERT + CMD_RECEIVER:
			ret = TZ_AKE_Send_Cert_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_AKE_NO_STORED_KM + CMD_RECEIVER:
			ret = TZ_AKE_No_Store_km_R(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_STORED_KM + CMD_RECEIVER:
			ret = TZ_AKE_Store_km_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_AKE_SEND_RRX + CMD_RECEIVER:
			ret = TZ_AKE_Send_rrx_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_AKE_SEND_H_PRIME + CMD_RECEIVER:
			ret = TZ_AKE_Send_h_prime_R(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_SEND_PAIRING_INFO + CMD_RECEIVER:
			ret = TZ_AKE_Send_Pairing_Info_R(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_LC_INIT + CMD_RECEIVER:
			ret	 = TZ_LC_Init_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_RTT_READY + CMD_RECEIVER:
			ret = TZ_RTT_READY_R(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_RTT_CHALLENGE + CMD_RECEIVER:
			ret = TZ_RTT_CHALLENGE_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_LC_SEND_L_PRIME + CMD_RECEIVER:
		 ret = TZ_LC_Send_L_prime_R(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_SKE_SEND_EKS + CMD_RECEIVER:
			ret = TZ_SKE_Send_Eks_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_AKE_TRANSMITTER_INFO + CMD_RECEIVER:
			ret = TZ_AKE_Transmitter_Info_R(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_RECEIVER_INFO + CMD_RECEIVER:
			ret = TZ_AKE_Receiver_Info_R(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_REPEATERAUTH_SEND_RECEIVER_ID_LIST + CMD_RECEIVER:
			ret = TZ_RepeaterAuth_Send_ReceiverId_List_Rep(command - CMD_RECEIVER,
						request, req_size, response, res_size);
			break;
		case CMD_REPEATERAUTH_SEND_ACK + CMD_RECEIVER:
			ret = TZ_RepeaterAuth_Send_Ack_Rep(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_RECEIVER_AUTHSTATUS + CMD_RECEIVER:
			ret = TZ_Receiver_AuthStatus_Rep(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
			case CMD_REPEATERAUTH_STREAM_MANAGE+CMD_RECEIVER:
			ret = TZ_RepeaterAuth_Stream_Manage_Rep(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_REPEATERAUTH_STREAM_READY+CMD_RECEIVER:
			ret = TZ_RepeaterAuth_Stream_Ready_Rep(command - CMD_RECEIVER, request,
						req_size, response, res_size);
			break;
		case CMD_CIP_DEC_DATA + CMD_RECEIVER:
			ret = TZ_DEC_Data(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_TRUSTZONE_INIT + CMD_RECEIVER:
			ret = TZ_HW_Init_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			
			if (ret == HDCP2_OK)
				TZ_Initialize = true;
			break;
		case CMD_TRUSTZONE_CLOSE + CMD_RECEIVER:
			ret = TZ_HW_Close_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);

			TZ_Initialize = false;
			break;
		case CMD_TRUSTZONE_RESET + CMD_RECEIVER:
			ret = TZ_HW_Reset_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_CIP_DEC_VIR + CMD_RECEIVER:
			ret = TZ_DEC_Data_Vir(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_CIP_DEC_AUDIO + CMD_RECEIVER:
			ret = TZ_DEC_Data_Audio(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_CIP_SPS_PPS_DATA + CMD_RECEIVER:
			ret = TZ_SPSPPS_COPY(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_SET_HDCP_VERSION + CMD_RECEIVER:
			ret = TZ_SET_HDCP_VERSION_R(command - CMD_RECEIVER, request, req_size,
						response, res_size);
		break;
		case CMD_NULL_MESSAGE + CMD_TRANSMITTER:
			break;
		case CMD_HDCP2_LOADKEY + CMD_TRANSMITTER:
			ret = TZ_HDCP2_LOADKEY_T(command - CMD_RECEIVER, request, req_size,
						response, res_size);
			break;
		case CMD_AKE_SET_PAIRING_INFO + CMD_TRANSMITTER:
			ret = TZ_AKE_SET_PAIRING_INFO_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_INIT + CMD_TRANSMITTER:
			ret = TZ_AKE_Init_T(command - CMD_TRANSMITTER, request, req_size,
						response, res_size);
			break;
		case CMD_AKE_SEND_CERT + CMD_TRANSMITTER:
			ret = TZ_AKE_Send_Cert_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_NO_STORED_KM + CMD_TRANSMITTER:
			ret = TZ_AKE_No_Store_km_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_STORED_KM + CMD_TRANSMITTER:
			ret = TZ_AKE_Store_km_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_SEND_RRX + CMD_TRANSMITTER:
			ret = TZ_AKE_Send_rrx_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_SEND_H_PRIME + CMD_TRANSMITTER:
			ret = TZ_AKE_Send_h_prime_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_SEND_PAIRING_INFO + CMD_TRANSMITTER:
			ret = TZ_AKE_Send_Pairing_Info_T(command - CMD_TRANSMITTER,
						request, req_size, response, res_size);
			break;
		case CMD_LC_INIT + CMD_TRANSMITTER:
			ret = TZ_LC_Init_T(command - CMD_TRANSMITTER, request, req_size,
						response, res_size);
			break;
		case CMD_RTT_CHALLENGE + CMD_TRANSMITTER:
			ret = TZ_RTT_Challenge_T(command - CMD_TRANSMITTER, request, req_size,
						response, res_size);
			break;
		case CMD_LC_SEND_L_PRIME + CMD_TRANSMITTER:
			ret = TZ_LC_Send_L_prime_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_SKE_SEND_EKS + CMD_TRANSMITTER:
			ret = TZ_SKE_Send_Eks_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_TRANSMITTER_INFO + CMD_TRANSMITTER:
			ret = TZ_AKE_Transmitter_Info_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_AKE_RECEIVER_INFO + CMD_TRANSMITTER:
			ret = TZ_AKE_Receiver_Info_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_REPEATERAUTH_SEND_RECEIVER_ID_LIST + CMD_TRANSMITTER:
			ret = TZ_RepeaterAuth_Send_ReceiverId_List_T(
						command - CMD_TRANSMITTER, request, req_size, response,
						res_size);
			break;
		case CMD_REPEATERAUTH_SEND_ACK + CMD_TRANSMITTER:
			ret = TZ_RepeaterAuth_Send_Ack_T(command - CMD_TRANSMITTER,
						request, req_size, response, res_size);
			break;
		case CMD_RECEIVER_AUTHSTATUS + CMD_TRANSMITTER:
			ret = TZ_Receiver_AuthStatus_T(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_REPEATERAUTH_STREAM_MANAGE + CMD_TRANSMITTER:
			ret = TZ_RepeaterAuth_Stream_Manage_T(command - CMD_TRANSMITTER,
						request, req_size, response, res_size);
			break;
		case CMD_REPEATERAUTH_STREAM_READY + CMD_TRANSMITTER:
			ret = TZ_RepeaterAuth_Stream_Ready_T(command - CMD_TRANSMITTER,
						request, req_size, response, res_size);
			break;
		case CMD_CIP_ENC_DATA + CMD_TRANSMITTER:
			ret = TZ_ENC_Data(command - CMD_TRANSMITTER, request, req_size,
						response, res_size);
			break;
		case CMD_TRUSTZONE_INIT + CMD_TRANSMITTER:
			ret = TZ_HW_Init_T(command - CMD_TRANSMITTER, request, req_size,
						response, res_size);
			
			if (ret == HDCP2_OK)
				TZ_Initialize = true;
			break;
		case CMD_TRUSTZONE_CLOSE + CMD_TRANSMITTER:
			ret = TZ_HW_Close_T(command - CMD_TRANSMITTER, request, req_size,
						response, res_size);

			TZ_Initialize = false;
			break;
		case CMD_SET_HDCP2_MAGICKEY + CMD_TRANSMITTER:
			ret = TZ_DRM_Set_HDCP2_MagicKey(command - CMD_TRANSMITTER, request,
						req_size, response, res_size);
			break;
		case CMD_OEM_GET_FLAG + CMD_TRANSMITTER:
			ret = TZ_OEM_GET_FLAG(command - CMD_TRANSMITTER, request, req_size,
						response, res_size);
			break;
		case CMD_SET_HDCP_VERSION + CMD_TRANSMITTER:
			ret = TZ_SET_HDCP_VERSION_T(command - CMD_TRANSMITTER, request, req_size,
						response, res_size);
			break;
		default:
			break;
	}

	return ret;
}

#ifdef ARM64
uint64_t __stack_chk_guard = 0;
#else //ARM32
uint32_t __stack_chk_guard = 0;
#endif /* ARM64 */

void stack_protection_init(void)
{
	size_t length = sizeof(__stack_chk_guard);
	tlApiRandomGenerateData(TLAPI_ALG_SECURE_RANDOM, (uint8_t *)&__stack_chk_guard, &length);
}

#ifdef CONFIG_USE_STACK_CHECK_FUNC
void __stack_chk_fail(void)
{
	TZ_HDCP_LOG("Stack smashing detected");
	abort();
}
#endif /* CONFIG_USE_STACK_CHECK_FUNC */

/**
 * @fn void TLAPI_ENTRY void tlMain(const addr_t tciBuffer, const uint32_t tciBufferLen)
 * @brief This function makes the trustlet entry
 * @param tciBuffer -  structure used to describe an internet address.
 * @param tciBufferLen - length of buffer
 * @return void
 */
_TLAPI_ENTRY void tlMain(const addr_t tciBuffer, const uint32_t tciBufferLen)
{
	tciCommandId_t commandId;
	uint8_t *request;
	uint32_t req_size;
	bool use_static_buffer = false;

	stack_protection_init();
	if ((NULL == tciBuffer) || (sizeof(tci_t) > tciBufferLen)) {
		TZ_HDCP_LOG("invalid TCI (got 0x%08X, %d, need %d), exit",
				tciBuffer, tciBufferLen, sizeof(tci_t));
		tlApiExit(EXIT_ERROR);
	}

	tci_t* tci = (tci_t*) tciBuffer;

	for (;;) {
		tlApiWaitNotification(TLAPI_INFINITE_TIMEOUT);
		commandId = tci->message.commandHeader.commandId;

		if (!IS_CMD(commandId)) {
			tlApiNotify();
			continue;
		}

		tci->message.response.length = 0;
		tci->message.responseHeader.responseId = RSP_ID(commandId);

		switch (commandId) {
			case (CMD_TRUSTZONE_INIT + CMD_RECEIVER) :
			case (CMD_TRUSTZONE_INIT + CMD_TRANSMITTER) :
				use_static_buffer = true;
				break;
			case (CMD_HDCP2_WRAPKEY + CMD_RECEIVER) :
			case (CMD_HDCP2_LOADKEY + CMD_RECEIVER) :
			case (CMD_HDCP2_LOADKEY + CMD_TRANSMITTER) :
				use_static_buffer = false;
				break;
			default :
				if (!TZ_Initialize) {
					TZ_HDCP_LOG("Tlhdcp2::doesn't initialize secure world & memory\n");
					tlApiExit(EXIT_ERROR);
				}
				
				if (!tci->message.request.length) {
					TZ_HDCP_LOG("Tlhdcp2::invalid msg size [%d]\n", tci->message.request.length);
					tlApiExit(EXIT_ERROR);
				}
				use_static_buffer = false;
				break;
		}

		if (use_static_buffer) {
			request = tci->message.request_static.buffer + tci->message.request_static.offset;
			req_size = tci->message.request_static.length;
		} else {
			request = tci->message.request.buffer + tci->message.request.offset;
			req_size = tci->message.request.length;
		}

		if (!tlApiIsNwdBufferValid((addr_t)request, req_size)) {
			TZ_HDCP_LOG("Tlhdcp2::invalid nwd memory : 0x%08x\n", tci->message.request.buffer);
			tlApiExit(EXIT_ERROR);
		}

		tci->message.responseHeader.returnCode = TZ_COMMAND(commandId, request, req_size,
													tci->message.response.buffer, &tci->message.response.length);
		
		tlApiNotify();
	}
}
