mirror of
https://github.com/torvalds/linux.git
synced 2026-10-10 12:28:02 +02:00
iommu/tegra241-cmdqv: Don't fall back to a freed smmu after devm_krealloc()
__tegra241_cmdqv_probe() uses devm_krealloc() to grow @smmu into the larger
tegra241_cmdqv, which frees the original @smmu once it relocates. A failure
after that returned NULL, and the caller then dereferenced the freed @smmu
on its fallback path.
Return an int and take @smmu by reference instead, then update *smmu to the
reallocated pointer after devm_krealloc() succeeds, so the caller and its
fallback path both use the live @smmu rather than the freed original.
Fixes: 918eb5c856 ("iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Will Deacon <will@kernel.org>
This commit is contained in:
parent
5acd67ceb3
commit
d4d05f55e9
|
|
@ -944,16 +944,22 @@ static int tegra241_cmdqv_init_structures(struct arm_smmu_device *smmu)
|
|||
static struct dentry *cmdqv_debugfs_dir;
|
||||
#endif
|
||||
|
||||
static struct arm_smmu_device *
|
||||
__tegra241_cmdqv_probe(struct arm_smmu_device *smmu, struct resource *res,
|
||||
int irq)
|
||||
/*
|
||||
* Probe the CMDQV and reallocate @smmu into the larger cmdqv->smmu.
|
||||
*
|
||||
* devm_krealloc() may relocate and free the original @smmu, so update *smmu to
|
||||
* the new pointer once it succeeds. The error paths after it do the same, so a
|
||||
* caller falling back keeps a live @smmu instead of the freed original.
|
||||
*/
|
||||
static int __tegra241_cmdqv_probe(struct arm_smmu_device **smmu,
|
||||
struct resource *res, int irq)
|
||||
{
|
||||
static const struct arm_smmu_impl_ops init_ops = {
|
||||
.init_structures = tegra241_cmdqv_init_structures,
|
||||
.device_remove = tegra241_cmdqv_remove,
|
||||
};
|
||||
struct tegra241_cmdqv *cmdqv = NULL;
|
||||
struct arm_smmu_device *new_smmu;
|
||||
struct device *dev = (*smmu)->dev;
|
||||
struct tegra241_cmdqv *cmdqv;
|
||||
void __iomem *base;
|
||||
u32 regval;
|
||||
int ret;
|
||||
|
|
@ -962,25 +968,28 @@ __tegra241_cmdqv_probe(struct arm_smmu_device *smmu, struct resource *res,
|
|||
|
||||
base = ioremap(res->start, resource_size(res));
|
||||
if (!base) {
|
||||
dev_err(smmu->dev, "failed to ioremap\n");
|
||||
return NULL;
|
||||
dev_err(dev, "failed to ioremap\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
regval = readl(base + TEGRA241_CMDQV_CONFIG);
|
||||
if (disable_cmdqv) {
|
||||
dev_info(smmu->dev, "Detected disable_cmdqv=true\n");
|
||||
dev_info(dev, "Detected disable_cmdqv=true\n");
|
||||
writel(regval & ~CMDQV_EN, base + TEGRA241_CMDQV_CONFIG);
|
||||
ret = -ENODEV;
|
||||
goto iounmap;
|
||||
}
|
||||
|
||||
cmdqv = devm_krealloc(smmu->dev, smmu, sizeof(*cmdqv), GFP_KERNEL);
|
||||
if (!cmdqv)
|
||||
cmdqv = devm_krealloc(dev, *smmu, sizeof(*cmdqv), GFP_KERNEL);
|
||||
if (!cmdqv) {
|
||||
ret = -ENOMEM;
|
||||
goto iounmap;
|
||||
new_smmu = &cmdqv->smmu;
|
||||
}
|
||||
*smmu = &cmdqv->smmu;
|
||||
|
||||
cmdqv->irq = irq;
|
||||
cmdqv->base = base;
|
||||
cmdqv->dev = smmu->impl_dev;
|
||||
cmdqv->dev = (*smmu)->impl_dev;
|
||||
cmdqv->base_phys = res->start;
|
||||
|
||||
regval = readl_relaxed(REG_CMDQV(cmdqv, PARAM));
|
||||
|
|
@ -992,8 +1001,10 @@ __tegra241_cmdqv_probe(struct arm_smmu_device *smmu, struct resource *res,
|
|||
|
||||
cmdqv->vintfs =
|
||||
kzalloc_objs(*cmdqv->vintfs, cmdqv->num_vintfs);
|
||||
if (!cmdqv->vintfs)
|
||||
if (!cmdqv->vintfs) {
|
||||
ret = -ENOMEM;
|
||||
goto iounmap;
|
||||
}
|
||||
|
||||
ida_init(&cmdqv->vintf_ids);
|
||||
|
||||
|
|
@ -1022,24 +1033,23 @@ __tegra241_cmdqv_probe(struct arm_smmu_device *smmu, struct resource *res,
|
|||
#endif
|
||||
|
||||
/* Provide init-level ops only, until tegra241_cmdqv_init_structures */
|
||||
new_smmu->impl_ops = &init_ops;
|
||||
cmdqv->smmu.impl_ops = &init_ops;
|
||||
|
||||
return new_smmu;
|
||||
return 0;
|
||||
|
||||
free_vintfs:
|
||||
ida_destroy(&cmdqv->vintf_ids);
|
||||
kfree(cmdqv->vintfs);
|
||||
iounmap:
|
||||
iounmap(base);
|
||||
return NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct arm_smmu_device *tegra241_cmdqv_probe(struct arm_smmu_device *smmu)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(smmu->impl_dev);
|
||||
struct arm_smmu_device *new_smmu;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
int irq, ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
|
|
@ -1052,15 +1062,15 @@ struct arm_smmu_device *tegra241_cmdqv_probe(struct arm_smmu_device *smmu)
|
|||
dev_warn(&pdev->dev,
|
||||
"no interrupt. errors will not be reported\n");
|
||||
|
||||
new_smmu = __tegra241_cmdqv_probe(smmu, res, irq);
|
||||
if (new_smmu)
|
||||
return new_smmu;
|
||||
ret = __tegra241_cmdqv_probe(&smmu, res, irq);
|
||||
if (!ret)
|
||||
return smmu;
|
||||
|
||||
out_fallback:
|
||||
dev_info(smmu->impl_dev, "Falling back to standard SMMU CMDQ\n");
|
||||
smmu->options &= ~ARM_SMMU_OPT_TEGRA241_CMDQV;
|
||||
put_device(smmu->impl_dev);
|
||||
return ERR_PTR(-ENODEV);
|
||||
return smmu;
|
||||
}
|
||||
|
||||
/* User space VINTF and VCMDQ Functions */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user