aboutsummaryrefslogtreecommitdiff
path: root/src/interface
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2020-09-24 21:41:35 +0100
committerMichael Brown <mcb30@ipxe.org>2020-09-25 14:20:18 +0100
commit27e886c67b63b9a381f87238ed7a856775199fd9 (patch)
treeed3d5f0028da8f1d40bc25d7d672b9a97631e3d2 /src/interface
parenteecb75ba4809bd5d3e6d63413a45c5fccbda2bc2 (diff)
downloadipxe-27e886c67b63b9a381f87238ed7a856775199fd9.zip
ipxe-27e886c67b63b9a381f87238ed7a856775199fd9.tar.gz
ipxe-27e886c67b63b9a381f87238ed7a856775199fd9.tar.bz2
[efi] Use address offset as reported by EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL
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>
Diffstat (limited to 'src/interface')
-rw-r--r--src/interface/efi/efi_pci.c75
1 files changed, 74 insertions, 1 deletions
diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c
index 6a92909..93a3738 100644
--- a/src/interface/efi/efi_pci.c
+++ b/src/interface/efi/efi_pci.c
@@ -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 );
/******************************************************************************
*