diff options
author | Michael Brown <mcb30@ipxe.org> | 2020-09-24 21:41:35 +0100 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2020-09-25 14:20:18 +0100 |
commit | 27e886c67b63b9a381f87238ed7a856775199fd9 (patch) | |
tree | ed3d5f0028da8f1d40bc25d7d672b9a97631e3d2 /src/interface | |
parent | eecb75ba4809bd5d3e6d63413a45c5fccbda2bc2 (diff) | |
download | ipxe-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.c | 75 |
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 ); /****************************************************************************** * |