/*
 * app_main.c
 */

#include <tee_internal_api.h>
#include <tees_extension.h>

#include "app_property.h"
#include "app_version.h"

#include "app_main.h"
#include "app_core.h"
#include "app_driver.h"
#include "app_allowlist.h"
#include "app_saveData.h"
#include "app_readData.h"
#include "app_getDeviceStatus.h"
#include "app_attestation.h"

#include "icccOperations_v4.h"
#include "iccc_tata_common.h"

static tciMessage_t sendMsgCopy;
static tciMessage_t rspMsgCopy;
static iccc_tata_message_t tata_sendMsgCopy;
static iccc_tata_message_t tata_rspMsgCopy;

// ICCC local Memory
iccc_secure_pamameters_info_t *iccc_local_memory;

TEE_Result TA_CreateEntryPoint(void)
{
    ICCC_LOG("TZ_ICCC: TA_CreateEntryPoint");
    ICCC_init_memory();

    return TEE_SUCCESS;
}

void TA_DestroyEntryPoint(void)
{
    ICCC_LOG("TZ_ICCC: TA_DestroyEntryPoint");
    TEE_Free(iccc_local_memory);
}

TEE_Result TA_OpenSessionEntryPoint(uint32_t paramTypes, TEE_Param params[4], void** sessionContext)
{
    ICCC_LOG("TZ_ICCC: TA_OpenSessionEntryPoint");
    (void) paramTypes;
    (void) params;
    (void) sessionContext;

    return TEE_SUCCESS;
}

void TA_CloseSessionEntryPoint(void* sessionContext)
{
    ICCC_LOG("TZ_ICCC: TA_CloseSessionEntryPoint");
    (void) sessionContext;
}

uint32_t process_cmd(uint32_t commandId, tciMessage_t *sendmsg, tciMessage_t *respmsg)
{
    uint32_t ret = ICCC_SUCCESS;

    /*  Process command message */
    switch (commandId) {
        case CMD_ICCC_SAVEDATA:
            ret = ICCC_save_data(&sendmsg->payload.generic, &respmsg->payload.generic);
            break;
        case CMD_ICCC_READDATA:
            ret = ICCC_read_data(&sendmsg->payload.generic, &respmsg->payload.generic);
            break;
        case CMD_ICCC_DEVICE_STATUS:
            ret = ICCC_device_status(&sendmsg->payload.status, &respmsg->payload.status);
            break;
        case CMD_ICCC_ATTESTATION:
            ret = ICCC_attestation(&sendmsg->payload.attestation, &respmsg->payload.attestation);
            break;
        default:
            ICCC_LOG("TZ_ICCC: received unknown command: %d!", commandId);
            ret = CMD_ICCC_UNKNOWN;
            respmsg->payload.generic.content.iccc_rsp.ret = ret;
            break;
    }

    ICCC_LOG("TZ_ICCC: process_cmd: ret: %d", ret);
    return ret;
}

uint32_t process_cmd_from_ta(uint32_t commandId, iccc_tata_message_t *sendmsg, iccc_tata_message_t *respmsg, TEE_UUID *ta_name)
{
    uint32_t ret = ICCC_SUCCESS;

    /* Process command message */
    switch (commandId) {
        case CMD_ICCC_SAVEDATA_TA:
            if (check_permission(ta_name, ICCC_PERMISSION_CMD_SAVE_DATA, sendmsg->payload.tata_generic.content.iccc_common_req.type)) {
                ret = Iccc_Core_SaveData_TA(sendmsg->payload.tata_generic.content.iccc_common_req.type,
                                            sendmsg->payload.tata_generic.content.iccc_common_req.value);
            } else {
                ICCC_LOG("TZ_ICCC: command denied");
                ICCC_LOG_DEBUG("TZ_ICCC: TA %02x%02x%02x%02x%02x%02x%02x%02x doesn't have permission to execute command %x with type %x",
                                ta_name->clockSeqAndNode[0],
                                ta_name->clockSeqAndNode[1],
                                ta_name->clockSeqAndNode[2],
                                ta_name->clockSeqAndNode[3],
                                ta_name->clockSeqAndNode[4],
                                ta_name->clockSeqAndNode[5],
                                ta_name->clockSeqAndNode[6],
                                ta_name->clockSeqAndNode[7], CMD_ICCC_SAVEDATA_TA,
                                sendmsg->payload.tata_generic.content.iccc_common_req.type);
                ret = ICCC_COMMAND_DENIED;
            }
            break;
        case CMD_ICCC_READDATA_TA:
            if (check_permission(ta_name, ICCC_PERMISSION_CMD_READ_DATA, sendmsg->payload.tata_generic.content.iccc_common_req.type)) {
                ret = Iccc_Core_ReadData_TA(sendmsg->payload.tata_generic.content.iccc_common_req.type,
                                          &(respmsg->payload.tata_generic.content.iccc_common_req.value));
            } else {
                ICCC_LOG("TZ_ICCC: command denied");
                ICCC_LOG_DEBUG("TZ_ICCC: TA %02x%02x%02x%02x%02x%02x%02x%02x doesn't have permission to execute command %x with type %x",
                                ta_name->clockSeqAndNode[0],
                                ta_name->clockSeqAndNode[1],
                                ta_name->clockSeqAndNode[2],
                                ta_name->clockSeqAndNode[3],
                                ta_name->clockSeqAndNode[4],
                                ta_name->clockSeqAndNode[5],
                                ta_name->clockSeqAndNode[6],
                                ta_name->clockSeqAndNode[7], CMD_ICCC_READDATA_TA,
                                sendmsg->payload.tata_generic.content.iccc_common_req.type);
                ret = ICCC_COMMAND_DENIED;
            }
            break;
        case CMD_ICCC_DEVICE_STATUS_TA:
            if (check_device_status_permission(ta_name)) {
                ret = Iccc_Core_DeviceStatus_TA(sendmsg->payload.tata_status.comp_type,
                                                respmsg->payload.tata_status.resp_msg_buf,
                                                sendmsg->payload.tata_status.resp_msg_buf_size,
                                              &(respmsg->payload.tata_status.resp_msg_len),
                                              &(respmsg->payload.tata_status.result_code));
            } else {
                ICCC_LOG("TZ_ICCC: command denied");
                ICCC_LOG_DEBUG("TZ_ICCC: TA %02x%02x%02x%02x%02x%02x%02x%02x doesn't have permission to execute command %x",
                                ta_name->clockSeqAndNode[0],
                                ta_name->clockSeqAndNode[1],
                                ta_name->clockSeqAndNode[2],
                                ta_name->clockSeqAndNode[3],
                                ta_name->clockSeqAndNode[4],
                                ta_name->clockSeqAndNode[5],
                                ta_name->clockSeqAndNode[6],
                                ta_name->clockSeqAndNode[7], CMD_ICCC_DEVICE_STATUS_TA);
                ret = ICCC_COMMAND_DENIED;
            }
            break;
        default:
            ICCC_LOG("TZ_ICCC: received unknown command: %d!", commandId);
            ret = CMD_ICCC_UNKNOWN;
            break;
    }

    ICCC_LOG("TZ_ICCC: process_cmd_from_ta: ret: %d", ret);
    return ret;
}

/* Trustlet entry */
TEE_Result TA_InvokeCommandEntryPoint(void* sessionContext, uint32_t commandID, uint32_t paramTypes, TEE_Param params[4])
{
    ICCC_LOG("TZ_ICCC: TA_InvokeCommandEntryPoint");

    uint32_t ret;
    tciMessage_t* sendmsg = NULL;
    tciMessage_t* respmsg = NULL;
    iccc_tata_message_t* tata_sendmsg = NULL;
    iccc_tata_message_t* tata_respmsg = NULL;

    ICCC_LOG("TZ_ICCC: tz buildinfo: version %s, build %s, teegris %s, chipset %s", TZ_ICCC_VERSION, TARGET_BUILD_VARIANT, TEEGRIS_VERSION, TEEGRIS_BUILD_MODEL);
    ICCC_LOG("TZ_ICCC: paramTypes: %d, TEE_PARAM_TYPES: %d",
                       paramTypes, TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT, TEE_PARAM_TYPE_MEMREF_OUTPUT, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE));
    if (paramTypes != (uint32_t)TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT, TEE_PARAM_TYPE_MEMREF_OUTPUT, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE)) {
        ICCC_LOG("TZ_ICCC: Bad Parameters in TA_InvokeCommandEntryPoint");
        return TEE_ERROR_BAD_PARAMETERS;
    }

    TEE_Identity *clnt_id = TEE_Malloc(sizeof(TEE_Identity), 0);
    if (clnt_id == NULL) {
        ICCC_LOG("TZ_ICCC: Error to allocate TEE_Identity memory");
        return TEE_ERROR_BAD_PARAMETERS;
    }
    ret = TEE_GetPropertyAsIdentity(TEE_PROPSET_CURRENT_CLIENT, "gpd.client.identity", clnt_id);
    ICCC_LOG_DEBUG("TZ_ICCC: login %x", clnt_id->login);
    if (ret != TEE_SUCCESS) {
        ICCC_LOG_DEBUG("TZ_ICCC: Error to get Session Identity ret: %x",ret);
        TEE_MemFill(clnt_id, 0, sizeof(TEE_Identity));
        TEE_Free(clnt_id);
        return ret;
    } else {
        if (clnt_id->login == TEE_LOGIN_TRUSTED_APP) {
            /* Entry if session was open from another TA */
            // We can check TA UUID requester session
            ICCC_LOG_DEBUG("TZ_ICCC: Session opened from uuid=%02x%02x%02x%02x%02x%02x%02x%02x",
                                clnt_id->uuid.clockSeqAndNode[0],
                                clnt_id->uuid.clockSeqAndNode[1],
                                clnt_id->uuid.clockSeqAndNode[2],
                                clnt_id->uuid.clockSeqAndNode[3],
                                clnt_id->uuid.clockSeqAndNode[4],
                                clnt_id->uuid.clockSeqAndNode[5],
                                clnt_id->uuid.clockSeqAndNode[6],
                                clnt_id->uuid.clockSeqAndNode[7]);

            if (params[0].memref.buffer == NULL || params[1].memref.buffer == NULL ||
                params[0].memref.size != sizeof(iccc_tata_message_t) || params[1].memref.size != sizeof(iccc_tata_message_t)) {
                ICCC_LOG("TZ_ICCC: Shared memory buffer size check error");
                TEE_MemFill(clnt_id, 0, sizeof(TEE_Identity));
                TEE_Free(clnt_id);
                return TEE_ERROR_BAD_PARAMETERS;
            }

            /* Local buffer to prevent Race Condition */
            TEE_MemFill(&tata_sendMsgCopy, 0, sizeof(iccc_tata_message_t));
            TEE_MemFill(&tata_rspMsgCopy, 0, sizeof(iccc_tata_message_t));
            TEE_MemMove(&tata_sendMsgCopy, params[0].memref.buffer, sizeof(iccc_tata_message_t));

            tata_sendmsg = &tata_sendMsgCopy;
            tata_respmsg = &tata_rspMsgCopy;

            ret = process_cmd_from_ta(commandID, tata_sendmsg, tata_respmsg, &clnt_id->uuid);

            tata_respmsg->header.id = RSP_ID(commandID);
            tata_respmsg->header.status = ret;

            TEE_MemMove(params[1].memref.buffer, &tata_rspMsgCopy, sizeof(iccc_tata_message_t));
            TEE_MemFill(&tata_sendMsgCopy, 0, sizeof(iccc_tata_message_t));
            TEE_MemFill(&tata_rspMsgCopy, 0, sizeof(iccc_tata_message_t));

        } else { // TEE_LOGIN_PUBLIC
            /* Entry if session was open from NW */
            if (params[0].memref.buffer == NULL || params[1].memref.buffer == NULL ||
                params[0].memref.size < sizeof(tciMessage_t) || params[1].memref.size < sizeof(tciMessage_t)) {
                ICCC_LOG("TZ_ICCC: Shared memory buffer size check error");
                TEE_MemFill(clnt_id, 0, sizeof(TEE_Identity));
                TEE_Free(clnt_id);
                return TEE_ERROR_BAD_PARAMETERS;
            }

            if (TEES_IsREESharedMemory(TEE_MEMORY_ACCESS_READ | TEE_MEMORY_ACCESS_WRITE, params[0].memref.buffer, params[0].memref.size) != TEE_SUCCESS ||
                TEES_IsREESharedMemory(TEE_MEMORY_ACCESS_WRITE, params[1].memref.buffer, params[1].memref.size) != TEE_SUCCESS) {
                ICCC_LOG("TZ_ICCC: TEES_IsREESharedMemory access check error");
                TEE_MemFill(clnt_id, 0, sizeof(TEE_Identity));
                TEE_Free(clnt_id);
                return TEE_ERROR_ACCESS_DENIED;
            }

            /* Local buffer to prevent Race Condition */
            TEE_MemFill(&sendMsgCopy, 0, sizeof(tciMessage_t));
            TEE_MemFill(&rspMsgCopy, 0, sizeof(tciMessage_t));
            TEE_MemMove(&sendMsgCopy, params[0].memref.buffer, sizeof(tciMessage_t));

            sendmsg = &sendMsgCopy;
            respmsg = &rspMsgCopy;

            ret = process_cmd(commandID, sendmsg, respmsg);

            respmsg->header.id = RSP_ID(commandID);
            respmsg->header.status = ret;

            TEE_MemMove(params[1].memref.buffer, &rspMsgCopy, sizeof(tciMessage_t));
            TEE_MemFill(&sendMsgCopy, 0, sizeof(tciMessage_t));
            TEE_MemFill(&rspMsgCopy, 0, sizeof(tciMessage_t));
        }
    }

    TEE_MemFill(clnt_id, 0, sizeof(TEE_Identity));
    TEE_Free(clnt_id);

    return TEE_SUCCESS;
}
