From nobody Fri Sep 25 00:03:00 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 7E13F4E9C30; Fri, 18 Sep 2026 14:11:33 +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=1789740696; cv=none; b=WZlZU7Y/cZBd4n3UhGOS+0dkoDF3NB7MhktkZMfsxHWb43+uifPv8EOeN/W8G3VhtRytXRQzXhdx6lktzg9M1b+ZoAbzM6PgKNxAskfjlbgXRNrDYhug3sxjd1BkRPJCGObtZRXzi5As1cpdf7nGiFCB4DDTN8z6mTQi0dJ4mbw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789740696; c=relaxed/simple; bh=Dws0+kP7wIvnciLiLRG0KeMMYGifjNtg4V1UwKQlzQA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=d32lFO/UUIfgk0tow5TSW1CD8mqFcaPXsyhUBZsHMvz/1CD5wFvhcFpsc3gfzsF4we2LSuX/zR0Fxm8QfWk/Io1oZejqWHY+AVpfW/KYaTbXbHmqnl3hqLWq328/WCtF2dD2iFH/ZmOgJ95gACwp2BvpW6xqgr8yE+ZzyMnfH6Y= 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; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=E9uWzsZ7; 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 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="E9uWzsZ7" 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 599851C00; Fri, 18 Sep 2026 07:11:23 -0700 (PDT) Received: from e142021.Arm.com (e142021.arm.com [10.41.4.144]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id E2E733F86F; Fri, 18 Sep 2026 07:11:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789740686; bh=Dws0+kP7wIvnciLiLRG0KeMMYGifjNtg4V1UwKQlzQA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=E9uWzsZ7sS4KuvJ8qtJYv5lutyhYe1Hn9vg3IxX9eYihuDRFC4Qlf9jQbFKFRZ82e 5AcfhCO3xpzsgABAjqNtbdXBrRfHoJmbRhNEyfbDC04foJDSuiAeP2xDnWQGjoFpJK Yz6Es4bWMev78IT/hgeNyqhKl5eu76taJ+TWoSCc= From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, Varun Wadekar , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org Subject: [PATCH v4 1/8] dt-bindings: arm: Add Live Firmware Activation Date: Fri, 18 Sep 2026 16:11:04 +0200 Message-ID: <20260918141112.2115555-2-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260918141112.2115555-1-andre.przywara@arm.com> References: <20260918141112.2115555-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. Describe 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 Reviewed-by: Rob Herring (Arm) --- .../devicetree/bindings/arm/arm,lfa.yaml | 50 +++++++++++++++++++ 1 file changed, 50 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 0000000000000..179c542f383d4 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/arm,lfa.yaml @@ -0,0 +1,50 @@ +# 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: + The notification interrupt for changed firmware image status. For + an out-of-band firmware update, some system entity would signal + the availability of a firmware update to the host OS via this interr= upt. + + This must be an edge-triggered IRQ. + +required: + - compatible + - interrupts + +additionalProperties: false + +examples: + - | + #include + + firmware { + firmware-update { + compatible =3D "arm,lfa"; + interrupts =3D ; + }; + }; +... --=20 2.43.0 From nobody Fri Sep 25 00:03:00 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id F319E3ACA41; Fri, 18 Sep 2026 14:11:30 +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=1789740694; cv=none; b=l82IDobRZ3SzhqzmaUDJplAUONkavU70AHsZtWilANMBcV9vWt2cSgkJ00XLwy0W5RmJ82ITq/lEIQvfdMu3NrmoBlFm0jF3bQdVFTcVPkmKJqi3cQTIGwXZtNCQUCRa8BXDXi2YAjia8HpK1v2BgagXhhZPij4+Vv1wGYgJOTU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789740694; c=relaxed/simple; bh=w+AXLxZA8eWIp+eXz5q/xp/OJp80clPs0xepQjQhKrA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HRDxeQZH/79aBAupvVEuZPbbT1I4Lmzu9bFgthJc2CsN0/0c4vKyoRVpqud45rvQ//IeTx4p8khVUBdeq9XBACdWScU8QL/2FpH5yXIBlhokWtQrOlFDgNkYzQvqOHJjhPdt2+oWoZOb8NPKWIpVjlUSK3fwYJgXfffObp8GQp8= 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; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=m2wpqo87; 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 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="m2wpqo87" 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 A9BCA1D14; Fri, 18 Sep 2026 07:11:26 -0700 (PDT) Received: from e142021.Arm.com (e142021.arm.com [10.41.4.144]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 482993F86C; Fri, 18 Sep 2026 07:11:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789740690; bh=w+AXLxZA8eWIp+eXz5q/xp/OJp80clPs0xepQjQhKrA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=m2wpqo87aUHPpdu9RFZSGBvu+SWvvbAx896BidF9jWqeH0IuPS9DGzJcDsH2OU7ls 9uxYQi3lorbQDw6Vtzme2MFthOn9ha1AsNvZEr/CUwyX8u5dypqI49aAkNtlP9s1AQ 2IBy13PS5Oph4QbjbkCM7D7oc/3LSZCi5skpjHro= From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, Varun Wadekar , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org, Greg Kroah-Hartman Subject: [PATCH v4 2/8] firmware: smccc: Add support for Live Firmware Activation (LFA) Date: Fri, 18 Sep 2026 16:11:05 +0200 Message-ID: <20260918141112.2115555-3-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260918141112.2115555-1-andre.przywara@arm.com> References: <20260918141112.2115555-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 | 766 ++++++++++++++++++++++++++++++++ drivers/firmware/smccc/smccc.c | 5 + include/linux/arm-smccc.h | 15 + 5 files changed, 797 insertions(+) create mode 100644 drivers/firmware/smccc/lfa_fw.c diff --git a/drivers/firmware/smccc/Kconfig b/drivers/firmware/smccc/Kconfig index 15e7466179a62..7fd646d515f86 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 68bbff1407b8b..cddf2c460ab9d 100644 --- a/drivers/firmware/smccc/Makefile +++ b/drivers/firmware/smccc/Makefile @@ -2,3 +2,4 @@ # obj-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) +=3D bus.o 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 0000000000000..7cf847e102d5a --- /dev/null +++ b/drivers/firmware/smccc/lfa_fw.c @@ -0,0 +1,766 @@ +// 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 + +#include + +#undef pr_fmt +#define pr_fmt(fmt) "Arm LFA: " fmt + +/* 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 + +#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) +}; + +static const char *lfa_error_string(long 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]; + + return lfa_error_strings[LFA_DEVICE_ERROR]; +} + +static const int lfa_error_map[] =3D { + [LFA_SUCCESS] =3D 0, + [LFA_NOT_SUPPORTED] =3D -EOPNOTSUPP, + [LFA_BUSY] =3D -EBUSY, + [LFA_AUTH_ERROR] =3D -EPERM, + [LFA_NO_MEMORY] =3D -ENOMEM, + [LFA_CRITICAL_ERROR] =3D -EACCES, + [LFA_DEVICE_ERROR] =3D -EIO, + [LFA_WRONG_STATE] =3D -EPROTO, + [LFA_INVALID_PARAMETERS] =3D -EINVAL, + [LFA_COMPONENT_WRONG_STATE] =3D -EPROTO, + [LFA_INVALID_ADDRESS] =3D -ENXIO, + [LFA_ACTIVATION_FAILED] =3D -EIO, +}; + +static int lfa_to_linux_errno(long lfa_error) +{ + if (lfa_error > 0) + return -EINVAL; + + if (-lfa_error > LFA_ACTIVATION_FAILED) + return -EINVAL; + + return lfa_error_map[-lfa_error]; +} + +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; + bool use_cpu_rendezvous; + 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 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 ARM_SMCCC_LFA_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 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); +} + +static int lfa_cancel(void *data) +{ + struct fw_image *image =3D data; + struct arm_smccc_1_2_regs reg =3D { 0 }; + + reg.a0 =3D ARM_SMCCC_LFA_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", + get_image_name(image)); + } else { + pr_err("Activation not cancelled for image %s: %s\n", + get_image_name(image), lfa_error_string(reg.a0)); + return -EINVAL; + } + + return reg.a0; +} + +/* + * 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. + * Returns a Linux error code, not an LFA one. + */ +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 ARM_SMCCC_LFA_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. + * Use the cached value of the rendezvous status, to be consistent + * with how we were called (via stop_machine() or not). + */ + reg.a2 =3D !image->use_cpu_rendezvous; + arm_smccc_1_2_invoke(®, &res); + + if ((long)res.a0 < 0) + return lfa_to_linux_errno((long)res.a0); + + if (res.a1 & LFA_ACTIVATE_CALL_AGAIN) + return -EAGAIN; + + return 0; +} + +static int activate_fw_image(struct fw_image *image) +{ + int ret; + +retry: + /* + * cpu_rendezvous_forced is set by the administrator, via sysfs, + * cpu_rendezvous is dictated by each firmware component. + */ + image->use_cpu_rendezvous =3D image->cpu_rendezvous_forced || + image->cpu_rendezvous; + if (image->use_cpu_rendezvous) + ret =3D stop_machine(call_lfa_activate, image, cpu_online_mask); + else + ret =3D call_lfa_activate(image); + + if (!ret) { + update_fw_images_tree(); + + return 0; + } + + /* SMC returned with call_again flag set, or with LFA_BUSY */ + if (ret =3D=3D -EAGAIN || ret =3D=3D -EBUSY) + goto retry; + + lfa_cancel(image); + + pr_err("LFA_ACTIVATE for image %s failed\n", get_image_name(image)); + + return ret; +} + +static int prime_fw_image(struct fw_image *image) +{ + struct arm_smccc_1_2_regs reg =3D { 0 }, res; + int ret; + + if (image->may_reset_cpu) { + pr_err("CPU reset not supported by kernel driver\n"); + + return -EINVAL; + } + + reg.a0 =3D ARM_SMCCC_LFA_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((long)res.a0)); + + return lfa_to_linux_errno((long)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 ARM_SMCCC_LFA_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 ARM_SMCCC_LFA_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); + bool activate; + int ret; + + ret =3D kstrtobool(buf, &activate); + if (ret) + return ret; + + if (!activate) + return count; + + ret =3D prime_fw_image(image); + if (ret) + return ret; + + ret =3D activate_fw_image(image); + if (ret) + return ret; + + 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); + bool cancel; + int ret; + + ret =3D kstrtobool(buf, &cancel); + if (ret) + return ret; + + if (!cancel) + return count; + + 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; + break; + } + } + + 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; + struct kobject *kobj; + 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; + } + + /* + * 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 ARM_SMCCC_LFA_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; + } + } + + /* + * 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; +} + +static int lfa_smccc_probe(struct arm_smccc_device *sdev) +{ + struct arm_smccc_1_2_regs reg =3D { 0 }; + int err; + + reg.a0 =3D ARM_SMCCC_LFA_GET_VERSION; + arm_smccc_1_2_invoke(®, ®); + if ((s32)reg.a0 =3D=3D -LFA_NOT_SUPPORTED) + 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) { + destroy_workqueue(fw_images_update_wq); + + return -ENOMEM; + } + + err =3D update_fw_images_tree(); + if (err !=3D 0) { + kset_unregister(lfa_kset); + destroy_workqueue(fw_images_update_wq); + } + + return err; +} + +static void lfa_smccc_remove(struct arm_smccc_device *sdev) +{ + clean_fw_images_tree(); + flush_workqueue(fw_images_update_wq); + destroy_workqueue(fw_images_update_wq); + kset_unregister(lfa_kset); +} + +static const struct arm_smccc_device_id lfa_smccc_id_table[] =3D { + { .func_id =3D ARM_SMCCC_LFA_GET_VERSION }, + {} +}; +MODULE_DEVICE_TABLE(arm_smccc, lfa_smccc_id_table); + +static struct arm_smccc_driver smccc_lfa_driver =3D { + .name =3D KBUILD_MODNAME, + .probe =3D lfa_smccc_probe, + .remove =3D lfa_smccc_remove, + .id_table =3D lfa_smccc_id_table, +}; + +module_arm_smccc_driver(smccc_lfa_driver); + +MODULE_DESCRIPTION("ARM Live Firmware Activation (LFA)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/smccc/smccc.c b/drivers/firmware/smccc/smccc.c index 5ea3478be9d3c..d7cd01e5c92b8 100644 --- a/drivers/firmware/smccc/smccc.c +++ b/drivers/firmware/smccc/smccc.c @@ -94,6 +94,11 @@ static const struct smccc_device_info smccc_devices[] __= initconst =3D { .requires_smc =3D false, .device_name =3D "arm-smccc-trng", }, + { + .func_id =3D ARM_SMCCC_LFA_GET_VERSION, + .requires_smc =3D false, + .device_name =3D "arm-smccc-lfa", + }, }; =20 static bool __init smccc_probe_smccc_device(const struct smccc_device_info= *smccc_dev) diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 4de81848fe2ee..956a5af96c702 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -304,6 +304,21 @@ ARM_SMCCC_OWNER_STANDARD, \ 0x53) =20 +/* Live Firmware Activation (LFA) calls (defined by ARM DEN0147) */ +#define ARM_SMCCC_LFA_FN_BASE \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_64, \ + ARM_SMCCC_OWNER_STANDARD, \ + 0x2e0) +#define ARM_SMCCC_LFA_FN(n) (ARM_SMCCC_LFA_FN_BASE + (n)) +#define ARM_SMCCC_LFA_GET_VERSION ARM_SMCCC_LFA_FN(0) +#define ARM_SMCCC_LFA_CHECK_FEATURE ARM_SMCCC_LFA_FN(1) +#define ARM_SMCCC_LFA_GET_INFO ARM_SMCCC_LFA_FN(2) +#define ARM_SMCCC_LFA_GET_INVENTORY ARM_SMCCC_LFA_FN(3) +#define ARM_SMCCC_LFA_PRIME ARM_SMCCC_LFA_FN(4) +#define ARM_SMCCC_LFA_ACTIVATE ARM_SMCCC_LFA_FN(5) +#define ARM_SMCCC_LFA_CANCEL ARM_SMCCC_LFA_FN(6) + /* * Return codes defined in ARM DEN 0070A * ARM DEN 0070A is now merged/consolidated into ARM DEN 0028 C --=20 2.43.0 From nobody Fri Sep 25 00:03:00 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id CFB0F4F0553; Fri, 18 Sep 2026 14:11:33 +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=1789740696; cv=none; b=sJWl446WkzKcWVR6sp/1h8Lc5Zc9PSLUV0T8NJCwy2U4/Mb2RSCsnKnoi4e3ET9YV0qcLgRVYQQHtx8C2JaCBrO9oxBpmbgvukUNiMH+YOYdRqbd21L8Q6mgahEew544vkhw9hEZHOeSE3s70lgl7PsQKKdyNeJqmKPFYQzOT24= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789740696; c=relaxed/simple; bh=EqKIdJZ0C+yXX2cIFzzx7FCYTPgkMMFND81vIIt+GIE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=u5NBVFKTU3AHAUxySrv1tjaBwMnsPwAZE/aeBHTfCcsMDMqvMVe3IbEpzeJ3lwmWy1wnRP3Q3zGb66FGeq6OCFOneDO2CprHvWdhLI8D1o7IyoYXq+0gL4b2Bqyc1x3hz9En8y9yjQ+nB58Xo1NWRDcPhFjPF/mA1MK3gqCK+Dg= 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; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=F8FyOcCX; 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 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="F8FyOcCX" 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 B0DF2168F; Fri, 18 Sep 2026 07:11:29 -0700 (PDT) Received: from e142021.Arm.com (e142021.arm.com [10.41.4.144]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 9851E3F86C; Fri, 18 Sep 2026 07:11:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789740693; bh=EqKIdJZ0C+yXX2cIFzzx7FCYTPgkMMFND81vIIt+GIE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=F8FyOcCXQckp4bzNt3OPkNZh/YqjJUFVK3Ks90QuELF1e+SfV/oHOHnLlbvEJbIjW +0+W6JoEjuh4aPKeFPi1H2YKg+jWtMQuTPHrDEB65dDB4bMg/rHN6FapE+LfjOWR5d ec95aBneWJET/Ps4qFVCb0yDoYk/OG7950gLn2Bc= From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, Varun Wadekar , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org Subject: [PATCH v4 3/8] firmware: smccc: lfa: Add timeout and trigger watchdog Date: Fri, 18 Sep 2026 16:11:06 +0200 Message-ID: <20260918141112.2115555-4-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260918141112.2115555-1-andre.przywara@arm.com> References: <20260918141112.2115555-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, 38 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/smccc/lfa_fw.c b/drivers/firmware/smccc/lfa_f= w.c index 7cf847e102d5a..b6ce478d3fc01 100644 --- a/drivers/firmware/smccc/lfa_fw.c +++ b/drivers/firmware/smccc/lfa_fw.c @@ -6,11 +6,14 @@ #include #include #include +#include #include #include #include +#include #include #include +#include #include #include #include @@ -27,6 +30,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 @@ -295,6 +303,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 ARM_SMCCC_LFA_ACTIVATE; reg.a1 =3D image->fw_seq_id; /* @@ -318,6 +327,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: @@ -339,8 +349,15 @@ static int activate_fw_image(struct fw_image *image) } =20 /* SMC returned with call_again flag set, or with LFA_BUSY */ - if (ret =3D=3D -EAGAIN || ret =3D=3D -EBUSY) - goto retry; + if (ret =3D=3D -EAGAIN || ret =3D=3D -EBUSY) { + if (ktime_before(ktime_get(), end)) { + if (!msleep_interruptible(LFA_ACTIVATE_DELAY_MS)) + goto retry; + ret =3D -EINTR; + } else { + ret =3D -ETIMEDOUT; + } + } =20 lfa_cancel(image); =20 @@ -352,6 +369,7 @@ 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) { @@ -360,6 +378,8 @@ static int prime_fw_image(struct fw_image *image) return -EINVAL; } =20 + touch_nmi_watchdog(); + reg.a0 =3D ARM_SMCCC_LFA_PRIME; retry: /* @@ -378,10 +398,23 @@ static int prime_fw_image(struct fw_image *image) return lfa_to_linux_errno((long)res.a0); } =20 - if (res.a1 & LFA_PRIME_CALL_AGAIN) - goto retry; + if (!(res.a1 & LFA_PRIME_CALL_AGAIN)) + return 0; =20 - return 0; + /* SMC returned with call_again flag set */ + if (ktime_before(ktime_get(), end)) { + if (!msleep_interruptible(LFA_PRIME_DELAY_MS)) + goto retry; + ret =3D -EINTR; + } else { + pr_err("LFA_PRIME for image %s timed out", + get_image_name(image)); + ret =3D -ETIMEDOUT; + } + + lfa_cancel(image); + + return ret; } =20 static ssize_t name_show(struct kobject *kobj, struct kobj_attribute *attr, --=20 2.43.0 From nobody Fri Sep 25 00:03:00 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 532172E06E6; Fri, 18 Sep 2026 14:11:37 +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=1789740699; cv=none; b=BuRZJLVXkTqzlE/7kdmDicvyWQrYZyfWAGzT/OKUE2vpZ7Rhxt+PwIrcdmsMwC5f+eWPqWpVgv9DTkTDEG/bAkCpZBhU/gSuz6MK17YYLgyjJ1ZjItaehct5RM9TDA5aYNgvVIctweFqSIT2OPtRrQmFcc+/Qjia1uY7Nqm5sNc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789740699; c=relaxed/simple; bh=tqqHViVw4RtjptjFs9M84wpARYpMZc1L7EOtLQIh1oo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XftadRTfsar0jyIU4E+CXKVX2l3nBRmWtnHqq0nZLpqjxvK2la0JFqY+LnrHjFBOBDNWhsIOSfNb8BSzsz9k/cdB6sXhssrutBy7BBDhiIkjN5MDHw2XNrbcfBcUnw97gecc6R6/DQYGgkQ/1R3vy5+63sdzhbfh1p6+yb17QX4= 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; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=YLmw7Nwo; 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 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="YLmw7Nwo" 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 B52B21E4D; Fri, 18 Sep 2026 07:11:32 -0700 (PDT) Received: from e142021.Arm.com (e142021.arm.com [10.41.4.144]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 9CF073F86C; Fri, 18 Sep 2026 07:11:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789740696; bh=tqqHViVw4RtjptjFs9M84wpARYpMZc1L7EOtLQIh1oo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YLmw7Nwo80b6aduB5DEetF7b3bjN96QlcDU+DDuMilue4/P/UWwGZXj7+cgSSgiAB +vK7xolsHS6S7ansbLxSMUguVmiqgOUriItR/PIsEwp+bDA3WgfzAwxaVecpy0E+4g TvPPj6M+GMyQnrRryW9okdFmZzx4Ty5aBbfSZoGE= From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, Varun Wadekar , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org Subject: [PATCH v4 4/8] firmware: smccc: lfa: Register ACPI notification Date: Fri, 18 Sep 2026 16:11:07 +0200 Message-ID: <20260918141112.2115555-5-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260918141112.2115555-1-andre.przywara@arm.com> References: <20260918141112.2115555-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 smccc bus] Signed-off-by: Andre Przywara --- drivers/firmware/smccc/lfa_fw.c | 122 +++++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/smccc/lfa_fw.c b/drivers/firmware/smccc/lfa_f= w.c index b6ce478d3fc01..bb89fffde6856 100644 --- a/drivers/firmware/smccc/lfa_fw.c +++ b/drivers/firmware/smccc/lfa_fw.c @@ -3,12 +3,14 @@ * Copyright (C) 2025 Arm Limited */ =20 +#include #include #include #include #include #include #include +#include #include #include #include @@ -18,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 @@ -733,6 +737,112 @@ 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_smccc_probe(struct arm_smccc_device *sdev) { struct arm_smccc_1_2_regs reg =3D { 0 }; @@ -769,11 +879,21 @@ static int lfa_smccc_probe(struct arm_smccc_device *s= dev) destroy_workqueue(fw_images_update_wq); } =20 - return err; + if (!acpi_disabled) { + err =3D lfa_register_acpi(&sdev->dev); + if (!err) + pr_info("registered LFA ACPI notification\n"); + if (err !=3D -ENODEV) + return err; + } + + return 0; } =20 static void lfa_smccc_remove(struct arm_smccc_device *sdev) { + if (!acpi_disabled) + lfa_remove_acpi(&sdev->dev); clean_fw_images_tree(); flush_workqueue(fw_images_update_wq); destroy_workqueue(fw_images_update_wq); --=20 2.43.0 From nobody Fri Sep 25 00:03:00 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id D38094FD29C; Fri, 18 Sep 2026 14:11:39 +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=1789740702; cv=none; b=UNxG7MjiRgiFET7XnBBNOsbFptNUS/C0/ZsGcqlvlBowQ8YV8bkVpU//BAp/JeUWmHkAvhLNAc/En7x/dJF6MtBxFtYkvSISVTkG7IU0sXhhKRiTcpogLQ5Zl79VdTKMjC22U1Cr/1SE8BglD3dCrdta0J6k+gTdAqPxPWnUod0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789740702; c=relaxed/simple; bh=6+DNaJ0KhfXvVkfpY/J73/5RJw4ENnx4YQhzelT1LV4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=X3ubY5GmyiPG0WC3biKg1sJpzYYRt+E0RBjVSXMJxZmLkLeJuzGOStTfYEm1Rle81+wQCgue9hteGvIJBRwso3B3X/0pNRbbKUwAZOSuJ95VjyF5T5eZHVexuDAtx4u7eqJw2QpC+wIJ0xaPm6+FT7KMdVnJpO71oqMFedzepKc= 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; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=Zty74dQb; 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 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="Zty74dQb" 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 B8132168F; Fri, 18 Sep 2026 07:11:35 -0700 (PDT) Received: from e142021.Arm.com (e142021.arm.com [10.41.4.144]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id A2F433F86C; Fri, 18 Sep 2026 07:11:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789740699; bh=6+DNaJ0KhfXvVkfpY/J73/5RJw4ENnx4YQhzelT1LV4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Zty74dQbT+PosZ6ET2SfcR33YteCFQ/iKYSCG4AGzza7UyEqBp0YvhTH8XbYYxSqD w+2WQEsMiTlkphsaIZS7xK7y6lCdjT/UtOa2RHV9/t+Ega1MUUNmFqTUc3bkCzt/P5 b5c4NAQV0uS9E2kY9UL558fZTIxSUbvceq70wcgI= From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, Varun Wadekar , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org Subject: [PATCH v4 5/8] firmware: smccc: lfa: Add auto_activate sysfs file Date: Fri, 18 Sep 2026 16:11:08 +0200 Message-ID: <20260918141112.2115555-6-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260918141112.2115555-1-andre.przywara@arm.com> References: <20260918141112.2115555-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 bb89fffde6856..7a55b4a6785d8 100644 --- a/drivers/firmware/smccc/lfa_fw.c +++ b/drivers/firmware/smccc/lfa_fw.c @@ -120,6 +120,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 @@ -135,6 +136,7 @@ struct fw_image { bool cpu_rendezvous; bool cpu_rendezvous_forced; bool use_cpu_rendezvous; + bool auto_activate; struct kobj_attribute image_attrs[LFA_ATTR_NR_IMAGES]; }; =20 @@ -589,6 +591,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), @@ -599,7 +623,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) @@ -670,6 +695,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, @@ -739,7 +765,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. */ @@ -758,7 +785,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 Fri Sep 25 00:03:00 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 967D84F68DB; Fri, 18 Sep 2026 14:11:43 +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=1789740705; cv=none; b=iugMQLS0g88Pe3sjMWR4DzN0bEj9cq7OqfScZHkxZ8x6Lpb7Vv4oBwBU3htlyhWlM5ZkkhAwrqHcjj7wAnsbuuzxBOmzQ3xxE8D/nhU2hwrb00wkPupyl26oPo5ayfmZdHZGRLtzy613uiiS6X7sAnPot/xu2x9sDVTUZ7Fn4mM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789740705; c=relaxed/simple; bh=FKj7gJ++G++n7fDeDo9/Cdn7Ipnxnl9yUVmYKTr5z1w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IwbtE9cn+0kpkhUeYOQagXwYle0ht5b+wjh6B8FZANBTfgYQtNZsPNqKGHzOFTzzM/W1+gCQW1cnZZdpjPKvS7nN9d8A5GmbLCyz+ffPjmAzEQ+y4UBVaDRmdlGJsONTsRin7MnEHOjN/tEYUj7zHAiHkk+uZIbzWn7lVVBUF7g= 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; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=iMLKelrQ; 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 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="iMLKelrQ" 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 C0DE2168F; Fri, 18 Sep 2026 07:11:38 -0700 (PDT) Received: from e142021.Arm.com (e142021.arm.com [10.41.4.144]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id A87433F86C; Fri, 18 Sep 2026 07:11:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789740702; bh=FKj7gJ++G++n7fDeDo9/Cdn7Ipnxnl9yUVmYKTr5z1w=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=iMLKelrQtB4f/cgVg0lvweqH3GjnXOudFJBq2gCVmJ7PhvUPhqj8CnNqNfA2ypH6W 8tW7i8jXYCKyv/9USa11d7pXv6ngt5+T8K0l525RFlHLVGRQxzfCLg3gtIge4HnBeR OosPNEe4A0+PbOS2PKb0b1TdDyEB7NojvbcGBkJE= From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, Varun Wadekar , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org Subject: [PATCH v4 6/8] firmware: smccc: lfa: Register DT interrupt Date: Fri, 18 Sep 2026 16:11:09 +0200 Message-ID: <20260918141112.2115555-7-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260918141112.2115555-1-andre.przywara@arm.com> References: <20260918141112.2115555-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 | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/firmware/smccc/lfa_fw.c b/drivers/firmware/smccc/lfa_f= w.c index 7a55b4a6785d8..a23df331ffde9 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 @@ -871,6 +873,37 @@ static void lfa_remove_acpi(struct device *dev) } #endif =20 +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, NULL, lfa_irq_handler_thread, + IRQF_COND_ONESHOT, NULL, NULL); +} + static int lfa_smccc_probe(struct arm_smccc_device *sdev) { struct arm_smccc_1_2_regs reg =3D { 0 }; @@ -913,6 +946,12 @@ static int lfa_smccc_probe(struct arm_smccc_device *sd= ev) pr_info("registered LFA ACPI notification\n"); if (err !=3D -ENODEV) return err; + } else { + err =3D lfa_register_dt(&sdev->dev); + if (!err) + pr_info("registered LFA DT notification interrupt\n"); + if (err !=3D -ENODEV) + return err; } =20 return 0; --=20 2.43.0 From nobody Fri Sep 25 00:03:00 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 0ECD54FDA68; Fri, 18 Sep 2026 14:11:45 +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=1789740707; cv=none; b=X4QfDlz0YizY/BdKlHTliFiZlYFLKZ8vL+1NzVaKWd7PkX47K+1T90UIKzMDYtIVWLvGH02wcHqodi9uVNFW4Kmsz1BTFFWb2yqrjNULE1MCrAg0HT/WK6d4O1SpeqiWtYK1BH1ZoPHCZy41oZd4Rn7T2z69hsZ2zJChH+nEeGM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789740707; c=relaxed/simple; bh=l6z/d8J+mDpH7w64Ngl6JhjLmuSLkYpaYqcIi48vBz4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=klrMVz3xcKmS/JTr8jxTWn2LZqZ4CvAWGlUdJzDXEG8FLavP5EuKdbW13tUkAC5NCW8sWfsri9ZXmCso1Fu2QDM2CjuHmrqHPTqccrfj0a7PeAnQqiZrWt15khyP5trQbDNtmDXaDjkwcLfTQLrgpFobDljLsVzgmeAYat0OKEk= 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; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=EktjJMX1; 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 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="EktjJMX1" 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 C84291C00; Fri, 18 Sep 2026 07:11:41 -0700 (PDT) Received: from e142021.Arm.com (e142021.arm.com [10.41.4.144]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id AF9293F86C; Fri, 18 Sep 2026 07:11:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789740705; bh=l6z/d8J+mDpH7w64Ngl6JhjLmuSLkYpaYqcIi48vBz4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EktjJMX104nV8fGBNIxtoKmxhb7pql0uIgXDsyF7l6CyphhBcS3i/BX+zPhuTRn9j ZdkzOpUGOAGyDbqf8ng+eLX43LFeF61MKQlZbyP63mJeSk67Kf6aeZZGjqtBq59GST 8rdehKl70TdFnI4uttHmVXIzMTtghVUCsTxXrzWg= From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, Varun Wadekar , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org Subject: [PATCH v4 7/8] firmware: smccc: lfa: introduce SMC access lock Date: Fri, 18 Sep 2026 16:11:10 +0200 Message-ID: <20260918141112.2115555-8-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260918141112.2115555-1-andre.przywara@arm.com> References: <20260918141112.2115555-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 a23df331ffde9..bf97e7d34e6c7 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 @@ -177,6 +178,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, }; @@ -259,6 +270,7 @@ static unsigned long get_nr_lfa_components(void) reg.a0 =3D ARM_SMCCC_LFA_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; @@ -279,9 +291,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 ARM_SMCCC_LFA_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", @@ -339,6 +353,7 @@ static int activate_fw_image(struct fw_image *image) int ret; =20 retry: + down_write(&smc_lock); /* * cpu_rendezvous_forced is set by the administrator, via sysfs, * cpu_rendezvous is dictated by each firmware component. @@ -352,10 +367,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); + /* SMC returned with call_again flag set, or with LFA_BUSY */ if (ret =3D=3D -EAGAIN || ret =3D=3D -EBUSY) { if (ktime_before(ktime_get(), end)) { @@ -396,8 +414,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), @@ -441,7 +462,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 @@ -453,6 +474,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) { @@ -527,9 +555,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 ARM_SMCCC_LFA_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; @@ -779,6 +809,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); @@ -786,7 +817,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; @@ -794,6 +825,7 @@ static int activate_pending_image(void) } } spin_unlock(&lfa_kset->list_lock); + up_read(&smc_lock); =20 if (!found_pending) return -ENOENT; @@ -934,6 +966,8 @@ static int lfa_smccc_probe(struct arm_smccc_device *sde= v) return -ENOMEM; } =20 + init_rwsem(&smc_lock); + err =3D update_fw_images_tree(); if (err !=3D 0) { kset_unregister(lfa_kset); --=20 2.43.0 From nobody Fri Sep 25 00:03:00 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 012DA352027; Fri, 18 Sep 2026 14:11:48 +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=1789740711; cv=none; b=LZs7jaL/YnHnAfzbNIKZDft0K3WzU5tljPYkkuIFJUMkhxjt4WsgLAjlravvR10GFD5o85vW+JRkM/sWUnB/7r7LxgkGwL+Cp9/Cckch9jpHEu+/ErUyIV0l0nF4vRMrU0oGuEs5cCUHwBGvz1bDR71BMRlynvMdTH52MP45Neo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789740711; c=relaxed/simple; bh=h3MfiV0woiOCQI6Gw/byJn2fJjYmiAn1DfvOYZgNqro=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=vB6Ab28fD4DfXIPWLaigGup3gtH4vNhfhoxn18G2DJomFJmi/BUB6w/87rgEaXRmIWIASiUVBu9xvdFaeJ2032AdJYwgzv1Znq2oWG3wHXzox6v5mUYuPxMDLm0CemzrlFF/JNK7E2xXtCeS5D1NtoT3JAwlw9Zv917PZSvJi6I= 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; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b=U9bvvXF6; 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 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=arm.com header.i=@arm.com header.b="U9bvvXF6" 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 CF94F1D14; Fri, 18 Sep 2026 07:11:44 -0700 (PDT) Received: from e142021.Arm.com (e142021.arm.com [10.41.4.144]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id B6D673F86C; Fri, 18 Sep 2026 07:11:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1789740708; bh=h3MfiV0woiOCQI6Gw/byJn2fJjYmiAn1DfvOYZgNqro=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=U9bvvXF6jmRsyhz8nEU6QFc31N3ZiSYUbdjdttMs+pbLAuDkT1znSBQoHYBjbLq9Z gzMvjG1K522CWrnMwQAU+Z0ud/pY3cbRJzBvBKDBBoj1ThSuQ7XOj5IwCHddc/+Bi2 M8nRY8fwe7qnwLpEXSaZu0WX01m4LsW1fzaZg3yQ= From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, Varun Wadekar , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org Subject: [PATCH v4 8/8] firmware: smccc: lfa: add sysfs ABI documentation Date: Fri, 18 Sep 2026 16:11:11 +0200 Message-ID: <20260918141112.2115555-9-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260918141112.2115555-1-andre.przywara@arm.com> References: <20260918141112.2115555-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" Document the sysfs attribute files as exported by the LFA driver. Signed-off-by: Andre Przywara --- Documentation/ABI/testing/sysfs-firmware-lfa | 106 +++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-firmware-lfa diff --git a/Documentation/ABI/testing/sysfs-firmware-lfa b/Documentation/A= BI/testing/sysfs-firmware-lfa new file mode 100644 index 0000000000000..afd349af9cf14 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-firmware-lfa @@ -0,0 +1,106 @@ +What: /sys/firmware/lfa +Date: May 2026 +Contact: Andre Przywara +Description: + The Arm Live Firmware Activation (LFA) spec describes an + interface between platform firmware (an agent) and any user + on the non-secure side (like the Linux kernel) to activate + pending firmware updates at runtime. The actual firmware + update would be provided independently (out-of-band, or + via any existing explicit firmware update mechanisms), then, + if supported, can be activated immediately, without rebooting + the system. + This directory contains one directory for each of the LFA + capable firmware images supported by the agent, identified by + its GUID. Within each directory, the same set of sysfs files + provides access to the properties and actions for this + particular firmware image. + +What: /sys/firmware/lfa//activate +Date: May 2026 +Contact: Andre Przywara +Description: + (WO) Write a boolean 'true' value to trigger the + activation of that firmware component. An update must be + pending for this to be successful. + +What: /sys/firmware/lfa//activation_capable +Date: May 2026 +Contact: Andre Przywara +Description: + (RO) A boolean value to show whether this firmware image is + capable of being activated. + +What: /sys/firmware/lfa//auto_activate +Date: May 2026 +Contact: Andre Przywara +Description: + (RW) Write a boolean 'true' value to allow a firmware image + to be automatically activated once a new firmware update + becomes available ("pending"). This depends on a working + notification scheme, for instance via an ACPI notification + or an interrupt to notify the kernel. + +What: /sys/firmware/lfa//cancel +Date: May 2026 +Contact: Andre Przywara +Description: + (WO) Write a boolean 'true' value to cancel a recently + triggered activation, if that has not finished yet. + +What: /sys/firmware/lfa//cpu_rendezvous +Date: May 2026 +Contact: Andre Przywara +Description: + (RO) A boolean value to show whether this image requires + a CPU rendezvous in firmware to be activated. If true, this + means all CPUs will enter the firmware during the activation + process, so will not be able to execute kernel code or + handle interrupts for a brief moment. + +What: /sys/firmware/lfa//current_version +Date: May 2026 +Contact: Andre Przywara +Description: + (RO) Shows the currently running version of that firmware + image, as reported by the agent (if supported). + +What: /sys/firmware/lfa//force_cpu_rendezvous +Date: May 2026 +Contact: Andre Przywara +Description: + (RW) Write a boolean 'true' value to force whether the kernel + will perform a CPU rendezvous during activation. This has no + effect if the firmware already *requires* a CPU rendezvous. + Firmware images might provide services to the kernel, at which + point is might be unsafe to continue running the kernel while + the activation is performed. Sending all CPUs into firmware + provides the safest option for the kernel. If you know for + certain that there are no services provides by that component + or the firmware component can handle requests during the + activation, you can write a 0 in here to avoid the brief + kernel downtime. + +What: /sys/firmware/lfa//may_reset_cpu +Date: May 2026 +Contact: Andre Przywara +Description: + (RO) A boolean value to show whether this firmware image + requires a CPU reset during the activation. + Currently this type of firmware image is not supported by + the kernel driver, as we cannot handle a CPU reset yet. + +What: /sys/firmware/lfa//name +Date: May 2026 +Contact: Andre Przywara +Description: + (RO) Shows a human readable name for that firmware image, if + known. Depends on the kernel driver knowing this particular + GUID, via a built-in list. + +What: /sys/firmware/lfa//pending_version +Date: May 2026 +Contact: Andre Przywara +Description: + (RO) Shows the version number of a pending firmware update + image, as reported by the agent (if supported). --=20 2.43.0