On 10/26/2026 10:16 PM, Tan Chi wrote:
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]>
---
The AddressSpace translation follows the existing
kvm_arch_fixup_msi_route() implementation on ARM.
Changes in v2:
- 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..7c3d3c8c69 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, len = sizeof(uint32_t), translated;
Seems unnecessary to initialize 'len'.
Otherwise,
Reviewed-by: Nutty Liu <[email protected]>
Thanks,
Nutty
+ 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, &len, 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)"