@@ -1467,14 +1467,35 @@ 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_deassign_dev(struct domain *d, uint8_t devfn,
+ struct device *dev);
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;
+
+ /* Ignore calls for phantom functions */
+ 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;
@@ -1519,17 +1540,38 @@ static int arm_smmu_add_device(u8 devfn, struct device *dev)
*/
arm_smmu_enable_pasid(master);
- if (dt_device_is_protected(dev_to_dt(dev))) {
- dev_err(dev, "Already added to SMMUv3\n");
- return -EEXIST;
- }
+ if ( !dev_is_pci(dev) )
+ {
+ if (dt_device_is_protected(dev_to_dt(dev))) {
+ dev_err(dev, "Already added to SMMUv3\n");
+ return -EEXIST;
+ }
- /* Let Xen know that the master device is protected by an IOMMU. */
- dt_device_set_protected(dev_to_dt(dev));
+ /* Let Xen know that the master device is protected by an IOMMU. */
+ dt_device_set_protected(dev_to_dt(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);
+
+ /*
+ * During PHYSDEVOP_pci_device_add, Xen does not assign the
+ * device, so we must do it here.
+ */
+ if ( pdev->domain )
+ {
+ ret = arm_smmu_assign_dev(pdev->domain, devfn, dev, 0);
+ if (ret)
+ goto err_free_master;
+ }
+ }
+#endif
+
return 0;
err_free_master:
@@ -2622,6 +2664,42 @@ 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) )
+ {
+ struct pci_dev *pdev = dev_to_pci(dev);
+
+ /* Ignore calls for phantom functions */
+ if ( devfn != pdev->devfn )
+ return 0;
+
+ ASSERT(pcidevs_locked());
+
+ write_lock(&pdev->domain->pci_lock);
+ list_del(&pdev->domain_list);
+ write_unlock(&pdev->domain->pci_lock);
+
+ pdev->domain = d;
+
+ write_lock(&d->pci_lock);
+ list_add(&pdev->domain_list, &d->pdev_list);
+ write_unlock(&d->pci_lock);
+
+ /* dom_io is used as a sentinel for quarantined devices */
+ if ( d == dom_io )
+ {
+ struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+ if ( !iommu_quarantine )
+ return 0;
+
+ if ( master && master->domain )
+ arm_smmu_deassign_dev(master->domain->d, devfn, dev);
+
+ return 0;
+ }
+ }
+#endif
+
spin_lock(&xen_domain->lock);
/*
@@ -2655,7 +2733,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 +2745,21 @@ 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);
+
+ /* Ignore calls for phantom functions */
+ 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);
@@ -2685,14 +2778,16 @@ 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) )
+ /* Don't allow remapping on other domain than hwdom
+ * or dom_io for PCI devices
+ */
+ if ( t && !is_hardware_domain(t) && (t != dom_io || !dev_is_pci(dev)) )
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;