From 0c50b7fcf2773b4853e83fc15aba1a196ba95966 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Mon, 4 Mar 2024 14:14:53 +0100 Subject: firmware: qcom_scm: disable clocks if qcom_scm_bw_enable() fails There are several functions which are calling qcom_scm_bw_enable() then returns immediately if the call fails and leaves the clocks enabled. Change the code of these functions to disable clocks when the qcom_scm_bw_enable() call fails. This also fixes a possible dma buffer leak in the qcom_scm_pas_init_image() function. Compile tested only due to lack of hardware with interconnect support. Cc: stable@vger.kernel.org Fixes: 65b7ebda5028 ("firmware: qcom_scm: Add bw voting support to the SCM interface") Signed-off-by: Gabor Juhos Reviewed-by: Mukesh Ojha Link: https://lore.kernel.org/r/20240304-qcom-scm-disable-clk-v1-1-b36e51577ca1@gmail.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 520de9b5633a..e8460626fb0c 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -569,13 +569,14 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size, ret = qcom_scm_bw_enable(); if (ret) - return ret; + goto disable_clk; desc.args[1] = mdata_phys; ret = qcom_scm_call(__scm->dev, &desc, &res); - qcom_scm_bw_disable(); + +disable_clk: qcom_scm_clk_disable(); out: @@ -637,10 +638,12 @@ int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size) ret = qcom_scm_bw_enable(); if (ret) - return ret; + goto disable_clk; ret = qcom_scm_call(__scm->dev, &desc, &res); qcom_scm_bw_disable(); + +disable_clk: qcom_scm_clk_disable(); return ret ? : res.result[0]; @@ -672,10 +675,12 @@ int qcom_scm_pas_auth_and_reset(u32 peripheral) ret = qcom_scm_bw_enable(); if (ret) - return ret; + goto disable_clk; ret = qcom_scm_call(__scm->dev, &desc, &res); qcom_scm_bw_disable(); + +disable_clk: qcom_scm_clk_disable(); return ret ? : res.result[0]; @@ -706,11 +711,12 @@ int qcom_scm_pas_shutdown(u32 peripheral) ret = qcom_scm_bw_enable(); if (ret) - return ret; + goto disable_clk; ret = qcom_scm_call(__scm->dev, &desc, &res); - qcom_scm_bw_disable(); + +disable_clk: qcom_scm_clk_disable(); return ret ? : res.result[0]; -- cgit v1.2.3 From e6f3dac9cf11eff1daddeaa69521370c8941a5f9 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Fri, 8 Mar 2024 10:25:07 +0100 Subject: firmware: qcom_scm: remove IS_ERR() checks from qcom_scm_bw_{en,dis}able() Since the qcom_scm_probe() function returns with an error if __scm->path contains an error pointer, it is not needed to verify that again in the qcom_scm_bw_{en,dis}able() functions so remove the superfluous IS_ERR() checks. Signed-off-by: Gabor Juhos Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20240308-qcom_scm-is_err-check-v1-1-9c3e1ceefafe@gmail.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index e8460626fb0c..49ddbcab0680 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -163,9 +163,6 @@ static int qcom_scm_bw_enable(void) if (!__scm->path) return 0; - if (IS_ERR(__scm->path)) - return -EINVAL; - mutex_lock(&__scm->scm_bw_lock); if (!__scm->scm_vote_count) { ret = icc_set_bw(__scm->path, 0, UINT_MAX); @@ -183,7 +180,7 @@ err_bw: static void qcom_scm_bw_disable(void) { - if (IS_ERR_OR_NULL(__scm->path)) + if (!__scm->path) return; mutex_lock(&__scm->scm_bw_lock); -- cgit v1.2.3 From e478c5fb6aa10af7b7edbff69bc8aef6fbb5f0ed Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 20 Nov 2023 19:56:23 +0100 Subject: firmware: qcom: qcm: fix unused qcom_scm_qseecom_allowlist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For !OF builds, the qcom_scm_qseecom_allowlist is unused: drivers/firmware/qcom/qcom_scm.c:1652:34: error: ‘qcom_scm_qseecom_allowlist’ defined but not used [-Werror=unused-const-variable=] Fixes: 00b1248606ba ("firmware: qcom_scm: Add support for Qualcomm Secure Execution Environment SCM interface") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202311191654.S4wlVUrz-lkp@intel.com/ Signed-off-by: Krzysztof Kozlowski Acked-by: Maximilian Luz Link: https://lore.kernel.org/r/20231120185623.338608-1-krzysztof.kozlowski@linaro.org Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 49ddbcab0680..81c15aeff934 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -1652,7 +1652,7 @@ EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_send); * We do not yet support re-entrant calls via the qseecom interface. To prevent + any potential issues with this, only allow validated machines for now. */ -static const struct of_device_id qcom_scm_qseecom_allowlist[] = { +static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = { { .compatible = "lenovo,thinkpad-x13s", }, { } }; -- cgit v1.2.3 From 3de990f7895906a7a18d2dff63e3e525acaa4ecc Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Thu, 21 Mar 2024 20:53:59 +0530 Subject: firmware: qcom: scm: Remove log reporting memory allocation failure Remove redundant memory allocation failure. WARNING: Possible unnecessary 'out of memory' message + if (!mdata_buf) { + dev_err(__scm->dev, "Allocation of metadata buffer failed.\n"); Signed-off-by: Mukesh Ojha Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/1711034642-22860-1-git-send-email-quic_mojha@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 81c15aeff934..137bda5a0a63 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -554,10 +554,9 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size, */ mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys, GFP_KERNEL); - if (!mdata_buf) { - dev_err(__scm->dev, "Allocation of metadata buffer failed.\n"); + if (!mdata_buf) return -ENOMEM; - } + memcpy(mdata_buf, metadata, size); ret = qcom_scm_clk_enable(); -- cgit v1.2.3 From 000636d91d605f6209a635a29d0487af5b12b237 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Thu, 21 Mar 2024 20:54:00 +0530 Subject: firmware: qcom: scm: Remove redundant scm argument from qcom_scm_waitq_wakeup() Remove redundant scm argument from qcom_scm_waitq_wakeup(). Signed-off-by: Mukesh Ojha Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/1711034642-22860-2-git-send-email-quic_mojha@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 137bda5a0a63..d32fae53be1c 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -1771,7 +1771,7 @@ int qcom_scm_wait_for_wq_completion(u32 wq_ctx) return 0; } -static int qcom_scm_waitq_wakeup(struct qcom_scm *scm, unsigned int wq_ctx) +static int qcom_scm_waitq_wakeup(unsigned int wq_ctx) { int ret; @@ -1803,7 +1803,7 @@ static irqreturn_t qcom_scm_irq_handler(int irq, void *data) goto out; } - ret = qcom_scm_waitq_wakeup(scm, wq_ctx); + ret = qcom_scm_waitq_wakeup(wq_ctx); if (ret) goto out; } while (more_pending); -- cgit v1.2.3 From 398a4c58f3f29ac3ff4d777dc91fe40a07bbca8c Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Thu, 21 Mar 2024 20:54:01 +0530 Subject: firmware: qcom: scm: Rework dload mode availability check QCOM_SCM_BOOT_SET_DLOAD_MODE scm command is applicable for very older SoCs where this command is supported from firmware and for newer SoCs, dload mode tcsr registers is used for setting the download mode. Currently, qcom_scm_set_download_mode() checks for availability of QCOM_SCM_BOOT_SET_DLOAD_MODE command even for SoCs where this is not used. Fix this by switching the condition to keep the command availability check only if dload mode registers are not available. Signed-off-by: Mukesh Ojha Reviewed-by: Elliot Berman Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/1711034642-22860-3-git-send-email-quic_mojha@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index d32fae53be1c..d9cee441d81c 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -495,17 +495,14 @@ static int __qcom_scm_set_dload_mode(struct device *dev, bool enable) static void qcom_scm_set_download_mode(bool enable) { - bool avail; int ret = 0; - avail = __qcom_scm_is_call_available(__scm->dev, - QCOM_SCM_SVC_BOOT, - QCOM_SCM_BOOT_SET_DLOAD_MODE); - if (avail) { - ret = __qcom_scm_set_dload_mode(__scm->dev, enable); - } else if (__scm->dload_mode_addr) { + if (__scm->dload_mode_addr) { ret = qcom_scm_io_writel(__scm->dload_mode_addr, - enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0); + enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0); + } else if (__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_BOOT, + QCOM_SCM_BOOT_SET_DLOAD_MODE)) { + ret = __qcom_scm_set_dload_mode(__scm->dev, enable); } else { dev_err(__scm->dev, "No available mechanism for setting download mode\n"); -- cgit v1.2.3 From 2e4955167ec5c04534cebea9e8273a907e7a75e1 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Thu, 21 Mar 2024 20:54:02 +0530 Subject: firmware: qcom: scm: Fix __scm and waitq completion variable initialization It is possible qcom_scm_is_available() gives wrong indication that if __scm is initialized while __scm->dev is not and similar issue is also possible with __scm->waitq_comp. Fix this appropriately by the use of release barrier and read barrier that will make sure if __scm is initialized so, is all of its field variable. Fixes: d0f6fa7ba2d6 ("firmware: qcom: scm: Convert SCM to platform driver") Fixes: 6bf325992236 ("firmware: qcom: scm: Add wait-queue handling logic") Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/1711034642-22860-4-git-send-email-quic_mojha@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index d9cee441d81c..84aa30b5a19e 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -1737,7 +1737,7 @@ static int qcom_scm_qseecom_init(struct qcom_scm *scm) */ bool qcom_scm_is_available(void) { - return !!__scm; + return !!READ_ONCE(__scm); } EXPORT_SYMBOL_GPL(qcom_scm_is_available); @@ -1818,10 +1818,12 @@ static int qcom_scm_probe(struct platform_device *pdev) if (!scm) return -ENOMEM; + scm->dev = &pdev->dev; ret = qcom_scm_find_dload_address(&pdev->dev, &scm->dload_mode_addr); if (ret < 0) return ret; + init_completion(&scm->waitq_comp); mutex_init(&scm->scm_bw_lock); scm->path = devm_of_icc_get(&pdev->dev, NULL); @@ -1853,10 +1855,8 @@ static int qcom_scm_probe(struct platform_device *pdev) if (ret) return ret; - __scm = scm; - __scm->dev = &pdev->dev; - - init_completion(&__scm->waitq_comp); + /* Let all above stores be available after this */ + smp_store_release(&__scm, scm); irq = platform_get_irq_optional(pdev, 0); if (irq < 0) { -- cgit v1.2.3 From b9718298e028f9edbe0fcdf48c02a1c355409410 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Thu, 21 Mar 2024 23:07:35 +0530 Subject: firmware: qcom: scm: Modify only the download bits in TCSR register Crashdump collection is done based on DLOAD bits of TCSR register. To retain other bits, scm driver need to read the register and modify only the DLOAD bits, as other bits in TCSR may have their own significance. Co-developed-by: Poovendhan Selvaraj Signed-off-by: Poovendhan Selvaraj Signed-off-by: Mukesh Ojha Tested-by: Kathiravan Thirumoorthy # IPQ9574 and IPQ5332 Reviewed-by: Dmitry Baryshkov Reviewed-by: Elliot Berman Link: https://lore.kernel.org/r/1711042655-31948-1-git-send-email-quic_mojha@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 84aa30b5a19e..5ff62f57aa55 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -4,6 +4,8 @@ */ #include +#include +#include #include #include #include @@ -114,6 +116,10 @@ static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = { #define QCOM_SMC_WAITQ_FLAG_WAKE_ONE BIT(0) #define QCOM_SMC_WAITQ_FLAG_WAKE_ALL BIT(1) +#define QCOM_DLOAD_MASK GENMASK(5, 4) +#define QCOM_DLOAD_NODUMP 0 +#define QCOM_DLOAD_FULLDUMP 1 + static const char * const qcom_scm_convention_names[] = { [SMC_CONVENTION_UNKNOWN] = "unknown", [SMC_CONVENTION_ARM_32] = "smc arm 32", @@ -493,13 +499,29 @@ static int __qcom_scm_set_dload_mode(struct device *dev, bool enable) return qcom_scm_call_atomic(__scm->dev, &desc, NULL); } +static int qcom_scm_io_rmw(phys_addr_t addr, unsigned int mask, unsigned int val) +{ + unsigned int old; + unsigned int new; + int ret; + + ret = qcom_scm_io_readl(addr, &old); + if (ret) + return ret; + + new = (old & ~mask) | (val & mask); + + return qcom_scm_io_writel(addr, new); +} + static void qcom_scm_set_download_mode(bool enable) { + u32 val = enable ? QCOM_DLOAD_FULLDUMP : QCOM_DLOAD_NODUMP; int ret = 0; if (__scm->dload_mode_addr) { - ret = qcom_scm_io_writel(__scm->dload_mode_addr, - enable ? QCOM_SCM_BOOT_SET_DLOAD_MODE : 0); + ret = qcom_scm_io_rmw(__scm->dload_mode_addr, QCOM_DLOAD_MASK, + FIELD_PREP(QCOM_DLOAD_MASK, val)); } else if (__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_SET_DLOAD_MODE)) { ret = __qcom_scm_set_dload_mode(__scm->dev, enable); -- cgit v1.2.3