From ca2be7e094c900542e36f70f3abc3c8ff7c3055d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 13 Nov 2022 20:42:09 +0000 Subject: [pci] Allow PCI config space backup to be limited by maximum offset Signed-off-by: Michael Brown --- src/drivers/bus/pcibackup.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/drivers/bus') diff --git a/src/drivers/bus/pcibackup.c b/src/drivers/bus/pcibackup.c index fecad8192..4cf126f83 100644 --- a/src/drivers/bus/pcibackup.c +++ b/src/drivers/bus/pcibackup.c @@ -61,14 +61,15 @@ pci_backup_excluded ( struct pci_device *pci, unsigned int offset, * * @v pci PCI device * @v backup PCI configuration space backup + * @v limit Maximum offset in PCI configuration space * @v exclude PCI configuration space backup exclusion list, or NULL */ void pci_backup ( struct pci_device *pci, struct pci_config_backup *backup, - const uint8_t *exclude ) { + unsigned int limit, const uint8_t *exclude ) { unsigned int offset; uint32_t *dword; - for ( offset = 0, dword = backup->dwords ; offset < 0x100 ; + for ( offset = 0, dword = backup->dwords ; offset < limit ; offset += sizeof ( *dword ) , dword++ ) { if ( ! pci_backup_excluded ( pci, offset, exclude ) ) pci_read_config_dword ( pci, offset, dword ); @@ -80,14 +81,15 @@ void pci_backup ( struct pci_device *pci, struct pci_config_backup *backup, * * @v pci PCI device * @v backup PCI configuration space backup + * @v limit Maximum offset in PCI configuration space * @v exclude PCI configuration space backup exclusion list, or NULL */ void pci_restore ( struct pci_device *pci, struct pci_config_backup *backup, - const uint8_t *exclude ) { + unsigned int limit, const uint8_t *exclude ) { unsigned int offset; uint32_t *dword; - for ( offset = 0, dword = backup->dwords ; offset < 0x100 ; + for ( offset = 0, dword = backup->dwords ; offset < limit ; offset += sizeof ( *dword ) , dword++ ) { if ( ! pci_backup_excluded ( pci, offset, exclude ) ) pci_write_config_dword ( pci, offset, *dword ); -- cgit v1.2.3-55-g7522 From 2ae535532188b7772c2aba80247dfdce23d8f275 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 13 Nov 2022 20:45:38 +0000 Subject: [pci] Backup and restore standard config space across PCIe FLR The behaviour of PCI devices across a function-level reset seems to be inconsistent in practice: some devices will preserve PCI BARs, some will not. Fix the behaviour of FLR on devices that do not preserve PCI BARs by backing up and restoring PCI configuration space across the reset. Preserve only the standard portion of the configuration space, since there may be registers with unexpected side effects in the remaining non-standardised space. Signed-off-by: Michael Brown --- src/drivers/bus/pciextra.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/drivers/bus') diff --git a/src/drivers/bus/pciextra.c b/src/drivers/bus/pciextra.c index 23617bc9a..1eeb9b2a0 100644 --- a/src/drivers/bus/pciextra.c +++ b/src/drivers/bus/pciextra.c @@ -3,6 +3,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include static int pci_find_capability_common ( struct pci_device *pci, uint8_t pos, int cap ) { @@ -121,8 +122,12 @@ unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) { * @v exp PCI Express Capability address */ void pci_reset ( struct pci_device *pci, unsigned int exp ) { + struct pci_config_backup backup; uint16_t control; + /* Back up configuration space */ + pci_backup ( pci, &backup, PCI_CONFIG_BACKUP_STANDARD, NULL ); + /* Perform a PCIe function-level reset */ pci_read_config_word ( pci, ( exp + PCI_EXP_DEVCTL ), &control ); control |= PCI_EXP_DEVCTL_FLR; @@ -131,6 +136,6 @@ void pci_reset ( struct pci_device *pci, unsigned int exp ) { /* Allow time for reset to complete */ mdelay ( PCI_EXP_FLR_DELAY_MS ); - /* Re-enable device */ - adjust_pci_device ( pci ); + /* Restore configuration */ + pci_restore ( pci, &backup, PCI_CONFIG_BACKUP_STANDARD, NULL ); } -- cgit v1.2.3-55-g7522 From 4e456d992889569e2dbb0426f2438797ab06ca1f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 3 Feb 2023 16:10:31 +0000 Subject: [efi] Do not attempt to drive PCI bridge devices The "bridge" driver introduced in 3aa6b79 ("[pci] Add minimal PCI bridge driver") is required only for BIOS builds using the ENA driver, where experimentation shows that we cannot rely on the BIOS to fully assign MMIO addresses. Since the driver is a valid PCI driver, it will end up binding to all PCI bridge devices even on a UEFI platform, where the firmware is likely to have completed MMIO address assignment correctly. This has no impact on most systems since there is generally no UEFI driver for PCI bridges: the enumeration of the whole PCI bus is handled by the PciBusDxe driver bound to the root bridge. Experimentation shows that at least one laptop will freeze at the point that iPXE attempts to bind to the bridge device. No deeper investigation has been carried out to find the root cause. Fix by causing efipci_supported() to return an error unless the configuration space header type indicates a non-bridge device. Reported-by: Marcel Petersen Signed-off-by: Michael Brown --- src/drivers/bus/pci.c | 1 + src/include/ipxe/pci.h | 2 ++ src/interface/efi/efi_pci.c | 10 ++++++++++ 3 files changed, 13 insertions(+) (limited to 'src/drivers/bus') diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index 7953aaedd..92b389641 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -205,6 +205,7 @@ int pci_read_config ( struct pci_device *pci ) { pci_read_config_dword ( pci, PCI_REVISION, &tmp ); pci->class = ( tmp >> 8 ); pci_read_config_byte ( pci, PCI_INTERRUPT_LINE, &pci->irq ); + pci_read_config_byte ( pci, PCI_HEADER_TYPE, &pci->hdrtype ); pci_read_bases ( pci ); /* Initialise generic device component */ diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index 637b20d60..8c6d9e4e2 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -227,6 +227,8 @@ struct pci_device { uint32_t class; /** Interrupt number */ uint8_t irq; + /** Header type */ + uint8_t hdrtype; /** Segment, bus, device, and function (bus:dev.fn) number */ uint32_t busdevfn; /** Driver for this device */ diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index 4796201e9..e2eeeb344 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -785,12 +785,22 @@ int efipci_info ( EFI_HANDLE device, struct efi_pci_device *efipci ) { */ static int efipci_supported ( EFI_HANDLE device ) { struct efi_pci_device efipci; + uint8_t hdrtype; int rc; /* Get PCI device information */ if ( ( rc = efipci_info ( device, &efipci ) ) != 0 ) return rc; + /* Do not attempt to drive bridges */ + hdrtype = efipci.pci.hdrtype; + if ( ( hdrtype & PCI_HEADER_TYPE_MASK ) != PCI_HEADER_TYPE_NORMAL ) { + DBGC ( device, "EFIPCI " PCI_FMT " type %02x is not type %02x\n", + PCI_ARGS ( &efipci.pci ), hdrtype, + PCI_HEADER_TYPE_NORMAL ); + return -ENOTTY; + } + /* Look for a driver */ if ( ( rc = pci_find_driver ( &efipci.pci ) ) != 0 ) { DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) " -- cgit v1.2.3-55-g7522 From be8ecaf8059a34f27ed34bbc2e95806537802108 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 10 Feb 2023 23:18:47 +0000 Subject: [eisa] Check for system board presence before probing for slots EISA expansion slot I/O port addresses overlap space that may be assigned to PCI devices, which can lead to register reads and writes with unwanted side effects during EISA probing. Reduce the chances of performing EISA probing on PCI devices by probing EISA slot vendor and product ID registers only if the EISA system board vendor ID register indicates that the motherboard supports EISA. Debugged-by: Václav Ovsík Tested-by: Václav Ovsík Signed-off-by: Michael Brown --- src/drivers/bus/eisa.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/drivers/bus') diff --git a/src/drivers/bus/eisa.c b/src/drivers/bus/eisa.c index a4efe2621..68837eb4d 100644 --- a/src/drivers/bus/eisa.c +++ b/src/drivers/bus/eisa.c @@ -98,8 +98,16 @@ static void eisa_remove ( struct eisa_device *eisa ) { static int eisabus_probe ( struct root_device *rootdev ) { struct eisa_device *eisa = NULL; unsigned int slot; + uint8_t system; int rc; + /* Check for EISA system board */ + system = inb ( EISA_VENDOR_ID ); + if ( system & 0x80 ) { + DBG ( "No EISA system board (read %02x)\n", system ); + return -ENODEV; + } + for ( slot = EISA_MIN_SLOT ; slot <= EISA_MAX_SLOT ; slot++ ) { /* Allocate struct eisa_device */ if ( ! eisa ) -- cgit v1.2.3-55-g7522