blob: 46242336c098e6be3a3ef1345cb401898043c97c [file] [log] [blame]
/* 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;
}