]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
kvm tools: Virtio console support
authorAsias He <asias.hejun@gmail.com>
Fri, 8 Apr 2011 14:12:52 +0000 (22:12 +0800)
committerPekka Enberg <penberg@kernel.org>
Fri, 8 Apr 2011 11:53:25 +0000 (14:53 +0300)
This patch adds simple virtio console support to the hypervisor.

NOTE, NOTE, NOTE: Please add something like this to your /etc/inittab file:

    T2:23:respawn:/sbin/getty -L hvc0 9600 vt100

to get a virtio console login.

Signed-off-by: Asias He <asias.hejun@gmail.com>
Signed-off-by: Pekka Enberg <penberg@kernel.org>
tools/kvm/Makefile
tools/kvm/console-virtio.c [new file with mode: 0644]
tools/kvm/include/kvm/console-virtio.h [new file with mode: 0644]
tools/kvm/main.c

index 0244f9bc25eda2fcaca65300f2f881e14015edf7..55f342d9d2601b3cd546ceca59210d5bdaac27f3 100644 (file)
@@ -14,6 +14,7 @@ TAGS = ctags
 
 OBJS   += 8250-serial.o
 OBJS   += blk-virtio.o
+OBJS   += console-virtio.o
 OBJS   += cpuid.o
 OBJS   += read-write.o
 OBJS   += disk-image.o
diff --git a/tools/kvm/console-virtio.c b/tools/kvm/console-virtio.c
new file mode 100644 (file)
index 0000000..f094610
--- /dev/null
@@ -0,0 +1,218 @@
+#include "kvm/console-virtio.h"
+#include "kvm/virtio_pci.h"
+#include "kvm/disk-image.h"
+#include "kvm/virtqueue.h"
+#include "kvm/ioport.h"
+#include "kvm/util.h"
+#include "kvm/term.h"
+#include "kvm/kvm.h"
+#include "kvm/pci.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 <inttypes.h>
+#include <termios.h>
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define VIRTIO_CONSOLE_IRQ             14
+#define VIRTIO_CONSOLE_QUEUE_SIZE      128
+#define VIRTIO_CONSOLE_NUM_QUEUES      2
+#define VIRTIO_CONSOLE_RX_QUEUE                0
+#define VIRTIO_CONSOLE_TX_QUEUE                1
+#define PCI_VIRTIO_CONSOLE_DEVNUM      2
+
+struct console_device {
+       struct virt_queue               vqs[VIRTIO_CONSOLE_NUM_QUEUES];
+       struct virtio_console_config    console_config;
+       uint32_t                        host_features;
+       uint32_t                        guest_features;
+       uint16_t                        config_vector;
+       uint8_t                         status;
+       uint16_t                        queue_selector;
+};
+
+static struct console_device console_device = {
+       .console_config = {
+               .cols           = 80,
+               .rows           = 24,
+               .max_nr_ports   = 1,
+       },
+
+       .host_features          = 0,
+};
+
+/*
+ * Interrupts are injected for hvc0 only.
+ */
+void virtio_console__inject_interrupt(struct kvm *self)
+{
+       struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE];
+       struct virt_queue *vq;
+       uint16_t out, in;
+       uint16_t head;
+       int len;
+
+       vq = &console_device.vqs[VIRTIO_CONSOLE_RX_QUEUE];
+
+       if (term_readable(CONSOLE_VIRTIO) && virt_queue__available(vq)) {
+               head = virt_queue__get_iov(vq, iov, &out, &in, self);
+               len = term_getc_iov(CONSOLE_VIRTIO, iov, in);
+               virt_queue__set_used_elem(vq, head, len);
+               kvm__irq_line(self, VIRTIO_CONSOLE_IRQ, 1);
+       }
+}
+
+static bool virtio_console_pci_io_device_specific_in(void *data, unsigned long offset, int size, uint32_t count)
+{
+       uint8_t *config_space = (uint8_t *) &console_device.console_config;
+
+       if (size != 1 || count != 1)
+               return false;
+
+       if ((offset - VIRTIO_PCI_CONFIG_NOMSI) > sizeof(struct virtio_console_config))
+               error("config offset is too big: %li", offset - VIRTIO_PCI_CONFIG_NOMSI);
+
+       ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]);
+
+       return true;
+}
+
+static bool virtio_console_pci_io_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
+{
+       unsigned long offset = port - IOPORT_VIRTIO_CONSOLE;
+
+       switch (offset) {
+       case VIRTIO_PCI_HOST_FEATURES:
+               ioport__write32(data, console_device.host_features);
+               break;
+       case VIRTIO_PCI_GUEST_FEATURES:
+               return false;
+       case VIRTIO_PCI_QUEUE_PFN:
+               ioport__write32(data, console_device.vqs[console_device.queue_selector].pfn);
+               break;
+       case VIRTIO_PCI_QUEUE_NUM:
+               ioport__write16(data, VIRTIO_CONSOLE_QUEUE_SIZE);
+               break;
+       case VIRTIO_PCI_QUEUE_SEL:
+       case VIRTIO_PCI_QUEUE_NOTIFY:
+               return false;
+       case VIRTIO_PCI_STATUS:
+               ioport__write8(data, console_device.status);
+               break;
+       case VIRTIO_PCI_ISR:
+               ioport__write8(data, 0x1);
+               kvm__irq_line(self, VIRTIO_CONSOLE_IRQ, 0);
+               break;
+       case VIRTIO_MSI_CONFIG_VECTOR:
+               ioport__write16(data, console_device.config_vector);
+               break;
+       default:
+               return virtio_console_pci_io_device_specific_in(data, offset, size, count);
+       };
+
+       return true;
+}
+
+static void virtio_console_handle_callback(struct kvm *self, uint16_t queue_index)
+{
+       struct iovec iov[VIRTIO_CONSOLE_QUEUE_SIZE];
+       struct virt_queue *vq;
+       uint16_t out, in;
+       uint16_t head;
+       uint32_t len;
+
+       vq = &console_device.vqs[queue_index];
+
+       if (queue_index == VIRTIO_CONSOLE_TX_QUEUE) {
+
+               while (virt_queue__available(vq)) {
+                       head = virt_queue__get_iov(vq, iov, &out, &in, self);
+                       len = term_putc_iov(CONSOLE_VIRTIO, iov, out);
+                       virt_queue__set_used_elem(vq, head, len);
+               }
+
+               kvm__irq_line(self, VIRTIO_CONSOLE_IRQ, 1);
+       }
+}
+
+static bool virtio_console_pci_io_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
+{
+       unsigned long offset = port - IOPORT_VIRTIO_CONSOLE;
+
+       switch (offset) {
+       case VIRTIO_PCI_GUEST_FEATURES:
+               console_device.guest_features   = ioport__read32(data);
+               break;
+       case VIRTIO_PCI_QUEUE_PFN: {
+               struct virt_queue *queue;
+               void *p;
+
+               assert(console_device.queue_selector < VIRTIO_CONSOLE_NUM_QUEUES);
+
+               queue           = &console_device.vqs[console_device.queue_selector];
+               queue->pfn      = ioport__read32(data);
+               p               = guest_flat_to_host(self, queue->pfn << 12);
+
+               vring_init(&queue->vring, VIRTIO_CONSOLE_QUEUE_SIZE, p, 4096);
+
+               break;
+       }
+       case VIRTIO_PCI_QUEUE_SEL:
+               console_device.queue_selector   = ioport__read16(data);
+               break;
+       case VIRTIO_PCI_QUEUE_NOTIFY: {
+               uint16_t queue_index;
+               queue_index     = ioport__read16(data);
+               virtio_console_handle_callback(self, queue_index);
+               break;
+       }
+       case VIRTIO_PCI_STATUS:
+               console_device.status           = ioport__read8(data);
+               break;
+       case VIRTIO_MSI_CONFIG_VECTOR:
+               console_device.config_vector    = VIRTIO_MSI_NO_VECTOR;
+               break;
+       case VIRTIO_MSI_QUEUE_VECTOR:
+               break;
+       default:
+               return false;
+       };
+
+       return true;
+}
+
+static struct ioport_operations virtio_console_io_ops = {
+       .io_in  = virtio_console_pci_io_in,
+       .io_out = virtio_console_pci_io_out,
+};
+
+#define PCI_VENDOR_ID_REDHAT_QUMRANET          0x1af4
+#define PCI_DEVICE_ID_VIRTIO_CONSOLE           0x1002
+#define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET        0x1af4
+#define PCI_SUBSYSTEM_ID_VIRTIO_CONSOLE                0x0003
+
+static struct pci_device_header virtio_console_pci_device = {
+       .vendor_id              = PCI_VENDOR_ID_REDHAT_QUMRANET,
+       .device_id              = PCI_DEVICE_ID_VIRTIO_CONSOLE,
+       .header_type            = PCI_HEADER_TYPE_NORMAL,
+       .revision_id            = 0,
+       .class                  = (0x07 << 8) | (0x80 << 4) | (0x0 << 0),
+       .subsys_vendor_id       = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
+       .subsys_id              = PCI_SUBSYSTEM_ID_VIRTIO_CONSOLE,
+       .bar[0]                 = IOPORT_VIRTIO_CONSOLE | PCI_BASE_ADDRESS_SPACE_IO,
+       .irq_pin                = 3,
+       .irq_line               = VIRTIO_CONSOLE_IRQ,
+};
+
+void virtio_console__init(struct kvm *self)
+{
+       pci__register(&virtio_console_pci_device, PCI_VIRTIO_CONSOLE_DEVNUM);
+       ioport__register(IOPORT_VIRTIO_CONSOLE, &virtio_console_io_ops, IOPORT_VIRTIO_CONSOLE_SIZE);
+}
diff --git a/tools/kvm/include/kvm/console-virtio.h b/tools/kvm/include/kvm/console-virtio.h
new file mode 100644 (file)
index 0000000..d2e5d19
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef KVM__CONSOLE_VIRTIO_H
+#define KVM__CONSOLE_VIRTIO_H
+
+struct kvm;
+
+void virtio_console__init(struct kvm *self);
+void virtio_console__inject_interrupt(struct kvm *self);
+
+#endif /* KVM__CONSOLE_VIRTIO_H */
index 159453504888c1dce593625131b7f5f868fe8964..90906d29307a1630731aa507f8733fda998f7eb7 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "kvm/8250-serial.h"
 #include "kvm/blk-virtio.h"
+#include "kvm/console-virtio.h"
 #include "kvm/disk-image.h"
 #include "kvm/util.h"
 #include "kvm/pci.h"
@@ -139,10 +140,13 @@ int main(int argc, char *argv[])
                kvm__enable_singlestep(kvm);
 
        serial8250__init(kvm);
+
        pci__init();
 
        blk_virtio__init(kvm);
 
+       virtio_console__init(kvm);
+
        kvm__start_timer(kvm);
 
        for (;;) {
@@ -182,6 +186,7 @@ int main(int argc, char *argv[])
                }
                case KVM_EXIT_INTR: {
                        serial8250__inject_interrupt(kvm);
+                       virtio_console__inject_interrupt(kvm);
                        break;
                }
                case KVM_EXIT_SHUTDOWN: