When the virtio device is behind a virtual IOMMU, the doorbell address
written into the MSI-X table by the guest is an IOVA, not a physical one.
When injecting an MSI, KVM needs a physical address to recognize the
doorbell and the associated IRQ chip. Translate the address given by the
guest into a physical one, and store it in a secondary table for easy
access.

Signed-off-by: Jean-Philippe Brucker <jean-philippe.bruc...@arm.com>
---
 include/kvm/iommu.h      |  4 ++++
 include/kvm/virtio-pci.h |  1 +
 iommu.c                  | 23 +++++++++++++++++++++++
 virtio/pci.c             | 33 ++++++++++++++++++++++++---------
 4 files changed, 52 insertions(+), 9 deletions(-)

diff --git a/include/kvm/iommu.h b/include/kvm/iommu.h
index 4164ba20..8f87ce5a 100644
--- a/include/kvm/iommu.h
+++ b/include/kvm/iommu.h
@@ -70,4 +70,8 @@ int iommu_unmap(void *address_space, u64 virt_addr, u64 size, 
int flags);
 u64 iommu_access(void *address_space, u64 addr, size_t size, size_t *out_size,
                 int prot);
 
+struct msi_msg;
+
+int iommu_translate_msi(void *address_space, struct msi_msg *msi);
+
 #endif /* KVM_IOMMU_H */
diff --git a/include/kvm/virtio-pci.h b/include/kvm/virtio-pci.h
index 26772f74..cb5225d6 100644
--- a/include/kvm/virtio-pci.h
+++ b/include/kvm/virtio-pci.h
@@ -47,6 +47,7 @@ struct virtio_pci {
        u32                     msix_io_block;
        u64                     msix_pba;
        struct msix_table       msix_table[VIRTIO_PCI_MAX_VQ + 
VIRTIO_PCI_MAX_CONFIG];
+       struct msi_msg          msix_msgs[VIRTIO_PCI_MAX_VQ + 
VIRTIO_PCI_MAX_CONFIG];
 
        /* virtio queue */
        u16                     queue_selector;
diff --git a/iommu.c b/iommu.c
index 0a662404..c10a3f0b 100644
--- a/iommu.c
+++ b/iommu.c
@@ -5,6 +5,7 @@
 
 #include "kvm/iommu.h"
 #include "kvm/kvm.h"
+#include "kvm/msi.h"
 #include "kvm/mutex.h"
 #include "kvm/rbtree-interval.h"
 
@@ -160,3 +161,25 @@ out_unlock:
 
        return out_addr;
 }
+
+int iommu_translate_msi(void *address_space, struct msi_msg *msg)
+{
+       size_t size = 4, out_size;
+       u64 addr = ((u64)msg->address_hi << 32) | msg->address_lo;
+
+       if (!address_space)
+               return 0;
+
+       addr = iommu_access(address_space, addr, size, &out_size,
+                           IOMMU_PROT_WRITE);
+
+       if (!addr || out_size != size) {
+               pr_err("could not translate MSI doorbell");
+               return -EFAULT;
+       }
+
+       msg->address_lo = addr & 0xffffffff;
+       msg->address_hi = addr >> 32;
+
+       return 0;
+}
diff --git a/virtio/pci.c b/virtio/pci.c
index 674d5143..88b1a129 100644
--- a/virtio/pci.c
+++ b/virtio/pci.c
@@ -156,6 +156,7 @@ static void update_msix_map(struct virtio_pci *vpci,
                            struct msix_table *msix_entry, u32 vecnum)
 {
        u32 gsi, i;
+       struct msi_msg *msg;
 
        /* Find the GSI number used for that vector */
        if (vecnum == vpci->config_vector) {
@@ -172,14 +173,20 @@ static void update_msix_map(struct virtio_pci *vpci,
        if (gsi == 0)
                return;
 
-       msix_entry = &msix_entry[vecnum];
-       irq__update_msix_route(vpci->kvm, gsi, &msix_entry->msg);
+       msg = &vpci->msix_msgs[vecnum];
+       *msg = msix_entry[vecnum].msg;
+
+       if (iommu_translate_msi(vpci->vdev->iotlb, msg))
+               return;
+
+       irq__update_msix_route(vpci->kvm, gsi, msg);
 }
 
 static bool virtio_pci__specific_io_out(struct kvm *kvm, struct virtio_device 
*vdev, u16 port,
                                        void *data, int size, int offset)
 {
        struct virtio_pci *vpci = vdev->virtio;
+       struct msi_msg *msg;
        u32 config_offset, vec;
        int gsi;
        int type = virtio__get_dev_specific_field(offset - 20, 
virtio_pci__msix_enabled(vpci),
@@ -191,8 +198,12 @@ static bool virtio_pci__specific_io_out(struct kvm *kvm, 
struct virtio_device *v
                        if (vec == VIRTIO_MSI_NO_VECTOR)
                                break;
 
-                       gsi = irq__add_msix_route(kvm,
-                                                 &vpci->msix_table[vec].msg,
+                       msg = &vpci->msix_msgs[vec];
+                       *msg = vpci->msix_table[vec].msg;
+                       if (iommu_translate_msi(vdev->iotlb, msg))
+                               break;
+
+                       gsi = irq__add_msix_route(kvm, msg,
                                                  vpci->dev_hdr.dev_num << 3);
                        if (gsi >= 0) {
                                vpci->config_gsi = gsi;
@@ -210,8 +221,12 @@ static bool virtio_pci__specific_io_out(struct kvm *kvm, 
struct virtio_device *v
                        if (vec == VIRTIO_MSI_NO_VECTOR)
                                break;
 
-                       gsi = irq__add_msix_route(kvm,
-                                                 &vpci->msix_table[vec].msg,
+                       msg = &vpci->msix_msgs[vec];
+                       *msg = vpci->msix_table[vec].msg;
+                       if (iommu_translate_msi(vdev->iotlb, msg))
+                               break;
+
+                       gsi = irq__add_msix_route(kvm, msg,
                                                  vpci->dev_hdr.dev_num << 3);
                        if (gsi < 0) {
                                if (gsi == -ENXIO &&
@@ -328,9 +343,9 @@ static void virtio_pci__signal_msi(struct kvm *kvm, struct 
virtio_pci *vpci,
 {
        static int needs_devid = 0;
        struct kvm_msi msi = {
-               .address_lo = vpci->msix_table[vec].msg.address_lo,
-               .address_hi = vpci->msix_table[vec].msg.address_hi,
-               .data = vpci->msix_table[vec].msg.data,
+               .address_lo = vpci->msix_msgs[vec].address_lo,
+               .address_hi = vpci->msix_msgs[vec].address_hi,
+               .data = vpci->msix_msgs[vec].data,
        };
 
        if (needs_devid == 0) {
-- 
2.12.1

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

Reply via email to