aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvik Sil <aviksil@linux.vnet.ibm.com>2013-09-23 14:07:35 +0530
committerNikunj A Dadhania <nikunj@linux.vnet.ibm.com>2013-10-04 12:00:13 +0530
commit46a32aa9c74be70b7f247f4aae00951755784973 (patch)
tree864720ec001977fcd2e310735f02e70c6a0c747d
parenteb5994df206de4b36501a978c00309962d6dd3f5 (diff)
downloadSLOF-46a32aa9c74be70b7f247f4aae00951755784973.zip
SLOF-46a32aa9c74be70b7f247f4aae00951755784973.tar.gz
SLOF-46a32aa9c74be70b7f247f4aae00951755784973.tar.bz2
Add virtio-net driver in libvirtio
Signed-off-by: Avik Sil <aviksil@linux.vnet.ibm.com> Signed-off-by: Nikunj A Dadhania <nikunj@linux.vnet.ibm.com>
-rw-r--r--board-qemu/slof/virtio-net.fs50
-rw-r--r--clients/net-snk/kernel/modules.c1
-rw-r--r--lib/libvirtio/Makefile2
-rw-r--r--lib/libvirtio/virtio-net.c369
-rw-r--r--lib/libvirtio/virtio-net.h43
-rw-r--r--lib/libvirtio/virtio.code43
-rw-r--r--lib/libvirtio/virtio.in5
7 files changed, 503 insertions, 10 deletions
diff --git a/board-qemu/slof/virtio-net.fs b/board-qemu/slof/virtio-net.fs
index 8170f99..80ed15c 100644
--- a/board-qemu/slof/virtio-net.fs
+++ b/board-qemu/slof/virtio-net.fs
@@ -18,21 +18,55 @@ INSTANCE VARIABLE obp-tftp-package
/vd-len BUFFER: virtiodev
virtiodev virtio-setup-vd
+0 VALUE virtio-net-priv
+0 VALUE open-count
: open ( -- okay? )
- open IF
- \ my-unit 1 rtas-set-tce-bypass
- my-args s" obp-tftp" $open-package obp-tftp-package !
- true
+ open-count 0= IF
+ open IF
+ \ my-unit 1 rtas-set-tce-bypass
+ my-args s" obp-tftp" $open-package obp-tftp-package !
+ s" local-mac-address" get-node get-property not IF
+ virtiodev virtio-net-open dup not IF ." virtio-net-open failed" EXIT THEN
+ drop TO virtio-net-priv
+ THEN
+ true
+ ELSE
+ false
+ THEN
ELSE
- false
+ true
THEN
+ open-count 1 + to open-count
;
+
: close ( -- )
- s" close" obp-tftp-package @ $call-method
- \ my-unit 0 rtas-set-tce-bypass
- close
+ open-count 0> IF
+ open-count 1 - dup to open-count
+ 0= IF
+ s" close" obp-tftp-package @ $call-method
+ virtio-net-priv virtio-net-close
+ \ my-unit 0 rtas-set-tce-bypass
+ close
+ THEN
+ THEN
+;
+
+: read ( buf len -- actual )
+ dup IF
+ virtio-net-read
+ ELSE
+ nip
+ THEN
+;
+
+: write ( buf len -- actual )
+ dup IF
+ virtio-net-write
+ ELSE
+ nip
+ THEN
;
: load ( addr -- len )
diff --git a/clients/net-snk/kernel/modules.c b/clients/net-snk/kernel/modules.c
index 4484bc3..bf4bbfe 100644
--- a/clients/net-snk/kernel/modules.c
+++ b/clients/net-snk/kernel/modules.c
@@ -38,7 +38,6 @@ typedef struct {
static const mod_descriptor_t modules[] = {
{ "net_e1000", MOD_TYPE_NETWORK },
{ "net_bcm", MOD_TYPE_NETWORK },
- { "net_virtio", MOD_TYPE_NETWORK },
{ NULL, 0 }
};
diff --git a/lib/libvirtio/Makefile b/lib/libvirtio/Makefile
index e61457f..430f9a7 100644
--- a/lib/libvirtio/Makefile
+++ b/lib/libvirtio/Makefile
@@ -22,7 +22,7 @@ TARGET = ../libvirtio.a
all: $(TARGET)
-SRCS = virtio.c virtio-blk.c p9.c virtio-9p.c virtio-scsi.c
+SRCS = virtio.c virtio-blk.c p9.c virtio-9p.c virtio-scsi.c virtio-net.c
OBJS = $(SRCS:%.c=%.o)
diff --git a/lib/libvirtio/virtio-net.c b/lib/libvirtio/virtio-net.c
new file mode 100644
index 0000000..275da3a
--- /dev/null
+++ b/lib/libvirtio/virtio-net.c
@@ -0,0 +1,369 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * This is the implementation for the Virtio network device driver. Details
+ * about the virtio-net interface can be found in Rusty Russel's "Virtio PCI
+ * Card Specification v0.8.10", appendix C, which can be found here:
+ *
+ * http://ozlabs.org/~rusty/virtio-spec/virtio-spec.pdf
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <helpers.h>
+#include <cache.h>
+#include <byteorder.h>
+#include "virtio.h"
+#include "virtio-net.h"
+
+#undef DEBUG
+//#define DEBUG
+#ifdef DEBUG
+# define dprintf(fmt...) printf(fmt)
+#else
+# define dprintf(fmt...)
+#endif
+
+#define sync() asm volatile (" sync \n" ::: "memory")
+
+/* PCI virtio header offsets */
+#define VIRTIOHDR_DEVICE_FEATURES 0
+#define VIRTIOHDR_GUEST_FEATURES 4
+#define VIRTIOHDR_QUEUE_ADDRESS 8
+#define VIRTIOHDR_QUEUE_SIZE 12
+#define VIRTIOHDR_QUEUE_SELECT 14
+#define VIRTIOHDR_QUEUE_NOTIFY 16
+#define VIRTIOHDR_DEVICE_STATUS 18
+#define VIRTIOHDR_ISR_STATUS 19
+#define VIRTIOHDR_DEVICE_CONFIG 20
+#define VIRTIOHDR_MAC_ADDRESS 20
+
+struct virtio_device virtiodev;
+struct vqs vq[2]; /* Information about virtqueues */
+
+/* See Virtio Spec, appendix C, "Device Operation" */
+struct virtio_net_hdr {
+ uint8_t flags;
+ uint8_t gso_type;
+ uint16_t hdr_len;
+ uint16_t gso_size;
+ uint16_t csum_start;
+ uint16_t csum_offset;
+ // uint16_t num_buffers; /* Only if VIRTIO_NET_F_MRG_RXBUF */
+};
+
+static uint16_t last_rx_idx; /* Last index in RX "used" ring */
+
+/**
+ * Module init for virtio via PCI.
+ * Checks whether we're reponsible for the given device and set up
+ * the virtqueue configuration.
+ */
+static int virtionet_init_pci(struct virtio_device *dev)
+{
+ int i;
+
+ dprintf("virtionet: doing virtionet_init_pci!\n");
+
+ if (!dev)
+ return -1;
+
+ virtiodev.base = dev->base;
+ virtiodev.type = dev->type;
+
+ /* Reset device */
+ virtio_reset_device(&virtiodev);
+
+ /* The queue information can be retrieved via the virtio header that
+ * can be found in the I/O BAR. First queue is the receive queue,
+ * second the transmit queue, and the forth is the control queue for
+ * networking options.
+ * We are only interested in the receive and transmit queue here. */
+
+ for (i=VQ_RX; i<=VQ_TX; i++) {
+ /* Select ring (0=RX, 1=TX): */
+ vq[i].id = i-VQ_RX;
+ ci_write_16(virtiodev.base+VIRTIOHDR_QUEUE_SELECT,
+ cpu_to_le16(vq[i].id));
+
+ vq[i].size = le16_to_cpu(ci_read_16(virtiodev.base+VIRTIOHDR_QUEUE_SIZE));
+ vq[i].desc = SLOF_alloc_mem_aligned(virtio_vring_size(vq[i].size), 4096);
+ if (!vq[i].desc) {
+ printf("memory allocation failed!\n");
+ return -1;
+ }
+ memset(vq[i].desc, 0, virtio_vring_size(vq[i].size));
+ ci_write_32(virtiodev.base+VIRTIOHDR_QUEUE_ADDRESS,
+ cpu_to_le32((long)vq[i].desc / 4096));
+ vq[i].avail = (void*)vq[i].desc
+ + vq[i].size * sizeof(struct vring_desc);
+ vq[i].used = (void*)VQ_ALIGN((long)vq[i].avail
+ + vq[i].size * sizeof(struct vring_avail));
+
+ dprintf("%i: vq.id = %llx\nvq.size =%x\n vq.avail =%p\nvq.used=%p\n",
+ i, vq[i].id, vq[i].size, vq[i].avail, vq[i].used);
+ }
+
+ /* Acknowledge device. */
+ virtio_set_status(&virtiodev, VIRTIO_STAT_ACKNOWLEDGE);
+
+ return 0;
+}
+
+/**
+ * Initialize the virtio-net device.
+ * See the Virtio Spec, chapter 2.2.1 and Appendix C "Device Initialization"
+ * for details.
+ */
+static int virtionet_init(net_driver_t *driver)
+{
+ int i;
+
+ dprintf("virtionet_init(%02x:%02x:%02x:%02x:%02x:%02x)\n",
+ driver->mac_addr[0], driver->mac_addr[1],
+ driver->mac_addr[2], driver->mac_addr[3],
+ driver->mac_addr[4], driver->mac_addr[5]);
+
+ if (driver->running != 0)
+ return 0;
+
+ /* Tell HV that we know how to drive the device. */
+ virtio_set_status(&virtiodev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER);
+
+ /* Device specific setup - we do not support special features right now */
+ virtio_set_guest_features(&virtiodev, 0);
+
+ /* Allocate memory for one transmit an multiple receive buffers */
+ vq[VQ_RX].buf_mem = SLOF_alloc_mem((BUFFER_ENTRY_SIZE+sizeof(struct virtio_net_hdr))
+ * RX_QUEUE_SIZE);
+ if (!vq[VQ_RX].buf_mem) {
+ printf("virtionet: Failed to allocate buffers!\n");
+ virtio_set_status(&virtiodev, VIRTIO_STAT_FAILED);
+ return -1;
+ }
+
+ /* Prepare receive buffer queue */
+ for (i = 0; i < RX_QUEUE_SIZE; i++) {
+ struct vring_desc *desc;
+ /* Descriptor for net_hdr: */
+ desc = &vq[VQ_RX].desc[i*2];
+ desc->addr = (uint64_t)vq[VQ_RX].buf_mem
+ + i * (BUFFER_ENTRY_SIZE+sizeof(struct virtio_net_hdr));
+ desc->len = sizeof(struct virtio_net_hdr);
+ desc->flags = VRING_DESC_F_NEXT | VRING_DESC_F_WRITE;
+ desc->next = i*2+1;
+
+ /* Descriptor for data: */
+ desc = &vq[VQ_RX].desc[i*2+1];
+ desc->addr = vq[VQ_RX].desc[i*2].addr + sizeof(struct virtio_net_hdr);
+ desc->len = BUFFER_ENTRY_SIZE;
+ desc->flags = VRING_DESC_F_WRITE;
+ desc->next = 0;
+
+ vq[VQ_RX].avail->ring[i] = i*2;
+ }
+ sync();
+ vq[VQ_RX].avail->flags = VRING_AVAIL_F_NO_INTERRUPT;
+ vq[VQ_RX].avail->idx = RX_QUEUE_SIZE;
+
+ last_rx_idx = vq[VQ_RX].used->idx;
+
+ vq[VQ_TX].avail->flags = VRING_AVAIL_F_NO_INTERRUPT;
+ vq[VQ_TX].avail->idx = 0;
+
+ /* Tell HV that setup succeeded */
+ virtio_set_status(&virtiodev, VIRTIO_STAT_ACKNOWLEDGE
+ |VIRTIO_STAT_DRIVER
+ |VIRTIO_STAT_DRIVER_OK);
+
+ /* Tell HV that RX queues are ready */
+ virtio_queue_notify(&virtiodev, VQ_RX);
+
+ driver->running = 1;
+
+ return 0;
+}
+
+
+/**
+ * Shutdown driver.
+ * We've got to make sure that the hosts stops all transfers since the buffers
+ * in our main memory will become invalid after this module has been terminated.
+ */
+static int virtionet_term(net_driver_t *driver)
+{
+ dprintf("virtionet_term()\n");
+
+ if (driver->running == 0)
+ return 0;
+
+ /* Quiesce device */
+ virtio_set_status(&virtiodev, VIRTIO_STAT_FAILED);
+
+ /* Reset device */
+ virtio_reset_device(&virtiodev);
+
+ driver->running = 0;
+
+ return 0;
+}
+
+
+/**
+ * Transmit a packet
+ */
+static int virtionet_xmit(char *buf, int len)
+{
+ struct vring_desc *desc;
+ int id;
+ static struct virtio_net_hdr nethdr;
+
+ if (len > BUFFER_ENTRY_SIZE) {
+ printf("virtionet: Packet too big!\n");
+ return 0;
+ }
+
+ dprintf("\nvirtionet_xmit(packet at %p, %d bytes)\n", buf, len);
+
+ memset(&nethdr, 0, sizeof(nethdr));
+
+ /* Determine descriptor index */
+ id = (vq[VQ_TX].avail->idx * 2) % vq[VQ_TX].size;
+
+ /* Set up virtqueue descriptor for header */
+ desc = &vq[VQ_TX].desc[id];
+ desc->addr = (uint64_t)&nethdr;
+ desc->len = sizeof(struct virtio_net_hdr);
+ desc->flags = VRING_DESC_F_NEXT;
+ desc->next = id + 1;
+
+ /* Set up virtqueue descriptor for data */
+ desc = &vq[VQ_TX].desc[id+1];
+ desc->addr = (uint64_t)buf;
+ desc->len = len;
+ desc->flags = 0;
+ desc->next = 0;
+
+ vq[VQ_TX].avail->ring[vq[VQ_TX].avail->idx % vq[VQ_TX].size] = id;
+ sync();
+ vq[VQ_TX].avail->idx += 1;
+ sync();
+
+ /* Tell HV that TX queue is ready */
+ virtio_queue_notify(&virtiodev, VQ_TX);
+
+ return len;
+}
+
+
+/**
+ * Receive a packet
+ */
+static int virtionet_receive(char *buf, int maxlen)
+{
+ int len = 0;
+ int id;
+
+ if (last_rx_idx == vq[VQ_RX].used->idx) {
+ /* Nothing received yet */
+ return 0;
+ }
+
+ id = (vq[VQ_RX].used->ring[last_rx_idx % vq[VQ_RX].size].id + 1)
+ % vq[VQ_RX].size;
+ len = vq[VQ_RX].used->ring[last_rx_idx % vq[VQ_RX].size].len
+ - sizeof(struct virtio_net_hdr);
+
+ dprintf("virtionet_receive() last_rx_idx=%i, vq[VQ_RX].used->idx=%i,"
+ " id=%i len=%i\n", last_rx_idx, vq[VQ_RX].used->idx, id, len);
+
+ if (len > maxlen) {
+ printf("virtio-net: Receive buffer not big enough!\n");
+ len = maxlen;
+ }
+
+#if 0
+ /* Dump packet */
+ printf("\n");
+ int i;
+ for (i=0; i<64; i++) {
+ printf(" %02x", *(uint8_t*)(vq[VQ_RX].desc[id].addr+i));
+ if ((i%16)==15)
+ printf("\n");
+ }
+ prinfk("\n");
+#endif
+
+ /* Copy data to destination buffer */
+ memcpy(buf, (void*)vq[VQ_RX].desc[id].addr, len);
+
+ /* Move indices to next entries */
+ last_rx_idx = last_rx_idx + 1;
+
+ vq[VQ_RX].avail->ring[vq[VQ_RX].avail->idx % vq[VQ_RX].size] = id - 1;
+ sync();
+ vq[VQ_RX].avail->idx += 1;
+
+ /* Tell HV that RX queue entry is ready */
+ virtio_queue_notify(&virtiodev, VQ_RX);
+
+ return len;
+}
+
+net_driver_t *virtionet_open(char *mac_addr, int len, struct virtio_device *dev)
+{
+ net_driver_t *driver;
+
+ driver = SLOF_alloc_mem(sizeof(*driver));
+ if (!driver) {
+ printf("Unable to allocate virtio-net driver\n");
+ return NULL;
+ }
+
+ memcpy(driver->mac_addr, mac_addr, 6);
+ driver->running = 0;
+
+ if (virtionet_init_pci(dev))
+ goto FAIL;
+
+ if (virtionet_init(driver))
+ goto FAIL;
+
+ return driver;
+
+FAIL: SLOF_free_mem(driver, sizeof(*driver));
+ return NULL;
+}
+
+void virtionet_close(net_driver_t *driver)
+{
+ if (driver) {
+ virtionet_term(driver);
+ SLOF_free_mem(driver, sizeof(*driver));
+ }
+}
+
+int virtionet_read(char *buf, int len)
+{
+ if (buf)
+ return virtionet_receive(buf, len);
+ return -1;
+}
+
+int virtionet_write(char *buf, int len)
+{
+ if (buf)
+ return virtionet_xmit(buf, len);
+ return -1;
+}
diff --git a/lib/libvirtio/virtio-net.h b/lib/libvirtio/virtio-net.h
new file mode 100644
index 0000000..e2a2925
--- /dev/null
+++ b/lib/libvirtio/virtio-net.h
@@ -0,0 +1,43 @@
+/******************************************************************************
+ * Copyright (c) 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#ifndef VIRTIO_NET_H
+#define VIRTIO_NET_H
+
+#include <netdriver.h>
+
+#define RX_QUEUE_SIZE 16
+#define BUFFER_ENTRY_SIZE 1514
+
+enum {
+ VQ_RX = 0, /* Receive Queue */
+ VQ_TX = 1, /* Transmit Queue */
+};
+
+struct vqs {
+ uint64_t id; /* Queue ID */
+ uint32_t size;
+ void *buf_mem;
+ struct vring_desc *desc;
+ struct vring_avail *avail;
+ struct vring_used *used;
+};
+
+/* Device is identified by RX queue ID: */
+#define DEVICE_ID vq[0].id
+
+extern net_driver_t *virtionet_open(char *mac_addr, int len, struct virtio_device *dev);
+extern void virtionet_close(net_driver_t *driver);
+extern int virtionet_read(char *buf, int len);
+extern int virtionet_write(char *buf, int len);
+
+#endif
diff --git a/lib/libvirtio/virtio.code b/lib/libvirtio/virtio.code
index 9c50c92..304849e 100644
--- a/lib/libvirtio/virtio.code
+++ b/lib/libvirtio/virtio.code
@@ -14,6 +14,7 @@
#include <virtio-blk.h>
#include <virtio-9p.h>
#include <virtio-scsi.h>
+#include <virtio-net.h>
/******** core virtio ********/
@@ -119,3 +120,45 @@ PRIM(virtio_X2d_scsi_X2d_send)
TOS.n = virtioscsi_send(dev, req, resp, is_read, buf, blen);
MIRP
+/******** virtio-net ********/
+
+// : virtio-net-open ( mac-addr-str len dev -- false | [ driver true ] )
+PRIM(virtio_X2d_net_X2d_open)
+{
+ void *dev = TOS.a; POP;
+ int len = TOS.u; POP;
+ char *mac_addr = TOS.a;
+
+ net_driver_t *net_driver = virtionet_open(mac_addr, len, dev);
+
+ if (net_driver) {
+ TOS.u = (unsigned long)net_driver; PUSH;
+ TOS.n = -1;
+ } else
+ TOS.n = 0;
+}
+MIRP
+
+// : virtio-net-close ( driver -- )
+PRIM(virtio_X2d_net_X2d_close)
+{
+ net_driver_t *driver = TOS.a; POP;
+ virtionet_close(driver);
+}
+MIRP
+
+// : virtio-net-read ( addr len -- actual )
+PRIM(virtio_X2d_net_X2d_read)
+{
+ int len = TOS.u; POP;
+ TOS.n = virtionet_read(TOS.a, len);
+}
+MIRP
+
+// : virtio-net-write ( addr len -- actual )
+PRIM(virtio_X2d_net_X2d_write)
+{
+ int len = TOS.u; POP;
+ TOS.n = virtionet_write(TOS.a, len);
+}
+MIRP
diff --git a/lib/libvirtio/virtio.in b/lib/libvirtio/virtio.in
index e62341d..c36d127 100644
--- a/lib/libvirtio/virtio.in
+++ b/lib/libvirtio/virtio.in
@@ -26,3 +26,8 @@ cod(virtio-scsi-send)
cod(virtio-fs-init)
cod(virtio-fs-shutdown)
cod(virtio-fs-load)
+
+cod(virtio-net-open)
+cod(virtio-net-close)
+cod(virtio-net-read)
+cod(virtio-net-write)