* [RFC PATCH v3 01/10] iommupt: Add RISC-V Second-stage (iohgatp) page table support
2026-08-21 13:27 [RFC PATCH v3 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
@ 2026-08-21 13:27 ` fangyu.yu
2026-08-21 13:55 ` Jason Gunthorpe
2026-08-21 13:27 ` [RFC PATCH v3 02/10] iommupt: Add RISC-V dirty tracking PTE ops fangyu.yu
` (7 subsequent siblings)
8 siblings, 1 reply; 14+ messages in thread
From: fangyu.yu @ 2026-08-21 13:27 UTC (permalink / raw)
To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
kevin.tian, atish.patra, skhawaja, vasant.hegde
Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv
From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
Add support for Sv39x4/Sv48x4/Sv57x4 Second-stage page tables used by
the RISC-V IOMMU iohgatp register. The x4 root page table is 16 KiB
instead of the usual 4 KiB, covering 2 extra GPA bits (hw_max_vasz_lg2
= 41/50/59).
Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
---
drivers/iommu/generic_pt/fmt/iommu_riscv64.c | 2 +-
drivers/iommu/generic_pt/fmt/riscv.h | 77 +++++++++++++++++---
include/linux/generic_pt/common.h | 4 +
include/linux/generic_pt/iommu.h | 17 ++++-
4 files changed, 89 insertions(+), 11 deletions(-)
diff --git a/drivers/iommu/generic_pt/fmt/iommu_riscv64.c b/drivers/iommu/generic_pt/fmt/iommu_riscv64.c
index 1bb74114fb02..fa61839e5599 100644
--- a/drivers/iommu/generic_pt/fmt/iommu_riscv64.c
+++ b/drivers/iommu/generic_pt/fmt/iommu_riscv64.c
@@ -8,7 +8,7 @@
(BIT(PT_FEAT_SIGN_EXTEND) | BIT(PT_FEAT_FLUSH_RANGE) | \
BIT(PT_FEAT_RISCV_SVNAPOT_64K) | \
BIT(PT_FEAT_DETAILED_GATHER) | \
- BIT(PT_FEAT_RISCV_SVPBMT))
+ BIT(PT_FEAT_RISCV_SVPBMT) | BIT(PT_FEAT_RISCV_S2))
#define PT_FORCE_ENABLED_FEATURES BIT(PT_FEAT_DETAILED_GATHER)
#include "iommu_template.h"
diff --git a/drivers/iommu/generic_pt/fmt/riscv.h b/drivers/iommu/generic_pt/fmt/riscv.h
index ae9a76514416..5692c033dbde 100644
--- a/drivers/iommu/generic_pt/fmt/riscv.h
+++ b/drivers/iommu/generic_pt/fmt/riscv.h
@@ -37,7 +37,16 @@ enum {
PT_MAX_OUTPUT_ADDRESS_LG2 = 34,
PT_MAX_TOP_LEVEL = 1,
#else
- PT_MAX_VA_ADDRESS_LG2 = 57,
+ /*
+ * PT_MAX_VA_ADDRESS_LG2 is the upper bound accepted by the generic
+ * pt_iommu_init() range check. It must cover both first-stage and
+ * second-stage (G-stage) modes:
+ *
+ * First-stage (fsc/iosatp): Sv39=39, Sv48=48, Sv57=57
+ * Second-stage (iohgatp): Sv39x4=41, Sv48x4=50, Sv57x4=59
+ *
+ */
+ PT_MAX_VA_ADDRESS_LG2 = 59,
PT_MAX_OUTPUT_ADDRESS_LG2 = 56,
PT_MAX_TOP_LEVEL = 4,
#endif
@@ -126,6 +135,15 @@ riscvpt_entry_num_contig_lg2(const struct pt_state *pts)
static inline unsigned int riscvpt_num_items_lg2(const struct pt_state *pts)
{
+ /*
+ * Second-stage (iohgatp) root page tables have 4x the usual number of
+ * entries (2048 = 2^11 instead of 512 = 2^9) to cover the 2 extra GPA
+ * bits in Sv39x4/Sv48x4/Sv57x4. Only the root (top) level is
+ * enlarged; all other levels remain at the standard 9-bit index width.
+ */
+ if (pts_feature(pts, PT_FEAT_RISCV_S2) &&
+ pts->level == pts->range->top_level)
+ return PT_TABLEMEM_LG2SZ - ilog2(sizeof(u64)) + 2;
return PT_TABLEMEM_LG2SZ - ilog2(sizeof(u64));
}
#define pt_num_items_lg2 riscvpt_num_items_lg2
@@ -263,6 +281,7 @@ riscvpt_iommu_fmt_init(struct pt_iommu_riscv_64 *iommu_table,
struct pt_riscv *table = &iommu_table->riscv_64pt;
switch (cfg->common.hw_max_vasz_lg2) {
+ /* First-stage (fsc/iosatp): Sv39 / Sv48 / Sv57 */
case 39:
pt_top_set_level(&table->common, 2);
break;
@@ -272,6 +291,19 @@ riscvpt_iommu_fmt_init(struct pt_iommu_riscv_64 *iommu_table,
case 57:
pt_top_set_level(&table->common, 4);
break;
+ /*
+ * Second-stage (iohgatp): Sv39x4 / Sv48x4 / Sv57x4.
+ * The top level is the same as for the first-stage counterpart.
+ */
+ case 41:
+ pt_top_set_level(&table->common, 2);
+ break;
+ case 50:
+ pt_top_set_level(&table->common, 3);
+ break;
+ case 59:
+ pt_top_set_level(&table->common, 4);
+ break;
default:
return -EINVAL;
}
@@ -292,10 +324,17 @@ riscvpt_iommu_fmt_hw_info(struct pt_iommu_riscv_64 *table,
PT_WARN_ON(top_phys & ~PT_TOP_PHYS_MASK);
/*
- * See Table 3. Encodings of iosatp.MODE field" for DC.tx.SXL = 0:
- * 8 = Sv39 = top level 2
- * 9 = Sv38 = top level 3
- * 10 = Sv57 = top level 4
+ * Both first-stage (fsc/iosatp) and second-stage (iohgatp) share the
+ * same MODE numeric values for a given top level:
+ * top_level 2 -> MODE 8 (Sv39 / Sv39x4)
+ * top_level 3 -> MODE 9 (Sv48 / Sv48x4)
+ * top_level 4 -> MODE 10 (Sv57 / Sv57x4)
+ *
+ * The union members fsc_iosatp_mode and iohgatp_mode occupy the same
+ * byte; the caller selects the appropriate name based on domain type.
+ *
+ * See "Table 3. Encodings of iosatp.MODE field" (DC.tc.SXL = 0) and
+ * "Table 2. Encoding of iohgatp.MODE field" in the RISC-V IOMMU spec.
*/
info->fsc_iosatp_mode = top_range->top_level + 6;
}
@@ -303,19 +342,39 @@ riscvpt_iommu_fmt_hw_info(struct pt_iommu_riscv_64 *table,
#if defined(GENERIC_PT_KUNIT)
static const struct pt_iommu_riscv_64_cfg riscv_64_kunit_fmt_cfgs[] = {
- [0] = { .common.features = BIT(PT_FEAT_RISCV_SVNAPOT_64K),
+ /* First-stage (fsc/iosatp): Sv39 / Sv48 / Sv57 */
+ [0] = { .common.features = BIT(PT_FEAT_SIGN_EXTEND) |
+ BIT(PT_FEAT_RISCV_SVNAPOT_64K),
.common.hw_max_oasz_lg2 = 56,
.common.hw_max_vasz_lg2 = 39 },
- [1] = { .common.features = 0,
+ [1] = { .common.features = BIT(PT_FEAT_SIGN_EXTEND),
.common.hw_max_oasz_lg2 = 56,
.common.hw_max_vasz_lg2 = 48 },
- [2] = { .common.features = BIT(PT_FEAT_RISCV_SVNAPOT_64K),
+ [2] = { .common.features = BIT(PT_FEAT_SIGN_EXTEND) |
+ BIT(PT_FEAT_RISCV_SVNAPOT_64K),
.common.hw_max_oasz_lg2 = 56,
.common.hw_max_vasz_lg2 = 57 },
+ /*
+ * Second-stage (iohgatp): Sv39x4 / Sv48x4 / Sv57x4.
+ */
+ [3] = { .common.features = BIT(PT_FEAT_RISCV_S2) |
+ BIT(PT_FEAT_RISCV_SVNAPOT_64K),
+ .common.hw_max_oasz_lg2 = 56,
+ .common.hw_max_vasz_lg2 = 41 },
+ [4] = { .common.features = BIT(PT_FEAT_RISCV_S2),
+ .common.hw_max_oasz_lg2 = 56,
+ .common.hw_max_vasz_lg2 = 50 },
+ [5] = { .common.features = BIT(PT_FEAT_RISCV_S2) |
+ BIT(PT_FEAT_RISCV_SVNAPOT_64K),
+ .common.hw_max_oasz_lg2 = 56,
+ .common.hw_max_vasz_lg2 = 59 },
};
#define kunit_fmt_cfgs riscv_64_kunit_fmt_cfgs
+/* Sign extension is only valid for first-stage configs, not S2. */
enum {
- KUNIT_FMT_FEATURES = BIT(PT_FEAT_RISCV_SVNAPOT_64K),
+ KUNIT_FMT_FEATURES = BIT(PT_FEAT_SIGN_EXTEND) |
+ BIT(PT_FEAT_RISCV_SVNAPOT_64K) |
+ BIT(PT_FEAT_RISCV_S2),
};
#endif
diff --git a/include/linux/generic_pt/common.h b/include/linux/generic_pt/common.h
index 07ef1c8341a4..f2e130c2b804 100644
--- a/include/linux/generic_pt/common.h
+++ b/include/linux/generic_pt/common.h
@@ -197,6 +197,10 @@ enum {
* Support Svpbmt extension: encode page-based memory type (PBMT) in PTEs.
*/
PT_FEAT_RISCV_SVPBMT,
+ /*
+ * Using second-stage / iohgatp address translation.
+ */
+ PT_FEAT_RISCV_S2,
};
diff --git a/include/linux/generic_pt/iommu.h b/include/linux/generic_pt/iommu.h
index dd0edd02a48a..f27d229ff318 100644
--- a/include/linux/generic_pt/iommu.h
+++ b/include/linux/generic_pt/iommu.h
@@ -328,7 +328,22 @@ struct pt_iommu_riscv_64_cfg {
struct pt_iommu_riscv_64_hw_info {
u64 ppn;
- u8 fsc_iosatp_mode;
+ union {
+ /*
+ * First-stage (fsc/iosatp) MODE encoding:
+ * 8 = Sv39, 9 = Sv48, 10 = Sv57
+ * Used to program DC.fsc.iosatp.MODE.
+ */
+ u8 fsc_iosatp_mode;
+ /*
+ * Second-stage (iohgatp) MODE encoding:
+ * 8 = Sv39x4, 9 = Sv48x4, 10 = Sv57x4
+ * Used to program DC.iohgatp.MODE.
+ * The numeric values are identical to fsc_iosatp_mode;
+ * the caller selects the interpretation based on domain type.
+ */
+ u8 iohgatp_mode;
+ };
};
IOMMU_FORMAT(riscv_64, riscv_64pt);
--
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] 14+ messages in thread* Re: [RFC PATCH v3 01/10] iommupt: Add RISC-V Second-stage (iohgatp) page table support
2026-08-21 13:27 ` [RFC PATCH v3 01/10] iommupt: Add RISC-V Second-stage (iohgatp) page table support fangyu.yu
@ 2026-08-21 13:55 ` Jason Gunthorpe
2026-08-22 14:55 ` fangyu.yu
0 siblings, 1 reply; 14+ messages in thread
From: Jason Gunthorpe @ 2026-08-21 13:55 UTC (permalink / raw)
To: fangyu.yu
Cc: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
baolu.lu, jroedel, zong.li, andrew.jones, anup, kevin.tian,
atish.patra, skhawaja, vasant.hegde, guoren, iommu, linux-kernel,
linux-riscv
On Fri, Aug 21, 2026 at 09:27:41PM +0800, fangyu.yu@linux.alibaba.com wrote:
> @@ -126,6 +135,15 @@ riscvpt_entry_num_contig_lg2(const struct pt_state *pts)
>
> static inline unsigned int riscvpt_num_items_lg2(const struct pt_state *pts)
> {
> + /*
> + * Second-stage (iohgatp) root page tables have 4x the usual number of
> + * entries (2048 = 2^11 instead of 512 = 2^9) to cover the 2 extra GPA
> + * bits in Sv39x4/Sv48x4/Sv57x4. Only the root (top) level is
> + * enlarged; all other levels remain at the standard 9-bit index width.
> + */
> + if (pts_feature(pts, PT_FEAT_RISCV_S2) &&
> + pts->level == pts->range->top_level)
> + return PT_TABLEMEM_LG2SZ - ilog2(sizeof(u64)) + 2;
You shouldn't need this, see how ARMv8 is constructed which does the
same thing. Instead
static inline unsigned int armv8pt_num_items_lg2(const struct pt_state *pts)
{
/*
* It is not allowed to call pt_num_items_lg2() at the top level, this
* API restriction is specifically an optimization avoid overheads
* dealing with concatenated tables here.
*/
PT_WARN_ON(pts->level == pts->range->top_level);
> @@ -272,6 +291,19 @@ riscvpt_iommu_fmt_init(struct pt_iommu_riscv_64 *iommu_table,
> case 57:
> pt_top_set_level(&table->common, 4);
> break;
> + /*
> + * Second-stage (iohgatp): Sv39x4 / Sv48x4 / Sv57x4.
> + * The top level is the same as for the first-stage counterpart.
> + */
> + case 41:
> + pt_top_set_level(&table->common, 2);
> + break;
> + case 50:
> + pt_top_set_level(&table->common, 3);
> + break;
> + case 59:
> + pt_top_set_level(&table->common, 4);
> + break;
Are all these widths valid for both S1 and S2? It should reject
illegal vasz..
> struct pt_iommu_riscv_64_hw_info {
> u64 ppn;
> - u8 fsc_iosatp_mode;
> + union {
> + /*
> + * First-stage (fsc/iosatp) MODE encoding:
> + * 8 = Sv39, 9 = Sv48, 10 = Sv57
> + * Used to program DC.fsc.iosatp.MODE.
> + */
> + u8 fsc_iosatp_mode;
> + /*
> + * Second-stage (iohgatp) MODE encoding:
> + * 8 = Sv39x4, 9 = Sv48x4, 10 = Sv57x4
> + * Used to program DC.iohgatp.MODE.
> + * The numeric values are identical to fsc_iosatp_mode;
> + * the caller selects the interpretation based on domain type.
> + */
> + u8 iohgatp_mode;
> + };
suggest not using a union and instead have the get_info populate only
the one correct for the fmt. It makes it slightly more robust that
s1/s2 don't get intermixed improperly?
Jason
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [RFC PATCH v3 01/10] iommupt: Add RISC-V Second-stage (iohgatp) page table support
2026-08-21 13:55 ` Jason Gunthorpe
@ 2026-08-22 14:55 ` fangyu.yu
0 siblings, 0 replies; 14+ messages in thread
From: fangyu.yu @ 2026-08-22 14:55 UTC (permalink / raw)
To: jgg
Cc: alex, andrew.jones, anup, aou, atish.patra, baolu.lu, fangyu.yu,
guoren, iommu, joro, jroedel, kevin.tian, linux-kernel,
linux-riscv, palmer, pjw, robin.murphy, skhawaja, tomasz.jeznach,
vasant.hegde, will, zong.li
>> @@ -126,6 +135,15 @@ riscvpt_entry_num_contig_lg2(const struct pt_state *pts)
>>
>> static inline unsigned int riscvpt_num_items_lg2(const struct pt_state *pts)
>> {
>> + /*
>> + * Second-stage (iohgatp) root page tables have 4x the usual number of
>> + * entries (2048 = 2^11 instead of 512 = 2^9) to cover the 2 extra GPA
>> + * bits in Sv39x4/Sv48x4/Sv57x4. Only the root (top) level is
>> + * enlarged; all other levels remain at the standard 9-bit index width.
>> + */
>> + if (pts_feature(pts, PT_FEAT_RISCV_S2) &&
>> + pts->level == pts->range->top_level)
>> + return PT_TABLEMEM_LG2SZ - ilog2(sizeof(u64)) + 2;
>
>You shouldn't need this, see how ARMv8 is constructed which does the
>same thing. Instead
>
>static inline unsigned int armv8pt_num_items_lg2(const struct pt_state *pts)
>{
> /*
> * It is not allowed to call pt_num_items_lg2() at the top level, this
> * API restriction is specifically an optimization avoid overheads
> * dealing with concatenated tables here.
> */
> PT_WARN_ON(pts->level == pts->range->top_level);
>
Thanks for pointing at ARMv8 for comparison. I hadn't noticed your
newly posted patch [1], Now that I've looked at it, I agree
armv8pt_num_items_lg2() is the right way to implement this and
will follow that pattern for RISC-V in the next version.
>> @@ -272,6 +291,19 @@ riscvpt_iommu_fmt_init(struct pt_iommu_riscv_64 *iommu_table,
>> case 57:
>> pt_top_set_level(&table->common, 4);
>> break;
>> + /*
>> + * Second-stage (iohgatp): Sv39x4 / Sv48x4 / Sv57x4.
>> + * The top level is the same as for the first-stage counterpart.
>> + */
>> + case 41:
>> + pt_top_set_level(&table->common, 2);
>> + break;
>> + case 50:
>> + pt_top_set_level(&table->common, 3);
>> + break;
>> + case 59:
>> + pt_top_set_level(&table->common, 4);
>> + break;
>
>Are all these widths valid for both S1 and S2? It should reject
>illegal vasz..
>
Agreed, will fix. 39/48/57 are only valid for S1 and 41/50/59 are only
valid for S2 -- I'll gate each group on PT_FEAT_RISCV_S2 and reject the
other combination.
>> struct pt_iommu_riscv_64_hw_info {
>> u64 ppn;
>> - u8 fsc_iosatp_mode;
>> + union {
>> + /*
>> + * First-stage (fsc/iosatp) MODE encoding:
>> + * 8 = Sv39, 9 = Sv48, 10 = Sv57
>> + * Used to program DC.fsc.iosatp.MODE.
>> + */
>> + u8 fsc_iosatp_mode;
>> + /*
>> + * Second-stage (iohgatp) MODE encoding:
>> + * 8 = Sv39x4, 9 = Sv48x4, 10 = Sv57x4
>> + * Used to program DC.iohgatp.MODE.
>> + * The numeric values are identical to fsc_iosatp_mode;
>> + * the caller selects the interpretation based on domain type.
>> + */
>> + u8 iohgatp_mode;
>> + };
>
>suggest not using a union and instead have the get_info populate only
>the one correct for the fmt. It makes it slightly more robust that
>s1/s2 don't get intermixed improperly?
>
Makes sense, will drop the union in favor of two separate fields and
have riscvpt_iommu_fmt_hw_info() only fill in the one that matches the
format's stage.
Thanks,
Fangyu
>Jason
[1] https://lore.kernel.org/linux-iommu/0-v2-563ee63886f0+1209-iommupt_armv8_jgg@nvidia.com/
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 14+ messages in thread
* [RFC PATCH v3 02/10] iommupt: Add RISC-V dirty tracking PTE ops
2026-08-21 13:27 [RFC PATCH v3 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
2026-08-21 13:27 ` [RFC PATCH v3 01/10] iommupt: Add RISC-V Second-stage (iohgatp) page table support fangyu.yu
@ 2026-08-21 13:27 ` fangyu.yu
2026-08-21 14:03 ` Jason Gunthorpe
2026-08-21 13:27 ` [RFC PATCH v3 03/10] iommu/riscv: report iommu capabilities fangyu.yu
` (6 subsequent siblings)
8 siblings, 1 reply; 14+ messages in thread
From: fangyu.yu @ 2026-08-21 13:27 UTC (permalink / raw)
To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
kevin.tian, atish.patra, skhawaja, vasant.hegde
Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv
From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
Implement the three dirty-tracking hooks required by the generic page
table framework for the RISC-V format:
pt_entry_is_write_dirty():
Check the D bit (bit 7) in the PTE.
pt_entry_make_write_clean():
Clear the D bit across the full contiguous range.
pt_entry_make_write_dirty():
Atomically set D via try_cmpxchg64() on a single PTE.
Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
---
drivers/iommu/generic_pt/fmt/riscv.h | 43 ++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/drivers/iommu/generic_pt/fmt/riscv.h b/drivers/iommu/generic_pt/fmt/riscv.h
index 5692c033dbde..09581798e753 100644
--- a/drivers/iommu/generic_pt/fmt/riscv.h
+++ b/drivers/iommu/generic_pt/fmt/riscv.h
@@ -226,6 +226,49 @@ static inline void riscvpt_attr_from_entry(const struct pt_state *pts,
}
#define pt_attr_from_entry riscvpt_attr_from_entry
+/*
+ * Dirty tracking: RISC-V PTEs use D (bit 7) as the hardware dirty bit.
+ * When Svnapot 64K is active a leaf entry spans 16 consecutive PTEs; we
+ * must check / clear all of them so that no dirty indication is lost.
+ */
+static inline bool riscvpt_entry_is_write_dirty(const struct pt_state *pts)
+{
+ unsigned int num_contig_lg2 = riscvpt_entry_num_contig_lg2(pts);
+ const pt_riscv_entry_t *tablep =
+ pt_cur_table(pts, pt_riscv_entry_t) +
+ log2_set_mod(pts->index, 0, num_contig_lg2);
+ const pt_riscv_entry_t *end = tablep + log2_to_int(num_contig_lg2);
+
+ for (; tablep != end; tablep++)
+ if (READ_ONCE(*tablep) & RISCVPT_D)
+ return true;
+ return false;
+}
+#define pt_entry_is_write_dirty riscvpt_entry_is_write_dirty
+
+static inline void riscvpt_entry_make_write_clean(struct pt_state *pts)
+{
+ unsigned int num_contig_lg2 = riscvpt_entry_num_contig_lg2(pts);
+ pt_riscv_entry_t *tablep =
+ pt_cur_table(pts, pt_riscv_entry_t) +
+ log2_set_mod(pts->index, 0, num_contig_lg2);
+ pt_riscv_entry_t *end = tablep + log2_to_int(num_contig_lg2);
+
+ for (; tablep != end; tablep++)
+ WRITE_ONCE(*tablep, READ_ONCE(*tablep) & ~(pt_riscv_entry_t)RISCVPT_D);
+}
+#define pt_entry_make_write_clean riscvpt_entry_make_write_clean
+
+static inline bool riscvpt_entry_make_write_dirty(struct pt_state *pts)
+{
+ pt_riscv_entry_t *tablep =
+ pt_cur_table(pts, pt_riscv_entry_t) + pts->index;
+ pt_riscv_entry_t new = pts->entry | RISCVPT_D;
+
+ return try_cmpxchg64(tablep, &pts->entry, new);
+}
+#define pt_entry_make_write_dirty riscvpt_entry_make_write_dirty
+
/* --- iommu */
#include <linux/generic_pt/iommu.h>
#include <linux/iommu.h>
--
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] 14+ messages in thread* Re: [RFC PATCH v3 02/10] iommupt: Add RISC-V dirty tracking PTE ops
2026-08-21 13:27 ` [RFC PATCH v3 02/10] iommupt: Add RISC-V dirty tracking PTE ops fangyu.yu
@ 2026-08-21 14:03 ` Jason Gunthorpe
2026-08-22 15:01 ` fangyu.yu
0 siblings, 1 reply; 14+ messages in thread
From: Jason Gunthorpe @ 2026-08-21 14:03 UTC (permalink / raw)
To: fangyu.yu
Cc: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
baolu.lu, jroedel, zong.li, andrew.jones, anup, kevin.tian,
atish.patra, skhawaja, vasant.hegde, guoren, iommu, linux-kernel,
linux-riscv
On Fri, Aug 21, 2026 at 09:27:42PM +0800, fangyu.yu@linux.alibaba.com wrote:
> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>
> Implement the three dirty-tracking hooks required by the generic page
> table framework for the RISC-V format:
>
> pt_entry_is_write_dirty():
> Check the D bit (bit 7) in the PTE.
>
> pt_entry_make_write_clean():
> Clear the D bit across the full contiguous range.
>
> pt_entry_make_write_dirty():
> Atomically set D via try_cmpxchg64() on a single PTE.
>
> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
> ---
> drivers/iommu/generic_pt/fmt/riscv.h | 43 ++++++++++++++++++++++++++++
> 1 file changed, 43 insertions(+)
>
> diff --git a/drivers/iommu/generic_pt/fmt/riscv.h b/drivers/iommu/generic_pt/fmt/riscv.h
> index 5692c033dbde..09581798e753 100644
> --- a/drivers/iommu/generic_pt/fmt/riscv.h
> +++ b/drivers/iommu/generic_pt/fmt/riscv.h
> @@ -226,6 +226,49 @@ static inline void riscvpt_attr_from_entry(const struct pt_state *pts,
> }
> #define pt_attr_from_entry riscvpt_attr_from_entry
Does this work for all stages or does it need pt_dirty_supported ?
Jason
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [RFC PATCH v3 02/10] iommupt: Add RISC-V dirty tracking PTE ops
2026-08-21 14:03 ` Jason Gunthorpe
@ 2026-08-22 15:01 ` fangyu.yu
0 siblings, 0 replies; 14+ messages in thread
From: fangyu.yu @ 2026-08-22 15:01 UTC (permalink / raw)
To: jgg
Cc: alex, andrew.jones, anup, aou, atish.patra, baolu.lu, fangyu.yu,
guoren, iommu, joro, jroedel, kevin.tian, linux-kernel,
linux-riscv, palmer, pjw, robin.murphy, skhawaja, tomasz.jeznach,
vasant.hegde, will, zong.li
>> From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>>
>> Implement the three dirty-tracking hooks required by the generic page
>> table framework for the RISC-V format:
>>
>> pt_entry_is_write_dirty():
>> Check the D bit (bit 7) in the PTE.
>>
>> pt_entry_make_write_clean():
>> Clear the D bit across the full contiguous range.
>>
>> pt_entry_make_write_dirty():
>> Atomically set D via try_cmpxchg64() on a single PTE.
>>
>> Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>> ---
>> drivers/iommu/generic_pt/fmt/riscv.h | 43 ++++++++++++++++++++++++++++
>> 1 file changed, 43 insertions(+)
>>
>> diff --git a/drivers/iommu/generic_pt/fmt/riscv.h b/drivers/iommu/generic_pt/fmt/riscv.h
>> index 5692c033dbde..09581798e753 100644
>> --- a/drivers/iommu/generic_pt/fmt/riscv.h
>> +++ b/drivers/iommu/generic_pt/fmt/riscv.h
>> @@ -226,6 +226,49 @@ static inline void riscvpt_attr_from_entry(const struct pt_state *pts,
>> }
>> #define pt_attr_from_entry riscvpt_attr_from_entry
>
>Does this work for all stages or does it need pt_dirty_supported ?
Good catch, no it doesn't. This patch didn't add a pt_dirty_supported(),
so it falls back to the default "always true", which isn't right --
iommu.c only sets TC.GADE and wires up dirty_ops for second-stage
domains, first-stage never gets TC.SADE set.
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] 14+ messages in thread
* [RFC PATCH v3 03/10] iommu/riscv: report iommu capabilities
2026-08-21 13:27 [RFC PATCH v3 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
2026-08-21 13:27 ` [RFC PATCH v3 01/10] iommupt: Add RISC-V Second-stage (iohgatp) page table support fangyu.yu
2026-08-21 13:27 ` [RFC PATCH v3 02/10] iommupt: Add RISC-V dirty tracking PTE ops fangyu.yu
@ 2026-08-21 13:27 ` fangyu.yu
2026-08-21 13:27 ` [RFC PATCH v3 04/10] iommu/riscv: use data structure instead of individual values fangyu.yu
` (5 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: fangyu.yu @ 2026-08-21 13:27 UTC (permalink / raw)
To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
kevin.tian, atish.patra, skhawaja, vasant.hegde
Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv,
Tomasz Jeznach
From: Tomasz Jeznach <tjeznach@rivosinc.com>
Report RISC-V IOMMU capabilities required by VFIO subsystem
to enable PCIe device assignment.
Signed-off-by: Tomasz Jeznach <tjeznach@rivosinc.com>
Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
---
drivers/iommu/riscv/iommu.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index cec3ddd7ab10..a6c8307e82ea 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1422,6 +1422,16 @@ static struct iommu_group *riscv_iommu_device_group(struct device *dev)
return generic_device_group(dev);
}
+static bool riscv_iommu_capable(struct device *dev, enum iommu_cap cap)
+{
+ switch (cap) {
+ case IOMMU_CAP_CACHE_COHERENCY:
+ return true;
+ default:
+ return false;
+ }
+}
+
static int riscv_iommu_of_xlate(struct device *dev, const struct of_phandle_args *args)
{
return iommu_fwspec_add_ids(dev, args->args, 1);
@@ -1483,6 +1493,7 @@ static void riscv_iommu_release_device(struct device *dev)
static const struct iommu_ops riscv_iommu_ops = {
.of_xlate = riscv_iommu_of_xlate,
+ .capable = riscv_iommu_capable,
.identity_domain = &riscv_iommu_identity_domain,
.blocked_domain = &riscv_iommu_blocking_domain,
.release_domain = &riscv_iommu_blocking_domain,
--
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] 14+ messages in thread* [RFC PATCH v3 04/10] iommu/riscv: use data structure instead of individual values
2026-08-21 13:27 [RFC PATCH v3 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
` (2 preceding siblings ...)
2026-08-21 13:27 ` [RFC PATCH v3 03/10] iommu/riscv: report iommu capabilities fangyu.yu
@ 2026-08-21 13:27 ` fangyu.yu
2026-08-21 13:27 ` [RFC PATCH v3 05/10] iommu/riscv: support GSCID and GVMA invalidation command fangyu.yu
` (4 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: fangyu.yu @ 2026-08-21 13:27 UTC (permalink / raw)
To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
kevin.tian, atish.patra, skhawaja, vasant.hegde
Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv
From: Zong Li <zong.li@sifive.com>
The parameter will be increased when we need to set up more
bit fields in the device context. Use a data structure to
wrap them up.
Signed-off-by: Zong Li <zong.li@sifive.com>
Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
---
drivers/iommu/riscv/iommu.c | 27 +++++++++++++++++----------
1 file changed, 17 insertions(+), 10 deletions(-)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index a6c8307e82ea..c5b6214ee672 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1165,7 +1165,7 @@ static void riscv_iommu_iodir_iotinval(struct riscv_iommu_device *iommu,
* interim translation faults.
*/
static void riscv_iommu_iodir_update(struct riscv_iommu_device *iommu,
- struct device *dev, u64 fsc, u64 ta)
+ struct device *dev, struct riscv_iommu_dc *new_dc)
{
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
struct riscv_iommu_dc *dc;
@@ -1204,10 +1204,10 @@ static void riscv_iommu_iodir_update(struct riscv_iommu_device *iommu,
for (i = 0; i < fwspec->num_ids; i++) {
dc = riscv_iommu_get_dc(iommu, fwspec->ids[i]);
tc = READ_ONCE(dc->tc);
- tc |= ta & RISCV_IOMMU_DC_TC_V;
+ tc |= new_dc->ta & RISCV_IOMMU_DC_TC_V;
- WRITE_ONCE(dc->fsc, fsc);
- WRITE_ONCE(dc->ta, ta & RISCV_IOMMU_PC_TA_PSCID);
+ WRITE_ONCE(dc->fsc, new_dc->fsc);
+ WRITE_ONCE(dc->ta, new_dc->ta & RISCV_IOMMU_PC_TA_PSCID);
/* Update device context, write TC.V as the last step. */
dma_wmb();
WRITE_ONCE(dc->tc, tc);
@@ -1288,22 +1288,22 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
struct riscv_iommu_device *iommu = dev_to_iommu(dev);
struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
struct pt_iommu_riscv_64_hw_info pt_info;
- u64 fsc, ta;
+ struct riscv_iommu_dc dc = {0};
pt_iommu_riscv_64_hw_info(&domain->riscvpt, &pt_info);
if (!riscv_iommu_pt_supported(iommu, pt_info.fsc_iosatp_mode))
return -ENODEV;
- fsc = FIELD_PREP(RISCV_IOMMU_PC_FSC_MODE, pt_info.fsc_iosatp_mode) |
+ dc.fsc = FIELD_PREP(RISCV_IOMMU_PC_FSC_MODE, pt_info.fsc_iosatp_mode) |
FIELD_PREP(RISCV_IOMMU_PC_FSC_PPN, pt_info.ppn);
- ta = FIELD_PREP(RISCV_IOMMU_PC_TA_PSCID, domain->pscid) |
+ dc.ta = FIELD_PREP(RISCV_IOMMU_PC_TA_PSCID, domain->pscid) |
RISCV_IOMMU_PC_TA_V;
if (riscv_iommu_bond_link(domain, dev))
return -ENOMEM;
- riscv_iommu_iodir_update(iommu, dev, fsc, ta);
+ riscv_iommu_iodir_update(iommu, dev, &dc);
riscv_iommu_bond_unlink(info->domain, dev);
info->domain = domain;
@@ -1378,9 +1378,12 @@ static int riscv_iommu_attach_blocking_domain(struct iommu_domain *iommu_domain,
{
struct riscv_iommu_device *iommu = dev_to_iommu(dev);
struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
+ struct riscv_iommu_dc dc = {0};
+
+ dc.fsc = RISCV_IOMMU_FSC_BARE;
/* Make device context invalid, translation requests will fault w/ #258 */
- riscv_iommu_iodir_update(iommu, dev, RISCV_IOMMU_FSC_BARE, 0);
+ riscv_iommu_iodir_update(iommu, dev, &dc);
riscv_iommu_bond_unlink(info->domain, dev);
info->domain = NULL;
@@ -1400,8 +1403,12 @@ static int riscv_iommu_attach_identity_domain(struct iommu_domain *iommu_domain,
{
struct riscv_iommu_device *iommu = dev_to_iommu(dev);
struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
+ struct riscv_iommu_dc dc = {0};
+
+ dc.fsc = RISCV_IOMMU_FSC_BARE;
+ dc.ta = RISCV_IOMMU_PC_TA_V;
- riscv_iommu_iodir_update(iommu, dev, RISCV_IOMMU_FSC_BARE, RISCV_IOMMU_PC_TA_V);
+ riscv_iommu_iodir_update(iommu, dev, &dc);
riscv_iommu_bond_unlink(info->domain, dev);
info->domain = NULL;
--
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] 14+ messages in thread* [RFC PATCH v3 05/10] iommu/riscv: support GSCID and GVMA invalidation command
2026-08-21 13:27 [RFC PATCH v3 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
` (3 preceding siblings ...)
2026-08-21 13:27 ` [RFC PATCH v3 04/10] iommu/riscv: use data structure instead of individual values fangyu.yu
@ 2026-08-21 13:27 ` fangyu.yu
2026-08-21 13:27 ` [RFC PATCH v3 06/10] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch fangyu.yu
` (3 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: fangyu.yu @ 2026-08-21 13:27 UTC (permalink / raw)
To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
kevin.tian, atish.patra, skhawaja, vasant.hegde
Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv
From: Zong Li <zong.li@sifive.com>
This patch adds a ID Allocator for GSCID and a wrap for setting up
GSCID in IOTLB invalidation command.
Set up iohgatp to enable second stage table and flush stage-2 table if
the GSCID is set.
The GSCID of domain should be freed when release domain. GSCID will be
allocated for parent domain in nested IOMMU process.
Signed-off-by: Zong Li <zong.li@sifive.com>
Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
---
drivers/iommu/riscv/iommu-bits.h | 7 ++++
drivers/iommu/riscv/iommu.c | 62 ++++++++++++++++++++++++++------
2 files changed, 58 insertions(+), 11 deletions(-)
diff --git a/drivers/iommu/riscv/iommu-bits.h b/drivers/iommu/riscv/iommu-bits.h
index f2ef9bd3cde9..65d1f2250011 100644
--- a/drivers/iommu/riscv/iommu-bits.h
+++ b/drivers/iommu/riscv/iommu-bits.h
@@ -720,6 +720,13 @@ static inline void riscv_iommu_cmd_inval_vma(struct riscv_iommu_command *cmd)
cmd->dword1 = 0;
}
+static inline void riscv_iommu_cmd_inval_gvma(struct riscv_iommu_command *cmd)
+{
+ cmd->dword0 = FIELD_PREP(RISCV_IOMMU_CMD0_OPCODE, RISCV_IOMMU_CMD_IOTINVAL_OPCODE) |
+ FIELD_PREP(RISCV_IOMMU_CMD0_FUNC, RISCV_IOMMU_CMD_IOTINVAL_FUNC_GVMA);
+ cmd->dword1 = 0;
+}
+
static inline void riscv_iommu_cmd_inval_set_addr(struct riscv_iommu_command *cmd,
u64 addr)
{
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index c5b6214ee672..99f05f1db35b 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -48,6 +48,10 @@
static DEFINE_IDA(riscv_iommu_pscids);
#define RISCV_IOMMU_MAX_PSCID (BIT(20) - 1)
+/* IOMMU GSCID allocation namespace. */
+static DEFINE_IDA(riscv_iommu_gscids);
+#define RISCV_IOMMU_MAX_GSCID (BIT(16) - 1)
+
/* Device resource-managed allocations */
struct riscv_iommu_devres {
void *addr;
@@ -819,6 +823,7 @@ struct riscv_iommu_domain {
struct list_head bonds;
spinlock_t lock; /* protect bonds list updates. */
int pscid;
+ int gscid;
};
PT_IOMMU_CHECK_DOMAIN(struct riscv_iommu_domain, riscvpt.iommu, domain);
@@ -981,7 +986,7 @@ static void riscv_iommu_tlbi_calc(struct riscv_iommu_tlbi *tlbi,
}
static void riscv_iommu_iotlb_inval_iommu(struct riscv_iommu_device *iommu,
- int pscid,
+ int pscid, int gscid,
struct riscv_iommu_tlbi *tlbi)
{
bool use_nl = tlbi->non_leaf &&
@@ -989,8 +994,13 @@ static void riscv_iommu_iotlb_inval_iommu(struct riscv_iommu_device *iommu,
struct riscv_iommu_command cmd;
unsigned int i;
- riscv_iommu_cmd_inval_vma(&cmd);
- riscv_iommu_cmd_inval_set_pscid(&cmd, pscid);
+ if (gscid) {
+ riscv_iommu_cmd_inval_gvma(&cmd);
+ riscv_iommu_cmd_inval_set_gscid(&cmd, gscid);
+ } else {
+ riscv_iommu_cmd_inval_vma(&cmd);
+ riscv_iommu_cmd_inval_set_pscid(&cmd, pscid);
+ }
/*
* If non-leaf entries were changed and the IOMMU doesn't
@@ -1074,7 +1084,7 @@ static void riscv_iommu_iotlb_inval(struct riscv_iommu_domain *domain,
if (iommu == prev)
continue;
- riscv_iommu_iotlb_inval_iommu(iommu, domain->pscid, &tlbi);
+ riscv_iommu_iotlb_inval_iommu(iommu, domain->pscid, domain->gscid, &tlbi);
prev = iommu;
}
@@ -1208,6 +1218,7 @@ static void riscv_iommu_iodir_update(struct riscv_iommu_device *iommu,
WRITE_ONCE(dc->fsc, new_dc->fsc);
WRITE_ONCE(dc->ta, new_dc->ta & RISCV_IOMMU_PC_TA_PSCID);
+ WRITE_ONCE(dc->iohgatp, new_dc->iohgatp);
/* Update device context, write TC.V as the last step. */
dma_wmb();
WRITE_ONCE(dc->tc, tc);
@@ -1258,16 +1269,19 @@ static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
WARN_ON(!list_empty(&domain->bonds));
- if ((int)domain->pscid > 0)
+ if (domain->pscid > 0)
ida_free(&riscv_iommu_pscids, domain->pscid);
+ if (domain->gscid > 0)
+ ida_free(&riscv_iommu_gscids, domain->gscid);
pt_iommu_deinit(&domain->riscvpt.iommu);
kfree(domain);
}
-static bool riscv_iommu_pt_supported(struct riscv_iommu_device *iommu, int pgd_mode)
+static bool riscv_iommu_fsc_supported(struct riscv_iommu_device *iommu,
+ int mode)
{
- switch (pgd_mode) {
+ switch (mode) {
case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
return iommu->caps & RISCV_IOMMU_CAPABILITIES_SV39;
@@ -1280,6 +1294,22 @@ static bool riscv_iommu_pt_supported(struct riscv_iommu_device *iommu, int pgd_m
return false;
}
+static bool riscv_iommu_iohgatp_supported(struct riscv_iommu_device *iommu,
+ int mode)
+{
+ switch (mode) {
+ case RISCV_IOMMU_DC_IOHGATP_MODE_SV39X4:
+ return iommu->caps & RISCV_IOMMU_CAPABILITIES_SV39X4;
+
+ case RISCV_IOMMU_DC_IOHGATP_MODE_SV48X4:
+ return iommu->caps & RISCV_IOMMU_CAPABILITIES_SV48X4;
+
+ case RISCV_IOMMU_DC_IOHGATP_MODE_SV57X4:
+ return iommu->caps & RISCV_IOMMU_CAPABILITIES_SV57X4;
+ }
+ return false;
+}
+
static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
struct device *dev,
struct iommu_domain *old)
@@ -1292,11 +1322,21 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
pt_iommu_riscv_64_hw_info(&domain->riscvpt, &pt_info);
- if (!riscv_iommu_pt_supported(iommu, pt_info.fsc_iosatp_mode))
- return -ENODEV;
+ if (domain->gscid) {
+ if (!riscv_iommu_iohgatp_supported(iommu, pt_info.iohgatp_mode))
+ return -ENODEV;
+
+ dc.iohgatp =
+ FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_MODE, pt_info.iohgatp_mode) |
+ FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_GSCID, domain->gscid) |
+ FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_PPN, pt_info.ppn);
+ } else {
+ if (!riscv_iommu_fsc_supported(iommu, pt_info.fsc_iosatp_mode))
+ return -ENODEV;
- dc.fsc = FIELD_PREP(RISCV_IOMMU_PC_FSC_MODE, pt_info.fsc_iosatp_mode) |
- FIELD_PREP(RISCV_IOMMU_PC_FSC_PPN, pt_info.ppn);
+ dc.fsc = FIELD_PREP(RISCV_IOMMU_PC_FSC_MODE, pt_info.fsc_iosatp_mode) |
+ FIELD_PREP(RISCV_IOMMU_PC_FSC_PPN, pt_info.ppn);
+ }
dc.ta = FIELD_PREP(RISCV_IOMMU_PC_TA_PSCID, domain->pscid) |
RISCV_IOMMU_PC_TA_V;
--
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] 14+ messages in thread* [RFC PATCH v3 06/10] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch
2026-08-21 13:27 [RFC PATCH v3 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
` (4 preceding siblings ...)
2026-08-21 13:27 ` [RFC PATCH v3 05/10] iommu/riscv: support GSCID and GVMA invalidation command fangyu.yu
@ 2026-08-21 13:27 ` fangyu.yu
2026-08-21 13:27 ` [RFC PATCH v3 07/10] iommu/riscv: Add domain_alloc_paging_flags for second-stage domain fangyu.yu
` (2 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: fangyu.yu @ 2026-08-21 13:27 UTC (permalink / raw)
To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
kevin.tian, atish.patra, skhawaja, vasant.hegde
Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv,
Tomasz Jeznach, Andrew Jones
From: Tomasz Jeznach <tjeznach@rivosinc.com>
Enable KVM/VFIO support on RISC-V architecture.
Signed-off-by: Tomasz Jeznach <tjeznach@rivosinc.com>
Signed-off-by: Andrew Jones <ajones@ventanamicro.com>
Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
---
arch/riscv/kvm/Kconfig | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/riscv/kvm/Kconfig b/arch/riscv/kvm/Kconfig
index ec2cee0a39e0..54ee90f010ef 100644
--- a/arch/riscv/kvm/Kconfig
+++ b/arch/riscv/kvm/Kconfig
@@ -30,8 +30,10 @@ config KVM
select KVM_GENERIC_HARDWARE_ENABLING
select KVM_MMIO
select VIRT_XFER_TO_GUEST_WORK
+ select KVM_VFIO
select SCHED_INFO
select GUEST_PERF_EVENTS if PERF_EVENTS
+ select SRCU
help
Support hosting virtualized guest machines.
--
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] 14+ messages in thread* [RFC PATCH v3 07/10] iommu/riscv: Add domain_alloc_paging_flags for second-stage domain
2026-08-21 13:27 [RFC PATCH v3 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
` (5 preceding siblings ...)
2026-08-21 13:27 ` [RFC PATCH v3 06/10] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch fangyu.yu
@ 2026-08-21 13:27 ` fangyu.yu
2026-08-21 13:27 ` [RFC PATCH v3 08/10] iommu/riscv: Pre-enable GADE for second-stage domains fangyu.yu
2026-08-21 13:27 ` [RFC PATCH v3 09/10] iommu/riscv: Add dirty tracking support " fangyu.yu
8 siblings, 0 replies; 14+ messages in thread
From: fangyu.yu @ 2026-08-21 13:27 UTC (permalink / raw)
To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
kevin.tian, atish.patra, skhawaja, vasant.hegde
Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv
From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
Replace .domain_alloc_paging with .domain_alloc_paging_flags so callers
can pass allocation flags to select the appropriate page-table type.
When IOMMU_HWPT_ALLOC_NEST_PARENT or IOMMU_HWPT_ALLOC_DIRTY_TRACKING is
set in @flags, allocate a second-stage (iohgatp) domain.
When @flags is 0 the behaviour is identical to the previous
domain_alloc_paging: first-stage (iosatp) domain.
Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
---
drivers/iommu/riscv/iommu.c | 94 +++++++++++++++++++++++++++----------
1 file changed, 69 insertions(+), 25 deletions(-)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 99f05f1db35b..16779877351b 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1358,25 +1358,21 @@ static const struct iommu_domain_ops riscv_iommu_paging_domain_ops = {
.flush_iotlb_all = riscv_iommu_iotlb_flush_all,
};
-static struct iommu_domain *riscv_iommu_alloc_paging_domain(struct device *dev)
+static struct iommu_domain *
+riscv_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags,
+ const struct iommu_user_data *user_data)
{
struct pt_iommu_riscv_64_cfg cfg = {};
struct riscv_iommu_domain *domain;
struct riscv_iommu_device *iommu;
int ret;
+ const u32 supported_flags = IOMMU_HWPT_ALLOC_DIRTY_TRACKING |
+ IOMMU_HWPT_ALLOC_NEST_PARENT;
- iommu = dev_to_iommu(dev);
- if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV57) {
- cfg.common.hw_max_vasz_lg2 = 57;
- } else if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV48) {
- cfg.common.hw_max_vasz_lg2 = 48;
- } else if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV39) {
- cfg.common.hw_max_vasz_lg2 = 39;
- } else {
- dev_err(dev, "cannot find supported page table mode\n");
- return ERR_PTR(-ENODEV);
- }
- cfg.common.hw_max_oasz_lg2 = 56;
+ if (flags & ~supported_flags)
+ return ERR_PTR(-EOPNOTSUPP);
+ if (user_data)
+ return ERR_PTR(-EOPNOTSUPP);
domain = kzalloc_obj(*domain);
if (!domain)
@@ -1384,12 +1380,13 @@ static struct iommu_domain *riscv_iommu_alloc_paging_domain(struct device *dev)
INIT_LIST_HEAD_RCU(&domain->bonds);
spin_lock_init(&domain->lock);
+ iommu = dev_to_iommu(dev);
+ cfg.common.hw_max_oasz_lg2 = 56;
/*
* 6.4 IOMMU capabilities [..] IOMMU implementations must support the
* Svnapot standard extension for NAPOT Translation Contiguity.
*/
- cfg.common.features = BIT(PT_FEAT_SIGN_EXTEND) |
- BIT(PT_FEAT_FLUSH_RANGE) |
+ cfg.common.features = BIT(PT_FEAT_FLUSH_RANGE) |
BIT(PT_FEAT_RISCV_SVNAPOT_64K) |
BIT(PT_FEAT_DETAILED_GATHER);
if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SVPBMT)
@@ -1397,19 +1394,66 @@ static struct iommu_domain *riscv_iommu_alloc_paging_domain(struct device *dev)
domain->riscvpt.iommu.nid = dev_to_node(iommu->dev);
domain->domain.ops = &riscv_iommu_paging_domain_ops;
- domain->pscid = ida_alloc_range(&riscv_iommu_pscids, 1,
- RISCV_IOMMU_MAX_PSCID, GFP_KERNEL);
- if (domain->pscid < 0) {
- riscv_iommu_free_paging_domain(&domain->domain);
- return ERR_PTR(-ENOMEM);
+ switch (flags) {
+ case 0:
+ cfg.common.features |= BIT(PT_FEAT_SIGN_EXTEND);
+ if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV57) {
+ cfg.common.hw_max_vasz_lg2 = 57;
+ } else if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV48) {
+ cfg.common.hw_max_vasz_lg2 = 48;
+ } else if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV39) {
+ cfg.common.hw_max_vasz_lg2 = 39;
+ } else {
+ ret = -ENODEV;
+ goto err_free;
+ }
+ domain->pscid = ida_alloc_range(&riscv_iommu_pscids, 1,
+ RISCV_IOMMU_MAX_PSCID, GFP_KERNEL);
+ if (domain->pscid < 0) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+ break;
+ case IOMMU_HWPT_ALLOC_NEST_PARENT:
+ case IOMMU_HWPT_ALLOC_DIRTY_TRACKING:
+ case IOMMU_HWPT_ALLOC_DIRTY_TRACKING | IOMMU_HWPT_ALLOC_NEST_PARENT:
+ /*
+ * Second-stage (iohgatp) page table for KVM VFIO device
+ * pass-through and dirty tracking. The GPA space is 2 bits
+ * wider than the corresponding first-stage VA space (x4 root
+ * page table), so hw_max_vasz_lg2 values are 41/50/59.
+ */
+ if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV57X4) {
+ cfg.common.hw_max_vasz_lg2 = 59;
+ } else if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV48X4) {
+ cfg.common.hw_max_vasz_lg2 = 50;
+ } else if (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV39X4) {
+ cfg.common.hw_max_vasz_lg2 = 41;
+ } else {
+ ret = -ENODEV;
+ goto err_free;
+ }
+ domain->gscid = ida_alloc_range(&riscv_iommu_gscids, 1,
+ RISCV_IOMMU_MAX_GSCID, GFP_KERNEL);
+ if (domain->gscid < 0) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
+ cfg.common.features |= BIT(PT_FEAT_RISCV_S2);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ goto err_free;
}
ret = pt_iommu_riscv_64_init(&domain->riscvpt, &cfg, GFP_KERNEL);
- if (ret) {
- riscv_iommu_free_paging_domain(&domain->domain);
- return ERR_PTR(ret);
- }
+ if (ret)
+ goto err_free;
return &domain->domain;
+
+err_free:
+ riscv_iommu_free_paging_domain(&domain->domain);
+ return ERR_PTR(ret);
}
static int riscv_iommu_attach_blocking_domain(struct iommu_domain *iommu_domain,
@@ -1544,7 +1588,7 @@ static const struct iommu_ops riscv_iommu_ops = {
.identity_domain = &riscv_iommu_identity_domain,
.blocked_domain = &riscv_iommu_blocking_domain,
.release_domain = &riscv_iommu_blocking_domain,
- .domain_alloc_paging = riscv_iommu_alloc_paging_domain,
+ .domain_alloc_paging_flags = riscv_iommu_domain_alloc_paging_flags,
.device_group = riscv_iommu_device_group,
.probe_device = riscv_iommu_probe_device,
.release_device = riscv_iommu_release_device,
--
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] 14+ messages in thread* [RFC PATCH v3 08/10] iommu/riscv: Pre-enable GADE for second-stage domains
2026-08-21 13:27 [RFC PATCH v3 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
` (6 preceding siblings ...)
2026-08-21 13:27 ` [RFC PATCH v3 07/10] iommu/riscv: Add domain_alloc_paging_flags for second-stage domain fangyu.yu
@ 2026-08-21 13:27 ` fangyu.yu
2026-08-21 13:27 ` [RFC PATCH v3 09/10] iommu/riscv: Add dirty tracking support " fangyu.yu
8 siblings, 0 replies; 14+ messages in thread
From: fangyu.yu @ 2026-08-21 13:27 UTC (permalink / raw)
To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
kevin.tian, atish.patra, skhawaja, vasant.hegde
Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv
From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
Pre-enable RISCV_IOMMU_DC_TC_GADE in the device context when
attaching a second-stage domain, if the IOMMU supports AMO_HWAD.
Software pre-populates second-stage page tables with D set, so
enabling GADE by default does not change normal behavior. When
dirty tracking is enabled, iommufd clears the pre-set D bits and
GADE becomes necessary for hardware to update the dirty bit on
write access.
This avoids toggling GADE dynamically and keeps device context
setup consistent with second-stage domain attachment.
Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
---
drivers/iommu/riscv/iommu.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 16779877351b..f01fd4e2f840 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1330,6 +1330,8 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_MODE, pt_info.iohgatp_mode) |
FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_GSCID, domain->gscid) |
FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_PPN, pt_info.ppn);
+ if (iommu->caps & RISCV_IOMMU_CAPABILITIES_AMO_HWAD)
+ dc.tc |= RISCV_IOMMU_DC_TC_GADE;
} else {
if (!riscv_iommu_fsc_supported(iommu, pt_info.fsc_iosatp_mode))
return -ENODEV;
--
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] 14+ messages in thread* [RFC PATCH v3 09/10] iommu/riscv: Add dirty tracking support for second-stage domains
2026-08-21 13:27 [RFC PATCH v3 00/10] iommu/riscv: Add hardware dirty tracking for second-stage domains fangyu.yu
` (7 preceding siblings ...)
2026-08-21 13:27 ` [RFC PATCH v3 08/10] iommu/riscv: Pre-enable GADE for second-stage domains fangyu.yu
@ 2026-08-21 13:27 ` fangyu.yu
8 siblings, 0 replies; 14+ messages in thread
From: fangyu.yu @ 2026-08-21 13:27 UTC (permalink / raw)
To: tomasz.jeznach, joro, will, robin.murphy, pjw, palmer, aou, alex,
baolu.lu, jroedel, zong.li, andrew.jones, anup, jgg, jgg,
kevin.tian, atish.patra, skhawaja, vasant.hegde
Cc: fangyu.yu, guoren, iommu, linux-kernel, linux-riscv
From: Fangyu Yu <fangyu.yu@linux.alibaba.com>
Add hardware dirty tracking support for second-stage (iohgatp) domains
used in KVM VFIO device pass-through.
The RISC-V IOMMU can automatically set the dirty bit in PTEs on write
access when DC.tc.GADE is set and the hardware has AMO_HWAD capability.
Wire this up to the iommufd dirty tracking interface:
- riscv_iommu_set_dirty_tracking(): Always enabled dirty tracking for
second-stage domain.
- riscv_iommu_dirty_ops: Exposes set_dirty_tracking and the generic
page-table read_and_clear_dirty via IOMMU_PT_DIRTY_OPS(riscv_64).
- domain_alloc_paging_flags: Assigns dirty_ops to second-stage domains
when AMO_HWAD is advertised in hardware capabilities.
- riscv_iommu_capable: Reports IOMMU_CAP_DIRTY_TRACKING when
AMO_HWAD is present.
Signed-off-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
---
drivers/iommu/riscv/iommu.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index f01fd4e2f840..41b593def1f5 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1352,6 +1352,21 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
return 0;
}
+static int riscv_iommu_set_dirty_tracking(struct iommu_domain *iommu_domain,
+ bool enable)
+{
+ /*
+ * Always enabled and the dirty bitmap is cleared prior to
+ * set_dirty_tracking().
+ */
+ return 0;
+}
+
+static const struct iommu_dirty_ops riscv_iommu_dirty_ops = {
+ IOMMU_PT_DIRTY_OPS(riscv_64),
+ .set_dirty_tracking = riscv_iommu_set_dirty_tracking,
+};
+
static const struct iommu_domain_ops riscv_iommu_paging_domain_ops = {
IOMMU_PT_DOMAIN_OPS(riscv_64),
.attach_dev = riscv_iommu_attach_paging_domain,
@@ -1442,6 +1457,8 @@ riscv_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags,
goto err_free;
}
cfg.common.features |= BIT(PT_FEAT_RISCV_S2);
+ if (iommu->caps & RISCV_IOMMU_CAPABILITIES_AMO_HWAD)
+ domain->domain.dirty_ops = &riscv_iommu_dirty_ops;
break;
default:
ret = -EOPNOTSUPP;
@@ -1517,9 +1534,13 @@ static struct iommu_group *riscv_iommu_device_group(struct device *dev)
static bool riscv_iommu_capable(struct device *dev, enum iommu_cap cap)
{
+ struct riscv_iommu_device *iommu = dev_to_iommu(dev);
+
switch (cap) {
case IOMMU_CAP_CACHE_COHERENCY:
return true;
+ case IOMMU_CAP_DIRTY_TRACKING:
+ return !!(iommu->caps & RISCV_IOMMU_CAPABILITIES_AMO_HWAD);
default:
return false;
}
--
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] 14+ messages in thread