Commit a35d5b20 authored by Oliver Upton's avatar Oliver Upton
Browse files

Merge branch kvm-arm64/ffa-1p1 into kvmarm/next



* kvm-arm64/ffa-1p1:
  : Improvements to the pKVM FF-A Proxy, courtesy of Sebastian Ene
  :
  : Various minor improvements to how host FF-A calls are proxied with the
  : TEE, along with support for v1.1 of the protocol.
  KVM: arm64: Use FF-A 1.1 with pKVM
  KVM: arm64: Update the identification range for the FF-A smcs
  KVM: arm64: Add support for FFA_PARTITION_INFO_GET
  KVM: arm64: Trap FFA_VERSION host call in pKVM

Signed-off-by: default avatarOliver Upton <oliver.upton@linux.dev>
parents bd2e9513 42fb33dd
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@
#include <asm/kvm_host.h>

#define FFA_MIN_FUNC_NUM 0x60
#define FFA_MAX_FUNC_NUM 0x7F
#define FFA_MAX_FUNC_NUM 0xFF

int hyp_ffa_init(void *pages);
bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt, u32 func_id);
+148 −32
Original line number Diff line number Diff line
@@ -67,6 +67,9 @@ struct kvm_ffa_buffers {
 */
static struct kvm_ffa_buffers hyp_buffers;
static struct kvm_ffa_buffers host_buffers;
static u32 hyp_ffa_version;
static bool has_version_negotiated;
static hyp_spinlock_t version_lock;

static void ffa_to_smccc_error(struct arm_smccc_res *res, u64 ffa_errno)
{
@@ -454,7 +457,7 @@ static __always_inline void do_ffa_mem_xfer(const u64 func_id,
	memcpy(buf, host_buffers.tx, fraglen);

	ep_mem_access = (void *)buf +
			ffa_mem_desc_offset(buf, 0, FFA_VERSION_1_0);
			ffa_mem_desc_offset(buf, 0, hyp_ffa_version);
	offset = ep_mem_access->composite_off;
	if (!offset || buf->ep_count != 1 || buf->sender_id != HOST_FFA_ID) {
		ret = FFA_RET_INVALID_PARAMETERS;
@@ -533,7 +536,7 @@ static void do_ffa_mem_reclaim(struct arm_smccc_res *res,
	fraglen = res->a2;

	ep_mem_access = (void *)buf +
			ffa_mem_desc_offset(buf, 0, FFA_VERSION_1_0);
			ffa_mem_desc_offset(buf, 0, hyp_ffa_version);
	offset = ep_mem_access->composite_off;
	/*
	 * We can trust the SPMD to get this right, but let's at least
@@ -639,6 +642,132 @@ static bool do_ffa_features(struct arm_smccc_res *res,
	return true;
}

static int hyp_ffa_post_init(void)
{
	size_t min_rxtx_sz;
	struct arm_smccc_res res;

	arm_smccc_1_1_smc(FFA_ID_GET, 0, 0, 0, 0, 0, 0, 0, &res);
	if (res.a0 != FFA_SUCCESS)
		return -EOPNOTSUPP;

	if (res.a2 != HOST_FFA_ID)
		return -EINVAL;

	arm_smccc_1_1_smc(FFA_FEATURES, FFA_FN64_RXTX_MAP,
			  0, 0, 0, 0, 0, 0, &res);
	if (res.a0 != FFA_SUCCESS)
		return -EOPNOTSUPP;

	switch (res.a2) {
	case FFA_FEAT_RXTX_MIN_SZ_4K:
		min_rxtx_sz = SZ_4K;
		break;
	case FFA_FEAT_RXTX_MIN_SZ_16K:
		min_rxtx_sz = SZ_16K;
		break;
	case FFA_FEAT_RXTX_MIN_SZ_64K:
		min_rxtx_sz = SZ_64K;
		break;
	default:
		return -EINVAL;
	}

	if (min_rxtx_sz > PAGE_SIZE)
		return -EOPNOTSUPP;

	return 0;
}

static void do_ffa_version(struct arm_smccc_res *res,
			   struct kvm_cpu_context *ctxt)
{
	DECLARE_REG(u32, ffa_req_version, ctxt, 1);

	if (FFA_MAJOR_VERSION(ffa_req_version) != 1) {
		res->a0 = FFA_RET_NOT_SUPPORTED;
		return;
	}

	hyp_spin_lock(&version_lock);
	if (has_version_negotiated) {
		res->a0 = hyp_ffa_version;
		goto unlock;
	}

	/*
	 * If the client driver tries to downgrade the version, we need to ask
	 * first if TEE supports it.
	 */
	if (FFA_MINOR_VERSION(ffa_req_version) < FFA_MINOR_VERSION(hyp_ffa_version)) {
		arm_smccc_1_1_smc(FFA_VERSION, ffa_req_version, 0,
				  0, 0, 0, 0, 0,
				  res);
		if (res->a0 == FFA_RET_NOT_SUPPORTED)
			goto unlock;

		hyp_ffa_version = ffa_req_version;
	}

	if (hyp_ffa_post_init())
		res->a0 = FFA_RET_NOT_SUPPORTED;
	else {
		has_version_negotiated = true;
		res->a0 = hyp_ffa_version;
	}
unlock:
	hyp_spin_unlock(&version_lock);
}

static void do_ffa_part_get(struct arm_smccc_res *res,
			    struct kvm_cpu_context *ctxt)
{
	DECLARE_REG(u32, uuid0, ctxt, 1);
	DECLARE_REG(u32, uuid1, ctxt, 2);
	DECLARE_REG(u32, uuid2, ctxt, 3);
	DECLARE_REG(u32, uuid3, ctxt, 4);
	DECLARE_REG(u32, flags, ctxt, 5);
	u32 count, partition_sz, copy_sz;

	hyp_spin_lock(&host_buffers.lock);
	if (!host_buffers.rx) {
		ffa_to_smccc_res(res, FFA_RET_BUSY);
		goto out_unlock;
	}

	arm_smccc_1_1_smc(FFA_PARTITION_INFO_GET, uuid0, uuid1,
			  uuid2, uuid3, flags, 0, 0,
			  res);

	if (res->a0 != FFA_SUCCESS)
		goto out_unlock;

	count = res->a2;
	if (!count)
		goto out_unlock;

	if (hyp_ffa_version > FFA_VERSION_1_0) {
		/* Get the number of partitions deployed in the system */
		if (flags & 0x1)
			goto out_unlock;

		partition_sz  = res->a3;
	} else {
		/* FFA_VERSION_1_0 lacks the size in the response */
		partition_sz = FFA_1_0_PARTITON_INFO_SZ;
	}

	copy_sz = partition_sz * count;
	if (copy_sz > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE) {
		ffa_to_smccc_res(res, FFA_RET_ABORTED);
		goto out_unlock;
	}

	memcpy(host_buffers.rx, hyp_buffers.rx, copy_sz);
out_unlock:
	hyp_spin_unlock(&host_buffers.lock);
}

bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt, u32 func_id)
{
	struct arm_smccc_res res;
@@ -659,6 +788,11 @@ bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt, u32 func_id)
	if (!is_ffa_call(func_id))
		return false;

	if (!has_version_negotiated && func_id != FFA_VERSION) {
		ffa_to_smccc_error(&res, FFA_RET_INVALID_PARAMETERS);
		goto out_handled;
	}

	switch (func_id) {
	case FFA_FEATURES:
		if (!do_ffa_features(&res, host_ctxt))
@@ -685,6 +819,12 @@ bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt, u32 func_id)
	case FFA_MEM_FRAG_TX:
		do_ffa_mem_frag_tx(&res, host_ctxt);
		goto out_handled;
	case FFA_VERSION:
		do_ffa_version(&res, host_ctxt);
		goto out_handled;
	case FFA_PARTITION_INFO_GET:
		do_ffa_part_get(&res, host_ctxt);
		goto out_handled;
	}

	if (ffa_call_supported(func_id))
@@ -699,13 +839,12 @@ bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt, u32 func_id)
int hyp_ffa_init(void *pages)
{
	struct arm_smccc_res res;
	size_t min_rxtx_sz;
	void *tx, *rx;

	if (kvm_host_psci_config.smccc_version < ARM_SMCCC_VERSION_1_2)
		return 0;

	arm_smccc_1_1_smc(FFA_VERSION, FFA_VERSION_1_0, 0, 0, 0, 0, 0, 0, &res);
	arm_smccc_1_1_smc(FFA_VERSION, FFA_VERSION_1_1, 0, 0, 0, 0, 0, 0, &res);
	if (res.a0 == FFA_RET_NOT_SUPPORTED)
		return 0;

@@ -725,34 +864,10 @@ int hyp_ffa_init(void *pages)
	if (FFA_MAJOR_VERSION(res.a0) != 1)
		return -EOPNOTSUPP;

	arm_smccc_1_1_smc(FFA_ID_GET, 0, 0, 0, 0, 0, 0, 0, &res);
	if (res.a0 != FFA_SUCCESS)
		return -EOPNOTSUPP;

	if (res.a2 != HOST_FFA_ID)
		return -EINVAL;

	arm_smccc_1_1_smc(FFA_FEATURES, FFA_FN64_RXTX_MAP,
			  0, 0, 0, 0, 0, 0, &res);
	if (res.a0 != FFA_SUCCESS)
		return -EOPNOTSUPP;

	switch (res.a2) {
	case FFA_FEAT_RXTX_MIN_SZ_4K:
		min_rxtx_sz = SZ_4K;
		break;
	case FFA_FEAT_RXTX_MIN_SZ_16K:
		min_rxtx_sz = SZ_16K;
		break;
	case FFA_FEAT_RXTX_MIN_SZ_64K:
		min_rxtx_sz = SZ_64K;
		break;
	default:
		return -EINVAL;
	}

	if (min_rxtx_sz > PAGE_SIZE)
		return -EOPNOTSUPP;
	if (FFA_MINOR_VERSION(res.a0) < FFA_MINOR_VERSION(FFA_VERSION_1_1))
		hyp_ffa_version = res.a0;
	else
		hyp_ffa_version = FFA_VERSION_1_1;

	tx = pages;
	pages += KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE;
@@ -775,5 +890,6 @@ int hyp_ffa_init(void *pages)
		.lock	= __HYP_SPIN_LOCK_UNLOCKED,
	};

	version_lock = __HYP_SPIN_LOCK_UNLOCKED;
	return 0;
}
+3 −0
Original line number Diff line number Diff line
@@ -212,6 +212,9 @@ bool ffa_device_is_valid(struct ffa_device *ffa_dev) { return false; }

extern const struct bus_type ffa_bus_type;

/* The FF-A 1.0 partition structure lacks the uuid[4] */
#define FFA_1_0_PARTITON_INFO_SZ	(8)

/* FFA transport related */
struct ffa_partition_info {
	u16 id;