[ipxe-devel] [PATCH] efcpci_root doesn't account for bus ranges

Thomas Walker Thomas.Walker at twosigma.com
Tue May 12 18:36:03 UTC 2020


efipci_root only looks at PCI_SEG when trying to determine the correct I/O
root handle for a device but each has a list of descriptors that show which
bus numbers it covers.  On a system where PCI_SEG is the same across all devices,
this always selects the first handle which may not be correct.  As a result,
reads of PCI config space will fail and NICs will fall back to NII even if they
have an appropriate driver available.
---
 src/interface/efi/efi_pci.c | 37 +++++++++++++++++++++++++++++++++----
 1 file changed, 33 insertions(+), 4 deletions(-)

diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c
index c1f451c9..7d094d1c 100644
--- a/src/interface/efi/efi_pci.c
+++ b/src/interface/efi/efi_pci.c
@@ -78,8 +78,10 @@ static int efipci_root ( struct pci_device *pci, EFI_HANDLE *handle,
 		void *interface;
 		EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root;
 	} u;
+	EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *acpi;
 	EFI_STATUS efirc;
 	UINTN i;
+	uint16_t bus = PCI_BUS (pci->busdevfn);
 	int rc;
 
 	/* Enumerate all handles */
@@ -106,9 +108,36 @@ static int efipci_root ( struct pci_device *pci, EFI_HANDLE *handle,
 			continue;
 		}
 		if ( u.root->SegmentNumber == PCI_SEG ( pci->busdevfn ) ) {
-			*root = u.root;
-			bs->FreePool ( handles );
-			return 0;
+			./*
+			  * Just matching PCI_SEG is insufficient
+			  * We need to check the bus ranges in the ACPI address space
+			  * descriptor to determine correct root bridge I/O protocl handle
+			  */
+			if ( (efirc = u.root->Configuration (u.root, (void **) &acpi)) != 0) {
+				rc = -EEFI ( efirc );
+				DBGC ( pci, "EFIPCI " PCI_FMT " cannot get address space descriptor %s: %s\n",
+				       PCI_ARGS ( pci ), efi_handle_name ( *handle ), strerror ( rc ) );
+				continue;
+			}
+			/* if acpi is NULL, Configuration() is not implemented
+			 * and this root bridge covers all buses 
+			 */
+			if (acpi == NULL) {
+				*root = u.root;
+				rc = 0;
+				goto out;
+			} else {
+				while (acpi->Desc != ACPI_END_TAG_DESCRIPTOR) {
+					if ( (acpi->ResType == ACPI_ADDRESS_SPACE_TYPE_BUS) &&
+					     (bus >= (uint16_t) acpi->AddrRangeMin) &&
+					     (bus <= (uint16_t) acpi->AddrRangeMax)) {
+							*root = u.root;
+							rc = 0;
+							goto out;
+					}
+					acpi++;
+				}
+			}
 		}
 		bs->CloseProtocol ( *handle,
 				    &efi_pci_root_bridge_io_protocol_guid,
@@ -117,7 +146,7 @@ static int efipci_root ( struct pci_device *pci, EFI_HANDLE *handle,
 	DBGC ( pci, "EFIPCI " PCI_FMT " found no root bridge\n",
 	       PCI_ARGS ( pci ) );
 	rc = -ENOENT;
-
+ out:
 	bs->FreePool ( handles );
  err_locate:
 	return rc;
-- 
2.20.1



More information about the ipxe-devel mailing list