From 8a9430e18b890b03a13f171c55aec9a70b810a46 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Fri, 7 Apr 2023 21:05:51 +0000 Subject: [PATCH] ANDROID: KVM: arm64: pviommu: Add attach/detach_dev() guest HVC ops Add attach/detach_dev() guest HVC ops implementation. If -ENOMEM returned, the HVC is retried but first it returns to host to refill the vcpu IOMMU mem cache. Bug: 357781595 Bug: 348382247 Bug: 236685427 Change-Id: I11338878857f6c4be6c756b23139e1ba8584dbdf Signed-off-by: Mostafa Saleh --- arch/arm64/kvm/hyp/include/nvhe/pkvm.h | 2 +- arch/arm64/kvm/hyp/nvhe/device/device.c | 6 +-- arch/arm64/kvm/hyp/nvhe/iommu/iommu.c | 15 ++++-- arch/arm64/kvm/hyp/nvhe/iommu/pviommu.c | 71 +++++++++++++++++++++++-- 4 files changed, 81 insertions(+), 13 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h index 47251c7f98ed..eb6855151295 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h +++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h @@ -185,7 +185,7 @@ int pkvm_init_scmi_pd(struct kvm_power_domain *pd, bool pkvm_device_request_mmio(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code); void pkvm_devices_teardown(struct pkvm_hyp_vm *vm); -int pkvm_devices_get_context(u64 iommu_id, u32 endpoint_id); +int pkvm_devices_get_context(u64 iommu_id, u32 endpoint_id, struct pkvm_hyp_vm *vm); void pkvm_devices_put_context(u64 iommu_id, u32 endpoint_id); /* diff --git a/arch/arm64/kvm/hyp/nvhe/device/device.c b/arch/arm64/kvm/hyp/nvhe/device/device.c index 48533fc02ab1..dfcc6ec221c0 100644 --- a/arch/arm64/kvm/hyp/nvhe/device/device.c +++ b/arch/arm64/kvm/hyp/nvhe/device/device.c @@ -382,7 +382,7 @@ static struct pkvm_device *pkvm_get_device_by_iommu(u64 id, u32 endpoint_id) return NULL; } -int pkvm_devices_get_context(u64 iommu_id, u32 endpoint_id) +int pkvm_devices_get_context(u64 iommu_id, u32 endpoint_id, struct pkvm_hyp_vm *vm) { struct pkvm_device *dev = pkvm_get_device_by_iommu(iommu_id, endpoint_id); int ret = 0; @@ -391,7 +391,7 @@ int pkvm_devices_get_context(u64 iommu_id, u32 endpoint_id) return 0; hyp_spin_lock(&device_spinlock); - if (dev->ctxt) + if (dev->ctxt != vm) ret = -EPERM; else hyp_refcount_inc(dev->refcount); @@ -407,9 +407,7 @@ void pkvm_devices_put_context(u64 iommu_id, u32 endpoint_id) return; hyp_spin_lock(&device_spinlock); - BUG_ON(dev->ctxt); hyp_refcount_dec(dev->refcount); - hyp_spin_unlock(&device_spinlock); } diff --git a/arch/arm64/kvm/hyp/nvhe/iommu/iommu.c b/arch/arm64/kvm/hyp/nvhe/iommu/iommu.c index 2a018ef17ba0..198952c02bba 100644 --- a/arch/arm64/kvm/hyp/nvhe/iommu/iommu.c +++ b/arch/arm64/kvm/hyp/nvhe/iommu/iommu.c @@ -336,16 +336,19 @@ int kvm_iommu_attach_dev(pkvm_handle_t iommu_id, pkvm_handle_t domain_id, int ret; struct kvm_hyp_iommu *iommu; struct kvm_hyp_iommu_domain *domain; + struct pkvm_hyp_vcpu *hyp_vcpu = __get_vcpu(); + struct pkvm_hyp_vm *vm = NULL; iommu = kvm_iommu_ops->get_iommu_by_id(iommu_id); if (!iommu) return -EINVAL; + if (hyp_vcpu) + vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu); /* - * At the moment the IOMMU in EL2 is not aware of guests and pvIOMMU - * doesn't exist yet, so all attaches come from host, this should change soon. + * Make sure device can't transition to/from VMs while in the middle of attach. */ - ret = pkvm_devices_get_context(iommu_id, endpoint_id); + ret = pkvm_devices_get_context(iommu_id, endpoint_id, vm); if (ret) return ret; @@ -370,13 +373,17 @@ int kvm_iommu_detach_dev(pkvm_handle_t iommu_id, pkvm_handle_t domain_id, int ret; struct kvm_hyp_iommu *iommu; struct kvm_hyp_iommu_domain *domain; + struct pkvm_hyp_vcpu *hyp_vcpu = __get_vcpu(); + struct pkvm_hyp_vm *vm = NULL; iommu = kvm_iommu_ops->get_iommu_by_id(iommu_id); if (!iommu) return -EINVAL; + if (hyp_vcpu) + vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu); /* See kvm_iommu_attach_dev(). */ - ret = pkvm_devices_get_context(iommu_id, endpoint_id); + ret = pkvm_devices_get_context(iommu_id, endpoint_id, vm); if (ret) return ret; diff --git a/arch/arm64/kvm/hyp/nvhe/iommu/pviommu.c b/arch/arm64/kvm/hyp/nvhe/iommu/pviommu.c index e37f3f4086a1..11d97a825d2a 100644 --- a/arch/arm64/kvm/hyp/nvhe/iommu/pviommu.c +++ b/arch/arm64/kvm/hyp/nvhe/iommu/pviommu.c @@ -6,9 +6,11 @@ #include +#include #include #include #include +#include static bool pkvm_guest_iommu_map(struct pkvm_hyp_vcpu *hyp_vcpu) { @@ -20,14 +22,75 @@ static bool pkvm_guest_iommu_unmap(struct pkvm_hyp_vcpu *hyp_vcpu) return false; } -static bool pkvm_guest_iommu_attach_dev(struct pkvm_hyp_vcpu *hyp_vcpu) +static void pkvm_pviommu_hyp_req(u64 *exit_code) { - return false; + write_sysreg_el2(read_sysreg_el2(SYS_ELR) - 4, SYS_ELR); + *exit_code = ARM_EXCEPTION_HYP_REQ; +} + +static bool pkvm_guest_iommu_attach_dev(struct pkvm_hyp_vcpu *hyp_vcpu, u64 *exit_code) +{ + int ret; + struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu; + u64 iommu_id = smccc_get_arg2(vcpu); + u64 sid = smccc_get_arg3(vcpu); + u64 pasid = smccc_get_arg4(vcpu); + u64 domain_id = smccc_get_arg5(vcpu); + u64 pasid_bits = smccc_get_arg6(vcpu); + struct pviommu_route route; + struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu); + + ret = pkvm_pviommu_get_route(vm, iommu_id, sid, &route); + if (ret) + goto out_ret; + iommu_id = route.iommu; + sid = route.sid; + + ret = kvm_iommu_attach_dev(iommu_id, domain_id, sid, pasid, pasid_bits); + if (ret == -ENOMEM) { + /* + * The driver will request memory when returning -ENOMEM, so go back to host to + * fulfill the request and repeat the HVC. + */ + pkvm_pviommu_hyp_req(exit_code); + return false; + } + +out_ret: + smccc_set_retval(vcpu, ret ? SMCCC_RET_INVALID_PARAMETER : SMCCC_RET_SUCCESS, + 0, 0, 0); + return true; } static bool pkvm_guest_iommu_detach_dev(struct pkvm_hyp_vcpu *hyp_vcpu) { - return false; + int ret; + struct kvm_vcpu *vcpu = &hyp_vcpu->vcpu; + u64 iommu_id = smccc_get_arg2(vcpu); + u64 sid = smccc_get_arg3(vcpu); + u64 pasid = smccc_get_arg4(vcpu); + u64 domain_id = smccc_get_arg5(vcpu); + struct pviommu_route route; + struct pkvm_hyp_vm *vm = pkvm_hyp_vcpu_to_hyp_vm(hyp_vcpu); + + /* MBZ */ + if (smccc_get_arg6(vcpu)) { + ret = -EINVAL; + goto out_ret; + } + + ret = pkvm_pviommu_get_route(vm, iommu_id, sid, &route); + if (ret) + goto out_ret; + iommu_id = route.iommu; + sid = route.sid; + + ret = kvm_iommu_detach_dev(iommu_id, domain_id, sid, pasid); + +out_ret: + smccc_set_retval(vcpu, ret ? SMCCC_RET_INVALID_PARAMETER : SMCCC_RET_SUCCESS, + 0, 0, 0); + return true; } static bool pkvm_guest_iommu_alloc_domain(struct pkvm_hyp_vcpu *hyp_vcpu) @@ -57,7 +120,7 @@ bool kvm_handle_pviommu_hvc(struct kvm_vcpu *vcpu, u64 *exit_code) case KVM_PVIOMMU_OP_FREE_DOMAIN: return pkvm_guest_iommu_free_domain(hyp_vcpu); case KVM_PVIOMMU_OP_ATTACH_DEV: - return pkvm_guest_iommu_attach_dev(hyp_vcpu); + return pkvm_guest_iommu_attach_dev(hyp_vcpu, exit_code); case KVM_PVIOMMU_OP_DETACH_DEV: return pkvm_guest_iommu_detach_dev(hyp_vcpu); case KVM_PVIOMMU_OP_MAP_PAGES: