aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/include/ipxe/acpi.h3
-rw-r--r--src/interface/efi/efi_pci.c72
2 files changed, 71 insertions, 4 deletions
diff --git a/src/include/ipxe/acpi.h b/src/include/ipxe/acpi.h
index e41bd28..f979ace 100644
--- a/src/include/ipxe/acpi.h
+++ b/src/include/ipxe/acpi.h
@@ -78,6 +78,9 @@ struct acpi_qword_address_space_resource {
/** A memory address space type */
#define ACPI_ADDRESS_TYPE_MEM 0x00
+/** A bus number address space type */
+#define ACPI_ADDRESS_TYPE_BUS 0x02
+
/** An ACPI resource descriptor */
union acpi_resource {
/** Tag byte */
diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c
index 6b32fd6..4adee0f 100644
--- a/src/interface/efi/efi_pci.c
+++ b/src/interface/efi/efi_pci.c
@@ -63,6 +63,70 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
/**
+ * Check for a matching PCI root bridge I/O protocol
+ *
+ * @v pci PCI device
+ * @v handle EFI PCI root bridge handle
+ * @v root EFI PCI root bridge I/O protocol
+ * @ret rc Return status code
+ */
+static int efipci_root_match ( struct pci_device *pci, EFI_HANDLE handle,
+ EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root ) {
+ union {
+ union acpi_resource *res;
+ void *raw;
+ } u;
+ unsigned int segment = PCI_SEG ( pci->busdevfn );
+ unsigned int bus = PCI_BUS ( pci->busdevfn );
+ unsigned int start;
+ unsigned int end;
+ unsigned int tag;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Check segment number */
+ if ( root->SegmentNumber != segment )
+ return -ENOENT;
+
+ /* Get ACPI resource descriptors */
+ if ( ( efirc = root->Configuration ( root, &u.raw ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( pci, "EFIPCI " PCI_FMT " cannot get configuration for "
+ "%s: %s\n", PCI_ARGS ( pci ),
+ efi_handle_name ( handle ), strerror ( rc ) );
+ return rc;
+ }
+
+ /* Assume success if no bus number range descriptors are found */
+ rc = 0;
+
+ /* Parse resource descriptors */
+ for ( ; ( ( tag = acpi_resource_tag ( u.res ) ) != ACPI_END_RESOURCE ) ;
+ u.res = acpi_resource_next ( u.res ) ) {
+
+ /* Ignore anything other than a bus number range descriptor */
+ if ( tag != ACPI_QWORD_ADDRESS_SPACE_RESOURCE )
+ continue;
+ if ( u.res->qword.type != ACPI_ADDRESS_TYPE_BUS )
+ continue;
+
+ /* Check for a matching bus number */
+ start = le64_to_cpu ( u.res->qword.min );
+ end = ( start + le64_to_cpu ( u.res->qword.len ) );
+ if ( ( bus >= start ) && ( bus < end ) )
+ return 0;
+
+ /* We have seen at least one non-matching range
+ * descriptor, so assume failure unless we find a
+ * subsequent match.
+ */
+ rc = -ENOENT;
+ }
+
+ return rc;
+}
+
+/**
* Open EFI PCI root bridge I/O protocol
*
* @v pci PCI device
@@ -106,7 +170,7 @@ static int efipci_root_open ( struct pci_device *pci, EFI_HANDLE *handle,
strerror ( rc ) );
continue;
}
- if ( u.root->SegmentNumber == PCI_SEG ( pci->busdevfn ) ) {
+ if ( efipci_root_match ( pci, *handle, u.root ) == 0 ) {
*root = u.root;
bs->FreePool ( handles );
return 0;
@@ -263,13 +327,13 @@ void * efipci_ioremap ( struct pci_device *pci, unsigned long bus_addr,
for ( ; ( ( tag = acpi_resource_tag ( u.res ) ) != ACPI_END_RESOURCE ) ;
u.res = acpi_resource_next ( u.res ) ) {
- /* Ignore anything other than an address space descriptor */
+ /* Ignore anything other than a memory range descriptor */
if ( tag != ACPI_QWORD_ADDRESS_SPACE_RESOURCE )
continue;
-
- /* Ignore descriptors that do not cover this memory range */
if ( u.res->qword.type != ACPI_ADDRESS_TYPE_MEM )
continue;
+
+ /* Ignore descriptors that do not cover this memory range */
offset = le64_to_cpu ( u.res->qword.offset );
start = ( offset + le64_to_cpu ( u.res->qword.min ) );
end = ( start + le64_to_cpu ( u.res->qword.len ) );