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 <penberg@kernel.org>
This commit is contained in:
Pekka Enberg
2015-06-01 16:39:40 +01:00
committed by Will Deacon
parent c9bb7f2059
commit 8b1ff07e1f
6 changed files with 65 additions and 7 deletions
+28 -6
View 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);
}
+10
View 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;
+1
View 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);
+4
View 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 */
+15
View 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",
+7 -1
View 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);