From: Kyle Milka <[email protected]> We were able to get Linux to send a ping through 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.
Fixes: b/29178446 Change-Id: I840c0acc617d238fb2b24b5662ee76f615bc743f Signed-off-by: Kyle Milka <[email protected]> --- tests/vmm/vmrunkernel.c | 58 ++++++++++- user/vmm/include/vmm/virtio_net.h | 9 +- user/vmm/include/vmm/vmm.h | 5 +- user/vmm/virtio.c | 5 + user/vmm/virtio_net.c | 209 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 278 insertions(+), 8 deletions(-) create mode 100644 user/vmm/virtio_net.c diff --git a/tests/vmm/vmrunkernel.c b/tests/vmm/vmrunkernel.c index d00c441..da704f2 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> @@ -92,10 +93,11 @@ struct acpi_table_madt madt = { }, .address = 0xfee00000ULL, + .flags = 0, }; struct acpi_madt_local_apic Apic0 = {.header = {.type = ACPI_MADT_TYPE_LOCAL_APIC, .length = sizeof(struct acpi_madt_local_apic)}, - .processor_id = 0, .id = 0}; + .processor_id = 0, .id = 0, .lapic_flags = 1}; struct acpi_madt_io_apic Apic1 = {.header = {.type = ACPI_MADT_TYPE_IO_APIC, .length = sizeof(struct acpi_madt_io_apic)}, .id = 0, .address = 0xfec00000, .global_irq_base = 0}; struct acpi_madt_local_x2apic X2Apic0 = { @@ -164,6 +166,7 @@ void vapic_status_dump(FILE *f, void *vapic); #define LOCK_PREFIX "lock " #define ADDR BITOP_ADDR(addr) static inline int test_and_set_bit(int nr, volatile unsigned long *addr); +static int default_nic = 1; pthread_t timerthread_struct; @@ -204,9 +207,8 @@ static struct virtio_console_config cons_cfg_d; 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) - , + .dev_feat = + (1ULL << VIRTIO_F_VERSION_1) | (1 << VIRTIO_RING_F_INDIRECT_DESC), .num_vqs = 2, .cfg = &cons_cfg, .cfg_d = &cons_cfg_d, @@ -228,6 +230,44 @@ static struct virtio_vq_dev cons_vqdev = { } }; +static struct virtio_mmio_dev net_mmio_dev = { + .poke_guest = virtio_poke_guest, + .irq = 27, +}; + +static struct virtio_net_config net_cfg = { + .max_virtqueue_pairs = 1 +}; +static struct virtio_net_config net_cfg_d = { + .max_virtqueue_pairs = 1 +}; + +static struct virtio_vq_dev net_vqdev = { + .name = "network", + .dev_id = VIRTIO_ID_NET, + .dev_feat = (1ULL << 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"); } @@ -566,10 +606,18 @@ int main(int argc, char **argv) bp->e820_map[e820i - 1].size), 512 * GiB); - cons_mmio_dev.addr = virtio_mmio_base_addr; + cons_mmio_dev.addr = + virtio_mmio_base_addr + PGSIZE * VIRTIO_MMIO_CONSOLE_DEV; cons_mmio_dev.vqdev = &cons_vqdev; vm->virtio_mmio_devices[VIRTIO_MMIO_CONSOLE_DEV] = &cons_mmio_dev; + net_mmio_dev.addr = + virtio_mmio_base_addr + PGSIZE * VIRTIO_MMIO_NETWORK_DEV; + net_mmio_dev.vqdev = &net_vqdev; + vm->virtio_mmio_devices[VIRTIO_MMIO_NETWORK_DEV] = &net_mmio_dev; + + net_init_fn(&net_vqdev, default_nic); + /* Set the kernel command line parameters */ a += 4096; cmdline = a; diff --git a/user/vmm/include/vmm/virtio_net.h b/user/vmm/include/vmm/virtio_net.h index 31b8f54..c020e69 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,7 @@ 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); +void net_init_fn(struct virtio_vq_dev *vqdev, int nic); diff --git a/user/vmm/include/vmm/vmm.h b/user/vmm/include/vmm/vmm.h index a61d9ad..ca1d6ec 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..40299a0 --- /dev/null +++ b/user/vmm/virtio_net.c @@ -0,0 +1,209 @@ +/* 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 <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <vmm/virtio.h> +#include <vmm/virtio_mmio.h> +#include <vmm/virtio_net.h> + +#define VIRTIO_HEADER_SIZE 12 + +static int ctlfd; +static int etherfd; +static char data_path[128]; +static char clone_path[64]; + +void net_init_fn(struct virtio_vq_dev *vqdev, int nic) +{ + char type[] = "connect -1"; + char buf[8]; + char addr_path[32]; + char addr_buf[3]; + int addr_fd; + uint8_t addr_bytes; + int num_read; + int total_read = 0; + + snprintf(addr_path, sizeof(addr_path), "/net/ether%d/addr", nic); + addr_fd = open(addr_path, O_RDONLY); + if (addr_fd < 0) + VIRTIO_DEV_ERRX(vqdev, "Bad addr_fd\n"); + + for (int i = 0; i < ETH_ALEN; ++i) { + assert(read(addr_fd, addr_buf, 2) == 2); + addr_buf[2] = 0; + addr_bytes = (uint8_t)(strtol(addr_buf, 0, 16)); + ((struct virtio_net_config *)(vqdev->cfg))->mac[i] = addr_bytes; + ((struct virtio_net_config *)(vqdev->cfg_d))->mac[i] = addr_bytes; + } + + snprintf(clone_path, sizeof(clone_path), "/net/ether%d/clone", nic); + ctlfd = open(clone_path, O_RDWR); + if (ctlfd < 0) + VIRTIO_DEV_ERRX(vqdev, "%s", clone_path); + + do { + num_read = read(ctlfd, buf + total_read, sizeof(buf) - total_read); + total_read += num_read; + } while(num_read > 0); + + etherfd = strtol(buf, 0, 10); + if (etherfd < 0) + VIRTIO_DEV_ERRX(vqdev, "bad etherfd %d (%s)", etherfd, buf); + + snprintf(data_path, sizeof(data_path), + "/net/ether%d/%d/data", nic, etherfd); + + if (write(ctlfd, type, sizeof(type)) != sizeof(type)) + VIRTIO_DEV_ERRX(vqdev, "write to ctlfd failed"); +} + +/* net_receiveq_fn receives packets for the guest thoeugh the virtio networking + * device and the _vq virtio queue. + */ +void net_receiveq_fn(void *_vq) +{ + struct virtio_vq *vq = _vq; + uint32_t head; + uint32_t olen, ilen; + int num_read; + struct iovec *iov; + struct virtio_mmio_dev *dev = vq->vqdev->transport_dev; + int fd; + struct virtio_net_hdr_v1 *net_header; + + fd = open(data_path, O_RDWR); + if (fd == -1) + VIRTIO_DEV_ERRX(vq->vqdev, "Could not open data file for ether1."); + + 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__); + + if (vq->qready == 0x0) + VIRTIO_DEV_ERRX(vq->vqdev, + "The service function for queue '%s' was launched before the driver set QueueReady to 0x1.", + vq->name); + + iov = malloc(vq->qnum_max * sizeof(struct iovec)); + assert(iov != NULL); + + if (!dev->poke_guest) { + free(iov); + VIRTIO_DEV_ERRX(vq->vqdev, + "The 'poke_guest' function pointer was not set."); + } + + for (;;) { + 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"); + } + + /* For receive the virtio header is in iov[0], so we only want + * the packet to be read into iov[1] and above. + */ + num_read = readv(fd, iov + 1, ilen - 1); + if (num_read < 0) { + free(iov); + VIRTIO_DEV_ERRX(vq->vqdev, + "Encountered an error trying to read input from the ethernet device."); + } + + /* See virtio spec virtio-v1.0-cs04 s5.1.6.3.2 Device Requirments: + * Setting Up Receive Buffers + * + * VIRTIO_NET_F_MRG_RXBUF is not currently negotiated. + * num_buffers will always be 1 if VIRTIO_NET_F_MRG_RXBUF is not + * negotiated. + */ + net_header = iov[0].iov_base; + net_header->num_buffers = 1; + virtio_add_used_desc(vq, head, num_read + VIRTIO_HEADER_SIZE); + + virtio_mmio_set_vring_irq(dev); + dev->poke_guest(dev->vec); + } +} + +/* net_transmitq_fn transmits packets from the guest through the virtio + * networking device through the _vq virtio queue. + */ +void net_transmitq_fn(void *_vq) +{ + struct virtio_vq *vq = _vq; + uint32_t head; + uint32_t olen, ilen; + struct iovec *iov; + struct virtio_mmio_dev *dev = vq->vqdev->transport_dev; + void *stripped; + int fd = open(data_path, O_RDWR); + if (fd == -1) + VIRTIO_DEV_ERRX(vq->vqdev, "Could not open data file for ether1."); + + iov = malloc(vq->qnum_max * sizeof(struct iovec)); + assert(iov != NULL); + + if (!dev->poke_guest) { + free(iov); + VIRTIO_DEV_ERRX(vq->vqdev, + "The 'poke_guest' function pointer was not set."); + } + + for (;;) { + 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"); + } + + /* Strip off the virtio header (the first 12 bytes), as it is + * not a part of the actual ethernet frame. + */ + for (int i = 0; i < olen; i++) { + stripped = iov[i].iov_base + VIRTIO_HEADER_SIZE; + assert(write(fd, stripped, iov[i].iov_len - VIRTIO_HEADER_SIZE) == iov[i].iov_len - VIRTIO_HEADER_SIZE); + } + + virtio_add_used_desc(vq, head, 0); + + virtio_mmio_set_vring_irq(dev); + dev->poke_guest(dev->vec); + } +} -- 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.
