Files
ipxe/src/drivers/net/intelxlvf.c
T
Michael Brown adcaaf9b93 [build] Mark known reviewed files as permitted for UEFI Secure Boot
Some past security reviews carried out for UEFI Secure Boot signing
submissions have covered specific drivers or functional areas of iPXE.
Mark all of the files comprising these areas as permitted for UEFI
Secure Boot.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-01-14 16:10:29 +00:00

900 lines
25 KiB
C

/*
* Copyright (C) 2019 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 );
FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/pci.h>
#include <ipxe/netdevice.h>
#include <ipxe/ethernet.h>
#include "intelxlvf.h"
/** @file
*
* Intel 40 Gigabit Ethernet virtual function network card driver
*
*/
/******************************************************************************
*
* Device reset
*
******************************************************************************
*/
/**
* Wait for admin event queue to be torn down
*
* @v intelxl Intel device
* @ret rc Return status code
*/
static int intelxlvf_reset_wait_teardown ( struct intelxl_nic *intelxl ) {
uint32_t admin_evt_len;
unsigned int i;
/* Wait for admin event queue to be torn down */
for ( i = 0 ; i < INTELXLVF_RESET_MAX_WAIT_MS ; i++ ) {
/* Check admin event queue length register */
admin_evt_len = readl ( intelxl->regs + INTELXLVF_ADMIN +
INTELXLVF_ADMIN_EVT_LEN );
if ( ! ( admin_evt_len & INTELXL_ADMIN_LEN_ENABLE ) )
return 0;
/* Delay */
mdelay ( 1 );
}
DBGC ( intelxl, "INTELXL %p timed out waiting for teardown (%#08x)\n",
intelxl, admin_evt_len );
return -ETIMEDOUT;
}
/**
* Wait for virtual function to be marked as active
*
* @v intelxl Intel device
* @ret rc Return status code
*/
static int intelxlvf_reset_wait_active ( struct intelxl_nic *intelxl ) {
uint32_t vfgen_rstat;
unsigned int vfr_state;
unsigned int i;
/* Wait for virtual function to be marked as active */
for ( i = 0 ; i < INTELXLVF_RESET_MAX_WAIT_MS ; i++ ) {
/* Check status as written by physical function driver */
vfgen_rstat = readl ( intelxl->regs + INTELXLVF_VFGEN_RSTAT );
vfr_state = INTELXLVF_VFGEN_RSTAT_VFR_STATE ( vfgen_rstat );
if ( vfr_state == INTELXLVF_VFGEN_RSTAT_VFR_STATE_ACTIVE )
return 0;
/* Delay */
mdelay ( 1 );
}
DBGC ( intelxl, "INTELXL %p timed out waiting for activation "
"(%#08x)\n", intelxl, vfgen_rstat );
return -ETIMEDOUT;
}
/**
* Wait for reset to complete
*
* @v intelxl Intel device
* @ret rc Return status code
*/
static int intelxlvf_reset_wait ( struct intelxl_nic *intelxl ) {
int rc;
/* Wait for minimum reset time */
mdelay ( INTELXLVF_RESET_DELAY_MS );
/* Wait for reset to take effect */
if ( ( rc = intelxlvf_reset_wait_teardown ( intelxl ) ) != 0 )
goto err_teardown;
/* Wait for virtual function to become active */
if ( ( rc = intelxlvf_reset_wait_active ( intelxl ) ) != 0 )
goto err_active;
err_active:
err_teardown:
intelxl_reopen_admin ( intelxl );
return rc;
}
/**
* Reset hardware via admin queue
*
* @v intelxl Intel device
* @ret rc Return status code
*/
static int intelxlvf_reset_admin ( struct intelxl_nic *intelxl ) {
struct intelxlvf_admin_descriptor *cmd;
int rc;
/* Populate descriptor */
cmd = intelxlvf_admin_command_descriptor ( intelxl );
cmd->opcode = cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_PF );
cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_RESET );
/* Issue command */
if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
/* Wait for reset to complete */
if ( ( rc = intelxlvf_reset_wait ( intelxl ) ) != 0 )
return rc;
return 0;
}
/******************************************************************************
*
* Admin queue
*
******************************************************************************
*/
/** Admin command queue register offsets */
static const struct intelxl_admin_offsets intelxlvf_admin_command_offsets = {
.bal = INTELXLVF_ADMIN_CMD_BAL,
.bah = INTELXLVF_ADMIN_CMD_BAH,
.len = INTELXLVF_ADMIN_CMD_LEN,
.head = INTELXLVF_ADMIN_CMD_HEAD,
.tail = INTELXLVF_ADMIN_CMD_TAIL,
};
/** Admin event queue register offsets */
static const struct intelxl_admin_offsets intelxlvf_admin_event_offsets = {
.bal = INTELXLVF_ADMIN_EVT_BAL,
.bah = INTELXLVF_ADMIN_EVT_BAH,
.len = INTELXLVF_ADMIN_EVT_LEN,
.head = INTELXLVF_ADMIN_EVT_HEAD,
.tail = INTELXLVF_ADMIN_EVT_TAIL,
};
/**
* Issue admin queue virtual function command
*
* @v netdev Network device
* @ret rc Return status code
*/
static int intelxlvf_admin_command ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
struct intelxl_admin *admin = &intelxl->command;
struct intelxl_admin_descriptor *xlcmd;
struct intelxlvf_admin_descriptor *cmd;
unsigned int i;
int rc;
/* Populate descriptor */
xlcmd = &admin->desc[ admin->index % INTELXL_ADMIN_NUM_DESC ];
cmd = container_of ( xlcmd, struct intelxlvf_admin_descriptor, xl );
cmd->opcode = cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_PF );
/* Record opcode */
intelxl->vopcode = le32_to_cpu ( cmd->vopcode );
/* Issue command */
if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
goto err_command;
/* Wait for response */
for ( i = 0 ; i < INTELXLVF_ADMIN_MAX_WAIT_MS ; i++ ) {
/* Poll admin event queue */
intelxl_poll_admin ( netdev );
/* If response has not arrived, delay 1ms and retry */
if ( intelxl->vopcode ) {
mdelay ( 1 );
continue;
}
/* Check for errors */
if ( cmd->vret != 0 )
return -EIO;
return 0;
}
rc = -ETIMEDOUT;
DBGC ( intelxl, "INTELXL %p timed out waiting for admin VF command "
"%#x\n", intelxl, intelxl->vopcode );
err_command:
intelxl->vopcode = 0;
return rc;
}
/**
* Handle link status event
*
* @v netdev Network device
* @v link Link status
*/
static void intelxlvf_admin_link ( struct net_device *netdev,
struct intelxlvf_admin_status_link *link ) {
struct intelxl_nic *intelxl = netdev->priv;
DBGC ( intelxl, "INTELXL %p link %#02x speed %#02x\n", intelxl,
link->status, link->speed );
/* Update network device */
if ( link->status ) {
netdev_link_up ( netdev );
} else {
netdev_link_down ( netdev );
}
}
/**
* Handle status change event
*
* @v netdev Network device
* @v stat Status change event
*/
static void
intelxlvf_admin_status ( struct net_device *netdev,
struct intelxlvf_admin_status_buffer *stat ) {
struct intelxl_nic *intelxl = netdev->priv;
/* Handle event */
switch ( stat->event ) {
case cpu_to_le32 ( INTELXLVF_ADMIN_STATUS_LINK ):
intelxlvf_admin_link ( netdev, &stat->data.link );
break;
default:
DBGC ( intelxl, "INTELXL %p unrecognised status change "
"event %#x:\n", intelxl, le32_to_cpu ( stat->event ) );
DBGC_HDA ( intelxl, 0, stat, sizeof ( *stat ) );
break;
}
}
/**
* Handle admin event
*
* @v netdev Network device
* @v xlevt Admin queue event descriptor
* @v xlbuf Admin queue event data buffer
*/
static void intelxlvf_admin_event ( struct net_device *netdev,
struct intelxl_admin_descriptor *xlevt,
union intelxl_admin_buffer *xlbuf ) {
struct intelxl_nic *intelxl = netdev->priv;
struct intelxl_admin *admin = &intelxl->command;
struct intelxlvf_admin_descriptor *evt =
container_of ( xlevt, struct intelxlvf_admin_descriptor, xl );
union intelxlvf_admin_buffer *buf =
container_of ( xlbuf, union intelxlvf_admin_buffer, xl );
unsigned int vopcode;
unsigned int index;
/* Ignore unrecognised events */
if ( evt->opcode != cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_VF ) ) {
DBGC ( intelxl, "INTELXL %p unrecognised event opcode "
"%#04x\n", intelxl, le16_to_cpu ( evt->opcode ) );
return;
}
/* Record command response if applicable */
vopcode = le32_to_cpu ( evt->vopcode );
if ( vopcode == intelxl->vopcode ) {
index = ( ( admin->index - 1 ) % INTELXL_ADMIN_NUM_DESC );
memcpy ( &admin->desc[index], evt, sizeof ( *evt ) );
memcpy ( &admin->buf[index], buf, sizeof ( *buf ) );
intelxl->vopcode = 0;
if ( evt->vret != 0 ) {
DBGC ( intelxl, "INTELXL %p admin VF command %#x "
"error %d\n", intelxl, vopcode,
le32_to_cpu ( evt->vret ) );
DBGC_HDA ( intelxl, virt_to_phys ( evt ), evt,
sizeof ( *evt ) );
DBGC_HDA ( intelxl, virt_to_phys ( buf ), buf,
le16_to_cpu ( evt->len ) );
}
return;
}
/* Handle unsolicited events */
switch ( vopcode ) {
case INTELXLVF_ADMIN_STATUS:
intelxlvf_admin_status ( netdev, &buf->stat );
break;
default:
DBGC ( intelxl, "INTELXL %p unrecognised VF event %#x:\n",
intelxl, vopcode );
DBGC_HDA ( intelxl, virt_to_phys ( evt ), evt,
sizeof ( *evt ) );
DBGC_HDA ( intelxl, virt_to_phys ( buf ), buf,
le16_to_cpu ( evt->len ) );
break;
}
}
/**
* Negotiate API version
*
* @v netdev Network device
* @ret rc Return status code
*/
static int intelxlvf_admin_version ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
struct intelxlvf_admin_descriptor *cmd;
union intelxlvf_admin_buffer *buf;
unsigned int api;
int rc;
/* Populate descriptor */
cmd = intelxlvf_admin_command_descriptor ( intelxl );
cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_VERSION );
cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
cmd->len = cpu_to_le16 ( sizeof ( buf->ver ) );
buf = intelxlvf_admin_command_buffer ( intelxl );
buf->ver.major = cpu_to_le32 ( INTELXLVF_ADMIN_API_MAJOR );
buf->ver.minor = cpu_to_le32 ( INTELXLVF_ADMIN_API_MINOR );
/* Issue command */
if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
return rc;
api = le32_to_cpu ( buf->ver.major );
DBGC ( intelxl, "INTELXL %p API v%d.%d\n",
intelxl, api, le32_to_cpu ( buf->ver.minor ) );
/* Check for API compatibility */
if ( api > INTELXLVF_ADMIN_API_MAJOR ) {
DBGC ( intelxl, "INTELXL %p unsupported API v%d\n",
intelxl, api );
return -ENOTSUP;
}
return 0;
}
/**
* Get resources
*
* @v netdev Network device
* @ret rc Return status code
*/
static int intelxlvf_admin_get_resources ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
struct intelxlvf_admin_descriptor *cmd;
union intelxlvf_admin_buffer *buf;
int rc;
/* Populate descriptor */
cmd = intelxlvf_admin_command_descriptor ( intelxl );
cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_GET_RESOURCES );
cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
cmd->len = cpu_to_le16 ( sizeof ( buf->caps ) );
buf = intelxlvf_admin_command_buffer ( intelxl );
buf->caps.caps = cpu_to_le32 ( INTELXLVF_ADMIN_CAP_L2 |
INTELXLVF_ADMIN_CAP_RQPS );
/* Issue command */
if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
return rc;
/* Parse response */
intelxl->caps = le32_to_cpu ( buf->res.caps );
intelxl->vsi = le16_to_cpu ( buf->res.vsi );
memcpy ( netdev->hw_addr, buf->res.mac, ETH_ALEN );
DBGC ( intelxl, "INTELXL %p capabilities %#08x VSI %#04x\n",
intelxl, intelxl->caps, intelxl->vsi );
return 0;
}
/**
* Get statistics (for debugging)
*
* @v netdev Network device
* @ret rc Return status code
*/
static int intelxlvf_admin_stats ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
struct intelxlvf_admin_descriptor *cmd;
union intelxlvf_admin_buffer *buf;
struct intelxlvf_admin_stats *tx;
struct intelxlvf_admin_stats *rx;
int rc;
/* Populate descriptor */
cmd = intelxlvf_admin_command_descriptor ( intelxl );
cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_GET_STATS );
cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
cmd->len = cpu_to_le16 ( sizeof ( buf->queues ) );
buf = intelxlvf_admin_command_buffer ( intelxl );
buf->queues.vsi = cpu_to_le16 ( intelxl->vsi );
tx = &buf->stats.tx;
rx = &buf->stats.rx;
/* Issue command */
if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
return rc;
DBGC ( intelxl, "INTELXL %p TX bytes %#llx discards %#llx errors "
"%#llx\n", intelxl,
( ( unsigned long long ) le64_to_cpu ( tx->bytes ) ),
( ( unsigned long long ) le64_to_cpu ( tx->discards ) ),
( ( unsigned long long ) le64_to_cpu ( tx->errors ) ) );
DBGC ( intelxl, "INTELXL %p TX unicasts %#llx multicasts %#llx "
"broadcasts %#llx\n", intelxl,
( ( unsigned long long ) le64_to_cpu ( tx->unicasts ) ),
( ( unsigned long long ) le64_to_cpu ( tx->multicasts ) ),
( ( unsigned long long ) le64_to_cpu ( tx->broadcasts ) ) );
DBGC ( intelxl, "INTELXL %p RX bytes %#llx discards %#llx errors "
"%#llx\n", intelxl,
( ( unsigned long long ) le64_to_cpu ( rx->bytes ) ),
( ( unsigned long long ) le64_to_cpu ( rx->discards ) ),
( ( unsigned long long ) le64_to_cpu ( rx->errors ) ) );
DBGC ( intelxl, "INTELXL %p RX unicasts %#llx multicasts %#llx "
"broadcasts %#llx\n", intelxl,
( ( unsigned long long ) le64_to_cpu ( rx->unicasts ) ),
( ( unsigned long long ) le64_to_cpu ( rx->multicasts ) ),
( ( unsigned long long ) le64_to_cpu ( rx->broadcasts ) ) );
return 0;
}
/**
* Configure number of queue pairs
*
* @v netdev Network device
* @ret rc Return status code
*/
static int intelxlvf_admin_request_qps ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
struct intelxlvf_admin_descriptor *cmd;
union intelxlvf_admin_buffer *buf;
int rc;
/* Populate descriptor */
cmd = intelxlvf_admin_command_descriptor ( intelxl );
cmd->opcode = cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_PF );
cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_REQUEST_QPS );
cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
cmd->len = cpu_to_le16 ( sizeof ( buf->rqps ) );
buf = intelxlvf_admin_command_buffer ( intelxl );
buf->rqps.count = cpu_to_le16 ( 1 );
/* Issue command (which will trigger a reset) */
if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
/* Wait for reset to complete */
if ( ( rc = intelxlvf_reset_wait ( intelxl ) ) != 0 )
return rc;
/* Reestablish capabilities to reactivate VF after reset */
if ( ( rc = intelxlvf_admin_get_resources ( netdev ) ) != 0 )
return rc;
return 0;
}
/******************************************************************************
*
* Network device interface
*
******************************************************************************
*/
/**
* Configure queues
*
* @v netdev Network device
* @ret rc Return status code
*/
static int intelxlvf_admin_configure ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
struct intelxlvf_admin_descriptor *cmd;
union intelxlvf_admin_buffer *buf;
int rc;
/* Populate descriptor */
cmd = intelxlvf_admin_command_descriptor ( intelxl );
cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_CONFIGURE );
cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
cmd->len = cpu_to_le16 ( sizeof ( buf->cfg ) );
buf = intelxlvf_admin_command_buffer ( intelxl );
buf->cfg.vsi = cpu_to_le16 ( intelxl->vsi );
buf->cfg.count = cpu_to_le16 ( 1 );
buf->cfg.tx.vsi = cpu_to_le16 ( intelxl->vsi );
buf->cfg.tx.count = cpu_to_le16 ( INTELXL_TX_NUM_DESC );
buf->cfg.tx.base = cpu_to_le64 ( dma ( &intelxl->tx.map,
intelxl->tx.desc.raw ) );
buf->cfg.rx.vsi = cpu_to_le16 ( intelxl->vsi );
buf->cfg.rx.count = cpu_to_le32 ( INTELXL_RX_NUM_DESC );
buf->cfg.rx.len = cpu_to_le32 ( intelxl->mfs );
buf->cfg.rx.mfs = cpu_to_le32 ( intelxl->mfs );
buf->cfg.rx.base = cpu_to_le64 ( dma ( &intelxl->rx.map,
intelxl->rx.desc.raw ) );
/* Issue command */
if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
return rc;
return 0;
}
/**
* Configure IRQ mapping
*
* @v netdev Network device
* @ret rc Return status code
*/
static int intelxlvf_admin_irq_map ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
struct intelxlvf_admin_descriptor *cmd;
union intelxlvf_admin_buffer *buf;
int rc;
/* Populate descriptor */
cmd = intelxlvf_admin_command_descriptor ( intelxl );
cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_IRQ_MAP );
cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
cmd->len = cpu_to_le16 ( sizeof ( buf->irq ) );
buf = intelxlvf_admin_command_buffer ( intelxl );
buf->irq.count = cpu_to_le16 ( 1 );
buf->irq.vsi = cpu_to_le16 ( intelxl->vsi );
buf->irq.vec = cpu_to_le16 ( INTELXLVF_MSIX_VECTOR );
buf->irq.rxmap = cpu_to_le16 ( 0x0001 );
buf->irq.txmap = cpu_to_le16 ( 0x0001 );
/* Issue command */
if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
return rc;
return 0;
}
/**
* Enable/disable queues
*
* @v netdev Network device
* @v enable Enable queues
* @ret rc Return status code
*/
static int intelxlvf_admin_queues ( struct net_device *netdev, int enable ) {
struct intelxl_nic *intelxl = netdev->priv;
struct intelxlvf_admin_descriptor *cmd;
union intelxlvf_admin_buffer *buf;
int rc;
/* Populate descriptor */
cmd = intelxlvf_admin_command_descriptor ( intelxl );
cmd->vopcode = ( enable ? cpu_to_le32 ( INTELXLVF_ADMIN_ENABLE ) :
cpu_to_le32 ( INTELXLVF_ADMIN_DISABLE ) );
cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
cmd->len = cpu_to_le16 ( sizeof ( buf->queues ) );
buf = intelxlvf_admin_command_buffer ( intelxl );
buf->queues.vsi = cpu_to_le16 ( intelxl->vsi );
buf->queues.rx = cpu_to_le32 ( 1 );
buf->queues.tx = cpu_to_le32 ( 1 );
/* Issue command */
if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
return rc;
return 0;
}
/**
* Configure promiscuous mode
*
* @v netdev Network device
* @ret rc Return status code
*/
static int intelxlvf_admin_promisc ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
struct intelxlvf_admin_descriptor *cmd;
union intelxlvf_admin_buffer *buf;
int rc;
/* Populate descriptor */
cmd = intelxlvf_admin_command_descriptor ( intelxl );
cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_PROMISC );
cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
cmd->len = cpu_to_le16 ( sizeof ( buf->promisc ) );
buf = intelxlvf_admin_command_buffer ( intelxl );
buf->promisc.vsi = cpu_to_le16 ( intelxl->vsi );
buf->promisc.flags = cpu_to_le16 ( INTELXL_ADMIN_PROMISC_FL_UNICAST |
INTELXL_ADMIN_PROMISC_FL_MULTICAST );
/* Issue command */
if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
return rc;
return 0;
}
/**
* Open network device
*
* @v netdev Network device
* @ret rc Return status code
*/
static int intelxlvf_open ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
int rc;
/* Calculate maximum frame size */
intelxl->mfs = ( ( ETH_HLEN + netdev->mtu + 4 /* CRC */ +
INTELXL_ALIGN - 1 ) & ~( INTELXL_ALIGN - 1 ) );
/* Allocate transmit descriptor ring */
if ( ( rc = intelxl_alloc_ring ( intelxl, &intelxl->tx ) ) != 0 )
goto err_alloc_tx;
/* Allocate receive descriptor ring */
if ( ( rc = intelxl_alloc_ring ( intelxl, &intelxl->rx ) ) != 0 )
goto err_alloc_rx;
/* Configure queues */
if ( ( rc = intelxlvf_admin_configure ( netdev ) ) != 0 )
goto err_configure;
/* Configure IRQ map */
if ( ( rc = intelxlvf_admin_irq_map ( netdev ) ) != 0 )
goto err_irq_map;
/* Enable queues */
if ( ( rc = intelxlvf_admin_queues ( netdev, 1 ) ) != 0 )
goto err_enable;
/* Configure promiscuous mode */
if ( ( rc = intelxlvf_admin_promisc ( netdev ) ) != 0 )
goto err_promisc;
/* Reset statistics counters (if debugging) */
if ( DBG_LOG )
intelxlvf_admin_stats ( netdev );
return 0;
err_promisc:
intelxlvf_admin_queues ( netdev, 0 );
err_enable:
err_irq_map:
err_configure:
intelxl_free_ring ( intelxl, &intelxl->rx );
err_alloc_rx:
intelxl_free_ring ( intelxl, &intelxl->tx );
err_alloc_tx:
return rc;
}
/**
* Close network device
*
* @v netdev Network device
*/
static void intelxlvf_close ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
int rc;
/* Show statistics (if debugging) */
if ( DBG_LOG )
intelxlvf_admin_stats ( netdev );
/* Disable queues */
if ( ( rc = intelxlvf_admin_queues ( netdev, 0 ) ) != 0 ) {
/* Leak memory; there's nothing else we can do */
return;
}
/* Free receive descriptor ring */
intelxl_free_ring ( intelxl, &intelxl->rx );
/* Free transmit descriptor ring */
intelxl_free_ring ( intelxl, &intelxl->tx );
/* Discard any unused receive buffers */
intelxl_empty_rx ( intelxl );
}
/** Network device operations */
static struct net_device_operations intelxlvf_operations = {
.open = intelxlvf_open,
.close = intelxlvf_close,
.transmit = intelxl_transmit,
.poll = intelxl_poll,
};
/******************************************************************************
*
* PCI interface
*
******************************************************************************
*/
/**
* Probe PCI device
*
* @v pci PCI device
* @ret rc Return status code
*/
static int intelxlvf_probe ( struct pci_device *pci ) {
struct net_device *netdev;
struct intelxl_nic *intelxl;
int rc;
/* Allocate and initialise net device */
netdev = alloc_etherdev ( sizeof ( *intelxl ) );
if ( ! netdev ) {
rc = -ENOMEM;
goto err_alloc;
}
netdev_init ( netdev, &intelxlvf_operations );
intelxl = netdev->priv;
pci_set_drvdata ( pci, netdev );
netdev->dev = &pci->dev;
memset ( intelxl, 0, sizeof ( *intelxl ) );
intelxl->intr = INTELXLVF_VFINT_DYN_CTLN ( INTELXLVF_MSIX_VECTOR );
intelxl->handle = intelxlvf_admin_event;
intelxl_init_admin ( &intelxl->command, INTELXLVF_ADMIN,
&intelxlvf_admin_command_offsets );
intelxl_init_admin ( &intelxl->event, INTELXLVF_ADMIN,
&intelxlvf_admin_event_offsets );
intelxlvf_init_ring ( &intelxl->tx, INTELXL_TX_NUM_DESC,
sizeof ( intelxl->tx.desc.tx[0] ),
INTELXLVF_QTX_TAIL );
intelxlvf_init_ring ( &intelxl->rx, INTELXL_RX_NUM_DESC,
sizeof ( intelxl->rx.desc.rx[0] ),
INTELXLVF_QRX_TAIL );
/* Fix up PCI device */
adjust_pci_device ( pci );
/* Map registers */
intelxl->regs = pci_ioremap ( pci, pci->membase, INTELXLVF_BAR_SIZE );
if ( ! intelxl->regs ) {
rc = -ENODEV;
goto err_ioremap;
}
/* Configure DMA */
intelxl->dma = &pci->dma;
dma_set_mask_64bit ( intelxl->dma );
netdev->dma = intelxl->dma;
/* Locate PCI Express capability */
intelxl->exp = pci_find_capability ( pci, PCI_CAP_ID_EXP );
if ( ! intelxl->exp ) {
DBGC ( intelxl, "INTELXL %p missing PCIe capability\n",
intelxl );
rc = -ENXIO;
goto err_exp;
}
/* Reset the function via PCIe FLR */
pci_reset ( pci, intelxl->exp );
/* Enable MSI-X dummy interrupt */
if ( ( rc = intelxl_msix_enable ( intelxl, pci,
INTELXLVF_MSIX_VECTOR ) ) != 0 )
goto err_msix;
/* Open admin queues */
if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 )
goto err_open_admin;
/* Reset the function via admin queue */
if ( ( rc = intelxlvf_reset_admin ( intelxl ) ) != 0 )
goto err_reset_admin;
/* Negotiate API version */
if ( ( rc = intelxlvf_admin_version ( netdev ) ) != 0 )
goto err_version;
/* Get MAC address */
if ( ( rc = intelxlvf_admin_get_resources ( netdev ) ) != 0 )
goto err_get_resources;
/* Configure number of queue pairs, if applicable */
if ( ( intelxl->caps & INTELXLVF_ADMIN_CAP_RQPS ) &&
( ( rc = intelxlvf_admin_request_qps ( netdev ) ) != 0 ) )
goto err_rqps;
/* Register network device */
if ( ( rc = register_netdev ( netdev ) ) != 0 )
goto err_register_netdev;
return 0;
unregister_netdev ( netdev );
err_register_netdev:
err_rqps:
err_get_resources:
err_version:
err_reset_admin:
intelxl_close_admin ( intelxl );
err_open_admin:
intelxl_msix_disable ( intelxl, pci, INTELXLVF_MSIX_VECTOR );
err_msix:
pci_reset ( pci, intelxl->exp );
err_exp:
iounmap ( intelxl->regs );
err_ioremap:
netdev_nullify ( netdev );
netdev_put ( netdev );
err_alloc:
return rc;
}
/**
* Remove PCI device
*
* @v pci PCI device
*/
static void intelxlvf_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 );
/* Reset the function via admin queue */
intelxlvf_reset_admin ( intelxl );
/* Close admin queues */
intelxl_close_admin ( intelxl );
/* Disable MSI-X dummy interrupt */
intelxl_msix_disable ( intelxl, pci, INTELXLVF_MSIX_VECTOR );
/* Reset the function via PCIe FLR */
pci_reset ( pci, intelxl->exp );
/* Free network device */
iounmap ( intelxl->regs );
netdev_nullify ( netdev );
netdev_put ( netdev );
}
/** PCI device IDs */
static struct pci_device_id intelxlvf_nics[] = {
PCI_ROM ( 0x8086, 0x154c, "xl710-vf", "XL710 VF", 0 ),
PCI_ROM ( 0x8086, 0x1571, "xl710-vf-hv", "XL710 VF (Hyper-V)", 0 ),
PCI_ROM ( 0x8086, 0x1889, "iavf", "Intel adaptive VF", 0 ),
PCI_ROM ( 0x8086, 0x37cd, "x722-vf", "X722 VF", 0 ),
PCI_ROM ( 0x8086, 0x37d9, "x722-vf-hv", "X722 VF (Hyper-V)", 0 ),
};
/** PCI driver */
struct pci_driver intelxlvf_driver __pci_driver = {
.ids = intelxlvf_nics,
.id_count = ( sizeof ( intelxlvf_nics ) /
sizeof ( intelxlvf_nics[0] ) ),
.probe = intelxlvf_probe,
.remove = intelxlvf_remove,
};