From: Kyle Milka <[email protected]>

We were able to get Linux to send a packet to the virtio device. Used
linux/lguest.c as a starting point and virtio_lguest_console.c for finding
the names of our version of the functions. This included adding the
network device struct and transmit and receive functions.

Bug:29178446

Change-Id: I840c0acc617d238fb2b24b5662ee76f615bc743f
Signed-off-by: Kyle Milka <[email protected]>
---
 tests/vmm/vmrunkernel.c            |  64 ++++++++++++++++++--
 user/vmm/include/vmm/virtio_mmio.h |   3 +
 user/vmm/include/vmm/virtio_net.h  |   8 ++-
 user/vmm/include/vmm/vmm.h         |   5 +-
 user/vmm/virtio.c                  |   5 ++
 user/vmm/virtio_net.c              | 120 +++++++++++++++++++++++++++++++++++++
 6 files changed, 197 insertions(+), 8 deletions(-)
 create mode 100644 user/vmm/virtio_net.c

diff --git a/tests/vmm/vmrunkernel.c b/tests/vmm/vmrunkernel.c
index 6dd66a4..410b8a1 100644
--- a/tests/vmm/vmrunkernel.c
+++ b/tests/vmm/vmrunkernel.c
@@ -27,6 +27,7 @@
 #include <vmm/virtio_ids.h>
 #include <vmm/virtio_config.h>
 #include <vmm/virtio_console.h>
+#include <vmm/virtio_net.h>
 #include <vmm/virtio_lguest_console.h>
 
 #include <vmm/sched.h>
@@ -206,14 +207,15 @@ void timer_thread(void *arg)
 // FIXME.
 volatile int consdata = 0;
 
-static void virtio_poke_guest(void)
+static void virtio_cons_poke_guest(void)
 {
        set_posted_interrupt(0xE5);
        ros_syscall(SYS_vmm_poke_guest, 0, 0, 0, 0, 0, 0);
 }
 
 static struct virtio_mmio_dev cons_mmio_dev = {
-       .poke_guest = virtio_poke_guest
+       .poke_guest = virtio_cons_poke_guest,
+       .irq = 32
 };
 
 static struct virtio_console_config cons_cfg;
@@ -223,7 +225,7 @@ static struct virtio_vq_dev cons_vqdev = {
        .name = "console",
        .dev_id = VIRTIO_ID_CONSOLE,
        .dev_feat = ((uint64_t)1 << VIRTIO_F_VERSION_1)
-                                         | (1 << VIRTIO_RING_F_INDIRECT_DESC)
+                   | (1 << VIRTIO_RING_F_INDIRECT_DESC)
                          ,
        .num_vqs = 2,
        .cfg = &cons_cfg,
@@ -246,6 +248,52 @@ static struct virtio_vq_dev cons_vqdev = {
                }
 };
 
+/* Sends an interrupt to the guest. */
+static void virtio_net_poke_guest(void)
+{
+       set_posted_interrupt(0xE6);
+       ros_syscall(SYS_vmm_poke_guest, 0, 0, 0, 0, 0, 0);
+}
+
+static struct virtio_mmio_dev net_mmio_dev = {
+       .poke_guest = virtio_net_poke_guest,
+       .irq = 29
+};
+
+static struct virtio_net_config net_cfg = {
+       .mac = {0xec, 0xb1, 0xd7, 0x42, 0x90, 0xbf},
+       .max_virtqueue_pairs = 1
+};
+static struct virtio_net_config net_cfg_d = {
+       .mac = {0xec, 0xb1, 0xd7, 0x42, 0x90, 0xbf},
+       .max_virtqueue_pairs = 1
+};
+
+static struct virtio_vq_dev net_vqdev = {
+       .name = "network",
+       .dev_id = VIRTIO_ID_NET,
+       .dev_feat = ((uint64_t)1 << VIRTIO_F_VERSION_1 | 1 << VIRTIO_NET_F_MAC),
+
+       .num_vqs = 2,
+       .cfg = &net_cfg,
+       .cfg_d = &net_cfg_d,
+       .cfg_sz = sizeof(struct virtio_net_config),
+       .transport_dev = &net_mmio_dev,
+       .vqs = {
+               {
+                       .name = "net_receiveq",
+                       .qnum_max = 64,
+                       .srv_fn = net_receiveq_fn,
+                       .vqdev = &net_vqdev
+               },
+               {
+                       .name = "net_transmitq",
+                       .qnum_max = 64,
+                       .srv_fn = net_transmitq_fn,
+                       .vqdev = &net_vqdev
+               },
+       }
+};
 
 void lowmem() {
        __asm__ __volatile__ (".section .lowmem, \"aw\"\n\tlow: 
\n\t.=0x1000\n\t.align 0x100000\n\t.previous\n");
@@ -585,6 +633,10 @@ int main(int argc, char **argv)
        cons_mmio_dev.vqdev = &cons_vqdev;
        vm->virtio_mmio_devices[VIRTIO_MMIO_CONSOLE_DEV] = &cons_mmio_dev;
 
+       net_mmio_dev.addr = cons_mmio_dev.addr + PGSIZE;
+       net_mmio_dev.vqdev = &net_vqdev;
+       vm->virtio_mmio_devices[VIRTIO_MMIO_NETWORK_DEV] = &net_mmio_dev;
+
        /* Set the kernel command line parameters */
        a += 4096;
        cmdline = a;
@@ -602,8 +654,10 @@ int main(int argc, char **argv)
                if (vm->virtio_mmio_devices[i] == NULL)
                        continue;
                /* Append all the virtio mmio base addresses. */
-               len = snprintf(cmdlinep, cmdlinesz, " 
virtio_mmio.device=1K@0x%llx:32",
-                              vm->virtio_mmio_devices[i]->addr);
+               len = snprintf(cmdlinep, cmdlinesz,
+                              " virtio_mmio.device=1K@0x%llx:%lld",
+                              vm->virtio_mmio_devices[i]->addr,
+                              vm->virtio_mmio_devices[i]->irq);
                if (len >= cmdlinesz) {
                        fprintf(stderr, "Too many arguments to the linux 
command line.");
                        exit(1);
diff --git a/user/vmm/include/vmm/virtio_mmio.h 
b/user/vmm/include/vmm/virtio_mmio.h
index f0b8979..afd079e 100644
--- a/user/vmm/include/vmm/virtio_mmio.h
+++ b/user/vmm/include/vmm/virtio_mmio.h
@@ -212,6 +212,9 @@ struct virtio_mmio_dev {
 
        // The generic vq device contained by this mmio transport
        struct virtio_vq_dev *vqdev;
+
+       // The specific irq number for this device.
+       uint64_t irq;
 };
 
 // Sets the VIRTIO_MMIO_INT_VRING bit in the interrupt status
diff --git a/user/vmm/include/vmm/virtio_net.h 
b/user/vmm/include/vmm/virtio_net.h
index 31b8f54..a73e646 100644
--- a/user/vmm/include/vmm/virtio_net.h
+++ b/user/vmm/include/vmm/virtio_net.h
@@ -27,7 +27,6 @@
 #include <stdint.h>
 #include <vmm/virtio_ids.h>
 #include <vmm/virtio_config.h>
-#include <linux/if_ether.h>
 
 /* The feature bitmap for virtio net */
 #define VIRTIO_NET_F_CSUM      0       /* Host handles pkts w/ partial csum */
@@ -61,6 +60,10 @@
 #define VIRTIO_NET_S_LINK_UP   1       /* Link is up */
 #define VIRTIO_NET_S_ANNOUNCE  2       /* Announcement is needed */
 
+#ifndef ETH_ALEN
+#define ETH_ALEN       6       /* Length of a MAC address (48 bits) */
+#endif
+
 struct virtio_net_config {
        /* The config defining mac address (if VIRTIO_NET_F_MAC) */
        uint8_t mac[ETH_ALEN];
@@ -239,3 +242,6 @@ struct virtio_net_ctrl_mq {
  */
 #define VIRTIO_NET_CTRL_GUEST_OFFLOADS   5
 #define VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET        0
+
+void net_receiveq_fn(void *_vq);
+void net_transmitq_fn(void *_vq);
diff --git a/user/vmm/include/vmm/vmm.h b/user/vmm/include/vmm/vmm.h
index fbc12b7..d5416fe 100644
--- a/user/vmm/include/vmm/vmm.h
+++ b/user/vmm/include/vmm/vmm.h
@@ -10,12 +10,13 @@
 #include <vmm/sched.h>
 
 /* The listing of VIRTIO MMIO devices. We currently only expect to have 2,
- * console and network. Only the console is implemented right now.*/
+ * console and network. Only the console is fully implemented right now.*/
 enum {
        VIRTIO_MMIO_CONSOLE_DEV,
+       VIRTIO_MMIO_NETWORK_DEV,
 
        /* This should always be the last entry. */
-       VIRTIO_MMIO_MAX_NUM_DEV = 2,
+       VIRTIO_MMIO_MAX_NUM_DEV,
 };
 
 /* Structure to encapsulate all of the bookkeeping for a VM. */
diff --git a/user/vmm/virtio.c b/user/vmm/virtio.c
index 165cb47..f64d1e2 100644
--- a/user/vmm/virtio.c
+++ b/user/vmm/virtio.c
@@ -32,6 +32,11 @@ const char *virtio_validate_feat(struct virtio_vq_dev 
*vqdev, uint64_t feat)
                case VIRTIO_ID_CONSOLE:
                        // No interdependent features for the console.
                        break;
+               case VIRTIO_ID_NET:
+                       // There is no "mandatory" feature bit that we always 
want to have,
+                       // either the device can set its own MAC Address (as it 
does now)
+                       // or the driver can set it using a controller thread.
+                       break;
                case 0:
                        return "Invalid device id (0x0)! On the MMIO transport, 
this value indicates that the device is a system memory map with placeholder 
devices at static, well known addresses. In any case, this is not something you 
validate features for.";
                default:
diff --git a/user/vmm/virtio_net.c b/user/vmm/virtio_net.c
new file mode 100644
index 0000000..6b6f670
--- /dev/null
+++ b/user/vmm/virtio_net.c
@@ -0,0 +1,120 @@
+/* Virtio helper functions from linux/tools/lguest/lguest.c
+ *
+ * Copyright (C) 1991-2016, the Linux Kernel authors
+ * Copyright (c) 2016 Google Inc.
+ *
+ * Author:
+ *  Rusty Russell <[email protected]>
+ *  Kyle Milka <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * The code from lguest.c has been modified for Akaros.
+ *
+ * Original linux/tools/lguest/lguest.c:
+ *   https://github.com/torvalds/linux/blob/v4.5/tools/lguest/lguest.c
+ *   most recent hash on the file as of v4.5 tag:
+ *     e523caa601f4a7c2fa1ecd040db921baf7453798
+ */
+
+#include <stdlib.h>
+#include <vmm/virtio.h>
+#include <vmm/virtio_mmio.h>
+
+/* net_receiveq_fn receives packets from the virtio networking device through
+ * the _vq virtio queue.
+ *
+ * TODO(kmilka): Not working until we have networking capabilities.
+ */
+void net_receiveq_fn(void *_vq)
+{
+       struct virtio_vq *vq = _vq;
+       uint32_t head;
+       uint32_t olen, ilen;
+       uint32_t num_read;
+       struct iovec *iov;
+
+       if (!vq)
+               VIRTIO_DEV_ERRX(vq->vqdev,
+                       "\n  %s:%d\n"
+                       "  Virtio device: (not sure which one): Error, device 
behavior.\n"
+                       "  The device must provide a valid virtio_vq as an 
argument to %s."
+                       , __FILE__, __LINE__, __func__);
+
+       iov = malloc(vq->qnum_max * sizeof(struct iovec));
+
+       if (vq->qready == 0x0) {
+               free(iov);
+               VIRTIO_DEV_ERRX(vq->vqdev,
+                       "The service function for queue '%s' was launched 
before the driver set QueueReady to 0x1."
+                       , vq->name);
+       }
+
+       while (1) {
+               head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);
+
+               if (olen) {
+                       free(iov);
+                       VIRTIO_DRI_ERRX(vq->vqdev,
+                               "The driver placed a device-readable buffer in 
the console device's receiveq.\n"
+                               "  See virtio-v1.0-cs04 s5.3.6.1 Device 
Operation");
+               }
+               /* Grab actual external packet
+                * num_read = readv(0, iov, ilen);
+                * if (num_read <= 0)
+                * wait forever since we dont actually have
+                * something from the network */
+               while (1);
+
+               virtio_add_used_desc(vq, head, num_read);
+
+               virtio_mmio_set_vring_irq(vq->vqdev->transport_dev);
+               if (!((struct 
virtio_mmio_dev*)vq->vqdev->transport_dev)->poke_guest) {
+                       free(iov);
+                       VIRTIO_DEV_ERRX(vq->vqdev,
+                                       "The 'poke_guest' function pointer was 
not set.");
+               }
+               ((struct 
virtio_mmio_dev*)vq->vqdev->transport_dev)->poke_guest();
+       }
+
+       free(iov);
+}
+
+/* net_receiveq_fn transmits packets from the virtio networking device through
+ * the _vq virtio queue.
+ *
+ * TODO(kmilka): At this point we can receive packets from the guest, but still
+ * can not transmit a packet until we have networking capabilities.
+ */
+void net_transmitq_fn(void *_vq)
+{
+       struct virtio_vq *vq = _vq;
+       uint32_t head;
+       uint32_t olen, ilen;
+       struct iovec *iov;
+
+       iov = malloc(vq->qnum_max * sizeof(struct iovec));
+
+       while (1) {
+               head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen);
+
+               if (ilen) {
+                       free(iov);
+                       VIRTIO_DRI_ERRX(vq->vqdev,
+                                       "The driver placed a device-writeable 
buffer in the network device's transmitq.\n"
+                                   "  See virtio-v1.0-cs04 s5.3.6.1 Device 
Operation");
+               }
+
+               virtio_add_used_desc(vq, head, 0);
+       }
+
+       free(iov);
+}
-- 
2.8.0.rc3.226.g39d4020

-- 
You received this message because you are subscribed to the Google Groups 
"Akaros" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to