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

_______________________________________________
ipxe-devel mailing list
ipxe-devel@lists.ipxe.org
https://lists.ipxe.org/mailman/listinfo/ipxe-devel

Reply via email to