This patch adds an MMIO interface for each virtio-pci device, so that
they can be accessed without having to use an ioport. For each device, a
new memory BAR is added which corresponds to an area of MMIO space with
a shim trap handler. This handler simply translates the access into an
ioport access via kvm__emulate_io. Since guests can generate accesses
via either the ioport or MMIO regions, an ioeventfd is registered for
both.

Signed-off-by: Will Deacon <will.dea...@arm.com>
---
 tools/kvm/include/kvm/virtio-pci.h |   4 +-
 tools/kvm/virtio/pci.c             | 111 +++++++++++++++++++++++++------------
 2 files changed, 78 insertions(+), 37 deletions(-)

diff --git a/tools/kvm/include/kvm/virtio-pci.h 
b/tools/kvm/include/kvm/virtio-pci.h
index 9b063924e5da..c795ce71fa88 100644
--- a/tools/kvm/include/kvm/virtio-pci.h
+++ b/tools/kvm/include/kvm/virtio-pci.h
@@ -22,8 +22,10 @@ struct virtio_pci {
        struct pci_device_header pci_hdr;
        struct device_header    dev_hdr;
        void                    *dev;
+       struct kvm              *kvm;
 
-       u16                     base_addr;
+       u16                     port_addr;
+       u32                     mmio_addr;
        u8                      status;
        u8                      isr;
        u32                     features;
diff --git a/tools/kvm/virtio/pci.c b/tools/kvm/virtio/pci.c
index 77c933fd4ab2..e1b5be6b036e 100644
--- a/tools/kvm/virtio/pci.c
+++ b/tools/kvm/virtio/pci.c
@@ -24,7 +24,8 @@ static int virtio_pci__init_ioeventfd(struct kvm *kvm, struct 
virtio_device *vde
 {
        struct ioevent ioevent;
        struct virtio_pci *vpci = vdev->virtio;
-       int r;
+       int i, r, flags = IOEVENTFD_FLAG_PIO;
+       int fds[2];
 
        vpci->ioeventfds[vq] = (struct virtio_pci_ioevent_param) {
                .vdev           = vdev,
@@ -32,32 +33,44 @@ static int virtio_pci__init_ioeventfd(struct kvm *kvm, 
struct virtio_device *vde
        };
 
        ioevent = (struct ioevent) {
-               .io_addr        = vpci->base_addr + VIRTIO_PCI_QUEUE_NOTIFY,
-               .io_len         = sizeof(u16),
                .fn             = virtio_pci__ioevent_callback,
                .fn_ptr         = &vpci->ioeventfds[vq],
                .datamatch      = vq,
                .fn_kvm         = kvm,
-               .fd             = eventfd(0, 0),
        };
 
-       if (vdev->use_vhost)
-               /*
-                * Vhost will poll the eventfd in host kernel side,
-                * no need to poll in userspace.
-                */
-               r = ioeventfd__add_event(&ioevent, IOEVENTFD_FLAG_PIO);
-       else
-               /* Need to poll in userspace. */
-               r = ioeventfd__add_event(&ioevent, IOEVENTFD_FLAG_PIO |
-                                                  IOEVENTFD_FLAG_USER_POLL);
+       /*
+        * Vhost will poll the eventfd in host kernel side, otherwise we
+        * need to poll in userspace.
+        */
+       if (!vdev->use_vhost)
+               flags |= IOEVENTFD_FLAG_USER_POLL;
+
+       /* ioport */
+       ioevent.io_addr = vpci->port_addr + VIRTIO_PCI_QUEUE_NOTIFY;
+       ioevent.io_len  = sizeof(u16);
+       ioevent.fd      = fds[0] = eventfd(0, 0);
+       r = ioeventfd__add_event(&ioevent, flags);
        if (r)
                return r;
 
-       if (vdev->ops->notify_vq_eventfd)
-               vdev->ops->notify_vq_eventfd(kvm, vpci->dev, vq, ioevent.fd);
+       /* mmio */
+       ioevent.io_addr = vpci->mmio_addr + VIRTIO_PCI_QUEUE_NOTIFY;
+       ioevent.io_len  = sizeof(u32);
+       ioevent.fd      = fds[1] = eventfd(0, 0);
+       r = ioeventfd__add_event(&ioevent, flags);
+       if (r)
+               goto free_ioport_evt;
 
+       if (vdev->ops->notify_vq_eventfd)
+               for (i = 0; i < 2; ++i)
+                       vdev->ops->notify_vq_eventfd(kvm, vpci->dev, vq,
+                                                    fds[i]);
        return 0;
+
+free_ioport_evt:
+       ioeventfd__del_event(vpci->port_addr + VIRTIO_PCI_QUEUE_NOTIFY, vq);
+       return r;
 }
 
 static inline bool virtio_pci__msix_enabled(struct virtio_pci *vpci)
@@ -105,7 +118,7 @@ static bool virtio_pci__io_in(struct ioport *ioport, struct 
kvm *kvm, u16 port,
 
        vdev = ioport->priv;
        vpci = vdev->virtio;
-       offset = port - vpci->base_addr;
+       offset = port - vpci->port_addr;
 
        switch (offset) {
        case VIRTIO_PCI_HOST_FEATURES:
@@ -188,7 +201,7 @@ static bool virtio_pci__io_out(struct ioport *ioport, 
struct kvm *kvm, u16 port,
 
        vdev = ioport->priv;
        vpci = vdev->virtio;
-       offset = port - vpci->base_addr;
+       offset = port - vpci->port_addr;
 
        switch (offset) {
        case VIRTIO_PCI_GUEST_FEATURES:
@@ -227,7 +240,8 @@ static struct ioport_operations virtio_pci__io_ops = {
        .io_out = virtio_pci__io_out,
 };
 
-static void virtio_pci__mmio_callback(u64 addr, u8 *data, u32 len, u8 
is_write, void *ptr)
+static void virtio_pci__msix_mmio_callback(u64 addr, u8 *data, u32 len,
+                                          u8 is_write, void *ptr)
 {
        struct virtio_pci *vpci = ptr;
        void *table;
@@ -307,6 +321,16 @@ int virtio_pci__signal_config(struct kvm *kvm, struct 
virtio_device *vdev)
        return 0;
 }
 
+static void virtio_pci__io_mmio_callback(u64 addr, u8 *data, u32 len,
+                                        u8 is_write, void *ptr)
+{
+       struct virtio_pci *vpci = ptr;
+       int direction = is_write ? KVM_EXIT_IO_OUT : KVM_EXIT_IO_IN;
+       u16 port = vpci->port_addr + (addr & (IOPORT_SIZE - 1));
+
+       kvm__emulate_io(vpci->kvm, port, data, direction, len, 1);
+}
+
 int virtio_pci__init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
                     int device_id, int subsys_id, int class)
 {
@@ -314,19 +338,26 @@ int virtio_pci__init(struct kvm *kvm, void *dev, struct 
virtio_device *vdev,
        u8 pin, line;
        int r;
 
+       vpci->kvm = kvm;
        vpci->dev = dev;
-       vpci->msix_io_block = pci_get_io_space_block(PCI_IO_SIZE * 2);
 
        r = ioport__register(kvm, IOPORT_EMPTY, &virtio_pci__io_ops, 
IOPORT_SIZE, vdev);
        if (r < 0)
                return r;
+       vpci->port_addr = (u16)r;
 
-       vpci->base_addr = (u16)r;
-       r = kvm__register_mmio(kvm, vpci->msix_io_block, PCI_IO_SIZE * 2, false,
-                              virtio_pci__mmio_callback, vpci);
+       vpci->mmio_addr = pci_get_io_space_block(IOPORT_SIZE);
+       r = kvm__register_mmio(kvm, vpci->mmio_addr, IOPORT_SIZE, false,
+                              virtio_pci__io_mmio_callback, vpci);
        if (r < 0)
                goto free_ioport;
 
+       vpci->msix_io_block = pci_get_io_space_block(PCI_IO_SIZE * 2);
+       r = kvm__register_mmio(kvm, vpci->msix_io_block, PCI_IO_SIZE * 2, false,
+                              virtio_pci__msix_mmio_callback, vpci);
+       if (r < 0)
+               goto free_mmio;
+
        vpci->pci_hdr = (struct pci_device_header) {
                .vendor_id              = 
cpu_to_le16(PCI_VENDOR_ID_REDHAT_QUMRANET),
                .device_id              = cpu_to_le16(device_id),
@@ -337,14 +368,17 @@ int virtio_pci__init(struct kvm *kvm, void *dev, struct 
virtio_device *vdev,
                .class[2]               = (class >> 16) & 0xff,
                .subsys_vendor_id       = 
cpu_to_le16(PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET),
                .subsys_id              = cpu_to_le16(subsys_id),
-               .bar[0]                 = cpu_to_le32(vpci->base_addr
+               .bar[0]                 = cpu_to_le32(vpci->mmio_addr
+                                                       | 
PCI_BASE_ADDRESS_SPACE_MEMORY),
+               .bar[1]                 = cpu_to_le32(vpci->port_addr
                                                        | 
PCI_BASE_ADDRESS_SPACE_IO),
-               .bar[1]                 = cpu_to_le32(vpci->msix_io_block
+               .bar[2]                 = cpu_to_le32(vpci->msix_io_block
                                                        | 
PCI_BASE_ADDRESS_SPACE_MEMORY),
                .status                 = cpu_to_le16(PCI_STATUS_CAP_LIST),
                .capabilities           = (void *)&vpci->pci_hdr.msix - (void 
*)&vpci->pci_hdr,
                .bar_size[0]            = IOPORT_SIZE,
-               .bar_size[1]            = PCI_IO_SIZE * 2,
+               .bar_size[1]            = IOPORT_SIZE,
+               .bar_size[2]            = PCI_IO_SIZE * 2,
        };
 
        vpci->dev_hdr = (struct device_header) {
@@ -367,14 +401,14 @@ int virtio_pci__init(struct kvm *kvm, void *dev, struct 
virtio_device *vdev,
         */
        vpci->pci_hdr.msix.ctrl = cpu_to_le16(VIRTIO_PCI_MAX_VQ + 
VIRTIO_PCI_MAX_CONFIG - 1);
 
-       /* Both table and PBA are mapped to the same BAR (1) */
-       vpci->pci_hdr.msix.table_offset = cpu_to_le32(1);
-       vpci->pci_hdr.msix.pba_offset = cpu_to_le32(1 | PCI_IO_SIZE);
+       /* Both table and PBA are mapped to the same BAR (2) */
+       vpci->pci_hdr.msix.table_offset = cpu_to_le32(2);
+       vpci->pci_hdr.msix.pba_offset = cpu_to_le32(2 | PCI_IO_SIZE);
        vpci->config_vector = 0;
 
        r = irq__register_device(subsys_id, &pin, &line);
        if (r < 0)
-               goto free_mmio;
+               goto free_msix_mmio;
 
        if (kvm__supports_extension(kvm, KVM_CAP_SIGNAL_MSI))
                vpci->features |= VIRTIO_PCI_F_SIGNAL_MSI;
@@ -383,14 +417,16 @@ int virtio_pci__init(struct kvm *kvm, void *dev, struct 
virtio_device *vdev,
        vpci->pci_hdr.irq_line  = line;
        r = device__register(&vpci->dev_hdr);
        if (r < 0)
-               goto free_ioport;
+               goto free_msix_mmio;
 
        return 0;
 
-free_mmio:
+free_msix_mmio:
        kvm__deregister_mmio(kvm, vpci->msix_io_block);
+free_mmio:
+       kvm__deregister_mmio(kvm, vpci->mmio_addr);
 free_ioport:
-       ioport__unregister(kvm, vpci->base_addr);
+       ioport__unregister(kvm, vpci->port_addr);
        return r;
 }
 
@@ -399,11 +435,14 @@ int virtio_pci__exit(struct kvm *kvm, struct 
virtio_device *vdev)
        struct virtio_pci *vpci = vdev->virtio;
        int i;
 
+       kvm__deregister_mmio(kvm, vpci->mmio_addr);
        kvm__deregister_mmio(kvm, vpci->msix_io_block);
-       ioport__unregister(kvm, vpci->base_addr);
+       ioport__unregister(kvm, vpci->port_addr);
 
-       for (i = 0; i < VIRTIO_PCI_MAX_VQ; i++)
-               ioeventfd__del_event(vpci->base_addr + VIRTIO_PCI_QUEUE_NOTIFY, 
i);
+       for (i = 0; i < VIRTIO_PCI_MAX_VQ; i++) {
+               ioeventfd__del_event(vpci->port_addr + VIRTIO_PCI_QUEUE_NOTIFY, 
i);
+               ioeventfd__del_event(vpci->mmio_addr + VIRTIO_PCI_QUEUE_NOTIFY, 
i);
+       }
 
        return 0;
 }
-- 
1.8.2.2

--
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