From ca2be7e094c900542e36f70f3abc3c8ff7c3055d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 13 Nov 2022 20:42:09 +0000 Subject: [pci] Allow PCI config space backup to be limited by maximum offset Signed-off-by: Michael Brown --- src/drivers/bus/pcibackup.c | 10 ++++++---- src/drivers/infiniband/arbel.c | 4 ++-- src/drivers/infiniband/hermon.c | 5 +++-- src/drivers/infiniband/qib7322.c | 4 ++-- 4 files changed, 13 insertions(+), 10 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/bus/pcibackup.c b/src/drivers/bus/pcibackup.c index fecad8192..4cf126f83 100644 --- a/src/drivers/bus/pcibackup.c +++ b/src/drivers/bus/pcibackup.c @@ -61,14 +61,15 @@ pci_backup_excluded ( struct pci_device *pci, unsigned int offset, * * @v pci PCI device * @v backup PCI configuration space backup + * @v limit Maximum offset in PCI configuration space * @v exclude PCI configuration space backup exclusion list, or NULL */ void pci_backup ( struct pci_device *pci, struct pci_config_backup *backup, - const uint8_t *exclude ) { + unsigned int limit, const uint8_t *exclude ) { unsigned int offset; uint32_t *dword; - for ( offset = 0, dword = backup->dwords ; offset < 0x100 ; + for ( offset = 0, dword = backup->dwords ; offset < limit ; offset += sizeof ( *dword ) , dword++ ) { if ( ! pci_backup_excluded ( pci, offset, exclude ) ) pci_read_config_dword ( pci, offset, dword ); @@ -80,14 +81,15 @@ void pci_backup ( struct pci_device *pci, struct pci_config_backup *backup, * * @v pci PCI device * @v backup PCI configuration space backup + * @v limit Maximum offset in PCI configuration space * @v exclude PCI configuration space backup exclusion list, or NULL */ void pci_restore ( struct pci_device *pci, struct pci_config_backup *backup, - const uint8_t *exclude ) { + unsigned int limit, const uint8_t *exclude ) { unsigned int offset; uint32_t *dword; - for ( offset = 0, dword = backup->dwords ; offset < 0x100 ; + for ( offset = 0, dword = backup->dwords ; offset < limit ; offset += sizeof ( *dword ) , dword++ ) { if ( ! pci_backup_excluded ( pci, offset, exclude ) ) pci_write_config_dword ( pci, offset, *dword ); diff --git a/src/drivers/infiniband/arbel.c b/src/drivers/infiniband/arbel.c index fbef3f8a6..293c1b647 100644 --- a/src/drivers/infiniband/arbel.c +++ b/src/drivers/infiniband/arbel.c @@ -2561,7 +2561,7 @@ static void arbel_reset ( struct arbel *arbel ) { unsigned int i; /* Perform device reset and preserve PCI configuration */ - pci_backup ( pci, &backup, backup_exclude ); + pci_backup ( pci, &backup, PCI_CONFIG_BACKUP_ALL, backup_exclude ); writel ( ARBEL_RESET_MAGIC, ( arbel->config + ARBEL_RESET_OFFSET ) ); for ( i = 0 ; i < ARBEL_RESET_WAIT_TIME_MS ; i++ ) { @@ -2570,7 +2570,7 @@ static void arbel_reset ( struct arbel *arbel ) { if ( vendor != 0xffff ) break; } - pci_restore ( pci, &backup, backup_exclude ); + pci_restore ( pci, &backup, PCI_CONFIG_BACKUP_ALL, backup_exclude ); } /** diff --git a/src/drivers/infiniband/hermon.c b/src/drivers/infiniband/hermon.c index 2afaaf991..c09baf7ae 100644 --- a/src/drivers/infiniband/hermon.c +++ b/src/drivers/infiniband/hermon.c @@ -2840,7 +2840,7 @@ static int hermon_reset ( struct hermon *hermon ) { hermon->toggle = 0; /* Perform device reset and preserve PCI configuration */ - pci_backup ( pci, &backup, backup_exclude ); + pci_backup ( pci, &backup, PCI_CONFIG_BACKUP_ALL, backup_exclude ); writel ( HERMON_RESET_MAGIC, ( hermon->config + HERMON_RESET_OFFSET ) ); @@ -2852,7 +2852,8 @@ static int hermon_reset ( struct hermon *hermon ) { if ( vendor == pci->vendor ) { /* Restore PCI configuration */ - pci_restore ( pci, &backup, backup_exclude ); + pci_restore ( pci, &backup, PCI_CONFIG_BACKUP_ALL, + backup_exclude ); DBGC ( hermon, "Hermon %p reset after %dms\n", hermon, i ); diff --git a/src/drivers/infiniband/qib7322.c b/src/drivers/infiniband/qib7322.c index a4b51db05..da055b744 100644 --- a/src/drivers/infiniband/qib7322.c +++ b/src/drivers/infiniband/qib7322.c @@ -2256,7 +2256,7 @@ static void qib7322_reset ( struct qib7322 *qib7322, struct pci_device *pci ) { struct pci_config_backup backup; /* Back up PCI configuration space */ - pci_backup ( pci, &backup, NULL ); + pci_backup ( pci, &backup, PCI_CONFIG_BACKUP_ALL, NULL ); /* Assert reset */ memset ( &control, 0, sizeof ( control ) ); @@ -2267,7 +2267,7 @@ static void qib7322_reset ( struct qib7322 *qib7322, struct pci_device *pci ) { mdelay ( 1000 ); /* Restore PCI configuration space */ - pci_restore ( pci, &backup, NULL ); + pci_restore ( pci, &backup, PCI_CONFIG_BACKUP_ALL, NULL ); } /** -- cgit v1.2.3-55-g7522 From 2ae535532188b7772c2aba80247dfdce23d8f275 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 13 Nov 2022 20:45:38 +0000 Subject: [pci] Backup and restore standard config space across PCIe FLR The behaviour of PCI devices across a function-level reset seems to be inconsistent in practice: some devices will preserve PCI BARs, some will not. Fix the behaviour of FLR on devices that do not preserve PCI BARs by backing up and restoring PCI configuration space across the reset. Preserve only the standard portion of the configuration space, since there may be registers with unexpected side effects in the remaining non-standardised space. Signed-off-by: Michael Brown --- src/drivers/bus/pciextra.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/bus/pciextra.c b/src/drivers/bus/pciextra.c index 23617bc9a..1eeb9b2a0 100644 --- a/src/drivers/bus/pciextra.c +++ b/src/drivers/bus/pciextra.c @@ -3,6 +3,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include static int pci_find_capability_common ( struct pci_device *pci, uint8_t pos, int cap ) { @@ -121,8 +122,12 @@ unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) { * @v exp PCI Express Capability address */ void pci_reset ( struct pci_device *pci, unsigned int exp ) { + struct pci_config_backup backup; uint16_t control; + /* Back up configuration space */ + pci_backup ( pci, &backup, PCI_CONFIG_BACKUP_STANDARD, NULL ); + /* Perform a PCIe function-level reset */ pci_read_config_word ( pci, ( exp + PCI_EXP_DEVCTL ), &control ); control |= PCI_EXP_DEVCTL_FLR; @@ -131,6 +136,6 @@ void pci_reset ( struct pci_device *pci, unsigned int exp ) { /* Allow time for reset to complete */ mdelay ( PCI_EXP_FLR_DELAY_MS ); - /* Re-enable device */ - adjust_pci_device ( pci ); + /* Restore configuration */ + pci_restore ( pci, &backup, PCI_CONFIG_BACKUP_STANDARD, NULL ); } -- cgit v1.2.3-55-g7522 From 563bff472276b25a6788abc3c916cda65e7ceead Mon Sep 17 00:00:00 2001 From: Christian I. Nilsson Date: Tue, 15 Nov 2022 13:05:28 +0000 Subject: [intel] Add PCI ID for I219-V and -LM 16,17 Signed-off-by: Christian I. Nilsson Signed-off-by: Michael Brown --- src/drivers/net/intel.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/drivers') diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index ea3ebf68d..df73c4249 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -1173,6 +1173,10 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x15fa, "i219v-14", "I219-V (14)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15fb, "i219lm-13", "I219-LM (13)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15fc, "i219v-13", "I219-V (13)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x1a1c, "i219lm-17", "I219-LM (17)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x1a1d, "i219v-17", "I219-V (17)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x1a1e, "i219lm-16", "I219-LM (16)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x1a1f, "i219v-16", "I219-V (16)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x1f41, "i354", "I354", INTEL_NO_ASDE ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), -- cgit v1.2.3-55-g7522 From ab19546386b13d6c54aea1647fac06960c544efc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 11 Jan 2023 00:18:18 +0000 Subject: [efi] Disable receive filters to work around buggy UNDI drivers Some UNDI drivers (such as the AMI UsbNetworkPkg currently in the process of being upstreamed into EDK2) have a bug that will prevent any packets from being received unless at least one attempt has been made to disable some receive filters. Work around these buggy drivers by attempting to disable receive filters before enabling them. Ignore any errors, since we genuinely do not care whether or not the disabling succeeds. Signed-off-by: Michael Brown --- src/drivers/net/efi/nii.c | 57 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 10 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/net/efi/nii.c b/src/drivers/net/efi/nii.c index 833462e77..5d9aea8d5 100644 --- a/src/drivers/net/efi/nii.c +++ b/src/drivers/net/efi/nii.c @@ -921,18 +921,17 @@ static int nii_set_station_address ( struct nii_nic *nii, * Set receive filters * * @v nii NII NIC + * @v flags Flags * @ret rc Return status code */ -static int nii_set_rx_filters ( struct nii_nic *nii ) { +static int nii_set_rx_filters ( struct nii_nic *nii, unsigned int flags ) { uint32_t implementation = nii->undi->Implementation; - unsigned int flags; unsigned int op; int stat; int rc; /* Construct receive filter set */ - flags = ( PXE_OPFLAGS_RECEIVE_FILTER_ENABLE | - PXE_OPFLAGS_RECEIVE_FILTER_UNICAST ); + flags |= PXE_OPFLAGS_RECEIVE_FILTER_UNICAST; if ( implementation & PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED ) flags |= PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST; if ( implementation & PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED ) @@ -944,14 +943,40 @@ static int nii_set_rx_filters ( struct nii_nic *nii ) { op = NII_OP ( PXE_OPCODE_RECEIVE_FILTERS, flags ); if ( ( stat = nii_issue ( nii, op ) ) < 0 ) { rc = -EIO_STAT ( stat ); - DBGC ( nii, "NII %s could not set receive filters %#04x: %s\n", - nii->dev.name, flags, strerror ( rc ) ); + DBGC ( nii, "NII %s could not %s%sable receive filters " + "%#04x: %s\n", nii->dev.name, + ( ( flags & PXE_OPFLAGS_RECEIVE_FILTER_ENABLE ) ? + "en" : "" ), + ( ( flags & PXE_OPFLAGS_RECEIVE_FILTER_DISABLE ) ? + "dis" : "" ), flags, strerror ( rc ) ); return rc; } return 0; } +/** + * Enable receive filters + * + * @v nii NII NIC + * @ret rc Return status code + */ +static int nii_enable_rx_filters ( struct nii_nic *nii ) { + + return nii_set_rx_filters ( nii, PXE_OPFLAGS_RECEIVE_FILTER_ENABLE ); +} + +/** + * Disable receive filters + * + * @v nii NII NIC + * @ret rc Return status code + */ +static int nii_disable_rx_filters ( struct nii_nic *nii ) { + + return nii_set_rx_filters ( nii, PXE_OPFLAGS_RECEIVE_FILTER_DISABLE ); +} + /** * Transmit packet * @@ -1175,13 +1200,25 @@ static int nii_open ( struct net_device *netdev ) { /* Treat as non-fatal */ } - /* Set receive filters */ - if ( ( rc = nii_set_rx_filters ( nii ) ) != 0 ) - goto err_set_rx_filters; + /* Disable receive filters + * + * We have no reason to disable receive filters here (or + * anywhere), but some NII drivers have a bug which prevents + * packets from being received unless we attempt to disable + * the receive filters. + * + * Ignore any failures, since we genuinely don't care if the + * NII driver cannot disable the filters. + */ + nii_disable_rx_filters ( nii ); + + /* Enable receive filters */ + if ( ( rc = nii_enable_rx_filters ( nii ) ) != 0 ) + goto err_enable_rx_filters; return 0; - err_set_rx_filters: + err_enable_rx_filters: nii_shutdown ( nii ); err_initialise: return rc; -- cgit v1.2.3-55-g7522 From c4c03e5be867a9b7be4dc48fe6576deca1dce8d8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 14 Jan 2023 00:31:54 +0000 Subject: [netdevice] Allow duplicate MAC addresses Many laptops now include the ability to specify a "system-specific MAC address" (also known as "pass-through MAC"), which is supposed to be used for both the onboard NIC and for any attached docking station or other USB NIC. This is intended to simplify interoperability with software or hardware that relies on a MAC address to recognise an individual machine: for example, a deployment server may associate the MAC address with a particular operating system image to be deployed. This therefore creates legitimate situations in which duplicate MAC addresses may exist within the same system. As described in commit 98d09a1 ("[netdevice] Avoid registering duplicate network devices"), the Xen netfront driver relies on the rejection of duplicate MAC addresses in order to inhibit registration of the emulated PCI devices that a Xen PV-HVM guest will create to shadow each of the paravirtual network devices. Move the code that rejects duplicate MAC addresses from the network device core to the Xen netfront driver, to allow for the existence of duplicate MAC addresses in non-Xen setups. Signed-off-by: Michael Brown --- src/drivers/net/ecm.c | 5 ++--- src/drivers/net/netfront.c | 49 ++++++++++++++++++++++++++++++++++++++++++++ src/drivers/net/netfront.h | 5 +++++ src/include/ipxe/netdevice.h | 2 -- src/net/netdevice.c | 33 ----------------------------- 5 files changed, 56 insertions(+), 38 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/net/ecm.c b/src/drivers/net/ecm.c index 68ac962ab..ab1f98370 100644 --- a/src/drivers/net/ecm.c +++ b/src/drivers/net/ecm.c @@ -121,10 +121,9 @@ int ecm_fetch_mac ( struct usb_function *func, } /* Apply system-specific MAC address as current link-layer - * address, if present and not already used. + * address, if present. */ - if ( ( ( rc = acpi_mac ( amac ) ) == 0 ) && - ! find_netdev_by_ll_addr ( ðernet_protocol, amac ) ) { + if ( ( rc = acpi_mac ( amac ) ) == 0 ) { memcpy ( netdev->ll_addr, amac, ETH_ALEN ); DBGC ( usb, "USB %s using system-specific MAC %s\n", func->name, eth_ntoa ( netdev->ll_addr ) ); diff --git a/src/drivers/net/netfront.c b/src/drivers/net/netfront.c index 1203e585c..90930a5a3 100644 --- a/src/drivers/net/netfront.c +++ b/src/drivers/net/netfront.c @@ -59,6 +59,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); EUNIQ ( EINFO_EIO, ( -(status) & 0x1f ), \ EIO_NETIF_RSP_ERROR, EIO_NETIF_RSP_DROPPED ) +/** List of netfront devices */ +static LIST_HEAD ( netfront_devices ); + /****************************************************************************** * * XenStore interface @@ -952,6 +955,7 @@ static int netfront_probe ( struct xen_device *xendev ) { netdev->dev = &xendev->dev; netfront = netdev->priv; netfront->xendev = xendev; + netfront->netdev = netdev; INIT_LIST_HEAD ( &netfront->rx_partial ); DBGC ( netfront, "NETFRONT %s backend=\"%s\" in domain %ld\n", xendev->key, xendev->backend, xendev->backend_id ); @@ -991,9 +995,13 @@ static int netfront_probe ( struct xen_device *xendev ) { /* Set initial link state */ netdev_link_down ( netdev ); + /* Add to list of netfront devices */ + list_add_tail ( &netfront->list, &netfront_devices ); + xen_set_drvdata ( xendev, netdev ); return 0; + list_del ( &netfront->list ); unregister_netdev ( netdev ); err_register_netdev: err_read_mac: @@ -1015,6 +1023,9 @@ static void netfront_remove ( struct xen_device *xendev ) { struct netfront_nic *netfront = netdev->priv; struct xen_hypervisor *xen = xendev->xen; + /* Remove from list of netfront devices */ + list_del ( &netfront->list ); + /* Unregister network device */ unregister_netdev ( netdev ); @@ -1033,3 +1044,41 @@ struct xen_driver netfront_driver __xen_driver = { .probe = netfront_probe, .remove = netfront_remove, }; + +/****************************************************************************** + * + * Emulated PCI device inhibitor + * + ****************************************************************************** + */ + +/** + * Inhibit emulated PCI devices + * + * @v netdev Network device + * @ret rc Return status code + */ +static int netfront_net_probe ( struct net_device *netdev ) { + struct netfront_nic *netfront; + + /* Inhibit emulated PCI devices matching an existing netfront device */ + list_for_each_entry ( netfront, &netfront_devices, list ) { + if ( ( netdev->dev != netfront->netdev->dev ) && + ( netdev->ll_protocol->ll_addr_len == ETH_ALEN ) && + ( memcmp ( netdev->hw_addr, netfront->netdev->hw_addr, + ETH_ALEN ) == 0 ) ) { + DBGC ( netfront, "NETFRONT %s inhibiting emulated %s " + "%s\n", netfront->xendev->key, + netdev->dev->driver_name, netdev->dev->name ); + return -EEXIST; + } + } + + return 0; +} + +/** Emulated PCI device inhibitor driver */ +struct net_driver netfront_net_driver __net_driver = { + .name = "netfront", + .probe = netfront_net_probe, +}; diff --git a/src/drivers/net/netfront.h b/src/drivers/net/netfront.h index dca3ff1c5..de16d5291 100644 --- a/src/drivers/net/netfront.h +++ b/src/drivers/net/netfront.h @@ -159,6 +159,11 @@ struct netfront_nic { /** Grant references */ grant_ref_t refs[NETFRONT_REF_COUNT]; + /** Network device */ + struct net_device *netdev; + /** List of netfront NICs */ + struct list_head list; + /** Transmit ring */ struct netfront_ring tx; /** Transmit front ring */ diff --git a/src/include/ipxe/netdevice.h b/src/include/ipxe/netdevice.h index 29358dba0..af932c259 100644 --- a/src/include/ipxe/netdevice.h +++ b/src/include/ipxe/netdevice.h @@ -729,8 +729,6 @@ extern struct net_device * find_netdev ( const char *name ); extern struct net_device * find_netdev_by_scope_id ( unsigned int scope_id ); extern struct net_device * find_netdev_by_location ( unsigned int bus_type, unsigned int location ); -extern struct net_device * -find_netdev_by_ll_addr ( struct ll_protocol *ll_protocol, const void *ll_addr ); extern struct net_device * last_opened_netdev ( void ); extern int net_tx ( struct io_buffer *iobuf, struct net_device *netdev, struct net_protocol *net_protocol, const void *ll_dest, diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 597c62285..07961bf20 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -735,18 +735,6 @@ int register_netdev ( struct net_device *netdev ) { ll_protocol->ll_header_len ); } - /* Reject network devices that are already available via a - * different hardware device. - */ - duplicate = find_netdev_by_ll_addr ( ll_protocol, netdev->ll_addr ); - if ( duplicate && ( duplicate->dev != netdev->dev ) ) { - DBGC ( netdev, "NETDEV rejecting duplicate (phys %s) of %s " - "(phys %s)\n", netdev->dev->name, duplicate->name, - duplicate->dev->name ); - rc = -EEXIST; - goto err_duplicate; - } - /* Reject named network devices that already exist */ if ( netdev->name[0] && ( duplicate = find_netdev ( netdev->name ) ) ) { DBGC ( netdev, "NETDEV rejecting duplicate name %s\n", @@ -1002,27 +990,6 @@ struct net_device * find_netdev_by_location ( unsigned int bus_type, return NULL; } -/** - * Get network device by link-layer address - * - * @v ll_protocol Link-layer protocol - * @v ll_addr Link-layer address - * @ret netdev Network device, or NULL - */ -struct net_device * find_netdev_by_ll_addr ( struct ll_protocol *ll_protocol, - const void *ll_addr ) { - struct net_device *netdev; - - list_for_each_entry ( netdev, &net_devices, list ) { - if ( ( netdev->ll_protocol == ll_protocol ) && - ( memcmp ( netdev->ll_addr, ll_addr, - ll_protocol->ll_addr_len ) == 0 ) ) - return netdev; - } - - return NULL; -} - /** * Get most recently opened network device * -- cgit v1.2.3-55-g7522 From 6b977d1250e497abd357cca863140361472a6082 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 16 Jan 2023 21:56:53 +0000 Subject: [ena] Allocate an unused Asynchronous Event Notification Queue (AENQ) We currently don't allocate an Asynchronous Event Notification Queue (AENQ) because we don't actually care about any of the events that may come in. The ENA firmware found on Graviton instances requires the AENQ to exist, otherwise all admin queue commands will fail. Fix by allocating an AENQ and disabling all events (so that we do not need to include code to acknowledge any events that may arrive). Signed-off-by: Alexander Graf --- src/drivers/net/ena.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/drivers/net/ena.h | 46 +++++++++++++++++++++++++ 2 files changed, 139 insertions(+) (limited to 'src/drivers') diff --git a/src/drivers/net/ena.c b/src/drivers/net/ena.c index 22e7e1e30..7ce5b9eb9 100644 --- a/src/drivers/net/ena.c +++ b/src/drivers/net/ena.c @@ -350,6 +350,90 @@ static int ena_admin ( struct ena_nic *ena, union ena_aq_req *req, return rc; } +/** + * Set async event notification queue config + * + * @v ena ENA device + * @v enabled Bitmask of the groups to enable + * @ret rc Return status code + */ +static int ena_set_aenq_config ( struct ena_nic *ena, uint32_t enabled ) { + union ena_aq_req *req; + union ena_acq_rsp *rsp; + union ena_feature *feature; + int rc; + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_SET_FEATURE; + req->set_feature.id = ENA_AENQ_CONFIG; + feature = &req->set_feature.feature; + feature->aenq.enabled = cpu_to_le32 ( enabled ); + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Create async event notification queue + * + * @v ena ENA device + * @ret rc Return status code + */ +static int ena_create_async ( struct ena_nic *ena ) { + size_t aenq_len = ( ENA_AENQ_COUNT * sizeof ( ena->aenq.evt[0] ) ); + int rc; + + /* Allocate async event notification queue */ + ena->aenq.evt = malloc_phys ( aenq_len, aenq_len ); + if ( ! ena->aenq.evt ) { + rc = -ENOMEM; + goto err_alloc_aenq; + } + memset ( ena->aenq.evt, 0, aenq_len ); + + /* Program queue address and capabilities */ + ena_set_base ( ena, ENA_AENQ_BASE, ena->aenq.evt ); + ena_set_caps ( ena, ENA_AENQ_CAPS, ENA_AENQ_COUNT, + sizeof ( ena->aenq.evt[0] ) ); + + DBGC ( ena, "ENA %p AENQ [%08lx,%08lx)\n", + ena, virt_to_phys ( ena->aenq.evt ), + ( virt_to_phys ( ena->aenq.evt ) + aenq_len ) ); + + /* Disable all events */ + if ( ( rc = ena_set_aenq_config ( ena, 0 ) ) != 0 ) + goto err_set_aenq_config; + + return 0; + + err_set_aenq_config: + ena_clear_caps ( ena, ENA_AENQ_CAPS ); + free_phys ( ena->aenq.evt, aenq_len ); + err_alloc_aenq: + return rc; +} + +/** + * Destroy async event notification queue + * + * @v ena ENA device + */ +static void ena_destroy_async ( struct ena_nic *ena ) { + size_t aenq_len = ( ENA_AENQ_COUNT * sizeof ( ena->aenq.evt[0] ) ); + + /* Clear queue capabilities */ + ena_clear_caps ( ena, ENA_AENQ_CAPS ); + wmb(); + + /* Free queue */ + free_phys ( ena->aenq.evt, aenq_len ); + DBGC ( ena, "ENA %p AENQ destroyed\n", ena ); +} + /** * Create submission queue * @@ -1098,6 +1182,10 @@ static int ena_probe ( struct pci_device *pci ) { if ( ( rc = ena_create_admin ( ena ) ) != 0 ) goto err_create_admin; + /* Create async event notification queue */ + if ( ( rc = ena_create_async ( ena ) ) != 0 ) + goto err_create_async; + /* Set host attributes */ if ( ( rc = ena_set_host_attributes ( ena ) ) != 0 ) goto err_set_host_attributes; @@ -1121,6 +1209,8 @@ static int ena_probe ( struct pci_device *pci ) { err_register_netdev: err_get_device_attributes: err_set_host_attributes: + ena_destroy_async ( ena ); + err_create_async: ena_destroy_admin ( ena ); err_create_admin: ena_reset ( ena ); @@ -1148,6 +1238,9 @@ static void ena_remove ( struct pci_device *pci ) { /* Unregister network device */ unregister_netdev ( netdev ); + /* Destroy async event notification queue */ + ena_destroy_async ( ena ); + /* Destroy admin queues */ ena_destroy_admin ( ena ); diff --git a/src/drivers/net/ena.h b/src/drivers/net/ena.h index 4e1896e86..0f280c700 100644 --- a/src/drivers/net/ena.h +++ b/src/drivers/net/ena.h @@ -24,6 +24,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Number of admin completion queue entries */ #define ENA_ACQ_COUNT 2 +/** Number of async event notification queue entries */ +#define ENA_AENQ_COUNT 2 + /** Number of transmit queue entries */ #define ENA_TX_COUNT 16 @@ -60,6 +63,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Maximum time to wait for admin requests */ #define ENA_ADMIN_MAX_WAIT_MS 5000 +/** Async event notification queue capabilities register */ +#define ENA_AENQ_CAPS 0x34 + +/** Async event notification queue base address register */ +#define ENA_AENQ_BASE 0x38 + /** Device control register */ #define ENA_CTRL 0x54 #define ENA_CTRL_RESET 0x00000001UL /**< Reset */ @@ -130,6 +139,17 @@ struct ena_device_attributes { uint32_t mtu; } __attribute__ (( packed )); +/** Async event notification queue config */ +#define ENA_AENQ_CONFIG 26 + +/** Async event notification queue config */ +struct ena_aenq_config { + /** Bitmask of supported AENQ groups (device -> host) */ + uint32_t supported; + /** Bitmask of enabled AENQ groups (host -> device) */ + uint32_t enabled; +} __attribute__ (( packed )); + /** Host attributes */ #define ENA_HOST_ATTRIBUTES 28 @@ -208,6 +228,8 @@ struct ena_host_info { union ena_feature { /** Device attributes */ struct ena_device_attributes device; + /** Async event notification queue config */ + struct ena_aenq_config aenq; /** Host attributes */ struct ena_host_attributes host; }; @@ -506,6 +528,28 @@ struct ena_acq { unsigned int phase; }; +/** Async event notification queue event */ +struct ena_aenq_event { + /** Type of event */ + uint16_t group; + /** ID of event */ + uint16_t syndrome; + /** Phase */ + uint8_t flags; + /** Reserved */ + uint8_t reserved[3]; + /** Timestamp */ + uint64_t timestamp; + /** Additional event data */ + uint8_t data[48]; +} __attribute__ (( packed )); + +/** Async event notification queue */ +struct ena_aenq { + /** Events */ + struct ena_aenq_event *evt; +}; + /** Transmit submission queue entry */ struct ena_tx_sqe { /** Length */ @@ -702,6 +746,8 @@ struct ena_nic { struct ena_aq aq; /** Admin completion queue */ struct ena_acq acq; + /** Async event notification queue */ + struct ena_aenq aenq; /** Transmit queue */ struct ena_qp tx; /** Receive queue */ -- cgit v1.2.3-55-g7522 From 2fef0c541e4e2417fc285c4d9ddfcb6f23f394ad Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 23 Jan 2023 19:15:45 +0000 Subject: [efi] Extend efi_locate_device() to allow searching up the device path Extend the functionality of efi_locate_device() to allow callers to find instances of the protocol that may exist further up the device path. Signed-off-by: Michael Brown --- src/drivers/net/efi/nii.c | 2 +- src/drivers/net/efi/snponly.c | 2 +- src/include/ipxe/efi/efi_utils.h | 2 +- src/interface/efi/efi_utils.c | 56 ++++++++++++++++++++++++++++++---------- 4 files changed, 45 insertions(+), 17 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/net/efi/nii.c b/src/drivers/net/efi/nii.c index 5d9aea8d5..be5bce4b4 100644 --- a/src/drivers/net/efi/nii.c +++ b/src/drivers/net/efi/nii.c @@ -222,7 +222,7 @@ static int nii_pci_open ( struct nii_nic *nii ) { /* Locate PCI I/O protocol */ if ( ( rc = efi_locate_device ( device, &efi_pci_io_protocol_guid, - &pci_device ) ) != 0 ) { + &pci_device, 0 ) ) != 0 ) { DBGC ( nii, "NII %s could not locate PCI I/O protocol: %s\n", nii->dev.name, strerror ( rc ) ); goto err_locate; diff --git a/src/drivers/net/efi/snponly.c b/src/drivers/net/efi/snponly.c index cb7ea1bbc..674e0a050 100644 --- a/src/drivers/net/efi/snponly.c +++ b/src/drivers/net/efi/snponly.c @@ -80,7 +80,7 @@ static int chained_locate ( struct chained_protocol *chained ) { /* Locate handle supporting this protocol */ if ( ( rc = efi_locate_device ( device, chained->protocol, - &parent ) ) != 0 ) { + &parent, 0 ) ) != 0 ) { DBGC ( device, "CHAINED %s does not support %s: %s\n", efi_handle_name ( device ), efi_guid_ntoa ( chained->protocol ), strerror ( rc ) ); diff --git a/src/include/ipxe/efi/efi_utils.h b/src/include/ipxe/efi/efi_utils.h index 270d38dc8..98659b150 100644 --- a/src/include/ipxe/efi/efi_utils.h +++ b/src/include/ipxe/efi/efi_utils.h @@ -13,7 +13,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct device; extern int efi_locate_device ( EFI_HANDLE device, EFI_GUID *protocol, - EFI_HANDLE *parent ); + EFI_HANDLE *parent, unsigned int skip ); extern int efi_child_add ( EFI_HANDLE parent, EFI_HANDLE child ); extern void efi_child_del ( EFI_HANDLE parent, EFI_HANDLE child ); extern void efi_device_info ( EFI_HANDLE device, const char *prefix, diff --git a/src/interface/efi/efi_utils.c b/src/interface/efi/efi_utils.c index 8e660e9d7..53f82bfe4 100644 --- a/src/interface/efi/efi_utils.c +++ b/src/interface/efi/efi_utils.c @@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include @@ -38,23 +39,26 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @v device EFI device handle * @v protocol Protocol GUID * @v parent Parent EFI device handle to fill in + * @v skip Number of protocol-supporting parent devices to skip * @ret rc Return status code */ int efi_locate_device ( EFI_HANDLE device, EFI_GUID *protocol, - EFI_HANDLE *parent ) { + EFI_HANDLE *parent, unsigned int skip ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; union { EFI_DEVICE_PATH_PROTOCOL *path; void *interface; - } path; - EFI_DEVICE_PATH_PROTOCOL *devpath; + } u; + EFI_DEVICE_PATH_PROTOCOL *path; + EFI_DEVICE_PATH_PROTOCOL *end; + size_t len; EFI_STATUS efirc; int rc; /* Get device path */ if ( ( efirc = bs->OpenProtocol ( device, &efi_device_path_protocol_guid, - &path.interface, + &u.interface, efi_image_handle, device, EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ rc = -EEFI ( efirc ); @@ -62,22 +66,46 @@ int efi_locate_device ( EFI_HANDLE device, EFI_GUID *protocol, efi_handle_name ( device ), strerror ( rc ) ); goto err_open_device_path; } - devpath = path.path; - /* Check for presence of specified protocol */ - if ( ( efirc = bs->LocateDevicePath ( protocol, &devpath, - parent ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( device, "EFIDEV %s has no parent supporting %s: %s\n", - efi_handle_name ( device ), - efi_guid_ntoa ( protocol ), strerror ( rc ) ); - goto err_locate_protocol; + /* Create modifiable copy of device path */ + len = ( efi_path_len ( u.path ) + sizeof ( EFI_DEVICE_PATH_PROTOCOL )); + path = malloc ( len ); + if ( ! path ) { + rc = -ENOMEM; + goto err_alloc_path; + } + memcpy ( path, u.path, len ); + + /* Locate parent device(s) */ + while ( 1 ) { + + /* Check for presence of specified protocol */ + end = path; + if ( ( efirc = bs->LocateDevicePath ( protocol, &end, + parent ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFIDEV %s has no parent supporting " + "%s: %s\n", efi_devpath_text ( path ), + efi_guid_ntoa ( protocol ), strerror ( rc ) ); + goto err_locate_protocol; + } + + /* Stop if we have skipped the requested number of devices */ + if ( ! skip-- ) + break; + + /* Trim device path */ + efi_path_terminate ( end ); + end = efi_path_prev ( path, end ); + efi_path_terminate ( end ); } /* Success */ rc = 0; err_locate_protocol: + free ( path ); + err_alloc_path: bs->CloseProtocol ( device, &efi_device_path_protocol_guid, efi_image_handle, device ); err_open_device_path: @@ -150,7 +178,7 @@ static int efi_pci_info ( EFI_HANDLE device, const char *prefix, /* Find parent PCI device */ if ( ( rc = efi_locate_device ( device, &efi_pci_io_protocol_guid, - &pci_device ) ) != 0 ) { + &pci_device, 0 ) ) != 0 ) { DBGC ( device, "EFIDEV %s is not a PCI device: %s\n", efi_handle_name ( device ), strerror ( rc ) ); return rc; -- cgit v1.2.3-55-g7522 From 68734b9a4dafa540e5333d7af3849b59a10f7a93 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 23 Jan 2023 19:18:21 +0000 Subject: [efi] Bind to only the topmost instance of the SNP or NII protocols UEFI has the mildly annoying habit of installing copies of the EFI_SIMPLE_NETWORK_PROTOCOL instance on the IPv4 and IPv6 child device handles. This can cause iPXE's SNP driver to attempt to bind to a copy of the EFI_SIMPLE_NETWORK_PROTOCOL that iPXE itself provided on a different handle. Fix by refusing to bind to an SNP (or NII) handle if there exists another instance of the same protocol further up the device path (on the basis that we always want to bind to the highest possible device). Signed-off-by: Michael Brown --- src/drivers/net/efi/snp.c | 66 ++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 30 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/net/efi/snp.c b/src/drivers/net/efi/snp.c index fbd606902..1920cdbc5 100644 --- a/src/drivers/net/efi/snp.c +++ b/src/drivers/net/efi/snp.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include "snpnet.h" #include "nii.h" @@ -40,31 +41,46 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * Check to see if driver supports a device * * @v device EFI device handle + * @v protocol Protocol GUID * @ret rc Return status code */ -static int snp_supported ( EFI_HANDLE device ) { +static int snp_nii_supported ( EFI_HANDLE device, EFI_GUID *protocol ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE parent; EFI_STATUS efirc; + int rc; /* Check that this is not a device we are providing ourselves */ if ( find_snpdev ( device ) != NULL ) { - DBGCP ( device, "SNP %s is provided by this binary\n", + DBGCP ( device, "HANDLE %s is provided by this binary\n", efi_handle_name ( device ) ); return -ENOTTY; } - /* Test for presence of simple network protocol */ - if ( ( efirc = bs->OpenProtocol ( device, - &efi_simple_network_protocol_guid, + /* Test for presence of protocol */ + if ( ( efirc = bs->OpenProtocol ( device, protocol, NULL, efi_image_handle, device, EFI_OPEN_PROTOCOL_TEST_PROTOCOL))!=0){ - DBGCP ( device, "SNP %s is not an SNP device\n", - efi_handle_name ( device ) ); + DBGCP ( device, "HANDLE %s is not a %s device\n", + efi_handle_name ( device ), + efi_guid_ntoa ( protocol ) ); return -EEFI ( efirc ); } - DBGC ( device, "SNP %s is an SNP device\n", - efi_handle_name ( device ) ); + /* Check that there are no instances of this protocol further + * up this device path. + */ + if ( ( rc = efi_locate_device ( device, protocol, + &parent, 1 ) ) == 0 ) { + DBGC2 ( device, "HANDLE %s has %s-supporting parent ", + efi_handle_name ( device ), + efi_guid_ntoa ( protocol ) ); + DBGC2 ( device, "%s\n", efi_handle_name ( parent ) ); + return -ENOTTY; + } + + DBGC ( device, "HANDLE %s is a %s device\n", + efi_handle_name ( device ), efi_guid_ntoa ( protocol ) ); return 0; } @@ -74,30 +90,20 @@ static int snp_supported ( EFI_HANDLE device ) { * @v device EFI device handle * @ret rc Return status code */ -static int nii_supported ( EFI_HANDLE device ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_STATUS efirc; +static int snp_supported ( EFI_HANDLE device ) { - /* Check that this is not a device we are providing ourselves */ - if ( find_snpdev ( device ) != NULL ) { - DBGCP ( device, "NII %s is provided by this binary\n", - efi_handle_name ( device ) ); - return -ENOTTY; - } + return snp_nii_supported ( device, &efi_simple_network_protocol_guid ); +} - /* Test for presence of NII protocol */ - if ( ( efirc = bs->OpenProtocol ( device, - &efi_nii31_protocol_guid, - NULL, efi_image_handle, device, - EFI_OPEN_PROTOCOL_TEST_PROTOCOL))!=0){ - DBGCP ( device, "NII %s is not an NII device\n", - efi_handle_name ( device ) ); - return -EEFI ( efirc ); - } - DBGC ( device, "NII %s is an NII device\n", - efi_handle_name ( device ) ); +/** + * Check to see if driver supports a device + * + * @v device EFI device handle + * @ret rc Return status code + */ +static int nii_supported ( EFI_HANDLE device ) { - return 0; + return snp_nii_supported ( device, &efi_nii31_protocol_guid ); } /** EFI SNP driver */ -- cgit v1.2.3-55-g7522 From c5426cdaa943a14183edb424b472d7e0d7e55cc3 Mon Sep 17 00:00:00 2001 From: Mohammed Taha Date: Mon, 23 Jan 2023 22:52:30 +0000 Subject: [golan] Add new PCI ID for NVIDIA BlueField-3 network device Signed-off-by: Michael Brown --- src/drivers/infiniband/golan.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/drivers') diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c index 2f1ab2357..ce02a867f 100755 --- a/src/drivers/infiniband/golan.c +++ b/src/drivers/infiniband/golan.c @@ -2647,6 +2647,7 @@ static struct pci_device_id golan_nics[] = { PCI_ROM ( 0x15b3, 0x1021, "ConnectX-7", "ConnectX-7 HCA driver, DevID 4129", 0 ), PCI_ROM ( 0x15b3, 0xa2d2, "BlueField", "BlueField integrated ConnectX-5 network controller HCA driver, DevID 41682", 0 ), PCI_ROM ( 0x15b3, 0xa2d6, "BlueField-2", "BlueField-2 network controller HCA driver, DevID 41686", 0 ), + PCI_ROM ( 0x15b3, 0xa2dc, "BlueField-3", "BlueField-3 network controller HCA driver, DevID 41692", 0 ), }; struct pci_driver golan_driver __pci_driver = { -- cgit v1.2.3-55-g7522 From b6304f298499001a55045618e9fbf763b59b7321 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 1 Feb 2023 18:19:32 +0000 Subject: [realtek] Explicitly disable VLAN offload Some cards seem to have the receive VLAN tag stripping feature enabled by default, which causes received VLAN packets to be misinterpreted as being received by the trunk device. Fix by disabling VLAN tag stripping in the C+ Command Register. Debugged-by: Xinming Lai Tested-by: Xinming Lai Signed-off-by: Michael Brown --- src/drivers/net/realtek.c | 4 ++++ src/drivers/net/realtek.h | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/net/realtek.c b/src/drivers/net/realtek.c index a43efb68b..80442ab83 100644 --- a/src/drivers/net/realtek.c +++ b/src/drivers/net/realtek.c @@ -1067,11 +1067,15 @@ static void realtek_detect ( struct realtek_nic *rtl ) { * Note that enabling DAC seems to cause bizarre behaviour * (lockups, garbage data on the wire) on some systems, even * if only 32-bit addresses are used. + * + * Disable VLAN offload, since some cards seem to have it + * enabled by default. */ cpcr = readw ( rtl->regs + RTL_CPCR ); cpcr |= ( RTL_CPCR_MULRW | RTL_CPCR_CPRX | RTL_CPCR_CPTX ); if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) cpcr |= RTL_CPCR_DAC; + cpcr &= ~RTL_CPCR_VLAN; writew ( cpcr, rtl->regs + RTL_CPCR ); check_cpcr = readw ( rtl->regs + RTL_CPCR ); diff --git a/src/drivers/net/realtek.h b/src/drivers/net/realtek.h index d4642fd76..d50e349b0 100644 --- a/src/drivers/net/realtek.h +++ b/src/drivers/net/realtek.h @@ -228,8 +228,9 @@ enum realtek_legacy_status { /** C+ Command Register (word) */ #define RTL_CPCR 0xe0 -#define RTL_CPCR_DAC 0x0010 /**< PCI Dual Address Cycle Enable */ -#define RTL_CPCR_MULRW 0x0008 /**< PCI Multiple Read/Write Enable */ +#define RTL_CPCR_VLAN 0x0040 /**< VLAN tag stripping enable */ +#define RTL_CPCR_DAC 0x0010 /**< PCI Dual Address Cycle enable */ +#define RTL_CPCR_MULRW 0x0008 /**< PCI Multiple Read/Write enable */ #define RTL_CPCR_CPRX 0x0002 /**< C+ receive enable */ #define RTL_CPCR_CPTX 0x0001 /**< C+ transmit enable */ -- cgit v1.2.3-55-g7522 From 4e456d992889569e2dbb0426f2438797ab06ca1f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 3 Feb 2023 16:10:31 +0000 Subject: [efi] Do not attempt to drive PCI bridge devices The "bridge" driver introduced in 3aa6b79 ("[pci] Add minimal PCI bridge driver") is required only for BIOS builds using the ENA driver, where experimentation shows that we cannot rely on the BIOS to fully assign MMIO addresses. Since the driver is a valid PCI driver, it will end up binding to all PCI bridge devices even on a UEFI platform, where the firmware is likely to have completed MMIO address assignment correctly. This has no impact on most systems since there is generally no UEFI driver for PCI bridges: the enumeration of the whole PCI bus is handled by the PciBusDxe driver bound to the root bridge. Experimentation shows that at least one laptop will freeze at the point that iPXE attempts to bind to the bridge device. No deeper investigation has been carried out to find the root cause. Fix by causing efipci_supported() to return an error unless the configuration space header type indicates a non-bridge device. Reported-by: Marcel Petersen Signed-off-by: Michael Brown --- src/drivers/bus/pci.c | 1 + src/include/ipxe/pci.h | 2 ++ src/interface/efi/efi_pci.c | 10 ++++++++++ 3 files changed, 13 insertions(+) (limited to 'src/drivers') diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index 7953aaedd..92b389641 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -205,6 +205,7 @@ int pci_read_config ( struct pci_device *pci ) { pci_read_config_dword ( pci, PCI_REVISION, &tmp ); pci->class = ( tmp >> 8 ); pci_read_config_byte ( pci, PCI_INTERRUPT_LINE, &pci->irq ); + pci_read_config_byte ( pci, PCI_HEADER_TYPE, &pci->hdrtype ); pci_read_bases ( pci ); /* Initialise generic device component */ diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index 637b20d60..8c6d9e4e2 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -227,6 +227,8 @@ struct pci_device { uint32_t class; /** Interrupt number */ uint8_t irq; + /** Header type */ + uint8_t hdrtype; /** Segment, bus, device, and function (bus:dev.fn) number */ uint32_t busdevfn; /** Driver for this device */ diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index 4796201e9..e2eeeb344 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -785,12 +785,22 @@ int efipci_info ( EFI_HANDLE device, struct efi_pci_device *efipci ) { */ static int efipci_supported ( EFI_HANDLE device ) { struct efi_pci_device efipci; + uint8_t hdrtype; int rc; /* Get PCI device information */ if ( ( rc = efipci_info ( device, &efipci ) ) != 0 ) return rc; + /* Do not attempt to drive bridges */ + hdrtype = efipci.pci.hdrtype; + if ( ( hdrtype & PCI_HEADER_TYPE_MASK ) != PCI_HEADER_TYPE_NORMAL ) { + DBGC ( device, "EFIPCI " PCI_FMT " type %02x is not type %02x\n", + PCI_ARGS ( &efipci.pci ), hdrtype, + PCI_HEADER_TYPE_NORMAL ); + return -ENOTTY; + } + /* Look for a driver */ if ( ( rc = pci_find_driver ( &efipci.pci ) ) != 0 ) { DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) " -- cgit v1.2.3-55-g7522 From be8ecaf8059a34f27ed34bbc2e95806537802108 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 10 Feb 2023 23:18:47 +0000 Subject: [eisa] Check for system board presence before probing for slots EISA expansion slot I/O port addresses overlap space that may be assigned to PCI devices, which can lead to register reads and writes with unwanted side effects during EISA probing. Reduce the chances of performing EISA probing on PCI devices by probing EISA slot vendor and product ID registers only if the EISA system board vendor ID register indicates that the motherboard supports EISA. Debugged-by: Václav Ovsík Tested-by: Václav Ovsík Signed-off-by: Michael Brown --- src/drivers/bus/eisa.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/drivers') diff --git a/src/drivers/bus/eisa.c b/src/drivers/bus/eisa.c index a4efe2621..68837eb4d 100644 --- a/src/drivers/bus/eisa.c +++ b/src/drivers/bus/eisa.c @@ -98,8 +98,16 @@ static void eisa_remove ( struct eisa_device *eisa ) { static int eisabus_probe ( struct root_device *rootdev ) { struct eisa_device *eisa = NULL; unsigned int slot; + uint8_t system; int rc; + /* Check for EISA system board */ + system = inb ( EISA_VENDOR_ID ); + if ( system & 0x80 ) { + DBG ( "No EISA system board (read %02x)\n", system ); + return -ENODEV; + } + for ( slot = EISA_MIN_SLOT ; slot <= EISA_MAX_SLOT ; slot++ ) { /* Allocate struct eisa_device */ if ( ! eisa ) -- cgit v1.2.3-55-g7522 From 2733c4763a50b9eb0c206e7430d4d0638451e5e9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 16 Feb 2023 12:54:47 +0000 Subject: [iscsi] Limit maximum transfer size to MaxBurstLength We currently specify only the iSCSI default value for MaxBurstLength and ignore any negotiated value, since our internal block device API allows only for receiving directly into caller-allocated buffers and so we have no intrinsic limit on burst length. A conscientious target may however refuse to attempt a transfer that we request for a number of blocks that would exceed the negotiated maximum burst length. Fix by recording the negotiated maximum burst length and using it to limit the maximum number of blocks per transfer as reported by the SCSI layer. Signed-off-by: Michael Brown --- src/drivers/block/scsi.c | 4 +++ src/include/ipxe/iscsi.h | 12 +++++++++ src/net/tcp/iscsi.c | 65 +++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 77 insertions(+), 4 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c index f765c9762..ff415f5c6 100644 --- a/src/drivers/block/scsi.c +++ b/src/drivers/block/scsi.c @@ -609,6 +609,7 @@ static void scsicmd_read_capacity_cmd ( struct scsi_command *scsicmd, */ static void scsicmd_read_capacity_done ( struct scsi_command *scsicmd, int rc ) { + struct scsi_device *scsidev = scsicmd->scsidev; struct scsi_read_capacity_private *priv = scsicmd_priv ( scsicmd ); struct scsi_capacity_16 *capacity16 = &priv->capacity.capacity16; struct scsi_capacity_10 *capacity10 = &priv->capacity.capacity10; @@ -645,6 +646,9 @@ static void scsicmd_read_capacity_done ( struct scsi_command *scsicmd, } capacity.max_count = -1U; + /* Allow transport layer to update capacity */ + block_capacity ( &scsidev->scsi, &capacity ); + /* Return capacity to caller */ block_capacity ( &scsicmd->block, &capacity ); diff --git a/src/include/ipxe/iscsi.h b/src/include/ipxe/iscsi.h index 966cf52b6..a25eec257 100644 --- a/src/include/ipxe/iscsi.h +++ b/src/include/ipxe/iscsi.h @@ -22,6 +22,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Default iSCSI port */ #define ISCSI_PORT 3260 +/** Default iSCSI first burst length */ +#define ISCSI_FIRST_BURST_LEN 65536 + +/** Default iSCSI maximum burst length */ +#define ISCSI_MAX_BURST_LEN 262144 + +/** Default iSCSI maximum receive data segment length */ +#define ISCSI_MAX_RECV_DATA_SEG_LEN 8192 + /** * iSCSI segment lengths * @@ -577,6 +586,9 @@ struct iscsi_session { /** CHAP response (used for both initiator and target auth) */ struct chap_response chap; + /** Maximum burst length */ + size_t max_burst_len; + /** Initiator session ID (IANA format) qualifier * * This is part of the ISID. It is generated randomly diff --git a/src/net/tcp/iscsi.c b/src/net/tcp/iscsi.c index e36d5619d..dd20849ce 100644 --- a/src/net/tcp/iscsi.c +++ b/src/net/tcp/iscsi.c @@ -46,6 +46,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include @@ -86,6 +87,10 @@ FEATURE ( FEATURE_PROTOCOL, "iSCSI", DHCP_EB_FEATURE_ISCSI, 1 ); __einfo_error ( EINFO_EINVAL_NO_INITIATOR_IQN ) #define EINFO_EINVAL_NO_INITIATOR_IQN \ __einfo_uniqify ( EINFO_EINVAL, 0x05, "No initiator IQN" ) +#define EINVAL_MAXBURSTLENGTH \ + __einfo_error ( EINFO_EINVAL_MAXBURSTLENGTH ) +#define EINFO_EINVAL_MAXBURSTLENGTH \ + __einfo_uniqify ( EINFO_EINVAL, 0x06, "Invalid MaxBurstLength" ) #define EIO_TARGET_UNAVAILABLE \ __einfo_error ( EINFO_EIO_TARGET_UNAVAILABLE ) #define EINFO_EIO_TARGET_UNAVAILABLE \ @@ -281,6 +286,9 @@ static int iscsi_open_connection ( struct iscsi_session *iscsi ) { /* Assign fresh initiator task tag */ iscsi_new_itt ( iscsi ); + /* Set default operational parameters */ + iscsi->max_burst_len = ISCSI_MAX_BURST_LEN; + /* Initiate login */ iscsi_start_login ( iscsi ); @@ -736,16 +744,20 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, "MaxConnections=1%c" "InitialR2T=Yes%c" "ImmediateData=No%c" - "MaxRecvDataSegmentLength=8192%c" - "MaxBurstLength=262144%c" - "FirstBurstLength=65536%c" + "MaxRecvDataSegmentLength=%d%c" + "MaxBurstLength=%d%c" + "FirstBurstLength=%d%c" "DefaultTime2Wait=0%c" "DefaultTime2Retain=0%c" "MaxOutstandingR2T=1%c" "DataPDUInOrder=Yes%c" "DataSequenceInOrder=Yes%c" "ErrorRecoveryLevel=0%c", - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + 0, 0, 0, 0, 0, + ISCSI_MAX_RECV_DATA_SEG_LEN, 0, + ISCSI_MAX_BURST_LEN, 0, + ISCSI_FIRST_BURST_LEN, 0, + 0, 0, 0, 0, 0, 0 ); } return used; @@ -908,6 +920,31 @@ static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi, return 0; } +/** + * Handle iSCSI MaxBurstLength text value + * + * @v iscsi iSCSI session + * @v value MaxBurstLength value + * @ret rc Return status code + */ +static int iscsi_handle_maxburstlength_value ( struct iscsi_session *iscsi, + const char *value ) { + unsigned long max_burst_len; + char *end; + + /* Update maximum burst length */ + max_burst_len = strtoul ( value, &end, 0 ); + if ( *end ) { + DBGC ( iscsi, "iSCSI %p invalid MaxBurstLength \"%s\"\n", + iscsi, value ); + return -EINVAL_MAXBURSTLENGTH; + } + if ( max_burst_len < iscsi->max_burst_len ) + iscsi->max_burst_len = max_burst_len; + + return 0; +} + /** * Handle iSCSI CHAP_A text value * @@ -1148,6 +1185,7 @@ struct iscsi_string_type { /** iSCSI text strings that we want to handle */ static struct iscsi_string_type iscsi_string_types[] = { { "TargetAddress", iscsi_handle_targetaddress_value }, + { "MaxBurstLength", iscsi_handle_maxburstlength_value }, { "AuthMethod", iscsi_handle_authmethod_value }, { "CHAP_A", iscsi_handle_chap_a_value }, { "CHAP_I", iscsi_handle_chap_i_value }, @@ -1847,6 +1885,24 @@ static int iscsi_scsi_command ( struct iscsi_session *iscsi, return iscsi->itt; } +/** + * Update SCSI block device capacity + * + * @v iscsi iSCSI session + * @v capacity Block device capacity + */ +static void iscsi_scsi_capacity ( struct iscsi_session *iscsi, + struct block_device_capacity *capacity ) { + unsigned int max_count; + + /* Limit maximum number of blocks per transfer to fit MaxBurstLength */ + if ( capacity->blksize ) { + max_count = ( iscsi->max_burst_len / capacity->blksize ); + if ( max_count < capacity->max_count ) + capacity->max_count = max_count; + } +} + /** * Get iSCSI ACPI descriptor * @@ -1862,6 +1918,7 @@ static struct acpi_descriptor * iscsi_describe ( struct iscsi_session *iscsi ) { static struct interface_operation iscsi_control_op[] = { INTF_OP ( scsi_command, struct iscsi_session *, iscsi_scsi_command ), INTF_OP ( xfer_window, struct iscsi_session *, iscsi_scsi_window ), + INTF_OP ( block_capacity, struct iscsi_session *, iscsi_scsi_capacity ), INTF_OP ( intf_close, struct iscsi_session *, iscsi_close ), INTF_OP ( acpi_describe, struct iscsi_session *, iscsi_describe ), EFI_INTF_OP ( efi_describe, struct iscsi_session *, efi_iscsi_path ), -- cgit v1.2.3-55-g7522 From 523788ccdaff515194347b8e3f0c8de4dcbf221f Mon Sep 17 00:00:00 2001 From: Forest Crossman Date: Sun, 5 Mar 2023 18:22:18 -0600 Subject: [intelx] Add PCI IDs for Intel 82599 10GBASE-T NIC Signed-off-by: Forest Crossman --- src/drivers/net/intelx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/drivers') diff --git a/src/drivers/net/intelx.c b/src/drivers/net/intelx.c index f4dad8859..343d01374 100644 --- a/src/drivers/net/intelx.c +++ b/src/drivers/net/intelx.c @@ -473,6 +473,7 @@ static struct pci_device_id intelx_nics[] = { PCI_ROM ( 0x8086, 0x10f9, "82599-cx4", "82599 (CX4)", 0 ), PCI_ROM ( 0x8086, 0x10fb, "82599-sfp", "82599 (SFI/SFP+)", 0 ), PCI_ROM ( 0x8086, 0x10fc, "82599-xaui", "82599 (XAUI/BX4)", 0 ), + PCI_ROM ( 0x8086, 0x151c, "82599-tn", "82599 (TN)", 0 ), PCI_ROM ( 0x8086, 0x1528, "x540t", "X540-AT2/X540-BT2", 0 ), PCI_ROM ( 0x8086, 0x154d, "82599-sfp-sf2", "82599 (SFI/SFP+)", 0 ), PCI_ROM ( 0x8086, 0x1557, "82599en-sfp", "82599 (Single Port SFI Only)", 0 ), -- cgit v1.2.3-55-g7522 From bf25e23d07f16be62825650c0826c4eadf2699b6 Mon Sep 17 00:00:00 2001 From: Matt Parrella Date: Tue, 14 Mar 2023 14:43:19 +0000 Subject: [intel] Add workaround for I210 reset hardware bugs The Intel I210's packet buffer size registers reset only on power up, not when a reset signal is asserted. This can lead to the inability to pass traffic in the event that the DMA TX Maximum Packet Size (which does reset to its default value on reset) is bigger than the TX Packet Buffer Size. For example, an operating system may be using the time sensitive networking features of the I210 and the registers may be programmed correctly, but then a reset signal is asserted and iPXE on the next boot will be unable to use the I210. Mimic what Linux does and forcibly set the registers to their default values. Signed-off-by: Matt Parrella --- src/drivers/net/intel.c | 16 ++++++++++++++-- src/drivers/net/intel.h | 10 ++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) (limited to 'src/drivers') diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index df73c4249..46527bdbb 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -290,6 +290,18 @@ static int intel_reset ( struct intel_nic *intel ) { pba, readl ( intel->regs + INTEL_PBA ) ); } + /* The Intel I210's packet buffer size registers reset only on + * power up. If an operating system changes these but then + * the computer recieves a reset signal without losing power, + * the registers will stay the same (but be incompatible with + * other register defaults), thus making the device unable to + * pass traffic. + */ + if ( intel->flags & INTEL_PBSIZE_RST ) { + writel ( INTEL_RXPBS_I210, intel->regs + INTEL_RXPBS ); + writel ( INTEL_TXPBS_I210, intel->regs + INTEL_TXPBS ); + } + /* Always reset MAC. Required to reset the TX and RX rings. */ writel ( ( ctrl | INTEL_CTRL_RST ), intel->regs + INTEL_CTRL ); mdelay ( INTEL_RESET_DELAY_MS ); @@ -1139,7 +1151,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x1525, "82567v-4", "82567V-4", 0 ), PCI_ROM ( 0x8086, 0x1526, "82576-5", "82576", 0 ), PCI_ROM ( 0x8086, 0x1527, "82580-f2", "82580 Fiber", 0 ), - PCI_ROM ( 0x8086, 0x1533, "i210", "I210", 0 ), + PCI_ROM ( 0x8086, 0x1533, "i210", "I210", INTEL_PBSIZE_RST ), PCI_ROM ( 0x8086, 0x1539, "i211", "I211", 0 ), PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x153b, "i217v", "I217-V", 0 ), @@ -1147,7 +1159,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x156f, "i219lm", "I219-LM", INTEL_I219 ), PCI_ROM ( 0x8086, 0x1570, "i219v", "I219-V", INTEL_I219 ), - PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", 0 ), + PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", INTEL_PBSIZE_RST ), PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", INTEL_NO_PHY_RST ), PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ), PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", INTEL_NO_PHY_RST ), diff --git a/src/drivers/net/intel.h b/src/drivers/net/intel.h index 4f51a80f6..29cf3a7d8 100644 --- a/src/drivers/net/intel.h +++ b/src/drivers/net/intel.h @@ -138,6 +138,10 @@ struct intel_descriptor { /** Packet Buffer Size */ #define INTEL_PBS 0x01008UL +/** Receive packet buffer size */ +#define INTEL_RXPBS 0x02404UL +#define INTEL_RXPBS_I210 0x000000a2UL /**< I210 power-up default */ + /** Receive Descriptor register block */ #define INTEL_RD 0x02800UL @@ -154,6 +158,10 @@ struct intel_descriptor { /** Receive buffer length */ #define INTEL_RX_MAX_LEN 2048 +/** Transmit packet buffer size */ +#define INTEL_TXPBS 0x03404UL +#define INTEL_TXPBS_I210 0x04000014UL /**< I210 power-up default */ + /** Transmit Descriptor register block */ #define INTEL_TD 0x03800UL @@ -319,6 +327,8 @@ enum intel_flags { INTEL_NO_ASDE = 0x0008, /** Reset may cause a complete device hang */ INTEL_RST_HANG = 0x0010, + /** PBSIZE registers must be explicitly reset */ + INTEL_PBSIZE_RST = 0x0020, }; /** The i219 has a seriously broken reset mechanism */ -- cgit v1.2.3-55-g7522