iommu: arm-smmu: Fail device attach when SMR write fails

On systems with Hypervisor support, writes to SMR registers
are trapped by Hypervisor where the value is vetted and gets
written to the SMR register only if valid. If such SMR writes
fails, the SID value doesn't get populated to the stream mapping
table, and any device transaction would lead to USF which would
crash the system. Hence, to avoid any USF fault, fail the device
attach sequence.

Change-Id: I1435c7aa8290d7c2916cc86cc30cfa1cefae3910
Signed-off-by: Sudarshan Rajagopalan <sudaraja@codeaurora.org>
Signed-off-by: Prakash Gupta <guptap@codeaurora.org>
This commit is contained in:
Sudarshan Rajagopalan 2018-08-16 15:29:41 -07:00 • committed by Prakash Gupta
commit e49e9c98bd

View file

@ -2392,14 +2392,30 @@ static void arm_smmu_domain_free(struct iommu_domain *domain)
kfree(smmu_domain);
}
static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
static int arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
{
struct arm_smmu_smr *smr = smmu->smrs + idx;
u32 reg = FIELD_PREP(SMR_ID, smr->id) | FIELD_PREP(SMR_MASK, smr->mask);
u32 val;
if (!(smmu->features & ARM_SMMU_FEAT_EXIDS) && smr->valid)
reg |= SMR_VALID;
arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_SMR(idx), reg);
/*
* Check if the write went properly. If failed, we would have to fail
* the attach sequence to avoid any USF faults being generated in the
* future due to device transactions, since this SID entry would not
* be present in the stream mapping table.
*/
val = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(idx));
if (val != reg) {
dev_err(smmu->dev, "SMR[%d] write err write:0x%lx, read:0x%lx\n",
idx, reg, val);
return -EINVAL;
}
return 0;
}
static void arm_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx)
@ -2416,11 +2432,14 @@ static void arm_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx)
arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_S2CR(idx), reg);
}
static void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx)
static int arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx)
{
arm_smmu_write_s2cr(smmu, idx);
if (smmu->smrs)
arm_smmu_write_smr(smmu, idx);
if (arm_smmu_write_smr(smmu, idx))
return -EINVAL;
return 0;
}
/*
@ -2674,7 +2693,10 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
s2cr[idx].type = type;
s2cr[idx].privcfg = S2CR_PRIVCFG_DEFAULT;
s2cr[idx].cbndx = cbndx;
arm_smmu_write_sme(smmu, idx);
if (arm_smmu_write_sme(smmu, idx)) {
mutex_unlock(&smmu->stream_map_mutex);
return -EINVAL;
}
}
mutex_unlock(&smmu->stream_map_mutex);