From 28d20b0d895849ce3fe8c6f77baffc08886c2157 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 14 Mar 2026 00:55:18 +0800 Subject: [PATCH 1/5] PCI: Add pcie_get_link_speed() helper for safe array access The pcie_link_speed[] array is indexed by PCIe generation numbers (1 = 2.5 GT/s, 2 = 5 GT/s, ...). Several drivers use it directly, which can lead to out-of-bounds accesses if an invalid generation number is used. Introduce a helper function pcie_get_link_speed() that returns the pci_bus_speed value for a given generation number, or PCI_SPEED_UNKNOWN if the generation is out of range. This will allow us to safely handle invalid values after the range check is removed from of_pci_get_max_link_speed(). Signed-off-by: Hans Zhang <18255117159@163.com> [mani: Fixed kernel-doc] Signed-off-by: Manivannan Sadhasivam Acked-by: Bjorn Helgaas Link: https://patch.msgid.link/20260313165522.123518-2-18255117159@163.com --- drivers/pci/pci.h | 2 ++ drivers/pci/probe.c | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 13d998fbacce..409aca7d737a 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -108,6 +108,8 @@ struct pcie_tlp_log; PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE) extern const unsigned char pcie_link_speed[]; +unsigned char pcie_get_link_speed(unsigned int speed); + extern bool pci_early_dump; extern struct mutex pci_rescan_remove_lock; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index bccc7a4bdd79..f85da50a3d83 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -783,6 +783,22 @@ const unsigned char pcie_link_speed[] = { }; EXPORT_SYMBOL_GPL(pcie_link_speed); +/** + * pcie_get_link_speed - Get speed value from PCIe generation number + * @speed: PCIe speed (1-based: 1 = 2.5GT, 2 = 5GT, ...) + * + * Returns the speed value (e.g., PCIE_SPEED_2_5GT) if @speed is valid, + * otherwise returns PCI_SPEED_UNKNOWN. + */ +unsigned char pcie_get_link_speed(unsigned int speed) +{ + if (speed >= ARRAY_SIZE(pcie_link_speed)) + return PCI_SPEED_UNKNOWN; + + return pcie_link_speed[speed]; +} +EXPORT_SYMBOL_GPL(pcie_get_link_speed); + const char *pci_speed_string(enum pci_bus_speed speed) { /* Indexed by the pci_bus_speed enum */ From 0f34f647f19465b475b13e889dcb33526e166d24 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 14 Mar 2026 00:55:19 +0800 Subject: [PATCH 2/5] PCI: dwc: Use pcie_get_link_speed() helper for safe array access Replace direct indexing of pcie_link_speed[] with the new helper pcie_get_link_speed() in all DesignWare core and glue drivers. This ensures that out-of-range generation numbers do not cause out-of-bounds accesses when the helper returns PCI_SPEED_UNKNOWN, and prepares for the removal of the range check in of_pci_get_max_link_speed(). The actual validation of the "max-link-speed" DT property (e.g., fallback to a safe default and warning) is added in subsequent patches for each driver that reads the property. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260313165522.123518-3-18255117159@163.com --- drivers/pci/controller/dwc/pcie-designware-host.c | 2 +- drivers/pci/controller/dwc/pcie-designware.c | 2 +- drivers/pci/controller/dwc/pcie-qcom-common.c | 2 +- drivers/pci/controller/dwc/pcie-qcom-ep.c | 4 ++-- drivers/pci/controller/dwc/pcie-qcom.c | 6 +++--- drivers/pci/controller/dwc/pcie-tegra194.c | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 6ae6189e9b8a..0e05c5280344 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -1081,7 +1081,7 @@ static void dw_pcie_program_presets(struct dw_pcie_rp *pp, enum pci_bus_speed sp static void dw_pcie_config_presets(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - enum pci_bus_speed speed = pcie_link_speed[pci->max_link_speed]; + enum pci_bus_speed speed = pcie_get_link_speed(pci->max_link_speed); /* * Lane equalization settings need to be applied for all data rates the diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 5741c09dde7f..06792ba92aa7 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -861,7 +861,7 @@ static void dw_pcie_link_set_max_speed(struct dw_pcie *pci) ctrl2 = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCTL2); ctrl2 &= ~PCI_EXP_LNKCTL2_TLS; - switch (pcie_link_speed[pci->max_link_speed]) { + switch (pcie_get_link_speed(pci->max_link_speed)) { case PCIE_SPEED_2_5GT: link_speed = PCI_EXP_LNKCTL2_TLS_2_5GT; break; diff --git a/drivers/pci/controller/dwc/pcie-qcom-common.c b/drivers/pci/controller/dwc/pcie-qcom-common.c index 01c5387e53bf..5aa73c628737 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-common.c +++ b/drivers/pci/controller/dwc/pcie-qcom-common.c @@ -22,7 +22,7 @@ void qcom_pcie_common_set_equalization(struct dw_pcie *pci) * applied. */ - for (speed = PCIE_SPEED_8_0GT; speed <= pcie_link_speed[pci->max_link_speed]; speed++) { + for (speed = PCIE_SPEED_8_0GT; speed <= pcie_get_link_speed(pci->max_link_speed); speed++) { if (speed > PCIE_SPEED_32_0GT) { dev_warn(dev, "Skipped equalization settings for unsupported data rate\n"); break; diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index 18460f01b2c6..4b7184d4a6fa 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -152,7 +152,7 @@ #define WAKE_DELAY_US 2000 /* 2 ms */ #define QCOM_PCIE_LINK_SPEED_TO_BW(speed) \ - Mbps_to_icc(PCIE_SPEED2MBS_ENC(pcie_link_speed[speed])) + Mbps_to_icc(PCIE_SPEED2MBS_ENC(pcie_get_link_speed(speed))) #define to_pcie_ep(x) dev_get_drvdata((x)->dev) @@ -531,7 +531,7 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci) qcom_pcie_common_set_equalization(pci); - if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT) + if (pcie_get_link_speed(pci->max_link_speed) == PCIE_SPEED_16_0GT) qcom_pcie_common_set_16gt_lane_margining(pci); /* diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 67a16af69ddc..5c7c105bb745 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -170,7 +170,7 @@ #define QCOM_PCIE_CRC8_POLYNOMIAL (BIT(2) | BIT(1) | BIT(0)) #define QCOM_PCIE_LINK_SPEED_TO_BW(speed) \ - Mbps_to_icc(PCIE_SPEED2MBS_ENC(pcie_link_speed[speed])) + Mbps_to_icc(PCIE_SPEED2MBS_ENC(pcie_get_link_speed(speed))) struct qcom_pcie_resources_1_0_0 { struct clk_bulk_data *clks; @@ -320,7 +320,7 @@ static int qcom_pcie_start_link(struct dw_pcie *pci) qcom_pcie_common_set_equalization(pci); - if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT) + if (pcie_get_link_speed(pci->max_link_speed) == PCIE_SPEED_16_0GT) qcom_pcie_common_set_16gt_lane_margining(pci); /* Enable Link Training state machine */ @@ -1579,7 +1579,7 @@ static void qcom_pcie_icc_opp_update(struct qcom_pcie *pcie) ret); } } else if (pcie->use_pm_opp) { - freq_mbps = pcie_dev_speed_mbps(pcie_link_speed[speed]); + freq_mbps = pcie_dev_speed_mbps(pcie_get_link_speed(speed)); if (freq_mbps < 0) return; diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 06571d806ab3..47f08adfbd79 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -310,7 +310,7 @@ static void tegra_pcie_icc_set(struct tegra_pcie_dw *pcie) speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, val); width = FIELD_GET(PCI_EXP_LNKSTA_NLW, val); - val = width * PCIE_SPEED2MBS_ENC(pcie_link_speed[speed]); + val = width * PCIE_SPEED2MBS_ENC(pcie_get_link_speed(speed)); if (icc_set_bw(pcie->icc_path, Mbps_to_icc(val), 0)) dev_err(pcie->dev, "can't set bw[%u]\n", val); From 126d04398cd4e29743828c38c21f39f46fc2e004 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 14 Mar 2026 00:55:20 +0800 Subject: [PATCH 3/5] PCI: j721e: Validate max-link-speed from DT Use the new pcie_get_link_speed() helper to validate the value read from the "max-link-speed" DT property. If the value is missing or invalid, fall back to Gen2 (speed = 2). This prepares for the removal of the range check in of_pci_get_max_link_speed(). Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260313165522.123518-4-18255117159@163.com --- drivers/pci/controller/cadence/pci-j721e.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c index 6f2501479c70..bfdfe98d5aba 100644 --- a/drivers/pci/controller/cadence/pci-j721e.c +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -202,7 +202,8 @@ static int j721e_pcie_set_link_speed(struct j721e_pcie *pcie, int ret; link_speed = of_pci_get_max_link_speed(np); - if (link_speed < 2) + if ((link_speed < 2) || + (pcie_get_link_speed(link_speed) == PCI_SPEED_UNKNOWN)) link_speed = 2; val = link_speed - 1; From 03f920936977b5133a0e57a7c9fdeb4e584eeb06 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 14 Mar 2026 00:55:21 +0800 Subject: [PATCH 4/5] PCI: controller: Validate max-link-speed Add validation for the "max-link-speed" DT property in three more drivers, using the pcie_get_link_speed() helper. - brcmstb: If the value is missing or invalid, fall back to no limitation (pcie->gen = 0). Fix the previous incorrect logic. - mediatek-gen3: If the value is missing or invalid, use the maximum speed supported by the controller. - rzg3s-host: If the value is missing or invalid, fall back to Gen2. This ensures that all users of of_pci_get_max_link_speed() are ready for the removal of the central range check. Signed-off-by: Hans Zhang <18255117159@163.com> Signed-off-by: Manivannan Sadhasivam Reviewed-by: Florian Fainelli Link: https://patch.msgid.link/20260313165522.123518-5-18255117159@163.com --- drivers/pci/controller/pcie-brcmstb.c | 5 +++-- drivers/pci/controller/pcie-mediatek-gen3.c | 2 +- drivers/pci/controller/pcie-rzg3s-host.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 062f55690012..714bcab97b60 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1442,7 +1442,7 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie) cls = FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta); nlw = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta); dev_info(dev, "link up, %s x%u %s\n", - pci_speed_string(pcie_link_speed[cls]), nlw, + pci_speed_string(pcie_get_link_speed(cls)), nlw, ssc_good ? "(SSC)" : "(!SSC)"); return 0; @@ -2072,7 +2072,8 @@ static int brcm_pcie_probe(struct platform_device *pdev) return PTR_ERR(pcie->clk); ret = of_pci_get_max_link_speed(np); - pcie->gen = (ret < 0) ? 0 : ret; + if (pcie_get_link_speed(ret) == PCI_SPEED_UNKNOWN) + pcie->gen = 0; pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc"); diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index 75ddb8bee168..3b903ef7d3cf 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -1150,7 +1150,7 @@ static int mtk_pcie_setup(struct mtk_gen3_pcie *pcie) return err; err = of_pci_get_max_link_speed(pcie->dev->of_node); - if (err) { + if (pcie_get_link_speed(err) != PCI_SPEED_UNKNOWN) { /* Get the maximum speed supported by the controller */ max_speed = mtk_pcie_get_controller_max_link_speed(pcie); diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c index 2809112e6317..00a11f986117 100644 --- a/drivers/pci/controller/pcie-rzg3s-host.c +++ b/drivers/pci/controller/pcie-rzg3s-host.c @@ -966,7 +966,7 @@ static int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host) ls = readw_relaxed(host->pcie + pcie_cap + PCI_EXP_LNKSTA); cs2 = readl_relaxed(host->axi + RZG3S_PCI_PCSTAT2); - switch (pcie_link_speed[host->max_link_speed]) { + switch (pcie_get_link_speed(host->max_link_speed)) { case PCIE_SPEED_5_0GT: max_supported_link_speeds = GENMASK(PCI_EXP_LNKSTA_CLS_5_0GB - 1, 0); link_speed = PCI_EXP_LNKCTL2_TLS_5_0GT; From c8b610341914cb62899e31b41f724b5c4831a645 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Sat, 14 Mar 2026 00:55:22 +0800 Subject: [PATCH 5/5] PCI: of: Remove max-link-speed generation validation The of_pci_get_max_link_speed() function currently validates the "max-link-speed" DT property to be in the range 1..4 (Gen1..Gen4). This imposes a maintenance burden because each new PCIe generation would require updating this validation. Remove the range check so the function returns the raw property value (or a negative error code if the property is missing or malformed). Since the callers are now validating the returned speed against the range they support, this check can now be safely removed. Removing the validation from this common function also allows future PCIe generations to be supported without modifying drivers/pci/of.c. Signed-off-by: Hans Zhang <18255117159@163.com> [mani: commit log and kernel doc fix] Signed-off-by: Manivannan Sadhasivam Link: https://patch.msgid.link/20260313165522.123518-6-18255117159@163.com --- drivers/pci/of.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 9f8eb5df279e..c216701c75dd 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -875,24 +875,19 @@ EXPORT_SYMBOL_GPL(of_pci_supply_present); * of_pci_get_max_link_speed - Find the maximum link speed of the given device node. * @node: Device tree node with the maximum link speed information. * - * This function will try to find the limitation of link speed by finding - * a property called "max-link-speed" of the given device node. + * This function will try to read the "max-link-speed" property of the given + * device tree node. It does NOT validate the value of the property. * - * Return: - * * > 0 - On success, a maximum link speed. - * * -EINVAL - Invalid "max-link-speed" property value, or failure to access - * the property of the device tree node. - * - * Returns the associated max link speed from DT, or a negative value if the - * required property is not found or is invalid. + * Return: Maximum link speed value on success, errno on failure. */ int of_pci_get_max_link_speed(struct device_node *node) { u32 max_link_speed; + int ret; - if (of_property_read_u32(node, "max-link-speed", &max_link_speed) || - max_link_speed == 0 || max_link_speed > 4) - return -EINVAL; + ret = of_property_read_u32(node, "max-link-speed", &max_link_speed); + if (ret) + return ret; return max_link_speed; }