From 90565c9e2c0ac83fbfc828e15a0e7e26d3fda83f Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Thu, 12 Dec 2024 18:03:48 +0000 Subject: [PATCH] BACKPORT: FROMLIST: KVM: arm64: iommu: Support DABT for IOMMU Soon, SMMUv3 driver would be added and it would need to emulate access to some of its MMIO space. Add a handler for DABTs for IOMMU drivers to be able to do so. Link: https://lore.kernel.org/all/20241212180423.1578358-25-smostafa@google.com/ Bug: 357781595 Bug: 384432312 Change-Id: I1d55aec4b7bc37c87834c70b15ab288e2de42b5e Signed-off-by: Mostafa Saleh --- arch/arm64/kvm/hyp/include/nvhe/iommu.h | 2 ++ arch/arm64/kvm/hyp/nvhe/iommu/iommu.c | 17 +++++++++++++++++ arch/arm64/kvm/hyp/nvhe/mem_protect.c | 19 +++++++++++++++++-- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/nvhe/iommu.h b/arch/arm64/kvm/hyp/include/nvhe/iommu.h index 06d12b35fa3e..cff75d67d807 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/iommu.h +++ b/arch/arm64/kvm/hyp/include/nvhe/iommu.h @@ -21,6 +21,7 @@ size_t kvm_iommu_map_pages(pkvm_handle_t domain_id, size_t kvm_iommu_unmap_pages(pkvm_handle_t domain_id, unsigned long iova, size_t pgsize, size_t pgcount); phys_addr_t kvm_iommu_iova_to_phys(pkvm_handle_t domain_id, unsigned long iova); +bool kvm_iommu_host_dabt_handler(struct kvm_cpu_context *host_ctxt, u64 esr, u64 addr); /* Flags for memory allocation for IOMMU drivers */ #define IOMMU_PAGE_NOCACHE BIT(0) @@ -49,6 +50,7 @@ struct kvm_iommu_ops { phys_addr_t (*iova_to_phys)(struct kvm_hyp_iommu_domain *domain, unsigned long iova); void (*iotlb_sync)(struct kvm_hyp_iommu_domain *domain, struct iommu_iotlb_gather *gather); + bool (*dabt_handler)(struct kvm_cpu_context *host_ctxt, u64 esr, u64 addr); }; int kvm_iommu_init(void); diff --git a/arch/arm64/kvm/hyp/nvhe/iommu/iommu.c b/arch/arm64/kvm/hyp/nvhe/iommu/iommu.c index fbab335d3490..e45dadd0c4aa 100644 --- a/arch/arm64/kvm/hyp/nvhe/iommu/iommu.c +++ b/arch/arm64/kvm/hyp/nvhe/iommu/iommu.c @@ -4,6 +4,10 @@ * * Copyright (C) 2022 Linaro Ltd. */ +#include + +#include + #include #include @@ -375,6 +379,19 @@ phys_addr_t kvm_iommu_iova_to_phys(pkvm_handle_t domain_id, unsigned long iova) return phys; } +bool kvm_iommu_host_dabt_handler(struct kvm_cpu_context *host_ctxt, u64 esr, u64 addr) +{ + bool ret = false; + + if (kvm_iommu_ops && kvm_iommu_ops->dabt_handler) + ret = kvm_iommu_ops->dabt_handler(host_ctxt, esr, addr); + + if (ret) + kvm_skip_host_instr(); + + return ret; +} + static int iommu_power_on(struct kvm_power_domain *pd) { struct kvm_hyp_iommu *iommu = container_of(pd, struct kvm_hyp_iommu, diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 85f497e80a96..154ca6d6973b 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -828,11 +829,16 @@ static int handle_host_perm_fault(struct kvm_cpu_context *host_ctxt, u64 esr, u6 return handled ? 0 : -EPERM; } +static bool is_dabt(u64 esr) +{ + return ESR_ELx_EC(esr) == ESR_ELx_EC_DABT_LOW; +} + void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt) { struct kvm_vcpu_fault_info fault; u64 esr, addr; - int ret = 0; + int ret = -EPERM; esr = read_sysreg_el2(SYS_ESR); if (!__get_fault_info(esr, &fault)) { @@ -846,7 +852,15 @@ void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt) } addr = (fault.hpfar_el2 & HPFAR_MASK) << 8; - ret = host_stage2_idmap(addr); + addr |= fault.far_el2 & FAR_MASK; + + if (is_dabt(esr) && !addr_is_memory(addr) && + kvm_iommu_host_dabt_handler(host_ctxt, esr, addr)) + goto return_to_host; + + /* If not handled, attempt to map the page. */ + if (ret == -EPERM) + ret = host_stage2_idmap(addr); if ((esr & ESR_ELx_FSC_TYPE) == ESR_ELx_FSC_PERM) ret = handle_host_perm_fault(host_ctxt, esr, addr); @@ -856,6 +870,7 @@ void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt) else BUG_ON(ret && ret != -EAGAIN); +return_to_host: trace_host_mem_abort(esr, addr); }