diff mbox series

[1/1] arm-bsp: corstone1000: Enable FF-A-backed EFI runtime variables and selftests

Message ID 20260512151924.89401-2-harsimransingh.tungal@arm.com
State New
Headers show
Series arm-bsp: corstone1000: Enable FF-A-backed EFI runtime variables and selftests | expand

Commit Message

Harsimran Singh Tungal May 12, 2026, 3:19 p.m. UTC
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
diff mbox series

Patch

diff --git a/meta-arm-bsp/files/wic/corstone1000-flash-firmware-fvp.wks.in b/meta-arm-bsp/files/wic/corstone1000-flash-firmware-fvp.wks.in
index 33699512..89e68426 100644
--- a/meta-arm-bsp/files/wic/corstone1000-flash-firmware-fvp.wks.in
+++ b/meta-arm-bsp/files/wic/corstone1000-flash-firmware-fvp.wks.in
@@ -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
diff --git a/meta-arm-bsp/recipes-bsp/images/corstone1000-flash-firmware-image.bb b/meta-arm-bsp/recipes-bsp/images/corstone1000-flash-firmware-image.bb
index a8c74086..5ce621bb 100644
--- a/meta-arm-bsp/recipes-bsp/images/corstone1000-flash-firmware-image.bb
+++ b/meta-arm-bsp/recipes-bsp/images/corstone1000-flash-firmware-image.bb
@@ -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"
diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0060-platform-corstone1000-Increase-FIP-partition-size.patch b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0060-platform-corstone1000-Increase-FIP-partition-size.patch
new file mode 100644
index 00000000..57cb3071
--- /dev/null
+++ b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0060-platform-corstone1000-Increase-FIP-partition-size.patch
@@ -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 */
diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-corstone1000.inc b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-corstone1000.inc
index 6ee13b30..2f96cb90 100644
--- a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-corstone1000.inc
+++ b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-corstone1000.inc
@@ -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"
diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot-corstone1000.inc b/meta-arm-bsp/recipes-bsp/u-boot/u-boot-corstone1000.inc
index e3dec3c0..fe5ffafc 100644
--- a/meta-arm-bsp/recipes-bsp/u-boot/u-boot-corstone1000.inc
+++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot-corstone1000.inc
@@ -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
 }
diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0041-efi_loader-add-runtime-memset-helper.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0041-efi_loader-add-runtime-memset-helper.patch
new file mode 100644
index 00000000..b0da200c
--- /dev/null
+++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0041-efi_loader-add-runtime-memset-helper.patch
@@ -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
+  *
diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0042-arm-ffa-add-FF-A-bus-runtime-support.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0042-arm-ffa-add-FF-A-bus-runtime-support.patch
new file mode 100644
index 00000000..94d24a7f
--- /dev/null
+++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0042-arm-ffa-add-FF-A-bus-runtime-support.patch
@@ -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;
+ }
diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0043-efi_loader-add-FF-A-runtime-support-in-EFI-variable-.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0043-efi_loader-add-FF-A-runtime-support-in-EFI-variable-.patch
new file mode 100644
index 00000000..2f0c1d68
--- /dev/null
+++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0043-efi_loader-add-FF-A-runtime-support-in-EFI-variable-.patch
@@ -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)
diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0044-efi_loader-enable-EFI-runtime-SetVariable-GetVariabl.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0044-efi_loader-enable-EFI-runtime-SetVariable-GetVariabl.patch
new file mode 100644
index 00000000..6a5748b8
--- /dev/null
+++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0044-efi_loader-enable-EFI-runtime-SetVariable-GetVariabl.patch
@@ -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;
+ }
+ 
+ /**
diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0045-efi_loader-move-runtime-GetVariable-helpers-to-efi_v.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0045-efi_loader-move-runtime-GetVariable-helpers-to-efi_v.patch
new file mode 100644
index 00000000..f3da408a
--- /dev/null
+++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0045-efi_loader-move-runtime-GetVariable-helpers-to-efi_v.patch
@@ -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
+  */
diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0046-corstone1000-enable-bootefi-selftest.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0046-corstone1000-enable-bootefi-selftest.patch
new file mode 100644
index 00000000..c1dc141a
--- /dev/null
+++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0046-corstone1000-enable-bootefi-selftest.patch
@@ -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
diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0047-efi-selftest-add-runtime-variable-tests-with-non-vol.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0047-efi-selftest-add-runtime-variable-tests-with-non-vol.patch
new file mode 100644
index 00000000..b24f128e
--- /dev/null
+++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0047-efi-selftest-add-runtime-variable-tests-with-non-vol.patch
@@ -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;
+ 		}
diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0048-test-dm-add-sandbox-FF-A-runtime-transport-tests.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0048-test-dm-add-sandbox-FF-A-runtime-transport-tests.patch
new file mode 100644
index 00000000..8682a181
--- /dev/null
+++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0048-test-dm-add-sandbox-FF-A-runtime-transport-tests.patch
@@ -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);
diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0049-sandbox-ffa-share-synthetic-partition-metadata-via-m.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0049-sandbox-ffa-share-synthetic-partition-metadata-via-m.patch
new file mode 100644
index 00000000..f807be49
--- /dev/null
+++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0049-sandbox-ffa-share-synthetic-partition-metadata-via-m.patch
@@ -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,
+-		}
+-	}
++		},
++	},
+ 
+ };
+ 
diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0050-doc-arm64-document-FF-A-runtime-path-for-EFI-variabl.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0050-doc-arm64-document-FF-A-runtime-path-for-EFI-variabl.patch
new file mode 100644
index 00000000..f4107244
--- /dev/null
+++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0050-doc-arm64-document-FF-A-runtime-path-for-EFI-variabl.patch
@@ -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
+ -------
+ 
diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0051-doc-bootefi-note-two-phase-runtime-variables-selftes.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0051-doc-bootefi-note-two-phase-runtime-variables-selftes.patch
new file mode 100644
index 00000000..0b5d0348
--- /dev/null
+++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0051-doc-bootefi-note-two-phase-runtime-variables-selftes.patch
@@ -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
+ --------
diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0052-efi_loader-align-FF-A-cache-maintenance-with-runtime.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0052-efi_loader-align-FF-A-cache-maintenance-with-runtime.patch
new file mode 100644
index 00000000..3f7a0d2f
--- /dev/null
+++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0052-efi_loader-align-FF-A-cache-maintenance-with-runtime.patch
@@ -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;
diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0053-efi_loader-fix-AllocatePages-overlap-status.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0053-efi_loader-fix-AllocatePages-overlap-status.patch
new file mode 100644
index 00000000..5f71526f
--- /dev/null
+++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0053-efi_loader-fix-AllocatePages-overlap-status.patch
@@ -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;
diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0054-corstone1000-a320-enable-bootefi-selftest.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0054-corstone1000-a320-enable-bootefi-selftest.patch
new file mode 100644
index 00000000..f53e2ff2
--- /dev/null
+++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0054-corstone1000-a320-enable-bootefi-selftest.patch
@@ -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