|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v1 3/3] iommu/ipmmu-vmsa: Implement basic PCIE-IPMMU OSID support
On Mon, 7 Jul 2025, Mykyta Poturai wrote:
> From: Oleksandr Tyshchenko <oleksandr_tyshchenko@xxxxxxxx>
>
> Program PCIE BDF-OSID assignment according to the S4_PCIe_IPMMU-OSID
> when adding PCI device to the IOMMU in ipmmu_add_device callback.
> This is needed for being able to assign PCI devices to different
> domains at the same time. Programmed OSID is emmited as sideband data on
> the AXI bus during PCI DMA transactions and then used by IPMMU to match
> the transaction to the corresponding uTLB.
>
> Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@xxxxxxxx>
> Signed-off-by: Mykyta Poturai <mykyta_poturai@xxxxxxxx>
> ---
> xen/drivers/passthrough/arm/ipmmu-vmsa.c | 141 +++++++++++++++++++++--
> 1 file changed, 133 insertions(+), 8 deletions(-)
>
> diff --git a/xen/drivers/passthrough/arm/ipmmu-vmsa.c
> b/xen/drivers/passthrough/arm/ipmmu-vmsa.c
> index d828d9cf6a..14ddabb30e 100644
> --- a/xen/drivers/passthrough/arm/ipmmu-vmsa.c
> +++ b/xen/drivers/passthrough/arm/ipmmu-vmsa.c
> @@ -51,12 +51,25 @@
> #include <asm/device.h>
> #include <asm/io.h>
> #include <asm/iommu_fwspec.h>
> +#include "../arch/arm/pci/pci-host-rcar4.h"
>
> #define dev_name(dev) dt_node_full_name(dev_to_dt(dev))
>
> /* Device logger functions */
> +#ifndef CONFIG_HAS_PCI
> #define dev_print(dev, lvl, fmt, ...) \
> printk(lvl "ipmmu: %s: " fmt, dev_name(dev), ## __VA_ARGS__)
> +#else
> +#define dev_print(dev, lvl, fmt, ...) ({ \
> + if ( !dev_is_pci((dev)) ) \
> + printk(lvl "ipmmu: %s: " fmt, dev_name((dev)), ## __VA_ARGS__); \
> + else \
> + { \
> + struct pci_dev *pdev = dev_to_pci((dev)); \
> + printk(lvl "ipmmu: %pp: " fmt, &pdev->sbdf, ## __VA_ARGS__); \
> + } \
> +})
> +#endif
>
> #define dev_info(dev, fmt, ...) \
> dev_print(dev, XENLOG_INFO, fmt, ## __VA_ARGS__)
> @@ -1121,6 +1134,8 @@ static void ipmmu_free_root_domain(struct
> ipmmu_vmsa_domain *domain)
> xfree(domain);
> }
>
> +static int ipmmu_deassign_device(struct domain *d, struct device *dev);
> +
> static int ipmmu_assign_device(struct domain *d, u8 devfn, struct device
> *dev,
> uint32_t flag)
> {
> @@ -1134,8 +1149,43 @@ static int ipmmu_assign_device(struct domain *d, u8
> devfn, struct device *dev,
> if ( !to_ipmmu(dev) )
> return -ENODEV;
>
> - spin_lock(&xen_domain->lock);
> +#ifdef CONFIG_HAS_PCI
> + if ( dev_is_pci(dev) )
> + {
> + struct pci_dev *pdev = dev_to_pci(dev);
> + struct domain *old_d = pdev->domain;
> +
> + 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);
> +
> + /*
> + * XXX What would be the proper behavior? This could happen if
> + * pdev->phantom_stride > 0
> + */
> + if ( devfn != pdev->devfn )
> + ASSERT_UNREACHABLE();
If it is possible under normal conditions, I would expand the check to
include those normal conditions and continue if it is normal, panic or
returning error if not normal. A panic is better than an ASSERT for
abnormal conditions. Even better is simply to return error.
> + list_move(&pdev->domain_list, &d->pdev_list);
This needs a lock, but I am guessing this function is already called
with the d->pci_lock lock held.
> + pdev->domain = d;
> +
> + /* dom_io is used as a sentinel for quarantined devices */
> + if ( d == dom_io )
> + {
> + int ret;
> +
> + /*
> + * Try to de-assign: do not return error if it was already
> + * de-assigned.
> + */
> + ret = ipmmu_deassign_device(old_d, dev);
> +
> + return ret == -ESRCH ? 0 : ret;
> + }
> + }
> +#endif
>
> + spin_lock(&xen_domain->lock);
> /*
> * The IPMMU context for the Xen domain is not allocated beforehand
> * (at the Xen domain creation time), but on demand only, when the first
> @@ -1240,7 +1290,7 @@ static int ipmmu_reassign_device(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 )
> @@ -1288,20 +1338,95 @@ static int ipmmu_dt_xlate(struct device *dev,
>
> static int ipmmu_add_device(u8 devfn, struct device *dev)
> {
> - 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);
>
> /* Only let through devices that have been verified in xlate(). */
> if ( !to_ipmmu(dev) )
> return -ENODEV;
>
> - if ( dt_device_is_protected(dev_to_dt(dev)) )
> + if ( !dev_is_pci(dev) )
> {
> - dev_err(dev, "Already added to IPMMU\n");
> - return -EEXIST;
> + if ( dt_device_is_protected(dev_to_dt(dev)) )
> + {
> + dev_err(dev, "Already added to IPMMU\n");
> + return -EEXIST;
> + }
> +
> + /* Let Xen know that the master device is protected by an IOMMU. */
> + dt_device_set_protected(dev_to_dt(dev));
> }
> +#ifdef CONFIG_HAS_PCI
> + if ( dev_is_pci(dev) )
> + {
> + struct pci_dev *pdev = dev_to_pci(dev);
> + unsigned int reg_id, osid;
> + struct pci_host_bridge *bridge;
> + struct iommu_fwspec *fwspec_bridge;
> + unsigned int utlb_osid0 = 0;
> + int ret;
> +
> + bridge = pci_find_host_bridge(pdev->seg, pdev->bus);
> + if ( !bridge )
> + {
> + dev_err(dev, "Failed to find host bridge\n");
> + return -ENODEV;
> + }
> +
> + fwspec_bridge = dev_iommu_fwspec_get(dt_to_dev(bridge->dt_node));
> + if ( fwspec_bridge->num_ids < 1 )
> + {
> + dev_err(dev, "Failed to find host bridge uTLB\n");
> + return -ENXIO;
> + }
> +
> + if ( fwspec->num_ids < 1 )
> + {
> + dev_err(dev, "Failed to find uTLB");
> + return -ENXIO;
> + }
> +
> + rcar4_pcie_osid_regs_init(bridge);
>
> - /* Let Xen know that the master device is protected by an IOMMU. */
> - dt_device_set_protected(dev_to_dt(dev));
> + ret = rcar4_pcie_osid_reg_alloc(bridge);
> + if ( ret < 0 )
> + {
> + dev_err(dev, "No unused OSID regs\n");
> + return ret;
> + }
> + reg_id = ret;
> +
> + osid = fwspec->ids[0] - utlb_osid0;
> + rcar4_pcie_osid_bdf_set(bridge, reg_id, osid, pdev->sbdf.bdf);
> + rcar4_pcie_bdf_msk_set(bridge, reg_id, 0);
> +
> + dev_info(dev, "Allocated OSID reg %u (OSID %u)\n", reg_id, osid);
> +
> + ret = ipmmu_assign_device(pdev->domain, devfn, dev, 0);
> + if ( ret )
> + {
> + rcar4_pcie_osid_bdf_clear(bridge, reg_id);
> + rcar4_pcie_osid_reg_free(bridge, reg_id);
> + return ret;
> + }
> + }
> +#endif
>
> dev_info(dev, "Added master device (IPMMU %s micro-TLBs %u)\n",
> dev_name(fwspec->iommu_dev), fwspec->num_ids);
> --
> 2.34.1
>
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |