* [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
@ 2026-10-08 13:59 ` fangyu.yu
0 siblings, 0 replies; 27+ messages in thread
From: fangyu.yu @ 2026-10-08 13:59 UTC (permalink / raw)
To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
jroedel, zong.li, andrew.jones, jgg, jgg
Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv, kvm-riscv
From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
IOMMU implementations without the MSI_FLAT capability translate MSI
writes through the second-stage page table, so IRQ forwarding on them
will need guest IMSIC pages mapped into the second-stage domain.
Add an xarray to the MSI table that tracks the HPA installed for each
mapped guest IMSIC GPA, and two helpers for use with the MSI table lock
held: riscv_iommu_msi_table_map_gpa() installs the initial 4 KiB
mapping, and riscv_iommu_msi_table_replace_gpa_leaf() atomically swaps
the leaf PTE's PFN when a vCPU's VS-file host page moves, rejecting
leaves that do not map the expected old HPA.
Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
---
drivers/iommu/riscv/iommu.c | 94 +++++++++++++++++++++++++++++++++++++
drivers/iommu/riscv/iommu.h | 12 +++++
2 files changed, 106 insertions(+)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index e2e77469ea3c..48fc57d6409c 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -24,6 +24,7 @@
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/pci.h>
+#include <linux/pgtable.h>
#include <linux/generic_pt/iommu.h>
#include "../dma-iommu.h"
@@ -1217,6 +1218,92 @@ void riscv_iommu_msi_table_inval_all(struct riscv_iommu_msi_table *msi_table)
riscv_iommu_iotlb_inval(domain, &gather);
}
+int riscv_iommu_msi_table_map_gpa(struct riscv_iommu_msi_table *msi_table,
+ dma_addr_t gpa, phys_addr_t hpa)
+{
+ struct riscv_iommu_domain *domain =
+ container_of(msi_table, struct riscv_iommu_domain, msi_table);
+ const int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
+
+ /* Guest IMSIC GPA mapping only exists in second-stage translations. */
+ if (!domain->gscid)
+ return -EOPNOTSUPP;
+
+ return iommu_map(&domain->domain, gpa, hpa, IMSIC_MMIO_PAGE_SZ, prot,
+ GFP_ATOMIC);
+}
+
+int riscv_iommu_msi_table_replace_gpa_leaf(struct riscv_iommu_msi_table *msi_table,
+ dma_addr_t gpa, phys_addr_t old_hpa,
+ phys_addr_t new_hpa)
+{
+ struct riscv_iommu_domain *domain =
+ container_of(msi_table, struct riscv_iommu_domain, msi_table);
+ struct pt_iommu_riscv_64_hw_info pt_info;
+ u64 *root, *table, *ptep;
+ u64 old, new;
+ int top_level, level;
+
+ if (!IS_ALIGNED(gpa | old_hpa | new_hpa, PAGE_SIZE))
+ return -EINVAL;
+ if (!domain->gscid)
+ return -EOPNOTSUPP;
+
+ pt_iommu_riscv_64_hw_info(&domain->riscvpt, &pt_info);
+ switch (pt_info.iohgatp_mode) {
+ case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4:
+ top_level = 2;
+ break;
+ case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4:
+ top_level = 3;
+ break;
+ case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4:
+ top_level = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ root = phys_to_virt(pt_info.ppn << PAGE_SHIFT);
+ for (;;) {
+ table = root;
+ for (level = top_level; level >= 0; level--) {
+ unsigned int shift = PAGE_SHIFT + level * 9;
+ unsigned int index = gpa >> shift;
+
+ if (level == top_level)
+ index &= GENMASK(10, 0);
+ else
+ index &= GENMASK(8, 0);
+ ptep = &table[index];
+ old = READ_ONCE(*ptep);
+
+ if (level) {
+ /* A valid non-leaf PTE has R/W/X clear. */
+ if ((old & (_PAGE_PRESENT | _PAGE_LEAF)) !=
+ _PAGE_PRESENT)
+ return -EADDRINUSE;
+ table = phys_to_virt(FIELD_GET(_PAGE_PFN_MASK,
+ old) << PAGE_SHIFT);
+ continue;
+ }
+
+ /* Replace only the L0 leaf previously installed for this GPA. */
+ if (!(old & _PAGE_PRESENT) || !(old & _PAGE_LEAF) ||
+ FIELD_GET(_PAGE_PFN_MASK, old) !=
+ old_hpa >> PAGE_SHIFT)
+ return -EADDRINUSE;
+
+ new = (old & ~_PAGE_PFN_MASK) |
+ FIELD_PREP(_PAGE_PFN_MASK,
+ new_hpa >> PAGE_SHIFT);
+ if (cmpxchg64(ptep, old, new) == old)
+ return 0;
+ break;
+ }
+ }
+}
+
#define RISCV_IOMMU_FSC_BARE 0
/*
* This function sends IOTINVAL commands as required by the RISC-V
@@ -1425,6 +1512,8 @@ static void riscv_iommu_iotlb_sync(struct iommu_domain *iommu_domain,
static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
{
struct riscv_iommu_domain *domain = iommu_domain_to_riscv(iommu_domain);
+ struct riscv_iommu_noflat_imsic *imsic;
+ unsigned long index;
WARN_ON(!list_empty(&domain->bonds));
@@ -1435,6 +1524,10 @@ static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
if (domain->gscid > 0)
ida_free(&riscv_iommu_gscids, domain->gscid);
+ xa_for_each(&domain->msi_table.noflat_imsics, index, imsic)
+ kfree(imsic);
+ xa_destroy(&domain->msi_table.noflat_imsics);
+
pt_iommu_deinit(&domain->riscvpt.iommu);
iommu_free_pages(domain->msi_table.root);
kfree(domain);
@@ -1676,6 +1769,7 @@ riscv_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags,
INIT_LIST_HEAD_RCU(&domain->bonds);
raw_spin_lock_init(&domain->lock);
raw_spin_lock_init(&domain->msi_table.lock);
+ xa_init(&domain->msi_table.noflat_imsics);
mutex_init(&domain->mutex);
iommu = dev_to_iommu(dev);
cfg.common.hw_max_oasz_lg2 = 56;
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 9852962e245b..53a368fbbdf2 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -18,6 +18,7 @@
#include <linux/irqdomain.h>
#include <linux/rcupdate.h>
#include <linux/sizes.h>
+#include <linux/xarray.h>
#include "iommu-bits.h"
@@ -74,6 +75,11 @@ struct riscv_iommu_device {
struct irq_domain *irqdomain;
};
+/* Tracks a guest IMSIC GPA mapped into an S2 domain on IOMMUs without MSI_FLAT. */
+struct riscv_iommu_noflat_imsic {
+ phys_addr_t hpa;
+};
+
struct riscv_iommu_msi_table {
/* Protects attachment, interrupt forwarding state, and MSI PTE updates. */
raw_spinlock_t lock;
@@ -84,6 +90,7 @@ struct riscv_iommu_msi_table {
u64 msi_addr_pattern;
const void *owner;
u64 required_caps; /* RISCV_IOMMU_CAPABILITIES_* required by active MSI PTEs */
+ struct xarray noflat_imsics;
};
/* Private IOMMU data for managed devices, dev_iommu_priv_* */
@@ -109,6 +116,11 @@ bool riscv_iommu_msi_table_check_caps(struct riscv_iommu_msi_table *msi_table, u
void riscv_iommu_msi_table_inval(struct riscv_iommu_msi_table *msi_table, unsigned long addr);
void riscv_iommu_msi_table_inval_all(struct riscv_iommu_msi_table *msi_table);
void riscv_iommu_msi_table_update(struct riscv_iommu_msi_table *msi_table, bool activate);
+int riscv_iommu_msi_table_map_gpa(struct riscv_iommu_msi_table *msi_table,
+ dma_addr_t gpa, phys_addr_t hpa);
+int riscv_iommu_msi_table_replace_gpa_leaf(struct riscv_iommu_msi_table *msi_table,
+ dma_addr_t gpa, phys_addr_t old_hpa,
+ phys_addr_t new_hpa);
#ifdef CONFIG_RISCV_IMSIC
void riscv_iommu_ir_irq_domain_remove(struct riscv_iommu_device *iommu);
--
2.50.1
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 27+ messages in thread* [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
@ 2026-10-08 13:59 ` fangyu.yu
0 siblings, 0 replies; 27+ messages in thread
From: fangyu.yu @ 2026-10-08 13:59 UTC (permalink / raw)
To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
jroedel, zong.li, andrew.jones, jgg, jgg
Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv, kvm-riscv
From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
IOMMU implementations without the MSI_FLAT capability translate MSI
writes through the second-stage page table, so IRQ forwarding on them
will need guest IMSIC pages mapped into the second-stage domain.
Add an xarray to the MSI table that tracks the HPA installed for each
mapped guest IMSIC GPA, and two helpers for use with the MSI table lock
held: riscv_iommu_msi_table_map_gpa() installs the initial 4 KiB
mapping, and riscv_iommu_msi_table_replace_gpa_leaf() atomically swaps
the leaf PTE's PFN when a vCPU's VS-file host page moves, rejecting
leaves that do not map the expected old HPA.
Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
---
drivers/iommu/riscv/iommu.c | 94 +++++++++++++++++++++++++++++++++++++
drivers/iommu/riscv/iommu.h | 12 +++++
2 files changed, 106 insertions(+)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index e2e77469ea3c..48fc57d6409c 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -24,6 +24,7 @@
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/pci.h>
+#include <linux/pgtable.h>
#include <linux/generic_pt/iommu.h>
#include "../dma-iommu.h"
@@ -1217,6 +1218,92 @@ void riscv_iommu_msi_table_inval_all(struct riscv_iommu_msi_table *msi_table)
riscv_iommu_iotlb_inval(domain, &gather);
}
+int riscv_iommu_msi_table_map_gpa(struct riscv_iommu_msi_table *msi_table,
+ dma_addr_t gpa, phys_addr_t hpa)
+{
+ struct riscv_iommu_domain *domain =
+ container_of(msi_table, struct riscv_iommu_domain, msi_table);
+ const int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
+
+ /* Guest IMSIC GPA mapping only exists in second-stage translations. */
+ if (!domain->gscid)
+ return -EOPNOTSUPP;
+
+ return iommu_map(&domain->domain, gpa, hpa, IMSIC_MMIO_PAGE_SZ, prot,
+ GFP_ATOMIC);
+}
+
+int riscv_iommu_msi_table_replace_gpa_leaf(struct riscv_iommu_msi_table *msi_table,
+ dma_addr_t gpa, phys_addr_t old_hpa,
+ phys_addr_t new_hpa)
+{
+ struct riscv_iommu_domain *domain =
+ container_of(msi_table, struct riscv_iommu_domain, msi_table);
+ struct pt_iommu_riscv_64_hw_info pt_info;
+ u64 *root, *table, *ptep;
+ u64 old, new;
+ int top_level, level;
+
+ if (!IS_ALIGNED(gpa | old_hpa | new_hpa, PAGE_SIZE))
+ return -EINVAL;
+ if (!domain->gscid)
+ return -EOPNOTSUPP;
+
+ pt_iommu_riscv_64_hw_info(&domain->riscvpt, &pt_info);
+ switch (pt_info.iohgatp_mode) {
+ case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4:
+ top_level = 2;
+ break;
+ case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4:
+ top_level = 3;
+ break;
+ case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4:
+ top_level = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ root = phys_to_virt(pt_info.ppn << PAGE_SHIFT);
+ for (;;) {
+ table = root;
+ for (level = top_level; level >= 0; level--) {
+ unsigned int shift = PAGE_SHIFT + level * 9;
+ unsigned int index = gpa >> shift;
+
+ if (level == top_level)
+ index &= GENMASK(10, 0);
+ else
+ index &= GENMASK(8, 0);
+ ptep = &table[index];
+ old = READ_ONCE(*ptep);
+
+ if (level) {
+ /* A valid non-leaf PTE has R/W/X clear. */
+ if ((old & (_PAGE_PRESENT | _PAGE_LEAF)) !=
+ _PAGE_PRESENT)
+ return -EADDRINUSE;
+ table = phys_to_virt(FIELD_GET(_PAGE_PFN_MASK,
+ old) << PAGE_SHIFT);
+ continue;
+ }
+
+ /* Replace only the L0 leaf previously installed for this GPA. */
+ if (!(old & _PAGE_PRESENT) || !(old & _PAGE_LEAF) ||
+ FIELD_GET(_PAGE_PFN_MASK, old) !=
+ old_hpa >> PAGE_SHIFT)
+ return -EADDRINUSE;
+
+ new = (old & ~_PAGE_PFN_MASK) |
+ FIELD_PREP(_PAGE_PFN_MASK,
+ new_hpa >> PAGE_SHIFT);
+ if (cmpxchg64(ptep, old, new) == old)
+ return 0;
+ break;
+ }
+ }
+}
+
#define RISCV_IOMMU_FSC_BARE 0
/*
* This function sends IOTINVAL commands as required by the RISC-V
@@ -1425,6 +1512,8 @@ static void riscv_iommu_iotlb_sync(struct iommu_domain *iommu_domain,
static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
{
struct riscv_iommu_domain *domain = iommu_domain_to_riscv(iommu_domain);
+ struct riscv_iommu_noflat_imsic *imsic;
+ unsigned long index;
WARN_ON(!list_empty(&domain->bonds));
@@ -1435,6 +1524,10 @@ static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
if (domain->gscid > 0)
ida_free(&riscv_iommu_gscids, domain->gscid);
+ xa_for_each(&domain->msi_table.noflat_imsics, index, imsic)
+ kfree(imsic);
+ xa_destroy(&domain->msi_table.noflat_imsics);
+
pt_iommu_deinit(&domain->riscvpt.iommu);
iommu_free_pages(domain->msi_table.root);
kfree(domain);
@@ -1676,6 +1769,7 @@ riscv_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags,
INIT_LIST_HEAD_RCU(&domain->bonds);
raw_spin_lock_init(&domain->lock);
raw_spin_lock_init(&domain->msi_table.lock);
+ xa_init(&domain->msi_table.noflat_imsics);
mutex_init(&domain->mutex);
iommu = dev_to_iommu(dev);
cfg.common.hw_max_oasz_lg2 = 56;
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 9852962e245b..53a368fbbdf2 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -18,6 +18,7 @@
#include <linux/irqdomain.h>
#include <linux/rcupdate.h>
#include <linux/sizes.h>
+#include <linux/xarray.h>
#include "iommu-bits.h"
@@ -74,6 +75,11 @@ struct riscv_iommu_device {
struct irq_domain *irqdomain;
};
+/* Tracks a guest IMSIC GPA mapped into an S2 domain on IOMMUs without MSI_FLAT. */
+struct riscv_iommu_noflat_imsic {
+ phys_addr_t hpa;
+};
+
struct riscv_iommu_msi_table {
/* Protects attachment, interrupt forwarding state, and MSI PTE updates. */
raw_spinlock_t lock;
@@ -84,6 +90,7 @@ struct riscv_iommu_msi_table {
u64 msi_addr_pattern;
const void *owner;
u64 required_caps; /* RISCV_IOMMU_CAPABILITIES_* required by active MSI PTEs */
+ struct xarray noflat_imsics;
};
/* Private IOMMU data for managed devices, dev_iommu_priv_* */
@@ -109,6 +116,11 @@ bool riscv_iommu_msi_table_check_caps(struct riscv_iommu_msi_table *msi_table, u
void riscv_iommu_msi_table_inval(struct riscv_iommu_msi_table *msi_table, unsigned long addr);
void riscv_iommu_msi_table_inval_all(struct riscv_iommu_msi_table *msi_table);
void riscv_iommu_msi_table_update(struct riscv_iommu_msi_table *msi_table, bool activate);
+int riscv_iommu_msi_table_map_gpa(struct riscv_iommu_msi_table *msi_table,
+ dma_addr_t gpa, phys_addr_t hpa);
+int riscv_iommu_msi_table_replace_gpa_leaf(struct riscv_iommu_msi_table *msi_table,
+ dma_addr_t gpa, phys_addr_t old_hpa,
+ phys_addr_t new_hpa);
#ifdef CONFIG_RISCV_IMSIC
void riscv_iommu_ir_irq_domain_remove(struct riscv_iommu_device *iommu);
--
2.50.1
^ permalink raw reply related [flat|nested] 27+ messages in thread* Re: [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
2026-10-08 13:59 ` fangyu.yu
(?)
@ 2026-10-09 8:50 ` Gong Shuai
-1 siblings, 0 replies; 27+ messages in thread
From: Gong Shuai @ 2026-10-09 8:50 UTC (permalink / raw)
To: fangyu.yu
Cc: alex, andrew.jones, aou, guoren, iommu, jgg, jgg, joro, jroedel,
kvm-riscv, linux-kernel, linux-riscv, palmer, pjw, robin.murphy,
tomasz.jeznach, will, zong.li, gong.shuai, gsh517025
Hi Fangyu,
> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>
> IOMMU implementations without the MSI_FLAT capability translate MSI
> writes through the second-stage page table, so IRQ forwarding on them
> will need guest IMSIC pages mapped into the second-stage domain.
>
> Add an xarray to the MSI table that tracks the HPA installed for each
> mapped guest IMSIC GPA, and two helpers for use with the MSI table lock
> held: riscv_iommu_msi_table_map_gpa() installs the initial 4 KiB
> mapping, and riscv_iommu_msi_table_replace_gpa_leaf() atomically swaps
> the leaf PTE's PFN when a vCPU's VS-file host page moves, rejecting
> leaves that do not map the expected old HPA.
>
> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
> ---
> drivers/iommu/riscv/iommu.c | 94 +++++++++++++++++++++++++++++++++++++
> drivers/iommu/riscv/iommu.h | 12 +++++
> 2 files changed, 106 insertions(+)
>
> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> index e2e77469ea3c..48fc57d6409c 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -24,6 +24,7 @@
> #include <linux/moduleparam.h>
> #include <linux/mutex.h>
> #include <linux/pci.h>
> +#include <linux/pgtable.h>
> #include <linux/generic_pt/iommu.h>
>
> #include "../dma-iommu.h"
> @@ -1217,6 +1218,92 @@ void riscv_iommu_msi_table_inval_all(struct riscv_iommu_msi_table *msi_table)
> riscv_iommu_iotlb_inval(domain, &gather);
> }
>
> +int riscv_iommu_msi_table_map_gpa(struct riscv_iommu_msi_table *msi_table,
> + dma_addr_t gpa, phys_addr_t hpa)
> +{
> + struct riscv_iommu_domain *domain =
> + container_of(msi_table, struct riscv_iommu_domain, msi_table);
> + const int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
> +
> + /* Guest IMSIC GPA mapping only exists in second-stage translations. */
> + if (!domain->gscid)
> + return -EOPNOTSUPP;
> +
> + return iommu_map(&domain->domain, gpa, hpa, IMSIC_MMIO_PAGE_SZ, prot,
> + GFP_ATOMIC);
> +}
> +
> +int riscv_iommu_msi_table_replace_gpa_leaf(struct riscv_iommu_msi_table *msi_table,
> + dma_addr_t gpa, phys_addr_t old_hpa,
> + phys_addr_t new_hpa)
This might fit better inside generic_pt rather than in the driver,
since it walks the page tables on its own and duplicates things
the library already owns, like the per-level index widths, the
x4 root size, and the PTE layout.
I recently tried something very similar, and doing this inside
generic_pt turned out to be quite feasible (only a quick experiment,
not cleaned up for posting).
Thanks,
Shuai
> +{
> + struct riscv_iommu_domain *domain =
> + container_of(msi_table, struct riscv_iommu_domain, msi_table);
> + struct pt_iommu_riscv_64_hw_info pt_info;
> + u64 *root, *table, *ptep;
> + u64 old, new;
> + int top_level, level;
> +
> + if (!IS_ALIGNED(gpa | old_hpa | new_hpa, PAGE_SIZE))
> + return -EINVAL;
> + if (!domain->gscid)
> + return -EOPNOTSUPP;
> +
> + pt_iommu_riscv_64_hw_info(&domain->riscvpt, &pt_info);
> + switch (pt_info.iohgatp_mode) {
> + case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4:
> + top_level = 2;
> + break;
> + case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4:
> + top_level = 3;
> + break;
> + case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4:
> + top_level = 4;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + root = phys_to_virt(pt_info.ppn << PAGE_SHIFT);
> + for (;;) {
> + table = root;
> + for (level = top_level; level >= 0; level--) {
> + unsigned int shift = PAGE_SHIFT + level * 9;
> + unsigned int index = gpa >> shift;
> +
> + if (level == top_level)
> + index &= GENMASK(10, 0);
> + else
> + index &= GENMASK(8, 0);
> + ptep = &table[index];
> + old = READ_ONCE(*ptep);
> +
> + if (level) {
> + /* A valid non-leaf PTE has R/W/X clear. */
> + if ((old & (_PAGE_PRESENT | _PAGE_LEAF)) !=
> + _PAGE_PRESENT)
> + return -EADDRINUSE;
> + table = phys_to_virt(FIELD_GET(_PAGE_PFN_MASK,
> + old) << PAGE_SHIFT);
> + continue;
> + }
> +
> + /* Replace only the L0 leaf previously installed for this GPA. */
> + if (!(old & _PAGE_PRESENT) || !(old & _PAGE_LEAF) ||
> + FIELD_GET(_PAGE_PFN_MASK, old) !=
> + old_hpa >> PAGE_SHIFT)
> + return -EADDRINUSE;
> +
> + new = (old & ~_PAGE_PFN_MASK) |
> + FIELD_PREP(_PAGE_PFN_MASK,
> + new_hpa >> PAGE_SHIFT);
> + if (cmpxchg64(ptep, old, new) == old)
> + return 0;
> + break;
> + }
> + }
> +}
> +
> #define RISCV_IOMMU_FSC_BARE 0
> /*
> * This function sends IOTINVAL commands as required by the RISC-V
> @@ -1425,6 +1512,8 @@ static void riscv_iommu_iotlb_sync(struct iommu_domain *iommu_domain,
> static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
> {
> struct riscv_iommu_domain *domain = iommu_domain_to_riscv(iommu_domain);
> + struct riscv_iommu_noflat_imsic *imsic;
> + unsigned long index;
>
> WARN_ON(!list_empty(&domain->bonds));
>
> @@ -1435,6 +1524,10 @@ static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
> if (domain->gscid > 0)
> ida_free(&riscv_iommu_gscids, domain->gscid);
>
> + xa_for_each(&domain->msi_table.noflat_imsics, index, imsic)
> + kfree(imsic);
> + xa_destroy(&domain->msi_table.noflat_imsics);
> +
> pt_iommu_deinit(&domain->riscvpt.iommu);
> iommu_free_pages(domain->msi_table.root);
> kfree(domain);
> @@ -1676,6 +1769,7 @@ riscv_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags,
> INIT_LIST_HEAD_RCU(&domain->bonds);
> raw_spin_lock_init(&domain->lock);
> raw_spin_lock_init(&domain->msi_table.lock);
> + xa_init(&domain->msi_table.noflat_imsics);
> mutex_init(&domain->mutex);
> iommu = dev_to_iommu(dev);
> cfg.common.hw_max_oasz_lg2 = 56;
> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> index 9852962e245b..53a368fbbdf2 100644
> --- a/drivers/iommu/riscv/iommu.h
> +++ b/drivers/iommu/riscv/iommu.h
> @@ -18,6 +18,7 @@
> #include <linux/irqdomain.h>
> #include <linux/rcupdate.h>
> #include <linux/sizes.h>
> +#include <linux/xarray.h>
>
> #include "iommu-bits.h"
>
> @@ -74,6 +75,11 @@ struct riscv_iommu_device {
> struct irq_domain *irqdomain;
> };
>
> +/* Tracks a guest IMSIC GPA mapped into an S2 domain on IOMMUs without MSI_FLAT. */
> +struct riscv_iommu_noflat_imsic {
> + phys_addr_t hpa;
> +};
> +
> struct riscv_iommu_msi_table {
> /* Protects attachment, interrupt forwarding state, and MSI PTE updates. */
> raw_spinlock_t lock;
> @@ -84,6 +90,7 @@ struct riscv_iommu_msi_table {
> u64 msi_addr_pattern;
> const void *owner;
> u64 required_caps; /* RISCV_IOMMU_CAPABILITIES_* required by active MSI PTEs */
> + struct xarray noflat_imsics;
> };
>
> /* Private IOMMU data for managed devices, dev_iommu_priv_* */
> @@ -109,6 +116,11 @@ bool riscv_iommu_msi_table_check_caps(struct riscv_iommu_msi_table *msi_table, u
> void riscv_iommu_msi_table_inval(struct riscv_iommu_msi_table *msi_table, unsigned long addr);
> void riscv_iommu_msi_table_inval_all(struct riscv_iommu_msi_table *msi_table);
> void riscv_iommu_msi_table_update(struct riscv_iommu_msi_table *msi_table, bool activate);
> +int riscv_iommu_msi_table_map_gpa(struct riscv_iommu_msi_table *msi_table,
> + dma_addr_t gpa, phys_addr_t hpa);
> +int riscv_iommu_msi_table_replace_gpa_leaf(struct riscv_iommu_msi_table *msi_table,
> + dma_addr_t gpa, phys_addr_t old_hpa,
> + phys_addr_t new_hpa);
>
> #ifdef CONFIG_RISCV_IMSIC
> void riscv_iommu_ir_irq_domain_remove(struct riscv_iommu_device *iommu);
> --
> 2.50.1
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
@ 2026-10-09 8:50 ` Gong Shuai
0 siblings, 0 replies; 27+ messages in thread
From: Gong Shuai @ 2026-10-09 8:50 UTC (permalink / raw)
To: fangyu.yu
Cc: alex, andrew.jones, aou, guoren, iommu, jgg, jgg, joro, jroedel,
kvm-riscv, linux-kernel, linux-riscv, palmer, pjw, robin.murphy,
tomasz.jeznach, will, zong.li, gong.shuai, gsh517025
Hi Fangyu,
> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>
> IOMMU implementations without the MSI_FLAT capability translate MSI
> writes through the second-stage page table, so IRQ forwarding on them
> will need guest IMSIC pages mapped into the second-stage domain.
>
> Add an xarray to the MSI table that tracks the HPA installed for each
> mapped guest IMSIC GPA, and two helpers for use with the MSI table lock
> held: riscv_iommu_msi_table_map_gpa() installs the initial 4 KiB
> mapping, and riscv_iommu_msi_table_replace_gpa_leaf() atomically swaps
> the leaf PTE's PFN when a vCPU's VS-file host page moves, rejecting
> leaves that do not map the expected old HPA.
>
> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
> ---
> drivers/iommu/riscv/iommu.c | 94 +++++++++++++++++++++++++++++++++++++
> drivers/iommu/riscv/iommu.h | 12 +++++
> 2 files changed, 106 insertions(+)
>
> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> index e2e77469ea3c..48fc57d6409c 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -24,6 +24,7 @@
> #include <linux/moduleparam.h>
> #include <linux/mutex.h>
> #include <linux/pci.h>
> +#include <linux/pgtable.h>
> #include <linux/generic_pt/iommu.h>
>
> #include "../dma-iommu.h"
> @@ -1217,6 +1218,92 @@ void riscv_iommu_msi_table_inval_all(struct riscv_iommu_msi_table *msi_table)
> riscv_iommu_iotlb_inval(domain, &gather);
> }
>
> +int riscv_iommu_msi_table_map_gpa(struct riscv_iommu_msi_table *msi_table,
> + dma_addr_t gpa, phys_addr_t hpa)
> +{
> + struct riscv_iommu_domain *domain =
> + container_of(msi_table, struct riscv_iommu_domain, msi_table);
> + const int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
> +
> + /* Guest IMSIC GPA mapping only exists in second-stage translations. */
> + if (!domain->gscid)
> + return -EOPNOTSUPP;
> +
> + return iommu_map(&domain->domain, gpa, hpa, IMSIC_MMIO_PAGE_SZ, prot,
> + GFP_ATOMIC);
> +}
> +
> +int riscv_iommu_msi_table_replace_gpa_leaf(struct riscv_iommu_msi_table *msi_table,
> + dma_addr_t gpa, phys_addr_t old_hpa,
> + phys_addr_t new_hpa)
This might fit better inside generic_pt rather than in the driver,
since it walks the page tables on its own and duplicates things
the library already owns, like the per-level index widths, the
x4 root size, and the PTE layout.
I recently tried something very similar, and doing this inside
generic_pt turned out to be quite feasible (only a quick experiment,
not cleaned up for posting).
Thanks,
Shuai
> +{
> + struct riscv_iommu_domain *domain =
> + container_of(msi_table, struct riscv_iommu_domain, msi_table);
> + struct pt_iommu_riscv_64_hw_info pt_info;
> + u64 *root, *table, *ptep;
> + u64 old, new;
> + int top_level, level;
> +
> + if (!IS_ALIGNED(gpa | old_hpa | new_hpa, PAGE_SIZE))
> + return -EINVAL;
> + if (!domain->gscid)
> + return -EOPNOTSUPP;
> +
> + pt_iommu_riscv_64_hw_info(&domain->riscvpt, &pt_info);
> + switch (pt_info.iohgatp_mode) {
> + case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4:
> + top_level = 2;
> + break;
> + case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4:
> + top_level = 3;
> + break;
> + case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4:
> + top_level = 4;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + root = phys_to_virt(pt_info.ppn << PAGE_SHIFT);
> + for (;;) {
> + table = root;
> + for (level = top_level; level >= 0; level--) {
> + unsigned int shift = PAGE_SHIFT + level * 9;
> + unsigned int index = gpa >> shift;
> +
> + if (level == top_level)
> + index &= GENMASK(10, 0);
> + else
> + index &= GENMASK(8, 0);
> + ptep = &table[index];
> + old = READ_ONCE(*ptep);
> +
> + if (level) {
> + /* A valid non-leaf PTE has R/W/X clear. */
> + if ((old & (_PAGE_PRESENT | _PAGE_LEAF)) !=
> + _PAGE_PRESENT)
> + return -EADDRINUSE;
> + table = phys_to_virt(FIELD_GET(_PAGE_PFN_MASK,
> + old) << PAGE_SHIFT);
> + continue;
> + }
> +
> + /* Replace only the L0 leaf previously installed for this GPA. */
> + if (!(old & _PAGE_PRESENT) || !(old & _PAGE_LEAF) ||
> + FIELD_GET(_PAGE_PFN_MASK, old) !=
> + old_hpa >> PAGE_SHIFT)
> + return -EADDRINUSE;
> +
> + new = (old & ~_PAGE_PFN_MASK) |
> + FIELD_PREP(_PAGE_PFN_MASK,
> + new_hpa >> PAGE_SHIFT);
> + if (cmpxchg64(ptep, old, new) == old)
> + return 0;
> + break;
> + }
> + }
> +}
> +
> #define RISCV_IOMMU_FSC_BARE 0
> /*
> * This function sends IOTINVAL commands as required by the RISC-V
> @@ -1425,6 +1512,8 @@ static void riscv_iommu_iotlb_sync(struct iommu_domain *iommu_domain,
> static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
> {
> struct riscv_iommu_domain *domain = iommu_domain_to_riscv(iommu_domain);
> + struct riscv_iommu_noflat_imsic *imsic;
> + unsigned long index;
>
> WARN_ON(!list_empty(&domain->bonds));
>
> @@ -1435,6 +1524,10 @@ static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
> if (domain->gscid > 0)
> ida_free(&riscv_iommu_gscids, domain->gscid);
>
> + xa_for_each(&domain->msi_table.noflat_imsics, index, imsic)
> + kfree(imsic);
> + xa_destroy(&domain->msi_table.noflat_imsics);
> +
> pt_iommu_deinit(&domain->riscvpt.iommu);
> iommu_free_pages(domain->msi_table.root);
> kfree(domain);
> @@ -1676,6 +1769,7 @@ riscv_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags,
> INIT_LIST_HEAD_RCU(&domain->bonds);
> raw_spin_lock_init(&domain->lock);
> raw_spin_lock_init(&domain->msi_table.lock);
> + xa_init(&domain->msi_table.noflat_imsics);
> mutex_init(&domain->mutex);
> iommu = dev_to_iommu(dev);
> cfg.common.hw_max_oasz_lg2 = 56;
> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> index 9852962e245b..53a368fbbdf2 100644
> --- a/drivers/iommu/riscv/iommu.h
> +++ b/drivers/iommu/riscv/iommu.h
> @@ -18,6 +18,7 @@
> #include <linux/irqdomain.h>
> #include <linux/rcupdate.h>
> #include <linux/sizes.h>
> +#include <linux/xarray.h>
>
> #include "iommu-bits.h"
>
> @@ -74,6 +75,11 @@ struct riscv_iommu_device {
> struct irq_domain *irqdomain;
> };
>
> +/* Tracks a guest IMSIC GPA mapped into an S2 domain on IOMMUs without MSI_FLAT. */
> +struct riscv_iommu_noflat_imsic {
> + phys_addr_t hpa;
> +};
> +
> struct riscv_iommu_msi_table {
> /* Protects attachment, interrupt forwarding state, and MSI PTE updates. */
> raw_spinlock_t lock;
> @@ -84,6 +90,7 @@ struct riscv_iommu_msi_table {
> u64 msi_addr_pattern;
> const void *owner;
> u64 required_caps; /* RISCV_IOMMU_CAPABILITIES_* required by active MSI PTEs */
> + struct xarray noflat_imsics;
> };
>
> /* Private IOMMU data for managed devices, dev_iommu_priv_* */
> @@ -109,6 +116,11 @@ bool riscv_iommu_msi_table_check_caps(struct riscv_iommu_msi_table *msi_table, u
> void riscv_iommu_msi_table_inval(struct riscv_iommu_msi_table *msi_table, unsigned long addr);
> void riscv_iommu_msi_table_inval_all(struct riscv_iommu_msi_table *msi_table);
> void riscv_iommu_msi_table_update(struct riscv_iommu_msi_table *msi_table, bool activate);
> +int riscv_iommu_msi_table_map_gpa(struct riscv_iommu_msi_table *msi_table,
> + dma_addr_t gpa, phys_addr_t hpa);
> +int riscv_iommu_msi_table_replace_gpa_leaf(struct riscv_iommu_msi_table *msi_table,
> + dma_addr_t gpa, phys_addr_t old_hpa,
> + phys_addr_t new_hpa);
>
> #ifdef CONFIG_RISCV_IMSIC
> void riscv_iommu_ir_irq_domain_remove(struct riscv_iommu_device *iommu);
> --
> 2.50.1
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
@ 2026-10-09 8:50 ` Gong Shuai
0 siblings, 0 replies; 27+ messages in thread
From: Gong Shuai @ 2026-10-09 8:50 UTC (permalink / raw)
To: fangyu.yu
Cc: alex, andrew.jones, aou, guoren, iommu, jgg, jgg, joro, jroedel,
kvm-riscv, linux-kernel, linux-riscv, palmer, pjw, robin.murphy,
tomasz.jeznach, will, zong.li, gong.shuai, gsh517025
Hi Fangyu,
> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>
> IOMMU implementations without the MSI_FLAT capability translate MSI
> writes through the second-stage page table, so IRQ forwarding on them
> will need guest IMSIC pages mapped into the second-stage domain.
>
> Add an xarray to the MSI table that tracks the HPA installed for each
> mapped guest IMSIC GPA, and two helpers for use with the MSI table lock
> held: riscv_iommu_msi_table_map_gpa() installs the initial 4 KiB
> mapping, and riscv_iommu_msi_table_replace_gpa_leaf() atomically swaps
> the leaf PTE's PFN when a vCPU's VS-file host page moves, rejecting
> leaves that do not map the expected old HPA.
>
> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
> ---
> drivers/iommu/riscv/iommu.c | 94 +++++++++++++++++++++++++++++++++++++
> drivers/iommu/riscv/iommu.h | 12 +++++
> 2 files changed, 106 insertions(+)
>
> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
> index e2e77469ea3c..48fc57d6409c 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -24,6 +24,7 @@
> #include <linux/moduleparam.h>
> #include <linux/mutex.h>
> #include <linux/pci.h>
> +#include <linux/pgtable.h>
> #include <linux/generic_pt/iommu.h>
>
> #include "../dma-iommu.h"
> @@ -1217,6 +1218,92 @@ void riscv_iommu_msi_table_inval_all(struct riscv_iommu_msi_table *msi_table)
> riscv_iommu_iotlb_inval(domain, &gather);
> }
>
> +int riscv_iommu_msi_table_map_gpa(struct riscv_iommu_msi_table *msi_table,
> + dma_addr_t gpa, phys_addr_t hpa)
> +{
> + struct riscv_iommu_domain *domain =
> + container_of(msi_table, struct riscv_iommu_domain, msi_table);
> + const int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
> +
> + /* Guest IMSIC GPA mapping only exists in second-stage translations. */
> + if (!domain->gscid)
> + return -EOPNOTSUPP;
> +
> + return iommu_map(&domain->domain, gpa, hpa, IMSIC_MMIO_PAGE_SZ, prot,
> + GFP_ATOMIC);
> +}
> +
> +int riscv_iommu_msi_table_replace_gpa_leaf(struct riscv_iommu_msi_table *msi_table,
> + dma_addr_t gpa, phys_addr_t old_hpa,
> + phys_addr_t new_hpa)
This might fit better inside generic_pt rather than in the driver,
since it walks the page tables on its own and duplicates things
the library already owns, like the per-level index widths, the
x4 root size, and the PTE layout.
I recently tried something very similar, and doing this inside
generic_pt turned out to be quite feasible (only a quick experiment,
not cleaned up for posting).
Thanks,
Shuai
> +{
> + struct riscv_iommu_domain *domain =
> + container_of(msi_table, struct riscv_iommu_domain, msi_table);
> + struct pt_iommu_riscv_64_hw_info pt_info;
> + u64 *root, *table, *ptep;
> + u64 old, new;
> + int top_level, level;
> +
> + if (!IS_ALIGNED(gpa | old_hpa | new_hpa, PAGE_SIZE))
> + return -EINVAL;
> + if (!domain->gscid)
> + return -EOPNOTSUPP;
> +
> + pt_iommu_riscv_64_hw_info(&domain->riscvpt, &pt_info);
> + switch (pt_info.iohgatp_mode) {
> + case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4:
> + top_level = 2;
> + break;
> + case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4:
> + top_level = 3;
> + break;
> + case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4:
> + top_level = 4;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + root = phys_to_virt(pt_info.ppn << PAGE_SHIFT);
> + for (;;) {
> + table = root;
> + for (level = top_level; level >= 0; level--) {
> + unsigned int shift = PAGE_SHIFT + level * 9;
> + unsigned int index = gpa >> shift;
> +
> + if (level == top_level)
> + index &= GENMASK(10, 0);
> + else
> + index &= GENMASK(8, 0);
> + ptep = &table[index];
> + old = READ_ONCE(*ptep);
> +
> + if (level) {
> + /* A valid non-leaf PTE has R/W/X clear. */
> + if ((old & (_PAGE_PRESENT | _PAGE_LEAF)) !=
> + _PAGE_PRESENT)
> + return -EADDRINUSE;
> + table = phys_to_virt(FIELD_GET(_PAGE_PFN_MASK,
> + old) << PAGE_SHIFT);
> + continue;
> + }
> +
> + /* Replace only the L0 leaf previously installed for this GPA. */
> + if (!(old & _PAGE_PRESENT) || !(old & _PAGE_LEAF) ||
> + FIELD_GET(_PAGE_PFN_MASK, old) !=
> + old_hpa >> PAGE_SHIFT)
> + return -EADDRINUSE;
> +
> + new = (old & ~_PAGE_PFN_MASK) |
> + FIELD_PREP(_PAGE_PFN_MASK,
> + new_hpa >> PAGE_SHIFT);
> + if (cmpxchg64(ptep, old, new) == old)
> + return 0;
> + break;
> + }
> + }
> +}
> +
> #define RISCV_IOMMU_FSC_BARE 0
> /*
> * This function sends IOTINVAL commands as required by the RISC-V
> @@ -1425,6 +1512,8 @@ static void riscv_iommu_iotlb_sync(struct iommu_domain *iommu_domain,
> static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
> {
> struct riscv_iommu_domain *domain = iommu_domain_to_riscv(iommu_domain);
> + struct riscv_iommu_noflat_imsic *imsic;
> + unsigned long index;
>
> WARN_ON(!list_empty(&domain->bonds));
>
> @@ -1435,6 +1524,10 @@ static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
> if (domain->gscid > 0)
> ida_free(&riscv_iommu_gscids, domain->gscid);
>
> + xa_for_each(&domain->msi_table.noflat_imsics, index, imsic)
> + kfree(imsic);
> + xa_destroy(&domain->msi_table.noflat_imsics);
> +
> pt_iommu_deinit(&domain->riscvpt.iommu);
> iommu_free_pages(domain->msi_table.root);
> kfree(domain);
> @@ -1676,6 +1769,7 @@ riscv_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags,
> INIT_LIST_HEAD_RCU(&domain->bonds);
> raw_spin_lock_init(&domain->lock);
> raw_spin_lock_init(&domain->msi_table.lock);
> + xa_init(&domain->msi_table.noflat_imsics);
> mutex_init(&domain->mutex);
> iommu = dev_to_iommu(dev);
> cfg.common.hw_max_oasz_lg2 = 56;
> diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
> index 9852962e245b..53a368fbbdf2 100644
> --- a/drivers/iommu/riscv/iommu.h
> +++ b/drivers/iommu/riscv/iommu.h
> @@ -18,6 +18,7 @@
> #include <linux/irqdomain.h>
> #include <linux/rcupdate.h>
> #include <linux/sizes.h>
> +#include <linux/xarray.h>
>
> #include "iommu-bits.h"
>
> @@ -74,6 +75,11 @@ struct riscv_iommu_device {
> struct irq_domain *irqdomain;
> };
>
> +/* Tracks a guest IMSIC GPA mapped into an S2 domain on IOMMUs without MSI_FLAT. */
> +struct riscv_iommu_noflat_imsic {
> + phys_addr_t hpa;
> +};
> +
> struct riscv_iommu_msi_table {
> /* Protects attachment, interrupt forwarding state, and MSI PTE updates. */
> raw_spinlock_t lock;
> @@ -84,6 +90,7 @@ struct riscv_iommu_msi_table {
> u64 msi_addr_pattern;
> const void *owner;
> u64 required_caps; /* RISCV_IOMMU_CAPABILITIES_* required by active MSI PTEs */
> + struct xarray noflat_imsics;
> };
>
> /* Private IOMMU data for managed devices, dev_iommu_priv_* */
> @@ -109,6 +116,11 @@ bool riscv_iommu_msi_table_check_caps(struct riscv_iommu_msi_table *msi_table, u
> void riscv_iommu_msi_table_inval(struct riscv_iommu_msi_table *msi_table, unsigned long addr);
> void riscv_iommu_msi_table_inval_all(struct riscv_iommu_msi_table *msi_table);
> void riscv_iommu_msi_table_update(struct riscv_iommu_msi_table *msi_table, bool activate);
> +int riscv_iommu_msi_table_map_gpa(struct riscv_iommu_msi_table *msi_table,
> + dma_addr_t gpa, phys_addr_t hpa);
> +int riscv_iommu_msi_table_replace_gpa_leaf(struct riscv_iommu_msi_table *msi_table,
> + dma_addr_t gpa, phys_addr_t old_hpa,
> + phys_addr_t new_hpa);
>
> #ifdef CONFIG_RISCV_IMSIC
> void riscv_iommu_ir_irq_domain_remove(struct riscv_iommu_device *iommu);
> --
> 2.50.1
--
kvm-riscv mailing list
kvm-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kvm-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
2026-10-09 8:50 ` Gong Shuai
(?)
@ 2026-10-10 11:18 ` fangyu.yu
-1 siblings, 0 replies; 27+ messages in thread
From: fangyu.yu @ 2026-10-10 11:18 UTC (permalink / raw)
To: gong.shuai
Cc: alex, andrew.jones, aou, fangyu.yu, gsh517025, guoren, iommu, jgg,
jgg, joro, kvm-riscv, linux-kernel, linux-riscv, palmer, pjw,
robin.murphy, tomasz.jeznach, will, zong.li
>Hi Fangyu,
>
>> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>>
>> IOMMU implementations without the MSI_FLAT capability translate MSI
>> writes through the second-stage page table, so IRQ forwarding on them
>> will need guest IMSIC pages mapped into the second-stage domain.
>>
>> Add an xarray to the MSI table that tracks the HPA installed for each
>> mapped guest IMSIC GPA, and two helpers for use with the MSI table lock
>> held: riscv_iommu_msi_table_map_gpa() installs the initial 4 KiB
>> mapping, and riscv_iommu_msi_table_replace_gpa_leaf() atomically swaps
>> the leaf PTE's PFN when a vCPU's VS-file host page moves, rejecting
>> leaves that do not map the expected old HPA.
>>
>> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>> ---
>> drivers/iommu/riscv/iommu.c | 94 +++++++++++++++++++++++++++++++++++++
>> drivers/iommu/riscv/iommu.h | 12 +++++
>> 2 files changed, 106 insertions(+)
>>
>> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
>> index e2e77469ea3c..48fc57d6409c 100644
>> --- a/drivers/iommu/riscv/iommu.c
>> +++ b/drivers/iommu/riscv/iommu.c
>> @@ -24,6 +24,7 @@
>> #include <linux/moduleparam.h>
>> #include <linux/mutex.h>
>> #include <linux/pci.h>
>> +#include <linux/pgtable.h>
>> #include <linux/generic_pt/iommu.h>
>>
>> #include "../dma-iommu.h"
>> @@ -1217,6 +1218,92 @@ void riscv_iommu_msi_table_inval_all(struct riscv_iommu_msi_table *msi_table)
>> riscv_iommu_iotlb_inval(domain, &gather);
>> }
>>
>> +int riscv_iommu_msi_table_map_gpa(struct riscv_iommu_msi_table *msi_table,
>> + dma_addr_t gpa, phys_addr_t hpa)
>> +{
>> + struct riscv_iommu_domain *domain =
>> + container_of(msi_table, struct riscv_iommu_domain, msi_table);
>> + const int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
>> +
>> + /* Guest IMSIC GPA mapping only exists in second-stage translations. */
>> + if (!domain->gscid)
>> + return -EOPNOTSUPP;
>> +
>> + return iommu_map(&domain->domain, gpa, hpa, IMSIC_MMIO_PAGE_SZ, prot,
>> + GFP_ATOMIC);
>> +}
>> +
>> +int riscv_iommu_msi_table_replace_gpa_leaf(struct riscv_iommu_msi_table *msi_table,
>> + dma_addr_t gpa, phys_addr_t old_hpa,
>> + phys_addr_t new_hpa)
>
>
>This might fit better inside generic_pt rather than in the driver,
>since it walks the page tables on its own and duplicates things
>the library already owns, like the per-level index widths, the
>x4 root size, and the PTE layout.
>
>I recently tried something very similar, and doing this inside
>generic_pt turned out to be quite feasible (only a quick experiment,
>not cleaned up for posting).
>
Hi Shuai,
Thanks for the review. I agree the driver shouldn't carry format
knowledge that belongs to the library, but I think this particular
operation doesn't fit generic_pt.
This is not a generic page-table operation. It is a special
requirement of RISC-V IOMMU IRQ forwarding (irqbypass) on
implementations without an MSI page table: the guest IMSIC GPA
must be mapped into the second-stage domain, and when a vCPU's
VS-file host page migrates, its leaf must be replaced atomically.
Because a device may write an MSI at any moment, unmap + map
would fault. This operation is driven by the IRQ forwarding
state machine, and because I don't see another generic_pt format
sharing this flow, I think these helpers would be better placed
outside generic_pt. I plan to move them into iommu-ir.c so the
feature-scoped code stays together.
Thanks,
Fangyu
>Thanks,
>Shuai
--
kvm-riscv mailing list
kvm-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kvm-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
@ 2026-10-10 11:18 ` fangyu.yu
0 siblings, 0 replies; 27+ messages in thread
From: fangyu.yu @ 2026-10-10 11:18 UTC (permalink / raw)
To: gong.shuai
Cc: alex, andrew.jones, aou, fangyu.yu, gsh517025, guoren, iommu, jgg,
jgg, joro, kvm-riscv, linux-kernel, linux-riscv, palmer, pjw,
robin.murphy, tomasz.jeznach, will, zong.li
>Hi Fangyu,
>
>> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>>
>> IOMMU implementations without the MSI_FLAT capability translate MSI
>> writes through the second-stage page table, so IRQ forwarding on them
>> will need guest IMSIC pages mapped into the second-stage domain.
>>
>> Add an xarray to the MSI table that tracks the HPA installed for each
>> mapped guest IMSIC GPA, and two helpers for use with the MSI table lock
>> held: riscv_iommu_msi_table_map_gpa() installs the initial 4 KiB
>> mapping, and riscv_iommu_msi_table_replace_gpa_leaf() atomically swaps
>> the leaf PTE's PFN when a vCPU's VS-file host page moves, rejecting
>> leaves that do not map the expected old HPA.
>>
>> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>> ---
>> drivers/iommu/riscv/iommu.c | 94 +++++++++++++++++++++++++++++++++++++
>> drivers/iommu/riscv/iommu.h | 12 +++++
>> 2 files changed, 106 insertions(+)
>>
>> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
>> index e2e77469ea3c..48fc57d6409c 100644
>> --- a/drivers/iommu/riscv/iommu.c
>> +++ b/drivers/iommu/riscv/iommu.c
>> @@ -24,6 +24,7 @@
>> #include <linux/moduleparam.h>
>> #include <linux/mutex.h>
>> #include <linux/pci.h>
>> +#include <linux/pgtable.h>
>> #include <linux/generic_pt/iommu.h>
>>
>> #include "../dma-iommu.h"
>> @@ -1217,6 +1218,92 @@ void riscv_iommu_msi_table_inval_all(struct riscv_iommu_msi_table *msi_table)
>> riscv_iommu_iotlb_inval(domain, &gather);
>> }
>>
>> +int riscv_iommu_msi_table_map_gpa(struct riscv_iommu_msi_table *msi_table,
>> + dma_addr_t gpa, phys_addr_t hpa)
>> +{
>> + struct riscv_iommu_domain *domain =
>> + container_of(msi_table, struct riscv_iommu_domain, msi_table);
>> + const int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
>> +
>> + /* Guest IMSIC GPA mapping only exists in second-stage translations. */
>> + if (!domain->gscid)
>> + return -EOPNOTSUPP;
>> +
>> + return iommu_map(&domain->domain, gpa, hpa, IMSIC_MMIO_PAGE_SZ, prot,
>> + GFP_ATOMIC);
>> +}
>> +
>> +int riscv_iommu_msi_table_replace_gpa_leaf(struct riscv_iommu_msi_table *msi_table,
>> + dma_addr_t gpa, phys_addr_t old_hpa,
>> + phys_addr_t new_hpa)
>
>
>This might fit better inside generic_pt rather than in the driver,
>since it walks the page tables on its own and duplicates things
>the library already owns, like the per-level index widths, the
>x4 root size, and the PTE layout.
>
>I recently tried something very similar, and doing this inside
>generic_pt turned out to be quite feasible (only a quick experiment,
>not cleaned up for posting).
>
Hi Shuai,
Thanks for the review. I agree the driver shouldn't carry format
knowledge that belongs to the library, but I think this particular
operation doesn't fit generic_pt.
This is not a generic page-table operation. It is a special
requirement of RISC-V IOMMU IRQ forwarding (irqbypass) on
implementations without an MSI page table: the guest IMSIC GPA
must be mapped into the second-stage domain, and when a vCPU's
VS-file host page migrates, its leaf must be replaced atomically.
Because a device may write an MSI at any moment, unmap + map
would fault. This operation is driven by the IRQ forwarding
state machine, and because I don't see another generic_pt format
sharing this flow, I think these helpers would be better placed
outside generic_pt. I plan to move them into iommu-ir.c so the
feature-scoped code stays together.
Thanks,
Fangyu
>Thanks,
>Shuai
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
@ 2026-10-10 11:18 ` fangyu.yu
0 siblings, 0 replies; 27+ messages in thread
From: fangyu.yu @ 2026-10-10 11:18 UTC (permalink / raw)
To: gong.shuai
Cc: alex, andrew.jones, aou, fangyu.yu, gsh517025, guoren, iommu, jgg,
jgg, joro, kvm-riscv, linux-kernel, linux-riscv, palmer, pjw,
robin.murphy, tomasz.jeznach, will, zong.li
>Hi Fangyu,
>
>> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>>
>> IOMMU implementations without the MSI_FLAT capability translate MSI
>> writes through the second-stage page table, so IRQ forwarding on them
>> will need guest IMSIC pages mapped into the second-stage domain.
>>
>> Add an xarray to the MSI table that tracks the HPA installed for each
>> mapped guest IMSIC GPA, and two helpers for use with the MSI table lock
>> held: riscv_iommu_msi_table_map_gpa() installs the initial 4 KiB
>> mapping, and riscv_iommu_msi_table_replace_gpa_leaf() atomically swaps
>> the leaf PTE's PFN when a vCPU's VS-file host page moves, rejecting
>> leaves that do not map the expected old HPA.
>>
>> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>> ---
>> drivers/iommu/riscv/iommu.c | 94 +++++++++++++++++++++++++++++++++++++
>> drivers/iommu/riscv/iommu.h | 12 +++++
>> 2 files changed, 106 insertions(+)
>>
>> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
>> index e2e77469ea3c..48fc57d6409c 100644
>> --- a/drivers/iommu/riscv/iommu.c
>> +++ b/drivers/iommu/riscv/iommu.c
>> @@ -24,6 +24,7 @@
>> #include <linux/moduleparam.h>
>> #include <linux/mutex.h>
>> #include <linux/pci.h>
>> +#include <linux/pgtable.h>
>> #include <linux/generic_pt/iommu.h>
>>
>> #include "../dma-iommu.h"
>> @@ -1217,6 +1218,92 @@ void riscv_iommu_msi_table_inval_all(struct riscv_iommu_msi_table *msi_table)
>> riscv_iommu_iotlb_inval(domain, &gather);
>> }
>>
>> +int riscv_iommu_msi_table_map_gpa(struct riscv_iommu_msi_table *msi_table,
>> + dma_addr_t gpa, phys_addr_t hpa)
>> +{
>> + struct riscv_iommu_domain *domain =
>> + container_of(msi_table, struct riscv_iommu_domain, msi_table);
>> + const int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
>> +
>> + /* Guest IMSIC GPA mapping only exists in second-stage translations. */
>> + if (!domain->gscid)
>> + return -EOPNOTSUPP;
>> +
>> + return iommu_map(&domain->domain, gpa, hpa, IMSIC_MMIO_PAGE_SZ, prot,
>> + GFP_ATOMIC);
>> +}
>> +
>> +int riscv_iommu_msi_table_replace_gpa_leaf(struct riscv_iommu_msi_table *msi_table,
>> + dma_addr_t gpa, phys_addr_t old_hpa,
>> + phys_addr_t new_hpa)
>
>
>This might fit better inside generic_pt rather than in the driver,
>since it walks the page tables on its own and duplicates things
>the library already owns, like the per-level index widths, the
>x4 root size, and the PTE layout.
>
>I recently tried something very similar, and doing this inside
>generic_pt turned out to be quite feasible (only a quick experiment,
>not cleaned up for posting).
>
Hi Shuai,
Thanks for the review. I agree the driver shouldn't carry format
knowledge that belongs to the library, but I think this particular
operation doesn't fit generic_pt.
This is not a generic page-table operation. It is a special
requirement of RISC-V IOMMU IRQ forwarding (irqbypass) on
implementations without an MSI page table: the guest IMSIC GPA
must be mapped into the second-stage domain, and when a vCPU's
VS-file host page migrates, its leaf must be replaced atomically.
Because a device may write an MSI at any moment, unmap + map
would fault. This operation is driven by the IRQ forwarding
state machine, and because I don't see another generic_pt format
sharing this flow, I think these helpers would be better placed
outside generic_pt. I plan to move them into iommu-ir.c so the
feature-scoped code stays together.
Thanks,
Fangyu
>Thanks,
>Shuai
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
2026-10-10 11:18 ` fangyu.yu
(?)
@ 2026-10-10 15:33 ` Gong Shuai
-1 siblings, 0 replies; 27+ messages in thread
From: Gong Shuai @ 2026-10-10 15:33 UTC (permalink / raw)
To: fangyu.yu, gong.shuai
Cc: alex, andrew.jones, aou, guoren, iommu, jgg, jgg, joro, kvm-riscv,
linux-kernel, linux-riscv, palmer, pjw, robin.murphy,
tomasz.jeznach, will, zong.li
Hi Fangyu,
On 10/10/2026 7:18 PM, fangyu.yu@linux.alibaba.com wrote:
>>Hi Fangyu,
>>
>>> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>>>
>>> IOMMU implementations without the MSI_FLAT capability translate MSI
>>> writes through the second-stage page table, so IRQ forwarding on them
>>> will need guest IMSIC pages mapped into the second-stage domain.
>>>
>>> Add an xarray to the MSI table that tracks the HPA installed for each
>>> mapped guest IMSIC GPA, and two helpers for use with the MSI table lock
>>> held: riscv_iommu_msi_table_map_gpa() installs the initial 4 KiB
>>> mapping, and riscv_iommu_msi_table_replace_gpa_leaf() atomically swaps
>>> the leaf PTE's PFN when a vCPU's VS-file host page moves, rejecting
>>> leaves that do not map the expected old HPA.
>>>
>>> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>>> ---
>>> drivers/iommu/riscv/iommu.c | 94 +++++++++++++++++++++++++++++++++++++
>>> drivers/iommu/riscv/iommu.h | 12 +++++
>>> 2 files changed, 106 insertions(+)
>>>
>>> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
>>> index e2e77469ea3c..48fc57d6409c 100644
>>> --- a/drivers/iommu/riscv/iommu.c
>>> +++ b/drivers/iommu/riscv/iommu.c
>>> @@ -24,6 +24,7 @@
>>> #include <linux/moduleparam.h>
>>> #include <linux/mutex.h>
>>> #include <linux/pci.h>
>>> +#include <linux/pgtable.h>
>>> #include <linux/generic_pt/iommu.h>
>>>
>>> #include "../dma-iommu.h"
>>> @@ -1217,6 +1218,92 @@ void riscv_iommu_msi_table_inval_all(struct riscv_iommu_msi_table *msi_table)
>>> riscv_iommu_iotlb_inval(domain, &gather);
>>> }
>>>
>>> +int riscv_iommu_msi_table_map_gpa(struct riscv_iommu_msi_table *msi_table,
>>> + dma_addr_t gpa, phys_addr_t hpa)
>>> +{
>>> + struct riscv_iommu_domain *domain =
>>> + container_of(msi_table, struct riscv_iommu_domain, msi_table);
>>> + const int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
>>> +
>>> + /* Guest IMSIC GPA mapping only exists in second-stage translations. */
>>> + if (!domain->gscid)
>>> + return -EOPNOTSUPP;
>>> +
>>> + return iommu_map(&domain->domain, gpa, hpa, IMSIC_MMIO_PAGE_SZ, prot,
>>> + GFP_ATOMIC);
>>> +}
>>> +
>>> +int riscv_iommu_msi_table_replace_gpa_leaf(struct riscv_iommu_msi_table *msi_table,
>>> + dma_addr_t gpa, phys_addr_t old_hpa,
>>> + phys_addr_t new_hpa)
>>
>>
>>This might fit better inside generic_pt rather than in the driver,
>>since it walks the page tables on its own and duplicates things
>>the library already owns, like the per-level index widths, the
>>x4 root size, and the PTE layout.
>>
>>I recently tried something very similar, and doing this inside
>>generic_pt turned out to be quite feasible (only a quick experiment,
>>not cleaned up for posting).
>>
>
> Hi Shuai,
>
> Thanks for the review. I agree the driver shouldn't carry format
> knowledge that belongs to the library, but I think this particular
> operation doesn't fit generic_pt.
>
> This is not a generic page-table operation. It is a special
> requirement of RISC-V IOMMU IRQ forwarding (irqbypass) on
> implementations without an MSI page table: the guest IMSIC GPA
> must be mapped into the second-stage domain, and when a vCPU's
> VS-file host page migrates, its leaf must be replaced atomically.
To be clear, I also meant updating the leaf in place,
not unmap + map.
> Because a device may write an MSI at any moment, unmap + map
> would fault. This operation is driven by the IRQ forwarding
> state machine, and because I don't see another generic_pt format
> sharing this flow, I think these helpers would be better placed
> outside generic_pt. I plan to move them into iommu-ir.c so the
> feature-scoped code stays together.
I'm fine with keeping it out of generic_pt. Respect your design.
Thanks,
Shuai
>
> Thanks,
> Fangyu
>
>>Thanks,
>>Shuai
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
@ 2026-10-10 15:33 ` Gong Shuai
0 siblings, 0 replies; 27+ messages in thread
From: Gong Shuai @ 2026-10-10 15:33 UTC (permalink / raw)
To: fangyu.yu, gong.shuai
Cc: alex, andrew.jones, aou, guoren, iommu, jgg, jgg, joro, kvm-riscv,
linux-kernel, linux-riscv, palmer, pjw, robin.murphy,
tomasz.jeznach, will, zong.li
Hi Fangyu,
On 10/10/2026 7:18 PM, fangyu.yu@linux.alibaba.com wrote:
>>Hi Fangyu,
>>
>>> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>>>
>>> IOMMU implementations without the MSI_FLAT capability translate MSI
>>> writes through the second-stage page table, so IRQ forwarding on them
>>> will need guest IMSIC pages mapped into the second-stage domain.
>>>
>>> Add an xarray to the MSI table that tracks the HPA installed for each
>>> mapped guest IMSIC GPA, and two helpers for use with the MSI table lock
>>> held: riscv_iommu_msi_table_map_gpa() installs the initial 4 KiB
>>> mapping, and riscv_iommu_msi_table_replace_gpa_leaf() atomically swaps
>>> the leaf PTE's PFN when a vCPU's VS-file host page moves, rejecting
>>> leaves that do not map the expected old HPA.
>>>
>>> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>>> ---
>>> drivers/iommu/riscv/iommu.c | 94 +++++++++++++++++++++++++++++++++++++
>>> drivers/iommu/riscv/iommu.h | 12 +++++
>>> 2 files changed, 106 insertions(+)
>>>
>>> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
>>> index e2e77469ea3c..48fc57d6409c 100644
>>> --- a/drivers/iommu/riscv/iommu.c
>>> +++ b/drivers/iommu/riscv/iommu.c
>>> @@ -24,6 +24,7 @@
>>> #include <linux/moduleparam.h>
>>> #include <linux/mutex.h>
>>> #include <linux/pci.h>
>>> +#include <linux/pgtable.h>
>>> #include <linux/generic_pt/iommu.h>
>>>
>>> #include "../dma-iommu.h"
>>> @@ -1217,6 +1218,92 @@ void riscv_iommu_msi_table_inval_all(struct riscv_iommu_msi_table *msi_table)
>>> riscv_iommu_iotlb_inval(domain, &gather);
>>> }
>>>
>>> +int riscv_iommu_msi_table_map_gpa(struct riscv_iommu_msi_table *msi_table,
>>> + dma_addr_t gpa, phys_addr_t hpa)
>>> +{
>>> + struct riscv_iommu_domain *domain =
>>> + container_of(msi_table, struct riscv_iommu_domain, msi_table);
>>> + const int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
>>> +
>>> + /* Guest IMSIC GPA mapping only exists in second-stage translations. */
>>> + if (!domain->gscid)
>>> + return -EOPNOTSUPP;
>>> +
>>> + return iommu_map(&domain->domain, gpa, hpa, IMSIC_MMIO_PAGE_SZ, prot,
>>> + GFP_ATOMIC);
>>> +}
>>> +
>>> +int riscv_iommu_msi_table_replace_gpa_leaf(struct riscv_iommu_msi_table *msi_table,
>>> + dma_addr_t gpa, phys_addr_t old_hpa,
>>> + phys_addr_t new_hpa)
>>
>>
>>This might fit better inside generic_pt rather than in the driver,
>>since it walks the page tables on its own and duplicates things
>>the library already owns, like the per-level index widths, the
>>x4 root size, and the PTE layout.
>>
>>I recently tried something very similar, and doing this inside
>>generic_pt turned out to be quite feasible (only a quick experiment,
>>not cleaned up for posting).
>>
>
> Hi Shuai,
>
> Thanks for the review. I agree the driver shouldn't carry format
> knowledge that belongs to the library, but I think this particular
> operation doesn't fit generic_pt.
>
> This is not a generic page-table operation. It is a special
> requirement of RISC-V IOMMU IRQ forwarding (irqbypass) on
> implementations without an MSI page table: the guest IMSIC GPA
> must be mapped into the second-stage domain, and when a vCPU's
> VS-file host page migrates, its leaf must be replaced atomically.
To be clear, I also meant updating the leaf in place,
not unmap + map.
> Because a device may write an MSI at any moment, unmap + map
> would fault. This operation is driven by the IRQ forwarding
> state machine, and because I don't see another generic_pt format
> sharing this flow, I think these helpers would be better placed
> outside generic_pt. I plan to move them into iommu-ir.c so the
> feature-scoped code stays together.
I'm fine with keeping it out of generic_pt. Respect your design.
Thanks,
Shuai
>
> Thanks,
> Fangyu
>
>>Thanks,
>>Shuai
^ permalink raw reply [flat|nested] 27+ messages in thread* Re: [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
@ 2026-10-10 15:33 ` Gong Shuai
0 siblings, 0 replies; 27+ messages in thread
From: Gong Shuai @ 2026-10-10 15:33 UTC (permalink / raw)
To: fangyu.yu, gong.shuai
Cc: alex, andrew.jones, aou, guoren, iommu, jgg, jgg, joro, kvm-riscv,
linux-kernel, linux-riscv, palmer, pjw, robin.murphy,
tomasz.jeznach, will, zong.li
Hi Fangyu,
On 10/10/2026 7:18 PM, fangyu.yu@linux.alibaba.com wrote:
>>Hi Fangyu,
>>
>>> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>>>
>>> IOMMU implementations without the MSI_FLAT capability translate MSI
>>> writes through the second-stage page table, so IRQ forwarding on them
>>> will need guest IMSIC pages mapped into the second-stage domain.
>>>
>>> Add an xarray to the MSI table that tracks the HPA installed for each
>>> mapped guest IMSIC GPA, and two helpers for use with the MSI table lock
>>> held: riscv_iommu_msi_table_map_gpa() installs the initial 4 KiB
>>> mapping, and riscv_iommu_msi_table_replace_gpa_leaf() atomically swaps
>>> the leaf PTE's PFN when a vCPU's VS-file host page moves, rejecting
>>> leaves that do not map the expected old HPA.
>>>
>>> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>>> ---
>>> drivers/iommu/riscv/iommu.c | 94 +++++++++++++++++++++++++++++++++++++
>>> drivers/iommu/riscv/iommu.h | 12 +++++
>>> 2 files changed, 106 insertions(+)
>>>
>>> diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
>>> index e2e77469ea3c..48fc57d6409c 100644
>>> --- a/drivers/iommu/riscv/iommu.c
>>> +++ b/drivers/iommu/riscv/iommu.c
>>> @@ -24,6 +24,7 @@
>>> #include <linux/moduleparam.h>
>>> #include <linux/mutex.h>
>>> #include <linux/pci.h>
>>> +#include <linux/pgtable.h>
>>> #include <linux/generic_pt/iommu.h>
>>>
>>> #include "../dma-iommu.h"
>>> @@ -1217,6 +1218,92 @@ void riscv_iommu_msi_table_inval_all(struct riscv_iommu_msi_table *msi_table)
>>> riscv_iommu_iotlb_inval(domain, &gather);
>>> }
>>>
>>> +int riscv_iommu_msi_table_map_gpa(struct riscv_iommu_msi_table *msi_table,
>>> + dma_addr_t gpa, phys_addr_t hpa)
>>> +{
>>> + struct riscv_iommu_domain *domain =
>>> + container_of(msi_table, struct riscv_iommu_domain, msi_table);
>>> + const int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
>>> +
>>> + /* Guest IMSIC GPA mapping only exists in second-stage translations. */
>>> + if (!domain->gscid)
>>> + return -EOPNOTSUPP;
>>> +
>>> + return iommu_map(&domain->domain, gpa, hpa, IMSIC_MMIO_PAGE_SZ, prot,
>>> + GFP_ATOMIC);
>>> +}
>>> +
>>> +int riscv_iommu_msi_table_replace_gpa_leaf(struct riscv_iommu_msi_table *msi_table,
>>> + dma_addr_t gpa, phys_addr_t old_hpa,
>>> + phys_addr_t new_hpa)
>>
>>
>>This might fit better inside generic_pt rather than in the driver,
>>since it walks the page tables on its own and duplicates things
>>the library already owns, like the per-level index widths, the
>>x4 root size, and the PTE layout.
>>
>>I recently tried something very similar, and doing this inside
>>generic_pt turned out to be quite feasible (only a quick experiment,
>>not cleaned up for posting).
>>
>
> Hi Shuai,
>
> Thanks for the review. I agree the driver shouldn't carry format
> knowledge that belongs to the library, but I think this particular
> operation doesn't fit generic_pt.
>
> This is not a generic page-table operation. It is a special
> requirement of RISC-V IOMMU IRQ forwarding (irqbypass) on
> implementations without an MSI page table: the guest IMSIC GPA
> must be mapped into the second-stage domain, and when a vCPU's
> VS-file host page migrates, its leaf must be replaced atomically.
To be clear, I also meant updating the leaf in place,
not unmap + map.
> Because a device may write an MSI at any moment, unmap + map
> would fault. This operation is driven by the IRQ forwarding
> state machine, and because I don't see another generic_pt format
> sharing this flow, I think these helpers would be better placed
> outside generic_pt. I plan to move them into iommu-ir.c so the
> feature-scoped code stays together.
I'm fine with keeping it out of generic_pt. Respect your design.
Thanks,
Shuai
>
> Thanks,
> Fangyu
>
>>Thanks,
>>Shuai
--
kvm-riscv mailing list
kvm-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kvm-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
2026-10-10 11:18 ` fangyu.yu
(?)
@ 2026-10-11 13:51 ` Jason Gunthorpe
-1 siblings, 0 replies; 27+ messages in thread
From: Jason Gunthorpe @ 2026-10-11 13:51 UTC (permalink / raw)
To: fangyu.yu
Cc: gong.shuai, alex, andrew.jones, aou, gsh517025, guoren, iommu,
joro, kvm-riscv, linux-kernel, linux-riscv, palmer, pjw,
robin.murphy, tomasz.jeznach, will, zong.li
On Sat, Oct 10, 2026 at 07:18:37PM +0800, fangyu.yu@linux.alibaba.com wrote:
> This is not a generic page-table operation. It is a special
> requirement of RISC-V IOMMU IRQ forwarding (irqbypass) on
> implementations without an MSI page table: the guest IMSIC GPA
> must be mapped into the second-stage domain, and when a vCPU's
> VS-file host page migrates, its leaf must be replaced atomically.
I think you can add an atomic PTE replace operation to generic_pt,
that should be fine.
Jason
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
@ 2026-10-11 13:51 ` Jason Gunthorpe
0 siblings, 0 replies; 27+ messages in thread
From: Jason Gunthorpe @ 2026-10-11 13:51 UTC (permalink / raw)
To: fangyu.yu
Cc: gong.shuai, alex, andrew.jones, aou, gsh517025, guoren, iommu,
joro, kvm-riscv, linux-kernel, linux-riscv, palmer, pjw,
robin.murphy, tomasz.jeznach, will, zong.li
On Sat, Oct 10, 2026 at 07:18:37PM +0800, fangyu.yu@linux.alibaba.com wrote:
> This is not a generic page-table operation. It is a special
> requirement of RISC-V IOMMU IRQ forwarding (irqbypass) on
> implementations without an MSI page table: the guest IMSIC GPA
> must be mapped into the second-stage domain, and when a vCPU's
> VS-file host page migrates, its leaf must be replaced atomically.
I think you can add an atomic PTE replace operation to generic_pt,
that should be fine.
Jason
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
@ 2026-10-11 13:51 ` Jason Gunthorpe
0 siblings, 0 replies; 27+ messages in thread
From: Jason Gunthorpe @ 2026-10-11 13:51 UTC (permalink / raw)
To: fangyu.yu
Cc: gong.shuai, alex, andrew.jones, aou, gsh517025, guoren, iommu,
joro, kvm-riscv, linux-kernel, linux-riscv, palmer, pjw,
robin.murphy, tomasz.jeznach, will, zong.li
On Sat, Oct 10, 2026 at 07:18:37PM +0800, fangyu.yu@linux.alibaba.com wrote:
> This is not a generic page-table operation. It is a special
> requirement of RISC-V IOMMU IRQ forwarding (irqbypass) on
> implementations without an MSI page table: the guest IMSIC GPA
> must be mapped into the second-stage domain, and when a vCPU's
> VS-file host page migrates, its leaf must be replaced atomically.
I think you can add an atomic PTE replace operation to generic_pt,
that should be fine.
Jason
--
kvm-riscv mailing list
kvm-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kvm-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
2026-10-11 13:51 ` Jason Gunthorpe
(?)
@ 2026-10-12 9:49 ` fangyu.yu
-1 siblings, 0 replies; 27+ messages in thread
From: fangyu.yu @ 2026-10-12 9:49 UTC (permalink / raw)
To: jgg
Cc: alex, andrew.jones, aou, fangyu.yu, gong.shuai, gsh517025, guoren,
iommu, joro, kvm-riscv, linux-kernel, linux-riscv, palmer, pjw,
robin.murphy, tomasz.jeznach, will, zong.li
>On Sat, Oct 10, 2026 at 07:18:37PM +0800, fangyu.yu@linux.alibaba.com wrote:
>> This is not a generic page-table operation. It is a special
>> requirement of RISC-V IOMMU IRQ forwarding (irqbypass) on
>> implementations without an MSI page table: the guest IMSIC GPA
>> must be mapped into the second-stage domain, and when a vCPU's
>> VS-file host page migrates, its leaf must be replaced atomically.
>
>I think you can add an atomic PTE replace operation to generic_pt,
>that should be fine.
Hi Jason,
Thanks for the suggestion. I plan to give this a try in v2 and move
the atomic PTE replacement logic into generic_pt.
CC Shuai, since he suggested the same earlier.
Thanks,
Fangyu
>
>Jason
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
@ 2026-10-12 9:49 ` fangyu.yu
0 siblings, 0 replies; 27+ messages in thread
From: fangyu.yu @ 2026-10-12 9:49 UTC (permalink / raw)
To: jgg
Cc: alex, andrew.jones, aou, fangyu.yu, gong.shuai, gsh517025, guoren,
iommu, joro, kvm-riscv, linux-kernel, linux-riscv, palmer, pjw,
robin.murphy, tomasz.jeznach, will, zong.li
>On Sat, Oct 10, 2026 at 07:18:37PM +0800, fangyu.yu@linux.alibaba.com wrote:
>> This is not a generic page-table operation. It is a special
>> requirement of RISC-V IOMMU IRQ forwarding (irqbypass) on
>> implementations without an MSI page table: the guest IMSIC GPA
>> must be mapped into the second-stage domain, and when a vCPU's
>> VS-file host page migrates, its leaf must be replaced atomically.
>
>I think you can add an atomic PTE replace operation to generic_pt,
>that should be fine.
Hi Jason,
Thanks for the suggestion. I plan to give this a try in v2 and move
the atomic PTE replacement logic into generic_pt.
CC Shuai, since he suggested the same earlier.
Thanks,
Fangyu
>
>Jason
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [RFC PATCH 1/3] iommu/riscv: Add guest IMSIC GPA mapping helpers
@ 2026-10-12 9:49 ` fangyu.yu
0 siblings, 0 replies; 27+ messages in thread
From: fangyu.yu @ 2026-10-12 9:49 UTC (permalink / raw)
To: jgg
Cc: alex, andrew.jones, aou, fangyu.yu, gong.shuai, gsh517025, guoren,
iommu, joro, kvm-riscv, linux-kernel, linux-riscv, palmer, pjw,
robin.murphy, tomasz.jeznach, will, zong.li
>On Sat, Oct 10, 2026 at 07:18:37PM +0800, fangyu.yu@linux.alibaba.com wrote:
>> This is not a generic page-table operation. It is a special
>> requirement of RISC-V IOMMU IRQ forwarding (irqbypass) on
>> implementations without an MSI page table: the guest IMSIC GPA
>> must be mapped into the second-stage domain, and when a vCPU's
>> VS-file host page migrates, its leaf must be replaced atomically.
>
>I think you can add an atomic PTE replace operation to generic_pt,
>that should be fine.
Hi Jason,
Thanks for the suggestion. I plan to give this a try in v2 and move
the atomic PTE replacement logic into generic_pt.
CC Shuai, since he suggested the same earlier.
Thanks,
Fangyu
>
>Jason
--
kvm-riscv mailing list
kvm-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kvm-riscv
^ permalink raw reply [flat|nested] 27+ messages in thread