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/net') 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/net') 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/net') 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/net') 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/net') 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/net') 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 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/net') 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 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/net') 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/net') 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