From d19844d69c9965c1d123f2e3e7e8644f7741fba8 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Sat, 15 Dec 2012 13:55:07 +0100 Subject: [PATCH] virtio: virtio-net driver --- drivers/Makefile | 2 +- drivers/virtio_net/Makefile | 18 + drivers/virtio_net/virtio_net.c | 680 +++++++++++++++++++++++++++++ drivers/virtio_net/virtio_net.conf | 13 + drivers/virtio_net/virtio_net.h | 172 ++++++++ 5 files changed, 884 insertions(+), 1 deletion(-) create mode 100644 drivers/virtio_net/Makefile create mode 100644 drivers/virtio_net/virtio_net.c create mode 100644 drivers/virtio_net/virtio_net.conf create mode 100644 drivers/virtio_net/virtio_net.h diff --git a/drivers/Makefile b/drivers/Makefile index 8e3e66e4c..380092167 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -19,7 +19,7 @@ SUBDIR= tty SUBDIR= ahci amddev atl2 at_wini audio dec21140A dp8390 dpeth \ e1000 fbd filter floppy fxp hello lance log mmc orinoco pci printer \ random readclock rtl8139 rtl8169 ti1225 tty vbox acpi \ - virtio_blk + virtio_blk virtio_net .endif .if ${MACHINE_ARCH} == "earm" diff --git a/drivers/virtio_net/Makefile b/drivers/virtio_net/Makefile new file mode 100644 index 000000000..bc2f15017 --- /dev/null +++ b/drivers/virtio_net/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for virtio_net +# +PROG= virtio_net +SRCS= virtio_net.c + +FILES=$(PROG).conf +FILESNAME=$(PROG) +FILESDIR= /etc/system.conf.d + +DPADD+= ${LIBNETDRIVER} ${LIBSYS} ${LIBVIRTIO} +LDADD+= -lnetdriver -lsys -lvirtio + +MAN= + +BINDIR?= /usr/sbin + +.include diff --git a/drivers/virtio_net/virtio_net.c b/drivers/virtio_net/virtio_net.c new file mode 100644 index 000000000..9f1749532 --- /dev/null +++ b/drivers/virtio_net/virtio_net.c @@ -0,0 +1,680 @@ +/* virtio net driver for MINIX 3 + * + * Copyright (c) 2013, A. Welzel, + * + * This software is released under the BSD license. See the LICENSE file + * included in the main directory of this source distribution for the + * license terms and conditions. + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "virtio_net.h" + +#define dput(s) do { dprintf(s); printf("\n"); } while (0) +#define dprintf(s) do { \ + printf("%s: ", name); \ + printf s; \ +} while (0) + +static struct virtio_device *net_dev; + +static const char *const name = "virtio-net"; + +enum queue {RX_Q, TX_Q, CTRL_Q}; + +/* Number of packets to work with */ +/* TODO: This should be an argument to the driver and possibly also + * depend on the queue sizes offered by this device. + */ +#define BUF_PACKETS 64 +/* Maximum size of a packet */ +#define MAX_PACK_SIZE ETH_MAX_PACK_SIZE +/* Buffer size needed for the payload of BUF_PACKETS */ +#define PACKET_BUF_SZ (BUF_PACKETS * MAX_PACK_SIZE) + +struct packet { + int idx; + struct virtio_net_hdr *vhdr; + phys_bytes phdr; + char *vdata; + phys_bytes pdata; + STAILQ_ENTRY(packet) next; +}; + +/* Allocated data chunks */ +static char *data_vir; +static phys_bytes data_phys; +static struct virtio_net_hdr *hdrs_vir; +static phys_bytes hdrs_phys; +static struct packet *packets; +static int in_rx; +static int started; + +/* Packets on this list can be given to the host */ +static STAILQ_HEAD(free_list, packet) free_list; + +/* Packets on this list are to be given to inet */ +static STAILQ_HEAD(recv_list, packet) recv_list; + +/* State about pending inet messages */ +static int rx_pending; +static message pending_rx_msg; +static int tx_pending; +static message pending_tx_msg; + +/* Various state data */ +static u8_t virtio_net_mac[6]; +static eth_stat_t virtio_net_stats; +static int spurious_interrupt; + + +/* Prototypes */ +static int virtio_net_probe(int skip); +static int virtio_net_config(void); +static int virtio_net_alloc_bufs(void); +static void virtio_net_init_queues(void); + +static void virtio_net_refill_rx_queue(void); +static void virtio_net_check_queues(void); +static void virtio_net_check_pending(void); + +static void virtio_net_fetch_iovec(iovec_s_t *iov, message *m); +static int virtio_net_cpy_to_user(message *m); +static int virtio_net_cpy_from_user(message *m); + +static void virtio_net_intr(message *m); +static void virtio_net_write(message *m); +static void virtio_net_read(message *m); +static void virtio_net_conf(message *m); +static void virtio_net_getstat(message *m); + +static void virtio_net_notify(message *m); +static void virtio_net_msg(message *m); +static void virtio_net_main_loop(void); + +static void sef_local_startup(void); +static int sef_cb_init_fresh(int type, sef_init_info_t *info); +static void sef_cb_signal_handler(int signo); + + +/* TODO: Features are pretty much ignored */ +struct virtio_feature netf[] = { + { "partial csum", VIRTIO_NET_F_CSUM, 0, 0 }, + { "given mac", VIRTIO_NET_F_MAC, 0, 0 }, + { "status ", VIRTIO_NET_F_STATUS, 0, 0 }, + { "control channel", VIRTIO_NET_F_CTRL_VQ, 0, 0 }, + { "control channel rx", VIRTIO_NET_F_CTRL_RX, 0, 0 } +}; + +static int +virtio_net_probe(int skip) +{ + /* virtio-net has at least 2 queues */ + int queues = 2; + net_dev= virtio_setup_device(0x00001, name, netf, + sizeof(netf) / sizeof(netf[0]), + 1 /* threads */, skip); + if (net_dev == NULL) + return ENXIO; + + /* If the host supports the control queue, allocate it as well */ + if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ)) + queues += 1; + + if (virtio_alloc_queues(net_dev, queues) != OK) { + virtio_free_device(net_dev); + return ENOMEM; + } + + return OK; +} + +static int +virtio_net_config(void) +{ + u32_t mac14; + u32_t mac56; + int i; + + if (virtio_host_supports(net_dev, VIRTIO_NET_F_MAC)) { + dprintf(("Mac set by host: ")); + mac14 = virtio_sread32(net_dev, 0); + mac56 = virtio_sread32(net_dev, 4); + *(u32_t*)virtio_net_mac = mac14; + *(u16_t*)(virtio_net_mac + 4) = mac56; + + for (i = 0; i < 6; i++) + printf("%02x%s", virtio_net_mac[i], + i == 5 ? "\n" : ":"); + } else { + dput(("No mac")); + } + + if (virtio_host_supports(net_dev, VIRTIO_NET_F_STATUS)) { + dput(("Current Status %x", (u32_t)virtio_sread16(net_dev, 6))); + } else { + dput(("No status")); + } + + if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_VQ)) + dput(("Host supports control channel")); + + if (virtio_host_supports(net_dev, VIRTIO_NET_F_CTRL_RX)) + dput(("Host supports control channel for RX")); + + return OK; +} + +static int +virtio_net_alloc_bufs(void) +{ + data_vir = alloc_contig(PACKET_BUF_SZ, 0, &data_phys); + + if (!data_vir) + return ENOMEM; + + hdrs_vir = alloc_contig(BUF_PACKETS * sizeof(hdrs_vir[0]), + 0, &hdrs_phys); + + if (!hdrs_vir) { + free_contig(data_vir, PACKET_BUF_SZ); + return ENOMEM; + } + + packets = malloc(BUF_PACKETS * sizeof(packets[0])); + + if (!packets) { + free_contig(data_vir, PACKET_BUF_SZ); + free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0])); + return ENOMEM; + } + + memset(data_vir, 0, PACKET_BUF_SZ); + memset(hdrs_vir, 0, BUF_PACKETS * sizeof(hdrs_vir[0])); + memset(packets, 0, BUF_PACKETS * sizeof(packets[0])); + + return OK; +} + +static void +virtio_net_init_queues(void) +{ + int i; + STAILQ_INIT(&free_list); + STAILQ_INIT(&recv_list); + + for (i = 0; i < BUF_PACKETS; i++) { + packets[i].idx = i; + packets[i].vhdr = &hdrs_vir[i]; + packets[i].phdr = hdrs_phys + i * sizeof(hdrs_vir[i]); + packets[i].vdata = data_vir + i * MAX_PACK_SIZE; + packets[i].pdata = data_phys + i * MAX_PACK_SIZE; + STAILQ_INSERT_HEAD(&free_list, &packets[i], next); + } +} + +static void +virtio_net_refill_rx_queue(void) +{ + struct vumap_phys phys[2]; + struct packet *p; + + while ((in_rx < BUF_PACKETS / 2) && !STAILQ_EMPTY(&free_list)) { + + /* peek */ + p = STAILQ_FIRST(&free_list); + /* remove */ + STAILQ_REMOVE_HEAD(&free_list, next); + + phys[0].vp_addr = p->phdr; + assert(!(phys[0].vp_addr & 1)); + phys[0].vp_size = sizeof(struct virtio_net_hdr); + + phys[1].vp_addr = p->pdata; + assert(!(phys[1].vp_addr & 1)); + phys[1].vp_size = MAX_PACK_SIZE; + + /* RX queue needs write */ + phys[0].vp_addr |= 1; + phys[1].vp_addr |= 1; + + virtio_to_queue(net_dev, RX_Q, phys, 2, p); + in_rx++; + + } + + if (in_rx == 0 && STAILQ_EMPTY(&free_list)) { + dput(("warning: rx queue underflow!")); + virtio_net_stats.ets_fifoUnder++; + } +} + +static void +virtio_net_check_queues(void) +{ + struct packet *p; + + /* Put the received packets into the recv list */ + while (virtio_from_queue(net_dev, RX_Q, (void **)&p) == 0) { + STAILQ_INSERT_TAIL(&recv_list, p, next); + in_rx--; + virtio_net_stats.ets_packetR++; + } + + /* Packets from the TX queue just indicated they are free to + * be reused now. inet already knows about them as being sent. + */ + while (virtio_from_queue(net_dev, TX_Q, (void **)&p) == 0) { + memset(p->vhdr, 0, sizeof(*p->vhdr)); + memset(p->vdata, 0, MAX_PACK_SIZE); + STAILQ_INSERT_HEAD(&free_list, p, next); + virtio_net_stats.ets_packetT++; + } +} + +static void +virtio_net_check_pending(void) +{ + int dst = 0xDEAD; + int r; + + message reply; + reply.m_type = DL_TASK_REPLY; + reply.DL_FLAGS = DL_NOFLAGS; + reply.DL_COUNT = 0; + + /* Pending read and something in recv_list? */ + if (!STAILQ_EMPTY(&recv_list) && rx_pending) { + dst = pending_rx_msg.m_source; + reply.DL_COUNT = virtio_net_cpy_to_user(&pending_rx_msg); + reply.DL_FLAGS |= DL_PACK_RECV; + rx_pending = 0; + } + + if (!STAILQ_EMPTY(&free_list) && tx_pending) { + dst = pending_tx_msg.m_source; + virtio_net_cpy_from_user(&pending_tx_msg); + reply.DL_FLAGS |= DL_PACK_SEND; + tx_pending = 0; + } + + /* Only reply if a pending request was handled */ + if (reply.DL_FLAGS != DL_NOFLAGS) + if ((r = send(dst, &reply)) != OK) + panic("%s: send to %d failed (%d)", name, dst, r); +} + +static void +virtio_net_fetch_iovec(iovec_s_t *iov, message *m) +{ + int r; + r = sys_safecopyfrom(m->m_source, m->DL_GRANT, 0, (vir_bytes)iov, + m->DL_COUNT * sizeof(iov[0])); + + if (r != OK) + panic("%s: iovec fail for %d (%d)", name, m->m_source, r); +} + +static int +virtio_net_cpy_to_user(message *m) +{ + /* Hmm, this looks so similar to cpy_from_user... TODO */ + int i, r, size, ivsz; + int left = MAX_PACK_SIZE; /* Try copying the whole packet */ + int bytes = 0; + iovec_s_t iovec[NR_IOREQS]; + struct packet *p; + + /* This should only be called if recv_list has some entries */ + assert(!STAILQ_EMPTY(&recv_list)); + + p = STAILQ_FIRST(&recv_list); + STAILQ_REMOVE_HEAD(&recv_list, next); + + virtio_net_fetch_iovec(iovec, m); + + for (i = 0; i < m->DL_COUNT && left > 0; i++) { + ivsz = iovec[i].iov_size; + size = left > ivsz ? ivsz : left; + r = sys_safecopyto(m->m_source, iovec[i].iov_grant, 0, + (vir_bytes) p->vdata + bytes, size); + + if (r != OK) + panic("%s: copy to %d failed (%d)", name, + m->m_source, + r); + + left -= size; + bytes += size; + } + + if (left != 0) + dput(("Uhm... left=%d", left)); + + /* Clean the packet */ + memset(p->vhdr, 0, sizeof(*p->vhdr)); + memset(p->vdata, 0, MAX_PACK_SIZE); + STAILQ_INSERT_HEAD(&free_list, p, next); + + return bytes; +} + +static int +sys_easy_vsafecopy_from(endpoint_t src_proc, iovec_s_t *iov, int count, + vir_bytes dst, size_t max, size_t *copied) +{ + int i, r; + size_t left = max; + vir_bytes cur_off = 0; + struct vscp_vec vv[NR_IOREQS]; + + for (i = 0; i < count && left > 0; i++) { + vv[i].v_from = src_proc; + vv[i].v_to = SELF; + vv[i].v_gid = iov[i].iov_grant; + vv[i].v_offset = 0; + vv[i].v_addr = dst + cur_off; + vv[i].v_bytes = iov[i].iov_size; + + /* More data in iov than the buffer can hold, this should be + * manageable by the caller. + */ + if (left - vv[i].v_bytes > left) { + printf("sys_easy_vsafecopy_from: buf too small!\n"); + return ENOMEM; + } + + left -= iov[i].iov_size; + cur_off += iov[i].iov_size; + } + + /* Now that we prepared the vscp_vec, we can call vsafecopy() */ + if ((r = sys_vsafecopy(vv, count)) != OK) + printf("sys_vsafecopy: failed: (%d)\n", r); + + if (copied) + *copied = cur_off; + + return OK; +} + +static int +virtio_net_cpy_from_user(message *m) +{ + /* Put user bytes into a a free packet buffer and + * then forward this packet to the TX queue. + */ + int r; + iovec_s_t iovec[NR_IOREQS]; + struct vumap_phys phys[2]; + struct packet *p; + size_t bytes; + + /* This should only be called if free_list has some entries */ + assert(!STAILQ_EMPTY(&free_list)); + + p = STAILQ_FIRST(&free_list); + STAILQ_REMOVE_HEAD(&free_list, next); + + virtio_net_fetch_iovec(iovec, m); + + r = sys_easy_vsafecopy_from(m->m_source, iovec, m->DL_COUNT, + (vir_bytes)p->vdata, MAX_PACK_SIZE, + &bytes); + + if (r != OK) + panic("%s: copy from %d failed", name, m->m_source); + + + phys[0].vp_addr = p->phdr; + assert(!(phys[0].vp_addr & 1)); + phys[0].vp_size = sizeof(struct virtio_net_hdr); + phys[1].vp_addr = p->pdata; + assert(!(phys[1].vp_addr & 1)); + phys[1].vp_size = bytes; + virtio_to_queue(net_dev, TX_Q, phys, 2, p); + return bytes; +} + +static void +virtio_net_intr(message *m) +{ + /* Check and clear interrupt flag */ + if (virtio_had_irq(net_dev)) { + virtio_net_check_queues(); + } else { + if (!spurious_interrupt) + dput(("Spurious interrupt")); + + spurious_interrupt = 1; + } + + virtio_net_check_pending(); + + virtio_irq_enable(net_dev); +} + +static void +virtio_net_write(message *m) +{ + int r; + message reply; + + reply.m_type = DL_TASK_REPLY; + reply.DL_FLAGS = DL_NOFLAGS; + reply.DL_COUNT = 0; + + + if (!STAILQ_EMPTY(&free_list)) { + /* free_list contains at least one packet, use it */ + reply.DL_COUNT = virtio_net_cpy_from_user(m); + reply.DL_FLAGS = DL_PACK_SEND; + } else { + pending_tx_msg = *m; + tx_pending = 1; + } + + if ((r = send(m->m_source, &reply)) != OK) + panic("%s: send to %d failed (%d)", name, m->m_source, r); +} + +static void +virtio_net_read(message *m) +{ + int r; + message reply; + + reply.m_type = DL_TASK_REPLY; + reply.DL_FLAGS = DL_NOFLAGS; + reply.DL_COUNT = 0; + + if (!STAILQ_EMPTY(&recv_list)) { + /* recv_list contains at least one packet, copy it */ + reply.DL_COUNT = virtio_net_cpy_to_user(m); + reply.DL_FLAGS = DL_PACK_RECV; + } else { + rx_pending = 1; + pending_rx_msg = *m; + } + + if ((r = send(m->m_source, &reply)) != OK) + panic("%s: send to %d failed (%d)", name, m->m_source, r); +} + +static void +virtio_net_conf(message *m) +{ + /* TODO: Add the multicast, broadcast filtering etc. */ + int i, r; + + message reply; + + /* If this is the first CONF message we see, fully initialize + * the device now. + */ + if (!started) { + started = 1; + virtio_device_ready(net_dev); + virtio_irq_enable(net_dev); + } + + /* Prepare reply */ + for (i = 0; i < sizeof(virtio_net_mac); i++) + ((u8_t*)reply.DL_HWADDR)[i] = virtio_net_mac[i]; + + reply.m_type = DL_CONF_REPLY; + reply.DL_STAT = OK; + reply.DL_COUNT = 0; + + if ((r = send(m->m_source, &reply)) != OK) + panic("%s: send to %d failed (%d)", name, m->m_source, r); +} + +static void +virtio_net_getstat(message *m) +{ + int r; + message reply; + + reply.m_type = DL_STAT_REPLY; + reply.DL_STAT = OK; + reply.DL_COUNT = 0; + + + r = sys_safecopyto(m->m_source, m->DL_GRANT, 0, + (vir_bytes)&virtio_net_stats, + sizeof(virtio_net_stats)); + + if (r != OK) + panic("%s: copy to %d failed (%d)", name, m->m_source, r); + + if ((r = send(m->m_source, &reply)) != OK) + panic("%s: send to %d failed (%d)", name, m->m_source, r); +} + +static void +virtio_net_notify(message *m) +{ + if (_ENDPOINT_P(m->m_source) == HARDWARE) + virtio_net_intr(m); +} + +static void +virtio_net_msg(message *m) +{ + switch (m->m_type) { + case DL_WRITEV_S: + virtio_net_write(m); + break; + case DL_READV_S: + virtio_net_read(m); + break; + case DL_CONF: + virtio_net_conf(m); + break; + case DL_GETSTAT_S: + virtio_net_getstat(m); + break; + default: + panic("%s: illegal message: %d", name, m->m_type); + } +} + +static void +virtio_net_main_loop(void) +{ + message m; + int ipc_status; + int r; + + while (TRUE) { + + virtio_net_refill_rx_queue(); + + if ((r = netdriver_receive(ANY, &m, &ipc_status)) != OK) + panic("%s: netdriver_receive failed: %d", name, r); + + if (is_ipc_notify(ipc_status)) + virtio_net_notify(&m); + else + virtio_net_msg(&m); + } +} + +int +main(int argc, char *argv[]) +{ + env_setargs(argc, argv); + sef_local_startup(); + + virtio_net_main_loop(); +} + +static void +sef_local_startup() +{ + sef_setcb_init_fresh(sef_cb_init_fresh); + sef_setcb_init_lu(sef_cb_init_fresh); + sef_setcb_init_restart(sef_cb_init_fresh); + + sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready); + sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_workfree); + + sef_setcb_signal_handler(sef_cb_signal_handler); + + sef_startup(); +} + +static int +sef_cb_init_fresh(int type, sef_init_info_t *info) +{ + long instance = 0; + env_parse("instance", "d", 0, &instance, 0, 255); + + if (virtio_net_probe((int)instance) != OK) + panic("%s: No device found", name); + + if (virtio_net_config() != OK) + panic("%s: No device found", name); + + if (virtio_net_alloc_bufs() != OK) + panic("%s: Buffer allocation failed", name); + + virtio_net_init_queues(); + + netdriver_announce(); + + return(OK); +} + +static void +sef_cb_signal_handler(int signo) +{ + if (signo != SIGTERM) + return; + + dput(("Terminating")); + + free_contig(data_vir, PACKET_BUF_SZ); + free_contig(hdrs_vir, BUF_PACKETS * sizeof(hdrs_vir[0])); + free(packets); + + virtio_reset_device(net_dev); + virtio_free_queues(net_dev); + virtio_free_device(net_dev); + net_dev = NULL; + + exit(1); +} diff --git a/drivers/virtio_net/virtio_net.conf b/drivers/virtio_net/virtio_net.conf new file mode 100644 index 000000000..46daa1e7e --- /dev/null +++ b/drivers/virtio_net/virtio_net.conf @@ -0,0 +1,13 @@ +service virtio_net +{ + type net; + descr "Virtio network device"; + system + UMAP + VUMAP + IRQCTL + DEVIO + ; + + pci device 1af4/1000; +} diff --git a/drivers/virtio_net/virtio_net.h b/drivers/virtio_net/virtio_net.h new file mode 100644 index 000000000..5edcf1cbc --- /dev/null +++ b/drivers/virtio_net/virtio_net.h @@ -0,0 +1,172 @@ +#ifndef _LINUX_VIRTIO_NET_H +#define _LINUX_VIRTIO_NET_H +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of IBM nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ +#if 0 +#include +#include +#include +#include +#endif + +/* The feature bitmap for virtio net */ +#define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */ +#define VIRTIO_NET_F_GUEST_CSUM 1 /* Guest handles pkts w/ partial csum */ +#define VIRTIO_NET_F_MAC 5 /* Host has given MAC address. */ +#define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */ +#define VIRTIO_NET_F_GUEST_TSO4 7 /* Guest can handle TSOv4 in. */ +#define VIRTIO_NET_F_GUEST_TSO6 8 /* Guest can handle TSOv6 in. */ +#define VIRTIO_NET_F_GUEST_ECN 9 /* Guest can handle TSO[6] w/ ECN in. */ +#define VIRTIO_NET_F_GUEST_UFO 10 /* Guest can handle UFO in. */ +#define VIRTIO_NET_F_HOST_TSO4 11 /* Host can handle TSOv4 in. */ +#define VIRTIO_NET_F_HOST_TSO6 12 /* Host can handle TSOv6 in. */ +#define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in. */ +#define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */ +#define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */ +#define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */ +#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */ +#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ +#define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */ +#define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */ +#define VIRTIO_NET_F_GUEST_ANNOUNCE 21 /* Guest can announce device on the + * network */ + +#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ +#define VIRTIO_NET_S_ANNOUNCE 2 /* Announcement is needed */ + +struct virtio_net_config { + /* The config defining mac address (if VIRTIO_NET_F_MAC) */ + u8_t mac[6]; + /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ + u16_t status; +} __attribute__((packed)); + +/* This is 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 { +#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset +#define VIRTIO_NET_HDR_F_DATA_VALID 2 // Csum is valid + u8_t flags; +#define VIRTIO_NET_HDR_GSO_NONE 0 // Not a GSO frame +#define VIRTIO_NET_HDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO) +#define VIRTIO_NET_HDR_GSO_UDP 3 // GSO frame, IPv4 UDP (UFO) +#define VIRTIO_NET_HDR_GSO_TCPV6 4 // GSO frame, IPv6 TCP +#define VIRTIO_NET_HDR_GSO_ECN 0x80 // TCP has ECN set + u8_t gso_type; + u16_t hdr_len; /* Ethernet + IP + tcp/udp hdrs */ + u16_t gso_size; /* Bytes to append to hdr_len per frame */ + u16_t csum_start; /* Position to start checksumming from */ + u16_t 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; + u16_t num_buffers; /* Number of merged rx buffers */ +}; + +/* + * 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 virtio_net_ctrl_hdr { + u8_t class; + u8_t cmd; +} __attribute__((packed)); + +typedef u8_t 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 filter table. + * + * 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. + */ +struct virtio_net_ctrl_mac { + u32_t entries; +#define ETH_ALEN 6 + u8_t macs[][ETH_ALEN]; +} __attribute__((packed)); + +#define VIRTIO_NET_CTRL_MAC 1 + #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 + +/* + * 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 acknowledgement + * + * 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 + +#endif /* _LINUX_VIRTIO_NET_H */ -- 2.44.0