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; }