6.16-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bartosz Golaszewski bartosz.golaszewski@linaro.org
commit dc3f4e75c54c19bad9a70419afae00ce6baf3ebf upstream.
qcom_scm_shm_bridge_enable() is used early in the SCM initialization routine. It makes an SCM call and so expects the internal __scm pointer in the SCM driver to be assigned. For this reason the tzmem memory pool is allocated *after* this pointer is assigned. However, this can lead to a crash if another consumer of the SCM API makes a call using the memory pool between the assignment of the __scm pointer and the initialization of the tzmem memory pool.
As qcom_scm_shm_bridge_enable() is a special case, not meant to be called by ordinary users, pull it into the local SCM header. Make it take struct device as argument. This is the device that will be used to make the SCM call as opposed to the global __scm pointer. This will allow us to move the tzmem initialization *before* the __scm assignment in the core SCM driver.
Signed-off-by: Bartosz Golaszewski bartosz.golaszewski@linaro.org Reviewed-by: Konrad Dybcio konrad.dybcio@oss.qualcomm.com Link: https://lore.kernel.org/r/20250630-qcom-scm-race-v2-2-fa3851c98611@linaro.or... Signed-off-by: Bjorn Andersson andersson@kernel.org Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- drivers/firmware/qcom/qcom_scm.c | 12 +++++++++--- drivers/firmware/qcom/qcom_scm.h | 1 + drivers/firmware/qcom/qcom_tzmem.c | 3 ++- include/linux/firmware/qcom/qcom_scm.h | 1 - 4 files changed, 12 insertions(+), 5 deletions(-)
--- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -1603,7 +1603,13 @@ bool qcom_scm_lmh_dcvsh_available(void) } EXPORT_SYMBOL_GPL(qcom_scm_lmh_dcvsh_available);
-int qcom_scm_shm_bridge_enable(void) +/* + * This is only supposed to be called once by the TZMem module. It takes the + * SCM struct device as argument and uses it to pass the call as at the time + * the SHM Bridge is enabled, the SCM is not yet fully set up and doesn't + * accept global user calls. Don't try to use the __scm pointer here. + */ +int qcom_scm_shm_bridge_enable(struct device *scm_dev) { int ret;
@@ -1615,11 +1621,11 @@ int qcom_scm_shm_bridge_enable(void)
struct qcom_scm_res res;
- if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_MP, + if (!__qcom_scm_is_call_available(scm_dev, QCOM_SCM_SVC_MP, QCOM_SCM_MP_SHM_BRIDGE_ENABLE)) return -EOPNOTSUPP;
- ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(scm_dev, &desc, &res);
if (ret) return ret; --- a/drivers/firmware/qcom/qcom_scm.h +++ b/drivers/firmware/qcom/qcom_scm.h @@ -83,6 +83,7 @@ int scm_legacy_call(struct device *dev, struct qcom_scm_res *res);
struct qcom_tzmem_pool *qcom_scm_get_tzmem_pool(void); +int qcom_scm_shm_bridge_enable(struct device *scm_dev);
#define QCOM_SCM_SVC_BOOT 0x01 #define QCOM_SCM_BOOT_SET_ADDR 0x01 --- a/drivers/firmware/qcom/qcom_tzmem.c +++ b/drivers/firmware/qcom/qcom_tzmem.c @@ -20,6 +20,7 @@ #include <linux/spinlock.h> #include <linux/types.h>
+#include "qcom_scm.h" #include "qcom_tzmem.h"
struct qcom_tzmem_area { @@ -94,7 +95,7 @@ static int qcom_tzmem_init(void) goto notsupp; }
- ret = qcom_scm_shm_bridge_enable(); + ret = qcom_scm_shm_bridge_enable(qcom_tzmem_dev); if (ret == -EOPNOTSUPP) goto notsupp;
--- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -148,7 +148,6 @@ bool qcom_scm_lmh_dcvsh_available(void);
int qcom_scm_gpu_init_regs(u32 gpu_req);
-int qcom_scm_shm_bridge_enable(void); int qcom_scm_shm_bridge_create(u64 pfn_and_ns_perm_flags, u64 ipfn_and_s_perm_flags, u64 size_and_flags, u64 ns_vmids, u64 *handle);