// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2021 MediaTek Inc.
 */

#include "mkp_hvc.h"
#include "debug.h"

DEBUG_SET_LEVEL(DEBUG_LEVEL_ERR);

static void mkp_smccc_hvc(unsigned long a0, unsigned long a1,
			  unsigned long a2, unsigned long a3,
			  unsigned long a4, unsigned long a5,
			  unsigned long a6, unsigned long a7,
			  struct arm_smccc_res *res)
{
	arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
}

int mkp_set_mapping_ro_hvc_call(uint32_t policy, uint32_t handle)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_SET_MAPPING_RO);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, handle, 0, 0, 0, 0, 0, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);

	return res.a0 ? -1 : 0;
}

int mkp_set_mapping_rw_hvc_call(uint32_t policy, uint32_t handle)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_SET_MAPPING_RW);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, handle, 0, 0, 0, 0, 0, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);

	return res.a0 ? -1 : 0;
}

int mkp_set_mapping_nx_hvc_call(uint32_t policy, uint32_t handle)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_SET_MAPPING_NX);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, handle, 0, 0, 0, 0, 0, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);

	return res.a0 ? -1 : 0;
}

int mkp_set_mapping_x_hvc_call(uint32_t policy, uint32_t handle)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_SET_MAPPING_X);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, handle, 0, 0, 0, 0, 0, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);

	return res.a0 ? -1 : 0;
}

int mkp_clear_mapping_hvc_call(uint32_t policy, uint32_t handle)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_CLEAR_MAPPING);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, handle, 0, 0, 0, 0, 0, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);

	return res.a0 ? -1 : 0;
}

int mkp_lookup_mapping_entry_hvc_call(uint32_t policy, uint32_t handle,
	unsigned long *entry_size, unsigned long *permission)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_LOOKUP_MAPPING_ENTRY);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, handle, 0, 0, 0, 0, 0, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);
	if (res.a0)
		return -1;

	*entry_size = res.a1;
	*permission = res.a2;
	return 0;

}
int mkp_req_new_policy_hvc_call(unsigned long policy_char)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;
	uint32_t policy = 0;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_NEW_POLICY);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, policy_char, 0, 0, 0, 0, 0, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);
	policy = (uint32_t)res.a0;

	if (res.a0 == 0)
		return -1;

	return (int)policy;
}

int mkp_change_policy_action_hvc_call(uint32_t policy, unsigned long policy_char_action)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;
	int ret = -1;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_POLICY_ACTION);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, policy_char_action, 0, 0, 0, 0, 0, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);
	ret = (int)res.a0;

	/* Success: 0, Fail: other */
	return ret;
}

int mkp_req_new_specified_policy_hvc_call(unsigned long policy_char, uint32_t specified_policy)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;
	uint32_t policy = 0;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_NEW_SPECIFIED_POLICY);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, policy_char, specified_policy, 0, 0, 0, 0, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, specified_policy, res.a0, res.a1, res.a2, res.a3);
	policy = (uint32_t)res.a0;

	if (res.a0 == 0)
		return -1;

	/* Expect "policy == specified_policy" */
	if (policy != specified_policy)
		return -1;

	return (int)policy;
}

uint32_t mkp_create_handle_hvc_call(uint32_t policy,
	unsigned long ipa, unsigned long size)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;
	uint32_t handle = 0;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_CREATE_HANDLE);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, ipa, size, 0, 0, 0, 0, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);
	handle = (uint32_t)(res.a0);

	// if fail return 0
	// success return >0

	return handle;
}
int mkp_destroy_handle_hvc_call(uint32_t policy, uint32_t handle)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_DESTROY_HANDLE);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, handle, 0, 0, 0, 0, 0, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);

	return res.a0 ? -1 : 0;
}

int mkp_configure_sharebuf_hvc_call(uint32_t policy, uint32_t handle, uint32_t type,
	unsigned long nr_entries, unsigned long size)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_CONFIGURE_SHAREBUF);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, handle, type, nr_entries, size, 0, 0, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);
	return res.a0 ? -1 : 0;
}

int mkp_update_sharebuf_1_argu_hvc_call(uint32_t policy, uint32_t handle, unsigned long index,
	unsigned long a1)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_UPDATE_SHAREBUF_1_ARGU);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, handle, index, a1, 0, 0, 0, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);
	return res.a0 ? -1 : (int)res.a1;
}

int mkp_update_sharebuf_2_argu_hvc_call(uint32_t policy, uint32_t handle, unsigned long index,
	unsigned long a1, unsigned long a2)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_UPDATE_SHAREBUF_2_ARGU);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, handle, index, a1, a2, 0, 0, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);
	return res.a0 ? -1 : (int)res.a1;
}

int mkp_update_sharebuf_3_argu_hvc_call(uint32_t policy, uint32_t handle, unsigned long index,
	unsigned long a1, unsigned long a2, unsigned long a3)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_UPDATE_SHAREBUF_3_ARGU);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, handle, index, a1, a2, a3, 0, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);
	return res.a0 ? -1 : (int)res.a1;
}

int mkp_update_sharebuf_4_argu_hvc_call(uint32_t policy, uint32_t handle, unsigned long index,
	unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_UPDATE_SHAREBUF_4_ARGU);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, handle, index, a1, a2, a3, a4, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);
	return res.a0 ? -1 : (int)res.a1;
}

int mkp_update_sharebuf_5_argu_hvc_call(uint32_t policy, uint32_t handle, unsigned long index,
	unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_UPDATE_SHAREBUF_5_ARGU);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, handle, index, a1, a2, a3, a4, a5, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);
	return res.a0 ? -1 : (int)res.a1;
}

int mkp_update_sharebuf_hvc_call(uint32_t policy, uint32_t handle, unsigned long index,
	unsigned long ipa)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(policy, HVC_FUNC_UPDATE_SHAREBUF);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, handle, index, ipa, 0, 0, 0, 0, &res);
	MKP_DEBUG("%s:%d hvc_id:0x%x, policy:%d res:0x%lx 0x%lx 0x%lx 0x%lx\n", __func__, __LINE__,
		mkp_hvc_fast_call_id, policy, res.a0, res.a1, res.a2, res.a3);
	return res.a0 ? -1 : 0;
}

/* Pass some essential information to MKP service, return 0 if success. */
int __init mkp_setup_essential_hvc_call(unsigned long phys_offset,
		unsigned long fixaddr_top, unsigned long fixaddr_real_start)
{
	uint64_t phys_addr;
	static int is_executed;
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;
	int ret = -1;

	/* Get subscribe's IPA */
	phys_addr = (uint64_t)(vmalloc_to_pfn((void *)&subscribe) << PAGE_SHIFT) +
		    ((uint64_t)&subscribe & ~PAGE_MASK);

	if (is_executed == 0) {
		mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(0, HVC_FUNC_ESS_0);
		mkp_smccc_hvc(mkp_hvc_fast_call_id, phys_offset, fixaddr_top,
			      fixaddr_real_start, phys_addr, 0, 0, 0, &res);
		is_executed = 1;

		/* Success */
		if (res.a0 == 0)
			ret = 0;
	}

	return ret;
}

/* Tell MKP service it should start granting */
int __init mkp_start_granting_hvc_call(void)
{
	struct arm_smccc_res res;
	int mkp_hvc_fast_call_id;
	int ret = -1;

	mkp_hvc_fast_call_id = MKP_HVC_CALL_ID(0, HVC_FUNC_ESS_1);
	mkp_smccc_hvc(mkp_hvc_fast_call_id, 0, 0, 0, 0, 0, 0, 0, &res);

	/* Success */
	if (res.a0 == 0)
		ret = 0;

	return ret;
}
