]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[pci] Avoid scanning nonexistent buses when using PCIAPI_DIRECT
authorMichael Brown <mcb30@ipxe.org>
Sat, 10 Apr 2021 12:14:30 +0000 (13:14 +0100)
committerMichael Brown <mcb30@ipxe.org>
Sat, 10 Apr 2021 14:05:05 +0000 (15:05 +0100)
There is no method for obtaining the number of PCI buses when using
PCIAPI_DIRECT, and we therefore currently scan all possible bus
numbers.  This can cause a several-second startup delay in some
virtualised environments, since PCI configuration space access will
necessarily require the involvement of the hypervisor.

Ameliorate this situation by defaulting to scanning only a single bus,
and expanding the number of PCI buses to accommodate any subordinate
buses that are detected during enumeration.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/arch/x86/include/ipxe/pcidirect.h
src/drivers/bus/pci.c
src/include/ipxe/pci.h

index 9570fd7d6023977ba1956bc1cbe2d3876765683b..decdc810008fb39033eee93f4978fbebfc0e54ae 100644 (file)
@@ -32,8 +32,8 @@ extern void pcidirect_prepare ( struct pci_device *pci, int where );
  */
 static inline __always_inline int
 PCIAPI_INLINE ( direct, pci_num_bus ) ( void ) {
-       /* No way to work this out via Type 1 accesses */
-       return 0x100;
+       /* Scan first bus and rely on bridge detection to find higher buses */
+       return 1;
 }
 
 /**
index 06b36a770ea2614cefaac33b6bfe630790a44633..1b7350c8b8cea04b4e0653ced344d8c07e1a49f4 100644 (file)
@@ -228,6 +228,9 @@ int pci_read_config ( struct pci_device *pci ) {
  */
 int pci_find_next ( struct pci_device *pci, unsigned int busdevfn ) {
        static unsigned int end;
+       unsigned int sub_end;
+       uint8_t hdrtype;
+       uint8_t sub;
        int rc;
 
        /* Determine number of PCI buses */
@@ -236,10 +239,30 @@ int pci_find_next ( struct pci_device *pci, unsigned int busdevfn ) {
 
        /* Find next PCI device, if any */
        for ( ; busdevfn < end ; busdevfn++ ) {
+
+               /* Check for PCI device existence */
                memset ( pci, 0, sizeof ( *pci ) );
                pci_init ( pci, busdevfn );
-               if ( ( rc = pci_read_config ( pci ) ) == 0 )
-                       return busdevfn;
+               if ( ( rc = pci_read_config ( pci ) ) != 0 )
+                       continue;
+
+               /* If device is a bridge, expand the number of PCI
+                * buses 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 ) {
+                               DBGC ( pci, PCI_FMT " found subordinate bus "
+                                      "%#02x\n", PCI_ARGS ( pci ), sub );
+                               end = sub_end;
+                       }
+               }
+
+               /* Return this device */
+               return busdevfn;
        }
 
        return -ENODEV;
index 6632c574d4a2a4405fb5391a701d8ed1f42df761..933f485307dcd29e5e73ad950885c5aaeee83c91 100644 (file)
@@ -135,6 +135,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #define PCI_CLASS_SERIAL_USB_EHCI       0x20   /**< ECHI USB controller */
 #define PCI_CLASS_SERIAL_USB_XHCI       0x30   /**< xHCI USB controller */
 
+/** Subordinate bus number */
+#define PCI_SUBORDINATE                0x1a
+
 /** Construct PCI class
  *
  * @v base             Base class (or PCI_ANY_ID)