#include "kg_read_data.h"

#include "kg_log.h"
#include "kg_err.h"
#include "tee_internal_api.h"

uint32_t read_info_object(kg_rpmb_info_t **info) {
    KG_LOG("read_info_object\n");
    uint32_t ret = KG_SUCCESS;
#ifdef CONFIG_QSEE
/* error: incompatible pointer types passing 'kg_rpmb_info_t **' (aka 'struct kg_rpmb_info **') to parameter of type 'uint8_t **' (aka 'unsigned char **') [-Werror,-Wincompatible-pointer-types]*/    
    if (0 != (ret = read_block(0, sizeof(kg_rpmb_info_t), (uint8_t **)info))) {
        KG_LOG("Failed to read info object\n");
    }
#else    
    if (0 != (ret = read_block(0, sizeof(kg_rpmb_info_t), info))) {
        KG_LOG("Failed to read info object\n");
    }
#endif    
    return ret;
}

uint32_t read_wrap_data(uint32_t* pLength, uint8_t** pData) {
    KG_LOG("read_wrap_data\n");
    uint32_t wrap_data_len = 0;
    kg_rpmb_info_t* info = NULL;
    uint32_t ret = 0;
    if (*pLength == 0) {
        // need to get data length from info_object
        if (0 != (ret = read_info_object(&info))) {
            KG_LOG("Failed to read info object\n");
            goto exit;
        }
        if(info->kg_wrap_data_len > KG_SECURE_DATA_LEN){
            KG_LOG("Received invalid size for wrap_data\n");
            ret = KG_BUFFER_SIZE_FAIL;
            goto exit;
        }
        wrap_data_len = info->kg_wrap_data_len;
        *pLength = info->kg_wrap_data_len;
    } else {
        wrap_data_len = *pLength;
    }

    if (0 == wrap_data_len) {
        KG_LOG("wrap data length is 0, so can not read it\n");
        ret = KG_RPMB_READ_FAIL;
        goto exit;
    }

    ret = read_block(WRAP_DATA_BLOCK_OFFSET, wrap_data_len, pData);

exit:
    if (NULL != info) {
        TEE_Free(info);
        info = NULL;
    }
    return ret;
}

uint32_t read_policy_file(uint32_t* pLength, uint8_t** pData) {
    KG_LOG("read_policy_file\n");
    uint32_t policy_file_len = 0;
    kg_rpmb_info_t* info = NULL;
    uint32_t ret = 0;
    if (*pLength == 0) {
        // need to get data length from info_object
        if (0 != (ret = read_info_object(&info))) {
            KG_LOG("Failed to read info object\n");
            goto exit;
        }
        if(info->policy_file_len > KG_POLICY_LEN_MAX){
            KG_LOG("Received invalid size for policy_file\n");
            ret = KG_BUFFER_SIZE_FAIL;
            goto exit;
        }
        policy_file_len = info->policy_file_len;
        *pLength = info->policy_file_len;
    } else {
        policy_file_len = *pLength;
    }

    if (0 == policy_file_len) {
        KG_LOG("policy length is 0, so can not read it\n");
        ret = KG_RPMB_READ_FAIL;
        goto exit;
    }

    ret = read_block(POLICY_FILE_BLOCK_OFFSET, policy_file_len, pData);

exit:
    if (NULL != info) {
        TEE_Free(info);
        info = NULL;
    }
    return ret;
}

uint32_t read_lock_object(uint32_t* pLength, uint8_t** pData) {
    KG_LOG("read_lock_object\n");
    uint32_t lock_object_len = 0;
    kg_rpmb_info_t* info = NULL;
    uint32_t ret = 0;
    if (*pLength == 0) {
        // need to get data length from info_object
        if (0 != (ret = read_info_object(&info))) {
            KG_LOG("Failed to read info object\n");
            goto exit;
        }
        if(info->lock_object_len > KG_LOCK_OBJECT_MAX){
            KG_LOG("Received invalid size for lock_object\n");
            ret = KG_BUFFER_SIZE_FAIL;
            goto exit;
        }
        lock_object_len = info->lock_object_len;
        *pLength = info->lock_object_len;
    } else {
        lock_object_len = *pLength;
    }

    if (0 == lock_object_len) {
        KG_LOG("lock object length is 0, so can not read it\n");
        ret = KG_RPMB_READ_FAIL;
        goto exit;
    }

    ret = read_block(LOCK_OBJECT_BLOCK_OFFSET, lock_object_len, pData);

exit:
    if (NULL != info) {
        TEE_Free(info);
        info = NULL;
    }
    return ret;
}

uint32_t read_client_data(uint32_t* pLength, uint8_t** pData) {
    KG_LOG("read_client_data\n");
    uint32_t client_data_len = 0;
    kg_rpmb_info_t* info = NULL;
    uint32_t ret = 0;
    if (*pLength == 0) {
        // need to get data length from info_object
        if (0 != (ret = read_info_object(&info))) {
            KG_LOG("Failed to read info object\n");
            goto exit;
        }
        if(info->client_data_len > KG_CLIENT_DATA_MAX){
            KG_LOG("Received invalid size for client_data\n");
            ret = KG_BUFFER_SIZE_FAIL;
            goto exit;
        }
        client_data_len = info->client_data_len;
        *pLength = info->client_data_len;
    } else {
        client_data_len = *pLength;
    }

    if (0 == client_data_len) {
        KG_LOG("client data length is 0, so can not read it\n");
        ret = KG_RPMB_READ_FAIL;
        goto exit;
    }

    ret = read_block(CLIENT_DATA_BLOCK_OFFSET, client_data_len, pData);

exit:
    if (NULL != info) {
        TEE_Free(info);
        info = NULL;
    }
    return ret;
}

uint32_t read_complete_token(kg_rpmb_complete_token_t **token) {
    KG_LOG("read_complete_token\n");
    uint32_t ret = KG_SUCCESS;
#ifdef CONFIG_QSEE    
    if (KG_SUCCESS != (ret = read_block(COMPLETE_TOKEN_BLOCK_OFFSET, sizeof(kg_rpmb_complete_token_t), (uint8_t **)token))) {
        KG_LOG("Failed to read complete token\n");
    }
#else
    if (KG_SUCCESS != (ret = read_block(COMPLETE_TOKEN_BLOCK_OFFSET, sizeof(kg_rpmb_complete_token_t), token))) {
        KG_LOG("Failed to read complete token\n");
    }
#endif    
    return ret;
}

uint32_t write_info_object(kg_rpmb_info_t *info) {
    KG_LOG("write_info_object\n");
    uint32_t ret = KG_SUCCESS;
#ifdef CONFIG_QSEE
/* error: incompatible pointer types passing 'kg_rpmb_info_t *' (aka 'struct kg_rpmb_info *') to parameter of type 'uint8_t *' (aka 'unsigned char *') [-Werror,-Wincompatible-pointer-types]*/    
    if (KG_SUCCESS != (ret = write_block(INFO_OBJECT_BLOCK_OFFSET, sizeof(kg_rpmb_info_t), (uint8_t *)info))) {
        KG_LOG("Failed to write info object\n");
    }
#else   
    if (KG_SUCCESS != (ret = write_block(INFO_OBJECT_BLOCK_OFFSET, sizeof(kg_rpmb_info_t), info))) {
        KG_LOG("Failed to write info object\n");
    }
#endif    
    return ret;
}

uint32_t write_wrap_data(uint32_t length, uint8_t* pData) {
    KG_LOG("write_wrap_data\n");
    uint32_t ret = KG_SUCCESS;
    if (KG_SUCCESS != (ret = write_block(WRAP_DATA_BLOCK_OFFSET, length, pData))) {
        KG_LOG("Failed to write wrap data\n");
    }
    return ret;
}

uint32_t write_policy_file(uint32_t length, uint8_t* pData) {
    KG_LOG("write_policy_file\n");
    uint32_t ret = KG_SUCCESS;
    if (KG_SUCCESS != (ret = write_block(POLICY_FILE_BLOCK_OFFSET, length, pData))) {
        KG_LOG("Failed to write policy file\n");
    }
    return ret;
}

uint32_t write_lock_object(uint32_t length, uint8_t* pData) {
    KG_LOG("write_lock_object\n");
    uint32_t ret = KG_SUCCESS;
    if (KG_SUCCESS != (ret = write_block(LOCK_OBJECT_BLOCK_OFFSET, length, pData))) {
        KG_LOG("Failed to write lock object\n");
    }
    return ret;
}

uint32_t write_client_data(uint32_t length, uint8_t* pData) {
    KG_LOG("write_client_data\n");
    uint32_t ret = KG_SUCCESS;
    if (KG_SUCCESS != (ret = write_block(CLIENT_DATA_BLOCK_OFFSET, length, pData))) {
        KG_LOG("Failed to write client data\n");
    }
    return ret;
}

uint32_t write_complete_token(kg_rpmb_complete_token_t* token) {
    KG_LOG("write_complete_token\n");
    uint32_t ret = KG_SUCCESS;
#ifdef CONFIG_QSEE    
    if (KG_SUCCESS != (ret = write_block(COMPLETE_TOKEN_BLOCK_OFFSET, sizeof(kg_rpmb_complete_token_t), (uint8_t *)token))) {
        KG_LOG("Failed to write info object\n");
    }
#else
    if (KG_SUCCESS != (ret = write_block(COMPLETE_TOKEN_BLOCK_OFFSET, sizeof(kg_rpmb_complete_token_t), token))) {
        KG_LOG("Failed to write info object\n");
    }
#endif    
    return ret;
}

uint32_t clear_rpmb(void) {
    KG_LOG("clear_rpmb\n");
    kg_rpmb_data_t* rpmb = NULL;
    uint32_t ret = KG_SUCCESS;
    rpmb = TEE_Malloc(sizeof(kg_rpmb_data_t), 0);
    if (NULL == rpmb) {
        KG_LOG("failed to alloc rpmb data\n");
        ret = KG_ALLOC_BUFFER_FAIL;
        goto exit;
    }
#ifdef CONFIG_QSEE    
    if (KG_SUCCESS != (ret = write_block(INFO_OBJECT_BLOCK_OFFSET, sizeof(kg_rpmb_data_t), (uint8_t *)rpmb))) {
        KG_LOG("Failed to clear rpmb\n");
    }
#else
    if (KG_SUCCESS != (ret = write_block(INFO_OBJECT_BLOCK_OFFSET, sizeof(kg_rpmb_data_t), rpmb))) {
        KG_LOG("Failed to clear rpmb\n");
    }
#endif    

exit:
    if (NULL != rpmb) {
        TEE_Free(rpmb);
        rpmb = NULL;
    }
    return 0;
}