firmware: qcom: scm: Cleanup global '__scm' on probe failures

If SCM driver fails the probe, it should not leave global '__scm'
variable assigned, because external users of this driver will assume the
probe finished successfully.  For example TZMEM parts ('__scm->mempool')
are initialized later in the probe, but users of it (__scm_smc_call())
rely on the '__scm' variable.

This fixes theoretical NULL pointer exception, triggered via introducing
probe deferral in SCM driver with call trace:

  qcom_tzmem_alloc+0x70/0x1ac (P)
  qcom_tzmem_alloc+0x64/0x1ac (L)
  qcom_scm_assign_mem+0x78/0x194
  qcom_rmtfs_mem_probe+0x2d4/0x38c
  platform_probe+0x68/0xc8

Fixes: 40289e35ca ("firmware: qcom: scm: enable the TZ mem allocator")
Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Link: https://lore.kernel.org/r/20241209-qcom-scm-missing-barriers-and-all-sort-of-srap-v2-4-9061013c8d92@linaro.org
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
This commit is contained in:
Krzysztof Kozlowski 2024-12-09 15:27:57 +01:00 committed by Bjorn Andersson
parent b628510397
commit 1e76b546e6

View File

@ -2038,13 +2038,17 @@ static int qcom_scm_probe(struct platform_device *pdev)
irq = platform_get_irq_optional(pdev, 0); irq = platform_get_irq_optional(pdev, 0);
if (irq < 0) { if (irq < 0) {
if (irq != -ENXIO) if (irq != -ENXIO) {
return irq; ret = irq;
goto err;
}
} else { } else {
ret = devm_request_threaded_irq(__scm->dev, irq, NULL, qcom_scm_irq_handler, ret = devm_request_threaded_irq(__scm->dev, irq, NULL, qcom_scm_irq_handler,
IRQF_ONESHOT, "qcom-scm", __scm); IRQF_ONESHOT, "qcom-scm", __scm);
if (ret < 0) if (ret < 0) {
return dev_err_probe(scm->dev, ret, "Failed to request qcom-scm irq\n"); dev_err_probe(scm->dev, ret, "Failed to request qcom-scm irq\n");
goto err;
}
} }
__get_convention(); __get_convention();
@ -2063,14 +2067,18 @@ static int qcom_scm_probe(struct platform_device *pdev)
qcom_scm_disable_sdi(); qcom_scm_disable_sdi();
ret = of_reserved_mem_device_init(__scm->dev); ret = of_reserved_mem_device_init(__scm->dev);
if (ret && ret != -ENODEV) if (ret && ret != -ENODEV) {
return dev_err_probe(__scm->dev, ret, dev_err_probe(__scm->dev, ret,
"Failed to setup the reserved memory region for TZ mem\n"); "Failed to setup the reserved memory region for TZ mem\n");
goto err;
}
ret = qcom_tzmem_enable(__scm->dev); ret = qcom_tzmem_enable(__scm->dev);
if (ret) if (ret) {
return dev_err_probe(__scm->dev, ret, dev_err_probe(__scm->dev, ret,
"Failed to enable the TrustZone memory allocator\n"); "Failed to enable the TrustZone memory allocator\n");
goto err;
}
memset(&pool_config, 0, sizeof(pool_config)); memset(&pool_config, 0, sizeof(pool_config));
pool_config.initial_size = 0; pool_config.initial_size = 0;
@ -2078,9 +2086,11 @@ static int qcom_scm_probe(struct platform_device *pdev)
pool_config.max_size = SZ_256K; pool_config.max_size = SZ_256K;
__scm->mempool = devm_qcom_tzmem_pool_new(__scm->dev, &pool_config); __scm->mempool = devm_qcom_tzmem_pool_new(__scm->dev, &pool_config);
if (IS_ERR(__scm->mempool)) if (IS_ERR(__scm->mempool)) {
return dev_err_probe(__scm->dev, PTR_ERR(__scm->mempool), dev_err_probe(__scm->dev, PTR_ERR(__scm->mempool),
"Failed to create the SCM memory pool\n"); "Failed to create the SCM memory pool\n");
goto err;
}
/* /*
* Initialize the QSEECOM interface. * Initialize the QSEECOM interface.
@ -2096,6 +2106,12 @@ static int qcom_scm_probe(struct platform_device *pdev)
WARN(ret < 0, "failed to initialize qseecom: %d\n", ret); WARN(ret < 0, "failed to initialize qseecom: %d\n", ret);
return 0; return 0;
err:
/* Paired with smp_load_acquire() in qcom_scm_is_available(). */
smp_store_release(&__scm, NULL);
return ret;
} }
static void qcom_scm_shutdown(struct platform_device *pdev) static void qcom_scm_shutdown(struct platform_device *pdev)