From 0cf2f8028c5468303f6f8ec7f639fdfd31d70e74 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 24 Nov 2025 14:44:53 +0000 Subject: [PATCH] [pci] Allow PCI configuration space access mechanism to vary by range 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 --- src/arch/x86/interface/pcbios/pcicloud.c | 169 ++++++++++++++++------- 1 file changed, 121 insertions(+), 48 deletions(-) diff --git a/src/arch/x86/interface/pcbios/pcicloud.c b/src/arch/x86/interface/pcbios/pcicloud.c index 5d4d02ac4..284cd078c 100644 --- a/src/arch/x86/interface/pcbios/pcicloud.c +++ b/src/arch/x86/interface/pcbios/pcicloud.c @@ -24,7 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -#include +#include #include #include #include @@ -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, -};