From nobody Mon Apr 6 23:17:37 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A71993A6F00; Tue, 17 Mar 2026 10:33:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773743628; cv=none; b=Mz9u6M4fNLBuuOKxdExuBaeQpo4OkhHF/m6r2bLLwCiBqI6In6E7CY93MabkWlLzdrZFtt8Wv9lONUJBIq9YIgyqIbOPQrhu2x9CAzSH4aYHRv/o4JuGy91gZxycLxXj13D4oiC8YNLxht8Vy/pEjCqzSe0Z2LTUUridTTwvtIo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773743628; c=relaxed/simple; bh=l/kDrBX29807jE/FxqVx1tp9soejGDrbHEFGKncrVhk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RI62jCnuYJhz0YR2yilfNa1Dm/XppyH736FxhEr1f1PB21tgPK5WH7bInE8dNSicy/6Kp4CicRSr6IZQRAJlHyRSvnqjicbvKp0++u5UHEoZ2RK2EHYR0tW5A8Vfo58StAGp7I7OzdCPYn7QhHlRR+cAFeP//8Sy7uilaKbATnM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 18D7A1477; Tue, 17 Mar 2026 03:33:41 -0700 (PDT) Received: from e142021.fritz.box (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id B8F143F7BD; Tue, 17 Mar 2026 03:33:44 -0700 (PDT) From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, vwadekar@nvidia.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org Subject: [PATCH v2 1/8] dt-bindings: arm: Add Live Firmware Activation binding Date: Tue, 17 Mar 2026 11:33:27 +0100 Message-ID: <20260317103336.1273582-2-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260317103336.1273582-1-andre.przywara@arm.com> References: <20260317103336.1273582-1-andre.przywara@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The Arm Live Firmware Activation spec [1] describes updating firmware images during runtime, without requiring a reboot. Update images might be deployed out-of-band, for instance via a BMC, in this case the OS needs to be notified about the availability of a new image. This binding describes an interrupt that could be triggered by the platform, to notify about any changes. [1] https://developer.arm.com/documentation/den0147/latest/ Signed-off-by: Andre Przywara --- .../devicetree/bindings/arm/arm,lfa.yaml | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/arm,lfa.yaml diff --git a/Documentation/devicetree/bindings/arm/arm,lfa.yaml b/Documenta= tion/devicetree/bindings/arm/arm,lfa.yaml new file mode 100644 index 000000000000..92f0564fd672 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/arm,lfa.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/arm,lfa.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Arm Live Firmware Activation (LFA) + +maintainers: + - Andre Przywara + - Sudeep Holla + +description: + The Arm Live Firmware Activation (LFA) specification [1] describes a + firmware interface to activate an updated firmware at runtime, without + requiring a reboot. Updates might be supplied out-of-band, for instance + via a BMC, in which case the platform needs to notify an OS about pending + image updates. + [1] https://developer.arm.com/documentation/den0147/latest/ + +properties: + compatible: + const: arm,lfa + + interrupts: + maxItems: 1 + description: notification interrupt for changed firmware image status + +required: + - compatible + - interrupts + +additionalProperties: false + +examples: + - | + #include + + firmware { + arm-lfa { + compatible =3D "arm,lfa"; + interrupts =3D ; + }; + }; +... --=20 2.43.0 From nobody Mon Apr 6 23:17:37 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id D61983A6F11 for ; Tue, 17 Mar 2026 10:33:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773743631; cv=none; b=XvZO8koTc1SsKZZL0cBjBJb3N2S5cHLzsOOe/8p+CT1PCSWWGvOsWSRxoNrO61+2yo6CdxOG6KOVBmVdKvtO1M8wYdXT8MdTFkoPwkQPJSfzitcnt97Qz8zW1D28cFDJPlI4wxg7YZeHS75AsNgEmDH60TnIxnuqC9UyLKPfzls= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773743631; c=relaxed/simple; bh=XsEzpX2CSpK7LAwwy0hwcPyHg//NzW2HtfCNj6aGbC8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=p92t2JXqlxRKPYSJjozG31BPjgoBmuciIhSXz8Tn8pYTg6n19Trh/H0aFzF2erG1Gz+BuZWT8xPn2tuMV/jW2rLaCGJReYKllkunCBvbRbAx8Kvr3pWIvULugc8Z4T+ayl7I+9WgGsoOXV94ilyO66vMhzSbMV+tRwp/XyJWV7o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 65B8616F2; Tue, 17 Mar 2026 03:33:43 -0700 (PDT) Received: from e142021.fritz.box (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 783B13F7BD; Tue, 17 Mar 2026 03:33:47 -0700 (PDT) From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, vwadekar@nvidia.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 2/8] firmware: smccc: Add support for Live Firmware Activation (LFA) Date: Tue, 17 Mar 2026 11:33:28 +0100 Message-ID: <20260317103336.1273582-3-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260317103336.1273582-1-andre.przywara@arm.com> References: <20260317103336.1273582-1-andre.przywara@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Salman Nabi The Arm Live Firmware Activation (LFA) is a specification [1] to describe activating firmware components without a reboot. Those components (like TF-A's BL31, EDK-II, TF-RMM, secure paylods) would be updated the usual way: via fwupd, FF-A or other secure storage methods, or via some IMPDEF Out-Of-Bound method. The user can then activate this new firmware, at system runtime, without requiring a reboot. The specification covers the SMCCC interface to list and query available components and eventually trigger the activation. Add a new directory under /sys/firmware to present firmware components capable of live activation. Each of them is a directory under lfa/, and is identified via its GUID. The activation will be triggered by echoing "1" into the "activate" file: =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D /sys/firmware/lfa # ls -l . 6c* .: total 0 drwxr-xr-x 2 0 0 0 Jan 19 11:33 47d4086d-4cfe-9846-9b95-2950cbbd= 5a00 drwxr-xr-x 2 0 0 0 Jan 19 11:33 6c0762a6-12f2-4b56-92cb-ba8f6336= 06d9 drwxr-xr-x 2 0 0 0 Jan 19 11:33 d6d0eea7-fcea-d54b-9782-9934f234= b6e4 6c0762a6-12f2-4b56-92cb-ba8f633606d9: total 0 --w------- 1 0 0 4096 Jan 19 11:33 activate -r--r--r-- 1 0 0 4096 Jan 19 11:33 activation_capable -r--r--r-- 1 0 0 4096 Jan 19 11:33 activation_pending --w------- 1 0 0 4096 Jan 19 11:33 cancel -r--r--r-- 1 0 0 4096 Jan 19 11:33 cpu_rendezvous -r--r--r-- 1 0 0 4096 Jan 19 11:33 current_version -rw-r--r-- 1 0 0 4096 Jan 19 11:33 force_cpu_rendezvo= us -r--r--r-- 1 0 0 4096 Jan 19 11:33 may_reset_cpu -r--r--r-- 1 0 0 4096 Jan 19 11:33 name -r--r--r-- 1 0 0 4096 Jan 19 11:33 pending_version /sys/firmware/lfa/6c0762a6-12f2-4b56-92cb-ba8f633606d9 # grep . * grep: activate: Permission denied activation_capable:1 activation_pending:1 grep: cancel: Permission denied cpu_rendezvous:1 current_version:0.0 force_cpu_rendezvous:1 may_reset_cpu:0 name:TF-RMM pending_version:0.0 /sys/firmware/lfa/6c0762a6-12f2-4b56-92cb-ba8f633606d9 # echo 1 > activate [ 2825.797871] Arm LFA: firmware activation succeeded. /sys/firmware/lfa/6c0762a6-12f2-4b56-92cb-ba8f633606d9 # =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D [1] https://developer.arm.com/documentation/den0147/latest/ Signed-off-by: Salman Nabi Signed-off-by: Andre Przywara --- drivers/firmware/smccc/Kconfig | 10 + drivers/firmware/smccc/Makefile | 1 + drivers/firmware/smccc/lfa_fw.c | 721 ++++++++++++++++++++++++++++++++ 3 files changed, 732 insertions(+) create mode 100644 drivers/firmware/smccc/lfa_fw.c diff --git a/drivers/firmware/smccc/Kconfig b/drivers/firmware/smccc/Kconfig index 15e7466179a6..7fd646d515f8 100644 --- a/drivers/firmware/smccc/Kconfig +++ b/drivers/firmware/smccc/Kconfig @@ -23,3 +23,13 @@ config ARM_SMCCC_SOC_ID help Include support for the SoC bus on the ARM SMCCC firmware based platforms providing some sysfs information about the SoC variant. + +config ARM_LFA + tristate "Arm Live Firmware activation support" + depends on HAVE_ARM_SMCCC_DISCOVERY && ARM64 + default y + help + Include support for triggering a Live Firmware Activation (LFA), + which allows to upgrade certain firmware components without a reboot. + This is described in the Arm DEN0147 specification, and relies on + a firmware agent running in EL3. diff --git a/drivers/firmware/smccc/Makefile b/drivers/firmware/smccc/Makef= ile index 40d19144a860..a6dd01558a94 100644 --- a/drivers/firmware/smccc/Makefile +++ b/drivers/firmware/smccc/Makefile @@ -2,3 +2,4 @@ # obj-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) +=3D smccc.o kvm_guest.o obj-$(CONFIG_ARM_SMCCC_SOC_ID) +=3D soc_id.o +obj-$(CONFIG_ARM_LFA) +=3D lfa_fw.o diff --git a/drivers/firmware/smccc/lfa_fw.c b/drivers/firmware/smccc/lfa_f= w.c new file mode 100644 index 000000000000..284b7c18d3d0 --- /dev/null +++ b/drivers/firmware/smccc/lfa_fw.c @@ -0,0 +1,721 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Arm Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#undef pr_fmt +#define pr_fmt(fmt) "Arm LFA: " fmt + +/* LFA v1.0b0 specification */ +#define LFA_1_0_FN_BASE 0xc40002e0 +#define LFA_1_0_FN(n) (LFA_1_0_FN_BASE + (n)) + +#define LFA_1_0_FN_GET_VERSION LFA_1_0_FN(0) +#define LFA_1_0_FN_CHECK_FEATURE LFA_1_0_FN(1) +#define LFA_1_0_FN_GET_INFO LFA_1_0_FN(2) +#define LFA_1_0_FN_GET_INVENTORY LFA_1_0_FN(3) +#define LFA_1_0_FN_PRIME LFA_1_0_FN(4) +#define LFA_1_0_FN_ACTIVATE LFA_1_0_FN(5) +#define LFA_1_0_FN_CANCEL LFA_1_0_FN(6) + +/* CALL_AGAIN flags (returned by SMC) */ +#define LFA_PRIME_CALL_AGAIN BIT(0) +#define LFA_ACTIVATE_CALL_AGAIN BIT(0) + +/* LFA return values */ +#define LFA_SUCCESS 0 +#define LFA_NOT_SUPPORTED 1 +#define LFA_BUSY 2 +#define LFA_AUTH_ERROR 3 +#define LFA_NO_MEMORY 4 +#define LFA_CRITICAL_ERROR 5 +#define LFA_DEVICE_ERROR 6 +#define LFA_WRONG_STATE 7 +#define LFA_INVALID_PARAMETERS 8 +#define LFA_COMPONENT_WRONG_STATE 9 +#define LFA_INVALID_ADDRESS 10 +#define LFA_ACTIVATION_FAILED 11 + +/* + * Not error codes described by the spec, but used internally when + * PRIME/ACTIVATE calls return with the CALL_AGAIN bit set. + */ +#define LFA_TIMED_OUT 32 +#define LFA_CALL_AGAIN 33 + +#define LFA_ERROR_STRING(name) \ + [name] =3D #name + +static const char * const lfa_error_strings[] =3D { + LFA_ERROR_STRING(LFA_SUCCESS), + LFA_ERROR_STRING(LFA_NOT_SUPPORTED), + LFA_ERROR_STRING(LFA_BUSY), + LFA_ERROR_STRING(LFA_AUTH_ERROR), + LFA_ERROR_STRING(LFA_NO_MEMORY), + LFA_ERROR_STRING(LFA_CRITICAL_ERROR), + LFA_ERROR_STRING(LFA_DEVICE_ERROR), + LFA_ERROR_STRING(LFA_WRONG_STATE), + LFA_ERROR_STRING(LFA_INVALID_PARAMETERS), + LFA_ERROR_STRING(LFA_COMPONENT_WRONG_STATE), + LFA_ERROR_STRING(LFA_INVALID_ADDRESS), + LFA_ERROR_STRING(LFA_ACTIVATION_FAILED) +}; + +enum image_attr_names { + LFA_ATTR_NAME, + LFA_ATTR_CURRENT_VERSION, + LFA_ATTR_PENDING_VERSION, + LFA_ATTR_ACT_CAPABLE, + LFA_ATTR_ACT_PENDING, + LFA_ATTR_MAY_RESET_CPU, + LFA_ATTR_CPU_RENDEZVOUS, + LFA_ATTR_FORCE_CPU_RENDEZVOUS, + LFA_ATTR_ACTIVATE, + LFA_ATTR_CANCEL, + LFA_ATTR_NR_IMAGES +}; + +struct fw_image { + struct kobject kobj; + const char *image_name; + int fw_seq_id; + u64 current_version; + u64 pending_version; + bool activation_capable; + bool activation_pending; + bool may_reset_cpu; + bool cpu_rendezvous; + bool cpu_rendezvous_forced; + struct kobj_attribute image_attrs[LFA_ATTR_NR_IMAGES]; +}; + +static struct fw_image *kobj_to_fw_image(struct kobject *kobj) +{ + return container_of(kobj, struct fw_image, kobj); +} + +/* A UUID split over two 64-bit registers */ +struct uuid_regs { + u64 uuid_lo; + u64 uuid_hi; +}; + +/* A list of known GUIDs, to be shown in the "name" sysfs file. */ +static const struct fw_image_uuid { + const char *name; + const char *uuid; +} fw_images_uuids[] =3D { + { + .name =3D "TF-A BL31 runtime", + .uuid =3D "47d4086d-4cfe-9846-9b95-2950cbbd5a00", + }, + { + .name =3D "BL33 non-secure payload", + .uuid =3D "d6d0eea7-fcea-d54b-9782-9934f234b6e4", + }, + { + .name =3D "TF-RMM", + .uuid =3D "6c0762a6-12f2-4b56-92cb-ba8f633606d9", + }, +}; + +static struct kset *lfa_kset; +static struct workqueue_struct *fw_images_update_wq; +static struct work_struct fw_images_update_work; +static struct attribute *image_default_attrs[LFA_ATTR_NR_IMAGES + 1]; + +static const struct attribute_group image_attr_group =3D { + .attrs =3D image_default_attrs, +}; + +static const struct attribute_group *image_default_groups[] =3D { + &image_attr_group, + NULL +}; + +static int update_fw_images_tree(void); + +static const char *lfa_error_string(int error) +{ + if (error > 0) + return lfa_error_strings[LFA_SUCCESS]; + + error =3D -error; + if (error < ARRAY_SIZE(lfa_error_strings)) + return lfa_error_strings[error]; + if (error =3D=3D -LFA_TIMED_OUT) + return "timed out"; + + return lfa_error_strings[LFA_DEVICE_ERROR]; +} + +static void image_release(struct kobject *kobj) +{ + struct fw_image *image =3D kobj_to_fw_image(kobj); + + kfree(image); +} + +static const struct kobj_type image_ktype =3D { + .release =3D image_release, + .sysfs_ops =3D &kobj_sysfs_ops, + .default_groups =3D image_default_groups, +}; + +static void delete_fw_image_node(struct fw_image *image) +{ + kobject_del(&image->kobj); + kobject_put(&image->kobj); +} + +static void remove_invalid_fw_images(struct work_struct *work) +{ + struct kobject *kobj, *tmp; + struct list_head images_to_delete =3D LIST_HEAD_INIT(images_to_delete); + + /* + * Remove firmware images including directories that are no longer + * present in the LFA agent after updating the existing ones. + * Delete list images before calling kobject_del() and kobject_put() on + * them. Kobject_del() uses kset->list_lock itself which can cause lock + * recursion, and kobject_put() may sleep. + */ + spin_lock(&lfa_kset->list_lock); + list_for_each_entry_safe(kobj, tmp, &lfa_kset->list, entry) { + struct fw_image *image =3D kobj_to_fw_image(kobj); + + if (image->fw_seq_id =3D=3D -1) + list_move_tail(&kobj->entry, &images_to_delete); + } + spin_unlock(&lfa_kset->list_lock); + + /* + * Now safely remove the sysfs kobjects for the deleted list items + */ + list_for_each_entry_safe(kobj, tmp, &images_to_delete, entry) { + struct fw_image *image =3D kobj_to_fw_image(kobj); + + delete_fw_image_node(image); + } +} + +static void set_image_flags(struct fw_image *image, int seq_id, + u32 image_flags, u64 reg_current_ver, + u64 reg_pending_ver) +{ + image->fw_seq_id =3D seq_id; + image->current_version =3D reg_current_ver; + image->pending_version =3D reg_pending_ver; + image->activation_capable =3D !!(image_flags & BIT(0)); + image->activation_pending =3D !!(image_flags & BIT(1)); + image->may_reset_cpu =3D !!(image_flags & BIT(2)); + /* cpu_rendezvous_optional bit has inverse logic in the spec */ + image->cpu_rendezvous =3D !(image_flags & BIT(3)); +} + +static unsigned long get_nr_lfa_components(void) +{ + struct arm_smccc_1_2_regs reg =3D { 0 }; + + reg.a0 =3D LFA_1_0_FN_GET_INFO; + reg.a1 =3D 0; /* lfa_info_selector =3D 0 */ + + arm_smccc_1_2_invoke(®, ®); + if (reg.a0 !=3D LFA_SUCCESS) + return reg.a0; + + return reg.a1; +} + +static int lfa_cancel(void *data) +{ + struct fw_image *image =3D data; + struct arm_smccc_1_2_regs reg =3D { 0 }; + + reg.a0 =3D LFA_1_0_FN_CANCEL; + reg.a1 =3D image->fw_seq_id; + arm_smccc_1_2_invoke(®, ®); + + /* + * When firmware activation is called with "skip_cpu_rendezvous=3D1", + * LFA_CANCEL can fail with LFA_BUSY if the activation could not be + * cancelled. + */ + if (reg.a0 =3D=3D LFA_SUCCESS) { + pr_info("Activation cancelled for image %s\n", + image->image_name); + } else { + pr_err("Activation not cancelled for image %s: %s\n", + image->image_name, lfa_error_string(reg.a0)); + return -EINVAL; + } + + return reg.a0; +} + +static const char *get_image_name(const struct fw_image *image) +{ + if (image->image_name && image->image_name[0] !=3D '\0') + return image->image_name; + + return kobject_name(&image->kobj); +} + +/* + * Try a single activation call. The smc_lock writer lock must be held, + * and it must be called from inside stop_machine() when CPU rendezvous is + * required. + */ +static int call_lfa_activate(void *data) +{ + struct fw_image *image =3D data; + struct arm_smccc_1_2_regs reg =3D { 0 }, res; + + reg.a0 =3D LFA_1_0_FN_ACTIVATE; + reg.a1 =3D image->fw_seq_id; + /* + * As we do not support updates requiring a CPU reset (yet), + * we pass 0 in reg.a3 and reg.a4, holding the entry point and + * context ID respectively. + * cpu_rendezvous_forced is set by the administrator, via sysfs, + * cpu_rendezvous is dictated by each firmware component. + */ + reg.a2 =3D !(image->cpu_rendezvous_forced || image->cpu_rendezvous); + arm_smccc_1_2_invoke(®, &res); + + if ((long)res.a0 < 0) + return (long)res.a0; + + if (res.a1 & LFA_ACTIVATE_CALL_AGAIN) + return -LFA_CALL_AGAIN; + + return 0; +} + +static int activate_fw_image(struct fw_image *image) +{ + struct kobject *kobj; + int ret; + +retry: + if (image->cpu_rendezvous_forced || image->cpu_rendezvous) + ret =3D stop_machine(call_lfa_activate, image, cpu_online_mask); + else + ret =3D call_lfa_activate(image); + + if (!ret) { + /* + * Invalidate fw_seq_ids (-1) for all images as the seq_ids + * and the number of firmware images in the LFA agent may + * change after a successful activation attempt. + * Negate all image flags as well. + */ + spin_lock(&lfa_kset->list_lock); + list_for_each_entry(kobj, &lfa_kset->list, entry) { + struct fw_image *image =3D kobj_to_fw_image(kobj); + + set_image_flags(image, -1, 0b1000, 0, 0); + } + spin_unlock(&lfa_kset->list_lock); + + update_fw_images_tree(); + + /* + * Removing non-valid image directories at the end of an + * activation. + * We can't remove the sysfs attributes while in the respective + * _store() handler, so have to postpone the list removal to a + * workqueue. + */ + queue_work(fw_images_update_wq, &fw_images_update_work); + + return 0; + } + + if (ret =3D=3D -LFA_CALL_AGAIN) + goto retry; + + lfa_cancel(image); + + pr_err("LFA_ACTIVATE for image %s failed: %s\n", + get_image_name(image), lfa_error_string(ret)); + + return ret; +} + +static int prime_fw_image(struct fw_image *image) +{ + struct arm_smccc_1_2_regs reg =3D { 0 }, res; + + if (image->may_reset_cpu) { + pr_err("CPU reset not supported by kernel driver\n"); + + return -EINVAL; + } + + reg.a0 =3D LFA_1_0_FN_PRIME; +retry: + /* + * LFA_PRIME will return 1 in reg.a1 if the firmware priming + * is still in progress. In that case LFA_PRIME will need to + * be called again. + * reg.a1 will become 0 once the prime process completes. + */ + reg.a1 =3D image->fw_seq_id; + arm_smccc_1_2_invoke(®, &res); + if ((long)res.a0 < 0) { + pr_err("LFA_PRIME for image %s failed: %s\n", + get_image_name(image), + lfa_error_string(res.a0)); + + return res.a0; + } + + if (res.a1 & LFA_PRIME_CALL_AGAIN) + goto retry; + + return 0; +} + +static ssize_t name_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct fw_image *image =3D kobj_to_fw_image(kobj); + + return sysfs_emit(buf, "%s\n", image->image_name); +} + +static ssize_t activation_capable_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct fw_image *image =3D kobj_to_fw_image(kobj); + + return sysfs_emit(buf, "%d\n", image->activation_capable); +} + +static void update_fw_image_pending(struct fw_image *image) +{ + struct arm_smccc_1_2_regs reg =3D { 0 }; + + reg.a0 =3D LFA_1_0_FN_GET_INVENTORY; + reg.a1 =3D image->fw_seq_id; + arm_smccc_1_2_invoke(®, ®); + + if (reg.a0 =3D=3D LFA_SUCCESS) + image->activation_pending =3D !!(reg.a3 & BIT(1)); +} + +static ssize_t activation_pending_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct fw_image *image =3D kobj_to_fw_image(kobj); + + /* + * Activation pending status can change anytime thus we need to update + * and return its current value + */ + update_fw_image_pending(image); + + return sysfs_emit(buf, "%d\n", image->activation_pending); +} + +static ssize_t may_reset_cpu_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct fw_image *image =3D kobj_to_fw_image(kobj); + + return sysfs_emit(buf, "%d\n", image->may_reset_cpu); +} + +static ssize_t cpu_rendezvous_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct fw_image *image =3D kobj_to_fw_image(kobj); + + return sysfs_emit(buf, "%d\n", image->cpu_rendezvous); +} + +static ssize_t force_cpu_rendezvous_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct fw_image *image =3D kobj_to_fw_image(kobj); + int ret; + + ret =3D kstrtobool(buf, &image->cpu_rendezvous_forced); + if (ret) + return ret; + + return count; +} + +static ssize_t force_cpu_rendezvous_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct fw_image *image =3D kobj_to_fw_image(kobj); + + return sysfs_emit(buf, "%d\n", image->cpu_rendezvous_forced); +} + +static ssize_t current_version_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct fw_image *image =3D kobj_to_fw_image(kobj); + u32 maj, min; + + maj =3D image->current_version >> 32; + min =3D image->current_version & 0xffffffff; + + return sysfs_emit(buf, "%u.%u\n", maj, min); +} + +static ssize_t pending_version_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct fw_image *image =3D kobj_to_fw_image(kobj); + struct arm_smccc_1_2_regs reg =3D { 0 }; + + /* + * Similar to activation pending, this value can change following an + * update, we need to retrieve fresh info instead of stale information. + */ + reg.a0 =3D LFA_1_0_FN_GET_INVENTORY; + reg.a1 =3D image->fw_seq_id; + arm_smccc_1_2_invoke(®, ®); + if (reg.a0 =3D=3D LFA_SUCCESS) { + if (reg.a5 !=3D 0 && image->activation_pending) { + u32 maj, min; + + image->pending_version =3D reg.a5; + maj =3D reg.a5 >> 32; + min =3D reg.a5 & 0xffffffff; + + return sysfs_emit(buf, "%u.%u\n", maj, min); + } + } + + return sysfs_emit(buf, "N/A\n"); +} + +static ssize_t activate_store(struct kobject *kobj, struct kobj_attribute = *attr, + const char *buf, size_t count) +{ + struct fw_image *image =3D kobj_to_fw_image(kobj); + int ret; + + ret =3D prime_fw_image(image); + if (ret) + return -ECANCELED; + + ret =3D activate_fw_image(image); + if (ret) + return -ECANCELED; + + pr_info("%s: successfully activated\n", get_image_name(image)); + + return count; +} + +static ssize_t cancel_store(struct kobject *kobj, struct kobj_attribute *a= ttr, + const char *buf, size_t count) +{ + struct fw_image *image =3D kobj_to_fw_image(kobj); + int ret; + + ret =3D lfa_cancel(image); + if (ret !=3D 0) + return ret; + + return count; +} + +static struct kobj_attribute image_attrs_group[LFA_ATTR_NR_IMAGES] =3D { + [LFA_ATTR_NAME] =3D __ATTR_RO(name), + [LFA_ATTR_CURRENT_VERSION] =3D __ATTR_RO(current_version), + [LFA_ATTR_PENDING_VERSION] =3D __ATTR_RO(pending_version), + [LFA_ATTR_ACT_CAPABLE] =3D __ATTR_RO(activation_capable), + [LFA_ATTR_ACT_PENDING] =3D __ATTR_RO(activation_pending), + [LFA_ATTR_MAY_RESET_CPU] =3D __ATTR_RO(may_reset_cpu), + [LFA_ATTR_CPU_RENDEZVOUS] =3D __ATTR_RO(cpu_rendezvous), + [LFA_ATTR_FORCE_CPU_RENDEZVOUS] =3D __ATTR_RW(force_cpu_rendezvous), + [LFA_ATTR_ACTIVATE] =3D __ATTR_WO(activate), + [LFA_ATTR_CANCEL] =3D __ATTR_WO(cancel) +}; + +static void init_image_default_attrs(void) +{ + for (int i =3D 0; i < LFA_ATTR_NR_IMAGES; i++) + image_default_attrs[i] =3D &image_attrs_group[i].attr; + image_default_attrs[LFA_ATTR_NR_IMAGES] =3D NULL; +} + +static void clean_fw_images_tree(void) +{ + struct kobject *kobj, *tmp; + struct list_head images_to_delete; + + INIT_LIST_HEAD(&images_to_delete); + + spin_lock(&lfa_kset->list_lock); + list_for_each_entry_safe(kobj, tmp, &lfa_kset->list, entry) { + list_move_tail(&kobj->entry, &images_to_delete); + } + spin_unlock(&lfa_kset->list_lock); + + list_for_each_entry_safe(kobj, tmp, &images_to_delete, entry) { + struct fw_image *image =3D kobj_to_fw_image(kobj); + + delete_fw_image_node(image); + } +} + +static int update_fw_image_node(char *fw_uuid, int seq_id, + u32 image_flags, u64 reg_current_ver, + u64 reg_pending_ver) +{ + const char *image_name =3D ""; + struct fw_image *image; + struct kobject *kobj; + int i; + + /* + * If a fw_image is already in the images list then we just update + * its flags and seq_id instead of trying to recreate it. + */ + spin_lock(&lfa_kset->list_lock); + list_for_each_entry(kobj, &lfa_kset->list, entry) { + if (!strcmp(kobject_name(kobj), fw_uuid)) { + struct fw_image *image =3D kobj_to_fw_image(kobj); + + set_image_flags(image, seq_id, image_flags, + reg_current_ver, reg_pending_ver); + spin_unlock(&lfa_kset->list_lock); + + return 0; + } + } + spin_unlock(&lfa_kset->list_lock); + + image =3D kzalloc_obj(*image); + if (!image) + return -ENOMEM; + + for (i =3D 0; i < ARRAY_SIZE(fw_images_uuids); i++) { + if (!strcmp(fw_images_uuids[i].uuid, fw_uuid)) + image_name =3D fw_images_uuids[i].name; + } + + image->kobj.kset =3D lfa_kset; + image->image_name =3D image_name; + image->cpu_rendezvous_forced =3D true; + set_image_flags(image, seq_id, image_flags, reg_current_ver, + reg_pending_ver); + if (kobject_init_and_add(&image->kobj, &image_ktype, NULL, + "%s", fw_uuid)) { + kobject_put(&image->kobj); + + return -ENOMEM; + } + + return 0; +} + +static int update_fw_images_tree(void) +{ + struct arm_smccc_1_2_regs reg =3D { 0 }, res; + struct uuid_regs image_uuid; + char image_id_str[40]; + int ret, num_of_components; + + num_of_components =3D get_nr_lfa_components(); + if (num_of_components <=3D 0) { + pr_err("Error getting number of LFA components\n"); + return -ENODEV; + } + + reg.a0 =3D LFA_1_0_FN_GET_INVENTORY; + for (int i =3D 0; i < num_of_components; i++) { + reg.a1 =3D i; /* fw_seq_id to be queried */ + arm_smccc_1_2_invoke(®, &res); + if (res.a0 =3D=3D LFA_SUCCESS) { + image_uuid.uuid_lo =3D res.a1; + image_uuid.uuid_hi =3D res.a2; + + snprintf(image_id_str, sizeof(image_id_str), "%pUb", + &image_uuid); + ret =3D update_fw_image_node(image_id_str, i, res.a3, + res.a4, res.a5); + if (ret) + return ret; + } + } + + return 0; +} + +static int __init lfa_init(void) +{ + struct arm_smccc_1_2_regs reg =3D { 0 }; + int err; + + reg.a0 =3D LFA_1_0_FN_GET_VERSION; + arm_smccc_1_2_invoke(®, ®); + if (reg.a0 =3D=3D -LFA_NOT_SUPPORTED) { + pr_info("Live Firmware activation: no firmware agent found\n"); + return -ENODEV; + } + + pr_info("Live Firmware Activation: detected v%ld.%ld\n", + reg.a0 >> 16, reg.a0 & 0xffff); + + fw_images_update_wq =3D alloc_workqueue("fw_images_update_wq", + WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + if (!fw_images_update_wq) { + pr_err("Live Firmware Activation: Failed to allocate workqueue.\n"); + + return -ENOMEM; + } + INIT_WORK(&fw_images_update_work, remove_invalid_fw_images); + + init_image_default_attrs(); + lfa_kset =3D kset_create_and_add("lfa", NULL, firmware_kobj); + if (!lfa_kset) + return -ENOMEM; + + err =3D update_fw_images_tree(); + if (err !=3D 0) { + kset_unregister(lfa_kset); + destroy_workqueue(fw_images_update_wq); + } + + return err; +} +module_init(lfa_init); + +static void __exit lfa_exit(void) +{ + flush_workqueue(fw_images_update_wq); + destroy_workqueue(fw_images_update_wq); + clean_fw_images_tree(); + kset_unregister(lfa_kset); +} +module_exit(lfa_exit); + +MODULE_DESCRIPTION("ARM Live Firmware Activation (LFA)"); +MODULE_LICENSE("GPL"); --=20 2.43.0 From nobody Mon Apr 6 23:17:37 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 403303A6EEC for ; Tue, 17 Mar 2026 10:33:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773743633; cv=none; b=Jof/HQ/ymXY5s5eaQUJWhy1K5X0eM9MrqAN9oyD93o0as5TBeLgFiHtZwoMhgmVwyrR4L62ceRBS0e3wRKR0G5JrZMKe2f5KY5k1L/FncEr20/DtqjvxfZjBQyY9FUpVBckg9lyA0nJvIJCxFXxsC6mqpuUu6FJOOOCKBlGzYNE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773743633; c=relaxed/simple; bh=mG+ZMV319adDJ9hbp99R8nbF5ZEu+HaazzxdZTOg7VE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=rRByRjF+QpkoZTNVFuJCWnC1BXKhQMJvw0crEo1v5aaJiHJe98i054InCHwqsbrKufHlCZ+zdduwOW5uJe8gFxRYXtcXBmkIP66/DpNZxwEE6KofX15n1qpCiBPEN58nrqss581Q1Jrf4qIuDMYHz2M1YRUhftjeAtIlDX/yhtc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 90EF31476; Tue, 17 Mar 2026 03:33:45 -0700 (PDT) Received: from e142021.fritz.box (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id C60593F7BD; Tue, 17 Mar 2026 03:33:49 -0700 (PDT) From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, vwadekar@nvidia.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 3/8] firmware: smccc: lfa: Move image rescanning Date: Tue, 17 Mar 2026 11:33:29 +0100 Message-ID: <20260317103336.1273582-4-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260317103336.1273582-1-andre.przywara@arm.com> References: <20260317103336.1273582-1-andre.przywara@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Vedashree Vidwans After an image activation, the list of firmware images might change, so we have to re-iterate them through the SMC interface. Move the corresponding code from the activate_fw_image() function into update_fw_images_tree(), where it could be reused more easily, for instance when triggered by an interrupt. Signed-off-by: Vedashree Vidwans [Andre: split off from another patch, rebased] Signed-off-by: Andre Przywara --- drivers/firmware/smccc/lfa_fw.c | 46 ++++++++++++++++----------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/drivers/firmware/smccc/lfa_fw.c b/drivers/firmware/smccc/lfa_f= w.c index 284b7c18d3d0..4831abf2b60e 100644 --- a/drivers/firmware/smccc/lfa_fw.c +++ b/drivers/firmware/smccc/lfa_fw.c @@ -310,7 +310,6 @@ static int call_lfa_activate(void *data) =20 static int activate_fw_image(struct fw_image *image) { - struct kobject *kobj; int ret; =20 retry: @@ -320,31 +319,8 @@ static int activate_fw_image(struct fw_image *image) ret =3D call_lfa_activate(image); =20 if (!ret) { - /* - * Invalidate fw_seq_ids (-1) for all images as the seq_ids - * and the number of firmware images in the LFA agent may - * change after a successful activation attempt. - * Negate all image flags as well. - */ - spin_lock(&lfa_kset->list_lock); - list_for_each_entry(kobj, &lfa_kset->list, entry) { - struct fw_image *image =3D kobj_to_fw_image(kobj); - - set_image_flags(image, -1, 0b1000, 0, 0); - } - spin_unlock(&lfa_kset->list_lock); - update_fw_images_tree(); =20 - /* - * Removing non-valid image directories at the end of an - * activation. - * We can't remove the sysfs attributes while in the respective - * _store() handler, so have to postpone the list removal to a - * workqueue. - */ - queue_work(fw_images_update_wq, &fw_images_update_work); - return 0; } =20 @@ -640,6 +616,7 @@ static int update_fw_images_tree(void) { struct arm_smccc_1_2_regs reg =3D { 0 }, res; struct uuid_regs image_uuid; + struct kobject *kobj; char image_id_str[40]; int ret, num_of_components; =20 @@ -649,6 +626,19 @@ static int update_fw_images_tree(void) return -ENODEV; } =20 + /* + * Invalidate fw_seq_ids (-1) for all images as the seq_ids and the + * number of firmware images in the LFA agent may change after a + * successful activation attempt. Negate all image flags as well. + */ + spin_lock(&lfa_kset->list_lock); + list_for_each_entry(kobj, &lfa_kset->list, entry) { + struct fw_image *image =3D kobj_to_fw_image(kobj); + + set_image_flags(image, -1, 0b1000, 0, 0); + } + spin_unlock(&lfa_kset->list_lock); + reg.a0 =3D LFA_1_0_FN_GET_INVENTORY; for (int i =3D 0; i < num_of_components; i++) { reg.a1 =3D i; /* fw_seq_id to be queried */ @@ -666,6 +656,14 @@ static int update_fw_images_tree(void) } } =20 + /* + * Removing non-valid image directories at the end of an activation. + * We can't remove the sysfs attributes while in the respective + * _store() handler, so have to postpone the list removal to a + * workqueue. + */ + queue_work(fw_images_update_wq, &fw_images_update_work); + return 0; } =20 --=20 2.43.0 From nobody Mon Apr 6 23:17:37 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 396CB3A6F08 for ; Tue, 17 Mar 2026 10:33:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773743635; cv=none; b=eqFrnQo7RthGIqg9o36E8Io339jFmAl7s4jklPC9vGkvJbLyId8p7bfErwZhF5PgCIbPRw8+CCmTHcTXEmlYksDXntm1pnWtxXtaziSsHodE0HkoFLfO6hNfomPPvtHBR9bS44nQ18AKeIip7L201tTD+tyWD32XoAUVPhgOv24= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773743635; c=relaxed/simple; bh=Ns3yvsR1TpZgxwW2qqqn+pk25xbdCKuVjIHfY9RVFso=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=qsqQ7CYGYzyH/4w9RoEVB0iSlwvH0+H7aBrgHuTjx6cVc0aQcl2kNU/u7IDf85qin/JKBon2jp8qUXm7CEAjBAm0YomYir4XtR6L7qpTrIuIQY2S5xwj9d5CZN2T5ll7JYcVVpm2nCboCg2K7d54Q9DZKCDHP6rTbk6x32xvUAQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id BD80C1477; Tue, 17 Mar 2026 03:33:47 -0700 (PDT) Received: from e142021.fritz.box (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id F29D53F7BD; Tue, 17 Mar 2026 03:33:51 -0700 (PDT) From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, vwadekar@nvidia.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 4/8] firmware: smccc: lfa: Add timeout and trigger watchdog Date: Tue, 17 Mar 2026 11:33:30 +0100 Message-ID: <20260317103336.1273582-5-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260317103336.1273582-1-andre.przywara@arm.com> References: <20260317103336.1273582-1-andre.przywara@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Vedashree Vidwans Enhance PRIME/ACTIVATION functions to touch watchdog and implement timeout mechanism. This update ensures that any potential hangs are detected promptly and that the LFA process is allocated sufficient execution time before the watchdog timer expires. These changes improve overall system reliability by reducing the risk of undetected process stalls and unexpected watchdog resets. Signed-off-by: Vedashree Vidwans Signed-off-by: Andre Przywara --- drivers/firmware/smccc/lfa_fw.c | 43 ++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/smccc/lfa_fw.c b/drivers/firmware/smccc/lfa_f= w.c index 4831abf2b60e..d1b5cd29b8a0 100644 --- a/drivers/firmware/smccc/lfa_fw.c +++ b/drivers/firmware/smccc/lfa_fw.c @@ -5,11 +5,14 @@ =20 #include #include +#include #include #include #include +#include #include #include +#include #include #include #include @@ -38,6 +41,11 @@ #define LFA_PRIME_CALL_AGAIN BIT(0) #define LFA_ACTIVATE_CALL_AGAIN BIT(0) =20 +#define LFA_PRIME_BUDGET_MS 30000 /* 30s cap */ +#define LFA_PRIME_DELAY_MS 10 /* 10ms between polls */ +#define LFA_ACTIVATE_BUDGET_MS 10000 /* 10s cap */ +#define LFA_ACTIVATE_DELAY_MS 10 /* 10ms between polls */ + /* LFA return values */ #define LFA_SUCCESS 0 #define LFA_NOT_SUPPORTED 1 @@ -287,6 +295,7 @@ static int call_lfa_activate(void *data) struct fw_image *image =3D data; struct arm_smccc_1_2_regs reg =3D { 0 }, res; =20 + touch_nmi_watchdog(); reg.a0 =3D LFA_1_0_FN_ACTIVATE; reg.a1 =3D image->fw_seq_id; /* @@ -310,6 +319,7 @@ static int call_lfa_activate(void *data) =20 static int activate_fw_image(struct fw_image *image) { + ktime_t end =3D ktime_add_ms(ktime_get(), LFA_ACTIVATE_BUDGET_MS); int ret; =20 retry: @@ -324,8 +334,15 @@ static int activate_fw_image(struct fw_image *image) return 0; } =20 - if (ret =3D=3D -LFA_CALL_AGAIN) - goto retry; + if (ret =3D=3D -LFA_CALL_AGAIN) { + /* SMC returned with call_again flag set */ + if (ktime_before(ktime_get(), end)) { + msleep_interruptible(LFA_ACTIVATE_DELAY_MS); + goto retry; + } + + ret =3D -LFA_TIMED_OUT; + } =20 lfa_cancel(image); =20 @@ -338,6 +355,8 @@ static int activate_fw_image(struct fw_image *image) static int prime_fw_image(struct fw_image *image) { struct arm_smccc_1_2_regs reg =3D { 0 }, res; + ktime_t end =3D ktime_add_ms(ktime_get(), LFA_PRIME_BUDGET_MS); + int ret; =20 if (image->may_reset_cpu) { pr_err("CPU reset not supported by kernel driver\n"); @@ -345,6 +364,8 @@ static int prime_fw_image(struct fw_image *image) return -EINVAL; } =20 + touch_nmi_watchdog(); + reg.a0 =3D LFA_1_0_FN_PRIME; retry: /* @@ -363,8 +384,22 @@ static int prime_fw_image(struct fw_image *image) return res.a0; } =20 - if (res.a1 & LFA_PRIME_CALL_AGAIN) - goto retry; + if (res.a1 & LFA_PRIME_CALL_AGAIN) { + /* SMC returned with call_again flag set */ + if (ktime_before(ktime_get(), end)) { + msleep_interruptible(LFA_PRIME_DELAY_MS); + goto retry; + } + + pr_err("LFA_PRIME for image %s timed out", + get_image_name(image)); + + ret =3D lfa_cancel(image); + if (ret !=3D 0) + return ret; + + return -ETIMEDOUT; + } =20 return 0; } --=20 2.43.0 From nobody Mon Apr 6 23:17:37 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 7F4403A8722 for ; Tue, 17 Mar 2026 10:33:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773743637; cv=none; b=Hx/DjHtqbvlZf+8YCMqGMvXnj6iEtWbzGvWgMfUsDF0etYF+9kL7iL9W80S2RoO4oPzvvbNrV6sqviR5L09401grIaKJZL71k9YHhcFiYPtaavPjVE0WjJ8hmK0L6pWGtwffVzyy8W3tkzmxHPlT0Dfrhi/QG+c5Jv35LHWDwQo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773743637; c=relaxed/simple; bh=x5vyjSncmlgsrZ4aw7CAHc7EvsVybime3od4fp8/j8c=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=K5Asp3jGYkAB61xwyEqgzOssNR244dk+DYFrRgUJ/d3DebtMdaqj/4BiY//QDpifynTD4y9Cy46jMhy3oItYRBcBoxS6SlBUJAljxZPvV0y+KDlOFuG5B7yAcJkINynHYHgbQ3EnHelnY/qUqi50t7k6NBGStLg1qz0ZV3yFg+I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id EA4371476; Tue, 17 Mar 2026 03:33:49 -0700 (PDT) Received: from e142021.fritz.box (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 2A9813F7BD; Tue, 17 Mar 2026 03:33:54 -0700 (PDT) From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, vwadekar@nvidia.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 5/8] firmware: smccc: lfa: Register ACPI notification Date: Tue, 17 Mar 2026 11:33:31 +0100 Message-ID: <20260317103336.1273582-6-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260317103336.1273582-1-andre.przywara@arm.com> References: <20260317103336.1273582-1-andre.przywara@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Vedashree Vidwans The Arm LFA spec describes an ACPI notification mechanism, where the platform (firmware) can notify an LFA client about newly available firmware imag updates ("pending images" in LFA terms). Add a faux device after discovering the existence of an LFA agent via the SMCCC discovery mechnism, and use that device to check for the ACPI notification description. Register this when one is provided. The notification just conveys the fact that at least one firmware image has now a pending update, it doesn't say which, also there could be more than one pending. Loop through all images to find every which needs to be activated, and trigger the activation. We need to do this is a loop, since an activation might change the number and the status of available images. Signed-off-by: Vedashree Vidwans [Andre: convert from platform driver to faux device] Signed-off-by: Andre Przywara --- drivers/firmware/smccc/lfa_fw.c | 147 ++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/drivers/firmware/smccc/lfa_fw.c b/drivers/firmware/smccc/lfa_f= w.c index d1b5cd29b8a0..f20ea45cdbd9 100644 --- a/drivers/firmware/smccc/lfa_fw.c +++ b/drivers/firmware/smccc/lfa_fw.c @@ -3,11 +3,14 @@ * Copyright (C) 2025 Arm Limited */ =20 +#include #include #include #include +#include #include #include +#include #include #include #include @@ -17,11 +20,13 @@ #include #include #include +#include #include #include =20 #include =20 +#define DRIVER_NAME "ARM_LFA" #undef pr_fmt #define pr_fmt(fmt) "Arm LFA: " fmt =20 @@ -702,6 +707,139 @@ static int update_fw_images_tree(void) return 0; } =20 +/* + * Go through all FW images in a loop and trigger activation + * of all activatible and pending images. + * We have to restart enumeration after every triggered activation, + * since the firmware images might have changed during the activation. + */ +static int activate_pending_image(void) +{ + struct kobject *kobj; + bool found_pending =3D false; + struct fw_image *image; + int ret; + + spin_lock(&lfa_kset->list_lock); + list_for_each_entry(kobj, &lfa_kset->list, entry) { + image =3D kobj_to_fw_image(kobj); + + if (image->fw_seq_id =3D=3D -1) + continue; /* Invalid FW component */ + + update_fw_image_pending(image); + if (image->activation_capable && image->activation_pending) { + found_pending =3D true; + break; + } + } + spin_unlock(&lfa_kset->list_lock); + + if (!found_pending) + return -ENOENT; + + ret =3D prime_fw_image(image); + if (ret) + return ret; + + ret =3D activate_fw_image(image); + if (ret) + return ret; + + pr_info("%s: automatic activation succeeded\n", get_image_name(image)); + + return 0; +} + +#ifdef CONFIG_ACPI +static void lfa_acpi_notify_handler(acpi_handle handle, u32 event, void *d= ata) +{ + int ret; + + while (!(ret =3D activate_pending_image())) + ; + + if (ret !=3D -ENOENT) + pr_warn("notified image activation failed: %d\n", ret); +} + +static int lfa_register_acpi(struct device *dev) +{ + struct acpi_device *acpi_dev; + acpi_handle handle; + acpi_status status; + + acpi_dev =3D acpi_dev_get_first_match_dev("ARML0003", NULL, -1); + if (!acpi_dev) + return -ENODEV; + handle =3D acpi_device_handle(acpi_dev); + if (!handle) { + acpi_dev_put(acpi_dev); + return -ENODEV; + } + + /* Register notify handler that indicates LFA updates are available */ + status =3D acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, + lfa_acpi_notify_handler, NULL); + if (ACPI_FAILURE(status)) { + acpi_dev_put(acpi_dev); + return -EIO; + } + + ACPI_COMPANION_SET(dev, acpi_dev); + + return 0; +} + +static void lfa_remove_acpi(struct device *dev) +{ + struct acpi_device *acpi_dev =3D ACPI_COMPANION(dev); + acpi_handle handle =3D acpi_device_handle(acpi_dev); + + if (handle) + acpi_remove_notify_handler(handle, + ACPI_DEVICE_NOTIFY, + lfa_acpi_notify_handler); + acpi_dev_put(acpi_dev); +} +#else /* !CONFIG_ACPI */ +static int lfa_register_acpi(struct device *dev) +{ + return -ENODEV; +} + +static void lfa_remove_acpi(struct device *dev) +{ +} +#endif + +static int lfa_faux_probe(struct faux_device *fdev) +{ + int ret; + + if (!acpi_disabled) { + ret =3D lfa_register_acpi(&fdev->dev); + if (ret !=3D -ENODEV) { + if (!ret) + pr_info("registered LFA ACPI notification\n"); + return ret; + } + } + + return 0; +} + +static void lfa_faux_remove(struct faux_device *fdev) +{ + lfa_remove_acpi(&fdev->dev); +} + +static struct faux_device *lfa_dev; +static struct faux_device_ops lfa_device_ops =3D { + .probe =3D lfa_faux_probe, + .remove =3D lfa_faux_remove, +}; + static int __init lfa_init(void) { struct arm_smccc_1_2_regs reg =3D { 0 }; @@ -731,6 +869,14 @@ static int __init lfa_init(void) if (!lfa_kset) return -ENOMEM; =20 + /* + * This faux device is just used for the optional notification + * mechanism, to register the ACPI notification or interrupt. + * If the firmware tables do not contain this information, the + * driver will still work. + */ + lfa_dev =3D faux_device_create("arm-lfa", NULL, &lfa_device_ops); + err =3D update_fw_images_tree(); if (err !=3D 0) { kset_unregister(lfa_kset); @@ -747,6 +893,7 @@ static void __exit lfa_exit(void) destroy_workqueue(fw_images_update_wq); clean_fw_images_tree(); kset_unregister(lfa_kset); + faux_device_destroy(lfa_dev); } module_exit(lfa_exit); =20 --=20 2.43.0 From nobody Mon Apr 6 23:17:37 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 9B5093A8745 for ; Tue, 17 Mar 2026 10:33:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773743639; cv=none; b=jcgg5XlSG0YB1MThnSkJKrg8b39Jpqu9uS7kl4n5tafA+sT4Tbrn+bYSNKg2okIIq36S6BjArWQhyzzsY5rExhQnWgifvC8DTJHfTAoLP2Rg4djvyRIpDvujM+BRVEt9gm2f6vKS1LodH/IlpWoGw9bcLd4x6vN/NP8p335MNs0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773743639; c=relaxed/simple; bh=1U5bCnqrA4/aCxWFSX8mysPlUD08c4SxqZ6xt0FXu5A=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eMxWqnqMe06v64k8sRUUaQPDMH4oBYo50fX2bLdzFNCmaaMw1fVJSa6DRfQ3cgZ+Pzwul/O6olpZ9kN9T0t5rhwAexmfeijOSes9QEMXdwXeduGV9s0LG65O90HSsQx9rThlYYe5cDCDgoYych4RJg7VJcK3uwMcE0Fvlz6LFUs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 249C41477; Tue, 17 Mar 2026 03:33:52 -0700 (PDT) Received: from e142021.fritz.box (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 564BD3F7BD; Tue, 17 Mar 2026 03:33:56 -0700 (PDT) From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, vwadekar@nvidia.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 6/8] firmware: smccc: lfa: Add auto_activate sysfs file Date: Tue, 17 Mar 2026 11:33:32 +0100 Message-ID: <20260317103336.1273582-7-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260317103336.1273582-1-andre.przywara@arm.com> References: <20260317103336.1273582-1-andre.przywara@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The Arm LFA spec places control over the actual activation process in the hands of the non-secure host OS. An platform initiated interrupt or notification signals the availability of an updateable firmware image, but does not necessarily need to trigger it automatically. Add a sysfs control file that guards such automatic activation. If an administrator wants to allow automatic platform initiated updates, they can activate that by echoing a "1" into the auto_activate file in the respective sysfs directory. Any incoming notification would then result in the activation triggered. Signed-off-by: Andre Przywara --- drivers/firmware/smccc/lfa_fw.c | 34 ++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/smccc/lfa_fw.c b/drivers/firmware/smccc/lfa_f= w.c index f20ea45cdbd9..5dc531e462eb 100644 --- a/drivers/firmware/smccc/lfa_fw.c +++ b/drivers/firmware/smccc/lfa_fw.c @@ -101,6 +101,7 @@ enum image_attr_names { LFA_ATTR_FORCE_CPU_RENDEZVOUS, LFA_ATTR_ACTIVATE, LFA_ATTR_CANCEL, + LFA_ATTR_AUTO_ACTIVATE, LFA_ATTR_NR_IMAGES }; =20 @@ -115,6 +116,7 @@ struct fw_image { bool may_reset_cpu; bool cpu_rendezvous; bool cpu_rendezvous_forced; + bool auto_activate; struct kobj_attribute image_attrs[LFA_ATTR_NR_IMAGES]; }; =20 @@ -561,6 +563,28 @@ static ssize_t cancel_store(struct kobject *kobj, stru= ct kobj_attribute *attr, return count; } =20 +static ssize_t auto_activate_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct fw_image *image =3D kobj_to_fw_image(kobj); + int ret; + + ret =3D kstrtobool(buf, &image->auto_activate); + if (ret) + return ret; + + return count; +} + +static ssize_t auto_activate_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct fw_image *image =3D kobj_to_fw_image(kobj); + + return sysfs_emit(buf, "%d\n", image->auto_activate); +} + static struct kobj_attribute image_attrs_group[LFA_ATTR_NR_IMAGES] =3D { [LFA_ATTR_NAME] =3D __ATTR_RO(name), [LFA_ATTR_CURRENT_VERSION] =3D __ATTR_RO(current_version), @@ -571,7 +595,8 @@ static struct kobj_attribute image_attrs_group[LFA_ATTR= _NR_IMAGES] =3D { [LFA_ATTR_CPU_RENDEZVOUS] =3D __ATTR_RO(cpu_rendezvous), [LFA_ATTR_FORCE_CPU_RENDEZVOUS] =3D __ATTR_RW(force_cpu_rendezvous), [LFA_ATTR_ACTIVATE] =3D __ATTR_WO(activate), - [LFA_ATTR_CANCEL] =3D __ATTR_WO(cancel) + [LFA_ATTR_CANCEL] =3D __ATTR_WO(cancel), + [LFA_ATTR_AUTO_ACTIVATE] =3D __ATTR_RW(auto_activate), }; =20 static void init_image_default_attrs(void) @@ -640,6 +665,7 @@ static int update_fw_image_node(char *fw_uuid, int seq_= id, image->kobj.kset =3D lfa_kset; image->image_name =3D image_name; image->cpu_rendezvous_forced =3D true; + image->auto_activate =3D false; set_image_flags(image, seq_id, image_flags, reg_current_ver, reg_pending_ver); if (kobject_init_and_add(&image->kobj, &image_ktype, NULL, @@ -709,7 +735,8 @@ static int update_fw_images_tree(void) =20 /* * Go through all FW images in a loop and trigger activation - * of all activatible and pending images. + * of all activatible and pending images, but only if automatic + * activation for that image is allowed. * We have to restart enumeration after every triggered activation, * since the firmware images might have changed during the activation. */ @@ -728,7 +755,8 @@ static int activate_pending_image(void) continue; /* Invalid FW component */ =20 update_fw_image_pending(image); - if (image->activation_capable && image->activation_pending) { + if (image->activation_capable && image->activation_pending && + image->auto_activate) { found_pending =3D true; break; } --=20 2.43.0 From nobody Mon Apr 6 23:17:37 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 70B413A6B80; Tue, 17 Mar 2026 10:34:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773743642; cv=none; b=SrPYgS2ajHgNjIvMp+V2LZjsnT/724U//f6joRCHTIeYAGWQmzEoZThvJsswEfKfJlIBrP3/lACdMo9RnZRpc1aFC/Gl6EMTu1n9GjBN4KNNllQDs+/7N2JJp0xVIjBNrT1P0KotgSFJ5gxLOY19/oSVKjddTW5uO3+RuN2skBU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773743642; c=relaxed/simple; bh=x56Yn23fv7LeS34uVxZPy9d6uuy0O72wl64Cf50Trvk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SS278lD8HDuHPbgFH7vPyQwwumrdwnOMYRv+8Pf1Su77X2meSk9pC409bXPVjYAUTMZArg/0iddh0QOJyZTT5+du1silQF8kke0vBo5Ik3kTJgJzBujYMw5cS1mHrfc9sRD9A1VTI7JNhGwPci8S5OLJZB6FxpAHKlA77OqyZ9I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id D62BB1476; Tue, 17 Mar 2026 03:33:54 -0700 (PDT) Received: from e142021.fritz.box (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 82C173F7BD; Tue, 17 Mar 2026 03:33:58 -0700 (PDT) From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, vwadekar@nvidia.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org Subject: [PATCH v2 7/8] firmware: smccc: lfa: Register DT interrupt Date: Tue, 17 Mar 2026 11:33:33 +0100 Message-ID: <20260317103336.1273582-8-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260317103336.1273582-1-andre.przywara@arm.com> References: <20260317103336.1273582-1-andre.przywara@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The Arm Live Firmware Activation spec describes an asynchronous notification mechanism, where the platform can notify the host OS about newly pending image updates. In the absence of the ACPI notification mechanism also a simple devicetree node can describe an interrupt. Add code to find the respective DT node and register the specified interrupt, to trigger the activation if needed. Signed-off-by: Andre Przywara --- drivers/firmware/smccc/lfa_fw.c | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/firmware/smccc/lfa_fw.c b/drivers/firmware/smccc/lfa_f= w.c index 5dc531e462eb..ecd056901b8d 100644 --- a/drivers/firmware/smccc/lfa_fw.c +++ b/drivers/firmware/smccc/lfa_fw.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include #include @@ -841,6 +843,43 @@ static void lfa_remove_acpi(struct device *dev) } #endif =20 +static irqreturn_t lfa_irq_handler(int irq, void *dev_id) +{ + return IRQ_WAKE_THREAD; +} + +static irqreturn_t lfa_irq_handler_thread(int irq, void *dev_id) +{ + int ret; + + while (!(ret =3D activate_pending_image())) + ; + + if (ret !=3D -ENOENT) + pr_warn("notified image activation failed: %d\n", ret); + + return IRQ_HANDLED; +} + +static int lfa_register_dt(struct device *dev) +{ + struct device_node *np; + unsigned int irq; + + np =3D of_find_compatible_node(NULL, NULL, "arm,lfa"); + if (!np) + return -ENODEV; + + irq =3D irq_of_parse_and_map(np, 0); + of_node_put(np); + if (!irq) + return -ENODEV; + + return devm_request_threaded_irq(dev, irq, lfa_irq_handler, + lfa_irq_handler_thread, + IRQF_COND_ONESHOT, NULL, NULL); +} + static int lfa_faux_probe(struct faux_device *fdev) { int ret; @@ -854,6 +893,12 @@ static int lfa_faux_probe(struct faux_device *fdev) } } =20 + ret =3D lfa_register_dt(&fdev->dev); + if (!ret) + pr_info("registered LFA DT notification interrupt\n"); + if (ret !=3D -ENODEV) + return ret; + return 0; } =20 --=20 2.43.0 From nobody Mon Apr 6 23:17:37 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id A24173A9D85 for ; Tue, 17 Mar 2026 10:34:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773743645; cv=none; b=k/uUNCS9lBLZ0SPSWEJmTVVtpmrExbaOjiAlGhj6N9utldj2Nbylsta0mbtCRogH+uCu5jBj5pkczKZ/wUKdbjXgq298vysQ7hs77lwa1em8rUy+j2MkPg1WzxlggTiEjg42G1HCVMb+EwmJbqmAxL4fpKajWMK1DU/d+dUc5B8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773743645; c=relaxed/simple; bh=n9MbcnH04/fL6kBYzOy3VUkLFwOIxI8+orr8kIPGED4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Haoaw0PCiOoWdMPlLEHtO1UxutWUBvi9uMxCQmchfgnHNsMoJJI92A5zJxSzkr1ADFhG4fScljUJBd0uOzs8R26J9EcR+kJrN0SQt1TT274lAHFFB6+Z5bfxqcnt+nhA4B1cMMwKw3LD5vx2o8dOHEQJEuy3OrUvJacG7toy+fE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 0B0A61477; Tue, 17 Mar 2026 03:33:57 -0700 (PDT) Received: from e142021.fritz.box (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 402483F7BD; Tue, 17 Mar 2026 03:34:01 -0700 (PDT) From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, vwadekar@nvidia.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 8/8] firmware: smccc: lfa: introduce SMC access lock Date: Tue, 17 Mar 2026 11:33:34 +0100 Message-ID: <20260317103336.1273582-9-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260317103336.1273582-1-andre.przywara@arm.com> References: <20260317103336.1273582-1-andre.przywara@arm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" After a successful live activation, the list of firmware images might change, which also affects the sequence IDs. We store the sequence ID in a data structure and connect it to its GUID, which is the identifier used to access certain image properties from userland. When an activation is happening, the sequence ID associations might change at any point, so we must be sure to not use any previously learned sequence ID during this time. Protect the association between a sequence ID and a firmware image (its GUID, really) by a reader/writer lock. In this case it's a R/W semaphore, so it can sleep and we can hold it for longer, also concurrent SMC calls are not blocked on each other, it's just an activation that blocks calls. Signed-off-by: Andre Przywara --- drivers/firmware/smccc/lfa_fw.c | 38 +++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/smccc/lfa_fw.c b/drivers/firmware/smccc/lfa_f= w.c index ecd056901b8d..663ba79f0713 100644 --- a/drivers/firmware/smccc/lfa_fw.c +++ b/drivers/firmware/smccc/lfa_fw.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -157,6 +158,16 @@ static struct workqueue_struct *fw_images_update_wq; static struct work_struct fw_images_update_work; static struct attribute *image_default_attrs[LFA_ATTR_NR_IMAGES + 1]; =20 +/* + * A successful image activation might change the number of available imag= es, + * leading to a re-order and thus re-assignment of the sequence IDs. + * The lock protects the connection between a firmware image (through its + * user visible UUID) and the sequence IDs. Anyone doing an SMC call with + * a sequence ID needs to take the readers lock. Doing an activation requi= res + * the writer lock, as that process might change the assocications. + */ +struct rw_semaphore smc_lock; + static const struct attribute_group image_attr_group =3D { .attrs =3D image_default_attrs, }; @@ -253,6 +264,7 @@ static unsigned long get_nr_lfa_components(void) reg.a0 =3D LFA_1_0_FN_GET_INFO; reg.a1 =3D 0; /* lfa_info_selector =3D 0 */ =20 + /* No need for the smc_lock, since no sequence IDs are involved. */ arm_smccc_1_2_invoke(®, ®); if (reg.a0 !=3D LFA_SUCCESS) return reg.a0; @@ -265,9 +277,11 @@ static int lfa_cancel(void *data) struct fw_image *image =3D data; struct arm_smccc_1_2_regs reg =3D { 0 }; =20 + down_read(&smc_lock); reg.a0 =3D LFA_1_0_FN_CANCEL; reg.a1 =3D image->fw_seq_id; arm_smccc_1_2_invoke(®, ®); + up_read(&smc_lock); =20 /* * When firmware activation is called with "skip_cpu_rendezvous=3D1", @@ -332,6 +346,7 @@ static int activate_fw_image(struct fw_image *image) int ret; =20 retry: + down_write(&smc_lock); if (image->cpu_rendezvous_forced || image->cpu_rendezvous) ret =3D stop_machine(call_lfa_activate, image, cpu_online_mask); else @@ -339,10 +354,13 @@ static int activate_fw_image(struct fw_image *image) =20 if (!ret) { update_fw_images_tree(); + up_write(&smc_lock); =20 return 0; } =20 + up_write(&smc_lock); + if (ret =3D=3D -LFA_CALL_AGAIN) { /* SMC returned with call_again flag set */ if (ktime_before(ktime_get(), end)) { @@ -383,8 +401,11 @@ static int prime_fw_image(struct fw_image *image) * be called again. * reg.a1 will become 0 once the prime process completes. */ + down_read(&smc_lock); reg.a1 =3D image->fw_seq_id; arm_smccc_1_2_invoke(®, &res); + up_read(&smc_lock); + if ((long)res.a0 < 0) { pr_err("LFA_PRIME for image %s failed: %s\n", get_image_name(image), @@ -429,7 +450,7 @@ static ssize_t activation_capable_show(struct kobject *= kobj, return sysfs_emit(buf, "%d\n", image->activation_capable); } =20 -static void update_fw_image_pending(struct fw_image *image) +static void _update_fw_image_pending(struct fw_image *image) { struct arm_smccc_1_2_regs reg =3D { 0 }; =20 @@ -441,6 +462,13 @@ static void update_fw_image_pending(struct fw_image *i= mage) image->activation_pending =3D !!(reg.a3 & BIT(1)); } =20 +static void update_fw_image_pending(struct fw_image *image) +{ + down_read(&smc_lock); + _update_fw_image_pending(image); + up_read(&smc_lock); +} + static ssize_t activation_pending_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -515,9 +543,11 @@ static ssize_t pending_version_show(struct kobject *ko= bj, * Similar to activation pending, this value can change following an * update, we need to retrieve fresh info instead of stale information. */ + down_read(&smc_lock); reg.a0 =3D LFA_1_0_FN_GET_INVENTORY; reg.a1 =3D image->fw_seq_id; arm_smccc_1_2_invoke(®, ®); + up_read(&smc_lock); if (reg.a0 =3D=3D LFA_SUCCESS) { if (reg.a5 !=3D 0 && image->activation_pending) { u32 maj, min; @@ -749,6 +779,7 @@ static int activate_pending_image(void) struct fw_image *image; int ret; =20 + down_read(&smc_lock); spin_lock(&lfa_kset->list_lock); list_for_each_entry(kobj, &lfa_kset->list, entry) { image =3D kobj_to_fw_image(kobj); @@ -756,7 +787,7 @@ static int activate_pending_image(void) if (image->fw_seq_id =3D=3D -1) continue; /* Invalid FW component */ =20 - update_fw_image_pending(image); + _update_fw_image_pending(image); if (image->activation_capable && image->activation_pending && image->auto_activate) { found_pending =3D true; @@ -764,6 +795,7 @@ static int activate_pending_image(void) } } spin_unlock(&lfa_kset->list_lock); + up_read(&smc_lock); =20 if (!found_pending) return -ENOENT; @@ -950,6 +982,8 @@ static int __init lfa_init(void) */ lfa_dev =3D faux_device_create("arm-lfa", NULL, &lfa_device_ops); =20 + init_rwsem(&smc_lock); + err =3D update_fw_images_tree(); if (err !=3D 0) { kset_unregister(lfa_kset); --=20 2.43.0