aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2022-09-15 16:47:04 +0100
committerMichael Brown <mcb30@ipxe.org>2022-09-15 16:49:47 +0100
commitff228f745c15594291fd3cbf3c02af27753a3885 (patch)
tree20fcd06b1407db029e1969095c9f4a449451ba64
parent56b30364c5db6367279ffe88929f286f15680b40 (diff)
downloadipxe-ff228f745c15594291fd3cbf3c02af27753a3885.zip
ipxe-ff228f745c15594291fd3cbf3c02af27753a3885.tar.gz
ipxe-ff228f745c15594291fd3cbf3c02af27753a3885.tar.bz2
[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 <mcb30@ipxe.org>
-rw-r--r--src/arch/x86/core/pcidirect.c2
-rw-r--r--src/arch/x86/include/ipxe/pcidirect.h14
-rw-r--r--src/arch/x86/interface/pcbios/pcibios.c14
-rw-r--r--src/drivers/bus/pci.c34
-rw-r--r--src/include/ipxe/efi/efi_pci_api.h13
-rw-r--r--src/include/ipxe/linux/linux_pci.h16
-rw-r--r--src/include/ipxe/pci.h3
-rw-r--r--src/include/ipxe/pci_io.h21
-rw-r--r--src/interface/efi/efi_pci.c2
-rw-r--r--src/interface/linux/linux_pci.c2
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 <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
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 );