diff options
author | Michael Brown <mcb30@ipxe.org> | 2016-06-09 09:36:28 +0100 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2016-06-09 09:36:28 +0100 |
commit | f76210961c76a3f54f673d668e21e30d2eed9612 (patch) | |
tree | df0eb4b64085a587d2df39bcee478a6b320b9c01 | |
parent | 2c197517f2a82970ab6866e197f06a3099418324 (diff) | |
download | ipxe-f76210961c76a3f54f673d668e21e30d2eed9612.zip ipxe-f76210961c76a3f54f673d668e21e30d2eed9612.tar.gz ipxe-f76210961c76a3f54f673d668e21e30d2eed9612.tar.bz2 |
[pci] Support systems with multiple PCI root bridges
Extend the 16-bit PCI bus:dev.fn address to a 32-bit seg:bus:dev.fn
address, assuming a segment value of zero in contexts where multiple
segments are unsupported by the underlying data structures (e.g. in
the iBFT or BOFM tables).
Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r-- | src/arch/x86/core/pcidirect.c | 6 | ||||
-rw-r--r-- | src/core/settings.c | 13 | ||||
-rw-r--r-- | src/drivers/bus/pci.c | 8 | ||||
-rw-r--r-- | src/drivers/bus/pci_settings.c | 2 | ||||
-rw-r--r-- | src/drivers/net/phantom/phantom.c | 12 | ||||
-rw-r--r-- | src/include/ipxe/pci.h | 16 | ||||
-rw-r--r-- | src/interface/bofm/bofm.c | 4 | ||||
-rw-r--r-- | src/interface/efi/efi_pci.c | 137 | ||||
-rw-r--r-- | src/tests/settings_test.c | 4 |
9 files changed, 160 insertions, 42 deletions
diff --git a/src/arch/x86/core/pcidirect.c b/src/arch/x86/core/pcidirect.c index 9b8e6b1..0d09be8 100644 --- a/src/arch/x86/core/pcidirect.c +++ b/src/arch/x86/core/pcidirect.c @@ -36,10 +36,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * Prepare for Type 1 PCI configuration space access * * @v pci PCI device - * @v where Location within PCI configuration space + * @v where Location within PCI configuration space */ void pcidirect_prepare ( struct pci_device *pci, int where ) { - outl ( ( 0x80000000 | ( pci->busdevfn << 8 ) | ( where & ~3 ) ), + uint16_t busdevfn = ( pci->busdevfn & 0xffff ); + + outl ( ( 0x80000000 | ( busdevfn << 8 ) | ( where & ~3 ) ), PCIDIRECT_CONFIG_ADDRESS ); } diff --git a/src/core/settings.c b/src/core/settings.c index 7575557..9cae0ca 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -2232,6 +2232,10 @@ static int format_busdevfn_setting ( const struct setting_type *type __unused, const void *raw, size_t raw_len, char *buf, size_t len ) { unsigned long busdevfn; + unsigned int seg; + unsigned int bus; + unsigned int slot; + unsigned int func; int check_len; /* Extract numeric value */ @@ -2240,9 +2244,14 @@ static int format_busdevfn_setting ( const struct setting_type *type __unused, return check_len; assert ( check_len == ( int ) raw_len ); + /* Extract PCI address components */ + seg = PCI_SEG ( busdevfn ); + bus = PCI_BUS ( busdevfn ); + slot = PCI_SLOT ( busdevfn ); + func = PCI_FUNC ( busdevfn ); + /* Format value */ - return snprintf ( buf, len, "%02lx:%02lx.%lx", PCI_BUS ( busdevfn ), - PCI_SLOT ( busdevfn ), PCI_FUNC ( busdevfn ) ); + return snprintf ( buf, len, "%04x:%02x:%02x.%x", seg, bus, slot, func ); } /** PCI bus:dev.fn setting type */ diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index 6fbedd9..06b36a7 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -175,7 +175,7 @@ void adjust_pci_device ( struct pci_device *pci ) { * @ret rc Return status code */ int pci_read_config ( struct pci_device *pci ) { - uint16_t busdevfn; + uint32_t busdevfn; uint8_t hdrtype; uint32_t tmp; @@ -203,8 +203,8 @@ int pci_read_config ( struct pci_device *pci ) { pci_read_bases ( pci ); /* Initialise generic device component */ - snprintf ( pci->dev.name, sizeof ( pci->dev.name ), - "PCI%02x:%02x.%x", PCI_BUS ( pci->busdevfn ), + snprintf ( pci->dev.name, sizeof ( pci->dev.name ), "%04x:%02x:%02x.%x", + PCI_SEG ( pci->busdevfn ), PCI_BUS ( pci->busdevfn ), PCI_SLOT ( pci->busdevfn ), PCI_FUNC ( pci->busdevfn ) ); pci->dev.desc.bus_type = BUS_TYPE_PCI; pci->dev.desc.location = pci->busdevfn; @@ -232,7 +232,7 @@ int pci_find_next ( struct pci_device *pci, unsigned int busdevfn ) { /* Determine number of PCI buses */ if ( ! end ) - end = PCI_BUSDEVFN ( pci_num_bus(), 0, 0 ); + end = PCI_BUSDEVFN ( 0, pci_num_bus(), 0, 0 ); /* Find next PCI device, if any */ for ( ; busdevfn < end ; busdevfn++ ) { diff --git a/src/drivers/bus/pci_settings.c b/src/drivers/bus/pci_settings.c index 1cb9fa5..9800555 100644 --- a/src/drivers/bus/pci_settings.c +++ b/src/drivers/bus/pci_settings.c @@ -70,7 +70,7 @@ static int pci_settings_fetch ( struct settings *settings __unused, unsigned int i; /* Extract busdevfn, offset, and length from tag */ - tag_busdevfn = ( ( setting->tag >> 16 ) & 0xffff ); + tag_busdevfn = ( setting->tag >> 16 ); tag_offset = ( ( setting->tag >> 8 ) & 0xff ); tag_len = ( ( setting->tag >> 0 ) & 0xff ); diff --git a/src/drivers/net/phantom/phantom.c b/src/drivers/net/phantom/phantom.c index 38b6674..781049f 100644 --- a/src/drivers/net/phantom/phantom.c +++ b/src/drivers/net/phantom/phantom.c @@ -2060,6 +2060,7 @@ static int phantom_probe ( struct pci_device *pci ) { struct net_device *netdev; struct phantom_nic *phantom; struct settings *parent_settings; + unsigned int busdevfn; int rc; /* Allocate Phantom device */ @@ -2090,19 +2091,20 @@ static int phantom_probe ( struct pci_device *pci ) { * B2 will have this fixed; remove this hack when B1 is no * longer in use. */ - if ( PCI_FUNC ( pci->busdevfn ) == 0 ) { + busdevfn = pci->busdevfn; + if ( PCI_FUNC ( busdevfn ) == 0 ) { unsigned int i; for ( i = 0 ; i < 8 ; i++ ) { uint32_t temp; pci->busdevfn = - PCI_BUSDEVFN ( PCI_BUS ( pci->busdevfn ), - PCI_SLOT ( pci->busdevfn ), i ); + PCI_BUSDEVFN ( PCI_SEG ( busdevfn ), + PCI_BUS ( busdevfn ), + PCI_SLOT ( busdevfn ), i ); pci_read_config_dword ( pci, 0xc8, &temp ); pci_read_config_dword ( pci, 0xc8, &temp ); pci_write_config_dword ( pci, 0xc8, 0xf1000 ); } - pci->busdevfn = PCI_BUSDEVFN ( PCI_BUS ( pci->busdevfn ), - PCI_SLOT ( pci->busdevfn ), 0 ); + pci->busdevfn = busdevfn; } /* Initialise the command PEG */ diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index bf0e81d..ddd8c8d 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -195,8 +195,8 @@ struct pci_device { uint32_t class; /** Interrupt number */ uint8_t irq; - /** Bus, device, and function (bus:dev.fn) number */ - uint16_t busdevfn; + /** Segment, bus, device, and function (bus:dev.fn) number */ + uint32_t busdevfn; /** Driver for this device */ struct pci_driver *driver; /** Driver-private data @@ -241,11 +241,13 @@ struct pci_driver { /** Declare a fallback PCI driver */ #define __pci_driver_fallback __table_entry ( PCI_DRIVERS, 02 ) +#define PCI_SEG( busdevfn ) ( ( (busdevfn) >> 16 ) & 0xffff ) #define PCI_BUS( busdevfn ) ( ( (busdevfn) >> 8 ) & 0xff ) #define PCI_SLOT( busdevfn ) ( ( (busdevfn) >> 3 ) & 0x1f ) #define PCI_FUNC( busdevfn ) ( ( (busdevfn) >> 0 ) & 0x07 ) -#define PCI_BUSDEVFN( bus, slot, func ) \ - ( ( (bus) << 8 ) | ( (slot) << 3 ) | ( (func) << 0 ) ) +#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 ) @@ -271,12 +273,12 @@ struct pci_driver { PCI_ID( _vendor, _device, _name, _description, _data ) /** PCI device debug message format */ -#define PCI_FMT "PCI %02x:%02x.%x" +#define PCI_FMT "%04x:%02x:%02x.%x" /** PCI device debug message arguments */ #define PCI_ARGS( pci ) \ - PCI_BUS ( (pci)->busdevfn ), PCI_SLOT ( (pci)->busdevfn ), \ - PCI_FUNC ( (pci)->busdevfn ) + PCI_SEG ( (pci)->busdevfn ), PCI_BUS ( (pci)->busdevfn ), \ + PCI_SLOT ( (pci)->busdevfn ), PCI_FUNC ( (pci)->busdevfn ) extern void adjust_pci_device ( struct pci_device *pci ); extern unsigned long pci_bar_start ( struct pci_device *pci, diff --git a/src/interface/bofm/bofm.c b/src/interface/bofm/bofm.c index 545088d..5403919 100644 --- a/src/interface/bofm/bofm.c +++ b/src/interface/bofm/bofm.c @@ -313,12 +313,12 @@ int bofm ( userptr_t bofmtab, struct pci_device *pci ) { } DBG ( "BOFM: slot %d port %d%s is " PCI_FMT " mport %d\n", en.slot, ( en.port + 1 ), - ( ( en.slot || en.port ) ? "" : "(?)" ), + ( ( en.slot || en.port ) ? "" : "(?)" ), 0, PCI_BUS ( en.busdevfn ), PCI_SLOT ( en.busdevfn ), PCI_FUNC ( en.busdevfn ), en.mport ); bofm = bofm_find_busdevfn ( en.busdevfn ); if ( ! bofm ) { - DBG ( "BOFM: " PCI_FMT " mport %d ignored\n", + DBG ( "BOFM: " PCI_FMT " mport %d ignored\n", 0, PCI_BUS ( en.busdevfn ), PCI_SLOT ( en.busdevfn ), PCI_FUNC ( en.busdevfn ), en.mport ); continue; diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index be305ba..9f0851a 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -61,58 +61,157 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); ****************************************************************************** */ -/** PCI root bridge I/O protocol */ -static EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *efipci; -EFI_REQUEST_PROTOCOL ( EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL, &efipci ); +/** + * Locate EFI PCI root bridge I/O protocol + * + * @v pci PCI device + * @ret handle EFI PCI root bridge handle + * @ret root EFI PCI root bridge I/O protocol, or NULL if not found + * @ret rc Return status code + */ +static int efipci_root ( struct pci_device *pci, EFI_HANDLE *handle, + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL **root ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE *handles; + UINTN num_handles; + union { + void *interface; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; + } u; + EFI_STATUS efirc; + UINTN i; + int rc; + + /* Enumerate all handles */ + if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, + &efi_pci_root_bridge_io_protocol_guid, + NULL, &num_handles, &handles ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( pci, "EFIPCI cannot locate root bridges: %s\n", + strerror ( rc ) ); + goto err_locate; + } + + /* Look for matching root bridge I/O protocol */ + for ( i = 0 ; i < num_handles ; i++ ) { + *handle = handles[i]; + if ( ( efirc = bs->OpenProtocol ( *handle, + &efi_pci_root_bridge_io_protocol_guid, + &u.interface, efi_image_handle, *handle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( pci, "EFIPCI cannot open %s: %s\n", + efi_handle_name ( *handle ), strerror ( rc ) ); + continue; + } + if ( u.root->SegmentNumber == PCI_SEG ( pci->busdevfn ) ) { + *root = u.root; + bs->FreePool ( handles ); + return 0; + } + bs->CloseProtocol ( *handle, + &efi_pci_root_bridge_io_protocol_guid, + efi_image_handle, *handle ); + } + DBGC ( pci, "EFIPCI found no root bridge for " PCI_FMT "\n", + PCI_ARGS ( pci ) ); + rc = -ENOENT; + + bs->FreePool ( handles ); + err_locate: + return rc; +} +/** + * Calculate EFI PCI configuration space address + * + * @v pci PCI device + * @v location Encoded offset and width + * @ret address EFI PCI address + */ static unsigned long efipci_address ( struct pci_device *pci, unsigned long location ) { + return EFI_PCI_ADDRESS ( PCI_BUS ( pci->busdevfn ), PCI_SLOT ( pci->busdevfn ), PCI_FUNC ( pci->busdevfn ), EFIPCI_OFFSET ( location ) ); } +/** + * Read from PCI configuration space + * + * @v pci PCI device + * @v location Encoded offset and width + * @ret value Value + * @ret rc Return status code + */ int efipci_read ( struct pci_device *pci, unsigned long location, void *value ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; + EFI_HANDLE handle; EFI_STATUS efirc; int rc; - if ( ! efipci ) - return -ENOTSUP; + /* Identify root bridge */ + if ( ( rc = efipci_root ( pci, &handle, &root ) ) != 0 ) + goto err_root; - if ( ( efirc = efipci->Pci.Read ( efipci, EFIPCI_WIDTH ( location ), - efipci_address ( pci, location ), 1, - value ) ) != 0 ) { + /* Read from configuration space */ + if ( ( efirc = root->Pci.Read ( root, EFIPCI_WIDTH ( location ), + efipci_address ( pci, location ), 1, + value ) ) != 0 ) { rc = -EEFI ( efirc ); DBG ( "EFIPCI config read from " PCI_FMT " offset %02lx " "failed: %s\n", PCI_ARGS ( pci ), EFIPCI_OFFSET ( location ), strerror ( rc ) ); - return -EIO; + goto err_read; } - return 0; + err_read: + bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid, + efi_image_handle, handle ); + err_root: + return rc; } +/** + * Write to PCI configuration space + * + * @v pci PCI device + * @v location Encoded offset and width + * @v value Value + * @ret rc Return status code + */ int efipci_write ( struct pci_device *pci, unsigned long location, unsigned long value ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; + EFI_HANDLE handle; EFI_STATUS efirc; int rc; - if ( ! efipci ) - return -ENOTSUP; + /* Identify root bridge */ + if ( ( rc = efipci_root ( pci, &handle, &root ) ) != 0 ) + goto err_root; - if ( ( efirc = efipci->Pci.Write ( efipci, EFIPCI_WIDTH ( location ), - efipci_address ( pci, location ), 1, - &value ) ) != 0 ) { + /* Read from configuration space */ + if ( ( efirc = root->Pci.Write ( root, EFIPCI_WIDTH ( location ), + efipci_address ( pci, location ), 1, + &value ) ) != 0 ) { rc = -EEFI ( efirc ); DBG ( "EFIPCI config write to " PCI_FMT " offset %02lx " "failed: %s\n", PCI_ARGS ( pci ), EFIPCI_OFFSET ( location ), strerror ( rc ) ); - return -EIO; + goto err_write; } - return 0; + err_write: + bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid, + efi_image_handle, handle ); + err_root: + return rc; } PROVIDE_PCIAPI_INLINE ( efi, pci_num_bus ); @@ -146,6 +245,7 @@ int efipci_open ( EFI_HANDLE device, UINT32 attributes, void *interface; } pci_io; UINTN pci_segment, pci_bus, pci_dev, pci_fn; + unsigned int busdevfn; EFI_STATUS efirc; int rc; @@ -190,7 +290,8 @@ int efipci_open ( EFI_HANDLE device, UINT32 attributes, EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL ); /* Populate PCI device */ - pci_init ( pci, PCI_BUSDEVFN ( pci_bus, pci_dev, pci_fn ) ); + busdevfn = PCI_BUSDEVFN ( pci_segment, pci_bus, pci_dev, pci_fn ); + pci_init ( pci, busdevfn ); if ( ( rc = pci_read_config ( pci ) ) != 0 ) { DBGC ( device, "EFIPCI %s cannot read PCI configuration: %s\n", efi_handle_name ( device ), strerror ( rc ) ); diff --git a/src/tests/settings_test.c b/src/tests/settings_test.c index 89203d4..828901b 100644 --- a/src/tests/settings_test.c +++ b/src/tests/settings_test.c @@ -422,7 +422,9 @@ static void settings_test_exec ( void ) { /* "busdevfn" setting type (no store capability) */ fetchf_ok ( &test_settings, &test_busdevfn_setting, - RAW ( 0x03, 0x45 ), "03:08.5" ); + RAW ( 0x03, 0x45 ), "0000:03:08.5" ); + fetchf_ok ( &test_settings, &test_busdevfn_setting, + RAW ( 0x00, 0x02, 0x0a, 0x21 ), "0002:0a:04.1" ); /* Clear and unregister test settings block */ clear_settings ( &test_settings ); |