diff --git a/src/drivers/net/vmxnet3.c b/src/drivers/net/vmxnet3.c new file mode 100644 index 00000000..79cf1d80 --- /dev/null +++ b/src/drivers/net/vmxnet3.c @@ -0,0 +1,669 @@ +/* + * Copyright (C) 2011 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vmxnet3.h" + +/** + * @file + * + * VMware vmxnet3 virtual NIC driver + * + */ + +/** + * Issue command + * + * @v vmxnet vmxnet3 NIC + * @v command Command to issue + * @ret result Command result + */ +static inline uint32_t vmxnet3_command ( struct vmxnet3_nic *vmxnet, + uint32_t command ) { + + /* Issue command */ + writel ( command, ( vmxnet->vd + VMXNET3_VD_CMD ) ); + return readl ( vmxnet->vd + VMXNET3_VD_CMD ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int vmxnet3_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct vmxnet3_nic *vmxnet = netdev_priv ( netdev ); + struct vmxnet3_tx_desc *tx_desc; + unsigned int desc_idx; + unsigned int generation; + + /* Check that we have a free transmit descriptor */ + desc_idx = ( vmxnet->count.tx_prod % VMXNET3_NUM_TX_DESC ); + generation = ( ( vmxnet->count.tx_prod & VMXNET3_NUM_TX_DESC ) ? + 0 : cpu_to_le32 ( VMXNET3_TXF_GEN ) ); + if ( vmxnet->tx_iobuf[desc_idx] ) { + DBGC ( vmxnet, "VMXNET3 %p out of transmit descriptors\n", + vmxnet ); + return -ENOBUFS; + } + + /* Increment producer counter */ + vmxnet->count.tx_prod++; + + /* Store I/O buffer for later completion */ + vmxnet->tx_iobuf[desc_idx] = iobuf; + + /* Populate transmit descriptor */ + tx_desc = &vmxnet->dma->tx_desc[desc_idx]; + tx_desc->address = cpu_to_le64 ( virt_to_bus ( iobuf->data ) ); + tx_desc->flags[0] = ( generation | cpu_to_le32 ( iob_len ( iobuf ) ) ); + tx_desc->flags[1] = cpu_to_le32 ( VMXNET3_TXF_CQ | VMXNET3_TXF_EOP ); + + /* Hand over descriptor to NIC */ + wmb(); + writel ( ( vmxnet->count.tx_prod % VMXNET3_NUM_TX_DESC ), + ( vmxnet->pt + VMXNET3_PT_TXPROD ) ); + + return 0; +} + +/** + * Poll for completed transmissions + * + * @v netdev Network device + */ +static void vmxnet3_poll_tx ( struct net_device *netdev ) { + struct vmxnet3_nic *vmxnet = netdev_priv ( netdev ); + struct vmxnet3_tx_comp *tx_comp; + struct io_buffer *iobuf; + unsigned int comp_idx; + unsigned int desc_idx; + unsigned int generation; + + while ( 1 ) { + + /* Look for completed descriptors */ + comp_idx = ( vmxnet->count.tx_cons % VMXNET3_NUM_TX_COMP ); + generation = ( ( vmxnet->count.tx_cons & VMXNET3_NUM_TX_COMP ) ? + 0 : cpu_to_le32 ( VMXNET3_TXCF_GEN ) ); + tx_comp = &vmxnet->dma->tx_comp[comp_idx]; + if ( generation != ( tx_comp->flags & + cpu_to_le32 ( VMXNET3_TXCF_GEN ) ) ) { + break; + } + + /* Increment consumer counter */ + vmxnet->count.tx_cons++; + + /* Locate corresponding transmit descriptor */ + desc_idx = ( le32_to_cpu ( tx_comp->index ) % + VMXNET3_NUM_TX_DESC ); + iobuf = vmxnet->tx_iobuf[desc_idx]; + if ( ! iobuf ) { + DBGC ( vmxnet, "VMXNET3 %p completed on empty transmit " + "buffer %#x/%#x\n", vmxnet, comp_idx, desc_idx ); + netdev_tx_err ( netdev, NULL, -ENOTTY ); + continue; + } + + /* Remove I/O buffer from transmit queue */ + vmxnet->tx_iobuf[desc_idx] = NULL; + + /* Report transmission completion to network layer */ + DBGC2 ( vmxnet, "VMXNET3 %p completed TX %#x/%#x (len %#zx)\n", + vmxnet, comp_idx, desc_idx, iob_len ( iobuf ) ); + netdev_tx_complete ( netdev, iobuf ); + } +} + +/** + * Flush any uncompleted transmit buffers + * + * @v netdev Network device + */ +static void vmxnet3_flush_tx ( struct net_device *netdev ) { + struct vmxnet3_nic *vmxnet = netdev_priv ( netdev ); + unsigned int i; + + for ( i = 0 ; i < VMXNET3_NUM_TX_DESC ; i++ ) { + if ( vmxnet->tx_iobuf[i] ) { + netdev_tx_complete_err ( netdev, vmxnet->tx_iobuf[i], + -ECANCELED ); + vmxnet->tx_iobuf[i] = NULL; + } + } +} + +/** + * Refill receive ring + * + * @v netdev Network device + */ +static void vmxnet3_refill_rx ( struct net_device *netdev ) { + struct vmxnet3_nic *vmxnet = netdev_priv ( netdev ); + struct vmxnet3_rx_desc *rx_desc; + struct io_buffer *iobuf; + unsigned int orig_rx_prod = vmxnet->count.rx_prod; + unsigned int desc_idx; + unsigned int generation; + + /* Fill receive ring to specified fill level */ + while ( vmxnet->count.rx_fill < VMXNET3_RX_FILL ) { + + /* Locate receive descriptor */ + desc_idx = ( vmxnet->count.rx_prod % VMXNET3_NUM_RX_DESC ); + generation = ( ( vmxnet->count.rx_prod & VMXNET3_NUM_RX_DESC ) ? + 0 : cpu_to_le32 ( VMXNET3_RXF_GEN ) ); + assert ( vmxnet->rx_iobuf[desc_idx] == NULL ); + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( VMXNET3_MTU + NET_IP_ALIGN ); + if ( ! iobuf ) { + /* Non-fatal low memory condition */ + break; + } + iob_reserve ( iobuf, NET_IP_ALIGN ); + + /* Increment producer counter and fill level */ + vmxnet->count.rx_prod++; + vmxnet->count.rx_fill++; + + /* Store I/O buffer for later completion */ + vmxnet->rx_iobuf[desc_idx] = iobuf; + + /* Populate receive descriptor */ + rx_desc = &vmxnet->dma->rx_desc[desc_idx]; + rx_desc->address = cpu_to_le64 ( virt_to_bus ( iobuf->data ) ); + rx_desc->flags = ( generation | cpu_to_le32 ( VMXNET3_MTU ) ); + + } + + /* Hand over any new descriptors to NIC */ + if ( vmxnet->count.rx_prod != orig_rx_prod ) { + wmb(); + writel ( ( vmxnet->count.rx_prod % VMXNET3_NUM_RX_DESC ), + ( vmxnet->pt + VMXNET3_PT_RXPROD ) ); + } +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void vmxnet3_poll_rx ( struct net_device *netdev ) { + struct vmxnet3_nic *vmxnet = netdev_priv ( netdev ); + struct vmxnet3_rx_comp *rx_comp; + struct io_buffer *iobuf; + unsigned int comp_idx; + unsigned int desc_idx; + unsigned int generation; + size_t len; + + while ( 1 ) { + + /* Look for completed descriptors */ + comp_idx = ( vmxnet->count.rx_cons % VMXNET3_NUM_RX_COMP ); + generation = ( ( vmxnet->count.rx_cons & VMXNET3_NUM_RX_COMP ) ? + 0 : cpu_to_le32 ( VMXNET3_RXCF_GEN ) ); + rx_comp = &vmxnet->dma->rx_comp[comp_idx]; + if ( generation != ( rx_comp->flags & + cpu_to_le32 ( VMXNET3_RXCF_GEN ) ) ) { + break; + } + + /* Increment consumer counter */ + vmxnet->count.rx_cons++; + + /* Locate corresponding receive descriptor */ + desc_idx = ( le32_to_cpu ( rx_comp->index ) % + VMXNET3_NUM_RX_DESC ); + iobuf = vmxnet->rx_iobuf[desc_idx]; + if ( ! iobuf ) { + DBGC ( vmxnet, "VMXNET3 %p completed on empty receive " + "buffer %#x/%#x\n", vmxnet, comp_idx, desc_idx ); + netdev_rx_err ( netdev, NULL, -ENOTTY ); + continue; + } + + /* Remove I/O buffer from receive queue */ + vmxnet->rx_iobuf[desc_idx] = NULL; + vmxnet->count.rx_fill--; + + /* Deliver packet to network layer */ + len = ( le32_to_cpu ( rx_comp->len ) & + ( VMXNET3_MAX_PACKET_LEN - 1 ) ); + DBGC2 ( vmxnet, "VMXNET3 %p completed RX %#x/%#x (len %#zx)\n", + vmxnet, comp_idx, desc_idx, len ); + iob_put ( iobuf, len ); + netdev_rx ( netdev, iobuf ); + } +} + +/** + * Flush any uncompleted receive buffers + * + * @v netdev Network device + */ +static void vmxnet3_flush_rx ( struct net_device *netdev ) { + struct vmxnet3_nic *vmxnet = netdev_priv ( netdev ); + struct io_buffer *iobuf; + unsigned int i; + + for ( i = 0 ; i < VMXNET3_NUM_RX_DESC ; i++ ) { + if ( ( iobuf = vmxnet->rx_iobuf[i] ) != NULL ) { + netdev_rx_err ( netdev, iobuf, -ECANCELED ); + vmxnet->rx_iobuf[i] = NULL; + } + } +} + +/** + * Check link state + * + * @v netdev Network device + */ +static void vmxnet3_check_link ( struct net_device *netdev ) { + struct vmxnet3_nic *vmxnet = netdev_priv ( netdev ); + uint32_t state; + int link_up; + unsigned int link_speed; + + /* Get link state */ + state = vmxnet3_command ( vmxnet, VMXNET3_CMD_GET_LINK ); + link_up = ( state & 1 ); + link_speed = ( state >> 16 ); + + /* Report link state to network device */ + if ( link_up ) { + DBGC ( vmxnet, "VMXNET3 %p link is up at %d Mbps\n", + vmxnet, link_speed ); + netdev_link_up ( netdev ); + } else { + DBGC ( vmxnet, "VMXNET3 %p link is down\n", vmxnet ); + netdev_link_down ( netdev ); + } +} + +/** + * Poll for events + * + * @v netdev Network device + */ +static void vmxnet3_poll_events ( struct net_device *netdev ) { + struct vmxnet3_nic *vmxnet = netdev_priv ( netdev ); + uint32_t events; + + /* Do nothing unless there are events to process */ + if ( ! vmxnet->dma->shared.ecr ) + return; + events = le32_to_cpu ( vmxnet->dma->shared.ecr ); + + /* Acknowledge these events */ + writel ( events, ( vmxnet->vd + VMXNET3_VD_ECR ) ); + + /* Check for link state change */ + if ( events & VMXNET3_ECR_LINK ) { + vmxnet3_check_link ( netdev ); + events &= ~VMXNET3_ECR_LINK; + } + + /* Check for queue errors */ + if ( events & ( VMXNET3_ECR_TQERR | VMXNET3_ECR_RQERR ) ) { + vmxnet3_command ( vmxnet, VMXNET3_CMD_GET_QUEUE_STATUS ); + DBGC ( vmxnet, "VMXNET3 %p queue error status (TX %08x, RX " + "%08x)\n", vmxnet, + le32_to_cpu ( vmxnet->dma->queues.tx.status.error ), + le32_to_cpu ( vmxnet->dma->queues.rx.status.error ) ); + /* Report errors to allow for visibility via "ifstat" */ + if ( events & VMXNET3_ECR_TQERR ) + netdev_tx_err ( netdev, NULL, -EPIPE ); + if ( events & VMXNET3_ECR_RQERR ) + netdev_rx_err ( netdev, NULL, -EPIPE ); + events &= ~( VMXNET3_ECR_TQERR | VMXNET3_ECR_RQERR ); + } + + /* Check for unknown events */ + if ( events ) { + DBGC ( vmxnet, "VMXNET3 %p unknown events %08x\n", + vmxnet, events ); + /* Report error to allow for visibility via "ifstat" */ + netdev_rx_err ( netdev, NULL, -ENODEV ); + } +} + +/** + * Poll network device + * + * @v netdev Network device + */ +static void vmxnet3_poll ( struct net_device *netdev ) { + + vmxnet3_poll_events ( netdev ); + vmxnet3_poll_tx ( netdev ); + vmxnet3_poll_rx ( netdev ); + vmxnet3_refill_rx ( netdev ); +} + +/** + * Enable/disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void vmxnet3_irq ( struct net_device *netdev, int enable ) { + struct vmxnet3_nic *vmxnet = netdev_priv ( netdev ); + + DBGC ( vmxnet, "VMXNET3 %p %s IRQ not implemented\n", + vmxnet, ( enable ? "enable" : "disable" ) ); +} + +/** + * Set MAC address + * + * @v vmxnet vmxnet3 NIC + * @v ll_addr Link-layer address to set + */ +static void vmxnet3_set_ll_addr ( struct vmxnet3_nic *vmxnet, + const void *ll_addr ) { + struct { + uint32_t low; + uint32_t high; + } __attribute__ (( packed )) mac; + + memset ( &mac, 0, sizeof ( mac ) ); + memcpy ( &mac, ll_addr, ETH_ALEN ); + writel ( cpu_to_le32 ( mac.low ), ( vmxnet->vd + VMXNET3_VD_MACL ) ); + writel ( cpu_to_le32 ( mac.high ), ( vmxnet->vd + VMXNET3_VD_MACH ) ); +} + +/** + * Open NIC + * + * @v netdev Network device + * @ret rc Return status code + */ +static int vmxnet3_open ( struct net_device *netdev ) { + struct vmxnet3_nic *vmxnet = netdev_priv ( netdev ); + struct vmxnet3_shared *shared; + struct vmxnet3_queues *queues; + uint64_t shared_bus; + uint64_t queues_bus; + uint32_t status; + int rc; + + /* Allocate DMA areas */ + vmxnet->dma = malloc_dma ( sizeof ( *vmxnet->dma ), VMXNET3_DMA_ALIGN ); + if ( ! vmxnet->dma ) { + DBGC ( vmxnet, "VMXNET3 %p could not allocate DMA area\n", + vmxnet ); + rc = -ENOMEM; + goto err_alloc_dma; + } + memset ( vmxnet->dma, 0, sizeof ( *vmxnet->dma ) ); + + /* Populate queue descriptors */ + queues = &vmxnet->dma->queues; + queues->tx.cfg.desc_address = + cpu_to_le64 ( virt_to_bus ( &vmxnet->dma->tx_desc ) ); + queues->tx.cfg.comp_address = + cpu_to_le64 ( virt_to_bus ( &vmxnet->dma->tx_comp ) ); + queues->tx.cfg.num_desc = cpu_to_le32 ( VMXNET3_NUM_TX_DESC ); + queues->tx.cfg.num_comp = cpu_to_le32 ( VMXNET3_NUM_TX_COMP ); + queues->rx.cfg.desc_address[0] = + cpu_to_le64 ( virt_to_bus ( &vmxnet->dma->rx_desc ) ); + queues->rx.cfg.comp_address = + cpu_to_le64 ( virt_to_bus ( &vmxnet->dma->rx_comp ) ); + queues->rx.cfg.num_desc[0] = cpu_to_le32 ( VMXNET3_NUM_RX_DESC ); + queues->rx.cfg.num_comp = cpu_to_le32 ( VMXNET3_NUM_RX_COMP ); + queues_bus = virt_to_bus ( queues ); + DBGC ( vmxnet, "VMXNET3 %p queue descriptors at %08llx+%zx\n", + vmxnet, queues_bus, sizeof ( *queues ) ); + + /* Populate shared area */ + shared = &vmxnet->dma->shared; + shared->magic = cpu_to_le32 ( VMXNET3_SHARED_MAGIC ); + shared->misc.version = cpu_to_le32 ( VMXNET3_VERSION_MAGIC ); + shared->misc.version_support = cpu_to_le32 ( VMXNET3_VERSION_SELECT ); + shared->misc.upt_version_support = + cpu_to_le32 ( VMXNET3_UPT_VERSION_SELECT ); + shared->misc.queue_desc_address = cpu_to_le64 ( queues_bus ); + shared->misc.queue_desc_len = cpu_to_le32 ( sizeof ( *queues ) ); + shared->misc.mtu = cpu_to_le32 ( VMXNET3_MTU ); + shared->misc.num_tx_queues = 1; + shared->misc.num_rx_queues = 1; + shared->interrupt.num_intrs = 1; + shared->interrupt.control = cpu_to_le32 ( VMXNET3_IC_DISABLE_ALL ); + shared->rx_filter.mode = cpu_to_le32 ( VMXNET3_RXM_UCAST | + VMXNET3_RXM_BCAST | + VMXNET3_RXM_ALL_MULTI ); + shared_bus = virt_to_bus ( shared ); + DBGC ( vmxnet, "VMXNET3 %p shared area at %08llx+%zx\n", + vmxnet, shared_bus, sizeof ( *shared ) ); + + /* Zero counters */ + memset ( &vmxnet->count, 0, sizeof ( vmxnet->count ) ); + + /* Set MAC address */ + vmxnet3_set_ll_addr ( vmxnet, &netdev->ll_addr ); + + /* Pass shared area to device */ + writel ( ( shared_bus >> 0 ), ( vmxnet->vd + VMXNET3_VD_DSAL ) ); + writel ( ( shared_bus >> 32 ), ( vmxnet->vd + VMXNET3_VD_DSAH ) ); + + /* Activate device */ + if ( ( status = vmxnet3_command ( vmxnet, + VMXNET3_CMD_ACTIVATE_DEV ) ) != 0 ) { + DBGC ( vmxnet, "VMXNET3 %p could not activate (status %#x)\n", + vmxnet, status ); + rc = -EIO; + goto err_activate; + } + + /* Fill receive ring */ + vmxnet3_refill_rx ( netdev ); + + return 0; + + vmxnet3_command ( vmxnet, VMXNET3_CMD_QUIESCE_DEV ); + vmxnet3_command ( vmxnet, VMXNET3_CMD_RESET_DEV ); + err_activate: + vmxnet3_flush_tx ( netdev ); + vmxnet3_flush_rx ( netdev ); + free_dma ( vmxnet->dma, sizeof ( *vmxnet->dma ) ); + err_alloc_dma: + return rc; +} + +/** + * Close NIC + * + * @v netdev Network device + */ +static void vmxnet3_close ( struct net_device *netdev ) { + struct vmxnet3_nic *vmxnet = netdev_priv ( netdev ); + + vmxnet3_command ( vmxnet, VMXNET3_CMD_QUIESCE_DEV ); + vmxnet3_command ( vmxnet, VMXNET3_CMD_RESET_DEV ); + vmxnet3_flush_tx ( netdev ); + vmxnet3_flush_rx ( netdev ); + free_dma ( vmxnet->dma, sizeof ( *vmxnet->dma ) ); +} + +/** vmxnet3 net device operations */ +static struct net_device_operations vmxnet3_operations = { + .open = vmxnet3_open, + .close = vmxnet3_close, + .transmit = vmxnet3_transmit, + .poll = vmxnet3_poll, + .irq = vmxnet3_irq, +}; + +/** + * Check version + * + * @v vmxnet vmxnet3 NIC + * @ret rc Return status code + */ +static int vmxnet3_check_version ( struct vmxnet3_nic *vmxnet ) { + uint32_t version; + uint32_t upt_version; + + /* Read version */ + version = readl ( vmxnet->vd + VMXNET3_VD_VRRS ); + upt_version = readl ( vmxnet->vd + VMXNET3_VD_UVRS ); + DBGC ( vmxnet, "VMXNET3 %p is version %d (UPT version %d)\n", + vmxnet, version, upt_version ); + + /* Inform NIC of driver version */ + writel ( VMXNET3_VERSION_SELECT, ( vmxnet->vd + VMXNET3_VD_VRRS ) ); + writel ( VMXNET3_UPT_VERSION_SELECT, ( vmxnet->vd + VMXNET3_VD_UVRS ) ); + + return 0; +} + +/** + * Get permanent MAC address + * + * @v vmxnet vmxnet3 NIC + * @v hw_addr Hardware address to fill in + */ +static void vmxnet3_get_hw_addr ( struct vmxnet3_nic *vmxnet, void *hw_addr ) { + struct { + uint32_t low; + uint32_t high; + } __attribute__ (( packed )) mac; + + mac.low = le32_to_cpu ( vmxnet3_command ( vmxnet, + VMXNET3_CMD_GET_PERM_MAC_LO ) ); + mac.high = le32_to_cpu ( vmxnet3_command ( vmxnet, + VMXNET3_CMD_GET_PERM_MAC_HI ) ); + memcpy ( hw_addr, &mac, ETH_ALEN ); +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @v id PCI ID + * @ret rc Return status code + */ +static int vmxnet3_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct vmxnet3_nic *vmxnet; + int rc; + + /* Allocate network device */ + netdev = alloc_etherdev ( sizeof ( *vmxnet ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc_etherdev; + } + netdev_init ( netdev, &vmxnet3_operations ); + vmxnet = netdev_priv ( netdev ); + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( vmxnet, 0, sizeof ( *vmxnet ) ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map PCI BARs */ + vmxnet->pt = ioremap ( pci_bar_start ( pci, VMXNET3_PT_BAR ), + VMXNET3_PT_LEN ); + vmxnet->vd = ioremap ( pci_bar_start ( pci, VMXNET3_VD_BAR ), + VMXNET3_VD_LEN ); + + /* Version check */ + if ( ( rc = vmxnet3_check_version ( vmxnet ) ) != 0 ) + goto err_check_version; + + /* Reset device */ + if ( ( rc = vmxnet3_command ( vmxnet, VMXNET3_CMD_RESET_DEV ) ) != 0 ) + goto err_reset; + + /* Read initial MAC address */ + vmxnet3_get_hw_addr ( vmxnet, &netdev->hw_addr ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) { + DBGC ( vmxnet, "VMXNET3 %p could not register net device: " + "%s\n", vmxnet, strerror ( rc ) ); + goto err_register_netdev; + } + + /* Get initial link state */ + vmxnet3_check_link ( netdev ); + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + err_reset: + err_check_version: + iounmap ( vmxnet->vd ); + iounmap ( vmxnet->pt ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc_etherdev: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void vmxnet3_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct vmxnet3_nic *vmxnet = netdev_priv ( netdev ); + + unregister_netdev ( netdev ); + iounmap ( vmxnet->vd ); + iounmap ( vmxnet->pt ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** vmxnet3 PCI IDs */ +static struct pci_device_id vmxnet3_nics[] = { + PCI_ROM ( 0x15ad, 0x07b0, "vmxnet3", "vmxnet3 virtual NIC", 0 ), +}; + +/** vmxnet3 PCI driver */ +struct pci_driver vmxnet3_driver __pci_driver = { + .ids = vmxnet3_nics, + .id_count = ( sizeof ( vmxnet3_nics ) / sizeof ( vmxnet3_nics[0] ) ), + .probe = vmxnet3_probe, + .remove = vmxnet3_remove, +}; diff --git a/src/drivers/net/vmxnet3.h b/src/drivers/net/vmxnet3.h new file mode 100644 index 00000000..22a93539 --- /dev/null +++ b/src/drivers/net/vmxnet3.h @@ -0,0 +1,497 @@ +#ifndef _VMXNET3_H +#define _VMXNET3_H + +/* + * Copyright (C) 2008 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * @file + * + * VMware vmxnet3 virtual NIC driver + * + */ + +#include + +/** Maximum number of TX queues */ +#define VMXNET3_MAX_TX_QUEUES 8 + +/** Maximum number of RX queues */ +#define VMXNET3_MAX_RX_QUEUES 16 + +/** Maximum number of interrupts */ +#define VMXNET3_MAX_INTRS 25 + +/** Maximum packet size */ +#define VMXNET3_MAX_PACKET_LEN 0x4000 + +/** "PT" PCI BAR address */ +#define VMXNET3_PT_BAR PCI_BASE_ADDRESS_0 + +/** "PT" PCI BAR size */ +#define VMXNET3_PT_LEN 0x1000 + +/** Interrupt Mask Register */ +#define VMXNET3_PT_IMR 0x0 + +/** Transmit producer index */ +#define VMXNET3_PT_TXPROD 0x600 + +/** Rx producer index for ring 1 */ +#define VMXNET3_PT_RXPROD 0x800 + +/** Rx producer index for ring 2 */ +#define VMXNET3_PT_RXPROD2 0xa00 + +/** "VD" PCI BAR address */ +#define VMXNET3_VD_BAR PCI_BASE_ADDRESS_1 + +/** "VD" PCI BAR size */ +#define VMXNET3_VD_LEN 0x1000 + +/** vmxnet3 Revision Report Selection */ +#define VMXNET3_VD_VRRS 0x0 + +/** UPT Version Report Selection */ +#define VMXNET3_VD_UVRS 0x8 + +/** Driver Shared Address Low */ +#define VMXNET3_VD_DSAL 0x10 + +/** Driver Shared Address High */ +#define VMXNET3_VD_DSAH 0x18 + +/** Command */ +#define VMXNET3_VD_CMD 0x20 + +/** MAC Address Low */ +#define VMXNET3_VD_MACL 0x28 + +/** MAC Address High */ +#define VMXNET3_VD_MACH 0x30 + +/** Interrupt Cause Register */ +#define VMXNET3_VD_ICR 0x38 + +/** Event Cause Register */ +#define VMXNET3_VD_ECR 0x40 + +/** Commands */ +enum vmxnet3_command { + VMXNET3_CMD_FIRST_SET = 0xcafe0000, + VMXNET3_CMD_ACTIVATE_DEV = VMXNET3_CMD_FIRST_SET, + VMXNET3_CMD_QUIESCE_DEV, + VMXNET3_CMD_RESET_DEV, + VMXNET3_CMD_UPDATE_RX_MODE, + VMXNET3_CMD_UPDATE_MAC_FILTERS, + VMXNET3_CMD_UPDATE_VLAN_FILTERS, + VMXNET3_CMD_UPDATE_RSSIDT, + VMXNET3_CMD_UPDATE_IML, + VMXNET3_CMD_UPDATE_PMCFG, + VMXNET3_CMD_UPDATE_FEATURE, + VMXNET3_CMD_LOAD_PLUGIN, + + VMXNET3_CMD_FIRST_GET = 0xf00d0000, + VMXNET3_CMD_GET_QUEUE_STATUS = VMXNET3_CMD_FIRST_GET, + VMXNET3_CMD_GET_STATS, + VMXNET3_CMD_GET_LINK, + VMXNET3_CMD_GET_PERM_MAC_LO, + VMXNET3_CMD_GET_PERM_MAC_HI, + VMXNET3_CMD_GET_DID_LO, + VMXNET3_CMD_GET_DID_HI, + VMXNET3_CMD_GET_DEV_EXTRA_INFO, + VMXNET3_CMD_GET_CONF_INTR +}; + +/** Events */ +enum vmxnet3_event { + VMXNET3_ECR_RQERR = 0x00000001, + VMXNET3_ECR_TQERR = 0x00000002, + VMXNET3_ECR_LINK = 0x00000004, + VMXNET3_ECR_DIC = 0x00000008, + VMXNET3_ECR_DEBUG = 0x00000010, +}; + +/** Miscellaneous configuration descriptor */ +struct vmxnet3_misc_config { + /** Driver version */ + uint32_t version; + /** Guest information */ + uint32_t guest_info; + /** Version supported */ + uint32_t version_support; + /** UPT version supported */ + uint32_t upt_version_support; + /** UPT features supported */ + uint64_t upt_features; + /** Driver-private data address */ + uint64_t driver_data_address; + /** Queue descriptors data address */ + uint64_t queue_desc_address; + /** Driver-private data length */ + uint32_t driver_data_len; + /** Queue descriptors data length */ + uint32_t queue_desc_len; + /** Maximum transmission unit */ + uint32_t mtu; + /** Maximum number of RX scatter-gather */ + uint16_t max_num_rx_sg; + /** Number of TX queues */ + uint8_t num_tx_queues; + /** Number of RX queues */ + uint8_t num_rx_queues; + /** Reserved */ + uint32_t reserved0[4]; +} __attribute__ (( packed )); + +/** Driver version magic */ +#define VMXNET3_VERSION_MAGIC 0x69505845 + +/** Interrupt configuration */ +struct vmxnet3_interrupt_config { + uint8_t mask_mode; + uint8_t num_intrs; + uint8_t event_intr_index; + uint8_t moderation_level[VMXNET3_MAX_INTRS]; + uint32_t control; + uint32_t reserved0[2]; +} __attribute__ (( packed )); + +/** Interrupt control - disable all interrupts */ +#define VMXNET3_IC_DISABLE_ALL 0x1 + +/** Receive filter configuration */ +struct vmxnet3_rx_filter_config { + /** Receive filter mode */ + uint32_t mode; + /** Multicast filter table length */ + uint16_t multicast_len; + /** Reserved */ + uint16_t reserved0; + /** Multicast filter table address */ + uint64_t multicast_address; + /** VLAN filter table (one bit per possible VLAN) */ + uint8_t vlan_filter[512]; +} __attribute__ (( packed )); + +/** Receive filter mode */ +enum vmxnet3_rx_filter_mode { + VMXNET3_RXM_UCAST = 0x01, /**< Unicast only */ + VMXNET3_RXM_MCAST = 0x02, /**< Multicast passing the filters */ + VMXNET3_RXM_BCAST = 0x04, /**< Broadcast only */ + VMXNET3_RXM_ALL_MULTI = 0x08, /**< All multicast */ + VMXNET3_RXM_PROMISC = 0x10, /**< Promiscuous */ +}; + +/** Variable-length configuration descriptor */ +struct vmxnet3_variable_config { + uint32_t version; + uint32_t length; + uint64_t address; +} __attribute__ (( packed )); + +/** Driver shared area */ +struct vmxnet3_shared { + /** Magic signature */ + uint32_t magic; + /** Reserved */ + uint32_t reserved0; + /** Miscellaneous configuration */ + struct vmxnet3_misc_config misc; + /** Interrupt configuration */ + struct vmxnet3_interrupt_config interrupt; + /** Receive filter configuration */ + struct vmxnet3_rx_filter_config rx_filter; + /** RSS configuration */ + struct vmxnet3_variable_config rss; + /** Pattern-matching configuration */ + struct vmxnet3_variable_config pattern; + /** Plugin configuration */ + struct vmxnet3_variable_config plugin; + /** Event notifications */ + uint32_t ecr; + /** Reserved */ + uint32_t reserved1[5]; +} __attribute__ (( packed )); + +/** Alignment of driver shared area */ +#define VMXNET3_SHARED_ALIGN 8 + +/** Driver shared area magic */ +#define VMXNET3_SHARED_MAGIC 0xbabefee1 + +/** Transmit descriptor */ +struct vmxnet3_tx_desc { + /** Address */ + uint64_t address; + /** Flags */ + uint32_t flags[2]; +} __attribute__ (( packed )); + +/** Transmit generation flag */ +#define VMXNET3_TXF_GEN 0x00004000UL + +/** Transmit end-of-packet flag */ +#define VMXNET3_TXF_EOP 0x000001000UL + +/** Transmit completion request flag */ +#define VMXNET3_TXF_CQ 0x000002000UL + +/** Transmit completion descriptor */ +struct vmxnet3_tx_comp { + /** Index of the end-of-packet descriptor */ + uint32_t index; + /** Reserved */ + uint32_t reserved0[2]; + /** Flags */ + uint32_t flags; +} __attribute__ (( packed )); + +/** Transmit completion generation flag */ +#define VMXNET3_TXCF_GEN 0x80000000UL + +/** Transmit queue control */ +struct vmxnet3_tx_queue_control { + uint32_t num_deferred; + uint32_t threshold; + uint64_t reserved0; +} __attribute__ (( packed )); + +/** Transmit queue configuration */ +struct vmxnet3_tx_queue_config { + /** Descriptor ring address */ + uint64_t desc_address; + /** Data ring address */ + uint64_t immediate_address; + /** Completion ring address */ + uint64_t comp_address; + /** Driver-private data address */ + uint64_t driver_data_address; + /** Reserved */ + uint64_t reserved0; + /** Number of descriptors */ + uint32_t num_desc; + /** Number of data descriptors */ + uint32_t num_immediate; + /** Number of completion descriptors */ + uint32_t num_comp; + /** Driver-private data length */ + uint32_t driver_data_len; + /** Interrupt index */ + uint8_t intr_index; + /** Reserved */ + uint8_t reserved[7]; +} __attribute__ (( packed )); + +/** Transmit queue statistics */ +struct vmxnet3_tx_stats { + /** Reserved */ + uint64_t reserved[10]; +} __attribute__ (( packed )); + +/** Receive descriptor */ +struct vmxnet3_rx_desc { + /** Address */ + uint64_t address; + /** Flags */ + uint32_t flags; + /** Reserved */ + uint32_t reserved0; +} __attribute__ (( packed )); + +/** Receive generation flag */ +#define VMXNET3_RXF_GEN 0x80000000UL + +/** Receive completion descriptor */ +struct vmxnet3_rx_comp { + /** Descriptor index */ + uint32_t index; + /** RSS hash value */ + uint32_t rss; + /** Length */ + uint32_t len; + /** Flags */ + uint32_t flags; +} __attribute__ (( packed )); + +/** Receive completion generation flag */ +#define VMXNET3_RXCF_GEN 0x80000000UL + +/** Receive queue control */ +struct vmxnet3_rx_queue_control { + uint8_t update_prod; + uint8_t reserved0[7]; + uint64_t reserved1; +} __attribute__ (( packed )); + +/** Receive queue configuration */ +struct vmxnet3_rx_queue_config { + /** Descriptor ring addresses */ + uint64_t desc_address[2]; + /** Completion ring address */ + uint64_t comp_address; + /** Driver-private data address */ + uint64_t driver_data_address; + /** Reserved */ + uint64_t reserved0; + /** Number of descriptors */ + uint32_t num_desc[2]; + /** Number of completion descriptors */ + uint32_t num_comp; + /** Driver-private data length */ + uint32_t driver_data_len; + /** Interrupt index */ + uint8_t intr_index; + /** Reserved */ + uint8_t reserved[7]; +} __attribute__ (( packed )); + +/** Receive queue statistics */ +struct vmxnet3_rx_stats { + /** Reserved */ + uint64_t reserved[10]; +} __attribute__ (( packed )); + +/** Queue status */ +struct vmxnet3_queue_status { + uint8_t stopped; + uint8_t reserved0[3]; + uint32_t error; +} __attribute__ (( packed )); + +/** Transmit queue descriptor */ +struct vmxnet3_tx_queue { + struct vmxnet3_tx_queue_control ctrl; + struct vmxnet3_tx_queue_config cfg; + struct vmxnet3_queue_status status; + struct vmxnet3_tx_stats state; + uint8_t reserved[88]; +} __attribute__ (( packed )); + +/** Receive queue descriptor */ +struct vmxnet3_rx_queue { + struct vmxnet3_rx_queue_control ctrl; + struct vmxnet3_rx_queue_config cfg; + struct vmxnet3_queue_status status; + struct vmxnet3_rx_stats stats; + uint8_t reserved[88]; +} __attribute__ (( packed )); + +/** + * Queue descriptor set + * + * We use only a single TX and RX queue + */ +struct vmxnet3_queues { + /** Transmit queue descriptor(s) */ + struct vmxnet3_tx_queue tx; + /** Receive queue descriptor(s) */ + struct vmxnet3_rx_queue rx; +} __attribute__ (( packed )); + +/** Alignment of queue descriptor set */ +#define VMXNET3_QUEUES_ALIGN 128 + +/** Alignment of rings */ +#define VMXNET3_RING_ALIGN 512 + +/** Number of TX descriptors */ +#define VMXNET3_NUM_TX_DESC 32 + +/** Number of TX completion descriptors */ +#define VMXNET3_NUM_TX_COMP 32 + +/** Number of RX descriptors */ +#define VMXNET3_NUM_RX_DESC 32 + +/** Number of RX completion descriptors */ +#define VMXNET3_NUM_RX_COMP 32 + +/** + * DMA areas + * + * These are arranged in order of decreasing alignment, to allow for a + * single allocation + */ +struct vmxnet3_dma { + /** TX descriptor ring */ + struct vmxnet3_tx_desc tx_desc[VMXNET3_NUM_TX_DESC]; + /** TX completion ring */ + struct vmxnet3_tx_comp tx_comp[VMXNET3_NUM_TX_COMP]; + /** RX descriptor ring */ + struct vmxnet3_rx_desc rx_desc[VMXNET3_NUM_RX_DESC]; + /** RX completion ring */ + struct vmxnet3_rx_comp rx_comp[VMXNET3_NUM_RX_COMP]; + /** Queue descriptors */ + struct vmxnet3_queues queues; + /** Shared area */ + struct vmxnet3_shared shared; +} __attribute__ (( packed )); + +/** DMA area alignment */ +#define VMXNET3_DMA_ALIGN 512 + +/** Producer and consumer counters */ +struct vmxnet3_counters { + /** Transmit producer counter */ + unsigned int tx_prod; + /** Transmit completion consumer counter */ + unsigned int tx_cons; + /** Receive producer counter */ + unsigned int rx_prod; + /** Receive fill level */ + unsigned int rx_fill; + /** Receive consumer counter */ + unsigned int rx_cons; +}; + +/** A vmxnet3 NIC */ +struct vmxnet3_nic { + /** "PT" register base address */ + void *pt; + /** "VD" register base address */ + void *vd; + + /** DMA area */ + struct vmxnet3_dma *dma; + /** Producer and consumer counters */ + struct vmxnet3_counters count; + /** Transmit I/O buffers */ + struct io_buffer *tx_iobuf[VMXNET3_NUM_TX_DESC]; + /** Receive I/O buffers */ + struct io_buffer *rx_iobuf[VMXNET3_NUM_RX_DESC]; +}; + +/** vmxnet3 version that we support */ +#define VMXNET3_VERSION_SELECT 1 + +/** UPT version that we support */ +#define VMXNET3_UPT_VERSION_SELECT 1 + +/** MTU size */ +#define VMXNET3_MTU ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* FCS */ ) + +/** Receive ring maximum fill level */ +#define VMXNET3_RX_FILL 8 + +/** Received packet alignment padding */ +#define NET_IP_ALIGN 2 + +#endif /* _VMXNET3_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 414d7f17..aacc722d 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -139,6 +139,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_igbvf_main ( ERRFILE_DRIVER | 0x005e0000 ) #define ERRFILE_ath9k ( ERRFILE_DRIVER | 0x005f0000 ) #define ERRFILE_ath ( ERRFILE_DRIVER | 0x00600000 ) +#define ERRFILE_vmxnet3 ( ERRFILE_DRIVER | 0x00610000 ) #define ERRFILE_scsi ( ERRFILE_DRIVER | 0x00700000 ) #define ERRFILE_arbel ( ERRFILE_DRIVER | 0x00710000 )