|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH] x86 / iommu: set up a scratch page in the quarantine domain
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 <pdurrant@xxxxxxxxxx>
---
Cc: Jan Beulich <jbeulich@xxxxxxxx>
Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
Cc: Kevin Tian <kevin.tian@xxxxxxxxx>
Cc: Wei Liu <wl@xxxxxxx>
Cc: "Roger Pau Monné" <roger.pau@xxxxxxxxxx>
---
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);
--
2.20.1
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |