#ifndef ICCC_v4
#include "tlStd.h"
#include "TlApi/TlApi.h"
#include "icccOperations.h"
#include "log.h"
#include "tl_tz_iccc_init.h"

#if TBASE_API_LEVEL >= 5
_TLAPI_EXTERN_C tlApiResult_t tlApi_callDriverEx(uint32_t driver_ID, void* payload, uint32_t payloadSize);
#else
_TLAPI_EXTERN_C tlApiResult_t tlApi_callDriver(uint32_t driver_ID, void* pMarParam);
#endif

//#define UINT8_MAX 0xff /* 255U */

/* For ICCC secure address */
static uint32_t iccc_sec_mem_addr = ICCC_SECURE_MEM_BASE_ADDR;

char iccc_log_msg[LOG_MSG_SIZE];

/* Copied from TZ_Vendor_tl.h */
#define SHA256_DIGEST_LENGTH 32

/**
 * Get ICCC secure memory base address.
 *
 * @addr : the pointer of ICCC secure memory base address
 *
 * @return : tlApiResult_t
 *	@TLAPI_OK : ICCC secure memory base is gotten successfully.
 */
_TLAPI_EXTERN_C tlApiResult_t tlApiSecGetIcccAddr(uint32_t *addr)
{
  tlApiResult_t ret;
  IcccMarshalingParam_t marParam = {
    .functionId = SEC_GET_ICCC_ADDR,
    .payload = {
      .addr = addr,
    }
  };

#if TBASE_API_LEVEL >= 5
  tlApi_callDriverEx(FC_DRV_ID, &marParam, sizeof(marParam));
#else
  tlApi_callDriver(FC_DRV_ID, &marParam);
#endif
  ret = marParam.payload.retVal;
  return ret;
}

/**
 * Tima Driver Physical Memory To TL Copy
 *
 * phys2TLData: pointer for physical and TL addresses and data length
 *
 * @return : tlApiResult_t
 * @TLAPI_OK : physical memory is accessed digest is generated and passed to TL
 */
_TLAPI_EXTERN_C tlApiResult_t tlApiIcccDriverPhysMemAccess(struct icccPhysMemAccessData_t *physMemAccessData)
{
  tlApiResult_t ret;
  IcccMarshalingParam_t marParam = {
    .functionId = ICCC_DRIVER_PHYS_MEM_ACCESS_COPY,
    .payload = {
      .pPhysMemAccessData = physMemAccessData}
  };

#if TBASE_API_LEVEL >= 5
  tlApi_callDriverEx(TIMA_DRV_ID, &marParam, sizeof(marParam));
#else
  tlApi_callDriver(TIMA_DRV_ID, &marParam);
#endif
  ret = marParam.payload.retVal;
  return ret;
}

uint32_t get_sec_ICCC_address(int type)
{
    if(iccc_sec_mem_addr == 0 ) { /* First time need to get address from FastCall API and then set them into global variables */
        tlApiResult_t api_ret;
        api_ret = tlApiSecGetIcccAddr(&iccc_sec_mem_addr);
        if (api_ret != TLAPI_OK){
            ICCC_LOG_DEBUG("ICCC: tlApiSecGetIcccAddr Error!: (%d)", api_ret);
            iccc_sec_mem_addr = 0;
            return ICCC_MEM_ADDR_ERROR;
        }else if(iccc_sec_mem_addr == 0){
            return ICCC_MEM_ADDR_ERROR;
        }

        ICCC_LOG_DEBUG("ICCC: get_sec_ICCC_address iccc_sec_mem_addr : %X", iccc_sec_mem_addr);
    }

    switch(type){
        case PARAM_FOR_ICCC_SEC_MEM:
            return iccc_sec_mem_addr;
        case PARAM_FOR_ICCC_BOOT_MSR:
            return (uint32_t)(iccc_sec_mem_addr + ICCC_BOOT_MSR_ADDR_OFFSET);
        case PARAM_FOR_ICCC_GOLDEN_MSR:
            return (uint32_t)(iccc_sec_mem_addr + ICCC_GOLDEN_MSR_ADDR_OFFSET);
        case PARAM_FOR_ICCC_WARRANTY_STRING:
            return (uint32_t)(iccc_sec_mem_addr + ICCC_BOOT_WARRANTY_ADDR_OFFSET);
        default:
            return ICCC_MEM_ADDR_ERROR;
    }
}

uint32_t Iccc_phys_write(
	void *physAddr,
	uint32_t len,
	void *addr
)
{
	return (Iccc_phys_access(physAddr, len, addr, ICCC_PHYS_WRITE));
}

uint32_t Iccc_phys_read(
	void *physAddr,
	uint32_t len,
	void *addr
)
{
	return (Iccc_phys_access(physAddr, len, addr, ICCC_PHYS_READ));
}

uint32_t Iccc_phys_access(
	void *physAddr,
	uint32_t len,
	void *addr,
	iccc_action_t action
)
{
	uint32_t ret = 0;
	IcccPhysMemAccessData_t PhysMemAccessData;
	PhysMemAccessData.physAddr = physAddr;
	PhysMemAccessData.len = len;
	PhysMemAccessData.tlAddr = addr;
	PhysMemAccessData.action = action;
	PhysMemAccessData.status = ~0;
	if (0 != (ret = tlApiIcccDriverPhysMemAccess(&PhysMemAccessData))) {
		return (ret);
	} else {
		return PhysMemAccessData.status;
	}
}

int is_type_valid(uint32_t type) {
	switch (type) {
		case RP_VER ... EM_TOKEN:
		case PKM_TEXT ... ATN_BLOB_HASH:
		case DMV_STATUS ... VERIFIEDBOOT_HASH:
		case SYSSCOPE_FLAG ... TIMA_VERSION_FLAG:
#ifdef CONFIG_ROT_IN_ICCC	
		case VERIFIEDBOOTSTATE_FLAG ... ROT_STRUCT:
#endif			
			return 1;
		default:
			return 0;
	}
}


/* return 0 if not locked */
uint32_t Iccc_check_lock_status(uint32_t type, uint32_t secure_mem_addr)
{
	uint32_t iccc_lock;
	uint32_t ret = ICCC_SUCCESS;

	if ((type != (uint32_t)SYSSCOPE_FLAG) && (type != (uint32_t)TRUSTBOOT_FLAG) && (type != (uint32_t)TIMA_VERSION_FLAG)
		&& (type != (uint32_t)PKM_TEXT) && (type != (uint32_t)PKM_RO)) {
		ICCC_LOG("ICCC: Not Locked");
		ICCC_LOG_DEBUG("ICCC: Only sysscope , trustboot, tima_version, PKM TEXT, PKM RO are lock protected");
		return 0;
	}

	if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_LOCK_FLAG_OFFSET),ICCC_SECURE_PARAMETERS_READING_LENGTH,(void *)(&iccc_lock)))) {
		ICCC_LOG("ICCC: failed to read iccc lock");
		ICCC_LOG_DEBUG("ICCC: failed to read iccc lock, ret = 0x%x", ret);
		return ret;
	}

	ICCC_LOG_DEBUG("ICCC: Iccc_check_lock_status : iccc lock is %x ", iccc_lock);
	if (type == (uint32_t)SYSSCOPE_FLAG)
		iccc_lock = iccc_lock & (uint32_t)ICCC_SYSSCOPE_LOCK;
	else if (type == (uint32_t)TRUSTBOOT_FLAG)
		iccc_lock = iccc_lock & (uint32_t)ICCC_TRUSTBOOT_LOCK;
	else if (type == (uint32_t)TIMA_VERSION_FLAG)
		iccc_lock = iccc_lock & (uint32_t)ICCC_TIMA_VERSION_LOCK;
	else if (type == (uint32_t)PKM_TEXT)
		iccc_lock = iccc_lock & (uint32_t)ICCC_LOCK_PKM_TEXT;
	else if (type == (uint32_t)PKM_RO)
		iccc_lock = iccc_lock & (uint32_t)ICCC_LOCK_PKM_RO;

	if (iccc_lock == 0)
		return 0 ; //not locked
	else
		return 1; //locked
}

uint32_t Iccc_lock_on_failure(uint32_t type, uint32_t value)
{
	uint32_t ret = 0;
	switch (type) {
		case TRUSTBOOT_FLAG:
			if(value != TEXT_TRUSTBOOT_SUCCESS)
				ret = 1;
			break;
       	case PKM_TEXT:
			if(value != TEXT_REG_SUCESS)
				ret = 1;
			break;
       	case PKM_RO:
			if(value != TEXT_RO_SUCESS)
				ret = 1;
			break;
		default:
			ret = 1;
	}
	return ret;
}

uint32_t Iccc_update_lock(uint32_t type, uint32_t secure_mem_addr, uint32_t value)
{
	uint32_t iccc_lock;
	uint32_t ret = ICCC_SUCCESS;

	if ((type != (uint32_t)SYSSCOPE_FLAG) && (type != (uint32_t)TRUSTBOOT_FLAG) && (type != (uint32_t)TIMA_VERSION_FLAG)
		&& (type != (uint32_t)PKM_TEXT) && (type != (uint32_t)PKM_RO)){		
		ICCC_LOG("ICCC: Not Locked");
		ICCC_LOG_DEBUG("ICCC: Only sysscope , trustboot, tima_version, PKM TEXT, PKM RO are lock protected");
		return 0;
	}

	if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_LOCK_FLAG_OFFSET), ICCC_SECURE_PARAMETERS_READING_LENGTH, (void *)(&iccc_lock)))) {
		ICCC_LOG("ICCC: failed to read iccc lock");
		ICCC_LOG_DEBUG("ICCC: failed to read iccc lock, ret = 0x%x", ret);
		return ret;
	}

	ICCC_LOG_DEBUG("ICCC: Iccc_update_lock : iccc lock is %x ", iccc_lock);
	
	if(!Iccc_lock_on_failure(type, value)) {
		ICCC_LOG("ICCC: Restriction to update the iccc lock ");
		return 0;	
	}

	if (type == (uint32_t)SYSSCOPE_FLAG)
		iccc_lock = iccc_lock | (uint32_t)ICCC_SYSSCOPE_LOCK;
	else if (type == (uint32_t)TRUSTBOOT_FLAG)
		iccc_lock = iccc_lock | (uint32_t)ICCC_TRUSTBOOT_LOCK;
	else if (type == (uint32_t)TIMA_VERSION_FLAG)
		iccc_lock = iccc_lock | (uint32_t)ICCC_TIMA_VERSION_LOCK;
	else if (type == (uint32_t)PKM_TEXT)
		iccc_lock = iccc_lock | (uint32_t)ICCC_LOCK_PKM_TEXT;
	else if (type == (uint32_t)PKM_RO)
		iccc_lock = iccc_lock | (uint32_t)ICCC_LOCK_PKM_RO;

	if (0 != (ret = Iccc_phys_write((void *)(secure_mem_addr + ICCC_LOCK_FLAG_OFFSET), ICCC_SECURE_PARAMETERS_READING_LENGTH, (void *)(&iccc_lock)))) {
		ICCC_LOG("ICCC: failed to write iccc lock");
		ICCC_LOG_DEBUG("ICCC: failed to write iccc lock, ret = 0x%x", ret);
		return ret;
	}
	
	return 0;
}

uint32_t Iccc_check_magic(uint32_t secure_param_type,uint32_t secure_mem_addr,uint16_t *magic_str)
{
	uint32_t ret = ICCC_SUCCESS;
	secure_param_header_t header = {0};
	if(secure_param_type == 0xF){
		//ICCC_LOG("ICCC : I am in bootloader block ");
		//ICCC_LOG("ICCC tima_sec_mem_addr = %x SECURE_PARAMETERS_OFFSET = %x, total = %x ", secure_mem_addr, ICCC_BL_SECURE_PARAMETERS_OFFSET, (secure_mem_addr + ICCC_BL_SECURE_PARAMETERS_OFFSET));
		if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_BL_SECURE_PARAMETERS_OFFSET), sizeof(secure_param_header_t),(void *)(&header)))) {
			ICCC_LOG("ICCC: Iccc_phys_read failed");
			ICCC_LOG_DEBUG("ICCC: Iccc_phys_read failed: (secure_mem_addr + ICCC_BL_SECURE_PARAMETERS_OFFSET) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_BL_SECURE_PARAMETERS_OFFSET), ret);
			ret = ICCC_ERROR_READ_FAILED;
		}
	}
	else if(secure_param_type == 0x0){
		//ICCC_LOG("ICCC : I am in TA block ");
		//ICCC_LOG("ICCC tima_sec_mem_addr = %x KERNEL_SECURE_PARAMETERS_OFFSET = %x, total = %x ", secure_mem_addr, ICCC_TA_SECURE_PARAMETERS_OFFSET, (secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET));
		if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET), sizeof(secure_param_header_t),(void *)(&header)))) {
			ICCC_LOG("ICCC: Iccc_phys_read failed");
			ICCC_LOG_DEBUG("ICCC: Iccc_phys_read failed: (secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET), ret);
			ret = ICCC_ERROR_READ_FAILED;
		}
	}
	else if(secure_param_type == 0x1){
		//ICCC_LOG("ICCC : I am in Kernel block ");
		//ICCC_LOG("ICCC tima_sec_mem_addr = %x KERN_SECURE_PARAMETERS_OFFSET = %x, total = %x ", secure_mem_addr, ICCC_KERN_SECURE_PARAMETERS_OFFSET, (secure_mem_addr + ICCC_KERN_SECURE_PARAMETERS_OFFSET));
		if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_KERN_SECURE_PARAMETERS_OFFSET), sizeof(secure_param_header_t), (void *)(&header)))) {
			ICCC_LOG("ICCC: Iccc_phys_read failed");
			ICCC_LOG_DEBUG("ICCC: Iccc_phys_read failed: (secure_mem_addr + ICCC_KERN_SECURE_PARAMETERS_OFFSET) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_KERN_SECURE_PARAMETERS_OFFSET), ret);
			ret = ICCC_ERROR_READ_FAILED;
		}
	}
	else if(secure_param_type == 0x2){
		//ICCC_LOG("ICCC : I am in System block ");
		//ICCC_LOG("ICCC tima_sec_mem_addr = %x ICCC_SYS_SECURE_PARAMETERS_OFFSET = %x, total = %x ", secure_mem_addr, ICCC_SYS_SECURE_PARAMETERS_OFFSET, (secure_mem_addr + ICCC_SYS_SECURE_PARAMETERS_OFFSET));
		if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_SYS_SECURE_PARAMETERS_OFFSET), sizeof(secure_param_header_t), (void *)(&header)))) {
			ICCC_LOG("ICCC: Iccc_phys_read failed");
			ICCC_LOG_DEBUG("ICCC: Iccc_phys_read failed: (secure_mem_addr + ICCC_SYS_SECURE_PARAMETERS_OFFSET) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_SYS_SECURE_PARAMETERS_OFFSET), ret);
			ret = ICCC_ERROR_READ_FAILED;
		}
	}
	else if(secure_param_type == 0x3){
		//ICCC_LOG("ICCC : I am in System block ");
		//ICCC_LOG("ICCC tima_sec_mem_addr = %x ICCC_ROT_SECURE_PARAMETERS_OFFSET = %x, total = %x ", secure_mem_addr, ICCC_ROT_SECURE_PARAMETERS_OFFSET, (secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET));
		if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET), sizeof(secure_param_header_t), (void *)(&header)))) {
			ICCC_LOG("ICCC: Iccc_phys_read failed");
			ICCC_LOG_DEBUG("ICCC: Iccc_phys_read failed: (secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET), ret);
			ret = ICCC_ERROR_READ_FAILED;
		}
	}
	else {
		ICCC_LOG("ICCC: I am in Unknown block ");
		ret = ICCC_ERROR_READ_FAILED;
	}

	ICCC_LOG_DEBUG("ICCC: Magic Got is  = %x , ret is = %x ", header.magic_str,ret);
	*magic_str = header.magic_str;
	return ret;

}

uint32_t Iccc_SaveData_TA(uint32_t type, uint32_t value)
{
	uint32_t secure_param_type;
	uint32_t secure_param_addr;
	uint32_t secure_mem_addr;
	uint32_t ret = ICCC_SUCCESS;
	uint16_t magic_str = 0;

	if ( is_type_valid(type) ) {
		secure_mem_addr = get_sec_ICCC_address(PARAM_FOR_ICCC_SEC_MEM);
		
		secure_param_type = (type >> 20) & (0xF);
		secure_param_addr = (uint32_t)((type & (0x1F)) * ICCC_SECURE_PARAMETERS_READING_LENGTH);
		
		//ICCC_LOG("ICCC secure_param_type = %x", secure_param_type);
		//ICCC_LOG("ICCC secure_param_addr = %x", secure_param_addr);
		ICCC_LOG("ICCC: ICCC save data@#");
		
		ret = Iccc_check_magic(secure_param_type,secure_mem_addr,&magic_str);
		if(ret) {
			ICCC_LOG("ICCC: check magic failed");
			ICCC_LOG_DEBUG("ICCC: Iccc_check_magic failed with ret = %x ", ret);
			return ICCC_ERROR_WRITE_FAILED;
		}
		
		if(secure_param_type == 0xF && magic_str == BL_MAGIC_STR ){
			if (secure_param_addr > sizeof(bl_secure_info_t)) {
				ICCC_LOG("ICCC: secure_param_addr error");
				ICCC_LOG_DEBUG("ICCC: secure_param_addr > sizeof(bl_secure_info_t)");
				return ICCC_ERROR_WRITE_FAILED;
			}
			ICCC_LOG("ICCC: type already set");
			ICCC_LOG_DEBUG("ICCC: Bootloader parameters were set during bootup by bootloader. No further modifications are allowed");
#ifndef ICCC_TEST_CODE_ENABLED
			ret = ICCC_PERMISSION_DENIED;
#else
			if (0 != (ret = Iccc_phys_write((void *)(secure_mem_addr + ICCC_BL_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ICCC_SECURE_PARAMETERS_READING_LENGTH, (void *)(&value)))) {
				ICCC_LOG("ICCC: Iccc_phys_write failed");
				ICCC_LOG_DEBUG("ICCC: Iccc_phys_write failed: (secure_mem_addr + ICCC_BL_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_BL_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ret);
				ret = ICCC_ERROR_WRITE_FAILED;
			}
#endif

		}
		else if(secure_param_type == 0x1 && magic_str == KERN_MAGIC_STR ) {
			if (secure_param_addr > sizeof(kern_secure_info_t)) {
				ICCC_LOG("ICCC: secure_param_addr error");
				ICCC_LOG_DEBUG("ICCC: secure_param_addr > sizeof(kern_secure_info_t)");
				return ICCC_ERROR_WRITE_FAILED;
			}

			if (type == DMV_STATUS || type == VERIFIEDBOOT_HASH) {
				ICCC_LOG("ICCC: special type");
				ICCC_LOG_DEBUG("ICCC: DMV, VerifiedBootHash parameter can be set by SMC call only.");
#ifndef ICCC_TEST_CODE_ENABLED
				return ICCC_PERMISSION_DENIED;
#endif
			}

			// ICCC_LOG("ICCC  tima_sec_mem_addr = %x KERNEL_SECURE_PARAMETERS_OFFSET = %x, total = %x ", secure_mem_addr, ICCC_KERN_SECURE_PARAMETERS_OFFSET, (secure_mem_addr + ICCC_KERN_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)));
			if (0 != (ret = Iccc_phys_write((void *)(secure_mem_addr + ICCC_KERN_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ICCC_SECURE_PARAMETERS_READING_LENGTH, (void *)(&value)))) {
				ICCC_LOG("ICCC: Iccc_phys_write failed");
				ICCC_LOG_DEBUG("ICCC: Iccc_phys_write failed: (secure_mem_addr + ICCC_KERN_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_KERN_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ret);
				ret = ICCC_ERROR_WRITE_FAILED;
			}
		}
		else if((secure_param_type == 0x2 && magic_str == SYS_MAGIC_STR ) || (secure_param_type == 0x0 && magic_str == TA_MAGIC_STR )){
			if (secure_param_addr > sizeof(sys_secure_info_t)) {
				ICCC_LOG("ICCC: secure_param_addr error");
				ICCC_LOG_DEBUG("ICCC: secure_param_addr > sizeof(sys_secure_info_t)");
				return ICCC_ERROR_WRITE_FAILED;
			}
			//ICCC_LOG("ICCC  tima_sec_mem_addr = %x ICCC_SYS_SECURE_PARAMETERS_OFFSET = %x, total = %x ", secure_mem_addr, ICCC_SYS_SECURE_PARAMETERS_OFFSET, (secure_mem_addr + ICCC_SYS_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)));

			if (Iccc_check_lock_status(type,secure_mem_addr)) {
				ICCC_LOG("ICCC: type is locked");
				ICCC_LOG_DEBUG("ICCC: only one write is allowed on this type" );
#ifndef ICCC_TEST_CODE_ENABLED
				return  ICCC_PERMISSION_DENIED;
#endif
			}

			if(secure_param_type == 0x2 && magic_str == SYS_MAGIC_STR ) {
				if (secure_param_addr > sizeof(sys_secure_info_t)) {
					ICCC_LOG("ICCC: secure_param_addr error");
					ICCC_LOG_DEBUG("ICCC: secure_param_addr > sizeof(sys_secure_info_t)");
					return ICCC_ERROR_WRITE_FAILED;
				}
				if (0 != (ret = Iccc_phys_write((void *)(secure_mem_addr + ICCC_SYS_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ICCC_SECURE_PARAMETERS_READING_LENGTH, (void *)(&value)))) {
					ICCC_LOG("ICCC: Iccc_phys_write failed");
					ICCC_LOG_DEBUG("ICCC: Iccc_phys_write failed: (secure_mem_addr + ICCC_SYS_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_SYS_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ret);
					ret = ICCC_ERROR_WRITE_FAILED;
				}
			}
			else if(secure_param_type == 0x0 && magic_str == TA_MAGIC_STR ) {
				if (secure_param_addr > sizeof(ta_secure_info_t)) {
					ICCC_LOG("ICCC: secure_param_addr error");
					ICCC_LOG_DEBUG("ICCC: secure_param_addr > sizeof(ta_secure_info_t)");
					return ICCC_ERROR_WRITE_FAILED;
				}
				if (0 != (ret = Iccc_phys_write((void *)(secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ICCC_SECURE_PARAMETERS_READING_LENGTH, (void *)(&value)))) {
					ICCC_LOG("ICCC: Iccc_phys_write failed");
					ICCC_LOG_DEBUG("ICCC: Iccc_phys_write failed: (secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ret);
					ret = ICCC_ERROR_WRITE_FAILED;
				}
			}
			if (0 != (ret = Iccc_update_lock(type,secure_mem_addr, value))) {
				ICCC_LOG("ICCC: Update Lock failed");
				ICCC_LOG_DEBUG("ICCC: iccc_update_lock failed with error = 0x%x",ret);
				ret = ICCC_ERROR_WRITE_FAILED;
			}
		}
		else if(secure_param_type == 0x3 && magic_str == ROT_MAGIC_STR ){
			ICCC_LOG("ICCC: Iccc_phys_write failed");
			ICCC_LOG_DEBUG("ICCC: Iccc_phys_write failed: rot locked");
			ret = ICCC_ERROR_WRITE_FAILED ;
			}

		else {
			ICCC_LOG("ICCC: Iccc_phys_write failed");
			ICCC_LOG_DEBUG("ICCC: Iccc_phys_write failed: unknown error");
			ret = ICCC_ERROR_WRITE_FAILED ;
		}
	} else {
		ret = ICCC_UNKNOWN_TYPE ;
	}

	//ICCC_LOG("ICCC: After writing");
	return ret;
}

uint32_t Iccc_ReadData_TA(uint32_t type, uint32_t *value)
{
	uint32_t ret = ICCC_SUCCESS;
	uint32_t secure_param_type;
	uint32_t secure_param_addr;
	uint32_t secure_mem_addr;
	uint16_t magic_str = 0;

	if ( is_type_valid(type) ) {
		secure_mem_addr = get_sec_ICCC_address(PARAM_FOR_ICCC_SEC_MEM);
		
		secure_param_type = (type >> 20) & (0xF);
		secure_param_addr = (uint32_t)((type & (0x1F)) * ICCC_SECURE_PARAMETERS_READING_LENGTH);
		
		
		//ICCC_LOG("ICCC secure_param_type = %x", secure_param_type);
		//ICCC_LOG("ICCC secure_param_addr = %x", secure_param_addr);
		ICCC_LOG("ICCC: ICCC read data@#");
		
		ret = Iccc_check_magic(secure_param_type,secure_mem_addr,&magic_str);
		if(ret) {
			ICCC_LOG("ICCC: check magic failed");
			ICCC_LOG_DEBUG("ICCC: Iccc_check_magic failed with ret = %x ", ret);
			return ICCC_ERROR_READ_FAILED;
		}
		
		if(secure_param_type == 0xF && magic_str == BL_MAGIC_STR ){
			if (secure_param_addr > sizeof(bl_secure_info_t)) {
				ICCC_LOG("ICCC: secure_param_addr error");
				ICCC_LOG_DEBUG(" ICCC: secure_param_addr > sizeof(bl_secure_info_t)");
				return ICCC_ERROR_READ_FAILED;
			}
			//ICCC_LOG("ICCC: tima_sec_mem_addr = %x ICCC_BL_SECURE_PARAMETERS_OFFSET = %x, total = %x ", secure_mem_addr, ICCC_BL_SECURE_PARAMETERS_OFFSET, (secure_mem_addr + ICCC_BL_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)));
			if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_BL_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ICCC_SECURE_PARAMETERS_READING_LENGTH, (void *)value))) {
				ICCC_LOG("ICCC: Iccc_phys_read failed");
				ICCC_LOG_DEBUG("ICCC: Iccc_phys_read failed: (secure_mem_addr + ICCC_BL_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_BL_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ret);
				ret = ICCC_ERROR_READ_FAILED;
			}
		}
		else if(secure_param_type == 0x0 && magic_str == TA_MAGIC_STR ){
			if (secure_param_addr > sizeof(ta_secure_info_t)) {
				ICCC_LOG("ICCC: secure_param_addr error");
				ICCC_LOG_DEBUG(" ICCC: secure_param_addr > sizeof(ta_secure_info_t)");
				return ICCC_ERROR_READ_FAILED;
			}
			if (type == ATN_BLOB_HASH) {
				if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), SHA256_DIGEST_LENGTH, (void *)value))) {
					ICCC_LOG("ICCC: Iccc_phys_read failed");
					ICCC_LOG_DEBUG("ICCC: Iccc_phys_read failed: (secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)) = 0x%x, ret = 0x%x", (secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ret);
					ret = ICCC_ERROR_READ_FAILED;
				}
			} else {
				//ICCC_LOG("ICCC: tima_sec_mem_addr = %x ICCC_TA_SECURE_PARAMETERS_OFFSET = %x, total = %x ", secure_mem_addr, ICCC_TA_SECURE_PARAMETERS_OFFSET, (secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)));
				if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ICCC_SECURE_PARAMETERS_READING_LENGTH, (void *)value))) {
					ICCC_LOG("ICCC: Iccc_phys_read failed");
					ICCC_LOG_DEBUG("ICCC: Iccc_phys_read failed: (secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ret);
					ret = ICCC_ERROR_READ_FAILED;
				}
			}
		}
		else if(secure_param_type == 0x1 && magic_str == KERN_MAGIC_STR){
			if (secure_param_addr > sizeof(kern_secure_info_t)) {
				ICCC_LOG("ICCC: secure_param_addr error");
				ICCC_LOG_DEBUG(" ICCC: secure_param_addr > sizeof(kern_secure_info_t)");
				return ICCC_ERROR_READ_FAILED;
			}
			if(type == VERIFIEDBOOT_HASH){
				//ICCC_LOG("ICCC: tima_sec_mem_addr = %x ICCC_KERN_SECURE_PARAMETERS_OFFSET = %x, total = %x ", secure_mem_addr, ICCC_KERN_SECURE_PARAMETERS_OFFSET, (secure_mem_addr + ICCC_KERN_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)));
				if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_KERN_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ICCC_ROT_KEY_LENGTH, (void *)value))) {
					ICCC_LOG("ICCC: Iccc_phys_read failed");
					ICCC_LOG_DEBUG("ICCC: Iccc_phys_read failed: (secure_mem_addr + ICCC_KERN_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_KERN_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ret);
					ret = ICCC_ERROR_READ_FAILED;
				}
			}
			else {
				//ICCC_LOG("ICCC: tima_sec_mem_addr = %x ICCC_KERN_SECURE_PARAMETERS_OFFSET = %x, total = %x ", secure_mem_addr, ICCC_KERN_SECURE_PARAMETERS_OFFSET, (secure_mem_addr + ICCC_KERN_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)));
				if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_KERN_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ICCC_SECURE_PARAMETERS_READING_LENGTH, (void *)value))) {
					ICCC_LOG("ICCC: Iccc_phys_read failed");
					ICCC_LOG_DEBUG("ICCC: Iccc_phys_read failed: (secure_mem_addr + ICCC_KERN_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_KERN_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ret);
					ret = ICCC_ERROR_READ_FAILED;
				}
			}
		}
		else if(secure_param_type == 0x2 && magic_str == SYS_MAGIC_STR){
			if (secure_param_addr > sizeof(sys_secure_info_t)) {
				ICCC_LOG("ICCC: secure_param_addr error");
				ICCC_LOG_DEBUG(" ICCC: secure_param_addr > sizeof(sys_secure_info_t)");
				return ICCC_ERROR_READ_FAILED;
			}
			//ICCC_LOG("ICCC: tima_sec_mem_addr = %x ICCC_SYS_SECURE_PARAMETERS_OFFSET = %x, total = %x ", secure_mem_addr, ICCC_SYS_SECURE_PARAMETERS_OFFSET, (secure_mem_addr + ICCC_SYS_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)));
			if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_SYS_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ICCC_SECURE_PARAMETERS_READING_LENGTH, (void *)value))) {
				ICCC_LOG("ICCC: Iccc_phys_read failed");
				ICCC_LOG_DEBUG("ICCC: Iccc_phys_read failed: (secure_mem_addr + ICCC_SYS_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_SYS_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ret);
				ret = ICCC_ERROR_READ_FAILED;
			}
		}
		else if(secure_param_type == 0x3 && magic_str == ROT_MAGIC_STR) {
			if(type == ROT_STRUCT) { // exceptional cases ((0xFF3000FF & 0x1F) x 0x4 = 124, rot_secure_info_t = 64)
				//ICCC_LOG("ICCC: tima_sec_mem_addr = %x ICCC_ROT_SECURE_PARAMETERS_OFFSET = %x, total = %x ", secure_mem_addr, ICCC_ROT_SECURE_PARAMETERS_OFFSET, (secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)));
				if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET ), ICCC_ROT_DATA_LENGTH, (void *)value))) {
					ICCC_LOG("ICCC: Iccc_phys_read failed");
					ICCC_LOG_DEBUG("ICCC: Iccc_phys_read failed: (secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET ) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET + secure_param_addr ), ret);
					ret = ICCC_ERROR_READ_FAILED;
				}
			} else {
				if (secure_param_addr > sizeof(rot_secure_info_t)) {
					ICCC_LOG("ICCC: secure_param_addr error");
					ICCC_LOG_DEBUG(" ICCC: secure_param_addr > sizeof(rot_secure_info_t)");
					return ICCC_ERROR_READ_FAILED;
				}
				if (type == VERIFIEDBOOTKEY_FLAG) {
					//ICCC_LOG("ICCC: tima_sec_mem_addr = %x ICCC_ROT_SECURE_PARAMETERS_OFFSET = %x, total = %x ", secure_mem_addr, ICCC_ROT_SECURE_PARAMETERS_OFFSET, (secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)));
					if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ICCC_ROT_KEY_LENGTH, (void *)value))) {
						ICCC_LOG("ICCC: Iccc_phys_read failed");
						ICCC_LOG_DEBUG("ICCC: Iccc_phys_read failed: (secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ret);
						ret = ICCC_ERROR_READ_FAILED;
					}
				}
				else {
					//ICCC_LOG("ICCC: tima_sec_mem_addr = %x ICCC_ROT_SECURE_PARAMETERS_OFFSET = %x, total = %x ", secure_mem_addr, ICCC_ROT_SECURE_PARAMETERS_OFFSET, (secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)));
					if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ICCC_SECURE_PARAMETERS_READING_LENGTH, (void *)value))) {
						ICCC_LOG("ICCC: Iccc_phys_read failed");
						ICCC_LOG_DEBUG("ICCC: Iccc_phys_read failed: (secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ret);
						ret = ICCC_ERROR_READ_FAILED;
					}
				}
			}
		}
		else {
			ICCC_LOG("ICCC: Iccc_phys_read failed");
			ICCC_LOG_DEBUG("ICCC: Iccc_phys_read failed: unknown error");
			ret = ICCC_ERROR_READ_FAILED ;
		}
	} else {
		ret = ICCC_UNKNOWN_TYPE ;
	}

	//ICCC_LOG("ICCC After reading");
	return ret;
}

uint32_t Iccc_SaveATN_TA (uint8_t *blob, uint32_t *blob_len) {
    uint32_t sectimer_header_size = sizeof(uint8_t) * 3; // Type(1 byte) + Length(2 Byte)
    uint32_t sectimer_type[2][2] = {
        {SECTIMER_BASE,     ICCC_ATN_RESULT_TYPE_SECTIMER_BASE},
        {SECTIMER_STATUS,   ICCC_ATN_RESULT_TYPE_SECTIMER_STATUS}
    };
    uint32_t sectimer_value;
    uint8_t digest[SHA256_DIGEST_LENGTH] = {0, };
    uint32_t digest_len = sizeof(digest);
    uint32_t secure_param_type;
    uint32_t secure_param_addr;
    uint32_t secure_mem_addr;
    uint16_t magic_str = 0;
    uint32_t ret = ICCC_SUCCESS;

    ICCC_LOG("ICCC: ICCC_save_atn@#");

    ICCC_LOG_DEBUG(" ICCC: given blob from N/W ATN= %s", blob);
    // Add securetimer value to blob before generate hash
    for (uint32_t i = 0; i < sizeof(sectimer_type) / sizeof(sectimer_type[0]); i++) {
        sectimer_value = -1;
        if (*blob_len + sectimer_header_size + sizeof(sectimer_value) > ICCC_ATN_BLOB_MAX_SIZE) {
            ICCC_LOG("ICCC: invalid blob length");
            ICCC_LOG_DEBUG("ICCC: invalid blob length: (blob_len + sectimer_header_size + sizeof(sectimer_value)) = 0x%x, ret = 0x%x", *blob_len + sectimer_header_size + sizeof(sectimer_value), ret);
            return ICCC_ERROR_ATTESTATION_FAILED;
        }

        ret = Iccc_ReadData_TA(sectimer_type[i][0], &sectimer_value);
        if (ret) {
            ICCC_LOG("ICCC: readdata failed");
            ICCC_LOG_DEBUG("ICCC: Error reading sectimer type = 0x%x, value = %d, ret = 0x%x\n", sectimer_type[i][0], sectimer_value, ret);
            ret = ICCC_ERROR_ATTESTATION_FAILED;
            goto error;
        }
        sectimer_value = switch_endianness(sectimer_value);

        blob[(*blob_len)++] = sectimer_type[i][1];
        blob[(*blob_len)++] = 0;
        blob[(*blob_len)++] = sizeof(sectimer_value);
        memcpy(blob + *blob_len, (uint8_t *)&sectimer_value, sizeof(sectimer_value)); 
        *blob_len += sizeof(sectimer_value);

        ICCC_LOG_DEBUG("ICCC: ATN blob after adding sectimer type 0x%x = %s", sectimer_type[i][0], blob);
    }

    ret = iccc_digest_SHA256(blob, *blob_len, digest, &digest_len); 
    if (ret != ICCC_SUCCESS) {
        ICCC_LOG("ICCC: Iccc_digest_SHA256 failed");
        ICCC_LOG_DEBUG("ICCC: Iccc_digest_SHA256 failed with ret = 0x%x\n", ret);
        ret = ICCC_ERROR_ATTESTATION_FAILED;
        goto error;
    }
    if (digest_len != SHA256_DIGEST_LENGTH) {
        ICCC_LOG("ICCC: invalid SHA256 digest length");
        ICCC_LOG_DEBUG("ICCC: digest_len != SHA256_DIGEST_LENGTH with ret = 0x%x, length = %d", ret, digest_len);
        ret = ICCC_ERROR_ATTESTATION_FAILED;
        goto error;
    }
    ICCC_LOG_DEBUG("ICCC: sha256 result = %s", digest);

    // Save hash to secure memory
    secure_mem_addr = get_sec_ICCC_address(PARAM_FOR_ICCC_SEC_MEM);
    secure_param_type = (ATN_BLOB_HASH >> 20) & (0xF);
    secure_param_addr = (uint32_t)((ATN_BLOB_HASH & (0x1F)) * ICCC_SECURE_PARAMETERS_READING_LENGTH);

    ret = Iccc_check_magic(secure_param_type,secure_mem_addr,&magic_str);
    if(ret) {
        ICCC_LOG("ICCC: check magic failed");
        ICCC_LOG_DEBUG("ICCC: Iccc_check_magic failed with ret = %x ", ret);
        return ICCC_ERROR_ATTESTATION_FAILED;
    }

    if(secure_param_type == 0x0 && magic_str == TA_MAGIC_STR ) {
        if (secure_param_addr > sizeof(ta_secure_info_t)) {
            ICCC_LOG("ICCC: secure_param_addr error");
            ICCC_LOG_DEBUG("ICCC: secure_param_addr > sizeof(ta_secure_info_t)");
            return ICCC_ERROR_ATTESTATION_FAILED;
        }
        if (0 != (ret = Iccc_phys_write((void *)(secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), SHA256_DIGEST_LENGTH, (void *)(digest)))) {
            ICCC_LOG("ICCC: Iccc_phys_write failed");
            ICCC_LOG_DEBUG("ICCC: Iccc_phys_write failed: (secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)) = 0x%x, ret = 0x%x",(secure_mem_addr + ICCC_TA_SECURE_PARAMETERS_OFFSET + secure_param_addr + sizeof(secure_param_header_t)), ret);
            ret = ICCC_ERROR_ATTESTATION_FAILED;
        }
    }
error:
    return ret;
}

static int is_sec_boot_secure(uint32_t value) {
  const uint32_t SEC_BOOT_SECURE_VALUE = 1;
  return value == SEC_BOOT_SECURE_VALUE ? true : false;
}

static int is_react_lock_secure(uint32_t value) {
  const uint32_t REACT_LOCK_SECURE_VALUE = 1;
  return value == REACT_LOCK_SECURE_VALUE ? true : false;
}

static int is_kiwi_lock_secure(uint32_t value) {
  const uint32_t KIWI_LOCK_SECURE_VALUE = 1;
  return value == KIWI_LOCK_SECURE_VALUE ? true : false;
}

static int is_frp_lock_secure(uint32_t value) {
  const uint32_t FRP_LOCK_SECURE_VALUE = 1;
  return value == FRP_LOCK_SECURE_VALUE ? true : false;
}

static int is_cc_mode_secure(uint32_t value) {
  const uint32_t CC_MODE_SECURE_VALUE = 1;
  return value == CC_MODE_SECURE_VALUE ? true : false;
}

static int is_curr_bin_status_secure(uint32_t value) {
  const uint32_t CURR_BIN_STATUS_SECURE_VALUE = 0;
  return value == CURR_BIN_STATUS_SECURE_VALUE ? true : false;
}

static int is_warranty_bit_secure(uint32_t value) {
  const uint32_t WARRANTY_BIT_SECURE_VALUE = 0;
  return value == WARRANTY_BIT_SECURE_VALUE ? true : false;
}

static int is_kap_secure(uint32_t value) {
  const uint32_t KAP_SECURE_VALUE = 1;
  return value == KAP_SECURE_VALUE ? true : false;
}

/**
 * 0:Market   User(01)      Official
 * 1:Factory  Eng(10)/Knox(11)  Custom
 *
 * Secure values:
 * No Download History  = 0 0 0 0
 * Market+User+Official = 0 0 1 0
 * Market+Knox+Official = 0 1 1 0
 * we are not using knox bit until now.
 **/
static int is_image_status1_secure(uint32_t value) {
  const uint32_t IMAGE_STATUS1_SECURE_VALUE_NO_HISTORY = 0; 
  const uint32_t IMAGE_STATUS1_SECURE_VALUE_USER = 2; 
  return (value == IMAGE_STATUS1_SECURE_VALUE_NO_HISTORY)
      || (value == IMAGE_STATUS1_SECURE_VALUE_USER) ? true : false;
}

static int is_pkm_text_secure(uint32_t value) {
  const uint32_t PKM_TEXT_SECURE_VALUE = 0;
  const uint32_t PKM_TEXT_UNINITIALIZED_VALUE = -1;
  return (value == PKM_TEXT_SECURE_VALUE) || (value == PKM_TEXT_UNINITIALIZED_VALUE) ? true : false;
}

static int is_pkm_ro_secure(uint32_t value) {
  const uint32_t PKM_RO_SECURE_VALUE = 0;
  const uint32_t PKM_RO_UNINITIALIZED_VALUE = -1;
  return (value == PKM_RO_SECURE_VALUE) || (value == PKM_RO_UNINITIALIZED_VALUE) ? true : false;
}

static int is_selinux_secure(uint32_t value) {
  const uint32_t SELINUX_SECURE_VALUE = 0;
  const uint32_t SELINUX_UNINITIALIZED_VALUE = -1;
  return (value == SELINUX_SECURE_VALUE) || (value == SELINUX_UNINITIALIZED_VALUE) ? true : false;
}

static int is_dmv_secure(uint32_t value) {
  const uint32_t DMV_SECURE_VALUE = 0;
  return value == DMV_SECURE_VALUE ? true : false;
}

static int is_sysscope_secure(uint32_t value) {
  const uint32_t SYSSCOPE_INSECURE_VALUE = 1;
  return value != SYSSCOPE_INSECURE_VALUE ? true : false;
}

static int is_trust_boot_secure(uint32_t value) {
  const uint32_t TRUSTBOOT_SECURE_VALUE = 0;
  return value == TRUSTBOOT_SECURE_VALUE ? true : false;
}

typedef int (*iccc_status_check_func_t)(uint32_t);

#define ICCC_NUMBER_OF_COMPONENT_TYPES 2

typedef uint8_t comp_type_array_t[ICCC_NUMBER_OF_COMPONENT_TYPES];

#define INSECURE_MSG_TYPE 0
#define FLAG_NOT_READ 1

typedef struct {
  const char *name;
  const uint8_t name_size;
  const uint32_t type;
  const uint32_t type_bitwise;
  iccc_status_check_func_t is_secure;
  const uint8_t * comp_type_array;
} iccc_status_flags_t;

//                                                        HARD_INTEGRITY  SOFT_INTEGRITY
static comp_type_array_t const comp_types_for_sec_boot        = { false,   false   };
static comp_type_array_t const comp_types_for_react_lock      = { false,   false  };
static comp_type_array_t const comp_types_for_kiwi_lock       = { false,   false  };
static comp_type_array_t const comp_types_for_frp_lock        = { false,   false  };
static comp_type_array_t const comp_types_for_cc_mode         = { false,   false  };
static comp_type_array_t const comp_types_for_curr_bin_status = { false,   false  };
static comp_type_array_t const comp_types_for_warrant_bit     = { false,   true   };
static comp_type_array_t const comp_types_for_kap_status      = { false,   false  };
static comp_type_array_t const comp_types_for_image_status1   = { false,   false  };
static comp_type_array_t const comp_types_for_pkm_text        = { false,   false  };
static comp_type_array_t const comp_types_for_pkm_ro          = { false,   false  };
static comp_type_array_t const comp_types_for_selinux_status  = { false,   false  };
static comp_type_array_t const comp_types_for_dmv_status      = { false,   false  };
static comp_type_array_t const comp_types_for_sysscope_flag   = { false,   false   };
static comp_type_array_t const comp_types_for_trustboot_flag  = { true,   true  };

static iccc_status_flags_t const iccc_status_flags[] =
{//  Name                         Size      Value             Bitwise value                     Function                   Component Types that use this flag.
     {"Secure Boot",                11,     SEC_BOOT,         ICCC_STATUS_FLAG_SEC_BOOT,        is_sec_boot_secure,        comp_types_for_sec_boot },
     {"Reactivation Lock",          17,     REACT_LOCK,       ICCC_STATUS_FLAG_REACT_LOCK,      is_react_lock_secure,      comp_types_for_react_lock },
     {"KIWI Lock",                   9,     KIWI_LOCK,        ICCC_STATUS_FLAG_KIWI_LOCK,       is_kiwi_lock_secure,       comp_types_for_kiwi_lock },
     {"Factory Reset Protection",   24,     FRP_LOCK,         ICCC_STATUS_FLAG_FRP_LOCK,        is_frp_lock_secure,        comp_types_for_frp_lock },
     {"CC Mode",                     7,     CC_MODE,          ICCC_STATUS_FLAG_CC_MODE,         is_cc_mode_secure,         comp_types_for_cc_mode },
     {"Current Binary Status",      21,     CURR_BIN_STATUS,  ICCC_STATUS_FLAG_CURR_BIN_STATUS, is_curr_bin_status_secure, comp_types_for_curr_bin_status },
     {"Warranty Bit",               12,     WARRANTY_BIT,     ICCC_STATUS_FLAG_WARRANT_BIT,     is_warranty_bit_secure,    comp_types_for_warrant_bit },
     {"Knox Active Protection",     22,     KAP_STATUS,       ICCC_STATUS_FLAG_KAP_STATUS,      is_kap_secure,             comp_types_for_kap_status },
     {"Status of Boot Image",       20,     IMAGE_STATUS1,    ICCC_STATUS_FLAG_IMAGE_STATUS1,   is_image_status1_secure,   comp_types_for_image_status1 },
     {"PKM Text",                    8,     PKM_TEXT,         ICCC_STATUS_FLAG_PKM_TEXT,        is_pkm_text_secure,        comp_types_for_pkm_text },
     {"PKM Read Only",              13,     PKM_RO,           ICCC_STATUS_FLAG_PKM_RO,          is_pkm_ro_secure,          comp_types_for_pkm_ro },
     {"SELINUX",                     7,     SELINUX_STATUS,   ICCC_STATUS_FLAG_SELINUX_STATUS,  is_selinux_secure,         comp_types_for_selinux_status },
     {"dm-verity",                   9,     DMV_STATUS,       ICCC_STATUS_FLAG_DMV_STATUS,      is_dmv_secure,             comp_types_for_dmv_status },
     {"Sysscope",                    8,     SYSSCOPE_FLAG,    ICCC_STATUS_FLAG_SYSSCOPE_FLAG,   is_sysscope_secure,        comp_types_for_sysscope_flag },
     {"Trusted Boot",               12,     TRUSTBOOT_FLAG,   ICCC_STATUS_FLAG_TRUSTBOOT_FLAG,  is_trust_boot_secure,      comp_types_for_trustboot_flag }
};

uint32_t append_to_result_msg(char *result_message, uint32_t *offset, uint32_t result_message_len, uint8_t type, const char* flag_name, const uint8_t flag_size) {
  const char insecure_msg_preffix[] = "The value of ";
  const char read_error_msg_preffix[] = "The value of ";
  const char insecure_msg_suffix[] = " is insecure. ";
  const char read_error_msg_suffix[] = " could not be read. ";
  uint8_t insecure_msg_size = 0;
  uint8_t read_error_msg_size = 0;
  uint32_t init_offset = *offset;

  switch(type) {
  case INSECURE_MSG_TYPE:
    if( (sizeof(insecure_msg_preffix) + sizeof(insecure_msg_suffix) + flag_size - 2) <= UINT8_MAX) {
        insecure_msg_size = sizeof(insecure_msg_preffix) + sizeof(insecure_msg_suffix) + flag_size - 2;
        if((*offset + insecure_msg_size) < result_message_len) {
          memcpy(result_message + *offset, insecure_msg_preffix, sizeof(insecure_msg_preffix) - 1);
          *offset += sizeof(insecure_msg_preffix) - 1;
          memcpy(result_message + *offset, flag_name, flag_size);
          *offset += flag_size;
          memcpy(result_message + *offset, insecure_msg_suffix, sizeof(insecure_msg_suffix) - 1);
          *offset += sizeof(insecure_msg_suffix) - 1;
          *offset -= init_offset;
        }
    }
    break;
  case FLAG_NOT_READ:
    if( (sizeof(read_error_msg_preffix) + sizeof(read_error_msg_suffix) + flag_size - 2) <= UINT8_MAX) {
        read_error_msg_size = sizeof(read_error_msg_preffix) + sizeof(read_error_msg_suffix) + flag_size - 2;
        if((*offset + read_error_msg_size) < result_message_len) {
          memcpy(result_message + *offset, read_error_msg_preffix, sizeof(read_error_msg_preffix) - 1);
          *offset += sizeof(read_error_msg_preffix) - 1;
          memcpy(result_message + *offset, flag_name, flag_size);
          *offset += flag_size;
          memcpy(result_message + *offset, read_error_msg_suffix, sizeof(read_error_msg_suffix) - 1);
          *offset += sizeof(read_error_msg_suffix) - 1;
          *offset -= init_offset;
        }
    }
    break;
  }

  return *offset ;
}

uint32_t check_flag(char * result_message, uint32_t * offset, const iccc_status_flags_t * flag) {
  uint32_t value;
  uint32_t read_result;
  uint32_t ret = ICCC_STATUS_RETURN_SECURE;

  read_result = Iccc_ReadData_TA(flag->type, &value);
  if(read_result == ICCC_SUCCESS) {
    if (flag->is_secure(value)) {
      // The Secure message is printed later.
      ret = ICCC_STATUS_RETURN_SECURE;
    } else {
      append_to_result_msg(result_message, offset, ICCC_STATUS_MAX_RESULT_MESSAGE, INSECURE_MSG_TYPE, flag->name, flag->name_size);
      ret = ICCC_STATUS_RETURN_NOT_SECURE;
    }
  } else {
    append_to_result_msg(result_message, offset, ICCC_STATUS_MAX_RESULT_MESSAGE, FLAG_NOT_READ, flag->name, flag->name_size);
    ret = ICCC_STATUS_RETURN_FAILURE_TO_READ;
  }
  return ret;
}

uint32_t ICCC_device_status_check_flags(uint32_t comp_type, uint32_t * result_code, char * result_message) {
  uint32_t result_check;
  uint32_t offset = 0;
  const char device_secure_msg[] = "Device Secure.";
  uint32_t i = 0;

  *result_code = ICCC_STATUS_RETURN_SECURE;
  for (i = 0; i < (sizeof(iccc_status_flags) / sizeof(iccc_status_flags_t)); i++) {
    if (iccc_status_flags[i].comp_type_array[comp_type - 1]) { // tz_iccc_device_status_comp_type_t starts on 1
      ICCC_LOG_DEBUG("ICCC: Checking flag %d for component %d\n", i, comp_type);
      result_check = check_flag(result_message, &offset, &iccc_status_flags[i]);
      if(result_check != ICCC_STATUS_RETURN_SECURE) {
	     *result_code |= iccc_status_flags[i].type_bitwise;
      }
    }
  }

  if (*result_code == ICCC_STATUS_RETURN_SECURE) {
    memcpy(result_message, device_secure_msg, sizeof(device_secure_msg));
  } else {
    // Remove the trailing space
    result_message[offset - 1] = '\0';
  }

  return ICCC_SUCCESS;
}

/**
  * API to check device integrity by analyzing a set of ICCC flags
  *
  * @param comp_type each component type will check a different set of ICCC flags
  * @param resp_msg_buf buffer where the result message is copied
  * @param resp_msg_buf_size the size of resp_msg_buf buffer
  * @param resp_msg_len the length of the result message that was copied to resp_msg_buf
  * @param result_code is 0 if device is secure, otherwise it is a bitwise value indicating which ICCC flags are not secure. Flag bit positions are defined in tz_iccc_device_status_flag_t type from IcccInterface.h
  * @return the operation status can be ICCC_ERROR_DEVICE_STATUS_FAILED or ICCC_SUCCESS
  *
  */
uint32_t Iccc_DeviceStatus_TA(uint32_t comp_type, uint8_t *resp_msg_buf, uint32_t resp_msg_buf_size, uint32_t *resp_msg_len, uint32_t *result_code) {
  uint32_t ret = ICCC_ERROR_DEVICE_STATUS_FAILED;

  if (resp_msg_buf == NULL) {
    ICCC_LOG_DEBUG("ICCC: Iccc_DeviceStatus_TA: resp_msg_buf NULL");
    return ret;
  }

  if (resp_msg_buf_size < ICCC_STATUS_MAX_RESULT_MESSAGE) {
    ICCC_LOG_DEBUG("ICCC: Iccc_DeviceStatus_TA: resp_msg_buf_size < ICCC_STATUS_MAX_RESULT_MESSAGE");
    return ret;
  }
  if(comp_type < ICCC_STATUS_COMP_TYPE_HARD_INTEGRITY || comp_type > ICCC_STATUS_COMP_TYPE_SOFT_INTEGRITY) {
    ICCC_LOG_DEBUG("ICCC: Iccc_DeviceStatus_TA: comp_type %d", comp_type);
    return ret;
  }

  ret = ICCC_device_status_check_flags(comp_type, result_code, (char *)resp_msg_buf);
  if (ret  != ICCC_SUCCESS) {
    ICCC_LOG("ICCC: error checking flags\n");
    return ret;
  }

  (*resp_msg_len) = strlen((char *)resp_msg_buf);
  ICCC_LOG("ICCC: Device Status comp_type = %d ret = %d", comp_type, ret);
  return ret;
}

#if defined (CONFIG_SUPPORT_ICCC_INIT)
uint32_t Iccc_ReadData_TAForTB( uint8_t *p_msr_buf, uint32_t p_msr_size) {
	uint32_t value;
  	uint32_t ret = ICCC_INIT_ERROR;

	
	ICCC_LOG_DEBUG("ICCC: ICCC_ReadData_TAForTB, p_msr_size: %d", p_msr_size);

	ret = ICCC_init_TB(p_msr_buf, 0);
	if(ret == ICCC_INIT_UNINITIALIZED_SECURE_MEM){
		ret = ICCC_init_TB(p_msr_buf, p_msr_size);
	}

	if(ret){
		ICCC_LOG("ICCC: ICCC Read Data TA For TB error!");
		ICCC_LOG_DEBUG("ICCC: End of ICCC_ReadData_TAForTB, error= %x", ret);
		return ret;
	}
	else{
		Iccc_ReadData_TA(TRUSTBOOT_FLAG, &value);
		ICCC_LOG("ICCC: End of ICCC Read Data TA For TB");
		ICCC_LOG_DEBUG("ICCC: End of ICCC_ReadData_TAForTB, TB = %x", value);

		return value;
	}
}
#endif
#endif
