From 387a926ee166814611acecb960207fe2f3c4fd3e Mon Sep 17 00:00:00 2001 From: Amirreza Zarrabi Date: Mon, 16 Feb 2026 14:24:06 -0800 Subject: [PATCH 01/28] tee: optee: prevent use-after-free when the client exits before the supplicant Commit 70b0d6b0a199 ("tee: optee: Fix supplicant wait loop") made the client wait as killable so it can be interrupted during shutdown or after a supplicant crash. This changes the original lifetime expectations: the client task can now terminate while the supplicant is still processing its request. If the client exits first it removes the request from its queue and kfree()s it, while the request ID remains in supp->idr. A subsequent lookup on the supplicant path then dereferences freed memory, leading to a use-after-free. Serialise access to the request with supp->mutex: * Hold supp->mutex in optee_supp_recv() and optee_supp_send() while looking up and touching the request. * Let optee_supp_thrd_req() notice that the client has terminated and signal optee_supp_send() accordingly. With these changes the request cannot be freed while the supplicant still has a reference, eliminating the race. Fixes: 70b0d6b0a199 ("tee: optee: Fix supplicant wait loop") Signed-off-by: Amirreza Zarrabi Tested-by: Ox Yeh Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/optee/supp.c | 107 +++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 33 deletions(-) diff --git a/drivers/tee/optee/supp.c b/drivers/tee/optee/supp.c index a3d11b1f90fa..06747e90c230 100644 --- a/drivers/tee/optee/supp.c +++ b/drivers/tee/optee/supp.c @@ -10,7 +10,11 @@ struct optee_supp_req { struct list_head link; + int id; + bool in_queue; + bool processed; + u32 func; u32 ret; size_t num_params; @@ -19,6 +23,9 @@ struct optee_supp_req { struct completion c; }; +/* It is temporary request used for revoked pending request in supp->idr. */ +#define INVALID_REQ_PTR ((struct optee_supp_req *)ERR_PTR(-EBADF)) + void optee_supp_init(struct optee_supp *supp) { memset(supp, 0, sizeof(*supp)); @@ -39,21 +46,23 @@ void optee_supp_release(struct optee_supp *supp) { int id; struct optee_supp_req *req; - struct optee_supp_req *req_tmp; mutex_lock(&supp->mutex); - /* Abort all request retrieved by supplicant */ + /* Abort all request */ idr_for_each_entry(&supp->idr, req, id) { idr_remove(&supp->idr, id); - req->ret = TEEC_ERROR_COMMUNICATION; - complete(&req->c); - } + /* Skip if request was already marked invalid */ + if (IS_ERR(req)) + continue; - /* Abort all queued requests */ - list_for_each_entry_safe(req, req_tmp, &supp->reqs, link) { - list_del(&req->link); - req->in_queue = false; + /* For queued requests where supplicant has not seen it */ + if (req->in_queue) { + list_del(&req->link); + req->in_queue = false; + } + + req->processed = true; req->ret = TEEC_ERROR_COMMUNICATION; complete(&req->c); } @@ -100,8 +109,16 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, /* Insert the request in the request list */ mutex_lock(&supp->mutex); + req->id = idr_alloc(&supp->idr, req, 1, 0, GFP_KERNEL); + if (req->id < 0) { + mutex_unlock(&supp->mutex); + kfree(req); + return TEEC_ERROR_OUT_OF_MEMORY; + } + list_add_tail(&req->link, &supp->reqs); req->in_queue = true; + req->processed = false; mutex_unlock(&supp->mutex); /* Tell an eventual waiter there's a new request */ @@ -117,21 +134,43 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params, if (wait_for_completion_killable(&req->c)) { mutex_lock(&supp->mutex); if (req->in_queue) { + /* Supplicant has not seen this request yet. */ + idr_remove(&supp->idr, req->id); list_del(&req->link); req->in_queue = false; + + ret = TEEC_ERROR_COMMUNICATION; + } else if (req->processed) { + /* + * Supplicant has processed this request. Ignore the + * kill signal for now and submit the result. req is not + * in supp->reqs (removed by supp_pop_entry()) nor in + * supp->idr (removed by supp_pop_req()). + */ + ret = req->ret; + } else { + /* + * Supplicant is in the middle of processing this + * request. Replace req with INVALID_REQ_PTR so that + * the ID remains busy, causing optee_supp_send() to + * fail on the next call to supp_pop_req() with this ID. + */ + idr_replace(&supp->idr, INVALID_REQ_PTR, req->id); + ret = TEEC_ERROR_COMMUNICATION; } + mutex_unlock(&supp->mutex); - req->ret = TEEC_ERROR_COMMUNICATION; + } else { + ret = req->ret; } - ret = req->ret; kfree(req); return ret; } static struct optee_supp_req *supp_pop_entry(struct optee_supp *supp, - int num_params, int *id) + int num_params) { struct optee_supp_req *req; @@ -153,10 +192,6 @@ static struct optee_supp_req *supp_pop_entry(struct optee_supp *supp, return ERR_PTR(-EINVAL); } - *id = idr_alloc(&supp->idr, req, 1, 0, GFP_KERNEL); - if (*id < 0) - return ERR_PTR(-ENOMEM); - list_del(&req->link); req->in_queue = false; @@ -214,7 +249,6 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, struct optee *optee = tee_get_drvdata(teedev); struct optee_supp *supp = &optee->supp; struct optee_supp_req *req = NULL; - int id; size_t num_meta; int rc; @@ -224,15 +258,11 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, while (true) { mutex_lock(&supp->mutex); - req = supp_pop_entry(supp, *num_params - num_meta, &id); + req = supp_pop_entry(supp, *num_params - num_meta); + if (req) + break; /* Keep mutex held. */ mutex_unlock(&supp->mutex); - if (req) { - if (IS_ERR(req)) - return PTR_ERR(req); - break; - } - /* * If we didn't get a request we'll block in * wait_for_completion() to avoid needless spinning. @@ -245,6 +275,13 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, return -ERESTARTSYS; } + /* supp->mutex held and req != NULL. */ + + if (IS_ERR(req)) { + mutex_unlock(&supp->mutex); + return PTR_ERR(req); + } + if (num_meta) { /* * tee-supplicant support meta parameters -> requsts can be @@ -252,13 +289,11 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, */ param->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT | TEE_IOCTL_PARAM_ATTR_META; - param->u.value.a = id; + param->u.value.a = req->id; param->u.value.b = 0; param->u.value.c = 0; } else { - mutex_lock(&supp->mutex); - supp->req_id = id; - mutex_unlock(&supp->mutex); + supp->req_id = req->id; } *func = req->func; @@ -266,6 +301,7 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params, memcpy(param + num_meta, req->param, sizeof(struct tee_param) * req->num_params); + mutex_unlock(&supp->mutex); return 0; } @@ -297,12 +333,17 @@ static struct optee_supp_req *supp_pop_req(struct optee_supp *supp, if (!req) return ERR_PTR(-ENOENT); + /* optee_supp_thrd_req() already returned to optee. */ + if (IS_ERR(req)) + goto failed_req; + if ((num_params - nm) != req->num_params) return ERR_PTR(-EINVAL); + *num_meta = nm; +failed_req: idr_remove(&supp->idr, id); supp->req_id = -1; - *num_meta = nm; return req; } @@ -328,10 +369,9 @@ int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, mutex_lock(&supp->mutex); req = supp_pop_req(supp, num_params, param, &num_meta); - mutex_unlock(&supp->mutex); - if (IS_ERR(req)) { - /* Something is wrong, let supplicant restart. */ + mutex_unlock(&supp->mutex); + /* Something is wrong, let supplicant handel it. */ return PTR_ERR(req); } @@ -355,9 +395,10 @@ int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params, } } req->ret = ret; - + req->processed = true; /* Let the requesting thread continue */ complete(&req->c); + mutex_unlock(&supp->mutex); return 0; } From 754d60ad1c91895be0bc7d771fbf9fb3c9448640 Mon Sep 17 00:00:00 2001 From: Alexander Dahl Date: Wed, 29 Apr 2026 14:59:30 +0200 Subject: [PATCH 02/28] memory: atmel-ebi: Allow deferred probing After removing of_platform_default_populate() calls the atmel-ebi driver was affected by deferred probing. platform_driver_probe() is incompatible with deferred probing. This led to atmel-ebi driver eventually not being probed on at91 sam9x60-curiosity and other sam9x60 based boards. Subsequently the nand-controller driver (nand-controller being a child node of ebi) on that platform was not probed and thus raw NAND flash was inaccessible, preventing devices to boot with rootfs on raw NAND flash (e.g. with UBI/UBIFS). Fixes: 0b0f7e6539a7 ("ARM: at91: remove unnecessary of_platform_default_populate calls") Cc: stable@vger.kernel.org Suggested-by: Miquel Raynal Signed-off-by: Alexander Dahl Link: https://patch.msgid.link/20260429125930.844790-1-ada@thorsis.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/atmel-ebi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c index 8db970da9af9..1e8e8aba2542 100644 --- a/drivers/memory/atmel-ebi.c +++ b/drivers/memory/atmel-ebi.c @@ -628,10 +628,11 @@ static __maybe_unused int atmel_ebi_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(atmel_ebi_pm_ops, NULL, atmel_ebi_resume); static struct platform_driver atmel_ebi_driver = { + .probe = atmel_ebi_probe, .driver = { .name = "atmel-ebi", .of_match_table = atmel_ebi_id_table, .pm = &atmel_ebi_pm_ops, }, }; -builtin_platform_driver_probe(atmel_ebi_driver, atmel_ebi_probe); +builtin_platform_driver(atmel_ebi_driver); From 2c6821657ce3b3c85f92719ea81ec9f9ff27df11 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Mon, 27 Apr 2026 09:01:48 +0800 Subject: [PATCH 03/28] soc: imx8m: Fix match data lookup for soc device The i.MX8M soc device is registered via platform_device_register_simple(), so it is not associated with a Device Tree node and the imx8m_soc_driver has no of_match_table. As a result, device_get_match_data() always returns NULL when probing the soc device. Retrieve the match data directly from the machine compatible using of_machine_get_match_data(imx8_soc_match), which provides the correct SoC data. Fixes: 2524b293a59e5 ("soc: imx8m: don't access of_root directly") Signed-off-by: Peng Fan Reviewed-by: Lucas Stach Signed-off-by: Frank Li --- drivers/soc/imx/soc-imx8m.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/imx/soc-imx8m.c b/drivers/soc/imx/soc-imx8m.c index 77763a107edb..fc080e56f50d 100644 --- a/drivers/soc/imx/soc-imx8m.c +++ b/drivers/soc/imx/soc-imx8m.c @@ -247,7 +247,7 @@ static int imx8m_soc_probe(struct platform_device *pdev) if (ret) return ret; - data = device_get_match_data(dev); + data = of_machine_get_match_data(imx8_soc_match); if (data) { soc_dev_attr->soc_id = data->name; ret = imx8m_soc_prepare(pdev, data->ocotp_compatible); From e27264daac7d9ce892a2a5b4a864d6d9a3c9276a Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:18 +0530 Subject: [PATCH 04/28] dt-bindings: crypto: qcom,ice: Fix missing power-domain and iface clk The DT bindings for inline-crypto engine do not specify the UFS_PHY_GDSC power-domain and iface clock. Without enabling the iface clock and the associated power-domain the ICE hardware cannot function correctly and leads to unclocked hardware accesses being observed during probe. Fix the DT bindings for inline-crypto engine to require the UFS_PHY_GDSC power-domain and iface clock for new devices (Eliza and Milos) introduced in the current release (7.1) with yet-to-stabilize ABI, while preserving backward compatibility for older devices. Fixes: 618195a7ac3df ("dt-bindings: crypto: qcom,inline-crypto-engine: Document the Eliza ICE") Fixes: 85faec1e85555 ("dt-bindings: crypto: qcom,inline-crypto-engine: document the Milos ICE") Reviewed-by: Kuldeep Singh Reviewed-by: Krzysztof Kozlowski Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-1-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../crypto/qcom,inline-crypto-engine.yaml | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml b/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml index 876bf90ed96e..ccb6b8dd8e11 100644 --- a/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml +++ b/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml @@ -30,6 +30,16 @@ properties: maxItems: 1 clocks: + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + items: + - const: core + - const: iface + + power-domains: maxItems: 1 operating-points-v2: true @@ -44,6 +54,25 @@ required: additionalProperties: false +allOf: + - if: + properties: + compatible: + contains: + enum: + - qcom,eliza-inline-crypto-engine + - qcom,milos-inline-crypto-engine + + then: + required: + - power-domains + - clock-names + properties: + clocks: + minItems: 2 + clock-names: + minItems: 2 + examples: - | #include @@ -52,7 +81,11 @@ examples: compatible = "qcom,sm8550-inline-crypto-engine", "qcom,inline-crypto-engine"; reg = <0x01d88000 0x8000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc UFS_PHY_GDSC>; operating-points-v2 = <&ice_opp_table>; From 0d5dc5818191b55e4364d04b1b898a14a2ccac38 Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:19 +0530 Subject: [PATCH 05/28] soc: qcom: ice: Allow explicit votes on 'iface' clock for ICE Since Qualcomm inline-crypto engine (ICE) is now a dedicated driver de-coupled from the QCOM UFS driver, it explicitly votes for its required clocks during probe. For scenarios where the 'clk_ignore_unused' flag is not passed on the kernel command line, to avoid potential unclocked ICE hardware register access during probe the ICE driver should additionally vote on the 'iface' clock. Also update the suspend and resume callbacks to handle un-voting and voting on the 'iface' clock. Fixes: 2afbf43a4aec6 ("soc: qcom: Make the Qualcomm UFS/SDCC ICE a dedicated driver") Reviewed-by: Manivannan Sadhasivam Reviewed-by: Kuldeep Singh Reviewed-by: Konrad Dybcio Signed-off-by: Harshal Dev Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-2-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ice.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index b203bc685cad..bf4ab2d9e5c0 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -108,6 +108,7 @@ struct qcom_ice { void __iomem *base; struct clk *core_clk; + struct clk *iface_clk; bool use_hwkm; bool hwkm_init_complete; u8 hwkm_version; @@ -312,8 +313,13 @@ int qcom_ice_resume(struct qcom_ice *ice) err = clk_prepare_enable(ice->core_clk); if (err) { - dev_err(dev, "failed to enable core clock (%d)\n", - err); + dev_err(dev, "Failed to enable core clock: %d\n", err); + return err; + } + + err = clk_prepare_enable(ice->iface_clk); + if (err) { + dev_err(dev, "Failed to enable iface clock: %d\n", err); return err; } qcom_ice_hwkm_init(ice); @@ -323,6 +329,7 @@ EXPORT_SYMBOL_GPL(qcom_ice_resume); int qcom_ice_suspend(struct qcom_ice *ice) { + clk_disable_unprepare(ice->iface_clk); clk_disable_unprepare(ice->core_clk); ice->hwkm_init_complete = false; @@ -579,11 +586,17 @@ static struct qcom_ice *qcom_ice_create(struct device *dev, engine->core_clk = devm_clk_get_optional_enabled(dev, "ice_core_clk"); if (!engine->core_clk) engine->core_clk = devm_clk_get_optional_enabled(dev, "ice"); + if (!engine->core_clk) + engine->core_clk = devm_clk_get_optional_enabled(dev, "core"); if (!engine->core_clk) engine->core_clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(engine->core_clk)) return ERR_CAST(engine->core_clk); + engine->iface_clk = devm_clk_get_optional_enabled(dev, "iface"); + if (IS_ERR(engine->iface_clk)) + return ERR_CAST(engine->iface_clk); + if (!qcom_ice_check_supported(engine)) return ERR_PTR(-EOPNOTSUPP); From 12c97d1c15f926cd430bf5cdf8ffe878cb478165 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Tue, 14 Apr 2026 20:05:51 +0300 Subject: [PATCH 06/28] arm64: dts: qcom: glymur: Drop RPMh CXO clocks from QMP PHYs On Glymur, all QMP PHYs except the one used by USB SS0 take their reference clock from the TCSR clock controller. Since these TCSR clocks already derive from RPMH_CXO_CLK as their sole parent, there is no need to provide an extra `clkref` clock to the PHY nodes. Drop the extra RPMh CXO clock inputs and use the TCSR clocks as the PHY reference clocks instead. This also fixes the devicetree schema validation, as the bindings do not allow a separate `clkref` clock. Fixes: 4eee57dd4df9 ("arm64: dts: qcom: glymur: Add USB related nodes") Reported-by: Krzysztof Kozlowski Reported-by: Rob Herring Closes: https://lore.kernel.org/r/20260410145205.GA554754-robh@kernel.org/ Signed-off-by: Abel Vesa Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260414-dts-glymur-drop-rpmh-cxo-clk-from-qmpphys-v1-1-ab12d77c4aec@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/glymur.dtsi | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi index f23cf81ddb77..82436984485d 100644 --- a/arch/arm64/boot/dts/qcom/glymur.dtsi +++ b/arch/arm64/boot/dts/qcom/glymur.dtsi @@ -2314,11 +2314,9 @@ clocks = <&gcc GCC_USB3_MP_PHY_AUX_CLK>, <&tcsr TCSR_USB3_0_CLKREF_EN>, - <&rpmhcc RPMH_CXO_CLK>, <&gcc GCC_USB3_MP_PHY_COM_AUX_CLK>, <&gcc GCC_USB3_MP_PHY_PIPE_0_CLK>; clock-names = "aux", - "clkref", "ref", "com_aux", "pipe"; @@ -2343,11 +2341,9 @@ clocks = <&gcc GCC_USB3_MP_PHY_AUX_CLK>, <&tcsr TCSR_USB3_1_CLKREF_EN>, - <&rpmhcc RPMH_CXO_CLK>, <&gcc GCC_USB3_MP_PHY_COM_AUX_CLK>, <&gcc GCC_USB3_MP_PHY_PIPE_1_CLK>; clock-names = "aux", - "clkref", "ref", "com_aux", "pipe"; @@ -2482,15 +2478,13 @@ reg = <0x0 0x00fde000 0x0 0x8000>; clocks = <&gcc GCC_USB3_SEC_PHY_AUX_CLK>, - <&rpmhcc RPMH_CXO_CLK>, + <&tcsr TCSR_USB4_1_CLKREF_EN>, <&gcc GCC_USB3_SEC_PHY_COM_AUX_CLK>, - <&gcc GCC_USB3_SEC_PHY_PIPE_CLK>, - <&tcsr TCSR_USB4_1_CLKREF_EN>; + <&gcc GCC_USB3_SEC_PHY_PIPE_CLK>; clock-names = "aux", "ref", "com_aux", - "usb3_pipe", - "clkref"; + "usb3_pipe"; power-domains = <&gcc GCC_USB_1_PHY_GDSC>; @@ -3750,15 +3744,13 @@ reg = <0x0 0x088e1000 0x0 0x8000>; clocks = <&gcc GCC_USB3_TERT_PHY_AUX_CLK>, - <&rpmhcc RPMH_CXO_CLK>, + <&tcsr TCSR_USB4_2_CLKREF_EN>, <&gcc GCC_USB3_TERT_PHY_COM_AUX_CLK>, - <&gcc GCC_USB3_TERT_PHY_PIPE_CLK>, - <&tcsr TCSR_USB4_2_CLKREF_EN>; + <&gcc GCC_USB3_TERT_PHY_PIPE_CLK>; clock-names = "aux", "ref", "com_aux", - "usb3_pipe", - "clkref"; + "usb3_pipe"; power-domains = <&gcc GCC_USB_2_PHY_GDSC>; From 4b15b03166cc5d28e9912287b1f9b6607c8710ec Mon Sep 17 00:00:00 2001 From: Val Packett Date: Wed, 11 Mar 2026 21:53:37 -0300 Subject: [PATCH 07/28] arm64: dts: qcom: x1-dell-thena: remove i2c20 (battery SMBus) and reserve its pins i2c20 is used by the battmgr service on the ADSP to communicate with the SBS interface of the battery. Initializing it from Linux would break the battmgr functionality when booted in EL2. Mark those pins as reserved. Fixes: e7733b42111c ("arm64: dts: qcom: Add support for Dell Inspiron 7441 / Latitude 7455") Reviewed-by: Konrad Dybcio Reviewed-by: Abel Vesa Signed-off-by: Val Packett Link: https://lore.kernel.org/r/20260312005731.12488-2-val@packett.cool Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi b/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi index 0d9a324cc6cc..db291730130c 100644 --- a/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi +++ b/arch/arm64/boot/dts/qcom/x1-dell-thena.dtsi @@ -982,12 +982,6 @@ status = "okay"; }; -&i2c20 { - clock-frequency = <400000>; - - status = "okay"; -}; - &lpass_tlmm { spkr_01_sd_n_active: spkr-01-sd-n-active-state { pins = "gpio12"; @@ -1308,6 +1302,7 @@ &tlmm { gpio-reserved-ranges = <44 4>, /* SPI11 (TPM) */ <76 4>, /* SPI19 (TZ Protected) */ + <80 2>, /* I2C20 (Battery SMBus) */ <238 1>; /* UFS Reset */ cam_rgb_default: cam-rgb-default-state { From f133bd4b5daf71bccdde0ad1a4f47fac76a6bfb1 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Tue, 5 May 2026 13:12:58 +0000 Subject: [PATCH 08/28] firmware: samsung: acpm: Fix cross-thread RX length corruption Sashiko identified a cross-thread RX length corruption bug when reviewing the thermal addition to ACPM [1]. When multiple threads concurrently send IPC requests, the ACPM polling mechanism can encounter responses belonging to other threads. To drain the queue, the driver saves these concurrent responses into an internal cache (`rx_data->cmd`) to be retrieved later by the owning thread. Previously, the driver incorrectly used `xfer->rxcnt` (the expected receive length of the *current* polling thread) when copying data for *other* threads into this cache. If the threads expected responses of different lengths, this resulted in buffer underflows (leading to reads of uninitialized memory) or potential buffer overflows. Fix this by replacing the boolean `response` flag in `struct acpm_rx_data` with `rxcnt`, caching the exact expected receive length for each specific transaction during transfer preparation. Use this cached length when saving concurrent responses. Consequently, ensure that `xfer->rxcnt` is explicitly zeroed in driver helpers (e.g., `acpm_dvfs_set_xfer`) for fire-and-forget messages to prevent uninitialized stack garbage from being interpreted as a massive expected receive length. Cc: stable@vger.kernel.org Fixes: a88927b534ba ("firmware: add Exynos ACPM protocol driver") Closes: https://sashiko.dev/#/patchset/20260420-acpm-tmu-v3-0-3dc8e93f0b26%40linaro.org [1] Reported-by: Titouan Ameline de Cadeville Closes: https://lore.kernel.org/r/20260426210255.73674-1-titouan.ameline@gmail.com/ Signed-off-by: Tudor Ambarus Link: https://patch.msgid.link/20260505-acpm-fixes-sashiko-reports-v5-1-43b5ee7f1674@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/firmware/samsung/exynos-acpm-dvfs.c | 3 +++ drivers/firmware/samsung/exynos-acpm.c | 15 ++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/samsung/exynos-acpm-dvfs.c b/drivers/firmware/samsung/exynos-acpm-dvfs.c index 06bdf62dea1f..fdea7aa24ca0 100644 --- a/drivers/firmware/samsung/exynos-acpm-dvfs.c +++ b/drivers/firmware/samsung/exynos-acpm-dvfs.c @@ -31,6 +31,9 @@ static void acpm_dvfs_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen, if (response) { xfer->rxcnt = cmdlen; xfer->rxd = cmd; + } else { + xfer->rxcnt = 0; + xfer->rxd = NULL; } } diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index 16c46ed60837..e95edc350efa 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -104,12 +104,12 @@ struct acpm_queue { * * @cmd: pointer to where the data shall be saved. * @n_cmd: number of 32-bit commands. - * @response: true if the client expects the RX data. + * @rxcnt: expected length of the response in 32-bit words. */ struct acpm_rx_data { u32 *cmd; size_t n_cmd; - bool response; + size_t rxcnt; }; #define ACPM_SEQNUM_MAX 64 @@ -199,7 +199,7 @@ static void acpm_get_saved_rx(struct acpm_chan *achan, const struct acpm_rx_data *rx_data = &achan->rx_data[tx_seqnum - 1]; u32 rx_seqnum; - if (!rx_data->response) + if (!rx_data->rxcnt) return; rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, rx_data->cmd[0]); @@ -256,7 +256,7 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) seqnum = rx_seqnum - 1; rx_data = &achan->rx_data[seqnum]; - if (rx_data->response) { + if (rx_data->rxcnt) { if (rx_seqnum == tx_seqnum) { __ioread32_copy(xfer->rxd, addr, xfer->rxcnt); rx_set = true; @@ -268,7 +268,8 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) * clear yet the bitmap. It will be cleared * after the response is copied to the request. */ - __ioread32_copy(rx_data->cmd, addr, xfer->rxcnt); + __ioread32_copy(rx_data->cmd, addr, + rx_data->rxcnt); } } else { clear_bit(seqnum, achan->bitmap_seqnum); @@ -380,8 +381,8 @@ static void acpm_prepare_xfer(struct acpm_chan *achan, /* Clear data for upcoming responses */ rx_data = &achan->rx_data[achan->seqnum - 1]; memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->n_cmd); - if (xfer->rxd) - rx_data->response = true; + /* zero means no response expected */ + rx_data->rxcnt = xfer->rxcnt; /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */ set_bit(achan->seqnum - 1, achan->bitmap_seqnum); From b66829b17f6385cc9ffbcbe2476d532d2e3121ad Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Tue, 5 May 2026 13:12:59 +0000 Subject: [PATCH 09/28] firmware: samsung: acpm: Fix mailbox channel leak on probe error Sashiko identified the leak at [1]. The ACPM driver allocates hardware mailbox channels using `mbox_request_channel()` during `acpm_channels_init()`. However, the driver lacked a `.remove` callback and did not free these channels on subsequent error paths inside `acpm_probe()`. Additionally, if `acpm_achan_alloc_cmds()` failed during the channel initialization loop, the function returned immediately, bypassing the manual cleanup and permanently leaking any channels successfully requested in previous loop iterations. Fix this by modifying `acpm_free_mbox_chans()` to match the `devres` action signature and registering it via `devm_add_action_or_reset()`. Cc: stable@vger.kernel.org Fixes: a88927b534ba ("firmware: add Exynos ACPM protocol driver") Closes: https://sashiko.dev/#/patchset/20260420-acpm-tmu-v3-0-3dc8e93f0b26%40linaro.org [1] Signed-off-by: Tudor Ambarus Link: https://patch.msgid.link/20260505-acpm-fixes-sashiko-reports-v5-2-43b5ee7f1674@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/firmware/samsung/exynos-acpm.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index e95edc350efa..9766425a44ab 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -527,10 +527,11 @@ static int acpm_achan_alloc_cmds(struct acpm_chan *achan) /** * acpm_free_mbox_chans() - free mailbox channels. - * @acpm: pointer to driver data. + * @data: pointer to driver data. */ -static void acpm_free_mbox_chans(struct acpm_info *acpm) +static void acpm_free_mbox_chans(void *data) { + struct acpm_info *acpm = data; int i; for (i = 0; i < acpm->num_chans; i++) @@ -558,6 +559,10 @@ static int acpm_channels_init(struct acpm_info *acpm) if (!acpm->chans) return -ENOMEM; + ret = devm_add_action_or_reset(dev, acpm_free_mbox_chans, acpm); + if (ret) + return dev_err_probe(dev, ret, "Failed to add mbox free action.\n"); + chans_shmem = acpm->sram_base + readl(&shmem->chans); for (i = 0; i < acpm->num_chans; i++) { @@ -579,10 +584,8 @@ static int acpm_channels_init(struct acpm_info *acpm) cl->dev = dev; achan->chan = mbox_request_channel(cl, 0); - if (IS_ERR(achan->chan)) { - acpm_free_mbox_chans(acpm); + if (IS_ERR(achan->chan)) return PTR_ERR(achan->chan); - } } return 0; From 765aaba18413a66f6c8fe8416336ca9b3dd98a79 Mon Sep 17 00:00:00 2001 From: Mihai Sain Date: Mon, 9 Mar 2026 09:53:29 +0200 Subject: [PATCH 10/28] ARM: dts: microchip: sam9x7: fix GMAC clock configuration The GMAC node incorrectly listed four clocks, including a separate tx_clk and a TSU GCK clock sourced from ID 67. According to the SAM9X7 clocking scheme, the GMAC uses only three clocks: HCLK, PCLK, and the TSU GCK derived from the GMAC peripheral clock (ID 24). Remove the unused tx_clk, update the clock-names accordingly, and correct the assigned clock to use GCK 24 instead of GCK 67. This aligns the device tree with the actual hardware clock topology and prevents misconfiguration of the GMAC clock tree. [root@SAM9X75 ~]$ cat /sys/kernel/debug/clk/clk_summary | grep gmac gmac_gclk 1 1 1 266666666 0 0 50000 Y f802c000.ethernet tsu_clk f802c000.ethernet tsu_clk gmac_clk 2 2 0 266666666 0 0 50000 Y f802c000.ethernet hclk f802c000.ethernet pclk Fixes: 41af45af8bc3 ("ARM: dts: at91: sam9x7: add device tree for SoC") Signed-off-by: Mihai Sain Link: https://lore.kernel.org/r/20260309075329.1528-5-mihai.sain@microchip.com [claudiu.beznea: massaged the patch description] Signed-off-by: Claudiu Beznea --- arch/arm/boot/dts/microchip/sam9x7.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/boot/dts/microchip/sam9x7.dtsi b/arch/arm/boot/dts/microchip/sam9x7.dtsi index d242d7a934d0..c680a5033b6b 100644 --- a/arch/arm/boot/dts/microchip/sam9x7.dtsi +++ b/arch/arm/boot/dts/microchip/sam9x7.dtsi @@ -990,9 +990,9 @@ <62 IRQ_TYPE_LEVEL_HIGH 3>, /* Queue 3 */ <63 IRQ_TYPE_LEVEL_HIGH 3>, /* Queue 4 */ <64 IRQ_TYPE_LEVEL_HIGH 3>; /* Queue 5 */ - clocks = <&pmc PMC_TYPE_PERIPHERAL 24>, <&pmc PMC_TYPE_PERIPHERAL 24>, <&pmc PMC_TYPE_GCK 24>, <&pmc PMC_TYPE_GCK 67>; - clock-names = "hclk", "pclk", "tx_clk", "tsu_clk"; - assigned-clocks = <&pmc PMC_TYPE_GCK 67>; + clocks = <&pmc PMC_TYPE_PERIPHERAL 24>, <&pmc PMC_TYPE_PERIPHERAL 24>, <&pmc PMC_TYPE_GCK 24>; + clock-names = "hclk", "pclk", "tsu_clk"; + assigned-clocks = <&pmc PMC_TYPE_GCK 24>; assigned-clock-rates = <266666666>; status = "disabled"; }; From d922113ef91e6e7e8065e9070f349365341ba32e Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 18 May 2026 19:22:17 +0530 Subject: [PATCH 11/28] soc: qcom: ice: Fix race between qcom_ice_probe() and of_qcom_ice_get() The current platform driver design causes probe ordering races with consumers (UFS, eMMC) due to ICE's dependency on SCM firmware calls. If ICE probe fails (missing ICE SCM or DT registers), devm_of_qcom_ice_get() loops with -EPROBE_DEFER, leaving consumers non-functional even when ICE should be gracefully disabled. devm_of_qcom_ice_get() doesn't know if the ICE driver probe has failed due to above reasons or it is waiting for the SCM driver. Moreover, there is no devlink dependency between ICE and consumer drivers as 'qcom,ice' is not considered as a DT 'supplier'. So the consumer drivers have no idea of when the ICE driver is going to probe. To address these issues, store the error pointer in a global xarray with ice node phandle as a key during probe in addition to the valid ice pointer and synchronize both qcom_ice_probe() and of_qcom_ice_get() using a mutex. If the xarray entry is NULL, then it implies that the driver is not probed yet, so return -EPROBE_DEFER. If it has any error pointer, return that error pointer directly. Otherwise, add the devlink as usual and return the valid pointer to the consumer. Xarray is used instead of platform drvdata, since driver core frees the drvdata during probe failure. So it cannot be used to pass the error pointer to the consumers. Note that this change only fixes the standalone ICE DT node bindings and not the ones with 'ice' range embedded in the consumer nodes, where there is no issue. Fixes: 2afbf43a4aec ("soc: qcom: Make the Qualcomm UFS/SDCC ICE a dedicated driver") Reported-by: Sumit Garg Tested-by: Sumit Garg # OP-TEE as TZ Acked-by: Sumit Garg Cc: stable@vger.kernel.org # 6.4 Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260518-qcom-ice-fix-v7-1-2a595382185b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ice.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index b203bc685cad..91991864b4a3 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -113,6 +114,9 @@ struct qcom_ice { u8 hwkm_version; }; +static DEFINE_XARRAY(ice_handles); +static DEFINE_MUTEX(ice_mutex); + static bool qcom_ice_check_supported(struct qcom_ice *ice) { u32 regval = qcom_ice_readl(ice, QCOM_ICE_REG_VERSION); @@ -631,6 +635,8 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) return qcom_ice_create(&pdev->dev, base); } + guard(mutex)(&ice_mutex); + /* * If the consumer node does not provider an 'ice' reg range * (legacy DT binding), then it must at least provide a phandle @@ -647,12 +653,13 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) return ERR_PTR(-EPROBE_DEFER); } - ice = platform_get_drvdata(pdev); - if (!ice) { - dev_err(dev, "Cannot get ice instance from %s\n", - dev_name(&pdev->dev)); + ice = xa_load(&ice_handles, pdev->dev.of_node->phandle); + if (IS_ERR_OR_NULL(ice)) { platform_device_put(pdev); - return ERR_PTR(-EPROBE_DEFER); + if (!ice) + return ERR_PTR(-EPROBE_DEFER); + else + return ice; } link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER); @@ -716,24 +723,40 @@ EXPORT_SYMBOL_GPL(devm_of_qcom_ice_get); static int qcom_ice_probe(struct platform_device *pdev) { + unsigned long phandle = pdev->dev.of_node->phandle; struct qcom_ice *engine; void __iomem *base; + guard(mutex)(&ice_mutex); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) { dev_warn(&pdev->dev, "ICE registers not found\n"); + /* Store the error pointer for devm_of_qcom_ice_get() */ + xa_store(&ice_handles, phandle, (__force void *)base, GFP_KERNEL); return PTR_ERR(base); } engine = qcom_ice_create(&pdev->dev, base); - if (IS_ERR(engine)) + if (IS_ERR(engine)) { + /* Store the error pointer for devm_of_qcom_ice_get() */ + xa_store(&ice_handles, phandle, engine, GFP_KERNEL); return PTR_ERR(engine); + } - platform_set_drvdata(pdev, engine); + xa_store(&ice_handles, phandle, engine, GFP_KERNEL); return 0; } +static void qcom_ice_remove(struct platform_device *pdev) +{ + unsigned long phandle = pdev->dev.of_node->phandle; + + guard(mutex)(&ice_mutex); + xa_store(&ice_handles, phandle, NULL, GFP_KERNEL); +} + static const struct of_device_id qcom_ice_of_match_table[] = { { .compatible = "qcom,inline-crypto-engine" }, { }, @@ -742,6 +765,7 @@ MODULE_DEVICE_TABLE(of, qcom_ice_of_match_table); static struct platform_driver qcom_ice_driver = { .probe = qcom_ice_probe, + .remove = qcom_ice_remove, .driver = { .name = "qcom-ice", .of_match_table = qcom_ice_of_match_table, From 5a4dc805a80e6fe303d6a4748cd451ea15987ffd Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 18 May 2026 19:22:18 +0530 Subject: [PATCH 12/28] soc: qcom: ice: Return -ENODEV if the ICE platform device is not found By the time the consumer driver calls devm_of_qcom_ice_get(), all the platform devices for ICE nodes would've been created by of_platform_default_populate(). So for the absence of any platform device, -ENODEV should not returned, not -EPROBE_DEFER. Fixes: 2afbf43a4aec ("soc: qcom: Make the Qualcomm UFS/SDCC ICE a dedicated driver") Tested-by: Sumit Garg # OP-TEE as TZ Acked-by: Sumit Garg Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260518-qcom-ice-fix-v7-2-2a595382185b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index 91991864b4a3..85deb9ea4a68 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -650,7 +650,7 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) pdev = of_find_device_by_node(node); if (!pdev) { dev_err(dev, "Cannot find device node %s\n", node->name); - return ERR_PTR(-EPROBE_DEFER); + return ERR_PTR(-ENODEV); } ice = xa_load(&ice_handles, pdev->dev.of_node->phandle); From b9ab7217dd7d567c50311afa94d6d6746cb77e04 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 18 May 2026 19:22:19 +0530 Subject: [PATCH 13/28] soc: qcom: ice: Return proper error codes from devm_of_qcom_ice_get() instead of NULL devm_of_qcom_ice_get() currently returns NULL if ICE SCM is not available or "qcom,ice" property is not found in DT. But this confuses the clients since NULL doesn't convey the reason for failure. So return proper error codes instead of NULL. Reported-by: Sumit Garg Reviewed-by: Konrad Dybcio Tested-by: Sumit Garg # OP-TEE as TZ Acked-by: Sumit Garg Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260518-qcom-ice-fix-v7-3-2a595382185b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ice.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index 85deb9ea4a68..2b592aa42941 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -563,7 +563,7 @@ static struct qcom_ice *qcom_ice_create(struct device *dev, if (!qcom_scm_ice_available()) { dev_warn(dev, "ICE SCM interface not found\n"); - return NULL; + return ERR_PTR(-EOPNOTSUPP); } engine = devm_kzalloc(dev, sizeof(*engine), GFP_KERNEL); @@ -645,7 +645,7 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) struct device_node *node __free(device_node) = of_parse_phandle(dev->of_node, "qcom,ice", 0); if (!node) - return NULL; + return ERR_PTR(-ENODEV); pdev = of_find_device_by_node(node); if (!pdev) { @@ -698,8 +698,7 @@ static void devm_of_qcom_ice_put(struct device *dev, void *res) * phandle via 'qcom,ice' property to an ICE DT, the ICE instance will already * be created and so this function will return that instead. * - * Return: ICE pointer on success, NULL if there is no ICE data provided by the - * consumer or ERR_PTR() on error. + * Return: ICE pointer on success, ERR_PTR() on error. */ struct qcom_ice *devm_of_qcom_ice_get(struct device *dev) { @@ -710,7 +709,7 @@ struct qcom_ice *devm_of_qcom_ice_get(struct device *dev) return ERR_PTR(-ENOMEM); ice = of_qcom_ice_get(dev); - if (!IS_ERR_OR_NULL(ice)) { + if (!IS_ERR(ice)) { *dr = ice; devres_add(dev, dr); } else { From 2ccbb3fa5cf47d05849cf6722aad1b4cc14df6d9 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 18 May 2026 19:22:20 +0530 Subject: [PATCH 14/28] mmc: sdhci-msm: Remove NULL check from devm_of_qcom_ice_get() Now since the devm_of_qcom_ice_get() API never returns NULL, remove the NULL check and also simplify the error handling. Reviewed-by: Konrad Dybcio Acked-by: Ulf Hansson Acked-by: Adrian Hunter Tested-by: Sumit Garg # OP-TEE as TZ Acked-by: Sumit Garg Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260518-qcom-ice-fix-v7-4-2a595382185b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/mmc/host/sdhci-msm.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 633462c0be5f..0882ce74e0c9 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1918,13 +1918,13 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, return 0; ice = devm_of_qcom_ice_get(dev); - if (ice == ERR_PTR(-EOPNOTSUPP)) { - dev_warn(dev, "Disabling inline encryption support\n"); - ice = NULL; - } + if (IS_ERR(ice)) { + if (ice != ERR_PTR(-EOPNOTSUPP)) + return PTR_ERR(ice); - if (IS_ERR_OR_NULL(ice)) - return PTR_ERR_OR_ZERO(ice); + dev_warn(dev, "Disabling inline encryption support\n"); + return 0; + } msm_host->ice = ice; From 4ac19b36bf4108706238cbc4f300b17dba8b881e Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 18 May 2026 19:22:21 +0530 Subject: [PATCH 15/28] scsi: ufs: ufs-qcom: Remove NULL check from devm_of_qcom_ice_get() Now since the devm_of_qcom_ice_get() API never returns NULL, remove the NULL check and also simplify the error handling. Reviewed-by: Konrad Dybcio Acked-by: Martin K. Petersen # UFS Tested-by: Sumit Garg # OP-TEE as TZ Acked-by: Sumit Garg Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260518-qcom-ice-fix-v7-5-2a595382185b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/ufs/host/ufs-qcom.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index bc037db46624..9c0973a7ffc3 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -177,13 +177,13 @@ static int ufs_qcom_ice_init(struct ufs_qcom_host *host) int i; ice = devm_of_qcom_ice_get(dev); - if (ice == ERR_PTR(-EOPNOTSUPP)) { - dev_warn(dev, "Disabling inline encryption support\n"); - ice = NULL; - } + if (IS_ERR(ice)) { + if (ice != ERR_PTR(-EOPNOTSUPP)) + return PTR_ERR(ice); - if (IS_ERR_OR_NULL(ice)) - return PTR_ERR_OR_ZERO(ice); + dev_warn(dev, "Disabling inline encryption support\n"); + return 0; + } host->ice = ice; From b73953af9bbd5c721c9d92b805a8aea8b0db74b1 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 14 May 2026 12:20:17 +0530 Subject: [PATCH 16/28] arm64: defconfig: Enable PCI M.2 power sequencing driver POWER_SEQUENCING_PCIE_M2 driver handles power supply to the PCIe M.2 connectors and is required on wide variety of ARM64 platforms such as Qcom Snapdragon X Elite laptops and Mediatek Dojo Chromebooks. Reviewed-by: Dmitry Baryshkov Reviewed-by: Krzysztof Kozlowski Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260514065017.11305-1-manivannan.sadhasivam@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index d905a0777f93..96ce783f24e7 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -260,6 +260,7 @@ CONFIG_PCI_ENDPOINT=y CONFIG_PCI_ENDPOINT_CONFIGFS=y CONFIG_PCI_EPF_TEST=m CONFIG_PCI_PWRCTRL_GENERIC=m +CONFIG_POWER_SEQUENCING_PCIE_M2=m CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y CONFIG_FW_LOADER_USER_HELPER=y From c15d7a2a11ea055bcecc0b538ae8ba79475637f9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 4 Dec 2025 11:17:23 +0100 Subject: [PATCH 17/28] tee: fix tee_ioctl_object_invoke_arg padding The tee_ioctl_object_invoke_arg structure has padding on some architectures but not on x86-32 and a few others: include/linux/tee.h:474:32: error: padding struct to align 'params' [-Werror=padded] I expect that all current users of this are on architectures that do have implicit padding here (arm64, arm, x86, riscv), so make the padding explicit in order to avoid surprises if this later gets used elsewhere. Fixes: d5b8b0fa1775 ("tee: add TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF") Signed-off-by: Arnd Bergmann Reviewed-by: Jens Wiklander Tested-by: Harshal Dev Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- include/uapi/linux/tee.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index cab5cadca8ef..5203977ed35d 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -470,6 +470,7 @@ struct tee_ioctl_object_invoke_arg { __u32 op; __u32 ret; __u32 num_params; + __u32 :32; /* num_params tells the actual number of element in params */ struct tee_ioctl_param params[]; }; From 26682f5efc276e3ad96d102019472bfbf03833b2 Mon Sep 17 00:00:00 2001 From: Georgiy Osokin Date: Wed, 8 Apr 2026 18:52:03 +0300 Subject: [PATCH 18/28] tee: shm: fix shm leak in register_shm_helper() register_shm_helper() allocates shm before calling iov_iter_npages(). If iov_iter_npages() returns 0, the function jumps to err_ctx_put and leaks shm. This can be triggered by TEE_IOC_SHM_REGISTER with struct tee_ioctl_shm_register_data where length is 0. Jump to err_free_shm instead. Fixes: 7bdee4157591 ("tee: Use iov_iter to better support shared buffer registration") Cc: stable@vger.kernel.org Cc: lvc-project@linuxtesting.org Signed-off-by: Georgiy Osokin Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/tee_shm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index e9ea9f80cfd9..6742b3579c86 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -435,7 +435,7 @@ register_shm_helper(struct tee_context *ctx, struct iov_iter *iter, u32 flags, num_pages = iov_iter_npages(iter, INT_MAX); if (!num_pages) { ret = ERR_PTR(-ENOMEM); - goto err_ctx_put; + goto err_free_shm; } shm->pages = kzalloc_objs(*shm->pages, num_pages); From 6fa9b543f6b4ed15ff72af266b29f316643de289 Mon Sep 17 00:00:00 2001 From: Qihang Date: Thu, 7 May 2026 23:39:17 +0800 Subject: [PATCH 19/28] tee: fix params_from_user() error path in tee_ioctl_supp_recv params_from_user() may acquire tee_shm references for MEMREF parameters before failing after partially processing the supplied parameter array. In tee_ioctl_supp_recv(), those references are currently not released on that error path. Fix this by freeing MEMREF references before returning when params_from_user() fails. Keep the final cleanup path in tee_ioctl_supp_recv() unchanged since supp_recv() may consume and replace the supplied parameters, unlike the other TEE ioctl callback paths. Signed-off-by: Qihang Signed-off-by: Jens Wiklander --- drivers/tee/tee_core.c | 56 +++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index ef9642d72672..1aac50c7c1de 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -530,11 +530,24 @@ static int params_to_user(struct tee_ioctl_param __user *uparams, return 0; } +static void free_params(struct tee_param *params, size_t num_params) +{ + size_t n; + + if (!params) + return; + + for (n = 0; n < num_params; n++) + if (tee_param_is_memref(params + n) && params[n].u.memref.shm) + tee_shm_put(params[n].u.memref.shm); + + kfree(params); +} + static int tee_ioctl_open_session(struct tee_context *ctx, struct tee_ioctl_buf_data __user *ubuf) { int rc; - size_t n; struct tee_ioctl_buf_data buf; struct tee_ioctl_open_session_arg __user *uarg; struct tee_ioctl_open_session_arg arg; @@ -595,16 +608,7 @@ out: */ if (rc && have_session && ctx->teedev->desc->ops->close_session) ctx->teedev->desc->ops->close_session(ctx, arg.session); - - if (params) { - /* Decrease ref count for all valid shared memory pointers */ - for (n = 0; n < arg.num_params; n++) - if (tee_param_is_memref(params + n) && - params[n].u.memref.shm) - tee_shm_put(params[n].u.memref.shm); - kfree(params); - } - + free_params(params, arg.num_params); return rc; } @@ -612,7 +616,6 @@ static int tee_ioctl_invoke(struct tee_context *ctx, struct tee_ioctl_buf_data __user *ubuf) { int rc; - size_t n; struct tee_ioctl_buf_data buf; struct tee_ioctl_invoke_arg __user *uarg; struct tee_ioctl_invoke_arg arg; @@ -657,14 +660,7 @@ static int tee_ioctl_invoke(struct tee_context *ctx, } rc = params_to_user(uparams, arg.num_params, params); out: - if (params) { - /* Decrease ref count for all valid shared memory pointers */ - for (n = 0; n < arg.num_params; n++) - if (tee_param_is_memref(params + n) && - params[n].u.memref.shm) - tee_shm_put(params[n].u.memref.shm); - kfree(params); - } + free_params(params, arg.num_params); return rc; } @@ -672,7 +668,6 @@ static int tee_ioctl_object_invoke(struct tee_context *ctx, struct tee_ioctl_buf_data __user *ubuf) { int rc; - size_t n; struct tee_ioctl_buf_data buf; struct tee_ioctl_object_invoke_arg __user *uarg; struct tee_ioctl_object_invoke_arg arg; @@ -716,14 +711,7 @@ static int tee_ioctl_object_invoke(struct tee_context *ctx, } rc = params_to_user(uparams, arg.num_params, params); out: - if (params) { - /* Decrease ref count for all valid shared memory pointers */ - for (n = 0; n < arg.num_params; n++) - if (tee_param_is_memref(params + n) && - params[n].u.memref.shm) - tee_shm_put(params[n].u.memref.shm); - kfree(params); - } + free_params(params, arg.num_params); return rc; } @@ -846,9 +834,15 @@ static int tee_ioctl_supp_recv(struct tee_context *ctx, return -ENOMEM; rc = params_from_user(ctx, params, num_params, uarg->params); - if (rc) - goto out; + if (rc) { + free_params(params, num_params); + return rc; + } + /* + * supp_recv() may consume and replace the supplied parameters, so the + * final cleanup cannot use free_params() like the other ioctl paths. + */ rc = ctx->teedev->desc->ops->supp_recv(ctx, &func, &num_params, params); if (rc) goto out; From 471c18323dfdfe7844e193b896a9267ae23a1026 Mon Sep 17 00:00:00 2001 From: Robertus Diawan Chris Date: Tue, 19 May 2026 09:05:28 +0700 Subject: [PATCH 20/28] tee: qcomtee: add missing va_end in early return qcomtee_object_user_init() qcomtee_object_user_init() is a variadic function and when the function return because there's no dispatch callback in QCOMTEE_OBJECT_TYPE_CB case, there's no va_end to cleanup "ap" object initialized by va_start and that can cause undefined behavior. So make sure to use va_end before returning the error code when there's no dispatch callback. This is reported by Coverity Scan as "Missing varargs init or cleanup". Fixes: d6e290837e50 ("tee: add Qualcomm TEE driver") Signed-off-by: Robertus Diawan Chris Reviewed-by: Amirreza Zarrabi Signed-off-by: Jens Wiklander --- drivers/tee/qcomtee/core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/tee/qcomtee/core.c b/drivers/tee/qcomtee/core.c index b1cb50e434f0..60fe3b5776e3 100644 --- a/drivers/tee/qcomtee/core.c +++ b/drivers/tee/qcomtee/core.c @@ -306,8 +306,10 @@ int qcomtee_object_user_init(struct qcomtee_object *object, break; case QCOMTEE_OBJECT_TYPE_CB: object->ops = ops; - if (!object->ops->dispatch) - return -EINVAL; + if (!object->ops->dispatch) { + ret = -EINVAL; + break; + } /* If failed, "no-name". */ object->name = kvasprintf_const(GFP_KERNEL, fmt, ap); From b7c9047f851e80b580aba485b61785c7554b992c Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:29 +0530 Subject: [PATCH 21/28] arm64: dts: qcom: milos: Add power-domain and iface clk for ice node Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the UFS_PHY_GDSC power domain is enabled. Specify both the UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for milos. Fixes: 04bb37433330e ("arm64: dts: qcom: milos: Add UFS nodes") Signed-off-by: Harshal Dev Reviewed-by: Konrad Dybcio Reviewed-by: Kuldeep Singh Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-12-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/milos.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/milos.dtsi b/arch/arm64/boot/dts/qcom/milos.dtsi index 4a64a98a434b..a6e463f3885d 100644 --- a/arch/arm64/boot/dts/qcom/milos.dtsi +++ b/arch/arm64/boot/dts/qcom/milos.dtsi @@ -1275,7 +1275,11 @@ "qcom,inline-crypto-engine"; reg = <0x0 0x01d88000 0x0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc UFS_PHY_GDSC>; }; tcsr_mutex: hwlock@1f40000 { From 90825ab392ac15a51f62e3f561ad77e0226a1cfc Mon Sep 17 00:00:00 2001 From: Harshal Dev Date: Thu, 16 Apr 2026 17:29:30 +0530 Subject: [PATCH 22/28] arm64: dts: qcom: eliza: Add power-domain and iface clk for ice node Qualcomm in-line crypto engine (ICE) platform driver specifies and votes for its own resources. Before accessing ICE hardware during probe, to avoid potential unclocked register access issues (when clk_ignore_unused is not passed on the kernel command line), in addition to the 'core' clock the 'iface' clock should also be turned on by the driver. This can only be done if the GCC_UFS_PHY_GDSC power domain is enabled. Specify both the GCC_UFS_PHY_GDSC power domain and the 'iface' clock in the ICE node for eliza. Fixes: af20af39fc09b ("arm64: dts: qcom: Introduce Eliza Soc base dtsi") Signed-off-by: Harshal Dev Reviewed-by: Konrad Dybcio Fixes: 54a4f0239f2e ("KVM: MMU: make kvm_mmu_zap_page() return the Reviewed-by: Kuldeep Singh Link: https://lore.kernel.org/r/20260416-qcom_ice_power_and_clk_vote-v5-13-5ccf5d7e2846@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/eliza.dtsi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/eliza.dtsi b/arch/arm64/boot/dts/qcom/eliza.dtsi index 4a7a0ac40ce6..7e97361a5dc5 100644 --- a/arch/arm64/boot/dts/qcom/eliza.dtsi +++ b/arch/arm64/boot/dts/qcom/eliza.dtsi @@ -843,7 +843,11 @@ "qcom,inline-crypto-engine"; reg = <0x0 0x01d88000 0x0 0x18000>; - clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; + clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, + <&gcc GCC_UFS_PHY_AHB_CLK>; + clock-names = "core", + "iface"; + power-domains = <&gcc GCC_UFS_PHY_GDSC>; }; tcsr_mutex: hwlock@1f40000 { From 462a85f9f887a4fef36550bb76c7f7d7a0fa296c Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Wed, 20 May 2026 21:27:04 +0530 Subject: [PATCH 23/28] soc: qcom: ice: Fix the error code when 'qcom,ice' property is not found When both 'ice' reg entry and 'qcom,ice' property are not found in DT, then it implies that ICE is not supported. So return -EOPNOTSUPP instead of -ENODEV to client drivers to specify ICE functionality is not supported. Fixes: b9ab7217dd7d ("soc: qcom: ice: Return proper error codes from devm_of_qcom_ice_get() instead of NULL") Reported-by: Marek Szyprowski Closes: https://lore.kernel.org/linux-arm-msm/8bac0358-9da0-4cbb-98ee-333b85ba4908@samsung.com Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20260520155704.130803-1-manivannan.sadhasivam@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index e26d5a64763c..5f20108aa03e 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -658,7 +658,7 @@ static struct qcom_ice *of_qcom_ice_get(struct device *dev) struct device_node *node __free(device_node) = of_parse_phandle(dev->of_node, "qcom,ice", 0); if (!node) - return ERR_PTR(-ENODEV); + return ERR_PTR(-EOPNOTSUPP); pdev = of_find_device_by_node(node); if (!pdev) { From 63838c323924fe4a78b2323bd45aa1030f72ca60 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Sun, 24 May 2026 22:47:09 -0400 Subject: [PATCH 24/28] ARM: socfpga: Fix OF node refcount leak in SMP setup socfpga_smp_prepare_cpus() looks up the Cortex-A9 SCU node with of_find_compatible_node(), which returns a node reference that must be released with of_node_put(). The function maps the SCU registers and then returns without dropping that reference, leaking the node on both the success path and the of_iomap() failure path. Drop the reference once the mapping attempt is complete. The returned MMIO mapping does not depend on keeping the device node reference held. Fixes: 122694a0c712 ("ARM: socfpga: use of_iomap to map the SCU") Cc: stable@vger.kernel.org Signed-off-by: Yuho Choi Signed-off-by: Dinh Nguyen --- arch/arm/mach-socfpga/platsmp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-socfpga/platsmp.c b/arch/arm/mach-socfpga/platsmp.c index 201191cf68f3..349e6c54518e 100644 --- a/arch/arm/mach-socfpga/platsmp.c +++ b/arch/arm/mach-socfpga/platsmp.c @@ -78,6 +78,7 @@ static void __init socfpga_smp_prepare_cpus(unsigned int max_cpus) } socfpga_scu_base_addr = of_iomap(np, 0); + of_node_put(np); if (!socfpga_scu_base_addr) return; scu_enable(socfpga_scu_base_addr); From 66ac2df408ede627aaae588d4ce7e611dd25b4f9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 28 May 2026 10:25:26 +0200 Subject: [PATCH 25/28] ARM: dts: gemini: Fix partition offsets These FIS partition offsets were never right: the comment clearly states the FIS index is at 0xfe0000 and 0x7f * 0x200000 is 0xfe0000. Tested on the iTian SQ201. Fixes: d88b11ef91b1 ("ARM: dts: Fix up SQ201 flash access") Fixes: b5a923f8c739 ("ARM: dts: gemini: Switch to redboot partition parsing") Signed-off-by: Linus Walleij Signed-off-by: Arnd Bergmann --- arch/arm/boot/dts/gemini/gemini-sl93512r.dts | 2 +- arch/arm/boot/dts/gemini/gemini-sq201.dts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/gemini/gemini-sl93512r.dts b/arch/arm/boot/dts/gemini/gemini-sl93512r.dts index 4992ec276de9..341dec9b636a 100644 --- a/arch/arm/boot/dts/gemini/gemini-sl93512r.dts +++ b/arch/arm/boot/dts/gemini/gemini-sl93512r.dts @@ -146,7 +146,7 @@ partitions { compatible = "redboot-fis"; /* Eraseblock at 0xfe0000 */ - fis-index-block = <0x1fc>; + fis-index-block = <0x7f>; }; }; diff --git a/arch/arm/boot/dts/gemini/gemini-sq201.dts b/arch/arm/boot/dts/gemini/gemini-sq201.dts index f8c6f6e5cdea..bfd1e8581ad6 100644 --- a/arch/arm/boot/dts/gemini/gemini-sq201.dts +++ b/arch/arm/boot/dts/gemini/gemini-sq201.dts @@ -134,7 +134,7 @@ partitions { compatible = "redboot-fis"; /* Eraseblock at 0xfe0000 */ - fis-index-block = <0x1fc>; + fis-index-block = <0x7f>; }; }; From c889b146478885344a220dd468e5a08de088cbc5 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Tue, 5 May 2026 13:13:02 +0000 Subject: [PATCH 26/28] firmware: samsung: acpm: Fix false timeouts and Use-After-Free in polling Sashiko identified severe races in the polling state machine [1]. In the ACPM driver's polling mode, threads waited for responses by monitoring the globally shared 'bitmap_seqnum'. This caused false timeouts because if a thread processed its response and freed the sequence number, a concurrent TX thread could immediately reallocate it before the polling thread woke up. Additionally, the driver suffered from a cross-thread Use-After-Free (UAF) preemption race. Previously, acpm_get_rx() cleared the sequence number of whichever RX message it drained from the hardware queue. This meant Thread A could globally free Thread B's sequence slot while Thread B was asleep. A new Thread C could then steal the slot, overwrite the buffer, and leave Thread B to wake up to corrupted state or a timeout. Fix this by rewriting the polling state machine: 1. Decouple polling from the global allocator by introducing a per-slot 'completed' flag, synchronized via smp_store_release() and smp_load_acquire(). 2. Strip acpm_get_saved_rx() out of acpm_get_rx() to make it a pure queue-draining function. Introduce a 'native_match' boolean argument which evaluates to true only if the thread natively processed its own sequence number during the call. This explicitly informs the polling loop whether it must retrieve its payload from the cross-thread cache. 3. Centralize the cache fallback and sequence number free (clear_bit) inside the polling loop. Crucially, the free operation now strictly targets the thread's own TX sequence number (xfer->txd[0]), rather than the drained RX sequence number. This enforces strict ownership: a thread only ever frees its own allocated sequence slot, and only at the exact moment it completes its poll, eliminating the UAF window. Furthermore, explicitly guard the 'native_match' assignment with an if (rx_seqnum == tx_seqnum) check, even for zero-length (no payload) responses. While an unguarded assignment wouldn't crash (because the cache fallback acpm_get_saved_rx() safely returns early on zero-length transfers) doing so would "lie" to the state machine. If a thread drained the queue and found another thread's zero-length message, setting native_match = true would falsely convince the polling loop that it natively handled its own response. Maintaining a rigorous state machine requires that native_match is only set when a thread explicitly processes its own sequence number. Cc: stable@vger.kernel.org Fixes: a88927b534ba ("firmware: add Exynos ACPM protocol driver") Closes: https://sashiko.dev/#/patchset/20260429-acpm-fixes-sashiko-reports-v3-0-47cf74ab09ad%40linaro.org [1] Signed-off-by: Tudor Ambarus Link: https://patch.msgid.link/20260505-acpm-fixes-sashiko-reports-v5-5-43b5ee7f1674@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/firmware/samsung/exynos-acpm.c | 68 ++++++++++++++++++-------- 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index 9766425a44ab..620880d8820a 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -105,11 +105,14 @@ struct acpm_queue { * @cmd: pointer to where the data shall be saved. * @n_cmd: number of 32-bit commands. * @rxcnt: expected length of the response in 32-bit words. + * @completed: flag indicating if the firmware response has been fully + * processed. */ struct acpm_rx_data { u32 *cmd; size_t n_cmd; size_t rxcnt; + bool completed; }; #define ACPM_SEQNUM_MAX 64 @@ -204,26 +207,28 @@ static void acpm_get_saved_rx(struct acpm_chan *achan, rx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, rx_data->cmd[0]); - if (rx_seqnum == tx_seqnum) { + if (rx_seqnum == tx_seqnum) memcpy(xfer->rxd, rx_data->cmd, xfer->rxcnt * sizeof(*xfer->rxd)); - clear_bit(rx_seqnum - 1, achan->bitmap_seqnum); - } } /** * acpm_get_rx() - get response from RX queue. * @achan: ACPM channel info. * @xfer: reference to the transfer to get response for. + * @native_match: pointer to a boolean set to true if the thread natively + * processed its own sequence number during this call. * * Return: 0 on success, -errno otherwise. */ -static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) +static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer, + bool *native_match) { u32 rx_front, rx_seqnum, tx_seqnum, seqnum; const void __iomem *base, *addr; struct acpm_rx_data *rx_data; u32 i, val, mlen; - bool rx_set = false; + + *native_match = false; guard(mutex)(&achan->rx_lock); @@ -232,10 +237,8 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) tx_seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, xfer->txd[0]); - if (i == rx_front) { - acpm_get_saved_rx(achan, xfer, tx_seqnum); + if (i == rx_front) return 0; - } base = achan->rx.base; mlen = achan->mlen; @@ -259,8 +262,13 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) if (rx_data->rxcnt) { if (rx_seqnum == tx_seqnum) { __ioread32_copy(xfer->rxd, addr, xfer->rxcnt); - rx_set = true; - clear_bit(seqnum, achan->bitmap_seqnum); + /* + * Signal completion to the polling thread. + * Pairs with smp_load_acquire() in polling + * loop. + */ + smp_store_release(&rx_data->completed, true); + *native_match = true; } else { /* * The RX data corresponds to another request. @@ -270,9 +278,21 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) */ __ioread32_copy(rx_data->cmd, addr, rx_data->rxcnt); + /* + * Signal completion to the polling thread. + * Pairs with smp_load_acquire() in polling + * loop. + */ + smp_store_release(&rx_data->completed, true); } } else { - clear_bit(seqnum, achan->bitmap_seqnum); + /* + * Signal completion to the polling thread. + * Pairs with smp_load_acquire() in polling loop. + */ + smp_store_release(&rx_data->completed, true); + if (rx_seqnum == tx_seqnum) + *native_match = true; } i = (i + 1) % achan->qlen; @@ -281,13 +301,6 @@ static int acpm_get_rx(struct acpm_chan *achan, const struct acpm_xfer *xfer) /* We saved all responses, mark RX empty. */ writel(rx_front, achan->rx.rear); - /* - * If the response was not in this iteration of the queue, check if the - * RX data was previously saved. - */ - if (!rx_set) - acpm_get_saved_rx(achan, xfer, tx_seqnum); - return 0; } @@ -302,6 +315,7 @@ static int acpm_dequeue_by_polling(struct acpm_chan *achan, const struct acpm_xfer *xfer) { struct device *dev = achan->acpm->dev; + bool native_match; ktime_t timeout; u32 seqnum; int ret; @@ -310,12 +324,25 @@ static int acpm_dequeue_by_polling(struct acpm_chan *achan, timeout = ktime_add_us(ktime_get(), ACPM_POLL_TIMEOUT_US); do { - ret = acpm_get_rx(achan, xfer); + ret = acpm_get_rx(achan, xfer, &native_match); if (ret) return ret; - if (!test_bit(seqnum - 1, achan->bitmap_seqnum)) + /* + * Safely check if our specific transaction has been processed. + * smp_load_acquire prevents the CPU from speculatively + * executing subsequent instructions before the transaction is + * synchronized. + */ + if (smp_load_acquire(&achan->rx_data[seqnum - 1].completed)) { + /* Retrieve payload if another thread cached it for us */ + if (!native_match) + acpm_get_saved_rx(achan, xfer, seqnum); + + /* Relinquish ownership of the sequence slot */ + clear_bit(seqnum - 1, achan->bitmap_seqnum); return 0; + } /* Determined experimentally. */ udelay(20); @@ -380,6 +407,7 @@ static void acpm_prepare_xfer(struct acpm_chan *achan, /* Clear data for upcoming responses */ rx_data = &achan->rx_data[achan->seqnum - 1]; + rx_data->completed = false; memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->n_cmd); /* zero means no response expected */ rx_data->rxcnt = xfer->rxcnt; From bf296f83a3ddab1ab875edc4e8862cb10553064f Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Tue, 5 May 2026 13:13:03 +0000 Subject: [PATCH 27/28] firmware: samsung: acpm: Fix missing LKMM barriers in sequence allocator Sashiko identified memory ordering races in [1]. The ACPM driver uses a globally shared 'bitmap_seqnum' to track available sequence numbers. Even though threads now strictly free their own sequence numbers, the allocation and freeing of these bits across concurrent threads are effectively lockless operations and require explicit LKMM memory barriers. Previously, the driver used plain bitwise operators (test_bit, set_bit, clear_bit), which lack ordering guarantees. This creates two race conditions on weakly ordered architectures like ARM64: 1. Polling Release Violation: The polling thread copies its payload and calls clear_bit(). Without a release barrier, the CPU can reorder the memory operations, making the cleared bit globally visible before the payload reads have fully completed. 2. TX Acquire Violation: The TX thread loops on test_bit(), calls set_bit(), and then wipes the payload buffer via memset(). Without an acquire barrier, the CPU can speculatively execute the memset() before the bit is safely and formally claimed. If these reorderings overlap, a new TX thread can claim the sequence number and overwrite the buffer while the original polling thread is still actively reading from it. Fix this by upgrading the bitwise operators. Wrap the TX allocation in test_and_set_bit_lock() to establish formal LKMM Acquire semantics, and pair it with clear_bit_unlock() in the polling path to enforce Release semantics. Cc: stable@vger.kernel.org Fixes: a88927b534ba ("firmware: add Exynos ACPM protocol driver") Closes: https://sashiko.dev/#/patchset/20260423-acpm-fixes-sashiko-reports-v1-0-2217b790925e%40linaro.org [1] Signed-off-by: Tudor Ambarus Link: https://patch.msgid.link/20260505-acpm-fixes-sashiko-reports-v5-6-43b5ee7f1674@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/firmware/samsung/exynos-acpm.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index 620880d8820a..3fa9fe283be4 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include @@ -340,7 +340,7 @@ static int acpm_dequeue_by_polling(struct acpm_chan *achan, acpm_get_saved_rx(achan, xfer, seqnum); /* Relinquish ownership of the sequence slot */ - clear_bit(seqnum - 1, achan->bitmap_seqnum); + clear_bit_unlock(seqnum - 1, achan->bitmap_seqnum); return 0; } @@ -397,11 +397,18 @@ static void acpm_prepare_xfer(struct acpm_chan *achan, struct acpm_rx_data *rx_data; u32 *txd = (u32 *)xfer->txd; - /* Prevent chan->seqnum from being re-used */ + /* + * Prevent chan->seqnum from being re-used. + * test_and_set_bit_lock() provides formal LKMM Acquire semantics. + * It pairs with the RX thread's clear_bit_unlock() to ensure the CPU + * does not speculatively execute the rx_data buffer wipe (memset) + * before the sequence number is safely claimed. + */ do { if (++achan->seqnum == ACPM_SEQNUM_MAX) achan->seqnum = 1; - } while (test_bit(achan->seqnum - 1, achan->bitmap_seqnum)); + /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */ + } while (test_and_set_bit_lock(achan->seqnum - 1, achan->bitmap_seqnum)); txd[0] |= FIELD_PREP(ACPM_PROTOCOL_SEQNUM, achan->seqnum); @@ -411,9 +418,6 @@ static void acpm_prepare_xfer(struct acpm_chan *achan, memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->n_cmd); /* zero means no response expected */ rx_data->rxcnt = xfer->rxcnt; - - /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */ - set_bit(achan->seqnum - 1, achan->bitmap_seqnum); } /** From 7fe40c32a33905302341797b5d12c541729dd08d Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Tue, 5 May 2026 13:13:04 +0000 Subject: [PATCH 28/28] firmware: samsung: acpm: Fix infinite loop on sequence number exhaustion Sashiko identified a possible infinite loop [1]. ACPM IPC sequence numbers are tracked via a 64-bit bitmap. Previously, acpm_prepare_xfer() used a do...while loop to search for a free sequence number. If all 63 available sequence numbers are leaked due to transient hardware timeouts or mailbox failures, the bitmap becomes full. The next call to acpm_prepare_xfer() would enter an infinite loop. Fix this by utilizing the kernel's optimized bitmap search functions (find_next_zero_bit / find_first_zero_bit). If the pool is completely exhausted, log the failure and return -EBUSY to allow the kernel to fail gracefully instead of hanging. Furthermore, drop the allocation loop entirely. Because acpm_prepare_xfer() is strictly called under the 'tx_lock' mutex, sequence number allocations are perfectly serialized. If find_next_zero_bit() locates a free bit, a single test_and_set_bit_lock() is mathematically guaranteed to succeed. To enforce this locking invariant, wrap the allocation in a WARN_ON_ONCE. If the atomic set fails, it indicates the driver's mutex serialization is fundamentally broken. The warning generates a stack trace for debugging, while returning -EIO immediately aborts the transfer to prevent silent payload corruption. Cc: stable@vger.kernel.org Fixes: a88927b534ba ("firmware: add Exynos ACPM protocol driver") Closes: https://sashiko.dev/#/patchset/20260420-acpm-tmu-v3-0-3dc8e93f0b26%40linaro.org [1] Signed-off-by: Tudor Ambarus Link: https://patch.msgid.link/20260505-acpm-fixes-sashiko-reports-v5-7-43b5ee7f1674@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/firmware/samsung/exynos-acpm.c | 45 ++++++++++++++++++-------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index 3fa9fe283be4..19db3674a28f 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -390,34 +391,48 @@ static int acpm_wait_for_queue_slots(struct acpm_chan *achan, u32 next_tx_front) * TX queue. * @achan: ACPM channel info. * @xfer: reference to the transfer being prepared. + * + * Return: 0 on success, -errno otherwise. */ -static void acpm_prepare_xfer(struct acpm_chan *achan, - const struct acpm_xfer *xfer) +static int acpm_prepare_xfer(struct acpm_chan *achan, + const struct acpm_xfer *xfer) { struct acpm_rx_data *rx_data; u32 *txd = (u32 *)xfer->txd; + unsigned long size = ACPM_SEQNUM_MAX - 1; + unsigned long bit = achan->seqnum; + + bit = find_next_zero_bit(achan->bitmap_seqnum, size, bit); + if (bit >= size) { + bit = find_first_zero_bit(achan->bitmap_seqnum, size); + if (bit >= size) { + dev_err_ratelimited(achan->acpm->dev, + "ACPM sequence number pool exhausted\n"); + return -EBUSY; + } + } /* - * Prevent chan->seqnum from being re-used. - * test_and_set_bit_lock() provides formal LKMM Acquire semantics. - * It pairs with the RX thread's clear_bit_unlock() to ensure the CPU - * does not speculatively execute the rx_data buffer wipe (memset) - * before the sequence number is safely claimed. + * Execute the atomic set to formally claim the bit and establish + * LKMM Acquire semantics against the RX thread's clear_bit_unlock(). + * A loop is unnecessary because allocations are strictly serialized + * by tx_lock. */ - do { - if (++achan->seqnum == ACPM_SEQNUM_MAX) - achan->seqnum = 1; - /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */ - } while (test_and_set_bit_lock(achan->seqnum - 1, achan->bitmap_seqnum)); + if (WARN_ON_ONCE(test_and_set_bit_lock(bit, achan->bitmap_seqnum))) + return -EIO; + /* Flag the index based on seqnum. (seqnum: 1~63, bitmap: 0~62) */ + achan->seqnum = bit + 1; txd[0] |= FIELD_PREP(ACPM_PROTOCOL_SEQNUM, achan->seqnum); /* Clear data for upcoming responses */ - rx_data = &achan->rx_data[achan->seqnum - 1]; + rx_data = &achan->rx_data[bit]; rx_data->completed = false; memset(rx_data->cmd, 0, sizeof(*rx_data->cmd) * rx_data->n_cmd); /* zero means no response expected */ rx_data->rxcnt = xfer->rxcnt; + + return 0; } /** @@ -477,7 +492,9 @@ int acpm_do_xfer(struct acpm_handle *handle, const struct acpm_xfer *xfer) if (ret) return ret; - acpm_prepare_xfer(achan, xfer); + ret = acpm_prepare_xfer(achan, xfer); + if (ret) + return ret; /* Write TX command. */ __iowrite32_copy(achan->tx.base + achan->mlen * tx_front,