From ff53ea39e12e4cc664fe2a7ce8fe7a1bd5d77b17 Mon Sep 17 00:00:00 2001 From: Sebastian Ene Date: Thu, 7 Nov 2024 15:52:09 +0000 Subject: [PATCH] ANDROID: KVM: arm64: Introduce KVM_CAP_ARM_PROTECTED_VM_FLAGS_SET_FFA uapi Restrict protected VMs from being able to talk to Trustzone unless userspace enables the capability. Add the option of getting the hypervisor negotiated FF-A version. Only processes having CAP_IPC_OWNER will be allowed to issue this flag. Bug: 269285339 Bug: 278749606 Change-Id: I9f8adb47b03f076f17b32fdb280d5531b4578e25 Signed-off-by: Sebastian Ene --- arch/arm64/include/asm/kvm_asm.h | 1 + arch/arm64/include/asm/kvm_host.h | 1 + arch/arm64/include/uapi/asm/kvm.h | 5 ++- arch/arm64/kvm/hyp/include/nvhe/ffa.h | 1 + arch/arm64/kvm/hyp/nvhe/ffa.c | 23 +++++++++++++ arch/arm64/kvm/hyp/nvhe/hyp-main.c | 6 ++++ arch/arm64/kvm/hyp/nvhe/pkvm.c | 1 + arch/arm64/kvm/pkvm.c | 47 +++++++++++++++++++++++++++ 8 files changed, 84 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h index 1fad502b8c48..38823ff6067f 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -129,6 +129,7 @@ enum __kvm_host_smccc_func { __KVM_HOST_SMCCC_FUNC___pkvm_host_donate_hyp_mmio, __KVM_HOST_SMCCC_FUNC___pkvm_host_reclaim_hyp_mmio, __KVM_HOST_SMCCC_FUNC___pkvm_host_map_guest_mmio, + __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 58d1283584b9..13eca94e88a5 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -297,6 +297,7 @@ struct kvm_protected_vm { struct rb_root_cached pinned_pages; 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 9e26634c7d49..85b1b7d774d6 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/ffa.h +++ b/arch/arm64/kvm/hyp/include/nvhe/ffa.h @@ -29,5 +29,6 @@ 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); #endif /* __KVM_HYP_FFA_H */ diff --git a/arch/arm64/kvm/hyp/nvhe/ffa.c b/arch/arm64/kvm/hyp/nvhe/ffa.c index 5ed2c9baccee..b5bbded1699a 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 @@ -1190,6 +1192,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); @@ -1291,6 +1299,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; @@ -1318,6 +1329,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 642a8a8c05e1..1de4cbc4a594 100644 --- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c +++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c @@ -1881,6 +1881,11 @@ out: cpu_reg(host_ctxt, 1) = ret; } +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 @@ -1960,6 +1965,7 @@ static const hcall_t host_hcall[] = { HANDLE_FUNC(__pkvm_host_donate_hyp_mmio), HANDLE_FUNC(__pkvm_host_reclaim_hyp_mmio), HANDLE_FUNC(__pkvm_host_map_guest_mmio), + 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 239ad9281dca..f7883f62f715 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -608,6 +608,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 d09d72d2ab4e..13f054d7f315 100644 --- a/arch/arm64/kvm/pkvm.c +++ b/arch/arm64/kvm/pkvm.c @@ -828,16 +828,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)) @@ -851,6 +896,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; }