From 079b8f1017d835a706eb3b38e3fb2be96a170f53 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Mon, 14 Feb 2022 18:13:49 +0000 Subject: [PATCH] ANDROID: KVM: arm64: iommu: Avoid mapping devices in host stage-2 Introduce a linked list of IOMMU devices and 'pkvm_iommu_host_stage2_adjust_range' called from host DABT handler. The function will adjust the memory range that is about to be mapped to avoid MMIO regions of all devices in the linked list. If the host tried to access a device MMIO region, the access is declined. The function replaces the existing call to 'kvm_iommu.ops.host_stage2_adjust_mmio_range' callback. Bug: 190463801 Change-Id: Iacd6b74147fea2fef04846a91f0a5e550daaf074 Signed-off-by: David Brazdil (cherry picked from commit d7adab5f9fcb7f7efd76bdbc23a1b37322156112) Signed-off-by: Mostafa Saleh --- arch/arm64/kvm/hyp/include/nvhe/iommu.h | 8 +++++ arch/arm64/kvm/hyp/nvhe/iommu.c | 41 +++++++++++++++++++++++++ arch/arm64/kvm/hyp/nvhe/mem_protect.c | 12 ++++---- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/nvhe/iommu.h b/arch/arm64/kvm/hyp/include/nvhe/iommu.h index b0a27f2ee9b7..54f5ce5976d7 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/iommu.h +++ b/arch/arm64/kvm/hyp/include/nvhe/iommu.h @@ -18,7 +18,15 @@ struct pkvm_iommu_ops { int (*init)(void *data, size_t size); }; +struct pkvm_iommu { + struct list_head list; + phys_addr_t pa; + size_t size; +}; + int __pkvm_iommu_driver_init(enum pkvm_iommu_driver_id id, void *data, size_t size); +int pkvm_iommu_host_stage2_adjust_range(phys_addr_t addr, phys_addr_t *start, + phys_addr_t *end); struct kvm_iommu_ops { int (*init)(void); diff --git a/arch/arm64/kvm/hyp/nvhe/iommu.c b/arch/arm64/kvm/hyp/nvhe/iommu.c index 3bf76be92821..f2b510fd2727 100644 --- a/arch/arm64/kvm/hyp/nvhe/iommu.c +++ b/arch/arm64/kvm/hyp/nvhe/iommu.c @@ -25,6 +25,14 @@ struct pkvm_iommu_driver { static struct pkvm_iommu_driver iommu_drivers[PKVM_IOMMU_NR_DRIVERS]; +/* IOMMU device list. Must only be accessed with host_mmu.lock held. */ +static LIST_HEAD(iommu_list); + +static void assert_host_component_locked(void) +{ + hyp_assert_lock_held(&host_mmu.lock); +} + /* * Find IOMMU driver by its ID. The input ID is treated as unstrusted * and is properly validated. @@ -101,3 +109,36 @@ out: driver_release_init(drv, /*success=*/!ret); return ret; } + +/* + * Check host memory access against IOMMUs' MMIO regions. + * Returns -EPERM if the address is within the bounds of a registered device. + * Otherwise returns zero and adjusts boundaries of the new mapping to avoid + * MMIO regions of registered IOMMUs. + */ +int pkvm_iommu_host_stage2_adjust_range(phys_addr_t addr, phys_addr_t *start, + phys_addr_t *end) +{ + struct pkvm_iommu *dev; + phys_addr_t new_start = *start; + phys_addr_t new_end = *end; + phys_addr_t dev_start, dev_end; + + assert_host_component_locked(); + + list_for_each_entry(dev, &iommu_list, list) { + dev_start = dev->pa; + dev_end = dev_start + dev->size; + + if (addr < dev_start) + new_end = min(new_end, dev_start); + else if (addr >= dev_end) + new_start = max(new_start, dev_end); + else + return -EPERM; + } + + *start = new_start; + *end = new_end; + return 0; +} diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 3faf77a513d9..3711f626fb4a 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -625,13 +625,13 @@ static int host_stage2_idmap(u64 addr) prot = is_memory ? PKVM_HOST_MEM_PROT : PKVM_HOST_MMIO_PROT; - /** - * Let device drivers adjust the permitted range first. - * host_stage2_adjust_range() should be last to also properly align it. + /* + * Adjust against IOMMU devices first. host_stage2_adjust_range() should + * be called last for proper alignment. */ - if (!is_memory && kvm_iommu_ops.host_stage2_adjust_mmio_range) { - ret = kvm_iommu_ops.host_stage2_adjust_mmio_range(addr, &range.start, - &range.end); + if (!is_memory) { + ret = pkvm_iommu_host_stage2_adjust_range(addr, &range.start, + &range.end); if (ret) return ret; }