/*
 * app_driver.c
 */

#ifdef TEEGRIS_SDK42
#include <core/uio.h>
#endif
#include <tee_internal_api.h>

#include <errno.h> // errno
#include <fcntl.h>  // O_RDWR
#include <sys/ioctl.h> // ioctl
#include <unistd.h> // close
#include <stdbool.h>

#ifdef TZ_TAG_MTK
#include "tz_tag.h"
#endif
#include "app_core.h"
#include "app_driver.h"

#include "icccOperations_v4.h"

#ifdef TEEGRIS_DBG
static inline uint32_t calculate_diff(const TEE_Time *time1, const TEE_Time *time2)
{
    return (time1->millis - time2->millis) + (time1->seconds - time2->seconds) * 1000;
}
#endif

/* For ICCC secure address */
static uint32_t iccc_sec_mem_addr = ICCC_SECURE_MEM_BASE_ADDR;

uint32_t get_sec_ICCC_address(int type)
{
#if defined (TZ_TAG_MTK)
    iccc_sec_mem_addr = get_swd_buffer();
    ICCC_LOG_DEBUG("TZ_ICCC: get_swd_buffer iccc_sec_mem_addr : %X", iccc_sec_mem_addr);
#endif
    if (iccc_sec_mem_addr == 0) {
        ICCC_LOG("TZ_ICCC: Wrong ICCC_SECURE_MEM_BASE_ADDR");
        return ICCC_MEM_ADDR_ERROR;
    }
    ICCC_LOG_DEBUG("TZ_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;
        default:
            return ICCC_MEM_ADDR_ERROR;
    }
}

// .../tima_common/src/TZ_Vendor_tl.c
uint32_t Iccc_phys_write(void *offset, uint32_t length, void *data_buf)
{
    uint32_t ret = ICCC_SUCCESS;
    int drv_ret = 0;
    struct ioctl_mem_access_data data;

    drv_fd = open(drv_name, O_RDWR, 0);
    if (drv_fd < 0) {
        ICCC_LOG("TZ_ICCC: Iccc_phys_write: drv_open_client failed errno = %d", errno);
        return drv_fd;
    }

    data.phys_addr = (uint32_t)offset;
    data.len = length;
    data.status = -1;
    data.rw_addr = data_buf;

#ifdef TEEGRIS_SDK42
    ICCC_LOG("TZ_ICCC: Iccc_phys_write: ioctl for TEEGRIS SDK 4.2");
    struct ioctl_arg data_new = {};
    data_new.input[0].iov_base = &data;
    data_new.input[0].iov_len = sizeof(struct ioctl_mem_access_data);
    data_new.input[1].iov_base = data_buf;
    data_new.input[1].iov_len = sizeof(data_buf);
    data_new.input_cnt = 2;

    data_new.output[0].iov_base = &data;
    data_new.output[0].iov_len = sizeof(struct ioctl_mem_access_data);
    data_new.output[1].iov_base = data_buf;
    data_new.output[1].iov_len = sizeof(data_buf);
    data_new.output_cnt = 2;
    drv_ret = ioctl(drv_fd, ICCCUTIL_SECURE_PHYS_WRITE, (unsigned long)&data_new);
#else
    ICCC_LOG("TZ_ICCC: Iccc_phys_write: ioctl for TEEGRIS SDK 4.x");
    drv_ret = ioctl(drv_fd, ICCCUTIL_SECURE_PHYS_WRITE, (unsigned long)&data);
#endif
    if (drv_ret < 0) {
        ICCC_LOG("TZ_ICCC: Iccc_phys_write: ioctl error, errno = %d", errno);
        ret = ICCC_ERROR_WRITE_FAILED;
        goto exit;
    }

    if (data.status < 0) {
        ICCC_LOG("TZ_ICCC: Iccc_phys_write: data.status = %d", data.status);
        ret = ICCC_ERROR_WRITE_FAILED;
        goto exit;
    }

exit:
    drv_ret = close(drv_fd);
    if (drv_ret < 0) {
        ICCC_LOG("Iccc_phys_write: drv_client_close(): close error, errno = %d\n", errno);
        return drv_ret;
    }

    return ret;
}

// .../tima_common/src/TZ_Vendor_tl.c
uint32_t Iccc_phys_read(void *offset, uint32_t length, void *ret_buf)
{
    uint32_t ret = ICCC_SUCCESS;
    int drv_ret = 0;
    struct ioctl_mem_access_data data;

#ifdef TEEGRIS_DBG
    TEE_Time start_time;
    TEE_Time end_time;
    uint32_t time_ms = 0;
    TEE_GetSystemTime(&start_time);
#endif

    drv_fd = open(drv_name, O_RDWR, 0);
    if (drv_fd < 0) {
        ICCC_LOG("TZ_ICCC: Iccc_phys_read: drv_open_client failed errno = %d", errno);
        return drv_fd;
    }

#ifdef TEEGRIS_DBG
    TEE_GetSystemTime(&end_time);
    time_ms = calculate_diff(&end_time, &start_time);
    printf("driver open time_ms: %u\n", time_ms);
    TEE_MemFill(&start_time, 0, sizeof(TEE_Time));
    TEE_MemFill(&end_time, 0, sizeof(TEE_Time));
    time_ms = 0;
    TEE_GetSystemTime(&start_time);
#endif

    data.phys_addr = (uint32_t)offset;
    data.len = length;
    data.rw_addr = ret_buf;
    data.status = -1;
    TEE_MemFill(data.rw_addr, 0, length);

#ifdef TEEGRIS_SDK42
    ICCC_LOG("TZ_ICCC: Iccc_phys_read: ioctl for TEEGRIS SDK 4.2");
    struct ioctl_arg data_new = {};
    data_new.input[0].iov_base = &data;
    data_new.input[0].iov_len = sizeof(struct ioctl_mem_access_data);
    data_new.input[1].iov_base = ret_buf;
    data_new.input[1].iov_len = sizeof(ret_buf);
    data_new.input_cnt = 2;

    data_new.output[0].iov_base = &data;
    data_new.output[0].iov_len = sizeof(struct ioctl_mem_access_data);
    data_new.output[1].iov_base = ret_buf;
    data_new.output[1].iov_len = sizeof(ret_buf);
    data_new.output_cnt = 2;
    drv_ret = ioctl(drv_fd, ICCCUTIL_SECURE_PHYS_READ, (unsigned long)&data_new);
#else
    ICCC_LOG("TZ_ICCC: Iccc_phys_read: ioctl for TEEGRIS SDK 4.x");
    drv_ret = ioctl(drv_fd, ICCCUTIL_SECURE_PHYS_READ, (unsigned long)&data);
#endif
    if (drv_ret < 0) {
        ICCC_LOG("TZ_ICCC: Iccc_phys_read: ioctl error, errno = %d", errno);
        ret = ICCC_ERROR_READ_FAILED;
        goto exit;
    }

#ifdef TEEGRIS_DBG
    TEE_GetSystemTime(&end_time);
    time_ms = calculate_diff(&end_time, &start_time);
    printf("driver ioctl time_ms: %u\n", time_ms);
    TEE_MemFill(&start_time, 0, sizeof(TEE_Time));
    TEE_MemFill(&end_time, 0, sizeof(TEE_Time));
    time_ms = 0;
#endif

    if (data.status < 0) {
        ICCC_LOG("TZ_ICCC: Iccc_phys_read: data.status = %d", data.status);
        ret = ICCC_ERROR_READ_FAILED;
        goto exit;
    }

exit:
    drv_ret = close(drv_fd);
    if (drv_ret < 0) {
        ICCC_LOG("TZ_ICCC: Iccc_phys_read: drv_client_close(): close error, errno = %d", errno);
        return drv_ret;
    }

    return ret;
}

// ICCC local Memory
extern iccc_secure_pamameters_info_t *iccc_local_memory;

uint32_t read_from_shared_memory(void *offset, uint32_t length, void *ret_buf)
{
    uint32_t ret = ICCC_SUCCESS;
    int drv_ret = 0;
    struct ioctl_mem_access_data data;

    /* avoid ROP chaining  */
    static bool first_time = true;
    if (first_time) {
        drv_fd = open(drv_name, O_RDWR, 0);
        if (drv_fd < 0) {
            ICCC_LOG("TZ_ICCC: drv_open_client failed errno = %d", errno);
            return drv_fd;
        }

        data.phys_addr = (uint32_t)offset;
        data.len = length;
        data.status = -1;
        data.rw_addr = ret_buf;
        TEE_MemFill(data.rw_addr, 0, length);

#ifdef TEEGRIS_SDK42
        ICCC_LOG("TZ_ICCC: Iccc_phys_read: ioctl for TEEGRIS SDK 4.2");
        struct ioctl_arg data_new = {};
        data_new.input[0].iov_base = &data;
        data_new.input[0].iov_len = sizeof(struct ioctl_mem_access_data);
        data_new.input[1].iov_base = ret_buf;
        data_new.input[1].iov_len = sizeof(ret_buf);
        data_new.input_cnt = 2;

        data_new.output[0].iov_base = &data;
        data_new.output[0].iov_len = sizeof(struct ioctl_mem_access_data);
        data_new.output[1].iov_base = ret_buf;
        data_new.output[1].iov_len = sizeof(ret_buf);
        data_new.output_cnt = 2;
        drv_ret = ioctl(drv_fd, ICCCUTIL_SECURE_PHYS_READ, (unsigned long)&data_new);
#else
        ICCC_LOG("TZ_ICCC: Iccc_phys_read: ioctl for TEEGRIS SDK 4.x");
        drv_ret = ioctl(drv_fd, ICCCUTIL_SECURE_PHYS_READ, (unsigned long)&data);
#endif
        if (drv_ret < 0) {
            ICCC_LOG("TZ_ICCC: ioctl error, errno = %d\n", errno);
            ret = ICCC_ERROR_READ_FAILED;
            goto exit;
        }

        if (data.status < 0) {
            ICCC_LOG("TZ_ICCC: data.status= %d\n", data.status);
            ret = ICCC_ERROR_READ_FAILED;
            goto exit;
        }

exit:
        drv_ret = close(drv_fd);
        if (drv_ret < 0) {
            ICCC_LOG("TZ_ICCC: drv_client_close(): close error, errno = %d\n", errno);
            return drv_ret;
        }
        first_time = false;
    }

    return ret;
}

uint32_t ICCC_init_memory()
{
    uint32_t secure_mem_addr;
    uint32_t ret;

    secure_mem_addr = get_sec_ICCC_address(PARAM_FOR_ICCC_SEC_MEM);
    if (secure_mem_addr == 0) {
        ret = ICCC_ERROR_READ_FAILED;
        return ret;
    }

    iccc_local_memory = TEE_Malloc(ICCC_SECURE_PARAMETERS_LENGTH + ICCC_LOCK_FLAG_LENGTH,0);

    if (0 != (ret = read_from_shared_memory((void *)(secure_mem_addr + ICCC_BL_SECURE_PARAMETERS_OFFSET), (ICCC_SECURE_PARAMETERS_LENGTH + ICCC_LOCK_FLAG_LENGTH), (void *)iccc_local_memory))) {
        ICCC_LOG("TZ_ICCC: read_from_shared_memory failed");
        ICCC_LOG_DEBUG("TZ_ICCC: read_from_shared_memory 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;
    }
    return ret;
}
