From patchwork Wed Nov 20 12:08:59 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Paul Durrant X-Patchwork-Id: 11253717 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 45C5A6C1 for ; Wed, 20 Nov 2019 12:10:26 +0000 (UTC) Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 160F1224C3 for ; Wed, 20 Nov 2019 12:10:26 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=fail reason="signature verification failed" (1024-bit key) header.d=amazon.com header.i=@amazon.com header.b="eAAdqjl+" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 160F1224C3 Authentication-Results: mail.kernel.org; dmarc=fail (p=quarantine dis=none) header.from=amazon.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=xen-devel-bounces@lists.xenproject.org Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.89) (envelope-from ) id 1iXOnO-0004vn-KJ; Wed, 20 Nov 2019 12:09:18 +0000 Received: from all-amaz-eas1.inumbo.com ([34.197.232.57] helo=us1-amaz-eas2.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.89) (envelope-from ) id 1iXOnN-0004vi-GK for xen-devel@lists.xenproject.org; Wed, 20 Nov 2019 12:09:17 +0000 X-Inumbo-ID: 9250d198-0b8e-11ea-a310-12813bfff9fa Received: from smtp-fw-33001.amazon.com (unknown [207.171.190.10]) by us1-amaz-eas2.inumbo.com (Halon) with ESMTPS id 9250d198-0b8e-11ea-a310-12813bfff9fa; Wed, 20 Nov 2019 12:09:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amazon.com; i=@amazon.com; q=dns/txt; s=amazon201209; t=1574251756; x=1605787756; h=from:to:cc:subject:date:message-id:mime-version: content-transfer-encoding; bh=fkjgxsz3JYpt8KNXfEebtVYSOlFcXVbUeZvMki1IoXs=; b=eAAdqjl+swr2maJY1GrlydSjYaKAO3/K5xjv4TGDMFgAofRSWRqRVHax YV1TR9nQTIdz+DqiwigQ2dPaa84SjjWLrr2MzjTdVF+oN/WCgpdwXPgDF WjKPVVPdP+nDUvwJDwtuM84V3DJoi3jnI7QDzX9mMctTl6e/16e3sQBr6 Q=; IronPort-SDR: 36HIpeX4fjMxYvB8l9OF0T24POXwWGBMJFGefmjJq7PSK78ohhDONvtWUS4pzwIh6QsglsDlBi C53/8cUTFjbw== X-IronPort-AV: E=Sophos;i="5.69,221,1571702400"; d="scan'208";a="10037037" Received: from sea32-co-svc-lb4-vlan3.sea.corp.amazon.com (HELO email-inbound-relay-2a-53356bf6.us-west-2.amazon.com) ([10.47.23.38]) by smtp-border-fw-out-33001.sea14.amazon.com with ESMTP; 20 Nov 2019 12:09:05 +0000 Received: from EX13MTAUEA001.ant.amazon.com (pdx4-ws-svc-p6-lb7-vlan2.pdx.amazon.com [10.170.41.162]) by email-inbound-relay-2a-53356bf6.us-west-2.amazon.com (Postfix) with ESMTPS id C06F8A1F40; Wed, 20 Nov 2019 12:09:04 +0000 (UTC) Received: from EX13D32EUC003.ant.amazon.com (10.43.164.24) by EX13MTAUEA001.ant.amazon.com (10.43.61.82) with Microsoft SMTP Server (TLS) id 15.0.1367.3; Wed, 20 Nov 2019 12:09:04 +0000 Received: from EX13MTAUWA001.ant.amazon.com (10.43.160.58) by EX13D32EUC003.ant.amazon.com (10.43.164.24) with Microsoft SMTP Server (TLS) id 15.0.1367.3; Wed, 20 Nov 2019 12:09:03 +0000 Received: from u2f063a87eabd5f.cbg10.amazon.com (10.125.106.135) by mail-relay.amazon.com (10.43.160.118) with Microsoft SMTP Server id 15.0.1367.3 via Frontend Transport; Wed, 20 Nov 2019 12:09:01 +0000 From: Paul Durrant To: Date: Wed, 20 Nov 2019 12:08:59 +0000 Message-ID: <20191120120859.1846-1-pdurrant@amazon.com> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Precedence: Bulk Subject: [Xen-devel] [PATCH] x86 / iommu: set up a scratch page in the quarantine domain X-BeenThere: xen-devel@lists.xenproject.org X-Mailman-Version: 2.1.23 List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Cc: Kevin Tian , Wei Liu , Andrew Cooper , Paul Durrant , Jan Beulich , =?utf-8?q?Roger_Pau_Monn=C3=A9?= Errors-To: xen-devel-bounces@lists.xenproject.org Sender: "Xen-devel" This patch introduces a new iommu_op to facilitate a per-implementation quarantine set up, and then further code for x86 implementations (amd and vtd) to set up a read/wrote scratch page to serve as the source/ target for all DMA whilst a device is assigned to dom_io. The reason for doing this is that some hardware may continue to re-try DMA, despite FLR, in the event of an error. Having a scratch page mapped will allow pending DMA to drain and thus quiesce such buggy hardware. Signed-off-by: Paul Durrant --- Cc: Jan Beulich Cc: Andrew Cooper Cc: Kevin Tian Cc: Wei Liu Cc: "Roger Pau Monné" --- xen/drivers/passthrough/amd/iommu_map.c | 57 +++++++++++++++ xen/drivers/passthrough/amd/pci_amd_iommu.c | 9 +-- xen/drivers/passthrough/iommu.c | 25 ++++++- xen/drivers/passthrough/vtd/iommu.c | 71 +++++++++++++++---- xen/include/asm-x86/hvm/svm/amd-iommu-proto.h | 2 + xen/include/xen/iommu.h | 1 + 6 files changed, 143 insertions(+), 22 deletions(-) diff --git a/xen/drivers/passthrough/amd/iommu_map.c b/xen/drivers/passthrough/amd/iommu_map.c index cd5c7de7c5..8440ccf1c1 100644 --- a/xen/drivers/passthrough/amd/iommu_map.c +++ b/xen/drivers/passthrough/amd/iommu_map.c @@ -560,6 +560,63 @@ int amd_iommu_reserve_domain_unity_map(struct domain *domain, return rt; } +int amd_iommu_quarantine_init(struct domain *d) +{ + struct domain_iommu *hd = dom_iommu(d); + unsigned int level; + struct amd_iommu_pte *table; + + if ( hd->arch.root_table ) + { + ASSERT_UNREACHABLE(); + return 0; + } + + spin_lock(&hd->arch.mapping_lock); + + level = hd->arch.paging_mode; + + hd->arch.root_table = alloc_amd_iommu_pgtable(); + if ( !hd->arch.root_table ) + goto out; + + table = __map_domain_page(hd->arch.root_table); + while ( level ) + { + struct page_info *pg; + unsigned int i; + + /* + * The pgtable allocator is fine for the leaf page, as well as + * page table pages. + */ + pg = alloc_amd_iommu_pgtable(); + if ( !pg ) + break; + + for ( i = 0; i < PTE_PER_TABLE_SIZE; i++ ) + { + struct amd_iommu_pte *pde = &table[i]; + + set_iommu_pde_present(pde, mfn_x(page_to_mfn(pg)), level - 1, + true, true); + } + + unmap_domain_page(table); + table = __map_domain_page(pg); + level--; + } + unmap_domain_page(table); + + out: + spin_unlock(&hd->arch.mapping_lock); + + amd_iommu_flush_all_pages(d); + + /* Pages leaked in failure case */ + return level ? -ENOMEM : 0; +} + /* * Local variables: * mode: C diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c index 75a0f1b4ab..c7858b4e8f 100644 --- a/xen/drivers/passthrough/amd/pci_amd_iommu.c +++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c @@ -95,10 +95,6 @@ static void amd_iommu_setup_domain_device( u8 bus = pdev->bus; const struct domain_iommu *hd = dom_iommu(domain); - /* dom_io is used as a sentinel for quarantined devices */ - if ( domain == dom_io ) - return; - BUG_ON( !hd->arch.root_table || !hd->arch.paging_mode || !iommu->dev_table.buffer ); @@ -290,10 +286,6 @@ static void amd_iommu_disable_domain_device(const struct domain *domain, int req_id; u8 bus = pdev->bus; - /* dom_io is used as a sentinel for quarantined devices */ - if ( domain == dom_io ) - return; - BUG_ON ( iommu->dev_table.buffer == NULL ); req_id = get_dma_requestor_id(iommu->seg, PCI_BDF2(bus, devfn)); table = iommu->dev_table.buffer; @@ -632,6 +624,7 @@ static void amd_dump_p2m_table(struct domain *d) static const struct iommu_ops __initconstrel _iommu_ops = { .init = amd_iommu_domain_init, .hwdom_init = amd_iommu_hwdom_init, + .quarantine_init = amd_iommu_quarantine_init, .add_device = amd_iommu_add_device, .remove_device = amd_iommu_remove_device, .assign_device = amd_iommu_assign_device, diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c index 8cbe908fff..25283263d7 100644 --- a/xen/drivers/passthrough/iommu.c +++ b/xen/drivers/passthrough/iommu.c @@ -440,6 +440,28 @@ int iommu_iotlb_flush_all(struct domain *d, unsigned int flush_flags) return rc; } +static int __init iommu_quarantine_init(void) +{ + const struct domain_iommu *hd = dom_iommu(dom_io); + int rc; + + dom_io->options |= XEN_DOMCTL_CDF_iommu; + + rc = iommu_domain_init(dom_io, 0); + if ( rc ) + return rc; + + if ( !hd->platform_ops->quarantine_init ) + return 0; + + rc = hd->platform_ops->quarantine_init(dom_io); + + if ( !rc ) + printk("Quarantine initialized\n"); + + return rc; +} + int __init iommu_setup(void) { int rc = -ENODEV; @@ -473,8 +495,7 @@ int __init iommu_setup(void) } else { - dom_io->options |= XEN_DOMCTL_CDF_iommu; - if ( iommu_domain_init(dom_io, 0) ) + if ( iommu_quarantine_init() ) panic("Could not set up quarantine\n"); printk(" - Dom0 mode: %s\n", diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c index 25ad649c34..c20f2ca029 100644 --- a/xen/drivers/passthrough/vtd/iommu.c +++ b/xen/drivers/passthrough/vtd/iommu.c @@ -1291,10 +1291,6 @@ int domain_context_mapping_one( int agaw, rc, ret; bool_t flush_dev_iotlb; - /* dom_io is used as a sentinel for quarantined devices */ - if ( domain == dom_io ) - return 0; - ASSERT(pcidevs_locked()); spin_lock(&iommu->lock); maddr = bus_to_context_maddr(iommu, bus); @@ -1541,10 +1537,6 @@ int domain_context_unmap_one( int iommu_domid, rc, ret; bool_t flush_dev_iotlb; - /* dom_io is used as a sentinel for quarantined devices */ - if ( domain == dom_io ) - return 0; - ASSERT(pcidevs_locked()); spin_lock(&iommu->lock); @@ -1677,10 +1669,6 @@ static int domain_context_unmap(struct domain *domain, u8 devfn, goto out; } - /* dom_io is used as a sentinel for quarantined devices */ - if ( domain == dom_io ) - goto out; - /* * if no other devices under the same iommu owned by this domain, * clear iommu in iommu_bitmap and clear domain_id in domid_bitmp @@ -2683,9 +2671,68 @@ static void vtd_dump_p2m_table(struct domain *d) vtd_dump_p2m_table_level(hd->arch.pgd_maddr, agaw_to_level(hd->arch.agaw), 0, 0); } +static int intel_iommu_quarantine_init(struct domain *d) +{ + struct domain_iommu *hd = dom_iommu(d); + struct dma_pte *parent; + unsigned int level = agaw_to_level(hd->arch.agaw); + int rc; + + if ( hd->arch.pgd_maddr ) + { + ASSERT_UNREACHABLE(); + return 0; + } + + spin_lock(&hd->arch.mapping_lock); + + hd->arch.pgd_maddr = alloc_pgtable_maddr(1, hd->node); + if ( !hd->arch.pgd_maddr ) + goto out; + + parent = (struct dma_pte *)map_vtd_domain_page(hd->arch.pgd_maddr); + while ( level ) + { + uint64_t maddr; + unsigned int offset; + + /* + * The pgtable allocator is fine for the leaf page, as well as + * page table pages. + */ + maddr = alloc_pgtable_maddr(1, hd->node); + if ( !maddr ) + break; + + for ( offset = 0; offset < PTE_NUM; offset++ ) + { + struct dma_pte *pte = &parent[offset]; + + dma_set_pte_addr(*pte, maddr); + dma_set_pte_readable(*pte); + dma_set_pte_writable(*pte); + } + iommu_flush_cache_page(parent, 1); + + unmap_vtd_domain_page(parent); + parent = map_vtd_domain_page(maddr); + level--; + } + unmap_vtd_domain_page(parent); + + out: + spin_unlock(&hd->arch.mapping_lock); + + rc = iommu_flush_iotlb_all(d); + + /* Pages leaked in failure case */ + return level ? -ENOMEM : rc; +} + const struct iommu_ops __initconstrel intel_iommu_ops = { .init = intel_iommu_domain_init, .hwdom_init = intel_iommu_hwdom_init, + .quarantine_init = intel_iommu_quarantine_init, .add_device = intel_iommu_add_device, .enable_device = intel_iommu_enable_device, .remove_device = intel_iommu_remove_device, diff --git a/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h b/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h index 8ed9482791..39fb10f567 100644 --- a/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h +++ b/xen/include/asm-x86/hvm/svm/amd-iommu-proto.h @@ -54,6 +54,8 @@ int amd_iommu_init_late(void); int amd_iommu_update_ivrs_mapping_acpi(void); int iov_adjust_irq_affinities(void); +int amd_iommu_quarantine_init(struct domain *d); + /* mapping functions */ int __must_check amd_iommu_map_page(struct domain *d, dfn_t dfn, mfn_t mfn, unsigned int flags, diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h index 974bd3ffe8..6977ddbb97 100644 --- a/xen/include/xen/iommu.h +++ b/xen/include/xen/iommu.h @@ -211,6 +211,7 @@ typedef int iommu_grdm_t(xen_pfn_t start, xen_ulong_t nr, u32 id, void *ctxt); struct iommu_ops { int (*init)(struct domain *d); void (*hwdom_init)(struct domain *d); + int (*quarantine_init)(struct domain *d); int (*add_device)(u8 devfn, device_t *dev); int (*enable_device)(device_t *dev); int (*remove_device)(u8 devfn, device_t *dev);