From e49e9c98bdb25318fc99c8182b00aa04b0f524e7 Mon Sep 17 00:00:00 2001 From: Sudarshan Rajagopalan Date: Thu, 16 Aug 2018 15:29:41 -0700 Subject: [PATCH] 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 Signed-off-by: Prakash Gupta --- drivers/iommu/arm-smmu.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index eab3087df00d..6d1a12297aad 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -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);