
/*
 * =====================================================================================
 *
 *       Filename:  hdm_rpmb.c
 *
 *    Description:  HDM RPMB manipulation
 *
 *        Version:  1.0
 *        Created:  09/16/2019 15:26:11 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *        Company:  Samsung Electronics
 *        Copyright (c) 2015 by Samsung Electronics, All rights reserved.
 *
 * =====================================================================================
 */

/** Includes */
#include "hdm_rpmb.h"

/**
 * QCOM global stor vairables
 */
qsee_stor_device_handle_t   qsee_stor_device_handle = 0;
qsee_stor_client_handle_t   qsee_stor_client_handle = 0;
qsee_stor_client_info_t     qsee_stor_client_info;

/**
 * Static functions prototypes
 */
static uint32_t get_bytes_per_sector(void);

/**
 * @brief
 * hdm_rpmb_init
 * RPMB initialization
 *
 * @return HDM status code
 */
hdm_return_code_t hdm_rpmb_init(void) {
        hdm_return_code_t ret = HDM_STATUS_SUCCESS;
        uint32_t num_sectors = TZ_RPMB_HDM_BLOCK_SIZE;
        int32_t rpmb_ret = QSEE_STOR_SUCCESS;

        rpmb_ret = qsee_stor_device_init(QSEE_STOR_EMMC_RPMB, 0, &qsee_stor_device_handle);
        if (rpmb_ret == QSEE_STOR_RPMB_NOT_PROVISIONED_ERROR
            || rpmb_ret == QSEE_STOR_RPMB_AUTOPROV_DISABLED) {
                HDM_LOG("%s: RPMB is not provisioned(%d)", __func__, rpmb_ret);
                ret = HDM_RPMB_NOT_PROVISIONED;
                goto exit;
        }
        else if (rpmb_ret != QSEE_STOR_SUCCESS) {
                HDM_LOG("%s: Failed to init RPMB device(%d)", __func__, rpmb_ret);
                ret = HDM_RPMB_FAIL;
                goto exit;
        }

        rpmb_ret = qsee_stor_open_partition(&qsee_stor_device_handle, TZ_RPMB_HDM_PARTITION_ID, &qsee_stor_client_handle);
        if (rpmb_ret == QSEE_STOR_PARTI_NOT_FOUND_ERROR) {
                qsee_stor_device_info_t device_info;

                HDM_LOG("%s: There is no partition for HDM, add it.", __func__);
                rpmb_ret = qsee_stor_device_get_info(&qsee_stor_device_handle, &device_info);

                if (rpmb_ret != QSEE_STOR_SUCCESS) {
		                HDM_LOG("%s: Failed to get device info (%d)", __func__, rpmb_ret);
		                num_sectors = TZ_RPMB_HDM_BLOCK_SIZE;
	            } else {
		                HDM_LOG("%s: Total RPMB sectors (%u), Available RPMB sectors (%u)", __func__,
                                device_info.total_sectors, device_info.available_sectors);
                        num_sectors = (TZ_RPMB_HDM_BLOCK_SIZE < device_info.available_sectors)
					            ? TZ_RPMB_HDM_BLOCK_SIZE : device_info.available_sectors;
                }

                rpmb_ret = qsee_stor_add_partition(&qsee_stor_device_handle, TZ_RPMB_HDM_PARTITION_ID, num_sectors);
	            if(rpmb_ret != QSEE_STOR_SUCCESS) {
                        HDM_LOG("%s: Failed to make new partition (%d)", __func__, rpmb_ret);
                        ret = HDM_RPMB_FAIL;
                        goto exit;
                }

        	    rpmb_ret = qsee_stor_open_partition(&qsee_stor_device_handle, TZ_RPMB_HDM_PARTITION_ID ,&qsee_stor_client_handle);
                if ((rpmb_ret != QSEE_STOR_SUCCESS) || (qsee_stor_client_handle == 0)) {
                        HDM_LOG("%s: Fail to open() return = 0x%x", __func__, rpmb_ret);
                        ret = HDM_RPMB_FAIL;
                        goto exit;
                }

        } else if (rpmb_ret != QSEE_STOR_SUCCESS) {
                HDM_LOG("%s: Failed to open partition (%d)", __func__, rpmb_ret);
                ret = HDM_RPMB_FAIL;
                goto exit;
        }

        ret = HDM_STATUS_SUCCESS;
exit:
        return ret;
}

/**
 * @brief
 * hdm_rpmb_read
 * Read RPMB data
 *
 * @param[out] *data     - data buffer with RPMB content
 * @param[in]  *data_len - read data length
 *
 * @return HDM status code
 */
hdm_return_code_t hdm_rpmb_read(uint8_t *data, uint32_t data_len) {

        hdm_return_code_t ret = HDM_STATUS_SUCCESS;
        uint32_t size;
        uint8_t *rpmb_data;

        size = get_bytes_per_sector();
        if(size <= 0 || (size * TZ_RPMB_HDM_BLOCK_SIZE) < sizeof(tz_hdm_rpmb_t)) {
                ret = HDM_RPMB_FAIL;
                goto exit;
        }
        HDM_LOG("%s: RPMB buffer size : %d", __func__, size*TZ_RPMB_HDM_BLOCK_SIZE);
        rpmb_data = TEE_Malloc(size * TZ_RPMB_HDM_BLOCK_SIZE, 0);
        if(rpmb_data == NULL)
        {
                HDM_LOG("Error to alloc memory");
                return HDM_ALLOC_ERROR;
        }

        //TODO: Fix start_sector and number of sectors:
        ret = qsee_stor_read_sectors(&qsee_stor_client_handle, 0, TZ_RPMB_HDM_BLOCK_SIZE, rpmb_data);
        if(ret != QSEE_STOR_SUCCESS) {
                HDM_LOG("%s: Failed to read data from rpmb(0x%08x)", __func__, ret);
                ret = HDM_RPMB_FAIL;
                goto exit;
        }

        TEE_MemMove(data, rpmb_data, sizeof(tz_hdm_rpmb_t));
        ret = HDM_STATUS_SUCCESS;
exit:
        if((size * TZ_RPMB_HDM_BLOCK_SIZE) >= sizeof(tz_hdm_rpmb_t)) {
            TEE_Free(rpmb_data);
        }

        return ret;
}

/**
 * @brief
 * hdm_rpmb_write
 * Write data on RPMB
 *
 * @param[out] *data     - data buffer with RPMB content
 * @param[in]  *data_len - read data length
 *
 * @return HDM status code
 */
hdm_return_code_t hdm_rpmb_write(uint8_t *data, uint32_t data_len) {
        hdm_return_code_t ret = HDM_STATUS_SUCCESS;
        uint32_t size;
        uint8_t *rpmb_data;

        size = get_bytes_per_sector();
        if(size <= 0) {
                ret = HDM_RPMB_FAIL;
                goto exit;
        }
        HDM_LOG("%s: RPMB buffer size : %d", __func__, size*TZ_RPMB_HDM_BLOCK_SIZE);
        rpmb_data = TEE_Malloc(size * TZ_RPMB_HDM_BLOCK_SIZE, 0);
        if(rpmb_data == NULL)
        {
                HDM_LOG("Error to alloc memory");
                return HDM_ALLOC_ERROR;
        }
        TEE_MemMove(rpmb_data, data, data_len);
        //TODO: Fix start_sector and number of sectors:
        ret = qsee_stor_write_sectors(&qsee_stor_client_handle, 0, TZ_RPMB_HDM_BLOCK_SIZE, rpmb_data);
        if (ret != QSEE_STOR_SUCCESS) {
                HDM_LOG("%s: Failed to write data to rpmb(%d)", __func__, ret);
                ret = HDM_RPMB_FAIL;
                goto exit;
        }
        ret = HDM_STATUS_SUCCESS;
exit:
        if(size > 0) {
            TEE_Free(rpmb_data);
        }

        return ret;
}

/**
 * @brief
 * get_bytes_per_sector
 * Get info from RPMB per sector
 * @return Bytes per sector
 */
static uint32_t get_bytes_per_sector(void) {
        int ret;

        ret = qsee_stor_client_get_info(&qsee_stor_client_handle, &qsee_stor_client_info);
        if (ret != QSEE_STOR_SUCCESS) {
                HDM_LOG("Error retrieving bytes per sector");
                return 0;
        }
        HDM_LOG_DEBUG("total_sectors : %d", qsee_stor_client_info.total_sectors);
        return qsee_stor_client_info.bytes_per_sector;
}
