From 3e529f57931417120fab700afeef6e49553250d5 Mon Sep 17 00:00:00 2001 From: Muhammad Amirul Asyraf Mohamad Jamian Date: Thu, 16 Apr 2026 00:22:06 -0700 Subject: [PATCH 1/3] firmware: stratix10-svc: Return -EOPNOTSUPP when ATF async unsupported Add a 'supported' flag to struct stratix10_async_ctrl to indicate whether the secure firmware supports SIP SVC v3 asynchronous communication. When the ATF version check in stratix10_svc_async_init() fails, set supported=false and return -EOPNOTSUPP instead of -EINVAL. This allows callers to distinguish between "async not supported by this ATF version" (-EOPNOTSUPP) and "programming error / bad argument" (-EINVAL), and take appropriate action (e.g. fall back to synchronous V1 SMC path) rather than treating both as fatal. Also update stratix10_svc_add_async_client() to return -EOPNOTSUPP immediately when async is not supported, rather than -EINVAL from the !actrl->initialized check, so client drivers receive a consistent and meaningful error code. This patch is a prerequisite for the following fix and must be applied together with it to correctly restore functionality on old ATF versions. Fixes: bcb9f4f07061 ("firmware: stratix10-svc: Add support for async communication") Cc: stable@vger.kernel.org Suggested-by: Anders Hedlund Signed-off-by: Mahesh Rao Signed-off-by: Muhammad Amirul Asyraf Mohamad Jamian Signed-off-by: Dinh Nguyen --- drivers/firmware/stratix10-svc.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index e9e35d67ef96..8a4f18602f36 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -212,6 +212,7 @@ struct stratix10_async_chan { /** * struct stratix10_async_ctrl - Control structure for Stratix10 * asynchronous operations + * @supported: Flag indicating whether the system supports async operations * @initialized: Flag indicating whether the control structure has * been initialized * @invoke_fn: Function pointer for invoking Stratix10 service calls @@ -228,6 +229,7 @@ struct stratix10_async_chan { */ struct stratix10_async_ctrl { + bool supported; bool initialized; void (*invoke_fn)(struct stratix10_async_ctrl *actrl, const struct arm_smccc_1_2_regs *args, @@ -1103,6 +1105,7 @@ EXPORT_SYMBOL_GPL(stratix10_svc_request_channel_byname); * Return: 0 on success, or a negative error code on failure: * -EINVAL if the channel is NULL or the async controller is * not initialized. + * -EOPNOTSUPP if async operations are not supported. * -EALREADY if the async channel is already allocated. * -ENOMEM if memory allocation fails. * Other negative values if ID allocation fails. @@ -1121,6 +1124,9 @@ int stratix10_svc_add_async_client(struct stratix10_svc_chan *chan, ctrl = chan->ctrl; actrl = &ctrl->actrl; + if (!actrl->supported) + return -EOPNOTSUPP; + if (!actrl->initialized) { dev_err(ctrl->dev, "Async controller not initialized\n"); return -EINVAL; @@ -1562,6 +1568,7 @@ static inline void stratix10_smc_1_2(struct stratix10_async_ctrl *actrl, * initialized, -ENOMEM if memory allocation fails, * -EADDRINUSE if the client ID is already reserved, or other * negative error codes on failure. + * -EOPNOTSUPP if system doesn't support async operations. */ static int stratix10_svc_async_init(struct stratix10_svc_controller *controller) { @@ -1585,10 +1592,12 @@ static int stratix10_svc_async_init(struct stratix10_svc_controller *controller) !(res.a1 > ASYNC_ATF_MINIMUM_MAJOR_VERSION || (res.a1 == ASYNC_ATF_MINIMUM_MAJOR_VERSION && res.a2 >= ASYNC_ATF_MINIMUM_MINOR_VERSION))) { - dev_err(dev, - "Intel Service Layer Driver: ATF version is not compatible for async operation\n"); - return -EINVAL; + dev_info(dev, + "Intel Service Layer Driver: ATF version is not compatible for async operation\n"); + actrl->supported = false; + return -EOPNOTSUPP; } + actrl->supported = true; actrl->invoke_fn = stratix10_smc_1_2; From 371aa062219a0af108fb8992f0759d1bac1e8c91 Mon Sep 17 00:00:00 2001 From: Muhammad Amirul Asyraf Mohamad Jamian Date: Thu, 16 Apr 2026 00:22:07 -0700 Subject: [PATCH 2/3] firmware: stratix10-svc: Don't fail probe when async ops unsupported When the ATF version is too old to support SIP SVC v3 asynchronous operations (e.g. ATF 2.5), stratix10_svc_async_init() returns -EOPNOTSUPP. The probe function currently treats any non-zero return as fatal and aborts, logging: stratix10-svc firmware:svc: Intel Service Layer Driver: ATF version \ is not compatible for async operation stratix10-svc firmware:svc: probe with driver stratix10-svc failed \ with error -95 This prevents the SVC driver from loading entirely, causing all dependent client drivers (hwmon, RSU, FCS) to also fail to probe even though they can operate correctly via the synchronous V1 SMC path. Fix this by treating -EOPNOTSUPP from stratix10_svc_async_init() as a non-fatal degraded condition. The driver loads in sync-only mode and logs: stratix10-svc firmware:svc: Intel Service Layer Driver Initialized \ (sync-only mode) Fixes: bcb9f4f07061 ("firmware: stratix10-svc: Add support for async communication") Cc: stable@vger.kernel.org Signed-off-by: Muhammad Amirul Asyraf Mohamad Jamian Signed-off-by: Dinh Nguyen --- drivers/firmware/stratix10-svc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index 8a4f18602f36..39eb78f5905b 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -1961,10 +1961,14 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) init_completion(&controller->complete_status); ret = stratix10_svc_async_init(controller); - if (ret) { + if (ret == -EOPNOTSUPP) { + dev_info(dev, "Intel Service Layer Driver Initialized (sync-only mode)\n"); + } else if (ret) { dev_dbg(dev, "Intel Service Layer Driver: Error on stratix10_svc_async_init %d\n", ret); goto err_destroy_pool; + } else { + dev_info(dev, "Intel Service Layer Driver Initialized\n"); } fifo_size = sizeof(struct stratix10_svc_data) * SVC_NUM_DATA_IN_FIFO; From bfd2eb9bba548a8f63c3339bb1fb9a2031a42d86 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Wed, 20 May 2026 21:54:57 -0500 Subject: [PATCH 3/3] firmware: stratix10-rsu: Fix NULL deref on rsu_send_msg() timeout in probe rsu_send_msg() can return -ETIMEDOUT when wait_for_completion_interruptible_timeout() fires while the SMC call is still pending. In stratix10_rsu_probe(), the error paths for COMMAND_RSU_DCMF_VERSION, COMMAND_RSU_DCMF_STATUS, COMMAND_RSU_MAX_RETRY and COMMAND_RSU_GET_SPT_TABLE call stratix10_svc_free_channel() - which sets chan->scl to NULL - but then fall through and queue the next request on the same channel. The next svc kthread that runs will dereference pdata->chan->scl in its receive callback path, triggering a NULL pointer dereference identical to the one fixed by commit c45f7263100c ("firmware: stratix10-rsu: Fix NULL pointer dereference when RSU is disabled") for the COMMAND_RSU_STATUS path. Apply the same cleanup pattern to the remaining failure paths: remove the async client, free the channel, and return early so no further messages are queued on a channel whose scl has been cleared. While at it, clean up stratix10_rsu_probe() in two ways without changing behavior: - Drop redundant zero-initialization of fields already cleared by devm_kzalloc(): client.receive_cb, status.* and spt0/1_address (INVALID_SPT_ADDRESS is 0x0). - Replace five identical 3-line error-cleanup blocks (stratix10_svc_remove_async_client() + stratix10_svc_free_channel() + return ret) with goto labels (remove_async_client, free_channel), matching the standard kernel resource-unwinding pattern and making it easier to extend the probe sequence without forgetting matching cleanup. Also move init_completion() next to mutex_init() so sync-primitive initialization is grouped before anything that could trigger a callback. Fixes: 15847537b623 ("firmware: stratix10-rsu: Migrate RSU driver to use stratix10 asynchronous framework.") Cc: stable@kernel.org Assisted-by: Claude:claude-4.7-opus-high Cursor Signed-off-by: Dinh Nguyen --- v2: Add a minor clean-up of the function stratix10_rsu_probe() to have a centralize exit for all the rsu_send_async_msg() and rsu_send_msg(). --- drivers/firmware/stratix10-rsu.c | 45 ++++++++++++++------------------ 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c index e1912108a0fe..2a7a0f774389 100644 --- a/drivers/firmware/stratix10-rsu.c +++ b/drivers/firmware/stratix10-rsu.c @@ -723,15 +723,9 @@ static int stratix10_rsu_probe(struct platform_device *pdev) return -ENOMEM; priv->client.dev = dev; - priv->client.receive_cb = NULL; priv->client.priv = priv; - priv->status.current_image = 0; - priv->status.fail_image = 0; - priv->status.error_location = 0; - priv->status.error_details = 0; - priv->status.version = 0; - priv->status.state = 0; priv->retry_counter = INVALID_RETRY_COUNTER; + priv->max_retry = INVALID_RETRY_COUNTER; priv->dcmf_version.dcmf0 = INVALID_DCMF_VERSION; priv->dcmf_version.dcmf1 = INVALID_DCMF_VERSION; priv->dcmf_version.dcmf2 = INVALID_DCMF_VERSION; @@ -740,11 +734,11 @@ static int stratix10_rsu_probe(struct platform_device *pdev) priv->dcmf_status.dcmf1 = INVALID_DCMF_STATUS; priv->dcmf_status.dcmf2 = INVALID_DCMF_STATUS; priv->dcmf_status.dcmf3 = INVALID_DCMF_STATUS; - priv->max_retry = INVALID_RETRY_COUNTER; - priv->spt0_address = INVALID_SPT_ADDRESS; - priv->spt1_address = INVALID_SPT_ADDRESS; + /* spt0/1_address and status fields default to 0 from kzalloc */ mutex_init(&priv->lock); + init_completion(&priv->completion); + priv->chan = stratix10_svc_request_channel_byname(&priv->client, SVC_CLIENT_RSU); if (IS_ERR(priv->chan)) { @@ -756,11 +750,9 @@ static int stratix10_rsu_probe(struct platform_device *pdev) ret = stratix10_svc_add_async_client(priv->chan, false); if (ret) { dev_err(dev, "failed to add async client\n"); - stratix10_svc_free_channel(priv->chan); - return ret; + goto free_channel; } - init_completion(&priv->completion); platform_set_drvdata(pdev, priv); /* get the initial state from firmware */ @@ -768,41 +760,44 @@ static int stratix10_rsu_probe(struct platform_device *pdev) rsu_async_status_callback); if (ret) { dev_err(dev, "Error, getting RSU status %i\n", ret); - stratix10_svc_remove_async_client(priv->chan); - stratix10_svc_free_channel(priv->chan); - return ret; + goto remove_async_client; } /* get DCMF version from firmware */ - ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_VERSION, - 0, rsu_dcmf_version_callback); + ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_VERSION, 0, + rsu_dcmf_version_callback); if (ret) { dev_err(dev, "Error, getting DCMF version %i\n", ret); - stratix10_svc_free_channel(priv->chan); + goto remove_async_client; } - ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_STATUS, - 0, rsu_dcmf_status_callback); + ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_STATUS, 0, + rsu_dcmf_status_callback); if (ret) { dev_err(dev, "Error, getting DCMF status %i\n", ret); - stratix10_svc_free_channel(priv->chan); + goto remove_async_client; } ret = rsu_send_msg(priv, COMMAND_RSU_MAX_RETRY, 0, rsu_max_retry_callback); if (ret) { dev_err(dev, "Error, getting RSU max retry %i\n", ret); - stratix10_svc_free_channel(priv->chan); + goto remove_async_client; } - ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_GET_SPT_TABLE, 0, rsu_async_get_spt_table_callback); if (ret) { dev_err(dev, "Error, getting SPT table %i\n", ret); - stratix10_svc_free_channel(priv->chan); + goto remove_async_client; } + return 0; + +remove_async_client: + stratix10_svc_remove_async_client(priv->chan); +free_channel: + stratix10_svc_free_channel(priv->chan); return ret; }