/*
 * app_readData.c
 */

#include "app_main.h"
#include "app_cipher.h"
#include "app_core.h"
#include "app_driver.h"
#include "app_readData.h"

#include "icccOperations_v4.h"

uint32_t Iccc_Core_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("TZ_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("TZ_ICCC: secure_param_type = %x", secure_param_type);
    ICCC_LOG_DEBUG("TZ_ICCC: secure_param_addr = %x", secure_param_addr);

    ICCC_LOG("TZ_ICCC: Iccc_Core_ReadData_TA@#");

    ret = Iccc_check_magic(secure_param_type, secure_mem_addr, &magic_str);
    if (ret) {
        ICCC_LOG("TZ_ICCC: check magic failed");
        ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: secure_param_addr error");
            ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: Iccc_phys_read failed");
            ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: secure_param_addr error");
            ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: Iccc_phys_read failed");
                ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: Iccc_phys_read failed");
                ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: secure_param_addr error");
            ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: Iccc_phys_read failed");
                ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: Iccc_phys_read failed");
                ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: secure_param_addr error");
            ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: Iccc_phys_read failed");
            ICCC_LOG_DEBUG("TZ_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)
            if (0 != (ret = Iccc_phys_read((void *)(secure_mem_addr + ICCC_ROT_SECURE_PARAMETERS_OFFSET),
                                                    ICCC_ROT_DATA_LENGTH, (void *)value))) {
                ICCC_LOG("TZ_ICCC: Iccc_phys_read failed");
                ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: secure_param_addr error");
                ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: Iccc_phys_read failed");
                    ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: Iccc_phys_read failed");
                    ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: Iccc_phys_read failed");
        ICCC_LOG_DEBUG("TZ_ICCC: Iccc_phys_read failed: unknown error");
        ret = ICCC_ERROR_READ_FAILED;
    }

exit:

#ifdef DEBUG_ICCC_MEM
    Iccc_DumpParams_TA();
#endif

    return ret;
}

uint32_t ICCC_read_data(tz_iccc_generic_payload_t *sendmsg, tz_iccc_generic_payload_t *respmsg)
{
    uint32_t type = 0;
    uint32_t secure_value;
    uint32_t ret = ICCC_SUCCESS;

    if (respmsg == NULL || sendmsg == NULL) {
        ICCC_LOG("TZ_ICCC: sendmsg or respmsg is NULL ");
        return ICCC_FAILURE;
    }

    type = sendmsg->content.iccc_req.type;

    ICCC_LOG("TZ_ICCC: ICCC_read_data, type = %x", type);

    if (type == ATN_BLOB_HASH || type == VERIFIEDBOOT_HASH || type == VERIFIEDBOOTKEY_FLAG || type == ROT_STRUCT) {
        ICCC_LOG("TZ_ICCC: ICCC_read_data permission is denied on type %x", type);
        ret = ICCC_PERMISSION_DENIED;
        goto error;
    }

    ret = Iccc_Core_ReadData_TA(type, &secure_value);
    if (ret) {
        ICCC_LOG("TZ_ICCC: Iccc_Core_ReadData_TA failed with ret = %x", ret);
        ret = ICCC_ERROR_READ_FAILED;
    } else {
        respmsg->content.iccc_rsp.value = secure_value;
        respmsg->content.iccc_rsp.type = type;
    }

    ICCC_LOG("TZ_ICCC: value = %x", secure_value);
    ICCC_LOG("TZ_ICCC: respmsg->content.iccc_rsp.type = %x", respmsg->content.iccc_rsp.type);

error:
    respmsg->content.iccc_rsp.ret = ret;
    return ret;
}
