From 25f81ec77b8915f45ba2d2c393cba37788b6eef8 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Thu, 28 Oct 2021 14:26:18 +0100 Subject: [PATCH] ANDROID: KVM: arm64: Add 'host_mmio_dabt_handler' to kvm_iommu_ops Add a new kvm_iommu_ops hook which allows the IOMMU driver to handle data aborts in unmapped device memory regions. If the abort is handled by the driver, the global abort handler will not attempt to map in the page. For example, this enables the IOMMU driver to virtualize access to the underlying IOMMU hardware, or to allow access to a subset of the functionality, eg. performance counters. Test: builds, boots Bug: 190463801 Signed-off-by: David Brazdil Change-Id: Ie2e38652f0568c27d30190fcc3879592872863ae --- arch/arm64/include/asm/kvm_hyp.h | 3 +++ arch/arm64/kvm/hyp/nvhe/mem_protect.c | 38 +++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index fd60175da295..d5148d006060 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -127,6 +127,9 @@ extern u64 kvm_nvhe_sym(id_aa64mmfr2_el1_sys_val); struct kvm_iommu_ops { int (*init)(void); bool (*host_smc_handler)(struct kvm_cpu_context *host_ctxt); + bool (*host_mmio_dabt_handler)(struct kvm_cpu_context *host_ctxt, + phys_addr_t fault_pa, unsigned int len, + bool is_write, int rd); void (*host_stage2_set_owner)(phys_addr_t addr, size_t size, u8 owner_id); }; diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 230f4f7456ad..badd206be6dc 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -445,16 +446,49 @@ unlock: return ret; } +static int host_mmio_dabt_handler(struct kvm_cpu_context *host_ctxt, u32 esr, + phys_addr_t addr) +{ + bool wnr = esr & ESR_ELx_WNR; + unsigned int len = BIT((esr & ESR_ELx_SAS) >> ESR_ELx_SAS_SHIFT); + int rd = (esr & ESR_ELx_SRT_MASK) >> ESR_ELx_SRT_SHIFT; + bool handled = false; + + if (kvm_iommu_ops.host_mmio_dabt_handler) { + handled = kvm_iommu_ops.host_mmio_dabt_handler(host_ctxt, addr, + len, wnr, rd); + } + + if (!handled) + return -EPERM; + + kvm_skip_host_instr(); + return 0; +} + +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); BUG_ON(!__get_fault_info(esr, &fault)); addr = (fault.hpfar_el2 & HPFAR_MASK) << 8; - ret = host_stage2_idmap(addr); + + /* See if any subsystem can handle this abort. */ + if (is_dabt(esr) && !addr_is_memory(addr)) + ret = host_mmio_dabt_handler(host_ctxt, esr, addr); + + /* If not handled, attempt to map the page. */ + if (ret == -EPERM) + ret = host_stage2_idmap(addr); + BUG_ON(ret && ret != -EAGAIN); }