2009-01-15 21:05:50 +01:00
|
|
|
/*
|
|
|
|
* Virtio Console Device
|
|
|
|
*
|
|
|
|
* Copyright IBM, Corp. 2008
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
|
|
|
|
*
|
|
|
|
* This work is licensed under the terms of the GNU GPL, version 2. See
|
|
|
|
* the COPYING file in the top-level directory.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "hw.h"
|
|
|
|
#include "qemu-char.h"
|
|
|
|
#include "virtio.h"
|
|
|
|
#include "virtio-console.h"
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct VirtIOConsole
|
|
|
|
{
|
|
|
|
VirtIODevice vdev;
|
|
|
|
VirtQueue *ivq, *dvq;
|
|
|
|
CharDriverState *chr;
|
|
|
|
} VirtIOConsole;
|
|
|
|
|
|
|
|
static VirtIOConsole *to_virtio_console(VirtIODevice *vdev)
|
|
|
|
{
|
|
|
|
return (VirtIOConsole *)vdev;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void virtio_console_handle_output(VirtIODevice *vdev, VirtQueue *vq)
|
|
|
|
{
|
|
|
|
VirtIOConsole *s = to_virtio_console(vdev);
|
|
|
|
VirtQueueElement elem;
|
|
|
|
|
|
|
|
while (virtqueue_pop(vq, &elem)) {
|
|
|
|
ssize_t len = 0;
|
|
|
|
int d;
|
|
|
|
|
|
|
|
for (d=0; d < elem.out_num; d++)
|
|
|
|
len += qemu_chr_write(s->chr, elem.out_sg[d].iov_base,elem.out_sg[d].iov_len);
|
|
|
|
virtqueue_push(vq, &elem, len);
|
|
|
|
virtio_notify(vdev, vq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void virtio_console_handle_input(VirtIODevice *vdev, VirtQueue *vq)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t virtio_console_get_features(VirtIODevice *vdev)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vcon_can_read(void *opaque)
|
|
|
|
{
|
|
|
|
VirtIOConsole *s = (VirtIOConsole *) opaque;
|
|
|
|
|
|
|
|
if (!virtio_queue_ready(s->ivq) ||
|
|
|
|
!(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) ||
|
|
|
|
virtio_queue_empty(s->ivq))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* current implementations have a page sized buffer.
|
|
|
|
* We fall back to a one byte per read if there is not enough room.
|
|
|
|
* It would be cool to have a function that returns the available byte
|
|
|
|
* instead of checking for a limit */
|
|
|
|
if (virtqueue_avail_bytes(s->ivq, TARGET_PAGE_SIZE, 0))
|
|
|
|
return TARGET_PAGE_SIZE;
|
|
|
|
if (virtqueue_avail_bytes(s->ivq, 1, 0))
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vcon_read(void *opaque, const uint8_t *buf, int size)
|
|
|
|
{
|
|
|
|
VirtIOConsole *s = (VirtIOConsole *) opaque;
|
|
|
|
VirtQueueElement elem;
|
|
|
|
int offset = 0;
|
|
|
|
|
|
|
|
/* The current kernel implementation has only one outstanding input
|
|
|
|
* buffer of PAGE_SIZE. Nevertheless, this function is prepared to
|
|
|
|
* handle multiple buffers with multiple sg element for input */
|
|
|
|
while (offset < size) {
|
|
|
|
int i = 0;
|
|
|
|
if (!virtqueue_pop(s->ivq, &elem))
|
|
|
|
break;
|
|
|
|
while (offset < size && i < elem.in_num) {
|
|
|
|
int len = MIN(elem.in_sg[i].iov_len, size - offset);
|
|
|
|
memcpy(elem.in_sg[i].iov_base, buf + offset, len);
|
|
|
|
offset += len;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
virtqueue_push(s->ivq, &elem, size);
|
|
|
|
}
|
|
|
|
virtio_notify(&s->vdev, s->ivq);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vcon_event(void *opaque, int event)
|
|
|
|
{
|
|
|
|
/* we will ignore any event for the time being */
|
|
|
|
}
|
|
|
|
|
|
|
|
static void virtio_console_save(QEMUFile *f, void *opaque)
|
|
|
|
{
|
|
|
|
VirtIOConsole *s = opaque;
|
|
|
|
|
|
|
|
virtio_save(&s->vdev, f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int virtio_console_load(QEMUFile *f, void *opaque, int version_id)
|
|
|
|
{
|
|
|
|
VirtIOConsole *s = opaque;
|
|
|
|
|
|
|
|
if (version_id != 1)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
virtio_load(&s->vdev, f);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *virtio_console_init(PCIBus *bus, CharDriverState *chr)
|
|
|
|
{
|
|
|
|
VirtIOConsole *s;
|
|
|
|
|
|
|
|
s = (VirtIOConsole *)virtio_init_pci(bus, "virtio-console",
|
2009-01-26 16:22:46 +01:00
|
|
|
PCI_VENDOR_ID_REDHAT_QUMRANET,
|
|
|
|
PCI_DEVICE_ID_VIRTIO_CONSOLE,
|
2009-01-26 16:22:57 +01:00
|
|
|
PCI_VENDOR_ID_REDHAT_QUMRANET,
|
|
|
|
VIRTIO_ID_CONSOLE,
|
2009-02-01 20:26:20 +01:00
|
|
|
PCI_CLASS_DISPLAY_OTHER, 0x00,
|
2009-01-15 21:05:50 +01:00
|
|
|
0, sizeof(VirtIOConsole));
|
|
|
|
if (s == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
s->vdev.get_features = virtio_console_get_features;
|
|
|
|
|
|
|
|
s->ivq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_input);
|
|
|
|
s->dvq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_output);
|
|
|
|
|
|
|
|
s->chr = chr;
|
|
|
|
qemu_chr_add_handlers(chr, vcon_can_read, vcon_read, vcon_event, s);
|
|
|
|
|
|
|
|
register_savevm("virtio-console", -1, 1, virtio_console_save, virtio_console_load, s);
|
|
|
|
|
|
|
|
return &s->vdev;
|
|
|
|
}
|