From patchwork Fri Sep 19 11:54:31 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: frank.blaschka@de.ibm.com X-Patchwork-Id: 4937651 Return-Path: X-Original-To: patchwork-kvm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 1C46CBEEA5 for ; Fri, 19 Sep 2014 12:00:19 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 316F0201F2 for ; Fri, 19 Sep 2014 12:00:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 8167B201ED for ; Fri, 19 Sep 2014 12:00:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756645AbaISMAA (ORCPT ); Fri, 19 Sep 2014 08:00:00 -0400 Received: from e06smtp11.uk.ibm.com ([195.75.94.107]:39121 "EHLO e06smtp11.uk.ibm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756640AbaISL7q (ORCPT ); Fri, 19 Sep 2014 07:59:46 -0400 Received: from /spool/local by e06smtp11.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Fri, 19 Sep 2014 12:59:44 +0100 Received: from d06dlp02.portsmouth.uk.ibm.com (9.149.20.14) by e06smtp11.uk.ibm.com (192.168.101.141) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Fri, 19 Sep 2014 12:59:42 +0100 Received: from b06cxnps3075.portsmouth.uk.ibm.com (d06relay10.portsmouth.uk.ibm.com [9.149.109.195]) by d06dlp02.portsmouth.uk.ibm.com (Postfix) with ESMTP id D7FC2219005E; Fri, 19 Sep 2014 12:59:21 +0100 (BST) Received: from d06av01.portsmouth.uk.ibm.com (d06av01.portsmouth.uk.ibm.com [9.149.37.212]) by b06cxnps3075.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id s8JBxgxL43384942; Fri, 19 Sep 2014 11:59:42 GMT Received: from d06av01.portsmouth.uk.ibm.com (localhost [127.0.0.1]) by d06av01.portsmouth.uk.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id s8JBxekh030158; Fri, 19 Sep 2014 05:59:41 -0600 Received: from tuxmaker.boeblingen.de.ibm.com (tuxmaker.boeblingen.de.ibm.com [9.152.85.9]) by d06av01.portsmouth.uk.ibm.com (8.14.4/8.14.4/NCO v10.0 AVin) with ESMTP id s8JBxeuS030147; Fri, 19 Sep 2014 05:59:40 -0600 Received: by tuxmaker.boeblingen.de.ibm.com (Postfix, from userid 24631) id 3BF521224439; Fri, 19 Sep 2014 13:59:40 +0200 (CEST) Message-Id: <20140919115940.138250030@de.ibm.com> User-Agent: quilt/0.61-1 Date: Fri, 19 Sep 2014 13:54:31 +0200 From: frank.blaschka@de.ibm.com To: qemu-devel@nongnu.org, linux-s390@vger.kernel.org, kvm@vger.kernel.org Cc: alex.williamson@redhat.com, pbonzini@redhat.com, agraf@suse.de Subject: [RFC patch 2/6] iommu: add iommu for s390 platform References: <20140919115429.557279920@de.ibm.com> MIME-Version: 1.0 Content-Disposition: inline; filename=006-s390_iommu-3.16.patch X-TM-AS-MML: disable X-Content-Scanned: Fidelis XPS MAILER x-cbid: 14091911-5024-0000-0000-0000015C2CD7 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Spam-Status: No, score=-7.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Frank Blaschka Add a basic iommu for the s390 platform. The code is pretty simple since on s390 each PCI device has its own virtual io address space starting at the same vio address. For this a domain could hold only one pci device. Also there is no relation between pci devices so each device belongs to a separate iommu group. Signed-off-by: Frank Blaschka --- arch/s390/include/asm/pci.h | 3 arch/s390/pci/pci_dma.c | 21 ++++- drivers/iommu/Kconfig | 9 ++ drivers/iommu/Makefile | 1 drivers/iommu/s390-iommu.c | 181 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 213 insertions(+), 2 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -177,6 +177,9 @@ struct zpci_dev *get_zdev_by_fid(u32); /* DMA */ int zpci_dma_init(void); void zpci_dma_exit(void); +int dma_update_trans(struct zpci_dev *zdev, unsigned long pa, + dma_addr_t dma_addr, size_t size, int flags); +void dma_purge_rto_entries(struct zpci_dev *zdev); /* FMB */ int zpci_fmb_enable_device(struct zpci_dev *); --- a/arch/s390/pci/pci_dma.c +++ b/arch/s390/pci/pci_dma.c @@ -139,8 +139,8 @@ static void dma_update_cpu_trans(struct entry_clr_protected(entry); } -static int dma_update_trans(struct zpci_dev *zdev, unsigned long pa, - dma_addr_t dma_addr, size_t size, int flags) +int dma_update_trans(struct zpci_dev *zdev, unsigned long pa, + dma_addr_t dma_addr, size_t size, int flags) { unsigned int nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; u8 *page_addr = (u8 *) (pa & PAGE_MASK); @@ -180,6 +180,7 @@ no_refresh: spin_unlock_irqrestore(&zdev->dma_table_lock, irq_flags); return rc; } +EXPORT_SYMBOL_GPL(dma_update_trans); static void dma_free_seg_table(unsigned long entry) { @@ -210,6 +211,22 @@ static void dma_cleanup_tables(struct zp zdev->dma_table = NULL; } +void dma_purge_rto_entries(struct zpci_dev *zdev) +{ + unsigned long *table; + int rtx; + + if (!zdev || !zdev->dma_table) + return; + table = zdev->dma_table; + for (rtx = 0; rtx < ZPCI_TABLE_ENTRIES; rtx++) + if (reg_entry_isvalid(table[rtx])) { + dma_free_seg_table(table[rtx]); + invalidate_table_entry(&table[rtx]); + } +} +EXPORT_SYMBOL_GPL(dma_purge_rto_entries); + static unsigned long __dma_alloc_iommu(struct zpci_dev *zdev, unsigned long start, int size) { --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -302,4 +302,13 @@ config ARM_SMMU Say Y here if your SoC includes an IOMMU device implementing the ARM SMMU architecture. +config S390_IOMMU + bool "s390 IOMMU Support" + depends on S390 + select IOMMU_API + help + Support for the IBM s/390 IOMMU + + If unsure, say N here. + endif # IOMMU_SUPPORT --- a/drivers/iommu/Makefile +++ b/drivers/iommu/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iom obj-$(CONFIG_SHMOBILE_IOMMU) += shmobile-iommu.o obj-$(CONFIG_SHMOBILE_IPMMU) += shmobile-ipmmu.o obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o +obj-$(CONFIG_S390_IOMMU) += s390-iommu.o --- /dev/null +++ b/drivers/iommu/s390-iommu.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define S390_IOMMU_PGSIZES SZ_4K + +struct s390_domain { + struct zpci_dev *zdev; +}; + +static int s390_iommu_domain_init(struct iommu_domain *domain) +{ + struct s390_domain *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + domain->priv = priv; + return 0; +} + +static void s390_iommu_domain_destroy(struct iommu_domain *domain) +{ + kfree(domain->priv); + domain->priv = NULL; +} + +static int s390_iommu_attach_device(struct iommu_domain *domain, + struct device *dev) +{ + struct s390_domain *priv = domain->priv; + + if (priv->zdev) + return -EEXIST; + + priv->zdev = (struct zpci_dev *)to_pci_dev(dev)->sysdata; + return 0; +} + +static void s390_iommu_detach_device(struct iommu_domain *domain, + struct device *dev) +{ + struct s390_domain *priv = domain->priv; + + dma_purge_rto_entries(priv->zdev); + priv->zdev = NULL; +} + +static int s390_iommu_map(struct iommu_domain *domain, unsigned long iova, + phys_addr_t paddr, size_t size, int prot) +{ + struct s390_domain *priv = domain->priv; + int flags = 0; + int rc; + + if (!priv->zdev) + return -ENODEV; + + /* if (read only) flags |= ZPCI_TABLE_PROTECTED; */ + rc = dma_update_trans(priv->zdev, (unsigned long)paddr, iova, size, + flags); + + return rc; +} + +static phys_addr_t s390_iommu_iova_to_phys(struct iommu_domain *domain, + dma_addr_t iova) +{ + struct s390_domain *priv = domain->priv; + phys_addr_t phys = 0; + unsigned long *sto, *pto, *rto; + unsigned int rtx, sx, px; + + if (!priv->zdev) + return -ENODEV; + + rtx = calc_rtx(iova); + sx = calc_sx(iova); + px = calc_px(iova); + rto = priv->zdev->dma_table; + + if (reg_entry_isvalid(rto[rtx])) { + sto = get_rt_sto(rto[rtx]); + if (reg_entry_isvalid(sto[sx])) { + pto = get_st_pto(sto[sx]); + if ((pto[px] & ZPCI_PTE_VALID_MASK) == ZPCI_PTE_VALID) + phys = pto[px] & ZPCI_PTE_ADDR_MASK; + } + } + + return phys; +} + +static size_t s390_iommu_unmap(struct iommu_domain *domain, + unsigned long iova, size_t size) +{ + struct s390_domain *priv = domain->priv; + int flags = ZPCI_PTE_INVALID; + phys_addr_t paddr; + int rc; + + if (!priv->zdev) + goto out; + + paddr = s390_iommu_iova_to_phys(domain, iova); + if (!paddr) + goto out; + + rc = dma_update_trans(priv->zdev, (unsigned long)paddr, iova, size, + flags); +out: + return size; +} + +static int s390_iommu_domain_has_cap(struct iommu_domain *domain, + unsigned long cap) +{ + switch (cap) { + case IOMMU_CAP_CACHE_COHERENCY: + return 1; + case IOMMU_CAP_INTR_REMAP: + return 1; + } + + return 0; +} + +static int s390_iommu_add_device(struct device *dev) +{ + struct iommu_group *group; + int ret; + + group = iommu_group_alloc(); + if (IS_ERR(group)) { + dev_err(dev, "Failed to allocate IOMMU group\n"); + return PTR_ERR(group); + } + + ret = iommu_group_add_device(group, dev); + return ret; +} + +static void s390_iommu_remove_device(struct device *dev) +{ + iommu_group_remove_device(dev); +} + +static struct iommu_ops s390_iommu_ops = { + .domain_init = s390_iommu_domain_init, + .domain_destroy = s390_iommu_domain_destroy, + .attach_dev = s390_iommu_attach_device, + .detach_dev = s390_iommu_detach_device, + .map = s390_iommu_map, + .unmap = s390_iommu_unmap, + .iova_to_phys = s390_iommu_iova_to_phys, + .domain_has_cap = s390_iommu_domain_has_cap, + .add_device = s390_iommu_add_device, + .remove_device = s390_iommu_remove_device, + .pgsize_bitmap = S390_IOMMU_PGSIZES, +}; + +static int __init s390_iommu_init(void) +{ + bus_set_iommu(&pci_bus_type, &s390_iommu_ops); + return 0; +} +subsys_initcall(s390_iommu_init);