* [PATCH v6 1/4] PCI: of: avoid allocations in of_pci_prop_compatible()
2026-09-24 15:02 [PATCH v6 0/4] PCI: of: warn on bogus device_type property Alex Elder
@ 2026-09-24 15:02 ` Alex Elder
2026-09-24 15:02 ` [PATCH v6 2/4] PCI: of: drop the reg_num argument to of_pci_set_address() Alex Elder
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Alex Elder @ 2026-09-24 15:02 UTC (permalink / raw)
To: bhelgaas, robh, saravanak
Cc: herve.codina, daniel, mohd.anwar, lorenzo.bianconi, linux-pci,
devicetree, linux-kernel, Sashiko
Three compatible strings are formatted in of_pci_prop_compatible().
Their sizes are known in advance, and the largest is 16 bytes.
Rather than dynamically allocating the space for those strings, just
set aside a buffer on the stack large enough to hold all three.
This avoids a problem that Sashiko pointed out, where an allocation
failure would cause subsequent crash because strlen() is called
unconditionally in of_changeset_add_prop_string_array().
Reported-by: Sashiko <sashiko-bot@kernel.org>
Link: https://lore.kernel.org/sashiko-reviews/a647bd56-7dc8-4fec-9d96-834622054cdf@riscstar.com
Signed-off-by: Alex Elder <elder@riscstar.com>
---
drivers/pci/of_property.c | 33 +++++++++++++++++++++------------
1 file changed, 21 insertions(+), 12 deletions(-)
diff --git a/drivers/pci/of_property.c b/drivers/pci/of_property.c
index 75a358f73e694..d66c702218081 100644
--- a/drivers/pci/of_property.c
+++ b/drivers/pci/of_property.c
@@ -324,27 +324,36 @@ static int of_pci_prop_intr_map(struct pci_dev *pdev, struct of_changeset *ocs,
return ret;
}
+/* The three compatible property strings have max sizes 12+1, 15+1, and 13+1 */
+#define PROP_SIZE 16 /* Max size of each compatible string */
static int of_pci_prop_compatible(struct pci_dev *pdev,
struct of_changeset *ocs,
struct device_node *np)
{
const char *compat_strs[PROP_COMPAT_NUM] = { 0 };
- int i, ret;
+ char buf[PROP_COMPAT_NUM * PROP_SIZE] = { };
+ char *bufp = buf;
+ int ret;
- compat_strs[PROP_COMPAT_PCI_VVVV_DDDD] =
- kasprintf(GFP_KERNEL, "pci%x,%x", pdev->vendor, pdev->device);
- compat_strs[PROP_COMPAT_PCICLASS_CCSSPP] =
- kasprintf(GFP_KERNEL, "pciclass,%06x", pdev->class);
- compat_strs[PROP_COMPAT_PCICLASS_CCSS] =
- kasprintf(GFP_KERNEL, "pciclass,%04x", pdev->class >> 8);
+ ret = snprintf(bufp, PROP_SIZE, "pci%x,%x", pdev->vendor, pdev->device);
+ if (ret >= PROP_SIZE)
+ return -EINVAL;
+ compat_strs[PROP_COMPAT_PCI_VVVV_DDDD] = bufp;
+ bufp += ret + 1;
- ret = of_changeset_add_prop_string_array(ocs, np, "compatible",
- compat_strs, PROP_COMPAT_NUM);
- for (i = 0; i < PROP_COMPAT_NUM; i++)
- kfree(compat_strs[i]);
+ ret = snprintf(bufp, PROP_SIZE, "pciclass,%06x", pdev->class);
+ if (ret >= PROP_SIZE)
+ return -EINVAL;
+ compat_strs[PROP_COMPAT_PCICLASS_CCSSPP] = bufp;
+ bufp += ret + 1;
- return ret;
+ ret = snprintf(bufp, PROP_SIZE, "pciclass,%04x", pdev->class >> 8);
+ compat_strs[PROP_COMPAT_PCICLASS_CCSS] = bufp;
+
+ return of_changeset_add_prop_string_array(ocs, np, "compatible",
+ compat_strs, PROP_COMPAT_NUM);
}
+#undef PROP_SIZE
int of_pci_add_properties(struct pci_dev *pdev, struct of_changeset *ocs,
struct device_node *np)
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v6 2/4] PCI: of: drop the reg_num argument to of_pci_set_address()
2026-09-24 15:02 [PATCH v6 0/4] PCI: of: warn on bogus device_type property Alex Elder
2026-09-24 15:02 ` [PATCH v6 1/4] PCI: of: avoid allocations in of_pci_prop_compatible() Alex Elder
@ 2026-09-24 15:02 ` Alex Elder
2026-09-24 15:02 ` [PATCH v6 3/4] PCI: of: don't zero flags in of_pci_get_addr_flags() Alex Elder
2026-09-24 15:02 ` [PATCH v6 4/4] PCI: of: introduce of_pci_verify_node() Alex Elder
3 siblings, 0 replies; 5+ messages in thread
From: Alex Elder @ 2026-09-24 15:02 UTC (permalink / raw)
To: bhelgaas, robh, saravanak
Cc: herve.codina, daniel, mohd.anwar, lorenzo.bianconi, linux-pci,
devicetree, linux-kernel
The reg_num argument passed to of_pci_set_address() is always zero,
so get rid of it.
Reviewed-by: Herve Codina <herve.codina@bootlin.com>
Signed-off-by: Alex Elder <elder@riscstar.com>
---
drivers/pci/of_property.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/pci/of_property.c b/drivers/pci/of_property.c
index d66c702218081..1caabbd4c18b5 100644
--- a/drivers/pci/of_property.c
+++ b/drivers/pci/of_property.c
@@ -52,7 +52,7 @@ enum of_pci_prop_compatible {
};
static void of_pci_set_address(struct pci_dev *pdev, u32 *prop, u64 addr,
- u32 reg_num, u32 flags, bool reloc)
+ u32 flags, bool reloc)
{
if (pdev) {
prop[0] = FIELD_PREP(OF_PCI_ADDR_FIELD_BUS, pdev->bus->number) |
@@ -61,7 +61,7 @@ static void of_pci_set_address(struct pci_dev *pdev, u32 *prop, u64 addr,
} else
prop[0] = 0;
- prop[0] |= flags | reg_num;
+ prop[0] |= flags;
if (!reloc) {
prop[0] |= OF_PCI_ADDR_FIELD_NONRELOC;
prop[1] = upper_32_bits(addr);
@@ -131,7 +131,7 @@ static int of_pci_prop_ranges(struct pci_dev *pdev, struct of_changeset *ocs,
continue;
val64 = pci_bus_address(pdev, &res[j] - pdev->resource);
- of_pci_set_address(pdev, rp[i].parent_addr, val64, 0, flags,
+ of_pci_set_address(pdev, rp[i].parent_addr, val64, flags,
false);
if (pci_is_bridge(pdev)) {
memcpy(rp[i].child_addr, rp[i].parent_addr,
@@ -164,7 +164,7 @@ static int of_pci_prop_reg(struct pci_dev *pdev, struct of_changeset *ocs,
struct of_pci_addr_pair reg = { 0 };
/* configuration space */
- of_pci_set_address(pdev, reg.phys_addr, 0, 0, 0, true);
+ of_pci_set_address(pdev, reg.phys_addr, 0, 0, true);
return of_changeset_add_prop_u32_array(ocs, np, "reg", (u32 *)®,
sizeof(reg) / sizeof(u32));
@@ -467,7 +467,7 @@ static int of_pci_host_bridge_prop_ranges(struct pci_host_bridge *bridge,
/* PCI bus address */
val64 = res->start;
of_pci_set_address(NULL, &ranges[ranges_sz],
- val64 - window->offset, 0, flags, false);
+ val64 - window->offset, flags, false);
ranges_sz += OF_PCI_ADDRESS_CELLS;
/* Host bus address */
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v6 3/4] PCI: of: don't zero flags in of_pci_get_addr_flags()
2026-09-24 15:02 [PATCH v6 0/4] PCI: of: warn on bogus device_type property Alex Elder
2026-09-24 15:02 ` [PATCH v6 1/4] PCI: of: avoid allocations in of_pci_prop_compatible() Alex Elder
2026-09-24 15:02 ` [PATCH v6 2/4] PCI: of: drop the reg_num argument to of_pci_set_address() Alex Elder
@ 2026-09-24 15:02 ` Alex Elder
2026-09-24 15:02 ` [PATCH v6 4/4] PCI: of: introduce of_pci_verify_node() Alex Elder
3 siblings, 0 replies; 5+ messages in thread
From: Alex Elder @ 2026-09-24 15:02 UTC (permalink / raw)
To: bhelgaas, robh, saravanak
Cc: herve.codina, daniel, mohd.anwar, lorenzo.bianconi, linux-pci,
devicetree, linux-kernel
The flags variable whose address is passed to of_pci_get_addr_flags()
is zeroed before assigning a value to it. Skip the zeroing and just
assign it instead.
Reviewed-by: Herve Codina <herve.codina@bootlin.com>
Signed-off-by: Alex Elder <elder@riscstar.com>
---
drivers/pci/of_property.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/pci/of_property.c b/drivers/pci/of_property.c
index 1caabbd4c18b5..1e5d7dde467b8 100644
--- a/drivers/pci/of_property.c
+++ b/drivers/pci/of_property.c
@@ -82,12 +82,10 @@ static int of_pci_get_addr_flags(const struct resource *res, u32 *flags)
else
return -EINVAL;
- *flags = 0;
+ *flags = FIELD_PREP(OF_PCI_ADDR_FIELD_SS, ss);
if (res->flags & IORESOURCE_PREFETCH)
*flags |= OF_PCI_ADDR_FIELD_PREFETCH;
- *flags |= FIELD_PREP(OF_PCI_ADDR_FIELD_SS, ss);
-
return 0;
}
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v6 4/4] PCI: of: introduce of_pci_verify_node()
2026-09-24 15:02 [PATCH v6 0/4] PCI: of: warn on bogus device_type property Alex Elder
` (2 preceding siblings ...)
2026-09-24 15:02 ` [PATCH v6 3/4] PCI: of: don't zero flags in of_pci_get_addr_flags() Alex Elder
@ 2026-09-24 15:02 ` Alex Elder
3 siblings, 0 replies; 5+ messages in thread
From: Alex Elder @ 2026-09-24 15:02 UTC (permalink / raw)
To: bhelgaas, robh, saravanak
Cc: herve.codina, daniel, mohd.anwar, lorenzo.bianconi, linux-pci,
devicetree, linux-kernel
The Open Firmware PCI Bus Supplement and Devicetree Specification
reserves the device_type property for PCI bridge nodes (with value
"pci" or "pciex"). Non-bridge PCI endpoint nodes must not include
this property. Rob Herring observed that developers seem to get
this wrong. Commit df4107fc729e3 ("arm64: dts: qcom: qcs6490-rb3gen2:
clean up PCI function nodes") and a few that follow demonstrate this.
Rob requested that a runtime check be added to spot this specific error,
only for non-bridge PCI devices. Herve Codina further suggested we
ensure that bridge PCI devices *do* define the device_type property,
with value "pci" or "pciex".
Implement these suggested warnings in of_pci_verify_node(), a new
function called by pci_bus_add_device() for both bridges and endpoints.
If a PCI bridge has a devicetree node, a warning is issued if it has
a device_type property whose value is not "pci" or "pciex" (or if it
has no such property). Similarly, a warning is issued for an endpoint
if it has a device_type property having one of those two values.
To be clear, this adds warnings, but otherwise ignores the errors
it warns about. It is meant to help developers; users should never
see them.
Reviewed-by: Herve Codina <herve.codina@bootlin.com>
Signed-off-by: Alex Elder <elder@riscstar.com>
---
drivers/pci/bus.c | 1 +
drivers/pci/of.c | 32 ++++++++++++++++++++++++++++++++
drivers/pci/pci.h | 3 +++
3 files changed, 36 insertions(+)
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 655ed53436d3e..679afbc6d3109 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -351,6 +351,7 @@ void pci_bus_add_device(struct pci_dev *dev)
* are not assigned yet for some devices.
*/
pcibios_bus_add_device(dev);
+ of_pci_verify_node(dev);
pci_fixup_device(pci_fixup_final, dev);
if (pci_is_bridge(dev))
of_pci_make_dev_node(dev);
diff --git a/drivers/pci/of.c b/drivers/pci/of.c
index a51dff91b196d..5a040ed836744 100644
--- a/drivers/pci/of.c
+++ b/drivers/pci/of.c
@@ -1085,3 +1085,35 @@ int of_pci_get_equalization_presets(struct device *dev,
return 0;
}
EXPORT_SYMBOL_GPL(of_pci_get_equalization_presets);
+
+/**
+ * of_pci_verify_node - Sanity check some PCI device node properties
+ * @pdev: The PCI device whose device node is checked
+ *
+ * PCI enumeration authoritatively discovers what we need to know about
+ * a PCI device. A devicetree-based platform will represent a PCI root
+ * bridge with a node, but otherwise devicetree doesn't typically include
+ * many PCI nodes. Where such nodes do exist, experience has shown that
+ * the "device_type" property is sometimes wrong, so warn about that.
+ */
+void of_pci_verify_node(struct pci_dev *pdev)
+{
+ struct device_node *np = pci_device_to_OF_node(pdev);
+ bool device_is_bridge;
+ bool device_type_pci;
+
+ /* Nothing to check if there's no pre-existing devicetree node */
+ if (!np)
+ return;
+
+ device_is_bridge = pci_is_bridge(pdev);
+ device_type_pci = of_node_is_type(np, "pci") ||
+ of_node_is_type(np, "pciex");
+
+ /* Bridges should have device type "pci"; endpoints should not */
+ if (device_is_bridge == device_type_pci)
+ return;
+
+ dev_err(&pdev->dev, "PCI %s have \"pci\" device_type property\n",
+ device_is_bridge ? "bridge should" : "endpoint should not");
+}
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index ba3c3fddddc23..2e33d3bd4b0ba 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -1253,6 +1253,7 @@ bool of_pci_supply_present(struct device_node *np);
int of_pci_get_equalization_presets(struct device *dev,
struct pci_eq_presets *presets,
int num_lanes);
+void of_pci_verify_node(struct pci_dev *pdev);
#else
static inline int
of_get_pci_domain_nr(struct device_node *node)
@@ -1308,6 +1309,8 @@ static inline int of_pci_get_equalization_presets(struct device *dev,
return 0;
}
+
+static inline void of_pci_verify_node(struct pci_dev *pdev) { }
#endif /* CONFIG_OF */
struct of_changeset;
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread