iPXE
Data Structures | Defines | Enumerations | Functions | Variables
virtio-net.c File Reference
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <ipxe/list.h>
#include <ipxe/iobuf.h>
#include <ipxe/netdevice.h>
#include <ipxe/pci.h>
#include <ipxe/if_ether.h>
#include <ipxe/ethernet.h>
#include <ipxe/virtio-pci.h>
#include <ipxe/virtio-ring.h>
#include "virtio-net.h"

Go to the source code of this file.

Data Structures

struct  virtnet_nic

Defines

#define NUM_RX_BUF   8
 Max number of pending rx packets.

Enumerations

enum  { RX_INDEX = 0, TX_INDEX, QUEUE_NB }

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static void virtnet_enqueue_iob (struct net_device *netdev, int vq_idx, struct io_buffer *iobuf)
 Add an iobuf to a virtqueue.
static void virtnet_refill_rx_virtqueue (struct net_device *netdev)
 Try to keep rx virtqueue filled with iobufs.
static void virtnet_free_virtqueues (struct net_device *netdev)
 Helper to free all virtqueue memory.
static int virtnet_open_legacy (struct net_device *netdev)
 Open network device, legacy virtio 0.9.5.
static int virtnet_open_modern (struct net_device *netdev)
 Open network device, modern virtio 1.0.
static int virtnet_open (struct net_device *netdev)
 Open network device.
static void virtnet_close (struct net_device *netdev)
 Close network device.
static int virtnet_transmit (struct net_device *netdev, struct io_buffer *iobuf)
 Transmit packet.
static void virtnet_process_tx_packets (struct net_device *netdev)
 Complete packet transmission.
static void virtnet_process_rx_packets (struct net_device *netdev)
 Complete packet reception.
static void virtnet_poll (struct net_device *netdev)
 Poll for completed and received packets.
static void virtnet_irq (struct net_device *netdev, int enable)
 Enable or disable interrupts.
static int virtnet_probe_legacy (struct pci_device *pci)
 Probe PCI device, legacy virtio 0.9.5.
static int virtnet_probe_modern (struct pci_device *pci, int *found_dev)
 Probe PCI device, modern virtio 1.0.
static int virtnet_probe (struct pci_device *pci)
 Probe PCI device.
static void virtnet_remove (struct pci_device *pci)
 Remove device.

Variables

static struct net_device_operations virtnet_operations
 virtio-net device operations
static struct pci_device_id virtnet_nics []
struct pci_driver virtnet_driver __pci_driver

Define Documentation

#define NUM_RX_BUF   8

Max number of pending rx packets.

Definition at line 81 of file virtio-net.c.

Referenced by virtnet_refill_rx_virtqueue().


Enumeration Type Documentation

anonymous enum
Enumerator:
RX_INDEX 
TX_INDEX 
QUEUE_NB 

Definition at line 74 of file virtio-net.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static void virtnet_enqueue_iob ( struct net_device netdev,
int  vq_idx,
struct io_buffer iobuf 
) [static]

Add an iobuf to a virtqueue.

Parameters:
netdevNetwork device
vq_idxVirtqueue index (RX_INDEX or TX_INDEX)
iobufI/O buffer

The virtqueue is kicked after the iobuf has been added.

Definition at line 114 of file virtio-net.c.

References vring_list::addr, io_buffer::data, DBGC2, virtnet_nic::empty_header, header, in, virtnet_nic::ioaddr, iob_len(), virtio_net_hdr_modern::legacy, length, NULL, out, net_device::priv, TX_INDEX, virtnet_nic::vdev, virtnet_nic::virtio_version, virtnet_nic::virtqueue, vring_add_buf(), and vring_kick().

Referenced by virtnet_refill_rx_virtqueue(), and virtnet_transmit().

                                                                        {
        struct virtnet_nic *virtnet = netdev->priv;
        struct vring_virtqueue *vq = &virtnet->virtqueue[vq_idx];
        struct virtio_net_hdr_modern *header = &virtnet->empty_header[vq_idx];
        unsigned int out = ( vq_idx == TX_INDEX ) ? 2 : 0;
        unsigned int in = ( vq_idx == TX_INDEX ) ? 0 : 2;
        size_t header_len = ( virtnet->virtio_version ?
                              sizeof ( *header ) : sizeof ( header->legacy ) );
        struct vring_list list[] = {
                {
                        /* Share a single zeroed virtio net header between all
                         * packets in a ring.  This works because this driver
                         * does not use any advanced features so none of the
                         * header fields get used.
                         *
                         * Some host implementations (notably Google Compute
                         * Platform) are known to unconditionally write back
                         * to header->flags for received packets.  Work around
                         * this by using separate RX and TX headers.
                         */
                        .addr = ( char* ) header,
                        .length = header_len,
                },
                {
                        .addr = ( char* ) iobuf->data,
                        .length = iob_len ( iobuf ),
                },
        };

        DBGC2 ( virtnet, "VIRTIO-NET %p enqueuing iobuf %p on vq %d\n",
                virtnet, iobuf, vq_idx );

        vring_add_buf ( vq, list, out, in, iobuf, 0 );
        vring_kick ( virtnet->virtio_version ? &virtnet->vdev : NULL,
                     virtnet->ioaddr, vq, 1 );
}
static void virtnet_refill_rx_virtqueue ( struct net_device netdev) [static]

Try to keep rx virtqueue filled with iobufs.

Parameters:
netdevNetwork device

Definition at line 156 of file virtio-net.c.

References alloc_iob(), iob_put, len, io_buffer::list, list_add, net_device::max_pkt_len, NUM_RX_BUF, net_device::priv, RX_INDEX, virtnet_nic::rx_iobufs, virtnet_nic::rx_num_iobufs, and virtnet_enqueue_iob().

Referenced by virtnet_open_legacy(), virtnet_open_modern(), and virtnet_process_rx_packets().

                                                                      {
        struct virtnet_nic *virtnet = netdev->priv;
        size_t len = ( netdev->max_pkt_len + 4 /* VLAN */ );

        while ( virtnet->rx_num_iobufs < NUM_RX_BUF ) {
                struct io_buffer *iobuf;

                /* Try to allocate a buffer, stop for now if out of memory */
                iobuf = alloc_iob ( len );
                if ( ! iobuf )
                        break;

                /* Keep track of iobuf so close() can free it */
                list_add ( &iobuf->list, &virtnet->rx_iobufs );

                /* Mark packet length until we know the actual size */
                iob_put ( iobuf, len );

                virtnet_enqueue_iob ( netdev, RX_INDEX, iobuf );
                virtnet->rx_num_iobufs++;
        }
}
static void virtnet_free_virtqueues ( struct net_device netdev) [static]

Helper to free all virtqueue memory.

Parameters:
netdevNetwork device

Definition at line 183 of file virtio-net.c.

References free, vring_virtqueue::notification, NULL, net_device::priv, QUEUE_NB, virtio_pci_unmap_capability(), virtnet_nic::virtqueue, and vp_free_vq().

Referenced by virtnet_close(), virtnet_open_legacy(), and virtnet_open_modern().

                                                                  {
        struct virtnet_nic *virtnet = netdev->priv;
        int i;

        for ( i = 0; i < QUEUE_NB; i++ ) {
                virtio_pci_unmap_capability ( &virtnet->virtqueue[i].notification );
                vp_free_vq ( &virtnet->virtqueue[i] );
        }

        free ( virtnet->virtqueue );
        virtnet->virtqueue = NULL;
}
static int virtnet_open_legacy ( struct net_device netdev) [static]

Open network device, legacy virtio 0.9.5.

Parameters:
netdevNetwork device
Return values:
rcReturn status code

Definition at line 201 of file virtio-net.c.

References DBGC, ENOENT, ENOMEM, features, INIT_LIST_HEAD, virtnet_nic::ioaddr, ioaddr, netdev_irq(), net_device::priv, QUEUE_NB, virtnet_nic::rx_iobufs, virtnet_nic::rx_num_iobufs, VIRTIO_CONFIG_S_DRIVER, VIRTIO_CONFIG_S_DRIVER_OK, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MTU, virtnet_free_virtqueues(), virtnet_refill_rx_virtqueue(), virtnet_nic::virtqueue, vp_find_vq(), vp_get_features(), vp_reset(), vp_set_features(), vp_set_status(), and zalloc().

Referenced by virtnet_open().

                                                             {
        struct virtnet_nic *virtnet = netdev->priv;
        unsigned long ioaddr = virtnet->ioaddr;
        u32 features;
        int i;

        /* Reset for sanity */
        vp_reset ( ioaddr );

        /* Allocate virtqueues */
        virtnet->virtqueue = zalloc ( QUEUE_NB *
                                      sizeof ( *virtnet->virtqueue ) );
        if ( ! virtnet->virtqueue )
                return -ENOMEM;

        /* Initialize rx/tx virtqueues */
        for ( i = 0; i < QUEUE_NB; i++ ) {
                if ( vp_find_vq ( ioaddr, i, &virtnet->virtqueue[i] ) == -1 ) {
                        DBGC ( virtnet, "VIRTIO-NET %p cannot register queue %d\n",
                               virtnet, i );
                        virtnet_free_virtqueues ( netdev );
                        return -ENOENT;
                }
        }

        /* Initialize rx packets */
        INIT_LIST_HEAD ( &virtnet->rx_iobufs );
        virtnet->rx_num_iobufs = 0;
        virtnet_refill_rx_virtqueue ( netdev );

        /* Disable interrupts before starting */
        netdev_irq ( netdev, 0 );

        /* Driver is ready */
        features = vp_get_features ( ioaddr );
        vp_set_features ( ioaddr, features & ( ( 1 << VIRTIO_NET_F_MAC ) |
                                               ( 1 << VIRTIO_NET_F_MTU ) ) );
        vp_set_status ( ioaddr, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK );
        return 0;
}
static int virtnet_open_modern ( struct net_device netdev) [static]

Open network device, modern virtio 1.0.

Parameters:
netdevNetwork device
Return values:
rcReturn status code

Definition at line 247 of file virtio-net.c.

References DBGC, EINVAL, ENOENT, ENOMEM, features, INIT_LIST_HEAD, netdev_irq(), net_device::priv, QUEUE_NB, virtnet_nic::rx_iobufs, virtnet_nic::rx_num_iobufs, status, virtnet_nic::vdev, VIRTIO_CONFIG_S_DRIVER_OK, VIRTIO_CONFIG_S_FAILED, VIRTIO_CONFIG_S_FEATURES_OK, VIRTIO_F_ANY_LAYOUT, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MTU, virtnet_free_virtqueues(), virtnet_refill_rx_virtqueue(), virtnet_nic::virtqueue, vpm_add_status(), vpm_find_vqs(), vpm_get_features(), vpm_get_status(), vpm_set_features(), and zalloc().

Referenced by virtnet_open().

                                                             {
        struct virtnet_nic *virtnet = netdev->priv;
        u64 features;
        u8 status;

        /* Negotiate features */
        features = vpm_get_features ( &virtnet->vdev );
        if ( ! ( features & VIRTIO_F_VERSION_1 ) ) {
                vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED );
                return -EINVAL;
        }
        vpm_set_features ( &virtnet->vdev, features & (
                ( 1ULL << VIRTIO_NET_F_MAC ) |
                ( 1ULL << VIRTIO_NET_F_MTU ) |
                ( 1ULL << VIRTIO_F_VERSION_1 ) |
                ( 1ULL << VIRTIO_F_ANY_LAYOUT ) |
                ( 1ULL << VIRTIO_F_IOMMU_PLATFORM ) ) );
        vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FEATURES_OK );

        status = vpm_get_status ( &virtnet->vdev );
        if ( ! ( status & VIRTIO_CONFIG_S_FEATURES_OK ) ) {
                DBGC ( virtnet, "VIRTIO-NET %p device didn't accept features\n",
                       virtnet );
                vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED );
                return -EINVAL;
        }

        /* Allocate virtqueues */
        virtnet->virtqueue = zalloc ( QUEUE_NB *
                                      sizeof ( *virtnet->virtqueue ) );
        if ( ! virtnet->virtqueue ) {
                vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED );
                return -ENOMEM;
        }

        /* Initialize rx/tx virtqueues */
        if ( vpm_find_vqs ( &virtnet->vdev, QUEUE_NB, virtnet->virtqueue ) ) {
                DBGC ( virtnet, "VIRTIO-NET %p cannot register queues\n",
                       virtnet );
                virtnet_free_virtqueues ( netdev );
                vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_FAILED );
                return -ENOENT;
        }

        /* Disable interrupts before starting */
        netdev_irq ( netdev, 0 );

        vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_DRIVER_OK );

        /* Initialize rx packets */
        INIT_LIST_HEAD ( &virtnet->rx_iobufs );
        virtnet->rx_num_iobufs = 0;
        virtnet_refill_rx_virtqueue ( netdev );
        return 0;
}
static int virtnet_open ( struct net_device netdev) [static]

Open network device.

Parameters:
netdevNetwork device
Return values:
rcReturn status code

Definition at line 308 of file virtio-net.c.

References net_device::priv, virtnet_nic::virtio_version, virtnet_open_legacy(), and virtnet_open_modern().

                                                      {
        struct virtnet_nic *virtnet = netdev->priv;

        if ( virtnet->virtio_version ) {
                return virtnet_open_modern ( netdev );
        } else {
                return virtnet_open_legacy ( netdev );
        }
}
static void virtnet_close ( struct net_device netdev) [static]

Close network device.

Parameters:
netdevNetwork device

Definition at line 322 of file virtio-net.c.

References free_iob(), INIT_LIST_HEAD, virtnet_nic::ioaddr, list_for_each_entry_safe, net_device::priv, virtnet_nic::rx_iobufs, virtnet_nic::rx_num_iobufs, virtnet_nic::vdev, virtnet_nic::virtio_version, virtnet_free_virtqueues(), vp_reset(), and vpm_reset().

                                                        {
        struct virtnet_nic *virtnet = netdev->priv;
        struct io_buffer *iobuf;
        struct io_buffer *next_iobuf;

        if ( virtnet->virtio_version ) {
                vpm_reset ( &virtnet->vdev );
        } else {
                vp_reset ( virtnet->ioaddr );
        }

        /* Virtqueues can be freed now that NIC is reset */
        virtnet_free_virtqueues ( netdev );

        /* Free rx iobufs */
        list_for_each_entry_safe ( iobuf, next_iobuf, &virtnet->rx_iobufs, list ) {
                free_iob ( iobuf );
        }
        INIT_LIST_HEAD ( &virtnet->rx_iobufs );
        virtnet->rx_num_iobufs = 0;
}
static int virtnet_transmit ( struct net_device netdev,
struct io_buffer iobuf 
) [static]

Transmit packet.

Parameters:
netdevNetwork device
iobufI/O buffer
Return values:
rcReturn status code

Definition at line 350 of file virtio-net.c.

References TX_INDEX, and virtnet_enqueue_iob().

                                                        {
        virtnet_enqueue_iob ( netdev, TX_INDEX, iobuf );
        return 0;
}
static void virtnet_process_tx_packets ( struct net_device netdev) [static]

Complete packet transmission.

Parameters:
netdevNetwork device

Definition at line 360 of file virtio-net.c.

References DBGC2, netdev_tx_complete(), NULL, net_device::priv, TX_INDEX, virtnet_nic::virtqueue, vring_get_buf(), and vring_more_used().

Referenced by virtnet_poll().

                                                                     {
        struct virtnet_nic *virtnet = netdev->priv;
        struct vring_virtqueue *tx_vq = &virtnet->virtqueue[TX_INDEX];

        while ( vring_more_used ( tx_vq ) ) {
                struct io_buffer *iobuf = vring_get_buf ( tx_vq, NULL );

                DBGC2 ( virtnet, "VIRTIO-NET %p tx complete iobuf %p\n",
                        virtnet, iobuf );

                netdev_tx_complete ( netdev, iobuf );
        }
}
static void virtnet_process_rx_packets ( struct net_device netdev) [static]

Complete packet reception.

Parameters:
netdevNetwork device

Definition at line 378 of file virtio-net.c.

References DBGC2, iob_len(), iob_put, iob_unput, len, io_buffer::list, list_del, netdev_rx(), net_device::priv, RX_INDEX, virtnet_nic::rx_num_iobufs, virtnet_refill_rx_virtqueue(), virtnet_nic::virtqueue, vring_get_buf(), and vring_more_used().

Referenced by virtnet_poll().

                                                                     {
        struct virtnet_nic *virtnet = netdev->priv;
        struct vring_virtqueue *rx_vq = &virtnet->virtqueue[RX_INDEX];

        while ( vring_more_used ( rx_vq ) ) {
                unsigned int len;
                struct io_buffer *iobuf = vring_get_buf ( rx_vq, &len );

                /* Release ownership of iobuf */
                list_del ( &iobuf->list );
                virtnet->rx_num_iobufs--;

                /* Update iobuf length */
                iob_unput ( iobuf, iob_len ( iobuf ) );
                iob_put ( iobuf, len - sizeof ( struct virtio_net_hdr ) );

                DBGC2 ( virtnet, "VIRTIO-NET %p rx complete iobuf %p len %zd\n",
                        virtnet, iobuf, iob_len ( iobuf ) );

                /* Pass completed packet to the network stack */
                netdev_rx ( netdev, iobuf );
        }

        virtnet_refill_rx_virtqueue ( netdev );
}
static void virtnet_poll ( struct net_device netdev) [static]

Poll for completed and received packets.

Parameters:
netdevNetwork device

Definition at line 408 of file virtio-net.c.

References virtnet_nic::ioaddr, net_device::priv, virtnet_nic::vdev, virtnet_nic::virtio_version, virtnet_process_rx_packets(), virtnet_process_tx_packets(), vp_get_isr(), and vpm_get_isr().

                                                       {
        struct virtnet_nic *virtnet = netdev->priv;

        /* Acknowledge interrupt.  This is necessary for UNDI operation and
         * interrupts that are raised despite VRING_AVAIL_F_NO_INTERRUPT being
         * set (that flag is just a hint and the hypervisor does not have to
         * honor it).
         */
        if ( virtnet->virtio_version ) {
                vpm_get_isr ( &virtnet->vdev );
        } else {
                vp_get_isr ( virtnet->ioaddr );
        }

        virtnet_process_tx_packets ( netdev );
        virtnet_process_rx_packets ( netdev );
}
static void virtnet_irq ( struct net_device netdev,
int  enable 
) [static]

Enable or disable interrupts.

Parameters:
netdevNetwork device
enableInterrupts should be enabled

Definition at line 431 of file virtio-net.c.

References net_device::priv, QUEUE_NB, virtnet_nic::virtqueue, vring_disable_cb(), and vring_enable_cb().

                                                                  {
        struct virtnet_nic *virtnet = netdev->priv;
        int i;

        for ( i = 0; i < QUEUE_NB; i++ ) {
                if ( enable )
                        vring_enable_cb ( &virtnet->virtqueue[i] );
                else
                        vring_disable_cb ( &virtnet->virtqueue[i] );
        }
}
static int virtnet_probe_legacy ( struct pci_device pci) [static]

Probe PCI device, legacy virtio 0.9.5.

Parameters:
pciPCI device
Return values:
rcReturn status code

Definition at line 458 of file virtio-net.c.

References adjust_pci_device(), alloc_etherdev(), DBGC, pci_device::dev, net_device::dev, ENOMEM, ETH_ALEN, ETH_HLEN, eth_ntoa(), features, net_device::hw_addr, virtnet_nic::ioaddr, ioaddr, pci_device::ioaddr, pci_device::irq, mac, net_device::max_pkt_len, mtu, device::name, netdev, netdev_init(), netdev_link_up(), netdev_nullify(), netdev_put(), offsetof, pci_set_drvdata(), net_device::priv, rc, register_netdev(), unregister_netdev(), VIRTIO_NET_F_MAC, VIRTIO_NET_F_MTU, vp_get(), vp_get_features(), and vp_reset().

Referenced by virtnet_probe().

                                                           {
        unsigned long ioaddr = pci->ioaddr;
        struct net_device *netdev;
        struct virtnet_nic *virtnet;
        u32 features;
        u16 mtu;
        int rc;

        /* Allocate and hook up net device */
        netdev = alloc_etherdev ( sizeof ( *virtnet ) );
        if ( ! netdev )
                return -ENOMEM;
        netdev_init ( netdev, &virtnet_operations );
        virtnet = netdev->priv;
        virtnet->ioaddr = ioaddr;
        pci_set_drvdata ( pci, netdev );
        netdev->dev = &pci->dev;

        DBGC ( virtnet, "VIRTIO-NET %p busaddr=%s ioaddr=%#lx irq=%d\n",
               virtnet, pci->dev.name, ioaddr, pci->irq );

        /* Enable PCI bus master and reset NIC */
        adjust_pci_device ( pci );
        vp_reset ( ioaddr );

        /* Load MAC address and MTU */
        features = vp_get_features ( ioaddr );
        if ( features & ( 1 << VIRTIO_NET_F_MAC ) ) {
                vp_get ( ioaddr, offsetof ( struct virtio_net_config, mac ),
                         netdev->hw_addr, ETH_ALEN );
                DBGC ( virtnet, "VIRTIO-NET %p mac=%s\n", virtnet,
                       eth_ntoa ( netdev->hw_addr ) );
        }
        if ( features & ( 1ULL << VIRTIO_NET_F_MTU ) ) {
                vp_get ( ioaddr, offsetof ( struct virtio_net_config, mtu ),
                         &mtu, sizeof ( mtu ) );
                DBGC ( virtnet, "VIRTIO-NET %p mtu=%d\n", virtnet, mtu );
                netdev->max_pkt_len = ( mtu + ETH_HLEN );
        }

        /* Register network device */
        if ( ( rc = register_netdev ( netdev ) ) != 0 )
                goto err_register_netdev;

        /* Mark link as up, control virtqueue is not used */
        netdev_link_up ( netdev );

        return 0;

        unregister_netdev ( netdev );
 err_register_netdev:
        vp_reset ( ioaddr );
        netdev_nullify ( netdev );
        netdev_put ( netdev );
        return rc;
}
static int virtnet_probe_modern ( struct pci_device pci,
int *  found_dev 
) [static]

Probe PCI device, modern virtio 1.0.

Parameters:
pciPCI device
found_devSet to non-zero if modern device was found (probe may still fail)
Return values:
rcReturn status code

Definition at line 522 of file virtio-net.c.

References adjust_pci_device(), alloc_etherdev(), virtio_pci_modern_device::cfg_cap_pos, virtio_pci_modern_device::common, common, DBG, DBGC, pci_device::dev, net_device::dev, virtio_pci_modern_device::device, device, EADDRNOTAVAIL, EINVAL, ENODEV, ENOMEM, ETH_ALEN, ETH_HLEN, eth_ntoa(), features, net_device::hw_addr, pci_device::irq, is_valid_ether_addr(), isr, virtio_pci_modern_device::isr, mac, net_device::max_pkt_len, mtu, device::name, netdev, netdev_init(), netdev_link_up(), netdev_nullify(), netdev_put(), virtio_pci_modern_device::notify_cap_pos, offsetof, virtio_pci_modern_device::pci, pci_set_drvdata(), net_device::priv, rc, register_netdev(), unregister_netdev(), virtnet_nic::vdev, VIRTIO_CONFIG_S_ACKNOWLEDGE, VIRTIO_CONFIG_S_DRIVER, VIRTIO_NET_F_MAC, VIRTIO_NET_F_MTU, VIRTIO_PCI_CAP_COMMON_CFG, VIRTIO_PCI_CAP_DEVICE_CFG, VIRTIO_PCI_CAP_ISR_CFG, VIRTIO_PCI_CAP_NOTIFY_CFG, VIRTIO_PCI_CAP_PCI_CFG, virtio_pci_find_capability(), virtio_pci_map_capability(), virtio_pci_unmap_capability(), virtnet_nic::virtio_version, vpm_add_status(), vpm_get(), vpm_get_features(), and vpm_reset().

Referenced by virtnet_probe().

                                                                           {
        struct net_device *netdev;
        struct virtnet_nic *virtnet;
        u64 features;
        u16 mtu;
        int rc, common, isr, notify, config, device;

        common = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_COMMON_CFG );
        if ( ! common ) {
                DBG ( "Common virtio capability not found!\n" );
                return -ENODEV;
        }
        *found_dev = 1;

        isr = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_ISR_CFG );
        notify = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_NOTIFY_CFG );
        config = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_PCI_CFG );
        if ( ! isr || ! notify || ! config ) {
                DBG ( "Missing virtio capabilities %i/%i/%i/%i\n",
                      common, isr, notify, config );
                return -EINVAL;
        }
        device = virtio_pci_find_capability ( pci, VIRTIO_PCI_CAP_DEVICE_CFG );

        /* Allocate and hook up net device */
        netdev = alloc_etherdev ( sizeof ( *virtnet ) );
        if ( ! netdev )
                return -ENOMEM;
        netdev_init ( netdev, &virtnet_operations );
        virtnet = netdev->priv;

        pci_set_drvdata ( pci, netdev );
        netdev->dev = &pci->dev;

        DBGC ( virtnet, "VIRTIO-NET modern %p busaddr=%s irq=%d\n",
               virtnet, pci->dev.name, pci->irq );

        virtnet->vdev.pci = pci;
        rc = virtio_pci_map_capability ( pci, common,
                sizeof ( struct virtio_pci_common_cfg ), 4,
                0, sizeof ( struct virtio_pci_common_cfg ),
                &virtnet->vdev.common );
        if ( rc )
                goto err_map_common;

        rc = virtio_pci_map_capability ( pci, isr, sizeof ( u8 ), 1,
                0, 1,
                &virtnet->vdev.isr );
        if ( rc )
                goto err_map_isr;

        virtnet->vdev.notify_cap_pos = notify;
        virtnet->vdev.cfg_cap_pos = config;

        /* Map the device capability */
        if ( device ) {
                rc = virtio_pci_map_capability ( pci, device,
                        0, 4, 0, sizeof ( struct virtio_net_config ),
                        &virtnet->vdev.device );
                if ( rc )
                        goto err_map_device;
        }

        /* Enable the PCI device */
        adjust_pci_device ( pci );

        /* Reset the device and set initial status bits */
        vpm_reset ( &virtnet->vdev );
        vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_ACKNOWLEDGE );
        vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_DRIVER );

        /* Load MAC address and MTU */
        if ( device ) {
                features = vpm_get_features ( &virtnet->vdev );
                if ( features & ( 1ULL << VIRTIO_NET_F_MAC ) ) {
                        vpm_get ( &virtnet->vdev,
                                  offsetof ( struct virtio_net_config, mac ),
                                  netdev->hw_addr, ETH_ALEN );
                        DBGC ( virtnet, "VIRTIO-NET %p mac=%s\n", virtnet,
                               eth_ntoa ( netdev->hw_addr ) );
                }
                if ( features & ( 1ULL << VIRTIO_NET_F_MTU ) ) {
                        vpm_get ( &virtnet->vdev,
                                  offsetof ( struct virtio_net_config, mtu ),
                                  &mtu, sizeof ( mtu ) );
                        DBGC ( virtnet, "VIRTIO-NET %p mtu=%d\n", virtnet,
                               mtu );
                        netdev->max_pkt_len = ( mtu + ETH_HLEN );
                }
        }

        /* We need a valid MAC address */
        if ( ! is_valid_ether_addr ( netdev->hw_addr ) ) {
                rc = -EADDRNOTAVAIL;
                goto err_mac_address;
        }

        /* Register network device */
        if ( ( rc = register_netdev ( netdev ) ) != 0 )
                goto err_register_netdev;

        /* Mark link as up, control virtqueue is not used */
        netdev_link_up ( netdev );

        virtnet->virtio_version = 1;
        return 0;

        unregister_netdev ( netdev );
err_register_netdev:
err_mac_address:
        vpm_reset ( &virtnet->vdev );
        netdev_nullify ( netdev );
        netdev_put ( netdev );

        virtio_pci_unmap_capability ( &virtnet->vdev.device );
err_map_device:
        virtio_pci_unmap_capability ( &virtnet->vdev.isr );
err_map_isr:
        virtio_pci_unmap_capability ( &virtnet->vdev.common );
err_map_common:
        return rc;
}
static int virtnet_probe ( struct pci_device pci) [static]

Probe PCI device.

Parameters:
pciPCI device
Return values:
rcReturn status code

Definition at line 651 of file virtio-net.c.

References pci_device::device, rc, virtnet_probe_legacy(), and virtnet_probe_modern().

                                                    {
        int found_modern = 0;
        int rc = virtnet_probe_modern ( pci, &found_modern );
        if ( ! found_modern && pci->device < 0x1040 ) {
                /* fall back to the legacy probe */
                rc = virtnet_probe_legacy ( pci );
        }
        return rc;
}
static void virtnet_remove ( struct pci_device pci) [static]

Variable Documentation

Initial value:
 {
        .open = virtnet_open,
        .close = virtnet_close,
        .transmit = virtnet_transmit,
        .poll = virtnet_poll,
        .irq = virtnet_irq,
}

virtio-net device operations

Definition at line 444 of file virtio-net.c.

struct pci_device_id virtnet_nics[] [static]
Initial value:
 {
PCI_ROM(0x1af4, 0x1000, "virtio-net", "Virtio Network Interface", 0),
PCI_ROM(0x1af4, 0x1041, "virtio-net", "Virtio Network Interface 1.0", 0),
}

Definition at line 679 of file virtio-net.c.

struct pci_driver virtnet_driver __pci_driver
Initial value:
 {
        .ids = virtnet_nics,
        .id_count = ( sizeof ( virtnet_nics ) / sizeof ( virtnet_nics[0] ) ),
        .probe = virtnet_probe,
        .remove = virtnet_remove,
}

Definition at line 684 of file virtio-net.c.