/*
 * init_TLC implementation
 */

#undef NDEBUG

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>

#define LOG_TAG "TZ_init: "
#include "log.h"

#include "tlc_communication.h"
#include "spay_init_tlc.h"

/* prototypes */
static uint32_t TZ_init_START(
    tz_spay_init_msg_cmd_t * cmd
);

static uint32_t TZ_init_with_data_START(
    tz_spay_init_msg_cmd_t * cmd
);

static uint32_t TZ_init_END(
    tz_spay_init_msg_resp_t * resp
);

/* implementation */

static uint32_t TZ_init_START(
    tz_spay_init_msg_cmd_t * cmd
)
{
    memset(cmd->measurement.blob, 0x00, TZ_SPAY_TIMA_MSR_MAX_SIZE);

    cmd->measurement.len = 0;

    return (TZ_SPAY_INIT_OK);
}

static uint32_t TZ_init_with_data_START(
    tz_spay_init_msg_cmd_t * cmd
)
{
    int msr_info_fd = -1;
    int msr_info_len = 0;

    LOG_I("TZ_init_START...");

    msr_info_fd = open(TIMA_MSR_FILE_NAME, O_RDONLY);

    if (msr_info_fd < 0) {
        /* Notify upper layer that the file is missing */
        LOG_E
            ("INIT Populate: Measurement info file missing:%s!",
             strerror(errno));
        return TZ_SPAY_INIT_MSR_MODIFIED;
    }

    lseek(msr_info_fd, 0, SEEK_END);
    msr_info_len = lseek(msr_info_fd, 0, SEEK_CUR);
    lseek(msr_info_fd, 0, SEEK_SET);

    if (msr_info_len > TZ_SPAY_TIMA_MSR_MAX_SIZE) {
        LOG_E
            ("tima_measurement_info too big for INIT buffer: %d",
             msr_info_len);
        close(msr_info_fd);
        return TZ_SPAY_INIT_ERROR;
    }

    if (read(msr_info_fd, cmd->measurement.blob, msr_info_len) != msr_info_len) {
        LOG_E("Init Populate: Cannot read file into memory: %s",
              strerror(errno));
        close(msr_info_fd);
        return TZ_SPAY_INIT_ERROR;
    }

    close(msr_info_fd);

    cmd->measurement.len = msr_info_len;

    return (TZ_SPAY_INIT_OK);
}

static uint32_t TZ_init_END(
    tz_spay_init_msg_resp_t * resp
)
{
    uint32_t ret = TZ_SPAY_INIT_ERROR;
    uint32_t tl_ret_code = TZ_SPAY_INIT_ERROR;

    tl_ret_code = resp->return_code;
    LOG_I("error msg: %s\n", resp->error_msg);
    switch (tl_ret_code) {
    case TZ_SPAY_INIT_OK:
        ret = TZ_SPAY_INIT_OK;
        LOG_I("TZ_init: successful initialization");
        break;
    case TZ_SPAY_INIT_UNINITIALIZED_SECURE_MEM:
        LOG_E("TZ_init Process: Secure memory is not initialized!");
        ret = TZ_SPAY_INIT_UNINITIALIZED_SECURE_MEM;
        break;
    case TZ_SPAY_INIT_MSR_MISMATCH:
        LOG_E
            ("TZ_init Process: Measurement mismatch detected in secure world!");
        ret = TZ_SPAY_INIT_MSR_MISMATCH;
        break;
    case TZ_SPAY_INIT_MSR_MODIFIED:
        LOG_E
            ("TZ_init Process: tima_measurement_info corruption detected in TZ");
        ret = TZ_SPAY_INIT_MSR_MODIFIED;
        break;
    case TZ_SPAY_INIT_ERROR_TAMPER_FUSE_FAIL:
        LOG_E("TZ_init Process: Device tampered, tamper fuse is set!");
        ret = TZ_SPAY_INIT_ERROR_TAMPER_FUSE_FAIL;
        break;
    case TZ_SPAY_INIT_ERROR:
        LOG_E("TZ_init Process: Internal Error");
        ret = TZ_SPAY_INIT_ERROR;
        break;
    default:
        LOG_I
            ("TZ_init Process: Strange errors! Unrecognized Return Code: %x - probably already initialized",
             tl_ret_code);
        ret = TZ_SPAY_INIT_OK;
    }

    return ret;
}

uint32_t TZ_init_with_data(
    uint32_t cmdId,
    tz_spay_init_msg_cmd_t * cmd,
    tz_spay_init_msg_resp_t * resp,
    comm_ctx_t * comm
)
{
    uint32_t ret;

    if (TZ_SPAY_INIT_OK != (ret = TZ_init_with_data_START(cmd))) {
        goto exit;
    }

    LOG_I("TZ_init_with_data: sending initialization request...");

    if (TZ_SPAY_INIT_OK !=
        (ret = comm_request(comm, cmdId))) {
        LOG_E("Error communicating with trustlet");
        goto exit;
    }

    if (TZ_SPAY_INIT_OK != (ret = TZ_init_END(resp))) {
        LOG_E("trustlet initialization failed");
    }

exit:
    return (ret);
}

uint32_t TZ_init(
    uint32_t cmdId,
    tz_spay_init_msg_cmd_t * cmd,
    tz_spay_init_msg_resp_t * resp,
    comm_ctx_t * comm
)
{
    uint32_t ret;

    if (TZ_SPAY_INIT_OK != (ret = TZ_init_START(cmd))) {
        goto quit;
    }

    LOG_I("TZ_init: sending initialization request...");

    if (TZ_SPAY_INIT_OK !=
        (ret = comm_request(comm, cmdId))) {
        LOG_E("Error communicating with trustlet");
        goto quit;
    }

    if (TZ_SPAY_INIT_OK != (ret = TZ_init_END(resp))) {
        LOG_E("trustlet initialization failed");
    }

quit:
    return (ret);
}

uint32_t spay_init_tlc(
    uint32_t cmdId,
    tz_spay_init_msg_cmd_t * cmd,
    tz_spay_init_msg_resp_t * resp,
    comm_ctx_t * comm
)
{
    uint32_t ret;

    if (TZ_SPAY_INIT_UNINITIALIZED_SECURE_MEM ==
        (ret = TZ_init(cmdId, cmd, resp, comm))) {
        ret = TZ_init_with_data(cmdId, cmd, resp, comm);
    }

    return (ret);
}
