* [PATCH v6 0/4] PCI: of: warn on bogus device_type property
@ 2026-09-24 15:02 Alex Elder
2026-09-24 15:02 ` [PATCH v6 1/4] PCI: of: avoid allocations in of_pci_prop_compatible() Alex Elder
` (3 more replies)
0 siblings, 4 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
Add a check when adding a PCI device to ensure the device_type
property is (or is not) correctly defined when the device has
a non-null devicetree node pointer.
While working on a Qualcomm platform, I learned that some PCI
endpoint nodes were defined with device_type = "pci" properties.
Herve Codina pointed out that this was not correct. Rob Herring
indicated that people seem to have trouble getting the PCI
devicetree nodes right, and asked whether we could warn if this
particular problem occurred. During review, Herve Codina also
suggested that bridge nodes be checked to ensure they *do*
have the proper device_type property, and later, Sashiko suggested
that "pciex" (and "cardbus") also be accepted as valid bridge
device_type property values.
The first patch prevents a possible null pointer dereference that
Sashiko pointed out some time back. This is the only patch that
has changed in this version. (Last time I neglected to enable the
Kconfig option that ensured this go compiled, and it contained
two obvious compile errors.)
The second and third patches are simple cleanups. The last adds
the new PCI devicetree node checks and warnings.
At this point we are waiting for (and requesting) an ack from
Rob Herring on the last patch.
-Alex
Between version 5 and version 6:
- Simplified the patch header on the last patch, as suggested by
Bjorn
- Rebased on v7.3-rc4; otherwise no code changes
Version 5 is available here:
https://lore.kernel.org/lkml/20260908213459.2519059-1-elder@riscstar.com/
Between version 4 and version 5:
- Made a local variable (bufp) a pointer type in the first patch
- Eliminated an unused local variable (i) in the first patch
Version 4 is available here:
https://lore.kernel.org/lkml/20260904134607.1856121-1-elder@riscstar.com/
Between version 3 and version 4:
- Insert a first patch that fixes a Sashiko-reported issue
- Drop the final "duplicate warning" patch from v3
- Include "pciex" as a valid PCI bridge device_type property value
- Add Herve's Reviewed-by tag on the last patch
Version 3 is available here:
https://lore.kernel.org/lkml/20260901011338.1323243-1-elder@riscstar.com/
Between version 2 and version 3:
- Drop a patch that made a change only needed by a different series
- Switch a function header to use kernel-doc format
- Add a warning if a PCI bridge node has no device_type property
- Added a patch to remove a duplicate warning in the devicetree code
Version 2 is available here:
https://lore.kernel.org/lkml/20260812172247.276554-1-elder@riscstar.com/
Between version 1 and version 2:
- Check the PCI devicetree node even when PCI_DYNAMIC_OF_NODES is
not enabled
Version 1 is available here:
https://lore.kernel.org/lkml/20260807194100.455599-1-elder@riscstar.com/
Alex Elder (4):
PCI: of: avoid allocations in of_pci_prop_compatible()
PCI: of: drop the reg_num argument to of_pci_set_address()
PCI: of: don't zero flags in of_pci_get_addr_flags()
PCI: of: introduce of_pci_verify_node()
drivers/pci/bus.c | 1 +
drivers/pci/of.c | 32 ++++++++++++++++++++++++++
drivers/pci/of_property.c | 47 ++++++++++++++++++++++-----------------
drivers/pci/pci.h | 3 +++
4 files changed, 63 insertions(+), 20 deletions(-)
base-commit: 93f51579e7df248780214094418f205253383cc5
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [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
end of thread, other threads:[~2026-09-24 15:02 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®