@@ -1469,14 +1469,32 @@ static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid)
}
/* Forward declaration */
static struct arm_smmu_device *arm_smmu_get_by_dev(const struct device *dev);
+static int arm_smmu_assign_dev(struct domain *d, u8 devfn,
+ struct device *dev, u32 flag);
static int arm_smmu_add_device(u8 devfn, struct device *dev)
{
int i, ret;
struct arm_smmu_device *smmu;
struct arm_smmu_master *master;
- struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+ struct iommu_fwspec *fwspec;
+
+#ifdef CONFIG_HAS_PCI
+ if ( dev_is_pci(dev) )
+ {
+ struct pci_dev *pdev = dev_to_pci(dev);
+ int ret;
+
+ if ( devfn != pdev->devfn )
+ return 0;
+
+ ret = iommu_add_pci_sideband_ids(pdev);
+ if ( ret < 0 )
+ iommu_fwspec_free(dev);
+ }
+#endif
+ fwspec = dev_iommu_fwspec_get(dev);
if (!fwspec)
return -ENODEV;
@@ -1533,6 +1551,17 @@ static int arm_smmu_add_device(u8 devfn, struct device *dev)
dev_info(dev, "Added master device (SMMUv3 %s StreamIds %u)\n",
dev_name(fwspec->iommu_dev), fwspec->num_ids);
+#ifdef CONFIG_HAS_PCI
+ if ( dev_is_pci(dev) )
+ {
+ struct pci_dev *pdev = dev_to_pci(dev);
+
+ ret = arm_smmu_assign_dev(pdev->domain, devfn, dev, 0);
+ if (ret)
+ goto err_free_master;
+ }
+#endif
+
return 0;
err_free_master:
@@ -2622,6 +2651,27 @@ static int arm_smmu_assign_dev(struct domain *d, u8 devfn,
struct arm_smmu_domain *smmu_domain;
struct arm_smmu_xen_domain *xen_domain = dom_iommu(d)->arch.priv;
+#ifdef CONFIG_HAS_PCI
+ if ( dev_is_pci(dev) && !is_hardware_domain(d) )
+ {
+ struct pci_dev *pdev = dev_to_pci(dev);
+
+ printk(XENLOG_INFO "Assigning device %04x:%02x:%02x.%u to dom%d\n",
+ pdev->seg, pdev->bus, PCI_SLOT(devfn),
+ PCI_FUNC(devfn), d->domain_id);
+
+ if ( devfn != pdev->devfn || pdev->domain == d )
+ return 0;
+
+ list_move(&pdev->domain_list, &d->pdev_list);
+ pdev->domain = d;
+
+ /* dom_io is used as a sentinel for quarantined devices */
+ if ( d == dom_io )
+ return 0;
+ }
+#endif
+
spin_lock(&xen_domain->lock);
/*
@@ -2655,7 +2705,7 @@ out:
return ret;
}
-static int arm_smmu_deassign_dev(struct domain *d, struct device *dev)
+static int arm_smmu_deassign_dev(struct domain *d, uint8_t devfn, struct device *dev)
{
struct iommu_domain *io_domain = arm_smmu_get_domain(d, dev);
struct arm_smmu_xen_domain *xen_domain = dom_iommu(d)->arch.priv;
@@ -2667,6 +2717,24 @@ static int arm_smmu_deassign_dev(struct domain *d, struct device *dev)
return -ESRCH;
}
+#ifdef CONFIG_HAS_PCI
+ if ( dev_is_pci(dev) )
+ {
+ struct pci_dev *pdev = dev_to_pci(dev);
+
+ printk(XENLOG_INFO "Deassigning device %04x:%02x:%02x.%u from dom%d\n",
+ pdev->seg, pdev->bus, PCI_SLOT(devfn),
+ PCI_FUNC(devfn), d->domain_id);
+
+ if ( devfn != pdev->devfn )
+ return 0;
+
+ /* dom_io is used as a sentinel for quarantined devices */
+ if ( d == dom_io )
+ return 0;
+ }
+#endif
+
spin_lock(&xen_domain->lock);
arm_smmu_detach_dev(master);
@@ -2686,13 +2754,13 @@ static int arm_smmu_reassign_dev(struct domain *s, struct domain *t,
int ret = 0;
/* Don't allow remapping on other domain than hwdom */
- if ( t && !is_hardware_domain(t) )
+ if ( t && !is_hardware_domain(t) && (t != dom_io) )
return -EPERM;
if (t == s)
return 0;
- ret = arm_smmu_deassign_dev(s, dev);
+ ret = arm_smmu_deassign_dev(s, devfn, dev);
if (ret)
return ret;