lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20251219174036.16738-24-ilpo.jarvinen@linux.intel.com>
Date: Fri, 19 Dec 2025 19:40:36 +0200
From: Ilpo Järvinen <ilpo.jarvinen@...ux.intel.com>
To: linux-pci@...r.kernel.org,
	Bjorn Helgaas <bhelgaas@...gle.com>,
	Dominik Brodowski <linux@...inikbrodowski.net>,
	linux-kernel@...r.kernel.org
Cc: Ilpo Järvinen <ilpo.jarvinen@...ux.intel.com>
Subject: [PATCH 23/23] PCI: Move scanbus bridge scanning to setup-cardbus.c

PCI core's pci_scan_bridge_extend() contains convoluted logic specific
to setting up bus numbers for legacy CardBus bridges. Extract the
CardBus specific part out into setup-cardbus.c to make the core code
cleaner and allow leaving CardBus bridge support out from modern
systems.

Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@...ux.intel.com>
---

I'm somewhat skeptical that EA capability is relevant for CardBus
bridge but I've left it in place as I'm not 100% sure about it. ECN for
EA Capability is from 2014 which is quite late considering CardBus
timeline (PCMCIA ceased to exist in 2009). If it's not relevant,
dropping its support from CardBus side would allow small
simplifications to pci_cardbus_scan_bridge_extend().
---
 drivers/pci/pci.h           |  16 +++++
 drivers/pci/probe.c         |  73 +++++-----------------
 drivers/pci/setup-cardbus.c | 118 ++++++++++++++++++++++++++++++++++++
 3 files changed, 149 insertions(+), 58 deletions(-)

diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index b20ff7ef20ff..c586bf8a9da9 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -242,6 +242,7 @@ void pci_config_pm_runtime_put(struct pci_dev *dev);
 void pci_pm_power_up_and_verify_state(struct pci_dev *pci_dev);
 void pci_pm_init(struct pci_dev *dev);
 void pci_ea_init(struct pci_dev *dev);
+bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub);
 void pci_msi_init(struct pci_dev *dev);
 void pci_msix_init(struct pci_dev *dev);
 bool pci_bridge_d3_possible(struct pci_dev *dev);
@@ -377,10 +378,17 @@ extern unsigned long pci_hotplug_mmio_size;
 extern unsigned long pci_hotplug_mmio_pref_size;
 extern unsigned long pci_hotplug_bus_size;
 
+static inline bool pci_is_cardbus_bridge(struct pci_dev *dev)
+{
+	return dev->hdr_type == PCI_HEADER_TYPE_CARDBUS;
+}
 #ifdef CONFIG_CARDBUS
 unsigned long pci_cardbus_resource_alignment(struct resource *res);
 int pci_bus_size_cardbus_bridge(struct pci_bus *bus,
 				struct list_head *realloc_head);
+int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
+				   u32 buses, int max,
+				   unsigned int available_buses, int pass);
 int pci_setup_cardbus(char *str);
 
 #else
@@ -393,6 +401,14 @@ static inline int pci_bus_size_cardbus_bridge(struct pci_bus *bus,
 {
 	return -EOPNOTSUPP;
 }
+static inline int pci_cardbus_scan_bridge_extend(struct pci_bus *bus,
+						 struct pci_dev *dev,
+						 u32 buses, int max,
+						 unsigned int available_buses,
+						 int pass)
+{
+	return max;
+}
 static inline int pci_setup_cardbus(char *str) { return -ENOENT; }
 #endif /* CONFIG_CARDBUS */
 
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 49468644e730..89f0717efd48 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -25,9 +25,6 @@
 #include <linux/bitfield.h>
 #include "pci.h"
 
-#define CARDBUS_LATENCY_TIMER	176	/* secondary latency timer */
-#define CARDBUS_RESERVE_BUSNR	3
-
 static struct resource busn_resource = {
 	.name	= "PCI busn",
 	.start	= 0,
@@ -1345,7 +1342,7 @@ void pbus_validate_busn(struct pci_bus *bus)
  * and subordinate bus numbers, return true with the bus numbers in @sec
  * and @sub.  Otherwise return false.
  */
-static bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub)
+bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub)
 {
 	int ea, offset;
 	u32 dw;
@@ -1399,8 +1396,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 				  int pass)
 {
 	struct pci_bus *child;
-	int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS);
-	u32 buses, i, j = 0;
+	u32 buses;
 	u16 bctl;
 	u8 primary, secondary, subordinate;
 	int broken = 0;
@@ -1444,8 +1440,15 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 	pci_write_config_word(dev, PCI_BRIDGE_CONTROL,
 			      bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT);
 
-	if ((secondary || subordinate) && !pcibios_assign_all_busses() &&
-	    !is_cardbus && !broken) {
+	if (pci_is_cardbus_bridge(dev)) {
+		max = pci_cardbus_scan_bridge_extend(bus, dev, buses, max,
+						     available_buses,
+						     pass);
+		goto out;
+	}
+
+	if ((secondary || subordinate) &&
+	    !pcibios_assign_all_busses() && !broken) {
 		unsigned int cmax, buses;
 
 		/*
@@ -1487,7 +1490,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 		 * do in the second pass.
 		 */
 		if (!pass) {
-			if (pcibios_assign_all_busses() || broken || is_cardbus)
+			if (pcibios_assign_all_busses() || broken)
 
 				/*
 				 * Temporarily disable forwarding of the
@@ -1534,55 +1537,11 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 			FIELD_PREP(PCI_SECONDARY_BUS_MASK, child->busn_res.start) |
 			FIELD_PREP(PCI_SUBORDINATE_BUS_MASK, child->busn_res.end);
 
-		/*
-		 * yenta.c forces a secondary latency timer of 176.
-		 * Copy that behaviour here.
-		 */
-		if (is_cardbus) {
-			buses &= ~PCI_SEC_LATENCY_TIMER_MASK;
-			buses |= FIELD_PREP(PCI_SEC_LATENCY_TIMER_MASK,
-					    CARDBUS_LATENCY_TIMER);
-		}
-
 		/* We need to blast all three values with a single write */
 		pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);
 
-		if (!is_cardbus) {
-			child->bridge_ctl = bctl;
-			max = pci_scan_child_bus_extend(child, available_buses);
-		} else {
-
-			/*
-			 * For CardBus bridges, we leave 4 bus numbers as
-			 * cards with a PCI-to-PCI bridge can be inserted
-			 * later.
-			 */
-			for (i = 0; i < CARDBUS_RESERVE_BUSNR; i++) {
-				struct pci_bus *parent = bus;
-				if (pci_find_bus(pci_domain_nr(bus),
-							max+i+1))
-					break;
-				while (parent->parent) {
-					if ((!pcibios_assign_all_busses()) &&
-					    (parent->busn_res.end > max) &&
-					    (parent->busn_res.end <= max+i)) {
-						j = 1;
-					}
-					parent = parent->parent;
-				}
-				if (j) {
-
-					/*
-					 * Often, there are two CardBus
-					 * bridges -- try to leave one
-					 * valid bus number for each one.
-					 */
-					i /= 2;
-					break;
-				}
-			}
-			max += i;
-		}
+		child->bridge_ctl = bctl;
+		max = pci_scan_child_bus_extend(child, available_buses);
 
 		/*
 		 * Set subordinate bus number to its real value.
@@ -1594,9 +1553,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
 		pci_bus_update_busn_res_end(child, max);
 		pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max);
 	}
-
-	scnprintf(child->name, sizeof(child->name),
-		  (is_cardbus ? "PCI CardBus %04x:%02x" : "PCI Bus %04x:%02x"),
+	scnprintf(child->name, sizeof(child->name), "PCI Bus %04x:%02x",
 		  pci_domain_nr(bus), child->number);
 
 	pbus_validate_busn(child);
diff --git a/drivers/pci/setup-cardbus.c b/drivers/pci/setup-cardbus.c
index 93a2b43c637b..1ebd13a1f730 100644
--- a/drivers/pci/setup-cardbus.c
+++ b/drivers/pci/setup-cardbus.c
@@ -3,14 +3,19 @@
  * Cardbus bridge setup routines.
  */
 
+#include <linux/bitfield.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/pci.h>
 #include <linux/sizes.h>
+#include <linux/sprintf.h>
 #include <linux/types.h>
 
 #include "pci.h"
 
+#define CARDBUS_LATENCY_TIMER		176	/* secondary latency timer */
+#define CARDBUS_RESERVE_BUSNR		3
+
 #define DEFAULT_CARDBUS_IO_SIZE		SZ_256
 #define DEFAULT_CARDBUS_MEM_SIZE	SZ_64M
 /* pci=cbmemsize=nnM,cbiosize=nn can override this */
@@ -186,3 +191,116 @@ int pci_setup_cardbus(char *str)
 
 	return -ENOENT;
 }
+
+int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
+				   u32 buses, int max,
+				   unsigned int available_buses, int pass)
+{
+	struct pci_bus *child;
+	bool fixed_buses;
+	u8 fixed_sec, fixed_sub;
+	int next_busnr;
+	u32 i, j = 0;
+
+	/*
+	 * We need to assign a number to this bus which we always do in the
+	 * second pass.
+	 */
+	if (!pass) {
+		/*
+		 * Temporarily disable forwarding of the configuration
+		 * cycles on all bridges in this bus segment to avoid
+		 * possible conflicts in the second pass between two bridges
+		 * programmed with overlapping bus ranges.
+		 */
+		pci_write_config_dword(dev, PCI_PRIMARY_BUS,
+				       buses & PCI_SEC_LATENCY_TIMER_MASK);
+		return max;
+	}
+
+	/* Clear errors */
+	pci_write_config_word(dev, PCI_STATUS, 0xffff);
+
+	/* Read bus numbers from EA Capability (if present) */
+	fixed_buses = pci_ea_fixed_busnrs(dev, &fixed_sec, &fixed_sub);
+	if (fixed_buses)
+		next_busnr = fixed_sec;
+	else
+		next_busnr = max + 1;
+
+	/*
+	 * Prevent assigning a bus number that already exists. This can
+	 * happen when a bridge is hot-plugged, so in this case we only
+	 * re-scan this bus.
+	 */
+	child = pci_find_bus(pci_domain_nr(bus), next_busnr);
+	if (!child) {
+		child = pci_add_new_bus(bus, dev, next_busnr);
+		if (!child)
+			return max;
+		pci_bus_insert_busn_res(child, next_busnr, bus->busn_res.end);
+	}
+	max++;
+	if (available_buses)
+		available_buses--;
+
+	buses = (buses & PCI_SEC_LATENCY_TIMER_MASK) |
+		FIELD_PREP(PCI_PRIMARY_BUS_MASK, child->primary) |
+		FIELD_PREP(PCI_SECONDARY_BUS_MASK, child->busn_res.start) |
+		FIELD_PREP(PCI_SUBORDINATE_BUS_MASK, child->busn_res.end);
+
+	/*
+	 * yenta.c forces a secondary latency timer of 176.
+	 * Copy that behaviour here.
+	 */
+	buses &= ~PCI_SEC_LATENCY_TIMER_MASK;
+	buses |= FIELD_PREP(PCI_SEC_LATENCY_TIMER_MASK, CARDBUS_LATENCY_TIMER);
+
+	/* We need to blast all three values with a single write */
+	pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);
+
+	/*
+	 * For CardBus bridges, we leave 4 bus numbers as cards with a
+	 * PCI-to-PCI bridge can be inserted later.
+	 */
+	for (i = 0; i < CARDBUS_RESERVE_BUSNR; i++) {
+		struct pci_bus *parent = bus;
+
+		if (pci_find_bus(pci_domain_nr(bus), max + i + 1))
+			break;
+
+		while (parent->parent) {
+			if (!pcibios_assign_all_busses() &&
+			    (parent->busn_res.end > max) &&
+			    (parent->busn_res.end <= max + i)) {
+				j = 1;
+			}
+			parent = parent->parent;
+		}
+		if (j) {
+			/*
+			 * Often, there are two CardBus bridges -- try to
+			 * leave one valid bus number for each one.
+			 */
+			i /= 2;
+			break;
+		}
+	}
+	max += i;
+
+	/*
+	 * Set subordinate bus number to its real value. If fixed
+	 * subordinate bus number exists from EA capability then use it.
+	 */
+	if (fixed_buses)
+		max = fixed_sub;
+	pci_bus_update_busn_res_end(child, max);
+	pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max);
+
+	scnprintf(child->name, sizeof(child->name), "PCI CardBus %04x:%02x",
+		  pci_domain_nr(bus), child->number);
+
+	pbus_validate_busn(child);
+
+	return max;
+}
-- 
2.39.5


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ