/*
 * @file app_main.c
 * @brief BKSECAPP handler entry point
 * Copyright (c) 2015, Samsung Electronics Corporation. All rights reserved.
 */
#include <stdio.h>
#include <comdef.h>
#include <qsee_services.h>
#include <qsee_log.h>
#include <qsee_heap.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
/* 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;
	} 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;

void BK_Hex_dumpss(uint8_t* 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;

	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);

	if(get_bl_boot_complete() != BKERR_BOOTCOM_PROCOM_SUCCESS) {
		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;
	}

	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;
	  case SEC_BOOT_CMD28_SET_BL_BOOT_COMPLETE:
		  rsp_ptr->status = set_bl_boot_complete();
		  break;
	  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_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;
}
