| /* 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; |
| } |