From eb41d1857424a7afa65d4a10d1a52f7451553df0 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 9 Dec 2021 13:15:41 +0000 Subject: [PATCH] ANDROID: KVM: arm64: Introduce KVM_CAP_ARM_PROTECTED_VM to set/query PVM firmware Expose a new capability, KVM_CAP_ARM_PROTECTED_VM, for protected VMs which allows the size of the PVM firmware region to be discovered from userspace and for the firmware load address to be specified if it is required. Signed-off-by: Will Deacon Bug: 209580772 Change-Id: I819b9b2cfa227f1a0607a8f683aa01d4ae50704f Signed-off-by: Will Deacon --- arch/arm64/include/asm/kvm_pkvm.h | 1 + arch/arm64/include/uapi/asm/kvm.h | 9 ++++++ arch/arm64/kvm/arm.c | 17 +++++++---- arch/arm64/kvm/hyp/nvhe/pkvm.c | 2 +- arch/arm64/kvm/pkvm.c | 48 +++++++++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 6 deletions(-) diff --git a/arch/arm64/include/asm/kvm_pkvm.h b/arch/arm64/include/asm/kvm_pkvm.h index b46ea822225b..447f852f2b2d 100644 --- a/arch/arm64/include/asm/kvm_pkvm.h +++ b/arch/arm64/include/asm/kvm_pkvm.h @@ -17,6 +17,7 @@ #define HYP_MEMBLOCK_REGIONS 128 #define PVMFW_INVALID_LOAD_ADDR (-1) +int kvm_arm_vm_ioctl_pkvm(struct kvm *kvm, struct kvm_enable_cap *cap); int kvm_init_pvm(struct kvm *kvm, unsigned long type); int create_el2_shadow(struct kvm *kvm); diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h index b3edde68bc3e..aebd2fa75f12 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -413,6 +413,15 @@ struct kvm_arm_copy_mte_tags { #define KVM_PSCI_RET_INVAL PSCI_RET_INVALID_PARAMS #define KVM_PSCI_RET_DENIED PSCI_RET_DENIED +/* Protected KVM */ +#define KVM_CAP_ARM_PROTECTED_VM_FLAGS_SET_FW_IPA 0 +#define KVM_CAP_ARM_PROTECTED_VM_FLAGS_INFO 1 + +struct kvm_protected_vm_info { + __u64 firmware_size; + __u64 __reserved[7]; +}; + #endif #endif /* __ARM_KVM_H__ */ diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 76af06182e45..f8b9ad14a750 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -88,9 +88,16 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, { int r; - if (cap->flags) - return -EINVAL; + /* Capabilities with flags */ + switch (cap->cap) { + case KVM_CAP_ARM_PROTECTED_VM: + return kvm_arm_vm_ioctl_pkvm(kvm, cap); + default: + if (cap->flags) + return -EINVAL; + } + /* Capabilities without flags */ switch (cap->cap) { case KVM_CAP_ARM_NISV_TO_USER: r = 0; @@ -107,9 +114,6 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, mutex_unlock(&kvm->lock); break; case KVM_CAP_EXIT_HYPERCALL: - if (cap->flags) - return -EINVAL; - if (cap->args[0] & ~KVM_EXIT_HYPERCALL_VALID_MASK) return -EINVAL; @@ -390,6 +394,9 @@ static int pkvm_check_extension(struct kvm *kvm, long ext, int kvm_cap) FIELD_GET(ARM64_FEATURE_MASK(ID_AA64ISAR1_GPA), PVM_ID_AA64ISAR1_ALLOW); break; + case KVM_CAP_ARM_PROTECTED_VM: + r = 1; + break; default: r = 0; break; diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c index 995825157996..d7d3155b039f 100644 --- a/arch/arm64/kvm/hyp/nvhe/pkvm.c +++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c @@ -356,7 +356,7 @@ static int init_shadow_structs(struct kvm *kvm, struct kvm_shadow_vm *vm, int nr vm->host_kvm = kvm; vm->created_vcpus = 0; - vm->arch.pkvm.pvmfw_load_addr = PVMFW_INVALID_LOAD_ADDR; + vm->arch.pkvm.pvmfw_load_addr = kvm->arch.pkvm.pvmfw_load_addr; for (i = 0; i < nr_vcpus; i++) { struct kvm_vcpu *host_vcpu = kern_hyp_va(kvm->vcpus[i]); diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c index dc60c8d60565..9bda3f033a31 100644 --- a/arch/arm64/kvm/pkvm.c +++ b/arch/arm64/kvm/pkvm.c @@ -245,6 +245,54 @@ static int __init pkvm_firmware_rmem_clear(void) } device_initcall_sync(pkvm_firmware_rmem_clear); +static int pkvm_vm_ioctl_set_fw_ipa(struct kvm *kvm, u64 ipa) +{ + int ret = 0; + + if (!pkvm_firmware_mem) + return -EINVAL; + + mutex_lock(&kvm->arch.pkvm.shadow_lock); + if (kvm->arch.pkvm.shadow_handle) { + ret = -EBUSY; + goto out_unlock; + } + + kvm->arch.pkvm.pvmfw_load_addr = ipa; +out_unlock: + mutex_unlock(&kvm->arch.pkvm.shadow_lock); + 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 = pkvm_firmware_mem ? + pkvm_firmware_mem->size : + 0, + }; + + return copy_to_user(info, &kinfo, sizeof(kinfo)) ? -EFAULT : 0; +} + +int kvm_arm_vm_ioctl_pkvm(struct kvm *kvm, struct kvm_enable_cap *cap) +{ + if (cap->args[1] || cap->args[2] || cap->args[3]) + return -EINVAL; + + switch (cap->flags) { + case KVM_CAP_ARM_PROTECTED_VM_FLAGS_SET_FW_IPA: + 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]); + default: + return -EINVAL; + } + + return 0; +} + int kvm_init_pvm(struct kvm *kvm, unsigned long type) { mutex_init(&kvm->arch.pkvm.shadow_lock);