@@ -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 */
@@ -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;
@@ -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;
+}
@@ -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) {
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@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(-)