/*
 * @file app_main.c
 * @brief BKSECAPP handler entry point
 * Copyright (c) 2015, Samsung Electronics Corporation. All rights reserved.
 */
#include <stdio.h>
#include <qsee_services.h>
#include <qsee_log.h>
#include <qsee_heap.h>
#include <qsee_core.h>
#include <bksecapp_common.h>

#define SEC_BOOT_CMD0_GET_WARRANTY_BIT           0
#define SEC_BOOT_CMD1_SET_WARRANTY_BIT           1
/* un used command Deprecated
#define SEC_BOOT_CMD7_GET_RP_ENABLE              7
#define SEC_BOOT_CMD8_GET_RP_IGNORE              8
#define SEC_BOOT_CMD9_SET_ICCC_BL_DATA           9   // not support
#define SEC_BOOT_CMD10_SET_ICCC_ROT_BL_DATA      10  // not support
#define SEC_BOOT_CMD33_SET_ICCC_SVB_BL_DATA      33 // not support
#define SEC_BOOT_CMD35_SET_ICCC_KERN_DATA        35 // not support
#define SEC_BOOT_CMD11_IS_SECBOOT_ENABLE         11
#define SEC_BOOT_CMD12_STORE_MEASURE             12
#define SEC_BOOT_CMD13_GET_RP_COUNT_XBL          13
#define SEC_BOOT_CMD14_GET_RP_COUNT_PIL          14
#define SEC_BOOT_CMD15_GET_RP_COUNT_RPM          15
#define SEC_BOOT_CMD16_GET_RP_COUNT_TZ           16
#define SEC_BOOT_CMD17_GET_RP_COUNT_HYP          17
#define SEC_BOOT_CMD18_GET_RP_COUNT_DP           18
#define SEC_BOOT_CMD19_GET_RP_COUNT_DEV_CFG      19
#define SEC_BOOT_CMD20_GET_RP_COUNT_ABL          20
#define SEC_BOOT_CMD21_GET_RPMB_FUSE             21
#define SEC_BOOT_CMD22_GET_RP_COUNT_PMIC         22
*/
#define SEC_BOOT_CMD24_GET_EK_FUSE               24
#define SEC_BOOT_CMD25_SET_EK_FUSE_DEV           25
#define SEC_BOOT_CMD26_SET_EK_FUSE_USER          26
#define SEC_BOOT_CMD27_SET_EK_FUSE_FORCE_USER    27
#define SEC_BOOT_CMD28_SET_BL_BOOT_COMPLETE      28
#define SEC_BOOT_CMD29_WRAP_KEY_DATA             29
#define SEC_BOOT_CMD30_UNWRAP_KEY_DATA           30

#define SEC_BOOT_CMD50_GET_KDF                   50
#define SEC_BOOT_CMD51_GET_FUSE                  51
/* un used command Deprecated
#define SEC_BOOT_CMD31_GET_MEM_ACC_FUSE          31
#define SEC_BOOT_CMD32_GET_RP_COUNT_XBL_CONFIG   32
#define SEC_BOOT_CMD34_SET_VBMETA_DIGEST         34
*/
#define SEC_BOOT_CMD36_STORE_RPMB_DATA           36
#define SEC_BOOT_CMD37_READ_RPMB_DATA            37
#define SEC_BOOT_CMD100_SET_KASLR_DATA           100

/**
 * Modify the app name to your specific app name
 */
char TZ_APP_NAME[] = {"bksecapp"};

static uint8_t qsee_log_mask;
/**
  @brief
	Add any app specific initialization code here
	QSEE will call this function after secure app is loaded and
	authenticated
*/
void tz_app_init(void)
{
  qsee_log_mask = qsee_log_get_mask();
  qsee_log_set_mask(QSEE_LOG_MSG_ERROR | QSEE_LOG_MSG_FATAL | QSEE_LOG_MSG_DEBUG);
  /*  App specific initialization code*/
  qsee_log(QSEE_LOG_MSG_DEBUG, "BKSECAPP Init ");
}

/**
@brief
Data structure

@param[in]   cmd_id    Requested command
@param[in]   data      information (could be data or a pointer to the memory that holds the data
@param[in]   len       if data is ptr to some buffer, len indicates length of the buffer
@param[in]   test_buf_size  When running crypto test, this indicates the test packet size
*/

typedef struct send_cmd{
	uint32_t cmd_id;
	union {
		uint8_t max_req_size[BKSECAPP_MAX_REQ_SIZE];
		wrap_unwrap_key_status_t bl_secure_info_key;
		kaslr_bl_status_t bl_secure_info2;
		rpmb_data_t rpmb_data;
		bksecapp_kdf_req_t kdf_in_data;
		uint32_t fuse_address;
	} data;
} send_cmd_t;

typedef struct send_cmd_rsp {
	int32_t status;
	union {
		uint32_t data;
		uint8_t max_rsp_size[BKSECAPP_MAX_RSP_SIZE];
		wrap_unwrap_key_status_t wrapdata;
		rpmb_data_t rpmb_data;
		bksecapp_kdf_rsp_t kdf_out_data;
	} data;
} send_cmd_rsp_t;

#define SECBOOT_FUSE 0
#define SHK_FUSE 1
#define DEBUG_DISABLED_FUSE 2
#define ANTI_ROLLBACK_FUSE 3
#define FEC_ENABLED_FUSE 4
#define RPMB_ENABLED_FUSE 5
#define DEBUG_RE_ENABLED_FUSE 6
#define CHECK_BIT(var, pos) ((var) & (1 << (pos)))

static int32_t get_security_state(void)
{
   int32_t ret = 0;
   qsee_secctrl_secure_status_t status;
   memset(&status, 0, sizeof(status));

   ret = qsee_get_secure_state(&status);
   qsee_log(QSEE_LOG_MSG_DEBUG, "qsee_get_secure_state() pass(=1): %d, val: %x %x",
     (ret == 0), status.value[0], status.value[1]);

   return status.value[0];
}

void BK_Hex_dumpss(void* log_ptr, uint32_t log_size)
{
	uint8_t *address = (uint8_t *)log_ptr;
	uint64_t count,i;
	char TempBuf[256];
	uint32_t Offset = 0;
	uint32_t MAXLogBufferSize = 256;

	for(count = 0; count < log_size; count += 16)
	{
		memset(&TempBuf, 0, sizeof(TempBuf));
		for(i = 0; i < 16; i++)
		{
			if(count + i >= log_size)
				Offset += snprintf(TempBuf + Offset, MAXLogBufferSize - Offset, "   ");
			else
				Offset += snprintf(TempBuf + Offset, MAXLogBufferSize - Offset, "%2x ", *(address+count+i));

		}
		Offset += snprintf(TempBuf + Offset, MAXLogBufferSize - Offset, " |");

		for(i = 0; i < 16; i++)
		{
			if(count + i >= log_size)
				Offset += snprintf(TempBuf + Offset, MAXLogBufferSize - Offset, " ");
			else
			{
				char c = *(const char *)(address+count+i);
				if(((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')))
					Offset += snprintf(TempBuf + Offset, MAXLogBufferSize - Offset, "%c",c);
				else
					Offset += snprintf(TempBuf + Offset, MAXLogBufferSize - Offset, ".");				
			}
		}
		Offset += snprintf(TempBuf + Offset, MAXLogBufferSize - Offset, "|");				

		Offset = 0;
		qsee_log(QSEE_LOG_MSG_ERROR, "%s\n", TempBuf);
	}
}

/**
  @brief
	App specific command handler
	App executes code based on input command
*/
void tz_app_cmd_handler(void* cmd, uint32_t cmdlen,
                        void* rsp, uint32_t rsplen)
{
	uint32_t cmd_id = 0;
	uint32_t expected_cmdlen;
	uint32_t expected_rpslen;
	int32_t is_secure_boot = 0;

	struct send_cmd       *cmd_ptr = NULL;
	struct send_cmd_rsp   *rsp_ptr = NULL;
	struct send_cmd_rsp   *error_rsp = (struct send_cmd_rsp *)rsp;


	qsee_log(QSEE_LOG_MSG_DEBUG, "BKSECAPP Handler start ");

	expected_cmdlen = sizeof(send_cmd_t);
	expected_rpslen = sizeof(send_cmd_rsp_t);

	if(cmdlen != expected_cmdlen)
	{
	  qsee_log(QSEE_LOG_MSG_FATAL, "Cmd buffer len insufficient - %x, expected - %x, ERROR OUT", cmdlen, expected_cmdlen);
	  error_rsp->status = BKERR_CMD_HANDLER_NOTMATCH_REQLEN;
	  goto out;
	}

	if( rsplen != expected_rpslen)
	{
	  qsee_log(QSEE_LOG_MSG_FATAL, "rsp buffer len insufficient - %x, expected - %x, ERROR OUT", rsplen, expected_rpslen);
	  error_rsp->status = BKERR_CMD_HANDLER_NOTMATCH_RSPLEN;
	  goto out;
	}

	if(cmd == NULL)
	{
	  qsee_log(QSEE_LOG_MSG_FATAL, "Cmd/ buffer NULL - %x, ERROR OUT", cmd);
	  error_rsp->status = BKERR_CMD_HANDLER_NULLPOINT_CMD;
	  goto out;
	}

	if(rsp == NULL)
	{
	  qsee_log(QSEE_LOG_MSG_FATAL, "rsp buffer NULL - %x, ERROR OUT", rsp);
	  error_rsp->status = BKERR_CMD_HANDLER_NULLPOINT_RSP;
	  goto out;
	}

	cmd_ptr = (struct send_cmd *)qsee_malloc(cmdlen);
	rsp_ptr = (struct send_cmd_rsp *)qsee_malloc(rsplen);

	if (!cmd_ptr)
	{
	  qsee_log(QSEE_LOG_MSG_FATAL, "Command pointer equals NULL !!");
	  error_rsp->status = BKERR_CMD_HANDLER_CMDBUFFER_ALLOC_FAIL;
	  goto out;
	}
	memset((void *)cmd_ptr, 0, cmdlen);	
	memcpy((void *)cmd_ptr, cmd, cmdlen);

	if (!rsp_ptr)
	{
	  qsee_log(QSEE_LOG_MSG_FATAL, "Command pointer equals NULL !!");
	  error_rsp->status = BKERR_CMD_HANDLER_RSPBUFFER_ALLOC_FAIL;
	  goto out;
	}
	memset((void *)rsp_ptr, 0, rsplen);
	memcpy((void *)rsp_ptr, rsp, rsplen);

	/*Determine the command id*/
	/*We check if the command buffer is large enough to support the uint32_t read for cmd_id*/
	/*It is assumed that the first member of the command buffer is the cmd_id*/
	if(cmdlen < sizeof(uint32_t))
	{
	  qsee_log(QSEE_LOG_MSG_FATAL, "Command buffer len insufficient for reading cmd_id, ERROR OUT");
	  rsp_ptr->status = BKERR_CMD_HANDLER_CMDBUF_SMALL_FOR_CMDID;
	  goto out;
	}

	cmd_id = cmd_ptr->cmd_id;
	qsee_log(QSEE_LOG_MSG_DEBUG, "TZ App cmd handler, cmd_id = %d", cmd_id);

	is_secure_boot = !CHECK_BIT(get_security_state(), SECBOOT_FUSE);

#ifndef BKSECAPP_BOOT_COMPLETE_DISBALE
	if((get_bl_boot_complete() != BKERR_BOOTCOM_PROCOM_SUCCESS) && is_secure_boot) {
		qsee_log(QSEE_LOG_MSG_FATAL, "BL boot completed. Operation not allowed! cmd_id = %d", cmd_id);
		rsp_ptr->status = BKERR_CMD_HANDLER_BLOCK_WORKING_DUETO_BOOTCOMPLETE;
		goto out;
	}
#endif

	switch(cmd_id)
	{
		case SEC_BOOT_CMD0_GET_WARRANTY_BIT :
			rsp_ptr->status = get_warranty_bit_fuse();
			break;
		case SEC_BOOT_CMD1_SET_WARRANTY_BIT :
			rsp_ptr->status = set_warranty_bit_fuse();
			break;
		case SEC_BOOT_CMD24_GET_EK_FUSE :
			rsp_ptr->status = get_ek_fuse();
			break;
		case SEC_BOOT_CMD25_SET_EK_FUSE_DEV :
			rsp_ptr->status = set_ek_fuse(EK_FUSE_DEV);
			break;
		case SEC_BOOT_CMD26_SET_EK_FUSE_USER :
			rsp_ptr->status = set_ek_fuse(EK_FUSE_USER);
			break;
		case SEC_BOOT_CMD27_SET_EK_FUSE_FORCE_USER :
			rsp_ptr->status = get_ek_fuse();
			if(rsp_ptr->status == EK_FUSE_DEV)
				rsp_ptr->status = set_ek_fuse(EK_FUSE_FORCE_USER);
			else
				qsee_log(QSEE_LOG_MSG_DEBUG, "[EM] Fail to force EK_FUSE_USER %d", rsp_ptr->status);
			break;
#ifndef BKSECAPP_BOOT_COMPLETE_DISBALE
		case SEC_BOOT_CMD28_SET_BL_BOOT_COMPLETE:
			rsp_ptr->status = set_bl_boot_complete();
			break;
#endif
		case SEC_BOOT_CMD29_WRAP_KEY_DATA:
			rsp_ptr->status = bk_wrap_function((void*)&cmd_ptr->data.bl_secure_info_key,(void*)&rsp_ptr->data.wrapdata);
			break;
		case SEC_BOOT_CMD30_UNWRAP_KEY_DATA:
			rsp_ptr->status = bk_unwrap_function((void*)&cmd_ptr->data.bl_secure_info_key,(void*)&rsp_ptr->data.wrapdata);
			break;
		case SEC_BOOT_CMD36_STORE_RPMB_DATA :
			rsp_ptr->status = bksecapp_rpmb_write((void*)&cmd_ptr->data.rpmb_data);
			break;
		case SEC_BOOT_CMD37_READ_RPMB_DATA :
			rsp_ptr->status = bksecapp_rpmb_read((void*)&rsp_ptr->data.rpmb_data);
			break;
		case SEC_BOOT_CMD50_GET_KDF :
			rsp_ptr->status = bksecapp_kdf((void*)&cmd_ptr->data.kdf_in_data,(void*)&rsp_ptr->data.kdf_out_data);
			break;
		case SEC_BOOT_CMD51_GET_FUSE :
			rsp_ptr->status = bksecapp_get_fuse(cmd_ptr->data.fuse_address);
			break;
		case SEC_BOOT_CMD100_SET_KASLR_DATA :
			rsp_ptr->status = set_kaslr_flag((void*)&cmd_ptr->data.bl_secure_info2);
			break;
		default :
			rsp_ptr->status = BKERR_CMD_HANDLER_UNDEFIND_CMDID;
			qsee_log(QSEE_LOG_MSG_DEBUG, "Unsupported command: %d.  Test not yet implemented or commented out.", cmd_ptr->cmd_id);
	}

out:
	if(cmd_ptr)
		qsee_free(cmd_ptr);

	if(rsp_ptr) {
		memcpy(rsp, (void *)rsp_ptr, rsplen);
		qsee_free(rsp_ptr);
	}
	qsee_log(QSEE_LOG_MSG_DEBUG, "BKSECAPP Handler End");
}
/**
  @brief
	App specific shutdown
	App will be given a chance to shutdown gracefully
*/
void tz_app_shutdown(void)
{
  /* app specific shutdown code*/
  qsee_log(QSEE_LOG_MSG_DEBUG, "BKSECAPP shutdown");
  return;
}
