* [RFC PATCH v2 0/9] iommu/riscv: Add nested translation (vIOMMU) support
@ 2026-10-29 6:11 Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 1/9] iommu/riscv: Use DC.tc.V as the device context valid bit Gong Shuai
` (8 more replies)
0 siblings, 9 replies; 12+ messages in thread
From: Gong Shuai @ 2026-10-29 6:11 UTC (permalink / raw)
To: kvm, kvm-riscv, linux-riscv, linux-kernel, iommu
Cc: Anup Patel, Atish Patra, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Tomasz Jeznach, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Paolo Bonzini, Sean Christopherson,
Jason Gunthorpe, Kevin Tian, Andrew Jones, Fangyu Yu, Wu Fei,
Zhao Qingye, Gong Shuai, Gong Shuai
An IOMMU allows an OS to restrict device DMA to the intended memory.
It also gives device drivers an IOVA space that is independent of the
physical memory layout. A guest OS needs these capabilities when it
directly controls a device, just as a native OS does.
Exposing a vIOMMU to the guest is not enough for a passthrough device.
DMA from the physical device must also obey the mappings that the guest
creates through its vIOMMU. Otherwise the guest cannot use its IOMMU to
protect guest memory or provide IOVA mappings for the assigned device.
The RISC-V IOMMU architecture supports this usage with two-stage
translation. Stage 1 applies the guest's vIOMMU configuration. Stage 2
is controlled by the hypervisor and restricts the device to memory
assigned to the VM. This division gives the guest control of its own
DMA mappings without allowing the device to escape the VM.
The Linux RISC-V IOMMU driver does not yet support this model through
iommufd. A VMM cannot provide the guest stage 1 configuration to the
physical IOMMU or synchronize its translation caches after the guest
updates its page tables. This series adds the missing nested
translation support so device passthrough can work with a guest that
uses its own vIOMMU.
This is an RFC and comments on the design are welcome.
Patch organization
Patch 1 prepares device context handling for nested translation.
Patches 2 and 3 add the uAPI for nested allocation and cache
invalidation.
Patches 4 to 6 add the vIOMMU, nested domains and cache invalidation.
Patches 7 and 8 allow a VMM to discover the physical IOMMU
capabilities.
Patch 9 adds nested device attachment.
Dependencies
The series applies on top of linux-iommu/next at 20bfd25757659f with
the generic iommu page table for SMMUv3 v2 [1], the RISC-V hardware
dirty tracking series v5 [2], and the generic_pt fix that adds Svpbmt
to the RISC-V supported feature mask.
Testing
The test requires MSI support, but the related dependencies are still
under review, so the interrupt changes are not included in this series.
The test needs the MSI remapping series v6 [3], the irqbypass series
RFC v3 [4] and a KVM client from Andrew's github repo [5].A branch with
the complete test stack, including the additional nested interrupt
support, is available at github repo [6].
The host is a RISC-V machine whose physical IOMMU is provided by an outer
QEMU riscv-iommu model, which implements both translation stages. An NVMe
controller under it is bound to vfio-pci and passed through to the guest.
The guest is started by a QEMU built with the accel series [7][8], which
exposes a first stage IOMMU to the guest and leaves the second stage to
the host IOMMU. For simplicity the guest runs the same kernel as the host,
and inside the guest it mainly uses the normal IOMMU driver and the NVMe
driver.
The layering of the setup is
Host HW, a RISC-V virt machine from latest QEMU(v11.1.2), with a
riscv-iommu model that implements two stage translation and with the NVMe
controller
Host OS, the kernel with this series
riscv-iommu driver, second stage and MSI table vfio-pci and KVM
Guest HW, QEMU with the accel series [7][8]
riscv-iommu accel, first stage only
Guest OS, the same kernel as the host
riscv-iommu driver, NVMe driver
A DMA address from the NVMe controller is translated by the guest first
stage and then by the host second stage on the physical IOMMU. An MSI
address is translated by the guest first stage into a guest IMSIC address,
which the flat MSI table then resolves straight to the interrupt file of
the target vCPU, instead of walking the second stage.
DMA device --> guest first stage --> host second stage --> RAM
MSI device --> guest first stage --> MSI table --> VS-file
The host was started with
$HOST_QEMU \
-M virt,aia=aplic-imsic,aia-guests=7,iommu-sys=on \
-cpu rva23s64,sv48=on -nographic \
-smp 16 -m 16G \
-kernel $HOST_KERNEL \
-append "root=/dev/vda rw earlycon=sbi console=ttyS0 iommufd.allow_unsafe_interrupts=1" \
-drive file=$HOST_ROOTFS,if=none,id=d0,format=qcow2 -device virtio-blk-device,drive=d0 \
-device pcie-root-port,id=port0,chassis=0,slot=0,addr=3.0 \
-drive file=fake-nvme-disk.img,if=none,id=nvme0,format=qcow2 \
-device nvme,drive=nvme0,serial=deadbeef,bus=port0,addr=0.0
and the guest with
$GUEST_QEMU \
-machine virt,accel=kvm,aia=aplic-imsic,aia-guests=5 \
-cpu host -nographic \
-smp 4 -m 4G \
-object iommufd,id=iommufd0 \
-device riscv-iommu-pci,addr=1.0,s-stage=on,g-stage=off,accel=on \
-device vfio-pci,host=0000:01:00.0,iommufd=iommufd0 \
-kernel $GUEST_KERNEL \
-append "root=/dev/vda rw earlycon=sbi console=ttyS0 iommu.passthrough=0 iommu.strict=1" \
-drive file=$GUEST_ROOTFS,if=none,id=d0,format=qcow2 -device virtio-blk-device,drive=d0
Correctness was checked by running fio with verification enabled against
the passed through disk.
Known limitations and future work
PASID and the process directory table are not supported, PDTV is rejected
and PSCID is fixed at 0. The ATS, PRI and T2GPA fields are validated on
the nested domain, but ATS.INVAL and the PRI queue are not forwarded. A
vIOMMU is bound to a single physical IOMMU instance. The new uAPI types
have no selftest coverage yet.
Coding assistants based on LLMs were used during development for code
exploration, patch review, test execution and for drafting and editing
commit messages. We have reviewed and finalized all resulting code and
text.
Changes in v2:
- Rebased on linux-iommu/next at 20bfd25757659f and the hardware
dirty tracking series v5 [2].
- Rewrote the commit messages and updated the uAPI definitions (Jason's
suggestions).
- Drop the MSI related patches (Jason's suggestions).
- Link to v1:
https://lore.kernel.org/linux-riscv/20261015065232.604503-1-gong.shuai@sanechips.com.cn/
[1] https://lore.kernel.org/linux-iommu/0-v2-563ee63886f0+1209-iommupt_armv8_jgg@nvidia.com/
[2] https://lore.kernel.org/linux-riscv/20261020094106.92722-1-fangyu.yu@linux.alibaba.com/
[3] https://lore.kernel.org/all/20260925151659.419512-1-andrew.jones@oss.qualcomm.com/
[4] https://lore.kernel.org/linux-riscv/20260928143113.49838-1-andrew.jones@oss.qualcomm.com/
[5] https://github.com/jones-drew/linux/commits/riscv/iommu-irqbypass-rfc-v3-kvm/
[6] https://github.com/leaprise/linux/tree/riscv/iommu-nested-rfc-v2
[7] https://lore.kernel.org/qemu-devel/20261029054751.563493-1-gong.shuai@sanechips.com.cn/
[8] https://github.com/leaprise/qemu/tree/riscv/iommu-nested-rfc-v2
Gong Shuai (2):
iommu/riscv: Use DC.tc.V as the device context valid bit
iommu/riscv: Add vIOMMU cache invalidation
Wu Fei (7):
iommufd: Add RISC-V IOMMU hwpt and vIOMMU types
iommufd: Add RISC-V IOMMU cache invalidation data type
iommu/riscv: Add vIOMMU object support
iommu/riscv: Add nested domain allocation
iommufd: Add RISC-V IOMMU hardware info type
iommu/riscv: Add riscv_iommu_hw_info
iommu/riscv: Add riscv_iommu_attach_dev_nested
drivers/iommu/riscv/iommu-bits.h | 19 ++
drivers/iommu/riscv/iommu.c | 391 +++++++++++++++++++++++++++++--
include/uapi/linux/iommufd.h | 81 +++++++
3 files changed, 475 insertions(+), 16 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [RFC PATCH v2 1/9] iommu/riscv: Use DC.tc.V as the device context valid bit
2026-10-29 6:11 [RFC PATCH v2 0/9] iommu/riscv: Add nested translation (vIOMMU) support Gong Shuai
@ 2026-10-29 6:11 ` Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 2/9] iommufd: Add RISC-V IOMMU hwpt and vIOMMU types Gong Shuai
` (7 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Gong Shuai @ 2026-10-29 6:11 UTC (permalink / raw)
To: kvm, kvm-riscv, linux-riscv, linux-kernel, iommu
Cc: Anup Patel, Atish Patra, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Tomasz Jeznach, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Paolo Bonzini, Sean Christopherson,
Jason Gunthorpe, Kevin Tian, Andrew Jones, Fangyu Yu, Wu Fei,
Zhao Qingye, Gong Shuai, Gong Shuai
Previously, riscv_iommu_iodir_update() accepted only a partial device
context and used bit 0 of the translation attributes as the valid
flag. It also kept only GADE in DC.tc and dropped the other fields
needed to pass guest translation settings for nested domains.
Now that the function accepts a complete device context, take the
valid bit from DC.tc.V and copy all defined TC fields so the guest
configuration reaches the hardware.
Use the DC field macros for the remaining device context updates as
well.
Signed-off-by: Gong Shuai <gong.shuai@sanechips.com.cn>
---
drivers/iommu/riscv/iommu-bits.h | 6 ++++++
drivers/iommu/riscv/iommu.c | 28 ++++++++++++----------------
2 files changed, 18 insertions(+), 16 deletions(-)
diff --git a/drivers/iommu/riscv/iommu-bits.h b/drivers/iommu/riscv/iommu-bits.h
index 65d1f2250011..80c32f00ee5e 100644
--- a/drivers/iommu/riscv/iommu-bits.h
+++ b/drivers/iommu/riscv/iommu-bits.h
@@ -348,6 +348,12 @@ struct riscv_iommu_dc {
#define RISCV_IOMMU_DC_TC_SBE BIT_ULL(10)
#define RISCV_IOMMU_DC_TC_SXL BIT_ULL(11)
+/*
+ * All bits of dc.tc as defined by the spec. Bits 12-23 and 32-63
+ * are reserved for future standard use, bits 24-31 are for custom use.
+ */
+#define RISCV_IOMMU_DC_TC_DEFINED GENMASK_ULL(11, 0)
+
/* Second-stage (aka G-stage) context fields */
#define RISCV_IOMMU_DC_IOHGATP_PPN RISCV_IOMMU_ATP_PPN_FIELD
#define RISCV_IOMMU_DC_IOHGATP_GSCID GENMASK_ULL(59, 44)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index ca24d1b836d9..554361dd3159 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1132,7 +1132,6 @@ static void riscv_iommu_iotlb_inval(struct riscv_iommu_domain *domain,
rcu_read_unlock();
}
-#define RISCV_IOMMU_FSC_BARE 0
/*
* This function sends IOTINVAL commands as required by the RISC-V
* IOMMU specification (Section 6.3.1 and 6.3.2 in 1.0 spec version)
@@ -1243,18 +1242,14 @@ static void riscv_iommu_iodir_update(struct riscv_iommu_device *iommu,
riscv_iommu_cmd_sync(iommu, RISCV_IOMMU_IOTINVAL_TIMEOUT);
/*
- * For device context with DC_TC_PDTV = 0, translation attributes valid bit
- * is stored as DC_TC_V bit (both sharing the same location at BIT(0)).
+ * Only defined bits are writable by the driver.
*/
for (i = 0; i < fwspec->num_ids; i++) {
dc = riscv_iommu_get_dc(iommu, fwspec->ids[i]);
- tc = READ_ONCE(dc->tc);
- tc &= ~RISCV_IOMMU_DC_TC_GADE;
- tc |= new_dc->tc & RISCV_IOMMU_DC_TC_GADE;
- tc |= new_dc->ta & RISCV_IOMMU_DC_TC_V;
+ tc = new_dc->tc & RISCV_IOMMU_DC_TC_DEFINED;
WRITE_ONCE(dc->fsc, new_dc->fsc);
- WRITE_ONCE(dc->ta, new_dc->ta & RISCV_IOMMU_PC_TA_PSCID);
+ WRITE_ONCE(dc->ta, new_dc->ta & RISCV_IOMMU_DC_TA_PSCID);
WRITE_ONCE(dc->iohgatp, new_dc->iohgatp);
/* Update device context, write TC.V as the last step. */
dma_wmb();
@@ -1373,11 +1368,11 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
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_DC_FSC_MODE, pt_info.fsc_iosatp_mode) |
+ FIELD_PREP(RISCV_IOMMU_DC_FSC_PPN, pt_info.ppn);
}
- dc.ta = FIELD_PREP(RISCV_IOMMU_PC_TA_PSCID, domain->pscid) |
- RISCV_IOMMU_PC_TA_V;
+ dc.ta = FIELD_PREP(RISCV_IOMMU_DC_TA_PSCID, domain->pscid);
+ dc.tc |= RISCV_IOMMU_DC_TC_V;
if (riscv_iommu_bond_link(domain, dev))
return -ENOMEM;
@@ -1520,9 +1515,10 @@ static int riscv_iommu_attach_blocking_domain(struct iommu_domain *iommu_domain,
struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
struct riscv_iommu_dc dc = {0};
- dc.fsc = RISCV_IOMMU_FSC_BARE;
+ dc.fsc = RISCV_IOMMU_DC_FSC_MODE_BARE;
+ dc.tc = 0;
- /* Make device context invalid, translation requests will fault w/ #258 */
+ /* Make device context invalid (DC.tc.V = 0), translation requests will fault w/ #258 */
riscv_iommu_iodir_update(iommu, dev, &dc);
riscv_iommu_bond_unlink(info->domain, dev);
info->domain = NULL;
@@ -1545,8 +1541,8 @@ static int riscv_iommu_attach_identity_domain(struct iommu_domain *iommu_domain,
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;
+ dc.fsc = RISCV_IOMMU_DC_FSC_MODE_BARE;
+ dc.tc = RISCV_IOMMU_DC_TC_V;
riscv_iommu_iodir_update(iommu, dev, &dc);
riscv_iommu_bond_unlink(info->domain, dev);
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [RFC PATCH v2 2/9] iommufd: Add RISC-V IOMMU hwpt and vIOMMU types
2026-10-29 6:11 [RFC PATCH v2 0/9] iommu/riscv: Add nested translation (vIOMMU) support Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 1/9] iommu/riscv: Use DC.tc.V as the device context valid bit Gong Shuai
@ 2026-10-29 6:11 ` Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 3/9] iommufd: Add RISC-V IOMMU cache invalidation data type Gong Shuai
` (6 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Gong Shuai @ 2026-10-29 6:11 UTC (permalink / raw)
To: kvm, kvm-riscv, linux-riscv, linux-kernel, iommu
Cc: Anup Patel, Atish Patra, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Tomasz Jeznach, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Paolo Bonzini, Sean Christopherson,
Jason Gunthorpe, Kevin Tian, Andrew Jones, Fangyu Yu, Wu Fei,
Zhao Qingye, Gong Shuai, Gong Shuai
From: Wu Fei <wu.fei9@sanechips.com.cn>
Add IOMMU_HWPT_DATA_RISCV_S1 and struct iommu_hwpt_riscv to carry
the guest device context in base format for nested hwpt allocation.
Add IOMMU_VIOMMU_TYPE_RISCV so user space can request a RISC-V
vIOMMU.
The RISC-V nested translation support added later needs these types.
Signed-off-by: Wu Fei <wu.fei9@sanechips.com.cn>
Co-developed-by: Gong Shuai <gong.shuai@sanechips.com.cn>
Signed-off-by: Gong Shuai <gong.shuai@sanechips.com.cn>
---
include/uapi/linux/iommufd.h | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h
index 206fa667c782..5b5ca6e41063 100644
--- a/include/uapi/linux/iommufd.h
+++ b/include/uapi/linux/iommufd.h
@@ -506,18 +506,46 @@ struct iommu_hwpt_amd_guest {
__aligned_u64 dte[4];
};
+/**
+ * struct iommu_hwpt_riscv - RISC-V IOMMU stage-1 page table data
+ * (IOMMU_HWPT_DATA_RISCV_S1)
+ *
+ * @dc: The guest device-context (DC) in the base format defined in
+ * "Leaf DDT entry" of the RISC-V IOMMU Architecture Specification,
+ * refer to "Device-context fields" for the meaning of each
+ * doubleword. Only the guest-owned doublewords are validated:
+ * - dc[0] (tc): Allowed bits are V, EN_ATS, EN_PRI, T2GPA, DTF,
+ * PRPR, SADE and SBE. GADE is owned by the host and the value
+ * here is ignored. PDTV, DPE and SXL are not supported and must
+ * be 0.
+ * - dc[3] (fsc): Carries iosatp when tc.PDTV=0, allowed fields
+ * are MODE (Bare, Sv39, Sv48, Sv57) and PPN; Carries pdtp when
+ * tc.PDTV=1, which not supported now.
+ *
+ * The host-owned doublewords dc[1] (iohgatp) and dc[2] (ta) are
+ * ignored. iohgatp is installed by the kernel from the nesting
+ * parent at attach time and ta is managed by the host. The
+ * extended-format MSI doublewords are configured by the host for
+ * interrupt remapping and are not passed via this structure.
+ */
+struct iommu_hwpt_riscv {
+ __aligned_u64 dc[4];
+};
+
/**
* enum iommu_hwpt_data_type - IOMMU HWPT Data Type
* @IOMMU_HWPT_DATA_NONE: no data
* @IOMMU_HWPT_DATA_VTD_S1: Intel VT-d stage-1 page table
* @IOMMU_HWPT_DATA_ARM_SMMUV3: ARM SMMUv3 Context Descriptor Table
* @IOMMU_HWPT_DATA_AMD_GUEST: AMD IOMMU guest page table
+ * @IOMMU_HWPT_DATA_RISCV_S1: RISC-V IOMMU stage-1 page table
*/
enum iommu_hwpt_data_type {
IOMMU_HWPT_DATA_NONE = 0,
IOMMU_HWPT_DATA_VTD_S1 = 1,
IOMMU_HWPT_DATA_ARM_SMMUV3 = 2,
IOMMU_HWPT_DATA_AMD_GUEST = 3,
+ IOMMU_HWPT_DATA_RISCV_S1 = 4,
};
/**
@@ -1095,6 +1123,7 @@ struct iommu_fault_alloc {
* @IOMMU_VIOMMU_TYPE_ARM_SMMUV3: ARM SMMUv3 driver specific type
* @IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV: NVIDIA Tegra241 CMDQV (extension for ARM
* SMMUv3) enabled ARM SMMUv3 type
+ * @IOMMU_VIOMMU_TYPE_RISCV: RISC-V IOMMU driver specific type
*/
enum iommu_viommu_type {
IOMMU_VIOMMU_TYPE_DEFAULT = 0,
@@ -1105,6 +1134,7 @@ enum iommu_viommu_type {
* VMM must wire the HYP_OWN bit to 0 in guest VINTF_CONFIG register
*/
IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV = 2,
+ IOMMU_VIOMMU_TYPE_RISCV = 3,
};
/**
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [RFC PATCH v2 3/9] iommufd: Add RISC-V IOMMU cache invalidation data type
2026-10-29 6:11 [RFC PATCH v2 0/9] iommu/riscv: Add nested translation (vIOMMU) support Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 1/9] iommu/riscv: Use DC.tc.V as the device context valid bit Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 2/9] iommufd: Add RISC-V IOMMU hwpt and vIOMMU types Gong Shuai
@ 2026-10-29 6:11 ` Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 4/9] iommu/riscv: Add vIOMMU object support Gong Shuai
` (5 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Gong Shuai @ 2026-10-29 6:11 UTC (permalink / raw)
To: kvm, kvm-riscv, linux-riscv, linux-kernel, iommu
Cc: Anup Patel, Atish Patra, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Tomasz Jeznach, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Paolo Bonzini, Sean Christopherson,
Jason Gunthorpe, Kevin Tian, Andrew Jones, Fangyu Yu, Wu Fei,
Zhao Qingye, Gong Shuai, Gong Shuai
From: Wu Fei <wu.fei9@sanechips.com.cn>
Add IOMMU_VIOMMU_INVALIDATE_DATA_RISCV and
struct iommu_viommu_riscv_invalidate to carry RISC-V IOTINVAL
commands from user space. Define the command payload in the uAPI
instead of relying on the driver internal struct
riscv_iommu_command.
The vIOMMU cache invalidation support added later uses this format
to forward guest IOTINVAL commands to the physical IOMMU.
Signed-off-by: Wu Fei <wu.fei9@sanechips.com.cn>
Co-developed-by: Gong Shuai <gong.shuai@sanechips.com.cn>
Signed-off-by: Gong Shuai <gong.shuai@sanechips.com.cn>
---
include/uapi/linux/iommufd.h | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h
index 5b5ca6e41063..aa65d494b992 100644
--- a/include/uapi/linux/iommufd.h
+++ b/include/uapi/linux/iommufd.h
@@ -918,10 +918,12 @@ struct iommu_hwpt_get_dirty_bitmap {
* Data Type
* @IOMMU_HWPT_INVALIDATE_DATA_VTD_S1: Invalidation data for VTD_S1
* @IOMMU_VIOMMU_INVALIDATE_DATA_ARM_SMMUV3: Invalidation data for ARM SMMUv3
+ * @IOMMU_VIOMMU_INVALIDATE_DATA_RISCV: Invalidation data for RISC-V IOMMU
*/
enum iommu_hwpt_invalidate_data_type {
IOMMU_HWPT_INVALIDATE_DATA_VTD_S1 = 0,
IOMMU_VIOMMU_INVALIDATE_DATA_ARM_SMMUV3 = 1,
+ IOMMU_VIOMMU_INVALIDATE_DATA_RISCV = 2,
};
/**
@@ -982,6 +984,31 @@ struct iommu_viommu_arm_smmuv3_invalidate {
__aligned_le64 cmd[2];
};
+/**
+ * struct iommu_viommu_riscv_invalidate - RISC-V IOMMU cache invalidation
+ * (IOMMU_VIOMMU_INVALIDATE_DATA_RISCV)
+ * @cmd: 128-bit cache invalidation command that runs in the IOMMU command
+ * queue, in the format defined by the "IOMMU Page-table cache
+ * invalidation" section of the RISC-V IOMMU specification. Must be
+ * little-endian.
+ *
+ * Supported command list only when passing in a vIOMMU via @hwpt_id:
+ * IOTINVAL.VMA
+ *
+ * The isolation operands are owned by the kernel, which overwrites them
+ * before the command is issued:
+ * GV = 1 and GSCID = the GSCID assigned to the vIOMMU
+ * PSCV = 1 and PSCID = 0
+ *
+ * All other reserved bits must be 0.
+ *
+ * -EIO will be returned if the command is not supported, -EINVAL if it is
+ * malformed.
+ */
+struct iommu_viommu_riscv_invalidate {
+ __aligned_le64 cmd[2];
+};
+
/**
* struct iommu_hwpt_invalidate - ioctl(IOMMU_HWPT_INVALIDATE)
* @size: sizeof(struct iommu_hwpt_invalidate)
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [RFC PATCH v2 4/9] iommu/riscv: Add vIOMMU object support
2026-10-29 6:11 [RFC PATCH v2 0/9] iommu/riscv: Add nested translation (vIOMMU) support Gong Shuai
` (2 preceding siblings ...)
2026-10-29 6:11 ` [RFC PATCH v2 3/9] iommufd: Add RISC-V IOMMU cache invalidation data type Gong Shuai
@ 2026-10-29 6:11 ` Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 5/9] iommu/riscv: Add nested domain allocation Gong Shuai
` (4 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Gong Shuai @ 2026-10-29 6:11 UTC (permalink / raw)
To: kvm, kvm-riscv, linux-riscv, linux-kernel, iommu
Cc: Anup Patel, Atish Patra, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Tomasz Jeznach, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Paolo Bonzini, Sean Christopherson,
Jason Gunthorpe, Kevin Tian, Andrew Jones, Fangyu Yu, Wu Fei,
Zhao Qingye, Gong Shuai, Gong Shuai
From: Wu Fei <wu.fei9@sanechips.com.cn>
Allow iommufd to allocate a RISC-V vIOMMU when the physical IOMMU
supports both translation stages.
Nested translation needs to associate guest-owned stage 1 contexts
with a host-owned stage 2 parent domain. A vIOMMU provides this
association and retains the parent GSCID needed to program device
contexts and target IOTINVAL commands at the correct guest.
Signed-off-by: Wu Fei <wu.fei9@sanechips.com.cn>
Signed-off-by: Gong Shuai <gong.shuai@sanechips.com.cn>
---
drivers/iommu/riscv/iommu.c | 60 +++++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 554361dd3159..a8c19c9e45d4 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -19,6 +19,7 @@
#include <linux/dma-map-ops.h>
#include <linux/init.h>
#include <linux/iommu.h>
+#include <linux/iommufd.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/pci.h>
@@ -856,6 +857,7 @@ struct riscv_iommu_domain {
spinlock_t lock; /* protect bonds list updates. */
int pscid;
int gscid;
+ bool nest_parent;
};
PT_IOMMU_CHECK_DOMAIN(struct riscv_iommu_domain, riscvpt.iommu, domain);
@@ -1342,6 +1344,20 @@ static bool riscv_iommu_iohgatp_supported(struct riscv_iommu_device *iommu,
return false;
}
+static bool riscv_iommu_supports_s1(struct riscv_iommu_device *iommu)
+{
+ return (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV57) ||
+ (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV48) ||
+ (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV39);
+}
+
+static bool riscv_iommu_supports_s2(struct riscv_iommu_device *iommu)
+{
+ return (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV57X4) ||
+ (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV48X4) ||
+ (iommu->caps & RISCV_IOMMU_CAPABILITIES_SV39X4);
+}
+
static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
struct device *dev,
struct iommu_domain *old)
@@ -1488,6 +1504,7 @@ riscv_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags,
ret = -ENOMEM;
goto err_free;
}
+ domain->nest_parent = !!(flags & IOMMU_HWPT_ALLOC_NEST_PARENT);
cfg.common.features |= BIT(PT_FEAT_RISCV_S2);
if (iommu->caps & RISCV_IOMMU_CAPABILITIES_AMO_HWAD)
domain->domain.dirty_ops = &riscv_iommu_dirty_ops;
@@ -1638,6 +1655,47 @@ static void riscv_iommu_release_device(struct device *dev)
kfree_rcu_mightsleep(info);
}
+struct riscv_viommu {
+ struct iommufd_viommu core;
+ struct riscv_iommu_device *iommu;
+ struct riscv_iommu_domain *s2_parent;
+ int gscid;
+};
+
+static const struct iommufd_viommu_ops riscv_viommu_ops = {
+};
+
+static size_t riscv_iommu_get_viommu_size(struct device *dev,
+ enum iommu_viommu_type viommu_type)
+{
+ struct riscv_iommu_device *iommu = dev_to_iommu(dev);
+
+ if (viommu_type != IOMMU_VIOMMU_TYPE_RISCV)
+ return 0;
+
+ if (!riscv_iommu_supports_s1(iommu) || !riscv_iommu_supports_s2(iommu))
+ return 0;
+
+ return VIOMMU_STRUCT_SIZE(struct riscv_viommu, core);
+}
+
+static int riscv_viommu_init(struct iommufd_viommu *viommu,
+ struct iommu_domain *parent_domain,
+ const struct iommu_user_data *user_data)
+{
+ struct riscv_viommu *rv_viommu = container_of(viommu, struct riscv_viommu, core);
+ struct riscv_iommu_device *iommu =
+ container_of(viommu->iommu_dev, struct riscv_iommu_device, iommu);
+ struct riscv_iommu_domain *s2_parent = iommu_domain_to_riscv(parent_domain);
+
+ rv_viommu->iommu = iommu;
+ rv_viommu->s2_parent = s2_parent;
+ rv_viommu->gscid = s2_parent->gscid;
+ viommu->ops = &riscv_viommu_ops;
+
+ return 0;
+}
+
static const struct iommu_ops riscv_iommu_ops = {
.of_xlate = riscv_iommu_of_xlate,
.capable = riscv_iommu_capable,
@@ -1645,6 +1703,8 @@ static const struct iommu_ops riscv_iommu_ops = {
.blocked_domain = &riscv_iommu_blocking_domain,
.release_domain = &riscv_iommu_blocking_domain,
.domain_alloc_paging_flags = riscv_iommu_domain_alloc_paging_flags,
+ .get_viommu_size = riscv_iommu_get_viommu_size,
+ .viommu_init = riscv_viommu_init,
.device_group = riscv_iommu_device_group,
.probe_device = riscv_iommu_probe_device,
.release_device = riscv_iommu_release_device,
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [RFC PATCH v2 5/9] iommu/riscv: Add nested domain allocation
2026-10-29 6:11 [RFC PATCH v2 0/9] iommu/riscv: Add nested translation (vIOMMU) support Gong Shuai
` (3 preceding siblings ...)
2026-10-29 6:11 ` [RFC PATCH v2 4/9] iommu/riscv: Add vIOMMU object support Gong Shuai
@ 2026-10-29 6:11 ` Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 6/9] iommu/riscv: Add vIOMMU cache invalidation Gong Shuai
` (3 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Gong Shuai @ 2026-10-29 6:11 UTC (permalink / raw)
To: kvm, kvm-riscv, linux-riscv, linux-kernel, iommu
Cc: Anup Patel, Atish Patra, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Tomasz Jeznach, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Paolo Bonzini, Sean Christopherson,
Jason Gunthorpe, Kevin Tian, Andrew Jones, Fangyu Yu, Wu Fei,
Zhao Qingye, Gong Shuai, Gong Shuai
From: Wu Fei <wu.fei9@sanechips.com.cn>
A nested domain describes two-stage translation through guest and
host page tables. Iommufd exposes this domain as a nested hwpt.
User space needs to provide the guest stage 1 configuration when
allocating a nested hwpt. Validate the configuration before storing
it for use in a physical device context.
Signed-off-by: Wu Fei <wu.fei9@sanechips.com.cn>
Co-developed-by: Gong Shuai <gong.shuai@sanechips.com.cn>
Signed-off-by: Gong Shuai <gong.shuai@sanechips.com.cn>
---
drivers/iommu/riscv/iommu.c | 151 ++++++++++++++++++++++++++++++++++++
1 file changed, 151 insertions(+)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index a8c19c9e45d4..371d60902eaa 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1662,7 +1662,158 @@ struct riscv_viommu {
int gscid;
};
+struct riscv_iommu_nested_domain {
+ struct iommu_domain domain;
+ struct riscv_viommu *viommu;
+
+ u64 tc;
+ u64 iosatp;
+};
+
+#define to_riscv_iommu_nested_domain(domain) \
+ container_of(domain, struct riscv_iommu_nested_domain, domain)
+
+static void riscv_iommu_domain_nested_free(struct iommu_domain *domain)
+{
+ kfree(to_riscv_iommu_nested_domain(domain));
+}
+
+static const struct iommu_domain_ops riscv_iommu_nested_ops = {
+ .free = riscv_iommu_domain_nested_free,
+};
+
+static int riscv_iommu_validate_vdc(struct riscv_iommu_device *iommu,
+ struct iommu_hwpt_riscv *arg)
+{
+ u64 tc = arg->dc[0];
+ u64 fsc = arg->dc[3];
+ u64 mode;
+
+ if (!(tc & RISCV_IOMMU_DC_TC_V)) {
+ arg->dc[0] = 0;
+ arg->dc[3] = 0;
+ return 0;
+ }
+
+ /*
+ * Reject reserved/custom bits, the physical IOMMU may treat the
+ * DC as misconfigured.
+ */
+ if (tc & ~RISCV_IOMMU_DC_TC_DEFINED)
+ return -EINVAL;
+ if (fsc & ~(RISCV_IOMMU_DC_FSC_PPN | RISCV_IOMMU_DC_FSC_MODE))
+ return -EINVAL;
+
+ /* Process directory tables are not supported now. */
+ if (tc & RISCV_IOMMU_DC_TC_PDTV)
+ return -EOPNOTSUPP;
+
+ /* With PDTV=0 the DPE bit is reserved for future use. */
+ if (tc & RISCV_IOMMU_DC_TC_DPE)
+ return -EINVAL;
+
+ /* Only 64-bit (SXL=0) stage-1 is supported. */
+ if (tc & RISCV_IOMMU_DC_TC_SXL)
+ return -EOPNOTSUPP;
+
+ /* ATS/PRI/T2GPA/PRPR consistency. */
+ if (!(iommu->caps & RISCV_IOMMU_CAPABILITIES_ATS) &&
+ (tc & (RISCV_IOMMU_DC_TC_EN_ATS | RISCV_IOMMU_DC_TC_EN_PRI |
+ RISCV_IOMMU_DC_TC_PRPR)))
+ return -EINVAL;
+ if ((tc & RISCV_IOMMU_DC_TC_EN_PRI) &&
+ !(tc & RISCV_IOMMU_DC_TC_EN_ATS))
+ return -EINVAL;
+ if ((tc & RISCV_IOMMU_DC_TC_PRPR) &&
+ !(tc & RISCV_IOMMU_DC_TC_EN_PRI))
+ return -EINVAL;
+ if ((tc & RISCV_IOMMU_DC_TC_T2GPA) &&
+ (!(iommu->caps & RISCV_IOMMU_CAPABILITIES_T2GPA) ||
+ !(tc & RISCV_IOMMU_DC_TC_EN_ATS)))
+ return -EINVAL;
+
+ /*
+ * A/D update behavior on stage-2 PTEs is owned by the host,
+ * decide it's value from the AMO_HWAD capability.
+ */
+ tc &= ~RISCV_IOMMU_DC_TC_GADE;
+ if (iommu->caps & RISCV_IOMMU_CAPABILITIES_AMO_HWAD)
+ tc |= RISCV_IOMMU_DC_TC_GADE;
+
+ /*
+ * A/D update behavior on stage-1 PTEs is owned by the guest but
+ * needs AMO_HWAD support.
+ */
+ if ((tc & RISCV_IOMMU_DC_TC_SADE) &&
+ !(iommu->caps & RISCV_IOMMU_CAPABILITIES_AMO_HWAD))
+ return -EOPNOTSUPP;
+
+ /* SBE must match the IOMMU byte order unless END is supported. */
+ if (!(iommu->caps & RISCV_IOMMU_CAPABILITIES_END) &&
+ (!!(tc & RISCV_IOMMU_DC_TC_SBE) !=
+ !!(iommu->fctl & RISCV_IOMMU_FCTL_BE)))
+ return -EINVAL;
+
+ /* Validate the stage-1 mode. */
+ mode = FIELD_GET(RISCV_IOMMU_DC_FSC_MODE, fsc);
+ if (mode == RISCV_IOMMU_DC_FSC_MODE_BARE) {
+ /* No stage-1 translation, root pointer is unused. */
+ fsc = 0;
+ } else {
+ switch (mode) {
+ case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV39:
+ case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV48:
+ case RISCV_IOMMU_DC_FSC_IOSATP_MODE_SV57:
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (!riscv_iommu_fsc_supported(iommu, mode))
+ return -EOPNOTSUPP;
+ }
+
+ arg->dc[0] = tc;
+ arg->dc[3] = fsc;
+
+ return 0;
+}
+
+static struct iommu_domain *
+riscv_viommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
+ const struct iommu_user_data *user_data)
+{
+ struct riscv_viommu *rv_viommu = container_of(viommu, struct riscv_viommu, core);
+ struct riscv_iommu_nested_domain *nested_domain;
+ struct iommu_hwpt_riscv arg;
+ int ret;
+
+ if (flags)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ ret = iommu_copy_struct_from_user(&arg, user_data,
+ IOMMU_HWPT_DATA_RISCV_S1, dc);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = riscv_iommu_validate_vdc(rv_viommu->iommu, &arg);
+ if (ret)
+ return ERR_PTR(ret);
+
+ nested_domain = kzalloc_obj(*nested_domain, GFP_KERNEL_ACCOUNT);
+ if (!nested_domain)
+ return ERR_PTR(-ENOMEM);
+
+ nested_domain->domain.type = IOMMU_DOMAIN_NESTED;
+ nested_domain->domain.ops = &riscv_iommu_nested_ops;
+ nested_domain->viommu = rv_viommu;
+ nested_domain->tc = arg.dc[0];
+ nested_domain->iosatp = arg.dc[3];
+
+ return &nested_domain->domain;
+}
+
static const struct iommufd_viommu_ops riscv_viommu_ops = {
+ .alloc_domain_nested = riscv_viommu_alloc_domain_nested,
};
static size_t riscv_iommu_get_viommu_size(struct device *dev,
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [RFC PATCH v2 6/9] iommu/riscv: Add vIOMMU cache invalidation
2026-10-29 6:11 [RFC PATCH v2 0/9] iommu/riscv: Add nested translation (vIOMMU) support Gong Shuai
` (4 preceding siblings ...)
2026-10-29 6:11 ` [RFC PATCH v2 5/9] iommu/riscv: Add nested domain allocation Gong Shuai
@ 2026-10-29 6:11 ` Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 7/9] iommufd: Add RISC-V IOMMU hardware info type Gong Shuai
` (2 subsequent siblings)
8 siblings, 0 replies; 12+ messages in thread
From: Gong Shuai @ 2026-10-29 6:11 UTC (permalink / raw)
To: kvm, kvm-riscv, linux-riscv, linux-kernel, iommu
Cc: Anup Patel, Atish Patra, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Tomasz Jeznach, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Paolo Bonzini, Sean Christopherson,
Jason Gunthorpe, Kevin Tian, Andrew Jones, Fangyu Yu, Wu Fei,
Zhao Qingye, Gong Shuai, Gong Shuai
Guest page table updates require IOTINVAL.VMA commands to invalidate
cached stage 1 translations in the physical IOMMU. Allow user space
to pass these commands through the vIOMMU.
Do not forward guest commands unchanged. Validate them and replace
the guest isolation operands with host-assigned values so a guest
cannot invalidate cached translations belonging to another guest.
Co-developed-by: Wu Fei <wu.fei9@sanechips.com.cn>
Signed-off-by: Wu Fei <wu.fei9@sanechips.com.cn>
Signed-off-by: Gong Shuai <gong.shuai@sanechips.com.cn>
---
drivers/iommu/riscv/iommu-bits.h | 13 +++++
drivers/iommu/riscv/iommu.c | 93 ++++++++++++++++++++++++++++++++
2 files changed, 106 insertions(+)
diff --git a/drivers/iommu/riscv/iommu-bits.h b/drivers/iommu/riscv/iommu-bits.h
index 80c32f00ee5e..2f32330fcd16 100644
--- a/drivers/iommu/riscv/iommu-bits.h
+++ b/drivers/iommu/riscv/iommu-bits.h
@@ -486,6 +486,19 @@ struct riscv_iommu_command {
/* dword1[61:10] is the 4K-aligned page address */
#define RISCV_IOMMU_CMD1_IOTINVAL_ADDR GENMASK_ULL(61, 10)
+/*
+ * All standard bits of the IOTINVAL command. The remaining bits are
+ * reserved for future standard use, forwarding them to the physical IOMMU
+ * may make it treat the command as illegal.
+ */
+#define RISCV_IOMMU_CMD0_IOTINVAL_STANDARD \
+ (RISCV_IOMMU_CMD0_OPCODE | RISCV_IOMMU_CMD0_FUNC | \
+ RISCV_IOMMU_CMD0_IOTINVAL_AV | RISCV_IOMMU_CMD0_IOTINVAL_PSCID | \
+ RISCV_IOMMU_CMD0_IOTINVAL_PSCV | RISCV_IOMMU_CMD0_IOTINVAL_GV | \
+ RISCV_IOMMU_CMD0_IOTINVAL_NL | RISCV_IOMMU_CMD0_IOTINVAL_GSCID)
+#define RISCV_IOMMU_CMD1_IOTINVAL_STANDARD \
+ (RISCV_IOMMU_CMD1_IOTINVAL_S | RISCV_IOMMU_CMD1_IOTINVAL_ADDR)
+
/* 3.1.2 IOMMU Command Queue Fences */
/* Fields on dword0 */
#define RISCV_IOMMU_CMD_IOFENCE_OPCODE 2
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 371d60902eaa..6bcf214016c3 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1812,8 +1812,101 @@ riscv_viommu_alloc_domain_nested(struct iommufd_viommu *viommu, u32 flags,
return &nested_domain->domain;
}
+/*
+ * One invalidation command, viewed as either the little-endian uAPI
+ * payload or the native command words.
+ */
+struct riscv_viommu_invalidation_cmd {
+ union {
+ struct riscv_iommu_command cmd;
+ struct iommu_viommu_riscv_invalidate ucmd;
+ };
+};
+
+static int riscv_viommu_cache_invalidate(struct iommufd_viommu *viommu,
+ struct iommu_user_data_array *array)
+{
+ struct riscv_viommu *rv_viommu = container_of(viommu, struct riscv_viommu, core);
+ struct riscv_iommu_device *iommu = rv_viommu->iommu;
+ struct riscv_viommu_invalidation_cmd *cmds;
+ unsigned int i = 0;
+ int ret;
+
+ if (array->type != IOMMU_VIOMMU_INVALIDATE_DATA_RISCV)
+ return -EINVAL;
+
+ /* entry_num==0 is the data_type support probe */
+ if (!array->entry_num)
+ return 0;
+
+ static_assert(sizeof(*cmds) == 2 * sizeof(u64));
+
+ cmds = kcalloc(array->entry_num, sizeof(*cmds), GFP_KERNEL);
+ if (!cmds)
+ return -ENOMEM;
+
+ ret = iommu_copy_struct_from_full_user_array(cmds, sizeof(*cmds), array,
+ IOMMU_VIOMMU_INVALIDATE_DATA_RISCV);
+ if (ret)
+ goto out_free;
+
+ for (i = 0; i < array->entry_num; i++) {
+ struct riscv_iommu_command *cmd = &cmds[i].cmd;
+ u64 opcode, func;
+
+ /* The uAPI payload is little-endian */
+ cmd->dword0 = le64_to_cpu(cmds[i].ucmd.cmd[0]);
+ cmd->dword1 = le64_to_cpu(cmds[i].ucmd.cmd[1]);
+
+ opcode = FIELD_GET(RISCV_IOMMU_CMD0_OPCODE, cmd->dword0);
+ if (opcode != RISCV_IOMMU_CMD_IOTINVAL_OPCODE) {
+ /* Only page-table cache invalidation is forwarded */
+ ret = -EIO;
+ goto out_free;
+ }
+
+ /* Reject reserved bits instead of forwarding them to the physical IOMMU */
+ if (cmd->dword0 & ~RISCV_IOMMU_CMD0_IOTINVAL_STANDARD ||
+ cmd->dword1 & ~RISCV_IOMMU_CMD1_IOTINVAL_STANDARD) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ func = FIELD_GET(RISCV_IOMMU_CMD0_FUNC, cmd->dword0);
+ if (func != RISCV_IOMMU_CMD_IOTINVAL_FUNC_VMA) {
+ ret = -EIO;
+ goto out_free;
+ }
+
+ /*
+ * The guest operates in its own GSCID/PSCID namespace.
+ * The physical IOMMU uses the host-assigned GSCID and
+ * PSCID=0. Enforce both for correct cache invalidation.
+ */
+ cmd->dword0 &= ~(RISCV_IOMMU_CMD0_IOTINVAL_GV |
+ RISCV_IOMMU_CMD0_IOTINVAL_GSCID |
+ RISCV_IOMMU_CMD0_IOTINVAL_PSCV |
+ RISCV_IOMMU_CMD0_IOTINVAL_PSCID);
+ cmd->dword0 |= FIELD_PREP(RISCV_IOMMU_CMD0_IOTINVAL_GSCID,
+ rv_viommu->gscid) |
+ RISCV_IOMMU_CMD0_IOTINVAL_GV |
+ FIELD_PREP(RISCV_IOMMU_CMD0_IOTINVAL_PSCID, 0) |
+ RISCV_IOMMU_CMD0_IOTINVAL_PSCV;
+
+ riscv_iommu_cmd_send(iommu, cmd);
+ }
+
+ riscv_iommu_cmd_sync(iommu, RISCV_IOMMU_IOTINVAL_TIMEOUT);
+
+out_free:
+ array->entry_num = i;
+ kfree(cmds);
+ return ret;
+}
+
static const struct iommufd_viommu_ops riscv_viommu_ops = {
.alloc_domain_nested = riscv_viommu_alloc_domain_nested,
+ .cache_invalidate = riscv_viommu_cache_invalidate,
};
static size_t riscv_iommu_get_viommu_size(struct device *dev,
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [RFC PATCH v2 7/9] iommufd: Add RISC-V IOMMU hardware info type
2026-10-29 6:11 [RFC PATCH v2 0/9] iommu/riscv: Add nested translation (vIOMMU) support Gong Shuai
` (5 preceding siblings ...)
2026-10-29 6:11 ` [RFC PATCH v2 6/9] iommu/riscv: Add vIOMMU cache invalidation Gong Shuai
@ 2026-10-29 6:11 ` Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 8/9] iommu/riscv: Add riscv_iommu_hw_info Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 9/9] iommu/riscv: Add riscv_iommu_attach_dev_nested Gong Shuai
8 siblings, 0 replies; 12+ messages in thread
From: Gong Shuai @ 2026-10-29 6:11 UTC (permalink / raw)
To: kvm, kvm-riscv, linux-riscv, linux-kernel, iommu
Cc: Anup Patel, Atish Patra, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Tomasz Jeznach, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Paolo Bonzini, Sean Christopherson,
Jason Gunthorpe, Kevin Tian, Andrew Jones, Fangyu Yu, Wu Fei,
Zhao Qingye, Gong Shuai, Gong Shuai
From: Wu Fei <wu.fei9@sanechips.com.cn>
A VMM needs the physical IOMMU capabilities to decide which features
it can expose to a guest. Define IOMMU_HW_INFO_TYPE_RISCV and struct
iommu_hw_info_riscv so IOMMU_GET_HW_INFO can return the RISC-V
capabilities register.
Signed-off-by: Wu Fei <wu.fei9@sanechips.com.cn>
Signed-off-by: Gong Shuai <gong.shuai@sanechips.com.cn>
---
include/uapi/linux/iommufd.h | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h
index aa65d494b992..bdabc8e64323 100644
--- a/include/uapi/linux/iommufd.h
+++ b/include/uapi/linux/iommufd.h
@@ -731,6 +731,28 @@ struct iommu_hw_info_amd {
__aligned_u64 efr2;
};
+/**
+ * struct iommu_hw_info_riscv - RISC-V IOMMU device info
+ * (IOMMU_HW_INFO_TYPE_RISCV)
+ *
+ * @capabilities: Value of the RISC-V IOMMU capabilities register, defined
+ * in "IOMMU capabilities (capabilities)" of the RISC-V
+ * IOMMU Architecture Specification. The value is reported
+ * raw by the hardware.
+ *
+ * The VMM is responsible to determine the appropriate fields to be
+ * exposed to the VM since certain features are not currently supported
+ * by the kernel for nested domains. Current list of supported fields:
+ * - capabilities[Sv39, Sv48, Sv57, END, AMO_HWAD]
+ *
+ * This list may expand in the future. It is important that VMMs do not
+ * expose bits outside the list to the VM, to allow for compatibility
+ * with future kernels.
+ */
+struct iommu_hw_info_riscv {
+ __aligned_u64 capabilities;
+};
+
/**
* enum iommu_hw_info_type - IOMMU Hardware Info Types
* @IOMMU_HW_INFO_TYPE_NONE: Output by the drivers that do not report hardware
@@ -741,6 +763,7 @@ struct iommu_hw_info_amd {
* @IOMMU_HW_INFO_TYPE_TEGRA241_CMDQV: NVIDIA Tegra241 CMDQV (extension for ARM
* SMMUv3) info type
* @IOMMU_HW_INFO_TYPE_AMD: AMD IOMMU info type
+ * @IOMMU_HW_INFO_TYPE_RISCV: RISC-V IOMMU info type
*/
enum iommu_hw_info_type {
IOMMU_HW_INFO_TYPE_NONE = 0,
@@ -749,6 +772,7 @@ enum iommu_hw_info_type {
IOMMU_HW_INFO_TYPE_ARM_SMMUV3 = 2,
IOMMU_HW_INFO_TYPE_TEGRA241_CMDQV = 3,
IOMMU_HW_INFO_TYPE_AMD = 4,
+ IOMMU_HW_INFO_TYPE_RISCV = 5,
};
/**
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [RFC PATCH v2 8/9] iommu/riscv: Add riscv_iommu_hw_info
2026-10-29 6:11 [RFC PATCH v2 0/9] iommu/riscv: Add nested translation (vIOMMU) support Gong Shuai
` (6 preceding siblings ...)
2026-10-29 6:11 ` [RFC PATCH v2 7/9] iommufd: Add RISC-V IOMMU hardware info type Gong Shuai
@ 2026-10-29 6:11 ` Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 9/9] iommu/riscv: Add riscv_iommu_attach_dev_nested Gong Shuai
8 siblings, 0 replies; 12+ messages in thread
From: Gong Shuai @ 2026-10-29 6:11 UTC (permalink / raw)
To: kvm, kvm-riscv, linux-riscv, linux-kernel, iommu
Cc: Anup Patel, Atish Patra, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Tomasz Jeznach, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Paolo Bonzini, Sean Christopherson,
Jason Gunthorpe, Kevin Tian, Andrew Jones, Fangyu Yu, Wu Fei,
Zhao Qingye, Gong Shuai, Gong Shuai
From: Wu Fei <wu.fei9@sanechips.com.cn>
Implement the hw_info op to return the physical IOMMU capabilities
register. This allows IOMMU_GET_HW_INFO to report the available host
features to a VMM.
Signed-off-by: Wu Fei <wu.fei9@sanechips.com.cn>
Signed-off-by: Gong Shuai <gong.shuai@sanechips.com.cn>
---
drivers/iommu/riscv/iommu.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 6bcf214016c3..44fab79110ac 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1940,9 +1940,27 @@ static int riscv_viommu_init(struct iommufd_viommu *viommu,
return 0;
}
+static void *riscv_iommu_hw_info(struct device *dev, u32 *length,
+ enum iommu_hw_info_type *type)
+{
+ struct iommu_hw_info_riscv *info;
+ struct riscv_iommu_device *iommu = dev_to_iommu(dev);
+
+ info = kzalloc_obj(*info);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+
+ info->capabilities = iommu->caps;
+ *length = sizeof(*info);
+ *type = IOMMU_HW_INFO_TYPE_RISCV;
+
+ return info;
+}
+
static const struct iommu_ops riscv_iommu_ops = {
.of_xlate = riscv_iommu_of_xlate,
.capable = riscv_iommu_capable,
+ .hw_info = riscv_iommu_hw_info,
.identity_domain = &riscv_iommu_identity_domain,
.blocked_domain = &riscv_iommu_blocking_domain,
.release_domain = &riscv_iommu_blocking_domain,
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [RFC PATCH v2 9/9] iommu/riscv: Add riscv_iommu_attach_dev_nested
2026-10-29 6:11 [RFC PATCH v2 0/9] iommu/riscv: Add nested translation (vIOMMU) support Gong Shuai
` (7 preceding siblings ...)
2026-10-29 6:11 ` [RFC PATCH v2 8/9] iommu/riscv: Add riscv_iommu_hw_info Gong Shuai
@ 2026-10-29 6:11 ` Gong Shuai
2026-11-05 14:49 ` Tan Chi
8 siblings, 1 reply; 12+ messages in thread
From: Gong Shuai @ 2026-10-29 6:11 UTC (permalink / raw)
To: kvm, kvm-riscv, linux-riscv, linux-kernel, iommu
Cc: Anup Patel, Atish Patra, Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Tomasz Jeznach, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Paolo Bonzini, Sean Christopherson,
Jason Gunthorpe, Kevin Tian, Andrew Jones, Fangyu Yu, Wu Fei,
Zhao Qingye, Gong Shuai, Gong Shuai
From: Wu Fei <wu.fei9@sanechips.com.cn>
Attach a device to a nested domain by combining the guest provided
stage 1 context with the host-owned stage 2 context in the device
directory entry. This allows the physical IOMMU to translate device
DMA through both stages.
The device is bonded to the stage 2 parent domain so that it shares
the parent's invalidation state.
Signed-off-by: Wu Fei <wu.fei9@sanechips.com.cn>
Co-developed-by: Gong Shuai <gong.shuai@sanechips.com.cn>
Signed-off-by: Gong Shuai <gong.shuai@sanechips.com.cn>
---
drivers/iommu/riscv/iommu.c | 41 +++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 44fab79110ac..a103c0465e62 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1673,12 +1673,53 @@ struct riscv_iommu_nested_domain {
#define to_riscv_iommu_nested_domain(domain) \
container_of(domain, struct riscv_iommu_nested_domain, domain)
+static int riscv_iommu_attach_dev_nested(struct iommu_domain *domain,
+ struct device *dev,
+ struct iommu_domain *old_domain)
+{
+ struct riscv_iommu_nested_domain *nested_domain =
+ to_riscv_iommu_nested_domain(domain);
+ struct riscv_iommu_device *iommu = dev_to_iommu(dev);
+ struct riscv_iommu_domain *s2_parent = nested_domain->viommu->s2_parent;
+ struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
+ struct pt_iommu_riscv_64_hw_info pt_info;
+ struct riscv_iommu_dc dc = {0};
+
+ if (nested_domain->viommu->iommu != iommu)
+ return -EINVAL;
+
+ if (!s2_parent->nest_parent)
+ return -EINVAL;
+
+ pt_iommu_riscv_64_hw_info(&s2_parent->riscvpt, &pt_info);
+
+ dc.tc = nested_domain->tc;
+ dc.fsc = nested_domain->iosatp;
+
+ dc.iohgatp = FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_MODE, pt_info.iohgatp_mode) |
+ FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_PPN, pt_info.ppn) |
+ FIELD_PREP(RISCV_IOMMU_DC_IOHGATP_GSCID, s2_parent->gscid);
+
+ /* SVA/PASID are not supported now, keep PSCID at 0. */
+ dc.ta = FIELD_PREP(RISCV_IOMMU_DC_TA_PSCID, 0);
+
+ if (riscv_iommu_bond_link(s2_parent, dev))
+ return -ENOMEM;
+
+ riscv_iommu_iodir_update(iommu, dev, &dc);
+ riscv_iommu_bond_unlink(info->domain, dev);
+ info->domain = s2_parent;
+
+ return 0;
+}
+
static void riscv_iommu_domain_nested_free(struct iommu_domain *domain)
{
kfree(to_riscv_iommu_nested_domain(domain));
}
static const struct iommu_domain_ops riscv_iommu_nested_ops = {
+ .attach_dev = riscv_iommu_attach_dev_nested,
.free = riscv_iommu_domain_nested_free,
};
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC PATCH v2 9/9] iommu/riscv: Add riscv_iommu_attach_dev_nested
2026-10-29 6:11 ` [RFC PATCH v2 9/9] iommu/riscv: Add riscv_iommu_attach_dev_nested Gong Shuai
@ 2026-11-05 14:49 ` Tan Chi
2026-11-05 18:32 ` Jason Gunthorpe
0 siblings, 1 reply; 12+ messages in thread
From: Tan Chi @ 2026-11-05 14:49 UTC (permalink / raw)
To: gong.shuai
Cc: kvm, kvm-riscv, linux-riscv, linux-kernel, iommu, anup,
atish.patra, pjw, palmer, aou, alex, tomasz.jeznach, joro, will,
robin.murphy, pbonzini, seanjc, jgg, kevin.tian, andrew.jones,
fangyu.yu, wu.fei9, zhao.qingye, gsh517025
Hi Shuai and Fei,
I noticed a potential issue with fixing the physical DC's PSCID to 0.
In QEMU, different VFIO devices can have separate stage-1 HWPTs while
sharing the same vIOMMU and stage-2 parent. This means two devices may
have different first-stage page table roots but the same host GSCID
and PSCID=0.
According to the RISC-V IOMMU specification, DC.ta.PSCID identifies
the first-stage address space when PDTV=0 and the first stage is not
Bare. Could this cause the physical IOMMU to reuse cached translations
across different stage-1 address spaces? Even broader invalidations
would not prevent such cache aliasing.
There also seems to be a related issue in patch 6.
riscv_viommu_cache_invalidate() unconditionally forces PSCV=1 and
PSCID=0. However, an IOTINVAL.VMA with PSCV=0 also invalidates global
mappings, while PSCV=1 excludes them. This appears to narrow the
guest's requested invalidation scope and could leave stale global
translations cached.
One possible approach might be to use the PSCID from the guest-provided
DC.ta when allocating the nested domain, and preserve the guest's PSCID
and PSCV settings during invalidation while substituting the host GSCID.
I'm not sure whether additional considerations are needed for PSCID
reuse or domain lifetime, though.
Thanks,
Tan Chi
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC PATCH v2 9/9] iommu/riscv: Add riscv_iommu_attach_dev_nested
2026-11-05 14:49 ` Tan Chi
@ 2026-11-05 18:32 ` Jason Gunthorpe
0 siblings, 0 replies; 12+ messages in thread
From: Jason Gunthorpe @ 2026-11-05 18:32 UTC (permalink / raw)
To: Tan Chi
Cc: gong.shuai, kvm, kvm-riscv, linux-riscv, linux-kernel, iommu,
anup, atish.patra, pjw, palmer, aou, alex, tomasz.jeznach, joro,
will, robin.murphy, pbonzini, seanjc, kevin.tian, andrew.jones,
fangyu.yu, wu.fei9, zhao.qingye, gsh517025
On Thu, Nov 05, 2026 at 10:49:44PM +0800, Tan Chi wrote:
> Hi Shuai and Fei,
>
> I noticed a potential issue with fixing the physical DC's PSCID to 0.
>
> In QEMU, different VFIO devices can have separate stage-1 HWPTs while
> sharing the same vIOMMU and stage-2 parent. This means two devices may
> have different first-stage page table roots but the same host GSCID
> and PSCID=0.
>
> According to the RISC-V IOMMU specification, DC.ta.PSCID identifies
> the first-stage address space when PDTV=0 and the first stage is not
> Bare. Could this cause the physical IOMMU to reuse cached translations
> across different stage-1 address spaces? Even broader invalidations
> would not prevent such cache aliasing.
>
> There also seems to be a related issue in patch 6.
> riscv_viommu_cache_invalidate() unconditionally forces PSCV=1 and
> PSCID=0. However, an IOTINVAL.VMA with PSCV=0 also invalidates global
> mappings, while PSCV=1 excludes them. This appears to narrow the
> guest's requested invalidation scope and could leave stale global
> translations cached.
>
> One possible approach might be to use the PSCID from the guest-provided
> DC.ta when allocating the nested domain, and preserve the guest's PSCID
> and PSCV settings during invalidation while substituting the host GSCID.
> I'm not sure whether additional considerations are needed for PSCID
> reuse or domain lifetime, though.
If the guest is issuing invalidations you must always preserve the
guests "ASID" or whatever your HW calls it. It can be virtualized and
mapped from a guest view to physical, but it must be preserved and
physical must be 1:1 with virtual.
Jason
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-11-05 18:32 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-29 6:11 [RFC PATCH v2 0/9] iommu/riscv: Add nested translation (vIOMMU) support Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 1/9] iommu/riscv: Use DC.tc.V as the device context valid bit Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 2/9] iommufd: Add RISC-V IOMMU hwpt and vIOMMU types Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 3/9] iommufd: Add RISC-V IOMMU cache invalidation data type Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 4/9] iommu/riscv: Add vIOMMU object support Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 5/9] iommu/riscv: Add nested domain allocation Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 6/9] iommu/riscv: Add vIOMMU cache invalidation Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 7/9] iommufd: Add RISC-V IOMMU hardware info type Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 8/9] iommu/riscv: Add riscv_iommu_hw_info Gong Shuai
2026-10-29 6:11 ` [RFC PATCH v2 9/9] iommu/riscv: Add riscv_iommu_attach_dev_nested Gong Shuai
2026-11-05 14:49 ` Tan Chi
2026-11-05 18:32 ` Jason Gunthorpe
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®