mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
xen/arm: smmuv3: Add PCI devices support for SMMUv3
Implement support for PCI devices in the SMMU driver. Trigger iommu-map parsing when new PCI device is added. Add checks to assign/deassign functions to ensure PCI devices are handled correctly. Implement basic quarantining. All pci devices are automatically assigned to hardware domain if it exists to ensure it can probe them. TODO: Implement scratch page quarantining support. Signed-off-by: Rahul Singh <rahul.singh@arm.com> Signed-off-by: Stewart Hildebrand <stewart.hildebrand@amd.com> Signed-off-by: Mykyta Poturai <mykyta_poturai@epam.com> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
This commit is contained in:
1 parent
ca8f6ffeb6
commit
63919fc4d1
1 file changed
+108
-11
@@ -1469,14 +1469,37 @@ 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);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
fwspec = dev_iommu_fwspec_get(dev);
|
||||
if (!fwspec)
|
||||
return -ENODEV;
|
||||
|
||||
@@ -1521,17 +1544,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:
|
||||
@@ -2624,6 +2668,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 )
|
||||
ret = arm_smmu_deassign_dev(master->domain->d, devfn, dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
spin_lock(&xen_domain->lock);
|
||||
|
||||
/*
|
||||
@@ -2657,7 +2737,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;
|
||||
@@ -2669,6 +2749,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);
|
||||
@@ -2687,14 +2782,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;
|
||||
|
||||
|
||||