/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (c) 2014, STMicroelectronics International N.V.
 * Copyright (c) 2020, Linaro Limited
 */

#ifndef XML_COMMON_API_H_
#define XML_COMMON_API_H_

#include <adbg.h>
#include <tee_client_api.h>

#include "xtest_helpers.h"
#include "xtest_test.h"

#define BIT(n) (1ul << (n))
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif

#define ALLOCATE_SHARED_MEMORY(context, sharedMemory, sharedMemorySize, \
			       memoryType, exit_label) \
	res = AllocateSharedMemory(context, sharedMemory, sharedMemorySize, \
				   memoryType); \
	if (!ADBG_EXPECT_TEEC_SUCCESS(c, res)) \
		goto exit_label; \
	memset(sharedMemory->buffer, 0, sharedMemorySize);

#define ALLOCATE_AND_FILL_SHARED_MEMORY(context, sharedMemory, \
					sharedMemorySize, \
					memoryType, copySize, data, \
					exit_label) \
	res = AllocateSharedMemory(context, sharedMemory, sharedMemorySize, \
				   memoryType); \
	if (!ADBG_EXPECT_TEEC_SUCCESS(c, res)) \
		goto exit_label; \
	memset(sharedMemory->buffer, 0, sharedMemorySize); \
	if (data != NULL) { \
		memcpy(sharedMemory->buffer, data, copySize); \
	}
#define ALLOCATE_AND_FILL_SHARED_MEMORY_6(a,b,c,d,e,f) \
		        ALLOCATE_AND_FILL_SHARED_MEMORY(a,b,c,d,c,e,f)

#define SET_SHARED_MEMORY_OPERATION_PARAMETER(parameterNumber, \
					      sharedMemoryOffset, \
					      sharedMemory, \
					      sharedMemorySize) \
	op.params[parameterNumber].memref.offset = sharedMemoryOffset; \
	op.params[parameterNumber].memref.size = sharedMemorySize; \
	op.params[parameterNumber].memref.parent = sharedMemory;

/*Open session using TEEC_OpenSession and
	check the returned value and/or returned origin.*/
#define XML_OpenSession(c, context, session, destination, connectionMethod, \
			connectionData, operation, returnOrigin, expected) \
	do { \
		uint32_t ret_orig = 0; \
 \
		XML_VERIFY(c, expected, \
			   TEEC_OpenSession(context, session, destination, \
					    connectionMethod, connectionData, \
					    operation, &ret_orig)); \
		if (!(unsigned long)(returnOrigin) || \
		    (unsigned long)(returnOrigin) == \
		    TEEC_ORIGIN_ANY_NOT_TRUSTED_APP) \
			ADBG_EXPECT_NOT(c, (unsigned long)returnOrigin, \
					ret_orig); \
		else \
			ADBG_EXPECT(c, (unsigned long)returnOrigin, ret_orig); \
	} while (0)

/* XML_VERIFY macro define.
 *
 * Use ADBG_EXPECT or ADBG_EXPECT_NOT depending on the expected return value.
 *
 * ADBG_EXPECT() -> IF(EXP == GOT) RETURN TRUE
 * ADBG_EXPECT() -> IF(EXP != GOT) RETURN TRUE
 */
#define XML_VERIFY(c, exp, got) \
	do { \
		if (exp == TEEC_UNDEFINED_ERROR) \
			ADBG_EXPECT_NOT(c, exp, got); \
		else \
			ADBG_EXPECT(c, exp, got); \
	} while (0)

/*Initialize context using TEEC_InitializeContext and
	check the returned value.*/
#define XML_InitializeContext(c, name, context, expected) \
	XML_VERIFY(c, expected, TEEC_InitializeContext(name, context))

#define OPERATION_TEEC_PARAM_TYPES(op, p0, p1, p2, p3) \
	do { \
		op->paramTypes = TEEC_PARAM_TYPES(p0, p1, p2, p3); \
	} while (0)

/*dummy functions*/
#define TEEC_SetUp_TEE()    /*do nothing for now*/
#define TEEC_TearDown_TEE(a)    /*do nothing for now*/
#define TEEC_SelectApp(a, b)    /*do nothing for now*/
#define TEEC_createThread(a, b) /*do nothing for now*/

struct attr_value {
	uint8_t buf[1024];
	size_t buf_size;
	uint32_t attr_id;
};

#define MAX_NUM_SAVED_ATTR_VALUES	8
static struct attr_value saved_attr[MAX_NUM_SAVED_ATTR_VALUES] __maybe_unused;


static TEEC_Result __maybe_unused
Invoke_Simple_Function(ADBG_Case_t *c __unused, TEEC_Session *sess,
		       uint32_t cmdId)
{
	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
	uint32_t org;

	return TEEC_InvokeCommand(sess, cmdId, &op, &org);
}

static TEEC_Result __maybe_unused
Invoke_Simple_Function_v1(ADBG_Case_t *c __unused, TEEC_Session *sess,
			  uint32_t cmd, uint32_t a, uint32_t b)
{
	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
	uint32_t org;

	op.params[0].value.a = a;
	op.params[0].value.b = b;

	op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_NONE,
					 TEEC_NONE, TEEC_NONE);

	return TEEC_InvokeCommand(sess, cmd, &op, &org);
}

static TEEC_Result __maybe_unused
Invoke_Simple_Function_v2(ADBG_Case_t *c __unused, TEEC_Session *sess,
			  uint32_t cmd, uint32_t a0, uint32_t b0,
			  uint32_t a1, uint32_t b1)
{
	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
	uint32_t org;

	op.params[0].value.a = a0;
	op.params[0].value.b = b0;
	op.params[1].value.a = a1;
	op.params[1].value.b = b1;

	op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_VALUE_INPUT,
					 TEEC_NONE, TEEC_NONE);

	return TEEC_InvokeCommand(sess, cmd, &op, &org);
}

static TEEC_Result __maybe_unused
Invoke_Simple_Function_v3(ADBG_Case_t *c __unused, TEEC_Session *sess,
			  uint32_t cmd, uint32_t a0, uint32_t b0, uint32_t a1,
			  uint32_t b1, uint32_t a2, uint32_t b2)
{
	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
	uint32_t org;

	op.params[0].value.a = a0;
	op.params[0].value.b = b0;
	op.params[1].value.a = a1;
	op.params[1].value.b = b1;
	op.params[2].value.a = a2;
	op.params[2].value.b = b2;

	op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_VALUE_INPUT,
					 TEEC_VALUE_INPUT, TEEC_NONE);

	return TEEC_InvokeCommand(sess, cmd, &op, &org);
}

static TEEC_Result __maybe_unused
Invoke_Simple_Function_v4(ADBG_Case_t *c __unused, TEEC_Session *sess,
			  uint32_t cmd, uint32_t a0, uint32_t b0, uint32_t a1,
			  uint32_t b1, uint32_t a2, uint32_t b2, uint32_t a3,
			  uint32_t b3)
{
	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
	uint32_t org;

	op.params[0].value.a = a0;
	op.params[0].value.b = b0;
	op.params[1].value.a = a1;
	op.params[1].value.b = b1;
	op.params[2].value.a = a2;
	op.params[2].value.b = b2;
	op.params[3].value.a = a3;
	op.params[3].value.b = b3;

	op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_VALUE_INPUT,
					 TEEC_VALUE_INPUT, TEEC_VALUE_INPUT);

	return TEEC_InvokeCommand(sess, cmd, &op, &org);
}

static TEEC_Result __maybe_unused AllocateSharedMemory(TEEC_Context *ctx,
						       TEEC_SharedMemory *shm,
						       uint32_t size,
						       uint32_t flags)
{
	shm->flags = flags;
	shm->size = size;
	return TEEC_AllocateSharedMemory(ctx, shm);
}

static TEEC_Result GetObjectBufferAttribute_helper(ADBG_Case_t *c,
						   TEEC_Session *sess,
						   size_t n,
						   uint32_t cmd, uint32_t obj,
						   uint32_t attr_id,
						   bool buffer_is_null,
						   uint32_t buffer_size)
{
	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;
	TEEC_Result res = TEE_SUCCESS;
	uint32_t org = 0;
	static TEEC_SharedMemory shm = { };
	uint32_t memref_type = TEEC_MEMREF_TEMP_OUTPUT;

	if (!ADBG_EXPECT_COMPARE_SIGNED(c, n, <, MAX_NUM_SAVED_ATTR_VALUES))
		return TEEC_ERROR_BAD_PARAMETERS;
	if (!ADBG_EXPECT_COMPARE_SIGNED(c, buffer_size, <=,
					sizeof(saved_attr[n].buf)))
		return TEEC_ERROR_BAD_PARAMETERS;

	if (!buffer_is_null) {
		shm.size = buffer_size;
		shm.flags = TEEC_MEM_OUTPUT;
		res = TEEC_AllocateSharedMemory(sess->ctx, &shm);
		if (!ADBG_EXPECT_TEEC_SUCCESS(c, res))
			return res;
		SET_SHARED_MEMORY_OPERATION_PARAMETER(1, 0, &shm, shm.size);
		memref_type = TEEC_MEMREF_PARTIAL_OUTPUT;
	}

	op.params[0].value.a = obj;
	op.params[0].value.b = attr_id;

	op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, memref_type,
					 TEEC_NONE, TEEC_NONE);

	res = TEEC_InvokeCommand(sess, cmd, &op, &org);

	if (!buffer_is_null) {
		if (res) {
			memset(saved_attr + n, 0, sizeof(saved_attr[n]));
		} else {
			memcpy(saved_attr[n].buf, shm.buffer,
			       sizeof(saved_attr[n].buf));
			saved_attr[n].buf_size = op.params[1].memref.size;
			saved_attr[n].attr_id = attr_id;
		}
		TEEC_ReleaseSharedMemory(&shm);
	}

	return res;
}

static TEEC_Result __maybe_unused
Invoke_GetObjectBufferAttribute(ADBG_Case_t *c, TEEC_Session *sess,
				uint32_t cmd, uint32_t obj, uint32_t attr_id,
				bool buffer_is_null, uint32_t buffer_size)
{
	return GetObjectBufferAttribute_helper(c, sess, 0, cmd, obj, attr_id,
					       buffer_is_null, buffer_size);
}

static TEEC_Result __maybe_unused
Invoke_FreeTransientObject(ADBG_Case_t *c, TEEC_Session *sess, uint32_t cmd,
			   uint32_t obj_handle)
{
	return Invoke_Simple_Function_v1(c, sess, cmd, obj_handle, 0);
}
#endif /* XML_COMMON_API_H_ */