From e7f67d5a4c6e9f06aa7a9db1b4245f5e16f00bb2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 14 May 2018 11:16:34 +0100 Subject: [http] Work around stateful authentication schemes As pointedly documented in RFC7230 section 2.3, HTTP is a stateless protocol: each request message can be understood in isolation from any other requests or responses. Various authentication schemes such as NTLM break this fundamental property of HTTP and rely on the same TCP connection being reused. Work around these broken authentication schemes by ensuring that the most recently pooled connection is reused for the subsequent authentication retry. Reported-by: Andreas Hammarskjöld Tested-by: Andreas Hammarskjöld Signed-off-by: Michael Brown --- src/net/tcp/httpconn.c | 9 +++++++-- src/net/tcp/httpcore.c | 12 ++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/net/tcp/httpconn.c b/src/net/tcp/httpconn.c index 2cfca9c94..5121ff6c2 100644 --- a/src/net/tcp/httpconn.c +++ b/src/net/tcp/httpconn.c @@ -252,8 +252,13 @@ int http_connect ( struct interface *xfer, struct uri *uri ) { /* Identify port */ port = uri_port ( uri, scheme->port ); - /* Look for a reusable connection in the pool */ - list_for_each_entry ( conn, &http_connection_pool, pool.list ) { + /* Look for a reusable connection in the pool. Reuse the most + * recent connection in order to accommodate authentication + * schemes that break the stateless nature of HTTP and rely on + * the same connection being reused for authentication + * responses. + */ + list_for_each_entry_reverse ( conn, &http_connection_pool, pool.list ) { /* Sanity checks */ assert ( conn->uri != NULL ); diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index b3c9a00e6..f755fb72d 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -778,6 +778,18 @@ static int http_transfer_complete ( struct http_transaction *http ) { http->len = 0; assert ( http->remaining == 0 ); + /* Retry immediately if applicable. We cannot rely on an + * immediate timer expiry, since certain Microsoft-designed + * HTTP extensions such as NTLM break the fundamentally + * stateless nature of HTTP and rely on the same connection + * being reused for authentication. See RFC7230 section 2.3 + * for further details. + */ + if ( ! http->response.retry_after ) { + http_reopen ( http ); + return 0; + } + /* Start timer to initiate retry */ DBGC2 ( http, "HTTP %p retrying after %d seconds\n", http, http->response.retry_after ); -- cgit v1.2.3-55-g7522 From 8ed4e3049f5fcb3a0b32f5190940e78c61654cff Mon Sep 17 00:00:00 2001 From: Bruce Rogers Date: Tue, 24 Apr 2018 08:47:32 -0600 Subject: [build] Disable gcc stringop-truncation warnings The gcc 8 compiler introduces a warning for certain string manipulation functions, flagging usages which _may_ not be intended. An audit of the iPXE sources indicates all usages of strncat and strncpy are as intended, so the warnings currently issued are not helpful, especially if warnings are considered errors. Fix by detecting gcc's support for -Wno-stringop-truncation and, if detected, using that option to avoid the warning. Signed-off-by: Bruce Rogers Modified-by: Michael Brown Also-fixed-by: Christian Hesse Also-fixed-by: Roman Kagan Also-fixed-by: Bernhard M. Wiedemann Also-fixed-by: Olaf Hering Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 00b079263..b53377bd5 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -177,6 +177,14 @@ ifeq ($(CCTYPE),gcc) WNA_TEST = $(CC) -Wno-address -x c -c /dev/null -o /dev/null >/dev/null 2>&1 WNA_FLAGS := $(shell $(WNA_TEST) && $(ECHO) '-Wno-address') WORKAROUND_CFLAGS += $(WNA_FLAGS) + +# gcc 8.0 generates warnings for certain suspect string operations. Our +# sources have been vetted for correct usage. Turn off these warnings. +# +WNST_TEST = $(CC) -Wno-stringop-truncation -x c -c /dev/null -o /dev/null \ + >/dev/null 2>&1 +WNST_FLAGS := $(shell $(WNST_TEST) && $(ECHO) '-Wno-stringop-truncation') +WORKAROUND_CFLAGS += $(WNST_FLAGS) endif # Some versions of gas choke on division operators, treating them as @@ -437,7 +445,7 @@ endif CFLAGS += $(WORKAROUND_CFLAGS) $(EXTRA_CFLAGS) ASFLAGS += $(WORKAROUND_ASFLAGS) $(EXTRA_ASFLAGS) LDFLAGS += $(WORKAROUND_LDFLAGS) $(EXTRA_LDFLAGS) -HOST_CFLAGS += -O2 -g +HOST_CFLAGS += $(WORKAROUND_CFLAGS) -O2 -g # Inhibit -Werror if NO_WERROR is specified on make command line # -- cgit v1.2.3-55-g7522 From 97a3d37285ed367c8ee55fe51c3186be0aa8f320 Mon Sep 17 00:00:00 2001 From: Steven Haber Date: Thu, 10 May 2018 16:12:32 -0700 Subject: [intelx] Add support for Intel X552 NIC Signed-off-by: Michael Brown --- src/drivers/net/intelx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/drivers/net/intelx.c b/src/drivers/net/intelx.c index afd744eda..47de90c88 100644 --- a/src/drivers/net/intelx.c +++ b/src/drivers/net/intelx.c @@ -474,6 +474,7 @@ static struct pci_device_id intelx_nics[] = { PCI_ROM ( 0x8086, 0x1557, "82599en-sfp", "82599 (Single Port SFI Only)", 0 ), PCI_ROM ( 0x8086, 0x1560, "x540t1", "X540-AT2/X540-BT2 (with single port NVM)", 0 ), PCI_ROM ( 0x8086, 0x1563, "x550t2", "X550-T2", 0 ), + PCI_ROM ( 0x8086, 0x15ab, "x552", "X552", 0 ), PCI_ROM ( 0x8086, 0x15e5, "x553", "X553", 0 ), }; -- cgit v1.2.3-55-g7522 From 88ac1d3fe4ad8e905f679355efac236d1e55ba82 Mon Sep 17 00:00:00 2001 From: Rob Taglang Date: Fri, 27 Apr 2018 14:19:07 -0400 Subject: [efi] Exclude link-layer header length from MaxPacketSize Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/interface/efi/efi_snp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index 3f95a8961..9c1b14d87 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -123,7 +123,7 @@ static void efi_snp_set_mode ( struct efi_snp_device *snpdev ) { mode->HwAddressSize = ll_addr_len; mode->MediaHeaderSize = ll_protocol->ll_header_len; - mode->MaxPacketSize = netdev->max_pkt_len; + mode->MaxPacketSize = netdev->mtu; mode->ReceiveFilterMask = ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST ); -- cgit v1.2.3-55-g7522 From 16d7495308058fb921b67a5ceddcbe169489787b Mon Sep 17 00:00:00 2001 From: Roman Kagan Date: Fri, 1 Jun 2018 09:59:01 +0300 Subject: [rndis] Register netdev with MAC filled register_netdev expects ->hw_addr and ->ll_addr to be already filled, so move it towards the end of register_rndis, after the respective fields have been successfully queried from the underlying device. Signed-off-by: Roman Kagan Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/net/rndis.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/net/rndis.c b/src/net/rndis.c index 8c4fe8b30..8a58cb72e 100644 --- a/src/net/rndis.c +++ b/src/net/rndis.c @@ -973,13 +973,6 @@ int register_rndis ( struct rndis_device *rndis ) { /* Assign device name (for debugging) */ rndis->name = netdev->dev->name; - /* Register network device */ - if ( ( rc = register_netdev ( netdev ) ) != 0 ) { - DBGC ( rndis, "RNDIS %s could not register: %s\n", - rndis->name, strerror ( rc ) ); - goto err_register; - } - /* Open RNDIS device to read MAC addresses */ if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) { DBGC ( rndis, "RNDIS %s could not open: %s\n", @@ -1012,8 +1005,17 @@ int register_rndis ( struct rndis_device *rndis ) { /* Close RNDIS device */ rndis->op->close ( rndis ); + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) { + DBGC ( rndis, "RNDIS %s could not register: %s\n", + rndis->name, strerror ( rc ) ); + goto err_register; + } + return 0; + unregister_netdev ( netdev ); + err_register: err_query_link: err_query_current: err_query_permanent: @@ -1021,8 +1023,6 @@ int register_rndis ( struct rndis_device *rndis ) { err_initialise: rndis->op->close ( rndis ); err_open: - unregister_netdev ( netdev ); - err_register: return rc; } -- cgit v1.2.3-55-g7522 From 40956545e25103fcc4c22e496e4d1d842e2aac53 Mon Sep 17 00:00:00 2001 From: Roman Kagan Date: Sat, 9 Jun 2018 17:53:31 +0300 Subject: [vmbus] Do not expect version in version_response The definition of version_response channel message in Linux doesn't include version field, so the upcoming VMBus implementation in QEMU doesn't set it either. Neither Windows nor Linux had any problem with this. The check against this field is redundant because the message is the response to initiate_contact message containing the specific version requested, so the response with version_supported=true is unambiguous. Drop this check and don't rely on the field to be present in the message. Signed-off-by: Roman Kagan Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/interface/hyperv/vmbus.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/interface/hyperv/vmbus.c b/src/interface/hyperv/vmbus.c index 45a7caec4..e50fe9951 100644 --- a/src/interface/hyperv/vmbus.c +++ b/src/interface/hyperv/vmbus.c @@ -195,16 +195,10 @@ static int vmbus_initiate_contact ( struct hv_hypervisor *hv, vmbus ); return -ENOTSUP; } - if ( version->version.raw != cpu_to_le32 ( raw ) ) { - DBGC ( vmbus, "VMBUS %p unexpected version %d.%d\n", - vmbus, le16_to_cpu ( version->version.major ), - le16_to_cpu ( version->version.minor ) ); - return -EPROTO; - } DBGC ( vmbus, "VMBUS %p initiated contact using version %d.%d\n", - vmbus, le16_to_cpu ( version->version.major ), - le16_to_cpu ( version->version.minor ) ); + vmbus, le16_to_cpu ( initiate.version.major ), + le16_to_cpu ( initiate.version.minor ) ); return 0; } -- cgit v1.2.3-55-g7522 From 1c47eb186e7e38bde2a1db528cc3557b1b300386 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 8 Jul 2018 18:12:43 +0100 Subject: [build] Use positive-form tests when checking for supported warnings Some versions of gcc seem to silently accept an attempt to disable an unrecognised warning (e.g. via -Wno-stringop-truncation) but will then report the unrecognised warning if any other error occurs during the build, resulting in a potentially misleading error message. Avoid this potential confusion by using the positive-form tests in order to determine the workaround CFLAGS. Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index b53377bd5..f8334921b 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -174,14 +174,14 @@ endif # Inhibit this. # ifeq ($(CCTYPE),gcc) -WNA_TEST = $(CC) -Wno-address -x c -c /dev/null -o /dev/null >/dev/null 2>&1 +WNA_TEST = $(CC) -Waddress -x c -c /dev/null -o /dev/null >/dev/null 2>&1 WNA_FLAGS := $(shell $(WNA_TEST) && $(ECHO) '-Wno-address') WORKAROUND_CFLAGS += $(WNA_FLAGS) # gcc 8.0 generates warnings for certain suspect string operations. Our # sources have been vetted for correct usage. Turn off these warnings. # -WNST_TEST = $(CC) -Wno-stringop-truncation -x c -c /dev/null -o /dev/null \ +WNST_TEST = $(CC) -Wstringop-truncation -x c -c /dev/null -o /dev/null \ >/dev/null 2>&1 WNST_FLAGS := $(shell $(WNST_TEST) && $(ECHO) '-Wno-stringop-truncation') WORKAROUND_CFLAGS += $(WNST_FLAGS) -- cgit v1.2.3-55-g7522 From 05b979146ddb0b957354663a99c181357ad310b2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 9 Jul 2018 10:35:57 +0100 Subject: [rndis] Clean up error handling path in register_rndis() Avoid calling rndis_halt() and rndis->op->close() twice if the call to register_netdev() fails. Reported-by: Roman Kagan Signed-off-by: Michael Brown --- src/net/rndis.c | 102 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 41 deletions(-) (limited to 'src') diff --git a/src/net/rndis.c b/src/net/rndis.c index 8a58cb72e..a3b562bc2 100644 --- a/src/net/rndis.c +++ b/src/net/rndis.c @@ -650,6 +650,63 @@ static int rndis_oid ( struct rndis_device *rndis, unsigned int oid, return 0; } +/** + * Describe RNDIS device + * + * @v rndis RNDIS device + * @ret rc Return status code + */ +static int rndis_describe ( struct rndis_device *rndis ) { + struct net_device *netdev = rndis->netdev; + int rc; + + /* Assign device name (for debugging) */ + rndis->name = netdev->dev->name; + + /* Open RNDIS device to read MAC addresses */ + if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) { + DBGC ( rndis, "RNDIS %s could not open: %s\n", + rndis->name, strerror ( rc ) ); + goto err_open; + } + + /* Initialise RNDIS */ + if ( ( rc = rndis_initialise ( rndis ) ) != 0 ) + goto err_initialise; + + /* Query permanent MAC address */ + if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_PERMANENT_ADDRESS, + NULL, 0 ) ) != 0 ) + goto err_query_permanent; + + /* Query current MAC address */ + if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_CURRENT_ADDRESS, + NULL, 0 ) ) != 0 ) + goto err_query_current; + + /* Get link status */ + if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, + NULL, 0 ) ) != 0 ) + goto err_query_link; + + /* Halt RNDIS device */ + rndis_halt ( rndis ); + + /* Close RNDIS device */ + rndis->op->close ( rndis ); + + return 0; + + err_query_link: + err_query_current: + err_query_permanent: + rndis_halt ( rndis ); + err_initialise: + rndis->op->close ( rndis ); + err_open: + return rc; +} + /** * Receive indicate status message * @@ -970,40 +1027,9 @@ int register_rndis ( struct rndis_device *rndis ) { struct net_device *netdev = rndis->netdev; int rc; - /* Assign device name (for debugging) */ - rndis->name = netdev->dev->name; - - /* Open RNDIS device to read MAC addresses */ - if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) { - DBGC ( rndis, "RNDIS %s could not open: %s\n", - rndis->name, strerror ( rc ) ); - goto err_open; - } - - /* Initialise RNDIS */ - if ( ( rc = rndis_initialise ( rndis ) ) != 0 ) - goto err_initialise; - - /* Query permanent MAC address */ - if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_PERMANENT_ADDRESS, - NULL, 0 ) ) != 0 ) - goto err_query_permanent; - - /* Query current MAC address */ - if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_CURRENT_ADDRESS, - NULL, 0 ) ) != 0 ) - goto err_query_current; - - /* Get link status */ - if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, - NULL, 0 ) ) != 0 ) - goto err_query_link; - - /* Halt RNDIS device */ - rndis_halt ( rndis ); - - /* Close RNDIS device */ - rndis->op->close ( rndis ); + /* Describe RNDIS device */ + if ( ( rc = rndis_describe ( rndis ) ) != 0 ) + goto err_describe; /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) { @@ -1016,13 +1042,7 @@ int register_rndis ( struct rndis_device *rndis ) { unregister_netdev ( netdev ); err_register: - err_query_link: - err_query_current: - err_query_permanent: - rndis_halt ( rndis ); - err_initialise: - rndis->op->close ( rndis ); - err_open: + err_describe: return rc; } -- cgit v1.2.3-55-g7522 From b9d68b9de02cc474c8a713f1232ce69fefafe18e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 17 Jul 2018 12:01:30 +0100 Subject: [ethernet] Use standard 1500 byte MTU unless explicitly overridden Devices that support jumbo frames will currently default to the largest possible MTU. This assumption is valid for virtual adapters such as virtio-net, where the MTU must have been configured by a system administrator, but is unsafe in the general case of a physical adapter. Default to the standard Ethernet MTU, unless explicitly overridden either by the driver or via the ${netX/mtu} setting. Signed-off-by: Michael Brown --- src/drivers/net/ena.c | 1 + src/drivers/net/virtio-net.c | 1 + src/net/ethernet.c | 1 + 3 files changed, 3 insertions(+) (limited to 'src') diff --git a/src/drivers/net/ena.c b/src/drivers/net/ena.c index 8d29979bb..0f25c0beb 100644 --- a/src/drivers/net/ena.c +++ b/src/drivers/net/ena.c @@ -565,6 +565,7 @@ static int ena_get_device_attributes ( struct net_device *netdev ) { feature = &rsp->get_feature.feature; memcpy ( netdev->hw_addr, feature->device.mac, ETH_ALEN ); netdev->max_pkt_len = le32_to_cpu ( feature->device.mtu ); + netdev->mtu = ( netdev->max_pkt_len - ETH_HLEN ); DBGC ( ena, "ENA %p MAC %s MTU %zd\n", ena, eth_ntoa ( netdev->hw_addr ), netdev->max_pkt_len ); diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c index fe79a92c4..78ec9ac4e 100644 --- a/src/drivers/net/virtio-net.c +++ b/src/drivers/net/virtio-net.c @@ -493,6 +493,7 @@ static int virtnet_probe_legacy ( struct pci_device *pci ) { &mtu, sizeof ( mtu ) ); DBGC ( virtnet, "VIRTIO-NET %p mtu=%d\n", virtnet, mtu ); netdev->max_pkt_len = ( mtu + ETH_HLEN ); + netdev->mtu = mtu; } /* Register network device */ diff --git a/src/net/ethernet.c b/src/net/ethernet.c index 26fdedea8..707bdf905 100644 --- a/src/net/ethernet.c +++ b/src/net/ethernet.c @@ -269,6 +269,7 @@ struct net_device * alloc_etherdev ( size_t priv_size ) { netdev->ll_protocol = ðernet_protocol; netdev->ll_broadcast = eth_broadcast; netdev->max_pkt_len = ETH_FRAME_LEN; + netdev->mtu = ETH_MAX_MTU; } return netdev; } -- cgit v1.2.3-55-g7522 From d2063b7693e0e35db97b2264aa987eb6341ae779 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 8 Jul 2018 16:41:12 +0100 Subject: [intelxl] Add driver for Intel 40 Gigabit Ethernet NICs Signed-off-by: Michael Brown --- src/drivers/net/intelxl.c | 1525 ++++++++++++++++++++++++++++++++++++++++++++ src/drivers/net/intelxl.h | 790 +++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + 3 files changed, 2316 insertions(+) create mode 100644 src/drivers/net/intelxl.c create mode 100644 src/drivers/net/intelxl.h (limited to 'src') diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c new file mode 100644 index 000000000..3e40fa4ae --- /dev/null +++ b/src/drivers/net/intelxl.c @@ -0,0 +1,1525 @@ +/* + * Copyright (C) 2018 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "intelxl.h" + +/** @file + * + * Intel 40 Gigabit Ethernet network card driver + * + */ + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset hardware + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxl_reset ( struct intelxl_nic *intelxl ) { + uint32_t pfgen_ctrl; + + /* Perform a global software reset */ + pfgen_ctrl = readl ( intelxl->regs + INTELXL_PFGEN_CTRL ); + writel ( ( pfgen_ctrl | INTELXL_PFGEN_CTRL_PFSWR ), + intelxl->regs + INTELXL_PFGEN_CTRL ); + mdelay ( INTELXL_RESET_DELAY_MS ); + + return 0; +} + +/****************************************************************************** + * + * MAC address + * + ****************************************************************************** + */ + +/** + * Fetch initial MAC address and maximum frame size + * + * @v intelxl Intel device + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxl_fetch_mac ( struct intelxl_nic *intelxl, + struct net_device *netdev ) { + union intelxl_receive_address mac; + uint32_t prtgl_sal; + uint32_t prtgl_sah; + size_t mfs; + + /* Read NVM-loaded address */ + prtgl_sal = readl ( intelxl->regs + INTELXL_PRTGL_SAL ); + prtgl_sah = readl ( intelxl->regs + INTELXL_PRTGL_SAH ); + mac.reg.low = cpu_to_le32 ( prtgl_sal ); + mac.reg.high = cpu_to_le32 ( prtgl_sah ); + + /* Check that address is valid */ + if ( ! is_valid_ether_addr ( mac.raw ) ) { + DBGC ( intelxl, "INTELXL %p has invalid MAC address (%s)\n", + intelxl, eth_ntoa ( mac.raw ) ); + return -ENOENT; + } + + /* Copy MAC address */ + DBGC ( intelxl, "INTELXL %p has autoloaded MAC address %s\n", + intelxl, eth_ntoa ( mac.raw ) ); + memcpy ( netdev->hw_addr, mac.raw, ETH_ALEN ); + + /* Get maximum frame size */ + mfs = INTELXL_PRTGL_SAH_MFS_GET ( prtgl_sah ); + netdev->max_pkt_len = ( mfs - 4 /* CRC */ ); + + return 0; +} + +/****************************************************************************** + * + * Admin queue + * + ****************************************************************************** + */ + +/** + * Create admin queue + * + * @v intelxl Intel device + * @v admin Admin queue + * @ret rc Return status code + */ +static int intelxl_create_admin ( struct intelxl_nic *intelxl, + struct intelxl_admin *admin ) { + size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC ); + void *admin_regs = ( intelxl->regs + admin->reg ); + physaddr_t address; + + /* Allocate admin queue */ + admin->desc = malloc_dma ( ( len + sizeof ( *admin->buffer ) ), + INTELXL_ALIGN ); + if ( ! admin->desc ) + return -ENOMEM; + admin->buffer = ( ( ( void * ) admin->desc ) + len ); + + /* Initialise admin queue */ + memset ( admin->desc, 0, len ); + + /* Reset head and tail registers */ + writel ( 0, admin_regs + INTELXL_ADMIN_HEAD ); + writel ( 0, admin_regs + INTELXL_ADMIN_TAIL ); + + /* Reset queue index */ + admin->index = 0; + + /* Program queue address */ + address = virt_to_bus ( admin->desc ); + writel ( ( address & 0xffffffffUL ), admin_regs + INTELXL_ADMIN_BAL ); + if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) { + writel ( ( ( ( uint64_t ) address ) >> 32 ), + admin_regs + INTELXL_ADMIN_BAH ); + } else { + writel ( 0, admin_regs + INTELXL_ADMIN_BAH ); + } + + /* Program queue length and enable queue */ + writel ( ( INTELXL_ADMIN_LEN_LEN ( INTELXL_ADMIN_NUM_DESC ) | + INTELXL_ADMIN_LEN_ENABLE ), + admin_regs + INTELXL_ADMIN_LEN ); + + DBGC ( intelxl, "INTELXL %p A%cQ is at [%08llx,%08llx) buf " + "[%08llx,%08llx)\n", intelxl, + ( ( admin->reg == INTELXL_ADMIN_CMD ) ? 'T' : 'R' ), + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + len ), + ( ( unsigned long long ) virt_to_bus ( admin->buffer ) ), + ( ( unsigned long long ) ( virt_to_bus ( admin->buffer ) + + sizeof ( admin->buffer[0] ) ) ) ); + return 0; +} + +/** + * Destroy admin queue + * + * @v intelxl Intel device + * @v admin Admin queue + */ +static void intelxl_destroy_admin ( struct intelxl_nic *intelxl, + struct intelxl_admin *admin ) { + size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC ); + void *admin_regs = ( intelxl->regs + admin->reg ); + + /* Disable queue */ + writel ( 0, admin_regs + INTELXL_ADMIN_LEN ); + + /* Free queue */ + free_dma ( admin->desc, ( len + sizeof ( *admin->buffer ) ) ); +} + +/** + * Issue admin queue command + * + * @v intelxl Intel device + * @v cmd Command descriptor + * @ret rc Return status code + */ +static int intelxl_admin_command ( struct intelxl_nic *intelxl, + struct intelxl_admin_descriptor *cmd ) { + struct intelxl_admin *admin = &intelxl->command; + void *admin_regs = ( intelxl->regs + admin->reg ); + struct intelxl_admin_descriptor *desc; + uint64_t buffer; + unsigned int index; + unsigned int tail; + unsigned int i; + int rc; + + /* Get next queue entry */ + index = admin->index++; + tail = ( admin->index % INTELXL_ADMIN_NUM_DESC ); + desc = &admin->desc[index % INTELXL_ADMIN_NUM_DESC]; + + /* Clear must-be-zero flags */ + cmd->flags &= ~cpu_to_le16 ( INTELXL_ADMIN_FL_DD | + INTELXL_ADMIN_FL_CMP | + INTELXL_ADMIN_FL_ERR ); + + /* Clear return value */ + cmd->ret = 0; + + /* Populate cookie */ + cmd->cookie = cpu_to_le32 ( index ); + + /* Populate data buffer address if applicable */ + if ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ) ) { + buffer = virt_to_bus ( admin->buffer ); + cmd->params.buffer.high = cpu_to_le32 ( buffer >> 32 ); + cmd->params.buffer.low = cpu_to_le32 ( buffer & 0xffffffffUL ); + } + + /* Copy command descriptor to queue entry */ + memcpy ( desc, cmd, sizeof ( *desc ) ); + DBGC2 ( intelxl, "INTELXL %p admin command %#x:\n", intelxl, index ); + DBGC2_HDA ( intelxl, virt_to_phys ( desc ), desc, sizeof ( *desc ) ); + + /* Post command descriptor */ + wmb(); + writel ( tail, admin_regs + INTELXL_ADMIN_TAIL ); + + /* Wait for completion */ + for ( i = 0 ; i < INTELXL_ADMIN_MAX_WAIT_MS ; i++ ) { + + /* If response is not complete, delay 1ms and retry */ + if ( ! ( desc->flags & INTELXL_ADMIN_FL_DD ) ) { + mdelay ( 1 ); + continue; + } + DBGC2 ( intelxl, "INTELXL %p admin command %#x response:\n", + intelxl, index ); + DBGC2_HDA ( intelxl, virt_to_phys ( desc ), desc, + sizeof ( *desc ) ); + + /* Check for cookie mismatch */ + if ( desc->cookie != cmd->cookie ) { + DBGC ( intelxl, "INTELXL %p admin command %#x bad " + "cookie %#x\n", intelxl, index, + le32_to_cpu ( desc->cookie ) ); + rc = -EPROTO; + goto err; + } + + /* Check for errors */ + if ( desc->ret != 0 ) { + DBGC ( intelxl, "INTELXL %p admin command %#x error " + "%d\n", intelxl, index, + le16_to_cpu ( desc->ret ) ); + rc = -EIO; + goto err; + } + + /* Copy response back to command descriptor */ + memcpy ( cmd, desc, sizeof ( *cmd ) ); + + /* Success */ + return 0; + } + + rc = -ETIMEDOUT; + DBGC ( intelxl, "INTELXL %p timed out waiting for admin command %#x:\n", + intelxl, index ); + err: + DBGC_HDA ( intelxl, virt_to_phys ( desc ), cmd, sizeof ( *cmd ) ); + DBGC_HDA ( intelxl, virt_to_phys ( desc ), desc, sizeof ( *desc ) ); + return rc; +} + +/** + * Get firmware version + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxl_admin_version ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor cmd; + struct intelxl_admin_version_params *version = &cmd.params.version; + unsigned int api; + int rc; + + /* Populate descriptor */ + memset ( &cmd, 0, sizeof ( cmd ) ); + cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_VERSION ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 ) + return rc; + api = le16_to_cpu ( version->api.major ); + DBGC ( intelxl, "INTELXL %p firmware v%d.%d API v%d.%d\n", + intelxl, le16_to_cpu ( version->firmware.major ), + le16_to_cpu ( version->firmware.minor ), + api, le16_to_cpu ( version->api.minor ) ); + + /* Check for API compatibility */ + if ( api > INTELXL_ADMIN_API_MAJOR ) { + DBGC ( intelxl, "INTELXL %p unsupported API v%d\n", + intelxl, api ); + return -ENOTSUP; + } + + return 0; +} + +/** + * Report driver version + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxl_admin_driver ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor cmd; + struct intelxl_admin_driver_params *driver = &cmd.params.driver; + struct intelxl_admin_driver_buffer *buf = + &intelxl->command.buffer->driver; + int rc; + + /* Populate descriptor */ + memset ( &cmd, 0, sizeof ( cmd ) ); + cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_DRIVER ); + cmd.flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd.len = cpu_to_le16 ( sizeof ( *buf ) ); + driver->major = product_major_version; + driver->minor = product_minor_version; + snprintf ( buf->name, sizeof ( buf->name ), "%s", + ( product_name[0] ? product_name : product_short_name ) ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Shutdown admin queues + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxl_admin_shutdown ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor cmd; + struct intelxl_admin_shutdown_params *shutdown = &cmd.params.shutdown; + int rc; + + /* Populate descriptor */ + memset ( &cmd, 0, sizeof ( cmd ) ); + cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_SHUTDOWN ); + shutdown->unloading = INTELXL_ADMIN_SHUTDOWN_UNLOADING; + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Get switch configuration + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxl_admin_switch ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor cmd; + struct intelxl_admin_switch_params *sw = &cmd.params.sw; + struct intelxl_admin_switch_buffer *buf = &intelxl->command.buffer->sw; + struct intelxl_admin_switch_config *cfg = &buf->cfg; + int rc; + + /* Populate descriptor */ + memset ( &cmd, 0, sizeof ( cmd ) ); + cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_SWITCH ); + cmd.flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd.len = cpu_to_le16 ( sizeof ( *buf ) ); + + /* Get each configuration in turn */ + do { + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 ) + return rc; + + /* Dump raw configuration */ + DBGC2 ( intelxl, "INTELXL %p SEID %#04x:\n", + intelxl, le16_to_cpu ( cfg->seid ) ); + DBGC2_HDA ( intelxl, 0, cfg, sizeof ( *cfg ) ); + + /* Parse response */ + if ( cfg->type == INTELXL_ADMIN_SWITCH_TYPE_VSI ) { + intelxl->vsi = le16_to_cpu ( cfg->seid ); + DBGC ( intelxl, "INTELXL %p VSI %#04x uplink %#04x " + "downlink %#04x conn %#02x\n", intelxl, + intelxl->vsi, le16_to_cpu ( cfg->uplink ), + le16_to_cpu ( cfg->downlink ), cfg->connection ); + } + + } while ( sw->next ); + + /* Check that we found a VSI */ + if ( ! intelxl->vsi ) { + DBGC ( intelxl, "INTELXL %p has no VSI\n", intelxl ); + return -ENOENT; + } + + return 0; +} + +/** + * Get VSI parameters + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxl_admin_vsi ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor cmd; + struct intelxl_admin_vsi_params *vsi = &cmd.params.vsi; + struct intelxl_admin_vsi_buffer *buf = &intelxl->command.buffer->vsi; + int rc; + + /* Populate descriptor */ + memset ( &cmd, 0, sizeof ( cmd ) ); + cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_VSI ); + cmd.flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd.len = cpu_to_le16 ( sizeof ( *buf ) ); + vsi->vsi = cpu_to_le16 ( intelxl->vsi ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 ) + return rc; + + /* Parse response */ + intelxl->queue = le16_to_cpu ( buf->queue[0] ); + intelxl->qset = le16_to_cpu ( buf->qset[0] ); + DBGC ( intelxl, "INTELXL %p VSI %#04x queue %#04x qset %#04x\n", + intelxl, intelxl->vsi, intelxl->queue, intelxl->qset ); + + return 0; +} + +/** + * Set VSI promiscuous modes + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxl_admin_promisc ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor cmd; + struct intelxl_admin_promisc_params *promisc = &cmd.params.promisc; + uint16_t flags; + int rc; + + /* Populate descriptor */ + memset ( &cmd, 0, sizeof ( cmd ) ); + cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_PROMISC ); + flags = ( INTELXL_ADMIN_PROMISC_FL_UNICAST | + INTELXL_ADMIN_PROMISC_FL_MULTICAST | + INTELXL_ADMIN_PROMISC_FL_BROADCAST | + INTELXL_ADMIN_PROMISC_FL_VLAN ); + promisc->flags = cpu_to_le16 ( flags ); + promisc->valid = cpu_to_le16 ( flags ); + promisc->vsi = cpu_to_le16 ( intelxl->vsi ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Restart autonegotiation + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxl_admin_autoneg ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor cmd; + struct intelxl_admin_autoneg_params *autoneg = &cmd.params.autoneg; + int rc; + + /* Populate descriptor */ + memset ( &cmd, 0, sizeof ( cmd ) ); + cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_AUTONEG ); + autoneg->flags = ( INTELXL_ADMIN_AUTONEG_FL_RESTART | + INTELXL_ADMIN_AUTONEG_FL_ENABLE ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Get link status + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxl_admin_link ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_admin_descriptor cmd; + struct intelxl_admin_link_params *link = &cmd.params.link; + int rc; + + /* Populate descriptor */ + memset ( &cmd, 0, sizeof ( cmd ) ); + cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_LINK ); + link->notify = INTELXL_ADMIN_LINK_NOTIFY; + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 ) + return rc; + DBGC ( intelxl, "INTELXL %p PHY %#02x speed %#02x status %#02x\n", + intelxl, link->phy, link->speed, link->status ); + + /* Update network device */ + if ( link->status & INTELXL_ADMIN_LINK_UP ) { + netdev_link_up ( netdev ); + } else { + netdev_link_down ( netdev ); + } + + return 0; +} + +/** + * Refill admin event queue + * + * @v intelxl Intel device + */ +static void intelxl_refill_admin ( struct intelxl_nic *intelxl ) { + struct intelxl_admin *admin = &intelxl->event; + void *admin_regs = ( intelxl->regs + admin->reg ); + unsigned int tail; + + /* Update tail pointer */ + tail = ( ( admin->index + INTELXL_ADMIN_NUM_DESC - 1 ) % + INTELXL_ADMIN_NUM_DESC ); + writel ( tail, admin_regs + INTELXL_ADMIN_TAIL ); +} + +/** + * Poll admin event queue + * + * @v netdev Network device + */ +static void intelxl_poll_admin ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_admin *admin = &intelxl->event; + struct intelxl_admin_descriptor *desc; + + /* Check for events */ + while ( 1 ) { + + /* Get next event descriptor */ + desc = &admin->desc[admin->index % INTELXL_ADMIN_NUM_DESC]; + + /* Stop if descriptor is not yet completed */ + if ( ! ( desc->flags & INTELXL_ADMIN_FL_DD ) ) + return; + DBGC2 ( intelxl, "INTELXL %p admin event %#x:\n", + intelxl, admin->index ); + DBGC2_HDA ( intelxl, virt_to_phys ( desc ), desc, + sizeof ( *desc ) ); + + /* Handle event */ + switch ( desc->opcode ) { + case cpu_to_le16 ( INTELXL_ADMIN_LINK ): + intelxl_admin_link ( netdev ); + break; + default: + DBGC ( intelxl, "INTELXL %p admin event %#x " + "unrecognised opcode %#04x\n", intelxl, + admin->index, le16_to_cpu ( desc->opcode ) ); + break; + } + + /* Clear event completion flag */ + desc->flags = 0; + wmb(); + + /* Update index and refill queue */ + admin->index++; + intelxl_refill_admin ( intelxl ); + } +} + +/** + * Open admin queues + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxl_open_admin ( struct intelxl_nic *intelxl ) { + int rc; + + /* Create admin event queue */ + if ( ( rc = intelxl_create_admin ( intelxl, &intelxl->event ) ) != 0 ) + goto err_create_event; + + /* Create admin command queue */ + if ( ( rc = intelxl_create_admin ( intelxl, &intelxl->command ) ) != 0 ) + goto err_create_command; + + /* Post all descriptors to event queue */ + intelxl_refill_admin ( intelxl ); + + /* Get firmware version */ + if ( ( rc = intelxl_admin_version ( intelxl ) ) != 0 ) + goto err_version; + + /* Report driver version */ + if ( ( rc = intelxl_admin_driver ( intelxl ) ) != 0 ) + goto err_driver; + + return 0; + + err_driver: + err_version: + intelxl_destroy_admin ( intelxl, &intelxl->command ); + err_create_command: + intelxl_destroy_admin ( intelxl, &intelxl->event ); + err_create_event: + return rc; +} + +/** + * Close admin queues + * + * @v intelxl Intel device + */ +static void intelxl_close_admin ( struct intelxl_nic *intelxl ) { + + /* Shut down admin queues */ + intelxl_admin_shutdown ( intelxl ); + + /* Destroy admin command queue */ + intelxl_destroy_admin ( intelxl, &intelxl->command ); + + /* Destroy admin event queue */ + intelxl_destroy_admin ( intelxl, &intelxl->event ); +} + +/****************************************************************************** + * + * Descriptor rings + * + ****************************************************************************** + */ + +/** + * Dump queue context (for debugging) + * + * @v intelxl Intel device + * @v op Context operation + * @v len Size of context + */ +static __attribute__ (( unused )) void +intelxl_context_dump ( struct intelxl_nic *intelxl, uint32_t op, size_t len ) { + struct intelxl_context_line line; + uint32_t pfcm_lanctxctl; + uint32_t pfcm_lanctxstat; + unsigned int queue; + unsigned int index; + unsigned int i; + + /* Do nothing unless debug output is enabled */ + if ( ! DBG_EXTRA ) + return; + + /* Dump context */ + DBGC2 ( intelxl, "INTELXL %p context %#08x:\n", intelxl, op ); + for ( index = 0 ; ( sizeof ( line ) * index ) < len ; index++ ) { + + /* Start context operation */ + queue = ( intelxl->base + intelxl->queue ); + pfcm_lanctxctl = + ( INTELXL_PFCM_LANCTXCTL_QUEUE_NUM ( queue ) | + INTELXL_PFCM_LANCTXCTL_SUB_LINE ( index ) | + INTELXL_PFCM_LANCTXCTL_OP_CODE_READ | op ); + writel ( pfcm_lanctxctl, + intelxl->regs + INTELXL_PFCM_LANCTXCTL ); + + /* Wait for operation to complete */ + for ( i = 0 ; i < INTELXL_CTX_MAX_WAIT_MS ; i++ ) { + + /* Check if operation is complete */ + pfcm_lanctxstat = readl ( intelxl->regs + + INTELXL_PFCM_LANCTXSTAT ); + if ( pfcm_lanctxstat & INTELXL_PFCM_LANCTXSTAT_DONE ) + break; + + /* Delay */ + mdelay ( 1 ); + } + + /* Read context data */ + for ( i = 0 ; i < ( sizeof ( line ) / + sizeof ( line.raw[0] ) ) ; i++ ) { + line.raw[i] = readl ( intelxl->regs + + INTELXL_PFCM_LANCTXDATA ( i ) ); + } + DBGC2_HDA ( intelxl, ( sizeof ( line ) * index ), + &line, sizeof ( line ) ); + } +} + +/** + * Program queue context line + * + * @v intelxl Intel device + * @v line Queue context line + * @v index Line number + * @v op Context operation + * @ret rc Return status code + */ +static int intelxl_context_line ( struct intelxl_nic *intelxl, + struct intelxl_context_line *line, + unsigned int index, uint32_t op ) { + uint32_t pfcm_lanctxctl; + uint32_t pfcm_lanctxstat; + unsigned int queue; + unsigned int i; + + /* Write context data */ + for ( i = 0; i < ( sizeof ( *line ) / sizeof ( line->raw[0] ) ); i++ ) { + writel ( le32_to_cpu ( line->raw[i] ), + intelxl->regs + INTELXL_PFCM_LANCTXDATA ( i ) ); + } + + /* Start context operation */ + queue = ( intelxl->base + intelxl->queue ); + pfcm_lanctxctl = ( INTELXL_PFCM_LANCTXCTL_QUEUE_NUM ( queue ) | + INTELXL_PFCM_LANCTXCTL_SUB_LINE ( index ) | + INTELXL_PFCM_LANCTXCTL_OP_CODE_WRITE | op ); + writel ( pfcm_lanctxctl, intelxl->regs + INTELXL_PFCM_LANCTXCTL ); + + /* Wait for operation to complete */ + for ( i = 0 ; i < INTELXL_CTX_MAX_WAIT_MS ; i++ ) { + + /* Check if operation is complete */ + pfcm_lanctxstat = readl ( intelxl->regs + + INTELXL_PFCM_LANCTXSTAT ); + if ( pfcm_lanctxstat & INTELXL_PFCM_LANCTXSTAT_DONE ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( intelxl, "INTELXL %p timed out waiting for context: %#08x\n", + intelxl, pfcm_lanctxctl ); + return -ETIMEDOUT; +} + +/** + * Program queue context + * + * @v intelxl Intel device + * @v line Queue context lines + * @v len Size of context + * @v op Context operation + * @ret rc Return status code + */ +static int intelxl_context ( struct intelxl_nic *intelxl, + struct intelxl_context_line *line, + size_t len, uint32_t op ) { + unsigned int index; + int rc; + + DBGC2 ( intelxl, "INTELXL %p context %#08x len %#zx:\n", + intelxl, op, len ); + DBGC2_HDA ( intelxl, 0, line, len ); + + /* Program one line at a time */ + for ( index = 0 ; ( sizeof ( *line ) * index ) < len ; index++ ) { + if ( ( rc = intelxl_context_line ( intelxl, line++, index, + op ) ) != 0 ) + return rc; + } + + return 0; +} + +/** + * Program transmit queue context + * + * @v intelxl Intel device + * @v address Descriptor ring base address + * @ret rc Return status code + */ +static int intelxl_context_tx ( struct intelxl_nic *intelxl, + physaddr_t address ) { + union { + struct intelxl_context_tx tx; + struct intelxl_context_line line; + } ctx; + int rc; + + /* Initialise context */ + memset ( &ctx, 0, sizeof ( ctx ) ); + ctx.tx.flags = cpu_to_le16 ( INTELXL_CTX_TX_FL_NEW ); + ctx.tx.base = cpu_to_le64 ( INTELXL_CTX_TX_BASE ( address ) ); + ctx.tx.count = + cpu_to_le16 ( INTELXL_CTX_TX_COUNT ( INTELXL_TX_NUM_DESC ) ); + ctx.tx.qset = INTELXL_CTX_TX_QSET ( intelxl->qset ); + + /* Program context */ + if ( ( rc = intelxl_context ( intelxl, &ctx.line, sizeof ( ctx ), + INTELXL_PFCM_LANCTXCTL_TYPE_TX ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Program receive queue context + * + * @v intelxl Intel device + * @v address Descriptor ring base address + * @ret rc Return status code + */ +static int intelxl_context_rx ( struct intelxl_nic *intelxl, + physaddr_t address ) { + union { + struct intelxl_context_rx rx; + struct intelxl_context_line line; + } ctx; + uint64_t base_count; + int rc; + + /* Initialise context */ + memset ( &ctx, 0, sizeof ( ctx ) ); + base_count = INTELXL_CTX_RX_BASE_COUNT ( address, INTELXL_RX_NUM_DESC ); + ctx.rx.base_count = cpu_to_le64 ( base_count ); + ctx.rx.len = cpu_to_le16 ( INTELXL_CTX_RX_LEN ( intelxl->mfs ) ); + ctx.rx.flags = INTELXL_CTX_RX_FL_CRCSTRIP; + ctx.rx.mfs = cpu_to_le16 ( INTELXL_CTX_RX_MFS ( intelxl->mfs ) ); + + /* Program context */ + if ( ( rc = intelxl_context ( intelxl, &ctx.line, sizeof ( ctx ), + INTELXL_PFCM_LANCTXCTL_TYPE_RX ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Enable descriptor ring + * + * @v intelxl Intel device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int intelxl_enable_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { + void *ring_regs = ( intelxl->regs + ring->reg ); + uint32_t qxx_ena; + + /* Enable ring */ + writel ( INTELXL_QXX_ENA_REQ, ( ring_regs + INTELXL_QXX_ENA ) ); + udelay ( INTELXL_QUEUE_ENABLE_DELAY_US ); + qxx_ena = readl ( ring_regs + INTELXL_QXX_ENA ); + if ( ! ( qxx_ena & INTELXL_QXX_ENA_STAT ) ) { + DBGC ( intelxl, "INTELXL %p ring %06x failed to enable: " + "%#08x\n", intelxl, ring->reg, qxx_ena ); + return -EIO; + } + + return 0; +} + +/** + * Disable descriptor ring + * + * @v intelxl Intel device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int intelxl_disable_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { + void *ring_regs = ( intelxl->regs + ring->reg ); + uint32_t qxx_ena; + unsigned int i; + + /* Disable ring */ + writel ( 0, ( ring_regs + INTELXL_QXX_ENA ) ); + + /* Wait for ring to be disabled */ + for ( i = 0 ; i < INTELXL_QUEUE_DISABLE_MAX_WAIT_MS ; i++ ) { + + /* Check if ring is disabled */ + qxx_ena = readl ( ring_regs + INTELXL_QXX_ENA ); + if ( ! ( qxx_ena & INTELXL_QXX_ENA_STAT ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( intelxl, "INTELXL %p ring %06x timed out waiting for disable: " + "%#08x\n", intelxl, ring->reg, qxx_ena ); + return -ETIMEDOUT; +} + +/** + * Create descriptor ring + * + * @v intelxl Intel device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int intelxl_create_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { + void *ring_regs = ( intelxl->regs + ring->reg ); + physaddr_t address; + int rc; + + /* Allocate descriptor ring */ + ring->desc = malloc_dma ( ring->len, INTELXL_ALIGN ); + if ( ! ring->desc ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Initialise descriptor ring */ + memset ( ring->desc, 0, ring->len ); + + /* Reset tail pointer */ + writel ( 0, ( ring_regs + INTELXL_QXX_TAIL ) ); + + /* Program queue context */ + address = virt_to_bus ( ring->desc ); + if ( ( rc = ring->context ( intelxl, address ) ) != 0 ) + goto err_context; + + /* Enable ring */ + if ( ( rc = intelxl_enable_ring ( intelxl, ring ) ) != 0 ) + goto err_enable; + + /* Reset counters */ + ring->prod = 0; + ring->cons = 0; + + DBGC ( intelxl, "INTELXL %p ring %06x is at [%08llx,%08llx)\n", + intelxl, ring->reg, ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + ring->len ) ); + + return 0; + + intelxl_disable_ring ( intelxl, ring ); + err_enable: + err_context: + free_dma ( ring->desc, ring->len ); + err_alloc: + return rc; +} + +/** + * Destroy descriptor ring + * + * @v intelxl Intel device + * @v ring Descriptor ring + */ +static void intelxl_destroy_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { + int rc; + + /* Disable ring */ + if ( ( rc = intelxl_disable_ring ( intelxl, ring ) ) != 0 ) { + /* Leak memory; there's nothing else we can do */ + return; + } + + /* Free descriptor ring */ + free_dma ( ring->desc, ring->len ); + ring->desc = NULL; +} + +/** + * Refill receive descriptor ring + * + * @v intelxl Intel device + */ +static void intelxl_refill_rx ( struct intelxl_nic *intelxl ) { + struct intelxl_rx_data_descriptor *rx; + struct io_buffer *iobuf; + unsigned int rx_idx; + unsigned int rx_tail; + physaddr_t address; + unsigned int refilled = 0; + + /* Refill ring */ + while ( ( intelxl->rx.prod - intelxl->rx.cons ) < INTELXL_RX_FILL ) { + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( intelxl->mfs ); + if ( ! iobuf ) { + /* Wait for next refill */ + break; + } + + /* Get next receive descriptor */ + rx_idx = ( intelxl->rx.prod++ % INTELXL_RX_NUM_DESC ); + rx = &intelxl->rx.desc[rx_idx].rx; + + /* Populate receive descriptor */ + address = virt_to_bus ( iobuf->data ); + rx->address = cpu_to_le64 ( address ); + rx->flags = 0; + + /* Record I/O buffer */ + assert ( intelxl->rx_iobuf[rx_idx] == NULL ); + intelxl->rx_iobuf[rx_idx] = iobuf; + + DBGC2 ( intelxl, "INTELXL %p RX %d is [%llx,%llx)\n", intelxl, + rx_idx, ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + intelxl->mfs ) ); + refilled++; + } + + /* Push descriptors to card, if applicable */ + if ( refilled ) { + wmb(); + rx_tail = ( intelxl->rx.prod % INTELXL_RX_NUM_DESC ); + writel ( rx_tail, + ( intelxl->regs + intelxl->rx.reg + INTELXL_QXX_TAIL)); + } +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxl_open ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + union intelxl_receive_address mac; + unsigned int queue; + uint32_t prtgl_sal; + uint32_t prtgl_sah; + int rc; + + /* Calculate maximum frame size */ + intelxl->mfs = ( ( ETH_HLEN + netdev->mtu + 4 /* CRC */ + + INTELXL_ALIGN - 1 ) & ~( INTELXL_ALIGN - 1 ) ); + + /* Program MAC address and maximum frame size */ + memset ( &mac, 0, sizeof ( mac ) ); + memcpy ( mac.raw, netdev->ll_addr, sizeof ( mac.raw ) ); + prtgl_sal = le32_to_cpu ( mac.reg.low ); + prtgl_sah = ( le32_to_cpu ( mac.reg.high ) | + INTELXL_PRTGL_SAH_MFS_SET ( intelxl->mfs ) ); + writel ( prtgl_sal, intelxl->regs + INTELXL_PRTGL_SAL ); + writel ( prtgl_sah, intelxl->regs + INTELXL_PRTGL_SAH ); + + /* Associate transmit queue to PF */ + writel ( ( INTELXL_QXX_CTL_PFVF_Q_PF | + INTELXL_QXX_CTL_PFVF_PF_INDX ( intelxl->pf ) ), + ( intelxl->regs + intelxl->tx.reg + INTELXL_QXX_CTL ) ); + + /* Clear transmit pre queue disable */ + queue = ( intelxl->base + intelxl->queue ); + writel ( ( INTELXL_GLLAN_TXPRE_QDIS_CLEAR_QDIS | + INTELXL_GLLAN_TXPRE_QDIS_QINDX ( queue ) ), + ( intelxl->regs + INTELXL_GLLAN_TXPRE_QDIS ( queue ) ) ); + + /* Reset transmit queue head */ + writel ( 0, ( intelxl->regs + INTELXL_QTX_HEAD ( intelxl->queue ) ) ); + + /* Create receive descriptor ring */ + if ( ( rc = intelxl_create_ring ( intelxl, &intelxl->rx ) ) != 0 ) + goto err_create_rx; + + /* Create transmit descriptor ring */ + if ( ( rc = intelxl_create_ring ( intelxl, &intelxl->tx ) ) != 0 ) + goto err_create_tx; + + /* Fill receive ring */ + intelxl_refill_rx ( intelxl ); + + /* Restart autonegotiation */ + intelxl_admin_autoneg ( intelxl ); + + /* Update link state */ + intelxl_admin_link ( netdev ); + + return 0; + + writel ( ( INTELXL_GLLAN_TXPRE_QDIS_SET_QDIS | + INTELXL_GLLAN_TXPRE_QDIS_QINDX ( queue ) ), + ( intelxl->regs + INTELXL_GLLAN_TXPRE_QDIS ( queue ) ) ); + udelay ( INTELXL_QUEUE_PRE_DISABLE_DELAY_US ); + intelxl_destroy_ring ( intelxl, &intelxl->tx ); + err_create_tx: + intelxl_destroy_ring ( intelxl, &intelxl->rx ); + err_create_rx: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void intelxl_close ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + unsigned int queue; + unsigned int i; + + /* Dump contexts (for debugging) */ + intelxl_context_dump ( intelxl, INTELXL_PFCM_LANCTXCTL_TYPE_TX, + sizeof ( struct intelxl_context_tx ) ); + intelxl_context_dump ( intelxl, INTELXL_PFCM_LANCTXCTL_TYPE_RX, + sizeof ( struct intelxl_context_rx ) ); + + /* Pre-disable transmit queue */ + queue = ( intelxl->base + intelxl->queue ); + writel ( ( INTELXL_GLLAN_TXPRE_QDIS_SET_QDIS | + INTELXL_GLLAN_TXPRE_QDIS_QINDX ( queue ) ), + ( intelxl->regs + INTELXL_GLLAN_TXPRE_QDIS ( queue ) ) ); + udelay ( INTELXL_QUEUE_PRE_DISABLE_DELAY_US ); + + /* Destroy transmit descriptor ring */ + intelxl_destroy_ring ( intelxl, &intelxl->tx ); + + /* Destroy receive descriptor ring */ + intelxl_destroy_ring ( intelxl, &intelxl->rx ); + + /* Discard any unused receive buffers */ + for ( i = 0 ; i < INTELXL_RX_NUM_DESC ; i++ ) { + if ( intelxl->rx_iobuf[i] ) + free_iob ( intelxl->rx_iobuf[i] ); + intelxl->rx_iobuf[i] = NULL; + } +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int intelxl_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_tx_data_descriptor *tx; + unsigned int tx_idx; + unsigned int tx_tail; + physaddr_t address; + size_t len; + + /* Get next transmit descriptor */ + if ( ( intelxl->tx.prod - intelxl->tx.cons ) >= INTELXL_TX_FILL ) { + DBGC ( intelxl, "INTELXL %p out of transmit descriptors\n", + intelxl ); + return -ENOBUFS; + } + tx_idx = ( intelxl->tx.prod++ % INTELXL_TX_NUM_DESC ); + tx_tail = ( intelxl->tx.prod % INTELXL_TX_NUM_DESC ); + tx = &intelxl->tx.desc[tx_idx].tx; + + /* Populate transmit descriptor */ + address = virt_to_bus ( iobuf->data ); + len = iob_len ( iobuf ); + tx->address = cpu_to_le64 ( address ); + tx->len = cpu_to_le32 ( INTELXL_TX_DATA_LEN ( len ) ); + tx->flags = cpu_to_le32 ( INTELXL_TX_DATA_DTYP | INTELXL_TX_DATA_EOP | + INTELXL_TX_DATA_RS | INTELXL_TX_DATA_JFDI ); + wmb(); + + /* Notify card that there are packets ready to transmit */ + writel ( tx_tail, + ( intelxl->regs + intelxl->tx.reg + INTELXL_QXX_TAIL ) ); + + DBGC2 ( intelxl, "INTELXL %p TX %d is [%llx,%llx)\n", intelxl, tx_idx, + ( ( unsigned long long ) address ), + ( ( unsigned long long ) address + len ) ); + return 0; +} + +/** + * Poll for completed packets + * + * @v netdev Network device + */ +static void intelxl_poll_tx ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_tx_writeback_descriptor *tx_wb; + unsigned int tx_idx; + + /* Check for completed packets */ + while ( intelxl->tx.cons != intelxl->tx.prod ) { + + /* Get next transmit descriptor */ + tx_idx = ( intelxl->tx.cons % INTELXL_TX_NUM_DESC ); + tx_wb = &intelxl->tx.desc[tx_idx].tx_wb; + + /* Stop if descriptor is still in use */ + if ( ! ( tx_wb->flags & INTELXL_TX_WB_FL_DD ) ) + return; + DBGC2 ( intelxl, "INTELXL %p TX %d complete\n", + intelxl, tx_idx ); + + /* Complete TX descriptor */ + netdev_tx_complete_next ( netdev ); + intelxl->tx.cons++; + } +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void intelxl_poll_rx ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_rx_writeback_descriptor *rx_wb; + struct io_buffer *iobuf; + unsigned int rx_idx; + size_t len; + + /* Check for received packets */ + while ( intelxl->rx.cons != intelxl->rx.prod ) { + + /* Get next receive descriptor */ + rx_idx = ( intelxl->rx.cons % INTELXL_RX_NUM_DESC ); + rx_wb = &intelxl->rx.desc[rx_idx].rx_wb; + + /* Stop if descriptor is still in use */ + if ( ! ( rx_wb->flags & cpu_to_le32 ( INTELXL_RX_WB_FL_DD ) ) ) + return; + + /* Populate I/O buffer */ + iobuf = intelxl->rx_iobuf[rx_idx]; + intelxl->rx_iobuf[rx_idx] = NULL; + len = INTELXL_RX_WB_LEN ( le32_to_cpu ( rx_wb->len ) ); + iob_put ( iobuf, len ); + + /* Hand off to network stack */ + if ( rx_wb->flags & cpu_to_le32 ( INTELXL_RX_WB_FL_RXE ) ) { + DBGC ( intelxl, "INTELXL %p RX %d error (length %zd, " + "flags %08x)\n", intelxl, rx_idx, len, + le32_to_cpu ( rx_wb->flags ) ); + netdev_rx_err ( netdev, iobuf, -EIO ); + } else { + DBGC2 ( intelxl, "INTELXL %p RX %d complete (length " + "%zd)\n", intelxl, rx_idx, len ); + netdev_rx ( netdev, iobuf ); + } + intelxl->rx.cons++; + } +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void intelxl_poll ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + + /* Acknowledge interrupts, if applicable */ + if ( netdev_irq_enabled ( netdev ) ) { + writel ( ( INTELXL_PFINT_DYN_CTL0_CLEARPBA | + INTELXL_PFINT_DYN_CTL0_INTENA_MASK ), + intelxl->regs + INTELXL_PFINT_DYN_CTL0 ); + } + + /* Poll for completed packets */ + intelxl_poll_tx ( netdev ); + + /* Poll for received packets */ + intelxl_poll_rx ( netdev ); + + /* Poll for admin events */ + intelxl_poll_admin ( netdev ); + + /* Refill RX ring */ + intelxl_refill_rx ( intelxl ); +} + +/** + * Enable or disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void intelxl_irq ( struct net_device *netdev, int enable ) { + struct intelxl_nic *intelxl = netdev->priv; + + if ( enable ) { + writel ( INTELXL_PFINT_DYN_CTL0_INTENA, + intelxl->regs + INTELXL_PFINT_DYN_CTL0 ); + } else { + writel ( 0, intelxl->regs + INTELXL_PFINT_DYN_CTL0 ); + } +} + +/** Network device operations */ +static struct net_device_operations intelxl_operations = { + .open = intelxl_open, + .close = intelxl_close, + .transmit = intelxl_transmit, + .poll = intelxl_poll, + .irq = intelxl_irq, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int intelxl_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct intelxl_nic *intelxl; + uint32_t pfgen_portnum; + uint32_t pflan_qalloc; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *intelxl ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &intelxl_operations ); + intelxl = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( intelxl, 0, sizeof ( *intelxl ) ); + intelxl->pf = PCI_FUNC ( pci->busdevfn ); + intelxl_init_admin ( &intelxl->command, INTELXL_ADMIN_CMD ); + intelxl_init_admin ( &intelxl->event, INTELXL_ADMIN_EVT ); + intelxl_init_ring ( &intelxl->tx, INTELXL_TX_NUM_DESC, + intelxl_context_tx ); + intelxl_init_ring ( &intelxl->rx, INTELXL_RX_NUM_DESC, + intelxl_context_rx ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + intelxl->regs = ioremap ( pci->membase, INTELXL_BAR_SIZE ); + if ( ! intelxl->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Reset the NIC */ + if ( ( rc = intelxl_reset ( intelxl ) ) != 0 ) + goto err_reset; + + /* Get port number and base queue number */ + pfgen_portnum = readl ( intelxl->regs + INTELXL_PFGEN_PORTNUM ); + intelxl->port = INTELXL_PFGEN_PORTNUM_PORT_NUM ( pfgen_portnum ); + pflan_qalloc = readl ( intelxl->regs + INTELXL_PFLAN_QALLOC ); + intelxl->base = INTELXL_PFLAN_QALLOC_FIRSTQ ( pflan_qalloc ); + DBGC ( intelxl, "INTELXL %p PF %d using port %d queues [%#04x-%#04x]\n", + intelxl, intelxl->pf, intelxl->port, intelxl->base, + INTELXL_PFLAN_QALLOC_LASTQ ( pflan_qalloc ) ); + + /* Fetch MAC address and maximum frame size */ + if ( ( rc = intelxl_fetch_mac ( intelxl, netdev ) ) != 0 ) + goto err_fetch_mac; + + /* Open admin queues */ + if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 ) + goto err_open_admin; + + /* Get switch configuration */ + if ( ( rc = intelxl_admin_switch ( intelxl ) ) != 0 ) + goto err_admin_switch; + + /* Get VSI configuration */ + if ( ( rc = intelxl_admin_vsi ( intelxl ) ) != 0 ) + goto err_admin_vsi; + + /* Configure switch for promiscuous mode */ + if ( ( rc = intelxl_admin_promisc ( intelxl ) ) != 0 ) + goto err_admin_promisc; + + /* Configure queue register addresses */ + intelxl->tx.reg = INTELXL_QTX ( intelxl->queue ); + intelxl->rx.reg = INTELXL_QRX ( intelxl->queue ); + + /* Configure interrupt causes */ + writel ( ( INTELXL_QINT_TQCTL_NEXTQ_INDX_NONE | + INTELXL_QINT_TQCTL_CAUSE_ENA ), + intelxl->regs + INTELXL_QINT_TQCTL ( intelxl->queue ) ); + writel ( ( INTELXL_QINT_RQCTL_NEXTQ_INDX ( intelxl->queue ) | + INTELXL_QINT_RQCTL_NEXTQ_TYPE_TX | + INTELXL_QINT_RQCTL_CAUSE_ENA ), + intelxl->regs + INTELXL_QINT_RQCTL ( intelxl->queue ) ); + writel ( ( INTELXL_PFINT_LNKLST0_FIRSTQ_INDX ( intelxl->queue ) | + INTELXL_PFINT_LNKLST0_FIRSTQ_TYPE_RX ), + intelxl->regs + INTELXL_PFINT_LNKLST0 ); + writel ( INTELXL_PFINT_ICR0_ENA_ADMINQ, + intelxl->regs + INTELXL_PFINT_ICR0_ENA ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Set initial link state */ + intelxl_admin_link ( netdev ); + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + err_admin_promisc: + err_admin_vsi: + err_admin_switch: + intelxl_close_admin ( intelxl ); + err_open_admin: + err_fetch_mac: + intelxl_reset ( intelxl ); + err_reset: + iounmap ( intelxl->regs ); + err_ioremap: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void intelxl_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct intelxl_nic *intelxl = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Close admin queues */ + intelxl_close_admin ( intelxl ); + + /* Reset the NIC */ + intelxl_reset ( intelxl ); + + /* Free network device */ + iounmap ( intelxl->regs ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** PCI device IDs */ +static struct pci_device_id intelxl_nics[] = { + PCI_ROM ( 0x8086, 0x1572, "x710-sfp", "X710 10GbE SFP+", 0 ), + PCI_ROM ( 0x8086, 0x1574, "xl710-qemu", "Virtual XL710", 0 ), + PCI_ROM ( 0x8086, 0x1580, "xl710-kx-b", "XL710 40GbE backplane", 0 ), + PCI_ROM ( 0x8086, 0x1581, "xl710-kx-c", "XL710 10GbE backplane", 0 ), + PCI_ROM ( 0x8086, 0x1583, "xl710-qda2", "XL710 40GbE QSFP+", 0 ), + PCI_ROM ( 0x8086, 0x1584, "xl710-qda1", "XL710 40GbE QSFP+", 0 ), + PCI_ROM ( 0x8086, 0x1585, "x710-qsfp", "X710 10GbE QSFP+", 0 ), + PCI_ROM ( 0x8086, 0x1586, "x710-10gt", "X710 10GBASE-T", 0 ), + PCI_ROM ( 0x8086, 0x1587, "x710-kr2", "XL710 20GbE backplane", 0 ), + PCI_ROM ( 0x8086, 0x1588, "x710-kr2-a", "XL710 20GbE backplane", 0 ), + PCI_ROM ( 0x8086, 0x1589, "x710-10gt4", "X710 10GBASE-T4", 0 ), + PCI_ROM ( 0x8086, 0x158a, "xxv710", "XXV710 25GbE backplane", 0 ), + PCI_ROM ( 0x8086, 0x158b, "xxv710-sfp28", "XXV710 25GbE SFP28", 0 ), + PCI_ROM ( 0x8086, 0x37ce, "x722-kx", "X722 10GbE backplane", 0 ), + PCI_ROM ( 0x8086, 0x37cf, "x722-qsfp", "X722 10GbE QSFP+", 0 ), + PCI_ROM ( 0x8086, 0x37d0, "x722-sfp", "X722 10GbE SFP+", 0 ), + PCI_ROM ( 0x8086, 0x37d1, "x722-1gt", "X722 1GBASE-T", 0 ), + PCI_ROM ( 0x8086, 0x37d2, "x722-10gt", "X722 10GBASE-T", 0 ), + PCI_ROM ( 0x8086, 0x37d3, "x722-sfp-i", "X722 10GbE SFP+", 0 ), +}; + +/** PCI driver */ +struct pci_driver intelxl_driver __pci_driver = { + .ids = intelxl_nics, + .id_count = ( sizeof ( intelxl_nics ) / sizeof ( intelxl_nics[0] ) ), + .probe = intelxl_probe, + .remove = intelxl_remove, +}; diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h new file mode 100644 index 000000000..02d9b98a2 --- /dev/null +++ b/src/drivers/net/intelxl.h @@ -0,0 +1,790 @@ +#ifndef _INTELX_H +#define _INTELX_H + +/** @file + * + * Intel 40 Gigabit Ethernet network card driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +struct intelxl_nic; + +/** BAR size */ +#define INTELXL_BAR_SIZE 0x200000 + +/** Alignment + * + * No data structure requires greater than 128 byte alignment. + */ +#define INTELXL_ALIGN 128 + +/****************************************************************************** + * + * Admin queue + * + ****************************************************************************** + */ + +/** PF Admin Command Queue register block */ +#define INTELXL_ADMIN_CMD 0x080000 + +/** PF Admin Event Queue register block */ +#define INTELXL_ADMIN_EVT 0x080080 + +/** Admin Queue Base Address Low Register (offset) */ +#define INTELXL_ADMIN_BAL 0x000 + +/** Admin Queue Base Address High Register (offset) */ +#define INTELXL_ADMIN_BAH 0x100 + +/** Admin Queue Length Register (offset) */ +#define INTELXL_ADMIN_LEN 0x200 +#define INTELXL_ADMIN_LEN_LEN(x) ( (x) << 0 ) /**< Queue length */ +#define INTELXL_ADMIN_LEN_ENABLE 0x80000000UL /**< Queue enable */ + +/** Admin Queue Head Register (offset) */ +#define INTELXL_ADMIN_HEAD 0x300 + +/** Admin Queue Tail Register (offset) */ +#define INTELXL_ADMIN_TAIL 0x400 + +/** Admin queue data buffer command parameters */ +struct intelxl_admin_buffer_params { + /** Reserved */ + uint8_t reserved[8]; + /** Buffer address high */ + uint32_t high; + /** Buffer address low */ + uint32_t low; +} __attribute__ (( packed )); + +/** Admin queue Get Version command */ +#define INTELXL_ADMIN_VERSION 0x0001 + +/** Admin queue version number */ +struct intelxl_admin_version { + /** Major version number */ + uint16_t major; + /** Minor version number */ + uint16_t minor; +} __attribute__ (( packed )); + +/** Admin queue Get Version command parameters */ +struct intelxl_admin_version_params { + /** ROM version */ + uint32_t rom; + /** Firmware build ID */ + uint32_t build; + /** Firmware version */ + struct intelxl_admin_version firmware; + /** API version */ + struct intelxl_admin_version api; +} __attribute__ (( packed )); + +/** Admin queue Driver Version command */ +#define INTELXL_ADMIN_DRIVER 0x0002 + +/** Admin queue Driver Version command parameters */ +struct intelxl_admin_driver_params { + /** Driver version */ + uint8_t major; + /** Minor version */ + uint8_t minor; + /** Build version */ + uint8_t build; + /** Sub-build version */ + uint8_t sub; + /** Reserved */ + uint8_t reserved[4]; + /** Data buffer address */ + uint64_t address; +} __attribute__ (( packed )); + +/** Admin queue Driver Version data buffer */ +struct intelxl_admin_driver_buffer { + /** Driver name */ + char name[32]; +} __attribute__ (( packed )); + +/** Admin queue Shutdown command */ +#define INTELXL_ADMIN_SHUTDOWN 0x0003 + +/** Admin queue Shutdown command parameters */ +struct intelxl_admin_shutdown_params { + /** Driver unloading */ + uint8_t unloading; + /** Reserved */ + uint8_t reserved[15]; +} __attribute__ (( packed )); + +/** Driver is unloading */ +#define INTELXL_ADMIN_SHUTDOWN_UNLOADING 0x01 + +/** Admin queue Get Switch Configuration command */ +#define INTELXL_ADMIN_SWITCH 0x0200 + +/** Switching element configuration */ +struct intelxl_admin_switch_config { + /** Switching element type */ + uint8_t type; + /** Revision */ + uint8_t revision; + /** Switching element ID */ + uint16_t seid; + /** Uplink switching element ID */ + uint16_t uplink; + /** Downlink switching element ID */ + uint16_t downlink; + /** Reserved */ + uint8_t reserved_b[3]; + /** Connection type */ + uint8_t connection; + /** Reserved */ + uint8_t reserved_c[2]; + /** Element specific information */ + uint16_t info; +} __attribute__ (( packed )); + +/** Virtual Station Inferface element type */ +#define INTELXL_ADMIN_SWITCH_TYPE_VSI 19 + +/** Admin queue Get Switch Configuration command parameters */ +struct intelxl_admin_switch_params { + /** Starting switching element identifier */ + uint16_t next; + /** Reserved */ + uint8_t reserved[6]; + /** Data buffer address */ + uint64_t address; +} __attribute__ (( packed )); + +/** Admin queue Get Switch Configuration data buffer */ +struct intelxl_admin_switch_buffer { + /** Number of switching elements reported */ + uint16_t count; + /** Total number of switching elements */ + uint16_t total; + /** Reserved */ + uint8_t reserved_a[12]; + /** Switch configuration */ + struct intelxl_admin_switch_config cfg; +} __attribute__ (( packed )); + +/** Admin queue Get VSI Parameters command */ +#define INTELXL_ADMIN_VSI 0x0212 + +/** Admin queue Get VSI Parameters command parameters */ +struct intelxl_admin_vsi_params { + /** VSI switching element ID */ + uint16_t vsi; + /** Reserved */ + uint8_t reserved[6]; + /** Data buffer address */ + uint64_t address; +} __attribute__ (( packed )); + +/** Admin queue Get VSI Parameters data buffer */ +struct intelxl_admin_vsi_buffer { + /** Reserved */ + uint8_t reserved_a[30]; + /** Queue numbers */ + uint16_t queue[16]; + /** Reserved */ + uint8_t reserved_b[34]; + /** Queue set handles for each traffic class */ + uint16_t qset[8]; + /** Reserved */ + uint8_t reserved_c[16]; +} __attribute__ (( packed )); + +/** Admin queue Set VSI Promiscuous Modes command */ +#define INTELXL_ADMIN_PROMISC 0x0254 + +/** Admin queue Set VSI Promiscuous Modes command parameters */ +struct intelxl_admin_promisc_params { + /** Flags */ + uint16_t flags; + /** Valid flags */ + uint16_t valid; + /** VSI switching element ID */ + uint16_t vsi; + /** Reserved */ + uint8_t reserved[10]; +} __attribute__ (( packed )); + +/** Promiscuous unicast mode */ +#define INTELXL_ADMIN_PROMISC_FL_UNICAST 0x0001 + +/** Promiscuous multicast mode */ +#define INTELXL_ADMIN_PROMISC_FL_MULTICAST 0x0002 + +/** Promiscuous broadcast mode */ +#define INTELXL_ADMIN_PROMISC_FL_BROADCAST 0x0004 + +/** Promiscuous VLAN mode */ +#define INTELXL_ADMIN_PROMISC_FL_VLAN 0x0010 + +/** Admin queue Restart Autonegotiation command */ +#define INTELXL_ADMIN_AUTONEG 0x0605 + +/** Admin queue Restart Autonegotiation command parameters */ +struct intelxl_admin_autoneg_params { + /** Flags */ + uint8_t flags; + /** Reserved */ + uint8_t reserved[15]; +} __attribute__ (( packed )); + +/** Restart autonegotiation */ +#define INTELXL_ADMIN_AUTONEG_FL_RESTART 0x02 + +/** Enable link */ +#define INTELXL_ADMIN_AUTONEG_FL_ENABLE 0x04 + +/** Admin queue Get Link Status command */ +#define INTELXL_ADMIN_LINK 0x0607 + +/** Admin queue Get Link Status command parameters */ +struct intelxl_admin_link_params { + /** Link status notification */ + uint8_t notify; + /** Reserved */ + uint8_t reserved_a; + /** PHY type */ + uint8_t phy; + /** Link speed */ + uint8_t speed; + /** Link status */ + uint8_t status; + /** Reserved */ + uint8_t reserved_b[11]; +} __attribute__ (( packed )); + +/** Notify driver of link status changes */ +#define INTELXL_ADMIN_LINK_NOTIFY 0x03 + +/** Link is up */ +#define INTELXL_ADMIN_LINK_UP 0x01 + +/** Admin queue command parameters */ +union intelxl_admin_params { + /** Additional data buffer command parameters */ + struct intelxl_admin_buffer_params buffer; + /** Get Version command parameters */ + struct intelxl_admin_version_params version; + /** Driver Version command parameters */ + struct intelxl_admin_driver_params driver; + /** Shutdown command parameters */ + struct intelxl_admin_shutdown_params shutdown; + /** Get Switch Configuration command parameters */ + struct intelxl_admin_switch_params sw; + /** Get VSI Parameters command parameters */ + struct intelxl_admin_vsi_params vsi; + /** Set VSI Promiscuous Modes command parameters */ + struct intelxl_admin_promisc_params promisc; + /** Restart Autonegotiation command parameters */ + struct intelxl_admin_autoneg_params autoneg; + /** Get Link Status command parameters */ + struct intelxl_admin_link_params link; +} __attribute__ (( packed )); + +/** Admin queue data buffer */ +union intelxl_admin_buffer { + /** Driver Version data buffer */ + struct intelxl_admin_driver_buffer driver; + /** Get Switch Configuration data buffer */ + struct intelxl_admin_switch_buffer sw; + /** Get VSI Parameters data buffer */ + struct intelxl_admin_vsi_buffer vsi; +} __attribute__ (( packed )); + +/** Admin queue descriptor */ +struct intelxl_admin_descriptor { + /** Flags */ + uint16_t flags; + /** Opcode */ + uint16_t opcode; + /** Data length */ + uint16_t len; + /** Return value */ + uint16_t ret; + /** Cookie */ + uint32_t cookie; + /** Reserved */ + uint32_t reserved; + /** Parameters */ + union intelxl_admin_params params; +} __attribute__ (( packed )); + +/** Admin descriptor done */ +#define INTELXL_ADMIN_FL_DD 0x0001 + +/** Admin descriptor contains a completion */ +#define INTELXL_ADMIN_FL_CMP 0x0002 + +/** Admin descriptor completed in error */ +#define INTELXL_ADMIN_FL_ERR 0x0004 + +/** Admin descriptor uses data buffer for command parameters */ +#define INTELXL_ADMIN_FL_RD 0x0400 + +/** Admin descriptor uses data buffer */ +#define INTELXL_ADMIN_FL_BUF 0x1000 + +/** Admin queue */ +struct intelxl_admin { + /** Descriptors */ + struct intelxl_admin_descriptor *desc; + /** Queue index */ + unsigned int index; + + /** Register block */ + unsigned int reg; + /** Data buffer */ + union intelxl_admin_buffer *buffer; +}; + +/** + * Initialise admin queue + * + * @v admin Admin queue + * @v reg Register block + */ +static inline __attribute__ (( always_inline )) void +intelxl_init_admin ( struct intelxl_admin *admin, unsigned int reg ) { + + admin->reg = reg; +} + +/** Number of admin queue descriptors */ +#define INTELXL_ADMIN_NUM_DESC 4 + +/** Maximum time to wait for an admin request to complete */ +#define INTELXL_ADMIN_MAX_WAIT_MS 100 + +/** Admin queue API major version */ +#define INTELXL_ADMIN_API_MAJOR 1 + +/****************************************************************************** + * + * Transmit and receive queue context + * + ****************************************************************************** + */ + +/** CMLAN Context Data Register */ +#define INTELXL_PFCM_LANCTXDATA(x) ( 0x10c100 + ( 0x80 * (x) ) ) + +/** CMLAN Context Control Register */ +#define INTELXL_PFCM_LANCTXCTL 0x10c300 +#define INTELXL_PFCM_LANCTXCTL_QUEUE_NUM(x) \ + ( (x) << 0 ) /**< Queue number */ +#define INTELXL_PFCM_LANCTXCTL_SUB_LINE(x) \ + ( (x) << 12 ) /**< Sub-line */ +#define INTELXL_PFCM_LANCTXCTL_TYPE(x) \ + ( (x) << 15 ) /**< Queue type */ +#define INTELXL_PFCM_LANCTXCTL_TYPE_RX \ + INTELXL_PFCM_LANCTXCTL_TYPE ( 0x0 ) /**< RX queue type */ +#define INTELXL_PFCM_LANCTXCTL_TYPE_TX \ + INTELXL_PFCM_LANCTXCTL_TYPE ( 0x1 ) /**< TX queue type */ +#define INTELXL_PFCM_LANCTXCTL_OP_CODE(x) \ + ( (x) << 17 ) /**< Op code */ +#define INTELXL_PFCM_LANCTXCTL_OP_CODE_READ \ + INTELXL_PFCM_LANCTXCTL_OP_CODE ( 0x0 ) /**< Read context */ +#define INTELXL_PFCM_LANCTXCTL_OP_CODE_WRITE \ + INTELXL_PFCM_LANCTXCTL_OP_CODE ( 0x1 ) /**< Write context */ + +/** CMLAN Context Status Register */ +#define INTELXL_PFCM_LANCTXSTAT 0x10c380 +#define INTELXL_PFCM_LANCTXSTAT_DONE 0x00000001UL /**< Complete */ + +/** Queue context line */ +struct intelxl_context_line { + /** Raw data */ + uint32_t raw[4]; +} __attribute__ (( packed )); + +/** Transmit queue context */ +struct intelxl_context_tx { + /** Head pointer */ + uint16_t head; + /** Flags */ + uint16_t flags; + /** Base address */ + uint64_t base; + /** Reserved */ + uint8_t reserved_a[8]; + /** Queue count */ + uint16_t count; + /** Reserved */ + uint8_t reserved_b[100]; + /** Queue set */ + uint16_t qset; + /** Reserved */ + uint8_t reserved_c[4]; +} __attribute__ (( packed )); + +/** New transmit queue context */ +#define INTELXL_CTX_TX_FL_NEW 0x4000 + +/** Transmit queue base address */ +#define INTELXL_CTX_TX_BASE( base ) ( (base) >> 7 ) + +/** Transmit queue count */ +#define INTELXL_CTX_TX_COUNT( count ) ( (count) << 1 ) + +/** Transmit queue set */ +#define INTELXL_CTX_TX_QSET( qset) ( (qset) << 4 ) + +/** Receive queue context */ +struct intelxl_context_rx { + /** Head pointer */ + uint16_t head; + /** Reserved */ + uint8_t reserved_a[2]; + /** Base address and queue count */ + uint64_t base_count; + /** Data buffer length */ + uint16_t len; + /** Flags */ + uint8_t flags; + /** Reserved */ + uint8_t reserved_b[7]; + /** Maximum frame size */ + uint16_t mfs; +} __attribute__ (( packed )); + +/** Receive queue base address and queue count */ +#define INTELXL_CTX_RX_BASE_COUNT( base, count ) \ + ( ( (base) >> 7 ) | ( ( ( uint64_t ) (count) ) << 57 ) ) + +/** Receive queue data buffer length */ +#define INTELXL_CTX_RX_LEN( len ) ( (len) >> 1 ) + +/** Strip CRC from received packets */ +#define INTELXL_CTX_RX_FL_CRCSTRIP 0x20 + +/** Receive queue maximum frame size */ +#define INTELXL_CTX_RX_MFS( mfs ) ( (mfs) >> 2 ) + +/** Maximum time to wait for a context operation to complete */ +#define INTELXL_CTX_MAX_WAIT_MS 100 + +/** Time to wait for a queue to become enabled */ +#define INTELXL_QUEUE_ENABLE_DELAY_US 20 + +/** Time to wait for a transmit queue to become pre-disabled */ +#define INTELXL_QUEUE_PRE_DISABLE_DELAY_US 400 + +/** Maximum time to wait for a queue to become disabled */ +#define INTELXL_QUEUE_DISABLE_MAX_WAIT_MS 1000 + +/****************************************************************************** + * + * Transmit and receive descriptors + * + ****************************************************************************** + */ + +/** Global Transmit Queue Head register */ +#define INTELXL_QTX_HEAD(x) ( 0x0e4000 + ( 0x4 * (x) ) ) + +/** Global Transmit Pre Queue Disable register */ +#define INTELXL_GLLAN_TXPRE_QDIS(x) ( 0x0e6500 + ( 0x4 * ( (x) / 0x80 ) ) ) +#define INTELXL_GLLAN_TXPRE_QDIS_QINDX(x) \ + ( (x) << 0 ) /**< Queue index */ +#define INTELXL_GLLAN_TXPRE_QDIS_SET_QDIS \ + 0x40000000UL /**< Set disable */ +#define INTELXL_GLLAN_TXPRE_QDIS_CLEAR_QDIS \ + 0x80000000UL /**< Clear disable */ + +/** Global Transmit Queue register block */ +#define INTELXL_QTX(x) ( 0x100000 + ( 0x4 * (x) ) ) + +/** Global Receive Queue register block */ +#define INTELXL_QRX(x) ( 0x120000 + ( 0x4 * (x) ) ) + +/** Queue Enable Register (offset) */ +#define INTELXL_QXX_ENA 0x0000 +#define INTELXL_QXX_ENA_REQ 0x00000001UL /**< Enable request */ +#define INTELXL_QXX_ENA_STAT 0x00000004UL /**< Enabled status */ + +/** Queue Control Register (offset) */ +#define INTELXL_QXX_CTL 0x4000 +#define INTELXL_QXX_CTL_PFVF_Q(x) ( (x) << 0 ) /**< PF/VF queue */ +#define INTELXL_QXX_CTL_PFVF_Q_PF \ + INTELXL_QXX_CTL_PFVF_Q ( 0x2 ) /**< PF queue */ +#define INTELXL_QXX_CTL_PFVF_PF_INDX(x) ( (x) << 2 ) /**< PF index */ + +/** Queue Tail Pointer Register (offset) */ +#define INTELXL_QXX_TAIL 0x8000 + +/** Transmit data descriptor */ +struct intelxl_tx_data_descriptor { + /** Buffer address */ + uint64_t address; + /** Flags */ + uint32_t flags; + /** Length */ + uint32_t len; +} __attribute__ (( packed )); + +/** Transmit data descriptor type */ +#define INTELXL_TX_DATA_DTYP 0x0 + +/** Transmit data descriptor end of packet */ +#define INTELXL_TX_DATA_EOP 0x10 + +/** Transmit data descriptor report status */ +#define INTELXL_TX_DATA_RS 0x20 + +/** Transmit data descriptor pretty please + * + * This bit is completely missing from older versions of the XL710 + * datasheet. Later versions describe it innocuously as "reserved, + * must be 1". Without this bit, everything will appear to work (up + * to and including the port "transmit good octets" counter), but no + * packet will actually be sent. + */ +#define INTELXL_TX_DATA_JFDI 0x40 + +/** Transmit data descriptor length */ +#define INTELXL_TX_DATA_LEN( len ) ( (len) << 2 ) + +/** Transmit writeback descriptor */ +struct intelxl_tx_writeback_descriptor { + /** Reserved */ + uint8_t reserved_a[8]; + /** Flags */ + uint8_t flags; + /** Reserved */ + uint8_t reserved_b[7]; +} __attribute__ (( packed )); + +/** Transmit writeback descriptor complete */ +#define INTELXL_TX_WB_FL_DD 0x01 + +/** Receive data descriptor */ +struct intelxl_rx_data_descriptor { + /** Buffer address */ + uint64_t address; + /** Flags */ + uint32_t flags; + /** Reserved */ + uint8_t reserved[4]; +} __attribute__ (( packed )); + +/** Receive writeback descriptor */ +struct intelxl_rx_writeback_descriptor { + /** Reserved */ + uint8_t reserved[8]; + /** Flags */ + uint32_t flags; + /** Length */ + uint32_t len; +} __attribute__ (( packed )); + +/** Receive writeback descriptor complete */ +#define INTELXL_RX_WB_FL_DD 0x00000001UL + +/** Receive writeback descriptor error */ +#define INTELXL_RX_WB_FL_RXE 0x00080000UL + +/** Receive writeback descriptor length */ +#define INTELXL_RX_WB_LEN(len) ( ( (len) >> 6 ) & 0x3fff ) + +/** Packet descriptor */ +union intelxl_descriptor { + /** Transmit data descriptor */ + struct intelxl_tx_data_descriptor tx; + /** Transmit writeback descriptor */ + struct intelxl_tx_writeback_descriptor tx_wb; + /** Receive data descriptor */ + struct intelxl_rx_data_descriptor rx; + /** Receive writeback descriptor */ + struct intelxl_rx_writeback_descriptor rx_wb; +}; + +/** Descriptor ring */ +struct intelxl_ring { + /** Descriptors */ + union intelxl_descriptor *desc; + /** Producer index */ + unsigned int prod; + /** Consumer index */ + unsigned int cons; + + /** Register block */ + unsigned int reg; + /** Length (in bytes) */ + size_t len; + /** Program queue context + * + * @v intelxl Intel device + * @v address Descriptor ring base address + */ + int ( * context ) ( struct intelxl_nic *intelxl, physaddr_t address ); +}; + +/** + * Initialise descriptor ring + * + * @v ring Descriptor ring + * @v count Number of descriptors + * @v context Method to program queue context + */ +static inline __attribute__ (( always_inline)) void +intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, + int ( * context ) ( struct intelxl_nic *intelxl, + physaddr_t address ) ) { + + ring->len = ( count * sizeof ( ring->desc[0] ) ); + ring->context = context; +} + +/** Number of transmit descriptors */ +#define INTELXL_TX_NUM_DESC 16 + +/** Transmit descriptor ring maximum fill level */ +#define INTELXL_TX_FILL ( INTELXL_TX_NUM_DESC - 1 ) + +/** Number of receive descriptors + * + * In PXE mode (i.e. able to post single receive descriptors), 8 + * descriptors is the only permitted value covering all possible + * numbers of PFs. + */ +#define INTELXL_RX_NUM_DESC 8 + +/** Receive descriptor ring fill level */ +#define INTELXL_RX_FILL ( INTELXL_RX_NUM_DESC - 1 ) + +/****************************************************************************** + * + * Top level + * + ****************************************************************************** + */ + +/** PF Interrupt Zero Dynamic Control Register */ +#define INTELXL_PFINT_DYN_CTL0 0x038480 +#define INTELXL_PFINT_DYN_CTL0_INTENA 0x00000001UL /**< Enable */ +#define INTELXL_PFINT_DYN_CTL0_CLEARPBA 0x00000002UL /**< Acknowledge */ +#define INTELXL_PFINT_DYN_CTL0_INTENA_MASK 0x80000000UL /**< Ignore enable */ + +/** PF Interrupt Zero Linked List Register */ +#define INTELXL_PFINT_LNKLST0 0x038500 +#define INTELXL_PFINT_LNKLST0_FIRSTQ_INDX(x) \ + ( (x) << 0 ) /**< Queue index */ +#define INTELXL_PFINT_LNKLST0_FIRSTQ_INDX_NONE \ + INTELXL_PFINT_LNKLST0_FIRSTQ_INDX ( 0x7ff ) /**< End of list */ +#define INTELXL_PFINT_LNKLST0_FIRSTQ_TYPE(x) \ + ( (x) << 11 ) /**< Queue type */ +#define INTELXL_PFINT_LNKLST0_FIRSTQ_TYPE_RX \ + INTELXL_PFINT_LNKLST0_FIRSTQ_TYPE ( 0x0 ) /**< Receive queue */ +#define INTELXL_PFINT_LNKLST0_FIRSTQ_TYPE_TX \ + INTELXL_PFINT_LNKLST0_FIRSTQ_TYPE ( 0x1 ) /**< Transmit queue */ + +/** PF Interrupt Zero Cause Enablement Register */ +#define INTELXL_PFINT_ICR0_ENA 0x038800 +#define INTELXL_PFINT_ICR0_ENA_ADMINQ 0x40000000UL /**< Admin event */ + +/** Receive Queue Interrupt Cause Control Register */ +#define INTELXL_QINT_RQCTL(x) ( 0x03a000 + ( 0x4 * (x) ) ) +#define INTELXL_QINT_RQCTL_NEXTQ_INDX(x) ( (x) << 16 ) /**< Queue index */ +#define INTELXL_QINT_RQCTL_NEXTQ_INDX_NONE \ + INTELXL_QINT_RQCTL_NEXTQ_INDX ( 0x7ff ) /**< End of list */ +#define INTELXL_QINT_RQCTL_NEXTQ_TYPE(x) ( (x) << 27 ) /**< Queue type */ +#define INTELXL_QINT_RQCTL_NEXTQ_TYPE_RX \ + INTELXL_QINT_RQCTL_NEXTQ_TYPE ( 0x0 ) /**< Receive queue */ +#define INTELXL_QINT_RQCTL_NEXTQ_TYPE_TX \ + INTELXL_QINT_RQCTL_NEXTQ_TYPE ( 0x1 ) /**< Transmit queue */ +#define INTELXL_QINT_RQCTL_CAUSE_ENA 0x40000000UL /**< Enable */ + +/** Transmit Queue Interrupt Cause Control Register */ +#define INTELXL_QINT_TQCTL(x) ( 0x03c000 + ( 0x4 * (x) ) ) +#define INTELXL_QINT_TQCTL_NEXTQ_INDX(x) ( (x) << 16 ) /**< Queue index */ +#define INTELXL_QINT_TQCTL_NEXTQ_INDX_NONE \ + INTELXL_QINT_TQCTL_NEXTQ_INDX ( 0x7ff ) /**< End of list */ +#define INTELXL_QINT_TQCTL_NEXTQ_TYPE(x) ( (x) << 27 ) /**< Queue type */ +#define INTELXL_QINT_TQCTL_NEXTQ_TYPE_RX \ + INTELXL_QINT_TQCTL_NEXTQ_TYPE ( 0x0 ) /**< Receive queue */ +#define INTELXL_QINT_TQCTL_NEXTQ_TYPE_TX \ + INTELXL_QINT_TQCTL_NEXTQ_TYPE ( 0x1 ) /**< Transmit queue */ +#define INTELXL_QINT_TQCTL_CAUSE_ENA 0x40000000UL /**< Enable */ + +/** PF Control Register */ +#define INTELXL_PFGEN_CTRL 0x092400 +#define INTELXL_PFGEN_CTRL_PFSWR 0x00000001UL /**< Software Reset */ + +/** Time to delay for device reset, in milliseconds */ +#define INTELXL_RESET_DELAY_MS 100 + +/** PF Queue Allocation Register */ +#define INTELXL_PFLAN_QALLOC 0x1c0400 +#define INTELXL_PFLAN_QALLOC_FIRSTQ(x) \ + ( ( (x) >> 0 ) & 0x7ff ) /**< First queue */ +#define INTELXL_PFLAN_QALLOC_LASTQ(x) \ + ( ( (x) >> 16 ) & 0x7ff ) /**< Last queue */ + +/** PF LAN Port Number Register */ +#define INTELXL_PFGEN_PORTNUM 0x1c0480 +#define INTELXL_PFGEN_PORTNUM_PORT_NUM(x) \ + ( ( (x) >> 0 ) & 0x3 ) /**< Port number */ + +/** Port MAC Address Low Register */ +#define INTELXL_PRTGL_SAL 0x1e2120 + +/** Port MAC Address High Register */ +#define INTELXL_PRTGL_SAH 0x1e2140 +#define INTELXL_PRTGL_SAH_MFS_GET(x) ( (x) >> 16 ) /**< Max frame size */ +#define INTELXL_PRTGL_SAH_MFS_SET(x) ( (x) << 16 ) /**< Max frame size */ + +/** Receive address */ +union intelxl_receive_address { + struct { + uint32_t low; + uint32_t high; + } __attribute__ (( packed )) reg; + uint8_t raw[ETH_ALEN]; +}; + +/** An Intel 40Gigabit network card */ +struct intelxl_nic { + /** Registers */ + void *regs; + /** Maximum frame size */ + size_t mfs; + + /** Physical function number */ + unsigned int pf; + /** Absolute queue number base */ + unsigned int base; + /** Port number */ + unsigned int port; + /** Queue number */ + unsigned int queue; + /** Virtual Station Interface switching element ID */ + unsigned int vsi; + /** Queue set handle */ + unsigned int qset; + + /** Admin command queue */ + struct intelxl_admin command; + /** Admin event queue */ + struct intelxl_admin event; + + /** Transmit descriptor ring */ + struct intelxl_ring tx; + /** Receive descriptor ring */ + struct intelxl_ring rx; + /** Receive I/O buffers */ + struct io_buffer *rx_iobuf[INTELXL_RX_NUM_DESC]; +}; + +#endif /* _INTELXL_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 8e989e42d..596491a1f 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -204,6 +204,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_lan78xx ( ERRFILE_DRIVER | 0x00c80000 ) #define ERRFILE_ena ( ERRFILE_DRIVER | 0x00c90000 ) #define ERRFILE_icplus ( ERRFILE_DRIVER | 0x00ca0000 ) +#define ERRFILE_intelxl ( ERRFILE_DRIVER | 0x00cb0000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) -- cgit v1.2.3-55-g7522 From af1860711a3823202a4ddee0ddfb9814591f506c Mon Sep 17 00:00:00 2001 From: Martin Habets Date: Mon, 18 Jun 2018 11:41:38 +0100 Subject: [sfc] Add support for X25xx adapters The first adapters in this family are X2522-10, X2522-25, X2541 and X2542. These no longer use PCI BAR 0 for I/O, but use that for memory. In other words, BAR 2 on SFN8xxx adapters now becomes BAR 0. Signed-off-by: Michael Brown --- src/drivers/net/sfc/efx_common.c | 12 +++++++++--- src/drivers/net/sfc/sfc_hunt.c | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/drivers/net/sfc/efx_common.c b/src/drivers/net/sfc/efx_common.c index 79a994355..403887707 100644 --- a/src/drivers/net/sfc/efx_common.c +++ b/src/drivers/net/sfc/efx_common.c @@ -72,13 +72,19 @@ void efx_probe(struct net_device *netdev, enum efx_revision revision) struct efx_nic *efx = netdev_priv(netdev); struct pci_device *pci = container_of(netdev->dev, struct pci_device, dev); + unsigned int reg = PCI_BASE_ADDRESS_0; + uint32_t bar_low; efx->netdev = netdev; efx->revision = revision; - /* MMIO bar */ - efx->mmio_start = pci_bar_start(pci, PCI_BASE_ADDRESS_2); - efx->mmio_len = pci_bar_size(pci, PCI_BASE_ADDRESS_2); + /* Find the memory bar to use */ + pci_read_config_dword(pci, reg, &bar_low); + if ((bar_low & PCI_BASE_ADDRESS_IO_MASK) == PCI_BASE_ADDRESS_SPACE_IO) + reg = PCI_BASE_ADDRESS_2; + + efx->mmio_start = pci_bar_start(pci, reg); + efx->mmio_len = pci_bar_size(pci, reg); efx->membase = ioremap(efx->mmio_start, efx->mmio_len); DBGCP(efx, "BAR of %lx bytes at phys %lx mapped at %p\n", diff --git a/src/drivers/net/sfc/sfc_hunt.c b/src/drivers/net/sfc/sfc_hunt.c index 25780ffd8..dd5f7043f 100644 --- a/src/drivers/net/sfc/sfc_hunt.c +++ b/src/drivers/net/sfc/sfc_hunt.c @@ -142,6 +142,7 @@ static void hunt_mcdi_copyin(struct hunt_nic *hunt, hdr_len = sizeof(hdr); memcpy(pdu, &hdr, hdr_len); + assert(inlen <= MCDI_CTL_SDU_LEN_MAX_V2); memcpy(pdu + hdr_len, inbuf, inlen); wmb(); /* Sync the data before ringing the doorbell */ @@ -1314,6 +1315,7 @@ const struct efx_nic_type hunt_nic_type = { static struct pci_device_id hunt_nics[] = { PCI_ROM(0x1924, 0x0a03, "SFC9220", "Solarflare SFN8xxx Adapter", 0), + PCI_ROM(0x1924, 0x0b03, "SFC9250", "Solarflare X25xx Adapter", 0), }; struct pci_driver hunt_driver __pci_driver = { -- cgit v1.2.3-55-g7522