/*
 * icccOperations.c
 */

#ifndef ICCC_v4

#include <tlStd.h>
#include <TlApi/TlApi.h>

#include "tl_tz_iccc_init.h" // ICCC_INIT_ERROR, ICCC_INIT_UNINITIALIZED_SECURE_MEM

#include "icccOperations.h"

#if ICCC_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

// .../tz_platform/public/tl/TZ_Vendor_tl.h
#define SHA256_DIGEST_LENGTH 32

//#define UINT8_MAX 0xff /* 255U */

char iccc_log_msg[LOG_MSG_SIZE];

/* For ICCC secure address */
static uint32_t iccc_sec_mem_addr = ICCC_SECURE_MEM_BASE_ADDR;

/* START OF DEPENDENCY REMOVAL */
/**
 * Get ICCC secure memory base address (FC Handler)
 *
 * @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 ICCC_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 (TZ Driver)
 *
 * 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 ICCC_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;
    }
}

// .../tz_platform/vendor/TBASE/tl/TBASE_TZ_Vendor.c
uint32_t Iccc_phys_write(
    void *physAddr,
    uint32_t len,
    void *addr)
{
    return (Iccc_phys_access(physAddr, len, addr, ICCC_PHYS_WRITE));
}

// .../tz_platform/vendor/TBASE/tl/TBASE_TZ_Vendor.c
uint32_t Iccc_phys_read(
    void *physAddr,
    uint32_t len,
    void *addr)
{
    return (Iccc_phys_access(physAddr, len, addr, ICCC_PHYS_READ));
}

// .../tz_platform/vendor/TBASE/tl/TBASE_TZ_Vendor.c
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;
    }
}
/* END OF DEPENDENCY REMOVAL */

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_FLAG, TRUSTBOOT_FLAG, TIMA_VERSION_FLAG, 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, 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 PKM_TEXT:
            if (value != TEXT_REG_SUCCESS)
                ret = 1;
            break;
        case PKM_RO:
            if (value != TEXT_RO_SUCCESS)
                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_FLAG, TRUSTBOOT_FLAG, TIMA_VERSION_FLAG, 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, 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, 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};

    ICCC_LOG_DEBUG("ICCC: Iccc_check_magic: secure_param_type = %#x secure_mem_addr = %#x", secure_param_type, secure_mem_addr);
    if (secure_param_type == 0xF) {
        //ICCC_LOG("ICCC: 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: 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: 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: 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;
        }
    }
#ifdef CONFIG_ROT_IN_ICCC
    else if (secure_param_type == 0x3) {
        //ICCC_LOG("ICCC: in RoT 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;
        }
    }
#endif
    else {
        ICCC_LOG("ICCC: unknown block ");
        ret = ICCC_ERROR_READ_FAILED;
    }

    ICCC_LOG_DEBUG("ICCC: magic = %x , ret = %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 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)) {
        ICCC_LOG_DEBUG("ICCC: Unknown type");
        ret = ICCC_UNKNOWN_TYPE;
        goto exit;
    }

    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_DEBUG("ICCC: secure_param_type = %x", secure_param_type);
    ICCC_LOG_DEBUG("ICCC: secure_param_addr = %x", secure_param_addr);

    ICCC_LOG("ICCC: Iccc_SaveData_TA@#");

    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_STATUS, VERIFIEDBOOT_HASH parameter can be set by SMC call only.");
#ifndef ICCC_TEST_CODE_ENABLED
            return ICCC_PERMISSION_DENIED;
#endif
        }
        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;
        }
        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;
        }
    }
#ifdef CONFIG_ROT_IN_ICCC
    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;
    }
#endif
    else {
        ICCC_LOG("ICCC: Iccc_phys_write failed");
        ICCC_LOG_DEBUG("ICCC: Iccc_phys_write failed: unknown error");
        ret = ICCC_ERROR_WRITE_FAILED;
    }

exit:
    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)) {
        ICCC_LOG_DEBUG("ICCC: Unknown type");
        ret = ICCC_UNKNOWN_TYPE;
        goto exit;
    }

    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_DEBUG("ICCC: secure_param_type = %x", secure_param_type);
    ICCC_LOG_DEBUG("ICCC: secure_param_addr = %x", secure_param_addr);

    ICCC_LOG("ICCC: Iccc_ReadData_TA@#");

    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;
        }
        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 {
            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) {
            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 {
            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;
        }
        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;
        }
    }
#ifdef CONFIG_ROT_IN_ICCC
    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)
            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) {
                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 {
                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;
                }
            }
        }
    }
#endif
    else {
        ICCC_LOG("ICCC: Iccc_phys_read failed");
        ICCC_LOG_DEBUG("ICCC: Iccc_phys_read failed: unknown error");
        ret = ICCC_ERROR_READ_FAILED;
    }

exit:
    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 ret = ICCC_SUCCESS;

    uint32_t secure_param_type;
    uint32_t secure_param_addr;
    uint32_t secure_mem_addr;
    uint16_t magic_str = 0;

    ICCC_LOG("ICCC: Iccc_SaveATN_TA: given blob from N/W, blob_len = %d", *blob_len);

    // 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: (*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: 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);
        ICCC_MemMove(blob + *blob_len, (uint8_t *)&sectimer_value, sizeof(sectimer_value)); 
        *blob_len += sizeof(sectimer_value);

        ICCC_LOG("ICCC: ATN blob after adding sectimer type 0x%x", sectimer_type[i][0]);
    }

    // Generate digest
    ret = Iccc_digest_SHA256(blob, *blob_len, digest, &digest_len);
    if (ret) {
        ICCC_LOG("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 with ret = 0x%x, length = %d", ret, digest_len);
        ret = ICCC_ERROR_ATTESTATION_FAILED;
        goto error;
    }
    //ICCC_LOG("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;
}

#define ICCC_NUMBER_OF_COMPONENT_TYPES 2

typedef int (*iccc_status_check_func_t)(uint32_t);
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             Bit value                         Function                   Component Types
    {"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)) <= UINT8_MAX) {
                insecure_msg_size = sizeof(insecure_msg_preffix);
                if ((sizeof(insecure_msg_suffix)) > 0 && (insecure_msg_size <= (UINT8_MAX-(sizeof(insecure_msg_suffix))))) {
                    insecure_msg_size += (sizeof(insecure_msg_suffix));
                    if (flag_size > 0 && (insecure_msg_size <= UINT8_MAX - flag_size)) {
                        insecure_msg_size = insecure_msg_size + flag_size - 2;
                        if ((*offset + insecure_msg_size) < result_message_len) {
                            ICCC_MemMove((void*)(result_message + *offset), (void*)insecure_msg_preffix, sizeof(insecure_msg_preffix) - 1);
                            *offset += sizeof(insecure_msg_preffix) - 1;
                            ICCC_MemMove((void*)(result_message + *offset), (void*)flag_name, flag_size);
                            *offset += flag_size;
                            ICCC_MemMove((void*)(result_message + *offset), (void*)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)) <= UINT8_MAX) {
                read_error_msg_size = sizeof(read_error_msg_preffix);
                if ((sizeof(read_error_msg_suffix)) > 0 && (read_error_msg_size <= (UINT8_MAX-(sizeof(read_error_msg_suffix))))) {
                    read_error_msg_size += (sizeof(read_error_msg_suffix));
                    if (flag_size > 0 && (read_error_msg_size <= UINT8_MAX - flag_size)) {
                        read_error_msg_size = read_error_msg_size + flag_size - 2;
                        if ((*offset + read_error_msg_size) < result_message_len) {
                            ICCC_MemMove((void*)(result_message + *offset), (void*)read_error_msg_preffix, sizeof(read_error_msg_preffix) - 1);
                            *offset += sizeof(read_error_msg_preffix) - 1;
                            ICCC_MemMove((void*)(result_message + *offset), (void*)flag_name, flag_size);
                            *offset += flag_size;
                            ICCC_MemMove((void*)(result_message + *offset), (void*)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("ICCC: Checking flag %d for component %d", 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) {
        ICCC_MemMove(result_message, device_secure_msg, strlen(device_secure_msg));
    } else {
        // Remove the trailing space
        result_message[offset - 1] = '\0';
    }

    return 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("ICCC: Iccc_DeviceStatus_TA: resp_msg_buf NULL");
        return ret;
    }

    if (resp_msg_buf_size < ICCC_STATUS_MAX_RESULT_MESSAGE) {
        ICCC_LOG("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("ICCC: Iccc_DeviceStatus_TA: comp_type %d", comp_type);
        return ret;
    }

    memset(resp_msg_buf, 0, resp_msg_buf_size); // invalid resp_msg_buf log

    ret = ICCC_device_status_check_flags(comp_type, result_code, (char *)resp_msg_buf);
    if (ret != ICCC_SUCCESS) {
        ICCC_LOG("ICCC: error checking flags");
        return ret;
    }

    (*resp_msg_len) = strlen((char *)resp_msg_buf);

    ICCC_LOG("ICCC: Device Status comp_type = %d ret = %d", comp_type, ret);
    ICCC_LOG("ICCC: Device Status resp_msg_buf = %s resp_msg_len = %d", (char *)resp_msg_buf, *resp_msg_len);
    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 // ICCC_v4
