/*
 * app_main.c
 */

#include <stdio.h>
#include <tee_internal_api.h>
#include <tees_extension.h>

#include "app_property.h"
#include "app_version.h"

#include "app_main.h"
#include "app_allowlist.h"
#include "app_credential_rot.h"
#include "app_credential_bl.h"
#include "app_credential_iccc.h"
#include "app_grdm_status.h"

#include "icccOperations_grdm.h"
#include "iccc_tata_common_grdm.h"

static tciMessage_t sendMsgCopy;
static tciMessage_t rspMsgCopy;
static iccc_tata_message_t tata_sendMsgCopy;
static iccc_tata_message_t tata_rspMsgCopy;

TEE_Result TA_CreateEntryPoint(void)
{
    printf(TAG "TA_CreateEntryPoint \n");
    grdm_ICCC_set_GRDM_status();
    printf(TAG "grdm_ICCC_set_GRDM_status \n");
    return TEE_SUCCESS;
}

void TA_DestroyEntryPoint(void)
{
    printf(TAG "TA_DestroyEntryPoint \n");
}

TEE_Result TA_OpenSessionEntryPoint(uint32_t paramTypes, TEE_Param params[4], void** sessionContext)
{
    printf(TAG "TA_OpenSessionEntryPoint \n");
    (void) paramTypes;
    (void) params;
    (void) sessionContext;

    return TEE_SUCCESS;
}

void TA_CloseSessionEntryPoint(void* sessionContext)
{
    printf(TAG "TA_CloseSessionEntryPoint \n");
    (void) sessionContext;
}

uint32_t process_cmd(uint32_t commandId, tciMessage_t *sendmsg, tciMessage_t *respmsg)
{
    uint32_t ret = TZ_GRDM_ICCC_SUCCESS;

    /* Process command message */
    switch (commandId) {
        case CMD_ICCC_GRDM_ATTESTATION:
            printf(TAG "received command: %d! \n", commandId);
            ret = grdm_ICCC_attestation(&sendmsg->payload.attestation, &respmsg->payload.attestation);
            break;
        default:
            printf(TAG "received unknown command: %d! \n", commandId);
            ret = TZ_GRDM_ICCC_UNKNOWN_CMD;
            respmsg->payload.generic.content.iccc_rsp.ret = ret;
            break;
    }

    printf(TAG "process_cmd: ret: %d \n", 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 = TZ_GRDM_ICCC_SUCCESS;

    /* Process command message */
    switch (commandId) {
        case CMD_ICCC_GRDM_GET_ROT_CRED_TA:
            if (check_ta_cmd_permission(ta_name, commandId) == TZ_GRDM_ICCC_SUCCESS) {
                ret = grdm_ICCC_get_ROT_credential((uint32_t *) &(respmsg->payload.tata_generic.content.iccc_common_req.grdm_rot_cred));
            } else {
                printf(TAG "command denied \n");
#if DEBUG_ICCC
                printf(TAG "TA %02x%02x%02x%02x%02x%02x%02x%02x doesn't have permission to execute command %x \n", // DEBUG
                                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], commandId);
#endif
                ret = TZ_GRDM_ICCC_PERMISSION_DENIED;
            }
            break;
        case CMD_ICCC_GRDM_GET_BL_CRED_TA:
            if (check_ta_cmd_permission(ta_name, commandId) == TZ_GRDM_ICCC_SUCCESS) {
                ret = grdm_ICCC_get_BL_credential((uint32_t *) &(respmsg->payload.tata_generic.content.iccc_common_req.grdm_bl_cred));
            } else {
                printf(TAG "command denied \n");
#if DEBUG_ICCC
                printf(TAG "TA %02x%02x%02x%02x%02x%02x%02x%02x doesn't have permission to execute command %x \n", // DEBUG
                                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], commandId);
#endif
                ret = TZ_GRDM_ICCC_PERMISSION_DENIED;
            }
            break;
        case CMD_ICCC_GRDM_GET_ICCC_CRED_TA:
            if (check_ta_cmd_type_permission(ta_name, commandId, sendmsg->payload.tata_generic.content.iccc_common_req.type) == TZ_GRDM_ICCC_SUCCESS) {
                uint32_t credential_index = type_to_credential_map(sendmsg->payload.tata_generic.content.iccc_common_req.type);
#if DEBUG_ICCC
                printf(TAG "credential_index %d \n", credential_index);
#endif
                switch (credential_index) {
                    case ICCC_CREDENTIAL_1:
                        ret = grdm_ICCC_get_ICCC_credential_1(sendmsg->payload.tata_generic.content.iccc_common_req.type, (uint32_t *) &(respmsg->payload.tata_generic.content.iccc_common_req.grdm_iccc_cred1));
                        break;
                    case ICCC_CREDENTIAL_2:
                        ret = grdm_ICCC_get_ICCC_credential_2(sendmsg->payload.tata_generic.content.iccc_common_req.type, (uint32_t *) &(respmsg->payload.tata_generic.content.iccc_common_req.grdm_iccc_cred2));
                        break;
                    default:
                        ret = TZ_GRDM_ICCC_ERROR_NOT_SUPPORT;
                        printf(TAG "check credential index failed on execute command %d on type %d \n", commandId, sendmsg->payload.tata_generic.content.iccc_common_req.type);
                        break;
                }
            } else {
                printf(TAG "command denied \n");
#if DEBUG_ICCC
                printf(TAG "TA %02x%02x%02x%02x%02x%02x%02x%02x doesn't have permission to execute command %x \n", // DEBUG
                                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], commandId);
#endif
                ret = TZ_GRDM_ICCC_PERMISSION_DENIED;
            }
            break;
        case CMD_ICCC_GRDM_SET_ICCC_CRED_TA:
            if (check_ta_cmd_type_permission(ta_name, commandId, sendmsg->payload.tata_generic.content.iccc_common_req.type) == TZ_GRDM_ICCC_SUCCESS) {
                uint32_t credential_index = type_to_credential_map(sendmsg->payload.tata_generic.content.iccc_common_req.type);
#if DEBUG_ICCC
                printf(TAG "credential_index %d \n", credential_index);
#endif
                switch (credential_index) {
                    case ICCC_CREDENTIAL_1:
                        ret = grdm_ICCC_store_ICCC_credential_1(sendmsg->payload.tata_generic.content.iccc_common_req.type, (uint32_t *) &(sendmsg->payload.tata_generic.content.iccc_common_req.grdm_iccc_cred1));
                        break;
                    case ICCC_CREDENTIAL_2:
                        ret = grdm_ICCC_store_ICCC_credential_2(sendmsg->payload.tata_generic.content.iccc_common_req.type, (uint32_t *) &(sendmsg->payload.tata_generic.content.iccc_common_req.grdm_iccc_cred2));
                        break;
                    default:
                        ret = TZ_GRDM_ICCC_ERROR_NOT_SUPPORT;
                        printf(TAG "check credential index failed on execute command %d on type %d \n", commandId, sendmsg->payload.tata_generic.content.iccc_common_req.type);
                        break;
                }
            } else {
                printf(TAG "command denied \n");
#if DEBUG_ICCC
                printf(TAG "TA %02x%02x%02x%02x%02x%02x%02x%02x doesn't have permission to execute command %x \n", // DEBUG
                                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], commandId);
#endif
                ret = TZ_GRDM_ICCC_PERMISSION_DENIED;
            }
            break;
        case CMD_ICCC_GRDM_DEL_ICCC_CRED_TA:
            if (check_ta_cmd_type_permission(ta_name, commandId, sendmsg->payload.tata_generic.content.iccc_common_req.type) == TZ_GRDM_ICCC_SUCCESS) {
                uint32_t credential_index = type_to_credential_map(sendmsg->payload.tata_generic.content.iccc_common_req.type);
#if DEBUG_ICCC
                printf(TAG "credential_index %d \n", credential_index);
#endif
                switch (credential_index) {
                    case ICCC_CREDENTIAL_1:
                        ret = grdm_ICCC_delete_ICCC_credential_1(sendmsg->payload.tata_generic.content.iccc_common_req.type);
                        break;
                    case ICCC_CREDENTIAL_2:
                        ret = grdm_ICCC_delete_ICCC_credential_2(sendmsg->payload.tata_generic.content.iccc_common_req.type);
                        break;
                    default:
                        ret = TZ_GRDM_ICCC_ERROR_NOT_SUPPORT;
                        printf(TAG "check credential index failed on execute command %d on type %d \n", commandId, sendmsg->payload.tata_generic.content.iccc_common_req.type);
                        break;
                }
            } else {
                printf(TAG "command denied \n");
#if DEBUG_ICCC
                printf(TAG "TA %02x%02x%02x%02x%02x%02x%02x%02x doesn't have permission to execute command %x \n", // DEBUG
                                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], commandId);
#endif
                ret = TZ_GRDM_ICCC_PERMISSION_DENIED;
            }
            break;
        default:
            printf(TAG "received unknown command: %d! \n", commandId);
            ret = TZ_GRDM_ICCC_UNKNOWN_CMD;
            break;
    }

    printf(TAG "process_cmd_from_ta: ret: %d \n", ret);
    return ret;
}

/* Trustlet entry */
TEE_Result TA_InvokeCommandEntryPoint(void* sessionContext, uint32_t commandID, uint32_t paramTypes, TEE_Param params[4])
{
    printf(TAG "TA_InvokeCommandEntryPoint \n");

    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;

    printf(TAG "tz buildinfo: version %s, build %s, teegris %s, chipset %s \n",  TZ_ICCC_GRDM_VERSION, TARGET_BUILD_VARIANT, TEEGRIS_VERSION, TEEGRIS_BUILD_MODEL);
    printf(TAG "paramTypes: %d, TEE_PARAM_TYPES: %d \n",
                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)) {
        printf(TAG "Bad Parameters in TA_InvokeCommandEntryPoint \n");
        return TEE_ERROR_BAD_PARAMETERS;
    }

    TEE_Identity *clnt_id = TEE_Malloc(sizeof(TEE_Identity), 0);
    if (clnt_id == NULL) {
        printf(TAG "Error to allocate TEE_Identity memory \n");
        return TEE_ERROR_BAD_PARAMETERS;
    }
    ret = TEE_GetPropertyAsIdentity(TEE_PROPSET_CURRENT_CLIENT, "gpd.client.identity", clnt_id);
#if DEBUG_ICCC
    printf(TAG "login %x \n", clnt_id->login); // DEBUG
#endif
    if (ret != TEE_SUCCESS) {
#if DEBUG_ICCC
        printf(TAG "Error to get Session Identity ret: %x \n", ret); // DEBUG
#endif
        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
#if DEBUG_ICCC
            printf(TAG "Session opened from uuid=%02x%02x%02x%02x%02x%02x%02x%02x \n", // DEBUG
                                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]);
#endif
            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)) {
                printf(TAG "Shared memory buffer size check error \n");
                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)) {
                printf(TAG "Shared memory buffer size check error \n");
                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) {
                printf(TAG "TEES_IsREESharedMemory access check error \n");
                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;
}
