diff options
| author | Simon Rettberg | 2018-09-13 15:45:29 +0200 |
|---|---|---|
| committer | Simon Rettberg | 2018-09-13 15:45:29 +0200 |
| commit | 53a91d5caf08276d2cbf0de468e1a5e46537454c (patch) | |
| tree | 3f53a6403785de9f2feebf25d1d82b55656d3456 /src | |
| parent | [settings] Add md5 encoding type for doing ${foo:md5} (diff) | |
| parent | [sfc] Add support for X25xx adapters (diff) | |
| download | ipxe-53a91d5caf08276d2cbf0de468e1a5e46537454c.tar.gz ipxe-53a91d5caf08276d2cbf0de468e1a5e46537454c.tar.xz ipxe-53a91d5caf08276d2cbf0de468e1a5e46537454c.zip | |
Merge branch 'master' into openslx
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.housekeeping | 12 | ||||
| -rw-r--r-- | src/drivers/net/ena.c | 1 | ||||
| -rw-r--r-- | src/drivers/net/intelx.c | 1 | ||||
| -rw-r--r-- | src/drivers/net/intelxl.c | 1525 | ||||
| -rw-r--r-- | src/drivers/net/intelxl.h | 790 | ||||
| -rw-r--r-- | src/drivers/net/sfc/efx_common.c | 12 | ||||
| -rw-r--r-- | src/drivers/net/sfc/sfc_hunt.c | 2 | ||||
| -rw-r--r-- | src/drivers/net/virtio-net.c | 1 | ||||
| -rw-r--r-- | src/include/ipxe/errfile.h | 1 | ||||
| -rw-r--r-- | src/interface/efi/efi_snp.c | 2 | ||||
| -rw-r--r-- | src/interface/hyperv/vmbus.c | 10 | ||||
| -rw-r--r-- | src/net/ethernet.c | 1 | ||||
| -rw-r--r-- | src/net/rndis.c | 102 | ||||
| -rw-r--r-- | src/net/tcp/httpconn.c | 9 | ||||
| -rw-r--r-- | src/net/tcp/httpcore.c | 12 |
15 files changed, 2424 insertions, 57 deletions
diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 00b079263..f8334921b 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -174,9 +174,17 @@ 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) -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) 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 # 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/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 ), }; 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 <mbrown@fensystems.co.uk>. + * + * 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 <stdint.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/netdevice.h> +#include <ipxe/ethernet.h> +#include <ipxe/if_ether.h> +#include <ipxe/iobuf.h> +#include <ipxe/malloc.h> +#include <ipxe/pci.h> +#include <ipxe/version.h> +#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 <stdint.h> +#include <ipxe/if_ether.h> + +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/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 = { 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/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 ) 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 ); 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; } 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; } diff --git a/src/net/rndis.c b/src/net/rndis.c index 8c4fe8b30..a3b562bc2 100644 --- a/src/net/rndis.c +++ b/src/net/rndis.c @@ -651,6 +651,63 @@ static int rndis_oid ( struct rndis_device *rndis, unsigned int oid, } /** + * 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 * * @v rndis RNDIS device @@ -970,8 +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; + /* Describe RNDIS device */ + if ( ( rc = rndis_describe ( rndis ) ) != 0 ) + goto err_describe; /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) { @@ -980,49 +1038,11 @@ int register_rndis ( struct rndis_device *rndis ) { 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", - 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: unregister_netdev ( netdev ); err_register: + err_describe: return rc; } 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 ); |
