mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 16:44:58 +02:00
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: Oliver Upton <oliver.upton@linux.dev>
This commit is contained in:
commit
a35d5b2032
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,91 +642,10 @@ static bool do_ffa_features(struct arm_smccc_res *res,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt, u32 func_id)
|
||||
static int hyp_ffa_post_init(void)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
/*
|
||||
* There's no way we can tell what a non-standard SMC call might
|
||||
* be up to. Ideally, we would terminate these here and return
|
||||
* an error to the host, but sadly devices make use of custom
|
||||
* firmware calls for things like power management, debugging,
|
||||
* RNG access and crash reporting.
|
||||
*
|
||||
* Given that the architecture requires us to trust EL3 anyway,
|
||||
* we forward unrecognised calls on under the assumption that
|
||||
* the firmware doesn't expose a mechanism to access arbitrary
|
||||
* non-secure memory. Short of a per-device table of SMCs, this
|
||||
* is the best we can do.
|
||||
*/
|
||||
if (!is_ffa_call(func_id))
|
||||
return false;
|
||||
|
||||
switch (func_id) {
|
||||
case FFA_FEATURES:
|
||||
if (!do_ffa_features(&res, host_ctxt))
|
||||
return false;
|
||||
goto out_handled;
|
||||
/* Memory management */
|
||||
case FFA_FN64_RXTX_MAP:
|
||||
do_ffa_rxtx_map(&res, host_ctxt);
|
||||
goto out_handled;
|
||||
case FFA_RXTX_UNMAP:
|
||||
do_ffa_rxtx_unmap(&res, host_ctxt);
|
||||
goto out_handled;
|
||||
case FFA_MEM_SHARE:
|
||||
case FFA_FN64_MEM_SHARE:
|
||||
do_ffa_mem_xfer(FFA_FN64_MEM_SHARE, &res, host_ctxt);
|
||||
goto out_handled;
|
||||
case FFA_MEM_RECLAIM:
|
||||
do_ffa_mem_reclaim(&res, host_ctxt);
|
||||
goto out_handled;
|
||||
case FFA_MEM_LEND:
|
||||
case FFA_FN64_MEM_LEND:
|
||||
do_ffa_mem_xfer(FFA_FN64_MEM_LEND, &res, host_ctxt);
|
||||
goto out_handled;
|
||||
case FFA_MEM_FRAG_TX:
|
||||
do_ffa_mem_frag_tx(&res, host_ctxt);
|
||||
goto out_handled;
|
||||
}
|
||||
|
||||
if (ffa_call_supported(func_id))
|
||||
return false; /* Pass through */
|
||||
|
||||
ffa_to_smccc_error(&res, FFA_RET_NOT_SUPPORTED);
|
||||
out_handled:
|
||||
ffa_set_retval(host_ctxt, &res);
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
if (res.a0 == FFA_RET_NOT_SUPPORTED)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Firmware returns the maximum supported version of the FF-A
|
||||
* implementation. Check that the returned version is
|
||||
* backwards-compatible with the hyp according to the rules in DEN0077A
|
||||
* v1.1 REL0 13.2.1.
|
||||
*
|
||||
* Of course, things are never simple when dealing with firmware. v1.1
|
||||
* broke ABI with v1.0 on several structures, which is itself
|
||||
* incompatible with the aforementioned versioning scheme. The
|
||||
* expectation is that v1.x implementations that do not support the v1.0
|
||||
* ABI return NOT_SUPPORTED rather than a version number, according to
|
||||
* DEN0077A v1.1 REL0 18.6.4.
|
||||
*/
|
||||
if (FFA_MAJOR_VERSION(res.a0) != 1)
|
||||
return -EOPNOTSUPP;
|
||||
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)
|
||||
|
|
@ -754,6 +676,199 @@ int hyp_ffa_init(void *pages)
|
|||
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;
|
||||
|
||||
/*
|
||||
* There's no way we can tell what a non-standard SMC call might
|
||||
* be up to. Ideally, we would terminate these here and return
|
||||
* an error to the host, but sadly devices make use of custom
|
||||
* firmware calls for things like power management, debugging,
|
||||
* RNG access and crash reporting.
|
||||
*
|
||||
* Given that the architecture requires us to trust EL3 anyway,
|
||||
* we forward unrecognised calls on under the assumption that
|
||||
* the firmware doesn't expose a mechanism to access arbitrary
|
||||
* non-secure memory. Short of a per-device table of SMCs, this
|
||||
* is the best we can do.
|
||||
*/
|
||||
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))
|
||||
return false;
|
||||
goto out_handled;
|
||||
/* Memory management */
|
||||
case FFA_FN64_RXTX_MAP:
|
||||
do_ffa_rxtx_map(&res, host_ctxt);
|
||||
goto out_handled;
|
||||
case FFA_RXTX_UNMAP:
|
||||
do_ffa_rxtx_unmap(&res, host_ctxt);
|
||||
goto out_handled;
|
||||
case FFA_MEM_SHARE:
|
||||
case FFA_FN64_MEM_SHARE:
|
||||
do_ffa_mem_xfer(FFA_FN64_MEM_SHARE, &res, host_ctxt);
|
||||
goto out_handled;
|
||||
case FFA_MEM_RECLAIM:
|
||||
do_ffa_mem_reclaim(&res, host_ctxt);
|
||||
goto out_handled;
|
||||
case FFA_MEM_LEND:
|
||||
case FFA_FN64_MEM_LEND:
|
||||
do_ffa_mem_xfer(FFA_FN64_MEM_LEND, &res, host_ctxt);
|
||||
goto out_handled;
|
||||
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))
|
||||
return false; /* Pass through */
|
||||
|
||||
ffa_to_smccc_error(&res, FFA_RET_NOT_SUPPORTED);
|
||||
out_handled:
|
||||
ffa_set_retval(host_ctxt, &res);
|
||||
return true;
|
||||
}
|
||||
|
||||
int hyp_ffa_init(void *pages)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
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_1, 0, 0, 0, 0, 0, 0, &res);
|
||||
if (res.a0 == FFA_RET_NOT_SUPPORTED)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Firmware returns the maximum supported version of the FF-A
|
||||
* implementation. Check that the returned version is
|
||||
* backwards-compatible with the hyp according to the rules in DEN0077A
|
||||
* v1.1 REL0 13.2.1.
|
||||
*
|
||||
* Of course, things are never simple when dealing with firmware. v1.1
|
||||
* broke ABI with v1.0 on several structures, which is itself
|
||||
* incompatible with the aforementioned versioning scheme. The
|
||||
* expectation is that v1.x implementations that do not support the v1.0
|
||||
* ABI return NOT_SUPPORTED rather than a version number, according to
|
||||
* DEN0077A v1.1 REL0 18.6.4.
|
||||
*/
|
||||
if (FFA_MAJOR_VERSION(res.a0) != 1)
|
||||
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;
|
||||
rx = pages;
|
||||
|
|
@ -775,5 +890,6 @@ int hyp_ffa_init(void *pages)
|
|||
.lock = __HYP_SPIN_LOCK_UNLOCKED,
|
||||
};
|
||||
|
||||
version_lock = __HYP_SPIN_LOCK_UNLOCKED;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user