aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeorge Dunlap <george.dunlap@eu.citrix.com>2012-07-09 10:22:58 +0100
committerGeorge Dunlap <george.dunlap@eu.citrix.com>2012-07-09 10:22:58 +0100
commit15b2fe84d8059f8e1d4da16ebc1bab8c9e96ae29 (patch)
treed9349dbb7d6a37bb009be54094a9a1e4f8a2bfab
parent9bf475a1be5d76895a3ddb50a032363b01768ee3 (diff)
downloadxen-15b2fe84d8059f8e1d4da16ebc1bab8c9e96ae29.tar.gz
xen-15b2fe84d8059f8e1d4da16ebc1bab8c9e96ae29.tar.bz2
xen-15b2fe84d8059f8e1d4da16ebc1bab8c9e96ae29.zip
xen, vtd: Fix device check for devices behind PCIe-to-PCI bridges
On some systems, requests devices behind a PCIe-to-PCI bridge all appear to the IOMMU as though they come from from slot 0, function 0 on that device; so the mapping code much punch a hole for X:0.0 in the IOMMU for such devices. When punching the hole, if that device has already been mapped once, we simply need to check ownership to make sure it's legal. To do so, domain_context_mapping_one() will look up the device for the mapping with pci_get_pdev() and look for the owner. However, if there is no device in X:0.0, this look up will fail. Rather than returning -ENODEV in this situation (causing a failure in mapping the device), try to get the domain ownership from the iommu context mapping itself. Signed-off-by: George Dunlap <george.dunlap@eu.citrix.com> xen-unstable changeset: 23813:5535d7ce2673 xen-unstable date: Mon Sep 05 15:00:46 2011 +0100
-rw-r--r--xen/drivers/passthrough/vtd/iommu.c63
1 files changed, 58 insertions, 5 deletions
diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c
index 158713abeb..ac0035a5a2 100644
--- a/xen/drivers/passthrough/vtd/iommu.c
+++ b/xen/drivers/passthrough/vtd/iommu.c
@@ -118,6 +118,27 @@ static int context_set_domain_id(struct context_entry *context,
return 0;
}
+static int context_get_domain_id(struct context_entry *context,
+ struct iommu *iommu)
+{
+ unsigned long dom_index, nr_dom;
+ int domid = -1;
+
+ if (iommu && context)
+ {
+ nr_dom = cap_ndoms(iommu->cap);
+
+ dom_index = context_domain_id(*context);
+
+ if ( dom_index < nr_dom && iommu->domid_map)
+ domid = iommu->domid_map[dom_index];
+ else
+ dprintk(XENLOG_DEBUG VTDPREFIX, "%s: dom_index %lu exceeds nr_dom %lu or iommu has no domid_map\n",
+ __func__, dom_index, nr_dom);
+ }
+ return domid;
+}
+
static struct intel_iommu *__init alloc_intel_iommu(void)
{
struct intel_iommu *intel;
@@ -1278,7 +1299,6 @@ int domain_context_mapping_one(
struct hvm_iommu *hd = domain_hvm_iommu(domain);
struct context_entry *context, *context_entries;
u64 maddr, pgd_maddr;
- struct pci_dev *pdev = NULL;
int agaw;
ASSERT(spin_is_locked(&pcidevs_lock));
@@ -1290,12 +1310,45 @@ int domain_context_mapping_one(
if ( context_present(*context) )
{
int res = 0;
+ struct pci_dev *pdev = NULL;
+ /* First try to get domain ownership from device structure. If that's
+ * not available, try to read it from the context itself. */
pdev = pci_get_pdev(bus, devfn);
- if (!pdev)
- res = -ENODEV;
- else if (pdev->domain != domain)
- res = -EINVAL;
+ if ( pdev )
+ {
+ if ( pdev->domain != domain )
+ {
+ dprintk(XENLOG_INFO VTDPREFIX, "d%d: bdf = %x:%x.%x owned by d%d!",
+ domain->domain_id,
+ bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
+ (pdev->domain)
+ ? pdev->domain->domain_id : -1);
+ res = -EINVAL;
+ }
+ }
+ else
+ {
+ int cdomain;
+ cdomain = context_get_domain_id(context, iommu);
+
+ if ( cdomain < 0 )
+ {
+ dprintk(VTDPREFIX, "d%d: bdf = %x:%x.%x mapped, but can't find owner!\n",
+ domain->domain_id,
+ bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
+ res = -EINVAL;
+ }
+ else if ( cdomain != domain->domain_id )
+ {
+ dprintk(XENLOG_INFO VTDPREFIX, "d%d: bdf = %x:%x.%x already mapped to d%d!",
+ domain->domain_id,
+ bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
+ cdomain);
+ res = -EINVAL;
+ }
+ }
+
unmap_vtd_domain_page(context_entries);
spin_unlock(&iommu->lock);
return res;