mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 02:50:49 +09:00
ANDROID: KVM: arm64: iommu: DABT handler callback
Replace the 'host_mmio_dabt_handler' hook in kvm_iommu_ops with
an equivalent callback in the new pkvm_iommu_ops. The generic portion
of the code finds the IOMMU device at the faulted address and invokes
the callback on it.
Bug: 190463801
Change-Id: I0ca008c3e1ae0ec12a259fa4ddac1aee65aaac5c
Signed-off-by: David Brazdil <dbrazdil@google.com>
(cherry picked from commit 5df451f35e)
Signed-off-by: Mostafa Saleh <smostafa@google.com>
This commit is contained in:
committed by
Mostafa Saleh
parent
770ec56a17
commit
3f07551fa6
@@ -29,6 +29,14 @@ struct pkvm_iommu_ops {
|
||||
int (*suspend)(struct pkvm_iommu *dev);
|
||||
int (*resume)(struct pkvm_iommu *dev);
|
||||
|
||||
/*
|
||||
* Host data abort handler callback. Called with host lock held.
|
||||
* Returns true if the data abort has been handled.
|
||||
*/
|
||||
bool (*host_dabt_handler)(struct pkvm_iommu *dev,
|
||||
struct kvm_cpu_context *host_ctxt,
|
||||
u32 esr, size_t off);
|
||||
|
||||
/* Amount of memory allocated per-device for use by the driver. */
|
||||
size_t data_size;
|
||||
};
|
||||
@@ -53,6 +61,8 @@ int __pkvm_iommu_pm_notify(unsigned long dev_id,
|
||||
enum pkvm_iommu_pm_event event);
|
||||
int pkvm_iommu_host_stage2_adjust_range(phys_addr_t addr, phys_addr_t *start,
|
||||
phys_addr_t *end);
|
||||
bool pkvm_iommu_host_dabt_handler(struct kvm_cpu_context *host_ctxt, u32 esr,
|
||||
phys_addr_t fault_pa);
|
||||
|
||||
struct kvm_iommu_ops {
|
||||
int (*init)(void);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <asm/kvm_mmu.h>
|
||||
#include <asm/kvm_pkvm.h>
|
||||
|
||||
#include <hyp/adjust_pc.h>
|
||||
#include <nvhe/iommu.h>
|
||||
#include <nvhe/mm.h>
|
||||
|
||||
@@ -358,3 +359,24 @@ int pkvm_iommu_host_stage2_adjust_range(phys_addr_t addr, phys_addr_t *start,
|
||||
*end = new_end;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool pkvm_iommu_host_dabt_handler(struct kvm_cpu_context *host_ctxt, u32 esr,
|
||||
phys_addr_t pa)
|
||||
{
|
||||
struct pkvm_iommu *dev;
|
||||
|
||||
assert_host_component_locked();
|
||||
|
||||
list_for_each_entry(dev, &iommu_list, list) {
|
||||
if (pa < dev->pa || pa >= dev->pa + dev->size)
|
||||
continue;
|
||||
|
||||
if (!dev->powered || !dev->ops->host_dabt_handler ||
|
||||
!dev->ops->host_dabt_handler(dev, host_ctxt, esr, pa - dev->pa))
|
||||
return false;
|
||||
|
||||
kvm_skip_host_instr();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -630,9 +630,9 @@ static int host_stage2_idmap(u64 addr)
|
||||
enum kvm_pgtable_prot prot;
|
||||
int ret;
|
||||
|
||||
prot = is_memory ? PKVM_HOST_MEM_PROT : PKVM_HOST_MMIO_PROT;
|
||||
hyp_assert_lock_held(&host_mmu.lock);
|
||||
|
||||
host_lock_component();
|
||||
prot = is_memory ? PKVM_HOST_MEM_PROT : PKVM_HOST_MMIO_PROT;
|
||||
/*
|
||||
* Adjust against IOMMU devices first. host_stage2_adjust_range() should
|
||||
* be called last for proper alignment.
|
||||
@@ -641,18 +641,14 @@ static int host_stage2_idmap(u64 addr)
|
||||
ret = pkvm_iommu_host_stage2_adjust_range(addr, &range.start,
|
||||
&range.end);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = host_stage2_adjust_range(addr, &range);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
return ret;
|
||||
|
||||
ret = host_stage2_idmap_locked(range.start, range.end - range.start, prot);
|
||||
unlock:
|
||||
host_unlock_component();
|
||||
|
||||
return ret;
|
||||
return host_stage2_idmap_locked(range.start, range.end - range.start, prot);
|
||||
}
|
||||
|
||||
static void host_inject_abort(struct kvm_cpu_context *host_ctxt)
|
||||
@@ -699,26 +695,6 @@ 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;
|
||||
@@ -736,14 +712,19 @@ void handle_host_mem_abort(struct kvm_cpu_context *host_ctxt)
|
||||
addr = (fault.hpfar_el2 & HPFAR_MASK) << 8;
|
||||
addr |= fault.far_el2 & FAR_MASK;
|
||||
|
||||
/* 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);
|
||||
host_lock_component();
|
||||
|
||||
/* Check if an IOMMU device can handle the DABT. */
|
||||
if (is_dabt(esr) && !addr_is_memory(addr) &&
|
||||
pkvm_iommu_host_dabt_handler(host_ctxt, esr, addr))
|
||||
ret = 0;
|
||||
|
||||
/* If not handled, attempt to map the page. */
|
||||
if (ret == -EPERM)
|
||||
ret = host_stage2_idmap(addr);
|
||||
|
||||
host_unlock_component();
|
||||
|
||||
if (ret == -EPERM)
|
||||
host_inject_abort(host_ctxt);
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user