/*
 * Copyright 2021 Sony Corporation
 */

#include <common.h>
#include <dtbo.h>
#include <debug_impl.h>
#include <autodet.h>
#include <rtos_switch.h>
#include <utility.h>
#include "secure_boot.h"
#include "secure/crypto_auth.h"
#include "secure/secure_common.h"
#include <image-android-dt.h>
#include <mapmem.h>
#include <dt_table.h>

static int kernel_dtb_num;
static struct list_head dtbo_idx_list;

static void add_dtbo_idx_to_bootargs(int kernel_dtb_num)
{
	struct DTBO_IDX_T *pos, *n;
	char bootargs_dtbo_idx[] = ANDROID_BOOTARGS_DTBO_IDX;
	char key[KEY_BUFFER_SIZE] = {0}, set_cfg[DTBO_INDEX_BUFFER_SIZE] = {0}, id_str[DIGIT_NUM];
	char *key_value;
	int len;

	strncpy(key, bootargs_dtbo_idx, strlen(bootargs_dtbo_idx));
	key[strlen(bootargs_dtbo_idx)] = '\0';
	if (kernel_dtb_num > 0) {
		key_value = (char *)malloc(kernel_dtb_num*DIGIT_NUM);
		if (key_value != NULL) {
			memset(key_value, 0, kernel_dtb_num*DIGIT_NUM);
			list_for_each_entry_safe(pos, n, &dtbo_idx_list, list) {
				memset(id_str, 0, sizeof(id_str));
				len = snprintf(id_str, sizeof(id_str)-1, "%d", pos->dtbo_id);
				if (len > 0) {
					strncat(key_value, id_str, strlen(id_str));
					UBOOT_DEBUG("Set key_value:%s as %s\n", ANDROID_BOOTARGS_DTBO_IDX, key_value);
					kernel_dtb_num--;
					if (kernel_dtb_num > 0)
						strncat(key_value, ",", 1);
				}
			}
			len = snprintf(set_cfg, sizeof(set_cfg) - 1, "%s=%s", key, key_value);
			if (len >= (sizeof(set_cfg) - 1))
				UBOOT_ERROR("The array size is too small(%d), snprintf fail '%s'\n", (int)(sizeof(set_cfg) - 1), set_cfg);
			free(key_value);
		} else {
			UBOOT_ERROR("Heap memory is not enough\n");
			len = snprintf(set_cfg, sizeof(set_cfg) - 1, "%s=%d", key, 0);
			if (len >= (sizeof(set_cfg) - 1))
				UBOOT_ERROR("The array size is too small(%d), snprintf fail '%s'\n", (int)(sizeof(set_cfg) - 1), set_cfg);
		}
	} else {
		len = snprintf(set_cfg, sizeof(set_cfg) - 1, "%s=%d", key, 0);
			if (len >= (sizeof(set_cfg) - 1))
				UBOOT_ERROR("The array size is too small(%d), snprintf fail '%s'\n", (int)(sizeof(set_cfg) - 1), set_cfg);
	}

	add_bootargs(key, set_cfg, 0);
	list_for_each_entry_safe(pos, n, &dtbo_idx_list, list)
		free(pos);
}

int get_dtbo_cfg_selection(char *partition, char *cfg_name, char *type, char *setting)
{
	return 0;
}

int set_dtbo_cfg_selection(char *partition, char *cfg_name)
{
	return 0;
}

int ls_dtbo_cfg(char *partition)
{
	return 0;
}

int add_dtbo_idx(int id)
{
	struct DTBO_IDX_T *dtbo_idx = NULL;

	dtbo_idx = (struct DTBO_IDX_T *)malloc(sizeof(struct DTBO_IDX_T));
	if (dtbo_idx != NULL) {
		kernel_dtb_num++;
		dtbo_idx->dtbo_id = id;
		list_add_tail(&dtbo_idx->list, &dtbo_idx_list);
	}

	return 0;
}

int dtbo_selection_and_overlay(void)
{
	INIT_LIST_HEAD(&dtbo_idx_list);

	sony_dtbo_selection_and_overlay();

	//If this function is called, it must be called after sony dtbo overlay.
	sony_dtbo_ovarlay_for_rtos_log();

	add_dtbo_idx_to_bootargs(kernel_dtb_num);

	return 0;
}

int get_dtbo_img_size(unsigned long dtbo_img_addr, unsigned long *dtbo_img_size)
{
	int ret = 0;
	const struct dt_table_header *hdr;
	u32 image_size;

	ret = android_dt_check_header(dtbo_img_addr);
	if (ret == false) {
		UBOOT_ERROR("Check device tree binary image header magic number failure, please check dtbo.img or storage cmd.\n");
		return -EINVAL;
	}

	hdr = map_sysmem(dtbo_img_addr, sizeof(*hdr));
	image_size = fdt32_to_cpu(hdr->total_size);
	unmap_sysmem(hdr);
	*dtbo_img_size = image_size;

	return 0;
}

static int get_public_key_n(EN_SECURE_KEY_TYPE en_key, unsigned char *buffer, unsigned int len)
{
	int ret;
	st_secure_key secure_key = {};

	if (!buffer) {
		UBOOT_ERROR("The input parameter 'buffer' is a null pointer\n");
		return -EINVAL;
	}

	if (len < RSA_PUBLIC_KEY_N_LEN) {
		UBOOT_ERROR("The len is smaller than the lenght of public-N (%d bytes).\n", RSA_PUBLIC_KEY_N_LEN);
		return -EINVAL;
	}

	ret = secure_get_key(en_key, &secure_key);
	if (ret == -1) {
		UBOOT_ERROR("Wrong EN_SECURE_KEY_TYPE Index !!\n");
		return ret;
	}
	memcpy(buffer, secure_key.data, len);
	flush_cache((unsigned long)buffer, len);

	return 0;
}

static int get_public_key_e(EN_SECURE_KEY_TYPE en_key, unsigned char *buffer, unsigned int len)
{
	int ret;
	st_secure_key secure_key = {};

	if (!buffer) {
		UBOOT_ERROR("The input parameter 'buffer' is a null pointer\n");
		return -EINVAL;
	}

	if (len < RSA_PUBLIC_KEY_E_LEN) {
		UBOOT_ERROR("The len is smaller than the lenght of public-E (%d bytes).\n", RSA_PUBLIC_KEY_E_LEN);
		return -EINVAL;
	}

	ret = secure_get_key(en_key, &secure_key);
	if (ret == -1) {
		UBOOT_ERROR("Wrong EN_SECURE_KEY_TYPE Index !!\n");
		return ret;
	}
	memcpy(buffer, secure_key.data + RSA_PUBLIC_KEY_N_LEN, len);
	flush_cache((unsigned long)buffer, len);

	return 0;
}

int verify_dtbo_img(unsigned char *auth_data, unsigned char *sign_data, unsigned long auth_size)
{
	int ret = 0;
	uint8_t rsa_public_key_N[RSA_PUBLIC_KEY_N_LEN] = {0};
	uint8_t rsa_public_key_E[RSA_PUBLIC_KEY_E_LEN] = {0};

	ret = get_public_key_n(KEY_TYPE_CKB_A_RI_KEY, rsa_public_key_N, RSA_PUBLIC_KEY_N_LEN);

	if (ret) {
		UBOOT_ERROR("Fail to get public N from CKB_A_RI_KEY.\n");
		return CMD_RET_FAILURE;
	}

	ret = get_public_key_e(KEY_TYPE_CKB_A_RI_KEY, rsa_public_key_E, RSA_PUBLIC_KEY_E_LEN);

	if (ret) {
		UBOOT_ERROR("Fail to get public E from CKB_A_RI_KEY.\n");
		return CMD_RET_FAILURE;
	}

	ret = secure_do_authentication(rsa_public_key_N, rsa_public_key_E, sign_data, auth_data, auth_size);
	if (ret != 0) {
		UBOOT_ERROR("secure_do_authentication fail.\n");
		UBOOT_DEBUG("RSA_PUBLIC_KEY_N_LEN:\n");
		UBOOT_DUMP((void *)rsa_public_key_N, RSA_PUBLIC_KEY_N_LEN);
		UBOOT_DEBUG("RSA_PUBLIC_KEY_E_LEN:\n");
		UBOOT_DUMP((void *)rsa_public_key_E, RSA_PUBLIC_KEY_E_LEN);
		UBOOT_DEBUG("signature:\n");
		UBOOT_DUMP((void *)sign_data, SIGNATURE_LEN);
		UBOOT_DEBUG("image :\n");
		UBOOT_DUMP((void *)auth_data, SIGNATURE_LEN);
		return CMD_RET_FAILURE;
	}
	return CMD_RET_SUCCESS;
}

