This patch builds a virtio-pci layer which can be used by virtio
devices as a layer to interact with virtio-pci. The purpose of the
patch is to seperate the common virtio-pci layer from being replicated
in all virtio devices.

The new layer provides a callback interface to receive information about
virtio events.

This allows us to share the entire functionality of virtio-pci throughout all
virtio devices, for example - we don't need to implement MSI-X for each device
and can just do it once for virtio-pci.

Signed-off-by: Sasha Levin <levinsasha...@gmail.com>
---
 tools/kvm/Makefile                 |    1 +
 tools/kvm/include/kvm/ioport.h     |    1 +
 tools/kvm/include/kvm/virtio-pci.h |   51 ++++++++
 tools/kvm/virtio/pci.c             |  231 ++++++++++++++++++++++++++++++++++++
 4 files changed, 284 insertions(+), 0 deletions(-)
 create mode 100644 tools/kvm/include/kvm/virtio-pci.h
 create mode 100644 tools/kvm/virtio/pci.c

diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile
index 316c2c9..669386f 100644
--- a/tools/kvm/Makefile
+++ b/tools/kvm/Makefile
@@ -52,6 +52,7 @@ OBJS  += virtio/core.o
 OBJS   += virtio/net.o
 OBJS   += virtio/rng.o
 OBJS    += virtio/balloon.o
+OBJS   += virtio/pci.o
 OBJS   += disk/blk.o
 OBJS   += disk/qcow.o
 OBJS   += disk/raw.o
diff --git a/tools/kvm/include/kvm/ioport.h b/tools/kvm/include/kvm/ioport.h
index 45c3856..5b857dd 100644
--- a/tools/kvm/include/kvm/ioport.h
+++ b/tools/kvm/include/kvm/ioport.h
@@ -4,6 +4,7 @@
 #include "kvm/rbtree-interval.h"
 
 #include <stdbool.h>
+#include <limits.h>
 #include <asm/types.h>
 #include <linux/types.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..4524a7f
--- /dev/null
+++ b/tools/kvm/include/kvm/virtio-pci.h
@@ -0,0 +1,51 @@
+#ifndef KVM__VIRTIO_PCI_H
+#define KVM__VIRTIO_PCI_H
+
+#include "kvm/pci.h"
+
+#include <linux/types.h>
+
+#define VIRTIO_PCI_MAX_VQ 3
+
+struct kvm;
+
+struct virtio_pci_ops {
+       void (*set_config)(struct kvm *kvm, void *dev, u8 data, u32 offset);
+       u8 (*get_config)(struct kvm *kvm, void *dev, u32 offset);
+
+       u32 (*get_host_features)(struct kvm *kvm, void *dev);
+       void (*set_guest_features)(struct kvm *kvm, void *dev, u32 features);
+
+       int (*init_vq)(struct kvm *kvm, void *dev, u32 vq, u32 pfn);
+       int (*notify_vq)(struct kvm *kvm, void *dev, u32 vq);
+       int (*get_pfn_vq)(struct kvm *kvm, void *dev, u32 vq);
+       int (*get_size_vq)(struct kvm *kvm, void *dev, u32 vq);
+};
+
+struct virtio_pci {
+       struct pci_device_header pci_hdr;
+       struct virtio_pci_ops   ops;
+       void                    *dev;
+
+       u16                     base_addr;
+       u8                      status;
+       u8                      isr;
+
+       /* MSI-X */
+       u16                     config_vector;
+       u32                     config_gsi;
+       u32                     vq_vector[VIRTIO_PCI_MAX_VQ];
+       u32                     gsis[VIRTIO_PCI_MAX_VQ];
+       u32                     msix_io_block;
+       int                     msix_enabled;
+
+       /* virtio queue */
+       u16                     queue_selector;
+};
+
+int virtio_pci__init(struct kvm *kvm, struct virtio_pci *vpci, void *dev,
+                       int device_id, int subsys_id);
+int virtio_pci__signal_vq(struct kvm *kvm, struct virtio_pci *vpci, u32 vq);
+int virtio_pci__signal_config(struct kvm *kvm, struct virtio_pci *vpci);
+
+#endif
diff --git a/tools/kvm/virtio/pci.c b/tools/kvm/virtio/pci.c
new file mode 100644
index 0000000..6d086fa
--- /dev/null
+++ b/tools/kvm/virtio/pci.c
@@ -0,0 +1,231 @@
+#include "kvm/virtio-pci.h"
+
+#include "kvm/ioport.h"
+#include "kvm/kvm.h"
+#include "kvm/virtio-pci-dev.h"
+#include "kvm/irq.h"
+#include "kvm/virtio.h"
+
+#include <linux/virtio_pci.h>
+#include <string.h>
+
+static bool virtio_pci__specific_io_in(struct kvm *kvm, struct virtio_pci 
*vpci, u16 port,
+                                       void *data, int size, int offset)
+{
+       u32 config_offset;
+       int type = virtio__get_dev_specific_field(offset - 20, 
vpci->msix_enabled,
+                                                       0, &config_offset);
+       if (type == VIRTIO_PCI_O_MSIX) {
+               switch (offset) {
+               case VIRTIO_MSI_CONFIG_VECTOR:
+                       ioport__write16(data, vpci->config_vector);
+                       break;
+               case VIRTIO_MSI_QUEUE_VECTOR:
+                       ioport__write16(data, 
vpci->vq_vector[vpci->queue_selector]);
+                       break;
+               };
+
+               return true;
+       } else if (type == VIRTIO_PCI_O_CONFIG) {
+               u8 cfg;
+
+               cfg = vpci->ops.get_config(kvm, vpci->dev, config_offset);
+               ioport__write8(data, cfg);
+               return true;
+       }
+
+       return false;
+}
+
+static bool virtio_pci__io_in(struct ioport *ioport, struct kvm *kvm, u16 
port, void *data, int size)
+{
+       unsigned long offset;
+       bool ret = true;
+       struct virtio_pci *vpci;
+       u32 val;
+
+       vpci = ioport->priv;
+       offset = port - vpci->base_addr;
+
+       switch (offset) {
+       case VIRTIO_PCI_HOST_FEATURES:
+               val = vpci->ops.get_host_features(kvm, vpci->dev);
+               ioport__write32(data, val);
+               break;
+       case VIRTIO_PCI_QUEUE_PFN:
+               val = vpci->ops.get_pfn_vq(kvm, vpci->dev, 
vpci->queue_selector);
+               ioport__write32(data, val);
+               break;
+       case VIRTIO_PCI_QUEUE_NUM:
+               val = vpci->ops.get_size_vq(kvm, vpci->dev, 
vpci->queue_selector);
+               ioport__write32(data, val);
+               break;
+               break;
+       case VIRTIO_PCI_STATUS:
+               ioport__write8(data, vpci->status);
+               break;
+       case VIRTIO_PCI_ISR:
+               ioport__write8(data, vpci->isr);
+               kvm__irq_line(kvm, vpci->pci_hdr.irq_line, VIRTIO_IRQ_LOW);
+               vpci->isr = VIRTIO_IRQ_LOW;
+               break;
+       default:
+               ret = virtio_pci__specific_io_in(kvm, vpci, port, data, size, 
offset);
+               break;
+       };
+
+       return ret;
+}
+
+static bool virtio_pci__specific_io_out(struct kvm *kvm, struct virtio_pci 
*vpci, u16 port,
+                                       void *data, int size, int offset)
+{
+       u32 config_offset, gsi, vec;
+       int type = virtio__get_dev_specific_field(offset - 20, 
vpci->msix_enabled,
+                                                       0, &config_offset);
+       if (type == VIRTIO_PCI_O_MSIX) {
+               switch (offset) {
+               case VIRTIO_MSI_CONFIG_VECTOR:
+                       vec = vpci->config_vector = ioport__read16(data);
+
+                       gsi = irq__add_msix_route(kvm,
+                                                 
vpci->pci_hdr.msix.table[vec].low,
+                                                 
vpci->pci_hdr.msix.table[vec].high,
+                                                 
vpci->pci_hdr.msix.table[vec].data);
+
+                       vpci->config_gsi = gsi;
+                       break;
+               case VIRTIO_MSI_QUEUE_VECTOR: {
+                       vec = vpci->vq_vector[vpci->queue_selector] = 
ioport__read16(data);
+
+                       gsi = irq__add_msix_route(kvm,
+                                                 
vpci->pci_hdr.msix.table[vec].low,
+                                                 
vpci->pci_hdr.msix.table[vec].high,
+                                                 
vpci->pci_hdr.msix.table[vec].data);
+                       vpci->gsis[vpci->queue_selector] = gsi;
+                       break;
+               }
+               };
+
+               return true;
+       } else if (type == VIRTIO_PCI_O_CONFIG) {
+               vpci->ops.set_config(kvm, vpci->dev, *(u8 *)data, 
config_offset);
+
+               return true;
+       }
+
+       return false;
+}
+
+static bool virtio_pci__io_out(struct ioport *ioport, struct kvm *kvm, u16 
port, void *data, int size)
+{
+       unsigned long offset;
+       bool ret = true;
+       struct virtio_pci *vpci;
+       u32 val;
+
+       vpci = ioport->priv;
+       offset = port - vpci->base_addr;
+
+       switch (offset) {
+       case VIRTIO_PCI_GUEST_FEATURES:
+               val = ioport__read32(data);
+               vpci->ops.set_guest_features(kvm, vpci, val);
+               break;
+       case VIRTIO_PCI_QUEUE_PFN:
+               val = ioport__read32(data);
+               vpci->ops.init_vq(kvm, vpci->dev, vpci->queue_selector, val);
+               break;
+       case VIRTIO_PCI_QUEUE_SEL:
+               vpci->queue_selector    = ioport__read16(data);
+               break;
+       case VIRTIO_PCI_QUEUE_NOTIFY:
+               val                     = ioport__read16(data);
+               vpci->ops.notify_vq(kvm, vpci->dev, val);
+               break;
+       case VIRTIO_PCI_STATUS:
+               vpci->status            = ioport__read8(data);
+               break;
+       default:
+               ret = virtio_pci__specific_io_out(kvm, vpci, port, data, size, 
offset);
+               break;
+       };
+
+       return ret;
+}
+
+static struct ioport_operations virtio_pci__io_ops = {
+       .io_in  = virtio_pci__io_in,
+       .io_out = virtio_pci__io_out,
+};
+
+static void callback_mmio(u64 addr, u8 *data, u32 len, u8 is_write, void *ptr)
+{
+       struct virtio_pci *vpci = ptr;
+       void *table = &vpci->pci_hdr.msix.table;
+
+       vpci->msix_enabled = 1;
+       if (is_write)
+               memcpy(table + addr - vpci->msix_io_block, data, len);
+       else
+               memcpy(data, table + addr - vpci->msix_io_block, len);
+}
+
+int virtio_pci__signal_vq(struct kvm *kvm, struct virtio_pci *vpci, u32 vq)
+{
+       kvm__irq_line(kvm, vpci->gsis[vq], VIRTIO_IRQ_HIGH);
+
+       return 0;
+}
+
+int virtio_pci__signal_config(struct kvm *kvm, struct virtio_pci *vpci)
+{
+       kvm__irq_line(kvm, vpci->config_gsi, VIRTIO_IRQ_HIGH);
+
+       return 0;
+}
+
+int virtio_pci__init(struct kvm *kvm, struct virtio_pci *vpci, void *dev,
+                       int device_id, int subsys_id)
+{
+       u8 pin, line, ndev;
+
+       vpci->dev = dev;
+       vpci->msix_io_block = pci_get_io_space_block();
+
+       vpci->base_addr = ioport__register(IOPORT_EMPTY, &virtio_pci__io_ops, 
IOPORT_SIZE, vpci);
+       kvm__register_mmio(kvm, vpci->msix_io_block, 0x100, callback_mmio, 
vpci);
+
+       vpci->pci_hdr = (struct pci_device_header) {
+               .vendor_id              = PCI_VENDOR_ID_REDHAT_QUMRANET,
+               .device_id              = device_id,
+               .header_type            = PCI_HEADER_TYPE_NORMAL,
+               .revision_id            = 0,
+               .class                  = 0x010000,
+               .subsys_vendor_id       = 
PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
+               .subsys_id              = subsys_id,
+               .bar[0]                 = vpci->base_addr | 
PCI_BASE_ADDRESS_SPACE_IO,
+               .bar[1]                 = vpci->msix_io_block |
+                                       PCI_BASE_ADDRESS_SPACE_MEMORY |
+                                       PCI_BASE_ADDRESS_MEM_TYPE_64,
+               /* bar[2] is the continuation of bar[1] for 64bit addressing */
+               .bar[2]                 = 0,
+               .status                 = PCI_STATUS_CAP_LIST,
+               .capabilities           = (void *)&vpci->pci_hdr.msix - (void 
*)&vpci->pci_hdr,
+       };
+
+       vpci->pci_hdr.msix.cap = PCI_CAP_ID_MSIX;
+       vpci->pci_hdr.msix.next = 0;
+       vpci->pci_hdr.msix.table_size = (VIRTIO_PCI_MAX_VQ + 1) | 
PCI_MSIX_FLAGS_ENABLE;
+       vpci->pci_hdr.msix.table_offset = 1; /* Use BAR 1 */
+       vpci->config_vector = 0;
+
+       if (irq__register_device(VIRTIO_ID_RNG, &ndev, &pin, &line) < 0)
+               return -1;
+
+       vpci->pci_hdr.irq_pin   = pin;
+       vpci->pci_hdr.irq_line  = line;
+       pci__register(&vpci->pci_hdr, ndev);
+
+       return 0;
+}
-- 
1.7.6

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to