|  | /* 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 <rusty@rustcorp.com.au> | 
|  | *  Michael Taufen <mtaufen@gmail.com> | 
|  | * | 
|  | * 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 <err.h> | 
|  | #include <sys/eventfd.h> | 
|  | #include <sys/uio.h> | 
|  | #include <vmm/virtio.h> | 
|  | #include <vmm/virtio_mmio.h> | 
|  | #include <parlib/stdio.h> | 
|  |  | 
|  | void *cons_receiveq_fn(void *_vq) // host -> guest | 
|  | { | 
|  | struct virtio_vq *vq = _vq; | 
|  | uint32_t head; | 
|  | uint32_t olen, ilen; | 
|  | uint32_t i, j; | 
|  | int num_read; | 
|  | struct iovec *iov; | 
|  | struct virtio_mmio_dev *dev = vq->vqdev->transport_dev; | 
|  |  | 
|  | if (!vq) | 
|  | errx(1, | 
|  | "\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__); | 
|  |  | 
|  | // NOTE: The virtio_next_avail_vq_desc will not write more than | 
|  | //       vq->vring.num entries to iov, and the device implementation | 
|  | //       (virtio_mmio.c) will not allow the driver to set vq->vring.num to a | 
|  | //       value greater than QueueNumMax (vq->qnum_max), so you are safe as | 
|  | //       long as your iov is at least vq->qnum_max iovecs in size. | 
|  | iov = malloc(vq->qnum_max * sizeof(struct iovec)); | 
|  |  | 
|  | 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); | 
|  |  | 
|  | // NOTE: This will block in 2 places: | 
|  | //       - reading from stdin | 
|  | //       - reading from eventfd in virtio_next_avail_vq_desc | 
|  | while (1) { | 
|  | head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen); | 
|  |  | 
|  | if (olen) { | 
|  | // virtio-v1.0-cs04 s5.3.6.1 Device Operation (console section) | 
|  | 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"); | 
|  | } | 
|  |  | 
|  | // TODO: We may want to add some sort of console abort | 
|  | //       (e.g. type q and enter to quit) | 
|  | // readv from stdin as much as we can (to end of bufs or end of input) | 
|  | num_read = readv(0, iov, ilen); | 
|  | if (num_read < 0) | 
|  | VIRTIO_DEV_ERRX(vq->vqdev, | 
|  | "Encountered an error trying to read input from stdin (fd 0)."); | 
|  |  | 
|  | if (num_read == 0) { | 
|  | VIRTIO_DEV_WARNX(vq->vqdev, | 
|  | "Encountered EOF reading from stdin; exiting input thread."); | 
|  | break; | 
|  | } | 
|  |  | 
|  | // You pass the number of bytes written to virtio_add_used_desc | 
|  | virtio_add_used_desc(vq, head, num_read); | 
|  |  | 
|  | // Poke the guest however the mmio transport prefers | 
|  | // NOTE: assuming that the mmio transport was used for now. | 
|  | virtio_mmio_set_vring_irq(dev); | 
|  | if (dev->poke_guest) | 
|  | dev->poke_guest(dev->vec, dev->dest); | 
|  | else | 
|  | VIRTIO_DEV_ERRX(vq->vqdev, | 
|  | "The host MUST provide a way for device interrupts to be sent to the guest. The 'poke_guest' function pointer on the vq->vqdev->transport_dev (assumed to be a struct virtio_mmio_dev) was not set."); | 
|  | } | 
|  | free(iov); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void *cons_transmitq_fn(void *_vq) // guest -> host | 
|  | { | 
|  | struct virtio_vq *vq = _vq; | 
|  | uint32_t head; | 
|  | uint32_t olen, ilen; | 
|  | uint32_t i, j; | 
|  | struct iovec *iov; | 
|  | struct virtio_mmio_dev *dev = vq->vqdev->transport_dev; | 
|  |  | 
|  | if (!vq) | 
|  | errx(1, | 
|  | "\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__); | 
|  |  | 
|  | // NOTE: The virtio_next_avail_vq_desc will not write more than | 
|  | //       vq->vring.num entries to iov, and the device implementation | 
|  | //       (virtio_mmio.c) will not allow the driver to set vq->vring.num to a | 
|  | //       value greater than QueueNumMax (vq->qnum_max), so you are safe as | 
|  | //       long as your iov is at least vq->qnum_max iovecs in size. | 
|  | iov = malloc(vq->qnum_max * sizeof(struct iovec)); | 
|  |  | 
|  | 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); | 
|  |  | 
|  | while (1) { | 
|  | // Get the buffers: | 
|  | head = virtio_next_avail_vq_desc(vq, iov, &olen, &ilen); | 
|  |  | 
|  | if (ilen) { | 
|  | // virtio-v1.0-cs04 s5.3.6.1 Device Operation (console section) | 
|  | VIRTIO_DRI_ERRX(vq->vqdev, | 
|  | "The driver placed a device-writeable buffer in the console device's transmitq.\n" | 
|  | "  See virtio-v1.0-cs04 s5.3.6.1 Device Operation"); | 
|  | } | 
|  | // Process the buffers: | 
|  | for (i = 0; i < olen; ++i) { | 
|  | for (j = 0; j < iov[i].iov_len; ++j) | 
|  | printf("%c", ((char *)iov[i].iov_base)[j]); | 
|  | } | 
|  | fflush(stdout); | 
|  |  | 
|  | // Add all the buffers to the used ring. | 
|  | // Pass 0 because we wrote nothing. | 
|  | virtio_add_used_desc(vq, head, 0); | 
|  |  | 
|  | // Poke the guest however the mmio transport prefers | 
|  | // NOTE: assuming that the mmio transport was used for now | 
|  | virtio_mmio_set_vring_irq(dev); | 
|  | if (dev->poke_guest) | 
|  | dev->poke_guest(dev->vec, dev->dest); | 
|  | else | 
|  | VIRTIO_DEV_ERRX(vq->vqdev, | 
|  | "The host MUST provide a way for device interrupts to be sent to the guest. The 'poke_guest' function pointer on the vq->vqdev->transport_dev (assumed to be a struct virtio_mmio_dev) was not set."); | 
|  | } | 
|  | free(iov); | 
|  | return 0; | 
|  | } |