From patchwork Fri Apr 8 14:12:52 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Asias He X-Patchwork-Id: 692571 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p37EEUT4001801 for ; Thu, 7 Apr 2011 14:14:30 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754134Ab1DGOO1 (ORCPT ); Thu, 7 Apr 2011 10:14:27 -0400 Received: from mail-iy0-f174.google.com ([209.85.210.174]:40472 "EHLO mail-iy0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752301Ab1DGOO0 (ORCPT ); Thu, 7 Apr 2011 10:14:26 -0400 Received: by iyb14 with SMTP id 14so2503015iyb.19 for ; Thu, 07 Apr 2011 07:14:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:from:to:cc:subject:date:message-id:x-mailer :in-reply-to:references; bh=HdeZ9wTA9hQY7tzwT4Rjt9VXgzmyWJEVRMrjaDUItmc=; b=ANr5zv15Yfbjv4XlKlKu6J3ju20oj/6fyYY0Xf3mxmf8GprcoEHYhkrAP/auuIO/4F 53MQgpl1glHN1fvBIM7m215mIaAKjkwnf/1wXhnsp2cj+zOW3Udp5OcPfzQdTvLkVxS9 hmGkFZflM669bKEH1sEv1ob0iuMChEXbfe/2s= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; b=C3a5g0PoatGTf0Ty4HpAF3eVukt3pWnXxvy4hFg8GaQSsqW41OwU25+TkyK0GwefQN +mWuU7p6J688nyjELq1zKkngNSYagR8ZVaAxa6KryOxVrnLm2vfJ4HV4VT7uhr5qlAjm X0eGUxpk6KfnxSyr+bcggJiKhTaULeDfOZk3g= Received: by 10.42.166.200 with SMTP id p8mr1131943icy.226.1302185665200; Thu, 07 Apr 2011 07:14:25 -0700 (PDT) Received: from localhost.localdomain ([219.224.169.130]) by mx.google.com with ESMTPS id m10sm2277615wfl.11.2011.04.07.07.14.19 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 07 Apr 2011 07:14:24 -0700 (PDT) From: Asias He To: Pekka Enberg , Cyrill Gorcunov Cc: kvm@vger.kernel.org, Ingo Molnar , Asias He Subject: [PATCH 5/6] kvm tools: add virtio console support v2 Date: Fri, 8 Apr 2011 22:12:52 +0800 Message-Id: <1302271973-7604-5-git-send-email-asias.hejun@gmail.com> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1302271973-7604-1-git-send-email-asias.hejun@gmail.com> References: <1302271973-7604-1-git-send-email-asias.hejun@gmail.com> Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Thu, 07 Apr 2011 14:14:30 +0000 (UTC) - consolidate all console related code to term.c - use timer based approach instead of thread to deal with user input NOTE: Please add something like this: T2:23:respawn:/sbin/getty -L hvc0 9600 vt100 to your /etc/inittab file to get a virtio console login. Signed-off-by: Asias He --- tools/kvm/Makefile | 1 + tools/kvm/console-virtio.c | 218 ++++++++++++++++++++++++++++++++ tools/kvm/include/kvm/console-virtio.h | 9 ++ tools/kvm/main.c | 5 + 4 files changed, 233 insertions(+), 0 deletions(-) create mode 100644 tools/kvm/console-virtio.c create mode 100644 tools/kvm/include/kvm/console-virtio.h diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile index 0244f9b..55f342d 100644 --- a/tools/kvm/Makefile +++ b/tools/kvm/Makefile @@ -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 index 0000000..f094610 --- /dev/null +++ b/tools/kvm/console-virtio.c @@ -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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..d2e5d19 --- /dev/null +++ b/tools/kvm/include/kvm/console-virtio.h @@ -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 */ diff --git a/tools/kvm/main.c b/tools/kvm/main.c index 1594535..90906d2 100644 --- a/tools/kvm/main.c +++ b/tools/kvm/main.c @@ -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: