KVM irqfd bypasses the PCI device's IOMMU AddressSpace, so MSI
addresses requiring translation may fail to reach the guest IMSIC.

Translate MSI addresses through the device's IOMMU AddressSpace when
setting up KVM routes. Only direct mappings to an IMSIC MemoryRegion
are installed in KVM routes.

Other IOMMU MSI remapping forms are not converted to KVM routes and
continue to use the existing userspace MSI delivery path.

Select PCI for RISCV64 KVM builds, as QEMU currently enables the
RISC-V KVM target only for riscv64-softmmu builds and the MSI route
fixup requires PCI support. Also trace translation failures to help
diagnose route setup failures.

Signed-off-by: Tan Chi <[email protected]>
Reviewed-by: Nutty Liu <[email protected]>
---

Changes in v3:
- Remove the unused length variable and pass NULL to
  address_space_translate(), following Nutty Liu's review.

Changes in v2 (based on Alistair Francis's review):
- Select PCI for RISCV64 KVM builds.
- Add trace events for MSI translation failures.

 target/riscv/Kconfig       |  2 ++
 target/riscv/kvm/kvm-cpu.c | 54 ++++++++++++++++++++++++++++++++++++++
 target/riscv/trace-events  |  4 +++
 3 files changed, 60 insertions(+)

diff --git a/target/riscv/Kconfig b/target/riscv/Kconfig
index 11bc09b414..0cf2c8e1c4 100644
--- a/target/riscv/Kconfig
+++ b/target/riscv/Kconfig
@@ -7,3 +7,5 @@ config RISCV64
     bool
     select ARM_COMPATIBLE_SEMIHOSTING if TCG
     select DEVICE_TREE # needed by boot.c
+    # kvm_arch_fixup_msi_route() needs to access PCIDevice
+    select PCI if KVM
diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c
index 45ad5e1525..a1eecd72b7 100644
--- a/target/riscv/kvm/kvm-cpu.c
+++ b/target/riscv/kvm/kvm-cpu.c
@@ -1507,6 +1507,60 @@ int kvm_arch_release_virq_post(int virq)
 int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
                              uint64_t address, uint32_t data, PCIDevice *dev)
 {
+    AddressSpace *as;
+    hwaddr xlat, translated;
+    MemoryRegionSection section;
+    MemoryRegion *mr;
+    Object *owner;
+
+    if (!dev) {
+        return 0;
+    }
+
+    as = pci_device_iommu_address_space(dev);
+    if (as == &address_space_memory) {
+        return 0;
+    }
+
+    RCU_READ_LOCK_GUARD();
+
+    mr = address_space_translate(as, address, &xlat, NULL, true,
+                                 MEMTXATTRS_UNSPECIFIED);
+
+    if (!mr) {
+        trace_kvm_riscv_fixup_msi_route_failed(address,
+                                               "address translation failed",
+                                               "none");
+        return 1;
+    }
+
+    /*
+     * Only direct IMSIC writes can be represented by a KVM MSI route.
+     * MSI page-table translations instead target the RISC-V IOMMU trap
+     * region, which requires further BASIC/MRIF processing.
+     */
+    owner = memory_region_owner(mr);
+    if (!owner || !object_dynamic_cast(owner, TYPE_RISCV_IMSIC)) {
+        trace_kvm_riscv_fixup_msi_route_failed(address,
+                                               "non-IMSIC target",
+                                               memory_region_name(mr));
+        return 1;
+    }
+
+    section = memory_region_find(mr, xlat, 1);
+    if (!section.mr) {
+        trace_kvm_riscv_fixup_msi_route_failed(address,
+                                               "memory region lookup failed",
+                                               memory_region_name(mr));
+        return 1;
+    }
+
+    translated = section.offset_within_address_space;
+    memory_region_unref(section.mr);
+
+    route->u.msi.address_lo = translated;
+    route->u.msi.address_hi = translated >> 32;
+    trace_kvm_riscv_fixup_msi_route_translated(address, translated);
     return 0;
 }
 
diff --git a/target/riscv/trace-events b/target/riscv/trace-events
index 93837f82a1..50980c031d 100644
--- a/target/riscv/trace-events
+++ b/target/riscv/trace-events
@@ -12,3 +12,7 @@ mseccfg_csr_write(uint64_t mhartid, uint64_t val) "hart %" 
PRIu64 ": write msecc
 
 # op_helper.c
 riscv_exception(uint32_t exception, const char *desc, uint64_t epc) "%u (%s) 
on epc 0x%"PRIx64""
+
+# kvm-cpu.c
+kvm_riscv_fixup_msi_route_translated(uint64_t iova, uint64_t gpa) "MSI iova = 
0x%"PRIx64" is translated into 0x%"PRIx64
+kvm_riscv_fixup_msi_route_failed(uint64_t iova, const char *reason, const char 
*region) "MSI iova = 0x%"PRIx64" failed: %s (region=%s)"
-- 
2.53.0


Reply via email to