mirror of https://gitee.com/openkylin/linux.git
IOMMU Fixes for Linux v3.17-rc4
Including: * Two fixes for issues found by Coverity * Various fixes for the ARM SMMU driver * A warning fix for the FSL PAMU driver -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJUEvbvAAoJECvwRC2XARrjTbgQAKfSftH7StWiVgR3MyiO1JVy c0AAImQZ2rk7LO1R3xMxDn6DZD9sLTJNy9ogWr5YEyqMIVOdJt7hicrbgo0gGSS1 zU8numZweMPD7tzvFk93GjsSEdy+T4PYpmR2KncYB2OQ3szMd7GqooKohp1P1BfC 3fb9k9rjstnZXn0nKN6KxZ7QWaGme8e02cRz+5U+8N+AYHmV6ZcUumqig6KmYjuP AtyQz2Sj8v2VAnVr+3yOz/bBLBSRChfc1q9aFSrD3mLhTLlJ7uk0+4NviGsPbZww vGvWqy3w8aLzRDA9x+036FU6BoBQtaRdF/JCCPw8yOmfmUi6hA6kvTJbfgxIJPRK ZZM6KBdPWfo8y83dVXsT3NpineA622VhstZVT7NGczDVtimnxI6wjLlNUj0ve4g5 AbaujBbnRtBclEWk23AjLpoiEA3iGw4putvCxFlWaclh0AEX0wx4W2scqCtF6hP5 w3d6HiuYnB5lKl+IMb1jYvybRLc1CrXbnsJGsyjjfYMztv31pY3s0gO9xAQldLCV 8Ah0njJ1GDKwXcmNUAgLcgrnmLIPat6IMT44vH15OYgjczsx0mN8ZChto24iOwx1 ZrSfBWQ2IVylHyIuGrIe6C1+HR6ixrzcOaCXJaa7cTvo7vXWL6E8KZa4uth74Jq1 VjmkefTgBjtrs656E+fr =jHx2 -----END PGP SIGNATURE----- Merge tag 'iommu-fixes-v3.17-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu Pull iommu fixes from Joerg Roedel: - two fixes for issues found by Coverity - various fixes for the ARM SMMU driver - a warning fix for the FSL PAMU driver * tag 'iommu-fixes-v3.17-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: iommu/fsl: Fix warning resulting from adding PCI device twice iommu/arm-smmu: fix corner cases in address size calculations iommu/arm-smmu: fix decimal printf format specifiers prefixed with 0x iommu/arm-smmu: Do not access non-existing S2CR registers iommu/arm-smmu: fix s2cr and smr teardown on device detach from domain iommu/arm-smmu: remove pgtable_page_{c,d}tor() iommu/arm-smmu: fix programming of SMMU_CBn_TCR for stage 1 iommu/arm-smmu: avoid calling request_irq in atomic context iommu/vt-d: Check return value of acpi_bus_get_device() iommu/core: Make iommu_group_get_for_dev() more robust
This commit is contained in:
commit
753a6cb7e4
|
@ -146,6 +146,8 @@
|
|||
#define ID0_CTTW (1 << 14)
|
||||
#define ID0_NUMIRPT_SHIFT 16
|
||||
#define ID0_NUMIRPT_MASK 0xff
|
||||
#define ID0_NUMSIDB_SHIFT 9
|
||||
#define ID0_NUMSIDB_MASK 0xf
|
||||
#define ID0_NUMSMRG_SHIFT 0
|
||||
#define ID0_NUMSMRG_MASK 0xff
|
||||
|
||||
|
@ -524,9 +526,18 @@ static int register_smmu_master(struct arm_smmu_device *smmu,
|
|||
master->of_node = masterspec->np;
|
||||
master->cfg.num_streamids = masterspec->args_count;
|
||||
|
||||
for (i = 0; i < master->cfg.num_streamids; ++i)
|
||||
master->cfg.streamids[i] = masterspec->args[i];
|
||||
for (i = 0; i < master->cfg.num_streamids; ++i) {
|
||||
u16 streamid = masterspec->args[i];
|
||||
|
||||
if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) &&
|
||||
(streamid >= smmu->num_mapping_groups)) {
|
||||
dev_err(dev,
|
||||
"stream ID for master device %s greater than maximum allowed (%d)\n",
|
||||
masterspec->np->name, smmu->num_mapping_groups);
|
||||
return -ERANGE;
|
||||
}
|
||||
master->cfg.streamids[i] = streamid;
|
||||
}
|
||||
return insert_smmu_master(smmu, master);
|
||||
}
|
||||
|
||||
|
@ -623,7 +634,7 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
|
|||
|
||||
if (fsr & FSR_IGN)
|
||||
dev_err_ratelimited(smmu->dev,
|
||||
"Unexpected context fault (fsr 0x%u)\n",
|
||||
"Unexpected context fault (fsr 0x%x)\n",
|
||||
fsr);
|
||||
|
||||
fsynr = readl_relaxed(cb_base + ARM_SMMU_CB_FSYNR0);
|
||||
|
@ -752,6 +763,7 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
|
|||
reg = (TTBCR2_ADDR_36 << TTBCR2_SEP_SHIFT);
|
||||
break;
|
||||
case 39:
|
||||
case 40:
|
||||
reg = (TTBCR2_ADDR_40 << TTBCR2_SEP_SHIFT);
|
||||
break;
|
||||
case 42:
|
||||
|
@ -773,6 +785,7 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
|
|||
reg |= (TTBCR2_ADDR_36 << TTBCR2_PASIZE_SHIFT);
|
||||
break;
|
||||
case 39:
|
||||
case 40:
|
||||
reg |= (TTBCR2_ADDR_40 << TTBCR2_PASIZE_SHIFT);
|
||||
break;
|
||||
case 42:
|
||||
|
@ -843,8 +856,11 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
|
|||
reg |= TTBCR_EAE |
|
||||
(TTBCR_SH_IS << TTBCR_SH0_SHIFT) |
|
||||
(TTBCR_RGN_WBWA << TTBCR_ORGN0_SHIFT) |
|
||||
(TTBCR_RGN_WBWA << TTBCR_IRGN0_SHIFT) |
|
||||
(TTBCR_SL0_LVL_1 << TTBCR_SL0_SHIFT);
|
||||
(TTBCR_RGN_WBWA << TTBCR_IRGN0_SHIFT);
|
||||
|
||||
if (!stage1)
|
||||
reg |= (TTBCR_SL0_LVL_1 << TTBCR_SL0_SHIFT);
|
||||
|
||||
writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR);
|
||||
|
||||
/* MAIR0 (stage-1 only) */
|
||||
|
@ -868,10 +884,15 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain)
|
|||
static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
||||
struct arm_smmu_device *smmu)
|
||||
{
|
||||
int irq, ret, start;
|
||||
int irq, start, ret = 0;
|
||||
unsigned long flags;
|
||||
struct arm_smmu_domain *smmu_domain = domain->priv;
|
||||
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
|
||||
|
||||
spin_lock_irqsave(&smmu_domain->lock, flags);
|
||||
if (smmu_domain->smmu)
|
||||
goto out_unlock;
|
||||
|
||||
if (smmu->features & ARM_SMMU_FEAT_TRANS_NESTED) {
|
||||
/*
|
||||
* We will likely want to change this if/when KVM gets
|
||||
|
@ -890,7 +911,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
|||
ret = __arm_smmu_alloc_bitmap(smmu->context_map, start,
|
||||
smmu->num_context_banks);
|
||||
if (IS_ERR_VALUE(ret))
|
||||
return ret;
|
||||
goto out_unlock;
|
||||
|
||||
cfg->cbndx = ret;
|
||||
if (smmu->version == 1) {
|
||||
|
@ -900,6 +921,10 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
|||
cfg->irptndx = cfg->cbndx;
|
||||
}
|
||||
|
||||
ACCESS_ONCE(smmu_domain->smmu) = smmu;
|
||||
arm_smmu_init_context_bank(smmu_domain);
|
||||
spin_unlock_irqrestore(&smmu_domain->lock, flags);
|
||||
|
||||
irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx];
|
||||
ret = request_irq(irq, arm_smmu_context_fault, IRQF_SHARED,
|
||||
"arm-smmu-context-fault", domain);
|
||||
|
@ -907,15 +932,12 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
|||
dev_err(smmu->dev, "failed to request context IRQ %d (%u)\n",
|
||||
cfg->irptndx, irq);
|
||||
cfg->irptndx = INVALID_IRPTNDX;
|
||||
goto out_free_context;
|
||||
}
|
||||
|
||||
smmu_domain->smmu = smmu;
|
||||
arm_smmu_init_context_bank(smmu_domain);
|
||||
return 0;
|
||||
|
||||
out_free_context:
|
||||
__arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx);
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&smmu_domain->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -975,7 +997,6 @@ static void arm_smmu_free_ptes(pmd_t *pmd)
|
|||
{
|
||||
pgtable_t table = pmd_pgtable(*pmd);
|
||||
|
||||
pgtable_page_dtor(table);
|
||||
__free_page(table);
|
||||
}
|
||||
|
||||
|
@ -1108,6 +1129,9 @@ static void arm_smmu_master_free_smrs(struct arm_smmu_device *smmu,
|
|||
void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
|
||||
struct arm_smmu_smr *smrs = cfg->smrs;
|
||||
|
||||
if (!smrs)
|
||||
return;
|
||||
|
||||
/* Invalidate the SMRs before freeing back to the allocator */
|
||||
for (i = 0; i < cfg->num_streamids; ++i) {
|
||||
u8 idx = smrs[i].idx;
|
||||
|
@ -1120,20 +1144,6 @@ static void arm_smmu_master_free_smrs(struct arm_smmu_device *smmu,
|
|||
kfree(smrs);
|
||||
}
|
||||
|
||||
static void arm_smmu_bypass_stream_mapping(struct arm_smmu_device *smmu,
|
||||
struct arm_smmu_master_cfg *cfg)
|
||||
{
|
||||
int i;
|
||||
void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
|
||||
|
||||
for (i = 0; i < cfg->num_streamids; ++i) {
|
||||
u16 sid = cfg->streamids[i];
|
||||
|
||||
writel_relaxed(S2CR_TYPE_BYPASS,
|
||||
gr0_base + ARM_SMMU_GR0_S2CR(sid));
|
||||
}
|
||||
}
|
||||
|
||||
static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
|
||||
struct arm_smmu_master_cfg *cfg)
|
||||
{
|
||||
|
@ -1160,23 +1170,30 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
|
|||
static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
|
||||
struct arm_smmu_master_cfg *cfg)
|
||||
{
|
||||
int i;
|
||||
struct arm_smmu_device *smmu = smmu_domain->smmu;
|
||||
void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
|
||||
|
||||
/*
|
||||
* We *must* clear the S2CR first, because freeing the SMR means
|
||||
* that it can be re-allocated immediately.
|
||||
*/
|
||||
arm_smmu_bypass_stream_mapping(smmu, cfg);
|
||||
for (i = 0; i < cfg->num_streamids; ++i) {
|
||||
u32 idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
|
||||
|
||||
writel_relaxed(S2CR_TYPE_BYPASS,
|
||||
gr0_base + ARM_SMMU_GR0_S2CR(idx));
|
||||
}
|
||||
|
||||
arm_smmu_master_free_smrs(smmu, cfg);
|
||||
}
|
||||
|
||||
static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
int ret;
|
||||
struct arm_smmu_domain *smmu_domain = domain->priv;
|
||||
struct arm_smmu_device *smmu;
|
||||
struct arm_smmu_device *smmu, *dom_smmu;
|
||||
struct arm_smmu_master_cfg *cfg;
|
||||
unsigned long flags;
|
||||
|
||||
smmu = dev_get_master_dev(dev)->archdata.iommu;
|
||||
if (!smmu) {
|
||||
|
@ -1188,20 +1205,22 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
|||
* Sanity check the domain. We don't support domains across
|
||||
* different SMMUs.
|
||||
*/
|
||||
spin_lock_irqsave(&smmu_domain->lock, flags);
|
||||
if (!smmu_domain->smmu) {
|
||||
dom_smmu = ACCESS_ONCE(smmu_domain->smmu);
|
||||
if (!dom_smmu) {
|
||||
/* Now that we have a master, we can finalise the domain */
|
||||
ret = arm_smmu_init_domain_context(domain, smmu);
|
||||
if (IS_ERR_VALUE(ret))
|
||||
goto err_unlock;
|
||||
} else if (smmu_domain->smmu != smmu) {
|
||||
return ret;
|
||||
|
||||
dom_smmu = smmu_domain->smmu;
|
||||
}
|
||||
|
||||
if (dom_smmu != smmu) {
|
||||
dev_err(dev,
|
||||
"cannot attach to SMMU %s whilst already attached to domain on SMMU %s\n",
|
||||
dev_name(smmu_domain->smmu->dev),
|
||||
dev_name(smmu->dev));
|
||||
goto err_unlock;
|
||||
dev_name(smmu_domain->smmu->dev), dev_name(smmu->dev));
|
||||
return -EINVAL;
|
||||
}
|
||||
spin_unlock_irqrestore(&smmu_domain->lock, flags);
|
||||
|
||||
/* Looks ok, so add the device to the domain */
|
||||
cfg = find_smmu_master_cfg(smmu_domain->smmu, dev);
|
||||
|
@ -1209,10 +1228,6 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
|||
return -ENODEV;
|
||||
|
||||
return arm_smmu_domain_add_master(smmu_domain, cfg);
|
||||
|
||||
err_unlock:
|
||||
spin_unlock_irqrestore(&smmu_domain->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
|
||||
|
@ -1247,10 +1262,6 @@ static int arm_smmu_alloc_init_pte(struct arm_smmu_device *smmu, pmd_t *pmd,
|
|||
return -ENOMEM;
|
||||
|
||||
arm_smmu_flush_pgtable(smmu, page_address(table), PAGE_SIZE);
|
||||
if (!pgtable_page_ctor(table)) {
|
||||
__free_page(table);
|
||||
return -ENOMEM;
|
||||
}
|
||||
pmd_populate(NULL, pmd, table);
|
||||
arm_smmu_flush_pgtable(smmu, pmd, sizeof(*pmd));
|
||||
}
|
||||
|
@ -1626,7 +1637,7 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
|
|||
|
||||
/* Mark all SMRn as invalid and all S2CRn as bypass */
|
||||
for (i = 0; i < smmu->num_mapping_groups; ++i) {
|
||||
writel_relaxed(~SMR_VALID, gr0_base + ARM_SMMU_GR0_SMR(i));
|
||||
writel_relaxed(0, gr0_base + ARM_SMMU_GR0_SMR(i));
|
||||
writel_relaxed(S2CR_TYPE_BYPASS,
|
||||
gr0_base + ARM_SMMU_GR0_S2CR(i));
|
||||
}
|
||||
|
@ -1761,6 +1772,9 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
|
|||
dev_notice(smmu->dev,
|
||||
"\tstream matching with %u register groups, mask 0x%x",
|
||||
smmu->num_mapping_groups, mask);
|
||||
} else {
|
||||
smmu->num_mapping_groups = (id >> ID0_NUMSIDB_SHIFT) &
|
||||
ID0_NUMSIDB_MASK;
|
||||
}
|
||||
|
||||
/* ID1 */
|
||||
|
@ -1794,11 +1808,16 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
|
|||
* Stage-1 output limited by stage-2 input size due to pgd
|
||||
* allocation (PTRS_PER_PGD).
|
||||
*/
|
||||
if (smmu->features & ARM_SMMU_FEAT_TRANS_NESTED) {
|
||||
#ifdef CONFIG_64BIT
|
||||
smmu->s1_output_size = min_t(unsigned long, VA_BITS, size);
|
||||
smmu->s1_output_size = min_t(unsigned long, VA_BITS, size);
|
||||
#else
|
||||
smmu->s1_output_size = min(32UL, size);
|
||||
smmu->s1_output_size = min(32UL, size);
|
||||
#endif
|
||||
} else {
|
||||
smmu->s1_output_size = min_t(unsigned long, PHYS_MASK_SHIFT,
|
||||
size);
|
||||
}
|
||||
|
||||
/* The stage-2 output mask is also applied for bypass */
|
||||
size = arm_smmu_id_size_to_bits((id >> ID2_OAS_SHIFT) & ID2_OAS_MASK);
|
||||
|
@ -1889,6 +1908,10 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
|
|||
smmu->irqs[i] = irq;
|
||||
}
|
||||
|
||||
err = arm_smmu_device_cfg_probe(smmu);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
i = 0;
|
||||
smmu->masters = RB_ROOT;
|
||||
while (!of_parse_phandle_with_args(dev->of_node, "mmu-masters",
|
||||
|
@ -1905,10 +1928,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
|
|||
}
|
||||
dev_notice(dev, "registered %d master devices\n", i);
|
||||
|
||||
err = arm_smmu_device_cfg_probe(smmu);
|
||||
if (err)
|
||||
goto out_put_masters;
|
||||
|
||||
parse_driver_options(smmu);
|
||||
|
||||
if (smmu->version > 1 &&
|
||||
|
|
|
@ -678,8 +678,7 @@ static int __init dmar_acpi_dev_scope_init(void)
|
|||
andd->device_name);
|
||||
continue;
|
||||
}
|
||||
acpi_bus_get_device(h, &adev);
|
||||
if (!adev) {
|
||||
if (acpi_bus_get_device(h, &adev)) {
|
||||
pr_err("Failed to get device for ACPI object %s\n",
|
||||
andd->device_name);
|
||||
continue;
|
||||
|
|
|
@ -984,7 +984,7 @@ static int fsl_pamu_add_device(struct device *dev)
|
|||
struct iommu_group *group = ERR_PTR(-ENODEV);
|
||||
struct pci_dev *pdev;
|
||||
const u32 *prop;
|
||||
int ret, len;
|
||||
int ret = 0, len;
|
||||
|
||||
/*
|
||||
* For platform devices we allocate a separate group for
|
||||
|
@ -1007,7 +1007,13 @@ static int fsl_pamu_add_device(struct device *dev)
|
|||
if (IS_ERR(group))
|
||||
return PTR_ERR(group);
|
||||
|
||||
ret = iommu_group_add_device(group, dev);
|
||||
/*
|
||||
* Check if device has already been added to an iommu group.
|
||||
* Group could have already been created for a PCI device in
|
||||
* the iommu_group_get_for_dev path.
|
||||
*/
|
||||
if (!dev->iommu_group)
|
||||
ret = iommu_group_add_device(group, dev);
|
||||
|
||||
iommu_group_put(group);
|
||||
return ret;
|
||||
|
|
|
@ -678,15 +678,17 @@ static struct iommu_group *iommu_group_get_for_pci_dev(struct pci_dev *pdev)
|
|||
*/
|
||||
struct iommu_group *iommu_group_get_for_dev(struct device *dev)
|
||||
{
|
||||
struct iommu_group *group = ERR_PTR(-EIO);
|
||||
struct iommu_group *group;
|
||||
int ret;
|
||||
|
||||
group = iommu_group_get(dev);
|
||||
if (group)
|
||||
return group;
|
||||
|
||||
if (dev_is_pci(dev))
|
||||
group = iommu_group_get_for_pci_dev(to_pci_dev(dev));
|
||||
if (!dev_is_pci(dev))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
group = iommu_group_get_for_pci_dev(to_pci_dev(dev));
|
||||
|
||||
if (IS_ERR(group))
|
||||
return group;
|
||||
|
|
Loading…
Reference in New Issue