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.brucker@xxxxxxx> --- 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@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linuxfoundation.org/mailman/listinfo/virtualization