From 59d406c88deed2694c09b59c65b2fe240765b347 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 Change-Id: I84adbc992e577ac6ceb09f4856e1c648df580f76 Signed-off-by: David Brazdil (cherry picked from commit 25f81ec77b8915f45ba2d2c393cba37788b6eef8) Signed-off-by: Mostafa Saleh Signed-off-by: Quentin Perret --- arch/arm64/include/asm/kvm_hyp.h | 5 +++- arch/arm64/kvm/hyp/nvhe/mem_protect.c | 37 +++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h index 61b8354e1b7d..ffed0238279f 100644 --- a/arch/arm64/include/asm/kvm_hyp.h +++ b/arch/arm64/include/asm/kvm_hyp.h @@ -134,7 +134,10 @@ extern bool kvm_nvhe_sym(smccc_trng_available); struct kvm_iommu_ops { int (*init)(void); bool (*host_smc_handler)(struct kvm_cpu_context *host_ctxt); - void (*host_stage2_set_owner)(phys_addr_t addr, size_t size, u8 owner_id); + 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, u32 owner_id); }; extern struct kvm_iommu_ops kvm_iommu_ops; diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 5ec7b76657b5..eb6fe11362d4 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -679,17 +680,49 @@ static void host_inject_abort(struct kvm_cpu_context *host_ctxt) write_sysreg_el2(spsr, SYS_SPSR); } +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); if (ret == -EPERM) host_inject_abort(host_ctxt);