From ff228f745c15594291fd3cbf3c02af27753a3885 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 15 Sep 2022 16:47:04 +0100 Subject: [pci] Generalise pci_num_bus() to pci_discover() 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 --- src/arch/x86/core/pcidirect.c | 2 +- src/arch/x86/include/ipxe/pcidirect.h | 14 +++++++++----- src/arch/x86/interface/pcbios/pcibios.c | 14 +++++++++----- src/drivers/bus/pci.c | 34 ++++++++++++++++++++------------- src/include/ipxe/efi/efi_pci_api.h | 13 ++++++++----- src/include/ipxe/linux/linux_pci.h | 16 ++++++++++------ src/include/ipxe/pci.h | 3 --- src/include/ipxe/pci_io.h | 21 +++++++++++++++++--- src/interface/efi/efi_pci.c | 2 +- src/interface/linux/linux_pci.c | 2 +- 10 files changed, 78 insertions(+), 43 deletions(-) diff --git a/src/arch/x86/core/pcidirect.c b/src/arch/x86/core/pcidirect.c index 9b8226f..88db904 100644 --- a/src/arch/x86/core/pcidirect.c +++ b/src/arch/x86/core/pcidirect.c @@ -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 ); diff --git a/src/arch/x86/include/ipxe/pcidirect.h b/src/arch/x86/include/ipxe/pcidirect.h index decdc81..394edb2 100644 --- a/src/arch/x86/include/ipxe/pcidirect.h +++ b/src/arch/x86/include/ipxe/pcidirect.h @@ -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 ); } /** diff --git a/src/arch/x86/interface/pcbios/pcibios.c b/src/arch/x86/interface/pcbios/pcibios.c index bf812f7..cf81863 100644 --- a/src/arch/x86/interface/pcbios/pcibios.c +++ b/src/arch/x86/interface/pcbios/pcibios.c @@ -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 ); diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index c5deb08..7953aae 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -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; } diff --git a/src/include/ipxe/efi/efi_pci_api.h b/src/include/ipxe/efi/efi_pci_api.h index 887d5ee..cf5e1d0 100644 --- a/src/include/ipxe/efi/efi_pci_api.h +++ b/src/include/ipxe/efi/efi_pci_api.h @@ -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; } /** diff --git a/src/include/ipxe/linux/linux_pci.h b/src/include/ipxe/linux/linux_pci.h index de42f76..ec6ff8b 100644 --- a/src/include/ipxe/linux/linux_pci.h +++ b/src/include/ipxe/linux/linux_pci.h @@ -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 ); } /** diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index bd12367..c91baad 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -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 ) diff --git a/src/include/ipxe/pci_io.h b/src/include/ipxe/pci_io.h index 2dcdd9b..91359ce 100644 --- a/src/include/ipxe/pci_io.h +++ b/src/include/ipxe/pci_io.h @@ -14,6 +14,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +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 /** - * 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 diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index fda4aba..19e3417 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -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 ); diff --git a/src/interface/linux/linux_pci.c b/src/interface/linux/linux_pci.c index e3c0daa..3008447 100644 --- a/src/interface/linux/linux_pci.c +++ b/src/interface/linux/linux_pci.c @@ -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 ); -- cgit v1.1