]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
kvm: Virtio block device emulation
authorPekka Enberg <penberg@kernel.org>
Sun, 15 Aug 2010 15:39:27 +0000 (18:39 +0300)
committerPekka Enberg <penberg@kernel.org>
Sun, 29 Aug 2010 10:41:21 +0000 (13:41 +0300)
This patch implements a rough first version of virtio block device emulation.
You can enable it with the '--enable-virtio' command line option. We're still
missing virtio queue handling so enabling virtio will cause a guest kernel to
hang.

Signed-off-by: Pekka Enberg <penberg@kernel.org>
tools/kvm/blk-virtio.c
tools/kvm/include/kvm/ioport.h
tools/kvm/include/kvm/kvm.h
tools/kvm/include/kvm/virtio_pci.h
tools/kvm/kvm.c
tools/kvm/main.c

index e7eb44e45a570337523ad9e9e28d10573656f505..906ab8bb1465101a0dee1f8808be740dd9a459fd 100644 (file)
@@ -2,9 +2,10 @@
 
 #include "kvm/virtio_pci.h"
 #include "kvm/ioport.h"
+#include "kvm/kvm.h"
 #include "kvm/pci.h"
 
-#define VIRTIO_PCI_IOPORT_SIZE         24
+#define VIRTIO_BLK_IRQ         14
 
 struct device {
        uint32_t                guest_features;
@@ -19,13 +20,22 @@ static bool blk_virtio_in(struct kvm *self, uint16_t port, void *data, int size,
 
        offset          = port - IOPORT_VIRTIO;
 
+       /* XXX: Let virtio block device handle this */
+       if (offset >= VIRTIO_PCI_CONFIG_NOMSI)
+               return true;
+
        switch (offset) {
        case VIRTIO_PCI_HOST_FEATURES:
                ioport__write32(data, 0x00);
                break;
        case VIRTIO_PCI_GUEST_FEATURES:
+               return false;
        case VIRTIO_PCI_QUEUE_PFN:
+               ioport__write32(data, 0x00);
+               break;
        case VIRTIO_PCI_QUEUE_NUM:
+               ioport__write16(data, 0x10);
+               break;
        case VIRTIO_PCI_QUEUE_SEL:
        case VIRTIO_PCI_QUEUE_NOTIFY:
                return false;
@@ -33,6 +43,9 @@ static bool blk_virtio_in(struct kvm *self, uint16_t port, void *data, int size,
                ioport__write8(data, device.status);
                break;
        case VIRTIO_PCI_ISR:
+               ioport__write8(data, 0x1);
+               kvm__irq_line(self, VIRTIO_BLK_IRQ, 0);
+               return true;
        case VIRTIO_MSI_CONFIG_VECTOR:
        default:
                return false;
@@ -41,22 +54,28 @@ static bool blk_virtio_in(struct kvm *self, uint16_t port, void *data, int size,
        return true;
 }
 
-#include <stdio.h>
-
 static bool blk_virtio_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
 {
        unsigned long offset;
 
        offset          = port - IOPORT_VIRTIO;
 
+       /* XXX: Let virtio block device handle this */
+       if (offset >= VIRTIO_PCI_CONFIG_NOMSI)
+               return true;
+
        switch (offset) {
        case VIRTIO_PCI_GUEST_FEATURES:
                device.guest_features   = ioport__read32(data);
                break;
        case VIRTIO_PCI_QUEUE_PFN:
+               return true;
        case VIRTIO_PCI_QUEUE_SEL:
-       case VIRTIO_PCI_QUEUE_NOTIFY:
-               return false;
+               return true;
+       case VIRTIO_PCI_QUEUE_NOTIFY: {
+               kvm__irq_line(self, VIRTIO_BLK_IRQ, 1);
+               return true;
+       }
        case VIRTIO_PCI_STATUS:
                device.status           = ioport__read8(data);
                break;
@@ -88,11 +107,14 @@ static struct pci_device_header blk_virtio_pci_device = {
        .subsys_vendor_id       = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
        .subsys_id              = PCI_SUBSYSTEM_ID_VIRTIO_BLK,
        .bar[0]                 = IOPORT_VIRTIO | PCI_BASE_ADDRESS_SPACE_IO,
+       /* XXX: Is this IRQ setup OK? */
+       .irq_pin                = 1,
+       .irq_line               = VIRTIO_BLK_IRQ,
 };
 
 void blk_virtio__init(void)
 {
        pci__register(&blk_virtio_pci_device, 1);
 
-       ioport__register(IOPORT_VIRTIO, &blk_virtio_io_ops, VIRTIO_PCI_IOPORT_SIZE);
+       ioport__register(IOPORT_VIRTIO, &blk_virtio_io_ops, 256);
 }
index b4ae9abea61e0c77c33fe991c5be31358c7c94a6..10589b0ac9907dbf7379acaddb8e5656053bd52e 100644 (file)
@@ -22,6 +22,11 @@ static inline uint8_t ioport__read8(uint8_t *data)
        return *data;
 }
 
+static inline uint16_t ioport__read16(uint16_t *data)
+{
+       return *data;
+}
+
 static inline uint32_t ioport__read32(uint32_t *data)
 {
        return *data;
@@ -32,6 +37,11 @@ static inline void ioport__write8(uint8_t *data, uint8_t value)
        *data            = value;
 }
 
+static inline void ioport__write16(uint16_t *data, uint16_t value)
+{
+       *data            = value;
+}
+
 static inline void ioport__write32(uint32_t *data, uint32_t value)
 {
        *data            = value;
index ca959c868af99e9c08b830523055ec34a7450dcf..9cf2f73a7d19132333cab02acd76d567390941cb 100644 (file)
@@ -40,6 +40,7 @@ bool kvm__load_kernel(struct kvm *kvm, const char *kernel_filename,
 void kvm__reset_vcpu(struct kvm *self);
 void kvm__setup_mem(struct kvm *self);
 void kvm__run(struct kvm *self);
+void kvm__irq_line(struct kvm *self, int irq, int level);
 bool kvm__emulate_io(struct kvm *self, uint16_t port, void *data, int direction, int size, uint32_t count);
 bool kvm__emulate_mmio(struct kvm *self, uint64_t phys_addr, uint8_t *data, uint32_t len, uint8_t is_write);
 
index 405e290ec04f539ffbc05d8723baf260e5d8a02e..d970ee9f782de62349eaf9dd7c77eb840da4a78d 100644 (file)
@@ -49,4 +49,8 @@
 /* A 16-bit vector for selected queue notifications. */
 #define VIRTIO_MSI_QUEUE_VECTOR         22
 
+/* Config space size */
+#define VIRTIO_PCI_CONFIG_NOMSI         20
+#define VIRTIO_PCI_CONFIG_MSI           24
+
 #endif /* _LINUX_VIRTIO_PCI_H */
index 4a75ea7c16780565535d025e24065b3febf87390..4174c28c6eed9c9493f5910b4a35ba58bcfb31f3 100644 (file)
@@ -611,6 +611,21 @@ void kvm__run(struct kvm *self)
                die_perror("KVM_RUN failed");
 }
 
+void kvm__irq_line(struct kvm *self, int irq, int level)
+{
+       struct kvm_irq_level irq_level;
+
+       irq_level       = (struct kvm_irq_level) {
+               {
+                       .irq            = irq,
+               },
+               .level          = level,
+       };
+
+       if (ioctl(self->vm_fd, KVM_IRQ_LINE, &irq_level) < 0)
+               die_perror("KVM_IRQ_LINE failed");
+}
+
 static void print_dtable(const char *name, struct kvm_dtable *dtable)
 {
        printf(" %s                 %016" PRIx64 "  %08" PRIx16 "\n",
index 1fd930181bb0f8e302909d42b8e2a21220ae3197..c12d3d0ad5a0f3a8c2d367e84694c96d02c3f940 100644 (file)
@@ -51,6 +51,7 @@ int main(int argc, char *argv[])
        const char *kernel_cmdline = NULL;
        const char *kvm_dev = "/dev/kvm";
        unsigned long ram_size = 64UL << 20;
+       bool enable_virtio = false;
        bool single_step = false;
        int i;
 
@@ -72,6 +73,9 @@ int main(int argc, char *argv[])
                } else if (option_matches(argv[i], "--single-step")) {
                        single_step     = true;
                        continue;
+               } else if (option_matches(argv[i], "--enable-virtio")) {
+                       enable_virtio   = true;
+                       continue;
                } else if (option_matches(argv[i], "--mem=")) {
                        unsigned long val = atol(&argv[i][6]) << 20;
                        if (val < ram_size)
@@ -117,7 +121,9 @@ int main(int argc, char *argv[])
 
        early_printk__init();
        pci__init();
-       blk_virtio__init();
+
+       if (enable_virtio)
+               blk_virtio__init();
 
        for (;;) {
                kvm__run(kvm);