@@ -23,7 +23,7 @@ part --source rawcopy --size 144k --sourceparams="file=trusted-firmware-m/bl2_si
part --source rawcopy --size 320k --sourceparams="file=trusted-firmware-m/tfm_s_signed.bin" --align 4 --part-name="tfm_primary" --uuid 07F9616C-1233-439C-ACBA-72D75421BF70 --part-type 7FAD470E-5EC5-5C03-A2C1-4756B495DE61
# Rawcopy of the FIP binary
-part --source rawcopy --size 2 --sourceparams="file=signed_fip.bin" --align 4 --part-name="FIP_A" --uuid B9C7AC9D-40FF-4675-956B-EEF4DE9DF1C5 --part-type F1933675-5A8C-5B6D-9EF4-846739E89BC8
+part --source rawcopy --size 2560k --sourceparams="file=signed_fip.bin" --align 4 --part-name="FIP_A" --uuid B9C7AC9D-40FF-4675-956B-EEF4DE9DF1C5 --part-type F1933675-5A8C-5B6D-9EF4-846739E89BC8
# Rawcopy of kernel with initramfs
part --source rawcopy --size 12 --sourceparams="file=Image.gz-initramfs-${MACHINE}.bin" --align 4 --part-name="kernel_primary" --uuid BF7A6142-0662-47FD-9434-6A8811980816 --part-type F771AFF9-C7E9-5F99-9EDA-2369DD694F61
@@ -160,7 +160,7 @@ TFA_FIP_BINARY = "fip.bin"
TFA_BL2_RE_IMAGE_LOAD_ADDRESS = "0x62353000"
TFA_BL2_RE_SIGN_BIN_SIZE = "0x2d000"
TFA_FIP_RE_IMAGE_LOAD_ADDRESS = "0x68130000"
-TFA_FIP_RE_SIGN_BIN_SIZE = "0x00200000"
+TFA_FIP_RE_SIGN_BIN_SIZE = "0x00280000"
RE_LAYOUT_WRAPPER_VERSION = "0.0.7"
TFM_SIGN_PRIVATE_KEY = "${libdir}/tfm-scripts/root-EC-P256_1.pem"
RE_IMAGE_OFFSET = "0x1000"
new file mode 100644
@@ -0,0 +1,32 @@
+From 9edcdd272a7d2d872f7e04b3a9db5185fd24fd97 Mon Sep 17 00:00:00 2001
+From: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+Date: Tue, 28 Apr 2026 08:57:06 +0100
+Subject: [PATCH] platform: corstone1000: Increase FIP partition size
+
+Increase the FIP partition size from 2MB to 2.5MB in the
+Corstone-1000 flash layout.
+
+The previous size is insufficient for current FIP images,
+which may lead to overflow during image generation or
+deployment. Expanding the partition ensures adequate
+space for firmware components packaged in the FIP.
+
+Upstream-Status: Submitted [https://review.trustedfirmware.org/c/TF-M/trusted-firmware-m/+/50628]
+Signed-off-by: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+---
+ platform/ext/target/arm/corstone1000/partition/flash_layout.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/platform/ext/target/arm/corstone1000/partition/flash_layout.h b/platform/ext/target/arm/corstone1000/partition/flash_layout.h
+index e2219d80a..b7282beb2 100644
+--- a/platform/ext/target/arm/corstone1000/partition/flash_layout.h
++++ b/platform/ext/target/arm/corstone1000/partition/flash_layout.h
+@@ -139,7 +139,7 @@
+ #define TFM_PARTITION_SIZE (0x50000) /* 320 KB */
+ #define TFM_PARTITION_BANK_OFFSET (SE_BL2_PARTITION_SIZE)
+
+-#define FIP_PARTITION_SIZE (0x200000) /* 2 MB */
++#define FIP_PARTITION_SIZE (0x280000) /* 2.5 MB */
+ #define FIP_PARTITION_BANK_OFFSET (TFM_PARTITION_BANK_OFFSET + TFM_PARTITION_SIZE)
+
+ #define INITRAMFS_PARTITION_SIZE (0xC00000) /* 12 MB */
@@ -91,6 +91,7 @@ SRC_URI:append:corstone1000 = " \
file://0057-plat-cs1k-Add-flash-erase-protection.patch \
file://0058-plat-cs1k-Remove-unused-FWU-partitions-upon-version-.patch \
file://0059-plat-cs1k-Duplicate-old-images-in-FWU.patch \
+ file://0060-platform-corstone1000-Increase-FIP-partition-size.patch \
"
SRCREV_tfm-psa-adac:corstone1000 = "f2809ae231be33a1afcd7714f40756c67d846c88"
@@ -86,6 +86,24 @@ SRC_URI:append = " \
file://0040-configs-corstone1000-disable-EFI-debug-support.patch \
"
+# Add FF-A bus runtime support
+SRC_URI:append = " \
+ file://0041-efi_loader-add-runtime-memset-helper.patch \
+ file://0042-arm-ffa-add-FF-A-bus-runtime-support.patch \
+ file://0043-efi_loader-add-FF-A-runtime-support-in-EFI-variable-.patch \
+ file://0044-efi_loader-enable-EFI-runtime-SetVariable-GetVariabl.patch \
+ file://0045-efi_loader-move-runtime-GetVariable-helpers-to-efi_v.patch \
+ file://0046-corstone1000-enable-bootefi-selftest.patch \
+ file://0047-efi-selftest-add-runtime-variable-tests-with-non-vol.patch \
+ file://0048-test-dm-add-sandbox-FF-A-runtime-transport-tests.patch \
+ file://0049-sandbox-ffa-share-synthetic-partition-metadata-via-m.patch \
+ file://0050-doc-arm64-document-FF-A-runtime-path-for-EFI-variabl.patch \
+ file://0051-doc-bootefi-note-two-phase-runtime-variables-selftes.patch \
+ file://0052-efi_loader-align-FF-A-cache-maintenance-with-runtime.patch \
+ file://0053-efi_loader-fix-AllocatePages-overlap-status.patch \
+ file://0054-corstone1000-a320-enable-bootefi-selftest.patch \
+"
+
uboot_configure_config:append() {
openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=CRT/ -keyout ${B}/CRT.key -out ${S}/CRT.crt -nodes -days 365
}
new file mode 100644
@@ -0,0 +1,63 @@
+From 7e878429dc5f186ff945281513593c54beaf03f6 Mon Sep 17 00:00:00 2001
+From: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+Date: Fri, 10 Apr 2026 17:20:59 +0100
+Subject: [PATCH 41/53] efi_loader: add runtime memset helper
+
+Add efi_memset_runtime() for EFI runtime paths
+
+This keeps buffer initialization in runtime-resident code after
+ExitBootServices() and avoids relying on non-runtime helpers.
+
+Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/]
+Signed-off-by: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+---
+ include/efi_loader.h | 3 +++
+ lib/efi_loader/efi_runtime.c | 21 +++++++++++++++++++++
+ 2 files changed, 24 insertions(+)
+
+diff --git a/include/efi_loader.h b/include/efi_loader.h
+index 0340847c0b4..ec30042665b 100644
+--- a/include/efi_loader.h
++++ b/include/efi_loader.h
+@@ -1149,6 +1149,9 @@ struct pkcs7_message *efi_parse_pkcs7_header(const void *buf,
+ /* runtime implementation of memcpy() */
+ void efi_memcpy_runtime(void *dest, const void *src, size_t n);
+
++/* runtime implementation of memset */
++__efi_runtime void *efi_memset_runtime(void *dest, int c, size_t n);
++
+ /* commonly used helper functions */
+ u16 *efi_create_indexed_name(u16 *buffer, size_t buffer_size, const char *name,
+ unsigned int index);
+diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c
+index 35eb6a77766..e91b1583b6e 100644
+--- a/lib/efi_loader/efi_runtime.c
++++ b/lib/efi_loader/efi_runtime.c
+@@ -209,6 +209,27 @@ void __efi_runtime efi_memcpy_runtime(void *dest, const void *src, size_t n)
+ *d++ = *s++;
+ }
+
++/**
++ * efi_memset_runtime() - fill a memory region with a byte value
++ *
++ * At runtime memset() is not available.
++ *
++ * @dest: pointer to the block of memory to fill.
++ * @c: value to be set. The value is passed as an int, but the
++ * function fills the block of memory using the u8 conversion of this value.
++ * @n: number of bytes to be set to the value.
++ * Return: dest is returned
++ */
++__efi_runtime void *efi_memset_runtime(void *dest, int c, size_t n)
++{
++ u8 *d = dest;
++
++ for (; n; --n)
++ *d++ = c;
++
++ return dest;
++}
++
+ /**
+ * efi_update_table_header_crc32() - Update crc32 in table header
+ *
new file mode 100644
@@ -0,0 +1,975 @@
+From 2f4000043db3a5658e946a66f1e2b8b8c48dffc7 Mon Sep 17 00:00:00 2001
+From: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+Date: Thu, 20 Nov 2025 21:56:22 +0000
+Subject: [PATCH 42/53] arm-ffa: add FF-A bus runtime support
+
+Enable FF-A runtime transport for EFI services
+
+This patch introduces the FF-A runtime infrastructure that enables
+U-Boot to use the Arm Firmware Framework for Arm A-profile (FF-A)
+transport layer after ExitBootServices().
+The runtime transport provides persistent, runtime-safe
+implementations of key FF-A functions used by EFI runtime services.
+
+Summary of key changes:
+-----------------------
+ - Add drivers/firmware/arm-ffa/arm-ffa-runtime.c implementing FF-A
+ runtime operations.
+ - Introduce include/arm_ffa_runtime.h exporting FF-A runtime
+ functions and data structures.
+ - Move FF-A error mapping into runtime context.
+ - Introduce invoke_ffa_fn_runtime() as runtime safe SMC wrapper.
+ - Add runtime SMC wrapper for sandbox emulation.
+ - Add ARM_FFA_RT_MODE Kconfig to enable runtime support.
+ - Register ExitBootServices hook for runtime context.
+ - Copy ffa_priv runtime fields into resident storage at
+ ExitBootServices.
+ - Extend Makefile to build arm-ffa-runtime.c.
+
+All runtime functions and global data are tagged with __efi_runtime or
+__efi_runtime_data to ensure persistence after ExitBootServices().
+
+Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/]
+Signed-off-by: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+---
+ drivers/firmware/arm-ffa/Kconfig | 11 +
+ drivers/firmware/arm-ffa/Makefile | 4 +-
+ drivers/firmware/arm-ffa/arm-ffa-runtime.c | 293 +++++++++++++++++++++
+ drivers/firmware/arm-ffa/arm-ffa-uclass.c | 113 ++------
+ drivers/firmware/arm-ffa/arm-ffa.c | 16 +-
+ drivers/firmware/arm-ffa/ffa-emul-uclass.c | 12 +
+ include/arm_ffa.h | 16 +-
+ include/arm_ffa_priv.h | 24 +-
+ include/arm_ffa_runtime.h | 189 +++++++++++++
+ test/dm/ffa.c | 6 +-
+ 10 files changed, 564 insertions(+), 120 deletions(-)
+ create mode 100644 drivers/firmware/arm-ffa/arm-ffa-runtime.c
+ create mode 100644 include/arm_ffa_runtime.h
+
+diff --git a/drivers/firmware/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig
+index d75f8b53fd8..1be9adeb1db 100644
+--- a/drivers/firmware/arm-ffa/Kconfig
++++ b/drivers/firmware/arm-ffa/Kconfig
+@@ -17,6 +17,9 @@ config ARM_FFA_TRANSPORT
+ The FF-A support in U-Boot is based on FF-A specification v1.0 and uses SMC32
+ calling convention.
+
++ The FF-A bus also provides a runtime layer to keep a minimal set of FF-A
++ operations available after ExitBootServices().
++
+ FF-A specification:
+
+ https://developer.arm.com/documentation/den0077/a/?lang=en
+@@ -40,3 +43,11 @@ config ARM_FFA_TRANSPORT
+ Secure World (sandbox_ffa.c).
+
+ For more details about the FF-A support, please refer to doc/arch/arm64.ffa.rst
++
++config ARM_FFA_RT_MODE
++ bool "Enable FF-A runtime support"
++ depends on ARM_FFA_TRANSPORT && EFI_LOADER
++ default y
++ help
++ Enable the FF-A runtime layer, keeping a minimal set of FF-A
++ operations available after ExitBootServices().
+diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile
+index 318123a7f42..9deb59ba640 100644
+--- a/drivers/firmware/arm-ffa/Makefile
++++ b/drivers/firmware/arm-ffa/Makefile
+@@ -1,12 +1,12 @@
+ # SPDX-License-Identifier: GPL-2.0+
+ #
+-# Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
++# Copyright 2022-2023, 2026 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ #
+ # Authors:
+ # Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+
+ # build the generic FF-A methods
+-obj-y += arm-ffa-uclass.o
++obj-y += arm-ffa-uclass.o arm-ffa-runtime.o
+ ifeq ($(CONFIG_SANDBOX),y)
+ # build the FF-A sandbox emulator and driver
+ obj-y += ffa-emul-uclass.o sandbox_ffa.o
+diff --git a/drivers/firmware/arm-ffa/arm-ffa-runtime.c b/drivers/firmware/arm-ffa/arm-ffa-runtime.c
+new file mode 100644
+index 00000000000..82c75da7d4b
+--- /dev/null
++++ b/drivers/firmware/arm-ffa/arm-ffa-runtime.c
+@@ -0,0 +1,293 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright 2026 Arm Limited and/or its affiliates <open-source-office@arm.com>
++ *
++ * Authors:
++ * Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
++ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
++ */
++
++#include <arm_ffa_runtime.h>
++#include <arm_ffa_priv.h>
++#include <log.h>
++#include <asm/global_data.h>
++#include <linux/errno.h>
++#include <linux/types.h>
++
++DECLARE_GLOBAL_DATA_PTR;
++
++/* Error mapping declarations */
++
++int __ffa_runtime_data ffa_to_std_errmap[MAX_NUMBER_FFA_ERR] = {
++ [NOT_SUPPORTED] = -EOPNOTSUPP,
++ [INVALID_PARAMETERS] = -EINVAL,
++ [NO_MEMORY] = -ENOMEM,
++ [BUSY] = -EBUSY,
++ [INTERRUPTED] = -EINTR,
++ [DENIED] = -EACCES,
++ [RETRY] = -EAGAIN,
++ [ABORTED] = -ECANCELED,
++};
++
++static __ffa_runtime_data struct ffa_priv_runtime ffa_priv_rt = {0};
++
++/* Arm FF-A driver runtime operations */
++static const __ffa_runtime_data struct ffa_bus_ops_runtime ffa_ops_rt = {
++ .sync_send_receive = ffa_msg_send_direct_req_hdlr_runtime,
++};
++
++#define ffa_get_ops_runtime() (&ffa_ops_rt)
++#define ffa_get_priv_runtime() (&ffa_priv_rt)
++
++#if CONFIG_IS_ENABLED(ARM_FFA_RT_MODE)
++static void EFIAPI ffa_rt_exit_boot_services_notify(struct efi_event *event,
++ void *context)
++{
++ struct ffa_priv *priv = context;
++
++ if (priv)
++ ffa_copy_runtime_priv(&priv->rt);
++ else
++ log_err("FF-A: Entering RT mode without FF-A runtime data information\n");
++
++ ffa_enable_runtime_context();
++}
++
++/**
++ * ffa_setup_efi_exit_boot_services_event() - register ExitBootServices event
++ * @priv: pointer to the FF-A private data
++ *
++ * Register an EFI event that enables the FF-A runtime context when
++ * ExitBootServices() is invoked.
++ *
++ * Return: 0 on success, -EPERM on failure to create the event.
++ */
++int ffa_setup_efi_exit_boot_services_event(struct ffa_priv *priv)
++{
++ efi_status_t efi_ret;
++ struct efi_event *evt = NULL;
++
++ efi_ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
++ ffa_rt_exit_boot_services_notify, priv,
++ &efi_guid_event_group_exit_boot_services,
++ &evt);
++ if (efi_ret != EFI_SUCCESS) {
++ log_err("FF-A: cannot install ExitBootServices event %p, err (%lu)\n",
++ evt, efi_ret);
++ return -EPERM;
++ }
++
++ return 0;
++}
++#endif
++
++/**
++ * ffa_copy_runtime_priv() - copy runtime data into resident storage
++ * @priv: pointer to the runtime private data
++ *
++ * Copy boot-time runtime data into the resident runtime storage to be used
++ * after ExitBootServices().
++ */
++void ffa_copy_runtime_priv(const struct ffa_priv_runtime *priv)
++{
++ struct ffa_priv_runtime *priv_rt = ffa_get_priv_runtime();
++
++ if (priv)
++ *priv_rt = *priv;
++}
++
++/**
++ * ffa_enable_runtime_context() - Enable FF-A runtime context
++ *
++ * This function marks the FF-A runtime environment as ready for use by
++ * EFI runtime services. It is called when ExitBootServices() is invoked,
++ * after the FF-A bus device has successfully probed and U-Boot’s FF-A
++ * endpoint ID has been discovered and stored in the runtime private data
++ * structure.
++ *
++ * The FF-A runtime flag allows the EFI runtime layer to verify that the
++ * FF-A transport was initialized during the boot phase and that all
++ * runtime-safe FF-A operations may now be used after ExitBootServices().
++ *
++ */
++void ffa_enable_runtime_context(void)
++{
++ struct ffa_priv_runtime *priv_rt = ffa_get_priv_runtime();
++
++ priv_rt->use_ffa_runtime = true;
++}
++
++/**
++ * ffa_get_status_runtime_context() - Query FF-A runtime readiness
++ *
++ * This helper returns whether the FF-A runtime environment has been
++ * enabled during the boot phase. Runtime FF-A operations must check this
++ * flag before attempting any FF-A access, as the U-Boot driver model
++ * (DM/uclass) is no longer available after ExitBootServices().
++ *
++ * The runtime context becomes enabled when ffa_enable_runtime_context()
++ * is called, typically after the FF-A bus device has probed and the
++ * endpoint ID has been discovered and stored in the runtime private
++ * data structure.
++ *
++ * Return:
++ * true FF-A runtime support is ready
++ * false FF-A runtime environment is not ready
++ */
++bool __ffa_runtime ffa_get_status_runtime_context(void)
++{
++ struct ffa_priv_runtime *priv_rt = ffa_get_priv_runtime();
++
++ return priv_rt->use_ffa_runtime;
++}
++
++/**
++ * ffa_to_std_errno() - convert FF-A error code to standard error code
++ * @ffa_errno: Error code returned by the FF-A ABI
++ *
++ * Map the given FF-A error code as specified
++ * by the spec to a u-boot standard error code.
++ *
++ * Return:
++ *
++ * The standard error code on success. . Otherwise, failure
++ */
++int __ffa_runtime ffa_to_std_errno(int ffa_errno)
++{
++ int err_idx = -ffa_errno;
++
++ /* Map the FF-A error code to the standard u-boot error code */
++ if (err_idx > 0 && err_idx < MAX_NUMBER_FFA_ERR)
++ return ffa_to_std_errmap[err_idx];
++ return -EINVAL;
++}
++
++/**
++ * ffa_invoke_msg_send_direct_req() - Invokes FFA_MSG_SEND_DIRECT_{REQ,RESP}
++ * @endpoint_id: u-boot endpoint id
++ * @dst_part_id: destination partition ID
++ * @msg: pointer to the message data preallocated by the client (in/out)
++ * @is_smc64: select 64-bit or 32-bit FF-A ABI
++ *
++ * This function invokes FFA_MSG_SEND_DIRECT_{REQ,RESP} FF-A functions.
++ *
++ * FFA_MSG_SEND_DIRECT_REQ is used to send the data to the secure partition.
++ * The response from the secure partition is handled by reading the
++ * FFA_MSG_SEND_DIRECT_RESP arguments.
++ *
++ * The maximum size of the data that can be exchanged is 40 bytes which is
++ * sizeof(struct ffa_send_direct_data) as defined by the FF-A specification 1.0
++ * in the section relevant to FFA_MSG_SEND_DIRECT_{REQ,RESP}
++ *
++ * Return:
++ *
++ * 0 on success. Otherwise, error on failure
++ */
++int __ffa_runtime ffa_invoke_msg_send_direct_req(u16 endpoint_id, u16 dst_part_id,
++ struct ffa_send_direct_data *msg, bool is_smc64)
++{
++ int ffa_errno;
++ u64 req_mode;
++ ffa_value_t ffa_args_rt;
++ ffa_value_t ffa_res_rt;
++
++ if (is_smc64) {
++ req_mode = FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ);
++ } else {
++ req_mode = FFA_SMC_32(FFA_MSG_SEND_DIRECT_REQ);
++ }
++
++ efi_memset_runtime(&ffa_args_rt, 0, sizeof(ffa_args_rt));
++ efi_memset_runtime(&ffa_res_rt, 0, sizeof(ffa_res_rt));
++ ffa_args_rt.a0 = req_mode;
++ ffa_args_rt.a1 = PREP_SELF_ENDPOINT_ID(endpoint_id) |
++ PREP_PART_ENDPOINT_ID(dst_part_id);
++ ffa_args_rt.a2 = 0;
++ ffa_args_rt.a3 = msg->data0;
++ ffa_args_rt.a4 = msg->data1;
++ ffa_args_rt.a5 = msg->data2;
++ ffa_args_rt.a6 = msg->data3;
++ ffa_args_rt.a7 = msg->data4;
++
++ invoke_ffa_fn_runtime(&ffa_args_rt, &ffa_res_rt);
++
++ while (ffa_res_rt.a0 == FFA_SMC_32(FFA_INTERRUPT) ||
++ ffa_res_rt.a0 == FFA_SMC_64(FFA_INTERRUPT)) {
++ efi_memset_runtime(&ffa_args_rt, 0, sizeof(ffa_args_rt));
++ ffa_args_rt.a0 = (ffa_res_rt.a0 == FFA_SMC_64(FFA_INTERRUPT)) ?
++ FFA_SMC_64(FFA_RUN) : FFA_SMC_32(FFA_RUN);
++ ffa_args_rt.a1 = ffa_res_rt.a1;
++
++ invoke_ffa_fn_runtime(&ffa_args_rt, &ffa_res_rt);
++ }
++ if (ffa_res_rt.a0 == FFA_SMC_32(FFA_SUCCESS) ||
++ ffa_res_rt.a0 == FFA_SMC_64(FFA_SUCCESS)) {
++ /* Message sent with no response */
++ return 0;
++ }
++
++ if (ffa_res_rt.a0 == FFA_SMC_32(FFA_MSG_SEND_DIRECT_RESP) ||
++ ffa_res_rt.a0 == FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP)) {
++ /* Message sent with response extract the return data */
++ msg->data0 = ffa_res_rt.a3;
++ msg->data1 = ffa_res_rt.a4;
++ msg->data2 = ffa_res_rt.a5;
++ msg->data3 = ffa_res_rt.a6;
++ msg->data4 = ffa_res_rt.a7;
++ return 0;
++ }
++
++ ffa_errno = ffa_res_rt.a2;
++ return ffa_to_std_errno(ffa_errno);
++}
++
++/**
++ * ffa_msg_send_direct_req_hdlr_runtime() - Runtime implementation of
++ * FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function
++ * @dst_part_id: destination partition ID
++ * @msg: pointer to the message data preallocated by the client (in/out)
++ * @is_smc64: select 64-bit or 32-bit FF-A ABI
++ *
++ * This function calls the ffa_invoke_msg_send_direct_req() function which
++ * invokes FFA_MSG_SEND_DIRECT_{REQ,RESP} FF-A functions.
++ *
++ * Return:
++ *
++ * 0 on success. Otherwise, failure
++ */
++int __ffa_runtime ffa_msg_send_direct_req_hdlr_runtime(u16 dst_part_id,
++ struct ffa_send_direct_data *msg,
++ bool is_smc64)
++{
++ struct ffa_priv_runtime *priv_rt = ffa_get_priv_runtime();
++
++ return ffa_invoke_msg_send_direct_req(priv_rt->id, dst_part_id, msg, is_smc64);
++}
++
++/**
++ * ffa_sync_send_receive_runtime() - Runtime implementation of
++ * ffa_sync_send_receive()
++ * @dst_part_id: destination partition ID
++ * @msg: pointer to the message data preallocated by the client (in/out)
++ * @is_smc64: select 64-bit or 32-bit FF-A ABI
++ *
++ * Please see ffa_msg_send_direct_req_hdlr_runtime() description for more details.
++ *
++ * Return:
++ *
++ * 0 on success. Otherwise, failure
++ */
++int __ffa_runtime ffa_sync_send_receive_runtime(u16 dst_part_id,
++ struct ffa_send_direct_data *msg,
++ bool is_smc64)
++{
++ const struct ffa_bus_ops_runtime *ops_rt = ffa_get_ops_runtime();
++
++ if (!ffa_get_status_runtime_context())
++ return -EPERM;
++
++ if (!ops_rt->sync_send_receive)
++ return -ENOSYS;
++
++ return ops_rt->sync_send_receive(dst_part_id, msg, is_smc64);
++}
+diff --git a/drivers/firmware/arm-ffa/arm-ffa-uclass.c b/drivers/firmware/arm-ffa/arm-ffa-uclass.c
+index 597b4e994b4..f769e0e9b05 100644
+--- a/drivers/firmware/arm-ffa/arm-ffa-uclass.c
++++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c
+@@ -1,12 +1,13 @@
+ // SPDX-License-Identifier: GPL-2.0+
+ /*
+- * Copyright 2022-2023, 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
++ * Copyright 2022-2023, 2026 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ *
+ * Authors:
+ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+ */
+ #include <arm_ffa.h>
+ #include <arm_ffa_priv.h>
++#include <arm_ffa_runtime.h>
+ #include <dm.h>
+ #include <log.h>
+ #include <malloc.h>
+@@ -21,19 +22,6 @@
+
+ DECLARE_GLOBAL_DATA_PTR;
+
+-/* Error mapping declarations */
+-
+-int ffa_to_std_errmap[MAX_NUMBER_FFA_ERR] = {
+- [NOT_SUPPORTED] = -EOPNOTSUPP,
+- [INVALID_PARAMETERS] = -EINVAL,
+- [NO_MEMORY] = -ENOMEM,
+- [BUSY] = -EBUSY,
+- [INTERRUPTED] = -EINTR,
+- [DENIED] = -EACCES,
+- [RETRY] = -EAGAIN,
+- [ABORTED] = -ECANCELED,
+-};
+-
+ static struct ffa_abi_errmap err_msg_map[FFA_ERRMAP_COUNT] = {
+ [FFA_ID_TO_ERRMAP_ID(FFA_VERSION)] = {
+ {
+@@ -123,27 +111,6 @@ static struct ffa_abi_errmap err_msg_map[FFA_ERRMAP_COUNT] = {
+ },
+ };
+
+-/**
+- * ffa_to_std_errno() - convert FF-A error code to standard error code
+- * @ffa_errno: Error code returned by the FF-A ABI
+- *
+- * Map the given FF-A error code as specified
+- * by the spec to a u-boot standard error code.
+- *
+- * Return:
+- *
+- * The standard error code on success. . Otherwise, failure
+- */
+-static int ffa_to_std_errno(int ffa_errno)
+-{
+- int err_idx = -ffa_errno;
+-
+- /* Map the FF-A error code to the standard u-boot error code */
+- if (err_idx > 0 && err_idx < MAX_NUMBER_FFA_ERR)
+- return ffa_to_std_errmap[err_idx];
+- return -EINVAL;
+-}
+-
+ /**
+ * ffa_print_error_log() - print the error log corresponding to the selected FF-A ABI
+ * @ffa_id: FF-A ABI ID
+@@ -233,7 +200,7 @@ int ffa_get_version_hdlr(struct udevice *dev)
+ if (dev) {
+ uc_priv = dev_get_uclass_priv(dev);
+ if (uc_priv)
+- uc_priv->fwk_version = res.a0;
++ uc_priv->rt.fwk_version = res.a0;
+ }
+
+ return 0;
+@@ -267,8 +234,8 @@ static int ffa_get_endpoint_id(struct udevice *dev)
+ }, &res);
+
+ if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
+- uc_priv->id = GET_SELF_ENDPOINT_ID((u32)res.a2);
+- log_debug("FF-A endpoint ID is %u\n", uc_priv->id);
++ uc_priv->rt.id = GET_SELF_ENDPOINT_ID((u32)res.a2);
++ log_debug("FF-A endpoint ID is %u\n", uc_priv->rt.id);
+
+ return 0;
+ }
+@@ -490,7 +457,7 @@ int ffa_unmap_rxtx_buffers_hdlr(struct udevice *dev)
+
+ invoke_ffa_fn((ffa_value_t){
+ .a0 = FFA_SMC_32(FFA_RXTX_UNMAP),
+- .a1 = PREP_SELF_ENDPOINT_ID(uc_priv->id),
++ .a1 = PREP_SELF_ENDPOINT_ID(uc_priv->rt.id),
+ }, &res);
+
+ if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
+@@ -880,16 +847,8 @@ static int ffa_cache_partitions_info(struct udevice *dev)
+ * @msg: pointer to the message data preallocated by the client (in/out)
+ * @is_smc64: select 64-bit or 32-bit FF-A ABI
+ *
+- * Implement FFA_MSG_SEND_DIRECT_{REQ,RESP}
+- * FF-A functions.
+- *
+- * FFA_MSG_SEND_DIRECT_REQ is used to send the data to the secure partition.
+- * The response from the secure partition is handled by reading the
+- * FFA_MSG_SEND_DIRECT_RESP arguments.
+- *
+- * The maximum size of the data that can be exchanged is 40 bytes which is
+- * sizeof(struct ffa_send_direct_data) as defined by the FF-A specification 1.0
+- * in the section relevant to FFA_MSG_SEND_DIRECT_{REQ,RESP}
++ * This function calls the ffa_invoke_msg_send_direct_req() function which
++ * invokes FFA_MSG_SEND_DIRECT_{REQ,RESP} FF-A functions.
+ *
+ * Return:
+ *
+@@ -898,9 +857,6 @@ static int ffa_cache_partitions_info(struct udevice *dev)
+ int ffa_msg_send_direct_req_hdlr(struct udevice *dev, u16 dst_part_id,
+ struct ffa_send_direct_data *msg, bool is_smc64)
+ {
+- ffa_value_t res = {0};
+- int ffa_errno;
+- u64 req_mode, resp_mode;
+ struct ffa_priv *uc_priv;
+
+ uc_priv = dev_get_uclass_priv(dev);
+@@ -909,50 +865,7 @@ int ffa_msg_send_direct_req_hdlr(struct udevice *dev, u16 dst_part_id,
+ if (!uc_priv->partitions.count || !uc_priv->partitions.descs)
+ return -ENODEV;
+
+- if (is_smc64) {
+- req_mode = FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ);
+- resp_mode = FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP);
+- } else {
+- req_mode = FFA_SMC_32(FFA_MSG_SEND_DIRECT_REQ);
+- resp_mode = FFA_SMC_32(FFA_MSG_SEND_DIRECT_RESP);
+- }
+-
+- invoke_ffa_fn((ffa_value_t){
+- .a0 = req_mode,
+- .a1 = PREP_SELF_ENDPOINT_ID(uc_priv->id) |
+- PREP_PART_ENDPOINT_ID(dst_part_id),
+- .a2 = 0,
+- .a3 = msg->data0,
+- .a4 = msg->data1,
+- .a5 = msg->data2,
+- .a6 = msg->data3,
+- .a7 = msg->data4,
+- }, &res);
+-
+- while (res.a0 == FFA_SMC_32(FFA_INTERRUPT))
+- invoke_ffa_fn((ffa_value_t){
+- .a0 = FFA_SMC_32(FFA_RUN),
+- .a1 = res.a1,
+- }, &res);
+-
+- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
+- /* Message sent with no response */
+- return 0;
+- }
+-
+- if (res.a0 == resp_mode) {
+- /* Message sent with response extract the return data */
+- msg->data0 = res.a3;
+- msg->data1 = res.a4;
+- msg->data2 = res.a5;
+- msg->data3 = res.a6;
+- msg->data4 = res.a7;
+-
+- return 0;
+- }
+-
+- ffa_errno = res.a2;
+- return ffa_to_std_errno(ffa_errno);
++return ffa_invoke_msg_send_direct_req(uc_priv->rt.id, dst_part_id, msg, is_smc64);
+ }
+
+ /**
+@@ -1008,7 +921,7 @@ static int ffa_setup_and_transmit(struct udevice *dev, u32 func_id,
+
+ mem_region->tag = args->tag;
+ mem_region->flags = args->flags;
+- mem_region->sender_id = uc_priv->id;
++ mem_region->sender_id = uc_priv->rt.id;
+
+ /*
+ * These attributes are only valid for FFA_MEM_SHARE.
+@@ -1322,6 +1235,7 @@ int ffa_memory_reclaim(struct udevice *dev, u64 g_handle, u32 flags)
+ static int ffa_do_probe(struct udevice *dev)
+ {
+ int ret;
++ struct ffa_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ ret = ffa_get_version_hdlr(dev);
+ if (ret)
+@@ -1345,6 +1259,11 @@ static int ffa_do_probe(struct udevice *dev)
+ return ret;
+ }
+
++ if (IS_ENABLED(CONFIG_ARM_FFA_RT_MODE)) {
++ ret = ffa_setup_efi_exit_boot_services_event(uc_priv);
++ if (ret)
++ return ret;
++ }
+ return 0;
+ }
+
+diff --git a/drivers/firmware/arm-ffa/arm-ffa.c b/drivers/firmware/arm-ffa/arm-ffa.c
+index de36f5647d2..0d451c0ddc7 100644
+--- a/drivers/firmware/arm-ffa/arm-ffa.c
++++ b/drivers/firmware/arm-ffa/arm-ffa.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0+
+ /*
+- * Copyright 2022-2023, 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
++ * Copyright 2022-2023, 2026 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ *
+ * Authors:
+ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+@@ -8,6 +8,7 @@
+
+ #include <arm_ffa.h>
+ #include <arm_ffa_priv.h>
++#include <arm_ffa_runtime.h>
+ #include <dm.h>
+ #include <log.h>
+ #include <asm/global_data.h>
+@@ -28,6 +29,19 @@ void invoke_ffa_fn(ffa_value_t args, ffa_value_t *res)
+ arm_smccc_1_2_smc(&args, res);
+ }
+
++/**
++ * invoke_ffa_fn_runtime() - Runtime-safe SMC wrapper
++ * @args: FF-A ABI arguments to be copied to Xn registers
++ * @res: FF-A ABI return values copied from Xn registers
++ *
++ * Calls the SMCCC SMC 1.2 helper from EFI runtime context. This wrapper
++ * is marked __ffa_runtime so it remains available after ExitBootServices().
++ */
++void __ffa_runtime invoke_ffa_fn_runtime(ffa_value_t *args, ffa_value_t *res)
++{
++ arm_smccc_1_2_smc(args, res);
++}
++
+ /**
+ * arm_ffa_discover() - perform FF-A discovery
+ * @dev: The Arm FF-A bus device (arm_ffa)
+diff --git a/drivers/firmware/arm-ffa/ffa-emul-uclass.c b/drivers/firmware/arm-ffa/ffa-emul-uclass.c
+index a630392b6d1..531ecb12f3a 100644
+--- a/drivers/firmware/arm-ffa/ffa-emul-uclass.c
++++ b/drivers/firmware/arm-ffa/ffa-emul-uclass.c
+@@ -758,6 +758,18 @@ void invoke_ffa_fn(ffa_value_t args, ffa_value_t *res)
+ sandbox_arm_ffa_smccc_smc(&args, res);
+ }
+
++/**
++ * invoke_ffa_fn_runtime() - Runtime-safe SMC wrapper
++ * @args: FF-A ABI arguments to be copied to Xn registers
++ * @res: FF-A ABI return data to be copied from Xn registers
++ *
++ * Calls the emulated SMC call.
++ */
++void invoke_ffa_fn_runtime(ffa_value_t *args, ffa_value_t *res)
++{
++ sandbox_arm_ffa_smccc_smc(args, res);
++}
++
+ /**
+ * ffa_emul_find() - Find the FF-A emulator
+ * @dev: the sandbox FF-A device (sandbox-arm-ffa)
+diff --git a/include/arm_ffa.h b/include/arm_ffa.h
+index 1229e6e3d02..f19a9759e55 100644
+--- a/include/arm_ffa.h
++++ b/include/arm_ffa.h
+@@ -1,6 +1,6 @@
+ /* SPDX-License-Identifier: GPL-2.0+ */
+ /*
+- * Copyright 2022-2023, 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
++ * Copyright 2022-2023, 2025-2026 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ *
+ * Authors:
+ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+@@ -195,21 +195,13 @@ int ffa_sync_send_receive(struct udevice *dev, u16 dst_part_id,
+
+ /**
+ * ffa_msg_send_direct_req_hdlr() - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function
+- * @dev: The arm_ffa bus device
++ * @dev: The FF-A bus device
+ * @dst_part_id: destination partition ID
+ * @msg: pointer to the message data preallocated by the client (in/out)
+ * @is_smc64: select 64-bit or 32-bit FF-A ABI
+ *
+- * This function implements FFA_MSG_SEND_DIRECT_{REQ,RESP}
+- * FF-A functions.
+- *
+- * FFA_MSG_SEND_DIRECT_REQ is used to send the data to the secure partition.
+- * The response from the secure partition is handled by reading the
+- * FFA_MSG_SEND_DIRECT_RESP arguments.
+- *
+- * The maximum size of the data that can be exchanged is 40 bytes which is
+- * sizeof(struct ffa_send_direct_data) as defined by the FF-A specification 1.0
+- * in the section relevant to FFA_MSG_SEND_DIRECT_{REQ,RESP}
++ * This function calls the ffa_invoke_msg_send_direct_req() function which
++ * invokes FFA_MSG_SEND_DIRECT_{REQ,RESP} FF-A functions.
+ *
+ * Return:
+ *
+diff --git a/include/arm_ffa_priv.h b/include/arm_ffa_priv.h
+index 54196199ce3..73eba942e3b 100644
+--- a/include/arm_ffa_priv.h
++++ b/include/arm_ffa_priv.h
+@@ -1,6 +1,6 @@
+ /* SPDX-License-Identifier: GPL-2.0+ */
+ /*
+- * Copyright 2022-2023, 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
++ * Copyright 2022-2023, 2025-2026 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ *
+ * Authors:
+ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+@@ -202,11 +202,26 @@ struct ffa_partitions {
+ };
+
+ /**
+- * struct ffa_priv - the driver private data structure
++ * struct ffa_priv_runtime - the driver's private runtime data structure
+ *
++ * @use_ffa_runtime: Whether FF-A runtime is available to use or not
+ * @fwk_version: FF-A framework version
+- * @emul: FF-A sandbox emulator
+ * @id: u-boot endpoint ID
++ *
++ * The device private runtime data structure containing all the
++ * data read from secure world.
++ */
++struct ffa_priv_runtime {
++ bool use_ffa_runtime;
++ u32 fwk_version;
++ u16 id;
++};
++
++/**
++ * struct ffa_priv - the driver private data structure
++ *
++ * @rt: Runtime data captured at boot time
++ * @emul: FF-A sandbox emulator
+ * @partitions: The partitions descriptors structure
+ * @pair: The RX/TX buffers pair
+ *
+@@ -214,9 +229,8 @@ struct ffa_partitions {
+ * data read from secure world.
+ */
+ struct ffa_priv {
+- u32 fwk_version;
++ struct ffa_priv_runtime rt;
+ struct udevice *emul;
+- u16 id;
+ struct ffa_partitions partitions;
+ struct ffa_rxtxpair pair;
+ };
+diff --git a/include/arm_ffa_runtime.h b/include/arm_ffa_runtime.h
+new file mode 100644
+index 00000000000..b6ddda68f4b
+--- /dev/null
++++ b/include/arm_ffa_runtime.h
+@@ -0,0 +1,189 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/*
++ * Copyright 2026 Arm Limited and/or its affiliates <open-source-office@arm.com>
++ *
++ * Authors:
++ * Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
++ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
++ */
++
++#ifndef __ARM_FFA_RUNTIME_H
++#define __ARM_FFA_RUNTIME_H
++
++#include <linux/types.h>
++#include <arm_ffa.h>
++#include <arm_ffa_priv.h>
++#include <efi_loader.h>
++
++/**
++ * __ffa_runtime - controls whether functions are
++ * available after calling the EFI ExitBootServices service.
++ * Functions tagged with these keywords are resident (available at boot time and
++ * at runtime)
++ */
++#if CONFIG_IS_ENABLED(ARM_FFA_RT_MODE)
++#define __ffa_runtime_data __efi_runtime_data
++#define __ffa_runtime __efi_runtime
++#else
++#define __ffa_runtime_data
++#define __ffa_runtime
++#endif
++
++/**
++ * invoke_ffa_fn_runtime() - Runtime-safe SMC wrapper
++ * @args: FF-A ABI arguments to be copied to Xn registers
++ * @res: FF-A ABI return values copied from Xn registers
++ *
++ * Calls low level SMC implementation. This wrapper
++ * is marked __ffa_runtime so it remains available after ExitBootServices().
++ */
++void __ffa_runtime invoke_ffa_fn_runtime(ffa_value_t *args, ffa_value_t *res);
++
++/**
++ * struct ffa_bus_ops_runtime - Operations for FF-A runtime
++ * @sync_send_receive: callback for the FFA_MSG_SEND_DIRECT_REQ
++ *
++ * The data structure providing all the runtime operations supported by the driver.
++ * This structure is an EFI runtime resident.
++ */
++struct ffa_bus_ops_runtime {
++ int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg,
++ bool is_smc64);
++};
++
++/**
++ * ffa_enable_runtime_context() - Enable FF-A runtime context
++ *
++ * This function marks the FF-A runtime environment as ready for use by
++ * EFI runtime services. It is called when ExitBootServices() is invoked,
++ * after the FF-A bus device has successfully probed and U-Boot’s FF-A
++ * endpoint ID has been discovered and stored in the runtime private data
++ * structure.
++ *
++ * The FF-A runtime flag allows the EFI runtime layer to verify that the
++ * FF-A transport was initialized during the boot phase and that all
++ * runtime-safe FF-A operations may now be used after ExitBootServices().
++ *
++ */
++void ffa_enable_runtime_context(void);
++
++/**
++ * ffa_copy_runtime_priv() - copy runtime data into resident storage
++ * @priv: pointer to the runtime private data
++ *
++ * Copy boot-time runtime data into the resident runtime storage to be used
++ * after ExitBootServices().
++ */
++void ffa_copy_runtime_priv(const struct ffa_priv_runtime *priv);
++
++/**
++ * ffa_setup_efi_exit_boot_services_event() - register ExitBootServices event
++ * @priv: pointer to the FF-A private data
++ *
++ * Register an EFI event that enables the FF-A runtime context when
++ * ExitBootServices() is invoked.
++ *
++ * Return: 0 on success, -EPERM on failure to create the event.
++ */
++#if CONFIG_IS_ENABLED(ARM_FFA_RT_MODE)
++int ffa_setup_efi_exit_boot_services_event(struct ffa_priv *priv);
++#else
++static inline int ffa_setup_efi_exit_boot_services_event(struct ffa_priv *priv)
++{
++ return 0;
++}
++#endif
++
++/**
++ * ffa_get_status_runtime_context() - Query FF-A runtime readiness
++ *
++ * This helper returns whether the FF-A runtime environment has been
++ * enabled during the boot phase. Runtime FF-A operations must check this
++ * flag before attempting any FF-A access, as the U-Boot driver model
++ * (DM/uclass) is no longer available after ExitBootServices().
++ *
++ * The runtime context becomes enabled when ffa_enable_runtime_context()
++ * is called, typically after the FF-A bus device has probed and the
++ * endpoint ID has been discovered and stored in the runtime private
++ * data structure.
++ *
++ * Return:
++ * true FF-A runtime support is ready
++ * false FF-A runtime environment is not ready
++ */
++bool __ffa_runtime ffa_get_status_runtime_context(void);
++
++/**
++ * ffa_to_std_errno() - convert FF-A error code to standard error code
++ * @ffa_errno: Error code returned by the FF-A ABI
++ *
++ * Map the given FF-A error code as specified
++ * by the spec to a u-boot standard error code.
++ *
++ * Return:
++ *
++ * The standard error code on success. . Otherwise, failure
++ */
++int __ffa_runtime ffa_to_std_errno(int ffa_errno);
++
++/**
++ * ffa_sync_send_receive_runtime() - Runtime implementation of
++ * ffa_sync_send_receive()
++ * @dst_part_id: destination partition ID
++ * @msg: pointer to the message data preallocated by the client (in/out)
++ * @is_smc64: select 64-bit or 32-bit FF-A ABI
++ *
++ * Please see ffa_msg_send_direct_req_hdlr_runtime() description for more details.
++ *
++ * Return:
++ *
++ * 0 on success. Otherwise, failure
++ */
++int __ffa_runtime ffa_sync_send_receive_runtime(u16 dst_part_id,
++ struct ffa_send_direct_data *msg,
++ bool is_smc64);
++
++/**
++ * ffa_invoke_msg_send_direct_req() - Invokes FFA_MSG_SEND_DIRECT_{REQ,RESP}
++ * @endpoint_id: u-boot endpoint id
++ * @dst_part_id: destination partition ID
++ * @msg: pointer to the message data preallocated by the client (in/out)
++ * @is_smc64: select 64-bit or 32-bit FF-A ABI
++ *
++ * This function invokes FFA_MSG_SEND_DIRECT_{REQ,RESP} FF-A functions.
++ *
++ * FFA_MSG_SEND_DIRECT_REQ is used to send the data to the secure partition.
++ * The response from the secure partition is handled by reading the
++ * FFA_MSG_SEND_DIRECT_RESP arguments.
++ *
++ * The maximum size of the data that can be exchanged is 40 bytes which is
++ * sizeof(struct ffa_send_direct_data) as defined by the FF-A specification 1.0
++ * in the section relevant to FFA_MSG_SEND_DIRECT_{REQ,RESP}
++ *
++ * Return:
++ *
++ * 0 on success. Otherwise, error on failure
++ */
++int __ffa_runtime ffa_invoke_msg_send_direct_req(u16 endpoint_id, u16 dst_part_id,
++ struct ffa_send_direct_data *msg,
++ bool is_smc64);
++
++/**
++ * ffa_msg_send_direct_req_hdlr_runtime() - Runtime implementation of
++ * FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function
++ * @dst_part_id: destination partition ID
++ * @msg: pointer to the message data preallocated by the client (in/out)
++ * @is_smc64: select 64-bit or 32-bit FF-A ABI
++ *
++ * This function calls the ffa_invoke_msg_send_direct_req() function which
++ * invokes FFA_MSG_SEND_DIRECT_{REQ,RESP} FF-A functions.
++ *
++ * Return:
++ *
++ * 0 on success. Otherwise, failure
++ */
++int __ffa_runtime ffa_msg_send_direct_req_hdlr_runtime(u16 dst_part_id,
++ struct ffa_send_direct_data *msg,
++ bool is_smc64);
++
++#endif
+diff --git a/test/dm/ffa.c b/test/dm/ffa.c
+index f4a6716cfd8..1240bb4141a 100644
+--- a/test/dm/ffa.c
++++ b/test/dm/ffa.c
+@@ -2,7 +2,7 @@
+ /*
+ * Functional tests for UCLASS_FFA class
+ *
+- * Copyright 2022-2023, 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
++ * Copyright 2022-2023, 2026 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ *
+ * Authors:
+ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+@@ -27,14 +27,14 @@ static int check_fwk_version(struct ffa_priv *uc_priv, struct unit_test_state *u
+ func_data.data0 = &fwk_version;
+ func_data.data0_size = sizeof(fwk_version);
+ ut_assertok(sandbox_query_ffa_emul_state(FFA_VERSION, &func_data));
+- ut_asserteq(uc_priv->fwk_version, fwk_version);
++ ut_asserteq(uc_priv->rt.fwk_version, fwk_version);
+
+ return 0;
+ }
+
+ static int check_endpoint_id(struct ffa_priv *uc_priv, struct unit_test_state *uts)
+ {
+- ut_asserteq(0, uc_priv->id);
++ ut_asserteq(0, uc_priv->rt.id);
+
+ return 0;
+ }
new file mode 100644
@@ -0,0 +1,478 @@
+From dd992d82e9958786bd8367a8b7018648d217d552 Mon Sep 17 00:00:00 2001
+From: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+Date: Thu, 20 Nov 2025 22:19:01 +0000
+Subject: [PATCH 43/53] efi_loader: add FF-A runtime support in EFI variable
+ TEE driver
+
+Enable MM variable services over FF-A after ExitBootServices
+
+This patch extends lib/efi_loader/efi_variable_tee.c to support FF-A
+communication with the secure world during EFI runtime. It enables EFI
+runtime variable access and MM communication using FF-A transport when
+ExitBootServices() has already been called.
+
+Key changes:
+ ------------
+ - Introduce runtime-safe implementations for MM communication,
+ notification, and variable access using FF-A driver.
+ - Introduce communication-buffer helper (get_comm_buf()) that switches
+ between dynamic allocation (boot phase) and the fixed FF-A shared
+ buffer (runtime phase).
+ - Mark persistent data and code with __efi_runtime and
+ __efi_runtime_data attributes.
+ - Use direct physical address mapping for shared buffers since
+ U-Boot operates with 1:1 physical-to-virtual mapping.
+ - Only per-buffer cache maintenance is performed at runtime,
+ as whole D-cache invalidation would violate the OS coherency model
+ after ExitBootServices().
+ - Add runtime-phase tracking (efi_runtime_enabled).
+
+The change reuses the statically reserved shared buffer, replaces
+allocations with __efi_runtime copies, and updates the runtime service
+table so EFI variable runtime calls reach the secure partition via FF-A.
+
+Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/]
+Signed-off-by: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+---
+ lib/efi_loader/efi_variable_tee.c | 331 ++++++++++++++++++++++++++++--
+ 1 file changed, 319 insertions(+), 12 deletions(-)
+
+diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c
+index 6a1fa39bb6f..e4d97dc55ab 100644
+--- a/lib/efi_loader/efi_variable_tee.c
++++ b/lib/efi_loader/efi_variable_tee.c
+@@ -4,7 +4,7 @@
+ *
+ * Copyright (C) 2019 Linaro Ltd. <sughosh.ganu@linaro.org>
+ * Copyright (C) 2019 Linaro Ltd. <ilias.apalodimas@linaro.org>
+- * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
++ * Copyright 2022-2023, 2026 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ *
+ * Authors:
+ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+@@ -14,6 +14,7 @@
+
+ #if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT)
+ #include <arm_ffa.h>
++#include <arm_ffa_runtime.h>
+ #endif
+ #include <cpu_func.h>
+ #include <dm.h>
+@@ -34,20 +35,47 @@
+ #define MM_DENIED (-3)
+ #define MM_NO_MEMORY (-5)
+
++static const int __efi_runtime_rodata mm_sp_errmap[] = {
++ [-MM_NOT_SUPPORTED] = -EINVAL,
++ [-MM_INVALID_PARAMETER] = -EPERM,
++ [-MM_DENIED] = -EACCES,
++ [-MM_NO_MEMORY] = -EBUSY,
++};
++
+ static const char *mm_sp_svc_uuid = MM_SP_UUID;
+-static u16 mm_sp_id;
++static u16 __efi_runtime_data mm_sp_id;
+ #endif
+
++static void *__efi_runtime_data ffa_shared_buf;
++static const efi_guid_t __efi_runtime_rodata mm_var_guid_runtime =
++ EFI_MM_VARIABLE_GUID;
++
+ extern struct efi_var_file __efi_runtime_data *efi_var_buf;
+-static efi_uintn_t max_buffer_size; /* comm + var + func + data */
+-static efi_uintn_t max_payload_size; /* func + data */
++static efi_uintn_t __efi_runtime_data max_buffer_size; /* comm + var + func + data */
++static efi_uintn_t __efi_runtime_data max_payload_size; /* func + data */
+ static const u16 __efi_runtime_rodata pk[] = u"PK";
++static bool __efi_runtime_data efi_runtime_enabled;
+
+ struct mm_connection {
+ struct udevice *tee;
+ u32 session;
+ };
+
++/**
++ * efi_is_runtime_enabled() - Indicate whether the system is in the UEFI runtime phase
++ *
++ * This helper returns whether the firmware has transitioned into the
++ * UEFI runtime phase, meaning that ExitBootServices() has been invoked.
++ *
++ * Return:
++ * true - The system is operating in UEFI runtime mode.
++ * false - The system is still in the boot services phase.
++ */
++static bool __efi_runtime efi_is_runtime_enabled(void)
++{
++ return efi_runtime_enabled;
++}
++
+ /**
+ * get_connection() - Retrieve OP-TEE session for a specific UUID.
+ *
+@@ -169,6 +197,28 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
+ }
+
+ #if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT)
++/**
++ * ffa_map_sp_event_runtime() - Map MM SP response to errno (runtime-safe)
++ * @sp_event_ret: MM SP return code from ffa_notify_mm_sp_runtime()
++ *
++ * Convert the MM SP return code into a standard U-Boot errno. This helper
++ * is marked __efi_runtime to ensure it is safe to call after
++ * ExitBootServices().
++ *
++ * Return: 0 on success, negative errno on failure
++ */
++static __efi_runtime int ffa_map_sp_event_runtime(int sp_event_ret)
++{
++ int idx = -sp_event_ret;
++
++ if (sp_event_ret == MM_SUCCESS)
++ return 0;
++ if (idx > 0 && idx < (int)ARRAY_SIZE(mm_sp_errmap) &&
++ mm_sp_errmap[idx])
++ return mm_sp_errmap[idx];
++ return -EACCES;
++}
++
+ /**
+ * ffa_notify_mm_sp() - Announce there is data in the shared buffer
+ *
+@@ -225,6 +275,35 @@ static int ffa_notify_mm_sp(void)
+ return ret;
+ }
+
++/**
++ * ffa_notify_mm_sp_runtime() - Runtime implementation of
++ * ffa_notify_mm_sp()
++ *
++ * Notify the MM partition in the trusted world that
++ * data is available in the shared buffer.
++ * This is a blocking call during which trusted world has exclusive access
++ * to the MM shared buffer.
++ *
++ * Return:
++ *
++ * 0 on success
++ */
++static int __efi_runtime ffa_notify_mm_sp_runtime(void)
++{
++ struct ffa_send_direct_data msg = {0};
++ int ret;
++ int sp_event_ret;
++
++ msg.data0 = CONFIG_FFA_SHARED_MM_BUF_OFFSET;
++
++ ret = ffa_sync_send_receive_runtime(mm_sp_id, &msg, 1);
++ if (ret)
++ return ret;
++
++ ret = ffa_map_sp_event_runtime(sp_event_ret);
++ return ret;
++}
++
+ /**
+ * ffa_discover_mm_sp_id() - Query the MM partition ID
+ *
+@@ -360,6 +439,116 @@ static efi_status_t ffa_mm_communicate(void *comm_buf, ulong comm_buf_size)
+ return efi_ret;
+ }
+
++/**
++ * ffa_mm_communicate_runtime() - Runtime implementation of ffa_mm_communicate()
++ * @comm_buf: locally allocated communication buffer used for rx/tx
++ * @comm_buf_size: communication buffer size
++ *
++ * Issue a door bell event to notify the MM partition (SP) running in OP-TEE
++ * that there is data to read from the shared buffer.
++ * Communication with the MM SP is performed using FF-A transport.
++ * On the event, MM SP can read the data from the buffer and
++ * update the MM shared buffer with response data.
++ * The response data is copied back to the communication buffer.
++ *
++ * Return:
++ *
++ * EFI status code
++ */
++static efi_status_t __efi_runtime ffa_mm_communicate_runtime(void *comm_buf,
++ ulong comm_buf_size)
++{
++ ulong tx_data_size;
++ int ffa_ret;
++ efi_status_t efi_ret;
++ struct efi_mm_communicate_header *mm_hdr;
++
++ if (!comm_buf)
++ return EFI_INVALID_PARAMETER;
++
++ /* Discover MM partition ID at boot time */
++ if (!mm_sp_id)
++ return EFI_UNSUPPORTED;
++
++ mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
++ tx_data_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t);
++
++ if (comm_buf_size != tx_data_size || tx_data_size > CONFIG_FFA_SHARED_MM_BUF_SIZE)
++ return EFI_INVALID_PARAMETER;
++
++ /*
++ * Shared buffer cache maintenance for FF-A / OP-TEE communication:
++ *
++ * NS -> S (request path):
++ *
++ * The non-secure side populates the shared buffer. If the buffer is cached
++ * in NS, the updated bytes may reside in dirty D-cache lines and not yet be
++ * visible in DDR. Since the secure world typically reads the shared buffer
++ * directly from DDR (e.g. with caches disabled / non-coherent mapping), we
++ * must clean the corresponding cache lines to the Point of Coherency (PoC)
++ * before entering secure world.
++ *
++ * S -> NS (response path):
++ *
++ * The secure world may update the same shared buffer in DDR. After returning
++ * to non-secure, any cached copies of that region in NS may be stale. We
++ * therefore invalidate the shared buffer range after the FF-A call to drop
++ * those lines and force subsequent reads to fetch the latest data from DDR.
++ *
++ * Note: Whole-cache invalidation must not be used in EFI runtime context.
++ * After ExitBootServices(), the OS owns the cache hierarchy; global invalidation
++ * could drop OS dirty lines and violate the OS coherency model. Always operate
++ * on the shared buffer range only.
++ */
++ if (IS_ENABLED(CONFIG_ARM64))
++ flush_dcache_range((unsigned long)comm_buf,
++ (unsigned long)((u8 *)comm_buf +
++ CONFIG_FFA_SHARED_MM_BUF_SIZE));
++
++ /* Announce there is data in the shared buffer */
++
++ ffa_ret = ffa_notify_mm_sp_runtime();
++
++ if (IS_ENABLED(CONFIG_ARM64))
++ invalidate_dcache_range((unsigned long)comm_buf,
++ (unsigned long)((u8 *)comm_buf +
++ CONFIG_FFA_SHARED_MM_BUF_SIZE));
++
++ switch (ffa_ret) {
++ case 0: {
++ ulong rx_data_size;
++
++ rx_data_size = ((struct efi_mm_communicate_header *)comm_buf)->message_len +
++ sizeof(efi_guid_t) +
++ sizeof(size_t);
++
++ if (rx_data_size > comm_buf_size) {
++ efi_ret = EFI_OUT_OF_RESOURCES;
++ break;
++ }
++
++ efi_ret = EFI_SUCCESS;
++ break;
++ }
++ case -EINVAL:
++ efi_ret = EFI_DEVICE_ERROR;
++ break;
++ case -EPERM:
++ efi_ret = EFI_INVALID_PARAMETER;
++ break;
++ case -EACCES:
++ efi_ret = EFI_ACCESS_DENIED;
++ break;
++ case -EBUSY:
++ efi_ret = EFI_OUT_OF_RESOURCES;
++ break;
++ default:
++ efi_ret = EFI_ACCESS_DENIED;
++ }
++
++ return efi_ret;
++}
++
+ /**
+ * get_mm_comms() - detect the available MM transport
+ *
+@@ -386,6 +575,27 @@ static enum mm_comms_select get_mm_comms(void)
+
+ return MM_COMMS_FFA;
+ }
++
++/**
++ * get_mm_comms_runtime() - detect the available MM transport at runtime
++ *
++ * Make sure the FF-A bus is available at runtime and ready
++ * for use.
++ *
++ * Return:
++ *
++ * MM_COMMS_FFA or MM_COMMS_UNDEFINED
++ */
++static enum mm_comms_select __efi_runtime get_mm_comms_runtime(void)
++{
++ bool ret;
++
++ ret = efi_is_runtime_enabled();
++ if (!ret)
++ return MM_COMMS_UNDEFINED;
++
++ return MM_COMMS_FFA;
++}
+ #endif
+
+ /**
+@@ -433,9 +643,86 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize)
+ return var_hdr->ret_status;
+ }
+
++/**
++ * mm_communicate_runtime() - Runtime implementation of mm_communicate()
++ *
++ * @comm_buf: locally allocated communication buffer
++ * @dsize: buffer size
++ *
++ * The SP (also called partition) can be any MM SP such as StandAlonneMM or smm-gateway.
++ * The comm_buf format is the same for both partitions.
++ * When using the u-boot OP-TEE driver, StandAlonneMM is supported.
++ * When using the u-boot FF-A driver, any MM SP is supported.
++ *
++ * Return: status code
++ */
++static efi_status_t __efi_runtime mm_communicate_runtime(u8 *comm_buf, efi_uintn_t dsize)
++{
++ efi_status_t ret = EFI_UNSUPPORTED;
++ struct efi_mm_communicate_header *mm_hdr;
++ struct smm_variable_communicate_header *var_hdr;
++ enum mm_comms_select mm_comms;
++
++ dsize += MM_COMMUNICATE_HEADER_SIZE + MM_VARIABLE_COMMUNICATE_SIZE;
++ mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
++ var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
++
++ if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) {
++ mm_comms = get_mm_comms_runtime();
++ if (mm_comms == MM_COMMS_FFA)
++ ret = ffa_mm_communicate_runtime(comm_buf, dsize);
++ }
++
++ if (ret != EFI_SUCCESS)
++ return ret;
++
++ return var_hdr->ret_status;
++}
++
++/**
++ * get_comm_buf() - Obtain a communication buffer for MM/FF-A exchange
++ * @payload_size: size of the payload that will be appended to the
++ * MM communication header
++ * This helper returns a buffer suitable for constructing an
++ * EFI_MM_COMMUNICATE message. During the boot phase a new buffer is
++ * dynamically allocated. After ExitBootServices(), dynamic
++ * allocation is no longer permitted, and all runtime communication must
++ * use the statically reserved FF-A shared buffer.
++ *
++ * Return:
++ * Pointer to a valid communication buffer on success,
++ * NULL if allocation fails during the boot phase.
++ */
++static __efi_runtime u8 *get_comm_buf(efi_uintn_t payload_size)
++{
++ u8 *comm_buf;
++
++ /* After ExitBootServices(), dynamic allocation is no longer permitted.
++ * Use the predefined FF-A shared buffer at runtime; otherwise allocate
++ * a fresh buffer during the boot phase.
++ */
++ if (efi_is_runtime_enabled()) {
++ if (IS_ENABLED(CONFIG_ARM_FFA_RT_MODE)) {
++ comm_buf = ffa_shared_buf;
++ if (!comm_buf)
++ return NULL;
++ efi_memset_runtime(comm_buf, 0, CONFIG_FFA_SHARED_MM_BUF_SIZE);
++ } else {
++ return NULL;
++ }
++ } else {
++ comm_buf = calloc(1, MM_COMMUNICATE_HEADER_SIZE +
++ MM_VARIABLE_COMMUNICATE_SIZE +
++ payload_size);
++ if (!comm_buf)
++ return NULL;
++ }
++ return comm_buf;
++}
++
+ /**
+ * setup_mm_hdr() - Allocate a buffer for StandAloneMM and initialize the
+- * header data.
++ * header data. It is runtime safe.
+ *
+ * @dptr: pointer address of the corresponding StandAloneMM
+ * function
+@@ -444,10 +731,9 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize)
+ * @ret: EFI return code
+ * Return: buffer or NULL
+ */
+-static u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size,
+- efi_uintn_t func, efi_status_t *ret)
++static __efi_runtime u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size,
++ efi_uintn_t func, efi_status_t *ret)
+ {
+- const efi_guid_t mm_var_guid = EFI_MM_VARIABLE_GUID;
+ struct efi_mm_communicate_header *mm_hdr;
+ struct smm_variable_communicate_header *var_hdr;
+ u8 *comm_buf;
+@@ -465,16 +751,15 @@ static u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size,
+ return NULL;
+ }
+
+- comm_buf = calloc(1, MM_COMMUNICATE_HEADER_SIZE +
+- MM_VARIABLE_COMMUNICATE_SIZE +
+- payload_size);
++ comm_buf = get_comm_buf(payload_size);
+ if (!comm_buf) {
+ *ret = EFI_OUT_OF_RESOURCES;
+ return NULL;
+ }
+
+ mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
+- guidcpy(&mm_hdr->header_guid, &mm_var_guid);
++ efi_memcpy_runtime(&mm_hdr->header_guid, &mm_var_guid_runtime,
++ sizeof(mm_hdr->header_guid));
+ mm_hdr->message_len = MM_VARIABLE_COMMUNICATE_SIZE + payload_size;
+
+ var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
+@@ -982,6 +1267,9 @@ void efi_variables_boot_exit_notify(void)
+ efi_get_next_variable_name_runtime;
+ efi_runtime_services.set_variable = efi_set_variable_runtime;
+ efi_update_table_header_crc32(&efi_runtime_services.hdr);
++
++ /* Set efi_runtime_enabled as true after ExitBootServices */
++ efi_runtime_enabled = true;
+ }
+
+ /**
+@@ -993,6 +1281,25 @@ efi_status_t efi_init_variables(void)
+ {
+ efi_status_t ret;
+
++ if (IS_ENABLED(CONFIG_ARM_FFA_RT_MODE)) {
++ /*
++ * The FF-A shared buffer is accessed by EFI runtime services, so it must
++ * be marked as runtime memory in the EFI memory map.
++ */
++ ffa_shared_buf = (void *)CONFIG_FFA_SHARED_MM_BUF_ADDR;
++ ret = efi_add_memory_map(CONFIG_FFA_SHARED_MM_BUF_ADDR,
++ CONFIG_FFA_SHARED_MM_BUF_SIZE,
++ EFI_RUNTIME_SERVICES_DATA);
++ if (ret != EFI_SUCCESS) {
++ log_err("EFI: failed to add FF-A shared buffer to runtime map (%lu)\n",
++ ret);
++ return ret;
++ }
++ log_info("EFI: FF-A shared buffer runtime map: addr=0x%lx size=0x%lx\n",
++ (ulong)CONFIG_FFA_SHARED_MM_BUF_ADDR,
++ (ulong)CONFIG_FFA_SHARED_MM_BUF_SIZE);
++ }
++
+ /* Create a cached copy of the variables that will be enabled on ExitBootServices() */
+ ret = efi_var_mem_init();
+ if (ret != EFI_SUCCESS)
new file mode 100644
@@ -0,0 +1,418 @@
+From 119d43911b72f8c90b09e91b727d33d21d539c49 Mon Sep 17 00:00:00 2001
+From: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+Date: Thu, 11 Dec 2025 15:28:39 +0000
+Subject: [PATCH 44/53] efi_loader: enable EFI runtime
+ SetVariable()/GetVariable() using FF-A transport
+
+Route EFI runtime variable APIs through FF-A MM communication
+
+This patch implements full EFI Runtime Variable Services (GetVariable,
+SetVariable, GetNextVariableName, and QueryVariableInfo) using the
+FF-A/MM communication backend. Once ExitBootServices() has been invoked,
+all variable operations now use the runtime-safe FF-A transport instead
+of the boot-time communication paths.
+
+Key changes:
+============
+
+ - Add runtime-safe variable property handling via FF-A MM SP.
+ - Add runtime versions of variable access and property operations.
+ - Replace all standard memory operations with EFI-runtime-safe variants.
+
+Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/]
+Signed-off-by: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+---
+ lib/charset.c | 2 +-
+ lib/efi_loader/efi_variable_tee.c | 322 +++++++++++++++++++++++++++++-
+ 2 files changed, 318 insertions(+), 6 deletions(-)
+
+diff --git a/lib/charset.c b/lib/charset.c
+index 182c92a50c4..738ad1352de 100644
+--- a/lib/charset.c
++++ b/lib/charset.c
+@@ -407,7 +407,7 @@ size_t __efi_runtime u16_strnlen(const u16 *in, size_t count)
+ return i;
+ }
+
+-size_t u16_strsize(const void *in)
++size_t __efi_runtime u16_strsize(const void *in)
+ {
+ return (u16_strlen(in) + 1) * sizeof(u16);
+ }
+diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c
+index e4d97dc55ab..30687c21b8e 100644
+--- a/lib/efi_loader/efi_variable_tee.c
++++ b/lib/efi_loader/efi_variable_tee.c
+@@ -859,6 +859,38 @@ out:
+ return ret;
+ }
+
++static efi_status_t __efi_runtime set_property_int_runtime(const u16 *variable_name,
++ efi_uintn_t name_size,
++ const efi_guid_t *vendor,
++ struct var_check_property *var_property)
++{
++ struct smm_variable_var_check_property *smm_property;
++ efi_uintn_t payload_size;
++ u8 *comm_buf = NULL;
++ efi_status_t ret;
++
++ payload_size = sizeof(*smm_property) + name_size;
++ if (payload_size > max_payload_size) {
++ ret = EFI_INVALID_PARAMETER;
++ return ret;
++ }
++ comm_buf = setup_mm_hdr((void **)&smm_property, payload_size,
++ SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET,
++ &ret);
++ if (!comm_buf)
++ return ret;
++
++ efi_memcpy_runtime(&smm_property->guid, vendor, sizeof(*vendor));
++ smm_property->name_size = name_size;
++ efi_memcpy_runtime(&smm_property->property, var_property,
++ sizeof(smm_property->property));
++ efi_memcpy_runtime(smm_property->name, variable_name, name_size);
++
++ ret = mm_communicate_runtime(comm_buf, payload_size);
++
++ return ret;
++}
++
+ static efi_status_t get_property_int(const u16 *variable_name,
+ efi_uintn_t name_size,
+ const efi_guid_t *vendor,
+@@ -904,6 +936,49 @@ out:
+ return ret;
+ }
+
++static efi_status_t __efi_runtime get_property_int_runtime(const u16 *variable_name,
++ efi_uintn_t name_size,
++ const efi_guid_t *vendor,
++ struct var_check_property *var_property)
++{
++ struct smm_variable_var_check_property *smm_property;
++ efi_uintn_t payload_size;
++ u8 *comm_buf = NULL;
++ efi_status_t ret;
++
++ efi_memset_runtime(var_property, 0, sizeof(*var_property));
++ payload_size = sizeof(*smm_property) + name_size;
++ if (payload_size > max_payload_size) {
++ ret = EFI_INVALID_PARAMETER;
++ return ret;
++ }
++ comm_buf = setup_mm_hdr((void **)&smm_property, payload_size,
++ SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET,
++ &ret);
++ if (!comm_buf)
++ return ret;
++
++ efi_memcpy_runtime(&smm_property->guid, vendor, sizeof(smm_property->guid));
++ smm_property->name_size = name_size;
++ efi_memcpy_runtime(smm_property->name, variable_name, name_size);
++
++ ret = mm_communicate_runtime(comm_buf, payload_size);
++ /*
++ * Currently only R/O property is supported in StMM.
++ * Variables that are not set to R/O will not set the property in StMM
++ * and the call will return EFI_NOT_FOUND. We are setting the
++ * properties to 0x0 so checking against that is enough for the
++ * EFI_NOT_FOUND case.
++ */
++ if (ret == EFI_NOT_FOUND)
++ ret = EFI_SUCCESS;
++ if (ret != EFI_SUCCESS)
++ return ret;
++ efi_memcpy_runtime(var_property, &smm_property->property, sizeof(*var_property));
++
++ return ret;
++}
++
+ efi_status_t efi_get_variable_int(const u16 *variable_name,
+ const efi_guid_t *vendor,
+ u32 *attributes, efi_uintn_t *data_size,
+@@ -991,6 +1066,92 @@ out:
+ return ret;
+ }
+
++efi_status_t __efi_runtime efi_get_variable_runtime(u16 *variable_name,
++ const efi_guid_t *vendor,
++ u32 *attributes,
++ efi_uintn_t *data_size,
++ void *data)
++{
++ struct var_check_property var_property;
++ struct smm_variable_access *var_acc;
++ efi_uintn_t payload_size;
++ efi_uintn_t name_size;
++ efi_uintn_t tmp_dsize;
++ u8 *comm_buf = NULL;
++ efi_status_t ret, tmp;
++
++ if (!variable_name || !vendor || !data_size) {
++ ret = EFI_INVALID_PARAMETER;
++ return ret;
++ }
++
++ /* Check payload size */
++ name_size = u16_strsize(variable_name);
++ if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
++ ret = EFI_INVALID_PARAMETER;
++ return ret;
++ }
++
++ /* Trim output buffer size */
++ tmp_dsize = *data_size;
++ if (name_size + tmp_dsize >
++ max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
++ tmp_dsize = max_payload_size -
++ MM_VARIABLE_ACCESS_HEADER_SIZE -
++ name_size;
++ }
++
++ /* Get communication buffer and initialize header */
++ payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + tmp_dsize;
++ comm_buf = setup_mm_hdr((void **)&var_acc, payload_size,
++ SMM_VARIABLE_FUNCTION_GET_VARIABLE, &ret);
++ if (!comm_buf)
++ return ret;
++
++ /* Fill in contents */
++ efi_memcpy_runtime(&var_acc->guid, vendor, sizeof(var_acc->guid));
++ var_acc->data_size = tmp_dsize;
++ var_acc->name_size = name_size;
++ var_acc->attr = attributes ? *attributes : 0;
++ efi_memcpy_runtime(var_acc->name, variable_name, name_size);
++
++ /* Communicate */
++ ret = mm_communicate_runtime(comm_buf, payload_size);
++ if (ret != EFI_SUCCESS && ret != EFI_BUFFER_TOO_SMALL)
++ return ret;
++
++ /* Update with reported data size for trimmed case */
++ *data_size = var_acc->data_size;
++
++ /* Copy the data if ret is EFI_SUCCESS */
++ if (ret == EFI_SUCCESS) {
++ if (data)
++ efi_memcpy_runtime(data, (u8 *)var_acc->name + var_acc->name_size,
++ var_acc->data_size);
++ else
++ ret = EFI_INVALID_PARAMETER;
++ }
++
++ /*
++ * UEFI > 2.7 needs the attributes set even if the buffer is
++ * smaller
++ */
++ if (attributes) {
++ tmp = get_property_int_runtime(variable_name, name_size, vendor,
++ &var_property);
++ if (tmp != EFI_SUCCESS) {
++ ret = tmp;
++ return ret;
++ }
++ *attributes = var_acc->attr;
++ if (var_property.property &
++ VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY)
++ *attributes |= EFI_VARIABLE_READ_ONLY;
++ }
++
++ return ret;
++}
++
+ efi_status_t efi_get_next_variable_name_int(efi_uintn_t *variable_name_size,
+ u16 *variable_name,
+ efi_guid_t *guid)
+@@ -1055,6 +1216,68 @@ out:
+ return ret;
+ }
+
++efi_status_t __efi_runtime efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size,
++ u16 *variable_name,
++ efi_guid_t *guid)
++{
++ struct smm_variable_getnext *var_getnext;
++ efi_uintn_t payload_size;
++ efi_uintn_t out_name_size;
++ efi_uintn_t in_name_size;
++ u8 *comm_buf = NULL;
++ efi_status_t ret;
++
++ if (!variable_name_size || !variable_name || !guid) {
++ ret = EFI_INVALID_PARAMETER;
++ return ret;
++ }
++
++ out_name_size = *variable_name_size;
++ in_name_size = u16_strsize(variable_name);
++
++ if (out_name_size < in_name_size) {
++ ret = EFI_INVALID_PARAMETER;
++ return ret;
++ }
++
++ if (in_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
++ ret = EFI_INVALID_PARAMETER;
++ return ret;
++ }
++
++ /* Trim output buffer size */
++ if (out_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE)
++ out_name_size = max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE;
++
++ payload_size = MM_VARIABLE_GET_NEXT_HEADER_SIZE + out_name_size;
++ comm_buf = setup_mm_hdr((void **)&var_getnext, payload_size,
++ SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME,
++ &ret);
++ if (!comm_buf)
++ return ret;
++
++ /* Fill in contents */
++ efi_memcpy_runtime(&var_getnext->guid, guid, sizeof(*guid));
++ var_getnext->name_size = out_name_size;
++ efi_memcpy_runtime(var_getnext->name, variable_name, in_name_size);
++ efi_memset_runtime((u8 *)var_getnext->name + in_name_size, 0x0,
++ out_name_size - in_name_size);
++
++ /* Communicate */
++ ret = mm_communicate_runtime(comm_buf, payload_size);
++ if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
++ /* Update with reported data size for trimmed case */
++ *variable_name_size = var_getnext->name_size;
++ }
++ if (ret != EFI_SUCCESS)
++ return ret;
++
++ efi_memcpy_runtime(guid, &var_getnext->guid, sizeof(*guid));
++ efi_memcpy_runtime(variable_name, var_getnext->name, var_getnext->name_size);
++
++ return ret;
++}
++
+ efi_status_t efi_set_variable_int(const u16 *variable_name,
+ const efi_guid_t *vendor, u32 attributes,
+ efi_uintn_t data_size, const void *data,
+@@ -1197,11 +1420,11 @@ out:
+ *
+ * @attributes: bitmask to select variables to be
+ * queried
+- * @maximum_variable_storage_size: maximum size of storage area for the
++ * @max_variable_storage_size: maximum size of storage area for the
+ * selected variable types
+- * @remaining_variable_storage_size: remaining size of storage are for the
++ * @remain_variable_storage_size: remaining size of storage are for the
+ * selected variable types
+- * @maximum_variable_size: maximum size of a variable of the
++ * @max_variable_size: maximum size of a variable of the
+ * selected type
+ * Return: status code
+ */
+@@ -1210,7 +1433,33 @@ efi_query_variable_info_runtime(u32 attributes, u64 *max_variable_storage_size,
+ u64 *remain_variable_storage_size,
+ u64 *max_variable_size)
+ {
+- return EFI_UNSUPPORTED;
++ struct smm_variable_query_info *mm_query_info;
++ efi_uintn_t payload_size;
++ efi_status_t ret;
++ u8 *comm_buf;
++
++ if (!max_variable_storage_size ||
++ !remain_variable_storage_size ||
++ !max_variable_size || !attributes)
++ return EFI_INVALID_PARAMETER;
++
++ payload_size = sizeof(*mm_query_info);
++ comm_buf = setup_mm_hdr((void **)&mm_query_info, payload_size,
++ SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO,
++ &ret);
++ if (!comm_buf)
++ return ret;
++
++ mm_query_info->attr = attributes;
++ ret = mm_communicate_runtime(comm_buf, payload_size);
++ if (ret != EFI_SUCCESS)
++ return ret;
++ *max_variable_storage_size = mm_query_info->max_variable_storage;
++ *remain_variable_storage_size =
++ mm_query_info->remaining_variable_storage;
++ *max_variable_size = mm_query_info->max_variable_size;
++
++ return ret;
+ }
+
+ /**
+@@ -1228,7 +1477,70 @@ efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *guid,
+ u32 attributes, efi_uintn_t data_size,
+ const void *data)
+ {
+- return EFI_UNSUPPORTED;
++ efi_status_t ret, mm_communicate_ret = EFI_SUCCESS;
++ struct var_check_property var_property;
++ struct smm_variable_access *var_acc;
++ efi_uintn_t payload_size;
++ efi_uintn_t name_size;
++ u8 *comm_buf = NULL;
++ bool ro;
++
++ if (!variable_name || variable_name[0] == 0 || !guid)
++ return EFI_INVALID_PARAMETER;
++
++ if (data_size > 0 && !data)
++ return EFI_INVALID_PARAMETER;
++
++ /* Check payload size */
++ name_size = u16_strsize(variable_name);
++ payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size;
++ if (payload_size > max_payload_size)
++ return EFI_INVALID_PARAMETER;
++
++ ro = !!(attributes & EFI_VARIABLE_READ_ONLY);
++ attributes &= EFI_VARIABLE_MASK;
++
++ ret = get_property_int_runtime(variable_name, name_size, guid,
++ &var_property);
++ if (ret != EFI_SUCCESS)
++ return ret;
++
++ /*
++ * Allocate the buffer early, before switching to RW (if needed)
++ * so we won't need to account for any failures in reading/setting
++ * the properties, if the allocation fails
++ */
++ comm_buf = setup_mm_hdr((void **)&var_acc, payload_size,
++ SMM_VARIABLE_FUNCTION_SET_VARIABLE, &ret);
++ if (!comm_buf)
++ return ret;
++
++ if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY)
++ return EFI_WRITE_PROTECTED;
++
++ /* Fill in contents */
++ efi_memcpy_runtime(&var_acc->guid, guid, sizeof(*guid));
++ var_acc->data_size = data_size;
++ var_acc->name_size = name_size;
++ var_acc->attr = attributes;
++ efi_memcpy_runtime(var_acc->name, variable_name, name_size);
++ efi_memcpy_runtime((u8 *)var_acc->name + name_size, data, data_size);
++
++ /* Communicate */
++ ret = mm_communicate_runtime(comm_buf, payload_size);
++ if (ret != EFI_SUCCESS)
++ mm_communicate_ret = ret;
++
++ if (ro && !(var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY)) {
++ var_property.revision = VAR_CHECK_VARIABLE_PROPERTY_REVISION;
++ var_property.property |= VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY;
++ var_property.attributes = attributes;
++ var_property.minsize = 1;
++ var_property.maxsize = var_acc->data_size;
++ ret = set_property_int_runtime(variable_name, name_size, guid, &var_property);
++ }
++
++ return (mm_communicate_ret == EFI_SUCCESS) ? ret : mm_communicate_ret;
+ }
+
+ /**
new file mode 100644
@@ -0,0 +1,103 @@
+From 5c2b1f517c97f3349108bfd9fef9aac40ba236ac Mon Sep 17 00:00:00 2001
+From: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+Date: Fri, 12 Dec 2025 10:59:47 +0000
+Subject: [PATCH 45/53] efi_loader: move runtime GetVariable() helpers to
+ efi_variable.c
+
+Consolidate runtime GetVariable helpers to avoid duplicates
+
+The functions efi_get_variable_runtime() and
+efi_get_next_variable_name_runtime() were implemented in
+efi_variable_tee.c as part of the FF-A runtime variable handling work.
+However, default implementations for these same runtime helpers also
+exist in efi_var_common.c. This results in duplicate symbol definitions.
+To resolve the conflict and centralize the runtime API, this patch moves
+the default implementations of the two runtime GetVariable() helpers
+from efi_var_common.c to efi_variable.c. This ensures that:
+
+ - only a single definition of each runtime helper exists,
+ - backend-specific implementations can override these cleanly,
+ - the EFI runtime variable service table continues to reference the
+ correct functions.
+
+No functional changes are introduced; this is a structural cleanup to
+avoid build-time conflicts and consolidate EFI runtime variable support
+in the appropriate file.
+
+Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/]
+Signed-off-by: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+---
+ lib/efi_loader/efi_var_common.c | 24 ------------------------
+ lib/efi_loader/efi_variable.c | 24 ++++++++++++++++++++++++
+ 2 files changed, 24 insertions(+), 24 deletions(-)
+
+diff --git a/lib/efi_loader/efi_var_common.c b/lib/efi_loader/efi_var_common.c
+index 4b34a58b4cf..0eaf89112e6 100644
+--- a/lib/efi_loader/efi_var_common.c
++++ b/lib/efi_loader/efi_var_common.c
+@@ -172,30 +172,6 @@ efi_status_t EFIAPI efi_query_variable_info(
+ return EFI_EXIT(ret);
+ }
+
+-efi_status_t __efi_runtime EFIAPI
+-efi_get_variable_runtime(u16 *variable_name, const efi_guid_t *guid,
+- u32 *attributes, efi_uintn_t *data_size, void *data)
+-{
+- efi_status_t ret;
+-
+- ret = efi_get_variable_mem(variable_name, guid, attributes, data_size,
+- data, NULL, EFI_VARIABLE_RUNTIME_ACCESS);
+-
+- /* Remove EFI_VARIABLE_READ_ONLY flag */
+- if (attributes)
+- *attributes &= EFI_VARIABLE_MASK;
+-
+- return ret;
+-}
+-
+-efi_status_t __efi_runtime EFIAPI
+-efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size,
+- u16 *variable_name, efi_guid_t *guid)
+-{
+- return efi_get_next_variable_name_mem(variable_name_size, variable_name,
+- guid, EFI_VARIABLE_RUNTIME_ACCESS);
+-}
+-
+ /**
+ * efi_set_secure_state - modify secure boot state variables
+ * @secure_boot: value of SecureBoot
+diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c
+index f3533f4def3..a688c6da58d 100644
+--- a/lib/efi_loader/efi_variable.c
++++ b/lib/efi_loader/efi_variable.c
+@@ -566,6 +566,30 @@ efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *vendor,
+ return EFI_SUCCESS;
+ }
+
++efi_status_t __efi_runtime EFIAPI
++efi_get_variable_runtime(u16 *variable_name, const efi_guid_t *guid,
++ u32 *attributes, efi_uintn_t *data_size, void *data)
++{
++ efi_status_t ret;
++
++ ret = efi_get_variable_mem(variable_name, guid, attributes, data_size,
++ data, NULL, EFI_VARIABLE_RUNTIME_ACCESS);
++
++ /* Remove EFI_VARIABLE_READ_ONLY flag */
++ if (attributes)
++ *attributes &= EFI_VARIABLE_MASK;
++
++ return ret;
++}
++
++efi_status_t __efi_runtime EFIAPI
++efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size,
++ u16 *variable_name, efi_guid_t *guid)
++{
++ return efi_get_next_variable_name_mem(variable_name_size, variable_name,
++ guid, EFI_VARIABLE_RUNTIME_ACCESS);
++}
++
+ /**
+ * efi_variables_boot_exit_notify() - notify ExitBootServices() is called
+ */
new file mode 100644
@@ -0,0 +1,38 @@
+From 4c8504fe21f91bf462486afdfe4113f8bbc9f930 Mon Sep 17 00:00:00 2001
+From: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+Date: Sun, 11 Jan 2026 20:34:13 +0000
+Subject: [PATCH 46/53] corstone1000: enable bootefi selftest
+
+Enable UEFI selftest command in Corstone-1000 defconfig
+
+Turn on CONFIG_CMD_BOOTEFI_SELFTEST in the Corstone-1000 defconfig
+so the board can run the built-in UEFI self-test suite during
+development and validation.
+
+Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/]
+Signed-off-by: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+---
+ configs/corstone1000_defconfig | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig
+index 350607892fa..8de2f9bd440 100644
+--- a/configs/corstone1000_defconfig
++++ b/configs/corstone1000_defconfig
+@@ -41,6 +41,8 @@ CONFIG_SYS_PROMPT="corstone1000# "
+ # CONFIG_CMD_CONSOLE is not set
+ CONFIG_CMD_FWU_METADATA=y
+ CONFIG_CMD_BOOTZ=y
++# CONFIG_CMD_BOOTEFI_HELLO is not set
++CONFIG_CMD_BOOTEFI_SELFTEST=y
+ # CONFIG_CMD_XIMG is not set
+ CONFIG_CMD_NVEDIT_EFI=y
+ CONFIG_CMD_GPT=y
+@@ -79,6 +81,7 @@ CONFIG_SYSRESET=y
+ CONFIG_SYSRESET_PSCI=y
+ CONFIG_TEE=y
+ CONFIG_OPTEE=y
++# CONFIG_CMD_POWEROFF is not set
+ CONFIG_USB=y
+ CONFIG_USB_ISP1760=y
+ CONFIG_VIRTIO_MMIO=y
new file mode 100644
@@ -0,0 +1,176 @@
+From 85edddc46913a19ce9e2dd0cb802a2dac60c9b9d Mon Sep 17 00:00:00 2001
+From: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+Date: Sun, 11 Jan 2026 20:36:45 +0000
+Subject: [PATCH 47/53] efi: selftest: add runtime variable tests with
+ non-volatile storage
+
+Extend runtime variable tests for persistent storage
+
+Previously, EFI selftesting of runtime variables was only supported
+under CONFIG_EFI_RT_VOLATILE_STORE, which uses VarToFile to simulate
+non-volatile variable storage. This commit adds new test cases that
+exercise runtime variable operations for persistent storage.
+
+Features tested:
+- Creation of runtime-accessible variables (set)
+- Retrieval of runtime variables (get)
+- Deletion using SetVariable() with size = 0
+- Append operation using EFI_VARIABLE_APPEND_WRITE
+
+This improves EFI compliance validation for non-volatile runtime storage
+scenarios and ensures proper attribute enforcement and variable
+management.
+
+Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/]
+Signed-off-by: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+---
+ .../efi_selftest_variables_runtime.c | 108 +++++++++++++++++-
+ 1 file changed, 105 insertions(+), 3 deletions(-)
+
+diff --git a/lib/efi_selftest/efi_selftest_variables_runtime.c b/lib/efi_selftest/efi_selftest_variables_runtime.c
+index 379c4f9c47b..7c14b9fefd4 100644
+--- a/lib/efi_selftest/efi_selftest_variables_runtime.c
++++ b/lib/efi_selftest/efi_selftest_variables_runtime.c
+@@ -3,6 +3,7 @@
+ * efi_selftest_variables_runtime
+ *
+ * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
++ * Copyright (c) 2026 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ *
+ * This unit test checks the runtime services for variables after
+ * ExitBootServices():
+@@ -40,7 +41,13 @@ static int setup(const efi_handle_t img_handle,
+ /**
+ * execute() - execute unit test
+ *
+- * As runtime support is not implmented expect EFI_UNSUPPORTED to be returned.
++ * For EFI variables in non-volatile storage, these tests have to be executed in two phases
++ * 1. During first phase, run these tests and it creates EFI variable in persistent storage.
++ * 2. Then reboot and run the test again to verify if the variable created above is still
++ * available in non-volatile storage. If available, validate the EFI variable, append
++ * it, and validate it again. If validation is successful, delete the same variable.
++ *
++ * Return: EFI_ST_SUCCESS on success, EFI_ST_FAILURE on failure
+ */
+ static int execute(void)
+ {
+@@ -57,6 +64,80 @@ static int execute(void)
+ u64 max_storage, rem_storage, max_size;
+ int test_ret;
+
++ /* Compare the value of EFI variable if it already exists in non volatile storage */
++ if (!IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE)) {
++ len = sizeof(v) / 2;
++ ret = runtime->get_variable(u"efi_st_var0", &guid_vendor0,
++ &attr, &len, data);
++ if (ret == EFI_SUCCESS) {
++ efi_st_printf("EFI Variable efi_st_var0 found. Executing Second Phase\n");
++ if (len != sizeof(v) / 2) {
++ efi_st_error("GetVariable failed\n");
++ return EFI_ST_FAILURE;
++ }
++ if (memcmp(data, v, len)) {
++ efi_st_error("GetVariable failed\n");
++ return EFI_ST_FAILURE;
++ }
++
++ /* Append an existing variable */
++ append_len = sizeof(v) - len;
++ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
++ EFI_VARIABLE_BOOTSERVICE_ACCESS |
++ EFI_VARIABLE_RUNTIME_ACCESS |
++ EFI_VARIABLE_APPEND_WRITE |
++ EFI_VARIABLE_NON_VOLATILE,
++ append_len, (v + len));
++ if (ret != EFI_SUCCESS) {
++ efi_st_error("SetVariable failed\n");
++ return EFI_ST_FAILURE;
++ }
++
++ len = sizeof(v);
++ ret = runtime->get_variable(u"efi_st_var0", &guid_vendor0,
++ &attr, &len, data);
++ if (ret != EFI_SUCCESS) {
++ efi_st_error("GetVariable failed\n");
++ return EFI_ST_FAILURE;
++ }
++
++ if (len != sizeof(v)) {
++ efi_st_error("GetVariable failed\n");
++ return EFI_ST_FAILURE;
++ }
++
++ if (memcmp(data, v, len)) {
++ efi_st_error("GetVariable failed\n");
++ return EFI_ST_FAILURE;
++ }
++
++ /* Delete it by setting the size to 0 */
++ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
++ EFI_VARIABLE_BOOTSERVICE_ACCESS |
++ EFI_VARIABLE_RUNTIME_ACCESS |
++ EFI_VARIABLE_NON_VOLATILE,
++ 0, NULL);
++ if (ret != EFI_SUCCESS) {
++ efi_st_error("SetVariable failed\n");
++ return EFI_ST_FAILURE;
++ }
++
++ ret = runtime->get_variable(u"efi_st_var0", &guid_vendor0,
++ &attr, &len, data);
++ if (ret != EFI_NOT_FOUND) {
++ efi_st_error("GetVariable failed\n");
++ return EFI_ST_FAILURE;
++ }
++
++ return EFI_ST_SUCCESS;
++ } else {
++ if (ret == EFI_NOT_FOUND) {
++ efi_st_printf("EFI Variable efi_st_var0 not found. "
++ "Executing First Phase\n");
++ }
++ }
++ }
++
+ memset(v2, 0x1, sizeof(v2));
+
+ if (IS_ENABLED(CONFIG_EFI_VARIABLE_FILE_STORE)) {
+@@ -70,7 +151,7 @@ static int execute(void)
+ ret = runtime->query_variable_info(EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ &max_storage, &rem_storage,
+ &max_size);
+- if (ret != EFI_UNSUPPORTED) {
++ if (ret != EFI_ST_SUCCESS) {
+ efi_st_error("QueryVariableInfo failed\n");
+ return EFI_ST_FAILURE;
+ }
+@@ -286,7 +367,28 @@ static int execute(void)
+ return EFI_ST_FAILURE;
+ }
+ } else {
+- if (ret != EFI_UNSUPPORTED) {
++ if (ret != EFI_SUCCESS) {
++ efi_st_error("SetVariable failed\n");
++ return EFI_ST_FAILURE;
++ }
++
++ /* Delete it by setting the size to 0 */
++ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
++ EFI_VARIABLE_BOOTSERVICE_ACCESS |
++ EFI_VARIABLE_RUNTIME_ACCESS,
++ 0, NULL);
++ if (ret != EFI_SUCCESS) {
++ efi_st_error("SetVariable failed\n");
++ return EFI_ST_FAILURE;
++ }
++
++ /* Add an 8byte aligned variable */
++ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0,
++ EFI_VARIABLE_BOOTSERVICE_ACCESS |
++ EFI_VARIABLE_RUNTIME_ACCESS |
++ EFI_VARIABLE_NON_VOLATILE,
++ sizeof(v) / 2, v);
++ if (ret != EFI_SUCCESS) {
+ efi_st_error("SetVariable failed\n");
+ return EFI_ST_FAILURE;
+ }
new file mode 100644
@@ -0,0 +1,163 @@
+From d12b38b7af88fdb37487d5196e214c8a4ad561fc Mon Sep 17 00:00:00 2001
+From: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+Date: Wed, 14 Jan 2026 13:55:02 +0000
+Subject: [PATCH 48/53] test: dm: add sandbox FF-A runtime transport tests
+
+Exercise FF-A runtime helpers via sandbox DM tests
+
+Add driver-model unit tests that exercise the FF-A runtime helpers on
+sandbox. The new tests reuse the sandbox emulator to validate both the
+positive direct-request flow and failure handling, priming the emulator
+state so the runtime path can be executed.
+
+Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/]
+Signed-off-by: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+---
+ arch/sandbox/include/asm/sandbox_arm_ffa.h | 16 ++++-
+ test/dm/Makefile | 3 +-
+ test/dm/ffa_runtime.c | 82 ++++++++++++++++++++++
+ 3 files changed, 99 insertions(+), 2 deletions(-)
+ create mode 100644 test/dm/ffa_runtime.c
+
+diff --git a/arch/sandbox/include/asm/sandbox_arm_ffa.h b/arch/sandbox/include/asm/sandbox_arm_ffa.h
+index be2790f4960..a20eb159b73 100644
+--- a/arch/sandbox/include/asm/sandbox_arm_ffa.h
++++ b/arch/sandbox/include/asm/sandbox_arm_ffa.h
+@@ -1,6 +1,6 @@
+ /* SPDX-License-Identifier: GPL-2.0+ */
+ /*
+- * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
++ * Copyright 2022-2023, 2026 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ *
+ * Authors:
+ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+@@ -26,6 +26,20 @@
+ #define SANDBOX_SP3_ID 0x6452
+ #define SANDBOX_SP4_ID 0x7814
+
++/*
++ * The sandbox FF-A emulator uses fixed, synthetic execution-context counts and
++ * property bitfields for each partition.
++ */
++#define SANDBOX_SP1_EXEC_CTXT 0x5687
++#define SANDBOX_SP2_EXEC_CTXT 0x9587
++#define SANDBOX_SP3_EXEC_CTXT 0x7687
++#define SANDBOX_SP4_EXEC_CTXT 0x1487
++
++#define SANDBOX_SP1_PROPERTIES 0x89325621
++#define SANDBOX_SP2_PROPERTIES 0x45325621
++#define SANDBOX_SP3_PROPERTIES 0x23325621
++#define SANDBOX_SP4_PROPERTIES 0x70325621
++
+ /* Invalid service UUID (no matching SP) */
+ #define SANDBOX_SERVICE3_UUID "55d532ed-0942-e699-722d-c09ca798d9cd"
+
+diff --git a/test/dm/Makefile b/test/dm/Makefile
+index 474e77a2151..218b0b1411f 100644
+--- a/test/dm/Makefile
++++ b/test/dm/Makefile
+@@ -1,7 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0+
+ #
+ # Copyright (c) 2013 Google, Inc
+-# Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
++# Copyright 2022-2023, 2026 Arm Limited and/or its affiliates <open-source-office@arm.com>
+
+ # Tests for particular subsystems - when enabling driver model for a new
+ # subsystem you must add sandbox tests here.
+@@ -95,6 +95,7 @@ obj-$(CONFIG_ACPI_PMC) += pmc.o
+ obj-$(CONFIG_DM_PMIC) += pmic.o
+ obj-$(CONFIG_DM_PWM) += pwm.o
+ obj-$(CONFIG_ARM_FFA_TRANSPORT) += ffa.o
++obj-$(CONFIG_ARM_FFA_TRANSPORT) += ffa_runtime.o
+ obj-$(CONFIG_QFW) += qfw.o
+ obj-$(CONFIG_RAM) += ram.o
+ obj-y += regmap.o
+diff --git a/test/dm/ffa_runtime.c b/test/dm/ffa_runtime.c
+new file mode 100644
+index 00000000000..4ba859fa314
+--- /dev/null
++++ b/test/dm/ffa_runtime.c
+@@ -0,0 +1,82 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Functional tests for FF-A runtime helpers
++ *
++ * Copyright 2026 Arm Limited and/or its affiliates <open-source-office@arm.com>
++ *
++ * Authors:
++ * Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
++ */
++
++#include <dm.h>
++#include <dm/test.h>
++#include <asm/sandbox_arm_ffa.h>
++#include <asm/sandbox_arm_ffa_priv.h>
++#include <arm_ffa_runtime.h>
++#include <test/ut.h>
++
++static int ffa_runtime_get_sp_id(struct unit_test_state *uts, u16 *sp_id)
++{
++ struct ffa_partition_desc *descs;
++ u32 count;
++ struct udevice *dev;
++ const char *svc_uuid = SANDBOX_SERVICE1_UUID;
++
++ ut_assertok(uclass_first_device_err(UCLASS_FFA, &dev));
++ ut_assertok(ffa_partition_info_get(dev, svc_uuid, &count, &descs));
++ ut_assert(count > 0);
++
++ *sp_id = descs[0].info.id;
++ return 0;
++}
++
++static int dm_test_ffa_runtime_ack(struct unit_test_state *uts)
++{
++ struct ffa_send_direct_data msg = {0};
++ u16 sp_id;
++ u8 cnt;
++
++ ut_assertok(ffa_runtime_get_sp_id(uts, &sp_id));
++
++ ffa_copy_runtime_priv(&(struct ffa_priv_runtime){
++ .id = NS_PHYS_ENDPOINT_ID,
++ });
++ ffa_enable_runtime_context();
++
++ /* Ensure runtime context is available before attempting runtime-only paths */
++ ut_assert(ffa_get_status_runtime_context());
++
++ /* Runtime messaging should reuse the sandbox emulator and return 0xff pattern */
++ ut_assertok(ffa_sync_send_receive_runtime(sp_id, &msg, true));
++ for (cnt = 0; cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); cnt++)
++ ut_asserteq_64(-1UL, ((u64 *)&msg)[cnt]);
++
++ /* Validate FF-A error to errno translation helpers */
++ ut_asserteq(-EINVAL, ffa_to_std_errno(-INVALID_PARAMETERS));
++ ut_asserteq(-EOPNOTSUPP, ffa_to_std_errno(-NOT_SUPPORTED));
++
++ return 0;
++}
++DM_TEST(dm_test_ffa_runtime_ack, UTF_SCAN_FDT | UTF_CONSOLE);
++
++static int dm_test_ffa_runtime_nack(struct unit_test_state *uts)
++{
++ struct ffa_send_direct_data msg = {0};
++ u16 sp_id;
++
++ ut_assertok(ffa_runtime_get_sp_id(uts, &sp_id));
++
++ ffa_copy_runtime_priv(&(struct ffa_priv_runtime){
++ .id = NS_PHYS_ENDPOINT_ID,
++ });
++ ffa_enable_runtime_context();
++
++ /* Ensure runtime context is available before attempting runtime-only paths */
++ ut_assert(ffa_get_status_runtime_context());
++
++ /* Invalid partition IDs must be rejected and mapped to -EINVAL */
++ ut_asserteq(-EINVAL, ffa_sync_send_receive_runtime(0, &msg, true));
++
++ return 0;
++}
++DM_TEST(dm_test_ffa_runtime_nack, UTF_SCAN_FDT | UTF_CONSOLE);
new file mode 100644
@@ -0,0 +1,96 @@
+From 6e707fe6a0120a3a6aa7954a250293162a441b21 Mon Sep 17 00:00:00 2001
+From: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+Date: Thu, 15 Jan 2026 10:57:48 +0000
+Subject: [PATCH 49/53] sandbox: ffa: share synthetic partition metadata via
+ macros
+
+Reuse sandbox FF-A partition constants in emulator tests
+
+Allow the sandbox FF-A emulator test to use execution-context and property
+constants defined in sandbox_arm_ffa.h
+
+Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/]
+Signed-off-by: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+---
+ drivers/firmware/arm-ffa/ffa-emul-uclass.c | 36 ++++++++++++++++------
+ 1 file changed, 26 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/firmware/arm-ffa/ffa-emul-uclass.c b/drivers/firmware/arm-ffa/ffa-emul-uclass.c
+index 531ecb12f3a..33d38d37ddb 100644
+--- a/drivers/firmware/arm-ffa/ffa-emul-uclass.c
++++ b/drivers/firmware/arm-ffa/ffa-emul-uclass.c
+@@ -1,6 +1,6 @@
+ // SPDX-License-Identifier: GPL-2.0+
+ /*
+- * Copyright 2022-2023, 2025 Arm Limited and/or its affiliates <open-source-office@arm.com>
++ * Copyright 2022-2023, 2025-2026 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ *
+ * Authors:
+ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+@@ -22,41 +22,57 @@ DECLARE_GLOBAL_DATA_PTR;
+ /* The partitions (SPs) table */
+ static struct ffa_partition_desc sandbox_partitions[SANDBOX_PARTITIONS_CNT] = {
+ {
+- .info = { .id = SANDBOX_SP1_ID, .exec_ctxt = 0x5687, .properties = 0x89325621 },
++ .info = {
++ .id = SANDBOX_SP1_ID,
++ .exec_ctxt = SANDBOX_SP1_EXEC_CTXT,
++ .properties = SANDBOX_SP1_PROPERTIES,
++ },
+ .sp_uuid = {
+ .a1 = SANDBOX_SERVICE1_UUID_A1,
+ .a2 = SANDBOX_SERVICE1_UUID_A2,
+ .a3 = SANDBOX_SERVICE1_UUID_A3,
+ .a4 = SANDBOX_SERVICE1_UUID_A4,
+- }
++ },
+ },
+ {
+- .info = { .id = SANDBOX_SP3_ID, .exec_ctxt = 0x7687, .properties = 0x23325621 },
++ .info = {
++ .id = SANDBOX_SP2_ID,
++ .exec_ctxt = SANDBOX_SP2_EXEC_CTXT,
++ .properties = SANDBOX_SP2_PROPERTIES,
++ },
+ .sp_uuid = {
+ .a1 = SANDBOX_SERVICE2_UUID_A1,
+ .a2 = SANDBOX_SERVICE2_UUID_A2,
+ .a3 = SANDBOX_SERVICE2_UUID_A3,
+ .a4 = SANDBOX_SERVICE2_UUID_A4,
+- }
++ },
+ },
+ {
+- .info = { .id = SANDBOX_SP2_ID, .exec_ctxt = 0x9587, .properties = 0x45325621 },
++ .info = {
++ .id = SANDBOX_SP3_ID,
++ .exec_ctxt = SANDBOX_SP3_EXEC_CTXT,
++ .properties = SANDBOX_SP3_PROPERTIES,
++ },
+ .sp_uuid = {
+ .a1 = SANDBOX_SERVICE1_UUID_A1,
+ .a2 = SANDBOX_SERVICE1_UUID_A2,
+ .a3 = SANDBOX_SERVICE1_UUID_A3,
+ .a4 = SANDBOX_SERVICE1_UUID_A4,
+- }
++ },
+ },
+ {
+- .info = { .id = SANDBOX_SP4_ID, .exec_ctxt = 0x1487, .properties = 0x70325621 },
++ .info = {
++ .id = SANDBOX_SP4_ID,
++ .exec_ctxt = SANDBOX_SP4_EXEC_CTXT,
++ .properties = SANDBOX_SP4_PROPERTIES,
++ },
+ .sp_uuid = {
+ .a1 = SANDBOX_SERVICE2_UUID_A1,
+ .a2 = SANDBOX_SERVICE2_UUID_A2,
+ .a3 = SANDBOX_SERVICE2_UUID_A3,
+ .a4 = SANDBOX_SERVICE2_UUID_A4,
+- }
+- }
++ },
++ },
+
+ };
+
new file mode 100644
@@ -0,0 +1,176 @@
+From a4d871a16513e86290aaa2304f0195e533200937 Mon Sep 17 00:00:00 2001
+From: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+Date: Wed, 25 Feb 2026 14:40:19 +0000
+Subject: [PATCH 50/53] doc: arm64: document FF-A runtime path for EFI
+ variables
+
+Document how EFI runtime variables use FF-A MM communication
+
+Describe the FF-A runtime layer on arm64 and how EFI variable
+runtime services use the FF-A transport and shared MM buffer.
+Also clarify armffa command scope and link to the FF-A doc.
+
+Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/]
+Signed-off-by: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+---
+ doc/arch/arm64.ffa.rst | 92 +++++++++++++++++++++++++++++++++++++---
+ doc/usage/cmd/armffa.rst | 11 +++++
+ 2 files changed, 96 insertions(+), 7 deletions(-)
+
+diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst
+index d2c4fb49f79..53218cd9c96 100644
+--- a/doc/arch/arm64.ffa.rst
++++ b/doc/arch/arm64.ffa.rst
+@@ -15,10 +15,10 @@ application in S-EL0, or a Trusted OS in S-EL1.
+ The U-Boot FF-A support (the bus) implements the interfaces to communicate
+ with partitions in the Secure world aka Secure partitions (SPs).
+
+-The FF-A support specifically focuses on communicating with SPs that
+-isolate portions of EFI runtime services that must run in a protected
+-environment which is inaccessible by the Host OS or Hypervisor.
+-Examples of such services are set/get variables.
++U-Boot's FF-A bus support exposes an optional transport that EFI runtime
++services can use to communicate with Secure Partitions. Through this
++interface, EFI services (such as variable access) can request or exchange
++data with the Secure World using FF-A.
+
+ The FF-A support uses the SMC ABIs defined by the FF-A specification to:
+
+@@ -26,13 +26,13 @@ The FF-A support uses the SMC ABIs defined by the FF-A specification to:
+ - Access an SP's service through communication protocols
+ e.g. EFI MM communication protocol
+
+-At this stage of development only EFI boot-time services are supported.
+-Runtime support will be added in future developments.
+-
+ The U-Boot FF-A support provides the following parts:
+
+ - A Uclass driver providing generic FF-A methods.
+ - An Arm FF-A device driver providing Arm-specific methods and reusing the Uclass methods.
++- An optional runtime FF-A transport (toggled via CONFIG_ARM_FFA_RT_MODE) that is
++ resident after ExitBootServices() and enables EFI runtime variable services
++ via FF-A helpers and a shared MM communication buffer.
+ - A sandbox emulator for Arm FF-A, emulates the FF-A side of the Secure World and provides
+ FF-A ABIs inspection methods.
+ - An FF-A sandbox device driver for FF-A communication with the emulated Secure World.
+@@ -69,6 +69,12 @@ CONFIG_ARM_FFA_TRANSPORT
+ When using an Arm 64-bit platform, the Arm FF-A driver will be used.
+ When using sandbox, the sandbox FF-A emulator and FF-A sandbox driver will be used.
+
++CONFIG_ARM_FFA_RT_MODE
++ Enables the FF-A runtime transport. This option depends on
++ CONFIG_ARM_FFA_TRANSPORT and CONFIG_EFI_LOADER, and is enabled by default.
++ When enabled, a minimal set of FF-A operations remains available after
++ ExitBootServices().
++
+ FF-A ABIs under the hood
+ ------------------------
+
+@@ -158,6 +164,77 @@ they want to use (32-bit vs 64-bit). Selecting the protocol means using
+ the 32-bit or 64-bit version of FFA_MSG_SEND_DIRECT_{REQ, RESP}.
+ The calling convention between U-Boot and the secure world stays the same: SMC32.
+
++FF-A runtime support for EFI variables
++--------------------------------------
++
++U-Boot provides an FF-A runtime transport to keep a minimal set of FF-A operations
++available after ExitBootServices(). This runtime transport is enabled with
++CONFIG_ARM_FFA_RT_MODE. The runtime helpers and data are marked with
++``__efi_runtime`` / ``__efi_runtime_data`` through the
++``__ffa_runtime`` / ``__ffa_runtime_data`` aliases in
++``include/arm_ffa_runtime.h``.
++
++Runtime context is enabled at ExitBootServices(), and the following prerequisites
++are prepared during FF-A bus probe:
++
++- The U-Boot FF-A endpoint ID is discovered at boot time with FFA_ID_GET and
++ stored in the runtime private data.
++- An ExitBootServices() event is registered by the runtime transport to enable
++ the runtime context with ffa_enable_runtime_context() when EFI transitions
++ to runtime.
++
++At runtime, the driver model is no longer available. Runtime users should
++use the runtime interface ffa_sync_send_receive_runtime(), which internally
++verifies the runtime context before issuing FF-A calls.
++
++EFI variable services over FF-A
++-------------------------------
++
++When CONFIG_EFI_MM_COMM_TEE and CONFIG_ARM_FFA_TRANSPORT are enabled, U-Boot
++routes EFI variable services to an MM Secure Partition (SP) over FF-A.
++The MM SP is discovered at boot time using FFA_PARTITION_INFO_GET and its
++partition ID is cached in runtime data.
++
++The data path uses a shared MM communication buffer and a door-bell style
++direct message:
++
++- The communication buffer is located at CONFIG_FFA_SHARED_MM_BUF_ADDR and
++ sized by CONFIG_FFA_SHARED_MM_BUF_SIZE.
++- CONFIG_FFA_SHARED_MM_BUF_OFFSET is sent in the FF-A direct message payload
++ to indicate where the data begins.
++- The buffer is filled by memcpy(), the cache is flushed before notifying the
++ MM SP, and later the buffer is reused directly with only the shared-buffer
++ range invalidated (invalidate_dcache_range()) to avoid whole-cache
++ invalidation.
++
++After ExitBootServices(), EFI SetVariable()/GetVariable() call paths use the
++runtime MM communication helpers:
++
++- get_comm_buf() switches to the static shared buffer
++- mm_communicate_runtime() selects FF-A transport when the runtime context
++ is enabled
++- ffa_mm_communicate_runtime() issues FFA_MSG_SEND_DIRECT_{REQ,RESP} through
++ ffa_sync_send_receive_runtime()
++
++Configuration notes
++-------------------
++
++Enable FF-A transport and MM communication:
++
++- CONFIG_ARM_FFA_TRANSPORT
++- CONFIG_ARM_FFA_RT_MODE
++- CONFIG_EFI_MM_COMM_TEE
++
++CONFIG_ARM_FFA_RT_MODE is enabled by default. It turns on MM communication for
++EFI runtime and may be disabled when EFI services over FF-A are not required
++after ExitBootServices().
++
++Configure the shared MM communication buffer:
++
++- CONFIG_FFA_SHARED_MM_BUF_ADDR
++- CONFIG_FFA_SHARED_MM_BUF_SIZE
++- CONFIG_FFA_SHARED_MM_BUF_OFFSET
++
+ Requirements for user drivers
+ -----------------------------
+
+@@ -260,6 +337,7 @@ For example, when using FF-A with Corstone-1000, debug logs enabled, the output
+ Contributors
+ ------------
+ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
++ * Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+
+ .. _`FF-A v1.0 specification`: https://documentation-service.arm.com/static/5fb7e8a6ca04df4095c1d65e
+ .. _`SMC Calling Convention v1.2 specification`: https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6
+diff --git a/doc/usage/cmd/armffa.rst b/doc/usage/cmd/armffa.rst
+index 4f41e3393fd..5dcc3047a45 100644
+--- a/doc/usage/cmd/armffa.rst
++++ b/doc/usage/cmd/armffa.rst
+@@ -39,6 +39,17 @@ The command also allows to gather secure partitions information and ping these
+
+ The command is also helpful in testing the communication with secure partitions.
+
++Notes
++-----
++
++armffa is a boot-time test command. It relies on the FF-A driver model device
++(UCLASS_FFA) and is not intended for EFI runtime use after
++ExitBootServices().
++
++armffa does not provide EFI variable operations. For more information about
++using EFI runtime SetVariable() and GetVariable() over FF-A please see
++:doc:`../../arch/arm64.ffa`.
++
+ Example
+ -------
+
new file mode 100644
@@ -0,0 +1,47 @@
+From d595e579413e8bcf1f65be25f3da340abb5d2c2b Mon Sep 17 00:00:00 2001
+From: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+Date: Wed, 1 Apr 2026 13:37:35 +0100
+Subject: [PATCH 51/53] doc: bootefi: note two-phase runtime variables selftest
+
+Explain how the runtime variable selftest runs in two phases
+
+Document that the "variables at runtime" selftest runs in two
+phases when CONFIG_EFI_RT_VOLATILE_STORE is not enabled, and
+show how to select it with efi_selftest.
+
+Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/]
+Signed-off-by: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+---
+ doc/usage/cmd/bootefi.rst | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/doc/usage/cmd/bootefi.rst b/doc/usage/cmd/bootefi.rst
+index 7c5448586b7..91aebf1a4b5 100644
+--- a/doc/usage/cmd/bootefi.rst
++++ b/doc/usage/cmd/bootefi.rst
+@@ -160,6 +160,16 @@ environment variable to match one of the listed identifiers
+ Some of the tests execute the ExitBootServices() UEFI boot service and will not
+ return to the command line but require a board reset.
+
++The test *variables at runtime* runs in two phases when
++CONFIG_EFI_RT_VOLATILE_STORE is not enabled. Run it once to create a
++runtime-accessible variable in non-volatile storage, reboot, then run it
++again to validate, append, and delete that variable.
++
++::
++
++ => setenv efi_selftest 'variables at runtime'
++ => bootefi selftest
++
+ Configuration
+ -------------
+
+@@ -167,6 +177,8 @@ To use the *bootefi* command you must specify CONFIG_CMD_BOOTEFI=y.
+ The *bootefi bootmgr* sub-command requries CMD_BOOTEFI_BOOTMGR=y.
+ The *bootefi hello* sub-command requries CMD_BOOTEFI_HELLO=y.
+ The *bootefi selftest* sub-command depends on CMD_BOOTEFI_SELFTEST=y.
++The *variables at runtime* selftest runs in two phases when
++CONFIG\_EFI\_RT\_VOLATILE\_STORE is not enabled.
+
+ See also
+ --------
new file mode 100644
@@ -0,0 +1,75 @@
+From 00b68d1c3dd2d182545bcacc668dbb2f637c94c1 Mon Sep 17 00:00:00 2001
+From: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+Date: Fri, 10 Apr 2026 13:50:25 +0100
+Subject: [PATCH 52/53] efi_loader: align FF-A cache maintenance with runtime
+ path
+
+Match boot-time FF-A cache handling to runtime behavior
+
+The boot-time FF-A MM communication path used invalidate_dcache_all()
+after copying the message into the shared buffer. This differs from the
+runtime path, which performs range-based maintenance to avoid global cache
+operations.
+
+Update ffa_mm_communicate() to use the same pattern as the runtime helper:
+clean the shared buffer range before the SMC and invalidate the same range
+after the response. This keeps boot-time and runtime behavior consistent
+and avoids whole-cache invalidation.
+
+Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/]
+Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+Signed-off-by: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+---
+ lib/efi_loader/efi_variable_tee.c | 33 ++++++++++++++++++++++++-------
+ 1 file changed, 26 insertions(+), 7 deletions(-)
+
+diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c
+index 30687c21b8e..df509a435b1 100644
+--- a/lib/efi_loader/efi_variable_tee.c
++++ b/lib/efi_loader/efi_variable_tee.c
+@@ -389,19 +389,38 @@ static efi_status_t ffa_mm_communicate(void *comm_buf, ulong comm_buf_size)
+ memcpy(virt_shared_buf, comm_buf, tx_data_size);
+
+ /*
+- * The secure world might have cache disabled for
+- * the device region used for shared buffer (which is the case for Optee).
+- * In this case, the secure world reads the data from DRAM.
+- * Let's flush the cache so the DRAM is updated with the latest data.
++ * Shared buffer cache maintenance for FF-A / OP-TEE communication:
++ *
++ * NS -> S (request path):
++ *
++ * The non-secure side populates the shared buffer. If the buffer is cached
++ * in NS, the updated bytes may reside in dirty D-cache lines and not yet be
++ * visible in DDR. Since the secure world typically reads the shared buffer
++ * directly from DDR (e.g. with caches disabled / non-coherent mapping), we
++ * must clean the corresponding cache lines to the Point of Coherency (PoC)
++ * before entering secure world.
++ *
++ * S -> NS (response path):
++ *
++ * The secure world may update the same shared buffer in DDR. After returning
++ * to non-secure, any cached copies of that region in NS may be stale. We
++ * therefore invalidate the shared buffer range after the FF-A call to drop
++ * those lines and force subsequent reads to fetch the latest data from DDR.
+ */
+-#ifdef CONFIG_ARM64
+- invalidate_dcache_all();
+-#endif
++ if (IS_ENABLED(CONFIG_ARM64))
++ flush_dcache_range((unsigned long)virt_shared_buf,
++ (unsigned long)virt_shared_buf +
++ CONFIG_FFA_SHARED_MM_BUF_SIZE);
+
+ /* Announce there is data in the shared buffer */
+
+ ffa_ret = ffa_notify_mm_sp();
+
++ if (IS_ENABLED(CONFIG_ARM64))
++ invalidate_dcache_range((unsigned long)virt_shared_buf,
++ (unsigned long)virt_shared_buf +
++ CONFIG_FFA_SHARED_MM_BUF_SIZE);
++
+ switch (ffa_ret) {
+ case 0: {
+ ulong rx_data_size;
new file mode 100644
@@ -0,0 +1,50 @@
+From 1be3bcbc0c7efc36b8fbac7ef0f8c8375dc6770d Mon Sep 17 00:00:00 2001
+From: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+Date: Sat, 18 Apr 2026 14:26:28 +0100
+Subject: [PATCH 53/53] efi_loader: fix AllocatePages overlap status
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Return EFI_NOT_FOUND for EFI_ALLOCATE_ADDRESS overlap
+
+When efi_allocate_pages() is called with EFI_ALLOCATE_ADDRESS, UEFI
+expects EFI_NOT_FOUND if the requested address range is already
+allocated or unavailable. U-Boot currently returns
+EFI_OUT_OF_RESOURCES when efi_update_memory_map() detects an overlap
+after a successful lmb_alloc_mem(), which does not match
+EFI_ALLOCATE_ADDRESS semantics.
+
+Return EFI_NOT_FOUND for EFI_ALLOCATE_ADDRESS requests that fail due
+to an overlapping EFI memory descriptor, while keeping
+EFI_OUT_OF_RESOURCES for other allocation types.
+
+The UEFI specification [1] specifies that
+EFI_BOOT_SERVICES.AllocatePages must return EFI_NOT_FOUND when the
+requested address range is unavailable or already allocated;
+EFI_OUT_OF_RESOURCES applies to non‑address‑specific allocation
+failures.
+
+[1] https://uefi.org/specs/UEFI/2.10_A/07_Services_Boot_Services.html
+
+Upstream-Status: Submitted [https://lore.kernel.org/u-boot/20260427150530.3156000-1-harsimransingh.tungal@arm.com/]
+Signed-off-by: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+---
+ lib/efi_loader/efi_memory.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c
+index b77c2f980cc..63cf1a7277f 100644
+--- a/lib/efi_loader/efi_memory.c
++++ b/lib/efi_loader/efi_memory.c
+@@ -510,7 +510,9 @@ efi_status_t efi_allocate_pages(enum efi_allocate_type type,
+ /* Map would overlap, bail out */
+ lmb_free(addr, (u64)pages << EFI_PAGE_SHIFT, flags);
+ unmap_sysmem((void *)(uintptr_t)efi_addr);
+- return EFI_OUT_OF_RESOURCES;
++ if (type == EFI_ALLOCATE_ADDRESS)
++ return EFI_NOT_FOUND;
++ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *memory = efi_addr;
new file mode 100644
@@ -0,0 +1,37 @@
+From 0da9650a3ed86e63c6a9621d7a9195eb3a75a233 Mon Sep 17 00:00:00 2001
+From: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+Date: Fri, 17 Apr 2026 13:41:07 +0100
+Subject: [PATCH] corstone1000-a320: enable bootefi selftest
+
+Turn on CONFIG_CMD_BOOTEFI_SELFTEST in the Corstone-1000 defconfig
+so the board can run the built-in UEFI self-test suite during
+development and validation.
+
+Upstream-Status: Pending [Will be submitted upstream with the
+pending Cortex-A320 related U-Boot changes]
+Signed-off-by: Harsimran Singh Tungal <harsimransingh.tungal@arm.com>
+---
+ configs/corstone1000-a320_defconfig | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/configs/corstone1000-a320_defconfig b/configs/corstone1000-a320_defconfig
+index d0ae1e745db..c4dcd1790cc 100644
+--- a/configs/corstone1000-a320_defconfig
++++ b/configs/corstone1000-a320_defconfig
+@@ -41,6 +41,8 @@ CONFIG_SYS_PROMPT="corstone1000# "
+ # CONFIG_CMD_CONSOLE is not set
+ CONFIG_CMD_FWU_METADATA=y
+ CONFIG_CMD_BOOTZ=y
++# CONFIG_CMD_BOOTEFI_HELLO is not set
++CONFIG_CMD_BOOTEFI_SELFTEST=y
+ # CONFIG_CMD_XIMG is not set
+ CONFIG_CMD_NVEDIT_EFI=y
+ CONFIG_CMD_GPT=y
+@@ -76,6 +78,7 @@ CONFIG_SYSRESET=y
+ CONFIG_SYSRESET_PSCI=y
+ CONFIG_TEE=y
+ CONFIG_OPTEE=y
++# CONFIG_CMD_POWEROFF is not set
+ CONFIG_USB=y
+ CONFIG_USB_ISP1760=y
+ CONFIG_VIRTIO_MMIO=y
This series wires up Arm FF-A support for EFI runtime services by factoring runtime-safe helpers into the FF-A bus, layer the EFI variable TEE transport on top, and then rehome the helper exports so there’s a single implementation shared between boot and runtime paths. Corstone1000 enables the self-test command and expands the runtime variable selftest so it exercises non-volatile storage across reboots. Key Changes =========== - Add the FF-A runtime transport patch stack to the corstone1000 U-Boot recipe. - Enable EFI runtime variable handling over FF-A and include the bootefi selftests and sandbox FF-A runtime transport test. - Increase the FIP partition sizes from 2MiB to 2.5MiB and update TFA_FIP_RE_SIGN_BIN_SIZE from 0x00200000 to 0x00280000 to reflect the new allocation. The size of u-boot-EFI-2025.10-r0.bin has increased from ~707KB to ~944KB following the FF-A/EFI runtime related changes. As this binary is packaged within the FIP, the overall signed FIP size has grown accordingly. - Add a Yocto patch to increase the FIP partition size from 2MB to 2.5MB in the TF-M flash layout for Corstone-1000. Signed-off-by: Harsimran Singh Tungal <harsimransingh.tungal@arm.com> --- .../corstone1000-flash-firmware-fvp.wks.in | 2 +- .../corstone1000-flash-firmware-image.bb | 2 +- ...tone1000-Increase-FIP-partition-size.patch | 32 + .../trusted-firmware-m-corstone1000.inc | 1 + .../u-boot/u-boot-corstone1000.inc | 18 + ...efi_loader-add-runtime-memset-helper.patch | 63 ++ ...arm-ffa-add-FF-A-bus-runtime-support.patch | 975 ++++++++++++++++++ ...F-A-runtime-support-in-EFI-variable-.patch | 478 +++++++++ ...e-EFI-runtime-SetVariable-GetVariabl.patch | 418 ++++++++ ...runtime-GetVariable-helpers-to-efi_v.patch | 103 ++ ...corstone1000-enable-bootefi-selftest.patch | 38 + ...-runtime-variable-tests-with-non-vol.patch | 176 ++++ ...sandbox-FF-A-runtime-transport-tests.patch | 163 +++ ...e-synthetic-partition-metadata-via-m.patch | 96 ++ ...nt-FF-A-runtime-path-for-EFI-variabl.patch | 176 ++++ ...-two-phase-runtime-variables-selftes.patch | 47 + ...-FF-A-cache-maintenance-with-runtime.patch | 75 ++ ...der-fix-AllocatePages-overlap-status.patch | 50 + ...one1000-a320-enable-bootefi-selftest.patch | 37 + 19 files changed, 2948 insertions(+), 2 deletions(-) create mode 100644 meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0060-platform-corstone1000-Increase-FIP-partition-size.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0041-efi_loader-add-runtime-memset-helper.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0042-arm-ffa-add-FF-A-bus-runtime-support.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0043-efi_loader-add-FF-A-runtime-support-in-EFI-variable-.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0044-efi_loader-enable-EFI-runtime-SetVariable-GetVariabl.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0045-efi_loader-move-runtime-GetVariable-helpers-to-efi_v.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0046-corstone1000-enable-bootefi-selftest.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0047-efi-selftest-add-runtime-variable-tests-with-non-vol.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0048-test-dm-add-sandbox-FF-A-runtime-transport-tests.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0049-sandbox-ffa-share-synthetic-partition-metadata-via-m.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0050-doc-arm64-document-FF-A-runtime-path-for-EFI-variabl.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0051-doc-bootefi-note-two-phase-runtime-variables-selftes.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0052-efi_loader-align-FF-A-cache-maintenance-with-runtime.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0053-efi_loader-fix-AllocatePages-overlap-status.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0054-corstone1000-a320-enable-bootefi-selftest.patch