Signed-off-by: Sasha Levin <levinsasha...@gmail.com>
---
 tools/kvm/include/kvm/pci.h        |   13 ++-
 tools/kvm/include/kvm/virtio-pci.h |    4 +
 tools/kvm/virtio/pci.c             |  248 ++++++++++++++++++++++++++++++++++--
 3 files changed, 254 insertions(+), 11 deletions(-)

diff --git a/tools/kvm/include/kvm/pci.h b/tools/kvm/include/kvm/pci.h
index f71af0b..4f5a09f 100644
--- a/tools/kvm/include/kvm/pci.h
+++ b/tools/kvm/include/kvm/pci.h
@@ -39,6 +39,16 @@ struct msix_cap {
        u32 pba_offset;
 };
 
+struct virtio_cap {
+       u8 cap;
+       u8 next;
+       u8 cap_len;
+       u8 structure_id;
+       u8 bir;
+       u32 size:24;
+       u32 offset;
+};
+
 struct pci_device_header {
        u16             vendor_id;
        u16             device_id;
@@ -63,7 +73,8 @@ struct pci_device_header {
        u8              min_gnt;
        u8              max_lat;
        struct msix_cap msix;
-       u8              empty[136]; /* Rest of PCI config space */
+       struct virtio_cap virtio[4];
+       u8              empty[90]; /* Rest of PCI config space */
        u32             bar_size[6];
 };
 
diff --git a/tools/kvm/include/kvm/virtio-pci.h 
b/tools/kvm/include/kvm/virtio-pci.h
index 73f7486..fc3c03f 100644
--- a/tools/kvm/include/kvm/virtio-pci.h
+++ b/tools/kvm/include/kvm/virtio-pci.h
@@ -19,6 +19,7 @@ struct virtio_pci_ioevent_param {
 struct virtio_pci {
        struct pci_device_header pci_hdr;
        void                    *dev;
+       struct kvm              *kvm;
 
        u16                     base_addr;
        u8                      status;
@@ -36,6 +37,9 @@ struct virtio_pci {
        /* virtio queue */
        u16                     queue_selector;
        struct virtio_pci_ioevent_param ioeventfds[VIRTIO_PCI_MAX_VQ];
+
+       u32                     virtio_mmio;
+       u16                     virtio_pio;
 };
 
 int virtio_pci__init(struct kvm *kvm, struct virtio_trans *vtrans, void *dev,
diff --git a/tools/kvm/virtio/pci.c b/tools/kvm/virtio/pci.c
index da38ba5..8606d87 100644
--- a/tools/kvm/virtio/pci.c
+++ b/tools/kvm/virtio/pci.c
@@ -40,7 +40,7 @@ static int virtio_pci__init_ioeventfd(struct kvm *kvm, struct 
virtio_trans *vtra
        };
 
        ioevent = (struct ioevent) {
-               .io_addr        = vpci->base_addr + VIRTIO_PCI_QUEUE_NOTIFY,
+               .io_addr        = vpci->virtio_pio,
                .io_len         = sizeof(u16),
                .fn             = virtio_pci__ioevent_callback,
                .fn_ptr         = &vpci->ioeventfds[vq],
@@ -59,7 +59,7 @@ static inline bool virtio_pci__msix_enabled(struct virtio_pci 
*vpci)
        return vpci->pci_hdr.msix.ctrl & PCI_MSIX_FLAGS_ENABLE;
 }
 
-static bool virtio_pci__specific_io_in(struct kvm *kvm, struct virtio_trans 
*vtrans, u16 port,
+static bool virtio_pci_legacy__specific_io_in(struct kvm *kvm, struct 
virtio_trans *vtrans, u16 port,
                                        void *data, int size, int offset)
 {
        u32 config_offset;
@@ -89,7 +89,8 @@ static bool virtio_pci__specific_io_in(struct kvm *kvm, 
struct virtio_trans *vtr
        return false;
 }
 
-static bool virtio_pci__io_in(struct ioport *ioport, struct kvm *kvm, u16 
port, void *data, int size)
+static bool virtio_pci_legacy__io_in(struct ioport *ioport, struct kvm *kvm,
+                                       u16 port, void *data, int size)
 {
        unsigned long offset;
        bool ret = true;
@@ -124,15 +125,15 @@ static bool virtio_pci__io_in(struct ioport *ioport, 
struct kvm *kvm, u16 port,
                vpci->isr = VIRTIO_IRQ_LOW;
                break;
        default:
-               ret = virtio_pci__specific_io_in(kvm, vtrans, port, data, size, 
offset);
+               ret = virtio_pci_legacy__specific_io_in(kvm, vtrans, port, 
data, size, offset);
                break;
        };
 
        return ret;
 }
 
-static bool virtio_pci__specific_io_out(struct kvm *kvm, struct virtio_trans 
*vtrans, u16 port,
-                                       void *data, int size, int offset)
+static bool virtio_pci_legacy__specific_io_out(struct kvm *kvm, struct 
virtio_trans *vtrans,
+                                       u16 port, void *data, int size, int 
offset)
 {
        struct virtio_pci *vpci = vtrans->virtio;
        u32 config_offset, gsi, vec;
@@ -166,7 +167,8 @@ static bool virtio_pci__specific_io_out(struct kvm *kvm, 
struct virtio_trans *vt
        return false;
 }
 
-static bool virtio_pci__io_out(struct ioport *ioport, struct kvm *kvm, u16 
port, void *data, int size)
+static bool virtio_pci_legacy__io_out(struct ioport *ioport,
+                               struct kvm *kvm, u16 port, void *data, int size)
 {
        unsigned long offset;
        bool ret = true;
@@ -199,7 +201,76 @@ static bool virtio_pci__io_out(struct ioport *ioport, 
struct kvm *kvm, u16 port,
                vpci->status            = ioport__read8(data);
                break;
        default:
-               ret = virtio_pci__specific_io_out(kvm, vtrans, port, data, 
size, offset);
+               ret = virtio_pci_legacy__specific_io_out(kvm, vtrans, port,
+                                                       data, size, offset);
+               break;
+       };
+
+       return ret;
+}
+
+static struct ioport_operations virtio_pci_legacy__io_ops = {
+       .io_in  = virtio_pci_legacy__io_in,
+       .io_out = virtio_pci_legacy__io_out,
+};
+
+static bool virtio_pci__io_out(struct ioport *ioport,
+                               struct kvm *kvm, u16 port, void *data, int size)
+{
+       unsigned long offset;
+       bool ret = true;
+       u16 val;
+       struct virtio_pci *vpci;
+       struct virtio_trans *vtrans;
+
+       vtrans = ioport->priv;
+       vpci = vtrans->virtio;
+       offset = port - vpci->virtio_pio;
+
+       /*
+        * 0-1: Notifications
+        * 2: ISR
+        */
+       switch (offset) {
+       case 0:
+               val             = ioport__read16(data);
+               vtrans->virtio_ops->notify_vq(kvm, vpci->dev, val);
+               break;
+       case 2:
+               vpci->isr       = ioport__read8(data);
+               break;
+       default:
+               ret = false;
+               break;
+       }
+
+       return ret;
+}
+
+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;
+
+       vpci = ioport->priv;
+       offset = port - vpci->virtio_pio;
+
+       /*
+        * 0-1: Notifications
+        * 2: ISR
+        */
+       switch (offset) {
+       case 0:
+               /* Shouldn't happen */
+       case 2:
+               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 = false;
                break;
        };
 
@@ -231,6 +302,113 @@ static void callback_mmio_table(u64 addr, u8 *data, u32 
len, u8 is_write, void *
                memcpy(data, table + addr - offset, len);
 }
 
+static void virtio_mmio_dev_specific(u64 addr, u8 *data, u32 len, u8 is_write,
+                                       struct virtio_trans *vtrans)
+{
+       struct virtio_pci *vpci = vtrans->virtio;
+       u32 i;
+
+       for (i = 0; i < len; i++) {
+               if (is_write)
+                       vtrans->virtio_ops->set_config(vpci->kvm, vpci->dev,
+                                                       *(u8 *)data + i, addr + 
i);
+               else
+                       data[i] =
+                               vtrans->virtio_ops->get_config(vpci->kvm, 
vpci->dev, addr + i);
+       }
+}
+
+static void virtio_mmio_in(u64 addr, u8 *data, u32 len, u8 is_write,
+                                       struct virtio_trans *vtrans)
+{
+       struct virtio_pci *vpci = vtrans->virtio;
+
+       switch (addr) {
+       case VIRTIO_PCI_HOST_FEATURES:
+               *(u32 *)data  = 
vtrans->virtio_ops->get_host_features(vpci->kvm, vpci->dev);
+               break;
+       case VIRTIO_PCI_QUEUE_PFN:
+               *(u32 *)data  = vtrans->virtio_ops->get_pfn_vq(vpci->kvm,
+                                                       vpci->dev, 
vpci->queue_selector);
+               break;
+       case VIRTIO_PCI_QUEUE_NUM:
+               *(u32 *)data  = vtrans->virtio_ops->get_size_vq(vpci->kvm,
+                                                       vpci->dev, 
vpci->queue_selector);
+               break;
+       case VIRTIO_PCI_STATUS:
+               *(u8 *)data  = vpci->status;
+               break;
+       case VIRTIO_MSI_CONFIG_VECTOR:
+               *(u8 *)data  = vpci->config_vector;
+               break;
+       case VIRTIO_MSI_QUEUE_VECTOR:
+               *(u8 *)data = vpci->vq_vector[vpci->queue_selector];
+               break;
+       };
+}
+
+static void virtio_mmio_out(u64 addr, u8 *data, u32 len, u8 is_write,
+                                       struct virtio_trans *vtrans)
+{
+       struct virtio_pci *vpci = vtrans->virtio;
+       u32 val;
+
+       switch (addr) {
+       case VIRTIO_PCI_GUEST_FEATURES:
+               val = *(u32 *)data;
+               vtrans->virtio_ops->set_guest_features(vpci->kvm, vpci, val);
+               break;
+       case VIRTIO_PCI_QUEUE_PFN:
+               val = *(u32 *)data;
+               virtio_pci__init_ioeventfd(vpci->kvm, vtrans, 
vpci->queue_selector);
+               vtrans->virtio_ops->init_vq(vpci->kvm, vpci->dev, 
vpci->queue_selector, val);
+               break;
+       case VIRTIO_PCI_QUEUE_SEL:
+               vpci->queue_selector    = *(u16 *)data;
+               break;
+       case VIRTIO_PCI_QUEUE_NOTIFY:
+               val                     = *(u16 *)data;
+               vtrans->virtio_ops->notify_vq(vpci->kvm, vpci->dev, val);
+               break;
+       case VIRTIO_PCI_STATUS:
+               vpci->status            = *(u8 *)data;
+               break;
+       case VIRTIO_MSI_CONFIG_VECTOR: {
+               u16 vec, gsi;
+
+               vec = *(u16 *)data;
+               gsi = irq__add_msix_route(vpci->kvm, 
&vpci->msix_table[vec].msg);
+               vpci->config_gsi = gsi;
+               break;
+       }
+       case VIRTIO_MSI_QUEUE_VECTOR: {
+               u16 vec, gsi;
+
+               vec = vpci->vq_vector[vpci->queue_selector] = *(u16 *)data;
+               gsi = irq__add_msix_route(vpci->kvm, 
&vpci->msix_table[vec].msg);
+               vpci->gsis[vpci->queue_selector] = gsi;
+               break;
+       }
+       };
+}
+
+static void callback_virtio_mmio(u64 addr, u8 *data, u32 len, u8 is_write, 
void *ptr)
+{
+       struct virtio_trans *vtrans = ptr;
+       struct virtio_pci *vpci = vtrans->virtio;
+       u32 offset = addr - vpci->virtio_mmio;
+
+       if (offset >= 0x100) {
+               virtio_mmio_dev_specific(offset - 0x100, data, len, is_write, 
vtrans);
+               return;
+       }
+
+       if (is_write == 0)
+               virtio_mmio_in(offset, data, len, is_write, vtrans);
+       else
+               virtio_mmio_out(offset, data, len, is_write, vtrans);
+}
+
 int virtio_pci__signal_vq(struct kvm *kvm, struct virtio_trans *vtrans, u32 vq)
 {
        struct virtio_pci *vpci = vtrans->virtio;
@@ -282,10 +460,16 @@ int virtio_pci__init(struct kvm *kvm, struct virtio_trans 
*vtrans, void *dev,
 
        vpci->dev = dev;
        vpci->msix_io_block = pci_get_io_space_block(PCI_IO_SIZE * 2);
+       vpci->virtio_mmio = pci_get_io_space_block(PCI_IO_SIZE * 2);
 
-       vpci->base_addr = ioport__register(IOPORT_EMPTY, &virtio_pci__io_ops, 
IOPORT_SIZE, vtrans);
+       vpci->base_addr = ioport__register(IOPORT_EMPTY,
+                               &virtio_pci_legacy__io_ops, IOPORT_SIZE, 
vtrans);
+       vpci->virtio_pio = ioport__register(IOPORT_EMPTY,
+                               &virtio_pci__io_ops, IOPORT_SIZE, vtrans);
        kvm__register_mmio(kvm, vpci->msix_io_block, 0x100, 
callback_mmio_table, vpci);
+       kvm__register_mmio(kvm, vpci->virtio_mmio, 0x200, callback_virtio_mmio, 
vtrans);
 
+       vpci->kvm = kvm;
        vpci->pci_hdr = (struct pci_device_header) {
                .vendor_id              = PCI_VENDOR_ID_REDHAT_QUMRANET,
                .device_id              = device_id,
@@ -296,12 +480,16 @@ int virtio_pci__init(struct kvm *kvm, struct virtio_trans 
*vtrans, void *dev,
                .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,
+               .bar[2]                 = vpci->virtio_mmio | 
PCI_BASE_ADDRESS_SPACE_MEMORY,
+               .bar_size[2]            = PCI_IO_SIZE * 2,
+               .bar[3]                 = vpci->virtio_pio | 
PCI_BASE_ADDRESS_SPACE_IO,
+               .bar_size[3]            = PCI_IO_SIZE * 2,
                .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.next = (void *)&vpci->pci_hdr.virtio[0] - (void 
*)&vpci->pci_hdr;
        /*
         * We at most have VIRTIO_PCI_MAX_VQ entries for virt queue,
         * VIRTIO_PCI_MAX_CONFIG entries for config.
@@ -321,6 +509,46 @@ int virtio_pci__init(struct kvm *kvm, struct virtio_trans 
*vtrans, void *dev,
         */
        vpci->pci_hdr.msix.table_offset = 1; /* Use BAR 1 */
        vpci->pci_hdr.msix.pba_offset = 1 | PCI_IO_SIZE; /* Use BAR 1 with 
offset */
+
+       vpci->pci_hdr.virtio[0] = (struct virtio_cap) {
+               .cap = PCI_CAP_ID_VNDR,
+               .next = (void *)&vpci->pci_hdr.virtio[1] - (void 
*)&vpci->pci_hdr,
+               .cap_len = sizeof(struct virtio_cap),
+               .structure_id = VIRTIO_PCI_CAP_COMMON_CFG,
+               .bir = 2,
+               .size = PCI_IO_SIZE,
+               .offset = 0,
+       };
+
+       vpci->pci_hdr.virtio[1] = (struct virtio_cap) {
+               .cap = PCI_CAP_ID_VNDR,
+               .next = (void *)&vpci->pci_hdr.virtio[2] - (void 
*)&vpci->pci_hdr,
+               .cap_len = sizeof(struct virtio_cap),
+               .structure_id = VIRTIO_PCI_CAP_ISR_CFG,
+               .bir = 3,
+               .size = 1,
+               .offset = 2,
+       };
+
+       vpci->pci_hdr.virtio[2] = (struct virtio_cap) {
+               .cap = PCI_CAP_ID_VNDR,
+               .next = (void *)&vpci->pci_hdr.virtio[3] - (void 
*)&vpci->pci_hdr,
+               .cap_len = sizeof(struct virtio_cap),
+               .structure_id = VIRTIO_PCI_CAP_NOTIFY_CFG,
+               .bir = 3,
+               .size = 2,
+               .offset = 0,
+       };
+
+       vpci->pci_hdr.virtio[3] = (struct virtio_cap) {
+               .cap = PCI_CAP_ID_VNDR,
+               .next = 0,
+               .cap_len = sizeof(struct virtio_cap),
+               .structure_id = VIRTIO_PCI_CAP_DEVICE_CFG,
+               .bir = 2,
+               .size = PCI_IO_SIZE,
+               .offset = PCI_IO_SIZE,
+       };
        vpci->config_vector = 0;
 
        if (irq__register_device(subsys_id, &ndev, &pin, &line) < 0)
-- 
1.7.8.rc1

_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization

Reply via email to