@@ -1469,14 +1469,34 @@ 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;
+
+ 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;
@@ -1521,17 +1541,35 @@ 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.
+ */
+ ret = arm_smmu_assign_dev(pdev->domain, devfn, dev, 0);
+ if (ret)
+ goto err_free_master;
+ }
+#endif
+
return 0;
err_free_master:
@@ -2621,6 +2659,61 @@ 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;
+
+ ASSERT(pcidevs_locked());
+
+ /* TODO: acquire pci_lock */
+#if 0
+ write_lock(&pdev->domain->pci_lock);
+#endif
+ list_del(&pdev->domain_list);
+#if 0
+ write_unlock(&pdev->domain->pci_lock);
+#endif
+
+ pdev->domain = d;
+
+#if 0
+ write_lock(&d->pci_lock);
+#endif
+ list_add(&pdev->domain_list, &d->pdev_list);
+#if 0
+ write_unlock(&d->pci_lock);
+#endif
+
+ if ( hardware_domain )
+ {
+ io_domain = arm_smmu_get_domain(hardware_domain, dev);
+
+ /*
+ * Xen may not deassign the device from hwdom before
+ * assigning it elsewhere.
+ */
+ if ( io_domain )
+ {
+ ret = arm_smmu_deassign_dev(hardware_domain, devfn, dev);
+ if ( ret )
+ return ret;
+ }
+ }
+
+ /* dom_io is used as a sentinel for quarantined devices */
+ if ( d == dom_io )
+ return 0;
+ }
+#endif
+
spin_lock(&xen_domain->lock);
/*
@@ -2654,7 +2747,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;
@@ -2666,6 +2759,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);
@@ -2685,13 +2796,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;