From 35f0059b0d3a458b575721871e19f464a73c7ddb Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Sun, 15 Aug 2010 18:39:27 +0300 Subject: [PATCH] kvm: Virtio block device emulation 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 --- tools/kvm/blk-virtio.c | 34 ++++++++++++++++++++++++------ tools/kvm/include/kvm/ioport.h | 10 +++++++++ tools/kvm/include/kvm/kvm.h | 1 + tools/kvm/include/kvm/virtio_pci.h | 4 ++++ tools/kvm/kvm.c | 15 +++++++++++++ tools/kvm/main.c | 8 ++++++- 6 files changed, 65 insertions(+), 7 deletions(-) diff --git a/tools/kvm/blk-virtio.c b/tools/kvm/blk-virtio.c index e7eb44e45a57..906ab8bb1465 100644 --- a/tools/kvm/blk-virtio.c +++ b/tools/kvm/blk-virtio.c @@ -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 - 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); } diff --git a/tools/kvm/include/kvm/ioport.h b/tools/kvm/include/kvm/ioport.h index b4ae9abea61e..10589b0ac990 100644 --- a/tools/kvm/include/kvm/ioport.h +++ b/tools/kvm/include/kvm/ioport.h @@ -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; diff --git a/tools/kvm/include/kvm/kvm.h b/tools/kvm/include/kvm/kvm.h index ca959c868af9..9cf2f73a7d19 100644 --- a/tools/kvm/include/kvm/kvm.h +++ b/tools/kvm/include/kvm/kvm.h @@ -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); diff --git a/tools/kvm/include/kvm/virtio_pci.h b/tools/kvm/include/kvm/virtio_pci.h index 405e290ec04f..d970ee9f782d 100644 --- a/tools/kvm/include/kvm/virtio_pci.h +++ b/tools/kvm/include/kvm/virtio_pci.h @@ -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 */ diff --git a/tools/kvm/kvm.c b/tools/kvm/kvm.c index 4a75ea7c1678..4174c28c6eed 100644 --- a/tools/kvm/kvm.c +++ b/tools/kvm/kvm.c @@ -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", diff --git a/tools/kvm/main.c b/tools/kvm/main.c index 1fd930181bb0..c12d3d0ad5a0 100644 --- a/tools/kvm/main.c +++ b/tools/kvm/main.c @@ -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); -- 2.39.5