]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[ena] Assign memory BAR if left empty by BIOS
authorMichael Brown <mcb30@ipxe.org>
Mon, 19 Sep 2022 16:49:25 +0000 (17:49 +0100)
committerMichael Brown <mcb30@ipxe.org>
Mon, 19 Sep 2022 16:49:25 +0000 (17:49 +0100)
Some BIOSes in AWS EC2 (observed with a c6i.metal instance in
eu-west-2) will fail to assign an MMIO address to the ENA device,
which causes ioremap() to fail.

Experiments show that the ENA device is the only device behind its
bridge, even when multiple ENA devices are present, and that the BIOS
does assign a memory window to the bridge.

We may therefore choose to assign the device an MMIO address at the
start of the bridge's memory window.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/drivers/net/ena.c

index 400ae44fac0c9e5c0bb28074085992a4decc8b86..22e7e1e30da3b6cd9c3368bda23a1410d80625c7 100644 (file)
@@ -35,6 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <ipxe/iobuf.h>
 #include <ipxe/malloc.h>
 #include <ipxe/pci.h>
+#include <ipxe/pcibridge.h>
 #include <ipxe/version.h>
 #include "ena.h"
 
@@ -984,6 +985,45 @@ static struct net_device_operations ena_operations = {
  ******************************************************************************
  */
 
+/**
+ * Assign memory BAR
+ *
+ * @v ena              ENA device
+ * @v pci              PCI device
+ * @ret rc             Return status code
+ *
+ * Some BIOSes in AWS EC2 are observed to fail to assign a base
+ * address to the ENA device.  The device is the only device behind
+ * its bridge, and the BIOS does assign a memory window to the bridge.
+ * We therefore place the device at the start of the memory window.
+ */
+static int ena_membase ( struct ena_nic *ena, struct pci_device *pci ) {
+       struct pci_bridge *bridge;
+
+       /* Locate PCI bridge */
+       bridge = pcibridge_find ( pci );
+       if ( ! bridge ) {
+               DBGC ( ena, "ENA %p found no PCI bridge\n", ena );
+               return -ENOTCONN;
+       }
+
+       /* Sanity check */
+       if ( PCI_SLOT ( pci->busdevfn ) || PCI_FUNC ( pci->busdevfn ) ) {
+               DBGC ( ena, "ENA %p at " PCI_FMT " may not be only device "
+                      "on bus\n", ena, PCI_ARGS ( pci ) );
+               return -ENOTSUP;
+       }
+
+       /* Place device at start of memory window */
+       pci_write_config_dword ( pci, PCI_BASE_ADDRESS_0, bridge->membase );
+       pci->membase = bridge->membase;
+       DBGC ( ena, "ENA %p at " PCI_FMT " claiming bridge " PCI_FMT " mem "
+              "%08x\n", ena, PCI_ARGS ( pci ), PCI_ARGS ( bridge->pci ),
+              bridge->membase );
+
+       return 0;
+}
+
 /**
  * Probe PCI device
  *
@@ -1020,6 +1060,10 @@ static int ena_probe ( struct pci_device *pci ) {
        /* Fix up PCI device */
        adjust_pci_device ( pci );
 
+       /* Fix up PCI BAR if left unassigned by BIOS */
+       if ( ( ! pci->membase ) && ( ( rc = ena_membase ( ena, pci ) ) != 0 ) )
+               goto err_membase;
+
        /* Map registers */
        ena->regs = pci_ioremap ( pci, pci->membase, ENA_BAR_SIZE );
        if ( ! ena->regs ) {
@@ -1085,6 +1129,7 @@ static int ena_probe ( struct pci_device *pci ) {
  err_info:
        iounmap ( ena->regs );
  err_ioremap:
+ err_membase:
        netdev_nullify ( netdev );
        netdev_put ( netdev );
  err_alloc: