Commit 54cb4594 authored by Eric Auger's avatar Eric Auger Committed by Zheng Zengkai
Browse files

iommu/smmuv3: Get prepared for nested stage support

virt inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I401IF


CVE: NA

------------------------------

When nested stage translation is setup, both s1_cfg and
s2_cfg are set.

We introduce a new smmu_domain abort field that will be set
upon guest stage1 configuration passing. If no guest stage1
config has been attached, it is ignored when writing the STE.

arm_smmu_write_strtab_ent() is modified to write both stage
fields in the STE and deal with the abort field.

In nested mode, only stage 2 is "finalized" as the host does
not own/configure the stage 1 context descriptor; guest does.

Signed-off-by: default avatarEric Auger <eric.auger@redhat.com>
Signed-off-by: default avatarKunkun <Jiang&lt;jiangkunkun@huawei.com>
Reviewed-by: default avatarKeqian Zhu <zhukeqian1@huawei.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
parent a07fcc1f
Loading
Loading
Loading
Loading
+47 −8
Original line number Original line Diff line number Diff line
@@ -1265,7 +1265,8 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
	 * 3. Update Config, sync
	 * 3. Update Config, sync
	 */
	 */
	u64 val = le64_to_cpu(dst[0]);
	u64 val = le64_to_cpu(dst[0]);
	bool ste_live = false;
	bool s1_live = false, s2_live = false, ste_live;
	bool abort, translate = false;
	struct arm_smmu_device *smmu = NULL;
	struct arm_smmu_device *smmu = NULL;
	struct arm_smmu_s1_cfg *s1_cfg;
	struct arm_smmu_s1_cfg *s1_cfg;
	struct arm_smmu_s2_cfg *s2_cfg;
	struct arm_smmu_s2_cfg *s2_cfg;
@@ -1305,6 +1306,7 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
		default:
		default:
			break;
			break;
		}
		}
		translate = s1_cfg->set || s2_cfg->set;
	}
	}


	if (val & STRTAB_STE_0_V) {
	if (val & STRTAB_STE_0_V) {
@@ -1312,23 +1314,36 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
		case STRTAB_STE_0_CFG_BYPASS:
		case STRTAB_STE_0_CFG_BYPASS:
			break;
			break;
		case STRTAB_STE_0_CFG_S1_TRANS:
		case STRTAB_STE_0_CFG_S1_TRANS:
			s1_live = true;
			break;
		case STRTAB_STE_0_CFG_S2_TRANS:
		case STRTAB_STE_0_CFG_S2_TRANS:
			ste_live = true;
			s2_live = true;
			break;
		case STRTAB_STE_0_CFG_NESTED:
			s1_live = true;
			s2_live = true;
			break;
			break;
		case STRTAB_STE_0_CFG_ABORT:
		case STRTAB_STE_0_CFG_ABORT:
			BUG_ON(!disable_bypass);
			break;
			break;
		default:
		default:
			BUG(); /* STE corruption */
			BUG(); /* STE corruption */
		}
		}
	}
	}


	ste_live = s1_live || s2_live;

	/* Nuke the existing STE_0 value, as we're going to rewrite it */
	/* Nuke the existing STE_0 value, as we're going to rewrite it */
	val = STRTAB_STE_0_V;
	val = STRTAB_STE_0_V;


	/* Bypass/fault */
	/* Bypass/fault */
	if (!smmu_domain || !(s1_cfg->set || s2_cfg->set)) {

		if (!smmu_domain && disable_bypass)
	if (!smmu_domain)
		abort = disable_bypass;
	else
		abort = smmu_domain->abort;

	if (abort || !translate) {
		if (abort)
			val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT);
			val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT);
		else
		else
			val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS);
			val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS);
@@ -1346,11 +1361,17 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
		return;
		return;
	}
	}


	if (ste_live) {
		/* First invalidate the live STE */
		dst[0] = cpu_to_le64(STRTAB_STE_0_CFG_ABORT);
		arm_smmu_sync_ste_for_sid(smmu, sid);
	}

	if (s1_cfg->set) {
	if (s1_cfg->set) {
		u64 strw = smmu->features & ARM_SMMU_FEAT_E2H ?
		u64 strw = smmu->features & ARM_SMMU_FEAT_E2H ?
			STRTAB_STE_1_STRW_EL2 : STRTAB_STE_1_STRW_NSEL1;
			STRTAB_STE_1_STRW_EL2 : STRTAB_STE_1_STRW_NSEL1;


		BUG_ON(ste_live);
		BUG_ON(s1_live);
		dst[1] = cpu_to_le64(
		dst[1] = cpu_to_le64(
			 FIELD_PREP(STRTAB_STE_1_S1DSS, STRTAB_STE_1_S1DSS_SSID0) |
			 FIELD_PREP(STRTAB_STE_1_S1DSS, STRTAB_STE_1_S1DSS_SSID0) |
			 FIELD_PREP(STRTAB_STE_1_S1CIR, STRTAB_STE_1_S1C_CACHE_WBRA) |
			 FIELD_PREP(STRTAB_STE_1_S1CIR, STRTAB_STE_1_S1C_CACHE_WBRA) |
@@ -1372,7 +1393,14 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
	}
	}


	if (s2_cfg->set) {
	if (s2_cfg->set) {
		BUG_ON(ste_live);
		u64 vttbr = s2_cfg->vttbr & STRTAB_STE_3_S2TTB_MASK;

		if (s2_live) {
			u64 s2ttb = le64_to_cpu(dst[3]) & STRTAB_STE_3_S2TTB_MASK;

			BUG_ON(s2ttb != vttbr);
		}

		dst[2] = cpu_to_le64(
		dst[2] = cpu_to_le64(
			 FIELD_PREP(STRTAB_STE_2_S2VMID, s2_cfg->vmid) |
			 FIELD_PREP(STRTAB_STE_2_S2VMID, s2_cfg->vmid) |
			 FIELD_PREP(STRTAB_STE_2_VTCR, s2_cfg->vtcr) |
			 FIELD_PREP(STRTAB_STE_2_VTCR, s2_cfg->vtcr) |
@@ -1382,9 +1410,12 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid,
			 STRTAB_STE_2_S2PTW | STRTAB_STE_2_S2AA64 |
			 STRTAB_STE_2_S2PTW | STRTAB_STE_2_S2AA64 |
			 STRTAB_STE_2_S2R);
			 STRTAB_STE_2_S2R);


		dst[3] = cpu_to_le64(s2_cfg->vttbr & STRTAB_STE_3_S2TTB_MASK);
		dst[3] = cpu_to_le64(vttbr);


		val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S2_TRANS);
		val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_S2_TRANS);
	} else {
		dst[2] = 0;
		dst[3] = 0;
	}
	}


	if (master->ats_enabled)
	if (master->ats_enabled)
@@ -2312,6 +2343,14 @@ static int arm_smmu_domain_finalise(struct iommu_domain *domain,
		return 0;
		return 0;
	}
	}


	if (smmu_domain->stage == ARM_SMMU_DOMAIN_NESTED &&
	    (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1) ||
	     !(smmu->features & ARM_SMMU_FEAT_TRANS_S2))) {
		dev_info(smmu_domain->smmu->dev,
			 "does not implement two stages\n");
		return -EINVAL;
	}

	/* Restrict the stage to what we can actually support */
	/* Restrict the stage to what we can actually support */
	if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1))
	if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1))
		smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
		smmu_domain->stage = ARM_SMMU_DOMAIN_S2;
+2 −0
Original line number Original line Diff line number Diff line
@@ -214,6 +214,7 @@
#define STRTAB_STE_0_CFG_BYPASS		4
#define STRTAB_STE_0_CFG_BYPASS		4
#define STRTAB_STE_0_CFG_S1_TRANS	5
#define STRTAB_STE_0_CFG_S1_TRANS	5
#define STRTAB_STE_0_CFG_S2_TRANS	6
#define STRTAB_STE_0_CFG_S2_TRANS	6
#define STRTAB_STE_0_CFG_NESTED		7


#define STRTAB_STE_0_S1FMT		GENMASK_ULL(5, 4)
#define STRTAB_STE_0_S1FMT		GENMASK_ULL(5, 4)
#define STRTAB_STE_0_S1FMT_LINEAR	0
#define STRTAB_STE_0_S1FMT_LINEAR	0
@@ -741,6 +742,7 @@ struct arm_smmu_domain {
	enum arm_smmu_domain_stage	stage;
	enum arm_smmu_domain_stage	stage;
	struct arm_smmu_s1_cfg	s1_cfg;
	struct arm_smmu_s1_cfg	s1_cfg;
	struct arm_smmu_s2_cfg	s2_cfg;
	struct arm_smmu_s2_cfg	s2_cfg;
	bool				abort;


	struct iommu_domain		domain;
	struct iommu_domain		domain;