/*
 * @file app_main.c
 * @brief BLGRD handler entry point
 * Copyright (c) 2015, Samsung Electronics Corporation. All rights reserved.
 */
#include <comdef.h>
#include "qsee_log.h"
#include <qsee_services.h>
#include "qsee_oem_buffer.h"
#include "qsee_heap.h"
//#include "TZ_Vendor_tl.h"

#include "app_grdm.h"
#include "app_grdm_rpmb.h"

#define PAGE_SIZE (4096)
#define PAGE_MASK (~(PAGE_SIZE -1))

#define SEC_GRDM_CMD0_SET_BL_ROT_DATA      	   0
#define SEC_GRDM_CMD1_SET_BL_FUSE              1

#define SEC_GRDM_CMD11_FW_UPDATE               11
#define SEC_GRDM_CMD12_GET_FW_VERSION          12

#define SEC_GRDM_CMD21_GET_DATA                21
#define SEC_GRDM_CMD22_SET_DATA                22
#if 0 //def GRDM_DEBUG_DATA
#define SEC_GRDM_CMD30_GET_BL_ROT_DATA          30
#endif
#define SEC_GRDM_CMD50_FDC_CHECK_STATUS        50
#define SEC_GRDM_CMD51_FDC_CHECK_RESTRICT      51

#ifdef GRDM_CHECK_FACTORY
#define SEC_GRDM_CMD200_FACTORY_CHECK          200
#endif

#ifdef GRDM_RESET
#define SEC_GRDM_CMD300_RESET                  300
#endif

#define FAIL_CMD_BUF_TOO_SMALL_FOR_CMD_ID   -1
#define FAIL_CMD_RSP_BUFS_TOO_SMALL         -2
#define FAIL_CMD_BL_BOOT_COMPLETE           -3
#define FAIL_CMD_BUFFER_ALLOC_FAIL          -4
#define FAIL_UNDEFINED_COMMAND              -99

/**
 * Modify the app name to your specific app name
 */
char TZ_APP_NAME[] = {"blgrd"};

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, "BLGRD 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
*/

/**
  @brief
    App specific command handler
    App executes code based on input command
*/
void tz_app_cmd_handler(void* cmd, uint32 cmdlen,
                        void* rsp, uint32 rsplen)
{
	uint32 cmd_id = 0;
	uint32 expected_cmdlen;
	uint32 expected_rpslen;

	blgrd_send_cmd_t       *cmd_ptr = NULL;
	blgrd_send_cmd_rsp_t   *rsp_ptr = NULL;
	blgrd_send_cmd_rsp_t   *error_rsp = (blgrd_send_cmd_rsp_t *)rsp;

    qsee_log(QSEE_LOG_MSG_FATAL, "cmdlen : %d, rsplen : %d", cmdlen, rsplen);
    qsee_log(QSEE_LOG_MSG_FATAL, "blgrd_send_cmd_t : %d, rsplen : %d", (uint32_t)sizeof(blgrd_send_cmd_t), (uint32_t)sizeof(blgrd_send_cmd_rsp_t));

	cmd_ptr = (blgrd_send_cmd_t *)qsee_malloc(cmdlen);
	rsp_ptr = (blgrd_send_cmd_rsp_t *)qsee_malloc(rsplen);


	if (!cmd_ptr)
	{
	  qsee_log(QSEE_LOG_MSG_FATAL, "Command pointer equals NULL !!");
	  error_rsp->status = FAIL_CMD_BUFFER_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 = FAIL_CMD_BUFFER_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 read for cmd_id*/
	/*It is assumed that the first member of the command buffer is the cmd_id*/
	if(cmdlen < sizeof(uint32))
	{
	  qsee_log(QSEE_LOG_MSG_FATAL, "Command buffer len insufficient for reading cmd_id, ERROR OUT");
	  rsp_ptr->status = FAIL_CMD_BUF_TOO_SMALL_FOR_CMD_ID;
	  goto out;
	}

	cmd_id = cmd_ptr->cmd_id;
	qsee_log(QSEE_LOG_MSG_DEBUG, "TZ App cmd handler, cmd_id = %d", cmd_id);


	expected_cmdlen = sizeof(blgrd_send_cmd_t);
	expected_rpslen = sizeof(blgrd_send_cmd_rsp_t);

	/*Validate the command buf    debug_rpdu_sw2 = 0x0;fer and response buffer are the correct size.
	If not, the the MPU protection and ns_range checks done by QSEE kernel might be insufficient*/
	if(cmdlen != expected_cmdlen || rsplen != expected_rpslen)
	{
	  qsee_log(QSEE_LOG_MSG_FATAL, "Cmd/rsp buffer len insufficient - %x, %x, expected - %x, %x, ERROR OUT", cmdlen, rsplen, expected_cmdlen, expected_rpslen);
	  rsp_ptr->status = FAIL_CMD_RSP_BUFS_TOO_SMALL;
	  goto out;
	}

	switch(cmd_id)
	{
        case SEC_GRDM_CMD0_SET_BL_ROT_DATA :
            qsee_log(QSEE_LOG_MSG_FATAL, "SEC_GRDM_CMD0_SET_BL_ROT_DATA !!");
            rsp_ptr->status = grdm_write_bl_rot_data(&cmd_ptr->grdm_session_info, &cmd_ptr->data.bl_rot_data);
            break;

        case SEC_GRDM_CMD1_SET_BL_FUSE :
            qsee_log(QSEE_LOG_MSG_FATAL, "SEC_GRDM_CMD1_SET_BL_FUSE !!");
            rsp_ptr->status = grdm_fuse(cmd_ptr->data.seed_id);
            break;

        case SEC_GRDM_CMD11_FW_UPDATE :
            if (cmd_ptr->data.grdm_fw_data.fw_state == Grdm_FW_Image_Header) {
                qsee_log(QSEE_LOG_MSG_FATAL, "SEC_GRDM_CMD11_FW_UPDATE, header!!");
                rsp_ptr->status = grdm_FWupgrade_Header(cmd_ptr->data.grdm_fw_data.fw_data, &rsp_ptr->data.fw_info);
            }
            else if (cmd_ptr->data.grdm_fw_data.fw_state == Grdm_FW_Start) {
                qsee_log(QSEE_LOG_MSG_FATAL, "SEC_GRDM_CMD11_FW_UPDATE, start!!");
                rsp_ptr->status = grdm_FWupgrade_Start(cmd_ptr->data.grdm_fw_data.fw_data); //, &rsp_ptr->data.fw_info);
            }
            else if (cmd_ptr->data.grdm_fw_data.fw_state == Grdm_FW_Update) {
                rsp_ptr->status = grdm_FWupgrade_Data(cmd_ptr->data.grdm_fw_data.fw_data, &rsp_ptr->data.fw_info.next_size); //, &rsp_ptr->data.fw_info);
            }
            else if (cmd_ptr->data.grdm_fw_data.fw_state == Grdm_FW_Finish) {
                qsee_log(QSEE_LOG_MSG_FATAL, "SEC_GRDM_CMD11_FW_UPDATE, finish!!");
                rsp_ptr->status = grdm_FWupgrade_Finish(rsp_ptr->data.fw_info.fw_version); //, &rsp_ptr->data.fw_info);

                if (rsp_ptr->status == GRDM_NO_ERROR) {
                    grdm_set_fw_version(rsp_ptr->data.fw_info.fw_version);
                }
            }
            else {
                rsp_ptr->status = -1;
            }

            if (rsp_ptr->status != GRDM_NO_ERROR) {
                grdm_closeLogicalChannel();
            }
            break;

        case SEC_GRDM_CMD12_GET_FW_VERSION:
            qsee_log(QSEE_LOG_MSG_FATAL, "SEC_GRDM_CMD12_GET_FW_VERSION !!");
            rsp_ptr->status = grdm_get_fw_version(&rsp_ptr->data.fw_info);
            break;

        case SEC_GRDM_CMD21_GET_DATA:
            qsee_log(QSEE_LOG_MSG_FATAL, "SEC_GRDM_CMD21_GET_DATA !!");
            rsp_ptr->status = grdm_get_data(cmd_ptr->data.seed_id, &rsp_ptr->data.rpmb_data);
            break;

        case SEC_GRDM_CMD22_SET_DATA:
            qsee_log(QSEE_LOG_MSG_FATAL, "SEC_GRDM_CMD22_SET_DATA !!");
#ifndef SUPPORT_SET_RPMB_SEED
            rsp_ptr->status = grdm_set_data(cmd_ptr->data.seed_id);
#else
            rsp_ptr->status = grdm_set_data(cmd_ptr->data.seed_id, cmd_ptr->data.bl_rot_data.grdm_secure_info_rot.verified_boot_state);
#endif

            break;
#if 0 //def GRDM_DEBUG_DATA
        case SEC_GRDM_CMD30_GET_BL_ROT_DATA:
            qsee_log(QSEE_LOG_MSG_FATAL, "SEC_GRDM_CMD30_GET_BL_ROT_DATA !!");
            rsp_ptr->status = grdm_get_bl_cred(&cmd_ptr->grdm_session_info, &rsp_ptr->bl_rot_data);
            break;
#endif
        case SEC_GRDM_CMD50_FDC_CHECK_STATUS:
            qsee_log(QSEE_LOG_MSG_FATAL, "SEC_GRDM_CMD50_FDC_STATUS_CHECK !!");
            rsp_ptr->status = grdm_fdc_check_status(&rsp_ptr->grdm_status);
            break;

        case SEC_GRDM_CMD51_FDC_CHECK_RESTRICT:
            qsee_log(QSEE_LOG_MSG_FATAL, "SEC_GRDM_CMD51_FDC_CHECK_RESTRICT !!");
            rsp_ptr->status = grdm_fdc_check_Restriction(&rsp_ptr->grdm_status);
            break;

#ifdef GRDM_CHECK_FACTORY
        case SEC_GRDM_CMD200_FACTORY_CHECK :
            qsee_log(QSEE_LOG_MSG_FATAL, "SEC_GRDM_CMD200_FACTORY_CHECK !!");
            rsp_ptr->status = grdm_check_bl_authkey();
            if (rsp_ptr->status == GRDM_COMM_ERROR) {
                rsp_ptr->status = grdm_check_bl_authkey();
            }
            break;
#endif

#ifdef GRDM_RESET
        case SEC_GRDM_CMD300_RESET :
            qsee_log(QSEE_LOG_MSG_FATAL, "SEC_GRDM_CMD300_RESET !!");
            rsp_ptr->status = grdm_reset(cmd_ptr->data.seed_id);
            break;
#endif

        default :
            rsp_ptr->status = FAIL_UNDEFINED_COMMAND;
            qsee_log(QSEE_LOG_MSG_FATAL, "Unsupported command: %d.  Test not yet implemented or commented out.", cmd_ptr->cmd_id);
            break;
	}

out:
	if(cmd_ptr)
		qsee_free(cmd_ptr);

	if(rsp_ptr) {
		memcpy(rsp, (void *)rsp_ptr, rsplen);
		qsee_free(rsp_ptr);
	}
}
/**
  @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, "BLGRD Shutdown");
  return;
}
