/*
 * Copyright (c) 2017, Linaro Limited
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fnmatch.h>
#include <inttypes.h>
#include <pta_secstor_ta_mgmt.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <tee_client_api.h>
#include <unistd.h>

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

static void *read_ta(const char *dname, const char *fname, size_t *size)
{
	char nbuf[PATH_MAX];
	FILE *f = NULL;
	void *buf = NULL;
	size_t s = 0;

	if (dname)
		snprintf(nbuf, sizeof(nbuf), "%s/%s", dname, fname);
	else
		snprintf(nbuf, sizeof(nbuf), "%s", fname);

	f = fopen(nbuf, "rb");
	if (!f)
		err(1, "fopen(\"%s\")", nbuf);

	if (fseek(f, 0, SEEK_END))
		err(1, "fseek");

	s = ftell(f);
	rewind(f);

	buf = malloc(s);
	if (!buf)
		err(1, "malloc(%zu)", s);

	if (fread(buf, 1, s, f) != s)
		err(1, "fread");

	*size = s;
	return buf;
}

static void install_ta(TEEC_Session *sess, void *buf, size_t blen)
{
	TEEC_Result res = TEEC_ERROR_GENERIC;
	uint32_t err_origin = 0;
	TEEC_Operation op = TEEC_OPERATION_INITIALIZER;

	op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT, TEEC_NONE,
					 TEEC_NONE, TEEC_NONE);
	op.params[0].tmpref.buffer = buf;
	op.params[0].tmpref.size = blen;

	res = TEEC_InvokeCommand(sess, PTA_SECSTOR_TA_MGMT_BOOTSTRAP, &op,
				 &err_origin);
	if (res)
		errx(1, "install_ta: TEEC_InvokeCommand: %#" PRIx32
			" err_origin %#" PRIx32, res, err_origin);
}

static void install_file(TEEC_Session *sess, const char *dirname,
			 const char *filename)
{
	void *ta = NULL;
	size_t ta_size = 0;

	printf("Installing \"%s\"\n", filename);
	ta = read_ta(dirname, filename, &ta_size);
	install_ta(sess, ta, ta_size);
	free(ta);
}

static void install_dir(TEEC_Session *sess, const char *dirname)
{
	DIR *dirp = NULL;

	printf("Searching directory \"%s\" for TAs\n", dirname);
	dirp = opendir(dirname);
	if (!dirp)
		err(1, "opendir(\"%s\")", dirname);

	while (true) {
		struct dirent *dent = readdir(dirp);

		if (!dent)
			break;

		if (fnmatch("*.ta", dent->d_name, 0))
			continue;

		install_file(sess, dirname, dent->d_name);
	}

	closedir(dirp);
}

int install_ta_runner_cmd_parser(int argc, char *argv[])
{
	TEEC_Result res = TEEC_ERROR_GENERIC;
	uint32_t err_origin = 0;
	TEEC_UUID uuid = PTA_SECSTOR_TA_MGMT_UUID;
	TEEC_Context ctx = { };
	TEEC_Session sess = { };
	int i = 0;

	res = TEEC_InitializeContext(NULL, &ctx);
	if (res)
		errx(1, "TEEC_InitializeContext: %#" PRIx32, res);

	res = TEEC_OpenSession(&ctx, &sess, &uuid, TEEC_LOGIN_PUBLIC, NULL,
			       NULL, &err_origin);
	if (res == TEEC_ERROR_ITEM_NOT_FOUND && err_origin == TEEC_ORIGIN_TEE)
		errx(1, "TA install is not supported by OP-TEE");
	if (res)
		errx(1, "TEEC_OpenSession: res %#" PRIx32 " err_orig %#" PRIx32,
			res, err_origin);

	for (i = 1; i < argc; i++) {
		struct stat sb = { };

		if (stat(argv[i], &sb)) {
			printf("Skipping \"%s\": %s", argv[i], strerror(errno));
			continue;
		}

		if (S_ISDIR(sb.st_mode))
			install_dir(&sess, argv[i]);
		else if (S_ISREG(sb.st_mode))
			install_file(&sess, NULL, argv[i]);
		else
			printf("Skipping unknown file type \"%s\", mode 0%o",
			       argv[i], sb.st_mode);
	}

	TEEC_CloseSession(&sess);
	TEEC_FinalizeContext(&ctx);

	printf("Installing TAs done\n");

	return 0;
}
