/*
 * Copyright (C) 2018 Intel Corporation. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <hypervisor.h>
#include <sbl_seed_parse.h>

#define SEED_ENTRY_TYPE_SVNSEED         0x1U
/* #define SEED_ENTRY_TYPE_RPMBSEED        0x2U */

/* #define SEED_ENTRY_USAGE_USEED          0x1U */
#define SEED_ENTRY_USAGE_DSEED          0x2U

struct seed_list_hob {
	uint8_t revision;
	uint8_t reserved0[3];
	uint32_t buffer_size;
	uint8_t total_seed_count;
	uint8_t reserved1[3];
};

struct seed_entry {
	/* SVN based seed or RPMB seed or attestation key_box */
	uint8_t type;
	/* For SVN seed: useed or dseed
	 * For RPMB seed: serial number based or not
	 */
	uint8_t usage;
	/* index for the same type and usage seed */
	uint8_t index;
	uint8_t reserved;
	/* reserved for future use */
	uint16_t flags;
	/* Total size of this seed entry */
	uint16_t seed_entry_size;
	/* SVN seed: struct seed_info
	 * RPMB seed: uint8_t rpmb_key[key_len]
	 */
	uint8_t seed[0];
};

struct image_boot_params {
	uint32_t size_of_this_struct;
	uint32_t version;
	uint64_t p_seed_list;
	uint64_t p_platform_info;
	uint64_t reserved;
};

static const char *boot_params_arg = "ImageBootParamsAddr=";

static void parse_seed_list_sbl(struct seed_list_hob *seed_hob)
{
	uint8_t i;
	uint8_t dseed_index = 0U;
	struct seed_entry *entry;
	struct seed_info dseed_list[BOOTLOADER_SEED_MAX_ENTRIES];
	bool parse_success = false;

	if ((seed_hob != NULL) && (seed_hob->total_seed_count != 0U)) {
		parse_success = true;

		entry = (struct seed_entry *)((uint8_t *)seed_hob + sizeof(struct seed_list_hob));

		for (i = 0U; i < seed_hob->total_seed_count; i++) {
			if (entry == NULL) {
				break;
			}
			/* retrieve dseed */
			if ((SEED_ENTRY_TYPE_SVNSEED == entry->type) &&	(SEED_ENTRY_USAGE_DSEED == entry->usage)) {

				/* The seed_entry with same type/usage are always
				 * arranged by index in order of 0~3.
				 */
				if (entry->index != dseed_index) {
					pr_warn("Index mismatch. Use fake seed!");
					parse_success = false;
					break;
				}

				if (entry->index >= BOOTLOADER_SEED_MAX_ENTRIES) {
					pr_warn("Index exceed max number!");
					parse_success = false;
					break;
				}

				(void)memcpy_s((void *)&dseed_list[dseed_index], sizeof(struct seed_info),
						(void *)entry->seed, sizeof(struct seed_info));
				dseed_index++;

				/* erase original seed in seed entry */
				(void)memset((void *)entry->seed, 0U, sizeof(struct seed_info));
			}

			entry = (struct seed_entry *)((uint8_t *)entry + entry->seed_entry_size);
		}
	}

	if (parse_success) {
		trusty_set_dseed((void *)dseed_list, dseed_index);
	} else {
		trusty_set_dseed(NULL, 0U);
	}

	(void)memset((void *)dseed_list, 0U, sizeof(dseed_list));
}

/*
 * sbl_seed_parse
 *
 * description:
 *    This function parse image_boot_params from cmdline. Mainly on
 *    1. parse structure address from cmdline
 *    2. get seed_list address and call parse_seed_list_sbl to parse seed
 *    3. convert address in the structure from HPA to SOS's GPA
 *    4. clear original image_boot_params argument in cmdline since
 *       original address is HPA.
 *
 * input:
 *    vm            pointer to vm structure
 *    cmdline       pointer to cmdline string
 *    out_len       the max len of out_arg
 *
 * output:
 *    out_arg       the argument with SOS's GPA
 *
 * return value:
 *    true if parse successfully, otherwise false.
 */
bool sbl_seed_parse(struct acrn_vm *vm, char *cmdline, char *out_arg, uint32_t out_len)
{
	char *arg, *arg_end;
	char *param;
	void *param_addr;
	struct image_boot_params *boot_params;
	uint32_t len;
	bool parse_success = false;

	if (is_sos_vm(vm) && (cmdline != NULL)) {
		len = strnlen_s(boot_params_arg, MEM_1K);
		arg = strstr_s((const char *)cmdline, MEM_2K, boot_params_arg, len);

		if (arg != NULL) {
			param = arg + len;
			param_addr = (void *)hpa2hva(strtoul_hex(param));
			if (param_addr != NULL) {
				boot_params = (struct image_boot_params *)param_addr;
				parse_seed_list_sbl((struct seed_list_hob *)hpa2hva(boot_params->p_seed_list));

				/*
				 * Convert the addresses to SOS GPA since this structure will
				 * be used in SOS.
				 */
				boot_params->p_seed_list = sos_vm_hpa2gpa(boot_params->p_seed_list);
				boot_params->p_platform_info = sos_vm_hpa2gpa(boot_params->p_platform_info);

				/*
				 * Replace original arguments with spaces since SOS's GPA is not
				 * identity mapped to HPA. The argument will be appended later when
				 * compose cmdline for SOS.
				 */
				arg_end = strchr(arg, ' ');
				len = (arg_end != NULL) ? (uint32_t)(arg_end - arg) : strnlen_s(arg, MEM_2K);
				(void)memset((void *)arg, ' ', len);

				/* Convert the param_addr to SOS GPA and copy to caller */
				if (out_arg != NULL) {
					snprintf(out_arg, out_len, "%s0x%X ",
						boot_params_arg, hva2gpa(vm, param_addr));
				}

				parse_success = true;
			}
		}
	}

	if (!parse_success) {
		parse_seed_list_sbl(NULL);
	}

	return parse_success;
}
