:p
atchew
Login
So far our parsing of {iommu,msi}-map properties has always blindly assumed that the output specifiers will always have exactly 1 cell. This typically does happen to be the case, but is not actually enforced (and the PCI msi-map binding even explicitly states support for 0 or 1 cells) - as a result we've now ended up with dodgy DTs out in the field which depend on this behaviour to map a 1-cell specifier for a 2-cell provider, despite that being bogus per the bindings themselves. Since there is some potential use[1] in being able to map at least single input IDs to multi-cell output specifiers (and properly support 0-cell outputs as well), add support for properly parsing and using the target nodes' #cells values, albeit with the unfortunate complication of still having to work around expectations of the old behaviour too. -- Robin. Unlike single #{}-cell, it is complex to establish a linear relation between input 'id' and output specifier for multi-cell properties, thus it is always expected that len never going to be > 1. These changes have been tested on QEMU for the arm64 architecture. Since, this would also need update in dt-schema, raised PR[2] for the same. [1] https://lore.kernel.org/all/20250627-video_cb-v3-0-51e18c0ffbce@quicinc.com/ [2] PR for iommu-map dtschema: https://github.com/devicetree-org/dt-schema/pull/184 Robin, Could this series be pulled into an immutable branch/tag, if it doesn't make it into the v7.1 merge window? There are client changes dependent on it, So it would help to get them moving forward rather than waiting another cycle. Thanks, Vijay V13: - Fix bad_map handling in of_map_id(): 'cells' is re-initialized to 0 on each loop iteration, so the !bad_map guard was insufficient, cells stayed 0 for all entries after the first. Fix by explicitly setting cells=1 when bad_map is true on every iteration. - Collected Acked-by from Frank Li. Link to v12: https://patch.msgid.link/20260331-parse_iommu_cells-v12-0-decfd305eea9@oss.qualcomm.com V12: - Call of_node_put() unconditionally in imx_pcie_add_lut_by_rid() thereby addressing comments from Bjorn Helgaas. Link to v11: https://lore.kernel.org/r/20260325-parse_iommu_cells-v11-0-1fefa5c0e82c@oss.qualcomm.com V11: - Added explicit filter_np parameter to of_map_id() and of_map_msi_id() per Dmitry Baryshkov's review feedback, making the filter explicit instead of overloading arg->np as both input filter and output parameter. - Removed of_node_put() from inside of_map_id(), making the caller responsible for reference management. Updated of_msi_xlate() to properly handle reference counting. - Collected ACKed by tags, and fixed minor typos. Link to v10: https://lore.kernel.org/r/20260309-parse_iommu_cells-v10-0-c62fcaa5a1d8@oss.qualcomm.com V10: - Move of_map_iommu_id()/of_map_msi_id() from include/linux/of.h to drivers/of/base.c as out-of-line helpers per feedback from Marc Zyngier and Rob Herring. - Add kernel-doc to document both helpers for discoverability and usage clarity. - Fix of_map_msi_id() wrapper and all its callers (cdx_msi.c, irq-gic-its-msi-parent.c, drivers/of/irq.c) to correctly use the new struct of_phandle_args-based API with proper of_node_put() handling as per feeback from Dmitry. Link to v9: https://lore.kernel.org/r/20260301-parse_iommu_cells-v9-0-4d1bceecc5e1@oss.qualcomm.com V9: - Updated TO/CC list based on feedback to include all relevant maintainers. - No functional changes to the patches themselves. Link to V8: https://lore.kernel.org/all/20260226074245.3098486-1-vijayanand.jitta@oss.qualcomm.com/ V8: - Removed mentions of of_map_args from commit message to match code. Link to V7: https://lore.kernel.org/all/20260210101157.2145113-1-vijayanand.jitta@oss.qualcomm.com/ V7: - Removed of_map_id_args structure and replaced it with of_phandle_args as suggested by Dmitry. Link to V6: https://lore.kernel.org/all/20260121055400.937856-1-vijayanand.jitta@oss.qualcomm.com/ V6: - Fixed build error reported by kernel test bot. Link to V5: https://lore.kernel.org/all/20260118181125.1436036-1-vijayanand.jitta@oss.qualcomm.com/ V5: - Fixed Build Warnings. - Raised PR for iommu-map dtschema: https://github.com/devicetree-org/dt-schema/pull/184 Link to V4: https://lore.kernel.org/all/20251231114257.2382820-1-vijayanand.jitta@oss.qualcomm.com/ V4: - Added Reviewed-by tag. - Resolved warnings reported by kernel test bot, minor code reorganization. Link to V3: https://lore.kernel.org/all/20251221213602.2413124-1-vijayanand.jitta@oss.qualcomm.com/ V3: - Added Reviewed-by tag. - Updated of_map_id_args struct as a wrapper to of_phandle_args and added comment description as suggested by Rob Herring. Link to V2: https://lore.kernel.org/all/20251204095530.8627-1-vijayanand.jitta@oss.qualcomm.com/ V2: - Incorporated the patches from Robin that does the clean implementation. - Dropped the patches the were adding multi-map support from this series as suggested. V1: https://lore.kernel.org/all/cover.1762235099.git.charan.kalla@oss.qualcomm.com/ RFC: https://lore.kernel.org/all/20250928171718.436440-1-charan.kalla@oss.qualcomm.com/#r Signed-off-by: Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com> --- --- Charan Teja Kalla (1): of: Factor arguments passed to of_map_id() into a struct Robin Murphy (2): of: Add convenience wrappers for of_map_id() of: Respect #{iommu,msi}-cells in maps drivers/cdx/cdx_msi.c | 8 +- drivers/iommu/of_iommu.c | 6 +- drivers/irqchip/irq-gic-its-msi-parent.c | 11 +- drivers/of/base.c | 215 ++++++++++++++++++++++++------- drivers/of/irq.c | 11 +- drivers/pci/controller/dwc/pci-imx6.c | 34 +++-- drivers/pci/controller/pcie-apple.c | 6 +- drivers/xen/grant-dma-ops.c | 5 +- include/linux/of.h | 30 ++++- 9 files changed, 242 insertions(+), 84 deletions(-) --- base-commit: 3fa5e5702a82d259897bd7e209469bc06368bf31 change-id: 20260301-parse_iommu_cells-1c33768aebba Best regards, -- Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com>
From: Robin Murphy <robin.murphy@arm.com> Since we now have quite a few users parsing "iommu-map" and "msi-map" properties, give them some wrappers to conveniently encapsulate the appropriate sets of property names. This will also make it easier to then change of_map_id() to correctly account for specifier cells. Reviewed-by: Rob Herring (Arm) <robh@kernel.org> Reviewed-by: Frank Li <Frank.Li@nxp.com> Acked-by: Marc Zyngier <maz@kernel.org> Acked-by: Bjorn Helgaas <bhelgaas@google.com> Signed-off-by: Robin Murphy <robin.murphy@arm.com> Signed-off-by: Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com> --- drivers/cdx/cdx_msi.c | 3 +-- drivers/iommu/of_iommu.c | 4 +--- drivers/irqchip/irq-gic-its-msi-parent.c | 2 +- drivers/of/base.c | 38 ++++++++++++++++++++++++++++++++ drivers/of/irq.c | 3 +-- drivers/pci/controller/dwc/pci-imx6.c | 6 ++--- drivers/pci/controller/pcie-apple.c | 3 +-- drivers/xen/grant-dma-ops.c | 3 +-- include/linux/of.h | 18 +++++++++++++++ 9 files changed, 64 insertions(+), 16 deletions(-) diff --git a/drivers/cdx/cdx_msi.c b/drivers/cdx/cdx_msi.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/cdx/cdx_msi.c +++ b/drivers/cdx/cdx_msi.c @@ -XXX,XX +XXX,XX @@ static int cdx_msi_prepare(struct irq_domain *msi_domain, int ret; /* Retrieve device ID from requestor ID using parent device */ - ret = of_map_id(parent->of_node, cdx_dev->msi_dev_id, "msi-map", "msi-map-mask", - NULL, &dev_id); + ret = of_map_msi_id(parent->of_node, cdx_dev->msi_dev_id, NULL, &dev_id); if (ret) { dev_err(dev, "of_map_id failed for MSI: %d\n", ret); return ret; diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -XXX,XX +XXX,XX @@ static int of_iommu_configure_dev_id(struct device_node *master_np, struct of_phandle_args iommu_spec = { .args_count = 1 }; int err; - err = of_map_id(master_np, *id, "iommu-map", - "iommu-map-mask", &iommu_spec.np, - iommu_spec.args); + err = of_map_iommu_id(master_np, *id, &iommu_spec.np, iommu_spec.args); if (err) return err; diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/irqchip/irq-gic-its-msi-parent.c +++ b/drivers/irqchip/irq-gic-its-msi-parent.c @@ -XXX,XX +XXX,XX @@ static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u struct device_node *msi_ctrl __free(device_node) = NULL; - return of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &msi_ctrl, dev_id); + return of_map_msi_id(dev->of_node, dev->id, &msi_ctrl, dev_id); } static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, diff --git a/drivers/of/base.c b/drivers/of/base.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -XXX,XX +XXX,XX @@ int of_map_id(const struct device_node *np, u32 id, return 0; } EXPORT_SYMBOL_GPL(of_map_id); + +/** + * of_map_iommu_id - Translate an ID using "iommu-map" bindings. + * @np: root complex device node. + * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform + * stream/device ID) used as the lookup key in the iommu-map table. + * @target: optional pointer to a target device node. + * @id_out: optional pointer to receive the translated ID. + * + * Convenience wrapper around of_map_id() using "iommu-map" and "iommu-map-mask". + * + * Return: 0 on success or a standard error code on failure. + */ +int of_map_iommu_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out) +{ + return of_map_id(np, id, "iommu-map", "iommu-map-mask", target, id_out); +} +EXPORT_SYMBOL_GPL(of_map_iommu_id); + +/** + * of_map_msi_id - Translate an ID using "msi-map" bindings. + * @np: root complex device node. + * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform + * stream/device ID) used as the lookup key in the msi-map table. + * @target: optional pointer to a target device node. + * @id_out: optional pointer to receive the translated ID. + * + * Convenience wrapper around of_map_id() using "msi-map" and "msi-map-mask". + * + * Return: 0 on success or a standard error code on failure. + */ +int of_map_msi_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out) +{ + return of_map_id(np, id, "msi-map", "msi-map-mask", target, id_out); +} +EXPORT_SYMBOL_GPL(of_map_msi_id); diff --git a/drivers/of/irq.c b/drivers/of/irq.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -XXX,XX +XXX,XX @@ u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in) * "msi-map" or an "msi-parent" property. */ for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) { - if (!of_map_id(parent_dev->of_node, id_in, "msi-map", - "msi-map-mask", msi_np, &id_out)) + if (!of_map_msi_id(parent_dev->of_node, id_in, msi_np, &id_out)) break; if (!of_check_msi_parent(parent_dev->of_node, msi_np)) break; diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -XXX,XX +XXX,XX @@ static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid) u32 sid = 0; target = NULL; - err_i = of_map_id(dev->of_node, rid, "iommu-map", "iommu-map-mask", - &target, &sid_i); + err_i = of_map_iommu_id(dev->of_node, rid, &target, &sid_i); if (target) { of_node_put(target); } else { @@ -XXX,XX +XXX,XX @@ static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid) } target = NULL; - err_m = of_map_id(dev->of_node, rid, "msi-map", "msi-map-mask", - &target, &sid_m); + err_m = of_map_msi_id(dev->of_node, rid, &target, &sid_m); /* * err_m target diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -XXX,XX +XXX,XX @@ static int apple_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_d dev_dbg(&pdev->dev, "added to bus %s, index %d\n", pci_name(pdev->bus->self), port->idx); - err = of_map_id(port->pcie->dev->of_node, rid, "iommu-map", - "iommu-map-mask", NULL, &sid); + err = of_map_iommu_id(port->pcie->dev->of_node, rid, NULL, &sid); if (err) return err; diff --git a/drivers/xen/grant-dma-ops.c b/drivers/xen/grant-dma-ops.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/xen/grant-dma-ops.c +++ b/drivers/xen/grant-dma-ops.c @@ -XXX,XX +XXX,XX @@ static int xen_dt_grant_init_backend_domid(struct device *dev, struct pci_dev *pdev = to_pci_dev(dev); u32 rid = PCI_DEVID(pdev->bus->number, pdev->devfn); - if (of_map_id(np, rid, "iommu-map", "iommu-map-mask", &iommu_spec.np, - iommu_spec.args)) { + if (of_map_iommu_id(np, rid, &iommu_spec.np, iommu_spec.args)) { dev_dbg(dev, "Cannot translate ID\n"); return -ESRCH; } diff --git a/include/linux/of.h b/include/linux/of.h index XXXXXXX..XXXXXXX 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -XXX,XX +XXX,XX @@ int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, struct device_node **target, u32 *id_out); +int of_map_iommu_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out); + +int of_map_msi_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out); + phys_addr_t of_dma_get_max_cpu_address(struct device_node *np); struct kimage; @@ -XXX,XX +XXX,XX @@ static inline int of_map_id(const struct device_node *np, u32 id, return -EINVAL; } +static inline int of_map_iommu_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out) +{ + return -EINVAL; +} + +static inline int of_map_msi_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out) +{ + return -EINVAL; +} + static inline phys_addr_t of_dma_get_max_cpu_address(struct device_node *np) { return PHYS_ADDR_MAX; -- 2.34.1
From: Charan Teja Kalla <charan.kalla@oss.qualcomm.com> Change of_map_id() to take a pointer to struct of_phandle_args instead of passing target device node and translated IDs separately. Update all callers accordingly. Add an explicit filter_np parameter to of_map_id() and of_map_msi_id() to separate the filter input from the output. Previously, the target parameter served dual purpose: as an input filter (if non-NULL, only match entries targeting that node) and as an output (receiving the matched node with a reference held). Now filter_np is the explicit input filter and arg->np is the pure output. Previously, of_map_id() would call of_node_put() on the matched node when a filter was provided, making reference ownership inconsistent. Remove this internal of_node_put() call so that of_map_id() now always transfers ownership of the matched node reference to the caller via arg->np. Callers are now consistently responsible for releasing this reference with of_node_put(arg->np) when done. Acked-by: Frank Li <Frank.Li@nxp.com> Suggested-by: Rob Herring (Arm) <robh@kernel.org> Suggested-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> Signed-off-by: Charan Teja Kalla <charan.kalla@oss.qualcomm.com> Signed-off-by: Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com> --- drivers/cdx/cdx_msi.c | 7 ++-- drivers/iommu/of_iommu.c | 4 +- drivers/irqchip/irq-gic-its-msi-parent.c | 11 ++++-- drivers/of/base.c | 68 +++++++++++++++++--------------- drivers/of/irq.c | 10 ++++- drivers/pci/controller/dwc/pci-imx6.c | 32 +++++++-------- drivers/pci/controller/pcie-apple.c | 5 ++- drivers/xen/grant-dma-ops.c | 4 +- include/linux/of.h | 14 ++++--- 9 files changed, 89 insertions(+), 66 deletions(-) diff --git a/drivers/cdx/cdx_msi.c b/drivers/cdx/cdx_msi.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/cdx/cdx_msi.c +++ b/drivers/cdx/cdx_msi.c @@ -XXX,XX +XXX,XX @@ static int cdx_msi_prepare(struct irq_domain *msi_domain, struct device *dev, int nvec, msi_alloc_info_t *info) { + struct of_phandle_args msi_spec = {}; struct cdx_device *cdx_dev = to_cdx_device(dev); struct device *parent = cdx_dev->cdx->dev; struct msi_domain_info *msi_info; - u32 dev_id; int ret; /* Retrieve device ID from requestor ID using parent device */ - ret = of_map_msi_id(parent->of_node, cdx_dev->msi_dev_id, NULL, &dev_id); + ret = of_map_msi_id(parent->of_node, cdx_dev->msi_dev_id, NULL, &msi_spec); if (ret) { dev_err(dev, "of_map_id failed for MSI: %d\n", ret); return ret; } + of_node_put(msi_spec.np); #ifdef GENERIC_MSI_DOMAIN_OPS /* Set the device Id to be passed to the GIC-ITS */ - info->scratchpad[0].ul = dev_id; + info->scratchpad[0].ul = msi_spec.args[0]; #endif msi_info = msi_get_domain_info(msi_domain->parent); diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -XXX,XX +XXX,XX @@ static int of_iommu_configure_dev_id(struct device_node *master_np, struct device *dev, const u32 *id) { - struct of_phandle_args iommu_spec = { .args_count = 1 }; + struct of_phandle_args iommu_spec = {}; int err; - err = of_map_iommu_id(master_np, *id, &iommu_spec.np, iommu_spec.args); + err = of_map_iommu_id(master_np, *id, &iommu_spec); if (err) return err; diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/irqchip/irq-gic-its-msi-parent.c +++ b/drivers/irqchip/irq-gic-its-msi-parent.c @@ -XXX,XX +XXX,XX @@ static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev, static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u32 *dev_id, phys_addr_t *pa) { + struct device_node *msi_ctrl __free(device_node) = NULL; + struct of_phandle_args msi_spec = {}; struct of_phandle_iterator it; int ret; @@ -XXX,XX +XXX,XX @@ static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u } } - struct device_node *msi_ctrl __free(device_node) = NULL; - - return of_map_msi_id(dev->of_node, dev->id, &msi_ctrl, dev_id); + ret = of_map_msi_id(dev->of_node, dev->id, NULL, &msi_spec); + if (!ret) { + msi_ctrl = msi_spec.np; + *dev_id = msi_spec.args[0]; + } + return ret; } static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, diff --git a/drivers/of/base.c b/drivers/of/base.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -XXX,XX +XXX,XX @@ int of_find_last_cache_level(unsigned int cpu) * @id: device ID to map. * @map_name: property name of the map to use. * @map_mask_name: optional property name of the mask to use. - * @target: optional pointer to a target device node. - * @id_out: optional pointer to receive the translated ID. + * @filter_np: optional device node to filter matches by, or NULL to match any. + * If non-NULL, only map entries targeting this node will be matched. + * @arg: pointer to a &struct of_phandle_args for the result. On success, + * @arg->args[0] will contain the translated ID. If a map entry was + * matched, @arg->np will be set to the target node with a reference + * held that the caller must release with of_node_put(). * * Given a device ID, look up the appropriate implementation-defined * platform ID and/or the target device which receives transactions on that - * ID, as per the "iommu-map" and "msi-map" bindings. Either of @target or - * @id_out may be NULL if only the other is required. If @target points to - * a non-NULL device node pointer, only entries targeting that node will be - * matched; if it points to a NULL value, it will receive the device node of - * the first matching target phandle, with a reference held. + * ID, as per the "iommu-map" and "msi-map" bindings. * * Return: 0 on success or a standard error code on failure. */ int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, - struct device_node **target, u32 *id_out) + const struct device_node *filter_np, struct of_phandle_args *arg) { u32 map_mask, masked_id; int map_len; const __be32 *map = NULL; - if (!np || !map_name || (!target && !id_out)) + if (!np || !map_name || !arg) return -EINVAL; map = of_get_property(np, map_name, &map_len); if (!map) { - if (target) + if (filter_np) return -ENODEV; /* Otherwise, no map implies no translation */ - *id_out = id; + arg->args[0] = id; + arg->args_count = 1; return 0; } @@ -XXX,XX +XXX,XX @@ int of_map_id(const struct device_node *np, u32 id, if (!phandle_node) return -ENODEV; - if (target) { - if (*target) - of_node_put(phandle_node); - else - *target = phandle_node; - - if (*target != phandle_node) - continue; + if (filter_np && filter_np != phandle_node) { + of_node_put(phandle_node); + continue; } - if (id_out) - *id_out = masked_id - id_base + out_base; + arg->np = phandle_node; + arg->args[0] = masked_id - id_base + out_base; + arg->args_count = 1; pr_debug("%pOF: %s, using mask %08x, id-base: %08x, out-base: %08x, length: %08x, id: %08x -> %08x\n", np, map_name, map_mask, id_base, out_base, @@ -XXX,XX +XXX,XX @@ int of_map_id(const struct device_node *np, u32 id, } pr_info("%pOF: no %s translation for id 0x%x on %pOF\n", np, map_name, - id, target && *target ? *target : NULL); + id, filter_np); /* Bypasses translation */ - if (id_out) - *id_out = id; + arg->args[0] = id; + arg->args_count = 1; return 0; } EXPORT_SYMBOL_GPL(of_map_id); @@ -XXX,XX +XXX,XX @@ EXPORT_SYMBOL_GPL(of_map_id); * @np: root complex device node. * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform * stream/device ID) used as the lookup key in the iommu-map table. - * @target: optional pointer to a target device node. - * @id_out: optional pointer to receive the translated ID. + * @arg: pointer to a &struct of_phandle_args for the result. On success, + * @arg->args[0] contains the translated ID. If a map entry was matched, + * @arg->np holds a reference to the target node that the caller must + * release with of_node_put(). * * Convenience wrapper around of_map_id() using "iommu-map" and "iommu-map-mask". * * Return: 0 on success or a standard error code on failure. */ int of_map_iommu_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out) + struct of_phandle_args *arg) { - return of_map_id(np, id, "iommu-map", "iommu-map-mask", target, id_out); + return of_map_id(np, id, "iommu-map", "iommu-map-mask", NULL, arg); } EXPORT_SYMBOL_GPL(of_map_iommu_id); @@ -XXX,XX +XXX,XX @@ EXPORT_SYMBOL_GPL(of_map_iommu_id); * @np: root complex device node. * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform * stream/device ID) used as the lookup key in the msi-map table. - * @target: optional pointer to a target device node. - * @id_out: optional pointer to receive the translated ID. + * @filter_np: optional MSI controller node to filter matches by, or NULL + * to match any. If non-NULL, only map entries targeting this node will + * be matched. + * @arg: pointer to a &struct of_phandle_args for the result. On success, + * @arg->args[0] contains the translated ID. If a map entry was matched, + * @arg->np holds a reference to the target node that the caller must + * release with of_node_put(). * * Convenience wrapper around of_map_id() using "msi-map" and "msi-map-mask". * * Return: 0 on success or a standard error code on failure. */ int of_map_msi_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out) + const struct device_node *filter_np, struct of_phandle_args *arg) { - return of_map_id(np, id, "msi-map", "msi-map-mask", target, id_out); + return of_map_id(np, id, "msi-map", "msi-map-mask", filter_np, arg); } EXPORT_SYMBOL_GPL(of_map_msi_id); diff --git a/drivers/of/irq.c b/drivers/of/irq.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -XXX,XX +XXX,XX @@ u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in) * "msi-map" or an "msi-parent" property. */ for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) { - if (!of_map_msi_id(parent_dev->of_node, id_in, msi_np, &id_out)) + struct of_phandle_args msi_spec = {}; + + if (!of_map_msi_id(parent_dev->of_node, id_in, *msi_np, &msi_spec)) { + id_out = msi_spec.args[0]; + if (!*msi_np) + *msi_np = msi_spec.np; + else + of_node_put(msi_spec.np); break; + } if (!of_check_msi_parent(parent_dev->of_node, msi_np)) break; } diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -XXX,XX +XXX,XX @@ static void imx_pcie_remove_lut(struct imx_pcie *imx_pcie, u16 rid) static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid) { + struct of_phandle_args iommu_spec = {}; + struct of_phandle_args msi_spec = {}; struct device *dev = imx_pcie->pci->dev; - struct device_node *target; u32 sid_i, sid_m; int err_i, err_m; u32 sid = 0; - target = NULL; - err_i = of_map_iommu_id(dev->of_node, rid, &target, &sid_i); - if (target) { - of_node_put(target); - } else { + err_i = of_map_iommu_id(dev->of_node, rid, &iommu_spec); + if (!err_i) + sid_i = iommu_spec.args[0]; + of_node_put(iommu_spec.np); + if (!err_i && !iommu_spec.np) { /* - * "target == NULL && err_i == 0" means RID out of map range. - * Use 1:1 map RID to streamID. Hardware can't support this - * because the streamID is only 6 bits + * "iommu_spec.np == NULL && err_i == 0" means RID out of map + * range. Use 1:1 map RID to streamID. Hardware can't support + * this because the streamID is only 6 bits. */ err_i = -EINVAL; } - target = NULL; - err_m = of_map_msi_id(dev->of_node, rid, &target, &sid_m); - + err_m = of_map_msi_id(dev->of_node, rid, NULL, &msi_spec); + if (!err_m) + sid_m = msi_spec.args[0]; + of_node_put(msi_spec.np); /* - * err_m target + * err_m msi_spec.np * 0 NULL RID out of range. Use 1:1 map RID to * streamID, Current hardware can't * support it, so return -EINVAL. @@ -XXX,XX +XXX,XX @@ static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid) * 0 != NULL Get correct streamID from RID * != 0 != NULL Invalid combination */ - if (!err_m && !target) + if (!err_m && !msi_spec.np) return -EINVAL; - else if (target) - of_node_put(target); /* Find streamID map entry for RID in msi-map */ /* * msi-map iommu-map diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -XXX,XX +XXX,XX @@ static int apple_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_d { u32 sid, rid = pci_dev_id(pdev); struct apple_pcie_port *port; + struct of_phandle_args iommu_spec = {}; int idx, err; port = apple_pcie_get_port(pdev); @@ -XXX,XX +XXX,XX @@ static int apple_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_d dev_dbg(&pdev->dev, "added to bus %s, index %d\n", pci_name(pdev->bus->self), port->idx); - err = of_map_iommu_id(port->pcie->dev->of_node, rid, NULL, &sid); + err = of_map_iommu_id(port->pcie->dev->of_node, rid, &iommu_spec); if (err) return err; + of_node_put(iommu_spec.np); + sid = iommu_spec.args[0]; mutex_lock(&port->pcie->lock); idx = bitmap_find_free_region(port->sid_map, port->sid_map_sz, 0); diff --git a/drivers/xen/grant-dma-ops.c b/drivers/xen/grant-dma-ops.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/xen/grant-dma-ops.c +++ b/drivers/xen/grant-dma-ops.c @@ -XXX,XX +XXX,XX @@ static int xen_dt_grant_init_backend_domid(struct device *dev, struct device_node *np, domid_t *backend_domid) { - struct of_phandle_args iommu_spec = { .args_count = 1 }; + struct of_phandle_args iommu_spec = {}; if (dev_is_pci(dev)) { struct pci_dev *pdev = to_pci_dev(dev); u32 rid = PCI_DEVID(pdev->bus->number, pdev->devfn); - if (of_map_iommu_id(np, rid, &iommu_spec.np, iommu_spec.args)) { + if (of_map_iommu_id(np, rid, &iommu_spec)) { dev_dbg(dev, "Cannot translate ID\n"); return -ESRCH; } diff --git a/include/linux/of.h b/include/linux/of.h index XXXXXXX..XXXXXXX 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -XXX,XX +XXX,XX @@ bool of_console_check(const struct device_node *dn, char *name, int index); int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, - struct device_node **target, u32 *id_out); + const struct device_node *filter_np, struct of_phandle_args *arg); int of_map_iommu_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out); + struct of_phandle_args *arg); int of_map_msi_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out); + const struct device_node *filter_np, struct of_phandle_args *arg); phys_addr_t of_dma_get_max_cpu_address(struct device_node *np); @@ -XXX,XX +XXX,XX @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag static inline int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, - struct device_node **target, u32 *id_out) + const struct device_node *filter_np, + struct of_phandle_args *arg) { return -EINVAL; } static inline int of_map_iommu_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out) + struct of_phandle_args *arg) { return -EINVAL; } static inline int of_map_msi_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out) + const struct device_node *filter_np, + struct of_phandle_args *arg) { return -EINVAL; } -- 2.34.1
From: Robin Murphy <robin.murphy@arm.com> So far our parsing of {iommu,msi}-map properties has always blindly assumed that the output specifiers will always have exactly 1 cell. This typically does happen to be the case, but is not actually enforced (and the PCI msi-map binding even explicitly states support for 0 or 1 cells) - as a result we've now ended up with dodgy DTs out in the field which depend on this behaviour to map a 1-cell specifier for a 2-cell provider, despite that being bogus per the bindings themselves. Since there is some potential use in being able to map at least single input IDs to multi-cell output specifiers (and properly support 0-cell outputs as well), add support for properly parsing and using the target nodes' #cells values, albeit with the unfortunate complication of still having to work around expectations of the old behaviour too. Since there are multi-cell output specifiers, the callers of of_map_id() may need to get the exact cell output value for further processing. Update of_map_id() to set args_count in the output to reflect the actual number of output specifier cells. Signed-off-by: Robin Murphy <robin.murphy@arm.com> Signed-off-by: Charan Teja Kalla <charan.kalla@oss.qualcomm.com> Signed-off-by: Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com> --- drivers/of/base.c | 157 +++++++++++++++++++++++++++++++++++++++++------------ include/linux/of.h | 6 +- 2 files changed, 125 insertions(+), 38 deletions(-) diff --git a/drivers/of/base.c b/drivers/of/base.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -XXX,XX +XXX,XX @@ int of_find_last_cache_level(unsigned int cpu) return cache_level; } +/* + * Some DTs have an iommu-map targeting a 2-cell IOMMU node while + * specifying only 1 cell. Fortunately they all consist of value '1' + * as the 2nd cell entry with the same target, so check for that pattern. + * + * Example: + * IOMMU node: + * #iommu-cells = <2>; + * + * Device node: + * iommu-map = <0x0000 &smmu 0x0000 0x1>, + * <0x0100 &smmu 0x0100 0x1>; + */ +static bool of_check_bad_map(const __be32 *map, int len) +{ + __be32 phandle = map[1]; + + if (len % 4) + return false; + for (int i = 0; i < len; i += 4) { + if (map[i + 1] != phandle || map[i + 3] != cpu_to_be32(1)) + return false; + } + return true; +} + /** * of_map_id - Translate an ID through a downstream mapping. * @np: root complex device node. * @id: device ID to map. * @map_name: property name of the map to use. + * @cells_name: property name of target specifier cells. * @map_mask_name: optional property name of the mask to use. * @filter_np: optional device node to filter matches by, or NULL to match any. * If non-NULL, only map entries targeting this node will be matched. * @arg: pointer to a &struct of_phandle_args for the result. On success, - * @arg->args[0] will contain the translated ID. If a map entry was - * matched, @arg->np will be set to the target node with a reference - * held that the caller must release with of_node_put(). + * @arg->args_count will be set to the number of output specifier cells + * as defined by @cells_name in the target node, and + * @arg->args[0..args_count-1] will contain the translated output + * specifier values. If a map entry was matched, @arg->np will be set + * to the target node with a reference held that the caller must release + * with of_node_put(). * * Given a device ID, look up the appropriate implementation-defined * platform ID and/or the target device which receives transactions on that @@ -XXX,XX +XXX,XX @@ int of_find_last_cache_level(unsigned int cpu) * Return: 0 on success or a standard error code on failure. */ int of_map_id(const struct device_node *np, u32 id, - const char *map_name, const char *map_mask_name, + const char *map_name, const char *cells_name, + const char *map_mask_name, const struct device_node *filter_np, struct of_phandle_args *arg) { u32 map_mask, masked_id; - int map_len; + int map_bytes, map_len, offset = 0; + bool bad_map = false; const __be32 *map = NULL; if (!np || !map_name || !arg) return -EINVAL; - map = of_get_property(np, map_name, &map_len); + map = of_get_property(np, map_name, &map_bytes); if (!map) { if (filter_np) return -ENODEV; @@ -XXX,XX +XXX,XX @@ int of_map_id(const struct device_node *np, u32 id, return 0; } - if (!map_len || map_len % (4 * sizeof(*map))) { - pr_err("%pOF: Error: Bad %s length: %d\n", np, - map_name, map_len); - return -EINVAL; - } + if (map_bytes % sizeof(*map)) + goto err_map_len; + map_len = map_bytes / sizeof(*map); /* The default is to select all bits. */ map_mask = 0xffffffff; @@ -XXX,XX +XXX,XX @@ int of_map_id(const struct device_node *np, u32 id, of_property_read_u32(np, map_mask_name, &map_mask); masked_id = map_mask & id; - for ( ; map_len > 0; map_len -= 4 * sizeof(*map), map += 4) { + + while (offset < map_len) { struct device_node *phandle_node; - u32 id_base = be32_to_cpup(map + 0); - u32 phandle = be32_to_cpup(map + 1); - u32 out_base = be32_to_cpup(map + 2); - u32 id_len = be32_to_cpup(map + 3); + u32 id_base, phandle, id_len, id_off, cells = 0; + const __be32 *out_base; + + if (map_len - offset < 2) + goto err_map_len; + + id_base = be32_to_cpup(map + offset); if (id_base & ~map_mask) { - pr_err("%pOF: Invalid %s translation - %s-mask (0x%x) ignores id-base (0x%x)\n", - np, map_name, map_name, - map_mask, id_base); + pr_err("%pOF: Invalid %s translation - %s (0x%x) ignores id-base (0x%x)\n", + np, map_name, map_mask_name, map_mask, id_base); return -EFAULT; } - if (masked_id < id_base || masked_id >= id_base + id_len) - continue; - + phandle = be32_to_cpup(map + offset + 1); phandle_node = of_find_node_by_phandle(phandle); if (!phandle_node) return -ENODEV; + if (bad_map) { + cells = 1; + } else if (of_property_read_u32(phandle_node, cells_name, &cells)) { + pr_err("%pOF: missing %s property\n", phandle_node, cells_name); + of_node_put(phandle_node); + return -EINVAL; + } + + if (map_len - offset < 3 + cells) { + of_node_put(phandle_node); + goto err_map_len; + } + + if (offset == 0 && cells == 2) { + bad_map = of_check_bad_map(map, map_len); + if (bad_map) { + pr_warn_once("%pOF: %s mismatches target %s, assuming extra cell of 0\n", + np, map_name, cells_name); + cells = 1; + } + } + + out_base = map + offset + 2; + offset += 3 + cells; + + id_len = be32_to_cpup(map + offset - 1); + if (id_len > 1 && cells > 1) { + /* + * With 1 output cell we reasonably assume its value + * has a linear relationship to the input; with more, + * we'd need help from the provider to know what to do. + */ + pr_err("%pOF: Unsupported %s - cannot handle %d-ID range with %d-cell output specifier\n", + np, map_name, id_len, cells); + of_node_put(phandle_node); + return -EINVAL; + } + id_off = masked_id - id_base; + if (masked_id < id_base || id_off >= id_len) { + of_node_put(phandle_node); + continue; + } + if (filter_np && filter_np != phandle_node) { of_node_put(phandle_node); continue; } arg->np = phandle_node; - arg->args[0] = masked_id - id_base + out_base; - arg->args_count = 1; + for (int i = 0; i < cells; i++) + arg->args[i] = id_off + be32_to_cpu(out_base[i]); + arg->args_count = cells; pr_debug("%pOF: %s, using mask %08x, id-base: %08x, out-base: %08x, length: %08x, id: %08x -> %08x\n", - np, map_name, map_mask, id_base, out_base, - id_len, id, masked_id - id_base + out_base); + np, map_name, map_mask, id_base, be32_to_cpup(out_base), + id_len, id, id_off + be32_to_cpup(out_base)); return 0; } @@ -XXX,XX +XXX,XX @@ int of_map_id(const struct device_node *np, u32 id, arg->args[0] = id; arg->args_count = 1; return 0; + +err_map_len: + pr_err("%pOF: Error: Bad %s length: %d\n", np, map_name, map_bytes); + return -EINVAL; } EXPORT_SYMBOL_GPL(of_map_id); @@ -XXX,XX +XXX,XX @@ EXPORT_SYMBOL_GPL(of_map_id); * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform * stream/device ID) used as the lookup key in the iommu-map table. * @arg: pointer to a &struct of_phandle_args for the result. On success, - * @arg->args[0] contains the translated ID. If a map entry was matched, - * @arg->np holds a reference to the target node that the caller must - * release with of_node_put(). + * @arg->args_count will be set to the number of output specifier cells + * and @arg->args[0..args_count-1] will contain the translated output + * specifier values. If a map entry was matched, @arg->np holds a + * reference to the target node that the caller must release with + * of_node_put(). * - * Convenience wrapper around of_map_id() using "iommu-map" and "iommu-map-mask". + * Convenience wrapper around of_map_id() using "iommu-map", "#iommu-cells", + * and "iommu-map-mask". * * Return: 0 on success or a standard error code on failure. */ int of_map_iommu_id(const struct device_node *np, u32 id, struct of_phandle_args *arg) { - return of_map_id(np, id, "iommu-map", "iommu-map-mask", NULL, arg); + return of_map_id(np, id, "iommu-map", "#iommu-cells", "iommu-map-mask", NULL, arg); } EXPORT_SYMBOL_GPL(of_map_iommu_id); @@ -XXX,XX +XXX,XX @@ EXPORT_SYMBOL_GPL(of_map_iommu_id); * to match any. If non-NULL, only map entries targeting this node will * be matched. * @arg: pointer to a &struct of_phandle_args for the result. On success, - * @arg->args[0] contains the translated ID. If a map entry was matched, - * @arg->np holds a reference to the target node that the caller must - * release with of_node_put(). + * @arg->args_count will be set to the number of output specifier cells + * and @arg->args[0..args_count-1] will contain the translated output + * specifier values. If a map entry was matched, @arg->np holds a + * reference to the target node that the caller must release with + * of_node_put(). * - * Convenience wrapper around of_map_id() using "msi-map" and "msi-map-mask". + * Convenience wrapper around of_map_id() using "msi-map", "#msi-cells", + * and "msi-map-mask". * * Return: 0 on success or a standard error code on failure. */ int of_map_msi_id(const struct device_node *np, u32 id, const struct device_node *filter_np, struct of_phandle_args *arg) { - return of_map_id(np, id, "msi-map", "msi-map-mask", filter_np, arg); + return of_map_id(np, id, "msi-map", "#msi-cells", "msi-map-mask", filter_np, arg); } EXPORT_SYMBOL_GPL(of_map_msi_id); diff --git a/include/linux/of.h b/include/linux/of.h index XXXXXXX..XXXXXXX 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -XXX,XX +XXX,XX @@ const char *of_prop_next_string(const struct property *prop, const char *cur); bool of_console_check(const struct device_node *dn, char *name, int index); int of_map_id(const struct device_node *np, u32 id, - const char *map_name, const char *map_mask_name, + const char *map_name, const char *cells_name, + const char *map_mask_name, const struct device_node *filter_np, struct of_phandle_args *arg); int of_map_iommu_id(const struct device_node *np, u32 id, @@ -XXX,XX +XXX,XX @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag } static inline int of_map_id(const struct device_node *np, u32 id, - const char *map_name, const char *map_mask_name, + const char *map_name, const char *cells_name, + const char *map_mask_name, const struct device_node *filter_np, struct of_phandle_args *arg) { -- 2.34.1
So far our parsing of {iommu,msi}-map properties has always blindly assumed that the output specifiers will always have exactly 1 cell. This typically does happen to be the case, but is not actually enforced (and the PCI msi-map binding even explicitly states support for 0 or 1 cells) - as a result we've now ended up with dodgy DTs out in the field which depend on this behaviour to map a 1-cell specifier for a 2-cell provider, despite that being bogus per the bindings themselves. Since there is some potential use[1] in being able to map at least single input IDs to multi-cell output specifiers (and properly support 0-cell outputs as well), add support for properly parsing and using the target nodes' #cells values, albeit with the unfortunate complication of still having to work around expectations of the old behaviour too. -- Robin. Unlike single #{}-cell, it is complex to establish a linear relation between input 'id' and output specifier for multi-cell properties, thus it is always expected that len never going to be > 1. These changes have been tested on QEMU for the arm64 architecture and on the glymur platform [3]. Since, this would also need update in dt-schema, raised PR[2] for the same. [1] https://lore.kernel.org/all/20250627-video_cb-v3-0-51e18c0ffbce@quicinc.com/ [2] PR for iommu-map dtschema: https://github.com/devicetree-org/dt-schema/pull/184 [3] https://lore.kernel.org/all/20260515-glymur-v6-5-f6a99cb43a24@oss.qualcomm.com/ V16: - Patch 2: Fix potential NULL pointer dereference in of_msi_xlate() when msi_np is NULL. Guard the of_check_msi_parent() call with "if (msi_np && ...)" to handle the case where the caller passes NULL for msi_np, as documented. Reported by Sashiko [1]. - Patch 2: Fix OF node refcount leak in of_msi_map_get_device_domain(): np was never released after of_msi_xlate() transferred ownership. - Patch 3: Default to 1-cell output specifier when the target node lacks the #iommu-cells/#msi-cells property, for backward compatibility with controllers that predate the property (e.g. arm,gic-v2m-frame). Reported by Sashiko [1]. - Patch 3: Add !cells_name to the initial parameter guard in of_map_id() to prevent a crash if cells_name is NULL. Reported by Sashiko [1]. [1] https://sashiko.dev/#/patchset/20260520-parse_iommu_cells-v15-0-b5f99ad4e7e8@oss.qualcomm.com Link to V15: https://lore.kernel.org/all/20260520-parse_iommu_cells-v15-0-b5f99ad4e7e8@oss.qualcomm.com/ V15: Address Sashiko AI review comments on v14: Patch 2: - [Critical] pci-imx6: pass &msi_filter (not NULL) to of_map_msi_id() so that of_map_id() returns -ENODEV when msi-map is absent, preventing the '!err_m && !msi_spec.np' path from incorrectly returning -EINVAL - [High] of_map_id(): explicitly set arg->np = NULL before any bypass path so callers can safely call of_node_put(arg->np) on all return paths - [Medium] of_msi_xlate(): pass msi_np directly to of_map_msi_id() and of_check_msi_parent() (removing the local_np/np indirection), and use 'break' (not 'continue') when msi_spec.np is NULL so that msi-parent bindings are still checked when msi-map is present but has no match - Guard 'id_out = msi_spec.args[0]' with 'args_count > 0' in of_msi_xlate() to correctly handle 0-cell MSI output specifiers - Use of_node_get() + unconditional of_node_put() in of_msi_xlate() for clearer reference ownership Patch 3: - [Critical] of_map_id(): add 'cells > MAX_PHANDLE_ARGS' check before using cells as an array index to prevent stack buffer overflow - [High] of_map_id(): the MAX_PHANDLE_ARGS bound on cells also prevents integer overflow in the '3 + cells' length check - [High] of_map_id(): fix misleading bad-map workaround message from "assuming extra cell of 0" to "treating as 1-cell output" to accurately describe the actual behavior - [Medium] of_msi_xlate(): guard 'id_out = msi_spec.args[0]' with 'args_count > 0' to preserve id_in for 0-cell MSI output specifiers Link to v14: https://patch.msgid.link/20260424-parse_iommu_cells-v14-0-fd02f11b6c38@oss.qualcomm.com V14: - Updated Patch 2 ("of: Factor arguments passed to of_map_id() into a struct") to fix below two issues in of_msi_xlate() that were introduced by the API refactoring: 1) The refactoring changed of_map_id()'s dual-purpose **target parameter to an explicit filter_np parameter. In of_msi_xlate(), this caused of_map_msi_id() to return 0 (pass-through) instead of -ENODEV when a node has no msi-map, terminating the device hierarchy walk prematurely before reaching the root complex node that has the msi-map. This broke MSI allocation for PCIe endpoint devices (e.g., wcn7850 Wi-Fi on ARM64). 2) Additionally, fsl_mc_get_msi_id() passes msi_np == NULL to of_msi_xlate(), which would dereference NULL with the new API. Link to v13: https://patch.msgid.link/20260408-parse_iommu_cells-v13-0-fa921e92661b@oss.qualcomm.com V13: - Fix bad_map handling in of_map_id(): 'cells' is re-initialized to 0 on each loop iteration, so the !bad_map guard was insufficient, cells stayed 0 for all entries after the first. Fix by explicitly setting cells=1 when bad_map is true on every iteration. - Collected Acked-by from Frank Li. Link to v12: https://patch.msgid.link/20260331-parse_iommu_cells-v12-0-decfd305eea9@oss.qualcomm.com V12: - Call of_node_put() unconditionally in imx_pcie_add_lut_by_rid() thereby addressing comments from Bjorn Helgaas. Link to v11: https://lore.kernel.org/r/20260325-parse_iommu_cells-v11-0-1fefa5c0e82c@oss.qualcomm.com V11: - Added explicit filter_np parameter to of_map_id() and of_map_msi_id() per Dmitry Baryshkov's review feedback, making the filter explicit instead of overloading arg->np as both input filter and output parameter. - Removed of_node_put() from inside of_map_id(), making the caller responsible for reference management. Updated of_msi_xlate() to properly handle reference counting. - Collected ACKed by tags, and fixed minor typos. Link to v10: https://lore.kernel.org/r/20260309-parse_iommu_cells-v10-0-c62fcaa5a1d8@oss.qualcomm.com V10: - Move of_map_iommu_id()/of_map_msi_id() from include/linux/of.h to drivers/of/base.c as out-of-line helpers per feedback from Marc Zyngier and Rob Herring. - Add kernel-doc to document both helpers for discoverability and usage clarity. - Fix of_map_msi_id() wrapper and all its callers (cdx_msi.c, irq-gic-its-msi-parent.c, drivers/of/irq.c) to correctly use the new struct of_phandle_args-based API with proper of_node_put() handling as per feeback from Dmitry. Link to v9: https://lore.kernel.org/r/20260301-parse_iommu_cells-v9-0-4d1bceecc5e1@oss.qualcomm.com V9: - Updated TO/CC list based on feedback to include all relevant maintainers. - No functional changes to the patches themselves. Link to V8: https://lore.kernel.org/all/20260226074245.3098486-1-vijayanand.jitta@oss.qualcomm.com/ V8: - Removed mentions of of_map_args from commit message to match code. Link to V7: https://lore.kernel.org/all/20260210101157.2145113-1-vijayanand.jitta@oss.qualcomm.com/ V7: - Removed of_map_id_args structure and replaced it with of_phandle_args as suggested by Dmitry. Link to V6: https://lore.kernel.org/all/20260121055400.937856-1-vijayanand.jitta@oss.qualcomm.com/ V6: - Fixed build error reported by kernel test bot. Link to V5: https://lore.kernel.org/all/20260118181125.1436036-1-vijayanand.jitta@oss.qualcomm.com/ V5: - Fixed Build Warnings. - Raised PR for iommu-map dtschema: https://github.com/devicetree-org/dt-schema/pull/184 Link to V4: https://lore.kernel.org/all/20251231114257.2382820-1-vijayanand.jitta@oss.qualcomm.com/ V4: - Added Reviewed-by tag. - Resolved warnings reported by kernel test bot, minor code reorganization. Link to V3: https://lore.kernel.org/all/20251221213602.2413124-1-vijayanand.jitta@oss.qualcomm.com/ V3: - Added Reviewed-by tag. - Updated of_map_id_args struct as a wrapper to of_phandle_args and added comment description as suggested by Rob Herring. Link to V2: https://lore.kernel.org/all/20251204095530.8627-1-vijayanand.jitta@oss.qualcomm.com/ V2: - Incorporated the patches from Robin that does the clean implementation. - Dropped the patches the were adding multi-map support from this series as suggested. V1: https://lore.kernel.org/all/cover.1762235099.git.charan.kalla@oss.qualcomm.com/ RFC: https://lore.kernel.org/all/20250928171718.436440-1-charan.kalla@oss.qualcomm.com/#r Signed-off-by: Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com> --- To: Nipun Gupta <nipun.gupta@amd.com> To: Nikhil Agarwal <nikhil.agarwal@amd.com> To: Joerg Roedel <joro@8bytes.org> To: Will Deacon <will@kernel.org> To: Robin Murphy <robin.murphy@arm.com> To: Lorenzo Pieralisi <lpieralisi@kernel.org> To: Marc Zyngier <maz@kernel.org> To: Thomas Gleixner <tglx@kernel.org> To: Rob Herring <robh@kernel.org> To: Saravana Kannan <saravanak@kernel.org> To: Richard Zhu <hongxing.zhu@nxp.com> To: Lucas Stach <l.stach@pengutronix.de> To: Krzysztof Wilczy\u0144ski <kwilczynski@kernel.org> To: Manivannan Sadhasivam <mani@kernel.org> To: Bjorn Helgaas <bhelgaas@google.com> To: Frank Li <Frank.Li@nxp.com> To: Sascha Hauer <s.hauer@pengutronix.de> To: Pengutronix Kernel Team <kernel@pengutronix.de> To: Fabio Estevam <festevam@gmail.com> To: Juergen Gross <jgross@suse.com> To: Stefano Stabellini <sstabellini@kernel.org> To: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com> Cc: linux-arm-msm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: iommu@lists.linux.dev Cc: linux-arm-kernel@lists.infradead.org Cc: devicetree@vger.kernel.org Cc: linux-pci@vger.kernel.org Cc: imx@lists.linux.dev Cc: xen-devel@lists.xenproject.org --- To: Nipun Gupta <nipun.gupta@amd.com> To: Nikhil Agarwal <nikhil.agarwal@amd.com> To: Joerg Roedel <joro@8bytes.org> To: Will Deacon <will@kernel.org> To: Robin Murphy <robin.murphy@arm.com> To: Lorenzo Pieralisi <lpieralisi@kernel.org> To: Marc Zyngier <maz@kernel.org> To: Thomas Gleixner <tglx@kernel.org> To: Rob Herring <robh@kernel.org> To: Saravana Kannan <saravanak@kernel.org> To: Richard Zhu <hongxing.zhu@nxp.com> To: Lucas Stach <l.stach@pengutronix.de> To: Krzysztof Wilczy\u0144ski <kwilczynski@kernel.org> To: Manivannan Sadhasivam <mani@kernel.org> To: Bjorn Helgaas <bhelgaas@google.com> To: Frank Li <Frank.Li@nxp.com> To: Sascha Hauer <s.hauer@pengutronix.de> To: Pengutronix Kernel Team <kernel@pengutronix.de> To: Fabio Estevam <festevam@gmail.com> To: Juergen Gross <jgross@suse.com> To: Stefano Stabellini <sstabellini@kernel.org> To: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com> Cc: linux-arm-msm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: iommu@lists.linux.dev Cc: linux-arm-kernel@lists.infradead.org Cc: devicetree@vger.kernel.org Cc: linux-pci@vger.kernel.org Cc: imx@lists.linux.dev Cc: xen-devel@lists.xenproject.org --- Charan Teja Kalla (1): of: Factor arguments passed to of_map_id() into a struct Robin Murphy (2): of: Add convenience wrappers for of_map_id() of: Respect #{iommu,msi}-cells in maps drivers/cdx/cdx_msi.c | 10 +- drivers/iommu/of_iommu.c | 6 +- drivers/irqchip/irq-gic-its-msi-parent.c | 10 +- drivers/of/base.c | 227 +++++++++++++++++++++++++------ drivers/of/irq.c | 25 +++- drivers/pci/controller/dwc/pci-imx6.c | 55 ++++---- drivers/pci/controller/pcie-apple.c | 6 +- drivers/xen/grant-dma-ops.c | 5 +- include/linux/of.h | 32 ++++- 9 files changed, 277 insertions(+), 99 deletions(-) --- base-commit: e98d21c170b01ddef366f023bbfcf6b31509fa83 change-id: 20260301-parse_iommu_cells-1c33768aebba Best regards, -- Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com> --- Charan Teja Kalla (1): of: Factor arguments passed to of_map_id() into a struct Robin Murphy (2): of: Add convenience wrappers for of_map_id() of: Respect #{iommu,msi}-cells in maps drivers/cdx/cdx_msi.c | 10 +- drivers/iommu/of_iommu.c | 6 +- drivers/irqchip/irq-gic-its-msi-parent.c | 10 +- drivers/of/base.c | 227 +++++++++++++++++++++++++------ drivers/of/irq.c | 32 +++-- drivers/pci/controller/dwc/pci-imx6.c | 55 ++++---- drivers/pci/controller/pcie-apple.c | 6 +- drivers/xen/grant-dma-ops.c | 5 +- include/linux/of.h | 32 ++++- 9 files changed, 282 insertions(+), 101 deletions(-) --- base-commit: cdf5682bcd98422242d95c4e6c10f9c38bcf56c7 change-id: 20260301-parse_iommu_cells-1c33768aebba Best regards, -- Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com>
From: Robin Murphy <robin.murphy@arm.com> Since we now have quite a few users parsing "iommu-map" and "msi-map" properties, give them some wrappers to conveniently encapsulate the appropriate sets of property names. This will also make it easier to then change of_map_id() to correctly account for specifier cells. Reviewed-by: Rob Herring (Arm) <robh@kernel.org> Reviewed-by: Frank Li <Frank.Li@nxp.com> Acked-by: Marc Zyngier <maz@kernel.org> Acked-by: Bjorn Helgaas <bhelgaas@google.com> Signed-off-by: Robin Murphy <robin.murphy@arm.com> Signed-off-by: Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com> --- drivers/cdx/cdx_msi.c | 5 ++--- drivers/iommu/of_iommu.c | 4 +--- drivers/irqchip/irq-gic-its-msi-parent.c | 2 +- drivers/of/base.c | 38 ++++++++++++++++++++++++++++++++ drivers/of/irq.c | 3 +-- drivers/pci/controller/dwc/pci-imx6.c | 6 ++--- drivers/pci/controller/pcie-apple.c | 3 +-- drivers/xen/grant-dma-ops.c | 3 +-- include/linux/of.h | 18 +++++++++++++++ 9 files changed, 65 insertions(+), 17 deletions(-) diff --git a/drivers/cdx/cdx_msi.c b/drivers/cdx/cdx_msi.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/cdx/cdx_msi.c +++ b/drivers/cdx/cdx_msi.c @@ -XXX,XX +XXX,XX @@ static int cdx_msi_prepare(struct irq_domain *msi_domain, int ret; /* Retrieve device ID from requestor ID using parent device */ - ret = of_map_id(parent->of_node, cdx_dev->msi_dev_id, "msi-map", "msi-map-mask", - NULL, &dev_id); + ret = of_map_msi_id(parent->of_node, cdx_dev->msi_dev_id, NULL, &dev_id); if (ret) { - dev_err(dev, "of_map_id failed for MSI: %d\n", ret); + dev_err(dev, "of_map_msi_id failed for MSI: %d\n", ret); return ret; } diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -XXX,XX +XXX,XX @@ static int of_iommu_configure_dev_id(struct device_node *master_np, struct of_phandle_args iommu_spec = { .args_count = 1 }; int err; - err = of_map_id(master_np, *id, "iommu-map", - "iommu-map-mask", &iommu_spec.np, - iommu_spec.args); + err = of_map_iommu_id(master_np, *id, &iommu_spec.np, iommu_spec.args); if (err) return err; diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/irqchip/irq-gic-its-msi-parent.c +++ b/drivers/irqchip/irq-gic-its-msi-parent.c @@ -XXX,XX +XXX,XX @@ static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u struct device_node *msi_ctrl __free(device_node) = NULL; - return of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &msi_ctrl, dev_id); + return of_map_msi_id(dev->of_node, dev->id, &msi_ctrl, dev_id); } static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, diff --git a/drivers/of/base.c b/drivers/of/base.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -XXX,XX +XXX,XX @@ int of_map_id(const struct device_node *np, u32 id, return 0; } EXPORT_SYMBOL_GPL(of_map_id); + +/** + * of_map_iommu_id - Translate an ID using "iommu-map" bindings. + * @np: root complex device node. + * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform + * stream/device ID) used as the lookup key in the iommu-map table. + * @target: optional pointer to a target device node. + * @id_out: optional pointer to receive the translated ID. + * + * Convenience wrapper around of_map_id() using "iommu-map" and "iommu-map-mask". + * + * Return: 0 on success or a standard error code on failure. + */ +int of_map_iommu_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out) +{ + return of_map_id(np, id, "iommu-map", "iommu-map-mask", target, id_out); +} +EXPORT_SYMBOL_GPL(of_map_iommu_id); + +/** + * of_map_msi_id - Translate an ID using "msi-map" bindings. + * @np: root complex device node. + * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform + * stream/device ID) used as the lookup key in the msi-map table. + * @target: optional pointer to a target device node. + * @id_out: optional pointer to receive the translated ID. + * + * Convenience wrapper around of_map_id() using "msi-map" and "msi-map-mask". + * + * Return: 0 on success or a standard error code on failure. + */ +int of_map_msi_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out) +{ + return of_map_id(np, id, "msi-map", "msi-map-mask", target, id_out); +} +EXPORT_SYMBOL_GPL(of_map_msi_id); diff --git a/drivers/of/irq.c b/drivers/of/irq.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -XXX,XX +XXX,XX @@ u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in) * "msi-map" or an "msi-parent" property. */ for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) { - if (!of_map_id(parent_dev->of_node, id_in, "msi-map", - "msi-map-mask", msi_np, &id_out)) + if (!of_map_msi_id(parent_dev->of_node, id_in, msi_np, &id_out)) break; if (!of_check_msi_parent(parent_dev->of_node, msi_np)) break; diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -XXX,XX +XXX,XX @@ static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid) u32 sid = 0; target = NULL; - err_i = of_map_id(dev->of_node, rid, "iommu-map", "iommu-map-mask", - &target, &sid_i); + err_i = of_map_iommu_id(dev->of_node, rid, &target, &sid_i); if (target) { of_node_put(target); } else { @@ -XXX,XX +XXX,XX @@ static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid) } target = NULL; - err_m = of_map_id(dev->of_node, rid, "msi-map", "msi-map-mask", - &target, &sid_m); + err_m = of_map_msi_id(dev->of_node, rid, &target, &sid_m); /* * err_m target diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -XXX,XX +XXX,XX @@ static int apple_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_d dev_dbg(&pdev->dev, "added to bus %s, index %d\n", pci_name(pdev->bus->self), port->idx); - err = of_map_id(port->pcie->dev->of_node, rid, "iommu-map", - "iommu-map-mask", NULL, &sid); + err = of_map_iommu_id(port->pcie->dev->of_node, rid, NULL, &sid); if (err) return err; diff --git a/drivers/xen/grant-dma-ops.c b/drivers/xen/grant-dma-ops.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/xen/grant-dma-ops.c +++ b/drivers/xen/grant-dma-ops.c @@ -XXX,XX +XXX,XX @@ static int xen_dt_grant_init_backend_domid(struct device *dev, struct pci_dev *pdev = to_pci_dev(dev); u32 rid = PCI_DEVID(pdev->bus->number, pdev->devfn); - if (of_map_id(np, rid, "iommu-map", "iommu-map-mask", &iommu_spec.np, - iommu_spec.args)) { + if (of_map_iommu_id(np, rid, &iommu_spec.np, iommu_spec.args)) { dev_dbg(dev, "Cannot translate ID\n"); return -ESRCH; } diff --git a/include/linux/of.h b/include/linux/of.h index XXXXXXX..XXXXXXX 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -XXX,XX +XXX,XX @@ int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, struct device_node **target, u32 *id_out); +int of_map_iommu_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out); + +int of_map_msi_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out); + phys_addr_t of_dma_get_max_cpu_address(struct device_node *np); struct kimage; @@ -XXX,XX +XXX,XX @@ static inline int of_map_id(const struct device_node *np, u32 id, return -EINVAL; } +static inline int of_map_iommu_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out) +{ + return -EINVAL; +} + +static inline int of_map_msi_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out) +{ + return -EINVAL; +} + static inline phys_addr_t of_dma_get_max_cpu_address(struct device_node *np) { return PHYS_ADDR_MAX; -- 2.34.1
From: Charan Teja Kalla <charan.kalla@oss.qualcomm.com> Change of_map_id() to take a pointer to struct of_phandle_args instead of passing target device node and translated IDs separately. Update all callers accordingly. Add an explicit filter_np parameter to of_map_id() and of_map_msi_id() to separate the filter input from the output. Previously, the target parameter served dual purpose: as an input filter (if non-NULL, only match entries targeting that node) and as an output (receiving the matched node with a reference held). Now filter_np is the explicit input filter and arg->np is the pure output. Previously, of_map_id() would call of_node_put() on the matched node when a filter was provided, making reference ownership inconsistent. Remove this internal of_node_put() call so that of_map_id() now always transfers ownership of the matched node reference to the caller via arg->np. Callers are now consistently responsible for releasing this reference with of_node_put(arg->np) when done. Acked-by: Frank Li <Frank.Li@nxp.com> Suggested-by: Rob Herring (Arm) <robh@kernel.org> Suggested-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> Signed-off-by: Charan Teja Kalla <charan.kalla@oss.qualcomm.com> Signed-off-by: Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com> --- drivers/cdx/cdx_msi.c | 7 ++-- drivers/iommu/of_iommu.c | 4 +- drivers/irqchip/irq-gic-its-msi-parent.c | 10 +++-- drivers/of/base.c | 71 ++++++++++++++++++-------------- drivers/of/irq.c | 31 ++++++++++---- drivers/pci/controller/dwc/pci-imx6.c | 53 ++++++++++++------------ drivers/pci/controller/pcie-apple.c | 5 ++- drivers/xen/grant-dma-ops.c | 4 +- include/linux/of.h | 16 ++++--- 9 files changed, 119 insertions(+), 82 deletions(-) diff --git a/drivers/cdx/cdx_msi.c b/drivers/cdx/cdx_msi.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/cdx/cdx_msi.c +++ b/drivers/cdx/cdx_msi.c @@ -XXX,XX +XXX,XX @@ static int cdx_msi_prepare(struct irq_domain *msi_domain, struct device *dev, int nvec, msi_alloc_info_t *info) { + struct of_phandle_args msi_spec = {}; struct cdx_device *cdx_dev = to_cdx_device(dev); struct device *parent = cdx_dev->cdx->dev; struct msi_domain_info *msi_info; - u32 dev_id; int ret; /* Retrieve device ID from requestor ID using parent device */ - ret = of_map_msi_id(parent->of_node, cdx_dev->msi_dev_id, NULL, &dev_id); + ret = of_map_msi_id(parent->of_node, cdx_dev->msi_dev_id, NULL, &msi_spec); if (ret) { dev_err(dev, "of_map_msi_id failed for MSI: %d\n", ret); return ret; } + of_node_put(msi_spec.np); #ifdef GENERIC_MSI_DOMAIN_OPS /* Set the device Id to be passed to the GIC-ITS */ - info->scratchpad[0].ul = dev_id; + info->scratchpad[0].ul = msi_spec.args[0]; #endif msi_info = msi_get_domain_info(msi_domain->parent); diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -XXX,XX +XXX,XX @@ static int of_iommu_configure_dev_id(struct device_node *master_np, struct device *dev, const u32 *id) { - struct of_phandle_args iommu_spec = { .args_count = 1 }; + struct of_phandle_args iommu_spec = {}; int err; - err = of_map_iommu_id(master_np, *id, &iommu_spec.np, iommu_spec.args); + err = of_map_iommu_id(master_np, *id, &iommu_spec); if (err) return err; diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/irqchip/irq-gic-its-msi-parent.c +++ b/drivers/irqchip/irq-gic-its-msi-parent.c @@ -XXX,XX +XXX,XX @@ static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev, static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u32 *dev_id, phys_addr_t *pa) { + struct device_node *msi_ctrl = NULL; + struct of_phandle_args msi_spec = {}; struct of_phandle_iterator it; int ret; @@ -XXX,XX +XXX,XX @@ static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u } } - struct device_node *msi_ctrl __free(device_node) = NULL; - - return of_map_msi_id(dev->of_node, dev->id, &msi_ctrl, dev_id); + ret = of_map_msi_id(dev->of_node, dev->id, &msi_ctrl, &msi_spec); + if (!ret) + *dev_id = msi_spec.args[0]; + of_node_put(msi_spec.np); + return ret; } static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev, diff --git a/drivers/of/base.c b/drivers/of/base.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -XXX,XX +XXX,XX @@ int of_find_last_cache_level(unsigned int cpu) * @id: device ID to map. * @map_name: property name of the map to use. * @map_mask_name: optional property name of the mask to use. - * @target: optional pointer to a target device node. - * @id_out: optional pointer to receive the translated ID. + * @filter_np: pointer to an optional filter node, or NULL to allow bypass. + * If non-NULL, the map property must exist (-ENODEV if absent). If + * *filter_np is also non-NULL, only entries targeting that node match. + * @arg: pointer to a &struct of_phandle_args for the result. On success, + * @arg->args[0] will contain the translated ID. If a map entry was + * matched, @arg->np will be set to the target node with a reference + * held that the caller must release with of_node_put(). * * Given a device ID, look up the appropriate implementation-defined * platform ID and/or the target device which receives transactions on that - * ID, as per the "iommu-map" and "msi-map" bindings. Either of @target or - * @id_out may be NULL if only the other is required. If @target points to - * a non-NULL device node pointer, only entries targeting that node will be - * matched; if it points to a NULL value, it will receive the device node of - * the first matching target phandle, with a reference held. + * ID, as per the "iommu-map" and "msi-map" bindings. * * Return: 0 on success or a standard error code on failure. */ int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, - struct device_node **target, u32 *id_out) + struct device_node * const *filter_np, struct of_phandle_args *arg) { u32 map_mask, masked_id; int map_len; const __be32 *map = NULL; - if (!np || !map_name || (!target && !id_out)) + if (!np || !map_name || !arg) return -EINVAL; + /* Ensure bypass/no-match success never returns a stale target node. */ + arg->np = NULL; map = of_get_property(np, map_name, &map_len); if (!map) { - if (target) + if (filter_np) return -ENODEV; /* Otherwise, no map implies no translation */ - *id_out = id; + arg->args[0] = id; + arg->args_count = 1; return 0; } @@ -XXX,XX +XXX,XX @@ int of_map_id(const struct device_node *np, u32 id, if (!phandle_node) return -ENODEV; - if (target) { - if (*target) - of_node_put(phandle_node); - else - *target = phandle_node; - - if (*target != phandle_node) - continue; + if (filter_np && *filter_np && *filter_np != phandle_node) { + of_node_put(phandle_node); + continue; } - if (id_out) - *id_out = masked_id - id_base + out_base; + arg->np = phandle_node; + arg->args[0] = masked_id - id_base + out_base; + arg->args_count = 1; pr_debug("%pOF: %s, using mask %08x, id-base: %08x, out-base: %08x, length: %08x, id: %08x -> %08x\n", np, map_name, map_mask, id_base, out_base, @@ -XXX,XX +XXX,XX @@ int of_map_id(const struct device_node *np, u32 id, } pr_info("%pOF: no %s translation for id 0x%x on %pOF\n", np, map_name, - id, target && *target ? *target : NULL); + id, filter_np && *filter_np ? *filter_np : NULL); /* Bypasses translation */ - if (id_out) - *id_out = id; + arg->args[0] = id; + arg->args_count = 1; return 0; } EXPORT_SYMBOL_GPL(of_map_id); @@ -XXX,XX +XXX,XX @@ EXPORT_SYMBOL_GPL(of_map_id); * @np: root complex device node. * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform * stream/device ID) used as the lookup key in the iommu-map table. - * @target: optional pointer to a target device node. - * @id_out: optional pointer to receive the translated ID. + * @arg: pointer to a &struct of_phandle_args for the result. On success, + * @arg->args[0] contains the translated ID. If a map entry was matched, + * @arg->np holds a reference to the target node that the caller must + * release with of_node_put(). * * Convenience wrapper around of_map_id() using "iommu-map" and "iommu-map-mask". * * Return: 0 on success or a standard error code on failure. */ int of_map_iommu_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out) + struct of_phandle_args *arg) { - return of_map_id(np, id, "iommu-map", "iommu-map-mask", target, id_out); + return of_map_id(np, id, "iommu-map", "iommu-map-mask", NULL, arg); } EXPORT_SYMBOL_GPL(of_map_iommu_id); @@ -XXX,XX +XXX,XX @@ EXPORT_SYMBOL_GPL(of_map_iommu_id); * @np: root complex device node. * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform * stream/device ID) used as the lookup key in the msi-map table. - * @target: optional pointer to a target device node. - * @id_out: optional pointer to receive the translated ID. + * @filter_np: pointer to an optional filter node, or NULL to allow bypass. + * If non-NULL, the map property must exist (-ENODEV if absent). If + * *filter_np is also non-NULL, only entries targeting that node match. + * @arg: pointer to a &struct of_phandle_args for the result. On success, + * @arg->args[0] contains the translated ID. If a map entry was matched, + * @arg->np holds a reference to the target node that the caller must + * release with of_node_put(). * * Convenience wrapper around of_map_id() using "msi-map" and "msi-map-mask". * * Return: 0 on success or a standard error code on failure. */ int of_map_msi_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out) + struct device_node * const *filter_np, struct of_phandle_args *arg) { - return of_map_id(np, id, "msi-map", "msi-map-mask", target, id_out); + return of_map_id(np, id, "msi-map", "msi-map-mask", filter_np, arg); } EXPORT_SYMBOL_GPL(of_map_msi_id); diff --git a/drivers/of/irq.c b/drivers/of/irq.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -XXX,XX +XXX,XX @@ static int of_check_msi_parent(struct device_node *dev_node, struct device_node /** * of_msi_xlate - map a MSI ID and find relevant MSI controller node * @dev: device for which the mapping is to be done. - * @msi_np: Pointer to target MSI controller node + * @msi_np: Pointer to target MSI controller node, or NULL if the caller + * only needs the translated ID without receiving the controller node. + * If non-NULL and pointing to a non-NULL node, only entries targeting + * that node will be matched. If non-NULL and pointing to NULL, it will + * receive the first matching target node with a reference held. * @id_in: Device ID. * * Walk up the device hierarchy looking for devices with a "msi-map" * or "msi-parent" property. If found, apply the mapping to @id_in. - * If @msi_np points to a non-NULL device node pointer, only entries targeting - * that node will be matched; if it points to a NULL value, it will receive the - * device node of the first matching target phandle, with a reference held. * * Returns: The mapped MSI id. */ @@ -XXX,XX +XXX,XX @@ u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in) * "msi-map" or an "msi-parent" property. */ for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) { - if (!of_map_msi_id(parent_dev->of_node, id_in, msi_np, &id_out)) + struct of_phandle_args msi_spec = {}; + + if (!of_map_msi_id(parent_dev->of_node, id_in, msi_np, &msi_spec)) { + if (msi_spec.np) { + /* msi-map matched: use the translated ID and target node */ + if (msi_spec.args_count > 0) + id_out = msi_spec.args[0]; + if (msi_np && !*msi_np) + *msi_np = of_node_get(msi_spec.np); + of_node_put(msi_spec.np); + } + /* msi-map present but no match → stop walking */ break; - if (!of_check_msi_parent(parent_dev->of_node, msi_np)) + } + /* -ENODEV: msi-map absent → check for msi-parent */ + if (msi_np && !of_check_msi_parent(parent_dev->of_node, msi_np)) break; } return id_out; @@ -XXX,XX +XXX,XX @@ struct irq_domain *of_msi_map_get_device_domain(struct device *dev, u32 id, u32 bus_token) { struct device_node *np = NULL; + struct irq_domain *d; of_msi_xlate(dev, &np, id); - return irq_find_matching_host(np, bus_token); + d = irq_find_matching_host(np, bus_token); + of_node_put(np); + return d; } /** diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -XXX,XX +XXX,XX @@ static void imx_pcie_remove_lut(struct imx_pcie *imx_pcie, u16 rid) static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid) { + struct of_phandle_args iommu_spec = {}; + struct of_phandle_args msi_spec = {}; struct device *dev = imx_pcie->pci->dev; - struct device_node *target; + struct device_node *msi_filter = NULL; u32 sid_i, sid_m; int err_i, err_m; u32 sid = 0; - target = NULL; - err_i = of_map_iommu_id(dev->of_node, rid, &target, &sid_i); - if (target) { - of_node_put(target); - } else { - /* - * "target == NULL && err_i == 0" means RID out of map range. - * Use 1:1 map RID to streamID. Hardware can't support this - * because the streamID is only 6 bits - */ - err_i = -EINVAL; + err_i = of_map_iommu_id(dev->of_node, rid, &iommu_spec); + if (!err_i) { + if (!iommu_spec.np) + /* + * "iommu_spec.np == NULL && err_i == 0" means RID out of map + * range. Use 1:1 map RID to streamID. Hardware can't support + * this because the streamID is only 6 bits. + */ + err_i = -EINVAL; + else + sid_i = iommu_spec.args[0]; } + of_node_put(iommu_spec.np); - target = NULL; - err_m = of_map_msi_id(dev->of_node, rid, &target, &sid_m); - + err_m = of_map_msi_id(dev->of_node, rid, &msi_filter, &msi_spec); /* - * err_m target - * 0 NULL RID out of range. Use 1:1 map RID to - * streamID, Current hardware can't - * support it, so return -EINVAL. - * != 0 NULL msi-map does not exist, use built-in MSI - * 0 != NULL Get correct streamID from RID - * != 0 != NULL Invalid combination + * err_m msi_spec.np + * 0 != NULL Got correct streamID from RID via msi-map + * 0 NULL msi-map present but RID out of range + * -ENODEV NULL msi-map absent, use built-in MSI controller */ - if (!err_m && !target) - return -EINVAL; - else if (target) - of_node_put(target); /* Find streamID map entry for RID in msi-map */ + if (!err_m) { + if (!msi_spec.np) + /* msi-map present but RID out of range */ + return -EINVAL; + sid_m = msi_spec.args[0]; + } + of_node_put(msi_spec.np); /* * msi-map iommu-map diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -XXX,XX +XXX,XX @@ static int apple_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_d { u32 sid, rid = pci_dev_id(pdev); struct apple_pcie_port *port; + struct of_phandle_args iommu_spec = {}; int idx, err; port = apple_pcie_get_port(pdev); @@ -XXX,XX +XXX,XX @@ static int apple_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_d dev_dbg(&pdev->dev, "added to bus %s, index %d\n", pci_name(pdev->bus->self), port->idx); - err = of_map_iommu_id(port->pcie->dev->of_node, rid, NULL, &sid); + err = of_map_iommu_id(port->pcie->dev->of_node, rid, &iommu_spec); if (err) return err; + of_node_put(iommu_spec.np); + sid = iommu_spec.args[0]; mutex_lock(&port->pcie->lock); idx = bitmap_find_free_region(port->sid_map, port->sid_map_sz, 0); diff --git a/drivers/xen/grant-dma-ops.c b/drivers/xen/grant-dma-ops.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/xen/grant-dma-ops.c +++ b/drivers/xen/grant-dma-ops.c @@ -XXX,XX +XXX,XX @@ static int xen_dt_grant_init_backend_domid(struct device *dev, struct device_node *np, domid_t *backend_domid) { - struct of_phandle_args iommu_spec = { .args_count = 1 }; + struct of_phandle_args iommu_spec = {}; if (dev_is_pci(dev)) { struct pci_dev *pdev = to_pci_dev(dev); u32 rid = PCI_DEVID(pdev->bus->number, pdev->devfn); - if (of_map_iommu_id(np, rid, &iommu_spec.np, iommu_spec.args)) { + if (of_map_iommu_id(np, rid, &iommu_spec)) { dev_dbg(dev, "Cannot translate ID\n"); return -ESRCH; } diff --git a/include/linux/of.h b/include/linux/of.h index XXXXXXX..XXXXXXX 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -XXX,XX +XXX,XX @@ bool of_console_check(const struct device_node *dn, char *name, int index); int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, - struct device_node **target, u32 *id_out); + struct device_node * const *filter_np, + struct of_phandle_args *arg); int of_map_iommu_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out); + struct of_phandle_args *arg); int of_map_msi_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out); + struct device_node * const *filter_np, + struct of_phandle_args *arg); phys_addr_t of_dma_get_max_cpu_address(struct device_node *np); @@ -XXX,XX +XXX,XX @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag static inline int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, - struct device_node **target, u32 *id_out) + struct device_node * const *filter_np, + struct of_phandle_args *arg) { return -EINVAL; } static inline int of_map_iommu_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out) + struct of_phandle_args *arg) { return -EINVAL; } static inline int of_map_msi_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out) + struct device_node * const *filter_np, + struct of_phandle_args *arg) { return -EINVAL; } -- 2.34.1
From: Robin Murphy <robin.murphy@arm.com> So far our parsing of {iommu,msi}-map properties has always blindly assumed that the output specifiers will always have exactly 1 cell. This typically does happen to be the case, but is not actually enforced (and the PCI msi-map binding even explicitly states support for 0 or 1 cells) - as a result we've now ended up with dodgy DTs out in the field which depend on this behaviour to map a 1-cell specifier for a 2-cell provider, despite that being bogus per the bindings themselves. Since there is some potential use in being able to map at least single input IDs to multi-cell output specifiers (and properly support 0-cell outputs as well), add support for properly parsing and using the target nodes' #cells values, albeit with the unfortunate complication of still having to work around expectations of the old behaviour too. Since there are multi-cell output specifiers, the callers of of_map_id() may need to get the exact cell output value for further processing. Update of_map_id() to set args_count in the output to reflect the actual number of output specifier cells. Signed-off-by: Robin Murphy <robin.murphy@arm.com> Signed-off-by: Charan Teja Kalla <charan.kalla@oss.qualcomm.com> Signed-off-by: Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com> --- drivers/of/base.c | 168 +++++++++++++++++++++++++++++++++++++++++------------ include/linux/of.h | 6 +- 2 files changed, 135 insertions(+), 39 deletions(-) diff --git a/drivers/of/base.c b/drivers/of/base.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -XXX,XX +XXX,XX @@ int of_find_last_cache_level(unsigned int cpu) return cache_level; } +/* + * Some DTs have an iommu-map targeting a 2-cell IOMMU node while + * specifying only 1 cell. Fortunately they all consist of value '1' + * as the 2nd cell entry with the same target, so check for that pattern. + * + * Example: + * IOMMU node: + * #iommu-cells = <2>; + * + * Device node: + * iommu-map = <0x0000 &smmu 0x0000 0x1>, + * <0x0100 &smmu 0x0100 0x1>; + */ +static bool of_check_bad_map(const __be32 *map, int len) +{ + __be32 phandle = map[1]; + + if (len % 4) + return false; + for (int i = 0; i < len; i += 4) { + if (map[i + 1] != phandle || map[i + 3] != cpu_to_be32(1)) + return false; + } + return true; +} + /** * of_map_id - Translate an ID through a downstream mapping. * @np: root complex device node. * @id: device ID to map. * @map_name: property name of the map to use. + * @cells_name: property name of target specifier cells. * @map_mask_name: optional property name of the mask to use. * @filter_np: pointer to an optional filter node, or NULL to allow bypass. * If non-NULL, the map property must exist (-ENODEV if absent). If * *filter_np is also non-NULL, only entries targeting that node match. * @arg: pointer to a &struct of_phandle_args for the result. On success, - * @arg->args[0] will contain the translated ID. If a map entry was - * matched, @arg->np will be set to the target node with a reference - * held that the caller must release with of_node_put(). + * @arg->args_count will be set to the number of output specifier cells + * as defined by @cells_name in the target node, and + * @arg->args[0..args_count-1] will contain the translated output + * specifier values. If a map entry was matched, @arg->np will be set + * to the target node with a reference held that the caller must release + * with of_node_put(). * * Given a device ID, look up the appropriate implementation-defined * platform ID and/or the target device which receives transactions on that @@ -XXX,XX +XXX,XX @@ int of_find_last_cache_level(unsigned int cpu) * Return: 0 on success or a standard error code on failure. */ int of_map_id(const struct device_node *np, u32 id, - const char *map_name, const char *map_mask_name, + const char *map_name, const char *cells_name, + const char *map_mask_name, struct device_node * const *filter_np, struct of_phandle_args *arg) { u32 map_mask, masked_id; - int map_len; + int map_bytes, map_len, offset = 0; + bool bad_map = false; const __be32 *map = NULL; - if (!np || !map_name || !arg) + if (!np || !map_name || !cells_name || !arg) return -EINVAL; /* Ensure bypass/no-match success never returns a stale target node. */ arg->np = NULL; - map = of_get_property(np, map_name, &map_len); + map = of_get_property(np, map_name, &map_bytes); if (!map) { if (filter_np) return -ENODEV; @@ -XXX,XX +XXX,XX @@ int of_map_id(const struct device_node *np, u32 id, return 0; } - if (!map_len || map_len % (4 * sizeof(*map))) { - pr_err("%pOF: Error: Bad %s length: %d\n", np, - map_name, map_len); - return -EINVAL; - } + if (map_bytes % sizeof(*map)) + goto err_map_len; + map_len = map_bytes / sizeof(*map); /* The default is to select all bits. */ map_mask = 0xffffffff; @@ -XXX,XX +XXX,XX @@ int of_map_id(const struct device_node *np, u32 id, of_property_read_u32(np, map_mask_name, &map_mask); masked_id = map_mask & id; - for ( ; map_len > 0; map_len -= 4 * sizeof(*map), map += 4) { + + while (offset < map_len) { struct device_node *phandle_node; - u32 id_base = be32_to_cpup(map + 0); - u32 phandle = be32_to_cpup(map + 1); - u32 out_base = be32_to_cpup(map + 2); - u32 id_len = be32_to_cpup(map + 3); + u32 id_base, phandle, id_len, id_off, cells = 0; + const __be32 *out_base; + + if (map_len - offset < 2) + goto err_map_len; + + id_base = be32_to_cpup(map + offset); if (id_base & ~map_mask) { - pr_err("%pOF: Invalid %s translation - %s-mask (0x%x) ignores id-base (0x%x)\n", - np, map_name, map_name, - map_mask, id_base); + pr_err("%pOF: Invalid %s translation - %s (0x%x) ignores id-base (0x%x)\n", + np, map_name, map_mask_name, map_mask, id_base); return -EFAULT; } - if (masked_id < id_base || masked_id >= id_base + id_len) - continue; - + phandle = be32_to_cpup(map + offset + 1); phandle_node = of_find_node_by_phandle(phandle); if (!phandle_node) return -ENODEV; + /* + * Assume 1-cell output specifier if the target node lacks the + * #cells property, for backward compatibility with controllers + * that predate the property (e.g. arm,gic-v2m-frame). + */ + if (bad_map || of_property_read_u32(phandle_node, cells_name, &cells)) + cells = 1; + + if (cells > MAX_PHANDLE_ARGS) { + pr_err("%pOF: %s cell count %d exceeds maximum\n", + phandle_node, cells_name, cells); + of_node_put(phandle_node); + return -EINVAL; + } + + if (offset == 0 && cells == 2) { + bad_map = of_check_bad_map(map, map_len); + if (bad_map) { + pr_warn_once("%pOF: %s has 1-cell entries targeting 2-cell %s, treating as 1-cell output\n", + np, map_name, cells_name); + cells = 1; + } + } + + if (map_len - offset < 3 + cells) { + of_node_put(phandle_node); + goto err_map_len; + } + + out_base = map + offset + 2; + offset += 3 + cells; + + id_len = be32_to_cpup(map + offset - 1); + id_off = masked_id - id_base; + if (masked_id < id_base || id_off >= id_len) { + of_node_put(phandle_node); + continue; + } + if (id_len > 1 && cells > 1) { + /* + * With 1 output cell we reasonably assume its value + * has a linear relationship to the input; with more, + * we'd need help from the provider to know what to do. + */ + pr_err("%pOF: Unsupported %s - cannot handle %d-ID range with %d-cell output specifier\n", + np, map_name, id_len, cells); + of_node_put(phandle_node); + return -EINVAL; + } + if (filter_np && *filter_np && *filter_np != phandle_node) { of_node_put(phandle_node); continue; } arg->np = phandle_node; - arg->args[0] = masked_id - id_base + out_base; - arg->args_count = 1; + for (int i = 0; i < cells; i++) + arg->args[i] = id_off + be32_to_cpu(out_base[i]); + arg->args_count = cells; pr_debug("%pOF: %s, using mask %08x, id-base: %08x, out-base: %08x, length: %08x, id: %08x -> %08x\n", - np, map_name, map_mask, id_base, out_base, - id_len, id, masked_id - id_base + out_base); + np, map_name, map_mask, id_base, + cells ? be32_to_cpup(out_base) : 0, + id_len, id, + cells ? id_off + be32_to_cpup(out_base) : id_off); return 0; } @@ -XXX,XX +XXX,XX @@ int of_map_id(const struct device_node *np, u32 id, arg->args[0] = id; arg->args_count = 1; return 0; + +err_map_len: + pr_err("%pOF: Error: Bad %s length: %d\n", np, map_name, map_bytes); + return -EINVAL; } EXPORT_SYMBOL_GPL(of_map_id); @@ -XXX,XX +XXX,XX @@ EXPORT_SYMBOL_GPL(of_map_id); * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform * stream/device ID) used as the lookup key in the iommu-map table. * @arg: pointer to a &struct of_phandle_args for the result. On success, - * @arg->args[0] contains the translated ID. If a map entry was matched, - * @arg->np holds a reference to the target node that the caller must - * release with of_node_put(). + * @arg->args_count will be set to the number of output specifier cells + * and @arg->args[0..args_count-1] will contain the translated output + * specifier values. If a map entry was matched, @arg->np holds a + * reference to the target node that the caller must release with + * of_node_put(). * - * Convenience wrapper around of_map_id() using "iommu-map" and "iommu-map-mask". + * Convenience wrapper around of_map_id() using "iommu-map", "#iommu-cells", + * and "iommu-map-mask". * * Return: 0 on success or a standard error code on failure. */ int of_map_iommu_id(const struct device_node *np, u32 id, struct of_phandle_args *arg) { - return of_map_id(np, id, "iommu-map", "iommu-map-mask", NULL, arg); + return of_map_id(np, id, "iommu-map", "#iommu-cells", "iommu-map-mask", NULL, arg); } EXPORT_SYMBOL_GPL(of_map_iommu_id); @@ -XXX,XX +XXX,XX @@ EXPORT_SYMBOL_GPL(of_map_iommu_id); * If non-NULL, the map property must exist (-ENODEV if absent). If * *filter_np is also non-NULL, only entries targeting that node match. * @arg: pointer to a &struct of_phandle_args for the result. On success, - * @arg->args[0] contains the translated ID. If a map entry was matched, - * @arg->np holds a reference to the target node that the caller must - * release with of_node_put(). + * @arg->args_count will be set to the number of output specifier cells + * and @arg->args[0..args_count-1] will contain the translated output + * specifier values. If a map entry was matched, @arg->np holds a + * reference to the target node that the caller must release with + * of_node_put(). * - * Convenience wrapper around of_map_id() using "msi-map" and "msi-map-mask". + * Convenience wrapper around of_map_id() using "msi-map", "#msi-cells", + * and "msi-map-mask". * * Return: 0 on success or a standard error code on failure. */ int of_map_msi_id(const struct device_node *np, u32 id, struct device_node * const *filter_np, struct of_phandle_args *arg) { - return of_map_id(np, id, "msi-map", "msi-map-mask", filter_np, arg); + return of_map_id(np, id, "msi-map", "#msi-cells", "msi-map-mask", filter_np, arg); } EXPORT_SYMBOL_GPL(of_map_msi_id); diff --git a/include/linux/of.h b/include/linux/of.h index XXXXXXX..XXXXXXX 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -XXX,XX +XXX,XX @@ const char *of_prop_next_string(const struct property *prop, const char *cur); bool of_console_check(const struct device_node *dn, char *name, int index); int of_map_id(const struct device_node *np, u32 id, - const char *map_name, const char *map_mask_name, + const char *map_name, const char *cells_name, + const char *map_mask_name, struct device_node * const *filter_np, struct of_phandle_args *arg); @@ -XXX,XX +XXX,XX @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag } static inline int of_map_id(const struct device_node *np, u32 id, - const char *map_name, const char *map_mask_name, + const char *map_name, const char *cells_name, + const char *map_mask_name, struct device_node * const *filter_np, struct of_phandle_args *arg) { -- 2.34.1