162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * IOMMU API for s390 PCI devices 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2015 662306a36Sopenharmony_ci * Author(s): Gerald Schaefer <gerald.schaefer@de.ibm.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/pci.h> 1062306a36Sopenharmony_ci#include <linux/iommu.h> 1162306a36Sopenharmony_ci#include <linux/iommu-helper.h> 1262306a36Sopenharmony_ci#include <linux/sizes.h> 1362306a36Sopenharmony_ci#include <linux/rculist.h> 1462306a36Sopenharmony_ci#include <linux/rcupdate.h> 1562306a36Sopenharmony_ci#include <asm/pci_dma.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic const struct iommu_ops s390_iommu_ops; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistruct s390_domain { 2062306a36Sopenharmony_ci struct iommu_domain domain; 2162306a36Sopenharmony_ci struct list_head devices; 2262306a36Sopenharmony_ci unsigned long *dma_table; 2362306a36Sopenharmony_ci spinlock_t list_lock; 2462306a36Sopenharmony_ci struct rcu_head rcu; 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic struct s390_domain *to_s390_domain(struct iommu_domain *dom) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci return container_of(dom, struct s390_domain, domain); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic bool s390_iommu_capable(struct device *dev, enum iommu_cap cap) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci switch (cap) { 3562306a36Sopenharmony_ci case IOMMU_CAP_CACHE_COHERENCY: 3662306a36Sopenharmony_ci return true; 3762306a36Sopenharmony_ci default: 3862306a36Sopenharmony_ci return false; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic struct iommu_domain *s390_domain_alloc(unsigned domain_type) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct s390_domain *s390_domain; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (domain_type != IOMMU_DOMAIN_UNMANAGED) 4762306a36Sopenharmony_ci return NULL; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci s390_domain = kzalloc(sizeof(*s390_domain), GFP_KERNEL); 5062306a36Sopenharmony_ci if (!s390_domain) 5162306a36Sopenharmony_ci return NULL; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci s390_domain->dma_table = dma_alloc_cpu_table(GFP_KERNEL); 5462306a36Sopenharmony_ci if (!s390_domain->dma_table) { 5562306a36Sopenharmony_ci kfree(s390_domain); 5662306a36Sopenharmony_ci return NULL; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci s390_domain->domain.geometry.force_aperture = true; 5962306a36Sopenharmony_ci s390_domain->domain.geometry.aperture_start = 0; 6062306a36Sopenharmony_ci s390_domain->domain.geometry.aperture_end = ZPCI_TABLE_SIZE_RT - 1; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci spin_lock_init(&s390_domain->list_lock); 6362306a36Sopenharmony_ci INIT_LIST_HEAD_RCU(&s390_domain->devices); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return &s390_domain->domain; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void s390_iommu_rcu_free_domain(struct rcu_head *head) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct s390_domain *s390_domain = container_of(head, struct s390_domain, rcu); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci dma_cleanup_tables(s390_domain->dma_table); 7362306a36Sopenharmony_ci kfree(s390_domain); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void s390_domain_free(struct iommu_domain *domain) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct s390_domain *s390_domain = to_s390_domain(domain); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci rcu_read_lock(); 8162306a36Sopenharmony_ci WARN_ON(!list_empty(&s390_domain->devices)); 8262306a36Sopenharmony_ci rcu_read_unlock(); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci call_rcu(&s390_domain->rcu, s390_iommu_rcu_free_domain); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic void __s390_iommu_detach_device(struct zpci_dev *zdev) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct s390_domain *s390_domain = zdev->s390_domain; 9062306a36Sopenharmony_ci unsigned long flags; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (!s390_domain) 9362306a36Sopenharmony_ci return; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci spin_lock_irqsave(&s390_domain->list_lock, flags); 9662306a36Sopenharmony_ci list_del_rcu(&zdev->iommu_list); 9762306a36Sopenharmony_ci spin_unlock_irqrestore(&s390_domain->list_lock, flags); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci zpci_unregister_ioat(zdev, 0); 10062306a36Sopenharmony_ci zdev->s390_domain = NULL; 10162306a36Sopenharmony_ci zdev->dma_table = NULL; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int s390_iommu_attach_device(struct iommu_domain *domain, 10562306a36Sopenharmony_ci struct device *dev) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct s390_domain *s390_domain = to_s390_domain(domain); 10862306a36Sopenharmony_ci struct zpci_dev *zdev = to_zpci_dev(dev); 10962306a36Sopenharmony_ci unsigned long flags; 11062306a36Sopenharmony_ci u8 status; 11162306a36Sopenharmony_ci int cc; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (!zdev) 11462306a36Sopenharmony_ci return -ENODEV; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (WARN_ON(domain->geometry.aperture_start > zdev->end_dma || 11762306a36Sopenharmony_ci domain->geometry.aperture_end < zdev->start_dma)) 11862306a36Sopenharmony_ci return -EINVAL; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (zdev->s390_domain) 12162306a36Sopenharmony_ci __s390_iommu_detach_device(zdev); 12262306a36Sopenharmony_ci else if (zdev->dma_table) 12362306a36Sopenharmony_ci zpci_dma_exit_device(zdev); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci cc = zpci_register_ioat(zdev, 0, zdev->start_dma, zdev->end_dma, 12662306a36Sopenharmony_ci virt_to_phys(s390_domain->dma_table), &status); 12762306a36Sopenharmony_ci /* 12862306a36Sopenharmony_ci * If the device is undergoing error recovery the reset code 12962306a36Sopenharmony_ci * will re-establish the new domain. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci if (cc && status != ZPCI_PCI_ST_FUNC_NOT_AVAIL) 13262306a36Sopenharmony_ci return -EIO; 13362306a36Sopenharmony_ci zdev->dma_table = s390_domain->dma_table; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci zdev->dma_table = s390_domain->dma_table; 13662306a36Sopenharmony_ci zdev->s390_domain = s390_domain; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci spin_lock_irqsave(&s390_domain->list_lock, flags); 13962306a36Sopenharmony_ci list_add_rcu(&zdev->iommu_list, &s390_domain->devices); 14062306a36Sopenharmony_ci spin_unlock_irqrestore(&s390_domain->list_lock, flags); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic void s390_iommu_set_platform_dma(struct device *dev) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct zpci_dev *zdev = to_zpci_dev(dev); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci __s390_iommu_detach_device(zdev); 15062306a36Sopenharmony_ci zpci_dma_init_device(zdev); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic void s390_iommu_get_resv_regions(struct device *dev, 15462306a36Sopenharmony_ci struct list_head *list) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct zpci_dev *zdev = to_zpci_dev(dev); 15762306a36Sopenharmony_ci struct iommu_resv_region *region; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (zdev->start_dma) { 16062306a36Sopenharmony_ci region = iommu_alloc_resv_region(0, zdev->start_dma, 0, 16162306a36Sopenharmony_ci IOMMU_RESV_RESERVED, GFP_KERNEL); 16262306a36Sopenharmony_ci if (!region) 16362306a36Sopenharmony_ci return; 16462306a36Sopenharmony_ci list_add_tail(®ion->list, list); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (zdev->end_dma < ZPCI_TABLE_SIZE_RT - 1) { 16862306a36Sopenharmony_ci region = iommu_alloc_resv_region(zdev->end_dma + 1, 16962306a36Sopenharmony_ci ZPCI_TABLE_SIZE_RT - zdev->end_dma - 1, 17062306a36Sopenharmony_ci 0, IOMMU_RESV_RESERVED, GFP_KERNEL); 17162306a36Sopenharmony_ci if (!region) 17262306a36Sopenharmony_ci return; 17362306a36Sopenharmony_ci list_add_tail(®ion->list, list); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic struct iommu_device *s390_iommu_probe_device(struct device *dev) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct zpci_dev *zdev; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (!dev_is_pci(dev)) 18262306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci zdev = to_zpci_dev(dev); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (zdev->start_dma > zdev->end_dma || 18762306a36Sopenharmony_ci zdev->start_dma > ZPCI_TABLE_SIZE_RT - 1) 18862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (zdev->end_dma > ZPCI_TABLE_SIZE_RT - 1) 19162306a36Sopenharmony_ci zdev->end_dma = ZPCI_TABLE_SIZE_RT - 1; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return &zdev->iommu_dev; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic void s390_iommu_release_device(struct device *dev) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct zpci_dev *zdev = to_zpci_dev(dev); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* 20162306a36Sopenharmony_ci * release_device is expected to detach any domain currently attached 20262306a36Sopenharmony_ci * to the device, but keep it attached to other devices in the group. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci if (zdev) 20562306a36Sopenharmony_ci __s390_iommu_detach_device(zdev); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void s390_iommu_flush_iotlb_all(struct iommu_domain *domain) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct s390_domain *s390_domain = to_s390_domain(domain); 21162306a36Sopenharmony_ci struct zpci_dev *zdev; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci rcu_read_lock(); 21462306a36Sopenharmony_ci list_for_each_entry_rcu(zdev, &s390_domain->devices, iommu_list) { 21562306a36Sopenharmony_ci zpci_refresh_trans((u64)zdev->fh << 32, zdev->start_dma, 21662306a36Sopenharmony_ci zdev->end_dma - zdev->start_dma + 1); 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci rcu_read_unlock(); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic void s390_iommu_iotlb_sync(struct iommu_domain *domain, 22262306a36Sopenharmony_ci struct iommu_iotlb_gather *gather) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct s390_domain *s390_domain = to_s390_domain(domain); 22562306a36Sopenharmony_ci size_t size = gather->end - gather->start + 1; 22662306a36Sopenharmony_ci struct zpci_dev *zdev; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* If gather was never added to there is nothing to flush */ 22962306a36Sopenharmony_ci if (!gather->end) 23062306a36Sopenharmony_ci return; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci rcu_read_lock(); 23362306a36Sopenharmony_ci list_for_each_entry_rcu(zdev, &s390_domain->devices, iommu_list) { 23462306a36Sopenharmony_ci zpci_refresh_trans((u64)zdev->fh << 32, gather->start, 23562306a36Sopenharmony_ci size); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci rcu_read_unlock(); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic void s390_iommu_iotlb_sync_map(struct iommu_domain *domain, 24162306a36Sopenharmony_ci unsigned long iova, size_t size) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct s390_domain *s390_domain = to_s390_domain(domain); 24462306a36Sopenharmony_ci struct zpci_dev *zdev; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci rcu_read_lock(); 24762306a36Sopenharmony_ci list_for_each_entry_rcu(zdev, &s390_domain->devices, iommu_list) { 24862306a36Sopenharmony_ci if (!zdev->tlb_refresh) 24962306a36Sopenharmony_ci continue; 25062306a36Sopenharmony_ci zpci_refresh_trans((u64)zdev->fh << 32, 25162306a36Sopenharmony_ci iova, size); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci rcu_read_unlock(); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int s390_iommu_validate_trans(struct s390_domain *s390_domain, 25762306a36Sopenharmony_ci phys_addr_t pa, dma_addr_t dma_addr, 25862306a36Sopenharmony_ci unsigned long nr_pages, int flags, 25962306a36Sopenharmony_ci gfp_t gfp) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci phys_addr_t page_addr = pa & PAGE_MASK; 26262306a36Sopenharmony_ci unsigned long *entry; 26362306a36Sopenharmony_ci unsigned long i; 26462306a36Sopenharmony_ci int rc; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci for (i = 0; i < nr_pages; i++) { 26762306a36Sopenharmony_ci entry = dma_walk_cpu_trans(s390_domain->dma_table, dma_addr, 26862306a36Sopenharmony_ci gfp); 26962306a36Sopenharmony_ci if (unlikely(!entry)) { 27062306a36Sopenharmony_ci rc = -ENOMEM; 27162306a36Sopenharmony_ci goto undo_cpu_trans; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci dma_update_cpu_trans(entry, page_addr, flags); 27462306a36Sopenharmony_ci page_addr += PAGE_SIZE; 27562306a36Sopenharmony_ci dma_addr += PAGE_SIZE; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ciundo_cpu_trans: 28162306a36Sopenharmony_ci while (i-- > 0) { 28262306a36Sopenharmony_ci dma_addr -= PAGE_SIZE; 28362306a36Sopenharmony_ci entry = dma_walk_cpu_trans(s390_domain->dma_table, 28462306a36Sopenharmony_ci dma_addr, gfp); 28562306a36Sopenharmony_ci if (!entry) 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci dma_update_cpu_trans(entry, 0, ZPCI_PTE_INVALID); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return rc; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int s390_iommu_invalidate_trans(struct s390_domain *s390_domain, 29462306a36Sopenharmony_ci dma_addr_t dma_addr, unsigned long nr_pages) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci unsigned long *entry; 29762306a36Sopenharmony_ci unsigned long i; 29862306a36Sopenharmony_ci int rc = 0; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci for (i = 0; i < nr_pages; i++) { 30162306a36Sopenharmony_ci entry = dma_walk_cpu_trans(s390_domain->dma_table, dma_addr, 30262306a36Sopenharmony_ci GFP_ATOMIC); 30362306a36Sopenharmony_ci if (unlikely(!entry)) { 30462306a36Sopenharmony_ci rc = -EINVAL; 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci dma_update_cpu_trans(entry, 0, ZPCI_PTE_INVALID); 30862306a36Sopenharmony_ci dma_addr += PAGE_SIZE; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci return rc; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int s390_iommu_map_pages(struct iommu_domain *domain, 31562306a36Sopenharmony_ci unsigned long iova, phys_addr_t paddr, 31662306a36Sopenharmony_ci size_t pgsize, size_t pgcount, 31762306a36Sopenharmony_ci int prot, gfp_t gfp, size_t *mapped) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct s390_domain *s390_domain = to_s390_domain(domain); 32062306a36Sopenharmony_ci size_t size = pgcount << __ffs(pgsize); 32162306a36Sopenharmony_ci int flags = ZPCI_PTE_VALID, rc = 0; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (pgsize != SZ_4K) 32462306a36Sopenharmony_ci return -EINVAL; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (iova < s390_domain->domain.geometry.aperture_start || 32762306a36Sopenharmony_ci (iova + size - 1) > s390_domain->domain.geometry.aperture_end) 32862306a36Sopenharmony_ci return -EINVAL; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (!IS_ALIGNED(iova | paddr, pgsize)) 33162306a36Sopenharmony_ci return -EINVAL; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (!(prot & IOMMU_READ)) 33462306a36Sopenharmony_ci return -EINVAL; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (!(prot & IOMMU_WRITE)) 33762306a36Sopenharmony_ci flags |= ZPCI_TABLE_PROTECTED; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci rc = s390_iommu_validate_trans(s390_domain, paddr, iova, 34062306a36Sopenharmony_ci pgcount, flags, gfp); 34162306a36Sopenharmony_ci if (!rc) 34262306a36Sopenharmony_ci *mapped = size; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return rc; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic phys_addr_t s390_iommu_iova_to_phys(struct iommu_domain *domain, 34862306a36Sopenharmony_ci dma_addr_t iova) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct s390_domain *s390_domain = to_s390_domain(domain); 35162306a36Sopenharmony_ci unsigned long *rto, *sto, *pto; 35262306a36Sopenharmony_ci unsigned long ste, pte, rte; 35362306a36Sopenharmony_ci unsigned int rtx, sx, px; 35462306a36Sopenharmony_ci phys_addr_t phys = 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (iova < domain->geometry.aperture_start || 35762306a36Sopenharmony_ci iova > domain->geometry.aperture_end) 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci rtx = calc_rtx(iova); 36162306a36Sopenharmony_ci sx = calc_sx(iova); 36262306a36Sopenharmony_ci px = calc_px(iova); 36362306a36Sopenharmony_ci rto = s390_domain->dma_table; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci rte = READ_ONCE(rto[rtx]); 36662306a36Sopenharmony_ci if (reg_entry_isvalid(rte)) { 36762306a36Sopenharmony_ci sto = get_rt_sto(rte); 36862306a36Sopenharmony_ci ste = READ_ONCE(sto[sx]); 36962306a36Sopenharmony_ci if (reg_entry_isvalid(ste)) { 37062306a36Sopenharmony_ci pto = get_st_pto(ste); 37162306a36Sopenharmony_ci pte = READ_ONCE(pto[px]); 37262306a36Sopenharmony_ci if (pt_entry_isvalid(pte)) 37362306a36Sopenharmony_ci phys = pte & ZPCI_PTE_ADDR_MASK; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return phys; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic size_t s390_iommu_unmap_pages(struct iommu_domain *domain, 38162306a36Sopenharmony_ci unsigned long iova, 38262306a36Sopenharmony_ci size_t pgsize, size_t pgcount, 38362306a36Sopenharmony_ci struct iommu_iotlb_gather *gather) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct s390_domain *s390_domain = to_s390_domain(domain); 38662306a36Sopenharmony_ci size_t size = pgcount << __ffs(pgsize); 38762306a36Sopenharmony_ci int rc; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (WARN_ON(iova < s390_domain->domain.geometry.aperture_start || 39062306a36Sopenharmony_ci (iova + size - 1) > s390_domain->domain.geometry.aperture_end)) 39162306a36Sopenharmony_ci return 0; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci rc = s390_iommu_invalidate_trans(s390_domain, iova, pgcount); 39462306a36Sopenharmony_ci if (rc) 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci iommu_iotlb_gather_add_range(gather, iova, size); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci return size; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ciint zpci_init_iommu(struct zpci_dev *zdev) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci int rc = 0; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci rc = iommu_device_sysfs_add(&zdev->iommu_dev, NULL, NULL, 40762306a36Sopenharmony_ci "s390-iommu.%08x", zdev->fid); 40862306a36Sopenharmony_ci if (rc) 40962306a36Sopenharmony_ci goto out_err; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci rc = iommu_device_register(&zdev->iommu_dev, &s390_iommu_ops, NULL); 41262306a36Sopenharmony_ci if (rc) 41362306a36Sopenharmony_ci goto out_sysfs; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ciout_sysfs: 41862306a36Sopenharmony_ci iommu_device_sysfs_remove(&zdev->iommu_dev); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ciout_err: 42162306a36Sopenharmony_ci return rc; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_civoid zpci_destroy_iommu(struct zpci_dev *zdev) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci iommu_device_unregister(&zdev->iommu_dev); 42762306a36Sopenharmony_ci iommu_device_sysfs_remove(&zdev->iommu_dev); 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic const struct iommu_ops s390_iommu_ops = { 43162306a36Sopenharmony_ci .capable = s390_iommu_capable, 43262306a36Sopenharmony_ci .domain_alloc = s390_domain_alloc, 43362306a36Sopenharmony_ci .probe_device = s390_iommu_probe_device, 43462306a36Sopenharmony_ci .release_device = s390_iommu_release_device, 43562306a36Sopenharmony_ci .device_group = generic_device_group, 43662306a36Sopenharmony_ci .set_platform_dma_ops = s390_iommu_set_platform_dma, 43762306a36Sopenharmony_ci .pgsize_bitmap = SZ_4K, 43862306a36Sopenharmony_ci .get_resv_regions = s390_iommu_get_resv_regions, 43962306a36Sopenharmony_ci .default_domain_ops = &(const struct iommu_domain_ops) { 44062306a36Sopenharmony_ci .attach_dev = s390_iommu_attach_device, 44162306a36Sopenharmony_ci .map_pages = s390_iommu_map_pages, 44262306a36Sopenharmony_ci .unmap_pages = s390_iommu_unmap_pages, 44362306a36Sopenharmony_ci .flush_iotlb_all = s390_iommu_flush_iotlb_all, 44462306a36Sopenharmony_ci .iotlb_sync = s390_iommu_iotlb_sync, 44562306a36Sopenharmony_ci .iotlb_sync_map = s390_iommu_iotlb_sync_map, 44662306a36Sopenharmony_ci .iova_to_phys = s390_iommu_iova_to_phys, 44762306a36Sopenharmony_ci .free = s390_domain_free, 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci}; 450