Files
Andre Przywara 15542bab78 avoid casts when initializing structures
Due to our kernel heritage we have code in kvmtool that relies on
the (still) implicit -std=gnu89 compiler switch.
It turns out that this just affects some structure initialization,
where we currently provide a cast to the type, which upsets GCC for
anything beyond -std=gnu89 (for instance gnu99 or gnu11).
We do need the casts when initializing structures that are not
assigned to the same type, so we put it there explicitly.

This allows us to compile with all the three GNU standards GCC
currently supports: gnu89/90, gnu99 and gnu11.
GCC threatens people with moving to gnu11 as the new default standard,
so lets fix this better sooner than later.
(Compiling without GNU extensions still breaks and I don't bother to
fix that without very good reasons.)

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
2015-07-20 18:25:47 +01:00

233 lines
5.2 KiB
C

#include "kvm/virtio-console.h"
#include "kvm/virtio-pci-dev.h"
#include "kvm/disk-image.h"
#include "kvm/virtio.h"
#include "kvm/ioport.h"
#include "kvm/util.h"
#include "kvm/term.h"
#include "kvm/mutex.h"
#include "kvm/kvm.h"
#include "kvm/pci.h"
#include "kvm/threadpool.h"
#include "kvm/irq.h"
#include "kvm/guest_compat.h"
#include <linux/virtio_console.h>
#include <linux/virtio_ring.h>
#include <linux/virtio_blk.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#define VIRTIO_CONSOLE_QUEUE_SIZE 128
#define VIRTIO_CONSOLE_NUM_QUEUES 2
#define VIRTIO_CONSOLE_RX_QUEUE 0
#define VIRTIO_CONSOLE_TX_QUEUE 1
struct con_dev {
struct mutex mutex;
struct virtio_device vdev;
struct virt_queue vqs[VIRTIO_CONSOLE_NUM_QUEUES];
struct virtio_console_config config;
u32 features;
pthread_cond_t poll_cond;
int vq_ready;
struct thread_pool__job jobs[VIRTIO_CONSOLE_NUM_QUEUES];
};
static struct con_dev cdev = {
.mutex = MUTEX_INITIALIZER,
.vq_ready = 0,
.config = {
.cols = 80,
.rows = 24,
.max_nr_ports = 1,
},
};
static int compat_id = -1;
/*
* Interrupts are injected for hvc0 only.
*/
static void virtio_console__inject_interrupt_callback(struct kvm *kvm, void *param)
{
struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE];
struct virt_queue *vq;
u16 out, in;
u16 head;
int len;
if (kvm->cfg.active_console != CONSOLE_VIRTIO)
return;
mutex_lock(&cdev.mutex);
vq = param;
if (!cdev.vq_ready)
pthread_cond_wait(&cdev.poll_cond, &cdev.mutex.mutex);
if (term_readable(0) && virt_queue__available(vq)) {
head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
len = term_getc_iov(kvm, iov, in, 0);
virt_queue__set_used_elem(vq, head, len);
cdev.vdev.ops->signal_vq(kvm, &cdev.vdev, vq - cdev.vqs);
}
mutex_unlock(&cdev.mutex);
}
void virtio_console__inject_interrupt(struct kvm *kvm)
{
virtio_console__inject_interrupt_callback(kvm,
&cdev.vqs[VIRTIO_CONSOLE_RX_QUEUE]);
}
static void virtio_console_handle_callback(struct kvm *kvm, void *param)
{
struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE];
struct virt_queue *vq;
u16 out, in;
u16 head;
u32 len;
vq = param;
/*
* The current Linux implementation polls for the buffer
* to be used, rather than waiting for an interrupt.
* So there is no need to inject an interrupt for the tx path.
*/
while (virt_queue__available(vq)) {
head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
len = term_putc_iov(iov, out, 0);
virt_queue__set_used_elem(vq, head, len);
}
}
static u8 *get_config(struct kvm *kvm, void *dev)
{
struct con_dev *cdev = dev;
return ((u8 *)(&cdev->config));
}
static u32 get_host_features(struct kvm *kvm, void *dev)
{
return 0;
}
static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
{
struct con_dev *cdev = dev;
struct virtio_console_config *conf = &cdev->config;
conf->cols = virtio_host_to_guest_u16(&cdev->vdev, conf->cols);
conf->rows = virtio_host_to_guest_u16(&cdev->vdev, conf->rows);
conf->max_nr_ports = virtio_host_to_guest_u32(&cdev->vdev, conf->max_nr_ports);
}
static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align,
u32 pfn)
{
struct virt_queue *queue;
void *p;
BUG_ON(vq >= VIRTIO_CONSOLE_NUM_QUEUES);
compat__remove_message(compat_id);
queue = &cdev.vqs[vq];
queue->pfn = pfn;
p = virtio_get_vq(kvm, queue->pfn, page_size);
vring_init(&queue->vring, VIRTIO_CONSOLE_QUEUE_SIZE, p, align);
virtio_init_device_vq(&cdev.vdev, queue);
if (vq == VIRTIO_CONSOLE_TX_QUEUE) {
thread_pool__init_job(&cdev.jobs[vq], kvm, virtio_console_handle_callback, queue);
} else if (vq == VIRTIO_CONSOLE_RX_QUEUE) {
thread_pool__init_job(&cdev.jobs[vq], kvm, virtio_console__inject_interrupt_callback, queue);
/* Tell the waiting poll thread that we're ready to go */
mutex_lock(&cdev.mutex);
cdev.vq_ready = 1;
pthread_cond_signal(&cdev.poll_cond);
mutex_unlock(&cdev.mutex);
}
return 0;
}
static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
{
struct con_dev *cdev = dev;
thread_pool__do_job(&cdev->jobs[vq]);
return 0;
}
static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
{
struct con_dev *cdev = dev;
return cdev->vqs[vq].pfn;
}
static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
{
return VIRTIO_CONSOLE_QUEUE_SIZE;
}
static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size)
{
/* FIXME: dynamic */
return size;
}
static struct virtio_ops con_dev_virtio_ops = {
.get_config = get_config,
.get_host_features = get_host_features,
.set_guest_features = set_guest_features,
.init_vq = init_vq,
.notify_vq = notify_vq,
.get_pfn_vq = get_pfn_vq,
.get_size_vq = get_size_vq,
.set_size_vq = set_size_vq,
};
int virtio_console__init(struct kvm *kvm)
{
if (kvm->cfg.active_console != CONSOLE_VIRTIO)
return 0;
pthread_cond_init(&cdev.poll_cond, NULL);
virtio_init(kvm, &cdev, &cdev.vdev, &con_dev_virtio_ops,
VIRTIO_DEFAULT_TRANS(kvm), PCI_DEVICE_ID_VIRTIO_CONSOLE,
VIRTIO_ID_CONSOLE, PCI_CLASS_CONSOLE);
if (compat_id == -1)
compat_id = virtio_compat_add_message("virtio-console", "CONFIG_VIRTIO_CONSOLE");
return 0;
}
virtio_dev_init(virtio_console__init);
int virtio_console__exit(struct kvm *kvm)
{
return 0;
}
virtio_dev_exit(virtio_console__exit);