162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * FF-A v1.0 proxy to filter out invalid memory-sharing SMC calls issued by
462306a36Sopenharmony_ci * the host. FF-A is a slightly more palatable abbreviation of "Arm Firmware
562306a36Sopenharmony_ci * Framework for Arm A-profile", which is specified by Arm in document
662306a36Sopenharmony_ci * number DEN0077.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (C) 2022 - Google LLC
962306a36Sopenharmony_ci * Author: Andrew Walbran <qwandor@google.com>
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * This driver hooks into the SMC trapping logic for the host and intercepts
1262306a36Sopenharmony_ci * all calls falling within the FF-A range. Each call is either:
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *	- Forwarded on unmodified to the SPMD at EL3
1562306a36Sopenharmony_ci *	- Rejected as "unsupported"
1662306a36Sopenharmony_ci *	- Accompanied by a host stage-2 page-table check/update and reissued
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * Consequently, any attempts by the host to make guest memory pages
1962306a36Sopenharmony_ci * accessible to the secure world using FF-A will be detected either here
2062306a36Sopenharmony_ci * (in the case that the memory is already owned by the guest) or during
2162306a36Sopenharmony_ci * donation to the guest (in the case that the memory was previously shared
2262306a36Sopenharmony_ci * with the secure world).
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * To allow the rolling-back of page-table updates and FF-A calls in the
2562306a36Sopenharmony_ci * event of failure, operations involving the RXTX buffers are locked for
2662306a36Sopenharmony_ci * the duration and are therefore serialised.
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include <linux/arm-smccc.h>
3062306a36Sopenharmony_ci#include <linux/arm_ffa.h>
3162306a36Sopenharmony_ci#include <asm/kvm_pkvm.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <nvhe/ffa.h>
3462306a36Sopenharmony_ci#include <nvhe/mem_protect.h>
3562306a36Sopenharmony_ci#include <nvhe/memory.h>
3662306a36Sopenharmony_ci#include <nvhe/trap_handler.h>
3762306a36Sopenharmony_ci#include <nvhe/spinlock.h>
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/*
4062306a36Sopenharmony_ci * "ID value 0 must be returned at the Non-secure physical FF-A instance"
4162306a36Sopenharmony_ci * We share this ID with the host.
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_ci#define HOST_FFA_ID	0
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*
4662306a36Sopenharmony_ci * A buffer to hold the maximum descriptor size we can see from the host,
4762306a36Sopenharmony_ci * which is required when the SPMD returns a fragmented FFA_MEM_RETRIEVE_RESP
4862306a36Sopenharmony_ci * when resolving the handle on the reclaim path.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_cistruct kvm_ffa_descriptor_buffer {
5162306a36Sopenharmony_ci	void	*buf;
5262306a36Sopenharmony_ci	size_t	len;
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic struct kvm_ffa_descriptor_buffer ffa_desc_buf;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistruct kvm_ffa_buffers {
5862306a36Sopenharmony_ci	hyp_spinlock_t lock;
5962306a36Sopenharmony_ci	void *tx;
6062306a36Sopenharmony_ci	void *rx;
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/*
6462306a36Sopenharmony_ci * Note that we don't currently lock these buffers explicitly, instead
6562306a36Sopenharmony_ci * relying on the locking of the host FFA buffers as we only have one
6662306a36Sopenharmony_ci * client.
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_cistatic struct kvm_ffa_buffers hyp_buffers;
6962306a36Sopenharmony_cistatic struct kvm_ffa_buffers host_buffers;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic void ffa_to_smccc_error(struct arm_smccc_res *res, u64 ffa_errno)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	*res = (struct arm_smccc_res) {
7462306a36Sopenharmony_ci		.a0	= FFA_ERROR,
7562306a36Sopenharmony_ci		.a2	= ffa_errno,
7662306a36Sopenharmony_ci	};
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic void ffa_to_smccc_res_prop(struct arm_smccc_res *res, int ret, u64 prop)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	if (ret == FFA_RET_SUCCESS) {
8262306a36Sopenharmony_ci		*res = (struct arm_smccc_res) { .a0 = FFA_SUCCESS,
8362306a36Sopenharmony_ci						.a2 = prop };
8462306a36Sopenharmony_ci	} else {
8562306a36Sopenharmony_ci		ffa_to_smccc_error(res, ret);
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic void ffa_to_smccc_res(struct arm_smccc_res *res, int ret)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	ffa_to_smccc_res_prop(res, ret, 0);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic void ffa_set_retval(struct kvm_cpu_context *ctxt,
9562306a36Sopenharmony_ci			   struct arm_smccc_res *res)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	cpu_reg(ctxt, 0) = res->a0;
9862306a36Sopenharmony_ci	cpu_reg(ctxt, 1) = res->a1;
9962306a36Sopenharmony_ci	cpu_reg(ctxt, 2) = res->a2;
10062306a36Sopenharmony_ci	cpu_reg(ctxt, 3) = res->a3;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic bool is_ffa_call(u64 func_id)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	return ARM_SMCCC_IS_FAST_CALL(func_id) &&
10662306a36Sopenharmony_ci	       ARM_SMCCC_OWNER_NUM(func_id) == ARM_SMCCC_OWNER_STANDARD &&
10762306a36Sopenharmony_ci	       ARM_SMCCC_FUNC_NUM(func_id) >= FFA_MIN_FUNC_NUM &&
10862306a36Sopenharmony_ci	       ARM_SMCCC_FUNC_NUM(func_id) <= FFA_MAX_FUNC_NUM;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic int ffa_map_hyp_buffers(u64 ffa_page_count)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct arm_smccc_res res;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	arm_smccc_1_1_smc(FFA_FN64_RXTX_MAP,
11662306a36Sopenharmony_ci			  hyp_virt_to_phys(hyp_buffers.tx),
11762306a36Sopenharmony_ci			  hyp_virt_to_phys(hyp_buffers.rx),
11862306a36Sopenharmony_ci			  ffa_page_count,
11962306a36Sopenharmony_ci			  0, 0, 0, 0,
12062306a36Sopenharmony_ci			  &res);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return res.a0 == FFA_SUCCESS ? FFA_RET_SUCCESS : res.a2;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic int ffa_unmap_hyp_buffers(void)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	struct arm_smccc_res res;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	arm_smccc_1_1_smc(FFA_RXTX_UNMAP,
13062306a36Sopenharmony_ci			  HOST_FFA_ID,
13162306a36Sopenharmony_ci			  0, 0, 0, 0, 0, 0,
13262306a36Sopenharmony_ci			  &res);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	return res.a0 == FFA_SUCCESS ? FFA_RET_SUCCESS : res.a2;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic void ffa_mem_frag_tx(struct arm_smccc_res *res, u32 handle_lo,
13862306a36Sopenharmony_ci			     u32 handle_hi, u32 fraglen, u32 endpoint_id)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	arm_smccc_1_1_smc(FFA_MEM_FRAG_TX,
14162306a36Sopenharmony_ci			  handle_lo, handle_hi, fraglen, endpoint_id,
14262306a36Sopenharmony_ci			  0, 0, 0,
14362306a36Sopenharmony_ci			  res);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic void ffa_mem_frag_rx(struct arm_smccc_res *res, u32 handle_lo,
14762306a36Sopenharmony_ci			     u32 handle_hi, u32 fragoff)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	arm_smccc_1_1_smc(FFA_MEM_FRAG_RX,
15062306a36Sopenharmony_ci			  handle_lo, handle_hi, fragoff, HOST_FFA_ID,
15162306a36Sopenharmony_ci			  0, 0, 0,
15262306a36Sopenharmony_ci			  res);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic void ffa_mem_xfer(struct arm_smccc_res *res, u64 func_id, u32 len,
15662306a36Sopenharmony_ci			  u32 fraglen)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	arm_smccc_1_1_smc(func_id, len, fraglen,
15962306a36Sopenharmony_ci			  0, 0, 0, 0, 0,
16062306a36Sopenharmony_ci			  res);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic void ffa_mem_reclaim(struct arm_smccc_res *res, u32 handle_lo,
16462306a36Sopenharmony_ci			     u32 handle_hi, u32 flags)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	arm_smccc_1_1_smc(FFA_MEM_RECLAIM,
16762306a36Sopenharmony_ci			  handle_lo, handle_hi, flags,
16862306a36Sopenharmony_ci			  0, 0, 0, 0,
16962306a36Sopenharmony_ci			  res);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic void ffa_retrieve_req(struct arm_smccc_res *res, u32 len)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	arm_smccc_1_1_smc(FFA_FN64_MEM_RETRIEVE_REQ,
17562306a36Sopenharmony_ci			  len, len,
17662306a36Sopenharmony_ci			  0, 0, 0, 0, 0,
17762306a36Sopenharmony_ci			  res);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic void do_ffa_rxtx_map(struct arm_smccc_res *res,
18162306a36Sopenharmony_ci			    struct kvm_cpu_context *ctxt)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	DECLARE_REG(phys_addr_t, tx, ctxt, 1);
18462306a36Sopenharmony_ci	DECLARE_REG(phys_addr_t, rx, ctxt, 2);
18562306a36Sopenharmony_ci	DECLARE_REG(u32, npages, ctxt, 3);
18662306a36Sopenharmony_ci	int ret = 0;
18762306a36Sopenharmony_ci	void *rx_virt, *tx_virt;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (npages != (KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE) / FFA_PAGE_SIZE) {
19062306a36Sopenharmony_ci		ret = FFA_RET_INVALID_PARAMETERS;
19162306a36Sopenharmony_ci		goto out;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (!PAGE_ALIGNED(tx) || !PAGE_ALIGNED(rx)) {
19562306a36Sopenharmony_ci		ret = FFA_RET_INVALID_PARAMETERS;
19662306a36Sopenharmony_ci		goto out;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	hyp_spin_lock(&host_buffers.lock);
20062306a36Sopenharmony_ci	if (host_buffers.tx) {
20162306a36Sopenharmony_ci		ret = FFA_RET_DENIED;
20262306a36Sopenharmony_ci		goto out_unlock;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/*
20662306a36Sopenharmony_ci	 * Map our hypervisor buffers into the SPMD before mapping and
20762306a36Sopenharmony_ci	 * pinning the host buffers in our own address space.
20862306a36Sopenharmony_ci	 */
20962306a36Sopenharmony_ci	ret = ffa_map_hyp_buffers(npages);
21062306a36Sopenharmony_ci	if (ret)
21162306a36Sopenharmony_ci		goto out_unlock;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	ret = __pkvm_host_share_hyp(hyp_phys_to_pfn(tx));
21462306a36Sopenharmony_ci	if (ret) {
21562306a36Sopenharmony_ci		ret = FFA_RET_INVALID_PARAMETERS;
21662306a36Sopenharmony_ci		goto err_unmap;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	ret = __pkvm_host_share_hyp(hyp_phys_to_pfn(rx));
22062306a36Sopenharmony_ci	if (ret) {
22162306a36Sopenharmony_ci		ret = FFA_RET_INVALID_PARAMETERS;
22262306a36Sopenharmony_ci		goto err_unshare_tx;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	tx_virt = hyp_phys_to_virt(tx);
22662306a36Sopenharmony_ci	ret = hyp_pin_shared_mem(tx_virt, tx_virt + 1);
22762306a36Sopenharmony_ci	if (ret) {
22862306a36Sopenharmony_ci		ret = FFA_RET_INVALID_PARAMETERS;
22962306a36Sopenharmony_ci		goto err_unshare_rx;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	rx_virt = hyp_phys_to_virt(rx);
23362306a36Sopenharmony_ci	ret = hyp_pin_shared_mem(rx_virt, rx_virt + 1);
23462306a36Sopenharmony_ci	if (ret) {
23562306a36Sopenharmony_ci		ret = FFA_RET_INVALID_PARAMETERS;
23662306a36Sopenharmony_ci		goto err_unpin_tx;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	host_buffers.tx = tx_virt;
24062306a36Sopenharmony_ci	host_buffers.rx = rx_virt;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ciout_unlock:
24362306a36Sopenharmony_ci	hyp_spin_unlock(&host_buffers.lock);
24462306a36Sopenharmony_ciout:
24562306a36Sopenharmony_ci	ffa_to_smccc_res(res, ret);
24662306a36Sopenharmony_ci	return;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cierr_unpin_tx:
24962306a36Sopenharmony_ci	hyp_unpin_shared_mem(tx_virt, tx_virt + 1);
25062306a36Sopenharmony_cierr_unshare_rx:
25162306a36Sopenharmony_ci	__pkvm_host_unshare_hyp(hyp_phys_to_pfn(rx));
25262306a36Sopenharmony_cierr_unshare_tx:
25362306a36Sopenharmony_ci	__pkvm_host_unshare_hyp(hyp_phys_to_pfn(tx));
25462306a36Sopenharmony_cierr_unmap:
25562306a36Sopenharmony_ci	ffa_unmap_hyp_buffers();
25662306a36Sopenharmony_ci	goto out_unlock;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void do_ffa_rxtx_unmap(struct arm_smccc_res *res,
26062306a36Sopenharmony_ci			      struct kvm_cpu_context *ctxt)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	DECLARE_REG(u32, id, ctxt, 1);
26362306a36Sopenharmony_ci	int ret = 0;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (id != HOST_FFA_ID) {
26662306a36Sopenharmony_ci		ret = FFA_RET_INVALID_PARAMETERS;
26762306a36Sopenharmony_ci		goto out;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	hyp_spin_lock(&host_buffers.lock);
27162306a36Sopenharmony_ci	if (!host_buffers.tx) {
27262306a36Sopenharmony_ci		ret = FFA_RET_INVALID_PARAMETERS;
27362306a36Sopenharmony_ci		goto out_unlock;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	hyp_unpin_shared_mem(host_buffers.tx, host_buffers.tx + 1);
27762306a36Sopenharmony_ci	WARN_ON(__pkvm_host_unshare_hyp(hyp_virt_to_pfn(host_buffers.tx)));
27862306a36Sopenharmony_ci	host_buffers.tx = NULL;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	hyp_unpin_shared_mem(host_buffers.rx, host_buffers.rx + 1);
28162306a36Sopenharmony_ci	WARN_ON(__pkvm_host_unshare_hyp(hyp_virt_to_pfn(host_buffers.rx)));
28262306a36Sopenharmony_ci	host_buffers.rx = NULL;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	ffa_unmap_hyp_buffers();
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ciout_unlock:
28762306a36Sopenharmony_ci	hyp_spin_unlock(&host_buffers.lock);
28862306a36Sopenharmony_ciout:
28962306a36Sopenharmony_ci	ffa_to_smccc_res(res, ret);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic u32 __ffa_host_share_ranges(struct ffa_mem_region_addr_range *ranges,
29362306a36Sopenharmony_ci				   u32 nranges)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	u32 i;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	for (i = 0; i < nranges; ++i) {
29862306a36Sopenharmony_ci		struct ffa_mem_region_addr_range *range = &ranges[i];
29962306a36Sopenharmony_ci		u64 sz = (u64)range->pg_cnt * FFA_PAGE_SIZE;
30062306a36Sopenharmony_ci		u64 pfn = hyp_phys_to_pfn(range->address);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		if (!PAGE_ALIGNED(sz))
30362306a36Sopenharmony_ci			break;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci		if (__pkvm_host_share_ffa(pfn, sz / PAGE_SIZE))
30662306a36Sopenharmony_ci			break;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	return i;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic u32 __ffa_host_unshare_ranges(struct ffa_mem_region_addr_range *ranges,
31362306a36Sopenharmony_ci				     u32 nranges)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	u32 i;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	for (i = 0; i < nranges; ++i) {
31862306a36Sopenharmony_ci		struct ffa_mem_region_addr_range *range = &ranges[i];
31962306a36Sopenharmony_ci		u64 sz = (u64)range->pg_cnt * FFA_PAGE_SIZE;
32062306a36Sopenharmony_ci		u64 pfn = hyp_phys_to_pfn(range->address);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		if (!PAGE_ALIGNED(sz))
32362306a36Sopenharmony_ci			break;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		if (__pkvm_host_unshare_ffa(pfn, sz / PAGE_SIZE))
32662306a36Sopenharmony_ci			break;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return i;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic int ffa_host_share_ranges(struct ffa_mem_region_addr_range *ranges,
33362306a36Sopenharmony_ci				 u32 nranges)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	u32 nshared = __ffa_host_share_ranges(ranges, nranges);
33662306a36Sopenharmony_ci	int ret = 0;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (nshared != nranges) {
33962306a36Sopenharmony_ci		WARN_ON(__ffa_host_unshare_ranges(ranges, nshared) != nshared);
34062306a36Sopenharmony_ci		ret = FFA_RET_DENIED;
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return ret;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic int ffa_host_unshare_ranges(struct ffa_mem_region_addr_range *ranges,
34762306a36Sopenharmony_ci				   u32 nranges)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	u32 nunshared = __ffa_host_unshare_ranges(ranges, nranges);
35062306a36Sopenharmony_ci	int ret = 0;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	if (nunshared != nranges) {
35362306a36Sopenharmony_ci		WARN_ON(__ffa_host_share_ranges(ranges, nunshared) != nunshared);
35462306a36Sopenharmony_ci		ret = FFA_RET_DENIED;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return ret;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic void do_ffa_mem_frag_tx(struct arm_smccc_res *res,
36162306a36Sopenharmony_ci			       struct kvm_cpu_context *ctxt)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	DECLARE_REG(u32, handle_lo, ctxt, 1);
36462306a36Sopenharmony_ci	DECLARE_REG(u32, handle_hi, ctxt, 2);
36562306a36Sopenharmony_ci	DECLARE_REG(u32, fraglen, ctxt, 3);
36662306a36Sopenharmony_ci	DECLARE_REG(u32, endpoint_id, ctxt, 4);
36762306a36Sopenharmony_ci	struct ffa_mem_region_addr_range *buf;
36862306a36Sopenharmony_ci	int ret = FFA_RET_INVALID_PARAMETERS;
36962306a36Sopenharmony_ci	u32 nr_ranges;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE)
37262306a36Sopenharmony_ci		goto out;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (fraglen % sizeof(*buf))
37562306a36Sopenharmony_ci		goto out;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	hyp_spin_lock(&host_buffers.lock);
37862306a36Sopenharmony_ci	if (!host_buffers.tx)
37962306a36Sopenharmony_ci		goto out_unlock;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	buf = hyp_buffers.tx;
38262306a36Sopenharmony_ci	memcpy(buf, host_buffers.tx, fraglen);
38362306a36Sopenharmony_ci	nr_ranges = fraglen / sizeof(*buf);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	ret = ffa_host_share_ranges(buf, nr_ranges);
38662306a36Sopenharmony_ci	if (ret) {
38762306a36Sopenharmony_ci		/*
38862306a36Sopenharmony_ci		 * We're effectively aborting the transaction, so we need
38962306a36Sopenharmony_ci		 * to restore the global state back to what it was prior to
39062306a36Sopenharmony_ci		 * transmission of the first fragment.
39162306a36Sopenharmony_ci		 */
39262306a36Sopenharmony_ci		ffa_mem_reclaim(res, handle_lo, handle_hi, 0);
39362306a36Sopenharmony_ci		WARN_ON(res->a0 != FFA_SUCCESS);
39462306a36Sopenharmony_ci		goto out_unlock;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	ffa_mem_frag_tx(res, handle_lo, handle_hi, fraglen, endpoint_id);
39862306a36Sopenharmony_ci	if (res->a0 != FFA_SUCCESS && res->a0 != FFA_MEM_FRAG_RX)
39962306a36Sopenharmony_ci		WARN_ON(ffa_host_unshare_ranges(buf, nr_ranges));
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ciout_unlock:
40262306a36Sopenharmony_ci	hyp_spin_unlock(&host_buffers.lock);
40362306a36Sopenharmony_ciout:
40462306a36Sopenharmony_ci	if (ret)
40562306a36Sopenharmony_ci		ffa_to_smccc_res(res, ret);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	/*
40862306a36Sopenharmony_ci	 * If for any reason this did not succeed, we're in trouble as we have
40962306a36Sopenharmony_ci	 * now lost the content of the previous fragments and we can't rollback
41062306a36Sopenharmony_ci	 * the host stage-2 changes. The pages previously marked as shared will
41162306a36Sopenharmony_ci	 * remain stuck in that state forever, hence preventing the host from
41262306a36Sopenharmony_ci	 * sharing/donating them again and may possibly lead to subsequent
41362306a36Sopenharmony_ci	 * failures, but this will not compromise confidentiality.
41462306a36Sopenharmony_ci	 */
41562306a36Sopenharmony_ci	return;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic __always_inline void do_ffa_mem_xfer(const u64 func_id,
41962306a36Sopenharmony_ci					    struct arm_smccc_res *res,
42062306a36Sopenharmony_ci					    struct kvm_cpu_context *ctxt)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	DECLARE_REG(u32, len, ctxt, 1);
42362306a36Sopenharmony_ci	DECLARE_REG(u32, fraglen, ctxt, 2);
42462306a36Sopenharmony_ci	DECLARE_REG(u64, addr_mbz, ctxt, 3);
42562306a36Sopenharmony_ci	DECLARE_REG(u32, npages_mbz, ctxt, 4);
42662306a36Sopenharmony_ci	struct ffa_composite_mem_region *reg;
42762306a36Sopenharmony_ci	struct ffa_mem_region *buf;
42862306a36Sopenharmony_ci	u32 offset, nr_ranges;
42962306a36Sopenharmony_ci	int ret = 0;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	BUILD_BUG_ON(func_id != FFA_FN64_MEM_SHARE &&
43262306a36Sopenharmony_ci		     func_id != FFA_FN64_MEM_LEND);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (addr_mbz || npages_mbz || fraglen > len ||
43562306a36Sopenharmony_ci	    fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE) {
43662306a36Sopenharmony_ci		ret = FFA_RET_INVALID_PARAMETERS;
43762306a36Sopenharmony_ci		goto out;
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (fraglen < sizeof(struct ffa_mem_region) +
44162306a36Sopenharmony_ci		      sizeof(struct ffa_mem_region_attributes)) {
44262306a36Sopenharmony_ci		ret = FFA_RET_INVALID_PARAMETERS;
44362306a36Sopenharmony_ci		goto out;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	hyp_spin_lock(&host_buffers.lock);
44762306a36Sopenharmony_ci	if (!host_buffers.tx) {
44862306a36Sopenharmony_ci		ret = FFA_RET_INVALID_PARAMETERS;
44962306a36Sopenharmony_ci		goto out_unlock;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	buf = hyp_buffers.tx;
45362306a36Sopenharmony_ci	memcpy(buf, host_buffers.tx, fraglen);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	offset = buf->ep_mem_access[0].composite_off;
45662306a36Sopenharmony_ci	if (!offset || buf->ep_count != 1 || buf->sender_id != HOST_FFA_ID) {
45762306a36Sopenharmony_ci		ret = FFA_RET_INVALID_PARAMETERS;
45862306a36Sopenharmony_ci		goto out_unlock;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (fraglen < offset + sizeof(struct ffa_composite_mem_region)) {
46262306a36Sopenharmony_ci		ret = FFA_RET_INVALID_PARAMETERS;
46362306a36Sopenharmony_ci		goto out_unlock;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	reg = (void *)buf + offset;
46762306a36Sopenharmony_ci	nr_ranges = ((void *)buf + fraglen) - (void *)reg->constituents;
46862306a36Sopenharmony_ci	if (nr_ranges % sizeof(reg->constituents[0])) {
46962306a36Sopenharmony_ci		ret = FFA_RET_INVALID_PARAMETERS;
47062306a36Sopenharmony_ci		goto out_unlock;
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	nr_ranges /= sizeof(reg->constituents[0]);
47462306a36Sopenharmony_ci	ret = ffa_host_share_ranges(reg->constituents, nr_ranges);
47562306a36Sopenharmony_ci	if (ret)
47662306a36Sopenharmony_ci		goto out_unlock;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	ffa_mem_xfer(res, func_id, len, fraglen);
47962306a36Sopenharmony_ci	if (fraglen != len) {
48062306a36Sopenharmony_ci		if (res->a0 != FFA_MEM_FRAG_RX)
48162306a36Sopenharmony_ci			goto err_unshare;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci		if (res->a3 != fraglen)
48462306a36Sopenharmony_ci			goto err_unshare;
48562306a36Sopenharmony_ci	} else if (res->a0 != FFA_SUCCESS) {
48662306a36Sopenharmony_ci		goto err_unshare;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ciout_unlock:
49062306a36Sopenharmony_ci	hyp_spin_unlock(&host_buffers.lock);
49162306a36Sopenharmony_ciout:
49262306a36Sopenharmony_ci	if (ret)
49362306a36Sopenharmony_ci		ffa_to_smccc_res(res, ret);
49462306a36Sopenharmony_ci	return;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cierr_unshare:
49762306a36Sopenharmony_ci	WARN_ON(ffa_host_unshare_ranges(reg->constituents, nr_ranges));
49862306a36Sopenharmony_ci	goto out_unlock;
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistatic void do_ffa_mem_reclaim(struct arm_smccc_res *res,
50262306a36Sopenharmony_ci			       struct kvm_cpu_context *ctxt)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	DECLARE_REG(u32, handle_lo, ctxt, 1);
50562306a36Sopenharmony_ci	DECLARE_REG(u32, handle_hi, ctxt, 2);
50662306a36Sopenharmony_ci	DECLARE_REG(u32, flags, ctxt, 3);
50762306a36Sopenharmony_ci	struct ffa_composite_mem_region *reg;
50862306a36Sopenharmony_ci	u32 offset, len, fraglen, fragoff;
50962306a36Sopenharmony_ci	struct ffa_mem_region *buf;
51062306a36Sopenharmony_ci	int ret = 0;
51162306a36Sopenharmony_ci	u64 handle;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	handle = PACK_HANDLE(handle_lo, handle_hi);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	hyp_spin_lock(&host_buffers.lock);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	buf = hyp_buffers.tx;
51862306a36Sopenharmony_ci	*buf = (struct ffa_mem_region) {
51962306a36Sopenharmony_ci		.sender_id	= HOST_FFA_ID,
52062306a36Sopenharmony_ci		.handle		= handle,
52162306a36Sopenharmony_ci	};
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	ffa_retrieve_req(res, sizeof(*buf));
52462306a36Sopenharmony_ci	buf = hyp_buffers.rx;
52562306a36Sopenharmony_ci	if (res->a0 != FFA_MEM_RETRIEVE_RESP)
52662306a36Sopenharmony_ci		goto out_unlock;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	len = res->a1;
52962306a36Sopenharmony_ci	fraglen = res->a2;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	offset = buf->ep_mem_access[0].composite_off;
53262306a36Sopenharmony_ci	/*
53362306a36Sopenharmony_ci	 * We can trust the SPMD to get this right, but let's at least
53462306a36Sopenharmony_ci	 * check that we end up with something that doesn't look _completely_
53562306a36Sopenharmony_ci	 * bogus.
53662306a36Sopenharmony_ci	 */
53762306a36Sopenharmony_ci	if (WARN_ON(offset > len ||
53862306a36Sopenharmony_ci		    fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE)) {
53962306a36Sopenharmony_ci		ret = FFA_RET_ABORTED;
54062306a36Sopenharmony_ci		goto out_unlock;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	if (len > ffa_desc_buf.len) {
54462306a36Sopenharmony_ci		ret = FFA_RET_NO_MEMORY;
54562306a36Sopenharmony_ci		goto out_unlock;
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	buf = ffa_desc_buf.buf;
54962306a36Sopenharmony_ci	memcpy(buf, hyp_buffers.rx, fraglen);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	for (fragoff = fraglen; fragoff < len; fragoff += fraglen) {
55262306a36Sopenharmony_ci		ffa_mem_frag_rx(res, handle_lo, handle_hi, fragoff);
55362306a36Sopenharmony_ci		if (res->a0 != FFA_MEM_FRAG_TX) {
55462306a36Sopenharmony_ci			ret = FFA_RET_INVALID_PARAMETERS;
55562306a36Sopenharmony_ci			goto out_unlock;
55662306a36Sopenharmony_ci		}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci		fraglen = res->a3;
55962306a36Sopenharmony_ci		memcpy((void *)buf + fragoff, hyp_buffers.rx, fraglen);
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	ffa_mem_reclaim(res, handle_lo, handle_hi, flags);
56362306a36Sopenharmony_ci	if (res->a0 != FFA_SUCCESS)
56462306a36Sopenharmony_ci		goto out_unlock;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	reg = (void *)buf + offset;
56762306a36Sopenharmony_ci	/* If the SPMD was happy, then we should be too. */
56862306a36Sopenharmony_ci	WARN_ON(ffa_host_unshare_ranges(reg->constituents,
56962306a36Sopenharmony_ci					reg->addr_range_cnt));
57062306a36Sopenharmony_ciout_unlock:
57162306a36Sopenharmony_ci	hyp_spin_unlock(&host_buffers.lock);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	if (ret)
57462306a36Sopenharmony_ci		ffa_to_smccc_res(res, ret);
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci/*
57862306a36Sopenharmony_ci * Is a given FFA function supported, either by forwarding on directly
57962306a36Sopenharmony_ci * or by handling at EL2?
58062306a36Sopenharmony_ci */
58162306a36Sopenharmony_cistatic bool ffa_call_supported(u64 func_id)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	switch (func_id) {
58462306a36Sopenharmony_ci	/* Unsupported memory management calls */
58562306a36Sopenharmony_ci	case FFA_FN64_MEM_RETRIEVE_REQ:
58662306a36Sopenharmony_ci	case FFA_MEM_RETRIEVE_RESP:
58762306a36Sopenharmony_ci	case FFA_MEM_RELINQUISH:
58862306a36Sopenharmony_ci	case FFA_MEM_OP_PAUSE:
58962306a36Sopenharmony_ci	case FFA_MEM_OP_RESUME:
59062306a36Sopenharmony_ci	case FFA_MEM_FRAG_RX:
59162306a36Sopenharmony_ci	case FFA_FN64_MEM_DONATE:
59262306a36Sopenharmony_ci	/* Indirect message passing via RX/TX buffers */
59362306a36Sopenharmony_ci	case FFA_MSG_SEND:
59462306a36Sopenharmony_ci	case FFA_MSG_POLL:
59562306a36Sopenharmony_ci	case FFA_MSG_WAIT:
59662306a36Sopenharmony_ci	/* 32-bit variants of 64-bit calls */
59762306a36Sopenharmony_ci	case FFA_MSG_SEND_DIRECT_REQ:
59862306a36Sopenharmony_ci	case FFA_MSG_SEND_DIRECT_RESP:
59962306a36Sopenharmony_ci	case FFA_RXTX_MAP:
60062306a36Sopenharmony_ci	case FFA_MEM_DONATE:
60162306a36Sopenharmony_ci	case FFA_MEM_RETRIEVE_REQ:
60262306a36Sopenharmony_ci		return false;
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	return true;
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cistatic bool do_ffa_features(struct arm_smccc_res *res,
60962306a36Sopenharmony_ci			    struct kvm_cpu_context *ctxt)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	DECLARE_REG(u32, id, ctxt, 1);
61262306a36Sopenharmony_ci	u64 prop = 0;
61362306a36Sopenharmony_ci	int ret = 0;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (!ffa_call_supported(id)) {
61662306a36Sopenharmony_ci		ret = FFA_RET_NOT_SUPPORTED;
61762306a36Sopenharmony_ci		goto out_handled;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	switch (id) {
62162306a36Sopenharmony_ci	case FFA_MEM_SHARE:
62262306a36Sopenharmony_ci	case FFA_FN64_MEM_SHARE:
62362306a36Sopenharmony_ci	case FFA_MEM_LEND:
62462306a36Sopenharmony_ci	case FFA_FN64_MEM_LEND:
62562306a36Sopenharmony_ci		ret = FFA_RET_SUCCESS;
62662306a36Sopenharmony_ci		prop = 0; /* No support for dynamic buffers */
62762306a36Sopenharmony_ci		goto out_handled;
62862306a36Sopenharmony_ci	default:
62962306a36Sopenharmony_ci		return false;
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ciout_handled:
63362306a36Sopenharmony_ci	ffa_to_smccc_res_prop(res, ret, prop);
63462306a36Sopenharmony_ci	return true;
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cibool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt, u32 func_id)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	struct arm_smccc_res res;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	/*
64262306a36Sopenharmony_ci	 * There's no way we can tell what a non-standard SMC call might
64362306a36Sopenharmony_ci	 * be up to. Ideally, we would terminate these here and return
64462306a36Sopenharmony_ci	 * an error to the host, but sadly devices make use of custom
64562306a36Sopenharmony_ci	 * firmware calls for things like power management, debugging,
64662306a36Sopenharmony_ci	 * RNG access and crash reporting.
64762306a36Sopenharmony_ci	 *
64862306a36Sopenharmony_ci	 * Given that the architecture requires us to trust EL3 anyway,
64962306a36Sopenharmony_ci	 * we forward unrecognised calls on under the assumption that
65062306a36Sopenharmony_ci	 * the firmware doesn't expose a mechanism to access arbitrary
65162306a36Sopenharmony_ci	 * non-secure memory. Short of a per-device table of SMCs, this
65262306a36Sopenharmony_ci	 * is the best we can do.
65362306a36Sopenharmony_ci	 */
65462306a36Sopenharmony_ci	if (!is_ffa_call(func_id))
65562306a36Sopenharmony_ci		return false;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	switch (func_id) {
65862306a36Sopenharmony_ci	case FFA_FEATURES:
65962306a36Sopenharmony_ci		if (!do_ffa_features(&res, host_ctxt))
66062306a36Sopenharmony_ci			return false;
66162306a36Sopenharmony_ci		goto out_handled;
66262306a36Sopenharmony_ci	/* Memory management */
66362306a36Sopenharmony_ci	case FFA_FN64_RXTX_MAP:
66462306a36Sopenharmony_ci		do_ffa_rxtx_map(&res, host_ctxt);
66562306a36Sopenharmony_ci		goto out_handled;
66662306a36Sopenharmony_ci	case FFA_RXTX_UNMAP:
66762306a36Sopenharmony_ci		do_ffa_rxtx_unmap(&res, host_ctxt);
66862306a36Sopenharmony_ci		goto out_handled;
66962306a36Sopenharmony_ci	case FFA_MEM_SHARE:
67062306a36Sopenharmony_ci	case FFA_FN64_MEM_SHARE:
67162306a36Sopenharmony_ci		do_ffa_mem_xfer(FFA_FN64_MEM_SHARE, &res, host_ctxt);
67262306a36Sopenharmony_ci		goto out_handled;
67362306a36Sopenharmony_ci	case FFA_MEM_RECLAIM:
67462306a36Sopenharmony_ci		do_ffa_mem_reclaim(&res, host_ctxt);
67562306a36Sopenharmony_ci		goto out_handled;
67662306a36Sopenharmony_ci	case FFA_MEM_LEND:
67762306a36Sopenharmony_ci	case FFA_FN64_MEM_LEND:
67862306a36Sopenharmony_ci		do_ffa_mem_xfer(FFA_FN64_MEM_LEND, &res, host_ctxt);
67962306a36Sopenharmony_ci		goto out_handled;
68062306a36Sopenharmony_ci	case FFA_MEM_FRAG_TX:
68162306a36Sopenharmony_ci		do_ffa_mem_frag_tx(&res, host_ctxt);
68262306a36Sopenharmony_ci		goto out_handled;
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	if (ffa_call_supported(func_id))
68662306a36Sopenharmony_ci		return false; /* Pass through */
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	ffa_to_smccc_error(&res, FFA_RET_NOT_SUPPORTED);
68962306a36Sopenharmony_ciout_handled:
69062306a36Sopenharmony_ci	ffa_set_retval(host_ctxt, &res);
69162306a36Sopenharmony_ci	return true;
69262306a36Sopenharmony_ci}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ciint hyp_ffa_init(void *pages)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	struct arm_smccc_res res;
69762306a36Sopenharmony_ci	size_t min_rxtx_sz;
69862306a36Sopenharmony_ci	void *tx, *rx;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	if (kvm_host_psci_config.smccc_version < ARM_SMCCC_VERSION_1_2)
70162306a36Sopenharmony_ci		return 0;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	arm_smccc_1_1_smc(FFA_VERSION, FFA_VERSION_1_0, 0, 0, 0, 0, 0, 0, &res);
70462306a36Sopenharmony_ci	if (res.a0 == FFA_RET_NOT_SUPPORTED)
70562306a36Sopenharmony_ci		return 0;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	/*
70862306a36Sopenharmony_ci	 * Firmware returns the maximum supported version of the FF-A
70962306a36Sopenharmony_ci	 * implementation. Check that the returned version is
71062306a36Sopenharmony_ci	 * backwards-compatible with the hyp according to the rules in DEN0077A
71162306a36Sopenharmony_ci	 * v1.1 REL0 13.2.1.
71262306a36Sopenharmony_ci	 *
71362306a36Sopenharmony_ci	 * Of course, things are never simple when dealing with firmware. v1.1
71462306a36Sopenharmony_ci	 * broke ABI with v1.0 on several structures, which is itself
71562306a36Sopenharmony_ci	 * incompatible with the aforementioned versioning scheme. The
71662306a36Sopenharmony_ci	 * expectation is that v1.x implementations that do not support the v1.0
71762306a36Sopenharmony_ci	 * ABI return NOT_SUPPORTED rather than a version number, according to
71862306a36Sopenharmony_ci	 * DEN0077A v1.1 REL0 18.6.4.
71962306a36Sopenharmony_ci	 */
72062306a36Sopenharmony_ci	if (FFA_MAJOR_VERSION(res.a0) != 1)
72162306a36Sopenharmony_ci		return -EOPNOTSUPP;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	arm_smccc_1_1_smc(FFA_ID_GET, 0, 0, 0, 0, 0, 0, 0, &res);
72462306a36Sopenharmony_ci	if (res.a0 != FFA_SUCCESS)
72562306a36Sopenharmony_ci		return -EOPNOTSUPP;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	if (res.a2 != HOST_FFA_ID)
72862306a36Sopenharmony_ci		return -EINVAL;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	arm_smccc_1_1_smc(FFA_FEATURES, FFA_FN64_RXTX_MAP,
73162306a36Sopenharmony_ci			  0, 0, 0, 0, 0, 0, &res);
73262306a36Sopenharmony_ci	if (res.a0 != FFA_SUCCESS)
73362306a36Sopenharmony_ci		return -EOPNOTSUPP;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	switch (res.a2) {
73662306a36Sopenharmony_ci	case FFA_FEAT_RXTX_MIN_SZ_4K:
73762306a36Sopenharmony_ci		min_rxtx_sz = SZ_4K;
73862306a36Sopenharmony_ci		break;
73962306a36Sopenharmony_ci	case FFA_FEAT_RXTX_MIN_SZ_16K:
74062306a36Sopenharmony_ci		min_rxtx_sz = SZ_16K;
74162306a36Sopenharmony_ci		break;
74262306a36Sopenharmony_ci	case FFA_FEAT_RXTX_MIN_SZ_64K:
74362306a36Sopenharmony_ci		min_rxtx_sz = SZ_64K;
74462306a36Sopenharmony_ci		break;
74562306a36Sopenharmony_ci	default:
74662306a36Sopenharmony_ci		return -EINVAL;
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	if (min_rxtx_sz > PAGE_SIZE)
75062306a36Sopenharmony_ci		return -EOPNOTSUPP;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	tx = pages;
75362306a36Sopenharmony_ci	pages += KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE;
75462306a36Sopenharmony_ci	rx = pages;
75562306a36Sopenharmony_ci	pages += KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	ffa_desc_buf = (struct kvm_ffa_descriptor_buffer) {
75862306a36Sopenharmony_ci		.buf	= pages,
75962306a36Sopenharmony_ci		.len	= PAGE_SIZE *
76062306a36Sopenharmony_ci			  (hyp_ffa_proxy_pages() - (2 * KVM_FFA_MBOX_NR_PAGES)),
76162306a36Sopenharmony_ci	};
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	hyp_buffers = (struct kvm_ffa_buffers) {
76462306a36Sopenharmony_ci		.lock	= __HYP_SPIN_LOCK_UNLOCKED,
76562306a36Sopenharmony_ci		.tx	= tx,
76662306a36Sopenharmony_ci		.rx	= rx,
76762306a36Sopenharmony_ci	};
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	host_buffers = (struct kvm_ffa_buffers) {
77062306a36Sopenharmony_ci		.lock	= __HYP_SPIN_LOCK_UNLOCKED,
77162306a36Sopenharmony_ci	};
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	return 0;
77462306a36Sopenharmony_ci}
775