[ipxe-devel] [PATCH] [efi] implement api to convert pci address to host address

Pankaj Bansal pankaj.bansal at oss.nxp.com
Wed May 13 12:58:09 UTC 2020


From: Pankaj Bansal <pankaj.bansal at nxp.com>

The PCI device address may not be same as host address (CPU view address)
To read/write directly to PCI device's BARs (without using
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.Mem.Read() and
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.Mem.Write()) we need to convert PCI
device address to host address.

The UEFI specifications 2.7 provide a method to translate the PCI device
address to host address and vice versa.

device address = host address + translation offset

EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.Configuration() method can provide us with
the translation offset and host address range.

Using this we can convert the PCI device address to host address.

Signed-off-by: Pankaj Bansal <pankaj.bansal at nxp.com>
---
 src/include/ipxe/pci_io.h   | 10 ++++
 src/interface/efi/efi_pci.c | 60 ++++++++++++++++++++
 2 files changed, 70 insertions(+)

diff --git a/src/include/ipxe/pci_io.h b/src/include/ipxe/pci_io.h
index 10e69763..5806390d 100644
--- a/src/include/ipxe/pci_io.h
+++ b/src/include/ipxe/pci_io.h
@@ -122,4 +122,14 @@ int pci_write_config_word ( struct pci_device *pci, unsigned int where,
 int pci_write_config_dword ( struct pci_device *pci, unsigned int where,
 			     uint32_t value );
 
+/**
+ * retrieve Host Address corresponding to pci bus address
+ *
+ * @v pci		PCI device
+ * @v bus_addr		PCI Bus address
+ * @v len		Length of region
+ * @ret io_addr		Host address
+ */
+uint64_t pci_ioremap ( struct pci_device *pci, uint64_t bus_addr, size_t len );
+
 #endif /* _IPXE_PCI_IO_H */
diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c
index c1f451c9..b688210e 100644
--- a/src/interface/efi/efi_pci.c
+++ b/src/interface/efi/efi_pci.c
@@ -215,6 +215,66 @@ int efipci_write ( struct pci_device *pci, unsigned long location,
 	return rc;
 }
 
+/**
+ * retrieve Host Address corresponding to pci bus address
+ *
+ * @v pci		PCI device
+ * @v bus_addr		PCI Bus address
+ * @v len		Length of region
+ * @ret io_addr		Host address
+ */
+static uint64_t efi_pci_ioremap ( struct pci_device *pci, uint64_t bus_addr,
+				  size_t  len ) {
+	EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+	EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root;
+	EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *config;
+	EFI_HANDLE handle;
+	EFI_STATUS efirc;
+	uint64_t bus_addr_start;
+	int rc;
+
+	/* Identify root bridge */
+	if ( ( rc = efipci_root ( pci, &handle, &root ) ) != 0 )
+		goto err_root;
+
+	/* Retrieves the current resource settings of this PCI root bridge */
+	efirc = root->Configuration ( root, (void **) &config);
+	if (efirc != 0) {
+		rc = -EEFI ( efirc );
+		DBGC ( pci, "EFIPCI " PCI_FMT "Retrieval of current resource "
+		       "settings of PCI root bridge failed: %s\n",
+		       PCI_ARGS ( pci ), strerror ( rc ) );
+		goto err_configuration;
+	}
+
+	/* According to UEFI 2.7,
+	 * EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL::Configuration()
+	 * returns host address instead of device address,
+	 * while AddrTranslationOffset is not zero, and
+	 * device address = host address + AddrTranslationOffset, so
+	 * we convert host address to device address for range compare.
+	 */
+	while ( config->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR ) {
+		bus_addr_start = config->AddrRangeMin +
+				 config->AddrTranslationOffset;
+		if ( ( config->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM ) &&
+		     ( bus_addr_start <= bus_addr ) &&
+		     ( bus_addr_start + config->AddrLen >= bus_addr + len ) ) {
+			return ( bus_addr - config->AddrTranslationOffset );
+		}
+		config++;
+	}
+
+	return bus_addr;
+
+err_configuration:
+	bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid,
+			    efi_image_handle, handle );
+err_root:
+	return rc;
+}
+
+PROVIDE_PCIAPI (efi, pci_ioremap, efi_pci_ioremap);
 PROVIDE_PCIAPI_INLINE ( efi, pci_num_bus );
 PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_byte );
 PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_word );
-- 
2.17.1




More information about the ipxe-devel mailing list