/*
 * app_saveData.c
 */

#include "app_main.h"
#include "app_core.h"
#include "app_driver.h"
#include "app_saveData.h"

#include "icccOperations_v4.h"

uint32_t Iccc_Core_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("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_SaveData_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_WRITE_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_WRITE_FAILED;
        }
        ICCC_LOG("TZ_ICCC: type already set");
        ICCC_LOG_DEBUG("TZ_ICCC: Bootloader parameters were set during bootup by bootloader. No further modifications are allowed");
        ret = ICCC_PERMISSION_DENIED;
    } 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_WRITE_FAILED;
        }
        if (type == DMV_STATUS || type == VERIFIEDBOOT_HASH) {
            ICCC_LOG("TZ_ICCC: special type");
            ICCC_LOG_DEBUG("TZ_ICCC: DMV_STATUS, VERIFIEDBOOT_HASH parameter can be set by SMC call only.");
            return ICCC_PERMISSION_DENIED;
        }
        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("TZ_ICCC: Iccc_phys_write failed");
            ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: secure_param_addr error");
            ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: type is locked");
            ICCC_LOG_DEBUG("TZ_ICCC: only one write is allowed on this type");
            return  ICCC_PERMISSION_DENIED;
        }
        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_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("TZ_ICCC: Iccc_phys_write failed");
                ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: secure_param_addr error");
                ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: Iccc_phys_write failed");
                ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: Update Lock failed");
            ICCC_LOG_DEBUG("TZ_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("TZ_ICCC: Iccc_phys_write failed");
            ICCC_LOG_DEBUG("TZ_ICCC: Iccc_phys_write failed: RoT locked");
            ret = ICCC_ERROR_WRITE_FAILED;
    } else {
        ICCC_LOG("TZ_ICCC: Iccc_phys_write failed");
        ICCC_LOG_DEBUG("TZ_ICCC: Iccc_phys_write failed: unknown error");
        ret = ICCC_ERROR_WRITE_FAILED;
    }

exit:
    return ret;
}

uint32_t ICCC_save_data(tz_iccc_generic_payload_t *sendmsg, tz_iccc_generic_payload_t *respmsg)
{
    uint32_t type = 0;
    uint32_t secure_value = 0;
    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;
    secure_value = sendmsg->content.iccc_req.value;

    ICCC_LOG("TZ_ICCC: ICCC_save_data, type = %x , value = %d", type, secure_value);

    if (ICCC_SECTION_TYPE(type) == TA_ICCC_TYPE_START && type != SELINUX_STATUS) {
        ICCC_LOG("TZ_ICCC: ICCC_save_data permission is denied on type %x", type);
        ret = ICCC_PERMISSION_DENIED;
        goto error;
    }

    ret = Iccc_Core_SaveData_TA(type, secure_value);
    if (ret) {
        ICCC_LOG("TZ_ICCC: Iccc_Core_SaveData_TA failed with ret = %x", ret);
        ret = ICCC_ERROR_WRITE_FAILED;
    }

error:
    respmsg->content.iccc_rsp.ret = ret;
    return ret;
}
