This gives virtio-enabled boards an easy route to network connectivity:
qemu-system-aarch64 -M virt -serial mon:stdio -trace file=/dev/null \
-kernel images/barebox-dt-2nd.img -cpu cortex-a57 -nographic \
-device virtio-net-device,netdev=network0 -netdev
tap,id=network0,ifname=tap0
The tap0 interface created by QEMU can then be bridged/listened on.
Signed-off-by: Ahmad Fatoum
---
Documentation/user/virtio.rst | 2 +-
drivers/net/Kconfig | 7 +
drivers/net/Makefile| 1 +
drivers/net/virtio.c| 236 +
include/linux/virtio_config.h | 7 +-
include/uapi/linux/virtio_net.h | 358
6 files changed, 608 insertions(+), 3 deletions(-)
create mode 100644 drivers/net/virtio.c
create mode 100644 include/uapi/linux/virtio_net.h
diff --git a/Documentation/user/virtio.rst b/Documentation/user/virtio.rst
index dde47d5f82b1..d944fa4821b1 100644
--- a/Documentation/user/virtio.rst
+++ b/Documentation/user/virtio.rst
@@ -35,7 +35,7 @@ queues configuration and buffer transfers are nearly
identical. Both MMIO
and non-legacy PCI are supported in barebox.
The VirtIO spec defines a lots of VirtIO device types, however at present only
-block, console, input and RNG devices are supported.
+block, network, console, input and RNG devices are supported.
Build Instructions
--
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 397164f3f175..4947296f278a 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -258,6 +258,13 @@ config DRIVER_NET_EFI_SNP
bool "EFI SNP ethernet driver"
depends on EFI_BOOTUP
+config DRIVER_NET_VIRTIO
+ bool "virtio net driver"
+ depends on VIRTIO
+ help
+ This is the virtual net driver for virtio. It can be used with
+ QEMU based targets.
+
config DRIVER_NET_AG71XX
bool "Atheros AG71xx ethernet driver"
depends on MACH_MIPS_ATH79
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index b1aa9571fc72..1921d0d9f91c 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -36,4 +36,5 @@ obj-$(CONFIG_DRIVER_NET_SMC9) += smc9.o
obj-$(CONFIG_DRIVER_NET_TAP) += tap.o
obj-$(CONFIG_DRIVER_NET_TSE) += altera_tse.o
obj-$(CONFIG_DRIVER_NET_EFI_SNP) += efi-snp.o
+obj-$(CONFIG_DRIVER_NET_VIRTIO)+= virtio.o
obj-$(CONFIG_DRIVER_NET_AG71XX)+= ag71xx.o
diff --git a/drivers/net/virtio.c b/drivers/net/virtio.c
new file mode 100644
index ..275588e15c50
--- /dev/null
+++ b/drivers/net/virtio.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Tuomas Tynkkynen
+ * Copyright (C) 2018, Bin Meng
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* 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;
+ int net_hdr_len;
+ struct eth_device edev;
+ struct virtio_device *vdev;
+};
+
+static inline struct virtio_net_priv *to_priv(struct eth_device *edev)
+{
+ return container_of(edev, struct virtio_net_priv, edev);
+}
+
+static int virtio_net_start(struct eth_device *edev)
+{
+ struct virtio_net_priv *priv = to_priv(edev);
+ struct virtio_sg sg;
+ struct virtio_sg *sgs[] = { };
+ 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];
+ virtqueue_add(priv->rx_vq, sgs, 0, 1);
+ }
+
+ virtqueue_kick(priv->rx_vq);
+
+ /* setup the receive queue only once */
+ priv->rx_running = true;
+ }
+
+ return 0;
+}
+
+static int virtio_net_send(struct eth_device *edev, void *packet, int length)
+{
+ struct virtio_net_priv *priv = to_priv(edev);
+ struct virtio_net_hdr_v1 hdr_v1;
+ struct virtio_net_hdr hdr;
+ struct virtio_sg hdr_sg;
+ struct virtio_sg data_sg = { packet, length };
+ struct virtio_sg *sgs[] = { _sg, _sg };
+ int ret;
+
+ if (priv->net_hdr_len == sizeof(struct virtio_net_hdr))
+ hdr_sg.addr =
+