]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[pci] Allow PCI configuration space access mechanism to vary by range
authorMichael Brown <mcb30@ipxe.org>
Mon, 24 Nov 2025 14:44:53 +0000 (14:44 +0000)
committerMichael Brown <mcb30@ipxe.org>
Mon, 24 Nov 2025 19:10:49 +0000 (19:10 +0000)
On some systems (observed on an AWS EC2 c7a.medium instance in
eu-west-2), there is no single PCI configuration space access
mechanism that covers all PCI devices.  For example: the ECAM may
provide access only to bus 01 and above, with type 1 accesses required
to access bus 00.

Allow the choice of PCI configuration space access mechanism to be
made on a per-range basis, rather than being made just once in an
initialisation function.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/arch/x86/interface/pcbios/pcicloud.c

index 5d4d02ac4357242d0fe810bd5ceb9871a85bf8cd..284cd078c9b4a101cb541d61516e32d3b6978d0d 100644 (file)
@@ -24,7 +24,7 @@
 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
 #include <stdint.h>
-#include <ipxe/init.h>
+#include <string.h>
 #include <ipxe/pci.h>
 #include <ipxe/ecam.h>
 #include <ipxe/pcibios.h>
@@ -37,8 +37,70 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  *
  */
 
-/** Selected PCI configuration space access API */
-static struct pci_api *pcicloud = &ecam_api;
+/** Underlying PCI configuration space access APIs */
+static struct pci_api *pcicloud_apis[] = {
+       &ecam_api, &pcibios_api, &pcidirect_api
+};
+
+/** Cached PCI configuration space access API */
+static struct {
+       /** PCI bus:dev.fn address range */
+       struct pci_range range;
+       /** API for this bus:dev.fn address */
+       struct pci_api *api;
+} pcicloud;
+
+/**
+ * Find PCI configuration space access API for address
+ *
+ * @v busdevfn         Starting PCI bus:dev.fn address
+ * @v range            PCI bus:dev.fn address range to fill in
+ * @ret api            Configuration space access API, or NULL
+ */
+static struct pci_api * pcicloud_find ( uint32_t busdevfn,
+                                       struct pci_range *range ) {
+       struct pci_range candidate;
+       struct pci_api *api;
+       uint32_t best = 0;
+       uint32_t index;
+       uint32_t first;
+       uint32_t last;
+       unsigned int i;
+
+       /* Return empty range on error */
+       range->count = 0;
+
+       /* Try discovery via all known APIs */
+       for ( i = 0 ; i < ( sizeof ( pcicloud_apis ) /
+                           sizeof ( pcicloud_apis[0] ) ) ; i++ ) {
+
+               /* Discover via this API */
+               api = pcicloud_apis[i];
+               api->pci_discover ( busdevfn, &candidate );
+
+               /* Check for a matching or new closest allocation */
+               index = ( busdevfn - candidate.start );
+               if ( ( index < candidate.count ) || ( index > best ) ) {
+                       memcpy ( range, &candidate, sizeof ( *range ) );
+                       best = index;
+               }
+
+               /* Stop if this range contains the target bus:dev.fn address */
+               if ( index < candidate.count ) {
+                       first = range->start;
+                       last = ( range->start + range->count - 1 );
+                       DBGC ( &pcicloud, "PCICLOUD [" PCI_FMT "," PCI_FMT ") "
+                              "using %s API\n", PCI_SEG ( first ),
+                              PCI_BUS ( first ), PCI_SLOT ( first ),
+                              PCI_FUNC ( first ), PCI_SEG ( last ),
+                              PCI_BUS ( last ), PCI_SLOT ( last ),
+                              PCI_FUNC ( last ), api->name );
+                       return api;
+               }
+       }
+
+       return NULL;
+}
 
 /**
  * Find next PCI bus:dev.fn address range in system
@@ -48,7 +110,48 @@ static struct pci_api *pcicloud = &ecam_api;
  */
 static void pcicloud_discover ( uint32_t busdevfn, struct pci_range *range ) {
 
-       pcicloud->pci_discover ( busdevfn, range );
+       /* Find new range, if any */
+       pcicloud_find ( busdevfn, range );
+}
+
+/**
+ * Find configuration space access API for PCI device
+ *
+ * @v pci              PCI device
+ * @ret api            Configuration space access API
+ */
+static struct pci_api * pcicloud_api ( struct pci_device *pci ) {
+       struct pci_range *range = &pcicloud.range;
+       struct pci_api *api;
+       uint32_t first;
+       uint32_t last;
+
+       /* Reuse cached API if applicable */
+       if ( ( pci->busdevfn - range->start ) < range->count )
+               return pcicloud.api;
+
+       /* Find highest priority API claiming this range */
+       api = pcicloud_find ( pci->busdevfn, range );
+
+       /* Fall back to lowest priority API for any unclaimed gaps in ranges */
+       if ( ! api ) {
+               api = pcicloud_apis[ ( sizeof ( pcicloud_apis ) /
+                                      sizeof ( pcicloud_apis[0] ) ) - 1 ];
+               range->count = ( range->start - pci->busdevfn );
+               range->start = pci->busdevfn;
+               first = range->start;
+               last = ( range->start + range->count - 1 );
+               DBGC ( &pcicloud, "PCICLOUD [" PCI_FMT "," PCI_FMT ") falling "
+                      "back to %s API\n", PCI_SEG ( first ),
+                      PCI_BUS ( first ), PCI_SLOT ( first ),
+                      PCI_FUNC ( first ), PCI_SEG ( last ), PCI_BUS ( last ),
+                      PCI_SLOT ( last ),  PCI_FUNC ( last ), api->name );
+       }
+
+       /* Cache API for this range */
+       pcicloud.api = api;
+
+       return api;
 }
 
 /**
@@ -61,8 +164,9 @@ static void pcicloud_discover ( uint32_t busdevfn, struct pci_range *range ) {
  */
 static int pcicloud_read_config_byte ( struct pci_device *pci,
                                       unsigned int where, uint8_t *value ) {
+       struct pci_api *api = pcicloud_api ( pci );
 
-       return pcicloud->pci_read_config_byte ( pci, where, value );
+       return api->pci_read_config_byte ( pci, where, value );
 }
 
 /**
@@ -75,8 +179,9 @@ static int pcicloud_read_config_byte ( struct pci_device *pci,
  */
 static int pcicloud_read_config_word ( struct pci_device *pci,
                                       unsigned int where, uint16_t *value ) {
+       struct pci_api *api = pcicloud_api ( pci );
 
-       return pcicloud->pci_read_config_word ( pci, where, value );
+       return api->pci_read_config_word ( pci, where, value );
 }
 
 /**
@@ -89,8 +194,9 @@ static int pcicloud_read_config_word ( struct pci_device *pci,
  */
 static int pcicloud_read_config_dword ( struct pci_device *pci,
                                        unsigned int where, uint32_t *value ) {
+       struct pci_api *api = pcicloud_api ( pci );
 
-       return pcicloud->pci_read_config_dword ( pci, where, value );
+       return api->pci_read_config_dword ( pci, where, value );
 }
 
 /**
@@ -103,8 +209,9 @@ static int pcicloud_read_config_dword ( struct pci_device *pci,
  */
 static int pcicloud_write_config_byte ( struct pci_device *pci,
                                        unsigned int where, uint8_t value ) {
+       struct pci_api *api = pcicloud_api ( pci );
 
-       return pcicloud->pci_write_config_byte ( pci, where, value );
+       return api->pci_write_config_byte ( pci, where, value );
 }
 
 /**
@@ -117,8 +224,9 @@ static int pcicloud_write_config_byte ( struct pci_device *pci,
  */
 static int pcicloud_write_config_word ( struct pci_device *pci,
                                        unsigned int where, uint16_t value ) {
+       struct pci_api *api = pcicloud_api ( pci );
 
-       return pcicloud->pci_write_config_word ( pci, where, value );
+       return api->pci_write_config_word ( pci, where, value );
 }
 
 /**
@@ -131,8 +239,9 @@ static int pcicloud_write_config_word ( struct pci_device *pci,
  */
 static int pcicloud_write_config_dword ( struct pci_device *pci,
                                         unsigned int where, uint32_t value ) {
+       struct pci_api *api = pcicloud_api ( pci );
 
-       return pcicloud->pci_write_config_dword ( pci, where, value );
+       return api->pci_write_config_dword ( pci, where, value );
 }
 
 /**
@@ -144,8 +253,9 @@ static int pcicloud_write_config_dword ( struct pci_device *pci,
  */
 static void * pcicloud_ioremap ( struct pci_device *pci,
                                 unsigned long bus_addr, size_t len ) {
+       struct pci_api *api = pcicloud_api ( pci );
 
-       return pcicloud->pci_ioremap ( pci, bus_addr, len );
+       return api->pci_ioremap ( pci, bus_addr, len );
 }
 
 PROVIDE_PCIAPI_INLINE ( cloud, pci_can_probe );
@@ -157,40 +267,3 @@ PROVIDE_PCIAPI ( cloud, pci_write_config_byte, pcicloud_write_config_byte );
 PROVIDE_PCIAPI ( cloud, pci_write_config_word, pcicloud_write_config_word );
 PROVIDE_PCIAPI ( cloud, pci_write_config_dword, pcicloud_write_config_dword );
 PROVIDE_PCIAPI ( cloud, pci_ioremap, pcicloud_ioremap );
-
-/**
- * Initialise cloud VM PCI configuration space access
- *
- */
-static void pcicloud_init ( void ) {
-       static struct pci_api *apis[] = {
-               &ecam_api, &pcibios_api, &pcidirect_api
-       };
-       struct pci_device pci;
-       uint32_t busdevfn;
-       unsigned int i;
-       int rc;
-
-       /* Select first API that successfully discovers a PCI device */
-       for ( i = 0 ; i < ( sizeof ( apis ) / sizeof ( apis[0] ) ) ; i++ ) {
-               pcicloud = apis[i];
-               busdevfn = 0;
-               if ( ( rc = pci_find_next ( &pci, &busdevfn ) ) == 0 ) {
-                       DBGC ( pcicloud, "PCICLOUD selected %s API (found "
-                              PCI_FMT ")\n", pcicloud->name,
-                              PCI_ARGS ( &pci ) );
-                       return;
-               }
-       }
-
-       /* Fall back to using final attempted API if no devices found */
-       pcicloud = apis[ i - 1 ];
-       DBGC ( pcicloud, "PCICLOUD selected %s API (nothing detected)\n",
-              pcicloud->name );
-}
-
-/** Cloud VM PCI configuration space access initialisation function */
-struct init_fn pcicloud_init_fn __init_fn ( INIT_EARLY ) = {
-       .name = "pcicloud",
-       .initialise = pcicloud_init,
-};