From mboxrd@z Thu Jan 1 00:00:00 1970 From: Joe Hershberger Date: Mon, 22 Oct 2018 23:09:18 +0000 Subject: [U-Boot] [PATCH 05/27] virtio: Add net driver support In-Reply-To: <1537710145-1888-6-git-send-email-bmeng.cn@gmail.com> References: <1537710145-1888-1-git-send-email-bmeng.cn@gmail.com> <1537710145-1888-6-git-send-email-bmeng.cn@gmail.com> Message-ID: List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de On Sun, Sep 23, 2018 at 8:48 AM Bin Meng wrote: > > From: Tuomas Tynkkynen > > This adds virtio net device driver support. > > Signed-off-by: Tuomas Tynkkynen > Signed-off-by: Bin Meng > --- > > drivers/virtio/Kconfig | 7 ++ > drivers/virtio/Makefile | 1 + > drivers/virtio/virtio_net.c | 215 +++++++++++++++++++++++++++++++++++ > drivers/virtio/virtio_net.h | 268 ++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 491 insertions(+) > create mode 100644 drivers/virtio/virtio_net.c > create mode 100644 drivers/virtio/virtio_net.h > > diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig > index 60cfaf8..ceea03a 100644 > --- a/drivers/virtio/Kconfig > +++ b/drivers/virtio/Kconfig > @@ -18,4 +18,11 @@ config VIRTIO_MMIO > This driver provides support for memory mapped virtio > platform device driver. > > +config VIRTIO_NET > + bool "virtio net driver" > + depends on VIRTIO > + help > + This is the virtual net driver for virtio. It can be used with > + QEMU based targets. > + > endmenu > diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile > index 2e48785..b7764f1 100644 > --- a/drivers/virtio/Makefile > +++ b/drivers/virtio/Makefile > @@ -5,3 +5,4 @@ > > obj-y += virtio-uclass.o virtio_ring.o > obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o > +obj-$(CONFIG_VIRTIO_NET) += virtio_net.o > diff --git a/drivers/virtio/virtio_net.c b/drivers/virtio/virtio_net.c > new file mode 100644 > index 0000000..1ab1513 > --- /dev/null > +++ b/drivers/virtio/virtio_net.c > @@ -0,0 +1,215 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2018, Tuomas Tynkkynen > + * Copyright (C) 2018, Bin Meng > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include "virtio_net.h" > + > +/* Amount of buffers to keep in the RX virtqueue */ > +#define VIRTIO_NET_NUM_RX_BUFS 32 > + > +/* > + * This value comes from the VirtIO spec: 1500 for maximum packet size, > + * 14 for the Ethernet header, 12 for virtio_net_hdr. In total 1526 bytes. > + */ > +#define VIRTIO_NET_RX_BUF_SIZE 1526 > + > +struct virtio_net_priv { > + union { > + struct virtqueue *vqs[2]; > + struct { > + struct virtqueue *rx_vq; > + struct virtqueue *tx_vq; > + }; > + }; > + > + char rx_buff[VIRTIO_NET_NUM_RX_BUFS][VIRTIO_NET_RX_BUF_SIZE]; > + bool rx_running; > +}; > + > +/* > + * For simplicity, the driver only negotiates the VIRTIO_NET_F_MAC feature. > + * For the VIRTIO_NET_F_STATUS feature, we don't negotiate it, hence per spec > + * we should assume the link is always active. > + */ > +static u32 feature[] = { > + VIRTIO_NET_F_MAC > +}; > + > +static u32 feature_legacy[] = { > + VIRTIO_NET_F_MAC > +}; > + > +static int virtio_net_start(struct udevice *dev) > +{ > + struct virtio_net_priv *priv = dev_get_priv(dev); > + struct virtio_sg sg; > + struct virtio_sg *sgs[] = { &sg }; > + int i; > + > + if (!priv->rx_running) { > + /* receive buffer length is always 1526 */ > + sg.length = VIRTIO_NET_RX_BUF_SIZE; > + > + /* setup the receive buffer address */ > + for (i = 0; i < VIRTIO_NET_NUM_RX_BUFS; i++) { > + sg.addr = priv->rx_buff[i]; It's kinda peculiar the way this is used in place multiple times. > + virtqueue_add(priv->rx_vq, sgs, 0, 1); I hope this copies the structure since it is stack memory and modified on each iteration. Probably justifies a comment. > + } > + > + virtqueue_kick(priv->rx_vq); > + > + /* setup the receive queue only once */ > + priv->rx_running = true; > + } > + > + return 0; > +} > + > +static int virtio_net_send(struct udevice *dev, void *packet, int length) > +{ > + struct virtio_net_priv *priv = dev_get_priv(dev); > + struct virtio_net_hdr hdr; > + struct virtio_sg hdr_sg = { &hdr, sizeof(hdr) }; > + struct virtio_sg data_sg = { packet, length }; > + struct virtio_sg *sgs[] = { &hdr_sg, &data_sg }; > + int ret; > + > + memset(&hdr, 0, sizeof(struct virtio_net_hdr)); > + > + ret = virtqueue_add(priv->tx_vq, sgs, 2, 0); > + if (ret) > + return ret; > + > + virtqueue_kick(priv->tx_vq); > + > + while (1) { > + if (virtqueue_get_buf(priv->tx_vq, NULL)) > + break; Potential infinite loop? > + } > + > + return 0; > +} > + > +static int virtio_net_recv(struct udevice *dev, int flags, uchar **packetp) > +{ > + struct virtio_net_priv *priv = dev_get_priv(dev); > + unsigned int len; > + void *buf; > + > + buf = virtqueue_get_buf(priv->rx_vq, &len); > + if (!buf) > + return -EAGAIN; > + > + *packetp = buf + sizeof(struct virtio_net_hdr); > + return len - sizeof(struct virtio_net_hdr); > +} > + > +static int virtio_net_free_pkt(struct udevice *dev, uchar *packet, int length) > +{ > + struct virtio_net_priv *priv = dev_get_priv(dev); > + void *buf = packet - sizeof(struct virtio_net_hdr); > + struct virtio_sg sg = { buf, VIRTIO_NET_RX_BUF_SIZE }; > + struct virtio_sg *sgs[] = { &sg }; > + > + /* Put the buffer back to the rx ring */ > + virtqueue_add(priv->rx_vq, sgs, 0, 1); So does that mean that the virtqueue_get_buf() function removed it? > + > + return 0; > +} > + > +static void virtio_net_stop(struct udevice *dev) > +{ > + /* > + * There is no way to stop the queue from running, unless we issue > + * a reset to the virtio device, and re-do the queue initialization > + * from the beginning. > + */ > +} > + > +static int virtio_net_write_hwaddr(struct udevice *dev) > +{ > + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent); > + struct eth_pdata *pdata = dev_get_platdata(dev); > + int i; > + > + /* > + * v1.0 compliant device's MAC address is set through control channel, > + * which we don't support for now. > + */ > + if (!uc_priv->legacy) > + return -ENOSYS; > + > + for (i = 0; i < sizeof(pdata->enetaddr); i++) { > + virtio_cwrite8(dev, > + offsetof(struct virtio_net_config, mac) + i, > + pdata->enetaddr[i]); > + } > + > + return 0; > +} > + > +static int virtio_net_read_rom_hwaddr(struct udevice *dev) > +{ > + struct eth_pdata *pdata = dev_get_platdata(dev); > + > + if (!pdata) > + return -ENOSYS; > + > + if (virtio_has_feature(dev, VIRTIO_NET_F_MAC)) { > + virtio_cread_bytes(dev, > + offsetof(struct virtio_net_config, mac), > + pdata->enetaddr, sizeof(pdata->enetaddr)); > + } > + > + return 0; > +} > + > +static int virtio_net_bind(struct udevice *dev) > +{ > + struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent); > + > + /* Indicate what driver features we support */ > + virtio_driver_features_init(uc_priv, feature, ARRAY_SIZE(feature), > + feature_legacy, ARRAY_SIZE(feature_legacy)); > + > + return 0; > +} > + > +static int virtio_net_probe(struct udevice *dev) > +{ > + struct virtio_net_priv *priv = dev_get_priv(dev); > + int ret; > + Seems like priv->rx_running should be initialized to false here. > + ret = virtio_find_vqs(dev, 2, priv->vqs); > + if (ret < 0) > + return ret; > + > + return 0; > +} > + > +static const struct eth_ops virtio_net_ops = { > + .start = virtio_net_start, > + .send = virtio_net_send, > + .recv = virtio_net_recv, > + .free_pkt = virtio_net_free_pkt, > + .stop = virtio_net_stop, > + .write_hwaddr = virtio_net_write_hwaddr, > + .read_rom_hwaddr = virtio_net_read_rom_hwaddr, > +}; > + > +U_BOOT_DRIVER(virtio_net) = { > + .name = VIRTIO_NET_DRV_NAME, > + .id = UCLASS_ETH, > + .bind = virtio_net_bind, > + .probe = virtio_net_probe, > + .ops = &virtio_net_ops, > + .priv_auto_alloc_size = sizeof(struct virtio_net_priv), > + .platdata_auto_alloc_size = sizeof(struct eth_pdata), > +}; > diff --git a/drivers/virtio/virtio_net.h b/drivers/virtio/virtio_net.h > new file mode 100644 > index 0000000..c92bae5 > --- /dev/null > +++ b/drivers/virtio/virtio_net.h > @@ -0,0 +1,268 @@ > +/* SPDX-License-Identifier: BSD-3-Clause */ > +/* > + * Copyright (C) 2018, Tuomas Tynkkynen > + * Copyright (C) 2018, Bin Meng > + * > + * From Linux kernel include/uapi/linux/virtio_net.h > + */ > + > +#ifndef _LINUX_VIRTIO_NET_H > +#define _LINUX_VIRTIO_NET_H > + > +/* TODO: needs to be removed! */ > +#define ETH_ALEN 6 > + > +/* The feature bitmap for virtio net */ > + > +/* Host handles pkts w/ partial csum */ > +#define VIRTIO_NET_F_CSUM 0 > +/* Guest handles pkts w/ partial csum */ > +#define VIRTIO_NET_F_GUEST_CSUM 1 > +/* Dynamic offload configuration */ > +#define VIRTIO_NET_F_CTRL_GUEST_OFFLOADS 2 > +/* Initial MTU advice */ > +#define VIRTIO_NET_F_MTU 3 > +/* Host has given MAC address */ > +#define VIRTIO_NET_F_MAC 5 > +/* Guest can handle TSOv4 in */ > +#define VIRTIO_NET_F_GUEST_TSO4 7 > +/* Guest can handle TSOv6 in */ > +#define VIRTIO_NET_F_GUEST_TSO6 8 > +/* Guest can handle TSO[6] w/ ECN in */ > +#define VIRTIO_NET_F_GUEST_ECN 9 > +/* Guest can handle UFO in */ > +#define VIRTIO_NET_F_GUEST_UFO 10 > +/* Host can handle TSOv4 in */ > +#define VIRTIO_NET_F_HOST_TSO4 11 > +/* Host can handle TSOv6 in */ > +#define VIRTIO_NET_F_HOST_TSO6 12 > +/* Host can handle TSO[6] w/ ECN in */ > +#define VIRTIO_NET_F_HOST_ECN 13 > +/* Host can handle UFO in */ > +#define VIRTIO_NET_F_HOST_UFO 14 > +/* Host can merge receive buffers */ > +#define VIRTIO_NET_F_MRG_RXBUF 15 > +/* virtio_net_config.status available */ > +#define VIRTIO_NET_F_STATUS 16 > +/* Control channel available */ > +#define VIRTIO_NET_F_CTRL_VQ 17 > +/* Control channel RX mode support */ > +#define VIRTIO_NET_F_CTRL_RX 18 > +/* Control channel VLAN filtering */ > +#define VIRTIO_NET_F_CTRL_VLAN 19 > +/* Extra RX mode control support */ > +#define VIRTIO_NET_F_CTRL_RX_EXTRA 20 > +/* Guest can announce device on the network */ > +#define VIRTIO_NET_F_GUEST_ANNOUNCE 21 > +/* Device supports receive flow steering */ > +#define VIRTIO_NET_F_MQ 22 > +/* Set MAC address */ > +#define VIRTIO_NET_F_CTRL_MAC_ADDR 23 > +/* Device set linkspeed and duplex */ > +#define VIRTIO_NET_F_SPEED_DUPLEX 63 > + > +#ifndef VIRTIO_NET_NO_LEGACY > +/* Host handles pkts w/ any GSO type */ > +#define VIRTIO_NET_F_GSO 6 > +#endif /* VIRTIO_NET_NO_LEGACY */ > + > +#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ > +#define VIRTIO_NET_S_ANNOUNCE 2 /* Announcement is needed */ > + > +struct __packed virtio_net_config { > + /* The config defining mac address (if VIRTIO_NET_F_MAC) */ > + __u8 mac[ETH_ALEN]; > + /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ > + __u16 status; > + /* > + * Maximum number of each of transmit and receive queues; > + * see VIRTIO_NET_F_MQ and VIRTIO_NET_CTRL_MQ. > + * Legal values are between 1 and 0x8000 > + */ > + __u16 max_virtqueue_pairs; > + /* Default maximum transmit unit advice */ > + __u16 mtu; > + /* > + * speed, in units of 1Mb. All values 0 to INT_MAX are legal. > + * Any other value stands for unknown. > + */ > + __u32 speed; > + /* > + * 0x00 - half duplex > + * 0x01 - full duplex > + * Any other value stands for unknown. > + */ > + __u8 duplex; > +}; > + > +/* > + * This header comes first in the scatter-gather list. If you don't > + * specify GSO or CSUM features, you can simply ignore the header. > + * > + * This is bitwise-equivalent to the legacy struct virtio_net_hdr_mrg_rxbuf, > + * only flattened. > + */ > +struct virtio_net_hdr_v1 { > +#define VIRTIO_NET_HDR_F_NEEDS_CSUM 0x01 /* Use csum_start, csum_offset */ > +#define VIRTIO_NET_HDR_F_DATA_VALID 0x02 /* Csum is valid */ > + __u8 flags; > +#define VIRTIO_NET_HDR_GSO_NONE 0x00 /* Not a GSO frame */ > +#define VIRTIO_NET_HDR_GSO_TCPV4 0x01 /* GSO frame, IPv4 TCP (TSO) */ > +#define VIRTIO_NET_HDR_GSO_UDP 0x03 /* GSO frame, IPv4 UDP (UFO) */ > +#define VIRTIO_NET_HDR_GSO_TCPV6 0x04 /* GSO frame, IPv6 TCP */ > +#define VIRTIO_NET_HDR_GSO_ECN 0x80 /* TCP has ECN set */ > + __u8 gso_type; > + __virtio16 hdr_len; /* Ethernet + IP + tcp/udp hdrs */ > + __virtio16 gso_size; /* Bytes to append to hdr_len per frame */ > + __virtio16 csum_start; /* Position to start checksumming from */ > + __virtio16 csum_offset; /* Offset after that to place checksum */ > + __virtio16 num_buffers; /* Number of merged rx buffers */ > +}; > + > +#ifndef VIRTIO_NET_NO_LEGACY > +/* > + * This header comes first in the scatter-gather list. > + * > + * For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must > + * be the first element of the scatter-gather list. If you don't > + * specify GSO or CSUM features, you can simply ignore the header. > + */ > +struct virtio_net_hdr { > + /* See VIRTIO_NET_HDR_F_* */ > + __u8 flags; > + /* See VIRTIO_NET_HDR_GSO_* */ > + __u8 gso_type; > + __virtio16 hdr_len; /* Ethernet + IP + tcp/udp hdrs */ > + __virtio16 gso_size; /* Bytes to append to hdr_len per frame */ > + __virtio16 csum_start; /* Position to start checksumming from */ > + __virtio16 csum_offset; /* Offset after that to place checksum */ > +}; > + > +/* > + * This is the version of the header to use when the MRG_RXBUF > + * feature has been negotiated. > + */ > +struct virtio_net_hdr_mrg_rxbuf { > + struct virtio_net_hdr hdr; > + __virtio16 num_buffers; /* Number of merged rx buffers */ > +}; > +#endif /* ...VIRTIO_NET_NO_LEGACY */ > + > +/* > + * Control virtqueue data structures > + * > + * The control virtqueue expects a header in the first sg entry > + * and an ack/status response in the last entry. Data for the > + * command goes in between. > + */ > +struct __packed virtio_net_ctrl_hdr { > + __u8 class; > + __u8 cmd; > +}; > + > +typedef __u8 virtio_net_ctrl_ack; > + > +#define VIRTIO_NET_OK 0 > +#define VIRTIO_NET_ERR 1 > + > +/* > + * Control the RX mode, ie. promisucous, allmulti, etc... > + * > + * All commands require an "out" sg entry containing a 1 byte state value, > + * zero = disable, non-zero = enable. > + * > + * Commands 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature. > + * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA. > + */ > +#define VIRTIO_NET_CTRL_RX 0 > +#define VIRTIO_NET_CTRL_RX_PROMISC 0 > +#define VIRTIO_NET_CTRL_RX_ALLMULTI 1 > +#define VIRTIO_NET_CTRL_RX_ALLUNI 2 > +#define VIRTIO_NET_CTRL_RX_NOMULTI 3 > +#define VIRTIO_NET_CTRL_RX_NOUNI 4 > +#define VIRTIO_NET_CTRL_RX_NOBCAST 5 > + > +/* > + * Control the MAC > + * > + * The MAC filter table is managed by the hypervisor, the guest should assume > + * the size is infinite. Filtering should be considered non-perfect, ie. based > + * on hypervisor resources, the guest may received packets from sources not > + * specified in the filter list. > + * > + * In addition to the class/cmd header, the TABLE_SET command requires two > + * out scatterlists. Each contains a 4 byte count of entries followed by a > + * concatenated byte stream of the ETH_ALEN MAC addresses. The first sg list > + * contains unicast addresses, the second is for multicast. This functionality > + * is present if the VIRTIO_NET_F_CTRL_RX feature is available. > + * > + * The ADDR_SET command requests one out scatterlist, it contains a 6 bytes MAC > + * address. This functionality is present if the VIRTIO_NET_F_CTRL_MAC_ADDR > + * feature is available. > + */ > +struct __packed virtio_net_ctrl_mac { > + __virtio32 entries; > + __u8 macs[][ETH_ALEN]; > +}; > + > +#define VIRTIO_NET_CTRL_MAC 1 > +#define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 > +#define VIRTIO_NET_CTRL_MAC_ADDR_SET 1 > + > +/* > + * Control VLAN filtering > + * > + * The VLAN filter table is controlled via a simple ADD/DEL interface. VLAN IDs > + * not added may be filterd by the hypervisor. Del is the opposite of add. Both > + * commands expect an out entry containing a 2 byte VLAN ID. VLAN filterting is > + * available with the VIRTIO_NET_F_CTRL_VLAN feature bit. > + */ > +#define VIRTIO_NET_CTRL_VLAN 2 > +#define VIRTIO_NET_CTRL_VLAN_ADD 0 > +#define VIRTIO_NET_CTRL_VLAN_DEL 1 > + > +/* > + * Control link announce acknowledgment > + * > + * The command VIRTIO_NET_CTRL_ANNOUNCE_ACK is used to indicate that driver has > + * recevied the notification; device would clear the VIRTIO_NET_S_ANNOUNCE bit > + * in the status field after it receives this command. > + */ > +#define VIRTIO_NET_CTRL_ANNOUNCE 3 > +#define VIRTIO_NET_CTRL_ANNOUNCE_ACK 0 > + > +/* > + * Control receive flow steering > + * > + * The command VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET enables receive flow steering, > + * specifying the number of the transmit and receive queues that will be used. > + * After the command is consumed and acked by the device, the device will not > + * steer new packets on receive virtqueues other than specified nor read from > + * transmit virtqueues other than specified. Accordingly, driver should not > + * transmit new packets on virtqueues other than specified. > + */ > +struct virtio_net_ctrl_mq { > + __virtio16 virtqueue_pairs; > +}; > + > +#define VIRTIO_NET_CTRL_MQ 4 > +#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0 > +#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1 > +#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 > + > +/* > + * Control network offloads > + * > + * Reconfigures the network offloads that guest can handle. > + * > + * Available with the VIRTIO_NET_F_CTRL_GUEST_OFFLOADS feature bit. > + * > + * Command data format matches the feature bit mask exactly. > + * > + * See VIRTIO_NET_F_GUEST_* for the list of offloads > + * that can be enabled/disabled. > + */ > +#define VIRTIO_NET_CTRL_GUEST_OFFLOADS 5 > +#define VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET 0 > + > +#endif /* _LINUX_VIRTIO_NET_H */ > -- > 2.7.4 > > _______________________________________________ > U-Boot mailing list > U-Boot at lists.denx.de > https://lists.denx.de/listinfo/u-boot