diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h index e7895068a5ac..64d0a9d5da86 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -131,6 +131,7 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___pkvm_host_map_guest_mmio, __KVM_HOST_SMCCC_FUNC___pkvm_pviommu_attach, __KVM_HOST_SMCCC_FUNC___pkvm_pviommu_add_vsid, + __KVM_HOST_SMCCC_FUNC___pkvm_host_get_ffa_version, /* * Start of the dynamically registered hypercalls. Start a bit diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 9add6d0de1dc..549731b8709b 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -298,6 +298,7 @@ struct kvm_protected_vm { struct kvm_hyp_memcache teardown_iommu_mc; gpa_t pvmfw_load_addr; bool enabled; + u32 ffa_support; }; struct kvm_mpidr_data { diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h index 8281385e9197..0653b527f452 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -480,10 +480,13 @@ enum { /* Protected KVM */ #define KVM_CAP_ARM_PROTECTED_VM_FLAGS_SET_FW_IPA 0 #define KVM_CAP_ARM_PROTECTED_VM_FLAGS_INFO 1 +#define KVM_CAP_ARM_PROTECTED_VM_FLAGS_SET_FFA 2 struct kvm_protected_vm_info { __u64 firmware_size; - __u64 __reserved[7]; + __u32 ffa_version; + __u32 pad; + __u64 __reserved[6]; }; /* arm64-specific kvm_run::system_event flags */ diff --git a/arch/arm64/kvm/hyp/include/nvhe/ffa.h b/arch/arm64/kvm/hyp/include/nvhe/ffa.h index 681588113131..ab010dfd1717 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/ffa.h +++ b/arch/arm64/kvm/hyp/include/nvhe/ffa.h @@ -29,6 +29,7 @@ bool kvm_host_ffa_handler(struct kvm_cpu_context *host_ctxt, u32 func_id); bool kvm_guest_ffa_handler(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code); struct ffa_mem_transfer *find_transfer_by_handle(u64 ffa_handle, struct kvm_ffa_buffers *buf); int kvm_dying_guest_reclaim_ffa_resources(struct pkvm_hyp_vm *vm); +u32 ffa_get_hypervisor_version(void); static inline bool is_ffa_call(u64 func_id) { diff --git a/arch/arm64/kvm/hyp/nvhe/ffa.c b/arch/arm64/kvm/hyp/nvhe/ffa.c index c46aceb5c8dd..81083856d9cf 100644 --- a/arch/arm64/kvm/hyp/nvhe/ffa.c +++ b/arch/arm64/kvm/hyp/nvhe/ffa.c @@ -39,6 +39,8 @@ #include #include +#define VM_FFA_SUPPORTED(vcpu) ((vcpu)->kvm->arch.pkvm.ffa_support) + /* * A buffer to hold the maximum descriptor size we can see from the host, * which is required when the SPMD returns a fragmented FFA_MEM_RETRIEVE_RESP @@ -1187,6 +1189,12 @@ bool kvm_guest_ffa_handler(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code) return true; } + if (!VM_FFA_SUPPORTED(vcpu)) { + ffa_to_smccc_error(&res, FFA_RET_NOT_SUPPORTED); + ffa_set_retval(ctxt, &res); + return true; + } + switch (func_id) { case FFA_FEATURES: do_ffa_guest_features(&res, ctxt); @@ -1293,6 +1301,9 @@ int kvm_dying_guest_reclaim_ffa_resources(struct pkvm_hyp_vm *vm) struct ffa_mem_transfer *transfer; int ret = 0; + if (!vm->kvm.arch.pkvm.ffa_support) + return 0; + hyp_spin_lock(&kvm_ffa_hyp_lock); if (!ffa_buf->tx && !ffa_buf->rx) goto unlock; @@ -1320,6 +1331,18 @@ unlock: return ret; } +u32 ffa_get_hypervisor_version(void) +{ + u32 version = 0; + + hyp_spin_lock(&version_lock); + if (has_version_negotiated) + version = hyp_ffa_version; + hyp_spin_unlock(&version_lock); + + return version; +} + int hyp_ffa_init(void *pages) { struct arm_smccc_res res; diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c index 15e126d1ae23..e03abea3b46d 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -1901,6 +1901,11 @@ static void handle___pkvm_pviommu_add_vsid(struct kvm_cpu_context *host_ctxt) cpu_reg(host_ctxt, 1) = pkvm_pviommu_add_vsid(host_kvm, pviommu, iommu, sid, vsid); } +static void handle___pkvm_host_get_ffa_version(struct kvm_cpu_context *host_ctxt) +{ + cpu_reg(host_ctxt, 1) = ffa_get_hypervisor_version(); +} + typedef void (*hcall_t)(struct kvm_cpu_context *); #define HANDLE_FUNC(x) [__KVM_HOST_SMCCC_FUNC_##x] = (hcall_t)handle_##x @@ -1982,6 +1987,7 @@ static const hcall_t host_hcall[] = { HANDLE_FUNC(__pkvm_host_map_guest_mmio), HANDLE_FUNC(__pkvm_pviommu_attach), HANDLE_FUNC(__pkvm_pviommu_add_vsid), + HANDLE_FUNC(__pkvm_host_get_ffa_version), }; static void handle_host_hcall(struct kvm_cpu_context *host_ctxt) diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index 532b1c154b1f..04a8454fdd68 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -610,6 +610,7 @@ static void init_pkvm_hyp_vm(struct kvm *host_kvm, struct pkvm_hyp_vm *hyp_vm, pvmfw_load_addr = READ_ONCE(host_kvm->arch.pkvm.pvmfw_load_addr); hyp_vm->kvm.arch.pkvm.pvmfw_load_addr = pvmfw_load_addr; + hyp_vm->kvm.arch.pkvm.ffa_support = READ_ONCE(host_kvm->arch.pkvm.ffa_support); hyp_vm->kvm.arch.mmu.last_vcpu_ran = (int __percpu *)last_ran; memset(last_ran, -1, pkvm_get_last_ran_size()); pkvm_init_features_from_host(hyp_vm, host_kvm); diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c index d1399e6d6c38..c0955191e519 100644 --- a/arch/arm64/kvm/pkvm.c +++ b/arch/arm64/kvm/pkvm.c @@ -832,16 +832,61 @@ out_unlock: return ret; } +static u32 pkvm_get_ffa_version(void) +{ + static u32 ffa_version; + u32 ret; + + ret = READ_ONCE(ffa_version); + if (ret) + return ret; + + ret = kvm_call_hyp_nvhe(__pkvm_host_get_ffa_version); + WRITE_ONCE(ffa_version, ret); + return ret; + +} + static int pkvm_vm_ioctl_info(struct kvm *kvm, struct kvm_protected_vm_info __user *info) { struct kvm_protected_vm_info kinfo = { .firmware_size = pvmfw_size, + .ffa_version = pkvm_get_ffa_version(), }; return copy_to_user(info, &kinfo, sizeof(kinfo)) ? -EFAULT : 0; } +static int pkvm_vm_ioctl_ffa_support(struct kvm *kvm, u32 enable) +{ + int ret = 0; + u32 ffa_version; + + /* Restrict userspace from having an IPC channel over FF-A with secure */ + if (!capable(CAP_IPC_OWNER)) + return -EPERM; + + /* + * If the host hasn't negotiated a version don't enable the + * FF-A capability. + */ + ffa_version = pkvm_get_ffa_version(); + if (!ffa_version) + return -EINVAL; + + mutex_lock(&kvm->arch.config_lock); + if (kvm->arch.pkvm.handle) { + ret = -EBUSY; + goto out_unlock; + } + + kvm->arch.pkvm.ffa_support = enable; +out_unlock: + mutex_unlock(&kvm->arch.config_lock); + return ret; +} + int pkvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap) { if (!kvm_vm_is_protected(kvm)) @@ -855,6 +900,8 @@ int pkvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap) return pkvm_vm_ioctl_set_fw_ipa(kvm, cap->args[0]); case KVM_CAP_ARM_PROTECTED_VM_FLAGS_INFO: return pkvm_vm_ioctl_info(kvm, (void __force __user *)cap->args[0]); + case KVM_CAP_ARM_PROTECTED_VM_FLAGS_SET_FFA: + return pkvm_vm_ioctl_ffa_support(kvm, cap->args[0]); default: return -EINVAL; }