- rename {blk,console}-virtio.* to virtio-{blk,console}.* - change the virtio block device PCI io space operation callback names Signed-off-by: Asias He <asias.hejun@xxxxxxxxx> --- tools/kvm/Makefile | 4 +- tools/kvm/blk-virtio.c | 293 -------------------------------- tools/kvm/console-virtio.c | 218 ------------------------ tools/kvm/include/kvm/blk-virtio.h | 8 - tools/kvm/include/kvm/console-virtio.h | 9 - tools/kvm/include/kvm/virtio-blk.h | 8 + tools/kvm/include/kvm/virtio-console.h | 9 + tools/kvm/include/kvm/virtio-pci.h | 59 +++++++ tools/kvm/include/kvm/virtio_pci.h | 59 ------- tools/kvm/main.c | 6 +- tools/kvm/virtio-blk.c | 293 ++++++++++++++++++++++++++++++++ tools/kvm/virtio-console.c | 218 ++++++++++++++++++++++++ 12 files changed, 592 insertions(+), 592 deletions(-) delete mode 100644 tools/kvm/blk-virtio.c delete mode 100644 tools/kvm/console-virtio.c delete mode 100644 tools/kvm/include/kvm/blk-virtio.h delete mode 100644 tools/kvm/include/kvm/console-virtio.h create mode 100644 tools/kvm/include/kvm/virtio-blk.h create mode 100644 tools/kvm/include/kvm/virtio-console.h create mode 100644 tools/kvm/include/kvm/virtio-pci.h delete mode 100644 tools/kvm/include/kvm/virtio_pci.h create mode 100644 tools/kvm/virtio-blk.c create mode 100644 tools/kvm/virtio-console.c diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile index 82d41b6..45498a2 100644 --- a/tools/kvm/Makefile +++ b/tools/kvm/Makefile @@ -13,8 +13,8 @@ CSCOPE = cscope TAGS = ctags OBJS += 8250-serial.o -OBJS += blk-virtio.o -OBJS += console-virtio.o +OBJS += virtio-blk.o +OBJS += virtio-console.o OBJS += cpuid.o OBJS += read-write.o OBJS += disk-image.o diff --git a/tools/kvm/blk-virtio.c b/tools/kvm/blk-virtio.c deleted file mode 100644 index 2eb9734..0000000 --- a/tools/kvm/blk-virtio.c +++ /dev/null @@ -1,293 +0,0 @@ -#include "kvm/blk-virtio.h" - -#include "kvm/virtio_pci.h" - -#include "kvm/disk-image.h" -#include "kvm/virtio.h" -#include "kvm/ioport.h" -#include "kvm/util.h" -#include "kvm/kvm.h" -#include "kvm/pci.h" - -#include <linux/virtio_ring.h> -#include <linux/virtio_blk.h> -#include <inttypes.h> -#include <assert.h> - -#define VIRTIO_BLK_IRQ 14 - -#define NUM_VIRT_QUEUES 1 - -#define VIRTIO_BLK_QUEUE_SIZE 128 - -struct blk_device { - struct virtio_blk_config blk_config; - uint32_t host_features; - uint32_t guest_features; - uint16_t config_vector; - uint8_t status; - - /* virtio queue */ - uint16_t queue_selector; - - struct virt_queue virt_queues[NUM_VIRT_QUEUES]; -}; - -#define DISK_SEG_MAX 126 - -static struct blk_device blk_device = { - .blk_config = (struct virtio_blk_config) { - /* VIRTIO_BLK_F_SEG_MAX */ - .seg_max = DISK_SEG_MAX, - }, - /* - * Note we don't set VIRTIO_BLK_F_GEOMETRY here so the - * node kernel will compute disk geometry by own, the - * same applies to VIRTIO_BLK_F_BLK_SIZE - */ - .host_features = (1UL << VIRTIO_BLK_F_SEG_MAX), -}; - -static bool virtio_blk_config_in(void *data, unsigned long offset, int size, uint32_t count) -{ - uint8_t *config_space = (uint8_t *) &blk_device.blk_config; - - if (size != 1 || count != 1) - return false; - - ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]); - - return true; -} - -static bool blk_virtio_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) -{ - unsigned long offset; - - offset = port - IOPORT_VIRTIO_BLK; - - switch (offset) { - case VIRTIO_PCI_HOST_FEATURES: - ioport__write32(data, blk_device.host_features); - break; - case VIRTIO_PCI_GUEST_FEATURES: - return false; - case VIRTIO_PCI_QUEUE_PFN: - ioport__write32(data, blk_device.virt_queues[blk_device.queue_selector].pfn); - break; - case VIRTIO_PCI_QUEUE_NUM: - ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE); - break; - case VIRTIO_PCI_QUEUE_SEL: - case VIRTIO_PCI_QUEUE_NOTIFY: - return false; - case VIRTIO_PCI_STATUS: - ioport__write8(data, blk_device.status); - break; - case VIRTIO_PCI_ISR: - ioport__write8(data, 0x1); - kvm__irq_line(self, VIRTIO_BLK_IRQ, 0); - break; - case VIRTIO_MSI_CONFIG_VECTOR: - ioport__write16(data, blk_device.config_vector); - break; - default: - return virtio_blk_config_in(data, offset, size, count); - }; - - return true; -} - -static bool blk_virtio_request(struct kvm *self, struct virt_queue *queue) -{ - struct vring_used_elem *used_elem; - struct virtio_blk_outhdr *req; - uint16_t desc_block_last; - struct vring_desc *desc; - uint16_t desc_status; - uint16_t desc_block; - uint32_t block_len; - uint32_t block_cnt; - uint16_t desc_hdr; - uint8_t *status; - void *block; - int err; - int err_cnt; - - /* header */ - desc_hdr = virt_queue__pop(queue); - - if (desc_hdr >= queue->vring.num) { - warning("fatal I/O error"); - return false; - } - - desc = virt_queue__get_desc(queue, desc_hdr); - assert(!(desc->flags & VRING_DESC_F_INDIRECT)); - - req = guest_flat_to_host(self, desc->addr); - - /* status */ - desc_status = desc_hdr; - - do { - desc_block_last = desc_status; - desc_status = virt_queue__get_desc(queue, desc_status)->next; - - if (desc_status >= queue->vring.num) { - warning("fatal I/O error"); - return false; - } - - desc = virt_queue__get_desc(queue, desc_status); - assert(!(desc->flags & VRING_DESC_F_INDIRECT)); - - } while (desc->flags & VRING_DESC_F_NEXT); - - status = guest_flat_to_host(self, desc->addr); - - /* block */ - desc_block = desc_hdr; - block_cnt = 0; - err_cnt = 0; - - do { - desc_block = virt_queue__get_desc(queue, desc_block)->next; - - desc = virt_queue__get_desc(queue, desc_block); - assert(!(desc->flags & VRING_DESC_F_INDIRECT)); - - block = guest_flat_to_host(self, desc->addr); - block_len = desc->len; - - switch (req->type) { - case VIRTIO_BLK_T_IN: - err = disk_image__read_sector(self->disk_image, req->sector, block, block_len); - break; - case VIRTIO_BLK_T_OUT: - err = disk_image__write_sector(self->disk_image, req->sector, block, block_len); - break; - default: - warning("request type %d", req->type); - err = -1; - } - - if (err) - err_cnt++; - - req->sector += block_len >> SECTOR_SHIFT; - block_cnt += block_len; - - if (desc_block == desc_block_last) - break; - - if (desc_block >= queue->vring.num) { - warning("fatal I/O error"); - return false; - } - - } while (true); - - *status = err_cnt ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK; - - used_elem = virt_queue__get_used_elem(queue); - used_elem->id = desc_hdr; - used_elem->len = block_cnt; - - return true; -} - -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_BLK; - - switch (offset) { - case VIRTIO_PCI_GUEST_FEATURES: - blk_device.guest_features = ioport__read32(data); - break; - case VIRTIO_PCI_QUEUE_PFN: { - struct virt_queue *queue; - void *p; - - queue = &blk_device.virt_queues[blk_device.queue_selector]; - - queue->pfn = ioport__read32(data); - - p = guest_flat_to_host(self, queue->pfn << 12); - - vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096); - - break; - } - case VIRTIO_PCI_QUEUE_SEL: - blk_device.queue_selector = ioport__read16(data); - break; - case VIRTIO_PCI_QUEUE_NOTIFY: { - struct virt_queue *queue; - uint16_t queue_index; - - queue_index = ioport__read16(data); - - queue = &blk_device.virt_queues[queue_index]; - - while (queue->vring.avail->idx != queue->last_avail_idx) { - if (!blk_virtio_request(self, queue)) - return false; - } - kvm__irq_line(self, VIRTIO_BLK_IRQ, 1); - - break; - } - case VIRTIO_PCI_STATUS: - blk_device.status = ioport__read8(data); - break; - case VIRTIO_MSI_CONFIG_VECTOR: - blk_device.config_vector = VIRTIO_MSI_NO_VECTOR; - break; - case VIRTIO_MSI_QUEUE_VECTOR: - break; - default: - return false; - }; - - return true; -} - -static struct ioport_operations blk_virtio_io_ops = { - .io_in = blk_virtio_in, - .io_out = blk_virtio_out, -}; - -#define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 -#define PCI_DEVICE_ID_VIRTIO_BLK 0x1001 -#define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET 0x1af4 -#define PCI_SUBSYSTEM_ID_VIRTIO_BLK 0x0002 - -static struct pci_device_header blk_virtio_pci_device = { - .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, - .device_id = PCI_DEVICE_ID_VIRTIO_BLK, - .header_type = PCI_HEADER_TYPE_NORMAL, - .revision_id = 0, - .class = 0x010000, - .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, - .subsys_id = PCI_SUBSYSTEM_ID_VIRTIO_BLK, - .bar[0] = IOPORT_VIRTIO_BLK | PCI_BASE_ADDRESS_SPACE_IO, - .irq_pin = 1, - .irq_line = VIRTIO_BLK_IRQ, -}; - -#define PCI_VIRTIO_BLK_DEVNUM 1 - -void blk_virtio__init(struct kvm *self) -{ - if (!self->disk_image) - return; - - blk_device.blk_config.capacity = self->disk_image->size / SECTOR_SIZE; - - pci__register(&blk_virtio_pci_device, PCI_VIRTIO_BLK_DEVNUM); - - ioport__register(IOPORT_VIRTIO_BLK, &blk_virtio_io_ops, IOPORT_VIRTIO_BLK_SIZE); -} diff --git a/tools/kvm/console-virtio.c b/tools/kvm/console-virtio.c deleted file mode 100644 index 3926536..0000000 --- a/tools/kvm/console-virtio.c +++ /dev/null @@ -1,218 +0,0 @@ -#include "kvm/console-virtio.h" -#include "kvm/virtio_pci.h" -#include "kvm/disk-image.h" -#include "kvm/virtio.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/blk-virtio.h b/tools/kvm/include/kvm/blk-virtio.h deleted file mode 100644 index 02f39df..0000000 --- a/tools/kvm/include/kvm/blk-virtio.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef KVM__BLK_VIRTIO_H -#define KVM__BLK_VIRTIO_H - -struct kvm; - -void blk_virtio__init(struct kvm *self); - -#endif /* KVM__BLK_VIRTIO_H */ diff --git a/tools/kvm/include/kvm/console-virtio.h b/tools/kvm/include/kvm/console-virtio.h deleted file mode 100644 index d2e5d19..0000000 --- a/tools/kvm/include/kvm/console-virtio.h +++ /dev/null @@ -1,9 +0,0 @@ -#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/include/kvm/virtio-blk.h b/tools/kvm/include/kvm/virtio-blk.h new file mode 100644 index 0000000..f82bbc7 --- /dev/null +++ b/tools/kvm/include/kvm/virtio-blk.h @@ -0,0 +1,8 @@ +#ifndef KVM__BLK_VIRTIO_H +#define KVM__BLK_VIRTIO_H + +struct kvm; + +void virtio_blk__init(struct kvm *self); + +#endif /* KVM__BLK_VIRTIO_H */ diff --git a/tools/kvm/include/kvm/virtio-console.h b/tools/kvm/include/kvm/virtio-console.h new file mode 100644 index 0000000..d2e5d19 --- /dev/null +++ b/tools/kvm/include/kvm/virtio-console.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/include/kvm/virtio-pci.h b/tools/kvm/include/kvm/virtio-pci.h new file mode 100644 index 0000000..8098090 --- /dev/null +++ b/tools/kvm/include/kvm/virtio-pci.h @@ -0,0 +1,59 @@ +/* + * Virtio PCI driver + * + * This module allows virtio devices to be used over a virtual PCI device. + * This can be used with QEMU based VMMs like KVM or Xen. + * + * Copyright IBM Corp. 2007 + * + * Authors: + * Anthony Liguori <aliguori@xxxxxxxxxx> + * + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + */ + +#ifndef _LINUX_VIRTIO_PCI_H +#define _LINUX_VIRTIO_PCI_H + +/* A 32-bit r/o bitmask of the features supported by the host */ +#define VIRTIO_PCI_HOST_FEATURES 0 + +/* A 32-bit r/w bitmask of features activated by the guest */ +#define VIRTIO_PCI_GUEST_FEATURES 4 + +/* A 32-bit r/w PFN for the currently selected queue */ +#define VIRTIO_PCI_QUEUE_PFN 8 + +/* A 16-bit r/o queue size for the currently selected queue */ +#define VIRTIO_PCI_QUEUE_NUM 12 + +/* A 16-bit r/w queue selector */ +#define VIRTIO_PCI_QUEUE_SEL 14 + +/* A 16-bit r/w queue notifier */ +#define VIRTIO_PCI_QUEUE_NOTIFY 16 + +/* An 8-bit device status register. */ +#define VIRTIO_PCI_STATUS 18 + +/* An 8-bit r/o interrupt status register. Reading the value will return the + * current contents of the ISR and will also clear it. This is effectively + * a read-and-acknowledge. */ +#define VIRTIO_PCI_ISR 19 + +/* MSI-X registers: only enabled if MSI-X is enabled. */ +/* A 16-bit vector for configuration changes. */ +#define VIRTIO_MSI_CONFIG_VECTOR 20 + +/* A 16-bit vector for selected queue notifications. */ +#define VIRTIO_MSI_QUEUE_VECTOR 22 + +/* Vector value used to disable MSI for queue */ +#define VIRTIO_MSI_NO_VECTOR 0xffff + +/* 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/include/kvm/virtio_pci.h b/tools/kvm/include/kvm/virtio_pci.h deleted file mode 100644 index 8098090..0000000 --- a/tools/kvm/include/kvm/virtio_pci.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Virtio PCI driver - * - * This module allows virtio devices to be used over a virtual PCI device. - * This can be used with QEMU based VMMs like KVM or Xen. - * - * Copyright IBM Corp. 2007 - * - * Authors: - * Anthony Liguori <aliguori@xxxxxxxxxx> - * - * This header is BSD licensed so anyone can use the definitions to implement - * compatible drivers/servers. - */ - -#ifndef _LINUX_VIRTIO_PCI_H -#define _LINUX_VIRTIO_PCI_H - -/* A 32-bit r/o bitmask of the features supported by the host */ -#define VIRTIO_PCI_HOST_FEATURES 0 - -/* A 32-bit r/w bitmask of features activated by the guest */ -#define VIRTIO_PCI_GUEST_FEATURES 4 - -/* A 32-bit r/w PFN for the currently selected queue */ -#define VIRTIO_PCI_QUEUE_PFN 8 - -/* A 16-bit r/o queue size for the currently selected queue */ -#define VIRTIO_PCI_QUEUE_NUM 12 - -/* A 16-bit r/w queue selector */ -#define VIRTIO_PCI_QUEUE_SEL 14 - -/* A 16-bit r/w queue notifier */ -#define VIRTIO_PCI_QUEUE_NOTIFY 16 - -/* An 8-bit device status register. */ -#define VIRTIO_PCI_STATUS 18 - -/* An 8-bit r/o interrupt status register. Reading the value will return the - * current contents of the ISR and will also clear it. This is effectively - * a read-and-acknowledge. */ -#define VIRTIO_PCI_ISR 19 - -/* MSI-X registers: only enabled if MSI-X is enabled. */ -/* A 16-bit vector for configuration changes. */ -#define VIRTIO_MSI_CONFIG_VECTOR 20 - -/* A 16-bit vector for selected queue notifications. */ -#define VIRTIO_MSI_QUEUE_VECTOR 22 - -/* Vector value used to disable MSI for queue */ -#define VIRTIO_MSI_NO_VECTOR 0xffff - -/* 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/main.c b/tools/kvm/main.c index d8f0c09..7401f6f 100644 --- a/tools/kvm/main.c +++ b/tools/kvm/main.c @@ -1,8 +1,8 @@ #include "kvm/kvm.h" #include "kvm/8250-serial.h" -#include "kvm/blk-virtio.h" -#include "kvm/console-virtio.h" +#include "kvm/virtio-blk.h" +#include "kvm/virtio-console.h" #include "kvm/disk-image.h" #include "kvm/util.h" #include "kvm/pci.h" @@ -145,7 +145,7 @@ int main(int argc, char *argv[]) pci__init(); - blk_virtio__init(kvm); + virtio_blk__init(kvm); virtio_console__init(kvm); diff --git a/tools/kvm/virtio-blk.c b/tools/kvm/virtio-blk.c new file mode 100644 index 0000000..a41d5ac --- /dev/null +++ b/tools/kvm/virtio-blk.c @@ -0,0 +1,293 @@ +#include "kvm/virtio-blk.h" + +#include "kvm/virtio-pci.h" + +#include "kvm/disk-image.h" +#include "kvm/virtio.h" +#include "kvm/ioport.h" +#include "kvm/util.h" +#include "kvm/kvm.h" +#include "kvm/pci.h" + +#include <linux/virtio_ring.h> +#include <linux/virtio_blk.h> +#include <inttypes.h> +#include <assert.h> + +#define VIRTIO_BLK_IRQ 14 + +#define NUM_VIRT_QUEUES 1 + +#define VIRTIO_BLK_QUEUE_SIZE 128 + +struct blk_device { + struct virtio_blk_config blk_config; + uint32_t host_features; + uint32_t guest_features; + uint16_t config_vector; + uint8_t status; + + /* virtio queue */ + uint16_t queue_selector; + + struct virt_queue virt_queues[NUM_VIRT_QUEUES]; +}; + +#define DISK_SEG_MAX 126 + +static struct blk_device blk_device = { + .blk_config = (struct virtio_blk_config) { + /* VIRTIO_BLK_F_SEG_MAX */ + .seg_max = DISK_SEG_MAX, + }, + /* + * Note we don't set VIRTIO_BLK_F_GEOMETRY here so the + * node kernel will compute disk geometry by own, the + * same applies to VIRTIO_BLK_F_BLK_SIZE + */ + .host_features = (1UL << VIRTIO_BLK_F_SEG_MAX), +}; + +static bool virtio_blk_pci_io_device_specific_in(void *data, unsigned long offset, int size, uint32_t count) +{ + uint8_t *config_space = (uint8_t *) &blk_device.blk_config; + + if (size != 1 || count != 1) + return false; + + ioport__write8(data, config_space[offset - VIRTIO_PCI_CONFIG_NOMSI]); + + return true; +} + +static bool virtio_blk_pci_io_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) +{ + unsigned long offset; + + offset = port - IOPORT_VIRTIO_BLK; + + switch (offset) { + case VIRTIO_PCI_HOST_FEATURES: + ioport__write32(data, blk_device.host_features); + break; + case VIRTIO_PCI_GUEST_FEATURES: + return false; + case VIRTIO_PCI_QUEUE_PFN: + ioport__write32(data, blk_device.virt_queues[blk_device.queue_selector].pfn); + break; + case VIRTIO_PCI_QUEUE_NUM: + ioport__write16(data, VIRTIO_BLK_QUEUE_SIZE); + break; + case VIRTIO_PCI_QUEUE_SEL: + case VIRTIO_PCI_QUEUE_NOTIFY: + return false; + case VIRTIO_PCI_STATUS: + ioport__write8(data, blk_device.status); + break; + case VIRTIO_PCI_ISR: + ioport__write8(data, 0x1); + kvm__irq_line(self, VIRTIO_BLK_IRQ, 0); + break; + case VIRTIO_MSI_CONFIG_VECTOR: + ioport__write16(data, blk_device.config_vector); + break; + default: + return virtio_blk_pci_io_device_specific_in(data, offset, size, count); + }; + + return true; +} + +static bool virtio_blk_request(struct kvm *self, struct virt_queue *queue) +{ + struct vring_used_elem *used_elem; + struct virtio_blk_outhdr *req; + uint16_t desc_block_last; + struct vring_desc *desc; + uint16_t desc_status; + uint16_t desc_block; + uint32_t block_len; + uint32_t block_cnt; + uint16_t desc_hdr; + uint8_t *status; + void *block; + int err; + int err_cnt; + + /* header */ + desc_hdr = virt_queue__pop(queue); + + if (desc_hdr >= queue->vring.num) { + warning("fatal I/O error"); + return false; + } + + desc = virt_queue__get_desc(queue, desc_hdr); + assert(!(desc->flags & VRING_DESC_F_INDIRECT)); + + req = guest_flat_to_host(self, desc->addr); + + /* status */ + desc_status = desc_hdr; + + do { + desc_block_last = desc_status; + desc_status = virt_queue__get_desc(queue, desc_status)->next; + + if (desc_status >= queue->vring.num) { + warning("fatal I/O error"); + return false; + } + + desc = virt_queue__get_desc(queue, desc_status); + assert(!(desc->flags & VRING_DESC_F_INDIRECT)); + + } while (desc->flags & VRING_DESC_F_NEXT); + + status = guest_flat_to_host(self, desc->addr); + + /* block */ + desc_block = desc_hdr; + block_cnt = 0; + err_cnt = 0; + + do { + desc_block = virt_queue__get_desc(queue, desc_block)->next; + + desc = virt_queue__get_desc(queue, desc_block); + assert(!(desc->flags & VRING_DESC_F_INDIRECT)); + + block = guest_flat_to_host(self, desc->addr); + block_len = desc->len; + + switch (req->type) { + case VIRTIO_BLK_T_IN: + err = disk_image__read_sector(self->disk_image, req->sector, block, block_len); + break; + case VIRTIO_BLK_T_OUT: + err = disk_image__write_sector(self->disk_image, req->sector, block, block_len); + break; + default: + warning("request type %d", req->type); + err = -1; + } + + if (err) + err_cnt++; + + req->sector += block_len >> SECTOR_SHIFT; + block_cnt += block_len; + + if (desc_block == desc_block_last) + break; + + if (desc_block >= queue->vring.num) { + warning("fatal I/O error"); + return false; + } + + } while (true); + + *status = err_cnt ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK; + + used_elem = virt_queue__get_used_elem(queue); + used_elem->id = desc_hdr; + used_elem->len = block_cnt; + + return true; +} + +static bool virtio_blk_pci_io_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count) +{ + unsigned long offset; + + offset = port - IOPORT_VIRTIO_BLK; + + switch (offset) { + case VIRTIO_PCI_GUEST_FEATURES: + blk_device.guest_features = ioport__read32(data); + break; + case VIRTIO_PCI_QUEUE_PFN: { + struct virt_queue *queue; + void *p; + + queue = &blk_device.virt_queues[blk_device.queue_selector]; + + queue->pfn = ioport__read32(data); + + p = guest_flat_to_host(self, queue->pfn << 12); + + vring_init(&queue->vring, VIRTIO_BLK_QUEUE_SIZE, p, 4096); + + break; + } + case VIRTIO_PCI_QUEUE_SEL: + blk_device.queue_selector = ioport__read16(data); + break; + case VIRTIO_PCI_QUEUE_NOTIFY: { + struct virt_queue *queue; + uint16_t queue_index; + + queue_index = ioport__read16(data); + + queue = &blk_device.virt_queues[queue_index]; + + while (queue->vring.avail->idx != queue->last_avail_idx) { + if (!virtio_blk_request(self, queue)) + return false; + } + kvm__irq_line(self, VIRTIO_BLK_IRQ, 1); + + break; + } + case VIRTIO_PCI_STATUS: + blk_device.status = ioport__read8(data); + break; + case VIRTIO_MSI_CONFIG_VECTOR: + blk_device.config_vector = VIRTIO_MSI_NO_VECTOR; + break; + case VIRTIO_MSI_QUEUE_VECTOR: + break; + default: + return false; + }; + + return true; +} + +static struct ioport_operations virtio_blk_io_ops = { + .io_in = virtio_blk_pci_io_in, + .io_out = virtio_blk_pci_io_out, +}; + +#define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 +#define PCI_DEVICE_ID_VIRTIO_BLK 0x1001 +#define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET 0x1af4 +#define PCI_SUBSYSTEM_ID_VIRTIO_BLK 0x0002 + +static struct pci_device_header virtio_blk_pci_device = { + .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, + .device_id = PCI_DEVICE_ID_VIRTIO_BLK, + .header_type = PCI_HEADER_TYPE_NORMAL, + .revision_id = 0, + .class = 0x010000, + .subsys_vendor_id = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET, + .subsys_id = PCI_SUBSYSTEM_ID_VIRTIO_BLK, + .bar[0] = IOPORT_VIRTIO_BLK | PCI_BASE_ADDRESS_SPACE_IO, + .irq_pin = 1, + .irq_line = VIRTIO_BLK_IRQ, +}; + +#define PCI_VIRTIO_BLK_DEVNUM 1 + +void virtio_blk__init(struct kvm *self) +{ + if (!self->disk_image) + return; + + blk_device.blk_config.capacity = self->disk_image->size / SECTOR_SIZE; + + pci__register(&virtio_blk_pci_device, PCI_VIRTIO_BLK_DEVNUM); + + ioport__register(IOPORT_VIRTIO_BLK, &virtio_blk_io_ops, IOPORT_VIRTIO_BLK_SIZE); +} diff --git a/tools/kvm/virtio-console.c b/tools/kvm/virtio-console.c new file mode 100644 index 0000000..fd79c61 --- /dev/null +++ b/tools/kvm/virtio-console.c @@ -0,0 +1,218 @@ +#include "kvm/virtio-console.h" +#include "kvm/virtio-pci.h" +#include "kvm/disk-image.h" +#include "kvm/virtio.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); +} -- 1.7.4.1 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html