[PATCH v7 05/10] iommu/riscv: support GSCID and GVMA invalidation command

Gong Shuai gong.shuai at sanechips.com.cn
Wed Nov 4 01:21:11 PST 2026


Hi Fangyu,

> From: Zong Li <zong.li at 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 at sifive.com>
> Signed-off-by: Fangyu Yu <fangyu.yu at linux.alibaba.com>
> Reviewed-by: Nutty Liu <nutty.liu at hotmail.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 918fce4aa8c4..5546dfcd976c 100644
> --- a/drivers/iommu/riscv/iommu.c
> +++ b/drivers/iommu/riscv/iommu.c
> @@ -49,6 +49,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;
> @@ -842,6 +846,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);
>  
> @@ -1004,7 +1009,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 &&
> @@ -1012,8 +1017,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);
> +	}

As I understand it, the same choice is needed in
riscv_iommu_bond_unlink().

The missing GVMA command does not break anything today, because every
caller of bond_unlink() runs iodir_update() just before it, and that
already sends the GVMA for the old GSCID. But depending on that
ordering seems fragile, and the comment in bond_unlink(), "invalidate
all cached entries for domain's PSCID", is no longer true now that we
have S2 domains.

The change, if you think it is needed, fits better in the last patch,
"iommu/riscv: Add IOTINVAL.GVMA invalidations". I am replying here
only because the code is very similar.

Thanks,
Shuai

>  
>  	/*
>  	 * If non-leaf entries were changed and the IOMMU doesn't
> @@ -1097,7 +1107,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;
>  	}
>  
> @@ -1231,6 +1241,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);
> @@ -1281,16 +1292,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;
>  
> @@ -1303,6 +1317,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)
> @@ -1315,11 +1345,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 -EINVAL;
> +
> +		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
> 



More information about the linux-riscv mailing list