virtio: virtio-net driver
This commit is contained in:
parent
a27b60af75
commit
d19844d69c
5 changed files with 884 additions and 1 deletions
|
@ -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"
|
||||
|
|
18
drivers/virtio_net/Makefile
Normal file
18
drivers/virtio_net/Makefile
Normal file
|
@ -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 <minix.service.mk>
|
680
drivers/virtio_net/virtio_net.c
Normal file
680
drivers/virtio_net/virtio_net.c
Normal file
|
@ -0,0 +1,680 @@
|
|||
/* virtio net driver for MINIX 3
|
||||
*
|
||||
* Copyright (c) 2013, A. Welzel, <arne.welzel@gmail.com>
|
||||
*
|
||||
* 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 <assert.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <net/gen/ether.h>
|
||||
#include <net/gen/eth_io.h>
|
||||
|
||||
#include <minix/drivers.h>
|
||||
#include <minix/netdriver.h>
|
||||
#include <minix/sysutil.h>
|
||||
#include <minix/virtio.h>
|
||||
|
||||
#include <sys/queue.h>
|
||||
|
||||
#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);
|
||||
}
|
13
drivers/virtio_net/virtio_net.conf
Normal file
13
drivers/virtio_net/virtio_net.conf
Normal file
|
@ -0,0 +1,13 @@
|
|||
service virtio_net
|
||||
{
|
||||
type net;
|
||||
descr "Virtio network device";
|
||||
system
|
||||
UMAP
|
||||
VUMAP
|
||||
IRQCTL
|
||||
DEVIO
|
||||
;
|
||||
|
||||
pci device 1af4/1000;
|
||||
}
|
172
drivers/virtio_net/virtio_net.h
Normal file
172
drivers/virtio_net/virtio_net.h
Normal file
|
@ -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 <linux/types.h>
|
||||
#include <linux/virtio_ids.h>
|
||||
#include <linux/virtio_config.h>
|
||||
#include <linux/if_ether.h>
|
||||
#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 */
|
Loading…
Reference in a new issue