]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[pci] Generalise pci_num_bus() to pci_discover()
authorMichael Brown <mcb30@ipxe.org>
Thu, 15 Sep 2022 15:47:04 +0000 (16:47 +0100)
committerMichael Brown <mcb30@ipxe.org>
Thu, 15 Sep 2022 15:49:47 +0000 (16:49 +0100)
Allow pci_find_next() to discover devices beyond the first PCI
segment, by generalising pci_num_bus() (which implicitly assumes that
there is only a single PCI segment) with pci_discover() (which has the
ability to return an arbitrary contiguous chunk of PCI bus:dev.fn
address space).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/arch/x86/core/pcidirect.c
src/arch/x86/include/ipxe/pcidirect.h
src/arch/x86/interface/pcbios/pcibios.c
src/drivers/bus/pci.c
src/include/ipxe/efi/efi_pci_api.h
src/include/ipxe/linux/linux_pci.h
src/include/ipxe/pci.h
src/include/ipxe/pci_io.h
src/interface/efi/efi_pci.c
src/interface/linux/linux_pci.c

index 9b8226feac97855732399a8dc401d13b6b82bbd4..88db9049948222f291dcf8a3df942fee13f1b3a2 100644 (file)
@@ -45,7 +45,7 @@ void pcidirect_prepare ( struct pci_device *pci, int where ) {
               PCIDIRECT_CONFIG_ADDRESS );
 }
 
-PROVIDE_PCIAPI_INLINE ( direct, pci_num_bus );
+PROVIDE_PCIAPI_INLINE ( direct, pci_discover );
 PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_byte );
 PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_word );
 PROVIDE_PCIAPI_INLINE ( direct, pci_read_config_dword );
index decdc810008fb39033eee93f4978fbebfc0e54ae..394edb2b8e7953b76ca32bce20e95f8b1cb7b729 100644 (file)
@@ -26,14 +26,18 @@ struct pci_device;
 extern void pcidirect_prepare ( struct pci_device *pci, int where );
 
 /**
- * Determine number of PCI buses within system
+ * Find next PCI bus:dev.fn address range in system
  *
- * @ret num_bus                Number of buses
+ * @v busdevfn         Starting PCI bus:dev.fn address
+ * @v range            PCI bus:dev.fn address range to fill in
  */
-static inline __always_inline int
-PCIAPI_INLINE ( direct, pci_num_bus ) ( void ) {
+static inline __always_inline void
+PCIAPI_INLINE ( direct, pci_discover ) ( uint32_t busdevfn __unused,
+                                        struct pci_range *range ) {
+
        /* Scan first bus and rely on bridge detection to find higher buses */
-       return 1;
+       range->start = PCI_BUSDEVFN ( 0, 0, 0, 0 );
+       range->count = PCI_BUSDEVFN ( 0, 1, 0, 0 );
 }
 
 /**
index bf812f77fee963ec371811e7959f82e48a99c99b..cf818630a92abf71d3fe491aef452186e75d8cb8 100644 (file)
@@ -34,11 +34,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  */
 
 /**
- * Determine number of PCI buses within system
+ * Find next PCI bus:dev.fn address range in system
  *
- * @ret num_bus                Number of buses
+ * @v busdevfn         Starting PCI bus:dev.fn address
+ * @v range            PCI bus:dev.fn address range to fill in
  */
-static int pcibios_num_bus ( void ) {
+static void pcibios_discover ( uint32_t busdevfn __unused,
+                              struct pci_range *range ) {
        int discard_a, discard_D;
        uint8_t max_bus;
 
@@ -57,7 +59,9 @@ static int pcibios_num_bus ( void ) {
                                 "D" ( 0 )
                               : "ebx", "edx" );
 
-       return ( max_bus + 1 );
+       /* Populate range */
+       range->start = PCI_BUSDEVFN ( 0, 0, 0, 0 );
+       range->count = PCI_BUSDEVFN ( 0, ( max_bus + 1 ), 0, 0 );
 }
 
 /**
@@ -114,7 +118,7 @@ int pcibios_write ( struct pci_device *pci, uint32_t command, uint32_t value ){
        return ( status >> 8 );
 }
 
-PROVIDE_PCIAPI ( pcbios, pci_num_bus, pcibios_num_bus );
+PROVIDE_PCIAPI ( pcbios, pci_discover, pcibios_discover );
 PROVIDE_PCIAPI_INLINE ( pcbios, pci_read_config_byte );
 PROVIDE_PCIAPI_INLINE ( pcbios, pci_read_config_word );
 PROVIDE_PCIAPI_INLINE ( pcbios, pci_read_config_dword );
index c5deb08f618863bc8b21fc6442ac8925500559cd..7953aaedd763dec4cf0980f1b7f4026c8ada3ac7 100644 (file)
@@ -233,18 +233,23 @@ int pci_read_config ( struct pci_device *pci ) {
  * @ret rc             Return status code
  */
 int pci_find_next ( struct pci_device *pci, uint32_t *busdevfn ) {
-       static unsigned int end;
-       unsigned int sub_end;
+       static struct pci_range range;
        uint8_t hdrtype;
        uint8_t sub;
+       uint32_t end;
+       unsigned int count;
        int rc;
 
-       /* Determine number of PCI buses */
-       if ( ! end )
-               end = PCI_BUSDEVFN ( 0, pci_num_bus(), 0, 0 );
-
        /* Find next PCI device, if any */
-       for ( ; *busdevfn < end ; (*busdevfn)++ ) {
+       do {
+               /* Find next PCI bus:dev.fn address range, if necessary */
+               if ( ( *busdevfn - range.start ) >= range.count ) {
+                       pci_discover ( *busdevfn, &range );
+                       if ( *busdevfn < range.start )
+                               *busdevfn = range.start;
+                       if ( ( *busdevfn - range.start ) >= range.count )
+                               break;
+               }
 
                /* Check for PCI device existence */
                memset ( pci, 0, sizeof ( *pci ) );
@@ -252,24 +257,27 @@ int pci_find_next ( struct pci_device *pci, uint32_t *busdevfn ) {
                if ( ( rc = pci_read_config ( pci ) ) != 0 )
                        continue;
 
-               /* If device is a bridge, expand the number of PCI
-                * buses as needed.
+               /* If device is a bridge, expand the PCI bus:dev.fn
+                * address range as needed.
                 */
                pci_read_config_byte ( pci, PCI_HEADER_TYPE, &hdrtype );
                hdrtype &= PCI_HEADER_TYPE_MASK;
                if ( hdrtype == PCI_HEADER_TYPE_BRIDGE ) {
                        pci_read_config_byte ( pci, PCI_SUBORDINATE, &sub );
-                       sub_end = PCI_BUSDEVFN ( 0, ( sub + 1 ), 0, 0 );
-                       if ( end < sub_end ) {
+                       end = PCI_BUSDEVFN ( PCI_SEG ( *busdevfn ),
+                                            ( sub + 1 ), 0, 0 );
+                       count = ( end - range.start );
+                       if ( count > range.count ) {
                                DBGC ( pci, PCI_FMT " found subordinate bus "
                                       "%#02x\n", PCI_ARGS ( pci ), sub );
-                               end = sub_end;
+                               range.count = count;
                        }
                }
 
                /* Return this device */
                return 0;
-       }
+
+       } while ( ++(*busdevfn) );
 
        return -ENODEV;
 }
index 887d5ee14e8b3a525ea6f09ac859a2852e4382dc..cf5e1d0200a5260ce358b0eb5ae47866da4d6cf0 100644 (file)
@@ -33,14 +33,17 @@ extern int efipci_write ( struct pci_device *pci, unsigned long location,
                          unsigned long value );
 
 /**
- * Determine number of PCI buses within system
+ * Find next PCI bus:dev.fn address range in system
  *
- * @ret num_bus                Number of buses
+ * @v busdevfn         Starting PCI bus:dev.fn address
+ * @v range            PCI bus:dev.fn address range to fill in
  */
-static inline __always_inline int
-PCIAPI_INLINE ( efi, pci_num_bus ) ( void ) {
+static inline __always_inline void
+PCIAPI_INLINE ( efi, pci_discover ) ( uint32_t busdevfn __unused,
+                                     struct pci_range *range ) {
+
        /* EFI does not want us to scan the PCI bus ourselves */
-       return 0;
+       range->count = 0;
 }
 
 /**
index de42f766b1c22d760123326e88fcedf679eab10c..ec6ff8b1c6a57bd202574dd9a1996e4a8d41c040 100644 (file)
@@ -23,14 +23,18 @@ extern int linux_pci_write ( struct pci_device *pci, unsigned long where,
                             unsigned long value, size_t len );
 
 /**
- * Determine number of PCI buses within system
+ * Find next PCI bus:dev.fn address range in system
  *
- * @ret num_bus                Number of buses
+ * @v busdevfn         Starting PCI bus:dev.fn address
+ * @v range            PCI bus:dev.fn address range to fill in
  */
-static inline __always_inline int
-PCIAPI_INLINE ( linux, pci_num_bus ) ( void ) {
-       /* Assume all buses may exist */
-       return 0x100;
+static inline __always_inline void
+PCIAPI_INLINE ( linux, pci_discover ) ( uint32_t busdevfn __unused,
+                                       struct pci_range *range ) {
+
+       /* Assume all buses in segment 0 may exist */
+       range->start = PCI_BUSDEVFN ( 0, 0, 0, 0 );
+       range->count = PCI_BUSDEVFN ( 1, 0, 0, 0 );
 }
 
 /**
index bd123679a8e79a7d89d9d04e5ade7beda7f1abcc..c91baaddd69274281c1c757706dc4a49f5c05a2d 100644 (file)
@@ -262,9 +262,6 @@ struct pci_driver {
 #define PCI_BUS( busdevfn )            ( ( (busdevfn) >> 8 ) & 0xff )
 #define PCI_SLOT( busdevfn )           ( ( (busdevfn) >> 3 ) & 0x1f )
 #define PCI_FUNC( busdevfn )           ( ( (busdevfn) >> 0 ) & 0x07 )
-#define PCI_BUSDEVFN( segment, bus, slot, func )                       \
-       ( ( (segment) << 16 ) | ( (bus) << 8 ) |                        \
-         ( (slot) << 3 ) | ( (func) << 0 ) )
 #define PCI_FIRST_FUNC( busdevfn )     ( (busdevfn) & ~0x07 )
 #define PCI_LAST_FUNC( busdevfn )      ( (busdevfn) | 0x07 )
 
index 2dcdd9b28a6411f0015255ddc6e8bfc12eeda25b..91359cec8c1e181a25d6877a84b74479d48712af 100644 (file)
@@ -14,6 +14,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <ipxe/iomap.h>
 #include <config/ioapi.h>
 
+struct pci_device;
+
+/** A PCI bus:dev.fn address range */
+struct pci_range {
+       /** Starting bus:dev.fn address */
+       uint32_t start;
+       /** Number of bus:dev.fn addresses within this range */
+       unsigned int count;
+};
+
+#define PCI_BUSDEVFN( segment, bus, slot, func )                       \
+       ( ( (segment) << 16 ) | ( (bus) << 8 ) |                        \
+         ( (slot) << 3 ) | ( (func) << 0 ) )
+
 /**
  * Calculate static inline PCI I/O API function name
  *
@@ -51,11 +65,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <bits/pci_io.h>
 
 /**
- * Determine number of PCI buses within system
+ * Find next PCI bus:dev.fn address range in system
  *
- * @ret num_bus                Number of buses
+ * @v busdevfn         Starting PCI bus:dev.fn address
+ * @v range            PCI bus:dev.fn address range to fill in
  */
-int pci_num_bus ( void );
+void pci_discover ( uint32_t busdevfn, struct pci_range *range );
 
 /**
  * Read byte from PCI configuration space
index fda4aba0e0b218d3f01d315e9faa49fa91eda517..19e34170765f8047dc0ddfa9de3138de4fc7c57d 100644 (file)
@@ -362,7 +362,7 @@ void * efipci_ioremap ( struct pci_device *pci, unsigned long bus_addr,
        return ioremap ( bus_addr, len );
 }
 
-PROVIDE_PCIAPI_INLINE ( efi, pci_num_bus );
+PROVIDE_PCIAPI_INLINE ( efi, pci_discover );
 PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_byte );
 PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_word );
 PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_dword );
index e3c0daa3279c1fec1d42e5b835f73e0b4bd10556..3008447375a6ca19d686ec9f7905ea98e5e8d198 100644 (file)
@@ -188,7 +188,7 @@ int linux_pci_write ( struct pci_device *pci, unsigned long where,
        return rc;
 }
 
-PROVIDE_PCIAPI_INLINE ( linux, pci_num_bus );
+PROVIDE_PCIAPI_INLINE ( linux, pci_discover );
 PROVIDE_PCIAPI_INLINE ( linux, pci_read_config_byte );
 PROVIDE_PCIAPI_INLINE ( linux, pci_read_config_word );
 PROVIDE_PCIAPI_INLINE ( linux, pci_read_config_dword );