/*
 * Copyright 2021 NXP
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */

#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <arch_helpers.h>
#include "caam.h"
#include <common/debug.h>
#include <drivers/auth/crypto_mod.h>

#include "hash.h"
#include "jobdesc.h"
#include "sec_hw_specific.h"

/* Since no Allocator is available . Taking a global static ctx.
 * This would mean that only one active ctx can be there at a time.
 */

static struct hash_ctx glbl_ctx;

static void hash_done(uint32_t *desc, uint32_t status, void *arg,
		      void *job_ring)
{
	INFO("Hash Desc SUCCESS with status %x\n", status);
}

/***************************************************************************
 * Function	: hash_init
 * Arguments	: ctx - SHA context
 * Return	: init,
 * Description	: This function initializes the context for SHA calculation
 ***************************************************************************/
int hash_init(enum hash_algo algo, void **ctx)
{
	if (glbl_ctx.active == false) {
		memset(&glbl_ctx, 0, sizeof(struct hash_ctx));
		glbl_ctx.active = true;
		glbl_ctx.algo = algo;
		*ctx = &glbl_ctx;
		return 0;
	} else {
		return -1;
	}
}

/***************************************************************************
 * Function	: hash_update
 * Arguments	: ctx - SHA context
 *		  buffer - Data
 *		  length - Length
 * Return	: -1 on error
 *		  0 on SUCCESS
 * Description	: This function creates SG entry of the data provided
 ***************************************************************************/
int hash_update(enum hash_algo algo, void *context, void *data_ptr,
		unsigned int data_len)
{
	struct hash_ctx *ctx = context;
	/* MAX_SG would be MAX_SG_ENTRIES + key + hdr + sg table */
	if (ctx->sg_num >= MAX_SG) {
		ERROR("Reached limit for calling %s\n", __func__);
		ctx->active = false;
		return -EINVAL;

	}

	if (ctx->algo != algo) {
		ERROR("ctx for algo not correct\n");
		ctx->active = false;
		return -EINVAL;
	}

#if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2)
	flush_dcache_range((uintptr_t)data_ptr, data_len);
	dmbsy();
#endif

#ifdef CONFIG_PHYS_64BIT
	sec_out32(&ctx->sg_tbl[ctx->sg_num].addr_hi,
		  (uint32_t) ((uintptr_t) data_ptr >> 32));
#else
	sec_out32(&ctx->sg_tbl[ctx->sg_num].addr_hi, 0x0);
#endif
	sec_out32(&ctx->sg_tbl[ctx->sg_num].addr_lo, (uintptr_t) data_ptr);

	sec_out32(&ctx->sg_tbl[ctx->sg_num].len_flag,
		  (data_len & SG_ENTRY_LENGTH_MASK));

	ctx->sg_num++;

	ctx->len += data_len;

	return 0;
}

/***************************************************************************
 * Function	: hash_final
 * Arguments	: ctx - SHA context
 * Return	: SUCCESS or FAILURE
 * Description	: This function sets the final bit and enqueues the descriptor
 ***************************************************************************/
int hash_final(enum hash_algo algo, void *context, void *hash_ptr,
	       unsigned int hash_len)
{
	int ret = 0;
	struct hash_ctx *ctx = context;
	uint32_t final = 0U;

	struct job_descriptor jobdesc __aligned(CACHE_WRITEBACK_GRANULE);

	jobdesc.arg = NULL;
	jobdesc.callback = hash_done;

	if (ctx->algo != algo) {
		ERROR("ctx for algo not correct\n");
		ctx->active = false;
		return -EINVAL;
	}

	final = sec_in32(&ctx->sg_tbl[ctx->sg_num - 1].len_flag) |
	    SG_ENTRY_FINAL_BIT;
	sec_out32(&ctx->sg_tbl[ctx->sg_num - 1].len_flag, final);

	dsb();

	/* create the hw_rng descriptor */
	cnstr_hash_jobdesc(jobdesc.desc, (uint8_t *) ctx->sg_tbl,
			   ctx->len, hash_ptr);

#if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2)
	flush_dcache_range((uintptr_t)ctx->sg_tbl,
			   (sizeof(struct sg_entry) * MAX_SG));
	inv_dcache_range((uintptr_t)hash_ptr, hash_len);

	dmbsy();
#endif

	/* Finally, generate the requested random data bytes */
	ret = run_descriptor_jr(&jobdesc);
	if (ret != 0) {
		ERROR("Error in running descriptor\n");
		ret = -1;
	}
	ctx->active = false;
	return ret;
}
