]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Use address offset as reported by EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL 146/head
authorMichael Brown <mcb30@ipxe.org>
Thu, 24 Sep 2020 20:41:35 +0000 (21:41 +0100)
committerMichael Brown <mcb30@ipxe.org>
Fri, 25 Sep 2020 13:20:18 +0000 (14:20 +0100)
Retrieve the address windows and translation offsets for the
appropriate PCI root bridge and use them to adjust the PCI BAR address
prior to calling ioremap().

Originally-implemented-by: Pankaj Bansal <pankaj.bansal@nxp.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/acpi.h
src/include/ipxe/efi/efi_pci_api.h
src/interface/efi/efi_pci.c

index 78f40253031dbb2b1f6428b18d07484f2cc5a32e..e41bd289033fd5149da69def5b07a47c5e323fb8 100644 (file)
@@ -19,6 +19,138 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <ipxe/api.h>
 #include <config/general.h>
 
+/** An ACPI small resource descriptor header */
+struct acpi_small_resource {
+       /** Tag byte */
+       uint8_t tag;
+} __attribute__ (( packed ));
+
+/** ACPI small resource length mask */
+#define ACPI_SMALL_LEN_MASK 0x03
+
+/** An ACPI end resource descriptor */
+#define ACPI_END_RESOURCE 0x78
+
+/** An ACPI end resource descriptor */
+struct acpi_end_resource {
+       /** Header */
+       struct acpi_small_resource hdr;
+       /** Checksum */
+       uint8_t checksum;
+} __attribute__ (( packed ));
+
+/** An ACPI large resource descriptor header */
+struct acpi_large_resource {
+       /** Tag byte */
+       uint8_t tag;
+       /** Length of data items */
+       uint16_t len;
+} __attribute__ (( packed ));
+
+/** ACPI large resource flag */
+#define ACPI_LARGE 0x80
+
+/** An ACPI QWORD address space resource descriptor */
+#define ACPI_QWORD_ADDRESS_SPACE_RESOURCE 0x8a
+
+/** An ACPI QWORD address space resource descriptor */
+struct acpi_qword_address_space_resource {
+       /** Header */
+       struct acpi_large_resource hdr;
+       /** Resource type */
+       uint8_t type;
+       /** General flags */
+       uint8_t general;
+       /** Type-specific flags */
+       uint8_t specific;
+       /** Granularity */
+       uint64_t granularity;
+       /** Minimum address */
+       uint64_t min;
+       /** Maximum address */
+       uint64_t max;
+       /** Translation offset */
+       uint64_t offset;
+       /** Length */
+       uint64_t len;
+} __attribute__ (( packed ));
+
+/** A memory address space type */
+#define ACPI_ADDRESS_TYPE_MEM 0x00
+
+/** An ACPI resource descriptor */
+union acpi_resource {
+       /** Tag byte */
+       uint8_t tag;
+       /** Small resource descriptor */
+       struct acpi_small_resource small;
+       /** End resource descriptor */
+       struct acpi_end_resource end;
+       /** Large resource descriptor */
+       struct acpi_large_resource large;
+       /** QWORD address space resource descriptor */
+       struct acpi_qword_address_space_resource qword;
+};
+
+/**
+ * Get ACPI resource tag
+ *
+ * @v res              ACPI resource descriptor
+ * @ret tag            Resource tag
+ */
+static inline unsigned int acpi_resource_tag ( union acpi_resource *res ) {
+
+       return ( ( res->tag & ACPI_LARGE ) ?
+                res->tag : ( res->tag & ~ACPI_SMALL_LEN_MASK ) );
+}
+
+/**
+ * Get length of ACPI small resource descriptor
+ *
+ * @v res              Small resource descriptor
+ * @ret len            Length of descriptor
+ */
+static inline size_t acpi_small_len ( struct acpi_small_resource *res ) {
+
+       return ( sizeof ( *res ) + ( res->tag & ACPI_SMALL_LEN_MASK ) );
+}
+
+/**
+ * Get length of ACPI large resource descriptor
+ *
+ * @v res              Large resource descriptor
+ * @ret len            Length of descriptor
+ */
+static inline size_t acpi_large_len ( struct acpi_large_resource *res ) {
+
+       return ( sizeof ( *res ) + le16_to_cpu ( res->len ) );
+}
+
+/**
+ * Get length of ACPI resource descriptor
+ *
+ * @v res              ACPI resource descriptor
+ * @ret len            Length of descriptor
+ */
+static inline size_t acpi_resource_len ( union acpi_resource *res ) {
+
+       return ( ( res->tag & ACPI_LARGE ) ?
+                acpi_large_len ( &res->large ) :
+                acpi_small_len ( &res->small ) );
+}
+
+/**
+ * Get next ACPI resource descriptor
+ *
+ * @v res              ACPI resource descriptor
+ * @ret next           Next ACPI resource descriptor
+ */
+static inline union acpi_resource *
+acpi_resource_next ( union acpi_resource *res ) {
+
+       return ( ( ( void * ) res ) + acpi_resource_len ( res ) );
+}
+
 /**
  * An ACPI description header
  *
index df28cef34b4f9b6299a9d0c975d4a8fb71dccc87..887d5ee14e8b3a525ea6f09ac859a2852e4382dc 100644 (file)
@@ -148,17 +148,4 @@ PCIAPI_INLINE ( efi, pci_write_config_dword ) ( struct pci_device *pci,
                              value );
 }
 
-/**
- * Map PCI bus address as an I/O address
- *
- * @v bus_addr         PCI bus address
- * @v len              Length of region
- * @ret io_addr                I/O address, or NULL on error
- */
-static inline __always_inline void *
-PCIAPI_INLINE ( efi, pci_ioremap ) ( struct pci_device *pci __unused,
-                                    unsigned long bus_addr, size_t len ) {
-       return ioremap ( bus_addr, len );
-}
-
 #endif /* _IPXE_EFI_PCI_API_H */
index 6a929090cebf170d90c244fc829c4a0234ed627e..93a3738a9fbcdd45767117354968f3a83b9ea06b 100644 (file)
@@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <stdlib.h>
 #include <errno.h>
 #include <ipxe/pci.h>
+#include <ipxe/acpi.h>
 #include <ipxe/efi/efi.h>
 #include <ipxe/efi/efi_pci.h>
 #include <ipxe/efi/efi_driver.h>
@@ -224,6 +225,78 @@ int efipci_write ( struct pci_device *pci, unsigned long location,
        return rc;
 }
 
+/**
+ * Map PCI bus address as an I/O address
+ *
+ * @v bus_addr         PCI bus address
+ * @v len              Length of region
+ * @ret io_addr                I/O address, or NULL on error
+ */
+void * efipci_ioremap ( struct pci_device *pci, unsigned long bus_addr,
+                       size_t len ) {
+       union {
+               union acpi_resource *res;
+               void *raw;
+       } u;
+       EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root;
+       EFI_HANDLE handle;
+       unsigned int tag;
+       uint64_t offset;
+       uint64_t start;
+       uint64_t end;
+       void *io_addr = NULL;
+       EFI_STATUS efirc;
+       int rc;
+
+       /* Open root bridge */
+       if ( ( rc = efipci_root_open ( pci, &handle, &root ) ) != 0 )
+               goto err_root;
+
+       /* Get ACPI resource descriptors */
+       if ( ( efirc = root->Configuration ( root, &u.raw ) ) != 0 ) {
+               rc = -EEFI ( efirc );
+               DBGC ( pci, "EFIPCI " PCI_FMT " cannot get configuration: "
+                      "%s\n", PCI_ARGS ( pci ), strerror ( rc ) );
+               goto err_config;
+       }
+
+       /* Parse resource descriptors */
+       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 */
+               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;
+               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 ) );
+               if ( ( bus_addr < start ) || ( ( bus_addr + len ) > end ) )
+                       continue;
+
+               /* Use this address space descriptor */
+               DBGC2 ( pci, "EFIPCI " PCI_FMT " %08lx+%zx -> ",
+                       PCI_ARGS ( pci ), bus_addr, len );
+               bus_addr -= offset;
+               DBGC2 ( pci, "%08lx\n", bus_addr );
+               io_addr = ioremap ( bus_addr, len );
+               break;
+       }
+       if ( ! io_addr ) {
+               DBGC ( pci, "EFIPCI " PCI_FMT " %08lx+%zx is not within "
+                      "root bridge address space\n",
+                      PCI_ARGS ( pci ), bus_addr, len );
+       }
+
+ err_config:
+       efipci_root_close ( handle );
+ err_root:
+       return io_addr;
+}
+
 PROVIDE_PCIAPI_INLINE ( efi, pci_num_bus );
 PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_byte );
 PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_word );
@@ -231,7 +304,7 @@ PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_dword );
 PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_byte );
 PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_word );
 PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_dword );
-PROVIDE_PCIAPI_INLINE ( efi, pci_ioremap );
+PROVIDE_PCIAPI ( efi, pci_ioremap, efipci_ioremap );
 
 /******************************************************************************
  *